diff --git a/.gitignore b/.gitignore index 71b77da25c4d53db49b24642d88062906e7db219..56f7580de26e47f75f8bf16346b35f35e229491d 100644 --- a/.gitignore +++ b/.gitignore @@ -130,6 +130,9 @@ venv.bak/ # VSCode project settings .vscode/ +# Visual Studio project settings +/.vs + # Rope project settings .ropeproject diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ffbf9e9f5f02bd0514d4584f6f2ad32761b6264f..3de792462d28b2d42e71b0329aefce2c2928984e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -41,3 +41,4 @@ include: #- local: '/src/l3_attackmitigator/.gitlab-ci.yml' #- local: '/src/slice/.gitlab-ci.yml' #- local: '/src/interdomain/.gitlab-ci.yml' + - local: '/src/pathcomp/.gitlab-ci.yml' diff --git a/scripts/run_tests_locally-pathcomp.sh b/scripts/run_tests_locally-pathcomp-frontend.sh similarity index 95% rename from scripts/run_tests_locally-pathcomp.sh rename to scripts/run_tests_locally-pathcomp-frontend.sh index f56f47a8b592939243a2ec5d9fd95d89046582d1..1bcf5e7f3792622622f9e59978fddbf11c54e492 100755 --- a/scripts/run_tests_locally-pathcomp.sh +++ b/scripts/run_tests_locally-pathcomp-frontend.sh @@ -25,4 +25,4 @@ RCFILE=$PROJECTDIR/coverage/.coveragerc #-o log_cli=true -o log_file=service.log -o log_file_level=DEBUG coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ - pathcomp/tests/test_unitary.py + pathcomp/frontend/tests/test_unitary.py diff --git a/src/common/tools/object_factory/Link.py b/src/common/tools/object_factory/Link.py index 13973566ece5e6f83312c9bd50ca0c4add2d262b..922a39dbe24f4f4b635f378180ab13c80322801b 100644 --- a/src/common/tools/object_factory/Link.py +++ b/src/common/tools/object_factory/Link.py @@ -18,7 +18,7 @@ from typing import Dict, List def get_link_uuid(a_endpoint_id : Dict, z_endpoint_id : Dict) -> str: return '{:s}/{:s}=={:s}/{:s}'.format( a_endpoint_id['device_id']['device_uuid']['uuid'], a_endpoint_id['endpoint_uuid']['uuid'], - a_endpoint_id['device_id']['device_uuid']['uuid'], z_endpoint_id['endpoint_uuid']['uuid']) + z_endpoint_id['device_id']['device_uuid']['uuid'], z_endpoint_id['endpoint_uuid']['uuid']) def json_link_id(link_uuid : str): return {'link_uuid': {'uuid': link_uuid}} diff --git a/src/common/tools/object_factory/Service.py b/src/common/tools/object_factory/Service.py index f0f49210ec067267984dede6f28d7adad8009261..51f75e6dbe5e430330e697da772d65703f7568c7 100644 --- a/src/common/tools/object_factory/Service.py +++ b/src/common/tools/object_factory/Service.py @@ -21,7 +21,7 @@ from common.tools.object_factory.Context import json_context_id def get_service_uuid(a_endpoint_id : Dict, z_endpoint_id : Dict) -> str: return 'svc:{:s}/{:s}=={:s}/{:s}'.format( a_endpoint_id['device_id']['device_uuid']['uuid'], a_endpoint_id['endpoint_uuid']['uuid'], - a_endpoint_id['device_id']['device_uuid']['uuid'], z_endpoint_id['endpoint_uuid']['uuid']) + z_endpoint_id['device_id']['device_uuid']['uuid'], z_endpoint_id['endpoint_uuid']['uuid']) def json_service_id(service_uuid : str, context_id : Optional[Dict] = None): result = {'service_uuid': {'uuid': service_uuid}} diff --git a/src/pathcomp/.gitlab-ci.yml b/src/pathcomp/.gitlab-ci.yml index fd52da6fb7bf8dc556b92e4db080c9927bb58a5d..1515ad0395b9e54142f1076d42d3eb5b2febac5d 100644 --- a/src/pathcomp/.gitlab-ci.yml +++ b/src/pathcomp/.gitlab-ci.yml @@ -13,7 +13,7 @@ # limitations under the License. # Build, tag, and push the Docker image to the GitLab Docker registry -build pathcomp: +build pathcomp-backend: variables: IMAGE_NAME: 'pathcomp' # name of the microservice IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) @@ -21,9 +21,11 @@ build pathcomp: before_script: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY script: - - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/Dockerfile . - - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" - - docker push "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" + # This first build tags the builder resulting image to prevent being removed by dangling image removal command + - docker build -t "${IMAGE_NAME}-backend:${IMAGE_TAG}-builder" --target builder -f ./src/$IMAGE_NAME/backend/Dockerfile . + - docker build -t "${IMAGE_NAME}-backend:$IMAGE_TAG" -f ./src/$IMAGE_NAME/backend/Dockerfile . + - docker tag "${IMAGE_NAME}-backend:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/${IMAGE_NAME}-backend:$IMAGE_TAG" + - docker push "$CI_REGISTRY_IMAGE/${IMAGE_NAME}-backend:$IMAGE_TAG" after_script: - docker images --filter="dangling=true" --quiet | xargs -r docker rmi rules: @@ -32,35 +34,126 @@ build pathcomp: - changes: - src/common/**/*.py - proto/*.proto - - src/$IMAGE_NAME/**/*.{py,in,yml} - - src/$IMAGE_NAME/Dockerfile - - src/$IMAGE_NAME/tests/*.py + - src/$IMAGE_NAME/.gitlab-ci.yml + - src/$IMAGE_NAME/backend/**/*.{c,h,conf} + - src/$IMAGE_NAME/backend/Makefile + - src/$IMAGE_NAME/backend/Dockerfile - manifests/${IMAGE_NAME}service.yaml - .gitlab-ci.yml +# Build, tag, and push the Docker image to the GitLab Docker registry +build pathcomp-frontend: + variables: + IMAGE_NAME: 'pathcomp' # name of the microservice + IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) + stage: build + before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + script: + - docker build -t "${IMAGE_NAME}-frontend:$IMAGE_TAG" -f ./src/$IMAGE_NAME/frontend/Dockerfile . + - docker tag "${IMAGE_NAME}-frontend:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/${IMAGE_NAME}-frontend:$IMAGE_TAG" + - docker push "$CI_REGISTRY_IMAGE/${IMAGE_NAME}-frontend:$IMAGE_TAG" + after_script: + - docker images --filter="dangling=true" --quiet | xargs -r docker rmi + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)' + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"' + - changes: + - src/common/**/*.py + - proto/*.proto + - src/$IMAGE_NAME/.gitlab-ci.yml + - src/$IMAGE_NAME/frontend/**/*.{py,in,yml} + - src/$IMAGE_NAME/frontend/Dockerfile + - src/$IMAGE_NAME/frontend/tests/*.py + - manifests/${IMAGE_NAME}service.yaml + - .gitlab-ci.yml + +# Apply unit test to the component +unit test pathcomp-backend: + variables: + IMAGE_NAME: 'pathcomp' # name of the microservice + IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) + stage: unit_test + needs: + - build pathcomp-backend + before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + - if docker network list | grep teraflowbridge; then echo "teraflowbridge is already created"; else docker network create -d bridge teraflowbridge; fi + - if docker container ls | grep ${IMAGE_NAME}-frontend; then docker rm -f ${IMAGE_NAME}-frontend; else echo "${IMAGE_NAME}-frontend image is not in the system"; fi + - if docker container ls | grep ${IMAGE_NAME}-backend; then docker rm -f ${IMAGE_NAME}-backend; else echo "${IMAGE_NAME}-backend image is not in the system"; fi + script: + - docker pull "$CI_REGISTRY_IMAGE/${IMAGE_NAME}-backend:$IMAGE_TAG" + #- docker run --name ${IMAGE_NAME}-backend -d -p 8081:8081 -v "$PWD/src/${IMAGE_NAME}/backend/tests:/opt/results" --network=teraflowbridge ${IMAGE_NAME}-backend:${IMAGE_TAG}-builder + - docker run --name ${IMAGE_NAME}-backend -d -p 8081:8081 --network=teraflowbridge ${IMAGE_NAME}-backend:${IMAGE_TAG} + - sleep 5 + - docker ps -a + - docker logs ${IMAGE_NAME}-backend + - sleep 5 + - docker exec -i ${IMAGE_NAME}-backend bash -c "curl -0 -v -X POST -H 'Expect:' -H 'Content-Type:\ application/json' http://127.0.0.1:8081/pathComp/api/v1/compRoute -d @/var/teraflow/tests/pc-req.json" + - sleep 5 + - docker exec -i ${IMAGE_NAME}-backend bash -c "killall -USR1 pathComp-cvr" + - sleep 5 + - docker exec -i ${IMAGE_NAME}-backend bash -c "gcovr" + coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' + after_script: + - docker logs ${IMAGE_NAME}-backend + - docker rm -f ${IMAGE_NAME}-backend + - docker network rm teraflowbridge + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)' + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"' + - changes: + - src/common/**/*.py + - proto/*.proto + - src/$IMAGE_NAME/.gitlab-ci.yml + - src/$IMAGE_NAME/backend/**/*.{c,h,conf} + - src/$IMAGE_NAME/backend/Makefile + - src/$IMAGE_NAME/backend/Dockerfile + - manifests/${IMAGE_NAME}service.yaml + - .gitlab-ci.yml + #artifacts: + # when: always + # reports: + # junit: src/$IMAGE_NAME/backend/tests/${IMAGE_NAME}-backend_report.xml + # Apply unit test to the component -unit test pathcomp: +unit test pathcomp-frontend: variables: IMAGE_NAME: 'pathcomp' # name of the microservice IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) stage: unit_test needs: - - build pathcomp + - build pathcomp-frontend + - build pathcomp-backend before_script: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY - if docker network list | grep teraflowbridge; then echo "teraflowbridge is already created"; else docker network create -d bridge teraflowbridge; fi - - if docker container ls | grep $IMAGE_NAME; then docker rm -f $IMAGE_NAME; else echo "$IMAGE_NAME image is not in the system"; fi + - if docker container ls | grep ${IMAGE_NAME}-frontend; then docker rm -f ${IMAGE_NAME}-frontend; else echo "${IMAGE_NAME}-frontend image is not in the system"; fi + - if docker container ls | grep ${IMAGE_NAME}-backend; then docker rm -f ${IMAGE_NAME}-backend; else echo "${IMAGE_NAME}-backend image is not in the system"; fi script: - - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" - - docker run --name $IMAGE_NAME -d -p 10020:10020 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --network=teraflowbridge $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG + - docker pull "$CI_REGISTRY_IMAGE/${IMAGE_NAME}-frontend:$IMAGE_TAG" + - docker pull "$CI_REGISTRY_IMAGE/${IMAGE_NAME}-backend:$IMAGE_TAG" + - docker run --name ${IMAGE_NAME}-backend -d -p 8081:8081 -v "$PWD/src/${IMAGE_NAME}/backend/tests:/opt/results" --network=teraflowbridge $CI_REGISTRY_IMAGE/${IMAGE_NAME}-backend:$IMAGE_TAG + - sleep 1 + - docker run --name ${IMAGE_NAME}-frontend -d -p 10020:10020 --env "PATHCOMP_BACKEND_HOST=${IMAGE_NAME}-backend" --env "PATHCOMP_BACKEND_PORT=8081" -v "$PWD/src/${IMAGE_NAME}/frontend/tests:/opt/results" --network=teraflowbridge $CI_REGISTRY_IMAGE/${IMAGE_NAME}-frontend:$IMAGE_TAG + - docker exec -i ${IMAGE_NAME}-frontend bash -c "env" + - docker exec -i ${IMAGE_NAME}-backend bash -c "env" - sleep 5 - docker ps -a - - docker logs $IMAGE_NAME - - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml" - - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing" + - docker logs ${IMAGE_NAME}-frontend + - docker logs ${IMAGE_NAME}-backend + - sleep 5 + - docker exec -i ${IMAGE_NAME}-frontend bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/frontend/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}-frontend_report.xml" + - sleep 5 + - docker exec -i ${IMAGE_NAME}-frontend bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing" + - sleep 5 coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' after_script: - - docker rm -f $IMAGE_NAME + - docker ps -a + - docker logs ${IMAGE_NAME}-frontend + - docker logs ${IMAGE_NAME}-backend + - docker rm -f ${IMAGE_NAME}-frontend + - docker rm -f ${IMAGE_NAME}-backend - docker network rm teraflowbridge rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)' @@ -68,16 +161,16 @@ unit test pathcomp: - changes: - src/common/**/*.py - proto/*.proto - - src/$IMAGE_NAME/**/*.{py,in,yml} - - src/$IMAGE_NAME/Dockerfile - - src/$IMAGE_NAME/tests/*.py - - src/$IMAGE_NAME/tests/Dockerfile + - src/$IMAGE_NAME/.gitlab-ci.yml + - src/$IMAGE_NAME/frontend/**/*.{py,in,yml} + - src/$IMAGE_NAME/frontend/Dockerfile + - src/$IMAGE_NAME/frontend/tests/*.py - manifests/${IMAGE_NAME}service.yaml - .gitlab-ci.yml artifacts: when: always reports: - junit: src/$IMAGE_NAME/tests/${IMAGE_NAME}_report.xml + junit: src/$IMAGE_NAME/frontend/tests/${IMAGE_NAME}-frontend_report.xml # Deployment of the service in Kubernetes Cluster deploy pathcomp: @@ -86,7 +179,8 @@ deploy pathcomp: IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) stage: deploy needs: - - unit test pathcomp + - unit test pathcomp-backend + - unit test pathcomp-frontend # - integ_test execute script: - 'sed -i "s/$IMAGE_NAME:.*/$IMAGE_NAME:$IMAGE_TAG/" manifests/${IMAGE_NAME}service.yaml' diff --git a/src/pathcomp/backend/Dockerfile b/src/pathcomp/backend/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..b239d2ea04d981869fa3a52905359bfc9336489f --- /dev/null +++ b/src/pathcomp/backend/Dockerfile @@ -0,0 +1,65 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +# Multi-stage Docker image build + +# Stage 1 +FROM ubuntu:20.04 AS builder +ARG DEBIAN_FRONTEND=noninteractive + +# Install build software +RUN apt-get update -y && apt-get install build-essential curl gcovr libglib2.0-dev -y + +# mkdir +RUN mkdir -p /var/teraflow + +# Define working directory +WORKDIR /var/teraflow + +# Copy every file in working directory +COPY src/pathcomp/backend/. ./ + +# Build release version and move it to bin folder +RUN make release +RUN mkdir /var/teraflow/bin +RUN mv pathComp /var/teraflow/bin + +# Build code coverage version +RUN make clean +RUN make coverage + +EXPOSE 8081 + +# builder defines coverage version of pathcomp by default +ENTRYPOINT [ "./pathComp-cvr", "config/pathcomp.conf", "pathcomp.log" ] + + +# Stage 2 +FROM ubuntu:20.04 AS release +ARG DEBIAN_FRONTEND=noninteractive + +# Install build software +RUN apt-get update -y && apt-get install curl libglib2.0-bin -y + +# mkdir +RUN mkdir -p /var/teraflow/config + +# Define working directory +WORKDIR /var/teraflow + +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --from=builder /var/teraflow/bin/pathComp . +COPY --from=builder /var/teraflow/config/pathcomp.conf ./config + +ENTRYPOINT [ "./pathComp", "config/pathcomp.conf", "pathcomp.log" ] diff --git a/src/pathcomp/backend/Dockerfile-gdb b/src/pathcomp/backend/Dockerfile-gdb new file mode 100644 index 0000000000000000000000000000000000000000..13af33006504095204878b465bdee7f84f549d20 --- /dev/null +++ b/src/pathcomp/backend/Dockerfile-gdb @@ -0,0 +1,37 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +# Multi-stage Docker image build + +# Stage 1 +FROM ubuntu:20.04 AS builder +ARG DEBIAN_FRONTEND=noninteractive + +# Install build software +RUN apt-get update -y && apt-get install build-essential libglib2.0-dev -y +RUN apt-get install gdb gdbserver -y + +# mkdir +RUN mkdir -p /var/teraflow + +# Define working directory +WORKDIR /var/teraflow + +# Copy every file in working directory +COPY src/pathcomp/backend/. ./ +RUN make + +EXPOSE 8081 + +ENTRYPOINT [ "gdb", "--args", "./pathComp", "config/pathcomp.conf", "pathcomp.log" ] diff --git a/src/pathcomp/backend/Makefile b/src/pathcomp/backend/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..2777468b2ee54d764aa410a5c41f9e443ade2f06 --- /dev/null +++ b/src/pathcomp/backend/Makefile @@ -0,0 +1,67 @@ +# +# # Copyright 2022 Centre Tecnològic de Telecomunicacions de Catalunya (CTTC/CERCA) www.cttc.es +# +# 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. +# +# Author: CTTC/CERCA PONS RU Ricardo MartÃnez (ricardo.martinez@cttc.es) + +CC = gcc +CFLAGS = -I. -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include/ +LDLIBS = -lm +LDFLAGS = + +all: pathComp + +release: CFLAGS += -O6 -Wall -DPOSIX_SOURCE +release: pathComp + +debug: CFLAGS += -O0 -ggdb -g -DDEBUG +debug: LDFLAGS += -g +debug: pathComp-dbg + +coverage: CFLAGS += -O0 -ggdb -g -DDEBUG -fprofile-arcs -ftest-coverage -DGCOV +coverage: LDFLAGS += -g -lgcov --coverage -fprofile-arcs -ftest-coverage -DGCOV +coverage: pathComp-cvr + +pathComp: pathComp.o pathComp_log.o pathComp_cjson.o pathComp_tools.o pathComp_ksp.o pathComp_RESTapi.o + gcc -o pathComp pathComp.o pathComp_log.o pathComp_cjson.o pathComp_tools.o pathComp_ksp.o pathComp_RESTapi.o \ + -L/usr/lib/x86_64-linux-gnu/ -lglib-2.0 -luuid $(LDFLAGS) $(LDLIBS) + +pathComp-dbg: pathComp.o pathComp_log.o pathComp_cjson.o pathComp_tools.o pathComp_ksp.o pathComp_RESTapi.o + gcc -o pathComp-dbg pathComp.o pathComp_log.o pathComp_cjson.o pathComp_tools.o pathComp_ksp.o pathComp_RESTapi.o \ + -L/usr/lib/x86_64-linux-gnu/ -lglib-2.0 -luuid $(LDFLAGS) $(LDLIBS) + +pathComp-cvr: pathComp.o pathComp_log.o pathComp_cjson.o pathComp_tools.o pathComp_ksp.o pathComp_RESTapi.o + gcc -o pathComp-cvr pathComp.o pathComp_log.o pathComp_cjson.o pathComp_tools.o pathComp_ksp.o pathComp_RESTapi.o \ + -L/usr/lib/x86_64-linux-gnu/ -lglib-2.0 -luuid $(LDFLAGS) $(LDLIBS) + +pathComp_log.o: pathComp_log.h pathComp_log.c + $(CC) $(CFLAGS) -c pathComp_log.c -o pathComp_log.o + +pathComp_cjson.o: pathComp_log.h pathComp_cjson.h pathComp_cjson.c + $(CC) $(CFLAGS) -c pathComp_cjson.c -o pathComp_cjson.o + +pathComp_tools.o: pathComp_log.h pathComp_tools.h pathComp_tools.c + $(CC) $(CFLAGS) -c pathComp_tools.c -o pathComp_tools.o + +pathComp_ksp.o: pathComp_log.h pathComp_tools.h pathComp_ksp.h pathComp_ksp.c + $(CC) $(CFLAGS) -c pathComp_ksp.c -o pathComp_ksp.o + +pathComp_RESTapi.o: pathComp_tools.h pathComp_log.h pathComp_cjson.h pathComp_ksp.h pathComp_RESTapi.h pathComp_RESTapi.c + $(CC) $(CFLAGS) -c pathComp_RESTapi.c -o pathComp_RESTapi.o + +pathComp.o: pathComp_log.h pathComp_RESTapi.h pathComp.c pathComp.h + $(CC) $(CFLAGS) -c pathComp.c -o pathComp.o + +clean: + rm -f *.o *.gcno *.gcda *.gcov *.log pathComp pathComp-dbg pathComp-cvr diff --git a/src/pathcomp/backend/config/pathcomp.conf b/src/pathcomp/backend/config/pathcomp.conf new file mode 100644 index 0000000000000000000000000000000000000000..cd4e677e97fd7c70b74e687fa13e776d47b4d8fb --- /dev/null +++ b/src/pathcomp/backend/config/pathcomp.conf @@ -0,0 +1,2 @@ +PATHCOMP_IP_ADDR 0.0.0.0 +RESTAPI 1 \ No newline at end of file diff --git a/src/pathcomp/backend/pathComp.c b/src/pathcomp/backend/pathComp.c new file mode 100644 index 0000000000000000000000000000000000000000..330f0f61a6d1ea9ff0d4327ede6541b4faca1528 --- /dev/null +++ b/src/pathcomp/backend/pathComp.c @@ -0,0 +1,185 @@ +//////////////////////////////////////////////////////////////////////////////////////// +/** + * # Copyright 2022 Centre Tecnològic de Telecomunicacions de Catalunya (CTTC/CERCA) www.cttc.es + * + * 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. + + * Author: CTTC/CERCA PONS RU Ricardo MartÃnez (ricardo.martinez@cttc.es) + */ +///////////////////////////////////////////////////////////////////////////////////////// + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> +#include <glib.h> +#include <sys/time.h> + +#ifdef GCOV +// Instrumentation to report code coverage live +// Ref: https://www.osadl.org/fileadmin/dam/interface/docbook/howtos/coverage.pdf +#include <signal.h> + +// Code coverage flush method; used to update code coverage reports while the server is running +void __gcov_flush(void); /* check in gcc sources gcc/gcov-io.h for the prototype */ + +void my_gcov_handler(int signum) +{ + printf("signal received: running __gcov_flush()\n"); + __gcov_flush(); /* dump coverage data on receiving SIGUSR1 */ +} +#endif + +#include "pathComp_log.h" +#include "pathComp_RESTapi.h" +#include "pathComp.h" + +// External Variables +FILE *logfile = NULL; + +// PATH COMP IP address API Client +struct in_addr pathComp_IpAddr; + +// REST API ENABLED +int RESTAPI_ENABLED = 0; + +GMainLoop * loop = NULL; + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp.c + * @brief Read the pathComp.conf file @ /etc/pathComp/ + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void read_pathComp_config_file(FILE *fp) +{ + DEBUG_PC ("Processing pathComp.conf"); + + char buff[128], ip[128]; + + // READ PATH COMP SERVER IP + memset (&pathComp_IpAddr, (int)0, sizeof (struct in_addr)); + fscanf(fp, "%s %s ", buff, ip); + pathComp_IpAddr.s_addr = inet_addr(ip); + DEBUG_PC("pathComp_IpAddr: %s", inet_ntoa (pathComp_IpAddr)); + memset (buff, 0, sizeof (buff)); + + // Read REST API + fscanf (fp, "%s %d ", buff, &RESTAPI_ENABLED); + if (RESTAPI_ENABLED) DEBUG_PC ("REST API: ON"); + if (RESTAPI_ENABLED == 0) DEBUG_PC ("REST API: OFF"); + + memset (buff, 0, sizeof (buff)); + DEBUG_PC ("CommandLine: %s", buff); + + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp.c + * @brief Main function for pathComp server + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +int main(int argc, char *argv[]) +{ + #ifdef GCOV + struct sigaction new_action, old_action; + /* setup signal hander */ + new_action.sa_handler = my_gcov_handler; + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = 0; + sigaction(SIGUSR1, NULL, &old_action); + if (old_action.sa_handler != SIG_IGN) + sigaction (SIGUSR1, &new_action, NULL); + #endif + + DEBUG_PC ("********************************************************************"); + DEBUG_PC ("********************************************************************"); + DEBUG_PC (" ---------------------- Path Computation Server---------------------"); + DEBUG_PC ("********************************************************************"); + DEBUG_PC ("********************************************************************"); + + // processing input parameters + if (argc == 1) + { + DEBUG_PC ("Arguments are missing ..."); + exit (-1); + } + + // argv[1] specifies the folder and the configuration file + gchar configFile[50]; + strcpy (configFile, argv[1]); + DEBUG_PC ("Path Computation Server Config File is: %s", configFile); + + // argv[2] specifies the folder and the log file + gchar log[50]; + strcpy (log, argv[2]); + DEBUG_PC ("PATH COMP log File: %s", log); + + // open the log file + logfile = fopen (log, "w"); + DEBUG_PC ("log file is opened"); + + // Read the pathComp.conf file + FILE *pathComp_config = NULL; + pathComp_config = fopen (configFile, "r"); + if (pathComp_config == NULL) + { + DEBUG_PC ("File error\n"); + exit (-1); + } + DEBUG_PC ("pathComp_config.conf is opened"); + + // Check if flag -d for daemonize + if (argc > 3) + { + gchar options[10]; + strcpy (options, argv[3]); + gint ret = strcmp (options, "-d"); + if (ret == 0) daemon(0,0); + } + + // Process the config file + read_pathComp_config_file (pathComp_config); + + DEBUG_PC ("\n ---- PATH COMP MAIN LOOP ------"); + /** Creates a new GMainLoop structure */ + loop = g_main_loop_new (NULL, FALSE); + + // Iff RESTAPI_ENABLED is ON + if (RESTAPI_ENABLED) + { + RESTapi_init(PATH_COMP_PORT); + } + + /** execute loop */ + g_main_loop_run (loop); + + /** decrease the one reference of loop when it is finished */ + g_main_loop_unref(loop); + loop = NULL; + return 0; +} diff --git a/src/pathcomp/backend/pathComp.h b/src/pathcomp/backend/pathComp.h new file mode 100644 index 0000000000000000000000000000000000000000..76b97fd606854f8aee5ecd4536ca9f2da906f9aa --- /dev/null +++ b/src/pathcomp/backend/pathComp.h @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////////////////// +/** + * # Copyright 2022 Centre Tecnològic de Telecomunicacions de Catalunya (CTTC/CERCA) www.cttc.es + * + * 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. + + * Author: CTTC/CERCA PONS RU Ricardo Martínez (ricardo.martinez@cttc.es) + */ + +#ifndef _PATHCOMP_H +#define _PATHCOMP_H + +#include <glib.h> +#include <glib/gstdio.h> +#include <glib-2.0/glib/gtypes.h> + +/////////////////////////////////////////////////// +// IP addressing of peer functional entities +/////////////////////////////////////////////////// +extern struct in_addr pathComp_IpAddr; + +// TCP Port for the REST API communication with the PATH COMP process +#define PATH_COMP_PORT 8081 + +// External Variables +extern GMainLoop * loop; + +#endif \ No newline at end of file diff --git a/src/pathcomp/backend/pathComp_RESTapi.c b/src/pathcomp/backend/pathComp_RESTapi.c new file mode 100644 index 0000000000000000000000000000000000000000..dd352c613d26124a97c2bdd79070634572a3c976 --- /dev/null +++ b/src/pathcomp/backend/pathComp_RESTapi.c @@ -0,0 +1,1963 @@ +//////////////////////////////////////////////////////////////////////////////////////// +/** + * # Copyright 2022 Centre Tecnològic de Telecomunicacions de Catalunya (CTTC/CERCA) www.cttc.es + * + * 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. + + * Author: CTTC/CERCA PONS RU Ricardo MartÃnez (ricardo.martinez@cttc.es) + */ +///////////////////////////////////////////////////////////////////////////////////////// +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <netdb.h> +#include <glib.h> +#include <sys/time.h> +#include <ctype.h> +#include <strings.h> +#include <time.h> +#include <fcntl.h> +#include <uuid/uuid.h> +#include <string.h> + +#include "pathComp_log.h" +#include "pathComp_tools.h" +#include "pathComp_cjson.h" +#include "pathComp_ksp.h" +#include "pathComp_RESTapi.h" + +#define ISspace(x) isspace((int)(x)) + +#define SERVER_STRING "Server: PATHCOMP/0.1.0\r\n" + +// List of Clients connected to the PATH COMP +GList *RESTapi_tcp_client_list = NULL; + +// Id for CLient HTTP (REST API) Connection +guint CLIENT_ID = 0; +guint32 paId_req = 0; + +// Global variables +struct linkList_t* linkList; +struct deviceList_t* deviceList; +struct serviceList_t* serviceList; + +gchar algId[MAX_ALG_ID_LENGTH]; +gboolean syncPath = FALSE; + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Seek a connected tcp client by its fd in the RESTapi_tcp_client_list + * + * @param data + * @param userdata + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint find_rl_client_by_fd (gconstpointer data, gconstpointer userdata) +{ + /** check values */ + g_assert(data != NULL); + g_assert(userdata != NULL); + + struct pathComp_client *client = (struct pathComp_client*)data; + gint fd = *(gint *)userdata; + + if (client->fd == fd) + return 0; + return -1; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to send a message to the corresponding channel + * + * @param source + * @param buf + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint rapi_send_message (GIOChannel *channel, char *buf) { + gsize nbytes, buffersize; + + //DEBUG_PC ("Msg prepared to be sent REST API RESPONSE "); + gint len = strlen ((const char*) buf); + //DEBUG_PC ("Targeted Length of the buffer: %d", len); + + buffersize = g_io_channel_get_buffer_size (channel); + //DEBUG_PC ("GIOChannel with Buffer Size: %d", (gint)buffersize); + + gsize newBufferSize = MAX_GIO_CHANNEL_BUFFER_SIZE; + g_io_channel_set_buffer_size (channel, newBufferSize); + + buffersize = g_io_channel_get_buffer_size (channel); + //DEBUG_PC ("GIOChannel with Buffer Size: %d", (gint)buffersize); + + /** Send message. */ + GError *error = NULL; + + char *ptr = buf; + gint nleft = strlen ((const char *)buf); + + while (nleft > 0) { + g_io_channel_write_chars (channel, (void *)ptr, nleft, &nbytes, &error); + if (error) { + DEBUG_PC ("Error sending the message to TCP Client"); + return (-1); + } + + //DEBUG_PC ("Sent %d bytes", (gint)nbytes); + nleft = nleft - nbytes; + //DEBUG_PC ("Remaining to be sent %d", nleft); + ptr += nbytes; + } + DEBUG_PC("RESPONSE MSG SENT"); + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to return when something goes wrong when processing the REST API Command + * + * @param source + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void RESTapi_unimplemented (GIOChannel *source) +{ + gint ret = 0; + guchar buftmp[1024]; + char *buf = g_malloc0 (sizeof (char) * 2048000); + sprintf((char *)buf, "HTTP/1.1 400 Bad request\r\n"); + + sprintf((char *)buftmp, SERVER_STRING); + strcat ((char *)buf, (const char *)buftmp); + + sprintf((char *)buftmp, "Content-Type: text/plain\r\n"); + strcat ((char *)buf, (const char *)buftmp); + + sprintf((char *)buftmp, "\r\n"); + strcat ((char *)buf, (const char *)buftmp); + + sprintf((char *)buftmp, "<HTML><HEAD><TITLE>Method Not Implemented\r\n"); + strcat ((char *)buf, (const char *)buftmp); + + sprintf((char *)buftmp, "</TITLE></HEAD>\r\n"); + strcat ((char *)buf, (const char *)buftmp); + + sprintf((char *)buftmp, "<BODY><P>HTTP request method not supported.\r\n"); + strcat ((char *)buf, (const char *)buftmp); + + sprintf((char *)buftmp, "</BODY></HTML>\r\n"); + strcat ((char *)buf, (const char *)buftmp); + + ret = rapi_send_message (source, buf); + g_free (buf); + (void)ret; + + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to put in the buffer the date according to RFC 1123 + * + * @param date + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void rapi_add_date_header (char *date) +{ + static const char *DAYS[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + static const char *MONTHS[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + time_t t = time(NULL); + struct tm *tm = NULL; + struct tm sys; + gmtime_r(&t, &sys); + tm = &sys; + + sprintf((char *)date, "DATE: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", DAYS[tm->tm_wday], tm->tm_mday, + MONTHS[tm->tm_mon], 1900 + tm->tm_year, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to add DeviceId and EndpointId forming the computed path + * + * @param pathObj + * @param p + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void add_comp_path_deviceId_endpointId_json(cJSON* pathObj, struct path_t* p, struct compRouteOutput_t* oElem) { + g_assert(p); + g_assert(pathObj); + + // add array for the devideId and endpointIds + cJSON* devicesArray = cJSON_CreateArray(); + cJSON_AddItemToObject(pathObj, "devices", devicesArray); + + // Add the source endpoint + cJSON* sEndPointIdObj; + cJSON_AddItemToArray(devicesArray, sEndPointIdObj = cJSON_CreateObject()); + // Add the topology Id Object containing the topology uuid and context uuid + cJSON* stopIdObj; + cJSON_AddItemToObject(sEndPointIdObj, "topology_id", stopIdObj = cJSON_CreateObject()); + cJSON_AddItemToObject(stopIdObj, "contextId", cJSON_CreateString(oElem->service_endpoints_id[0].topology_id.contextId)); + cJSON_AddItemToObject(stopIdObj, "topology_uuid", cJSON_CreateString(oElem->service_endpoints_id[0].topology_id.topology_uuid)); + + // Add the device Id (uuid) + cJSON_AddItemToObject(sEndPointIdObj, "device_id", cJSON_CreateString(oElem->service_endpoints_id[0].device_uuid)); + // Add the endpoint Id (uuid) + cJSON_AddItemToObject(sEndPointIdObj, "endpoint_uuid", cJSON_CreateString(oElem->service_endpoints_id[0].endpoint_uuid)); + + + for (gint i = 0; i < p->numPathLinks; i++) { + struct pathLink_t* pL = &(p->pathLinks[i]); + cJSON* dElemObj; // Device Element Object of the array + cJSON_AddItemToArray(devicesArray, dElemObj = cJSON_CreateObject()); + + // Add the topologyId with the topologyuuid and contextId + cJSON* tIdObj; + cJSON_AddItemToObject(dElemObj, "topology_id", tIdObj = cJSON_CreateObject()); + cJSON_AddItemToObject(tIdObj, "contextId", cJSON_CreateString(pL->topologyId.contextId)); + cJSON_AddItemToObject(tIdObj, "topology_uuid", cJSON_CreateString(pL->topologyId.topology_uuid)); + + // Add Device Id + cJSON_AddItemToObject(dElemObj, "device_id", cJSON_CreateString(pL->aDeviceId)); + + // Add endpoint Id + cJSON_AddItemToObject(dElemObj, "endpoint_uuid", cJSON_CreateString(pL->aEndPointId)); + } + + // Add the sink endpoint + cJSON* sinkEndPointIdObj; + cJSON_AddItemToArray(devicesArray, sinkEndPointIdObj = cJSON_CreateObject()); + // Add the topology Id Object containing the topology uuid and context uuid + cJSON* sinkTopIdObj; + cJSON_AddItemToObject(sinkEndPointIdObj, "topology_id", sinkTopIdObj = cJSON_CreateObject()); + cJSON_AddItemToObject(sinkTopIdObj, "contextId", cJSON_CreateString(oElem->service_endpoints_id[1].topology_id.contextId)); + cJSON_AddItemToObject(sinkTopIdObj, "topology_uuid", cJSON_CreateString(oElem->service_endpoints_id[1].topology_id.topology_uuid)); + + // Add the device Id (uuid) + cJSON_AddItemToObject(sinkEndPointIdObj, "device_id", cJSON_CreateString(oElem->service_endpoints_id[1].device_uuid)); + // Add the endpoint Id (uuid) + cJSON_AddItemToObject(sinkEndPointIdObj, "endpoint_uuid", cJSON_CreateString(oElem->service_endpoints_id[1].endpoint_uuid)); + + return; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to add the links forming the computed path + * + * @param pathObj + * @param p + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void add_comp_path_link_json(cJSON* pathObj, struct path_t* p) { + g_assert(p); + g_assert(pathObj); + + // Add array for the links + cJSON* linkArray = cJSON_CreateArray(); + cJSON_AddItemToObject(pathObj, "link", linkArray); + + for (gint i = 0; i < p->numPathLinks; i++) { + struct pathLink_t* pL = &(p->pathLinks[i]); + cJSON* lElemObj; // Link Element Object of the array + cJSON_AddItemToArray(linkArray, lElemObj = cJSON_CreateObject()); + + // Add link Id + cJSON_AddItemToObject(lElemObj, "link_Id", cJSON_CreateString(pL->linkId)); + + // Add link topologies + cJSON* linkTopoArray = cJSON_CreateArray(); + cJSON_AddItemToObject(lElemObj, "topology", linkTopoArray); + + for (gint j = 0; j < pL->numLinkTopologies; j++) { + struct linkTopology_t* linkTopo = &(pL->linkTopologies[j]); + cJSON* lTopoElemObj; + cJSON_AddItemToArray(linkTopoArray, lTopoElemObj = cJSON_CreateObject()); + cJSON_AddItemToObject(lTopoElemObj, "topology_uuid", cJSON_CreateString(linkTopo->topologyId)); + } + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Compose the JSON Body of the succesfully network connectivity service + * + * @param body + * @param length + * @param compRouteOutputList + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void rapi_response_json_contents (char *body, gint *length, struct compRouteOutputList_t *compRouteOutputList) +{ + char *buftmp; + cJSON *root = cJSON_CreateObject(); + DEBUG_PC ("Creating the JSON body of the response"); + + // Create response-list array + cJSON *responseListArray = cJSON_CreateArray(); + cJSON_AddItemToObject(root, "response-list", responseListArray); + + // Add computed routes to the response-list + for (gint i = 0; i < compRouteOutputList->numCompRouteConnList; i++) { + struct compRouteOutput_t *oElem = &(compRouteOutputList->compRouteConnection[i]); // reference to output element from the list of computed routes + cJSON *oElemObj; + cJSON_AddItemToArray (responseListArray, oElemObj = cJSON_CreateObject()); + + // Add the service Id Object + cJSON* servIdObj; + cJSON_AddItemToObject(oElemObj, "serviceId", servIdObj = cJSON_CreateObject()); + cJSON_AddItemToObject(servIdObj, "contextId", cJSON_CreateString(oElem->serviceId.contextId)); + cJSON_AddItemToObject(servIdObj, "service_uuid", cJSON_CreateString(oElem->serviceId.service_uuid)); + + // Add the service endpoints ids array + cJSON* sEndpointIdsArray = cJSON_CreateArray(); + cJSON_AddItemToObject(oElemObj, "service_endpoints_ids", sEndpointIdsArray); + + for (gint j = 0; j < oElem->num_service_endpoints_id; j++) { + //DEBUG_PC("parsing service endpoints ids"); + //DEBUG_PC("endpoint: %s [%s]", oElem->service_endpoints_id[j].device_uuid, oElem->service_endpoints_id[j].endpoint_uuid); + //struct service_endpoints_id_t* sEndPointId = &(oElem->service_endpoints_id[j]); + cJSON* sEndPointIdObj; + cJSON_AddItemToArray(sEndpointIdsArray, sEndPointIdObj = cJSON_CreateObject()); + // Add the topology Id Object containing the topology uuid and context uuid + cJSON* topIdObj; + cJSON_AddItemToObject(sEndPointIdObj, "topology_id", topIdObj = cJSON_CreateObject()); + cJSON_AddItemToObject(topIdObj, "contextId", cJSON_CreateString(oElem->service_endpoints_id[j].topology_id.contextId)); + cJSON_AddItemToObject(topIdObj, "topology_uuid", cJSON_CreateString(oElem->service_endpoints_id[j].topology_id.topology_uuid)); + + // Add the device Id (uuid) + cJSON_AddItemToObject(sEndPointIdObj, "device_id", cJSON_CreateString(oElem->service_endpoints_id[j].device_uuid)); + // Add the endpoint Id (uuid) + cJSON_AddItemToObject(sEndPointIdObj, "endpoint_uuid", cJSON_CreateString(oElem->service_endpoints_id[j].endpoint_uuid)); + } + // Add no path issue + if (oElem->noPathIssue == NO_PATH_CONS_ISSUE) { // Error on finding the route, e.g., no feasible path + DEBUG_PC("NO PATH FOUND, AN ISSUE OCCURRED: %d", oElem->noPathIssue); + cJSON* noPathObj; + cJSON_AddItemToObject(oElemObj, "noPath", noPathObj = cJSON_CreateObject()); + char str[5]; + sprintf(str, "%d", oElem->noPathIssue); + cJSON_AddItemToObject(noPathObj, "issue", cJSON_CreateString(str)); + continue; + } + + // Create the array to parse the computed path from the oElemObj + cJSON* pathArray = cJSON_CreateArray(); + cJSON_AddItemToObject(oElemObj, "path", pathArray); + for (gint k = 0; k < oElem->numPaths; k++) { + struct path_t* p = &(oElem->paths[k]); + cJSON* pathObj; + cJSON_AddItemToArray(pathArray, pathObj = cJSON_CreateObject()); + + // Add path capacity + cJSON* pathCapObj; + cJSON_AddItemToObject(pathObj, "path-capacity", pathCapObj = cJSON_CreateObject()); + cJSON* totalSizeObj; + cJSON_AddItemToObject(pathCapObj, "total-size", totalSizeObj = cJSON_CreateObject()); + cJSON_AddItemToObject(totalSizeObj, "value", cJSON_CreateNumber(p->path_capacity.value)); + cJSON_AddItemToObject(totalSizeObj, "unit", cJSON_CreateNumber(p->path_capacity.unit)); + + // Add path latency + cJSON* pathLatObj; + char lat[16]; + sprintf(lat, "%lf", p->path_latency.fixed_latency); + cJSON_AddItemToObject(pathObj, "path-latency", pathLatObj= cJSON_CreateObject()); + cJSON_AddItemToObject(pathLatObj, "fixed-latency-characteristic", cJSON_CreateString(lat)); + + // Add path cost + cJSON* pathCostObj; + cJSON_AddItemToObject(pathObj, "path-cost", pathCostObj = cJSON_CreateObject()); + cJSON_AddItemToObject(pathCostObj, "cost-name", cJSON_CreateString(p->path_cost.cost_name)); + char value[16]; + sprintf(value, "%lf", p->path_cost.cost_value); + cJSON_AddItemToObject(pathCostObj, "cost-value", cJSON_CreateString(value)); + char algorithm[16]; + sprintf(algorithm, "%lf", p->path_cost.cost_algorithm); + cJSON_AddItemToObject(pathCostObj, "cost-algorithm", cJSON_CreateString(algorithm)); + + // Add the links + //add_comp_path_link_json(pathObj, p); + // Add deviceId, endpointId + add_comp_path_deviceId_endpointId_json(pathObj, p, oElem); + } + } + + //DEBUG_PC ("JSON Body Response DONE"); + buftmp = (char *)cJSON_Print(root); + strcat (body, (const char*) buftmp); + *length = strlen ((const char*)body); + //DEBUG_PC ("JSON Body (length: %d)", *length); + //DEBUG_PC ("%s", body); + cJSON_Delete (root); + g_free(buftmp); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to return response OK via REST API with the computed serviceId + * + * @param source + * @param httpCode + * @param compRouteOutputList + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void rapi_response_ok (GIOChannel *source, gint httpCode, struct compRouteOutputList_t *compRouteOutputList) { + + g_assert(compRouteOutputList); + gint ret = 0; + + //DEBUG_PC ("Creating the JSON Body and sending the response of the computed Route List"); + + guchar buftmp[1024]; + char * buf = g_malloc0 (sizeof (char) * 2048000); + // Create the Body of the Response + char * jsonBody = g_malloc0 (sizeof (char) * 2048000); + gint length = 0; + + rapi_response_json_contents (jsonBody, &length, compRouteOutputList); + + sprintf((char *)buf, "HTTP/1.1 200 OK\r\n"); + + sprintf((char *)buftmp, SERVER_STRING); + strcat ((char *)buf, (const char *)buftmp); + + sprintf ((char *)buftmp, "Content-Type: application/json\r\n"); + strcat ((char *)buf, (const char *)buftmp); + + // Add the length of the JSON enconding to the Content_Length + char buff_length[16]; + + sprintf(buff_length, "%d", length); + //DEBUG_PC ("Buffer Length (JSON BODY): %d Added to the Content Length", length); + + sprintf ((char *)buftmp, "Content-Length: "); + strcat ((char *)buftmp, (const char *)buff_length); + strcat ((char *)buftmp, "\r\n"); + strcat ((char *)buf, (const char *)buftmp); + + // Add DATE header + rapi_add_date_header ((char *)buftmp); + strcat ((char *)buf, (const char *)buftmp); + sprintf((char *)buftmp, "\r\n"); + strcat ((char *)buf, (const char *)buftmp); + + strcat ((char *)buf, (const char *)jsonBody); + //DEBUG_PC ("%s", buf); + ret = rapi_send_message (source, buf); + g_free (buf); + memset (buftmp, '\0', sizeof ( buftmp)); + g_free (jsonBody); + (void)ret; + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to return response OK via REST API + * + * @param source + * @param error + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void rapi_response (GIOChannel *source, gint error) +{ + int ret = 0; + guchar buftmp[1024]; + char * buf = g_malloc0 (sizeof (char) * 2048000); + if (error == HTTP_RETURN_CODE_BAD_REQUEST) + sprintf((char *)buf, "HTTP/1.1 400 BAD REQUEST\r\n"); + else if (error == HTTP_RETURN_CODE_UNAUTHORIZED) + sprintf((char *)buf, "HTTP/1.1 401 UNAUTHORIZED\r\n"); + else if (error == HTTP_RETURN_CODE_FORBIDDEN) + sprintf((char *)buf, "HTTP/1.1 403 FORBIDDEN\r\n"); + else if (error == HTTP_RETURN_CODE_NOT_FOUND) + sprintf((char *)buf, "HTTP/1.1 404 NOT FOUND\r\n"); + + sprintf((char *)buftmp, SERVER_STRING); + strcat ((char *)buf, (const char *)buftmp); + + sprintf((char *)buftmp, "Content-Type: text/plain\r\n"); + strcat ((char *)buf, (const char *)buftmp); + + sprintf((char *)buftmp, "Content-Length: 0/plain\r\n"); + strcat ((char *)buf, (const char *)buftmp); + + // Add DATE header + rapi_add_date_header ((char *)buftmp); + strcat ((char *)buf, (const char *)buftmp); + + sprintf((char *)buftmp, "\r\n"); + strcat ((char *)buf, (const char *)buftmp); + // Print the prepared message + DEBUG_PC ("%s", buf); + + // Send the message + ret = rapi_send_message (source, buf); + g_free (buf); + (void)ret; + + return; +} + + +/////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to parse the array of Endpoint Ids + * + * @param endPointArray + * @param s + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void parse_service_endPointsIds_array(cJSON* endPointIdArray, struct service_t* s) { + + for (gint i = 0; i < cJSON_GetArraySize(endPointIdArray); i++) { + s->num_service_endpoints_id++; + struct service_endpoints_id_t* serviceEndPointId = &(s->service_endpoints_id[i]); + + cJSON* item = cJSON_GetArrayItem(endPointIdArray, i); + + // Get the topology Id Object + cJSON* topologyIdObj = cJSON_GetObjectItem(item, "topology_id"); + if (cJSON_IsObject(topologyIdObj)) { + // Get the context Id (UUID) from the topologyIdObj + cJSON* contextIdObj = cJSON_GetObjectItem(topologyIdObj, "contextId"); + if (cJSON_IsString(contextIdObj)) { + duplicate_string(serviceEndPointId->topology_id.contextId, contextIdObj->valuestring); + //DEBUG_PC("Service EndPoint [%d]-- ContextId: %s (uuid string format)", i + 1, serviceEndPointId->topology_id.contextId); + } + // Get the topologyId (UUID) from the topologyIdObj + cJSON* topologyUuidObj = cJSON_GetObjectItem(topologyIdObj, "topology_uuid"); + if (cJSON_IsString(topologyUuidObj)) { + duplicate_string(serviceEndPointId->topology_id.topology_uuid, topologyUuidObj->valuestring); + //DEBUG_PC("Service Endpoint (%d) -- TopologyId: %s (uuid string format)", i + 1, serviceEndPointId->topology_id.topology_uuid); + } + } + // Get the deviceId (UUID) + cJSON* deviceIdObj = cJSON_GetObjectItem(item, "device_id"); + if (cJSON_IsString(deviceIdObj)) { + duplicate_string(serviceEndPointId->device_uuid, deviceIdObj->valuestring); + DEBUG_PC("[%d] - DeviceId: %s", i + 1, serviceEndPointId->device_uuid); + } + // Get the endpointId (UUID) + cJSON* endPointIdObj = cJSON_GetObjectItem(item, "endpoint_uuid"); + if (cJSON_IsString(endPointIdObj)) { + duplicate_string(serviceEndPointId->endpoint_uuid, endPointIdObj->valuestring); + DEBUG_PC("[%d] EndPointId: %s", i + 1, serviceEndPointId->endpoint_uuid); + } + } + return; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to parse the array with the required service constraints + * + * @param constraintArray + * @param s + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void parse_service_constraints(cJSON* constraintArray, struct service_t* s) { + + for (gint i = 0; i < cJSON_GetArraySize(constraintArray); i++) { + + s->num_service_constraints++; + + struct constraint_t* constraint = &(s->constraints[i]); + + cJSON* item = cJSON_GetArrayItem(constraintArray, i); + + // Get the constraint type + cJSON* typeObj = cJSON_GetObjectItem(item, "constraint_type"); + if (cJSON_IsString(typeObj)) { + duplicate_string(constraint->constraint_type, typeObj->valuestring); + } + // Get the constraint value + cJSON* valueObj = cJSON_GetObjectItem(item, "constraint_value"); + if (cJSON_IsString(valueObj)) { + duplicate_string(constraint->constraint_value, valueObj->valuestring); + } + DEBUG_PC("Service Reqs [%d] -- Type: %s | Value: %s", i+1, constraint->constraint_type, constraint->constraint_value); + } + return; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to parse the array with the different + * network services + * + * @param serviceArray + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void parsing_json_serviceList_array(cJSON* serviceArray) { + + for (gint i = 0; i < cJSON_GetArraySize(serviceArray); i++) + { + serviceList->numServiceList++; + struct service_t* service = &(serviceList->services[i]); + + cJSON* item = cJSON_GetArrayItem(serviceArray, i); + + // Get the algorithm Id + cJSON* algIdItem = cJSON_GetObjectItem(item, "algId"); + if (cJSON_IsString(algIdItem)) + { + duplicate_string(service->algId, algIdItem->valuestring); + DEBUG_PC ("algId: %s", service->algId); + // assumed that all the services request the same algId + duplicate_string(algId, service->algId); + } + + // Get the syncPaths + cJSON* synchPathObj = cJSON_GetObjectItemCaseSensitive(item, "syncPaths"); + if (cJSON_IsBool(synchPathObj)) + { + // Check Synchronization of multiple Paths to attain e.g. global concurrent optimization + if (cJSON_IsTrue(synchPathObj)) + { + syncPath = TRUE; + DEBUG_PC("Path Synchronization is required"); + } + if (cJSON_IsFalse(synchPathObj)) + { + syncPath = FALSE; + DEBUG_PC("No Path Synchronization"); + } + } + + // Get service Id in terms of contextId and service uuids + cJSON* serviceIdObj = cJSON_GetObjectItem(item, "serviceId"); + if (cJSON_IsObject(serviceIdObj)) { + // Get context Id uuid + cJSON* contextIdObj = cJSON_GetObjectItem(serviceIdObj, "contextId"); + if (cJSON_IsString(contextIdObj)) { + // convert the string in contextId->valuestring in uuid binary format + duplicate_string(service->serviceId.contextId, contextIdObj->valuestring); + DEBUG_PC("ContextId: %s (uuid string format)", service->serviceId.contextId); + } + // Get service Id uuid + cJSON* serviceUuidObj = cJSON_GetObjectItem(serviceIdObj, "service_uuid"); + if (cJSON_IsString(serviceUuidObj)) { + duplicate_string(service->serviceId.service_uuid, serviceUuidObj->valuestring); + DEBUG_PC("Service UUID: %s (uuid string format)", service->serviceId.service_uuid); + } + } + + // Get de service type + cJSON* serviceTypeObj = cJSON_GetObjectItem(item, "serviceType"); + if (cJSON_IsNumber(serviceTypeObj)) + { + service->service_type = (guint)(serviceTypeObj->valuedouble); + print_service_type(service->service_type); + } + + // Get the endPoints array of the service + cJSON* endPointIdsArray = cJSON_GetObjectItem(item, "service_endpoints_ids"); + if (cJSON_IsArray(endPointIdsArray)) { + parse_service_endPointsIds_array(endPointIdsArray, service); + } + + // Get the service constraints + cJSON* constraintArray = cJSON_GetObjectItem(item, "service_constraints"); + if (cJSON_IsArray(constraintArray)) { + parse_service_constraints(constraintArray, service); + } + + // Get the maximum number of paths to be computed (kPaths) + cJSON* kPathsObj = cJSON_GetObjectItemCaseSensitive(item, "kPaths"); + if (cJSON_IsNumber(kPathsObj)){ + service->kPaths = (guint)(kPathsObj->valuedouble); + } + } + return; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function parsing the capacity attributes in the endpoint + * + * @param capacity + * @param c + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void parse_capacity_object(cJSON* capacity, struct capacity_t* c) { + + cJSON* totalSizeObj = cJSON_GetObjectItem(capacity, "total-size"); + if (cJSON_IsObject(totalSizeObj)) { + //Get the capacity value + cJSON* valueObj = cJSON_GetObjectItem(totalSizeObj, "value"); + if (cJSON_IsNumber(valueObj)) { + memcpy(&c->value, &valueObj->valuedouble, sizeof (gdouble)); + } + // Get the Units + cJSON* unitObj = cJSON_GetObjectItem(totalSizeObj, "unit"); + if (cJSON_IsNumber(unitObj)) { + c->unit = (guint)(unitObj->valuedouble); + } + } + return; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function parsing the device endpoints + * + * @param endPointsArray + * @param d + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void parse_json_device_endpoints_array(cJSON* endPointsArray, struct device_t* d) { + + for (gint i = 0; i < cJSON_GetArraySize(endPointsArray); i++) { + d->numEndPoints++; + struct endPoint_t* endpoint = &(d->endPoints[i]); + + cJSON* item = cJSON_GetArrayItem(endPointsArray, i); + + // Get the Endpoint Identifier: topology, context, device and endpointId + cJSON* endPointIdObj = cJSON_GetObjectItem(item, "endpoint_id"); + if (cJSON_IsObject(endPointIdObj)) { + // Get the topology Id Object + cJSON* topologyIdObj = cJSON_GetObjectItem(endPointIdObj, "topology_id"); + if (cJSON_IsObject(topologyIdObj)) { + // Get the context Id (UUID) from the topologyIdObj + cJSON* contextIdObj = cJSON_GetObjectItem(topologyIdObj, "contextId"); + if (cJSON_IsString(contextIdObj)) { + duplicate_string(endpoint->endPointId.topology_id.contextId, contextIdObj->valuestring); + //DEBUG_PC("Device EndPoint (%d)-- ContextId: %s (uuid string format)", i + 1, endpoint->endPointId.topology_id.contextId); + } + // Get the topologyId (UUID) from the topologyIdObj + cJSON* topologyUuidObj = cJSON_GetObjectItem(topologyIdObj, "topology_uuid"); + if (cJSON_IsString(topologyUuidObj)) { + duplicate_string(endpoint->endPointId.topology_id.topology_uuid, topologyUuidObj->valuestring); + //DEBUG_PC("Device Endpoint (%d) -- TopologyId: %s (uuid string format)", i + 1, endpoint->endPointId.topology_id.topology_uuid); + } + } + // Get the deviceId + cJSON* deviceIdObj = cJSON_GetObjectItem(endPointIdObj, "device_id"); + if (cJSON_IsString(deviceIdObj)) { + duplicate_string(endpoint->endPointId.device_id, deviceIdObj->valuestring); + //DEBUG_PC("Device Endpoint (%d) -- Device Id: %s (uuid)", i + 1, endpoint->endPointId.device_id); + } + // Get the endpoint_uuid + cJSON* endPointUuidObj = cJSON_GetObjectItem(endPointIdObj, "endpoint_uuid"); + if (cJSON_IsString(endPointUuidObj)) { + duplicate_string(endpoint->endPointId.endpoint_uuid, endPointUuidObj->valuestring); + //DEBUG_PC("Device Endpoint (%d) -- EndPoint Uuid: %s (uuid)", i + 1, endpoint->endPointId.endpoint_uuid); + } + } + // Get the EndPoint Type + cJSON* endPointTypeObj = cJSON_GetObjectItem(item, "endpoint_type"); + if (cJSON_IsString(endPointTypeObj)) { + duplicate_string(endpoint->endpointType, endPointTypeObj->valuestring); + //DEBUG_PC("Device Endpoint (%d) -- EndPoint Type: %s", i + 1, endpoint->endpointType); + } + // Link Port Direction + cJSON* linkPortDirectionObj = cJSON_GetObjectItem(item, "link_port_direction"); + if (cJSON_IsNumber(linkPortDirectionObj)) { + endpoint->link_port_direction = (guint)(linkPortDirectionObj->valuedouble); + print_link_port_direction(endpoint->link_port_direction); + } + // EndPoint Termination Direction + cJSON* terminationDirectionObj = cJSON_GetObjectItem(item, "termination-direction"); + if (cJSON_IsNumber(terminationDirectionObj)) { + endpoint->termination_direction = (guint)(terminationDirectionObj->valuedouble); + print_termination_direction(endpoint->termination_direction); + } + // Endpoint Termination State + cJSON* terminationStateObj = cJSON_GetObjectItem(item, "termination-state"); + if (cJSON_IsNumber(terminationStateObj)) { + endpoint->termination_state = (guint)(terminationStateObj->valuedouble); + print_termination_state(endpoint->termination_state); + } + // total potential capacity + cJSON* totalPotentialCapacityObj = cJSON_GetObjectItem(item, "total-potential-capacity"); + if (cJSON_IsObject(totalPotentialCapacityObj)) + { + parse_capacity_object(totalPotentialCapacityObj, &endpoint->potential_capacity); + //DEBUG_PC("Device Endpoint (%d) -- Potential Capacity: %f", i + 1, endpoint->potential_capacity.value); + print_capacity_unit(endpoint->potential_capacity.unit); + } + // total available capacity + cJSON* availableCapacityObj = cJSON_GetObjectItem(item, "available-capacity"); + if (cJSON_IsObject(availableCapacityObj)) + { + parse_capacity_object(availableCapacityObj, &endpoint->available_capacity); + //DEBUG_PC("Device Endpoint (%d) -- Available Capacity: %f", i + 1, endpoint->available_capacity.value); + print_capacity_unit(endpoint->available_capacity.unit); + } + // inter-domain plug-in + cJSON* interDomainPlugInObj = cJSON_GetObjectItem(item, "inter-domain-plug-in"); + if (cJSON_IsObject(interDomainPlugInObj)) { + // Get the local + cJSON* idInterDomainLocal = cJSON_GetObjectItem(interDomainPlugInObj, "plug-id-inter-domain-local-id"); + if (cJSON_IsString(idInterDomainLocal)) { + duplicate_string(endpoint->inter_domain_plug_in.inter_domain_plug_in_local_id, idInterDomainLocal->valuestring); + //DEBUG_PC("Inter-Domain Local Id: %s", endpoint->inter_domain_plug_in.inter_domain_plug_in_local_id); + } + // Get the remote + cJSON* idInterDomainRemote = cJSON_GetObjectItem(interDomainPlugInObj, "plug-id-inter-domain-remote-id"); + if (cJSON_IsString(idInterDomainRemote)) { + duplicate_string(endpoint->inter_domain_plug_in.inter_domain_plug_in_remote_id, idInterDomainRemote->valuestring); + //DEBUG_PC("Inter-Domain Remote Id: %s", endpoint->inter_domain_plug_in.inter_domain_plug_in_remote_id); + } + } + } + return; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to parse the set/list of devices forming the context/topology + * + * @param deviceArray + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void parsing_json_deviceList_array(cJSON* deviceArray) { + + for (gint i = 0; i < cJSON_GetArraySize(deviceArray); i++) { + deviceList->numDevices++; + struct device_t* d = &(deviceList->devices[i]); + cJSON* item = cJSON_GetArrayItem(deviceArray, i); + + // Get the device UUID + cJSON* deviceUuidObj = cJSON_GetObjectItem(item, "device_Id"); + if (cJSON_IsString(deviceUuidObj)) { + duplicate_string(d->deviceId, deviceUuidObj->valuestring); + //DEBUG_PC("Device (%d) -- Id: %s (uuid string format)", i + 1, d->deviceId); + } + + // Get the device Type + cJSON* deviceTypeObj = cJSON_GetObjectItem(item, "device_type"); + if (cJSON_IsString(deviceTypeObj)) { + duplicate_string(d->deviceType, deviceTypeObj->valuestring); + //DEBUG_PC(" Device Type: %s ---", d->deviceType); + } + //DEBUG_PC("DeviceId: %s, Device Type: %s", d->deviceId, d->deviceType); + + // get the device endPoints + cJSON* deviceEndpointsArray = cJSON_GetObjectItem(item, "device_endpoints"); + if (cJSON_IsArray(deviceEndpointsArray)) + { + parse_json_device_endpoints_array(deviceEndpointsArray, d); + } + } + return; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to parse the JSON objects the endPoint of a link + * + * @param endPointsLinkObj + * @param l + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void parse_json_link_endpoints_array(cJSON *endPointsLinkObj, struct link_t* l) { + + for (gint i = 0; i < cJSON_GetArraySize(endPointsLinkObj); i++) { + l->numLinkEndPointIds++; + struct link_endpointId_t* endPointLink = &(l->linkEndPointId[i]); + + cJSON* item = cJSON_GetArrayItem(endPointsLinkObj, i); + + // Get endPoint attributes (topologyId, deviceId, endpoint_uuid) + cJSON* endPointIdObj = cJSON_GetObjectItem(item, "endpoint_id"); + if (cJSON_IsObject(endPointIdObj)) { + // Get the topology Id Object + cJSON* topologyIdObj = cJSON_GetObjectItem(endPointIdObj, "topology_id"); + if (cJSON_IsObject(topologyIdObj)) { + // Get the context Id (UUID) from the topologyIdObj + cJSON* contextIdObj = cJSON_GetObjectItem(topologyIdObj, "contextId"); + if (cJSON_IsString(contextIdObj)) { + duplicate_string(endPointLink->topology_id.contextId, contextIdObj->valuestring); + //DEBUG_PC("Link EndPoint (%d)-- ContextId: %s (uuid string format)", i + 1, endPointLink->topology_id.contextId); + } + // Get the topologyId (UUID) from the topologyIdObj + cJSON* topologyUuidObj = cJSON_GetObjectItem(topologyIdObj, "topology_uuid"); + if (cJSON_IsString(topologyUuidObj)) { + duplicate_string(endPointLink->topology_id.topology_uuid, topologyUuidObj->valuestring); + //DEBUG_PC("Link Endpoint (%d) -- TopologyId: %s (uuid string format)", i + 1, endPointLink->topology_id.topology_uuid); + } + } + // Get the deviceId + cJSON* deviceIdObj = cJSON_GetObjectItem(endPointIdObj, "device_id"); + if (cJSON_IsString(deviceIdObj)) { + duplicate_string(endPointLink->deviceId, deviceIdObj->valuestring); + //DEBUG_PC("Link Endpoint (%d) -- Device Id: %s (uuid)", i + 1, endPointLink->deviceId); + } + // Get the endpoint_uuid + cJSON* endPointUuidObj = cJSON_GetObjectItem(endPointIdObj, "endpoint_uuid"); + if (cJSON_IsString(endPointUuidObj)) { + duplicate_string(endPointLink->endPointId, endPointUuidObj->valuestring); + //DEBUG_PC("Link Endpoint (%d) -- EndPoint Uuid: %s (uuid)", i + 1, endPointLink->endPointId); + } + } + } + return; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to parse the JSON objects describing the set of links + * + * @param linkListArray + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void parsing_json_linkList_array(cJSON* linkListArray) { + + for (gint i = 0; i < cJSON_GetArraySize(linkListArray); i++) { + linkList->numLinks++; + struct link_t* l = &(linkList->links[i]); + + cJSON* item = cJSON_GetArrayItem(linkListArray, i); + + // Get the link Id (uuid) + cJSON* linkIdObj = cJSON_GetObjectItem(item, "link_Id"); + if (cJSON_IsString(linkIdObj)) { + duplicate_string(l->linkId, linkIdObj->valuestring); + //DEBUG_PC("Link (%d) -- Id: %s (uuid)", i + 1, l->linkId); + } + // Get the link endpoints (assumed to be p2p) + cJSON* endPointsLinkObj = cJSON_GetObjectItem(item, "link_endpoint_ids"); + if (cJSON_IsArray(endPointsLinkObj)) { + parse_json_link_endpoints_array(endPointsLinkObj, l); + } + // get the fowarding direction + cJSON* fwdDirObj = cJSON_GetObjectItem(item, "forwarding_direction"); + if (cJSON_IsNumber(fwdDirObj)) { + l->forwarding_direction = (guint)(fwdDirObj->valuedouble); + print_link_forwarding_direction(l->forwarding_direction); + } + // total potential capacity + cJSON* totalPotentialCapacityObj = cJSON_GetObjectItem(item, "total-potential-capacity"); + if (cJSON_IsObject(totalPotentialCapacityObj)) + { + parse_capacity_object(totalPotentialCapacityObj, &l->potential_capacity); + //DEBUG_PC("Link (%d) -- Potential Capacity: %f", i + 1, l->potential_capacity.value); + print_capacity_unit(l->potential_capacity.unit); + } + // total available capacity + cJSON* availableCapacityObj = cJSON_GetObjectItem(item, "available-capacity"); + if (cJSON_IsObject(availableCapacityObj)) + { + parse_capacity_object(availableCapacityObj, &l->available_capacity); + //DEBUG_PC("Link (%d) -- Available Capacity: %f", i + 1, l->available_capacity.value); + print_capacity_unit(l->available_capacity.unit); + } + // Cost Characteristics + cJSON* costCharacObj = cJSON_GetObjectItem(item, "cost-characteristics"); + if (cJSON_IsObject(costCharacObj)) { + // Cost Name + cJSON* costNameObj = cJSON_GetObjectItem(costCharacObj, "cost-name"); + if (cJSON_IsString(costNameObj)) { + duplicate_string(l->cost_characteristics.cost_name, costNameObj->valuestring); + //DEBUG_PC("Link (%d) -- Cost Name: %s", i + 1, l->cost_characteristics.cost_name); + } + // Cost value + cJSON* costValueObj = cJSON_GetObjectItem(costCharacObj, "cost-value"); + if (cJSON_IsString(costValueObj)) { + char* endpr; + l->cost_characteristics.cost_value = (gdouble)(strtod(costValueObj->valuestring, &endpr)); + //DEBUG_PC("Link (%d) -- Cost Value: %f", i + 1, l->cost_characteristics.cost_value); + } + // Cost Algorithm + cJSON* costAlgObj = cJSON_GetObjectItem(costCharacObj, "cost-algorithm"); + if (cJSON_IsString(costAlgObj)) { + char* endpr; + l->cost_characteristics.cost_algorithm = (gdouble)(strtod(costAlgObj->valuestring, &endpr)); + //DEBUG_PC("Link (%d) -- Cost Algorithm: %f", i + 1, l->cost_characteristics.cost_algorithm); + } + } + // Latency Characteristics + cJSON* latencyCharacObj = cJSON_GetObjectItem(item, "latency-characteristics"); + if (cJSON_IsObject(latencyCharacObj)) { + cJSON* fixedLatencyCharacObj = cJSON_GetObjectItem(latencyCharacObj, "fixed-latency-characteristic"); + if (cJSON_IsString(fixedLatencyCharacObj)) { + char* endpr; + l->latency_characteristics.fixed_latency = (gdouble)(strtod(fixedLatencyCharacObj->valuestring, &endpr)); + //DEBUG_PC("Link (%d) -- Latency: %f", i + 1, l->latency_characteristics.fixed_latency); + } + } + } + return; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to generate the reverse (unidirecitonal) link from those being learnt + * from the received context + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + //////////////////////////////////////////////////////////////////////////////////////// +void generate_reverse_linkList() { + + DEBUG_PC("Starting the Creation of the Reverse Links [current: %d]", linkList->numLinks); + gint numLinks = linkList->numLinks; + + for (gint i = 0; i < numLinks; i++) { + struct link_t* refLink = &(linkList->links[i]); + struct link_t* newLink = &(linkList->links[numLinks + i]); + linkList->numLinks++; + // Copy the linkId + appending "_rev" + duplicate_string(newLink->linkId, refLink->linkId); + strcat(newLink->linkId, "_rev"); + + //DEBUG_PC("refLink: %s // newLink: %s", refLink->linkId, newLink->linkId); + + // Assumption: p2p links. The newLink endpoints are the reversed ones form the reference Link (refLink) + // i.e., refLink A->B, then newLink B->A +#if 0 + if (refLink->numLinkEndPointIds != 2) { + DEBUG_PC("To construct the new Link from ref, 2 EndPoints are a MUST"); + exit(-1); + } +#endif + //DEBUG_PC("Number of Endpoints in Link: %d", refLink->numLinkEndPointIds); + for (gint j = refLink->numLinkEndPointIds - 1, m = 0; j >= 0; j--, m++) { + + + struct link_endpointId_t* refEndPId = &(refLink->linkEndPointId[j]); + struct link_endpointId_t* newEndPId = &(newLink->linkEndPointId[m]); + // Duplicate the topologyId information, i.e., contextId and topology_uuid + duplicate_string(newEndPId->topology_id.contextId, refEndPId->topology_id.contextId); + duplicate_string(newEndPId->topology_id.topology_uuid, refEndPId->topology_id.topology_uuid); + //duplicate the deviceId and endPoint_uuid + duplicate_string(newEndPId->deviceId, refEndPId->deviceId); + duplicate_string(newEndPId->endPointId, refEndPId->endPointId); + //DEBUG_PC("refLink Endpoint[%d]: %s(%s)", j, refEndPId->deviceId, refEndPId->endPointId); + //DEBUG_PC("newLink Endpoint[%d]: %s(%s)", m, newEndPId->deviceId, newEndPId->endPointId); + newLink->numLinkEndPointIds++; + } + + // duplicate forwarding direction + newLink->forwarding_direction = refLink->forwarding_direction; + + // duplicate capacity attributes + memcpy(&newLink->potential_capacity.value, &refLink->potential_capacity.value, sizeof(gdouble)); + newLink->potential_capacity.unit = refLink->potential_capacity.unit; + + memcpy(&newLink->available_capacity.value, &refLink->available_capacity.value, sizeof(gdouble)); + newLink->available_capacity.unit = refLink->available_capacity.unit; + + // duplicate cost characteristics + memcpy(&newLink->cost_characteristics.cost_value, &refLink->cost_characteristics.cost_value, sizeof(gdouble)); + memcpy(&newLink->cost_characteristics.cost_algorithm, &refLink->cost_characteristics.cost_algorithm, sizeof(gdouble)); + duplicate_string(newLink->cost_characteristics.cost_name, refLink->cost_characteristics.cost_name); + + // duplicate latency characteristics + memcpy(&newLink->latency_characteristics.fixed_latency, &refLink->latency_characteristics.fixed_latency, sizeof(gdouble)); + } + DEBUG_PC("Terminating Reverse Links [total: %d]", linkList->numLinks); + return; +} + + +/////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to parse the JSON object/s for the PATH COMP request (i.e. service + * requests, device and links) + * + * @param root + * @param source + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void parsing_json_obj_pathComp_request(cJSON * root, GIOChannel * source) +{ + //DEBUG_PC("**"); + if (deviceList == NULL){ + DEBUG_PC ("Device List does not exist ... STOP"); + exit(-1); + } + + if (linkList == NULL) { + DEBUG_PC("Link List does not exist ... STOP") + } + + if (serviceList == NULL) + { + DEBUG_PC ("Service List does not exist ... STOP"); + exit(-1); + } + + // Set of services to seek their path and resource selection + cJSON* serviceListArray = cJSON_GetObjectItem(root, "serviceList"); + if (cJSON_IsArray(serviceListArray)) { + parsing_json_serviceList_array(serviceListArray); + } + + // Get the deviceList + cJSON* deviceListArray = cJSON_GetObjectItem(root, "deviceList"); + if (cJSON_IsArray(deviceListArray)) { + parsing_json_deviceList_array(deviceListArray); + } + + // Get the linkList + cJSON* linkListArray = cJSON_GetObjectItem(root, "linkList"); + if (cJSON_IsArray(linkListArray)) { + parsing_json_linkList_array(linkListArray); + + // In the context information, if solely the list of links are passed for a single direction, + // the reverse direction MUST be created sythetically + generate_reverse_linkList(); + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used parse the JSON object/s + * + * @param data + * @param source + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint parsing_json_obj (guchar *data, GIOChannel *source) { + cJSON * root = cJSON_Parse((const char *)data); + char * print = cJSON_Print(root); + + DEBUG_PC("STARTING PARSING JSON CONTENTS"); + parsing_json_obj_pathComp_request (root, source); + // Release the root JSON object variable + cJSON_free (root); + g_free(print); + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Create new tcp client connected to PATH COMP + * + * @param channel_client, GIOChannel + * @param fd + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +struct pathComp_client * RESTapi_client_create (GIOChannel * channel_client, gint fd) { + /** check values */ + g_assert(channel_client != NULL); + + struct pathComp_client* client = g_malloc0 (sizeof (struct pathComp_client)); + if (client == NULL ) + { + DEBUG_PC ("Malloc for the client failed"); + exit(-1); + } + + /** Make client input/output buffer. */ + client->channel = channel_client; + client->obuf = stream_new(MAXLENGTH); + client->ibuf = stream_new(MAXLENGTH); + client->fd = fd; + + // Clients connected to the PATH COMP SERVER + CLIENT_ID++; + client->type = CLIENT_ID; + + //DEBUG_PC ("Client Id: %u is created (%p)", client->type, client); + //DEBUG_PC ("Client ibuf: %p || obuf: %p", client->ibuf, client->obuf); + + // Add the tcp client to the list + RESTapi_tcp_client_list = g_list_append (RESTapi_tcp_client_list, client); + //DEBUG_PC ("Num of TCP Clients: %d", g_list_length (RESTapi_tcp_client_list)); + return client; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Close the tcp client, removing from the rapi_tcp_client_list + * + * @param client + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void RESTapi_client_close (struct pathComp_client* client) +{ + //DEBUG_PC("Closing the client (Id: %d) %p", client->type, client); + //DEBUG_PC("Client ibuf: %p || obuf: %p", client->ibuf, client->obuf); + + if (client->ibuf != NULL) + { + //DEBUG_PC("Client ibuf: %p", client->ibuf); + stream_free(client->ibuf); + client->ibuf = NULL; + } + if (client->obuf != NULL) + { + //DEBUG_PC("Client obuf: %p", client->obuf); + stream_free(client->obuf); + client->obuf = NULL; + } + // Remove from the list + RESTapi_tcp_client_list = g_list_remove (RESTapi_tcp_client_list, client); + //DEBUG_PC ("TCP Client List: %d", g_list_length(RESTapi_tcp_client_list)); + + g_free (client); + client = NULL; + DEBUG_PC ("client has been removed ..."); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Close operations over the passed tcp channel + * + * @param source + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void RESTapi_close_operations (GIOChannel * source) +{ + gint fd = g_io_channel_unix_get_fd (source); + + //DEBUG_PC ("Stop all the operations over the fd: %d", fd); + g_io_channel_flush(source, NULL); + GError *error = NULL; + g_io_channel_shutdown (source, TRUE, &error); + if(error) + { + DEBUG_PC ("An error occurred ..."); + } + g_io_channel_unref (source); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Remove the client and close operations over the TCP connection + * + * @param client + * @param source + * @param fd + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void RESTapi_stop (struct pathComp_client* client, GIOChannel * source, gint fd) +{ + + DEBUG_PC("Client Socket: %d is Stopped", fd); + // remove client + RESTapi_client_close(client); + // Stop operations over that channel + RESTapi_close_operations(source); + close (fd); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used read the different lines ending up in \r\n + * + * @param s + * @param buf + * @param size + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint RESTapi_get_line (GIOChannel *channel, gchar *buf, gint size) +{ + gint i = 0; + //DEBUG_PC ("\n"); + //DEBUG_PC ("----- Read REST API Line(\r\n) ------"); + gint n = 0; + guchar c = '\0'; // END OF FILE + gboolean cr = FALSE; + while (i < size - 1) + { + n = read_channel (channel, &c, 1); + if (n == -1) + { + //DEBUG_PC ("Close the channel and eliminate the client"); + return -1; + } + if (n > 0) + { + //DEBUG_PC ("%c", c); + buf[i] = c; + i++; + if (c == '\r') + { + cr = TRUE; + } + if ((c == '\n') && (cr == TRUE)) + { + break; + } + } + else + { + c = '\n'; + buf[i] = c; + i++; + break; + } + } + buf[i] = '\0'; + //DEBUG_PC ("Line (size: %d) buf: %s", i, buf); + return i; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used read the HTTP method + * + * @param buf + * @param j + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +guint RESTapi_get_method (gchar *buf, gint *j) +{ + guint RestApiMethod = 0; + gchar method[255]; + gint i = 0; + while (!ISspace(buf[*j]) && (i < sizeof(method) - 1)) + { + method[i] = buf[*j]; + i++; + *j = *j + 1; + } + method[i] = '\0'; + DEBUG_PC ("REST API METHOD: %s", method); + + // Check that the methods are GET, POST or PUT + if (strcasecmp((const char *)method, "GET") && strcasecmp((const char *)method, "POST") && + strcasecmp ((const char *)method, "HTTP/1.1") && strcasecmp ((const char *)method, "PUT")) + { + DEBUG_PC ("The method: %s is not currently supported ...", method); + return RestApiMethod; + } + // Method selector + if (strncmp ((const char*)method, "GET", 3) == 0) + { + RestApiMethod = REST_API_METHOD_GET; + } + else if (strncmp ((const char*)method, "POST", 4) == 0) + { + RestApiMethod = REST_API_METHOD_POST; + } + else if (strncmp ((const char *)method, "HTTP/1.1", 8) == 0) + { + RestApiMethod = REST_API_METHOD_HTTP; + } + else if (strncmp ((const char *)method, "PUT", 3) == 0) + { + RestApiMethod = REST_API_METHOD_PUT; + } + + return RestApiMethod; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used read the url + * + * @param buf + * @param j + * @param url + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint get_url (gchar *buf, gint *j, gchar *url) +{ + // Skip space char + while (ISspace(buf[*j]) && (*j < strlen(buf))) { + *j = *j + 1; + } + + //DEBUG_PC ("buf[%d]: %c", *j, buf[*j]); + int result = isspace (buf[*j]); + *buf = *buf + *j; + gint numChar = 0; + gint initChar = *j; + result = 0; + while (result == 0) { + *j = *j + 1; + result = isspace (buf[*j]); + numChar++; + } + //DEBUG_PC ("numChar: %d", numChar); + memcpy (url, buf + initChar, numChar); + url[numChar] = '\0'; + //DEBUG_PC ("url: %s", url); + return numChar; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used read the version + * + * @param buf + * @param j + * @param version + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint get_version (gchar *buf, gint *j, gchar *version) { + // Skip space char + while (ISspace(buf[*j]) && (*j < strlen(buf))) + { + *j = *j + 1; + } + //DEBUG_PC ("buf[%d]: %c", *j, buf[*j]); + int result = isspace (buf[*j]); + *buf = *buf + *j; + gint numChar = 0; + gint initChar = *j; + result = 0; + while (result == 0) { + *j = *j + 1; + result = isspace (buf[*j]); + numChar++; + } + //DEBUG_PC ("numChar: %d", numChar); + memcpy (version, buf + initChar, numChar); + version[numChar] = '\0'; + //DEBUG_PC ("version: %s", version); + return numChar; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to trigger the route computation for the network connectivity service + * List and retrieve the result + * + * @param compRouteList + * @param raId + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint triggering_routeComp (struct compRouteOutputList_t *compRouteList, gchar *algId) { + g_assert (compRouteList); + gint httpCode = HTTP_RETURN_CODE_OK; + DEBUG_PC("Requested Algorithm: %s", algId); + //////////////////// Algorithm Selector (RAId)////////////////////////////////////// + // Connectivity Service Abstraction (CSA) + if (strncmp ((const char*)algId, "KSP", 3) == 0) + { + DEBUG_PC ("Alg Id: KSP"); + httpCode = pathComp_ksp_alg(compRouteList); + } +#if 0 + // Infrastructure Abstraction (InA) + else if (strncmp ((const char*)raId, "InA", 3) == 0) + { + //DEBUG_PC ("RA: InA"); + httpCode = ra_InA_alg (compRouteList); + } + // Global Concurrent Optimization (GCO): Resoration / Re-Allocation / Re-Optimization + else if (strncmp ((const char*)raId, "GCO", 3) == 0) + { + //DEBUG_PC ("RA: GCO"); + httpCode = ra_GCO_alg (compRouteList); + } +#endif + return httpCode; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to process the REST API commands + * + * @param source + * @param cond + * @param data + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gboolean RESTapi_activity(GIOChannel *source, GIOCondition cond, gpointer data) +{ + /** some checks */ + g_assert(source != NULL); + g_assert(data != NULL); + + gchar buf[1024]; + gchar version[255]; + gchar http_result[255]; + gint body_length = 0; + + struct pathComp_client *client = (struct pathComp_client*)(data); + DEBUG_PC (" ************************************************************************** "); + DEBUG_PC (" REST API ACTIVITY Triggered "); + DEBUG_PC (" ************************************************************************** "); + + gint fd = g_io_channel_unix_get_fd (source); + DEBUG_PC ("fd: %d, cond: %d", fd, cond); + + if (cond != G_IO_IN) + { + DEBUG_PC ("Something happening with the channel and fd ... (cond: %d)", cond); + RESTapi_stop(client, source, fd); + return FALSE; + } + /** Clear input buffer. */ + stream_reset (client->ibuf); + + // get line + gint nbytes = RESTapi_get_line (source, buf, sizeof (buf)); + if (nbytes == -1) + { + DEBUG_PC ("nbytes -1 ... CLOSE CLIENT FD and eliminate CLIENT"); + RESTapi_stop(client, source, fd); + return FALSE; + } + + if ((buf[0] == '\n') && (nbytes == 1)) + { + //DEBUG_PC (" -- buf[0] = newline --"); + RESTapi_stop(client, source, fd); + return FALSE; + } + + gint i = 0, j = 0; + // Get the REST Method + guint RestApiMethod = RESTapi_get_method (buf, &j); + if (RestApiMethod == 0) + { + DEBUG_PC ("The method is NOT supported ..."); + RESTapi_unimplemented (source); + RESTapi_stop(client, source, fd); + return FALSE; + } + + // get the REST url + gchar url[255]; + i = get_url (buf, &j, url); + url[i] = '\0'; + + // for method POST, PUT check that the url is "/pathComp" + if (strncmp((const char*) url, "/pathComp/api/v1/compRoute", 26) != 0) + { + DEBUG_PC ("Unknown url: %s", url); + RESTapi_stop(client, source, fd); + exit (-1); + } + + // get the version + i = get_version (buf, &j, version); + version[i] = '\0'; + + // Assume HTTP/1.1, then there is Host Header + memset(buf, '\0', sizeof(buf)); + nbytes = RESTapi_get_line(source, buf, sizeof (buf)); + if (nbytes == -1) + { + DEBUG_PC ("nbytes -1 ... then close the fd and eliminate associated client"); + RESTapi_stop(client, source, fd); + return FALSE; + } + + //DEBUG_PC ("Header: %s", buf); + + // Headers --- The Header Fields ends up with a void line (i.e., \r\n) + while ((nbytes > 0) && (strcmp ("\r\n", (const char *)buf) != 0)) + { + /* read & discard headers */ + memset(buf, '\0', sizeof(buf)); + nbytes = RESTapi_get_line (source, buf, sizeof (buf)); + if (nbytes == -1) + { + DEBUG_PC ("nbytes -1 ... then close the fd and eliminate associated client"); + RESTapi_stop(client, source, fd); + return FALSE; + } + //DEBUG_PC ("Header: %s", buf); + if (strncmp ((const char *)buf, "Content-Length:", 15) == 0) + { + //DEBUG_PC ("Header Content-Length Found"); + gchar str[20]; + + gint i = 15, k = 0; // "Content-Length:" We skip the first 16 characters to directly retrieve the length in bytes of the Body of Request + gchar contentLength[255]; + memset (contentLength, '\0', sizeof (contentLength)); + while (buf[i] != '\r') + { + //DEBUG_PC ("%c", buf[i]); + str[k] = buf[i]; + k++, i++; + } + str[k] = '\0'; + j = 0, i = 0; + while (ISspace(str[j]) && (j < strlen(str))) + { + j++; + } + while (j < strlen(str)) + { + contentLength[i] = str[j]; + i++; j++; + } + contentLength[i] = '\0'; + body_length = atoi (contentLength); + //DEBUG_PC ("Body length: %d (%s) in Bytes", body_length, contentLength); + } + } + //DEBUG_PC("Read Entire HTTP Header"); + if (body_length == 0) + { + DEBUG_PC ("--- NO REST API Body length (length = %d) ---", body_length); + return TRUE; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Processing Body of the Request + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + //DEBUG_PC ("REST API Request - Body -"); + nbytes = read_channel (source, (guchar *)(client->ibuf->data + client->ibuf->putp), body_length); + if ((nbytes < 0) && (body_length > 0)) + { + DEBUG_PC ("nbytes: %d; body_length: %d", nbytes, body_length); + exit (-1); + } + + client->ibuf->putp += nbytes; + client->ibuf->endp += nbytes; + /////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Parsing the contents of the Request + /////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // build the device list + deviceList = create_device_list(); + // build the link list + linkList = create_link_list(); + // Create the network connectivity service list + serviceList = create_service_list(); + + // Process the json contents and store relevant information at Device, Link, + // and network connectivity service + gint ret = parsing_json_obj (client->ibuf->data, source); + if (ret == -1) { + DEBUG_PC ("Something wrong with the JSON Objects ... "); + RESTapi_stop(client, source, fd); + return FALSE; + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Trigger the path computation + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //DEBUG_PC ("Triggering the computation"); + struct compRouteOutputList_t *compRouteOutputList = create_route_list (); + gint httpCode = triggering_routeComp (compRouteOutputList, algId); + + // Send the response to the REST API Client + if (httpCode != HTTP_RETURN_CODE_OK) + { + DEBUG_PC ("HTTP CODE: %d -- NO OK", httpCode); + rapi_response (source, httpCode); + } + else + { + DEBUG_PC ("HTTP CODE: %d -- OK", httpCode); + rapi_response_ok (source, httpCode, compRouteOutputList); + } + + // Release the variables + g_free (compRouteOutputList); + g_free(linkList); + g_free(deviceList); + g_free(serviceList); + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Function used to accept a new connection and add the client to list of clients + * + * @param source, GIOChannel + * @param cond, GIOCondition + * @param data, gpointer + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gboolean RESTapi_tcp_new_connection(GIOChannel *source, GIOCondition cond, gpointer data) +{ + DEBUG_PC (" ****** New TCP Connection (REST API) ******"); + /** get size of client_addre structure */ + struct sockaddr_in client_addr; + socklen_t client = sizeof(client_addr); + + if ((cond == G_IO_HUP) || (cond == G_IO_ERR) || (G_IO_NVAL)) + { + //DEBUG_PC ("Something happening with the channel and fd ... cond: %d", cond); + // Find the associated client (by the fd) and remove from PATH COMP client list. + // Stop all the operations over that PATH COMP client bound channel + struct pathComp_client *pathComp_client = NULL; + gint fd = g_io_channel_unix_get_fd (source); + GList *found = g_list_find_custom (RESTapi_tcp_client_list, &fd, find_rl_client_by_fd); + if (found != NULL) + { + pathComp_client = (struct pathComp_client*)(found->data); + // remove client + RESTapi_client_close(pathComp_client); + // Stop operations over that channel + RESTapi_close_operations(source); + close (fd); + return FALSE; + } + } + if (cond == G_IO_IN) + { + gint new = accept(g_io_channel_unix_get_fd(source), (struct sockaddr*)&client_addr, &client); + if (new < 0) + { + //DEBUG_PC ("Unable to accept new connection"); + return FALSE; + } + + /** new channel */ + GIOChannel * new_channel = g_io_channel_unix_new (new); + //DEBUG_PC ("TCP Connection (REST API) is UP; (socket: %d)", new); + + /** create pathComp client */ + struct pathComp_client *new_client = RESTapi_client_create (new_channel, new); + + /** + * force binary encoding with NULL + */ + GError *error = NULL; + if ( g_io_channel_set_encoding (new_channel, NULL, &error) != G_IO_STATUS_NORMAL) + { + DEBUG_PC ("Error: %s", error->message); + exit (-1); + } + g_io_channel_set_close_on_unref (new_channel, TRUE); + // On unbuffered channels, it is safe to mix read + // & write calls from the new and old APIs. + g_io_channel_set_buffered (new_channel, FALSE); + if (g_io_channel_set_flags (new_channel, G_IO_FLAG_NONBLOCK, &error) != G_IO_STATUS_NORMAL ) + { + DEBUG_PC ("Error: %s", error->message); + exit (-1); + } + //Adds the new channel into the main event loop. + g_io_add_watch (new_channel, G_IO_IN, RESTapi_activity, new_client); + } + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief enabling the reuse of the addr for the Server TCP + * + * @param sock + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void RESTapi_tcp_enable_reuseaddr (gint sock) +{ + gint tmp = 1; + if (sock < 0) + { + DEBUG_PC (" socket: %d !!!",sock); + exit (-1); + } + if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (gchar *)&tmp, sizeof (tmp)) == -1) + { + DEBUG_PC ("bad setsockopt ..."); + exit (-1); + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_RESTapi.c + * @brief Main function for the creating / maintaining TCP session for the REST API + * + * @ port + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void RESTapi_init(gint port) +{ + DEBUG_PC ("REST API PORT (listening): %d", port); + + // File Descriptor - FD - for the socket + gint s = socket (AF_INET, SOCK_STREAM, 0); + if (s == -1) + { + DEBUG_PC ("Socket creation: FAILED!!"); + exit (-1); + } + DEBUG_PC (" CREATED TCP Connection [@fd: %d]", s); + + // Re-bind + RESTapi_tcp_enable_reuseaddr(s); + struct sockaddr_in addr; + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons ((u_short)port); + addr.sin_addr.s_addr = INADDR_ANY; + + // Associate IP address and Port to the created socket + if (bind (s, (struct sockaddr *)&addr, sizeof(addr)) == -1) + { + close (s); + DEBUG_PC ("Socket bind: FAILED!!"); + exit (-1); + } + DEBUG_PC ("Bind to Fd: %d DONE!!", s); + + /** Set up queue for incoming connections */ + if (listen (s, 10) == -1) + { + close (s); + DEBUG_PC ("Socket listen: FAILED!!"); + exit (-1); + } + + //DEBUG_PC ("Listen (up to 10) to Fd: %d Done", s); + + /** Create NEW channel to handle the socket operations*/ + GIOChannel *channel = g_io_channel_unix_new (s); + gsize buffersize = g_io_channel_get_buffer_size (channel); + //DEBUG_PC ("GIOChannel with Buffer Size: %d", (gint)buffersize); + + gsize newBufferSize = MAX_GIO_CHANNEL_BUFFER_SIZE; + g_io_channel_set_buffer_size (channel, newBufferSize); + buffersize = g_io_channel_get_buffer_size (channel); + + //DEBUG_PC ("GIOChannel with Buffer Size: %d", (gint)buffersize); + //DEBUG_PC ("Channel associated to fd: %d is created", s); + + // Adds the new channel into the main event loop. + g_io_add_watch (channel, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, RESTapi_tcp_new_connection, NULL); + return; +} \ No newline at end of file diff --git a/src/pathcomp/backend/pathComp_RESTapi.h b/src/pathcomp/backend/pathComp_RESTapi.h new file mode 100644 index 0000000000000000000000000000000000000000..80e63da7c13c353592931be9d72f53e30a8aca5b --- /dev/null +++ b/src/pathcomp/backend/pathComp_RESTapi.h @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////////////// +/** + * # Copyright 2022 Centre Tecnològic de Telecomunicacions de Catalunya (CTTC/CERCA) www.cttc.es + * + * 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. + + * Author: CTTC/CERCA PONS RU Ricardo Martínez (ricardo.martinez@cttc.es) + */ + +#ifndef _PATH_COMP_REST_API_H +#define _PATH_COMP_REST_API_H + + +#include <glib.h> +#include <glib/gstdio.h> +#include <glib-2.0/glib/gtypes.h> + + +#define MAX_GIO_CHANNEL_BUFFER_SIZE 131072 + +// HTTP RETURN CODES +#define HTTP_RETURN_CODE_OK 200 +#define HTTP_RETURN_CODE_CREATED 201 +#define HTTP_RETURN_CODE_BAD_REQUEST 400 +#define HTTP_RETURN_CODE_UNAUTHORIZED 401 +#define HTTP_RETURN_CODE_FORBIDDEN 403 +#define HTTP_RETURN_CODE_NOT_FOUND 404 +#define HTTP_RETURN_CODE_NOT_ACCEPTABLE 406 + +// REST API METHODS (SIMPLY INT ENCODING) +#define REST_API_METHOD_GET 1 +#define REST_API_METHOD_POST 2 +#define REST_API_METHOD_HTTP 3 +#define REST_API_METHOD_PUT 4 + +#define MAXLENGTH 131072 + +//////////////////////////////////////////////////// +// Client Struct for connecting to PATH COMP SERVER +//////////////////////////////////////////////////// +// List of tcp clients connected to PATH COMP + +#define PATH_COMP_CLIENT_TYPE 1000 +struct pathComp_client +{ + /** IO Channel from client. */ + GIOChannel *channel; + + /** Input/output buffer to the client. */ + struct stream *obuf; + struct stream *ibuf; + + gint fd; // file descriptor + guint type; +}; + +void RESTapi_init (gint); +#endif diff --git a/src/pathcomp/backend/pathComp_cjson.c b/src/pathcomp/backend/pathComp_cjson.c new file mode 100644 index 0000000000000000000000000000000000000000..093d80a6d5a342c7719b231cf1aeb8dcc2e90956 --- /dev/null +++ b/src/pathcomp/backend/pathComp_cjson.c @@ -0,0 +1,2732 @@ +//////////////////////////////////////////////////////////////////////////////////////// +/** + * # Copyright 2022 Centre Tecnològic de Telecomunicacions de Catalunya (CTTC/CERCA) www.cttc.es + * + * 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. + + * Author: CTTC/CERCA PONS RU Ricardo MartÃnez (ricardo.martinez@cttc.es) + */ + //////////////////////////////////////////////////////////////////////////////////////// + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> +#include <glib.h> +#include <sys/time.h> +#include <ctype.h> +#include <strings.h> +#include <time.h> +#include <math.h> +#include <float.h> +#include <limits.h> +#include <fcntl.h> + + +#include "pathComp_log.h" +#include "pathComp_cjson.h" + +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +typedef struct +{ + const unsigned char *json; + size_t position; +} error; + +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(*allocate)(size_t size); + void (*deallocate)(void *pointer); + void *(*reallocate)(void *pointer, size_t size); +} internal_hooks; + + +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc + + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ + return '.'; + +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occured */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(256); + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->length); + buffer->buffer = NULL; + if (printed == NULL) { + goto fail; + } + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((len < 0) || (buf == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buf; + p.length = (size_t)len; + p.offset = 0; + p.noalloc = true; + p.format = fmt; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + if (!output_buffer->noalloc) + { + output_buffer->hooks.deallocate(output_buffer->buffer); + } + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* faile to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = (size_t) ((output_buffer->format ? 1 : 0) + (current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) + { + return; + } + + child = array->child; + + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + } + else + { + /* append to the end */ + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + } +} + +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + if (item == NULL) + { + return; + } + + /* call cJSON_AddItemToObjectCS for code reuse */ + cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string, &global_hooks), item); + /* remove cJSON_StringIsConst flag */ + item->type &= ~cJSON_StringIsConst; +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + if ((item == NULL) || (string == NULL)) + { + return; + } + if (!(item->type & cJSON_StringIsConst) && item->string) + { + global_hooks.deallocate(item->string); + } + item->string = (char*)string; + item->type |= cJSON_StringIsConst; + cJSON_AddItemToArray(object, item); +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return; + } + + cJSON_AddItemToArray(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return; + } + + cJSON_AddItemToObject(object, string, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item->prev != NULL) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + cJSON_AddItemToArray(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (parent->child == item) + { + parent->child = replacement; + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return; + } + + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + unsigned char *into = (unsigned char*)json; + + if (json == NULL) + { + return; + } + + while (*json) + { + if (*json == ' ') + { + json++; + } + else if (*json == '\t') + { + /* Whitespace characters. */ + json++; + } + else if (*json == '\r') + { + json++; + } + else if (*json=='\n') + { + json++; + } + else if ((*json == '/') && (json[1] == '/')) + { + /* double-slash comments, to end of line. */ + while (*json && (*json != '\n')) + { + json++; + } + } + else if ((*json == '/') && (json[1] == '*')) + { + /* multiline comments. */ + while (*json && !((*json == '*') && (json[1] == '/'))) + { + json++; + } + json += 2; + } + else if (*json == '\"') + { + /* string literals, which are \" sensitive. */ + *into++ = (unsigned char)*json++; + while (*json && (*json != '\"')) + { + if (*json == '\\') + { + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + else + { + /* All other characters. */ + *into++ = (unsigned char)*json++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file transponder_agent/agent_cjson.c + * @brief Check if the item is a number (double) + * + * @param item + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2017 + */ +///////////////////////////////////////////////////////////////////////////////////////// +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file transponder_agent/agent_cjson.c + * @brief Check if the item is a string + * + * @param item + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2017 + */ +///////////////////////////////////////////////////////////////////////////////////////// +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (a->valuedouble == b->valuedouble) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/src/pathcomp/backend/pathComp_cjson.h b/src/pathcomp/backend/pathComp_cjson.h new file mode 100644 index 0000000000000000000000000000000000000000..47c2830ef3cb1bf6c72c5e3897a066035936b247 --- /dev/null +++ b/src/pathcomp/backend/pathComp_cjson.h @@ -0,0 +1,214 @@ +//////////////////////////////////////////////////////////////////////////////////////// +/** + * # Copyright 2022 Centre Tecnològic de Telecomunicacions de Catalunya (CTTC/CERCA) www.cttc.es + * + * 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. + + * Author: CTTC/CERCA PONS RU Ricardo Martínez (ricardo.martinez@cttc.es) + */ + //////////////////////////////////////////////////////////////////////////////////////// + +#ifndef _PATHCOMP_CJSON_H +#define _PATHCOMP_CJSON_H + + +#include <glib.h> +#include <glib/gstdio.h> +#include <glib-2.0/glib/gtypes.h> + +#include <stddef.h> + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 6 +#define CJSON_VERSION_PATCH 0 + + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +#define CJSON_PUBLIC(type) type + +#define CJSON_NESTING_LIMIT 1000 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + + +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Macros for creating things quickly. */ +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) +#define cJSON_AddRawToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateRaw(s)) + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + + +#endif diff --git a/src/pathcomp/backend/pathComp_ksp.c b/src/pathcomp/backend/pathComp_ksp.c new file mode 100644 index 0000000000000000000000000000000000000000..25e9d0b6041fdfab2373213408f62c12da31eb12 --- /dev/null +++ b/src/pathcomp/backend/pathComp_ksp.c @@ -0,0 +1,617 @@ +//////////////////////////////////////////////////////////////////////////////////////// +/** + * # Copyright 2022 Centre Tecnològic de Telecomunicacions de Catalunya (CTTC/CERCA) www.cttc.es + * + * 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. + + * Author: CTTC/CERCA PONS RU Ricardo MartÃnez (ricardo.martinez@cttc.es) + */ + //////////////////////////////////////////////////////////////////////////////////////// +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> +#include <glib.h> +#include <sys/time.h> +#include <ctype.h> +#include <strings.h> +#include <time.h> +#include <math.h> +#include <fcntl.h> + +#include "pathComp_log.h" +#include "pathComp_tools.h" +#include "pathComp_ksp.h" + +// Global Variables +struct map_nodes_t *mapNodes = NULL; +struct graph_t *graph = NULL; +struct contextSet_t* contextSet = NULL; +//struct linkList_t* linkList; +//struct deviceList_t* deviceList; +//struct serviceList_t* serviceList; + +gint numPathCompIntents = 0; // number of events triggering the path computation +gint numSuccesPathComp = 0; // number of events resulting in succesfully path computations fulfilling the constraints +struct timeval total_path_comp_time; +gdouble totalReqBw = 0.0; +gdouble totalServedBw = 0.0; + +/////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_ksp.c + * @brief update statistics of the KSP path computation + * + * @param routeConnList + * @param d + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2021 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void update_stats_ksp_path_comp(struct compRouteOutputList_t* routeConnList, struct timeval d) +{ + g_assert(routeConnList); + g_assert(serviceList); + + total_path_comp_time.tv_sec = total_path_comp_time.tv_sec + d.tv_sec; + total_path_comp_time.tv_usec = total_path_comp_time.tv_usec + d.tv_usec; + total_path_comp_time = tv_adjust(total_path_comp_time); + + gdouble path_comp_time_msec = (((total_path_comp_time.tv_sec) * 1000) + ((total_path_comp_time.tv_usec) / 1000)); + gdouble av_alg_comp_time = ((path_comp_time_msec / numSuccesPathComp)); + DEBUG_PC("\t --- STATS KSP PATH COMP ----"); + DEBUG_PC("Succesfully Comp: %d | Path Comp Requests: %d", numSuccesPathComp, numPathCompIntents); + DEBUG_PC("AV. PATH COMP ALG. TIME: %f ms", av_alg_comp_time); + + for (gint i = 0; i < serviceList->numServiceList; i++) { + struct service_t* s = &(serviceList->services[i]); + char* eptr; + for (gint j = 0; j < s->num_service_constraints; j++) { + struct constraint_t* constraints = &(s->constraints[j]); + if (strncmp((const char*)(constraints->constraint_type), "bandwidth", 9) == 0) { + totalReqBw += (gdouble)(strtod((char*)constraints->constraint_value, &eptr)); + } + } + } + for (gint k = 0; k < routeConnList->numCompRouteConnList; k++) { + struct compRouteOutput_t* rO = &(routeConnList->compRouteConnection[k]); + if (rO->noPathIssue == NO_PATH_CONS_ISSUE) { + continue; + } + // Get the requested service bw bound to that computed path + struct path_t* p = &(rO->paths[0]); + struct service_t* s = get_service_for_computed_path(rO->serviceId.service_uuid); + if (s == NULL) { + DEBUG_PC("Weird the service associated to a path is not found"); + exit(-1); + } + for (gint l = 0; l < s->num_service_constraints; l++) { + struct constraint_t* constraints = &(s->constraints[l]); + char* eptr; + if (strncmp((const char*)(constraints->constraint_type), "bandwidth", 9) == 0) { + totalServedBw += (gdouble)(strtod((char*)constraints->constraint_value, &eptr)); + } + } + } + gdouble avServedRatio = totalServedBw / totalReqBw; + DEBUG_PC("AV. Served Ratio: %f", avServedRatio); + gdouble avBlockedBwRatio = (gdouble)(1.0 - avServedRatio); + DEBUG_PC("AV. BBE: %f", avBlockedBwRatio); + return; +} + +/////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_ksp.c + * @brief Dijkstra algorithm + * + * @param srcMapIndex + * @param dstMapIndex + * @param g + * @param s + * @param SN + * @param RP + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void sp_comp(gint srcMapIndex, gint dstMapIndex, struct graph_t* g, struct service_t* s, + struct nodes_t* SN, struct compRouteOutputItem_t* RP) { + g_assert(s); + g_assert(g); + + // Set params into mapNodes related to the source nodes of the request + mapNodes->map[srcMapIndex].distance = 0.0; + mapNodes->map[srcMapIndex].latency = 0.0; + mapNodes->map[srcMapIndex].avaiBandwidth = 0.0; + + // Initialize the set Q and S + GList* S = NULL, * Q = NULL; + gint indexVertice = -1; + + // Add the source into the Q + struct nodeItem_t* nodeItem = g_malloc0(sizeof(struct nodeItem_t)); + if (nodeItem == NULL) { + DEBUG_PC("memory allocation failed\n"); + exit(-1); + } + // initialize some nodeItem attributes + nodeItem->distance = 0.0; + nodeItem->latency = 0.0; + duplicate_node_id(&mapNodes->map[srcMapIndex].verticeId, &nodeItem->node); + Q = g_list_insert_sorted(Q, nodeItem, sort_by_distance); + + // Check whether there is spurNode (SN) and rootPath (RP) + if (SN != NULL && RP != NULL) { + struct routeElement_t* re; + for (gint j = 0; j < RP->numRouteElements; j++) + { + // Get the source and target Nodes of the routeElement within the rootPath + re = &RP->routeElement[j]; + DEBUG_PC ("root Link: aNodeId: %s (%s) --> zNodeiId: %s (%s)", re->aNodeId.nodeId, re->aEndPointId, re->zNodeId.nodeId, re->zEndPointId); + + // if ingress of the root link (aNodeId) is the spurNode, then stops + if (compare_node_id(&re->aNodeId, SN) == 0) + { + DEBUG_PC ("root Link: aNodeId: %s and spurNode: %s -- stop exploring the rootPath (RP)", re->aNodeId.nodeId, SN->nodeId); + break; + } + // Extract from Q + GList* listnode = g_list_first(Q); + struct nodeItem_t* node = (struct nodeItem_t*)(listnode->data); + Q = g_list_remove(Q, node); + + //DEBUG_RL_RA ("Exploring node %s", node->node.nodeId); + indexVertice = graph_vertice_lookup(node->node.nodeId, g); + g_assert(indexVertice >= 0); + + // Get the indexTargetedVertice + gint indexTVertice = -1; + indexTVertice = graph_targeted_vertice_lookup(indexVertice, re->zNodeId.nodeId, g); + gint done = check_link(node, indexVertice, indexTVertice, g, s, &S, &Q, mapNodes); + (void)done; + + // Add to the S list + S = g_list_append(S, node); + } + + // Check that the first node in Q set is SpurNode, otherwise something went wrong ... + if (compare_node_id(&re->aNodeId, SN) != 0) { + //DEBUG_PC ("root Link: aNodeId: %s is NOT the spurNode: %s -- something wrong", re->aNodeId.nodeId, SN->nodeId); + g_list_free_full(S, g_free); + g_list_free_full(Q, g_free); + return; + } + } + while (g_list_length(Q) > 0) { + //Extract from Q set + GList* listnode = g_list_first(Q); + struct nodeItem_t* node = (struct nodeItem_t*)(listnode->data); + Q = g_list_remove(Q, node); + DEBUG_PC ("Q length: %d", g_list_length (Q)); + DEBUG_PC ("DeviceId: %s", node->node.nodeId); + + // visit all the links from u within the graph + indexVertice = graph_vertice_lookup(node->node.nodeId, g); + g_assert(indexVertice >= 0); + + // Check the targeted vertices from u + for (gint i = 0; i < g->vertices[indexVertice].numTargetedVertices; i++) { + gint done = check_link(node, indexVertice, i, g, s, &S, &Q, mapNodes); + (void)done; + } + // Add node into the S Set + S = g_list_append(S, node); + //DEBUG_PC ("S length: %d", g_list_length (S)); + } + g_list_free_full(S, g_free); + g_list_free_full(Q, g_free); + return; +} + +/////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_ksp.c + * @brief KSP computation using Dijkstra algorithm + * + * @param pred + * @param g + * @param s + * @param SN + * @param RP + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +gint ksp_comp(struct pred_t* pred, struct graph_t* g, struct service_t* s, + struct nodes_t *SN, struct compRouteOutputItem_t *RP) { + g_assert(pred); + g_assert(g); + g_assert(s); + + // Check the both ingress src and dst endpoints are in the graph + gint srcMapIndex = get_map_index_by_nodeId(s->service_endpoints_id[0].device_uuid, mapNodes); + if (srcMapIndex == -1) { + DEBUG_PC("ingress DeviceId: %s NOT in the graph", s->service_endpoints_id[0].device_uuid); + return -1; + } + + gint dstMapIndex = get_map_index_by_nodeId(s->service_endpoints_id[1].device_uuid, mapNodes); + if (dstMapIndex == -1) { + DEBUG_PC("egress DeviceId: %s NOT in the graph", s->service_endpoints_id[1].device_uuid); + return -1; + } + + // Compute the shortes path route + sp_comp(srcMapIndex, dstMapIndex, g, s, SN, RP); + + // Check that a feasible solution in term of latency and bandwidth is found + gint map_dstIndex = get_map_index_by_nodeId(s->service_endpoints_id[1].device_uuid, mapNodes); + struct map_t* dest_map = &mapNodes->map[map_dstIndex]; + if (!(dest_map->distance < INFINITY_COST)) { + DEBUG_PC("destination: %s NOT reachable", s->service_endpoints_id[1].device_uuid); + return -1; + } + + DEBUG_PC("AvailBw @ %s is %f", dest_map->verticeId.nodeId, dest_map->avaiBandwidth); + // Check that the computed available bandwidth is larger than 0.0 + if (dest_map->avaiBandwidth <= (gfloat)0.0) { + DEBUG_PC("dst: %s NOT REACHABLE", s->service_endpoints_id[1].device_uuid); + return -1; + } + DEBUG_PC("dst: %s REACHABLE", s->service_endpoints_id[1].device_uuid); + // Handle predecessors + build_predecessors(pred, s, mapNodes); + return 1; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_ksp.c + * @brief K-CSPF algorithm execution (YEN algorithm) + * + * @param s + * @param path + * @param g + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void alg_comp(struct service_t* s, struct compRouteOutput_t* path, struct graph_t *g) { + g_assert(s); + g_assert(path); + g_assert(g); + + // create map of devices/nodes to handle the path computation using the context + mapNodes = create_map_node(); + build_map_node(mapNodes, g); + + // predecessors to store the computed path + struct pred_t* predecessors = create_predecessors(); + + struct service_endpoints_id_t* iEp = &(s->service_endpoints_id[0]); + struct service_endpoints_id_t* eEp = &(s->service_endpoints_id[1]); + + // Compute the 1st KSP path + gint done = ksp_comp (predecessors, g, s, NULL, NULL); + if (done == -1) { + DEBUG_PC("NO PATH FOUND %s[%s] ---> %s[%s]", iEp->device_uuid, iEp->endpoint_uuid, eEp->device_uuid, eEp->endpoint_uuid); + comp_route_connection_issue_handler(path, s); + g_free(mapNodes); g_free(predecessors); + return; + } + + // Construct the path from the computed predecessors + struct compRouteOutputItem_t* p = create_path_item(); + //print_predecessors(predecessors); + build_path(p, predecessors, s); + //DEBUG_PC ("Path is constructed"); + + gint indexDest = get_map_index_by_nodeId(eEp->device_uuid, mapNodes); + struct map_t* dst_map = &mapNodes->map[indexDest]; + // Get the delay and cost + memcpy(&p->cost, &dst_map->distance, sizeof(gdouble)); + memcpy(&p->availCap, &dst_map->avaiBandwidth, sizeof(dst_map->avaiBandwidth)); + memcpy(&p->delay, &dst_map->latency, sizeof(mapNodes->map[indexDest].latency)); + DEBUG_PC ("Computed Path Avail Bw: %f, Path Cost: %f, latency: %f", p->availCap, p->cost, p->delay); + print_path(p); + + // If 1st SP satisfies the requirements from the req, STOP + gboolean feasibleRoute = check_computed_path_feasability(s, p); + if (feasibleRoute == TRUE) { + DEBUG_PC("1st K-CSPF FEASIBLE, STOP!"); + print_path (p); + path->numPaths++; + + // Copy the serviceId + DEBUG_PC("contextId: %s", s->serviceId.contextId); + copy_service_id(&path->serviceId, &s->serviceId); + + // copy the service endpoints, in general, there will be 2 (point-to-point network connectivity services) + for (gint i = 0; i < s->num_service_endpoints_id; i++) { + struct service_endpoints_id_t* iEp = &(s->service_endpoints_id[i]); + struct service_endpoints_id_t* oEp = &(path->service_endpoints_id[i]); + copy_service_endpoint_id(oEp, iEp); + } + path->num_service_endpoints_id = s->num_service_endpoints_id; + + // Copy the computed path + struct path_t* targetedPath = &(path->paths[path->numPaths - 1]); + duplicate_path_t(p, targetedPath); + print_path_t (targetedPath); + g_free(predecessors); + g_free(p); + g_free(mapNodes); + return; + } + + DEBUG_PC("1st CSPF COMPUTATION IS NOT FEASIBLE --> TRIGGER K COMPUTATIONS"); + // Create A and B sets of paths to handle the YEN algorithm + struct path_set_t* A = create_path_set(); + struct path_set_t* B = create_path_set(); + + // Add the previously computed path into A->paths[0] + duplicate_path(p, &A->paths[0]); + + A->numPaths++; + g_free(predecessors); + g_free(p); + for (gint k = 1; k < MAX_KSP_VALUE; k++) { + DEBUG_PC("------------ kth (%d) ---------------------", k); + struct compRouteOutputItem_t* p = create_path_item(); + duplicate_path(&A->paths[k - 1], p); + // The spurNode ranges from near-end node of the first link to the near-end of the last link forming the kth path + gint i = 0; + struct compRouteOutputItem_t* rootPath = create_path_item(); + for (i = 0; i < p->numRouteElements; i++) { + struct nodes_t* spurNode = create_node(); + struct nodes_t* nextSpurNode = create_node(); + struct routeElement_t* re = &(p->routeElement[i]); + // Create predecessors to store the computed path + struct pred_t* predecessors = create_predecessors(); + // Clear previous mapNodes, i.e. create it again + g_free(mapNodes); + mapNodes = create_map_node(); + build_map_node(mapNodes, g); + struct nodes_t* n = &re->aNodeId; + duplicate_node_id(n, spurNode); + n = &re->zNodeId; + duplicate_node_id(n, nextSpurNode); + DEBUG_PC("spurNode: %s --> nextSpurNode: %s", spurNode->nodeId, nextSpurNode->nodeId); + + // rootPath contains a set of links of A[k-1] from the source Node till the SpurNode -> NextSpurNode + // Example: A[k-1] = {L1, L2, L3, L4}, i.e. " Node_a -- L1 --> Node_b -- L2 --> Node_c -- L3 --> Node_d -- L4 --> Node_e " + // E.g., for the ith iteration if the spurNode = Node_c and NextSpurNode = Node_d; then rootPath = {L1, L2, L3} + add_routeElement_path_back(re, rootPath); + DEBUG_PC("rootPath:"); + print_path(rootPath); + + // For all existing and computed paths p in A check if from the source to the NextSpurNode + // the set of links matches with those contained in the rootPath + // If YES, remove from the auxiliary graph the next link in p from NextSpurNode + // Otherwise do nothing + struct graph_t* gAux = create_graph(); + // Baseline graph + //build_graph (gAux); + duplicate_graph(g, gAux); + // Modified graph + modify_targeted_graph(gAux, A, rootPath, spurNode); + + // Trigger the computation of the path from src to dst constrained to traverse all the links from src + // to spurNode contained into rootPath over the resulting graph + if (ksp_comp(predecessors, gAux, s, spurNode, rootPath) == -1) { + DEBUG_PC("FAILED SP from %s via spurNode: %s to %s", iEp->device_uuid, spurNode->nodeId, eEp->device_uuid); + g_free(nextSpurNode); + g_free(spurNode); + g_free(gAux); + g_free(predecessors); + continue; + } + DEBUG_PC("SUCCESFUL SP from %s via spurNode: %s to %s", iEp->device_uuid, spurNode->nodeId, eEp->device_uuid); + // Create the node list from the predecessors + struct compRouteOutputItem_t* newKpath = create_path_item(); + build_path(newKpath, predecessors, s); + DEBUG_PC("new K (for k: %d) Path is built", k); + gint indexDest = get_map_index_by_nodeId(eEp->device_uuid, mapNodes); + struct map_t* dst_map = &mapNodes->map[indexDest]; + + memcpy(&newKpath->cost, &dst_map->distance, sizeof(gdouble)); + memcpy(&newKpath->availCap, &dst_map->avaiBandwidth, sizeof(dst_map->avaiBandwidth)); + memcpy(&newKpath->delay, &dst_map->latency, sizeof(mapNodes->map[indexDest].latency)); + DEBUG_PC("New PATH (@ kth: %d) ADDED to B[%d] - {Path Cost: %f, e2e latency: %f, bw: %f ", k, B->numPaths, newKpath->cost, newKpath->delay, newKpath->availCap); + // Add the computed kth SP to the heap B + duplicate_path(newKpath, &B->paths[B->numPaths]); + B->numPaths++; + DEBUG_PC("Number of B paths: %d", B->numPaths); + + g_free(newKpath); + g_free(nextSpurNode); + g_free(spurNode); + g_free(gAux); + g_free(predecessors); + } + + // If B is empty then stops + if (B->numPaths == 0) { + DEBUG_PC("B does not have any path ... the stops kth computation"); + break; + } + + // Sort the potential paths contained in B by cost and latency and available bandwidth + sort_path_set(B); + + // Add the lowest path into A[k] + DEBUG_PC("-------------------------------------------------------------"); + DEBUG_PC("To Add SP from B[0] to A[%d] --- Path Cost: %f, e2e Latency: %f", A->numPaths, B->paths[0].cost, B->paths[0].delay); + duplicate_path(&B->paths[0], &A->paths[A->numPaths]); + A->numPaths++; + DEBUG_PC("A Set size: %d", A->numPaths); + DEBUG_PC("-------------------------------------------------------------"); + + // Remove/pòp front element from the path set B (i.e. remove B[0]) + pop_front_path_set(B); + DEBUG_PC("B Set Size: %d", B->numPaths); + } + + // Copy the serviceId + copy_service_id(&path->serviceId, &s->serviceId); + // copy the service endpoints, in general, there will be 2 (point-to-point network connectivity services) + for (gint m = 0; m < s->num_service_endpoints_id; m++) { + struct service_endpoints_id_t* iEp = &(s->service_endpoints_id[m]); + struct service_endpoints_id_t* oEp = &(path->service_endpoints_id[m]); + copy_service_endpoint_id(oEp, iEp); + } + + for (gint ksp = 1; ksp < A->numPaths; ksp++){ + if (ksp >= MAX_KSP_VALUE) { + DEBUG_PC("Number Requested paths (%d) REACHED - STOP", ksp); + break; + } + gdouble feasibleRoute = check_computed_path_feasability(s, &A->paths[ksp]); + if (feasibleRoute == TRUE) { + DEBUG_PC("A[k-th%d] available: %f, pathCost: %f; latency: %f", ksp, A->paths[ksp].availCap, A->paths[ksp].cost, A->paths[ksp].delay); + struct compRouteOutputItem_t* pathaux = &A->paths[ksp]; + path->numPaths++; + struct path_t* targetedPath = &path->paths[path->numPaths - 1]; + duplicate_path_t(pathaux, targetedPath); + print_path_t(targetedPath); + remove_path_set(A); + remove_path_set(B); + return; + } + } + remove_path_set(A); + remove_path_set(B); + // No paths found --> Issue + DEBUG_PC("K-SP failed!!!"); + comp_route_connection_issue_handler(path, s); + + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_ksp.c + * @brief Iterates over the list of network connectivity service requests + * to compute their own paths fulfilling the constraints + * + * @param outputList + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void ksp_alg_execution_services(struct compRouteOutputList_t* outputList) { + g_assert(outputList); + g_assert(contextSet); + g_assert(serviceList); + + DEBUG_PC("----- Starting the KSP Computation ------"); + + // Iterate over the list of requested network connectivity services + for (gint i = 0; i < serviceList->numServiceList; i++) { + struct service_t* service = &(serviceList->services[i]); + + DEBUG_PC("Starting the Computation for ServiceId: %s [ContextId: %s]", service->serviceId.service_uuid, service->serviceId.contextId); + struct compRouteOutput_t* pathService = &(outputList->compRouteConnection[i]); + // check endpoints of the service are different (PE devices/nodes are different) + if (same_src_dst_pe_nodeid(service) == 0) { + DEBUG_PC("PEs are the same... no path computation"); + comp_route_connection_issue_handler(pathService, service); + outputList->numCompRouteConnList++; + continue; + } + // get the graph associated to the contextId in the contextSet, if no then error + struct graph_t* g = get_graph_by_contextId(contextSet, service->serviceId.contextId); + if (g == NULL) { + DEBUG_PC("The targeted contextId is NOT in the ContextSet ... then NO graph"); + comp_route_connection_issue_handler(pathService, service); + outputList->numCompRouteConnList++; + continue; + } + alg_comp(service, pathService, g); + outputList->numCompRouteConnList++; + + // for each network connectivity service, a single computed path (out of the KCSP) is retuned + // If path is found, then the selected resources must be pre-assigned into the context information + if (pathService->noPathIssue == NO_PATH_CONS_ISSUE) + { + continue; + } + struct path_t* path = &(pathService->paths[pathService->numPaths - 1]); + allocate_graph_resources(path, service, g); + allocate_graph_reverse_resources(path, service, g); + print_graph(g); + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_ksp.c + * @brief handles the path computation triggering k-cspf algorithm + * + * @param compRouteOutput + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint pathComp_ksp_alg(struct compRouteOutputList_t * routeConnList) +{ + g_assert(routeConnList); + + DEBUG_PC ("================================================================"); + DEBUG_PC ("=========================== KSP ========================="); + DEBUG_PC ("================================================================"); + // increase the number of Path Comp. Intents + numPathCompIntents++; + gint http_code = HTTP_CODE_OK; + + // timestamp t0 + struct timeval t0; + gettimeofday(&t0, NULL); + + // Allocate memory for the context + contextSet = create_contextSet(); + // Build up the contextSet (>= 1) + build_contextSet(contextSet); + print_contextSet(contextSet); +#if 1 + //Triggering the path computation for each specific network connectivity service + ksp_alg_execution_services (routeConnList); + + // -- timestamp t1 + struct timeval t1, delta; + gettimeofday(&t1, NULL); + delta.tv_sec = t1.tv_sec - t0.tv_sec; + delta.tv_usec = t1.tv_usec - t0.tv_usec; + delta = tv_adjust(delta); + + numSuccesPathComp++; + update_stats_ksp_path_comp(routeConnList, delta); + print_path_connection_list(routeConnList); +#endif + + g_free(contextSet); + return http_code; +} \ No newline at end of file diff --git a/src/pathcomp/backend/pathComp_ksp.h b/src/pathcomp/backend/pathComp_ksp.h new file mode 100644 index 0000000000000000000000000000000000000000..628bd6e83b06f6bedda13ee31fe052eff01d6809 --- /dev/null +++ b/src/pathcomp/backend/pathComp_ksp.h @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////////////////// +/** + * # Copyright 2022 Centre Tecnològic de Telecomunicacions de Catalunya (CTTC/CERCA) www.cttc.es + * + * 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. + + * Author: CTTC/CERCA PONS RU Ricardo Martínez (ricardo.martinez@cttc.es) + */ + +#ifndef _PATHCOMP_KSP_H +#define _PATHCOMP_KSP_H + + +#include <glib.h> +#include <glib/gstdio.h> +#include <glib-2.0/glib/gtypes.h> + +#define MAX_KSP_VALUE 3 + +// HTTP RETURN CODES +#define HTTP_CODE_OK 200 +#define HTTP_CODE_CREATED 201 +#define HTTP_CODE_BAD_REQUEST 400 +#define HTTP_CODE_UNAUTHORIZED 401 +#define HTTP_CODE_FORBIDDEN 403 +#define HTTP_CODE_NOT_FOUND 404 +#define HTTP_CODE_NOT_ACCEPTABLE 406 + + +// Prototype of external declaration of functions +gint pathComp_ksp_alg (struct compRouteOutputList_t *); + +#endif diff --git a/src/pathcomp/backend/pathComp_log.c b/src/pathcomp/backend/pathComp_log.c new file mode 100644 index 0000000000000000000000000000000000000000..5f66e5a1edc8538dffcb20d89cbe2028a08d64d0 --- /dev/null +++ b/src/pathcomp/backend/pathComp_log.c @@ -0,0 +1,189 @@ +//////////////////////////////////////////////////////////////////////////////////////// +/** + * # Copyright 2022 Centre Tecnològic de Telecomunicacions de Catalunya (CTTC/CERCA) www.cttc.es + * + * 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. + + * Author: CTTC/CERCA PONS RU Ricardo Martínez (ricardo.martinez@cttc.es) + */ + ///////////////////////////////////////////////////////////////////////////////////////// +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> +#include <glib.h> +#include <sys/time.h> +#include <fcntl.h> + +#include "pathComp_log.h" + + +//////////////////////////////////////////////////////////////////////////////////////// + /** + * @file pathComp_log.c + * @brief Create a new variable + * + * @param size + * + * @author Ricardo Martínez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +struct stream* stream_new(size_t size) +{ + /** check values */ + g_assert(size > 0); + + struct stream* stream = g_malloc0(sizeof(struct stream)); + if (stream == NULL) + { + DEBUG_PC("%s memory failed\n", __FUNCTION__); + exit(-1); + } + + stream->data = g_malloc0(size); + if (stream->data == NULL) + { + DEBUG_PC("%s memory failed\n", __FUNCTION__); + exit(-1); + } + stream->size = size; + + /** check values */ + g_assert(stream != NULL); + + return stream; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_log.c + * @brief removal of a stream variable + * + * @param stream + * + * @author Ricardo Martínez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void stream_free(struct stream* s) +{ + /** check values */ + g_assert(s != NULL); + + //DEBUG_PC("s: %p, s->data: %p", s, s->data); + /** free data */ + g_free(s->data); + g_free(s); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_log.c + * @brief reset the contents of the stream + * + * @param stream + * + * @author Ricardo Martínez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void stream_reset(struct stream* s) +{ + /** check values */ + g_assert(s != NULL); + g_assert(s->putp >= 0); + g_assert(s->endp >= 0); + g_assert(s->endp >= 0); + + /** reset */ + s->putp = 0; + s->endp = 0; + s->getp = 0; + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_log.c + * @brief Read over a TCP channel the contents + * + * @param channel + * @param ptr + * @nbytes + * + * @author Ricardo Martínez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +gint read_channel(GIOChannel* channel, guchar* ptr, gint nbytes) +{ + /** check values */ + g_assert(channel != NULL); + g_assert(ptr != NULL); + g_assert(nbytes >= 0); + + /** get the file descriptor */ + gint fd; + fd = g_io_channel_unix_get_fd(channel); + + gsize nread; + gint nleft; + GError* error = NULL; + GIOStatus status; + + nleft = nbytes; + + // Set blocking + int flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags &= ~O_NONBLOCK); + + while (nleft > 0) + { + status = g_io_channel_read_chars(channel, (void*)ptr, nleft, &nread, &error); + if (status != G_IO_STATUS_NORMAL) + { + //DEBUG_PC ("gio-test: ...from %d: G_IO_STATUS_%s\n", fd, + // (status == G_IO_STATUS_AGAIN ? "AGAIN" : + // (status == G_IO_STATUS_EOF ? "EOF" : + // (status == G_IO_STATUS_ERROR ? "ERROR" : "???")))); + return -1; + } + if (nread < 0) + { + return (nread); + } + else + { + if (nread == 0) + break; + } + + nleft -= nread; + ptr += nread; + } + + /** check values */ + g_assert(channel != NULL); + g_assert(ptr != NULL); + g_assert(nleft >= 0); + g_assert(nbytes >= 0); + + return nbytes - nleft; +} \ No newline at end of file diff --git a/src/pathcomp/backend/pathComp_log.h b/src/pathcomp/backend/pathComp_log.h new file mode 100644 index 0000000000000000000000000000000000000000..d9a14209cb2790fed45c93f0b134251f6a990b16 --- /dev/null +++ b/src/pathcomp/backend/pathComp_log.h @@ -0,0 +1,89 @@ +//////////////////////////////////////////////////////////////////////////////////////// +/** + * # Copyright 2022 Centre Tecnològic de Telecomunicacions de Catalunya (CTTC/CERCA) www.cttc.es + * + * 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. + + * Author: CTTC/CERCA PONS RU Ricardo Martínez (ricardo.martinez@cttc.es) + */ + //////////////////////////////////////////////////////////////////////////////////////// + +#ifndef _PATHCOMP_LOG_H +#define _PATHCOMP_LOG_H + +#include <glib.h> +#include <glib/gstdio.h> +#include <glib-2.0/glib/gtypes.h> + +#define MAXLENGTH 131072 + + /** Stream buffer. */ +struct stream +{ + struct stream *next; + + guchar* data; + + /** Put pointer. */ + gulong putp; + + /** Get pointer. */ + gulong getp; + + /** End of pointer. */ + gulong endp; + + /** Data size. */ + gulong size; +}; + +extern FILE* logfile; + +////////////////////////////////////////////////////// +// For debugging +////////////////////////////////////////////////////// +////////////////////////////////////////////////////// +// For debugging +////////////////////////////////////////////////////// +#define __SHORT_FILENAME__ \ + (strrchr(__FILE__,'/') \ + ? strrchr(__FILE__,'/')+1 \ + : __FILE__ \ + ) + +#define DEBUG_PC(format,...) \ +{ \ + if (logfile != NULL) \ + { \ + g_fprintf(logfile,"%s:%1.5d %30s "format"\n",\ + __SHORT_FILENAME__, \ + __LINE__, __FUNCTION__, ##__VA_ARGS__); \ + fflush(logfile); \ + } \ + else \ + { \ + g_fprintf(stdout,"%s:%1.5d %30s "format"\n", \ + __SHORT_FILENAME__, \ + __LINE__, __FUNCTION__, ##__VA_ARGS__); \ + fflush(stdout); \ + } \ +} + +//// Prototypes //////// +struct stream* stream_new(size_t); +void stream_free(struct stream*); +void stream_reset(struct stream*); + +gint read_channel(GIOChannel*, guchar*, gint); + +#endif \ No newline at end of file diff --git a/src/pathcomp/backend/pathComp_tools.c b/src/pathcomp/backend/pathComp_tools.c new file mode 100644 index 0000000000000000000000000000000000000000..1e584438461e9b99fdda9607ae3143ac5f48edf3 --- /dev/null +++ b/src/pathcomp/backend/pathComp_tools.c @@ -0,0 +1,2703 @@ +//////////////////////////////////////////////////////////////////////////////////////// +/** + * # Copyright 2022 Centre Tecnològic de Telecomunicacions de Catalunya (CTTC/CERCA) www.cttc.es + * + * 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. + + * Author: CTTC/CERCA PONS RU Ricardo MartÃnez (ricardo.martinez@cttc.es) + */ +///////////////////////////////////////////////////////////////////////////////////////// + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> +#include <glib.h> +#include <sys/time.h> +#include <ctype.h> +#include <strings.h> +#include <time.h> +#include <math.h> +#include <fcntl.h> +#include <uuid/uuid.h> +#include <errno.h> + +#include "pathComp_log.h" +#include "pathComp.h" +#include "pathComp_tools.h" + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Function for time processing + * + * @param a + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + //////////////////////////////////////////////////////////////////////////////////////// +struct timeval tv_adjust (struct timeval a) { + while (a.tv_usec >= 1000000) { + a.tv_usec -= 1000000; + a.tv_sec++; + } + + while (a.tv_usec < 0) { + a.tv_usec += 1000000; + a.tv_sec--; + } + return a; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief friendly function to copy safely strings + * + * @param dst + * @param src + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + //////////////////////////////////////////////////////////////////////////////////////// +void duplicate_string(gchar* dst, gchar* src) { + g_assert(dst); + g_assert(src); + strcpy(dst, src); + dst[strlen(dst)] = '\0'; + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Function used to print the computed the path + * + * @param path + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void print_path (struct compRouteOutputItem_t *p) { + g_assert(p); + + DEBUG_PC ("=========== COMPUTED PATH ======================="); + DEBUG_PC ("Path Avail. Bw: %f, E2E Path Latency: %f, Path Cost: %f", p->availCap, p->delay, p->cost); + for (gint k = 0; k < p->numRouteElements; k++) { + DEBUG_PC ("aNodeId: %s (%s) --> zNodeId: %s (%s)", p->routeElement[k].aNodeId.nodeId, p->routeElement[k].aEndPointId, + p->routeElement[k].zNodeId.nodeId, p->routeElement[k].zEndPointId); + DEBUG_PC("linkId: %s", p->routeElement[k].linkId); + DEBUG_PC("aTopologyId: %s", p->routeElement[k].aTopologyId); + DEBUG_PC("zTopologyId: %s", p->routeElement[k].zTopologyId); + } + DEBUG_PC ("=================================================================="); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Function used to print the output path formed by link Ids + * + * @param p + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void print_path_t(struct path_t* p) { + g_assert(p); + DEBUG_PC(" ============ COMPUTED OUTPUT PATH ================="); + DEBUG_PC("Path Avail Capacity: %f, Cost: %f, Latency: %f", p->path_capacity.value, + p->path_cost.cost_value, p->path_latency.fixed_latency); + DEBUG_PC("number of links of path %d", p->numPathLinks); + for (gint k = 0; k < p->numPathLinks; k++) { + DEBUG_PC("Link: %s", p->pathLinks[k].linkId); + for (gint l = 0; l < p->pathLinks[k].numLinkTopologies; l++) { + DEBUG_PC("end Link [%d] TopologyId: %s", l, p->pathLinks[k].linkTopologies[l].topologyId); + } + DEBUG_PC(" ContextId: %s", p->pathLinks[k].topologyId.contextId); + DEBUG_PC(" TopologyUUid: %s", p->pathLinks[k].topologyId.topology_uuid); + DEBUG_PC(" aDeviceId: %s", p->pathLinks[k].aDeviceId); + DEBUG_PC(" aEndpointId: %s", p->pathLinks[k].aEndPointId); + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Returns the char (36 bytes) format of a uuid + * + * @param uuid + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +gchar* get_uuid_char(uuid_t uuid) { + gchar* uuidChar = g_malloc0(16); // uuid has 36 chars + if (uuidChar == NULL) { + DEBUG_PC("Memory Allocation failure"); + exit(-1); + } + uuid_unparse(uuid, (char *)uuidChar); + return uuidChar; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Makes a copy of the service identifier (including the context) + * + * @param o + * @param i + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void copy_service_id(struct serviceId_t* o, struct serviceId_t* i) { + g_assert(o); + g_assert(i); + + memcpy(o->contextId, i->contextId, sizeof(i->contextId)); + memcpy(o->service_uuid, i->service_uuid, sizeof(i->service_uuid)); + + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Makes a copy of the service endpoint identifier (including the topology (contect and topology id), device and endpoint (port)) + * + * @param oEp + * @param iEp + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void copy_service_endpoint_id(struct service_endpoints_id_t* oEp, struct service_endpoints_id_t* iEp) { + g_assert(oEp); + g_assert(iEp); + + // copy topology information + memcpy(oEp->topology_id.contextId, iEp->topology_id.contextId, sizeof(iEp->topology_id.contextId)); + memcpy(oEp->topology_id.topology_uuid, iEp->topology_id.topology_uuid, sizeof(iEp->topology_id.topology_uuid)); + + // copy the endpoint + memcpy(oEp->device_uuid, iEp->device_uuid, sizeof(iEp->device_uuid)); + memcpy(oEp->endpoint_uuid, iEp->endpoint_uuid, sizeof(iEp->endpoint_uuid)); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief From the set of contexts, it is returned the graph associated to that contexct matching + * with the passed contextId + * + * @param Set + * @param contextId + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct graph_t* get_graph_by_contextId(struct contextSet_t* Set, gchar* contextId) { + g_assert(Set); + g_assert(contextId); + + // iterate over the set of context. Pick the one matching with contextId, and return the graph. + // If not found, return NULL + struct graph_t* g = NULL; + for (gint i = 0; i < Set->num_context_set; i++) { + struct context_t* context = &(Set->contextList[i]); + if (strcmp(context->contextId, contextId) == 0) { + g = &(context->g); + return g; + } + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Process the service constraint and maps them into the path constraints + * to be fulfilled + * + * @param path_constraints + * @param s + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct path_constraints_t * get_path_constraints(struct service_t* s) { + g_assert(s); + + struct path_constraints_t* path_constraints = g_malloc0(sizeof(struct path_constraints_t)); + if (path_constraints == NULL) { + DEBUG_PC("Memory Allocation Failure"); + exit(-1); + } + + char* eptr; + for (gint i = 0; i < s->num_service_constraints; i++) { + struct constraint_t* constraint = &(s->constraints[i]);; + if (strncmp((const char*)constraint->constraint_type, "bandwidth", 9) == 0) { + path_constraints->bwConstraint = (gdouble)(strtod((char*)constraint->constraint_value, &eptr)); + path_constraints->bw = TRUE; + //DEBUG_PC("Path Constraint Bw: %f", path_constraints->bwConstraint); + } + if (strncmp((const char*)constraint->constraint_type, "cost", 4) == 0) { + path_constraints->costConstraint = (gdouble)(strtod((char*)constraint->constraint_value, &eptr)); + path_constraints->cost = TRUE; + //DEBUG_PC("Path Constraint Cost: %f", path_constraints->costConstraint); + } + if (strncmp((const char*)constraint->constraint_type, "latency", 7) == 0) { + path_constraints->latencyConstraint = (gdouble)(strtod((char*)constraint->constraint_value, &eptr)); + path_constraints->latency = TRUE; + //DEBUG_PC("Path Constraint Latency: %f", path_constraints->latencyConstraint); + } + if (strncmp((const char*)constraint->constraint_type, "energy", 6) == 0) { + path_constraints->energyConstraint = (gdouble)(strtod((char*)constraint->constraint_value, &eptr)); + path_constraints->energy = TRUE; + //DEBUG_PC("Path Constraint Energy: %f", path_constraints->energyConstraint); + } + } + return path_constraints; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Creates the predecessors to keep the computed path + * + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +struct pred_t * create_predecessors () +{ + struct pred_t *predecessors = g_malloc0 (sizeof (struct pred_t)); + if (predecessors == NULL) + { + DEBUG_PC ("memory allocation failed\n"); + exit (-1); + } + return predecessors; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief create edge + * + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct edges_t* create_edge() +{ + struct edges_t* e = g_malloc0(sizeof(struct edges_t)); + if (e == NULL) + { + DEBUG_PC("Memory allocation failed\n"); + exit(-1); + } + return e; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Prints the list of the predecessors for a given computed Shortest Path + * + * @param p + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void print_predecessors (struct pred_t *p) +{ + g_assert (p); + DEBUG_PC ("Number of Predecessors: %d", p->numPredComp); + for (gint i = 0; i < p->numPredComp; i++) { + struct pred_comp_t *pComp = &(p->predComp[i]); + DEBUG_PC ("deviceId: %s", pComp->v.nodeId); + struct edges_t *e = &(pComp->e); + DEBUG_PC("Edge[#%d] (linkId): %s", i, e->linkId); + DEBUG_PC ("\t %s[%s] ===>", e->aNodeId.nodeId, e->aEndPointId); + DEBUG_PC("\t %s[%s]", e->zNodeId.nodeId, e->zEndPointId); + DEBUG_PC("\t aTopologyId: %s", e->aTopologyId); + DEBUG_PC("\t zTopologyId: %s", e->zTopologyId); + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Builds the list of predecessors for the request destination using the computed Shortest Path + * being stored in map + * + * @param p + * @param s + * @param map + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void build_predecessors (struct pred_t *p, struct service_t *s, struct map_nodes_t *map) +{ + g_assert (p); + g_assert (s); + g_assert (map); + + struct nodes_t *v = create_node(); + duplicate_string(v->nodeId, s->service_endpoints_id[1].device_uuid); + + struct edges_t *e = create_edge (); + get_edge_from_map_by_node (e, v, map); + + // Get u (being source of edge e) + struct nodes_t u; + duplicate_node_id (&e->aNodeId, &u); + + // Add to the predecessors list + struct pred_comp_t *pred = &(p->predComp[p->numPredComp]); + duplicate_node_id (&u, &pred->v); + struct edges_t *e1 = &(pred->e); + duplicate_edge (e1, e); + p->numPredComp++; + // Back-trace edges till reaching the srcPEId + struct nodes_t* srcNode = create_node(); + duplicate_string(srcNode->nodeId, s->service_endpoints_id[0].device_uuid); + + while (compare_node_id (&u, srcNode) != 0) { + duplicate_node_id (&u, v); + get_edge_from_map_by_node (e, v, map); + // Get the u (being source of edge e) + duplicate_node_id (&e->aNodeId, &u); + // Get the new predecessor + struct pred_comp_t *pred = &p->predComp[p->numPredComp]; + // Add to the predecessors list + duplicate_node_id (&u, &pred->v); + struct edges_t *e1 = &(pred->e); + duplicate_edge (e1, e); + p->numPredComp++; + } + print_predecessors (p); + g_free (e); + g_free(v); + g_free(srcNode); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief It creates a struct nodes_t + * + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +struct nodes_t * create_node () +{ + struct nodes_t *n = g_malloc0 (sizeof (struct nodes_t)); + if (n == NULL) { + DEBUG_PC ("memory allocation problem"); + exit (-1); + } + return n; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief It creates a routeElement_t + * + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +struct routeElement_t * create_routeElement () +{ + struct routeElement_t *rE = g_malloc0 (sizeof (struct routeElement_t)); + if (rE == NULL) + { + DEBUG_PC ("memory allocation problem"); + exit (-1); + } + return rE; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief copy node ids + * + * @param src + * @param dst + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void duplicate_node_id (struct nodes_t *src, struct nodes_t *dst) +{ + g_assert (src); + g_assert (dst); + + //DEBUG_PC ("Duplicate nodeId for %s", src->nodeId); + strcpy (dst->nodeId, src->nodeId); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief compares a pair of node Ids + * + * @param a + * @param b + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint compare_node_id (struct nodes_t *a, struct nodes_t *b) +{ + g_assert (a); + g_assert (b); + return (memcmp (&a->nodeId, b->nodeId, strlen (b->nodeId))); +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief duplicate two routeElement_t + * + * @param src + * @param dst + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void duplicate_routeElement (struct routeElement_t *src, struct routeElement_t *dst) +{ + g_assert (src); + g_assert (dst); + + duplicate_node_id (&(src->aNodeId), &(dst->aNodeId)); + duplicate_node_id (&(src->zNodeId), &(dst->zNodeId)); + duplicate_string(dst->aEndPointId, src->aEndPointId); + duplicate_string(dst->zEndPointId, src->zEndPointId); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief duplicate two edges + * + * @param e1 (destination) + * @param e2 (source) + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void duplicate_edge (struct edges_t *e1, struct edges_t *e2) { + g_assert (e1); + g_assert (e2); + + duplicate_node_id (&e2->aNodeId, &e1->aNodeId); + duplicate_node_id (&e2->zNodeId, &e1->zNodeId); + //DEBUG_PC ("e->aNodeId: %s ---> e->zNodeId: %s", e1->aNodeId.nodeId, e1->zNodeId.nodeId); + duplicate_string(e1->aEndPointId, e2->aEndPointId); + duplicate_string(e1->zEndPointId, e2->zEndPointId); + duplicate_string(e1->linkId, e2->linkId); + duplicate_string(e1->interDomain_localId, e2->interDomain_localId); + duplicate_string(e1->interDomain_remoteId, e2->interDomain_remoteId); + duplicate_string(e1->aTopologyId, e2->aTopologyId); + duplicate_string(e1->zTopologyId, e2->zTopologyId); + + e1->unit = e2->unit; + memcpy(&e1->totalCap, &e2->totalCap, sizeof(gdouble)); + memcpy(&e1->availCap, &e2->availCap, sizeof(gdouble)); + + memcpy (&e1->cost, &e2->cost, sizeof (gdouble)); + memcpy (&e1->delay, &e2->delay, sizeof (gdouble)); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Duplicate path + * + * @param a - original + * @param b - copy + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void duplicate_path (struct compRouteOutputItem_t *a, struct compRouteOutputItem_t *b) { + g_assert (a); + g_assert (b); + memcpy (&b->availCap, &a->availCap, sizeof (gdouble)); + b->numRouteElements = a->numRouteElements; + memcpy(&b->cost, &a->cost, sizeof(gdouble)); + memcpy (&b->delay, &a->delay, sizeof (gdouble)); + for (gint k = 0; k < a->numRouteElements; k++) { + //DEBUG_PC ("aNodeId: %s // zNodeId: %s", a->routeElement[k].aNodeId.nodeId, a->routeElement[k].zNodeId.nodeId); + // aNodeId duplication + struct nodes_t *n1 = &(a->routeElement[k].aNodeId); + struct nodes_t *n2 = &(b->routeElement[k].aNodeId); + duplicate_node_id (n1, n2); + + //zNodeId duplication + n1 = &(a->routeElement[k].zNodeId); + n2 = &(b->routeElement[k].zNodeId); + duplicate_node_id (n1, n2); + duplicate_string(b->routeElement[k].aEndPointId, a->routeElement[k].aEndPointId); + duplicate_string(b->routeElement[k].zEndPointId, a->routeElement[k].zEndPointId); + duplicate_string(b->routeElement[k].linkId, a->routeElement[k].linkId); + duplicate_string(b->routeElement[k].aTopologyId, a->routeElement[k].aTopologyId); + duplicate_string(b->routeElement[k].zTopologyId, a->routeElement[k].zTopologyId); + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Duplicate path from compRouteOutputItem_t to path_t + * + * @param a - original + * @param b - copy + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void duplicate_path_t(struct compRouteOutputItem_t* a, struct path_t* b) +{ + g_assert(a); + g_assert(b); + + memcpy(&b->path_capacity.value, &a->availCap, sizeof(gdouble)); + memcpy(&b->path_cost.cost_value, &a->cost, sizeof(gdouble)); + memcpy(&b->path_latency.fixed_latency, &a->delay, sizeof(gdouble)); + + b->numPathLinks = a->numRouteElements; + + for (gint k = 0; k < a->numRouteElements; k++) { + struct routeElement_t* rE = &(a->routeElement[k]); + struct pathLink_t* pL = &(b->pathLinks[k]); + + // copy the aDeviceId and aEndpointId, zDeviceId and zEndPointId + duplicate_string(pL->aDeviceId, rE->aNodeId.nodeId); + duplicate_string(pL->zDeviceId, rE->zNodeId.nodeId); + duplicate_string(pL->aEndPointId, rE->aEndPointId); + duplicate_string(pL->zEndPointId, rE->zEndPointId); + + duplicate_string(pL->topologyId.topology_uuid, rE->aTopologyId); + duplicate_string(pL->topologyId.contextId, rE->contextId); + + //copy the linkId + duplicate_string(pL->linkId, rE->linkId); + pL->numLinkTopologies++; + duplicate_string(pL->linkTopologies[pL->numLinkTopologies - 1].topologyId, rE->aTopologyId); + pL->numLinkTopologies++; + duplicate_string(pL->linkTopologies[pL->numLinkTopologies - 1].topologyId, rE->zTopologyId); + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Return the index into mapN related nodeId + * + * @param nodeId + * @para mapN + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint get_map_index_by_nodeId (gchar *nodeId, struct map_nodes_t * mapN) +{ + gint index = -1; + gint i = 0; + + for (i = 0; i < mapN->numMapNodes; i++) + { + //DEBUG_PC ("i: %d; current: %s // targeted: %s", i, mapN->map[i].verticeId.nodeId, nodeId); + if (memcmp (mapN->map[i].verticeId.nodeId, nodeId, strlen (nodeId)) == 0) + { + index = i; + //DEBUG_PC ("Index: %d", index); + return index; + } + } + //DEBUG_PC ("Index: %d", index); + return index; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Get the edge e enabling reaching the computed v in mapNodes + * + * @param e + * @param v + * @param mapN + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void get_edge_from_map_by_node (struct edges_t *e, struct nodes_t* v, struct map_nodes_t *mapN) { + + //DEBUG_PC ("Get the Edge into map from node v: %s", v.nodeId); + // Get the edge reaching the node v from mapNodes + gint map_vIndex = get_map_index_by_nodeId (v->nodeId, mapN); + + //DEBUG_PC ("aNodeId: %s --> zNodeId: %s", mapN->map[map_vIndex].predecessor.aNodeId.nodeId, mapN->map[map_vIndex].predecessor.zNodeId.nodeId); + + struct edges_t *te = &(mapN->map[map_vIndex].predecessor); + duplicate_edge (e, te); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Get the edge from the predecessors array for a given node n + * + * @param e + * @param n + * @param predecessors + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void get_edge_from_predecessors (struct edges_t *e, struct nodes_t* n, struct pred_t *predecessors) { + g_assert(predecessors); + + DEBUG_PC ("Get edge outgoing node %s from predecessors list", n->nodeId); + //print_predecessors (predecessors); + for (gint i = 0; i < predecessors->numPredComp; i++) { + struct pred_comp_t *pred = &(predecessors->predComp[i]); + if (compare_node_id (n, &pred->v) == 0) { + // Add to the predecessors list + struct edges_t *te = &(pred->e); + DEBUG_PC("add e (linkId): %s", te->linkId); + duplicate_edge (e, te); + return; + } + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Construct the path using the predecessors list + * + * @param path + * @param predecessors + * @param s + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void build_path (struct compRouteOutputItem_t *p, struct pred_t *predecessors, struct service_t *s) { + //DEBUG_PC ("\n"); + // Get the source device Id of the network connectivity service + struct nodes_t *v = create_node(); + duplicate_string(v->nodeId, s->service_endpoints_id[0].device_uuid); + + struct edges_t* e = create_edge(); + + // Get the edge for v in predecessors + get_edge_from_predecessors (e, v, predecessors); + // Get the target for e + struct nodes_t u; + duplicate_node_id (&e->zNodeId, &u); + //DEBUG_PC ("u: %s", u.nodeId); + struct path_constraints_t* pathCons = get_path_constraints(s); + + // Add route element to the path being constructed + gint k = 0; + duplicate_node_id (&e->aNodeId, &p->routeElement[k].aNodeId); + duplicate_node_id (&e->zNodeId, &p->routeElement[k].zNodeId); + duplicate_string(p->routeElement[k].aEndPointId, e->aEndPointId); + duplicate_string(p->routeElement[k].zEndPointId, e->zEndPointId); + duplicate_string(p->routeElement[k].linkId, e->linkId); + duplicate_string(p->routeElement[k].aTopologyId, e->aTopologyId); + duplicate_string(p->routeElement[k].zTopologyId, e->zTopologyId); + duplicate_string(p->routeElement[k].contextId, s->serviceId.contextId); + p->numRouteElements++; + + // Get the destination device Id of the network connectivity service + struct nodes_t* dst = create_node(); + duplicate_string(dst->nodeId, s->service_endpoints_id[1].device_uuid); + while (compare_node_id (&u, dst) != 0) + { + k++; + p->numRouteElements++; + // v = u + duplicate_node_id (&u, v); + get_edge_from_predecessors (e, v, predecessors); + // Get the target u + duplicate_node_id (&e->zNodeId, &u); + // Add route element to the path being constructed + duplicate_node_id (&e->aNodeId, &p->routeElement[k].aNodeId); + duplicate_node_id (&e->zNodeId, &p->routeElement[k].zNodeId); + duplicate_string(p->routeElement[k].aEndPointId, e->aEndPointId); + duplicate_string(p->routeElement[k].zEndPointId, e->zEndPointId); + duplicate_string(p->routeElement[k].linkId, e->linkId); + duplicate_string(p->routeElement[k].aTopologyId, e->aTopologyId); + duplicate_string(p->routeElement[k].zTopologyId, e->zTopologyId); + duplicate_string(p->routeElement[k].contextId, s->serviceId.contextId); + + // copy the contextId + //duplicate_string(p->routeElement[k].contextId, s->service_endpoints_id[0].topology_id.contextId); + } + g_free(e); g_free(v); g_free(pathCons); + //DEBUG_PC ("Path is constructed"); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Print the graph for DEBUG_PCging purposes + * + * @param g + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void print_graph (struct graph_t *g) +{ + DEBUG_PC ("================================================================"); + DEBUG_PC ("=========================== GRAPH =========================="); + DEBUG_PC ("================================================================"); + + DEBUG_PC("Graph Num Vertices: %d", g->numVertices); + + gint i = 0, j = 0, k = 0; + for (i = 0; i < g->numVertices; i++) + { + DEBUG_PC ("Head Vertice [%s]", g->vertices[i].verticeId.nodeId); + for (j = 0; j < g->vertices[i].numTargetedVertices; j++) + { + DEBUG_PC (" Tail Vertice: %s", g->vertices[i].targetedVertices[j].tVertice.nodeId); + for (k = 0; k < g->vertices[i].targetedVertices[j].numEdges; k++) + { + struct edges_t *e = &(g->vertices[i].targetedVertices[j].edges[k]); + DEBUG_PC ("%s(%s) --> %s(%s) [C: %f, Bw: %f b/s, Delay: %f ms]", e->aNodeId.nodeId, e->aEndPointId, e->zNodeId.nodeId, + e->zEndPointId, e->cost, e->availCap, e->delay); + } + } + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Look for a given edge into the graph + * + * @param verticeIndex + * @param targetedVerticeIndex + * @param e + * @param g + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint graph_edge_lookup (gint verticeIndex, gint targetedVerticeIndex, struct edges_t *e, struct graph_t *g) { + gint indexEdge = -1; + + for (gint j = 0; j < g->vertices[verticeIndex].targetedVertices[targetedVerticeIndex].numEdges; j++) { + struct edges_t *e2 = &(g->vertices[verticeIndex].targetedVertices[targetedVerticeIndex].edges[j]); + if ((compare_node_id (&e->aNodeId, &e2->aNodeId) == 0) && + (compare_node_id (&e->zNodeId, &e2->zNodeId) == 0) && + (strcmp (e->aEndPointId, e2->aEndPointId) == 0) && + (strcmp (e->zEndPointId, e2->zEndPointId) == 0) && + (strcmp(e->linkId, e2->linkId) == 0)) { + DEBUG_PC ("%s (%s) --> %s (%s) [linkId: %s] FOUND in the Graph at index: %d", e->aNodeId.nodeId, e->aEndPointId, e->zNodeId.nodeId, + e->zEndPointId, e->linkId, j); + indexEdge = j; + return indexEdge; + } + } + return indexEdge; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Look for a given vertice within the graph using the nodeId + * + * @param nodeId + * @param g + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint graph_vertice_lookup (gchar *nodeId, struct graph_t *g) +{ + gint index = -1; + //DEBUG_PC("Searching Node: %s", nodeId); + for (gint i = 0; i < g->numVertices; i++) { + //DEBUG_PC("Checked Graph Node: %s", g->vertices[i].verticeId.nodeId); + if (memcmp (g->vertices[i].verticeId.nodeId, nodeId, strlen (nodeId)) == 0) + { + index = i; + //DEBUG_PC ("%s is found in the graph vertice [%d]", nodeId, index); + break; + } + } + return (index); +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Check if a nodeId is already considered into the set of targeted vertices from a given vertice + * + * @param nodeId + * @param vIndex + * @param g + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint graph_targeted_vertice_lookup (gint vIndex, gchar *nodeId, struct graph_t *g) +{ + gint addedTargetedVerticeIndex = -1; + gint i = 0; + + if (g->vertices[vIndex].numTargetedVertices == 0) + { + return (addedTargetedVerticeIndex); + } + + for (i = 0; i < g->vertices[vIndex].numTargetedVertices; i++) + { + if (memcmp (g->vertices[vIndex].targetedVertices[i].tVertice.nodeId, nodeId, strlen (nodeId)) == 0) + { + DEBUG_PC ("Targeted %s reachable from %s", nodeId, g->vertices[vIndex].verticeId.nodeId); + addedTargetedVerticeIndex = i; + return (addedTargetedVerticeIndex); + } + } + // not found ... + return (addedTargetedVerticeIndex); +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Check if a nodeId is already considered into the set of targeted vertices from a given vertice, if not to be added + * + * @param nodeId + * @param vIndex + * @param g + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint graph_targeted_vertice_add (gint vIndex, gchar *nodeId, struct graph_t *g) +{ + gint addedTargetedVerticeIndex = -1; + gint i = 0; + + if (g->vertices[vIndex].numTargetedVertices == 0) + { + //DEBUG_PC ("targeted vertice %s being reachable from vertice %s", nodeId, g->vertices[vIndex].verticeId.nodeId); + addedTargetedVerticeIndex = 0; + return (addedTargetedVerticeIndex); + } + + for (i = 0; i < g->vertices[vIndex].numTargetedVertices; i++) + { + if (memcmp (g->vertices[vIndex].targetedVertices[i].tVertice.nodeId, nodeId, strlen (nodeId)) == 0) + { + //DEBUG_PC ("Targeted vertice %s is already considered in the reachable from vertice %s", nodeId, g->vertices[vIndex].verticeId.nodeId); + addedTargetedVerticeIndex = -1; + return (addedTargetedVerticeIndex); + } + } + // It is not found, next to be added at i position + addedTargetedVerticeIndex = i; + return (addedTargetedVerticeIndex); +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Remove edge from the graph + * + * @param g + * @param e + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +void remove_edge_from_graph (struct graph_t *g, struct edges_t *e) +{ + // Find the ingress vertice into the graph + DEBUG_PC ("Removing from Graph %s[%s]) ---> %s[%s] (linkId: %s)", e->aNodeId.nodeId, e->aEndPointId, e->zNodeId.nodeId, e->aEndPointId, e->linkId); + gint verticeIndex = -1; + verticeIndex = graph_vertice_lookup (e->aNodeId.nodeId, g); + if (verticeIndex == -1) { + DEBUG_PC ("Edge w/ %s is NOT in the Graph!!", e->aNodeId.nodeId); + return; + } + + // Find the targeted vertice from vertice Id + gint targetedVerticeIndex = -1; + targetedVerticeIndex = graph_targeted_vertice_lookup (verticeIndex, e->zNodeId.nodeId, g); + if (targetedVerticeIndex == -1) { + DEBUG_PC ("%s --> %s NOT in the Graph!!", e->aNodeId.nodeId, e->zNodeId.nodeId); + return; + } + + //DEBUG_PC ("%s --> %s found in the Graph", e->aNodeId.nodeId, e->zNodeId.nodeId); + + // Get the edge position + gint edgeIndex = -1; + edgeIndex = graph_edge_lookup (verticeIndex, targetedVerticeIndex, e, g); + if (edgeIndex == -1) { + DEBUG_PC ("%s --> %s NOT in the Graph!!", e->aNodeId.nodeId, e->zNodeId.nodeId); + return; + } + + //DEBUG_PC ("%s --> %s FOUND in Graph w/ edgeIndex: %d", e->aNodeId.nodeId, e->zNodeId.nodeId, edgeIndex); + + // Remove the edge + //DEBUG_PC ("Start Removing %s --> %s from Graph", e->aNodeId.nodeId, e->zNodeId.nodeId); + struct targetNodes_t *v = &(g->vertices[verticeIndex].targetedVertices[targetedVerticeIndex]); + for (gint j = edgeIndex; j < v->numEdges; j++) { + struct edges_t *e1 = &(v->edges[j]); + struct edges_t *e2 = &(v->edges[j+1]); + duplicate_edge (e1, e2); + } + v->numEdges --; + DEBUG_PC ("Number of Edges between %s and %s is %d", e->aNodeId.nodeId, e->zNodeId.nodeId, v->numEdges); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief create the pointer for keeping a set of the paths (struct compRouteOutput_t) + * + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +struct path_set_t * create_path_set () +{ + struct path_set_t * p = g_malloc0 (sizeof (struct path_set_t)); + if (p == NULL) + { + DEBUG_PC ("Memory allocation problem"); + exit (-1); + } + return p; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Remove the path set + * + * @param p + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2021 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void remove_path_set(struct path_set_t* p) +{ + g_assert(p); + g_free(p); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Create map of nodes to handle the path computation + * + * @param mapN + * @param g + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void build_map_node (struct map_nodes_t *mapN, struct graph_t *g) +{ + //DEBUG_PC ("Construction of the Map of Nodes"); + for (gint i = 0; i < g->numVertices; i++) + { + duplicate_node_id (&g->vertices[i].verticeId, &mapN->map[i].verticeId); + mapN->map[i].distance = INFINITY_COST; + mapN->map[i].avaiBandwidth = 0.0; + mapN->map[i].latency = INFINITY_COST; + mapN->numMapNodes++; + } + //DEBUG_PC ("mapNodes formed by %d Nodes", mapN->numMapNodes); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Allocate memory for path of struct compRouteOutputList_t * + * + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +struct compRouteOutputList_t * create_route_list () +{ + struct compRouteOutputList_t *p = g_malloc0 (sizeof (struct compRouteOutputList_t)); + if (p == NULL) + { + DEBUG_PC ("Memory Allocation Problem"); + exit (-1); + } + return p; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Allocate memory for path of struct compRouteOutputItem_t * + * + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +struct compRouteOutputItem_t *create_path_item () +{ + struct compRouteOutputItem_t *p = g_malloc0 (sizeof (struct compRouteOutputItem_t)); + if (p == NULL) { + DEBUG_PC ("Memory Allocation Problem"); + exit (-1); + } + return p; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Sort the set of paths according to the metric (1st criteria) and latency (2nd criteria) + * + * @params setP + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void sort_path_set(struct path_set_t* setP) { + g_assert(setP); + // Sort the paths contained in setP by shotest metric and latency + float epsilon = 0.0000001; + + for (gint i = 0; i < setP->numPaths; i++) { + for (gint j = 0; j < (setP->numPaths - i - 1); j++) { + struct compRouteOutputItem_t* path1 = &setP->paths[j]; + struct compRouteOutputItem_t* path2 = &setP->paths[j + 1]; + + struct compRouteOutputItem_t* pathTmp = create_path_item(); + // 1st Criteria (avail Bw) + if ((path2->availCap - path1->availCap > 0.0) && (fabs(path1->availCap - path2->availCap) > epsilon)) { + duplicate_path(path1, pathTmp); + duplicate_path(path2, path1); + duplicate_path(pathTmp, path2); + g_free(pathTmp); + continue; + } + else if ((path1->availCap - path2->availCap > 0.0) && (fabs(path1->availCap - path2->availCap) > epsilon)) { + g_free(pathTmp); + continue; + } + // likely the same available bw between path1 and path2 + else if (fabs(path1->availCap - path2->availCap) < epsilon) { + // 2nd criteria: sort path cost + if (path1->cost > path2->cost) { + duplicate_path(path1, pathTmp); + duplicate_path(path2, path1); + duplicate_path(pathTmp, path2); + g_free(pathTmp); + continue; + } + else if (path1->cost < path2->cost) { + g_free(pathTmp); + continue; + } + // 3rd criteria: same path cost, prioritize the one with lowest e2e latency + else if (path1->cost == path2->cost) { + if ((path2->delay - path1->delay > 0.0) && (fabs(path1->delay - path2->delay) > epsilon)) { + g_free(pathTmp); + continue; + } + else if ((path1->delay - path2->delay > 0.0) && (fabs(path1->delay - path2->delay) > epsilon)) { + duplicate_path(path1, pathTmp); + duplicate_path(path2, path1); + duplicate_path(pathTmp, path2); + g_free(pathTmp); + continue; + } + // Same bw, same cost and same latency, path1 and path2 are practically the same + else if (fabs(path1->delay - path2->delay) < epsilon) { + g_free(pathTmp); + continue; + } + } + } + } + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Remove first element from the path sets + * + * @params setP + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void pop_front_path_set (struct path_set_t *setP) { + for (gint j = 0; j < setP->numPaths - 1; j++) { + struct compRouteOutputItem_t *path1 = &setP->paths[j]; + struct compRouteOutputItem_t *path2 = &setP->paths[j+1]; + duplicate_path (path2, path1); + } + setP->numPaths--; + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Add routeElement to the back of the path + * + * @param rE + * @param p + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void add_routeElement_path_back (struct routeElement_t *rE, struct compRouteOutputItem_t *p) +{ + //DEBUG_PC ("p->numRouteElements: %d", p->numRouteElements); + p->numRouteElements++; + gint index = p->numRouteElements - 1; + + struct nodes_t *pn = &(p->routeElement[index].aNodeId); + struct nodes_t *rEn = &(rE->aNodeId); + + // duplicate aNodeId + duplicate_node_id (rEn, pn); + pn = &(p->routeElement[index].zNodeId); + rEn = &(rE->zNodeId); + duplicate_node_id (rEn, pn); + duplicate_string(p->routeElement[index].aEndPointId, rE->aEndPointId); + duplicate_string(p->routeElement[index].zEndPointId, rE->zEndPointId); + duplicate_string(p->routeElement[index].linkId, rE->linkId); + duplicate_string(p->routeElement[index].aTopologyId, rE->aTopologyId); + duplicate_string(p->routeElement[index].zTopologyId, rE->zTopologyId); + + return; +} + +/////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief This function compares ap and rootPath. If all the links are equal between both ap and rootPath till the sN, then the link from sN to next node + * ap is returned + * + * @params ap + * @params p + * @params sN + * @params e + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gboolean matching_path_rootPath (struct compRouteOutputItem_t *ap, struct compRouteOutputItem_t *rootPath, struct nodes_t *sN, struct edges_t *e) { + gint j = 0; + gboolean ret = FALSE; + while ((j < ap->numRouteElements) && (j < rootPath->numRouteElements)) { + if ((memcmp (ap->routeElement[j].aNodeId.nodeId, rootPath->routeElement[j].aNodeId.nodeId, sizeof (ap->routeElement[j].aNodeId.nodeId)) == 0) && + //(memcmp (ap->routeElement[j].zNodeId.nodeId, rootPath->routeElement[j].zNodeId.nodeId, sizeof (ap->routeElement[j].zNodeId.nodeId)) != 0) && + (memcmp (sN->nodeId, rootPath->routeElement[j].aNodeId.nodeId, sizeof (ap->routeElement[j].aNodeId.nodeId)) == 0)) { + duplicate_node_id (&ap->routeElement[j].aNodeId, &e->aNodeId); + duplicate_node_id (&ap->routeElement[j].zNodeId, &e->zNodeId); + duplicate_string(e->aEndPointId, ap->routeElement[j].aEndPointId); + duplicate_string(e->zEndPointId, ap->routeElement[j].zEndPointId); + duplicate_string(e->linkId, ap->routeElement[j].linkId); + return TRUE; + } + if ((memcmp (ap->routeElement[j].aNodeId.nodeId, rootPath->routeElement[j].aNodeId.nodeId, sizeof (ap->routeElement[j].aNodeId.nodeId)) == 0) && + (memcmp (ap->routeElement[j].zNodeId.nodeId, rootPath->routeElement[j].zNodeId.nodeId, sizeof (ap->routeElement[j].zNodeId.nodeId)) == 0)) { + j++; + continue; + } + + if ((memcmp (ap->routeElement[j].aNodeId.nodeId, rootPath->routeElement[j].aNodeId.nodeId, sizeof (ap->routeElement[j].aNodeId.nodeId)) != 0) || + (memcmp (ap->routeElement[j].zNodeId.nodeId, rootPath->routeElement[j].zNodeId.nodeId, sizeof (ap->routeElement[j].zNodeId.nodeId)) != 0)) { + //DEBUG_PC ("ap and rootPath not in the same path"); + return ret; + } + } + return ret; +} + +/////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief This function is used to modify the graph to be used for running the subsequent SP computations acording to the YEN algorithm principles + * + * @params g + * @params A + * @params rootPath + * @params spurNode + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void modify_targeted_graph (struct graph_t *g, struct path_set_t *A, struct compRouteOutputItem_t * rootPath, struct nodes_t * spurNode) { + //DEBUG_PC ("Modify the Targeted graph according to the Yen algorithm principles"); + for (gint j = 0; j < A->numPaths; j++) + { + struct compRouteOutputItem_t *ap = &A->paths[j]; + struct edges_t *e = create_edge (); + gboolean ret = FALSE; + ret = matching_path_rootPath (ap, rootPath, spurNode, e); + if (ret == TRUE) { + //DEBUG_PC ("Removal %s [%u]--> %s [%u] from the graph", e->aNodeId.nodeId, e->aLinkId, e->zNodeId.nodeId, e->zLinkId); + remove_edge_from_graph (g, e); + //DEBUG_PC ("Print Resulting Graph"); + //print_graph (g); + g_free (e); + } + if (ret == FALSE) + { + g_free (e); + continue; + } + } + return; +} + +/////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Supporting fucntion to Check if a nodeId is already in the items of a given GList + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint find_nodeId (gconstpointer data, gconstpointer userdata) +{ + /** check values */ + g_assert(data != NULL); + g_assert(userdata != NULL); + + struct nodeItem_t *SNodeId = (struct nodeItem_t *)data; + guchar * nodeId = (guchar *)userdata; + + //DEBUG_PC ("SNodeId (%s) nodeId (%s)", SNodeId->node.nodeId, nodeId); + + if (!memcmp(SNodeId->node.nodeId, nodeId, strlen (SNodeId->node.nodeId))) + { + return (0); + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Explores the link between u and v + * + * @param u + * @param v + * @param g + * @param s + * @param S + * @param Q + * @param mapNodes + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint check_link (struct nodeItem_t *u, gint indexGraphU, gint indexGraphV, struct graph_t *g, + struct service_t *s, GList **S, GList **Q, struct map_nodes_t *mapNodes) { + g_assert(g); + g_assert(s); + g_assert(mapNodes); + + struct targetNodes_t *v = &(g->vertices[indexGraphU].targetedVertices[indexGraphV]); + DEBUG_PC("Explored link (u ===> v):"); + DEBUG_PC("u: %s ====>", u->node.nodeId); + DEBUG_PC("====> v: %s", v->tVertice.nodeId); + + // v already explored in S? then, discard it + GList *found = g_list_find_custom (*S, v->tVertice.nodeId, find_nodeId); + if (found != NULL) { + DEBUG_PC ("v (%s) in S, discard to explore it!", v->tVertice.nodeId); + return 0; + } + + // Get the set of constraints imposed by the service + struct path_constraints_t* path_constraints = get_path_constraints(s); + gdouble distance_through_u = INFINITY_COST; + gdouble latency_through_u = INFINITY_COST; + gint i = 0; + + // Check bandwidth requirement is fulfillied on edge u --> v + gint foundAvailBw = 0; + gdouble edgeAvailBw = 0.0; + for (i = 0; i < v->numEdges; i++) { + struct edges_t *e = &(v->edges[i]); + memcpy (&edgeAvailBw, &(e->availCap), sizeof (gdouble)); + DEBUG_PC("edge:u ===> v"); + DEBUG_PC ("%s[%s] ===>", u->node.nodeId, e->aEndPointId); + DEBUG_PC("====> %s[%s]", v->tVertice.nodeId, e->zEndPointId); + DEBUG_PC("edge available bw: %f", edgeAvailBw); + + // if network service constraint specifies "bandwidth" needs (assuming coherent units) + if (path_constraints->bw == TRUE) { + if (edgeAvailBw < path_constraints->bwConstraint) { + continue; + } + else { + foundAvailBw = 1; + break; + } + } + } + // if bw path constraint is specified but not sastified ... discard the edge + if ((path_constraints->bw == TRUE) && (foundAvailBw == 0)) + { + DEBUG_PC ("AvailBw: %f < path_constraint: %f -- Discard Edge", edgeAvailBw, path_constraints->bwConstraint); + g_free(path_constraints); + return 0; + } + + gint indexEdge = i; // get the index for the explored edge + // Update distance, latency and availBw through u to reach v + gint map_uIndex = get_map_index_by_nodeId (u->node.nodeId, mapNodes); + struct map_t *u_map = &mapNodes->map[map_uIndex]; + distance_through_u = u_map->distance + v->edges[indexEdge].cost; + latency_through_u = u_map->latency + v->edges[indexEdge].delay; + gdouble availBw_through_u = 0.0; + + // ingress endpoint (u) is the src of the request + if (strcmp (u->node.nodeId, s->service_endpoints_id[0].device_uuid) == 0) { + //DEBUG_PC ("AvailBw %f on %s --> %s", edgeAvailBw, u->node.nodeId, v->tVertice.nodeId); + memcpy (&availBw_through_u, &edgeAvailBw, sizeof (gdouble)); + } + else { + // Get the minimum available bandwidth between the src-->u and the new added edge u-->v + //DEBUG_PC ("Current AvailBw: %f from src to %s", u_map->avaiBandwidth, u->node.nodeId); + //DEBUG_PC ("AvailBw: %f %s --> %s", edgeAvailBw, u->node.nodeId, v->tVertice.nodeId); + if (u_map->avaiBandwidth <= edgeAvailBw) { + memcpy (&availBw_through_u, &u_map->avaiBandwidth, sizeof (gdouble)); + } + else { + memcpy (&availBw_through_u, &edgeAvailBw, sizeof (gdouble)); + } + } + // Relax the link according to the pathCost and latency + gint map_vIndex = get_map_index_by_nodeId (v->tVertice.nodeId, mapNodes); + struct map_t *v_map = &mapNodes->map[map_vIndex]; + // If cost dist (u, v) > dist (src, v) relax the link + if (distance_through_u > v_map->distance) { + //DEBUG_PC ("dist(src, u) + dist(u, v): %f > dist (src, v): %f --> Discard Link", distance_through_u, v_map->distance); + return 0; + } + // If dist (src, u) + dist (u, v) = current dist(src, v), then use the latency as discarding criteria + if ((distance_through_u == v_map->distance) && (latency_through_u > v_map->latency)) { + //DEBUG_PC ("dist(src, u) + dist(u,v) = current dist(src, v), but latency (src,u) + latency (u, v) > current latency (src, v)"); + return 0; + } + // If dist (src, u) + dist (u,v) == current dist(src, v) AND latency (src, u) + latency (u, v) == current latency (src, v), the available bandwidth is the criteria + if ((distance_through_u == v_map->distance) && (latency_through_u == v_map->latency) && (availBw_through_u < v_map->avaiBandwidth)) { + return 0; + } + DEBUG_PC ("%s --> %s Relaxed", u->node.nodeId, v->tVertice.nodeId); + DEBUG_PC ("\t AvailBw: %f Mb/s, Cost: %f, Latency: %f ms", availBw_through_u, distance_through_u, latency_through_u); + + // Update Q list -- + struct nodeItem_t *nodeItem = g_malloc0 (sizeof (struct nodeItem_t)); + if (nodeItem == NULL) { + DEBUG_PC ("memory allocation failed\n"); + exit (-1); + } + nodeItem->distance = distance_through_u; + memcpy(&nodeItem->distance, &distance_through_u, sizeof(gdouble)); + memcpy(&nodeItem->latency, &latency_through_u, sizeof(gdouble)); + duplicate_node_id (&v->tVertice, &nodeItem->node); + // add node to the Q list + *Q = g_list_insert_sorted (*Q, nodeItem, sort_by_distance); + //DEBUG_PC ("%s ADDED to Q (length: %d)", nodeItem->node.nodeId, g_list_length(*Q)); + + // Update the mapNodes for the specific reached tv + v_map->distance = distance_through_u; + memcpy(&v_map->distance, &distance_through_u, sizeof(gdouble)); + memcpy (&v_map->avaiBandwidth, &availBw_through_u, sizeof (gdouble)); + memcpy (&v_map->latency, &latency_through_u, sizeof (gdouble)); + // Duplicate the predecessor edge into the mapNodes + struct edges_t *e1 = &(v_map->predecessor); + struct edges_t *e2 = &(v->edges[indexEdge]); + duplicate_edge (e1, e2); + DEBUG_PC ("u->v Edge: %s(%s) --> %s(%s)", e2->aNodeId.nodeId, e2->aEndPointId, e2->zNodeId.nodeId, e2->zEndPointId); + DEBUG_PC("v-pred aTopology: %s", e2->aTopologyId); + DEBUG_PC("v-pred zTopology: %s", e2->zTopologyId); + + // Check whether v is dstPEId + //DEBUG_PC ("Targeted dstPEId: %s", req->dstPEId.nodeId); + //DEBUG_PC ("nodeId added to the map: %s", v_map->verticeId.nodeId); + //DEBUG_PC ("Q Length: %d", g_list_length(*Q)); + g_free(path_constraints); + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Check the feasability of a path wrt the constraints imposed by the request in terms of latency + * + * @param s + * @param p + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gboolean check_computed_path_feasability (struct service_t *s, struct compRouteOutputItem_t* p) { + float epsilon = 0.0000001; + struct path_constraints_t* pathCons = get_path_constraints(s); + gboolean ret = TRUE; + + if (pathCons->latency == TRUE) { + if ((pathCons->latencyConstraint - p->delay > 0.0) || (fabs(pathCons->latencyConstraint - p->delay) < epsilon)) { + DEBUG_PC("Computed Path (latency: %f) is feasible wrt Connection Demand: %f", p->delay, pathCons->latencyConstraint); + } + else { + DEBUG_PC("Computed Path (latency: %f) is NOT feasible wrt Connection Demand: %f", p->delay, pathCons->latencyConstraint); + g_free(pathCons); + return FALSE; + } + } + // Other constraints... + + g_free(pathCons); + return ret; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Sorting the GList Q items by distance + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint sort_by_distance (gconstpointer a, gconstpointer b) +{ + //DEBUG_PC ("sort by distance a and b"); + g_assert(a != NULL); + g_assert(b != NULL); + + //DEBUG_PC ("sort by distance a and b"); + struct nodeItem_t *node1 = (struct nodeItem_t *)a; + struct nodeItem_t *node2 = (struct nodeItem_t *)b; + g_assert (node1); + g_assert (node2); + + //DEBUG_PC ("a->distance %u; b->distance %u", node1->distance, node2->distance); + //DEBUG_PC("a->latency: %f; b->latency: %f", node1->latency, node2->latency); + //1st criteria, sorting by lowest distance + if (node1->distance > node2->distance) + return 1; + else if (node1->distance < node2->distance) + return 0; + if (node1->distance == node2->distance) + { + if (node1->latency > node2->latency) + return 1; + else if (node1->latency <= node2->latency) + return 0; + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Allocate memory for graph + * + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +struct graph_t * create_graph () { + struct graph_t * g = g_malloc0 (sizeof (struct graph_t)); + if (g == NULL) + { + DEBUG_PC ("Memory Allocation Problem"); + exit (-1); + } + return g; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Allocate memory for mapNodes + * + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +struct map_nodes_t * create_map_node () { + struct map_nodes_t * mN = g_malloc0 (sizeof (struct map_nodes_t)); + if (mN == NULL) + { + DEBUG_PC ("Memory allocation failed"); + exit (-1); + } + return mN; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Look up for the service in the servieList bound to a serviceUUID + * + * @params serviceUUID + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// + +struct service_t* get_service_for_computed_path(gchar* serviceUUID) { + for (gint i = 0; i < serviceList->numServiceList; i++) { + struct service_t* s = &(serviceList->services[i]); + if (strcmp(s->serviceId.service_uuid, serviceUUID) == 0) + return s; + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Allocate memory for struct deviceList_t + * + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct deviceList_t* create_device_list() +{ + struct deviceList_t* dList = g_malloc0(sizeof(struct deviceList_t)); + if (dList == NULL) + { + DEBUG_PC("Memory Allocation Failure"); + exit(-1); + } + return dList; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Allocate memory for struct linkList_t + * + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct linkList_t* create_link_list() { + struct linkList_t* lList = g_malloc0(sizeof(struct linkList_t)); + if (lList == NULL) + { + DEBUG_PC("Memory Allocation Failure"); + exit(-1); + } + return lList; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Allocate memory for struct serviceList_t + * + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct serviceList_t* create_service_list() { + struct serviceList_t* sList = g_malloc0(sizeof(struct serviceList_t)); + if (sList == NULL) + { + DEBUG_PC("Memory Allocation Failure"); + exit(-1); + } + return sList; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Friendly function to log the service type + * + * @param type + * + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void print_service_type(guint type) { + switch (type) { + case SERVICE_TYPE_UNKNOWN: + DEBUG_PC("Service Type UNKNOWN"); + break; + case SERVICE_TYPE_L3NM: + DEBUG_PC("Service Type L3NM"); + break; + case SERVICE_TYPE_L2NM: + DEBUG_PC("Service Type L2NM"); + break; + case SERVICE_TYPE_TAPI: + DEBUG_PC("Service Type L2NM"); + break; + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Friendly function to log the port direction + * + * @param direction + * + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void print_link_port_direction(guint direction) +{ + switch (direction) { + case LINK_PORT_DIRECTION_BIDIRECTIONAL: + //DEBUG_PC("Bidirectional Port Direction"); + break; + case LINK_PORT_DIRECTION_INPUT: + //DEBUG_PC("Input Port Direction"); + break; + case LINK_PORT_DIRECTION_OUTPUT: + //DEBUG_PC("Output Port Direction"); + break; + case LINK_PORT_DIRECTION_UNKNOWN: + //DEBUG_PC("Unknown Port Direction"); + break; + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Friendly function to log the port termination direction + * + * @param direction + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void print_termination_direction(guint direction) +{ + switch (direction) { + case TERMINATION_DIRECTION_BIDIRECTIONAL: + //DEBUG_PC("Bidirectional Termination Direction"); + break; + case TERMINATION_DIRECTION_SINK: + //DEBUG_PC("Input Termination Direction"); + break; + case TERMINATION_DIRECTION_SOURCE: + //DEBUG_PC("Output Termination Direction"); + break; + case TERMINATION_DIRECTION_UNKNOWN: + //DEBUG_PC("Unknown Termination Direction"); + break; + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Friendly function to log the termination state + * + * @param state + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void print_termination_state(guint state) +{ + switch (state) { + case TERMINATION_STATE_CAN_NEVER_TERMINATE: + //DEBUG_PC("Can never Terminate"); + break; + case TERMINATION_STATE_NOT_TERMINATED: + DEBUG_PC("Not terminated"); + break; + case TERMINATION_STATE_TERMINATED_SERVER_TO_CLIENT_FLOW: + DEBUG_PC("Terminated server to client flow"); + break; + case TERMINATION_STATE_TERMINATED_CLIENT_TO_SERVER_FLOW: + DEBUG_PC("Terminated client to server flow"); + break; + case TERMINATION_STATE_TERMINATED_BIDIRECTIONAL: + //DEBUG_PC("Terminated bidirectional"); + break; + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Friendly function to log the capacity unit + * + * @param unit + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void print_capacity_unit(guint unit) { + + switch (unit) { + case CAPACITY_UNIT_TB: + DEBUG_PC("Unit in TB"); + break; + case CAPACITY_UNIT_TBPS: + DEBUG_PC("Unit in TB/s"); + break; + case CAPACITY_UNIT_GB: + DEBUG_PC("Unit in GB"); + break; + case CAPACITY_UNIT_GBPS: + DEBUG_PC("Unit in GB/s"); + break; + case CAPACITY_UNIT_MB: + DEBUG_PC("Unit in MB"); + break; + case CAPACITY_UNIT_MBPS: + //DEBUG_PC("Unit in MB/s"); + break; + case CAPACITY_UNIT_KB: + DEBUG_PC("Unit in KB"); + break; + case CAPACITY_UNIT_KBPS: + DEBUG_PC("Unit in KB/s"); + break; + case CAPACITY_UNIT_GHZ: + DEBUG_PC("Unit in GHz"); + break; + case CAPACITY_UNIT_MHZ: + DEBUG_PC("Unit in MHz"); + break; + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Friendly function to log the link forwarding direction + * + * @param linkFwDir + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void print_link_forwarding_direction(guint linkFwDir) { + switch (linkFwDir) { + case LINK_FORWARDING_DIRECTION_BIDIRECTIONAL: + DEBUG_PC("BIDIRECTIONAL LINK FORWARDING DIRECTION"); + break; + case LINK_FORWARDING_DIRECTION_UNIDIRECTIONAL: + //DEBUG_PC("UNIDIRECTIONAL LINK FORWARDING DIRECTION"); + break; + case LINK_FORWARDING_DIRECTION_UNKNOWN: + DEBUG_PC("UNKNOWN LINK FORWARDING DIRECTION"); + break; + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Allocate memory for the contextSet + * + * @param + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct contextSet_t* create_contextSet() { + struct contextSet_t* c = g_malloc0(sizeof(struct contextSet_t)); + if (c == NULL) { + DEBUG_PC("Memory Allocation Failure"); + exit(-1); + } + return c; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Search a specific contextUuid element into the contextSet + * + * @param contextUuid + * @param set + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct context_t* find_contextId_in_set(gchar* contextUuid, struct contextSet_t* set) { + + g_assert(set); + //DEBUG_PC("Checking if contextId: %s in in the ContextList??", contextUuid); + + for (gint i = 0; i < set->num_context_set; i++) { + struct context_t* c = &(set->contextList[i]); + //DEBUG_PC("Context Item [%d] Id: %s", i, c->contextId); + if (strcmp(contextUuid, c->contextId) == 0) { + //DEBUG_PC("contextId: %s is FOUND in the ContextSet_List", contextUuid); + return c; + } + } + //DEBUG_PC("contextId: %s NOT FOUND in the ContextSet_List", contextUuid); + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Add a specific context uuid into the context set + * + * @param contextUuid + * @param set + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct context_t* add_contextId_in_set(gchar *contextUuid, struct contextSet_t *set) { + + set->num_context_set++; + struct context_t* c = &(set->contextList[set->num_context_set - 1]); + duplicate_string(c->contextId, contextUuid); + return c; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Find a vertex in a specific graph + * + * @param contextUuid + * @param set + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct vertices_t* find_vertex_in_graph_context(struct graph_t *g, gchar* deviceId) { + + for (gint i = 0; i < g->numVertices; i++) + { + struct vertices_t* v = &(g->vertices[i]); + if (strcmp(v->verticeId.nodeId, deviceId) == 0) { + return v; + } + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Adding a deviceId into a graph + * + * @param g + * @param deviceId + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct vertices_t* add_vertex_in_graph(struct graph_t* g, gchar* deviceId) { + g->numVertices++; + struct vertices_t* v = &(g->vertices[g->numVertices - 1]); + duplicate_string(v->verticeId.nodeId, deviceId); + return v; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Construct the graphs (vertices and edges) bound to every individual context + * + * @param cSet + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void build_contextSet_deviceList(struct contextSet_t* cSet) { + // Check every device their endpoints + for (gint i = 0; i < deviceList->numDevices; i++) { + struct device_t* d = &(deviceList->devices[i]); + //DEBUG_PC("Exploring DeviceId: %s", d->deviceId); + + // Check the associated endPoints + for (gint j = 0; j < d->numEndPoints; j++) { + struct endPoint_t* eP = &(d->endPoints[j]); + // Get endPointId (topology, context, device Id and endpoint uuid) + struct endPointId_t* ePid = &(eP->endPointId); //end point id + //DEBUG_PC(" EndPointId: %s || Type: %s", eP->endPointId.endpoint_uuid, d->deviceType); + //DEBUG_PC(" TopologyId: %s || ContextId: %s", eP->endPointId.topology_id.topology_uuid, eP->endPointId.topology_id.contextId); + + // Add contextId in ContextSet and the deviceId (+endpoint) into the vertex set + struct context_t *c = find_contextId_in_set(eP->endPointId.topology_id.contextId, cSet); + if (c == NULL) { + //DEBUG_PC(" contextUuid: %s MUST BE ADDED to ContextSet", eP->endPointId.topology_id.contextId); + c = add_contextId_in_set(eP->endPointId.topology_id.contextId, cSet); + } + // Check if the deviceId and endPointUuid are already considered in the graph of the context c + struct vertices_t* v = find_vertex_in_graph_context(&c->g, d->deviceId); + if (v == NULL) { + //DEBUG_PC(" deviceId: %s MUST BE ADDED to the Context Graph", d->deviceId); + v = add_vertex_in_graph(&c->g, d->deviceId); + } + } + } + //print_contextSet(cSet); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Determine whether a deviceId is in the targetNode list of a specific vertex v + * + * @param v + * @param deviceId + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct targetNodes_t* find_targeted_vertex_in_graph_context(struct vertices_t* v, gchar *deviceId) { + for (gint k = 0; k < v->numTargetedVertices; k++) { + struct targetNodes_t* w = &(v->targetedVertices[k]); + if (strcmp(w->tVertice.nodeId, deviceId) == 0) { + return w; + } + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Add a deviceId a targetNode of a specific vertex v + * + * @param v + * @param deviceId + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct targetNodes_t* add_targeted_vertex_in_graph_context(struct vertices_t* v, gchar* bDeviceId) { + v->numTargetedVertices++; + struct targetNodes_t* w = &(v->targetedVertices[v->numTargetedVertices - 1]); + duplicate_string(w->tVertice.nodeId, bDeviceId); + return w; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Returns the structure of a device endpoint bound to a specific deviceId and endPointId + * + * @param devId + * @param endPointUuid + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct endPoint_t* find_device_tied_endpoint(gchar* devId, gchar* endPointUuid) { + //DEBUG_PC("devId: %s ePId: %s", devId, endPointUuid); + for (gint i = 0; i < deviceList->numDevices; i++) { + struct device_t* d = &(deviceList->devices[i]); + if (strcmp(d->deviceId, devId) != 0) { + continue; + } + // Iterate over the endpoints tied to the deviceId + for (gint j = 0; j < d->numEndPoints; j++) { + struct endPoint_t* eP = &(d->endPoints[j]); + //DEBUG_PC("looked endPointId: %s", eP->endPointId.endpoint_uuid); + if (strcmp(eP->endPointId.endpoint_uuid, endPointUuid) == 0) { + return eP; + } + } + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Adding the edge/linnk in the targetedNodes w list + * + * @param w + * @param l + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void add_edge_in_targetedVertice_set(struct targetNodes_t* w, struct link_t* l) { + //DEBUG_PC("\t targetedVertex: %s", w->tVertice.nodeId); + w->numEdges++; + struct edges_t* e = &(w->edges[w->numEdges - 1]); + // Copy the link Id UUID + duplicate_string(e->linkId, l->linkId); + + // copy the deviceId and endpointsIds (A --> Z) + struct link_endpointId_t* aEndpointId = &(l->linkEndPointId[0]); + duplicate_string(e->aNodeId.nodeId, aEndpointId->deviceId); + duplicate_string(e->aEndPointId, aEndpointId->endPointId); + duplicate_string(e->aTopologyId, aEndpointId->topology_id.topology_uuid); + + struct link_endpointId_t* zEndpointId = &(l->linkEndPointId[1]); + duplicate_string(e->zNodeId.nodeId, zEndpointId->deviceId); + duplicate_string(e->zEndPointId, zEndpointId->endPointId); + duplicate_string(e->zTopologyId, zEndpointId->topology_id.topology_uuid); + + // The potential and available capacity is indeed retrieved using aEndpointId in the deviceList + struct endPoint_t* eP = find_device_tied_endpoint(aEndpointId->deviceId, aEndpointId->endPointId); + if (eP == NULL) { + DEBUG_PC("devId: %s endPointUuid: %s NOT in Device List!!--- Weird", aEndpointId->deviceId, aEndpointId->endPointId); + exit(-1); + } + //Potential(total) and available capacity + e->unit = eP->potential_capacity.unit; + memcpy(&e->totalCap, &eP->potential_capacity.value, sizeof(gdouble)); + memcpy(&e->availCap, &eP->available_capacity.value, sizeof(gdouble)); + + // Copy interdomain local/remote Ids + memcpy(e->interDomain_localId, eP->inter_domain_plug_in.inter_domain_plug_in_local_id, + strlen(eP->inter_domain_plug_in.inter_domain_plug_in_local_id)); + memcpy(e->interDomain_remoteId, eP->inter_domain_plug_in.inter_domain_plug_in_remote_id, + strlen(eP->inter_domain_plug_in.inter_domain_plug_in_remote_id)); + + // cost value + memcpy(&e->cost, &l->cost_characteristics.cost_value, sizeof(gdouble)); + + // latency + memcpy(&e->delay, &l->latency_characteristics.fixed_latency, sizeof(gdouble)); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Searching a specific edge/link by the linkId(UUID) + * + * @param w + * @param l + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct edges_t* find_edge_in_targetedVertice_set(struct targetNodes_t* w, struct link_t* l) { + + for (gint i = 0; i < w->numEdges; i++) { + struct edges_t* e = &(w->edges[i]); + if (strcmp(e->linkId, l->linkId) == 0) { + return e; + } + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief supporting the construction of the graph per context using the explicit + * contents/info of the link list + * + * @param set + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void build_contextSet_linklList(struct contextSet_t* set) { + g_assert(set); + + // for each link in linkList: + // 1st- Retrieve endpoints A --> B feauture (context Id, device Id, endpoint Id) + // 2st - In the graph associated to the contextId, check wheter A (deviceId) is in the vertices list + // o No, this is weird ... exist + // o Yes, get the other link endpoint (i.e., B) and check whether it exists. If NOT add it, considering + // all the attributes; Otherwise, check whether the link is different from existing edges between A and B + + for (gint j = 0; j < linkList->numLinks; j++) { + struct link_t* l = &(linkList->links[j]); + // link assumed to be P2P A --> B; I.e. 2 endPoints; 1st specifies A and 2nd specifie B + struct link_endpointId_t* aEndpointId = &(l->linkEndPointId[0]); + struct topology_id_t* topologyId = &(aEndpointId->topology_id); + // get the contextId + gchar contextUuid[UUID_CHAR_LENGTH]; + duplicate_string(contextUuid, topologyId->contextId); + //DEBUG_PC("Link: %s in Context: %s", l->linkId, contextUuid); + + // Check first contextUuid exists in the cSet + struct context_t* c = find_contextId_in_set(contextUuid, set); + if (c == NULL) { + DEBUG_PC("ContextId: %s does NOT exist... weird", contextUuid); + exit(-1); + } + + // get the device ID of A + gchar aDeviceId[UUID_CHAR_LENGTH]; + duplicate_string(aDeviceId, aEndpointId->deviceId); + + struct graph_t* g = &(c->g); // get the graph associated to the context c + struct vertices_t* v = find_vertex_in_graph_context(g, aDeviceId); + if (v == NULL) { + DEBUG_PC("aDeviceId: %s IS NOT IN Vertices of contextId: %s", aDeviceId, contextUuid); + exit(-1); + } + // get the bEndpointId + struct link_endpointId_t* bEndpointId = &(l->linkEndPointId[1]); + gchar bDeviceId[UUID_CHAR_LENGTH]; + duplicate_string(bDeviceId, bEndpointId->deviceId); + // Check whether device B is in the targeted Vertices from A (i.e., v)? + // If not, add B in the targeted vertices B + create the edge and add it + // If B exist, check whether the explored link/edge is already in the list of edges + struct targetNodes_t* w = find_targeted_vertex_in_graph_context(v, bDeviceId); + if (w == NULL) { + //DEBUG_PC("B device [%s] is PEER of A device [%s]", bDeviceId, v->verticeId.nodeId); + w = add_targeted_vertex_in_graph_context(v, bDeviceId); + add_edge_in_targetedVertice_set(w, l); + } + else { + // w exists, it is needed to check whether the edge (link) should be added + struct edges_t* e = find_edge_in_targetedVertice_set(w, l); + if (e == NULL) { + // Add the link into the list + add_edge_in_targetedVertice_set(w, l); + } + else { + DEBUG_PC("The link already exists ..."); + continue; + } + } + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Create the set of (distinct) contexts with the deviceList and linkList + * + * @param cSet + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void build_contextSet(struct contextSet_t* cSet) { + g_assert(cSet); + g_assert(deviceList); + g_assert(linkList); + + // devices are tied to contexts, i.e. depending on the contextId of the devices + build_contextSet_deviceList(cSet); + + // Once the diverse contexts are created and the devices/endpoints asigned to the + // respective graph tied to each context, it is needed to create the edges + build_contextSet_linklList(cSet); + + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Print the contents of the ContextIds + * + * @param set + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void print_contextSet(struct contextSet_t* set) { + g_assert(set); + + for (gint i = 0; i < set->num_context_set; i++) { + struct context_t* c = &(set->contextList[i]); + DEBUG_PC("-------------------------------------------------------------"); + DEBUG_PC(" Context Id: %s", c->contextId); + DEBUG_PC("-------------------------------------------------------------"); + + struct graph_t* g = &(c->g); + for (gint j = 0; j < g->numVertices; j++) { + struct vertices_t* v = &(g->vertices[j]); + DEBUG_PC(" Head Device Id: %s", v->verticeId.nodeId); + for (gint k = 0; k < v->numTargetedVertices; k++) { + struct targetNodes_t* w = &(v->targetedVertices[k]); + DEBUG_PC(" [%d] --- Peer Device Id: %s", k, w->tVertice.nodeId); + for (gint l = 0; l < w->numEdges; l++) { + struct edges_t* e = &(w->edges[l]); + DEBUG_PC(" \t link Id: %s", e->linkId); + DEBUG_PC(" \t aEndPointId: %s", e->aEndPointId); + DEBUG_PC(" \t zEndPointId: %s", e->zEndPointId); + DEBUG_PC(" \t Available Capacity: %f, Latency: %f, Cost: %f", e->availCap, e->delay, e->cost); + DEBUG_PC(" \t aTopologyId: %s", e->aTopologyId); + DEBUG_PC(" \t zTopologyId: %s", e->zTopologyId); + } + } + } + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Check whether src and dst PE nodeId of the req are the same + * + * @param r + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +gint same_src_dst_pe_nodeid(struct service_t* s) +{ + // Check that source PE and dst PE are NOT the same, i.e., different ingress and egress endpoints (iEp, eEp) + struct service_endpoints_id_t* iEp = &(s->service_endpoints_id[0]); + struct service_endpoints_id_t* eEp = &(s->service_endpoints_id[1]); + + gchar* iEpUUID = iEp->endpoint_uuid; + gchar* eEpUUID = eEp->endpoint_uuid; + gchar* iDevUUID = iEp->device_uuid; + gchar* eDevUUID = eEp->device_uuid; + + // Compare the device uuids + if (strcmp(iDevUUID, eDevUUID) != 0) { + DEBUG_PC("DIFFERENT --- iDevId: %s and eDevId: %s", iDevUUID, eDevUUID); + return 1; + } + // Compare the endpoints (ports) + if (strcmp(iEpUUID, eEpUUID) != 0) { + DEBUG_PC("DIFFERENT --- iEpUUID: %s and eEpUUID: %s", iEpUUID, eEpUUID); + return 1; + } + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Handles issues with the route computation + * + * @param route + * @param s + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void comp_route_connection_issue_handler (struct compRouteOutput_t *path, struct service_t *s) +{ + g_assert(path); + g_assert(s); + + // Increase the number of computed routes/paths despite there was an issue to be reported + path->numPaths++; + // Copy the serviceId + copy_service_id(&(path->serviceId), &(s->serviceId)); + + // copy the service endpoints, in general, there will be 2 (point-to-point network connectivity services) + for (gint i = 0; i < s->num_service_endpoints_id; i++) { + struct service_endpoints_id_t* iEp = &(s->service_endpoints_id[i]); + struct service_endpoints_id_t* oEp = &(path->service_endpoints_id[i]); + copy_service_endpoint_id(oEp, iEp); + } + path->num_service_endpoints_id = s->num_service_endpoints_id; + path->noPathIssue = NO_PATH_CONS_ISSUE; + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief released the allocated memory fo compRouteOutputList_t + * + * @param ro + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void destroy_compRouteOutputList (struct compRouteOutputList_t *ro) +{ + g_assert (ro); + g_free (ro); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief creates a copy of the underlying graph + * + * @param originalGraph + * @param destGraph + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void duplicate_graph (struct graph_t *originalGraph, struct graph_t *destGraph) { + g_assert (originalGraph); + g_assert (destGraph); + + destGraph->numVertices = originalGraph->numVertices; + for (gint i = 0; i < originalGraph->numVertices; i++) { + struct vertices_t *oVertex = &(originalGraph->vertices[i]); + struct vertices_t *dVertex = &(destGraph->vertices[i]); + dVertex->numTargetedVertices = oVertex->numTargetedVertices; + duplicate_node_id (&oVertex->verticeId, &dVertex->verticeId); + + for (gint j = 0; j < oVertex->numTargetedVertices; j++) { + struct targetNodes_t *oTargetedVertex = &(oVertex->targetedVertices[j]); + struct targetNodes_t *dTargetedVertex = &(dVertex->targetedVertices[j]); + duplicate_node_id (&oTargetedVertex->tVertice, &dTargetedVertex->tVertice); + dTargetedVertex->numEdges = oTargetedVertex->numEdges; + + for (gint k = 0; k < oTargetedVertex->numEdges; k++) { + struct edges_t *oEdge = &(oTargetedVertex->edges[k]); + struct edges_t *dEdge = &(dTargetedVertex->edges[k]); + duplicate_edge (dEdge, oEdge); + } + } + } + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Function used to retrieve from the graph the edge instance associated to the + * pathLink (pL) + * + * @param pL + * @parma g + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct edges_t* get_edge_from_graph_by_linkId(struct pathLink_t* pL, struct graph_t* g) { + g_assert(pL); + g_assert(g); + + for (gint i = 0; i < g->numVertices; i++) { + struct vertices_t* v = &(g->vertices[i]); + for (gint j = 0; j < v->numTargetedVertices; j++) { + struct targetNodes_t* tv = &(v->targetedVertices[j]); + for (gint k = 0; k < tv->numEdges; k++) { + struct edges_t* e = &(tv->edges[k]); + if (strcmp(e->linkId, pL->linkId) == 0) { + return e; + } + } + } + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Function used to retrieve from the graph the reverse edge (rev_e) associated to an edge (e) + * + * @param e + * @parma g + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +struct edges_t* get_reverse_edge_from_the_graph(struct edges_t* e, struct graph_t* g) { + g_assert(e); + g_assert(g); + + for (gint i = 0; i < g->numVertices; i++) { + struct vertices_t* v = &(g->vertices[i]); + // Check Route Element zNodeId with the v->verticeId + if (compare_node_id(&e->zNodeId, &v->verticeId) != 0) + continue; + // Check Route Element zNodeis with any of reachable targeted vertices from v + gboolean foundTargVert = FALSE; + gint indexTargVert = -1; + for (gint j = 0; j < v->numTargetedVertices; j++) { + struct targetNodes_t* tv = &(v->targetedVertices[j]); + if (compare_node_id(&e->aNodeId, &tv->tVertice) == 0) + { + foundTargVert = TRUE; + indexTargVert = j; + break; + } + } + if (foundTargVert == FALSE) { + continue; + } + + // The targeted vertice is found, then check matching with the endpoints + struct targetNodes_t* tv = &(v->targetedVertices[indexTargVert]); + for (gint k = 0; k < tv->numEdges; k++) { + struct edges_t* rev_e = &(tv->edges[k]); + if ((strcmp(rev_e->aEndPointId, e->zEndPointId) == 0) && + (strcmp(rev_e->zEndPointId, e->aEndPointId) == 0)) { + return rev_e; + } + } + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Function used to reflect in the graph the assigned/allocated resources contained in the path p + * considering the needs (e.g., bandwidth) of service s + * + * @param p + * @param s + * @parma g + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2022 + */ +///////////////////////////////////////////////////////////////////////////////////////// +void allocate_graph_resources (struct path_t *p, struct service_t *s, struct graph_t *g) +{ + g_assert (p); + g_assert (s); + g_assert (g); + // Retrieve the requested bw by the service + struct path_constraints_t* pathCons = get_path_constraints(s); + + for (gint i = 0; i < p->numPathLinks; i++) { + struct pathLink_t* pL = &(p->pathLinks[i]); + // get the edge associated to the linkId in the graph + struct edges_t* e = get_edge_from_graph_by_linkId(pL, g); + if (e == NULL) { + DEBUG_PC("The linkId: %s is NOT found in the Graph!!!", pL->linkId); + exit(-1); + } + //Update the availBw in the edge + gdouble resBw = e->availCap - pathCons->bwConstraint; + DEBUG_PC("Updating the Avail Bw @ edge/link: %s", e->linkId); + DEBUG_PC("Initial avaiCap @ e/link: %f, demanded Bw: %f, resulting Avail Bw: %f", e->availCap, pathCons->bwConstraint, resBw); + memcpy(&e->availCap, &resBw, sizeof(gdouble)); + DEBUG_PC("Final e/link avail Bw: %f", e->availCap); + } + g_free(pathCons); + + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Function used to reflect in the graph the assigned/allocated resources contained in the reverse direction of the path p + * considering the needs (e.g., bandwidth) of service s + * + * @param p + * @param s + * @parma g + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2021 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void allocate_graph_reverse_resources(struct path_t* p, struct service_t * s, struct graph_t* g) +{ + g_assert(p); + g_assert(s); + g_assert(g); + + struct path_constraints_t* pathCons = get_path_constraints(s); + for (gint i = 0; i < p->numPathLinks; i++) { + struct pathLink_t* pL = &(p->pathLinks[i]); + struct edges_t* e = get_edge_from_graph_by_linkId(pL, g); + if (e == NULL) { + DEBUG_PC("The linkId: %s is NOT found in the Graph!!!", pL->linkId); + exit(-1); + } + struct edges_t* rev_e = get_reverse_edge_from_the_graph(e, g); + if (rev_e == NULL) { + DEBUG_PC("the reverse edge of linkId: %s is NOT found in the Graph!!!", pL->linkId); + exit(-1); + } + //Update the availBw in the edge + gdouble resBw = rev_e->availCap - pathCons->bwConstraint; + DEBUG_PC("Updating the Avail Bw @ reverse edge/link: %s", rev_e->linkId); + DEBUG_PC("Initial avaiCap @ reverse edge e/link: %f, demanded Bw: %f, resulting Avail Bw: %f", rev_e->availCap, pathCons->bwConstraint, resBw); + memcpy(&rev_e->availCap, &resBw, sizeof(gdouble)); + DEBUG_PC("Final reverse edge e/link avail Bw: %f", rev_e->availCap); + } + g_free(pathCons); + return; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/** + * @file pathComp_tools.c + * @brief Function used to printall the computed paths for the requested network connectivity services + * + * @param routeList + * + * @author Ricardo MartÃnez <ricardo.martinez@cttc.es> + * @date 2021 + */ + ///////////////////////////////////////////////////////////////////////////////////////// +void print_path_connection_list(struct compRouteOutputList_t* routeList) { + g_assert(routeList); + for (gint i = 0; i < routeList->numCompRouteConnList; i++) { + DEBUG_PC("==================== Service Item: %d ===================", i); + struct compRouteOutput_t* rO = &(routeList->compRouteConnection[i]); + DEBUG_PC("num service endpoints: %d", rO->num_service_endpoints_id); + struct serviceId_t* s = &(rO->serviceId); + DEBUG_PC("ContextId: %s, ServiceId: %s", s->contextId, s->service_uuid); + DEBUG_PC("ingress --- %s [%s]", rO->service_endpoints_id[0].device_uuid, + rO->service_endpoints_id[0].endpoint_uuid); + DEBUG_PC("egress --- %s [%s]", rO->service_endpoints_id[1].device_uuid, + rO->service_endpoints_id[1].endpoint_uuid); + + if (rO->noPathIssue == NO_PATH_CONS_ISSUE) { + DEBUG_PC("NO PATH SUCCESSFULLY COMPUTED"); + continue; + } + // Path + DEBUG_PC("Number of paths: %d", rO->numPaths); + for (gint j = 0; j < rO->numPaths; j++) { + struct path_t* p = &(rO->paths[j]); + print_path_t(p); + } + } + return; +} + diff --git a/src/pathcomp/backend/pathComp_tools.h b/src/pathcomp/backend/pathComp_tools.h new file mode 100644 index 0000000000000000000000000000000000000000..90a0cc8b878134cf29fa4eb1728f08a8b72aa4d9 --- /dev/null +++ b/src/pathcomp/backend/pathComp_tools.h @@ -0,0 +1,606 @@ +//////////////////////////////////////////////////////////////////////////////////////// +/** + * # Copyright 2022 Centre Tecnològic de Telecomunicacions de Catalunya (CTTC/CERCA) www.cttc.es + * + * 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. + + * Author: CTTC/CERCA PONS RU Ricardo Martínez (ricardo.martinez@cttc.es) + */ +///////////////////////////////////////////////////////////////////////////////////////// + +#ifndef _PATHCOMP_TOOLS_H +#define _PATHCOMP_TOOLS_H + +#include <glib.h> +#include <glib/gstdio.h> +#include <glib-2.0/glib/gtypes.h> +#include <uuid/uuid.h> + +// External variables +extern struct map_nodes_t* mapNodes; +extern struct graph_t* graph; +extern struct contextSet_t* contextSet; +extern struct linkList_t* linkList; +extern struct deviceList_t* deviceList; +extern struct serviceList_t* serviceList; + +#define INFINITY_COST 0xFFFFFFFF +#define MAX_NUM_PRED 100 + +#define K_MAX 5 + +#define MAX_NODE_ID_SIZE 37 // UUID 128 Bits - In hexadecimal requires 36 char +#define MAX_CONTEXT_ID 37 +#define UUID_CHAR_LENGTH 37 +struct nodes_t { + gchar nodeId[UUID_CHAR_LENGTH]; +}; + +struct nodeItem_t { + struct nodes_t node; + gdouble distance; + gdouble latency; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Structures for collecting the RL topology including: intra WAN topology and inter-WAN links +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define MAX_INTER_DOMAIN_PLUG_IN_SIZE 128 +struct edges_t { + //aNodeId/Device Id + struct nodes_t aNodeId; + //zNodeId/Device Id + struct nodes_t zNodeId; + + // UUID of the endPointIds + gchar aEndPointId[UUID_CHAR_LENGTH]; + gchar zEndPointId[UUID_CHAR_LENGTH]; + + // UUID of the link + gchar linkId[UUID_CHAR_LENGTH]; + + // Potential(total) and available capacity + gint unit; + gdouble totalCap, availCap; + + gdouble cost; + gdouble delay; + + // inter-domain local and remote Ids + gchar interDomain_localId[MAX_INTER_DOMAIN_PLUG_IN_SIZE]; + gchar interDomain_remoteId[MAX_INTER_DOMAIN_PLUG_IN_SIZE]; + + gchar aTopologyId[UUID_CHAR_LENGTH]; + gchar zTopologyId[UUID_CHAR_LENGTH]; +}; + +// Structure to handle the path computation +struct pred_comp_t { + struct nodes_t v; + struct edges_t e; +}; + +struct pred_t { + struct pred_comp_t predComp[MAX_NUM_PRED]; + gint numPredComp; +}; + +// Structures for the managing the path computation algorithm +struct map_t { + struct nodes_t verticeId; + struct edges_t predecessor; + gdouble distance; + gdouble avaiBandwidth; + gdouble latency; +}; + +#define MAX_MAP_NODE_SIZE 100 +struct map_nodes_t { + struct map_t map[MAX_MAP_NODE_SIZE]; + gint numMapNodes; +}; + +#define MAX_NUM_VERTICES 100 +#define MAX_NUM_EDGES 100 +// Structures for the graph composition +struct targetNodes_t { + // remote / targeted node + struct nodes_t tVertice; + // edge conencting a pair of vertices + struct edges_t edges[MAX_NUM_EDGES]; + gint numEdges; +}; + +struct vertices_t { + struct targetNodes_t targetedVertices[MAX_NUM_VERTICES]; + gint numTargetedVertices; + struct nodes_t verticeId; +}; + +struct graph_t { + struct vertices_t vertices[MAX_NUM_VERTICES]; + gint numVertices; +}; + +//////////////////////////////////////////////////// +// Structure for the Set of Contexts +/////////////////////////////////////////////////// +struct context_t { + gchar contextId[UUID_CHAR_LENGTH]; // UUID char format 36 chars + // conext Id has a graph associated + struct graph_t g; +}; + +//////////////////////////////////////////////////// +// Structure for the Set of Contexts +/////////////////////////////////////////////////// +#define MAX_NUMBER_CONTEXT 100 +struct contextSet_t { + struct context_t contextList[MAX_NUMBER_CONTEXT]; + gint num_context_set; +}; + +#define MAX_ALG_ID_LENGTH 10 +//////////////////////////////////////////////////// +// External Variables +/////////////////////////////////////////////////// +extern gchar algId[MAX_ALG_ID_LENGTH]; + +//////////////////////////////////////////////////// +// Structure for the Requested Transport Connectivity Services +/////////////////////////////////////////////////// +#define SERVICE_TYPE_UNKNOWN 0 +#define SERVICE_TYPE_L3NM 1 +#define SERVICE_TYPE_L2NM 2 +#define SERVICE_TYPE_TAPI 3 + +/////////////////////////////////////////////////////////////////// +// Structure for the topology_id +/////////////////////////////////////////////////////////////////// +struct topology_id_t { + gchar contextId[UUID_CHAR_LENGTH]; + gchar topology_uuid[UUID_CHAR_LENGTH]; +}; + +struct inter_domain_plug_in_t { + gchar inter_domain_plug_in_local_id[MAX_INTER_DOMAIN_PLUG_IN_SIZE]; + gchar inter_domain_plug_in_remote_id[MAX_INTER_DOMAIN_PLUG_IN_SIZE]; +}; + +/////////////////////////////////////////////////////////////////// +// Structure for the endPointId +/////////////////////////////////////////////////////////////////// +struct endPointId_t { + struct topology_id_t topology_id; + gchar device_id[UUID_CHAR_LENGTH]; + gchar endpoint_uuid[UUID_CHAR_LENGTH]; +}; + +/////////////////////////////////////////////////////////////////// +// Structure for the endPoint +/////////////////////////////////////////////////////////////////// +#define CAPACITY_UNIT_TB 0 +#define CAPACITY_UNIT_TBPS 1 +#define CAPACITY_UNIT_GB 2 +#define CAPACITY_UNIT_GBPS 3 +#define CAPACITY_UNIT_MB 4 +#define CAPACITY_UNIT_MBPS 5 +#define CAPACITY_UNIT_KB 6 +#define CAPACITY_UNIT_KBPS 7 +#define CAPACITY_UNIT_GHZ 8 +#define CAPACITY_UNIT_MHZ 9 +struct capacity_t { + gdouble value; + gint unit; +}; + +/////////////////////////////////////////////////////////////////// +// Structure for the endPoint +/////////////////////////////////////////////////////////////////// +#define MAX_ENDPOINT_TYPE_SIZE 128 +// Link Port Direction +#define LINK_PORT_DIRECTION_BIDIRECTIONAL 0 +#define LINK_PORT_DIRECTION_INPUT 1 +#define LINK_PORT_DIRECTION_OUTPUT 2 +#define LINK_PORT_DIRECTION_UNKNOWN 3 +// Termination Direction +#define TERMINATION_DIRECTION_BIDIRECTIONAL 0 +#define TERMINATION_DIRECTION_SINK 1 +#define TERMINATION_DIRECTION_SOURCE 2 +#define TERMINATION_DIRECTION_UNKNOWN 3 +// Termination State +#define TERMINATION_STATE_CAN_NEVER_TERMINATE 0 +#define TERMINATION_STATE_NOT_TERMINATED 1 +#define TERMINATION_STATE_TERMINATED_SERVER_TO_CLIENT_FLOW 2 +#define TERMINATION_STATE_TERMINATED_CLIENT_TO_SERVER_FLOW 3 +#define TERMINATION_STATE_TERMINATED_BIDIRECTIONAL 4 + +struct endPoint_t { + struct endPointId_t endPointId; + gchar endpointType[MAX_ENDPOINT_TYPE_SIZE]; + guint link_port_direction; + guint termination_direction; + guint termination_state; + struct capacity_t potential_capacity; + struct capacity_t available_capacity; + // inter-domain identifiers + struct inter_domain_plug_in_t inter_domain_plug_in; +}; + +/////////////////////////////////////////////////////////////////// +// Structure for the device contents +/////////////////////////////////////////////////////////////////// +#define MAX_DEV_TYPE_SIZE 128 +#define MAX_DEV_ENDPOINT_LENGTH 10 +struct device_t { + gchar deviceId[UUID_CHAR_LENGTH]; // device ID using UUID (128 bits) + + gchar deviceType[MAX_DEV_TYPE_SIZE]; // Specifies the device type + + // define the endpoints attached to the device + gint numEndPoints; + struct endPoint_t endPoints[MAX_DEV_ENDPOINT_LENGTH]; +}; + +/////////////////////////////////////////////////////////////////// +// Structure for the device List +/////////////////////////////////////////////////////////////////// +#define MAX_NUM_DEVICE 200 +struct deviceList_t { + // device information + gint numDevices; + struct device_t devices[MAX_NUM_DEVICE]; +}; + +/////////////////////////////////////////////////////////////////// +// Structure for the link EndPoint Id +/////////////////////////////////////////////////////////////////// +struct link_endpointId_t { + struct topology_id_t topology_id; + gchar deviceId[UUID_CHAR_LENGTH]; // Device UUID + gchar endPointId[UUID_CHAR_LENGTH]; // Link EndPoint UUID +}; + +/////////////////////////////////////////////////////////////////// +// Structure for the link cost characteristics +/////////////////////////////////////////////////////////////////// +#define LINK_COST_NAME_SIZE 128 +struct cost_characteristics_t { + gchar cost_name[LINK_COST_NAME_SIZE]; + gdouble cost_value; + gdouble cost_algorithm; +}; + +/////////////////////////////////////////////////////////////////// +// Structure for the latency characteristics of the link +/////////////////////////////////////////////////////////////////// +struct latency_characteristics_t { + gdouble fixed_latency; +}; + +/////////////////////////////////////////////////////////////////// +// Structure for the link +/////////////////////////////////////////////////////////////////// +#define MAX_NUM_LINK_ENDPOINT_IDS 2 + +#define LINK_FORWARDING_DIRECTION_BIDIRECTIONAL 0 +#define LINK_FORWARDING_DIRECTION_UNIDIRECTIONAL 1 +#define LINK_FORWARDING_DIRECTION_UNKNOWN 2 +struct link_t { + gchar linkId[UUID_CHAR_LENGTH]; // link Id using UUID (128 bits) + + guint numLinkEndPointIds; + struct link_endpointId_t linkEndPointId[MAX_NUM_LINK_ENDPOINT_IDS]; + + guint forwarding_direction; + struct capacity_t potential_capacity; + struct capacity_t available_capacity; + struct cost_characteristics_t cost_characteristics; + struct latency_characteristics_t latency_characteristics; +}; + +/////////////////////////////////////////////////////////////////// +// Structure for the link List +/////////////////////////////////////////////////////////////////// +#define MAX_NUM_LIST 2000 +struct linkList_t { + gint numLinks; + struct link_t links[MAX_NUM_LIST]; +}; + +//////////////////////////////////////////////////// +// Structure for service Identifier +/////////////////////////////////////////////////// +struct serviceId_t { + gchar contextId[UUID_CHAR_LENGTH]; + gchar service_uuid[UUID_CHAR_LENGTH]; +}; + +//////////////////////////////////////////////////// +// Structure the service endpoint ids +/////////////////////////////////////////////////// +struct service_endpoints_id_t { + struct topology_id_t topology_id; + gchar device_uuid[UUID_CHAR_LENGTH]; + gchar endpoint_uuid[UUID_CHAR_LENGTH]; +}; + +//////////////////////////////////////////////////// +// Structure for handling generic targeted service constraints +//////////////////////////////////////////////////// +#define MAX_CONSTRAINT_SIZE 128 +// Constraint Type: bandwidth, latency, energy, cost +struct constraint_t { + gchar constraint_type[MAX_CONSTRAINT_SIZE]; + gchar constraint_value[MAX_CONSTRAINT_SIZE]; +}; + +//////////////////////////////////////////////////// +// Structure for individual service request +//////////////////////////////////////////////////// +#define SERVICE_TYPE_UNKNOWN 0 +#define SERVICE_TYPE_L3NM 1 +#define SERVICE_TYPE_L2NM 2 +#define SERVICE_TYPE_TAPI 3 + +#define MAX_NUM_SERVICE_ENPOINTS_ID 2 + +#define MAX_NUM_SERVICE_CONSTRAINTS 10 +struct service_t { + // Indentifier used to determine the used Algorithm Id, e.g., KSP + gchar algId[MAX_ALG_ID_LENGTH]; + + // PATHS expected for the output + guint kPaths; + + struct serviceId_t serviceId; + + guint service_type; // unknown, l2nm, l3nm, tapi + + // endpoints of the network connectivity service, assumed p2p + // the 1st one assumed to be the source (ingress) and the 2nd one is the sink (egress) + struct service_endpoints_id_t service_endpoints_id[MAX_NUM_SERVICE_ENPOINTS_ID]; + guint num_service_endpoints_id; + + // Service Constraints + struct constraint_t constraints[MAX_NUM_SERVICE_CONSTRAINTS]; + guint num_service_constraints; +}; + +//////////////////////////////////////////////////// +// Structure to handle path constraints during computation +//////////////////////////////////////////////////// +struct path_constraints_t { + gdouble bwConstraint; + gboolean bw; + + gdouble costConstraint; + gboolean cost; + + gdouble latencyConstraint; + gboolean latency; + + gdouble energyConstraint; + gboolean energy; +}; + +//////////////////////////////////////////////////// +// Structure for the handling the service requests +/////////////////////////////////////////////////// +#define MAX_SERVICE_LIST 100 +struct serviceList_t { + struct service_t services[MAX_SERVICE_LIST]; + gint numServiceList; +}; + +//////////////////////////////////////////////////////////////////////////////////////////// +// Structure describing the links forming a computed path +//////////////////////////////////////////////////////////////////////////////////////////// +struct linkTopology_t { + gchar topologyId[UUID_CHAR_LENGTH]; +}; + +struct pathLink_t { + gchar linkId[UUID_CHAR_LENGTH]; // link id UUID in char format + + gchar aDeviceId[UUID_CHAR_LENGTH]; + gchar zDeviceId[UUID_CHAR_LENGTH]; + gchar aEndPointId[UUID_CHAR_LENGTH]; + gchar zEndPointId[UUID_CHAR_LENGTH]; + + struct topology_id_t topologyId; + + struct linkTopology_t linkTopologies[2]; // a p2p link (at most) can connect to devices (endpoints) attached to 2 different topologies + gint numLinkTopologies; +}; + +//////////////////////////////////////////////////////////////////////////////////////////// +// Structure describing a computed path +//////////////////////////////////////////////////////////////////////////////////////////// +#define MAX_ROUTE_ELEMENTS 50 +struct routeElement_t { + //aNodeId/Device Id + struct nodes_t aNodeId; + //zNodeId/Device Id + struct nodes_t zNodeId; + + // UUID of the endPointIds + gchar aEndPointId[UUID_CHAR_LENGTH]; + gchar zEndPointId[UUID_CHAR_LENGTH]; + + // UUID of the link + gchar linkId[UUID_CHAR_LENGTH]; + + gchar aTopologyId[UUID_CHAR_LENGTH]; + gchar zTopologyId[UUID_CHAR_LENGTH]; + + // contextId + gchar contextId[UUID_CHAR_LENGTH]; +}; + +struct compRouteOutputItem_t { + // Potential(total) and available capacity + gint unit; + gdouble totalCap, availCap; + + gdouble cost; + gdouble delay; + + struct routeElement_t routeElement[MAX_ROUTE_ELEMENTS]; + gint numRouteElements; +}; + +#define MAX_NUM_PATHS 30 +struct path_set_t { + struct compRouteOutputItem_t paths[MAX_NUM_PATHS]; + gint numPaths; +}; + +#define MAX_NUM_PATH_LINKS 20 +struct path_t { + struct capacity_t path_capacity; + struct latency_characteristics_t path_latency; + struct cost_characteristics_t path_cost; + + struct pathLink_t pathLinks[MAX_NUM_PATH_LINKS]; + guint numPathLinks; +}; + +#define NO_PATH_CONS_ISSUE 1 // No path due to a constraint issue +#define MAX_NUM_COMPUTED_PATHS 10 +struct compRouteOutput_t +{ + // object describing the service identifier: serviceId and contextId + struct serviceId_t serviceId; + + // array describing the service endpoints ids + struct service_endpoints_id_t service_endpoints_id[MAX_NUM_SERVICE_ENPOINTS_ID]; + guint num_service_endpoints_id; + + struct path_t paths[MAX_NUM_COMPUTED_PATHS]; + gint numPaths; + + // if the transport connectivity service cannot be computed, this value is set to 0 determining the constraints were not fulfilled + gint noPathIssue; +}; + +//////////////////////////////////////////////////////////////////////////////////////////// +// Structure to handle the response list with all the computed network connectivity services +//////////////////////////////////////////////////////////////////////////////////////////// +#define MAX_COMP_CONN_LIST 100 +struct compRouteOutputList_t +{ + struct compRouteOutput_t compRouteConnection[MAX_COMP_CONN_LIST]; + gint numCompRouteConnList; + + ///////////////// Metrics ////////////////////////////////////////// + // Number of total succesfully computed connections, i.e., at least 1 feasible path exists + // for every connection in the list + gint compRouteOK; + // For the succesfully newly computed/recovered/re-allocated/re-optimized connections, this + // metric determines the average allocable bandwidth over all the (re-)computed paths for the succesfully + // (i.e., feasible path) connections + gdouble compRouteConnAvBandwidth; + // For the succesfully newly computed/recovered/re-allocated/re-optimized connections, this + // metric determines the average path length (in terms of number of hops) over the computed path for the + // succesfully (i.e., feasible path) connections + gdouble compRouteConnAvPathLength; +}; + +// Prototype of external declaration of functions +void print_path (struct compRouteOutputItem_t *); +void print_path_t(struct path_t*); + +void duplicate_string(gchar *, gchar *); + +gchar* get_uuid_char(uuid_t); +void copy_service_id(struct serviceId_t*, struct serviceId_t *); +void copy_service_endpoint_id(struct service_endpoints_id_t *, struct service_endpoints_id_t *); + +struct graph_t* get_graph_by_contextId(struct contextSet_t *, gchar *); + +struct pred_t * create_predecessors (); +struct edges_t* create_edge(); +void print_predecessors (struct pred_t *); +void build_predecessors (struct pred_t *, struct service_t *, struct map_nodes_t *); +struct nodes_t * create_node (); +struct routeElement_t * create_routeElement (); + +void duplicate_node_id (struct nodes_t *, struct nodes_t *); +gint compare_node_id (struct nodes_t *, struct nodes_t *); +void duplicate_routeElement (struct routeElement_t *, struct routeElement_t *); +void duplicate_edge (struct edges_t *, struct edges_t *); +void duplicate_path (struct compRouteOutputItem_t *, struct compRouteOutputItem_t *); +void duplicate_path_t(struct compRouteOutputItem_t *, struct path_t *); +gint get_map_index_by_nodeId (gchar *, struct map_nodes_t *); +void get_edge_from_map_by_node (struct edges_t *, struct nodes_t*, struct map_nodes_t *); +void get_edge_from_predecessors (struct edges_t *, struct nodes_t*, struct pred_t *); +void build_path (struct compRouteOutputItem_t *, struct pred_t *, struct service_t *); +void print_graph (struct graph_t *); + +gint graph_vertice_lookup (gchar *, struct graph_t *); +gint graph_targeted_vertice_lookup (gint, gchar *, struct graph_t *); +gint graph_targeted_vertice_add (gint, gchar *, struct graph_t *); + +void remove_edge_from_graph (struct graph_t *, struct edges_t *); + +struct path_set_t * create_path_set (); +void sort_path_set (struct path_set_t *); +void pop_front_path_set (struct path_set_t *); +void remove_path_set(struct path_set_t*); + +void build_map_node(struct map_nodes_t *, struct graph_t *); +struct compRouteOutputList_t * create_route_list(); +struct compRouteOutputItem_t * create_path_item (); +void add_routeElement_path_back (struct routeElement_t *, struct compRouteOutputItem_t *); +gboolean matching_path_rootPath (struct compRouteOutputItem_t *, struct compRouteOutputItem_t *, struct nodes_t *, struct edges_t *); +void modify_targeted_graph (struct graph_t *, struct path_set_t *, struct compRouteOutputItem_t *, struct nodes_t *); +gint find_nodeId (gconstpointer, gconstpointer); +gint check_link (struct nodeItem_t *, gint, gint, struct graph_t *, struct service_t *, GList **, GList **, struct map_nodes_t *); +gboolean check_computed_path_feasability (struct service_t *, struct compRouteOutputItem_t * ); + +gint sort_by_distance (gconstpointer, gconstpointer); + +struct graph_t * create_graph (); +struct map_nodes_t * create_map_node (); + +struct service_t * get_service_for_computed_path(gchar *); + +struct deviceList_t* create_device_list(); +struct linkList_t* create_link_list(); +struct serviceList_t* create_service_list(); + +void print_service_type(guint); +void print_link_port_direction(guint); +void print_termination_direction(guint); +void print_termination_state(guint); +void print_capacity_unit(guint); +void print_link_forwarding_direction(guint); + +struct contextSet_t* create_contextSet(); +void build_contextSet(struct contextSet_t *); +void print_contextSet(struct contextSet_t *); + +gint same_src_dst_pe_nodeid (struct service_t *); +void comp_route_connection_issue_handler (struct compRouteOutput_t *, struct service_t *); + +void destroy_compRouteOutputList (struct compRouteOutputList_t *); +void duplicate_graph (struct graph_t *, struct graph_t *); + +void allocate_graph_resources (struct path_t *, struct service_t *, struct graph_t *); +void allocate_graph_reverse_resources(struct path_t*, struct service_t *, struct graph_t *); +void print_route_solution_list (GList *); +struct timeval tv_adjust(struct timeval); + +void print_path_connection_list(struct compRouteOutputList_t*); +#endif \ No newline at end of file diff --git a/src/pathcomp/backend/tests/inter_domain_test.txt b/src/pathcomp/backend/tests/inter_domain_test.txt new file mode 100644 index 0000000000000000000000000000000000000000..3aeaa3779a816c7b79ff20601d80450de1743d4a --- /dev/null +++ b/src/pathcomp/backend/tests/inter_domain_test.txt @@ -0,0 +1,543 @@ +{ +"serviceList": [ +{ +"algId": "KSP", +"syncPaths": false, +"serviceId": { +"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"service_uuid": "651550d8-46a0-4f36-9fb9-21b8e4f1b8d2" +}, +"serviceType": 1, +"service_endpoints_ids": [ +{ +"topology_id": { +"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaa" +}, +"device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", +"endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa111" +}, +{ +"topology_id": { +"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" +}, +"device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", +"endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc331" +} +], +"service_constraints": [ +{"constraint_type": "bandwidth", "constraint_value": "100"}, +{"constraint_type": "latency", "constraint_value": "20"} +], +"kPaths": 3 +} +], +"deviceList": [ +{"device_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa111"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 4, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa112"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa113"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +}, +{"device_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa132"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa133"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa134"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +}, +{"device_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa122"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa123"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa124"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +}, +{"device_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa142"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa143"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa141"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, "inter-domain-plug-in": {"plug-id-inter-domain-local-id": "a2", "plug-id-inter-domain-remote-id":"c1"}} +] +}, +{"device_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa151"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, "inter-domain-plug-in":{"plug-id-inter-domain-local-id":"a1", +"plug-id-inter-domain-remote-id":"b1"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa152"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa153"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +}, +{"device_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb211"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, "inter-domain-plug-in":{"plug-id-inter-domain-local-id":"b1", +"plug-id-inter-domain-remote-id":"a1"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb212"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb213"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +}, +{"device_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb231"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, "inter-domain-plug-in":{"plug-id-inter-domain-local-id":"b2", +"plug-id-inter-domain-remote-id":"c2"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb232"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb233"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +}, +{"device_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb241"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, "inter-domain-plug-in":{"plug-id-inter-domain-local-id":"b4", +"plug-id-inter-domain-remote-id":"c3"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb242"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb243"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +}, +{"device_Id": "cccccccc-cccc-cccc-cccc-cccccccccc31", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc311"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, "inter-domain-plug-in":{"plug-id-inter-domain-local-id":"c1", +"plug-id-inter-domain-remote-id":"a2"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc312"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc313"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +}, +{"device_Id": "cccccccc-cccc-cccc-cccc-cccccccccc32", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc321"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, "inter-domain-plug-in":{"plug-id-inter-domain-local-id":"c2", +"plug-id-inter-domain-remote-id":"b2"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc322"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc323"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +}, +{"device_Id": "cccccccc-cccc-cccc-cccc-cccccccccc33", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc331"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, "inter-domain-plug-in":{"plug-id-inter-domain-local-id":"c3", +"plug-id-inter-domain-remote-id":"b4"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc332"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc333"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +} +], +"linkList": [ +{"link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1112", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa113"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa122"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1211", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa122"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa113"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1113", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa112"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa132"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1311", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa132"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa112"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1312", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa133"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa123"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1213", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa123"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa133"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1214", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa124"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa142"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1412", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa142"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa124"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1315", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa134"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa152"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1513", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa152"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa134"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1415", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa143"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa153"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1514", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa153"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa143"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1521", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa151"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb211"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2115", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb211"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa151"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1431", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa141"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc311"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3114", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc311"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa141"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2124", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb212"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb242"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2421", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb242"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb212"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2123", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb213"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb233"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2321", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb233"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb213"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2324", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb232"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb243"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2424", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb243"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb232"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2332", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb231"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc321"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3223", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc321"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb231"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2433", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb241"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc34", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc331"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3324", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc331"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb241"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3132", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc312"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc323"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3231", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc323"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc312"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3133", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc313"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc333"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3331", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc333"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc313"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3233", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc322"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc332"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3332", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc332"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc322"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +} +] +} \ No newline at end of file diff --git a/src/pathcomp/backend/tests/pc-req.json b/src/pathcomp/backend/tests/pc-req.json new file mode 100644 index 0000000000000000000000000000000000000000..f465c95235dc80851b9e91dc80c3df60d3b4dbe0 --- /dev/null +++ b/src/pathcomp/backend/tests/pc-req.json @@ -0,0 +1,1230 @@ +{ + "deviceList": [ + { + "device_Id": "A1", + "device_endpoints": [ + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "A1", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "A1", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "A1", + "endpoint_uuid": "2000", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + } + ], + "device_type": "emu-packet-router" + }, + { + "device_Id": "A2", + "device_endpoints": [ + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "A2", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "A2", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "A2", + "endpoint_uuid": "1001", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + } + ], + "device_type": "emu-packet-router" + }, + { + "device_Id": "A3", + "device_endpoints": [ + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "A3", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "A3", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + } + ], + "device_type": "emu-packet-router" + }, + { + "device_Id": "B1", + "device_endpoints": [ + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "B1", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "B1", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "B1", + "endpoint_uuid": "2000", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + } + ], + "device_type": "emu-packet-router" + }, + { + "device_Id": "B2", + "device_endpoints": [ + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "B2", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "B2", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "B2", + "endpoint_uuid": "1002", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + } + ], + "device_type": "emu-packet-router" + }, + { + "device_Id": "B3", + "device_endpoints": [ + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "B3", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "B3", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + } + ], + "device_type": "emu-packet-router" + }, + { + "device_Id": "C1", + "device_endpoints": [ + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "C1", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "C1", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "C1", + "endpoint_uuid": "1001", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + } + ], + "device_type": "emu-packet-router" + }, + { + "device_Id": "C2", + "device_endpoints": [ + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "C2", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "C2", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + } + ], + "device_type": "emu-packet-router" + }, + { + "device_Id": "C3", + "device_endpoints": [ + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "C3", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "C3", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "endpoint_id": { + "device_id": "C3", + "endpoint_uuid": "1002", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + }, + "endpoint_type": "copper", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + } + ], + "device_type": "emu-packet-router" + } + ], + "linkList": [ + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "cost-characteristics": { + "cost-algorithm": "0", + "cost-name": "linkcost", + "cost-value": "1" + }, + "forwarding_direction": 0, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + }, + "link_Id": "A1/1==A2/1", + "link_endpoint_ids": [ + { + "endpoint_id": { + "device_id": "A1", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + } + }, + { + "endpoint_id": { + "device_id": "A2", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + } + } + ], + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "cost-characteristics": { + "cost-algorithm": "0", + "cost-name": "linkcost", + "cost-value": "1" + }, + "forwarding_direction": 0, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + }, + "link_Id": "A1/2==A3/1", + "link_endpoint_ids": [ + { + "endpoint_id": { + "device_id": "A1", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + } + }, + { + "endpoint_id": { + "device_id": "A3", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + } + } + ], + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "cost-characteristics": { + "cost-algorithm": "0", + "cost-name": "linkcost", + "cost-value": "1" + }, + "forwarding_direction": 0, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + }, + "link_Id": "A2/1001==C3/1002", + "link_endpoint_ids": [ + { + "endpoint_id": { + "device_id": "A2", + "endpoint_uuid": "1001", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + } + }, + { + "endpoint_id": { + "device_id": "C3", + "endpoint_uuid": "1002", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + } + } + ], + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "cost-characteristics": { + "cost-algorithm": "0", + "cost-name": "linkcost", + "cost-value": "1" + }, + "forwarding_direction": 0, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + }, + "link_Id": "A2/2==A3/2", + "link_endpoint_ids": [ + { + "endpoint_id": { + "device_id": "A2", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + } + }, + { + "endpoint_id": { + "device_id": "A3", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + } + } + ], + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "cost-characteristics": { + "cost-algorithm": "0", + "cost-name": "linkcost", + "cost-value": "1" + }, + "forwarding_direction": 0, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + }, + "link_Id": "B1/1==B2/1", + "link_endpoint_ids": [ + { + "endpoint_id": { + "device_id": "B1", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + } + }, + { + "endpoint_id": { + "device_id": "B2", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + } + } + ], + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "cost-characteristics": { + "cost-algorithm": "0", + "cost-name": "linkcost", + "cost-value": "1" + }, + "forwarding_direction": 0, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + }, + "link_Id": "B1/2==B3/1", + "link_endpoint_ids": [ + { + "endpoint_id": { + "device_id": "B1", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + } + }, + { + "endpoint_id": { + "device_id": "B3", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + } + } + ], + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "cost-characteristics": { + "cost-algorithm": "0", + "cost-name": "linkcost", + "cost-value": "1" + }, + "forwarding_direction": 0, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + }, + "link_Id": "B2/2==B3/2", + "link_endpoint_ids": [ + { + "endpoint_id": { + "device_id": "B2", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + } + }, + { + "endpoint_id": { + "device_id": "B3", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + } + } + ], + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "cost-characteristics": { + "cost-algorithm": "0", + "cost-name": "linkcost", + "cost-value": "1" + }, + "forwarding_direction": 0, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + }, + "link_Id": "C1/1001==B2/1002", + "link_endpoint_ids": [ + { + "endpoint_id": { + "device_id": "C1", + "endpoint_uuid": "1001", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + } + }, + { + "endpoint_id": { + "device_id": "B2", + "endpoint_uuid": "1002", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + } + } + ], + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "cost-characteristics": { + "cost-algorithm": "0", + "cost-name": "linkcost", + "cost-value": "1" + }, + "forwarding_direction": 0, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + }, + "link_Id": "C1/1==C2/1", + "link_endpoint_ids": [ + { + "endpoint_id": { + "device_id": "C1", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + } + }, + { + "endpoint_id": { + "device_id": "C2", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + } + } + ], + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "cost-characteristics": { + "cost-algorithm": "0", + "cost-name": "linkcost", + "cost-value": "1" + }, + "forwarding_direction": 0, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + }, + "link_Id": "C1/2==C3/1", + "link_endpoint_ids": [ + { + "endpoint_id": { + "device_id": "C1", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + } + }, + { + "endpoint_id": { + "device_id": "C3", + "endpoint_uuid": "1", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + } + } + ], + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + }, + { + "available-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + }, + "cost-characteristics": { + "cost-algorithm": "0", + "cost-name": "linkcost", + "cost-value": "1" + }, + "forwarding_direction": 0, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + }, + "link_Id": "C2/2==C3/2", + "link_endpoint_ids": [ + { + "endpoint_id": { + "device_id": "C2", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + } + }, + { + "endpoint_id": { + "device_id": "C3", + "endpoint_uuid": "2", + "topology_id": { + "contextId": "admin", + "topology_uuid": "C" + } + } + } + ], + "total-potential-capacity": { + "total-size": { + "unit": 5, + "value": 200 + } + } + } + ], + "serviceList": [ + { + "algId": "KSP", + "kPaths": 2, + "serviceId": { + "contextId": "admin", + "service_uuid": "svc:A1/2000==B1/2000" + }, + "serviceType": 1, + "service_constraints": [ + { + "constraint_type": "bandwidth[gbps]", + "constraint_value": "10.0" + }, + { + "constraint_type": "latency[ms]", + "constraint_value": "12.0" + } + ], + "service_endpoints_ids": [ + { + "device_id": "A1", + "endpoint_uuid": "2000", + "topology_id": { + "contextId": "admin", + "topology_uuid": "A" + } + }, + { + "device_id": "B1", + "endpoint_uuid": "2000", + "topology_id": { + "contextId": "admin", + "topology_uuid": "B" + } + } + ], + "syncPaths": false + } + ] +} \ No newline at end of file diff --git a/src/pathcomp/backend/tests/run-test.sh b/src/pathcomp/backend/tests/run-test.sh new file mode 100644 index 0000000000000000000000000000000000000000..214acdf1b8172a8bbd43056b3e3842583ffff645 --- /dev/null +++ b/src/pathcomp/backend/tests/run-test.sh @@ -0,0 +1 @@ +curl -0 -v -X POST -H "Expect:" -H "Content-Type: application/json" http://172.17.0.2:8081/pathComp/api/v1/compRoute -d @inter_domain_test.txt \ No newline at end of file diff --git a/src/pathcomp/backend/tests/test.txt b/src/pathcomp/backend/tests/test.txt new file mode 100644 index 0000000000000000000000000000000000000000..a2bab9f2b908dc2c4a17b86134cea38f6d0ed0b7 --- /dev/null +++ b/src/pathcomp/backend/tests/test.txt @@ -0,0 +1,236 @@ +{ +"serviceList": [ +{ +"algId": "KSP", +"syncPaths": false, +"serviceId": { +"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"service_uuid": "651550d8-46a0-4f36-9fb9-21b8e4f1b8d2" +}, +"serviceType": 1, +"service_endpoints_ids": [ +{ +"topology_id": { +"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d" +}, +"device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", +"endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa1" +}, +{ +"topology_id": { +"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d" +}, +"device_id": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee", +"endpoint_uuid": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeee1" +} +], +"service_constraints": [ +{"constraint_type": "bandwidth", "constraint_value": "100"}, +{"constraint_type": "latency", "constraint_value": "10"} +], +"kPaths": 3 +} +], +"deviceList": [ +{"device_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa1"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 4, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa2"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa3"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +}, +{"device_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbb2"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbb3"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbb4"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +}, +{"device_Id": "cccccccc-cccc-cccc-cccc-cccccccccccc", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccccc", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccccc2"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccccc", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccccc3"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccccc", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccccc4"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +}, +{"device_Id": "dddddddd-dddd-dddd-dddd-dddddddddddd", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "dddddddd-dddd-dddd-dddd-dddddddddddd", "endpoint_uuid": "dddddddd-dddd-dddd-dddd-ddddddddddd2"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "dddddddd-dddd-dddd-dddd-dddddddddddd", "endpoint_uuid": "dddddddd-dddd-dddd-dddd-ddddddddddd3"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +}, +{"device_Id": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee", "device_type": "L3", +"device_endpoints": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee", "endpoint_uuid": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeee1"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 4, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee", "endpoint_uuid": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeee2"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee", "endpoint_uuid": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeee3"}, +"endpoint_type": "termination", "link_port_direction": 0, "termination-direction": 0, "termination-state": 0, "total-potential-capacity": {"total-size": {"value": 200, +"unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}} +] +} +], +"linkList": [ +{"link_Id": "ffffffff-ffff-ffff-ffff-ffffffffffab", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa2"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbb2"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "10"} +}, +{"link_Id": "ffffffff-ffff-ffff-ffff-ffffffffffba", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbb2"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa2"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "ffffffff-ffff-ffff-ffff-ffffffffffac", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa3"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccccc", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccccc2"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "ffffffff-ffff-ffff-ffff-ffffffffffca", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccccc", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccccc2"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa3"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "ffffffff-ffff-ffff-ffff-ffffffffffcb", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccccc", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccccc3"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbb3"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "ffffffff-ffff-ffff-ffff-ffffffffffbc", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbb3"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccccc", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccccc3"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "ffffffff-ffff-ffff-ffff-ffffffffffcd", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccccc", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccccc4"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "dddddddd-dddd-dddd-dddd-dddddddddddd", "endpoint_uuid": "dddddddd-dddd-dddd-dddd-ddddddddddd2"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "ffffffff-ffff-ffff-ffff-ffffffffffdc", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "dddddddd-dddd-dddd-dddd-dddddddddddd", "endpoint_uuid": "dddddddd-dddd-dddd-dddd-ddddddddddd2"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "cccccccc-cccc-cccc-cccc-cccccccccccc", "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccccc4"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "ffffffff-ffff-ffff-ffff-ffffffffffbe", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbb4"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee", "endpoint_uuid": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeee2"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "ffffffff-ffff-ffff-ffff-ffffffffffeb", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee", "endpoint_uuid": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeee2"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbb4"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "ffffffff-ffff-ffff-ffff-ffffffffffde", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "dddddddd-dddd-dddd-dddd-dddddddddddd", "endpoint_uuid": "dddddddd-dddd-dddd-dddd-ddddddddddd3"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee", "endpoint_uuid": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeee3"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +}, +{"link_Id": "ffffffff-ffff-ffff-ffff-ffffffffffed", +"link_endpoint_ids": [ +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee", "endpoint_uuid": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeee3"}}, +{"endpoint_id": {"topology_id": {"contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", +"topology_uuid": "abf65601-a36b-496f-8e21-1708e5f96e2d"}, "device_id": "dddddddd-dddd-dddd-dddd-dddddddddddd", "endpoint_uuid": "dddddddd-dddd-dddd-dddd-ddddddddddd3"}} +], +"forwarding_direction": 1, "total-potential-capacity": { "total-size": {"value": 200, "unit": 5}}, "available-capacity": {"total-size": {"value": 200, "unit": 5}}, +"cost-characteristics": {"cost-name": "linkcost","cost-value": "1","cost-algorithm": "0"},"latency-characteristics": {"fixed-latency-characteristic": "2"} +} +] +} \ No newline at end of file diff --git a/src/pathcomp/frontend/Config.py b/src/pathcomp/frontend/Config.py new file mode 100644 index 0000000000000000000000000000000000000000..75f910e36f92ca008b318cfc61718ae81f94214c --- /dev/null +++ b/src/pathcomp/frontend/Config.py @@ -0,0 +1,28 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 os + +DEFAULT_PATHCOMP_BACKEND_SCHEME = 'http' +DEFAULT_PATHCOMP_BACKEND_HOST = '127.0.0.1' +DEFAULT_PATHCOMP_BACKEND_PORT = 8081 +DEFAULT_PATHCOMP_BACKEND_BASEURL = '/pathComp/api/v1/compRoute' + +PATHCOMP_BACKEND_SCHEME = str(os.environ.get('PATHCOMP_BACKEND_SCHEME', DEFAULT_PATHCOMP_BACKEND_SCHEME )) +PATHCOMP_BACKEND_HOST = str(os.environ.get('PATHCOMP_BACKEND_HOST', DEFAULT_PATHCOMP_BACKEND_HOST )) +PATHCOMP_BACKEND_PORT = int(os.environ.get('PATHCOMP_BACKEND_PORT', DEFAULT_PATHCOMP_BACKEND_PORT )) +PATHCOMP_BACKEND_BASEURL = str(os.environ.get('PATHCOMP_BACKEND_BASEURL', DEFAULT_PATHCOMP_BACKEND_BASEURL)) + +BACKEND_URL = '{:s}://{:s}:{:d}{:s}'.format( + PATHCOMP_BACKEND_SCHEME, PATHCOMP_BACKEND_HOST, PATHCOMP_BACKEND_PORT, PATHCOMP_BACKEND_BASEURL) diff --git a/src/pathcomp/Dockerfile b/src/pathcomp/frontend/Dockerfile similarity index 94% rename from src/pathcomp/Dockerfile rename to src/pathcomp/frontend/Dockerfile index ec1ebbf06616233fb96acb4e54d3de24b8a016a4..2c511bc073b8d0631bbf120cff98d14a07187c3f 100644 --- a/src/pathcomp/Dockerfile +++ b/src/pathcomp/frontend/Dockerfile @@ -56,14 +56,15 @@ RUN find . -type f -exec sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' {} \; # Create component sub-folders, get specific Python packages RUN mkdir -p /var/teraflow/pathcomp WORKDIR /var/teraflow/pathcomp -COPY src/pathcomp/requirements.in requirements.in +COPY src/pathcomp/frontend/requirements.in requirements.in RUN pip-compile --quiet --output-file=requirements.txt requirements.in RUN python3 -m pip install -r requirements.txt # Add component files into working directory WORKDIR /var/teraflow COPY src/context/. context/ +COPY src/device/. device/ COPY src/pathcomp/. pathcomp/ # Start the service -ENTRYPOINT ["python", "-m", "pathcomp.service"] +ENTRYPOINT ["python", "-m", "pathcomp.frontend.service"] diff --git a/src/pathcomp/__init__.py b/src/pathcomp/frontend/__init__.py similarity index 100% rename from src/pathcomp/__init__.py rename to src/pathcomp/frontend/__init__.py diff --git a/src/pathcomp/client/PathCompClient.py b/src/pathcomp/frontend/client/PathCompClient.py similarity index 100% rename from src/pathcomp/client/PathCompClient.py rename to src/pathcomp/frontend/client/PathCompClient.py diff --git a/src/pathcomp/client/__init__.py b/src/pathcomp/frontend/client/__init__.py similarity index 100% rename from src/pathcomp/client/__init__.py rename to src/pathcomp/frontend/client/__init__.py diff --git a/src/pathcomp/frontend/example/command.txt b/src/pathcomp/frontend/example/command.txt new file mode 100644 index 0000000000000000000000000000000000000000..59002337f8efe1db607ebd724bb89287142e9d94 --- /dev/null +++ b/src/pathcomp/frontend/example/command.txt @@ -0,0 +1,4 @@ +curl -0 -v -X POST \ + -H "Expect:" -H "Content-Type: application/json" \ + http://172.17.0.2:8081/pathComp/api/v1/compRoute \ + -d @src/pathcomp/backend/test/inter_domain_test.txt diff --git a/src/pathcomp/frontend/example/reply.json b/src/pathcomp/frontend/example/reply.json new file mode 100644 index 0000000000000000000000000000000000000000..6bb4bef233d7eda6215a06d83a03f97901d8ad83 --- /dev/null +++ b/src/pathcomp/frontend/example/reply.json @@ -0,0 +1,92 @@ +{ + "response-list": [ + { + "serviceId": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "service_uuid": "651550d8-46a0-4f36-9fb9-21b8e4f1b8d2" + }, + "service_endpoints_ids": [ + { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa111" + }, + { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc331" + } + ], + "path": [ + { + "path-capacity": { + "total-size": { + "value": 200, + "unit": 0 + } + }, + "path-latency": { + "fixed-latency-characteristic": "8.000000" + }, + "path-cost": { + "cost-name": "", + "cost-value": "4.000000", + "cost-algorithm": "0.000000" + }, + "link": [ + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1112", + "topology": [ + { + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + { + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + } + ] + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1214", + "topology": [ + { + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + { + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + } + ] + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1431", + "topology": [ + { + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + { + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + } + ] + }, + { + "link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3133", + "topology": [ + { + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + { + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + } + ] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/pathcomp/frontend/example/request.json b/src/pathcomp/frontend/example/request.json new file mode 100644 index 0000000000000000000000000000000000000000..a13f20f12fd9d181d0a21ffc737979cae23bf457 --- /dev/null +++ b/src/pathcomp/frontend/example/request.json @@ -0,0 +1,2474 @@ +{ + "serviceList": [ + { + "algId": "KSP", + "syncPaths": false, + "serviceId": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "service_uuid": "651550d8-46a0-4f36-9fb9-21b8e4f1b8d2" + }, + "serviceType": 1, + "service_endpoints_ids": [ + { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa111" + }, + { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc331" + } + ], + "service_constraints": [ + { + "constraint_type": "bandwidth", + "constraint_value": "100" + }, + { + "constraint_type": "latency", + "constraint_value": "20" + } + ], + "kPaths": 3 + } + ], + "deviceList": [ + { + "device_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", + "device_type": "L3", + "device_endpoints": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa111" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 4, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa112" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa113" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + } + ] + }, + { + "device_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", + "device_type": "L3", + "device_endpoints": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa132" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa133" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa134" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + } + ] + }, + { + "device_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", + "device_type": "L3", + "device_endpoints": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa122" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa123" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa124" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + } + ] + }, + { + "device_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", + "device_type": "L3", + "device_endpoints": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa142" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa143" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa141" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "inter-domain-plug-in": { + "plug-id-inter-domain-local-id": "a2", + "plug-id-inter-domain-remote-id": "c1" + } + } + ] + }, + { + "device_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", + "device_type": "L3", + "device_endpoints": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa151" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "inter-domain-plug-in": { + "plug-id-inter-domain-local-id": "a1", + "plug-id-inter-domain-remote-id": "b1" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa152" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa153" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + } + ] + }, + { + "device_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", + "device_type": "L3", + "device_endpoints": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb211" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "inter-domain-plug-in": { + "plug-id-inter-domain-local-id": "b1", + "plug-id-inter-domain-remote-id": "a1" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb212" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb213" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + } + ] + }, + { + "device_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", + "device_type": "L3", + "device_endpoints": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb231" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "inter-domain-plug-in": { + "plug-id-inter-domain-local-id": "b2", + "plug-id-inter-domain-remote-id": "c2" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb232" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb233" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + } + ] + }, + { + "device_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", + "device_type": "L3", + "device_endpoints": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb241" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "inter-domain-plug-in": { + "plug-id-inter-domain-local-id": "b4", + "plug-id-inter-domain-remote-id": "c3" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb242" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb243" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + } + ] + }, + { + "device_Id": "cccccccc-cccc-cccc-cccc-cccccccccc31", + "device_type": "L3", + "device_endpoints": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc311" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "inter-domain-plug-in": { + "plug-id-inter-domain-local-id": "c1", + "plug-id-inter-domain-remote-id": "a2" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc312" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc313" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + } + ] + }, + { + "device_Id": "cccccccc-cccc-cccc-cccc-cccccccccc32", + "device_type": "L3", + "device_endpoints": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc321" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "inter-domain-plug-in": { + "plug-id-inter-domain-local-id": "c2", + "plug-id-inter-domain-remote-id": "b2" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc322" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc323" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + } + ] + }, + { + "device_Id": "cccccccc-cccc-cccc-cccc-cccccccccc33", + "device_type": "L3", + "device_endpoints": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc331" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "inter-domain-plug-in": { + "plug-id-inter-domain-local-id": "c3", + "plug-id-inter-domain-remote-id": "b4" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc332" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc333" + }, + "endpoint_type": "termination", + "link_port_direction": 0, + "termination-direction": 0, + "termination-state": 0, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + } + } + ] + } + ], + "linkList": [ + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1112", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa113" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa122" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1211", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa122" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa113" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1113", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa112" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa132" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1311", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa132" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa11", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa112" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1312", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa133" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa123" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1213", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa123" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa133" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1214", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa124" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa142" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1412", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa142" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa12", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa124" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1315", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa134" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa152" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1513", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa152" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa13", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa134" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1415", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa143" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa153" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1514", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa153" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa143" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1521", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa151" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb211" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2115", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb211" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa15", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa151" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaa1431", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa141" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc311" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3114", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc311" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + "device_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaa14", + "endpoint_uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaa141" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2124", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb212" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb242" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2421", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb242" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb212" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2123", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb213" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb233" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2321", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb233" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb21", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb213" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2324", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb232" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb243" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2424", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb243" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb232" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2332", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb231" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc321" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3223", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc321" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb23", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb231" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbb2433", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb241" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc34", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc331" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3324", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc331" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + }, + "device_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbb24", + "endpoint_uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbb241" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3132", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc312" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc323" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3231", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc323" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc312" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3133", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc313" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc333" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3331", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc333" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc31", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc313" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3233", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc322" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc332" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + }, + { + "link_Id": "cccccccc-cccc-cccc-cccc-cccccccc3332", + "link_endpoint_ids": [ + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc33", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc332" + } + }, + { + "endpoint_id": { + "topology_id": { + "contextId": "b55a05a5-ae2d-4ff3-85ca-97fac0c56f91", + "topology_uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + }, + "device_id": "cccccccc-cccc-cccc-cccc-cccccccccc32", + "endpoint_uuid": "cccccccc-cccc-cccc-cccc-ccccccccc322" + } + } + ], + "forwarding_direction": 1, + "total-potential-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "available-capacity": { + "total-size": { + "value": 200, + "unit": 5 + } + }, + "cost-characteristics": { + "cost-name": "linkcost", + "cost-value": "1", + "cost-algorithm": "0" + }, + "latency-characteristics": { + "fixed-latency-characteristic": "2" + } + } + ] +} diff --git a/src/pathcomp/requirements.in b/src/pathcomp/frontend/requirements.in similarity index 100% rename from src/pathcomp/requirements.in rename to src/pathcomp/frontend/requirements.in diff --git a/src/pathcomp/service/PathCompService.py b/src/pathcomp/frontend/service/PathCompService.py similarity index 93% rename from src/pathcomp/service/PathCompService.py rename to src/pathcomp/frontend/service/PathCompService.py index 7fd9eab3ba8de53ddc5fdee018519126c44361f0..88dc5134754cc1b198a505ca9d1cd1f98f2ace22 100644 --- a/src/pathcomp/service/PathCompService.py +++ b/src/pathcomp/frontend/service/PathCompService.py @@ -15,7 +15,7 @@ from common.Constants import ServiceNameEnum from common.Settings import get_service_port_grpc from common.tools.service.GenericGrpcService import GenericGrpcService -from pathcomp.proto.pathcomp_pb2_grpc import add_PathCompServiceServicer_to_server +from common.proto.pathcomp_pb2_grpc import add_PathCompServiceServicer_to_server from .PathCompServiceServicerImpl import PathCompServiceServicerImpl class PathCompService(GenericGrpcService): diff --git a/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py b/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py new file mode 100644 index 0000000000000000000000000000000000000000..b1da515631320d7d77dee073e8985a8d63bd3c30 --- /dev/null +++ b/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py @@ -0,0 +1,161 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 grpc, json, logging, requests, uuid +from typing import Dict, Tuple +from common.proto.context_pb2 import Empty, EndPointId, Service +from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest +from common.proto.pathcomp_pb2_grpc import PathCompServiceServicer +from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method +from common.tools.grpc.Tools import grpc_message_to_json_string +from context.client.ContextClient import ContextClient +from pathcomp.frontend.Config import BACKEND_URL +from pathcomp.frontend.service.tools.ComposeRequest import compose_device, compose_link, compose_service +#from pathcomp.frontend.service.tools.Constants import CapacityUnit + +LOGGER = logging.getLogger(__name__) + +SERVICE_NAME = 'PathComp' +METHOD_NAMES = ['Compute'] +METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) + +class PathCompServiceServicerImpl(PathCompServiceServicer): + def __init__(self) -> None: + LOGGER.debug('Creating Servicer...') + LOGGER.debug('Servicer Created') + + @safe_and_metered_rpc_method(METRICS, LOGGER) + def Compute(self, request : PathCompRequest, context : grpc.ServicerContext) -> PathCompReply: + LOGGER.info('[Compute] begin ; request = {:s}'.format(grpc_message_to_json_string(request))) + + algorithm = request.WhichOneof('algorithm') + if algorithm == 'shortest_path': + # no attributes + pass + elif algorithm == 'k_shortest_path': + k_inspection = request.k_shortest_path.k_inspection + k_return = request.k_shortest_path.k_return + else: + raise NotImplementedError('Unsupported Algorithm: {:s}'.format(str(algorithm))) + + context_client = ContextClient() + + algorithm = {'id': 'KSP', 'sync': False, 'k_paths': k_return} + service_list = [ + compose_service(grpc_service, algorithm) + for grpc_service in request.services + ] + get_service_key = lambda service_id: (service_id['contextId'], service_id['service_uuid']) + service_dict : Dict[Tuple[str, str], Tuple[Dict, Service]] = { + get_service_key(json_service['serviceId']): (json_service, grpc_service) + for json_service,grpc_service in zip(service_list, request.services) + } + #LOGGER.info('service_dict = {:s}'.format(str(service_dict))) + + # TODO: consider filtering resources + + #grpc_contexts = context_client.ListContexts(Empty()) + #for grpc_context in grpc_contexts.contexts: + # # TODO: add context to request + # grpc_topologies = context_client.ListTopologies(grpc_context.context_id) + # for grpc_topology in grpc_topologies.topologies: #pylint: disable=unused-variable + # # TODO: add topology to request + # pass + + grpc_devices = context_client.ListDevices(Empty()) + device_list = [ + compose_device(grpc_device) + for grpc_device in grpc_devices.devices + ] + endpoint_dict : Dict[str, Dict[str, Tuple[Dict, EndPointId]]] = { + json_device['device_Id']: { + json_endpoint['endpoint_id']['endpoint_uuid']: (json_endpoint['endpoint_id'], grpc_endpoint.endpoint_id) + for json_endpoint,grpc_endpoint in zip(json_device['device_endpoints'], grpc_device.device_endpoints) + } + for json_device,grpc_device in zip(device_list, grpc_devices.devices) + } + #LOGGER.info('endpoint_dict = {:s}'.format(str(endpoint_dict))) + + grpc_links = context_client.ListLinks(Empty()) + link_list = [ + compose_link(grpc_link) + for grpc_link in grpc_links.links + ] + + request = { + 'serviceList': service_list, + 'deviceList' : device_list, + 'linkList' : link_list, + } + + #with open('pc-req.json', 'w', encoding='UTF-8') as f: + # f.write(json.dumps(request, sort_keys=True, indent=4)) + + reply = requests.post(BACKEND_URL, json=request) + if reply.status_code not in {requests.codes.ok}: + raise Exception('Backend error({:s}) for request({:s})'.format( + str(reply.content.decode('UTF-8')), json.dumps(request, sort_keys=True))) + LOGGER.info('status_code={:s} reply={:s}'.format( + str(reply.status_code), str(reply.content.decode('UTF-8')))) + + json_reply = reply.json() + response_list = json_reply.get('response-list', []) + reply = PathCompReply() + for response in response_list: + service_key = get_service_key(response['serviceId']) + tuple_service = service_dict.get(service_key) + if tuple_service is None: raise Exception('ServiceKey({:s}) not found'.format(str(service_key))) + json_service, grpc_service = tuple_service + + # TODO: implement support for multi-point services + service_endpoint_ids = grpc_service.service_endpoint_ids + if len(service_endpoint_ids) != 2: raise NotImplementedError('Service must have 2 endpoints') + + service = reply.services.add() + service.CopyFrom(grpc_service) + + connection = reply.connections.add() + connection.connection_id.connection_uuid.uuid = str(uuid.uuid4()) + connection.service_id.CopyFrom(service.service_id) + + no_path_issue = response.get('noPath', {}).get('issue') + if no_path_issue is not None: + # no path found: leave connection with no endpoints + # no_path_issue == 1 => no path due to a constraint + continue + + service_paths = response['path'] + + for service_path in service_paths: + # ... "path-capacity": {"total-size": {"value": 200, "unit": 0}}, + # ... "path-latency": {"fixed-latency-characteristic": "10.000000"}, + # ... "path-cost": {"cost-name": "", "cost-value": "5.000000", "cost-algorithm": "0.000000"}, + #path_capacity = service_path['path-capacity']['total-size'] + #path_capacity_value = path_capacity['value'] + #path_capacity_unit = CapacityUnit(path_capacity['unit']) + #path_latency = service_path['path-latency']['fixed-latency-characteristic'] + #path_cost = service_path['path-cost'] + #path_cost_name = path_cost['cost-name'] + #path_cost_value = path_cost['cost-value'] + #path_cost_algorithm = path_cost['cost-algorithm'] + + path_endpoints = service_path['devices'] + for endpoint in path_endpoints: + device_uuid = endpoint['device_id'] + endpoint_uuid = endpoint['endpoint_uuid'] + endpoint_id = connection.path_hops_endpoint_ids.add() + endpoint_id.CopyFrom(endpoint_dict[device_uuid][endpoint_uuid][1]) + + LOGGER.info('[Compute] end ; reply = {:s}'.format(grpc_message_to_json_string(reply))) + return reply diff --git a/src/pathcomp/service/__init__.py b/src/pathcomp/frontend/service/__init__.py similarity index 100% rename from src/pathcomp/service/__init__.py rename to src/pathcomp/frontend/service/__init__.py diff --git a/src/pathcomp/service/__main__.py b/src/pathcomp/frontend/service/__main__.py similarity index 100% rename from src/pathcomp/service/__main__.py rename to src/pathcomp/frontend/service/__main__.py diff --git a/src/pathcomp/frontend/service/tools/ComposeRequest.py b/src/pathcomp/frontend/service/tools/ComposeRequest.py new file mode 100644 index 0000000000000000000000000000000000000000..68395523b4bb122483479451cc08b26094032b5c --- /dev/null +++ b/src/pathcomp/frontend/service/tools/ComposeRequest.py @@ -0,0 +1,137 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +from typing import Dict +from common.proto.context_pb2 import Constraint, Device, EndPointId, Link, Service, ServiceId, TopologyId +from common.tools.grpc.Tools import grpc_message_to_json_string +from .Constants import CapacityUnit, LinkForwardingDirection, LinkPortDirection, TerminationDirection, TerminationState + +def compose_topology_id(topology_id : TopologyId) -> Dict: + context_uuid = topology_id.context_id.context_uuid.uuid + topology_uuid = topology_id.topology_uuid.uuid + return {'contextId': context_uuid, 'topology_uuid': topology_uuid} + +def compose_service_id(service_id : ServiceId) -> Dict: + context_uuid = service_id.context_id.context_uuid.uuid + service_uuid = service_id.service_uuid.uuid + return {'contextId': context_uuid, 'service_uuid': service_uuid} + +def compose_endpoint_id(endpoint_id : EndPointId) -> Dict: + topology_id = compose_topology_id(endpoint_id.topology_id) + device_uuid = endpoint_id.device_id.device_uuid.uuid + endpoint_uuid = endpoint_id.endpoint_uuid.uuid + return {'topology_id': topology_id, 'device_id': device_uuid, 'endpoint_uuid': endpoint_uuid} + +def compose_capacity(value : str, unit : str) -> Dict: + return {'total-size': {'value': value, 'unit': unit}} + +def compose_endpoint( + endpoint_id : Dict, endpoint_type : str, link_port_direction : int, termination_direction : int, + termination_state : int, total_potential_capacity : Dict, available_capacity : Dict +) -> Dict: + return { + 'endpoint_id': endpoint_id, 'endpoint_type': endpoint_type, 'link_port_direction': link_port_direction, + 'termination-direction': termination_direction, 'termination-state': termination_state, + 'total-potential-capacity': total_potential_capacity, 'available-capacity': available_capacity, + } + +def compose_cost_characteristics(cost_name : str, cost_value : str, cost_algorithm : str) -> Dict: + return {'cost-name': cost_name, 'cost-value': cost_value, 'cost-algorithm': cost_algorithm} + +def compose_latency_characteristics(fixed_latency_characteristic : str) -> Dict: + return {'fixed-latency-characteristic': fixed_latency_characteristic} + +def compose_constraint(constraint : Constraint) -> Dict: + if constraint.WhichOneof('constraint') != 'custom': + MSG = 'Constraint({:s}) not supported' + str_constraint = grpc_message_to_json_string(constraint) + raise NotImplementedError(MSG.format(str_constraint)) + constraint_type = constraint.custom.constraint_type + constraint_value = constraint.custom.constraint_value + return {'constraint_type': constraint_type, 'constraint_value': constraint_value} + +def compose_device(grpc_device : Device) -> Dict: + device_uuid = grpc_device.device_id.device_uuid.uuid + device_type = grpc_device.device_type + + endpoints = [] + for device_endpoint in grpc_device.device_endpoints: + endpoint_id = compose_endpoint_id(device_endpoint.endpoint_id) + endpoint_type = device_endpoint.endpoint_type + link_port_direction = LinkPortDirection.BIDIRECTIONAL.value + termination_direction = TerminationDirection.BIDIRECTIONAL.value + termination_state = TerminationState.TERMINATED_BIDIRECTIONAL.value + total_potential_capacity = compose_capacity(200, CapacityUnit.MBPS.value) + available_capacity = compose_capacity(200, CapacityUnit.MBPS.value) + endpoint = compose_endpoint( + endpoint_id, endpoint_type, link_port_direction, termination_direction, + termination_state, total_potential_capacity, available_capacity) + endpoints.append(endpoint) + + return {'device_Id': device_uuid, 'device_type': device_type, 'device_endpoints': endpoints} + +def compose_link(grpc_link : Link) -> Dict: + link_uuid = grpc_link.link_id.link_uuid.uuid + + endpoint_ids = [ + {'endpoint_id' : compose_endpoint_id(link_endpoint_id)} + for link_endpoint_id in grpc_link.link_endpoint_ids + ] + + forwarding_direction = LinkForwardingDirection.BIDIRECTIONAL.value + total_potential_capacity = compose_capacity(200, CapacityUnit.MBPS.value) + available_capacity = compose_capacity(200, CapacityUnit.MBPS.value) + cost_characteristics = compose_cost_characteristics('linkcost', '1', '0') + latency_characteristics = compose_latency_characteristics('2') + + return { + 'link_Id': link_uuid, 'link_endpoint_ids': endpoint_ids, 'forwarding_direction': forwarding_direction, + 'total-potential-capacity': total_potential_capacity, 'available-capacity': available_capacity, + 'cost-characteristics': cost_characteristics, 'latency-characteristics': latency_characteristics, + } + +def compose_service(grpc_service : Service, algorithm : Dict) -> Dict: + service_id = compose_service_id(grpc_service.service_id) + service_type = grpc_service.service_type + + endpoint_ids = [ + compose_endpoint_id(service_endpoint_id) + for service_endpoint_id in grpc_service.service_endpoint_ids + ] + + constraints = [ + compose_constraint(service_constraint) + for service_constraint in grpc_service.service_constraints + ] + + # algorithm to be executed + algorithm_id = algorithm.get('id', 'SP') + + # if multiple services included in the request, prevent contention + # If true, services are computed one after the other and resources + # assigned to service i, are considered as used by services i+1..n + sync_paths = algorithm.get('sync', False) + + k_paths = algorithm.get('k_paths', 1) + + return { + 'serviceId': service_id, + 'serviceType': service_type, + 'service_endpoints_ids': endpoint_ids, + 'service_constraints': constraints, + + 'algId': algorithm_id, + 'syncPaths': sync_paths, + 'kPaths': k_paths, + } diff --git a/src/pathcomp/frontend/service/tools/Constants.py b/src/pathcomp/frontend/service/tools/Constants.py new file mode 100644 index 0000000000000000000000000000000000000000..cb774669c97144fa65afdaf0f3373c67a67c3212 --- /dev/null +++ b/src/pathcomp/frontend/service/tools/Constants.py @@ -0,0 +1,66 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +from enum import IntEnum + +class CapacityUnit(IntEnum): + TB = 0 + TBPS = 1 + GB = 2 + GBPS = 3 + MB = 4 + MBPS = 5 + KB = 6 + KBPS = 7 + GHZ = 8 + MHZ = 9 + +CAPACITY_MULTIPLIER = { + CapacityUnit.TB : 1.e12, + CapacityUnit.TBPS : 1.e12, + CapacityUnit.GB : 1.e9, + CapacityUnit.GBPS : 1.e9, + CapacityUnit.MB : 1.e6, + CapacityUnit.MBPS : 1.e6, + CapacityUnit.KB : 1.e3, + CapacityUnit.KBPS : 1.e3, + CapacityUnit.GHZ : 1.e9, + CapacityUnit.MHZ : 1.e6, +} + +class LinkPortDirection(IntEnum): + BIDIRECTIONAL = 0 + INPUT = 1 + OUTPUT = 2 + UNKNOWN = 3 + +class TerminationDirection(IntEnum): + BIDIRECTIONAL = 0 + SINK = 1 + SOURCE = 2 + UNKNOWN = 3 + +class TerminationState(IntEnum): + CAN_NEVER_TERMINATE = 0 + NOT_TERMINATED = 1 + TERMINATED_SERVER_TO_CLIENT = 2 + TERMINATED_CLIENT_TO_SERVER = 3 + TERMINATED_BIDIRECTIONAL = 4 + PERMENANTLY_TERMINATED = 5 + TERMINATION_STATE_UNKNOWN = 6 + +class LinkForwardingDirection(IntEnum): + BIDIRECTIONAL = 0 + UNIDIRECTIONAL = 1 + UNKNOWN = 2 diff --git a/src/pathcomp/tests/__init__.py b/src/pathcomp/frontend/service/tools/__init__.py similarity index 100% rename from src/pathcomp/tests/__init__.py rename to src/pathcomp/frontend/service/tools/__init__.py diff --git a/src/pathcomp/tests/.gitignore b/src/pathcomp/frontend/tests/.gitignore similarity index 100% rename from src/pathcomp/tests/.gitignore rename to src/pathcomp/frontend/tests/.gitignore diff --git a/src/pathcomp/tests/MockService_Dependencies.py b/src/pathcomp/frontend/tests/MockService_Dependencies.py similarity index 100% rename from src/pathcomp/tests/MockService_Dependencies.py rename to src/pathcomp/frontend/tests/MockService_Dependencies.py diff --git a/src/pathcomp/tests/Objects.py b/src/pathcomp/frontend/tests/Objects.py similarity index 71% rename from src/pathcomp/tests/Objects.py rename to src/pathcomp/frontend/tests/Objects.py index f4785d7ae670efcc5525d6b00c4baf3acf3f22b1..586911180bcb7379382b644cac1eb338a31bd522 100644 --- a/src/pathcomp/tests/Objects.py +++ b/src/pathcomp/frontend/tests/Objects.py @@ -21,10 +21,10 @@ from common.tools.object_factory.Link import get_link_uuid, json_link, json_link from common.tools.object_factory.Service import get_service_uuid, json_service_l3nm_planned from common.tools.object_factory.Topology import json_topology, json_topology_id -def compose_device(device_uuid, endpoint_uuids): +def compose_device(device_uuid, endpoint_uuids, topology_id=None): device_id = json_device_id(device_uuid) endpoints = [(endpoint_uuid, 'copper', []) for endpoint_uuid in endpoint_uuids] - endpoints = json_endpoints(device_id, endpoints, topology_id=TOPOLOGY_A_ID) + endpoints = json_endpoints(device_id, endpoints, topology_id=topology_id) device = json_device_emulated_packet_router_disabled(device_uuid, endpoints=endpoints) return device_id, endpoints, device @@ -45,32 +45,36 @@ CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_UUID) CONTEXT = json_context(DEFAULT_CONTEXT_UUID) # ----- Domains -------------------------------------------------------------------------------------------------------- -TOPOLOGY_ADMIN_ID = json_topology_id(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID) -TOPOLOGY_ADMIN = json_topology(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID) +TOPOLOGY_ADMIN_UUID = DEFAULT_TOPOLOGY_UUID +TOPOLOGY_ADMIN_ID = json_topology_id(TOPOLOGY_ADMIN_UUID, context_id=CONTEXT_ID) +TOPOLOGY_ADMIN = json_topology(TOPOLOGY_ADMIN_UUID, context_id=CONTEXT_ID) -TOPOLOGY_A_ID = json_topology_id('A', context_id=CONTEXT_ID) -TOPOLOGY_A = json_topology('A', context_id=CONTEXT_ID) +TOPOLOGY_A_UUID = 'A' +TOPOLOGY_A_ID = json_topology_id(TOPOLOGY_A_UUID, context_id=CONTEXT_ID) +TOPOLOGY_A = json_topology(TOPOLOGY_A_UUID, context_id=CONTEXT_ID) -TOPOLOGY_B_ID = json_topology_id('B', context_id=CONTEXT_ID) -TOPOLOGY_B = json_topology('B', context_id=CONTEXT_ID) +TOPOLOGY_B_UUID = 'B' +TOPOLOGY_B_ID = json_topology_id(TOPOLOGY_B_UUID, context_id=CONTEXT_ID) +TOPOLOGY_B = json_topology(TOPOLOGY_B_UUID, context_id=CONTEXT_ID) -TOPOLOGY_C_ID = json_topology_id('C', context_id=CONTEXT_ID) -TOPOLOGY_C = json_topology('C', context_id=CONTEXT_ID) +TOPOLOGY_C_UUID = 'C' +TOPOLOGY_C_ID = json_topology_id(TOPOLOGY_C_UUID, context_id=CONTEXT_ID) +TOPOLOGY_C = json_topology(TOPOLOGY_C_UUID, context_id=CONTEXT_ID) # ----- Devices Domain A ----------------------------------------------------------------------------------------------- -DEVICE_A1_ID, DEVICE_A1_ENDPOINTS, DEVICE_A1 = compose_device('A1', ['1', '2', '2000']) -DEVICE_A2_ID, DEVICE_A2_ENDPOINTS, DEVICE_A2 = compose_device('A2', ['1', '2', '1001']) -DEVICE_A3_ID, DEVICE_A3_ENDPOINTS, DEVICE_A3 = compose_device('A3', ['1', '2']) +DEVICE_A1_ID, DEVICE_A1_ENDPOINTS, DEVICE_A1 = compose_device('A1', ['1', '2', '2000'], topology_id=TOPOLOGY_A_ID) +DEVICE_A2_ID, DEVICE_A2_ENDPOINTS, DEVICE_A2 = compose_device('A2', ['1', '2', '1001'], topology_id=TOPOLOGY_A_ID) +DEVICE_A3_ID, DEVICE_A3_ENDPOINTS, DEVICE_A3 = compose_device('A3', ['1', '2' ], topology_id=TOPOLOGY_A_ID) # ----- Devices Domain B ----------------------------------------------------------------------------------------------- -DEVICE_B1_ID, DEVICE_B1_ENDPOINTS, DEVICE_B1 = compose_device('B1', ['1', '2', '2000']) -DEVICE_B2_ID, DEVICE_B2_ENDPOINTS, DEVICE_B2 = compose_device('B2', ['1', '2', '1002']) -DEVICE_B3_ID, DEVICE_B3_ENDPOINTS, DEVICE_B3 = compose_device('B3', ['1', '2']) +DEVICE_B1_ID, DEVICE_B1_ENDPOINTS, DEVICE_B1 = compose_device('B1', ['1', '2', '2000'], topology_id=TOPOLOGY_B_ID) +DEVICE_B2_ID, DEVICE_B2_ENDPOINTS, DEVICE_B2 = compose_device('B2', ['1', '2', '1002'], topology_id=TOPOLOGY_B_ID) +DEVICE_B3_ID, DEVICE_B3_ENDPOINTS, DEVICE_B3 = compose_device('B3', ['1', '2' ], topology_id=TOPOLOGY_B_ID) # ----- Devices Domain C ----------------------------------------------------------------------------------------------- -DEVICE_C1_ID, DEVICE_C1_ENDPOINTS, DEVICE_C1 = compose_device('C1', ['1', '2', '1001']) -DEVICE_C2_ID, DEVICE_C2_ENDPOINTS, DEVICE_C2 = compose_device('C2', ['1', '2']) -DEVICE_C3_ID, DEVICE_C3_ENDPOINTS, DEVICE_C3 = compose_device('C3', ['1', '2', '1002']) +DEVICE_C1_ID, DEVICE_C1_ENDPOINTS, DEVICE_C1 = compose_device('C1', ['1', '2', '1001'], topology_id=TOPOLOGY_C_ID) +DEVICE_C2_ID, DEVICE_C2_ENDPOINTS, DEVICE_C2 = compose_device('C2', ['1', '2' ], topology_id=TOPOLOGY_C_ID) +DEVICE_C3_ID, DEVICE_C3_ENDPOINTS, DEVICE_C3 = compose_device('C3', ['1', '2', '1002'], topology_id=TOPOLOGY_C_ID) # ----- InterDomain Links ---------------------------------------------------------------------------------------------- LINK_A2_C3_ID, LINK_A2_C3 = compose_link(DEVICE_A2_ENDPOINTS[2], DEVICE_C3_ENDPOINTS[2]) @@ -94,12 +98,12 @@ LINK_C2_C3_ID, LINK_C2_C3 = compose_link(DEVICE_C2_ENDPOINTS[1], DEVICE_C3_ENDPO # ----- Service -------------------------------------------------------------------------------------------------------- SERVICE_A1_B1 = compose_service(DEVICE_A1_ENDPOINTS[2], DEVICE_B1_ENDPOINTS[2], constraints=[ json_constraint('bandwidth[gbps]', 10.0), - json_constraint('latency[ms]', 5.0), + json_constraint('latency[ms]', 12.0), ]) # ----- Containers ----------------------------------------------------------------------------------------------------- -CONTEXTS = [CONTEXT] -TOPOLOGIES = [TOPOLOGY_ADMIN, TOPOLOGY_A, TOPOLOGY_B, TOPOLOGY_C] +CONTEXTS = [ CONTEXT] +TOPOLOGIES = [ TOPOLOGY_ADMIN, TOPOLOGY_A, TOPOLOGY_B, TOPOLOGY_C] DEVICES = [ DEVICE_A1, DEVICE_A2, DEVICE_A3, DEVICE_B1, DEVICE_B2, DEVICE_B3, DEVICE_C1, DEVICE_C2, DEVICE_C3, ] @@ -107,4 +111,28 @@ LINKS = [ LINK_A2_C3, LINK_C1_B2, LINK_A1_A2, LINK_A1_A3, LINK_A2_A3, LINK_B1_B2, LINK_B1_B3, LINK_B2_B3, LINK_C1_C2, LINK_C1_C3, LINK_C2_C3, ] -SERVICES = [SERVICE_A1_B1] +SERVICES = [ SERVICE_A1_B1] + +OBJECTS_PER_TOPOLOGY = [ + (TOPOLOGY_ADMIN_ID, + [ DEVICE_A1_ID, DEVICE_A2_ID, DEVICE_A3_ID, + DEVICE_B1_ID, DEVICE_B2_ID, DEVICE_B3_ID, + DEVICE_C1_ID, DEVICE_C2_ID, DEVICE_C3_ID, ], + [ LINK_A2_C3_ID, LINK_C1_B2_ID, + LINK_A1_A2_ID, LINK_A1_A3_ID, LINK_A2_A3_ID, + LINK_B1_B2_ID, LINK_B1_B3_ID, LINK_B2_B3_ID, + LINK_C1_C2_ID, LINK_C1_C3_ID, LINK_C2_C3_ID, ], + ), + (TOPOLOGY_A_ID, + [ DEVICE_A1_ID, DEVICE_A2_ID, DEVICE_A3_ID, ], + [ LINK_A1_A2_ID, LINK_A1_A3_ID, LINK_A2_A3_ID, ], + ), + (TOPOLOGY_B_ID, + [ DEVICE_B1_ID, DEVICE_B2_ID, DEVICE_B3_ID, ], + [ LINK_B1_B2_ID, LINK_B1_B3_ID, LINK_B2_B3_ID, ], + ), + (TOPOLOGY_C_ID, + [ DEVICE_C1_ID, DEVICE_C2_ID, DEVICE_C3_ID, ], + [ LINK_C1_C2_ID, LINK_C1_C3_ID, LINK_C2_C3_ID, ], + ), +] diff --git a/src/pathcomp/tests/PrepareTestScenario.py b/src/pathcomp/frontend/tests/PrepareTestScenario.py similarity index 91% rename from src/pathcomp/tests/PrepareTestScenario.py rename to src/pathcomp/frontend/tests/PrepareTestScenario.py index a4efcbdbfc0d311dfb120ab8124a9d2268660daf..9fb57e41b51f99b52bf71f7c6fc4d02d98ea100e 100644 --- a/src/pathcomp/tests/PrepareTestScenario.py +++ b/src/pathcomp/frontend/tests/PrepareTestScenario.py @@ -18,9 +18,9 @@ from common.Settings import ( ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name, get_service_port_grpc) from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient -from pathcomp.client.PathCompClient import PathCompClient -from pathcomp.service.PathCompService import PathCompService -from pathcomp.tests.MockService_Dependencies import MockService_Dependencies +from pathcomp.frontend.client.PathCompClient import PathCompClient +from pathcomp.frontend.service.PathCompService import PathCompService +from pathcomp.frontend.tests.MockService_Dependencies import MockService_Dependencies LOCAL_HOST = '127.0.0.1' MOCKSERVICE_PORT = 10000 diff --git a/src/pathcomp/Config.py b/src/pathcomp/frontend/tests/__init__.py similarity index 99% rename from src/pathcomp/Config.py rename to src/pathcomp/frontend/tests/__init__.py index 9953c820575d42fa88351cc8de022d880ba96e6a..70a33251242c51f49140e596b8208a19dd5245f7 100644 --- a/src/pathcomp/Config.py +++ b/src/pathcomp/frontend/tests/__init__.py @@ -11,3 +11,4 @@ # 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. + diff --git a/src/pathcomp/tests/test_unitary.py b/src/pathcomp/frontend/tests/test_unitary.py similarity index 74% rename from src/pathcomp/tests/test_unitary.py rename to src/pathcomp/frontend/tests/test_unitary.py index 23e574e0e1b512b7a69b69847ef5ef034bd2ca41..7f614e69c2c6594c2fbf7f1b85b5ae7a9f71f328 100644 --- a/src/pathcomp/tests/test_unitary.py +++ b/src/pathcomp/frontend/tests/test_unitary.py @@ -18,8 +18,8 @@ from common.proto.pathcomp_pb2 import PathCompRequest from common.tools.grpc.Tools import grpc_message_to_json from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient -from pathcomp.client.PathCompClient import PathCompClient -from .Objects import CONTEXTS, DEVICES, LINKS, SERVICES, TOPOLOGIES +from pathcomp.frontend.client.PathCompClient import PathCompClient +from .Objects import CONTEXTS, DEVICES, LINKS, OBJECTS_PER_TOPOLOGY, SERVICES, TOPOLOGIES from .PrepareTestScenario import ( # pylint: disable=unused-import # be careful, order of symbols is important here! mock_service, pathcomp_service, context_client, device_client, pathcomp_client) @@ -37,13 +37,34 @@ def test_prepare_environment( for device in DEVICES : device_client .AddDevice (Device (**device )) for link in LINKS : context_client.SetLink (Link (**link )) + for topology_id, device_ids, link_ids in OBJECTS_PER_TOPOLOGY: + topology = Topology() + topology.CopyFrom(context_client.GetTopology(TopologyId(**topology_id))) + + device_ids_in_topology = {device_id.device_uuid.uuid for device_id in topology.device_ids} + func_device_id_not_added = lambda device_id: device_id['device_uuid']['uuid'] not in device_ids_in_topology + func_device_id_json_to_grpc = lambda device_id: DeviceId(**device_id) + device_ids_to_add = list(map(func_device_id_json_to_grpc, filter(func_device_id_not_added, device_ids))) + topology.device_ids.extend(device_ids_to_add) + + link_ids_in_topology = {link_id.link_uuid.uuid for link_id in topology.link_ids} + func_link_id_not_added = lambda link_id: link_id['link_uuid']['uuid'] not in link_ids_in_topology + func_link_id_json_to_grpc = lambda link_id: LinkId(**link_id) + link_ids_to_add = list(map(func_link_id_json_to_grpc, filter(func_link_id_not_added, link_ids))) + topology.link_ids.extend(link_ids_to_add) + + context_client.SetTopology(topology) def test_request_service( pathcomp_client : PathCompClient): # pylint: disable=redefined-outer-name request_services = SERVICES pathcomp_request = PathCompRequest(services=request_services) + pathcomp_request.k_shortest_path.k_inspection = 2 #pylint: disable=no-member + pathcomp_request.k_shortest_path.k_return = 2 #pylint: disable=no-member + pathcomp_reply = pathcomp_client.Compute(pathcomp_request) + pathcomp_reply = grpc_message_to_json(pathcomp_reply) reply_services = pathcomp_reply['services'] reply_connections = pathcomp_reply['connections'] diff --git a/src/pathcomp/service/PathCompServiceServicerImpl.py b/src/pathcomp/service/PathCompServiceServicerImpl.py deleted file mode 100644 index 239ab6ab5bbe8b6051115a8200cc1f6f304e75b5..0000000000000000000000000000000000000000 --- a/src/pathcomp/service/PathCompServiceServicerImpl.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - -from typing import List -import grpc, logging, uuid -from common.proto.context_pb2 import Connection, Empty, EndPointId -from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest -from common.proto.pathcomp_pb2_grpc import PathCompServiceServicer -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method -from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_string -from context.client.ContextClient import ContextClient - -LOGGER = logging.getLogger(__name__) - -SERVICE_NAME = 'PathComp' -METHOD_NAMES = ['Compute'] -METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) - -class PathCompServiceServicerImpl(PathCompServiceServicer): - def __init__(self) -> None: - LOGGER.debug('Creating Servicer...') - LOGGER.debug('Servicer Created') - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def Compute(self, request : PathCompRequest, context : grpc.ServicerContext) -> PathCompReply: - LOGGER.info('[Compute] begin ; request = {:s}'.format(grpc_message_to_json_string(request))) - - context_client = ContextClient() - - # TODO: consider filtering resources - - grpc_contexts = context_client.ListContexts(Empty()) - grpc_devices = context_client.ListDevices(Empty()) - grpc_links = context_client.ListLinks(Empty()) - for grpc_context in grpc_contexts.contexts: - # TODO: add context to request - grpc_topologies = context_client.ListTopologies(grpc_context.context_id) - for grpc_topology in grpc_topologies.topologies: #pylint: disable=unused-variable - # TODO: add topology to request - pass - for grpc_device in grpc_devices.devices: #pylint: disable=unused-variable - # TODO: add device to request - pass - for grpc_link in grpc_links.links: #pylint: disable=unused-variable - # TODO: add link to request - pass - - reply = PathCompReply() - # TODO: issue path computation request - # TODO: compose reply populating reply.services and reply.connections - - for service in request.services: - # TODO: implement support for multi-point services - service_endpoint_ids = service.service_endpoint_ids - if len(service_endpoint_ids) != 2: raise NotImplementedError('Service must have 2 endpoints') - a_endpoint_id, z_endpoint_id = service_endpoint_ids[0], service_endpoint_ids[-1] - - connection_uuid = str(uuid.uuid4()) - connection_path_hops : List[EndPointId] = [] - connection_path_hops.extend([ - grpc_message_to_json(a_endpoint_id), - grpc_message_to_json(z_endpoint_id), - ]) - connection = Connection(**{ - 'connection_id': {'connection_uuid': {'uuid': connection_uuid}}, - 'service_id': grpc_message_to_json(service.service_id), - 'path_hops_endpoint_ids': connection_path_hops, - 'sub_service_ids': [], - }) - reply.connections.append(connection) #pylint: disable=no-member - reply.services.append(service) #pylint: disable=no-member - - LOGGER.info('[Compute] end ; reply = {:s}'.format(grpc_message_to_json_string(reply))) - return reply diff --git a/src/pathcomp/test-commands.sh b/src/pathcomp/test-commands.sh new file mode 100644 index 0000000000000000000000000000000000000000..cb764fae11bf1326edce75e24cc9229c0f480379 --- /dev/null +++ b/src/pathcomp/test-commands.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +docker build -t "pathcomp-frontend:latest" -f ./src/pathcomp/frontend/Dockerfile . +docker build -t "pathcomp-backend:builder" --target builder -f ./src/pathcomp/backend/Dockerfile . +docker build -t "pathcomp-backend:latest" -f ./src/pathcomp/backend/Dockerfile . +docker build -t "pathcomp-backend:gdb" -f ./src/pathcomp/backend/Dockerfile-gdb . + +docker network create --driver=bridge --subnet=172.28.0.0/24 --gateway=172.28.0.254 tfbr + +docker run --name pathcomp-frontend -d --network=tfbr --ip 172.28.0.1 pathcomp-frontend:latest +docker run --name pathcomp-backend -d --network=tfbr --ip 172.28.0.2 pathcomp-backend:latest + +docker rm -f pathcomp-frontend pathcomp-backend +docker network rm teraflowbridge + +docker images --filter="dangling=true" --quiet | xargs -r docker rmi + +docker exec -i pathcomp bash -c "pytest --log-level=INFO --verbose pathcomp/tests/test_unitary.py"