From 04c1171402ae005fd2004f01d0c0c439ad164bfd Mon Sep 17 00:00:00 2001 From: merle Date: Thu, 28 Jul 2022 03:05:08 -0700 Subject: [PATCH 01/63] Add TE service skeleton --- .gitignore | 3 + manifests/teservice.yaml | 79 ++++++++++++++++++++++++ src/te/.dockerignore | 4 ++ src/te/.gitignore | 21 +++++++ src/te/Dockerfile | 55 +++++++++++++++++ src/te/README.md | 50 +++++++++++++++ src/te/apps/tfte/src/tfte.app.src | 15 +++++ src/te/apps/tfte/src/tfte_app.erl | 18 ++++++ src/te/apps/tfte/src/tfte_sup.erl | 35 +++++++++++ src/te/config/dev.config.template | 19 ++++++ src/te/config/sys.config.src | 19 ++++++ src/te/config/vm.args.src | 4 ++ src/te/rebar.config | 28 +++++++++ src/te/rebar.lock | 1 + tutorial/1-6-setup-erlang-environmnet.md | 48 ++++++++++++++ 15 files changed, 399 insertions(+) create mode 100644 manifests/teservice.yaml create mode 100644 src/te/.dockerignore create mode 100644 src/te/.gitignore create mode 100644 src/te/Dockerfile create mode 100644 src/te/README.md create mode 100644 src/te/apps/tfte/src/tfte.app.src create mode 100644 src/te/apps/tfte/src/tfte_app.erl create mode 100644 src/te/apps/tfte/src/tfte_sup.erl create mode 100644 src/te/config/dev.config.template create mode 100644 src/te/config/sys.config.src create mode 100644 src/te/config/vm.args.src create mode 100644 src/te/rebar.config create mode 100644 src/te/rebar.lock create mode 100644 tutorial/1-6-setup-erlang-environmnet.md diff --git a/.gitignore b/.gitignore index 0a116f850..a9144d669 100644 --- a/.gitignore +++ b/.gitignore @@ -168,5 +168,8 @@ delete_local_deployment.sh local_docker_deployment.sh local_k8s_deployment.sh +# asdf configuration +.tool-versions + # Other logs **/logs/*.log.* diff --git a/manifests/teservice.yaml b/manifests/teservice.yaml new file mode 100644 index 000000000..2fca50879 --- /dev/null +++ b/manifests/teservice.yaml @@ -0,0 +1,79 @@ +# 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. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: teservice +spec: + selector: + matchLabels: + app: teservice + template: + metadata: + labels: + app: teservice + spec: + terminationGracePeriodSeconds: 5 + shareProcessNamespace: true + containers: + - name: server + image: registry.gitlab.com/teraflow-h2020/controller/te:latest + imagePullPolicy: Always + ports: + - containerPort: 11010 + env: + - name: ERLANG_LOGGER_LEVEL + value: "debug" + - name: ERLANG_COOKIE + value: "tfte-unsafe-cookie" + - name: ERLANG_NODE_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: ERLANG_NODE_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + exec: + command: ["/tfte/bin/tfte", "status"] + livenessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + exec: + command: ["/tfte/bin/tfte", "status"] + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: 700m + memory: 1024Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: teservice +spec: + type: ClusterIP + selector: + app: teservice + ports: + - name: grpc + protocol: TCP + port: 11010 + targetPort: 11010 diff --git a/src/te/.dockerignore b/src/te/.dockerignore new file mode 100644 index 000000000..558d2f3ec --- /dev/null +++ b/src/te/.dockerignore @@ -0,0 +1,4 @@ +Dockerfile +_build +README.md +.tool-versions diff --git a/src/te/.gitignore b/src/te/.gitignore new file mode 100644 index 000000000..eeb7cfc4a --- /dev/null +++ b/src/te/.gitignore @@ -0,0 +1,21 @@ +.tool-versions +.rebar3 +_* +.eunit +*.o +*.beam +*.plt +*.swp +*.swo +.erlang.cookie +ebin +log +erl_crash.dump +.rebar +logs +_build +.idea +*.iml +rebar3.crashdump +*~ +config/dev.config diff --git a/src/te/Dockerfile b/src/te/Dockerfile new file mode 100644 index 000000000..d8745ac74 --- /dev/null +++ b/src/te/Dockerfile @@ -0,0 +1,55 @@ +# 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 + +# Build stage 0 +FROM erlang:24.3-alpine + +RUN mkdir /buildroot +WORKDIR /buildroot + +# Copy our Erlang application +COPY . tfte + +# And build the release +WORKDIR tfte +RUN rebar3 as prod release + +# Build stage 1 +FROM alpine + +# Install some libs +RUN apk add --no-cache libgcc libstdc++ && \ + apk add --no-cache openssl && \ + apk add --no-cache ncurses-libs + +# Install the released application +COPY --from=0 /buildroot/tfte/_build/prod/rel/tfte /tfte + +# Expose relevant ports +#EXPOSE ???? + +ARG ERLANG_LOGGER_LEVEL_DEFAULT=debug +ARG ERLANG_COOKIE_DEFAULT=tfte-unsafe-cookie +ARG ERLANG_NODE_IP_DEFAULT=127.0.0.1 +ARG ERLANG_NODE_NAME_DEFAULT=tfte + +ENV ERLANG_LOGGER_LEVEL=$ERLANG_LOGGER_LEVEL_DEFAULT +ENV ERLANG_COOKIE=$ERLANG_COOKIE_DEFAULT +ENV ERLANG_NODE_IP=$ERLANG_NODE_IP_DEFAULT +ENV ERLANG_NODE_NAME=$ERLANG_NODE_NAME_DEFAULT + +ENTRYPOINT ["/tfte/bin/tfte"] +CMD ["foreground"] diff --git a/src/te/README.md b/src/te/README.md new file mode 100644 index 000000000..764021a4f --- /dev/null +++ b/src/te/README.md @@ -0,0 +1,50 @@ +TeraFlow Traffic Engineering Service +==================================== + +The Traffic Engineering service is tested on Ubuntu 20.04. Follow the instructions below to build, test, and run this service on your local environment. + + +## Build + + $ rebar3 compile + + +## Execute Unit Tests + + $ rebar3 eunit + + +## Run Service Console + +First you need to crete a configuration file if not already done, and customize it if required: + + $ cp config/dev.config.template config/dev.config + +Then you can start the service in console mode: + + $ rebar3 shell + + +## Build Docker Image + + $ docker build -t te:dev . + + +## Run Docker Container + + $ docker run -d --name te --init te:dev + + +## Open a Console to a Docker Container + + $ docker exec -it te /tfte/bin/tfte remote_console + + +## Open a Console to a Kubernetes Pod + + $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -- /tfte/bin/tfte remote_console + + +## Show Logs + + $ docker logs te \ No newline at end of file diff --git a/src/te/apps/tfte/src/tfte.app.src b/src/te/apps/tfte/src/tfte.app.src new file mode 100644 index 000000000..f1ed6cc80 --- /dev/null +++ b/src/te/apps/tfte/src/tfte.app.src @@ -0,0 +1,15 @@ +{application, tfte, + [{description, "Teraflow Traffic Engineering Service"}, + {vsn, "0.1.0"}, + {registered, []}, + {mod, {tfte_app, []}}, + {applications, + [kernel, + stdlib + ]}, + {env,[]}, + {modules, []}, + + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. diff --git a/src/te/apps/tfte/src/tfte_app.erl b/src/te/apps/tfte/src/tfte_app.erl new file mode 100644 index 000000000..aafcb2bcd --- /dev/null +++ b/src/te/apps/tfte/src/tfte_app.erl @@ -0,0 +1,18 @@ +%%%------------------------------------------------------------------- +%% @doc tfte public API +%% @end +%%%------------------------------------------------------------------- + +-module(tfte_app). + +-behaviour(application). + +-export([start/2, stop/1]). + +start(_StartType, _StartArgs) -> + tfte_sup:start_link(). + +stop(_State) -> + ok. + +%% internal functions diff --git a/src/te/apps/tfte/src/tfte_sup.erl b/src/te/apps/tfte/src/tfte_sup.erl new file mode 100644 index 000000000..a6f0a21f8 --- /dev/null +++ b/src/te/apps/tfte/src/tfte_sup.erl @@ -0,0 +1,35 @@ +%%%------------------------------------------------------------------- +%% @doc tfte top level supervisor. +%% @end +%%%------------------------------------------------------------------- + +-module(tfte_sup). + +-behaviour(supervisor). + +-export([start_link/0]). + +-export([init/1]). + +-define(SERVER, ?MODULE). + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%% sup_flags() = #{strategy => strategy(), % optional +%% intensity => non_neg_integer(), % optional +%% period => pos_integer()} % optional +%% child_spec() = #{id => child_id(), % mandatory +%% start => mfargs(), % mandatory +%% restart => restart(), % optional +%% shutdown => shutdown(), % optional +%% type => worker(), % optional +%% modules => modules()} % optional +init([]) -> + SupFlags = #{strategy => one_for_all, + intensity => 0, + period => 1}, + ChildSpecs = [], + {ok, {SupFlags, ChildSpecs}}. + +%% internal functions diff --git a/src/te/config/dev.config.template b/src/te/config/dev.config.template new file mode 100644 index 000000000..d6a4644ad --- /dev/null +++ b/src/te/config/dev.config.template @@ -0,0 +1,19 @@ +[ + {tfte, [ + ]}, + + {kernel, [ + {logger_level, debug}, + {logger, [ + {handler, default, logger_std_h, #{ + level => debug, + filter_default => log, + config => #{type => standard_io}, + formatter => {logger_formatter, #{ + legacy_header => false, + single_line => true + }} + }} + ]} + ]} +]. \ No newline at end of file diff --git a/src/te/config/sys.config.src b/src/te/config/sys.config.src new file mode 100644 index 000000000..70003158b --- /dev/null +++ b/src/te/config/sys.config.src @@ -0,0 +1,19 @@ +[ + {tfte, [ + ]}, + + {kernel, [ + {logger_level, ${ERLANG_LOGGER_LEVEL}}, + {logger, [ + {handler, default, logger_std_h, #{ + level => info, + filter_default => log, + config => #{type => standard_io}, + formatter => {logger_formatter, #{ + legacy_header => false, + single_line => true + }} + }} + ]} + ]} +]. \ No newline at end of file diff --git a/src/te/config/vm.args.src b/src/te/config/vm.args.src new file mode 100644 index 000000000..899705ce1 --- /dev/null +++ b/src/te/config/vm.args.src @@ -0,0 +1,4 @@ ++C multi_time_warp ++sbwt none +-name ${ERLANG_NODE_NAME}@${ERLANG_NODE_IP} +-setcookie ${ERLANG_COOKIE} diff --git a/src/te/rebar.config b/src/te/rebar.config new file mode 100644 index 000000000..b7b5e3e69 --- /dev/null +++ b/src/te/rebar.config @@ -0,0 +1,28 @@ +{erl_opts, [debug_info]}. + +{deps, [ +]}. + +{shell, [ + {config, "config/dev.config"}, + {apps, [tfte]} +]}. + +{relx, [ + {release, {tfte, "1.0.0"}, [tfte]}, + {vm_args_src, "config/vm.args.src"}, + {sys_config_src, "config/sys.config.src"}, + {dev_mode, true}, + {include_erts, false}, + {extended_start_script, true} +]}. + +{profiles, [ + {prod, [ + {relx, [ + {dev_mode, false}, + {include_erts, true}, + {include_src, false} + ]} + ]} +]}. diff --git a/src/te/rebar.lock b/src/te/rebar.lock new file mode 100644 index 000000000..57afcca04 --- /dev/null +++ b/src/te/rebar.lock @@ -0,0 +1 @@ +[]. diff --git a/tutorial/1-6-setup-erlang-environmnet.md b/tutorial/1-6-setup-erlang-environmnet.md new file mode 100644 index 000000000..11e65a49a --- /dev/null +++ b/tutorial/1-6-setup-erlang-environmnet.md @@ -0,0 +1,48 @@ +# 1.5. Setup Erlang Environment + +First we need to install Erlang. There is multiple way, for development we will be using asdf. + +## 1.5.1. Setup Erlang using asdf + +First, install any missing dependencies: + + sudo apt install curl git autoconf libncurses-dev build-essential m4 libssl-dev + +Download asdf: + + git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.10.2 + +Make asdf activate on login by adding these lines at the end of the `~/.bashrc` file: + + . $HOME/.asdf/asdf.sh + . $HOME/.asdf/completions/asdf.bash + +Logout and log back in to activate asdf. + +Install asdf plugin for Erlang: + + asdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git + +Install Erlang: + + asdf install erlang 24.3.4.2 + +Activate Erlang locally for TFS controller: + + cd tfs-ctrl/ + asdf local erlang 24.3.4.2 + +Install rebar3: + + cd ~ + git clone https://github.com/erlang/rebar3.git + cd rebar3 + asdf local erlang 24.3.4.2 + ./bootstrap + ./rebar3 local install + +Update `~/.bashrc` to use rebar3 by adding this line at the end: + + export PATH=$HOME/.cache/rebar3/bin:$PATH + +Logout and log back in. -- GitLab From 7c9db43857431c89bfbdd8424120f62650751102 Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 3 Aug 2022 07:34:43 -0700 Subject: [PATCH 02/63] te: Add support for protocol buffer --- manifests/teservice.yaml | 8 +-- proto/.gitignore | 3 + proto/generate_code_erlang.sh | 74 ++++++++++++++++++++ proto/src/erlang/.gitignore | 4 ++ proto/src/erlang/rebar.config | 7 ++ proto/src/erlang/rebar.lock | 23 +++++++ src/te/Dockerfile | 18 ++--- src/te/README.md | 34 +++++++-- src/te/apps/tfte/src/tfte.app.src | 3 +- src/te/apps/tfte/src/tfte_app.erl | 88 ++++++++++++++++++++++-- src/te/apps/tfte/src/tfte_sup.erl | 29 ++++---- src/te/apps/tfte/src/tfte_te_service.erl | 31 +++++++++ src/te/config/dev.config.template | 37 ++++++++++ src/te/config/sys.config.src | 58 ++++++++++++++++ src/te/rebar.config | 9 +-- src/te/rebar.lock | 24 ++++++- 16 files changed, 407 insertions(+), 43 deletions(-) create mode 100755 proto/generate_code_erlang.sh create mode 100644 proto/src/erlang/.gitignore create mode 100644 proto/src/erlang/rebar.config create mode 100644 proto/src/erlang/rebar.lock create mode 100644 src/te/apps/tfte/src/tfte_te_service.erl diff --git a/manifests/teservice.yaml b/manifests/teservice.yaml index 2fca50879..313acd273 100644 --- a/manifests/teservice.yaml +++ b/manifests/teservice.yaml @@ -47,15 +47,11 @@ spec: fieldRef: fieldPath: status.podIP readinessProbe: - initialDelaySeconds: 10 - periodSeconds: 10 exec: command: ["/tfte/bin/tfte", "status"] livenessProbe: - initialDelaySeconds: 10 - periodSeconds: 10 - exec: - command: ["/tfte/bin/tfte", "status"] + grpc: + port: 11010 resources: requests: cpu: 250m diff --git a/proto/.gitignore b/proto/.gitignore index d1dea37b3..4d6f9cbd7 100644 --- a/proto/.gitignore +++ b/proto/.gitignore @@ -3,5 +3,8 @@ src/*/* # used to prevent breaking symbolic links from source code folders !src/*/.gitignore !src/python/__init__.py +!src/erlang/rebar.config +!src/erlang/rebar.lock +!src/erlang/src/tfpb.app.src uml/generated diff --git a/proto/generate_code_erlang.sh b/proto/generate_code_erlang.sh new file mode 100755 index 000000000..471b654f9 --- /dev/null +++ b/proto/generate_code_erlang.sh @@ -0,0 +1,74 @@ +#!/bin/bash -eu +# 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. + +set -e + +FORCE=0 +DEFAULT_ACTION="generate" + +usage() { + echo "Usage: $0 [-f] [clean|generate]" 1>&2 + echo "Options:" + echo " -f: Force regeneration of all protocol buffers" + exit 1; +} + +while getopts "fc" o; do + case "${o}" in + f) + FORCE=1 + ;; + *) + usage + ;; + esac +done +shift $((OPTIND-1)) + +ACTION=${1:-$DEFAULT_ACTION} +cd $(dirname $0) +ROOT=$(pwd) +ERLANG_PROTO_DIR="$ROOT/src/erlang" +BUILD_CHECK="$ERLANG_PROTO_DIR/.generated" + +tfpb_clean() { + rm -f "$BUILD_CHECK" + rm -rf "$ERLANG_PROTO_DIR/src/"*.erl + rm -rf "$ERLANG_PROTO_DIR/src/erlang/_build" +} + +tfpb_generate() { + if [[ -f "$BUILD_CHECK" && $FORCE != 1 ]]; then + echo "Protocol buffer code for Erlang already generated, use -f to force" + exit 0 + fi + + tfpb_clean + mkdir -p "$ERLANG_PROTO_DIR" + cd "$ERLANG_PROTO_DIR" + rebar3 compile + rebar3 grpc gen + rebar3 compile + touch "$BUILD_CHECK" + + echo "Protocol buffer code for Erlang generated" +} + +case "$ACTION" in + clean) tfpb_clean;; + generate) tfpb_generate;; + *) usage;; +esac + diff --git a/proto/src/erlang/.gitignore b/proto/src/erlang/.gitignore new file mode 100644 index 000000000..9b34c9cdb --- /dev/null +++ b/proto/src/erlang/.gitignore @@ -0,0 +1,4 @@ +* +!rebar.config +!rebar.lock +!src/tfpb.app.src diff --git a/proto/src/erlang/rebar.config b/proto/src/erlang/rebar.config new file mode 100644 index 000000000..31ec32a36 --- /dev/null +++ b/proto/src/erlang/rebar.config @@ -0,0 +1,7 @@ +{erl_opts, [debug_info]}. +{deps, [grpcbox]}. + +{grpc, [{protos, "../.."}, + {gpb_opts, [{i, "../.."}, {strbin, true}, {descriptor, true}, {module_name_suffix, "_pb"}]}]}. + +{plugins, [grpcbox_plugin]}. diff --git a/proto/src/erlang/rebar.lock b/proto/src/erlang/rebar.lock new file mode 100644 index 000000000..d353eaf34 --- /dev/null +++ b/proto/src/erlang/rebar.lock @@ -0,0 +1,23 @@ +{"1.2.0", +[{<<"acceptor_pool">>,{pkg,<<"acceptor_pool">>,<<"1.0.0">>},1}, + {<<"chatterbox">>,{pkg,<<"ts_chatterbox">>,<<"0.12.0">>},1}, + {<<"ctx">>,{pkg,<<"ctx">>,<<"0.6.0">>},1}, + {<<"gproc">>,{pkg,<<"gproc">>,<<"0.8.0">>},1}, + {<<"grpcbox">>,{pkg,<<"grpcbox">>,<<"0.15.0">>},0}, + {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}]}. +[ +{pkg_hash,[ + {<<"acceptor_pool">>, <<"43C20D2ACAE35F0C2BCD64F9D2BDE267E459F0F3FD23DAB26485BF518C281B21">>}, + {<<"chatterbox">>, <<"4E54F199E15C0320B85372A24E35554A2CCFC4342E0B7CD8DAED9A04F9B8EF4A">>}, + {<<"ctx">>, <<"8FF88B70E6400C4DF90142E7F130625B82086077A45364A78D208ED3ED53C7FE">>}, + {<<"gproc">>, <<"CEA02C578589C61E5341FCE149EA36CCEF236CC2ECAC8691FBA408E7EA77EC2F">>}, + {<<"grpcbox">>, <<"97C7126296A091602D372EBF5860A04F7BC795B45B33A984CAD2B8E362774FD8">>}, + {<<"hpack">>, <<"17670F83FF984AE6CD74B1C456EDDE906D27FF013740EE4D9EFAA4F1BF999633">>}]}, +{pkg_hash_ext,[ + {<<"acceptor_pool">>, <<"0CBCD83FDC8B9AD2EEE2067EF8B91A14858A5883CB7CD800E6FCD5803E158788">>}, + {<<"chatterbox">>, <<"6478C161BC60244F41CD5847CC3ACCD26D997883E9F7FACD36FF24533B2FA579">>}, + {<<"ctx">>, <<"A14ED2D1B67723DBEBBE423B28D7615EB0BDCBA6FF28F2D1F1B0A7E1D4AA5FC2">>}, + {<<"gproc">>, <<"580ADAFA56463B75263EF5A5DF4C86AF321F68694E7786CB057FD805D1E2A7DE">>}, + {<<"grpcbox">>, <<"161ABE9E17E7D1982EFA6488ADEAA13C3E847A07984A6E6B224E553368918647">>}, + {<<"hpack">>, <<"06F580167C4B8B8A6429040DF36CC93BBA6D571FAEAEC1B28816523379CBB23A">>}]} +]. diff --git a/src/te/Dockerfile b/src/te/Dockerfile index d8745ac74..d9d561d4d 100644 --- a/src/te/Dockerfile +++ b/src/te/Dockerfile @@ -17,14 +17,16 @@ # Build stage 0 FROM erlang:24.3-alpine -RUN mkdir /buildroot -WORKDIR /buildroot +RUN apk add --no-cache bash -# Copy our Erlang application -COPY . tfte +RUN mkdir /var/teraflow +WORKDIR /var/teraflow -# And build the release -WORKDIR tfte +COPY proto proto +RUN bash -c proto/generate_code_erlang.sh +RUN mkdir src +COPY src/te src/te +WORKDIR src/te RUN rebar3 as prod release # Build stage 1 @@ -36,10 +38,10 @@ RUN apk add --no-cache libgcc libstdc++ && \ apk add --no-cache ncurses-libs # Install the released application -COPY --from=0 /buildroot/tfte/_build/prod/rel/tfte /tfte +COPY --from=0 /var/teraflow/src/te/_build/prod/rel/tfte /tfte # Expose relevant ports -#EXPOSE ???? +EXPOSE 11010 ARG ERLANG_LOGGER_LEVEL_DEFAULT=debug ARG ERLANG_COOKIE_DEFAULT=tfte-unsafe-cookie diff --git a/src/te/README.md b/src/te/README.md index 764021a4f..a316571bf 100644 --- a/src/te/README.md +++ b/src/te/README.md @@ -6,6 +6,12 @@ The Traffic Engineering service is tested on Ubuntu 20.04. Follow the instructio ## Build +First the TeraFlow protocol buffer code must have been generated: + + $ ../../proto/generate_code_erlang.sh + +Then the TE service can be built: + $ rebar3 compile @@ -25,26 +31,42 @@ Then you can start the service in console mode: $ rebar3 shell -## Build Docker Image +## Docker + +### Build Image + +The docker image must be built from the root of the Teraflow project: + + $ docker build -t te:dev -f src/te/Dockerfile . - $ docker build -t te:dev . +### Run a shell from inside the container -## Run Docker Container + $ docker run -ti --rm --entrypoint sh te:dev + + +### Run Docker Container $ docker run -d --name te --init te:dev -## Open a Console to a Docker Container +### Open a Console to a Docker Container's Service $ docker exec -it te /tfte/bin/tfte remote_console -## Open a Console to a Kubernetes Pod +### Show Logs + + $ docker logs te + + +## Kubernetes + +### Open a Console $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -- /tfte/bin/tfte remote_console ## Show Logs - $ docker logs te \ No newline at end of file + $ kubectl --namespace tfs logs $(kubectl --namespace tfs get pods --selector=app=teservice -o name) diff --git a/src/te/apps/tfte/src/tfte.app.src b/src/te/apps/tfte/src/tfte.app.src index f1ed6cc80..76d397939 100644 --- a/src/te/apps/tfte/src/tfte.app.src +++ b/src/te/apps/tfte/src/tfte.app.src @@ -5,7 +5,8 @@ {mod, {tfte_app, []}}, {applications, [kernel, - stdlib + stdlib, + tfpb ]}, {env,[]}, {modules, []}, diff --git a/src/te/apps/tfte/src/tfte_app.erl b/src/te/apps/tfte/src/tfte_app.erl index aafcb2bcd..159197fdf 100644 --- a/src/te/apps/tfte/src/tfte_app.erl +++ b/src/te/apps/tfte/src/tfte_app.erl @@ -1,18 +1,98 @@ -%%%------------------------------------------------------------------- +%%%----------------------------------------------------------------------------- %% @doc tfte public API %% @end -%%%------------------------------------------------------------------- +%%%----------------------------------------------------------------------------- -module(tfte_app). -behaviour(application). + +%--- Includes ------------------------------------------------------------------ + +-include_lib("kernel/include/logger.hrl"). + + +%--- Exports ------------------------------------------------------------------- + +% Behaviour application callback functions -export([start/2, stop/1]). + +%--- Behaviour application Callback Functions ---------------------------------- + start(_StartType, _StartArgs) -> - tfte_sup:start_link(). + case tfte_sup:start_link() of + {ok, Pid} -> + add_services(), + {ok, Pid}; + Other -> + Other + end. stop(_State) -> ok. -%% internal functions + +%--- Internal Functions -------------------------------------------------------- + +add_services() -> + case application:get_env(tfte, services) of + {ok, Services} -> add_services(Services); + _ -> ok + end. + +add_services([]) -> ok; +add_services([{Name, EndpointsSpecs, GrpcOpts} | Rest]) -> + try resolve_endpoints(Name, EndpointsSpecs, []) of + Endpoints -> + case grpcbox_channel_sup:start_child(Name, Endpoints, GrpcOpts) of + {ok, _Pid} -> + ?LOG_INFO("GRPC channel to ~s service started", [Name]), + ok; + {error, Reason} -> + ?LOG_WARNING("GRPC channel to ~s service failed to start: ~p", + [Name, Reason]), + ok + end + catch + throw:{Name, Reason, Extra} -> + ?LOG_WARNING("Failed to resolve ~s service configuration: ~s ~p ~p", + [Name, Reason, Extra]) + end, + add_services(Rest). + +resolve_endpoints(_Name, [], Acc) -> + lists:reverse(Acc); +resolve_endpoints(Name, [{Transport, HostSpec, PortSpec, SslOpts} | Rest], Acc) -> + Acc2 = [{Transport, resolve_host_spec(Name, HostSpec), + resolve_port_spec(Name, PortSpec), SslOpts} | Acc], + resolve_endpoints(Name, Rest, Acc2). + +resolve_host_spec(_Name, Hostname) when is_list(Hostname) -> Hostname; +resolve_host_spec(Name, {env, Key}) when is_list(Key) -> + ?LOG_DEBUG("????? HOST ~s ~s -> ~p", [Name, Key, os:getenv(Key)]), + try os:getenv(Key) of + false -> throw({Name, service_hostname_not_found, Key}); + Hostname -> Hostname + catch + _:Reason -> + throw({Name, service_hostname_error, Reason}) + end. + +resolve_port_spec(_Name, Port) when is_integer(Port) -> Port; +resolve_port_spec(Name, {env, Key}) when is_list(Key) -> + ?LOG_DEBUG("????? PORT ~s ~s -> ~p", [Name, Key, os:getenv(Key)]), + try os:getenv(Key) of + false -> throw({Name, service_port_not_found, Key}); + PortStr -> + try list_to_integer(PortStr) of + Port -> Port + catch + _:Reason -> + throw({Name, service_port_error, Reason}) + end + catch + _:Reason -> + throw({Name, service_port_error, Reason}) + end. diff --git a/src/te/apps/tfte/src/tfte_sup.erl b/src/te/apps/tfte/src/tfte_sup.erl index a6f0a21f8..2944889cb 100644 --- a/src/te/apps/tfte/src/tfte_sup.erl +++ b/src/te/apps/tfte/src/tfte_sup.erl @@ -1,35 +1,38 @@ -%%%------------------------------------------------------------------- +%%%----------------------------------------------------------------------------- %% @doc tfte top level supervisor. %% @end -%%%------------------------------------------------------------------- +%%%----------------------------------------------------------------------------- -module(tfte_sup). -behaviour(supervisor). + +%--- Exports ------------------------------------------------------------------- + +% API Functions -export([start_link/0]). +% Behaviour supervisor callback functions -export([init/1]). + +%--- Macros -------------------------------------------------------------------- + -define(SERVER, ?MODULE). + +%--- API Functions ------------------------------------------------------------- + start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). -%% sup_flags() = #{strategy => strategy(), % optional -%% intensity => non_neg_integer(), % optional -%% period => pos_integer()} % optional -%% child_spec() = #{id => child_id(), % mandatory -%% start => mfargs(), % mandatory -%% restart => restart(), % optional -%% shutdown => shutdown(), % optional -%% type => worker(), % optional -%% modules => modules()} % optional + +%--- Behaviour supervisor Callback Functions ----------------------------------- + init([]) -> SupFlags = #{strategy => one_for_all, intensity => 0, period => 1}, ChildSpecs = [], {ok, {SupFlags, ChildSpecs}}. - -%% internal functions diff --git a/src/te/apps/tfte/src/tfte_te_service.erl b/src/te/apps/tfte/src/tfte_te_service.erl new file mode 100644 index 000000000..7c2a7225b --- /dev/null +++ b/src/te/apps/tfte/src/tfte_te_service.erl @@ -0,0 +1,31 @@ +-module(tfte_te_service). + +-behaviour(te_te_service_bhvr). + + +%--- Includes ------------------------------------------------------------------ + +-include_lib("grpcbox/include/grpcbox.hrl"). + + +%--- Exports ------------------------------------------------------------------- + +% Behaviour te_te_service_bhvr callback functions +-export([request_lsp/2]). +-export([update_lsp/2]). +-export([delete_lsp/2]). + + +%--- Behaviour te_te_service_bhvr Callback Functions --------------------------- + +request_lsp(_Ctx, _Service) -> + {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, + #{headers => #{}, trailers => #{}}}. + +update_lsp(_Ctx, _Service) -> + {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, + #{headers => #{}, trailers => #{}}}. + +delete_lsp(_Ctx, _Service) -> + {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, + #{headers => #{}, trailers => #{}}}. diff --git a/src/te/config/dev.config.template b/src/te/config/dev.config.template index d6a4644ad..658bf13f8 100644 --- a/src/te/config/dev.config.template +++ b/src/te/config/dev.config.template @@ -1,5 +1,42 @@ [ {tfte, [ + {services, [ + {te, [{http, "localhost", 11010, []}], #{}}, + ]}, + + {grpcbox, [ + {servers, [#{ + grpc_opts => #{ + service_protos => [te_pb, grpcbox_health_pb, grpcbox_reflection_pb], + %client_cert_dir => "", + services => #{ + 'te.TEService' => tfte_te_service, + 'grpc.health.v1.Health' => grpcbox_health_service, + 'grpc.reflection.v1alpha.ServerReflection' => grpcbox_reflection_service + } + }, + transport_opts => #{ + ssl => false + %keyfile => "", + %certfile => "", + %cacertfile => "" + }, + listen_opts => #{ + port => 11010, + ip => {0,0,0,0} + }, + pool_opts => #{ + size => 10 + }, + server_opts => #{ + header_table_size => 4096, + enable_push => 1, + max_concurrent_streams => unlimited, + initial_window_size => 65535, + max_frame_size => 16384, + max_header_list_size => unlimited + } + }]} ]}, {kernel, [ diff --git a/src/te/config/sys.config.src b/src/te/config/sys.config.src index 70003158b..2bd5cf178 100644 --- a/src/te/config/sys.config.src +++ b/src/te/config/sys.config.src @@ -1,5 +1,63 @@ [ {tfte, [ + {services, [ + {te, [ + {http, {env, "TESERVICE_SERVICE_HOST"}, {env, "TESERVICE_SERVICE_PORT_GRPC"}, []} + ], #{}}, + {service, [ + {http, {env, "SERVICESERVICE_SERVICE_HOST"}, {env, "SERVICESERVICE_SERVICE_PORT_GRPC"}, []} + ], #{}}, + {monitoring, [ + {http, {env, "MONITORINGSERVICE_SERVICE_HOST"}, {env, "MONITORINGSERVICE_SERVICE_PORT_GRPC"}, []} + ], #{}}, + {compute, [ + {http, {env, "COMPUTESERVICE_SERVICE_HOST"}, {env, "COMPUTESERVICE_SERVICE_PORT_GRPC"}, []} + ], #{}}, + {device, [ + {http, {env, "DEVICESERVICE_SERVICE_HOST"}, {env, "DEVICESERVICE_SERVICE_PORT_GRPC"}, []} + ], #{}}, + {context, [ + {http, {env, "CONTEXTSERVICE_SERVICE_HOST"}, {env, "CONTEXTSERVICE_SERVICE_PORT_GRPC"}, []} + ], #{}}, + {automation, [ + {http, {env, "AUTOMATIONSERVICE_SERVICE_HOST"}, {env, "AUTOMATIONSERVICE_SERVICE_PORT_GRPC"}, []} + ], #{}} + ]} + ]}, + + {grpcbox, [ + {servers, [#{ + grpc_opts => #{ + service_protos => [te_pb, grpcbox_health_pb, grpcbox_reflection_pb], + %client_cert_dir => "", + services => #{ + 'te.TEService' => tfte_te_service, + 'grpc.health.v1.Health' => grpcbox_health_service, + 'grpc.reflection.v1alpha.ServerReflection' => grpcbox_reflection_service + } + }, + transport_opts => #{ + ssl => false + %keyfile => "", + %certfile => "", + %cacertfile => "" + }, + listen_opts => #{ + port => 11010, + ip => {0,0,0,0} + }, + pool_opts => #{ + size => 10 + }, + server_opts => #{ + header_table_size => 4096, + enable_push => 1, + max_concurrent_streams => unlimited, + initial_window_size => 65535, + max_frame_size => 16384, + max_header_list_size => unlimited + } + }]} ]}, {kernel, [ diff --git a/src/te/rebar.config b/src/te/rebar.config index b7b5e3e69..1063fd38c 100644 --- a/src/te/rebar.config +++ b/src/te/rebar.config @@ -1,15 +1,16 @@ {erl_opts, [debug_info]}. -{deps, [ -]}. +{deps, [grpcbox]}. {shell, [ {config, "config/dev.config"}, - {apps, [tfte]} + {apps, [tfte, tfpb, grpcbox]} ]}. +{project_app_dirs, ["apps/*", "../../proto/src/erlang"]}. + {relx, [ - {release, {tfte, "1.0.0"}, [tfte]}, + {release, {tfte, "1.0.0"}, [tfte, tfpb, grpcbox]}, {vm_args_src, "config/vm.args.src"}, {sys_config_src, "config/sys.config.src"}, {dev_mode, true}, diff --git a/src/te/rebar.lock b/src/te/rebar.lock index 57afcca04..d353eaf34 100644 --- a/src/te/rebar.lock +++ b/src/te/rebar.lock @@ -1 +1,23 @@ -[]. +{"1.2.0", +[{<<"acceptor_pool">>,{pkg,<<"acceptor_pool">>,<<"1.0.0">>},1}, + {<<"chatterbox">>,{pkg,<<"ts_chatterbox">>,<<"0.12.0">>},1}, + {<<"ctx">>,{pkg,<<"ctx">>,<<"0.6.0">>},1}, + {<<"gproc">>,{pkg,<<"gproc">>,<<"0.8.0">>},1}, + {<<"grpcbox">>,{pkg,<<"grpcbox">>,<<"0.15.0">>},0}, + {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}]}. +[ +{pkg_hash,[ + {<<"acceptor_pool">>, <<"43C20D2ACAE35F0C2BCD64F9D2BDE267E459F0F3FD23DAB26485BF518C281B21">>}, + {<<"chatterbox">>, <<"4E54F199E15C0320B85372A24E35554A2CCFC4342E0B7CD8DAED9A04F9B8EF4A">>}, + {<<"ctx">>, <<"8FF88B70E6400C4DF90142E7F130625B82086077A45364A78D208ED3ED53C7FE">>}, + {<<"gproc">>, <<"CEA02C578589C61E5341FCE149EA36CCEF236CC2ECAC8691FBA408E7EA77EC2F">>}, + {<<"grpcbox">>, <<"97C7126296A091602D372EBF5860A04F7BC795B45B33A984CAD2B8E362774FD8">>}, + {<<"hpack">>, <<"17670F83FF984AE6CD74B1C456EDDE906D27FF013740EE4D9EFAA4F1BF999633">>}]}, +{pkg_hash_ext,[ + {<<"acceptor_pool">>, <<"0CBCD83FDC8B9AD2EEE2067EF8B91A14858A5883CB7CD800E6FCD5803E158788">>}, + {<<"chatterbox">>, <<"6478C161BC60244F41CD5847CC3ACCD26D997883E9F7FACD36FF24533B2FA579">>}, + {<<"ctx">>, <<"A14ED2D1B67723DBEBBE423B28D7615EB0BDCBA6FF28F2D1F1B0A7E1D4AA5FC2">>}, + {<<"gproc">>, <<"580ADAFA56463B75263EF5A5DF4C86AF321F68694E7786CB057FD805D1E2A7DE">>}, + {<<"grpcbox">>, <<"161ABE9E17E7D1982EFA6488ADEAA13C3E847A07984A6E6B224E553368918647">>}, + {<<"hpack">>, <<"06F580167C4B8B8A6429040DF36CC93BBA6D571FAEAEC1B28816523379CBB23A">>}]} +]. -- GitLab From 1d496136dd559aa432c8da877565f513d011730f Mon Sep 17 00:00:00 2001 From: merle Date: Tue, 6 Sep 2022 04:49:32 -0700 Subject: [PATCH 03/63] Add basic PCE server and virtual testbed setup --- deploy/tfs.sh | 1 + manifests/teservice.yaml | 4 + src/te/Dockerfile | 3 +- src/te/apps/epce/src/epce.app.src | 16 + src/te/apps/epce/src/epce_app.erl | 18 + .../epce/src/epce_pcep_server_handler.erl | 69 +++ src/te/apps/epce/src/epce_server.erl | 238 ++++++++ src/te/apps/epce/src/epce_sup.erl | 37 ++ src/te/apps/tfte/src/tfte.app.src | 3 +- src/te/config/dev.config.template | 10 +- src/te/config/sys.config.src | 8 + src/te/rebar.config | 15 +- src/te/rebar.lock | 21 +- tutorial/2-6-netgen-config.yml | 101 ++++ tutorial/2-6-netgen-topology.yml.template | 545 ++++++++++++++++++ tutorial/2-6-te-demo-start-testbed.sh | 43 ++ tutorial/2-6-te-demo.md | 97 ++++ 17 files changed, 1220 insertions(+), 9 deletions(-) create mode 100644 src/te/apps/epce/src/epce.app.src create mode 100644 src/te/apps/epce/src/epce_app.erl create mode 100644 src/te/apps/epce/src/epce_pcep_server_handler.erl create mode 100644 src/te/apps/epce/src/epce_server.erl create mode 100644 src/te/apps/epce/src/epce_sup.erl create mode 100644 tutorial/2-6-netgen-config.yml create mode 100644 tutorial/2-6-netgen-topology.yml.template create mode 100755 tutorial/2-6-te-demo-start-testbed.sh create mode 100644 tutorial/2-6-te-demo.md diff --git a/deploy/tfs.sh b/deploy/tfs.sh index e6a0c0c10..ddc0d979c 100755 --- a/deploy/tfs.sh +++ b/deploy/tfs.sh @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +set -e ######################################################################################################################## # Read deployment settings diff --git a/manifests/teservice.yaml b/manifests/teservice.yaml index 313acd273..3f7bd1f04 100644 --- a/manifests/teservice.yaml +++ b/manifests/teservice.yaml @@ -73,3 +73,7 @@ spec: protocol: TCP port: 11010 targetPort: 11010 + - name: pcep + protocol: TCP + port: 4189 + targetPort: 4189 diff --git a/src/te/Dockerfile b/src/te/Dockerfile index d9d561d4d..f580ccf4e 100644 --- a/src/te/Dockerfile +++ b/src/te/Dockerfile @@ -17,7 +17,7 @@ # Build stage 0 FROM erlang:24.3-alpine -RUN apk add --no-cache bash +RUN apk add --no-cache bash git RUN mkdir /var/teraflow WORKDIR /var/teraflow @@ -42,6 +42,7 @@ COPY --from=0 /var/teraflow/src/te/_build/prod/rel/tfte /tfte # Expose relevant ports EXPOSE 11010 +EXPOSE 4189 ARG ERLANG_LOGGER_LEVEL_DEFAULT=debug ARG ERLANG_COOKIE_DEFAULT=tfte-unsafe-cookie diff --git a/src/te/apps/epce/src/epce.app.src b/src/te/apps/epce/src/epce.app.src new file mode 100644 index 000000000..92bf06111 --- /dev/null +++ b/src/te/apps/epce/src/epce.app.src @@ -0,0 +1,16 @@ +{application, epce, + [{description, "An Erlang PCE"}, + {vsn, "0.1.0"}, + {registered, []}, + {mod, {epce_app, []}}, + {applications, + [kernel, + stdlib, + pcep_server + ]}, + {env,[]}, + {modules, []}, + + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. diff --git a/src/te/apps/epce/src/epce_app.erl b/src/te/apps/epce/src/epce_app.erl new file mode 100644 index 000000000..022043b35 --- /dev/null +++ b/src/te/apps/epce/src/epce_app.erl @@ -0,0 +1,18 @@ +-module(epce_app). + +-behaviour(application). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Behaviour application functions +-export([start/2, stop/1]). + + +%%% BEHAVIOUR application FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start(_StartType, _StartArgs) -> + epce_sup:start_link(). + +stop(_State) -> + ok. diff --git a/src/te/apps/epce/src/epce_pcep_server_handler.erl b/src/te/apps/epce/src/epce_pcep_server_handler.erl new file mode 100644 index 000000000..210395885 --- /dev/null +++ b/src/te/apps/epce/src/epce_pcep_server_handler.erl @@ -0,0 +1,69 @@ +-module(epce_pcep_server_handler). + +-behaviour(gen_pcep_handler). + + +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include_lib("kernel/include/logger.hrl"). +-include_lib("pcep_codec/include/pcep_codec_te.hrl"). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API functions + +% Behaviour gen_pcep_handler functions +-export([init/1]). +-export([opened/4]). +-export([flow_added/2]). +-export([ready/1]). +-export([request_route/2]). +-export([flow_delegated/2]). +-export([flow_status_changed/3]). +-export([terminate/2]). + + +%%% RECORDS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(state, {}). + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +%%% BEHAVIOUR gen_pcep_handler FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([]) -> + {ok, #{}, #state{}}. + +opened(Id, Caps, Sess, State) -> + case epce_server:session_opened(Id, Caps, Sess) of + ok -> {ok, State}; + {error, Reason} -> {error, Reason} + end. + +flow_added(Flow, State) -> + case epce_server:flow_added(Flow) of + {error, _Reason} = Error -> Error; + ok -> {ok, State} + end. + +ready(State) -> + {ok, State}. + +request_route(RouteReq, State) -> + case epce_server:request_route(RouteReq) of + {error, _Reason} = Error -> Error; + {ok, Route} -> {ok, Route, State} + end. + +flow_delegated(_Flow, State) -> + {ok, State}. + +flow_status_changed(FlowId, NewStatus, State) -> + epce_server:flow_status_changed(FlowId, NewStatus), + {ok, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl new file mode 100644 index 000000000..bd3a13f44 --- /dev/null +++ b/src/te/apps/epce/src/epce_server.erl @@ -0,0 +1,238 @@ +-module(epce_server). + +-behaviour(gen_server). + + +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include_lib("kernel/include/logger.hrl"). +-include_lib("pcep_server/include/pcep_server.hrl"). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API Functions +-export([start_link/0]). +-export([get_flows/0]). +-export([update_flow/2]). + +% Handler Functions +-export([session_opened/3]). +-export([flow_added/1]). +-export([request_route/1]). +-export([flow_status_changed/2]). + +% Behaviour gen_server functions +-export([init/1]). +-export([handle_call/3]). +-export([handle_cast/2]). +-export([handle_continue/2]). +-export([handle_info/2]). +-export([code_change/3]). +-export([terminate/2]). + + +%%% RECORDS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(sess, { + id, + caps, + monref, + pid +}). + +-record(state, { + bouncer, + sessions = #{}, + sess_pids = #{}, + flows = #{} +}). + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +get_flows() -> + gen_server:call(?MODULE, get_flows). + +update_flow(FlowId, LabelStack) -> + gen_server:call(?MODULE, {update_flow, FlowId, LabelStack}). + + +%%% HANDLER FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +session_opened(Id, Caps, Pid) -> + gen_server:call(?MODULE, {session_opened, Id, Caps, Pid}). + +flow_added(Flow) -> + gen_server:call(?MODULE, {flow_added, Flow}). + +request_route(RouteReq) -> + gen_server:call(?MODULE, {request_route, RouteReq}). + +flow_status_changed(FlowId, NewStatus) -> + gen_server:call(?MODULE, {flow_status_changed, FlowId, NewStatus}). + + +%%% BEHAVIOUR gen_server FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([]) -> + {ok, bouncer_start(#state{})}. + +handle_call(get_flows, _From, #state{flows = Flows} = State) -> + {reply, {ok, Flows}, State}; +handle_call({update_flow, FlowId, Labels}, From, + #state{flows = Flows, sessions = SessMap} = State) -> + case maps:find(FlowId, Flows) of + error -> {reply, {error, flow_not_found}, State}; + {ok, #{owner := Owner, route := #{} = R}} -> + case maps:find(Owner, SessMap) of + error -> {reply, {error, session_not_found}, State}; + {ok, #sess{pid = Pid}} -> + #{source := S, destination := D, constraints := C} = R, + ReqRoute = route_from_labels(S, D, C, Labels), + session_update_flow(State, Pid, FlowId, ReqRoute, From), + {noreply, State} + end + end; +handle_call({session_opened, Id, Caps, Pid}, _From, + #state{sessions = SessMap, sess_pids = SessPids} = State) -> + logger:debug("Session with capabilities ~w open to ~w", [Caps, Id]), + case maps:find(Id, SessMap) of + {ok, _} -> {reply, {error, already_opened}, State}; + error -> + MonRef = erlang:monitor(process, Pid), + SessRec = #sess{id = Id, caps = Caps, monref = MonRef, pid = Pid}, + {reply, ok, State#state{ + sessions = SessMap#{Id => SessRec}, + sess_pids = SessPids#{Pid => SessRec} + }} + end; +handle_call({flow_added, #{id := Id, route := Route} = Flow}, + _From, #state{flows = Flows} = State) -> + logger:debug("Flow ~w with route ~w added", [Id, route_to_labels(Route)]), + {reply, ok, State#state{flows = Flows#{Id => Flow}}}; +handle_call({request_route, RouteReq}, _From, State) -> + logger:info("Route from ~w to ~w requested", + [maps:get(source, RouteReq), maps:get(destination, RouteReq)]), + #{source := S, destination := D, constraints := C} = RouteReq, + case compute_path(S, D) of + {error, _Reason} = Error -> + {reply, Error, State}; + {ok, Labels} -> + Route = route_from_labels(S, D, C, Labels), + {reply, {ok, Route}, State} + end; +handle_call({flow_status_changed, FlowId, NewStatus}, _From, + #state{flows = Flows} = State) -> + logger:info("Flow ~w status changed to ~w", [FlowId, NewStatus]), + Flow = maps:get(FlowId, Flows), + {reply, ok, State#state{ + flows = maps:put(FlowId, Flow#{status := NewStatus}, Flows)}}; +handle_call(Request, _From, State) -> + logger:warning("Unexpected request ~w", [Request]), + {reply, {error, unexpected_call}, State}. + + +handle_cast(_Request, State) -> + {noreply, State}. + +handle_continue(_Continue, State) -> + {noreply, State}. + +handle_info({flow_updated, FlowId, NewRoute, From}, + #state{flows = Flows} = State) -> + logger:info("Flow ~w updated to ~w", [FlowId, route_to_labels(NewRoute)]), + case maps:find(FlowId, Flows) of + error -> {noreply, State}; + {ok, Flow} -> + Flows2 = Flows#{FlowId => Flow#{route => NewRoute}}, + gen_server:reply(From, ok), + {noreply, State#state{flows = Flows2}} + end; +handle_info({flow_update_error, FlowId, Reason, From}, State) -> + logger:error("Flow ~w updated error: ~w", [FlowId, Reason]), + gen_server:reply(From, {error, Reason}), + {noreply, State}; +handle_info({'DOWN', MonRef, process, Pid, _Reason}, + #state{sessions = SessMap, sess_pids = PidMap} = State) -> + case maps:take(Pid, PidMap) of + {#sess{id = Id, monref = MonRef}, PidMap2} -> + SessMap2 = maps:remove(Id, SessMap), + %TODO: Do something about the flows from this session ? + {noreply, State#state{ + sessions = SessMap2, + sess_pids = PidMap2 + }}; + _X -> + {noreply, State} + end; +handle_info(_Info, State) -> + {noreply, State}. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +terminate(_Reason, _State) -> + ok. + + +%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +compute_path({1, 1, 1, 1}, {6, 6, 6, 6}) -> + {ok, [16020, 16040, 16060]}; +compute_path({6, 6, 6, 6}, {1, 1, 1, 1}) -> + {ok, [16040, 16020, 16010]}; +compute_path(_Src, _Dst) -> + {error, nopath}. + +route_from_labels(Source, Destination, Constraints, Labels) -> + #{ + source => Source, + destination => Destination, + constraints => Constraints, + steps => [ + #{ + is_loose => false, + nai_type => absent, + sid => #mpls_stack_entry{label = L} + } + || L <- Labels + ] + }. + +route_to_labels(#{steps := Steps}) -> + [Sid#mpls_stack_entry.label || #{sid := Sid} <- Steps]. + + +%-- Session Interface Functions ------------------------------------------------ + +session_update_flow(#state{bouncer = Pid}, SessPid, FlowId, Route, Args) -> + Pid ! {update_flow, SessPid, FlowId, Route, Args}. + +bouncer_start(#state{bouncer = undefined} = State) -> + Self = self(), + Pid = erlang:spawn_link(fun() -> + bouncer_bootstrap(Self) + end), + receive bouncer_ready -> ok end, + State#state{bouncer = Pid}. + +bouncer_bootstrap(Parent) -> + Parent ! bouncer_ready, + bouncer_loop(Parent). + +bouncer_loop(Parent) -> + receive + {update_flow, SessPid, FlowId, ReqRoute, Args} -> + case pcep_server_session:update_flow(SessPid, FlowId, ReqRoute) of + {ok, NewRoute} -> + Parent ! {flow_updated, FlowId, NewRoute, Args}, + bouncer_loop(Parent); + {error, Reason} -> + Parent ! {flow_update_error, FlowId, Reason, Args}, + bouncer_loop(Parent) + end + end. diff --git a/src/te/apps/epce/src/epce_sup.erl b/src/te/apps/epce/src/epce_sup.erl new file mode 100644 index 000000000..21a6a56b2 --- /dev/null +++ b/src/te/apps/epce/src/epce_sup.erl @@ -0,0 +1,37 @@ +-module(epce_sup). + +-behaviour(supervisor). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Behaviour supervisor functions +-export([start_link/0]). +-export([init/1]). + + +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(SERVER, ?MODULE). +-define(PCE_SERVER, epce_server). + + +%%% BEHAVIOUR SUPERVISOR FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +init([]) -> + SupFlags = #{ + strategy => one_for_all, + intensity => 0, + period => 1 + }, + ServerSpec = #{ + id => ?PCE_SERVER, + start => {?PCE_SERVER, start_link, []}, + restart => permanent, + shutdown => brutal_kill + }, + {ok, {SupFlags, [ServerSpec]}}. + diff --git a/src/te/apps/tfte/src/tfte.app.src b/src/te/apps/tfte/src/tfte.app.src index 76d397939..06adeb848 100644 --- a/src/te/apps/tfte/src/tfte.app.src +++ b/src/te/apps/tfte/src/tfte.app.src @@ -6,7 +6,8 @@ {applications, [kernel, stdlib, - tfpb + tfpb, + epce ]}, {env,[]}, {modules, []}, diff --git a/src/te/config/dev.config.template b/src/te/config/dev.config.template index 658bf13f8..5897c4e46 100644 --- a/src/te/config/dev.config.template +++ b/src/te/config/dev.config.template @@ -1,9 +1,17 @@ [ {tfte, [ {services, [ - {te, [{http, "localhost", 11010, []}], #{}}, + {te, [{http, "localhost", 11010, []}], #{}} + ]} + ]}, + + {pcep_server, [ + {handler, {epce_pcep_server_handler, []}} ]}, + {epce, [ + ]}, + {grpcbox, [ {servers, [#{ grpc_opts => #{ diff --git a/src/te/config/sys.config.src b/src/te/config/sys.config.src index 2bd5cf178..93bfbafb1 100644 --- a/src/te/config/sys.config.src +++ b/src/te/config/sys.config.src @@ -1,4 +1,5 @@ [ + {tfte, [ {services, [ {te, [ @@ -25,6 +26,13 @@ ]} ]}, + {pcep_server, [ + {handler, {epce_pcep_server_handler, []}} + ]}, + + {epce, [ + ]}, + {grpcbox, [ {servers, [#{ grpc_opts => #{ diff --git a/src/te/rebar.config b/src/te/rebar.config index 1063fd38c..3451955c2 100644 --- a/src/te/rebar.config +++ b/src/te/rebar.config @@ -1,16 +1,25 @@ {erl_opts, [debug_info]}. -{deps, [grpcbox]}. +{deps, [ + grpcbox, + {pcep_server, {git, "git@github.com:stritzinger/pcep_server.git", {branch, "master"}}} +]}. {shell, [ {config, "config/dev.config"}, - {apps, [tfte, tfpb, grpcbox]} + {apps, [epce, tfte, tfpb, grpcbox]} ]}. {project_app_dirs, ["apps/*", "../../proto/src/erlang"]}. {relx, [ - {release, {tfte, "1.0.0"}, [tfte, tfpb, grpcbox]}, + {release, {tfte, "1.0.0"}, [ + runtime_tools, + epce, + grpcbox, + tfpb, + tfte + ]}, {vm_args_src, "config/vm.args.src"}, {sys_config_src, "config/sys.config.src"}, {dev_mode, true}, diff --git a/src/te/rebar.lock b/src/te/rebar.lock index d353eaf34..6eb067ecd 100644 --- a/src/te/rebar.lock +++ b/src/te/rebar.lock @@ -1,10 +1,23 @@ {"1.2.0", [{<<"acceptor_pool">>,{pkg,<<"acceptor_pool">>,<<"1.0.0">>},1}, {<<"chatterbox">>,{pkg,<<"ts_chatterbox">>,<<"0.12.0">>},1}, + {<<"codec_sequencer">>, + {git,"git@github.com:stritzinger/codec_sequencer.git", + {ref,"fc8760894f7962ef1497bf6ce4247eb75db9d5ca"}}, + 2}, {<<"ctx">>,{pkg,<<"ctx">>,<<"0.6.0">>},1}, {<<"gproc">>,{pkg,<<"gproc">>,<<"0.8.0">>},1}, {<<"grpcbox">>,{pkg,<<"grpcbox">>,<<"0.15.0">>},0}, - {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}]}. + {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}, + {<<"pcep_codec">>, + {git,"git@github.com:stritzinger/pcep_codec.git", + {ref,"3d1623fdf0c62d3daf400ac65aaf985f8bd40835"}}, + 1}, + {<<"pcep_server">>, + {git,"git@github.com:stritzinger/pcep_server.git", + {ref,"2cf692e7e5fa2e9ac0fd54e5aa64ffb17f4f1b4a"}}, + 0}, + {<<"ranch">>,{pkg,<<"ranch">>,<<"2.0.0">>},1}]}. [ {pkg_hash,[ {<<"acceptor_pool">>, <<"43C20D2ACAE35F0C2BCD64F9D2BDE267E459F0F3FD23DAB26485BF518C281B21">>}, @@ -12,12 +25,14 @@ {<<"ctx">>, <<"8FF88B70E6400C4DF90142E7F130625B82086077A45364A78D208ED3ED53C7FE">>}, {<<"gproc">>, <<"CEA02C578589C61E5341FCE149EA36CCEF236CC2ECAC8691FBA408E7EA77EC2F">>}, {<<"grpcbox">>, <<"97C7126296A091602D372EBF5860A04F7BC795B45B33A984CAD2B8E362774FD8">>}, - {<<"hpack">>, <<"17670F83FF984AE6CD74B1C456EDDE906D27FF013740EE4D9EFAA4F1BF999633">>}]}, + {<<"hpack">>, <<"17670F83FF984AE6CD74B1C456EDDE906D27FF013740EE4D9EFAA4F1BF999633">>}, + {<<"ranch">>, <<"FBF3D79661C071543256F9051CAF19D65DAA6DF1CF6824D8F37A49B19A66F703">>}]}, {pkg_hash_ext,[ {<<"acceptor_pool">>, <<"0CBCD83FDC8B9AD2EEE2067EF8B91A14858A5883CB7CD800E6FCD5803E158788">>}, {<<"chatterbox">>, <<"6478C161BC60244F41CD5847CC3ACCD26D997883E9F7FACD36FF24533B2FA579">>}, {<<"ctx">>, <<"A14ED2D1B67723DBEBBE423B28D7615EB0BDCBA6FF28F2D1F1B0A7E1D4AA5FC2">>}, {<<"gproc">>, <<"580ADAFA56463B75263EF5A5DF4C86AF321F68694E7786CB057FD805D1E2A7DE">>}, {<<"grpcbox">>, <<"161ABE9E17E7D1982EFA6488ADEAA13C3E847A07984A6E6B224E553368918647">>}, - {<<"hpack">>, <<"06F580167C4B8B8A6429040DF36CC93BBA6D571FAEAEC1B28816523379CBB23A">>}]} + {<<"hpack">>, <<"06F580167C4B8B8A6429040DF36CC93BBA6D571FAEAEC1B28816523379CBB23A">>}, + {<<"ranch">>, <<"C20A4840C7D6623C19812D3A7C828B2F1BD153EF0F124CB69C54FE51D8A42AE0">>}]} ]. diff --git a/tutorial/2-6-netgen-config.yml b/tutorial/2-6-netgen-config.yml new file mode 100644 index 000000000..7f1de319f --- /dev/null +++ b/tutorial/2-6-netgen-config.yml @@ -0,0 +1,101 @@ +# Directory used for Netgen's operation. +# Default: "/tmp/netgen" +# netgen_runstatedir: + +# Clean exit. +# Default: 'false' +# clean_exit: + +# Valgrind parameters. +# Default: "--tool=memcheck" +# valgrind_params: "--tool=memcheck --leak-check=full --trace-children=yes" +# valgrind_params: "--tool=memcheck --leak-check=full" +# valgrind_params: "--tool=memcheck --leak-check=full --show-leak-kinds=all" +# valgrind_params: "--tool=callgrind --dump-instr=yes --collect-jumps=yes" + +# Perf directory +# Default: [netgen_runstatedir]/perf +# perf_dir: + +# Plugins configuration. +plugins: + frr: + # FRR's sysconfdir (--sysconfdir). + # Default: "/etc/frr" + # sysconfdir: + + # FRR's localstatedir (--localstatedir). + # Default: "/var/run/frr" + # localstatedir: + + # FRR's user (--enable-user). + # Default: "frr" + # user: + user: "root" + + # FRR's group (--enable-group). + # Default: "frr" + # group: + group: "root" + + # Directory to store FRR logs. + # Default: [netgen_runstatedir]/frrlogs + # logdir: + + tcpdump: + # Directory to store tcpdump captures. + # Default: [netgen_runstatedir]/pcaps + # pcap_dir: + + # Filter on which nodes tcpdump should run. + # Default: [] + # whitelist: + + # Filter on which nodes tcpdump should not run. + # Default: [] + # blacklist: + + tmux: + # Path of tmux script used to open a shell on all routers. + # Default: [netgen_runstatedir]/tmux.sh + # file: + + # Panels per node. + # Default: 1 + # panels-per-node: + + bird: + # BIRD's sysconfdir (--sysconfdir). + # Default: "/etc/bird" + # sysconfdir: + + # BIRD's localstatedir (--localstatedir). + # Default: "/var/run/bird" + # localstatedir: + + # BIRD's user (--enable-user). + # Default: "bird" + # user: + + # BIRD's group (--enable-group). + # Default: "bird" + # group: + + # Directory to store BIRD logs. + # Default: [netgen_runstatedir]/birdlogs + # logdir: + + bgpsimple: + # Path to bgp_simple script + # Default: "bgp_simple.pl" + # path: + + iou: + # IOU working directory. + # Default: [netgen_runstatedir]/iou + # dir: + + dynamips: + # dynamips working directory. + # Default: [netgen_runstatedir]/dynamips + # dir: diff --git a/tutorial/2-6-netgen-topology.yml.template b/tutorial/2-6-netgen-topology.yml.template new file mode 100644 index 000000000..286f40005 --- /dev/null +++ b/tutorial/2-6-netgen-topology.yml.template @@ -0,0 +1,545 @@ +# +# +---------+ +# | | +# | SRC | +# | 9.9.9.1 | +# | | +# +---------+ +# |eth-rt1 (.1) +# | +# |10.0.10.0/24 +# | +# |eth-src (.2) +# +---------+ . +# | | . +# | RT1 |eth-rt1-pce (???) . +# | 1.1.1.1 +----------------------------------+ +# | 16010 | . ??? | +# +---------+ . | +# |eth-sw1 . | +# | . | +# | . | +# | . | +# +---------+ | +---------+ . | +# | | | | | . | +# | RT2 |eth-sw1 | eth-sw1| RT3 | . | +# | 2.2.2.2 +----------+----------+ 3.3.3.3 | . | +# | 16020 | 10.0.1.0/24 | 16030 | . | +# +---------+ +---------+ .eth-pce-rt1|(???) +# eth-rt4-1| |eth-rt4-2 eth-rt5-1| |eth-rt5-2. +----+----+ +# | | | | . | | +# 10.0.2.0/24| |10.0.3.0/24 10.0.4.0/24| |10.0.5.0/24 | PCE | +# | | | | . | ???? | +# eth-rt2-1| |eth-rt2-2 eth-rt3-1| |eth-rt3-2. | | +# +---------+ +---------+ . +----+----+ +# | | | | .eth-pce-rt6|(???) +# | RT4 | 10.0.6.0/24 | RT5 | . | +# | 4.4.4.4 +---------------------+ 5.5.5.5 | . | +# | 16040 |eth-rt5 eth-rt4| 16050 | . | +# +---------+ +---------+ . | +# eth-rt6| |eth-rt6 . | +# | | . | +# 10.0.7.0/24| |10.0.8.0/24 | +# | +---------+ | . | +# +----------| |-----------+ . | +# eth-rt4| RT6 |eth-rt5 . | +# | 6.6.6.6 |eth-rt6-pce (????) . ???? | +# | 16060 +----------------------------------+ +# +---------+ . +# |eth-dst (.1) . +# | . +# |10.0.11.0/24 +# | +# |eth-rt6 (.2) +# +---------+ +# | | +# | DST | +# | 9.9.9.2 | +# | | +# +---------+ +# + +--- + +routers: + + src: + links: + lo: + ipv4: 9.9.9.1/32 + ipv6: 2001:db8:1066::1/128 + mpls: yes + eth-rt1: + peer: [rt1, eth-src] + ipv4: 10.0.10.1/24 + mpls: yes + frr: + zebra: + run: yes + config: + shell: | + ip route add 9.9.9.2/32 encap mpls 1111 via inet 10.0.10.2 src 9.9.9.1 + + rt1: + links: + lo: + ipv4: 1.1.1.1/32 + mpls: yes + eth-sw1: + peer: [sw1, sw1-rt1] + ipv4: 10.0.1.1/24 + mpls: yes + eth-src: + peer: [src, eth-rt1] + ipv4: 10.0.10.2/24 + mpls: yes + frr: + zebra: + run: yes + config: + pathd: + args: "-M pathd_pcep" + config: | + debug pathd pcep basic + segment-routing + traffic-eng + mpls-te on + policy color 1 endpoint 6.6.6.6 + name DEFAULT + binding-sid 1111 + candidate-path preference 100 name RUNTIME dynamic + ! + ! + pcep + pce-config CONFIG + source-address ip 1.1.1.1 + pce PCE + address ip ${PCE_IP} + config CONFIG + pcc + peer PCE + ! + isisd: + run: yes + config: | + interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive + ! + interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + ! + router isis 1 + net 49.0000.0000.0000.0001.00 + is-type level-1 + redistribute ipv4 static level-1 + redistribute ipv4 connected level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.1/32 index 10 explicit-null + segment-routing prefix 2001:db8:1000::1/128 index 11 explicit-null + ! + shell: | + ip route add 9.9.9.1/32 dev eth-src + ip link add eth-rt1-pce type veth peer name eth-pce-rt1 + ip addr add ${RT1_PCE_INT_IF_IP}/24 dev eth-rt1-pce + ip link set eth-pce-rt1 netns ${PCE_NETNS} + ip -n ${PCE_NETNS} addr add ${RT1_PCE_EXT_IF_IP}/24 dev eth-pce-rt1 + ip link set eth-rt1-pce up + ip -n ${PCE_NETNS} link set eth-pce-rt1 up + ip route add ${RT1_PCE_EXT_IF_IP}/24 via ${RT1_PCE_INT_IF_IP} dev eth-rt1-pce src 1.1.1.1 + ip -n ${PCE_NETNS} route add ${RT1_PCE_INT_IF_IP}/32 via ${RT1_PCE_EXT_IF_IP} dev eth-pce-rt1 + ip -n ${PCE_NETNS} route add 1.1.1.1/32 via ${RT1_PCE_EXT_IF_IP} dev eth-pce-rt1 + + rt2: + links: + lo: + ipv4: 2.2.2.2/32 + ipv6: 2001:db8:1000::2/128 + mpls: yes + eth-sw1: + peer: [sw1, sw1-rt2] + ipv4: 10.0.1.2/24 + mpls: yes + eth-rt4-1: + peer: [rt4, eth-rt2-1] + ipv4: 10.0.2.2/24 + mpls: yes + eth-rt4-2: + peer: [rt4, eth-rt2-2] + ipv4: 10.0.3.2/24 + mpls: yes + frr: + zebra: + run: yes + config: + isisd: + run: yes + config: | + interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive + ! + interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + ! + interface eth-rt4-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt4-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + router isis 1 + net 49.0000.0000.0000.0002.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 2.2.2.2/32 index 20 no-php-flag + segment-routing prefix 2001:db8:1000::2/128 index 21 no-php-flag + ! + + rt3: + links: + lo: + ipv4: 3.3.3.3/32 + ipv6: 2001:db8:1000::3/128 + mpls: yes + eth-sw1: + peer: [sw1, sw1-rt3] + ipv4: 10.0.1.3/24 + mpls: yes + eth-rt5-1: + peer: [rt5, eth-rt3-1] + ipv4: 10.0.4.3/24 + mpls: yes + eth-rt5-2: + peer: [rt5, eth-rt3-2] + ipv4: 10.0.5.3/24 + mpls: yes + frr: + zebra: + run: yes + config: + isisd: + run: yes + config: | + interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive + ! + interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + ! + interface eth-rt5-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt5-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + router isis 1 + net 49.0000.0000.0000.0003.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 3.3.3.3/32 index 30 no-php-flag + segment-routing prefix 2001:db8:1000::3/128 index 31 no-php-flag + ! + + rt4: + links: + lo: + ipv4: 4.4.4.4/32 + ipv6: 2001:db8:1000::4/128 + mpls: yes + eth-rt2-1: + peer: [rt2, eth-rt4-1] + ipv4: 10.0.2.4/24 + mpls: yes + eth-rt2-2: + peer: [rt2, eth-rt4-2] + ipv4: 10.0.3.4/24 + mpls: yes + eth-rt5: + peer: [rt5, eth-rt4] + ipv4: 10.0.6.4/24 + mpls: yes + eth-rt6: + peer: [rt6, eth-rt4] + ipv4: 10.0.7.4/24 + mpls: yes + frr: + zebra: + run: yes + config: + isisd: + run: yes + config: | + interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive + ! + interface eth-rt2-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt2-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt6 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + router isis 1 + net 49.0000.0000.0000.0004.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 4.4.4.4/32 index 40 no-php-flag + segment-routing prefix 2001:db8:1000::4/128 index 41 no-php-flag + ! + + rt5: + links: + lo: + ipv4: 5.5.5.5/32 + ipv6: 2001:db8:1000::5/128 + mpls: yes + eth-rt3-1: + peer: [rt3, eth-rt5-1] + ipv4: 10.0.4.5/24 + mpls: yes + eth-rt3-2: + peer: [rt3, eth-rt5-2] + ipv4: 10.0.5.5/24 + mpls: yes + eth-rt4: + peer: [rt4, eth-rt5] + ipv4: 10.0.6.5/24 + mpls: yes + eth-rt6: + peer: [rt6, eth-rt5] + ipv4: 10.0.8.5/24 + mpls: yes + frr: + zebra: + run: yes + config: + isisd: + run: yes + config: | + interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive + ! + interface eth-rt3-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt3-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt6 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + router isis 1 + net 49.0000.0000.0000.0005.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 5.5.5.5/32 index 50 no-php-flag + segment-routing prefix 2001:db8:1000::5/128 index 51 no-php-flag + ! + + rt6: + links: + lo: + ipv4: 6.6.6.6/32 + ipv6: 2001:db8:1000::6/128 + mpls: yes + eth-rt4: + peer: [rt4, eth-rt6] + ipv4: 10.0.7.6/24 + mpls: yes + eth-rt5: + peer: [rt5, eth-rt6] + ipv4: 10.0.8.6/24 + mpls: yes + eth-dst: + peer: [dst, eth-rt6] + ipv4: 10.0.11.1/24 + mpls: yes + frr: + zebra: + run: yes + config: + pathd: + args: "-M pathd_pcep" + config: | + debug pathd pcep + segment-routing + traffic-eng + policy color 1 endpoint 1.1.1.1 + name DEFAULT + binding-sid 6666 + candidate-path preference 200 name RUNTIME dynamic + ! + ! + pcep + pce-config CONFIG + source-address ip 6.6.6.6 + pce PCE + address ip ${PCE_IP} + config CONFIG + pcc + peer PCE + ! + isisd: + run: yes + config: | + interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive + ! + interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + router isis 1 + net 49.0000.0000.0000.0006.00 + is-type level-1 + redistribute ipv4 static level-1 + redistribute ipv4 connected level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 6.6.6.6/32 index 60 explicit-null + segment-routing prefix 2001:db8:1000::6/128 index 61 explicit-null + ! + shell: | + ip route add 9.9.9.2/32 dev eth-dst + ip link add eth-rt6-pce type veth peer name eth-pce-rt6 + ip addr add ${RT6_PCE_INT_IF_IP}/24 dev eth-rt6-pce + ip link set eth-pce-rt6 netns ${PCE_NETNS} + ip -n ${PCE_NETNS} addr add ${RT6_PCE_EXT_IF_IP}/24 dev eth-pce-rt6 + ip link set eth-rt6-pce up + ip -n ${PCE_NETNS} link set eth-pce-rt6 up + ip route add ${RT6_PCE_EXT_IF_IP}/24 via ${RT6_PCE_INT_IF_IP} dev eth-rt6-pce src 6.6.6.6 + ip -n ${PCE_NETNS} route add ${RT6_PCE_INT_IF_IP}/32 via ${RT6_PCE_EXT_IF_IP} dev eth-pce-rt6 + ip -n ${PCE_NETNS} route add 6.6.6.6/32 via ${RT6_PCE_EXT_IF_IP} dev eth-pce-rt6 + + dst: + links: + lo: + ipv4: 9.9.9.2/32 + ipv6: 2001:db8:1066::2/128 + mpls: yes + eth-rt6: + peer: [rt6, eth-dst] + ipv4: 10.0.11.2/24 + mpls: yes + frr: + zebra: + run: yes + config: + shell: | + ip route add 9.9.9.1/32 encap mpls 6666 via inet 10.0.11.1 + +switches: + sw1: + links: + sw1-rt1: + peer: [rt1, rt1-sw1] + sw1-rt2: + peer: [rt2, rt2-sw1] + sw1-rt3: + peer: [rt3, rt3-sw1] + +frr: + #perf: yes + #valgrind: yes + base-configs: + all: | + hostname %(node) + password 1 + log file %(logdir)/%(node)-%(daemon).log + log commands + zebra: | + debug zebra kernel + debug zebra packet + debug zebra mpls + isisd: | + debug isis events + debug isis route-events + debug isis spf-events + debug isis sr-events + debug isis lsp-gen diff --git a/tutorial/2-6-te-demo-start-testbed.sh b/tutorial/2-6-te-demo-start-testbed.sh new file mode 100755 index 000000000..dea41a71a --- /dev/null +++ b/tutorial/2-6-te-demo-start-testbed.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +set -e + +ROOTDIR="$( cd $( dirname $0 ); pwd )" +RUNDIR="$( pwd )" +NEGENDIR="${RUNDIR}/netgen" + +if [[ ! -f "${NEGENDIR}/exe/netgen" ]]; then + echo "Failed to find Netgen binary at ${NEGENDIR}/exe/netgen" + exit 1 +fi + +export PCE_NETNS="$1" +export PCE_IP="$2" +RT1_INT_IP="$3" +RT1_EXT_IP="$4" +RT6_INT_IP="$5" +RT6_EXT_IP="$6" + + +if [[ -z $PCE_NETNS || -z $PCE_IP || -z RT1_INT_IP || -z RT1_EXT_IP || -z RT6_INT_IP || -z RT6_EXT_IP ]]; then + echo "USAGE: $0 PCE_NETNS PCE_IP RT1_INT_IP RT1_EXT_IP RT6_INT_IP RT6_EXT_IP" + echo " e.g: $0 cni-588a2d06-e64f-907b-d51b-bed0307007c9 10.1.103.133 10 11 12 13" + exit 1 +fi + +IFS=. read PCE_IP1 PCE_IP2 PCE_IP3 PCE_IP4 <<< "$PCE_IP" + +export RT1_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT1_INT_IP" +export RT1_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT1_EXT_IP" +export RT6_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT6_INT_IP" +export RT6_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT6_EXT_IP" + +cp "${ROOTDIR}/2-6-netgen-config.yml" "${RUNDIR}/config.yml" +cat "${ROOTDIR}/2-6-netgen-topology.yml.template" | envsubst > "${RUNDIR}/topology.yml" + +sudo -i bash -c "\ + cd ${RUNDIR}/netgen;\ + sysctl -w net.ipv6.conf.all.disable_ipv6=1;\ + sysctl -w net.ipv6.conf.default.disable_ipv6=1;\ + sysctl -w net.ipv4.conf.all.rp_filter=0;\ + PATH=/usr/lib/frr:\$PATH ./exe/netgen ../topology.yml -c ../config.yml" diff --git a/tutorial/2-6-te-demo.md b/tutorial/2-6-te-demo.md new file mode 100644 index 000000000..6a99007b4 --- /dev/null +++ b/tutorial/2-6-te-demo.md @@ -0,0 +1,97 @@ +# 2.6. Traffic Engineering Demo (PENDING) + +## Setup Test-Bed + +### Setup libyang + + $ sudo apt update + $ sudo apt-get install cmake libpcre2-dev git make build-essential + $ mkdir -p ~/testbed + $ cd ~/testbed + $ git clone git@github.com:CESNET/libyang.git + $ cd libyang + $ git checkout v2.0.0 + $ mkdir build; cd build + $ cmake -D CMAKE_INSTALL_PREFIX:PATH=/usr -D CMAKE_BUILD_TYPE:String="Release" .. + $ make + $ sudo make install + + +### Setup Free Range Routing + + $ sudo apt update + $ sudo apt-get install git autoconf automake libtool make libreadline-dev texinfo pkg-config libpam0g-dev libjson-c-dev bison flex libc-ares-dev python3-dev python3-sphinx install-info build-essential libsnmp-dev perl libcap-dev python2 libelf-dev libunwind-dev protobuf-c-compiler libprotobuf-c-dev libsystemd-dev + $ mkdir -p ~/testbed + $ cd ~/testbed + $ git clone git@github.com:opensourcerouting/frr.git + $ cd frr + $ curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py + $ sudo python2 ./get-pip.py + $ export CFLAGS="-I /usr/local/include -g -O2" + $ sudo rm -rf /usr/lib/frr + $ sudo rm -rf /var/run/frr + $ sudo mkdir -p /etc/frr + $ sudo mkdir -p /var/run/frr + $ sudo chown -R root:root /etc/frr + $ ./bootstrap.sh + $ ./configure \ + --prefix=/usr \ + --includedir=\${prefix}/include \ + --enable-exampledir=\${prefix}/share/doc/frr/examples \ + --bindir=\${prefix}/bin \ + --sbindir=\${prefix}/lib/frr \ + --libdir=\${prefix}/lib/frr \ + --libexecdir=\${prefix}/lib/frr \ + --localstatedir=/var/run/frr \ + --sysconfdir=/etc/frr \ + --with-moduledir=\${prefix}/lib/frr/modules \ + --enable-configfile-mask=0640 \ + --enable-logfile-mask=0640 \ + --enable-snmp=agentx \ + --enable-multipath=64 \ + --enable-user=root \ + --enable-group=root \ + --enable-vty-group=root \ + --enable-vtysh \ + --with-pkg-git-version \ + --with-pkg-extra-version=-MyOwnFRRVersion \ + --enable-systemd=yes \ + --enable-config-rollbacks \ + --enable-pathd \ + --enable-pcep + $ make + $ sudo make install + + +### Setup NetGen + + $ sudo apt update + $ sudo apt-get install git ruby ruby-dev tmux gettext-base + $ mkdir -p ~/testbed + $ cd ~/testbed + $ git clone git@github.com:sylane/netgen.git + $ cd netgen + $ git checkout teraflow + $ sudo gem install bundler:1.15 + $ bundle _1.15_ install + + +### Run the Test-Bed + +To start the testbed, we need to figure out the IP of the TE service: + $ kubectl --namespace tfs get $(kubectl --namespace tfs get pods --selector=app=teservice -o name) --template '{{.status.podIP}}' + +And select the network namespace that defines it by looking into each of them until you find the correct one: + $ ip netns list + $ sudo ip -n XXX addr list + +When we have the IP and namespace we can start the testbed. + +In first console: + $ cd ~/testbed + $ ../tfs-ctrl/tutorial/2-6-te-demo-start-testbed.sh NETWORK_NAMESPACE TE_SERVICE_IP 10 11 12 13 + +Then in second console: + $ sudo -i + # cd /tmp/negen + # ./tmux.sh \ No newline at end of file -- GitLab From fd926bbbf799363c998f7b2ad469bf6ab15366aa Mon Sep 17 00:00:00 2001 From: merle Date: Fri, 23 Sep 2022 09:37:50 -0700 Subject: [PATCH 04/63] Retrieve the topology from Context service and add a TED - Add _connect/mpls_label and _connect/pcc_address to the allowed device configuration. --- proto/context.proto | 1 + .../database/models/enums/ServiceType.py | 1 + src/te/apps/epce/src/epce_server.erl | 16 +- src/te/apps/epce/src/epce_sup.erl | 18 +- src/te/apps/epce/src/epce_ted.erl | 203 +++++++++ src/te/apps/tfte/src/tfte_app.erl | 10 +- src/te/apps/tfte/src/tfte_context.erl | 162 +++++++ src/te/apps/tfte/src/tfte_server.erl | 100 +++++ src/te/apps/tfte/src/tfte_service_sup.erl | 50 +++ src/te/apps/tfte/src/tfte_sup.erl | 24 +- src/te/apps/tfte/src/tfte_te_service.erl | 6 +- src/te/apps/tfte/src/tfte_topology.erl | 394 ++++++++++++++++++ src/te/config/sys.config.src | 4 +- tutorial/2-6-te-demo-start-testbed.sh | 36 +- tutorial/2-6-te-demo.md | 14 +- tutorial/2-6-teraflow-topology.json | 210 ++++++++++ 16 files changed, 1196 insertions(+), 53 deletions(-) create mode 100644 src/te/apps/epce/src/epce_ted.erl create mode 100644 src/te/apps/tfte/src/tfte_context.erl create mode 100644 src/te/apps/tfte/src/tfte_server.erl create mode 100644 src/te/apps/tfte/src/tfte_service_sup.erl create mode 100644 src/te/apps/tfte/src/tfte_topology.erl create mode 100644 tutorial/2-6-teraflow-topology.json diff --git a/proto/context.proto b/proto/context.proto index 3104f1b54..dbc7fecb1 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -272,6 +272,7 @@ enum ServiceTypeEnum { SERVICETYPE_L3NM = 1; SERVICETYPE_L2NM = 2; SERVICETYPE_TAPI_CONNECTIVITY_SERVICE = 3; + SERVICETYPE_TE = 4; } enum ServiceStatusEnum { diff --git a/src/context/service/database/models/enums/ServiceType.py b/src/context/service/database/models/enums/ServiceType.py index 668133abf..3937eaa11 100644 --- a/src/context/service/database/models/enums/ServiceType.py +++ b/src/context/service/database/models/enums/ServiceType.py @@ -21,6 +21,7 @@ class ORM_ServiceTypeEnum(enum.Enum): L3NM = ServiceTypeEnum.SERVICETYPE_L3NM L2NM = ServiceTypeEnum.SERVICETYPE_L2NM TAPI_CONNECTIVITY_SERVICE = ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE + TE = ServiceTypeEnum.SERVICETYPE_TE grpc_to_enum__service_type = functools.partial( grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum) diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl index bd3a13f44..11a1625ef 100644 --- a/src/te/apps/epce/src/epce_server.erl +++ b/src/te/apps/epce/src/epce_server.erl @@ -181,12 +181,16 @@ terminate(_Reason, _State) -> %%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -compute_path({1, 1, 1, 1}, {6, 6, 6, 6}) -> - {ok, [16020, 16040, 16060]}; -compute_path({6, 6, 6, 6}, {1, 1, 1, 1}) -> - {ok, [16040, 16020, 16010]}; -compute_path(_Src, _Dst) -> - {error, nopath}. +compute_path(From, To) -> + case epce_ted:compute_path(pcc_address, From, To) of + {ok, Devices} -> + Labels = tl([L || #{mpls_label := L} <- Devices, L =/= undefined]), + logger:debug("Route from ~p to ~p: ~p", [From, To, Labels]), + {ok, Labels}; + {error, Reason} -> + logger:warning("Failed to find a route from ~p to ~p", [From, To]), + {error, Reason} + end. route_from_labels(Source, Destination, Constraints, Labels) -> #{ diff --git a/src/te/apps/epce/src/epce_sup.erl b/src/te/apps/epce/src/epce_sup.erl index 21a6a56b2..f04e9697e 100644 --- a/src/te/apps/epce/src/epce_sup.erl +++ b/src/te/apps/epce/src/epce_sup.erl @@ -12,14 +12,14 @@ %%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --define(SERVER, ?MODULE). --define(PCE_SERVER, epce_server). +-define(TED_WORKER, epce_ted). +-define(PCE_WORKER, epce_server). %%% BEHAVIOUR SUPERVISOR FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start_link() -> - supervisor:start_link({local, ?SERVER}, ?MODULE, []). + supervisor:start_link({local, ?MODULE}, ?MODULE, []). init([]) -> SupFlags = #{ @@ -27,11 +27,17 @@ init([]) -> intensity => 0, period => 1 }, + TEDSpec = #{ + id => ?TED_WORKER, + start => {?TED_WORKER, start_link, []}, + restart => permanent, + shutdown => brutal_kill + }, ServerSpec = #{ - id => ?PCE_SERVER, - start => {?PCE_SERVER, start_link, []}, + id => ?PCE_WORKER, + start => {?PCE_WORKER, start_link, []}, restart => permanent, shutdown => brutal_kill }, - {ok, {SupFlags, [ServerSpec]}}. + {ok, {SupFlags, [TEDSpec, ServerSpec]}}. diff --git a/src/te/apps/epce/src/epce_ted.erl b/src/te/apps/epce/src/epce_ted.erl new file mode 100644 index 000000000..aaf5a4e9a --- /dev/null +++ b/src/te/apps/epce/src/epce_ted.erl @@ -0,0 +1,203 @@ +-module(epce_ted). + +-behaviour(gen_server). + + +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include_lib("kernel/include/logger.hrl"). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API Functions +-export([start_link/0]). +-export([device_added/2]). +-export([device_updated/2]). +-export([device_deleted/1]). +-export([link_added/2]). +-export([link_updated/2]). +-export([link_deleted/1]). +-export([compute_path/3]). + +-export([get_graph/0]). + +% Behaviour gen_server functions +-export([init/1]). +-export([handle_call/3]). +-export([handle_cast/2]). +-export([handle_continue/2]). +-export([handle_info/2]). +-export([code_change/3]). +-export([terminate/2]). + + +%%% RECORDS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(state, { + graph :: diagraph:graph(), + pcc_address_to_id = #{} :: map() +}). + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +device_added(Id, Device) -> + gen_server:call(?MODULE, {device_added, Id, Device}). + +device_updated(Id, Device) -> + gen_server:call(?MODULE, {device_updated, Id, Device}). + +device_deleted(Id) -> + gen_server:call(?MODULE, {device_deleted, Id}). + +link_added(Id, Link) -> + gen_server:call(?MODULE, {link_added, Id, Link}). + +link_updated(Id, Link) -> + gen_server:call(?MODULE, {link_updated, Id, Link}). + +link_deleted(Id) -> + gen_server:call(?MODULE, {link_deleted, Id}). + +compute_path(Index, From, To) + when Index =:= id; Index =:= pcc_address -> + gen_server:call(?MODULE, {compute_path, Index, From, To}); +compute_path(Index, _From, _To) -> + {error, {invalid_index, Index}}. + + +get_graph() -> + gen_server:call(?MODULE, get_graph). + + +%%% BEHAVIOUR gen_server FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([]) -> + ?LOG_INFO("Starting TED process...", []), + % {ok, #state{graph = digraph:new([private, cyclic])}}. + {ok, #state{graph = digraph:new([protected, cyclic])}}. + +handle_call({device_added, Id, Device}, _From, State) -> + ?LOG_DEBUG("Adding TED device ~p: ~p", [Id, Device]), + {reply, ok, do_update_device(State, Id, Device)}; +handle_call({device_updated, Id, Device}, _From, State) -> + ?LOG_DEBUG("Updating TED device ~p: ~p", [Id, Device]), + {reply, ok, do_update_device(State, Id, Device)}; +handle_call({device_deleted, Id}, _From, State) -> + ?LOG_DEBUG("Deleting TED device ~p", [Id]), + {reply, ok, do_delete_device(State, Id)}; +handle_call({link_added, Id, Link}, _From, State) -> + ?LOG_DEBUG("Adding TED link ~p: ~p", [Id, Link]), + {reply, ok, do_update_link(State, Id, Link)}; +handle_call({link_updated, Id, Link}, _From, State) -> + ?LOG_DEBUG("Updating TED link ~p: ~p", [Id, Link]), + {reply, ok, do_update_link(State, Id, Link)}; +handle_call({link_deleted, Id}, _From, State) -> + ?LOG_DEBUG("Deleting TED link ~p", [Id]), + {reply, ok, do_delete_link(State, Id)}; +handle_call({compute_path, Index, From, To}, _From, #state{graph = G} = State) -> + case as_ids(State, Index, [From, To]) of + {ok, [FromId, ToId]} -> + {reply, do_compute_path(G, FromId, ToId), State}; + {error, Reason} -> + {reply, {error, Reason}, State} + end; +handle_call(get_graph, _From, #state{graph = G} = State) -> + {reply, G, State}; +handle_call(Request, _From, State) -> + logger:warning("Unexpected call to TED process ~w", [Request]), + {reply, {error, unexpected_call}, State}. + +handle_cast(Request, State) -> + logger:warning("Unexpected cast to TED process ~w", [Request]), + {noreply, State}. + +handle_continue(_Continue, State) -> + {noreply, State}. + +handle_info(Info, State) -> + logger:warning("Unexpected message to TED process ~w", [Info]), + {noreply, State}. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +terminate(_Reason, _State) -> + ?LOG_INFO("Terminating TED process...", []), + ok. + + +%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +as_ids(_State, id, Keys) -> + {ok, Keys}; +as_ids(State, IndexType, Keys) -> + as_ids(State, IndexType, Keys, []). + +as_ids(_State, _IndexType, [], Acc) -> + {ok, lists:reverse(Acc)}; +as_ids(#state{pcc_address_to_id = Index} = State, pcc_address, [Key | Rest], Acc) -> + case maps:find(Key, Index) of + error -> {error, {unknown_key, Key}}; + {ok, Id} -> as_ids(State, pcc_address, Rest, [Id | Acc]) + end. + +do_update_device(#state{graph = G} = State, Id, NewDevice) -> + State2 = case digraph:vertex(G, Id) of + false -> State; + {Id, OldDevice} -> index_remove_device(State, OldDevice) + end, + digraph:add_vertex(G, Id, NewDevice), + index_add_device(State2, NewDevice). + +do_delete_device(#state{graph = G} = State, Id) -> + case digraph:vertex(G, Id) of + false -> State; + {Id, OldDevice} -> + digraph:del_vertex(G, Id), + index_remove_device(State, OldDevice) + end. + +index_remove_device(#state{pcc_address_to_id = Index} = State, + #{pcc_address := OldAddress}) -> + Index2 = maps:remove(OldAddress, Index), + State#state{pcc_address_to_id = Index2}. + +index_add_device(State, #{pcc_address := undefined}) -> + State; +index_add_device(#state{pcc_address_to_id = Index} = State, + #{id := Id, pcc_address := NewAddress}) -> + Index2 = Index#{NewAddress => Id}, + State#state{pcc_address_to_id = Index2}. + +do_update_link(#state{graph = G} = State, Id, Link) -> + #{endpoints := [EP1, EP2]} = Link, + #{device := D1} = EP1, + #{device := D2} = EP2, + digraph:add_edge(G, {Id, a}, D1, D2, Link), + digraph:add_edge(G, {Id, b}, D2, D1, Link), + State. + +do_delete_link(#state{graph = G} = State, Id) -> + digraph:del_edge(G, {Id, a}), + digraph:del_edge(G, {Id, b}), + State. + +do_compute_path(G, FromId, ToId) -> + case digraph:get_short_path(G, FromId, ToId) of + false -> {error, not_found}; + Ids -> {ok, retrieve_devices(G, Ids, [])} + end. + +retrieve_devices(_G, [], Acc) -> + lists:reverse(Acc); +retrieve_devices(G, [Id | Rest], Acc) -> + case digraph:vertex(G, Id) of + false -> {error, invalid_path}; + {Id, Device} -> + retrieve_devices(G, Rest, [Device | Acc]) + end. diff --git a/src/te/apps/tfte/src/tfte_app.erl b/src/te/apps/tfte/src/tfte_app.erl index 159197fdf..96724904d 100644 --- a/src/te/apps/tfte/src/tfte_app.erl +++ b/src/te/apps/tfte/src/tfte_app.erl @@ -8,18 +8,18 @@ -behaviour(application). -%--- Includes ------------------------------------------------------------------ +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -include_lib("kernel/include/logger.hrl"). -%--- Exports ------------------------------------------------------------------- +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Behaviour application callback functions -export([start/2, stop/1]). -%--- Behaviour application Callback Functions ---------------------------------- +%%% BEHAVIOUR applicaation CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start(_StartType, _StartArgs) -> case tfte_sup:start_link() of @@ -34,7 +34,7 @@ stop(_State) -> ok. -%--- Internal Functions -------------------------------------------------------- +%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% add_services() -> case application:get_env(tfte, services) of @@ -71,7 +71,6 @@ resolve_endpoints(Name, [{Transport, HostSpec, PortSpec, SslOpts} | Rest], Acc) resolve_host_spec(_Name, Hostname) when is_list(Hostname) -> Hostname; resolve_host_spec(Name, {env, Key}) when is_list(Key) -> - ?LOG_DEBUG("????? HOST ~s ~s -> ~p", [Name, Key, os:getenv(Key)]), try os:getenv(Key) of false -> throw({Name, service_hostname_not_found, Key}); Hostname -> Hostname @@ -82,7 +81,6 @@ resolve_host_spec(Name, {env, Key}) when is_list(Key) -> resolve_port_spec(_Name, Port) when is_integer(Port) -> Port; resolve_port_spec(Name, {env, Key}) when is_list(Key) -> - ?LOG_DEBUG("????? PORT ~s ~s -> ~p", [Name, Key, os:getenv(Key)]), try os:getenv(Key) of false -> throw({Name, service_port_not_found, Key}); PortStr -> diff --git a/src/te/apps/tfte/src/tfte_context.erl b/src/te/apps/tfte/src/tfte_context.erl new file mode 100644 index 000000000..ee0fafc07 --- /dev/null +++ b/src/te/apps/tfte/src/tfte_context.erl @@ -0,0 +1,162 @@ +-module(tfte_context). + +-behaviour(gen_statem). + + +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include_lib("kernel/include/logger.hrl"). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API functions +-export([start_link/0]). + +% Behaviour gen_statem functions +-export([init/1]). +-export([callback_mode/0]). +-export([handle_event/4]). +-export([terminate/3]). +-export([code_change/4]). + + +%%% Records %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(data, { + uuid :: map(), + sub :: term() | undefined, + obj :: map() | undefined +}). + + +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(SUBSCRIBE_RETRY_TIMEOUT, 1000). +-define(RETRIEVE_RETRY_TIMEOUT, 10000). + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_link() -> + gen_statem:start_link(?MODULE, [], []). + + +%%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([]) -> + {ok, Name} = application:get_env(tfte, context), + ?LOG_INFO("Starting context ~s service handler...", [Name]), + UUID = #{context_uuid => #{uuid => Name}}, + {ok, subscribe, #data{uuid = UUID}}. + +callback_mode() -> [handle_event_function, state_enter]. + +%-- SUBSCRIBE STATE ------------------------------------------------------------ +handle_event(enter, _, subscribe, #data{sub = undefined}) -> + {keep_state_and_data, [{state_timeout, 0, do_suscribe}]}; +handle_event(enter, _, subscribe, Data) -> + % We already have a context subscription + {next_state, ready, Data}; +handle_event(state_timeout, do_suscribe, subscribe, Data) -> + ?LOG_DEBUG("Subscribing to context events...", []), + case do_subscribe() of + {ok, Sub} -> + ?LOG_INFO("Subscribed to context events", []), + Data2 = Data#data{sub = Sub}, + {next_state, retrieve, Data2}; + {error, Reason} -> + ?LOG_ERROR("Failed to subscribe to context service events: ~p", [Reason]), + {keep_state_and_data, [{state_timeout, ?SUBSCRIBE_RETRY_TIMEOUT, do_suscribe}]} + end; +%-- RETRIEVE STATE ------------------------------------------------------------- +handle_event(enter, _, retrieve, _Data) -> + {keep_state_and_data, [{state_timeout, 0, do_retrieve}]}; +handle_event(state_timeout, do_retrieve, retrieve, #data{uuid = UUID} = Data) -> + ?LOG_DEBUG("Retrieving context ~p...", [UUID]), + case get_object(UUID) of + error -> + {keep_state_and_data, [{state_timeout, ?RETRIEVE_RETRY_TIMEOUT, do_retrieve}]}; + {ok, Context} -> + ?LOG_DEBUG("Got context: ~p", [Context]), + tfte_server:context_ready(Context), + {next_state, ready, Data#data{obj = Context}} + end; +handle_event(info, {headers, Id, Value}, retrieve, + #data{sub = #{stream_id := Id}}) -> + %TODO: Handle HTTP errors ??? + ?LOG_DEBUG("Received context stream header: ~p", [Value]), + keep_state_and_data; +handle_event(info, {data, Id, Value}, retrieve, + #data{sub = #{stream_id := Id}}) -> + ?LOG_DEBUG("Received context event, retrying context: ~p", [Value]), + {keep_state_and_data, [{state_timeout, 0, do_retrieve}]}; +handle_event(info, {'DOWN', Ref, process, Pid, Reason}, retrieve, + #data{sub = #{stream_id := Id, monitor_ref := Ref, stream_pid := Pid}} = Data) -> + ?LOG_DEBUG("Context subscription is down: ~p", [Reason]), + Data2 = Data#data{sub = undefined}, + Info = receive + {trailers, Id, {Status, Message, Metadata}} -> + {Reason, Status, Message, Metadata} + after 0 -> + Reason + end, + ?LOG_ERROR("Context subscription error: ~p", [Info]), + {next_state, subscribe, Data2}; +%-- READY STATE ---------------------------------------------------------------- +handle_event(enter, _, ready, _Data) -> + keep_state_and_data; +handle_event(info, {headers, Id, Value}, ready, + #data{sub = #{stream_id := Id}}) -> + %TODO: Handle HTTP errors ??? + ?LOG_DEBUG("Received context stream header: ~p", [Value]), + keep_state_and_data; +handle_event(info, {data, Id, #{context_id := UUID, event := Event}}, ready, + #data{uuid = UUID, sub = #{stream_id := Id}}) -> + ?LOG_DEBUG("Received context event: ~p", [Event]), + tfte_server:context_event(Event), + keep_state_and_data; +handle_event(info, {'DOWN', Ref, process, Pid, Reason}, ready, + #data{sub = #{stream_id := Id, monitor_ref := Ref, stream_pid := Pid}} = Data) -> + ?LOG_DEBUG("Context subscription is down: ~p", [Reason]), + Data2 = Data#data{sub = undefined}, + Info = receive + {trailers, Id, {Status, Message, Metadata}} -> + {Reason, Status, Message, Metadata} + after 0 -> + Reason + end, + ?LOG_ERROR("Context subscription error: ~p", [Info]), + {next_state, subscribe, Data2}; +%-- ANY STATE ------------------------------------------------------------------ +handle_event(info, Msg, StateName, _Data) -> + ?LOG_WARNING("Unexpected context message in state ~w: ~p", [StateName, Msg]), + keep_state_and_data. + +terminate(Reason, _State, _Data) -> + ?LOG_INFO("Context service handler terminated: ~p", [Reason]), + ok. + +code_change(_OldVsn, OldState, OldData, _Extra) -> + {ok, OldState, OldData}. + + +%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +grpc_opts() -> + #{channel => context}. + +do_subscribe() -> + context_context_service_client:get_context_events(#{}, grpc_opts()). + +get_object(UUID) -> + case context_context_service_client:get_context(UUID, grpc_opts()) of + {error, Reason} -> + ?LOG_ERROR("Local error while retrieving the context object: ~p", [Reason]), + error; + {error, Reason, _Headers} -> + ?LOG_ERROR("Remote error while retrieving the context object: ~p", [Reason]), + error; + {ok, Result, _Headers} -> + {ok, Result} + end. \ No newline at end of file diff --git a/src/te/apps/tfte/src/tfte_server.erl b/src/te/apps/tfte/src/tfte_server.erl new file mode 100644 index 000000000..29dddf3d1 --- /dev/null +++ b/src/te/apps/tfte/src/tfte_server.erl @@ -0,0 +1,100 @@ +-module(tfte_server). + +-behaviour(gen_statem). + + +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include_lib("kernel/include/logger.hrl"). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API functions +-export([start_link/0]). +-export([context_ready/1]). +-export([context_event/1]). +-export([topology_ready/1]). +-export([topology_event/1]). + +% Behaviour gen_statem functions +-export([init/1]). +-export([callback_mode/0]). +-export([handle_event/4]). +-export([terminate/3]). +-export([code_change/4]). + + +%%% Records %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(data, { +}). + + +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_link() -> + gen_statem:start_link(?MODULE, [], []). + +context_ready(Context) -> + gen_statem:cast(?MODULE, {context_ready, Context}). + +context_event(Event) -> + gen_statem:cast(?MODULE, {context_event, Event}). + +topology_ready(Topology) -> + gen_statem:cast(?MODULE, {topology_ready, Topology}). + +topology_event(Event) -> + gen_statem:cast(?MODULE, {topology_event, Event}). + + +%%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([]) -> + ?LOG_INFO("Starting server...", []), + {ok, wait_context, #data{}}. + +callback_mode() -> [handle_event_function, state_enter]. + +%-- WAIT_CONTEXT STATE --------------------------------------------------------- +handle_event(enter, _, wait_context, _Data) -> + keep_state_and_data; +handle_event(cast, {context_ready, _Context}, wait_contex, Data) -> + ?LOG_DEBUG("Teraflow context initialized: ~p", [_Context]), + tfte_topology:context_updated(), + {next_state, ready, Data}; +%-- READY STATE ---------------------------------------------------------------- +handle_event(enter, _, ready, _Data) -> + keep_state_and_data; +handle_event(cast, {context_ready, _Context}, ready, _Data) -> + ?LOG_DEBUG("Teraflow context updated: ~p", [_Context]), + tfte_topology:context_updated(), + keep_state_and_data; +handle_event(cast, {context_event, _Event}, ready, _Data) -> + ?LOG_DEBUG("Teraflow context event: ~p", [_Event]), + keep_state_and_data; +handle_event(cast, {topology_ready, _Topology}, ready, _Data) -> + ?LOG_DEBUG("Teraflow topology updated: ~p", [_Topology]), + keep_state_and_data; +handle_event(cast, {topology_event, _Event}, ready, _Data) -> + ?LOG_DEBUG("Teraflow topology event: ~p", [_Event]), + keep_state_and_data; +%-- ANY STATE ------------------------------------------------------------------ +handle_event(EventType, EventContent, State, Data) -> + ?LOG_WARNING(Data, "Unexpected ~w event in state ~w: ~w", + [EventType, State, EventContent]), + keep_state_and_data. + +terminate(Reason, _State, _Data) -> + ?LOG_INFO("Server terminated: ~p", [Reason]), + ok. + +code_change(_OldVsn, OldState, OldData, _Extra) -> + {ok, OldState, OldData}. + + +%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/te/apps/tfte/src/tfte_service_sup.erl b/src/te/apps/tfte/src/tfte_service_sup.erl new file mode 100644 index 000000000..2223589e2 --- /dev/null +++ b/src/te/apps/tfte/src/tfte_service_sup.erl @@ -0,0 +1,50 @@ +%%%----------------------------------------------------------------------------- +%% @doc tfte service supervisor. +%% @end +%%%----------------------------------------------------------------------------- + +-module(tfte_service_sup). + +-behaviour(supervisor). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API Functions +-export([start_link/0]). + +% Behaviour supervisor callback functions +-export([init/1]). + + +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(SERVER, ?MODULE). + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + + +%%% BEHAVIOUR supervisor CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([]) -> + SupFlags = #{strategy => one_for_one, + intensity => 0, + period => 1}, + ContextSpec = #{ + id => tfte_context, + start => {tfte_context, start_link, []}, + restart => permanent, + shutdown => brutal_kill + }, + TopologySpec = #{ + id => tfte_topology, + start => {tfte_topology, start_link, []}, + restart => permanent, + shutdown => brutal_kill + }, + ChildSpecs = [ContextSpec, TopologySpec], + {ok, {SupFlags, ChildSpecs}}. diff --git a/src/te/apps/tfte/src/tfte_sup.erl b/src/te/apps/tfte/src/tfte_sup.erl index 2944889cb..57c95483e 100644 --- a/src/te/apps/tfte/src/tfte_sup.erl +++ b/src/te/apps/tfte/src/tfte_sup.erl @@ -8,7 +8,7 @@ -behaviour(supervisor). -%--- Exports ------------------------------------------------------------------- +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % API Functions -export([start_link/0]). @@ -17,22 +17,36 @@ -export([init/1]). -%--- Macros -------------------------------------------------------------------- +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -define(SERVER, ?MODULE). +-define(ROOT_SERVER, tfte_server). -%--- API Functions ------------------------------------------------------------- +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). -%--- Behaviour supervisor Callback Functions ----------------------------------- +%%% BEHAVIOUR supervisor CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init([]) -> SupFlags = #{strategy => one_for_all, intensity => 0, period => 1}, - ChildSpecs = [], + ServiceSupSpec = #{ + id => service_sup, + start => {tfte_service_sup, start_link, []}, + restart => permanent, + type => supervisor, + shutdown => brutal_kill + }, + ServerSpec = #{ + id => ?ROOT_SERVER, + start => {?ROOT_SERVER, start_link, []}, + restart => permanent, + shutdown => brutal_kill + }, + ChildSpecs = [ServerSpec, ServiceSupSpec], {ok, {SupFlags, ChildSpecs}}. diff --git a/src/te/apps/tfte/src/tfte_te_service.erl b/src/te/apps/tfte/src/tfte_te_service.erl index 7c2a7225b..1cadd7aad 100644 --- a/src/te/apps/tfte/src/tfte_te_service.erl +++ b/src/te/apps/tfte/src/tfte_te_service.erl @@ -3,12 +3,12 @@ -behaviour(te_te_service_bhvr). -%--- Includes ------------------------------------------------------------------ +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -include_lib("grpcbox/include/grpcbox.hrl"). -%--- Exports ------------------------------------------------------------------- +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Behaviour te_te_service_bhvr callback functions -export([request_lsp/2]). @@ -16,7 +16,7 @@ -export([delete_lsp/2]). -%--- Behaviour te_te_service_bhvr Callback Functions --------------------------- +%%% BEHAVIOUR te_te_service_bhvr CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%% request_lsp(_Ctx, _Service) -> {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, diff --git a/src/te/apps/tfte/src/tfte_topology.erl b/src/te/apps/tfte/src/tfte_topology.erl new file mode 100644 index 000000000..d2c6fc0d9 --- /dev/null +++ b/src/te/apps/tfte/src/tfte_topology.erl @@ -0,0 +1,394 @@ +-module(tfte_topology). + +-behaviour(gen_statem). + + +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include_lib("kernel/include/logger.hrl"). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API functions +-export([start_link/0]). +-export([context_updated/0]). + +% Behaviour gen_statem functions +-export([init/1]). +-export([callback_mode/0]). +-export([handle_event/4]). +-export([terminate/3]). +-export([code_change/4]). + + +%%% Records %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(data, { + uuid :: map(), + sub :: term() | undefined, + obj :: map() | undefined, + devices = #{} :: map(), + links = #{} :: map() +}). + + +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(SUBSCRIBE_RETRY_TIMEOUT, 1000). +-define(RETRIEVE_RETRY_TIMEOUT, 10000). + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_link() -> + gen_statem:start_link(?MODULE, [], []). + +context_updated() -> + gen_statem:cast(?MODULE, context_updated). + + +%%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([]) -> + {ok, ContextName} = application:get_env(tfte, context), + {ok, TopoName} = application:get_env(tfte, topology), + ContextUUID = #{context_uuid => #{uuid => ContextName}}, + TopoUUID = #{context_id => ContextUUID, + topology_uuid => #{uuid => TopoName}}, + ?LOG_INFO("Starting topology ~s service handler...", [TopoName]), + {ok, retrieve, #data{uuid = TopoUUID}}. + +callback_mode() -> [handle_event_function, state_enter]. + +%-- RETRIEVE STATE ------------------------------------------------------------- +handle_event(enter, _, retrieve, _Data) -> + {keep_state_and_data, [{state_timeout, 0, do_retrieve}]}; +handle_event(state_timeout, do_retrieve, retrieve, #data{uuid = UUID} = Data) -> + ?LOG_DEBUG("Retrieving topology ~p...", [UUID]), + case get_object(UUID) of + error -> + {keep_state_and_data, [{state_timeout, ?RETRIEVE_RETRY_TIMEOUT, do_retrieve}]}; + {ok, Topology} -> + ?LOG_DEBUG("Got topology: ~p", [Topology]), + {next_state, subscribe, Data#data{obj = Topology}} + end; +handle_event(cast, context_updated, retrieve, _Data) -> + {keep_state_and_data, [{state_timeout, 0, do_retrieve}]}; +%-- SUBSCRIBE STATE ------------------------------------------------------------ +handle_event(enter, _, subscribe, #data{sub = undefined}) -> + {keep_state_and_data, [{state_timeout, 0, do_suscribe}]}; +handle_event(enter, _, subscribe, Data) -> + % We already have a topology subscription + {next_state, ready, Data}; +handle_event(state_timeout, do_suscribe, subscribe, #data{uuid = UUID} = Data) -> + ?LOG_DEBUG("Subscribing to topology events...", []), + case do_subscribe(UUID) of + {ok, Sub} -> + ?LOG_INFO("Subscribed to topology events", []), + Data2 = #data{obj = Obj} = Data#data{sub = Sub}, + #{device_ids := DeviceIds, link_ids := LinkIds} = Obj, + case update_topology(Data2, DeviceIds, LinkIds) of + {ok, Data3} -> + tfte_server:topology_ready(Obj), + {next_state, ready, Data3}; + {error, Reason} -> + ?LOG_ERROR("Failed to load topology: ~p", [Reason]), + statem_rollback_to_retrieve(Data2) + end; + {error, Reason} -> + ?LOG_ERROR("Failed to subscribe to topology service events: ~p", [Reason]), + {next_state, retrieve, [{state_timeout, ?SUBSCRIBE_RETRY_TIMEOUT, do_retrieve}]} + end; +%-- READY STATE ---------------------------------------------------------------- +handle_event(enter, _, ready, _Data) -> + keep_state_and_data; +handle_event(info, {headers, Id, Value}, ready, + #data{sub = #{stream_id := Id}}) -> + %TODO: Handle HTTP errors ??? + ?LOG_DEBUG("Received topology stream header: ~p", [Value]), + keep_state_and_data; +handle_event(info, {data, Id, #{event := Event}}, ready, + #data{sub = #{stream_id := Id}} = Data) -> + ?LOG_DEBUG("Received topology event: ~p", [Event]), + handle_topology_event(Data, Event); +handle_event(info, {'DOWN', Ref, process, Pid, Reason}, ready, + #data{sub = #{stream_id := Id, monitor_ref := Ref, stream_pid := Pid}} = Data) -> + ?LOG_DEBUG("Topology subscription is down: ~p", [Reason]), + Data2 = Data#data{sub = undefined}, + Info = receive + {trailers, Id, {Status, Message, Metadata}} -> + {Reason, Status, Message, Metadata} + after 0 -> + Reason + end, + ?LOG_ERROR("Topology subscription error: ~p", [Info]), + {next_state, retrieve, Data2}; +handle_event(cast, context_updated, ready, _Data) -> + keep_state_and_data; +%-- ANY STATE ------------------------------------------------------------------ +handle_event(info, Msg, StateName, _Data) -> + ?LOG_WARNING("Unexpected topology message in state ~w: ~p", [StateName, Msg]), + keep_state_and_data. + +terminate(Reason, _State, _Data) -> + ?LOG_INFO("Topology service handler terminated: ~p", [Reason]), + ok. + +code_change(_OldVsn, OldState, OldData, _Extra) -> + {ok, OldState, OldData}. + + +%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +statem_rollback_to_retrieve(#data{sub = undefined} = Data) -> + {next_state, retrieve, Data, [{state_timeout, ?RETRIEVE_RETRY_TIMEOUT, do_retrieve}]}; +statem_rollback_to_retrieve(#data{sub = Sub} = Data) -> + grpcbox_client:close_send_and_recv(Sub), + Data2 = Data#data{sub = undefined}, + {next_state, retrieve, Data2, [{state_timeout, ?RETRIEVE_RETRY_TIMEOUT, do_retrieve}]}. + +handle_topology_event(#data{uuid = UUID} = Data, + #{event_type := 'EVENTTYPE_UPDATE'} = Event) -> + case get_object(UUID) of + error -> + statem_rollback_to_retrieve(Data); + {ok, #{device_ids := DeviceIds, link_ids := LinkIds} = Topology} -> + ?LOG_DEBUG("Got new topology: ~p", [Topology]), + Data2 = Data#data{obj = Topology}, + case update_topology(Data2, DeviceIds, LinkIds) of + {ok, Data3} -> + tfte_server:topology_event(Event), + {keep_state, Data3}; + {error, Reason} -> + ?LOG_ERROR("Failed to update topology: ~p", [Reason]), + statem_rollback_to_retrieve(Data2) + end + end; +handle_topology_event(_Data, Event) -> + tfte_server:topology_event(Event), + keep_state_and_data. + +update_topology(Data, DeviceIds, LinkIds) -> + try + {Data2, Events} = update_devices(Data, DeviceIds, []), + {Data3, Events2} = update_links(Data2, LinkIds, Events), + post_topology_events(lists:reverse(Events2)), + {ok, Data3} + catch + throw:Reason -> + {error, Reason} + end. + +post_topology_events(Events) -> + lists:foreach(fun post_topology_event/1, Events). + +post_topology_event({device_added, Id, Device}) -> + epce_ted:device_added(Id, Device); +post_topology_event({device_updated, Id, Device}) -> + epce_ted:device_updated(Id, Device); +post_topology_event({device_deleted, Id}) -> + epce_ted:device_deleted(Id); +post_topology_event({link_added, Id, Link}) -> + epce_ted:link_added(Id, Link); +post_topology_event({link_updated, Id, Link}) -> + epce_ted:link_updated(Id, Link); +post_topology_event({link_deleted, Id}) -> + epce_ted:link_deleted(Id). + +update_devices(#data{devices = OldDevices} = Data, DeviceIds, Events) -> + {NewDevices, Events2} = update_devices(OldDevices, #{}, DeviceIds, Events), + {Data#data{devices = NewDevices}, Events2}. + +update_devices(OldDevices, NewDevices, [], Events) -> + Events2 = [{device_deleted, post_process_device_id(I)} + || I <- maps:keys(OldDevices)] ++ Events, + {NewDevices, Events2}; +update_devices(OldDevices, NewDevices, [Id | Rest], Events) -> + case get_device(Id) of + error -> throw({device_retrieval_error, Id}); + {ok, Device} -> + Id2 = post_process_device_id(Id), + Device2 = post_process_device(Device), + NewDevices2 = NewDevices#{Id => Device}, + case maps:take(Id, OldDevices) of + error -> + % New device + Events2 = [{device_added, Id2, Device2} | Events], + update_devices(OldDevices, NewDevices2, Rest, Events2); + {Device, OldDevices2} -> + % Device did not change + update_devices(OldDevices2, NewDevices2, Rest, Events); + {_OldDevice, OldDevices2} -> + % Device changed + Events2 = [{device_updated, Id2, Device2} | Events], + update_devices(OldDevices2, NewDevices2, Rest, Events2) + end + end. + +update_links(#data{links = OldLinks} = Data, LinksIds, Events) -> + {NewLinks, Events2} = update_links(OldLinks, #{}, LinksIds, Events), + {Data#data{links = NewLinks}, Events2}. + +update_links(OldLinks, NewLinks, [], Events) -> + Events2 = [{link_deleted, post_process_link_id(I)} + || I <- maps:keys(OldLinks)] ++ Events, + {NewLinks, Events2}; +update_links(OldLinks, NewLinks, [Id | Rest], Events) -> + case get_link(Id) of + error -> throw({link_retrieval_error, Id}); + {ok, Link} -> + Id2 = post_process_link_id(Id), + Link2 = post_process_link(Link), + NewLinks2 = NewLinks#{Id => Link}, + case maps:take(Id, OldLinks) of + error -> + % New Link + Events2 = [{link_added, Id2, Link2} | Events], + update_links(OldLinks, NewLinks2, Rest, Events2); + {Link, OldLinks2} -> + % Link did not change + update_links(OldLinks2, NewLinks2, Rest, Events); + {_OldLink, OldLinks2} -> + % Link changed + Events2 = [{link_updated, Id2, Link2} | Events], + update_links(OldLinks2, NewLinks2, Rest, Events2) + end + end. + +post_process_device_id(#{device_uuid := #{uuid := Name}}) -> + Name. + +post_process_device(Device) -> + #{id => device_id(Device), + type => device_type(Device), + pcc_address => device_pcc_address(Device), + mpls_label => device_mpls_label(Device), + status => device_status(Device), + endpoints => device_endpoints(Device)}. + +device_id(#{device_id := Id}) -> + post_process_device_id(Id). + +device_type(#{device_type := Type}) -> + Type. + +device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_UNDEFINED'}) -> + undefined; +device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_DISABLED'}) -> + disabled; +device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_ENABLED'}) -> + enabled. + +device_mpls_label(Device) -> + case device_config_value(<<"mpls_label">>, Device) of + undefined -> undefined; + LabelBin -> + try binary_to_integer(LabelBin) + catch error:badarg -> undefined + end + end. + +device_pcc_address(Device) -> + case device_config_value(<<"pcc_address">>, Device) of + undefined -> undefined; + AddressBin -> + case inet_parse:address(binary_to_list(AddressBin)) of + {ok, Address} -> Address; + {error,einval} -> undefined + end + end. + +device_config_value(Key, #{device_config := #{config_rules := Rules}}) -> + device_config_value(Key, Rules); +device_config_value(_Key, []) -> + undefined; +device_config_value(Key, [#{action := 'CONFIGACTION_SET', + config_rule := {custom, Rule}} | Rest]) -> + case Rule of + #{resource_key := Key, resource_value := Value} -> Value; + _ -> device_config_value(Key, Rest) + end; +device_config_value(Key, [_Rule | Rest]) -> + device_config_value(Key, Rest). + + +device_endpoints(Device) -> + device_endpoints(Device, []). + +device_endpoints(#{device_endpoints := Endpoints}, Acc) -> + device_endpoints(Endpoints, Acc); +device_endpoints([], Acc) -> + lists:reverse(Acc); +device_endpoints([#{endpoint_id := #{endpoint_uuid := #{uuid := Name}}} | Rest], Acc) -> + device_endpoints(Rest, [Name | Acc]). + +post_process_link_id(#{link_uuid := #{uuid := Name}}) -> + Name. + +post_process_link(Link) -> + #{id => link_id(Link), + endpoints => link_endpoints(Link)}. + +link_id(#{link_id := Id}) -> + post_process_link_id(Id). + +link_endpoints(Link) -> + link_endpoints(Link, []). + +link_endpoints(#{link_endpoint_ids := Endpoints}, Acc) -> + link_endpoints(Endpoints, Acc); +link_endpoints([], Acc) -> + lists:reverse(Acc); +link_endpoints([#{device_id := #{device_uuid := #{uuid := DevName}}, + endpoint_uuid := #{uuid := EndpointName}} | Rest], Acc) -> + Endpoint = #{ + device => DevName, + endpoint => EndpointName + }, + link_endpoints(Rest, [Endpoint | Acc]). + + +%-- GRPC UNTILITY FUNCTION ----------------------------------------------------- + +grpc_opts() -> + #{channel => context}. + +do_subscribe(UUID) -> + context_context_service_client:get_topology_events(UUID, grpc_opts()). + +get_object(UUID) -> + case context_context_service_client:get_topology(UUID, grpc_opts()) of + {error, Reason} -> + ?LOG_ERROR("Local error while retrieving the topology object: ~p", [Reason]), + error; + {error, Reason, _Headers} -> + ?LOG_ERROR("Remote error while retrieving the topology object: ~p", [Reason]), + error; + {ok, Result, _Headers} -> + {ok, Result} + end. + +get_device(UUID) -> + case context_context_service_client:get_device(UUID, grpc_opts()) of + {error, Reason} -> + ?LOG_ERROR("Local error while retrieving a device object: ~p", [Reason]), + error; + {error, Reason, _Headers} -> + ?LOG_ERROR("Remote error while retrieving a device object: ~p", [Reason]), + error; + {ok, Result, _Headers} -> + {ok, Result} + end. + +get_link(UUID) -> + case context_context_service_client:get_link(UUID, grpc_opts()) of + {error, Reason} -> + ?LOG_ERROR("Local error while retrieving a link object: ~p", [Reason]), + error; + {error, Reason, _Headers} -> + ?LOG_ERROR("Remote error while retrieving a link object: ~p", [Reason]), + error; + {ok, Result, _Headers} -> + {ok, Result} + end. diff --git a/src/te/config/sys.config.src b/src/te/config/sys.config.src index 93bfbafb1..f3c185641 100644 --- a/src/te/config/sys.config.src +++ b/src/te/config/sys.config.src @@ -1,6 +1,8 @@ [ {tfte, [ + {context, <<"admin">>}, + {topology, <<"tetestbed">>}, {services, [ {te, [ {http, {env, "TESERVICE_SERVICE_HOST"}, {env, "TESERVICE_SERVICE_PORT_GRPC"}, []} @@ -72,7 +74,7 @@ {logger_level, ${ERLANG_LOGGER_LEVEL}}, {logger, [ {handler, default, logger_std_h, #{ - level => info, + level => ${ERLANG_LOGGER_LEVEL}, filter_default => log, config => #{type => standard_io}, formatter => {logger_formatter, #{ diff --git a/tutorial/2-6-te-demo-start-testbed.sh b/tutorial/2-6-te-demo-start-testbed.sh index dea41a71a..3266d1141 100755 --- a/tutorial/2-6-te-demo-start-testbed.sh +++ b/tutorial/2-6-te-demo-start-testbed.sh @@ -11,26 +11,30 @@ if [[ ! -f "${NEGENDIR}/exe/netgen" ]]; then exit 1 fi -export PCE_NETNS="$1" -export PCE_IP="$2" -RT1_INT_IP="$3" -RT1_EXT_IP="$4" -RT6_INT_IP="$5" -RT6_EXT_IP="$6" - - -if [[ -z $PCE_NETNS || -z $PCE_IP || -z RT1_INT_IP || -z RT1_EXT_IP || -z RT6_INT_IP || -z RT6_EXT_IP ]]; then - echo "USAGE: $0 PCE_NETNS PCE_IP RT1_INT_IP RT1_EXT_IP RT6_INT_IP RT6_EXT_IP" - echo " e.g: $0 cni-588a2d06-e64f-907b-d51b-bed0307007c9 10.1.103.133 10 11 12 13" - exit 1 +PCE_IP=$( kubectl --namespace tfs get $(kubectl --namespace tfs get pods --selector=app=teservice -o name) --template '{{.status.podIP}}' ) +echo "Teraflow PCE IP address: $PCE_IP" +NAMESPACES=$( ip netns list | cut -d' ' -f1 ) +PCE_NETNS="" +for n in $NAMESPACES; do + if sudo ip -n $n addr list | grep $PCE_IP > /dev/null; then + echo "Teraflow TE service namespace: $n" + PCE_NETNS=$n + break + fi +done +if [[ -z $PCE_NETNS ]]; then + echo "Teraflow network namespace for TE service not found" + exit1 fi IFS=. read PCE_IP1 PCE_IP2 PCE_IP3 PCE_IP4 <<< "$PCE_IP" -export RT1_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT1_INT_IP" -export RT1_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT1_EXT_IP" -export RT6_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT6_INT_IP" -export RT6_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT6_EXT_IP" +export PCE_IP +export PCE_NETNS +export RT1_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.10" +export RT1_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.11" +export RT6_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.12" +export RT6_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.13" cp "${ROOTDIR}/2-6-netgen-config.yml" "${RUNDIR}/config.yml" cat "${ROOTDIR}/2-6-netgen-topology.yml.template" | envsubst > "${RUNDIR}/topology.yml" diff --git a/tutorial/2-6-te-demo.md b/tutorial/2-6-te-demo.md index 6a99007b4..774a38bab 100644 --- a/tutorial/2-6-te-demo.md +++ b/tutorial/2-6-te-demo.md @@ -78,20 +78,14 @@ ### Run the Test-Bed -To start the testbed, we need to figure out the IP of the TE service: - $ kubectl --namespace tfs get $(kubectl --namespace tfs get pods --selector=app=teservice -o name) --template '{{.status.podIP}}' - -And select the network namespace that defines it by looking into each of them until you find the correct one: - $ ip netns list - $ sudo ip -n XXX addr list - -When we have the IP and namespace we can start the testbed. +First load the [teraflow configuration file](./2-6-teraflow-topology.json) using the webui. +The first time the configuration is loaded may partialy fail because it tries to load the topology before the devices and links, but a second load should work. In first console: $ cd ~/testbed - $ ../tfs-ctrl/tutorial/2-6-te-demo-start-testbed.sh NETWORK_NAMESPACE TE_SERVICE_IP 10 11 12 13 + $ ../tfs-ctrl/tutorial/2-6-te-demo-start-testbed.sh Then in second console: $ sudo -i # cd /tmp/negen - # ./tmux.sh \ No newline at end of file + # ./tmux.sh diff --git a/tutorial/2-6-teraflow-topology.json b/tutorial/2-6-teraflow-topology.json new file mode 100644 index 000000000..8b5188866 --- /dev/null +++ b/tutorial/2-6-teraflow-topology.json @@ -0,0 +1,210 @@ +{ + "contexts": [ + { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_ids": [], + "service_ids": [] + } + ], + "topologies": [ + { + "topology_id": {"topology_uuid": {"uuid": "tetestbed"}, "context_id": {"context_uuid": {"uuid": "admin"}}}, + "device_ids": [ + {"device_uuid": {"uuid": "SW1"}}, + {"device_uuid": {"uuid": "RT1"}}, + {"device_uuid": {"uuid": "RT2"}}, + {"device_uuid": {"uuid": "RT3"}}, + {"device_uuid": {"uuid": "RT4"}}, + {"device_uuid": {"uuid": "RT5"}}, + {"device_uuid": {"uuid": "RT6"}} + ], + "link_ids": [ + {"link_uuid": {"uuid": "RT1/SW1"}}, + {"link_uuid": {"uuid": "RT2/SW1"}}, + {"link_uuid": {"uuid": "RT3/SW1"}}, + {"link_uuid": {"uuid": "RT2/RT4/1"}}, + {"link_uuid": {"uuid": "RT2/RT4/2"}}, + {"link_uuid": {"uuid": "RT3/RT5/1"}}, + {"link_uuid": {"uuid": "RT3/RT5/2"}}, + {"link_uuid": {"uuid": "RT4/RT5"}}, + {"link_uuid": {"uuid": "RT4/RT6"}}, + {"link_uuid": {"uuid": "RT5/RT6"}} + ] + } + ], + "devices": [ + { + "device_id": {"device_uuid": {"uuid": "SW1"}}, + "device_type": "emu-packet-switch", + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": + "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"df8bb169-2013-4b82-9455-69777f7a01d6\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"061119c1-2aa4-48e9-be64-3ddf465fc80a\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"495ea3f8-e67f-46a0-84bd-a230a4b7067d\"}]}"}} + ]}, + "device_operational_status": 1, + "device_drivers": [0], + "device_endpoints": [] + }, + { + "device_id": {"device_uuid": {"uuid": "RT1"}}, + "device_type": "emu-packet-router", + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16010"}}, + {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "1.1.1.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "1.1.1.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": + "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-src\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}]}"}} + ]}, + "device_operational_status": 1, + "device_drivers": [0], + "device_endpoints": [] + }, + { + "device_id": {"device_uuid": {"uuid": "RT2"}}, + "device_type": "emu-packet-router", + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16020"}}, + {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "2.2.2.2"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "2.2.2.2"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": + "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4-2\"}]}"}} + ]}, + "device_operational_status": 1, + "device_drivers": [0], + "device_endpoints": [] + }, + { + "device_id": {"device_uuid": {"uuid": "RT3"}}, + "device_type": "emu-packet-router", + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16030"}}, + {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "3.3.3.3"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "3.3.3.3"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": + "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5-2\"}]}"}} + ]}, + "device_operational_status": 1, + "device_drivers": [0], + "device_endpoints": [] + }, + { + "device_id": {"device_uuid": {"uuid": "RT4"}}, + "device_type": "emu-packet-router", + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16040"}}, + {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "4.4.4.4"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "4.4.4.4"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": + "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt2-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt2-2\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt6\"}]}"}} + ]}, + "device_operational_status": 1, + "device_drivers": [0], + "device_endpoints": [] + }, + { + "device_id": {"device_uuid": {"uuid": "RT5"}}, + "device_type": "emu-packet-router", + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16050"}}, + {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "5.5.5.5"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "5.5.5.5"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": + "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt3-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt3-2\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt6\"}]}"}} + ]}, + "device_operational_status": 1, + "device_drivers": [0], + "device_endpoints": [] + }, + { + "device_id": {"device_uuid": {"uuid": "RT6"}}, + "device_type": "emu-packet-router", + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16060"}}, + {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "6.6.6.6"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "6.6.6.6"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": + "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-dst\"}]}"}} + ]}, + "device_operational_status": 1, + "device_drivers": [0], + "device_endpoints": [] + } + ], + "links": [ + { + "link_id": {"link_uuid": {"uuid": "RT1/SW1"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT1"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, + {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "df8bb169-2013-4b82-9455-69777f7a01d6"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT2/SW1"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, + {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "061119c1-2aa4-48e9-be64-3ddf465fc80a"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT3/SW1"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, + {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "495ea3f8-e67f-46a0-84bd-a230a4b7067d"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT2/RT4/1"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-rt4-1"}}, + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt2-1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT2/RT4/2"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-rt4-2"}}, + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt2-2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT3/RT5/1"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-rt5-1"}}, + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt3-1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT3/RT5/2"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-rt5-2"}}, + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt3-2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT4/RT5"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt5"}}, + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt4"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT4/RT6"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt6"}}, + {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-rt4"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT5/RT6"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt6"}}, + {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-rt5"}} + ] + } + ] +} -- GitLab From 8e8d82977c06e29918a072c06ebbe0629639dc19 Mon Sep 17 00:00:00 2001 From: merle Date: Fri, 30 Sep 2022 09:42:44 -0700 Subject: [PATCH 05/63] Add support for PCE initiated LSPs --- .../epce/src/epce_pcep_server_handler.erl | 5 ++ src/te/apps/epce/src/epce_server.erl | 83 +++++++++++++++++-- src/te/rebar.lock | 4 +- tutorial/2-6-netgen-topology.yml.template | 15 +--- tutorial/2-6-te-demo-start-testbed.sh | 2 - tutorial/2-6-te-demo.md | 17 ++++ 6 files changed, 100 insertions(+), 26 deletions(-) diff --git a/src/te/apps/epce/src/epce_pcep_server_handler.erl b/src/te/apps/epce/src/epce_pcep_server_handler.erl index 210395885..df786bfc6 100644 --- a/src/te/apps/epce/src/epce_pcep_server_handler.erl +++ b/src/te/apps/epce/src/epce_pcep_server_handler.erl @@ -17,6 +17,7 @@ -export([init/1]). -export([opened/4]). -export([flow_added/2]). +-export([flow_initiated/2]). -export([ready/1]). -export([request_route/2]). -export([flow_delegated/2]). @@ -49,6 +50,10 @@ flow_added(Flow, State) -> ok -> {ok, State} end. +flow_initiated(Flow, State) -> + ok = epce_server:flow_initiated(Flow), + {ok, State}. + ready(State) -> {ok, State}. diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl index 11a1625ef..507255385 100644 --- a/src/te/apps/epce/src/epce_server.erl +++ b/src/te/apps/epce/src/epce_server.erl @@ -15,10 +15,12 @@ -export([start_link/0]). -export([get_flows/0]). -export([update_flow/2]). +-export([initiate_flow/4]). % Handler Functions -export([session_opened/3]). -export([flow_added/1]). +-export([flow_initiated/1]). -export([request_route/1]). -export([flow_status_changed/2]). @@ -32,6 +34,11 @@ -export([terminate/2]). +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(LARGE_TIMEOUT, 20000). + + %%% RECORDS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -record(sess, { @@ -60,6 +67,10 @@ get_flows() -> update_flow(FlowId, LabelStack) -> gen_server:call(?MODULE, {update_flow, FlowId, LabelStack}). +initiate_flow(Name, FromAddr, ToAddr, BindingLabel) -> + gen_server:call(?MODULE, {initiate_flow, Name, FromAddr, ToAddr, + BindingLabel}, ?LARGE_TIMEOUT). + %%% HANDLER FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -69,6 +80,9 @@ session_opened(Id, Caps, Pid) -> flow_added(Flow) -> gen_server:call(?MODULE, {flow_added, Flow}). +flow_initiated(Flow) -> + gen_server:call(?MODULE, {flow_initiated, Flow}). + request_route(RouteReq) -> gen_server:call(?MODULE, {request_route, RouteReq}). @@ -92,11 +106,26 @@ handle_call({update_flow, FlowId, Labels}, From, error -> {reply, {error, session_not_found}, State}; {ok, #sess{pid = Pid}} -> #{source := S, destination := D, constraints := C} = R, - ReqRoute = route_from_labels(S, D, C, Labels), + ReqRoute = routereq_from_labels(S, D, C, Labels), session_update_flow(State, Pid, FlowId, ReqRoute, From), {noreply, State} end end; +handle_call({initiate_flow, Name, FromAddr, ToAddr, Binding}, From, + #state{sessions = SessMap} = State) -> + case maps:find(FromAddr, SessMap) of + error -> {reply, {error, session_not_found}, State}; + {ok, #sess{pid = Pid}} -> + case compute_path(FromAddr, ToAddr) of + {error, Reason} -> + {reply, {error, Reason}, State}; + {ok, Labels} -> + InitRoute = routeinit_from_labels(Name, FromAddr, ToAddr, + [], Binding, Labels), + session_initiate_flow(State, Pid, InitRoute, From), + {noreply, State} + end + end; handle_call({session_opened, Id, Caps, Pid}, _From, #state{sessions = SessMap, sess_pids = SessPids} = State) -> logger:debug("Session with capabilities ~w open to ~w", [Caps, Id]), @@ -114,6 +143,11 @@ handle_call({flow_added, #{id := Id, route := Route} = Flow}, _From, #state{flows = Flows} = State) -> logger:debug("Flow ~w with route ~w added", [Id, route_to_labels(Route)]), {reply, ok, State#state{flows = Flows#{Id => Flow}}}; +handle_call({flow_initiated, #{id := Id, route := Route} = Flow}, + _From, #state{flows = Flows} = State) -> + logger:debug("Flow ~w with route ~p initiated", + [Id, route_to_labels(Route)]), + {reply, ok, State#state{flows = Flows#{Id => Flow}}}; handle_call({request_route, RouteReq}, _From, State) -> logger:info("Route from ~w to ~w requested", [maps:get(source, RouteReq), maps:get(destination, RouteReq)]), @@ -122,7 +156,7 @@ handle_call({request_route, RouteReq}, _From, State) -> {error, _Reason} = Error -> {reply, Error, State}; {ok, Labels} -> - Route = route_from_labels(S, D, C, Labels), + Route = routereq_from_labels(S, D, C, Labels), {reply, {ok, Route}, State} end; handle_call({flow_status_changed, FlowId, NewStatus}, _From, @@ -131,12 +165,13 @@ handle_call({flow_status_changed, FlowId, NewStatus}, _From, Flow = maps:get(FlowId, Flows), {reply, ok, State#state{ flows = maps:put(FlowId, Flow#{status := NewStatus}, Flows)}}; -handle_call(Request, _From, State) -> - logger:warning("Unexpected request ~w", [Request]), +handle_call(Request, From, State) -> + logger:warning("Unexpected call from ~w: ~p", [From, Request]), {reply, {error, unexpected_call}, State}. -handle_cast(_Request, State) -> +handle_cast(Request, State) -> + logger:warning("Unexpected cast: ~p", [Request]), {noreply, State}. handle_continue(_Continue, State) -> @@ -153,7 +188,17 @@ handle_info({flow_updated, FlowId, NewRoute, From}, {noreply, State#state{flows = Flows2}} end; handle_info({flow_update_error, FlowId, Reason, From}, State) -> - logger:error("Flow ~w updated error: ~w", [FlowId, Reason]), + logger:error("Flow ~w updated error: ~p", [FlowId, Reason]), + gen_server:reply(From, {error, Reason}), + {noreply, State}; +handle_info({flow_initiated, #{id := FlowId, route := Route} = Flow, From}, + #state{flows = Flows} = State) -> + logger:info("Flow ~w initiated to ~p", + [FlowId, route_to_labels(Route)]), + gen_server:reply(From, {ok, FlowId}), + {noreply, State#state{flows = Flows#{FlowId => Flow}}}; +handle_info({flow_init_error, Reason, From}, State) -> + logger:error("Flow initialisation error: ~p", [Reason]), gen_server:reply(From, {error, Reason}), {noreply, State}; handle_info({'DOWN', MonRef, process, Pid, _Reason}, @@ -169,7 +214,8 @@ handle_info({'DOWN', MonRef, process, Pid, _Reason}, _X -> {noreply, State} end; -handle_info(_Info, State) -> +handle_info(Info, State) -> + logger:warning("Unexpected message: ~p", [Info]), {noreply, State}. code_change(_OldVsn, State, _Extra) -> @@ -189,10 +235,10 @@ compute_path(From, To) -> {ok, Labels}; {error, Reason} -> logger:warning("Failed to find a route from ~p to ~p", [From, To]), - {error, Reason} + {error, route_not_found} end. -route_from_labels(Source, Destination, Constraints, Labels) -> +routereq_from_labels(Source, Destination, Constraints, Labels) -> #{ source => Source, destination => Destination, @@ -207,6 +253,13 @@ route_from_labels(Source, Destination, Constraints, Labels) -> ] }. +routeinit_from_labels(Name, Source, Destination, Constraints, Binding, Labels) -> + Route = routereq_from_labels(Source, Destination, Constraints, Labels), + Route#{ + name => Name, + binding_label => Binding + }. + route_to_labels(#{steps := Steps}) -> [Sid#mpls_stack_entry.label || #{sid := Sid} <- Steps]. @@ -216,6 +269,9 @@ route_to_labels(#{steps := Steps}) -> session_update_flow(#state{bouncer = Pid}, SessPid, FlowId, Route, Args) -> Pid ! {update_flow, SessPid, FlowId, Route, Args}. +session_initiate_flow(#state{bouncer = Pid}, SessPid, Route, Args) -> + Pid ! {initiate_flow, SessPid, Route, Args}. + bouncer_start(#state{bouncer = undefined} = State) -> Self = self(), Pid = erlang:spawn_link(fun() -> @@ -238,5 +294,14 @@ bouncer_loop(Parent) -> {error, Reason} -> Parent ! {flow_update_error, FlowId, Reason, Args}, bouncer_loop(Parent) + end; + {initiate_flow, SessPid, InitRoute, Args} -> + case pcep_server_session:initiate_flow(SessPid, InitRoute) of + {ok, Flow} -> + Parent ! {flow_initiated, Flow, Args}, + bouncer_loop(Parent); + {error, Reason} -> + Parent ! {flow_init_error, Reason, Args}, + bouncer_loop(Parent) end end. diff --git a/src/te/rebar.lock b/src/te/rebar.lock index 6eb067ecd..ef9747fe4 100644 --- a/src/te/rebar.lock +++ b/src/te/rebar.lock @@ -11,11 +11,11 @@ {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}, {<<"pcep_codec">>, {git,"git@github.com:stritzinger/pcep_codec.git", - {ref,"3d1623fdf0c62d3daf400ac65aaf985f8bd40835"}}, + {ref,"84dcc430b2aa427984c100cc19dd35d946b22ff9"}}, 1}, {<<"pcep_server">>, {git,"git@github.com:stritzinger/pcep_server.git", - {ref,"2cf692e7e5fa2e9ac0fd54e5aa64ffb17f4f1b4a"}}, + {ref,"3910bf5546879cb91a3483008b6aed11753deaa6"}}, 0}, {<<"ranch">>,{pkg,<<"ranch">>,<<"2.0.0">>},1}]}. [ diff --git a/tutorial/2-6-netgen-topology.yml.template b/tutorial/2-6-netgen-topology.yml.template index 286f40005..d16e34841 100644 --- a/tutorial/2-6-netgen-topology.yml.template +++ b/tutorial/2-6-netgen-topology.yml.template @@ -103,17 +103,11 @@ routers: debug pathd pcep basic segment-routing traffic-eng - mpls-te on - policy color 1 endpoint 6.6.6.6 - name DEFAULT - binding-sid 1111 - candidate-path preference 100 name RUNTIME dynamic - ! - ! pcep pce-config CONFIG source-address ip 1.1.1.1 pce PCE + pce-initiated address ip ${PCE_IP} config CONFIG pcc @@ -438,16 +432,11 @@ routers: debug pathd pcep segment-routing traffic-eng - policy color 1 endpoint 1.1.1.1 - name DEFAULT - binding-sid 6666 - candidate-path preference 200 name RUNTIME dynamic - ! - ! pcep pce-config CONFIG source-address ip 6.6.6.6 pce PCE + pce-initiated address ip ${PCE_IP} config CONFIG pcc diff --git a/tutorial/2-6-te-demo-start-testbed.sh b/tutorial/2-6-te-demo-start-testbed.sh index 3266d1141..5ba48479c 100755 --- a/tutorial/2-6-te-demo-start-testbed.sh +++ b/tutorial/2-6-te-demo-start-testbed.sh @@ -41,7 +41,5 @@ cat "${ROOTDIR}/2-6-netgen-topology.yml.template" | envsubst > "${RUNDIR}/topolo sudo -i bash -c "\ cd ${RUNDIR}/netgen;\ - sysctl -w net.ipv6.conf.all.disable_ipv6=1;\ - sysctl -w net.ipv6.conf.default.disable_ipv6=1;\ sysctl -w net.ipv4.conf.all.rp_filter=0;\ PATH=/usr/lib/frr:\$PATH ./exe/netgen ../topology.yml -c ../config.yml" diff --git a/tutorial/2-6-te-demo.md b/tutorial/2-6-te-demo.md index 774a38bab..da822e3a3 100644 --- a/tutorial/2-6-te-demo.md +++ b/tutorial/2-6-te-demo.md @@ -89,3 +89,20 @@ Then in second console: $ sudo -i # cd /tmp/negen # ./tmux.sh + +### Setup a flow from the Erlang console + +We will setup two unidirectional flow between router 1 and 6. +We will use the binding label 1111 for the flow from router 1 to router 6, and the binding label 6666 for the flow from router 6 to router 1. + + $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -- /tfte/bin/tfte remote_console + 1> {ok, Flow1to6} = epce_server:initiate_flow(<<"foo">>, {1, 1, 1, 1}, {6, 6, 6, 6}, 1111). + 2> {ok, Flow6to1} = epce_server:initiate_flow(<<"bar">>, {6, 6, 6, 6}, {1, 1, 1, 1}, 6666). + +Now if we go to the tmux session src (Ctrl-B 0) we can ping dst: + + $ ping 9.9.9.2 + +From the Erlang console we can update the initiated flows to change the path the packets are flowing through: + + 3> epce_server:update_flow(Flow6to1, [16050, 16030, 16010]). -- GitLab From b6312de40cc146d7d7649f76ac6a1115dd555c7f Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 5 Oct 2022 07:25:10 -0700 Subject: [PATCH 06/63] Add skeleton for TE service handler --- .../service_handler_api/FilterFields.py | 3 +- .../service/service_handlers/__init__.py | 7 + .../service_handlers/te/ConfigRules.py | 44 +++++ .../service_handlers/te/TEServiceHandler.py | 161 ++++++++++++++++++ .../service/service_handlers/te/__init__.py | 14 ++ tutorial/2-6-teraflow-service.json | 18 ++ 6 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 src/service/service/service_handlers/te/ConfigRules.py create mode 100644 src/service/service/service_handlers/te/TEServiceHandler.py create mode 100644 src/service/service/service_handlers/te/__init__.py create mode 100644 tutorial/2-6-teraflow-service.json diff --git a/src/service/service/service_handler_api/FilterFields.py b/src/service/service/service_handler_api/FilterFields.py index 3ec71dc64..4f2fb5d12 100644 --- a/src/service/service/service_handler_api/FilterFields.py +++ b/src/service/service/service_handler_api/FilterFields.py @@ -23,7 +23,8 @@ SERVICE_TYPE_VALUES = { ServiceTypeEnum.SERVICETYPE_UNKNOWN, ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM, - ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE + ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, + ServiceTypeEnum.SERVICETYPE_TE, } DEVICE_DRIVER_VALUES = { diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py index 257bc138f..03a76ed26 100644 --- a/src/service/service/service_handlers/__init__.py +++ b/src/service/service/service_handlers/__init__.py @@ -23,6 +23,7 @@ from .microwave.MicrowaveServiceHandler import MicrowaveServiceHandler from .p4.p4_service_handler import P4ServiceHandler from .tapi_tapi.TapiServiceHandler import TapiServiceHandler from .tapi_xr.TapiXrServiceHandler import TapiXrServiceHandler +from .te.TEServiceHandler import TEServiceHandler SERVICE_HANDLERS = [ (L2NMEmulatedServiceHandler, [ @@ -79,4 +80,10 @@ SERVICE_HANDLERS = [ FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN], } ]), + (TEServiceHandler, [ + { + FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_TE, + FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_UNDEFINED, + } + ]), ] diff --git a/src/service/service/service_handlers/te/ConfigRules.py b/src/service/service/service_handlers/te/ConfigRules.py new file mode 100644 index 000000000..7b79fca5a --- /dev/null +++ b/src/service/service/service_handlers/te/ConfigRules.py @@ -0,0 +1,44 @@ +# 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 logging +from typing import Dict, List +from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set +from service.service.service_handler_api.AnyTreeTools import TreeNode + +LOGGER = logging.getLogger(__name__) + +def setup_config_rules( + service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, + service_settings : TreeNode, endpoint_settings : TreeNode +) -> List[Dict]: + + json_settings : Dict = {} if service_settings is None else service_settings.value + json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value + + json_config_rules = [ + ] + return json_config_rules + +def teardown_config_rules( + service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, + service_settings : TreeNode, endpoint_settings : TreeNode +) -> List[Dict]: + + json_settings : Dict = {} if service_settings is None else service_settings.value + json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value + + json_config_rules = [ + ] + return json_config_rules diff --git a/src/service/service/service_handlers/te/TEServiceHandler.py b/src/service/service/service_handlers/te/TEServiceHandler.py new file mode 100644 index 000000000..d12d642f0 --- /dev/null +++ b/src/service/service/service_handlers/te/TEServiceHandler.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 anytree, json, logging +from typing import Any, List, Optional, Tuple, Union +from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service +from common.tools.object_factory.Device import json_device_id +from common.type_checkers.Checkers import chk_length, chk_type +from service.service.service_handler_api._ServiceHandler import _ServiceHandler +from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value +from service.service.task_scheduler.TaskExecutor import TaskExecutor +from .ConfigRules import setup_config_rules, teardown_config_rules + +LOGGER = logging.getLogger(__name__) + +class TEServiceHandler(_ServiceHandler): + def __init__( # pylint: disable=super-init-not-called + self, service : Service, task_executor : TaskExecutor, **settings + ) -> None: + self.__service = service + self.__task_executor = task_executor # pylint: disable=unused-private-member + self.__resolver = anytree.Resolver(pathattr='name') + self.__config = TreeNode('.') + for config_rule in service.service_config.config_rules: + action = config_rule.action + if config_rule.WhichOneof('config_rule') != 'custom': continue + resource_key = config_rule.custom.resource_key + resource_value = config_rule.custom.resource_value + if action == ConfigActionEnum.CONFIGACTION_SET: + try: + resource_value = json.loads(resource_value) + except: # pylint: disable=bare-except + pass + set_subnode_value(self.__resolver, self.__config, resource_key, resource_value) + elif action == ConfigActionEnum.CONFIGACTION_DELETE: + delete_subnode(self.__resolver, self.__config, resource_key) + + def SetEndpoint( + self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None + ) -> List[Union[bool, Exception]]: + chk_type('endpoints', endpoints, list) + if len(endpoints) == 0: return [] + + service_uuid = self.__service.service_id.service_uuid.uuid + settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None) + + results = [] + for endpoint in endpoints: + try: + chk_type('endpoint', endpoint, (tuple, list)) + chk_length('endpoint', endpoint, min_length=2, max_length=3) + device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now + + endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid) + endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None) + + json_config_rules = setup_config_rules( + service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings) + + device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + for json_config_rule in json_config_rules: + device.device_config.config_rules.append(ConfigRule(**json_config_rule)) + self.__task_executor.configure_device(device) + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint))) + results.append(e) + + return results + + def DeleteEndpoint( + self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None + ) -> List[Union[bool, Exception]]: + chk_type('endpoints', endpoints, list) + if len(endpoints) == 0: return [] + + service_uuid = self.__service.service_id.service_uuid.uuid + settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None) + + results = [] + for endpoint in endpoints: + try: + chk_type('endpoint', endpoint, (tuple, list)) + chk_length('endpoint', endpoint, min_length=2, max_length=3) + device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now + + endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid) + endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None) + + json_config_rules = teardown_config_rules( + service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings) + + device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + for json_config_rule in json_config_rules: + device.device_config.config_rules.append(ConfigRule(**json_config_rule)) + self.__task_executor.configure_device(device) + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint))) + results.append(e) + + return results + + def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('constraints', constraints, list) + if len(constraints) == 0: return [] + + msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.' + LOGGER.warning(msg.format(str(constraints))) + return [True for _ in range(len(constraints))] + + def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('constraints', constraints, list) + if len(constraints) == 0: return [] + + msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.' + LOGGER.warning(msg.format(str(constraints))) + return [True for _ in range(len(constraints))] + + def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('resources', resources, list) + if len(resources) == 0: return [] + + results = [] + for resource in resources: + try: + resource_key, resource_value = resource + resource_value = json.loads(resource_value) + set_subnode_value(self.__resolver, self.__config, resource_key, resource_value) + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource))) + results.append(e) + + return results + + def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('resources', resources, list) + if len(resources) == 0: return [] + + results = [] + for resource in resources: + try: + resource_key, _ = resource + delete_subnode(self.__resolver, self.__config, resource_key) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource))) + results.append(e) + + return results diff --git a/src/service/service/service_handlers/te/__init__.py b/src/service/service/service_handlers/te/__init__.py new file mode 100644 index 000000000..70a332512 --- /dev/null +++ b/src/service/service/service_handlers/te/__init__.py @@ -0,0 +1,14 @@ +# 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. + diff --git a/tutorial/2-6-teraflow-service.json b/tutorial/2-6-teraflow-service.json new file mode 100644 index 000000000..6f8e1d52d --- /dev/null +++ b/tutorial/2-6-teraflow-service.json @@ -0,0 +1,18 @@ +{ + "services": [ + { + "service_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "service_uuid": {"uuid": "2c025055-bf6c-4250-8560-cf62f2d29e72"}}, + "service_type": 4, + "service_status": {"service_status": 2}, + "service_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid":"RT1"}}, "endpoint_uuid": {"uuid":"eth-src"}}, + {"device_id": {"device_uuid": {"uuid":"RT6"}}, "endpoint_uuid": {"uuid":"eth-dst"}} + ], + "service_constraints": [], + "service_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "/lsp[foo]", "resource_value": "{\"src\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT1\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-src\"}}, \"dest\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT6\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-dst\"}}, \"binding_label\": 1111}"}}, + {"action": 1, "custom": {"resource_key": "/lsp[bar]", "resource_value": "{\"src\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT6\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-dst\"}}, \"dest\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT1\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-src\"}}, \"binding_label\": 6666}"}} + ]} + } + ] +} \ No newline at end of file -- GitLab From 856128137d85b05e633648821df7479a45c734b5 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 23 Nov 2022 19:12:08 +0000 Subject: [PATCH 07/63] TE component: - arranged tests folder --- src/te/tests/deploy_specs.sh | 26 +++ .../te/tests/netgen-config.yml | 0 .../te/tests/netgen-topology.yml.template | 0 src/te/tests/service-descriptors.json | 24 ++ .../te/tests/start-testbed.sh | 10 +- src/te/tests/topology-descriptors.json | 201 +++++++++++++++++ tutorial/2-6-te-demo.md | 4 +- tutorial/2-6-teraflow-service.json | 18 -- tutorial/2-6-teraflow-topology.json | 210 ------------------ 9 files changed, 258 insertions(+), 235 deletions(-) create mode 100644 src/te/tests/deploy_specs.sh rename tutorial/2-6-netgen-config.yml => src/te/tests/netgen-config.yml (100%) rename tutorial/2-6-netgen-topology.yml.template => src/te/tests/netgen-topology.yml.template (100%) create mode 100644 src/te/tests/service-descriptors.json rename tutorial/2-6-te-demo-start-testbed.sh => src/te/tests/start-testbed.sh (79%) create mode 100644 src/te/tests/topology-descriptors.json delete mode 100644 tutorial/2-6-teraflow-service.json delete mode 100644 tutorial/2-6-teraflow-topology.json diff --git a/src/te/tests/deploy_specs.sh b/src/te/tests/deploy_specs.sh new file mode 100644 index 000000000..17dd44dd5 --- /dev/null +++ b/src/te/tests/deploy_specs.sh @@ -0,0 +1,26 @@ +# Set the URL of your local Docker registry where the images will be uploaded to. +export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" + +# Set the list of components, separated by spaces, you want to build images for, and deploy. +# Supported components are: +# context device automation policy service compute monitoring webui +# interdomain slice pathcomp dlt +# dbscanserving opticalattackmitigator opticalattackdetector +# l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector +export TFS_COMPONENTS="context device pathcomp service slice webui" # te + +# Set the tag you want to use for your images. +export TFS_IMAGE_TAG="dev" + +# Set the name of the Kubernetes namespace to deploy to. +export TFS_K8S_NAMESPACE="tfs" + +# Set additional manifest files to be applied after the deployment +export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml" + +# Set the new Grafana admin password +export TFS_GRAFANA_PASSWORD="admin123+" + +# If not already set, disable skip-build flag. +# If TFS_SKIP_BUILD is "YES", the containers are not rebuilt-retagged-repushed and existing ones are used. +export TFS_SKIP_BUILD=${TFS_SKIP_BUILD:-""} diff --git a/tutorial/2-6-netgen-config.yml b/src/te/tests/netgen-config.yml similarity index 100% rename from tutorial/2-6-netgen-config.yml rename to src/te/tests/netgen-config.yml diff --git a/tutorial/2-6-netgen-topology.yml.template b/src/te/tests/netgen-topology.yml.template similarity index 100% rename from tutorial/2-6-netgen-topology.yml.template rename to src/te/tests/netgen-topology.yml.template diff --git a/src/te/tests/service-descriptors.json b/src/te/tests/service-descriptors.json new file mode 100644 index 000000000..15023ac9d --- /dev/null +++ b/src/te/tests/service-descriptors.json @@ -0,0 +1,24 @@ +{ + "services": [ + { + "service_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "service_uuid": {"uuid": "2c025055-bf6c-4250-8560-cf62f2d29e72"} + }, + "service_type": 4, "service_status": {"service_status": 1}, + "service_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid":"RT1"}}, "endpoint_uuid": {"uuid":"eth-src"}}, + {"device_id": {"device_uuid": {"uuid":"RT6"}}, "endpoint_uuid": {"uuid":"eth-dst"}} + ], + "service_constraints": [], + "service_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "/lsp-fw", "resource_value": { + "symbolic_name": "foo", "binding_label": 1111 + }}}, + {"action": 1, "custom": {"resource_key": "/lsp-bw", "resource_value": { + "symbolic_name": "bar", "binding_label": 6666 + }}} + ]} + } + ] +} diff --git a/tutorial/2-6-te-demo-start-testbed.sh b/src/te/tests/start-testbed.sh similarity index 79% rename from tutorial/2-6-te-demo-start-testbed.sh rename to src/te/tests/start-testbed.sh index 5ba48479c..c480b3e75 100755 --- a/tutorial/2-6-te-demo-start-testbed.sh +++ b/src/te/tests/start-testbed.sh @@ -4,10 +4,10 @@ set -e ROOTDIR="$( cd $( dirname $0 ); pwd )" RUNDIR="$( pwd )" -NEGENDIR="${RUNDIR}/netgen" +NETGENDIR="${RUNDIR}/netgen" -if [[ ! -f "${NEGENDIR}/exe/netgen" ]]; then - echo "Failed to find Netgen binary at ${NEGENDIR}/exe/netgen" +if [[ ! -f "${NETGENDIR}/exe/netgen" ]]; then + echo "Failed to find Netgen binary at ${NETGENDIR}/exe/netgen" exit 1 fi @@ -36,8 +36,8 @@ export RT1_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.11" export RT6_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.12" export RT6_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.13" -cp "${ROOTDIR}/2-6-netgen-config.yml" "${RUNDIR}/config.yml" -cat "${ROOTDIR}/2-6-netgen-topology.yml.template" | envsubst > "${RUNDIR}/topology.yml" +cp "${ROOTDIR}/netgen-config.yml" "${RUNDIR}/config.yml" +cat "${ROOTDIR}/netgen-topology.yml.template" | envsubst > "${RUNDIR}/topology.yml" sudo -i bash -c "\ cd ${RUNDIR}/netgen;\ diff --git a/src/te/tests/topology-descriptors.json b/src/te/tests/topology-descriptors.json new file mode 100644 index 000000000..82f37ce29 --- /dev/null +++ b/src/te/tests/topology-descriptors.json @@ -0,0 +1,201 @@ +{ + "contexts": [ + { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_ids": [], + "service_ids": [] + } + ], + "topologies": [ + { + "topology_id": {"topology_uuid": {"uuid": "tetestbed"}, "context_id": {"context_uuid": {"uuid": "admin"}}}, + "device_ids": [ + {"device_uuid": {"uuid": "SW1"}}, + {"device_uuid": {"uuid": "RT1"}}, + {"device_uuid": {"uuid": "RT2"}}, + {"device_uuid": {"uuid": "RT3"}}, + {"device_uuid": {"uuid": "RT4"}}, + {"device_uuid": {"uuid": "RT5"}}, + {"device_uuid": {"uuid": "RT6"}} + ], + "link_ids": [ + {"link_uuid": {"uuid": "RT1/SW1"}}, + {"link_uuid": {"uuid": "RT2/SW1"}}, + {"link_uuid": {"uuid": "RT3/SW1"}}, + {"link_uuid": {"uuid": "RT2/RT4/1"}}, + {"link_uuid": {"uuid": "RT2/RT4/2"}}, + {"link_uuid": {"uuid": "RT3/RT5/1"}}, + {"link_uuid": {"uuid": "RT3/RT5/2"}}, + {"link_uuid": {"uuid": "RT4/RT5"}}, + {"link_uuid": {"uuid": "RT4/RT6"}}, + {"link_uuid": {"uuid": "RT5/RT6"}} + ] + } + ], + "devices": [ + { + "device_id": {"device_uuid": {"uuid": "SW1"}}, "device_type": "emu-packet-switch", + "device_operational_status": 2, "device_drivers": [0], "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "df8bb169-2013-4b82-9455-69777f7a01d6"}, + {"sample_types": [], "type": "copper", "uuid": "061119c1-2aa4-48e9-be64-3ddf465fc80a"}, + {"sample_types": [], "type": "copper", "uuid": "495ea3f8-e67f-46a0-84bd-a230a4b7067d"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "RT1"}}, "device_type": "emu-packet-router", + "device_operational_status": 2, "device_drivers": [0], "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "1.1.1.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "eth-src"}, + {"sample_types": [], "type": "copper", "uuid": "eth-sw1"} + ]}}}, + {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16010"}}, + {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "1.1.1.1"}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "RT2"}}, "device_type": "emu-packet-router", + "device_operational_status": 2, "device_drivers": [0], "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "2.2.2.2"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "eth-sw1"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt4-1"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt4-2"} + ]}}}, + {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16020"}}, + {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "2.2.2.2"}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "RT3"}}, "device_type": "emu-packet-router", + "device_operational_status": 2, "device_drivers": [0], "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "3.3.3.3"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "eth-sw1"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt5-1"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt5-2"} + ]}}}, + {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16030"}}, + {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "3.3.3.3"}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "RT4"}}, "device_type": "emu-packet-router", + "device_operational_status": 2, "device_drivers": [0], "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "4.4.4.4"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "eth-rt2-1"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt2-2"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt5"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt6"} + ]}}}, + {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16040"}}, + {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "4.4.4.4"}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "RT5"}}, "device_type": "emu-packet-router", + "device_operational_status": 2, "device_drivers": [0], "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "5.5.5.5"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "eth-rt3-1"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt3-2"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt4"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt6"} + ]}}}, + {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16050"}}, + {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "5.5.5.5"}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "RT6"}}, "device_type": "emu-packet-router", + "device_operational_status": 2, "device_drivers": [0], "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "6.6.6.6"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "eth-rt4"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt5"}, + {"sample_types": [], "type": "copper", "uuid": "eth-dst"} + ]}}}, + {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16060"}}, + {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "6.6.6.6"}} + ]} + } + ], + "links": [ + { + "link_id": {"link_uuid": {"uuid": "RT1/SW1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT1"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, + {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "df8bb169-2013-4b82-9455-69777f7a01d6"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT2/SW1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, + {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "061119c1-2aa4-48e9-be64-3ddf465fc80a"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT3/SW1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, + {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "495ea3f8-e67f-46a0-84bd-a230a4b7067d"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT2/RT4/1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-rt4-1"}}, + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt2-1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT2/RT4/2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-rt4-2"}}, + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt2-2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT3/RT5/1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-rt5-1"}}, + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt3-1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT3/RT5/2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-rt5-2"}}, + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt3-2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT4/RT5"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt5"}}, + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt4"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT4/RT6"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt6"}}, + {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-rt4"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT5/RT6"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt6"}}, + {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-rt5"}} + ] + } + ] +} diff --git a/tutorial/2-6-te-demo.md b/tutorial/2-6-te-demo.md index da822e3a3..9bfd7881c 100644 --- a/tutorial/2-6-te-demo.md +++ b/tutorial/2-6-te-demo.md @@ -78,12 +78,12 @@ ### Run the Test-Bed -First load the [teraflow configuration file](./2-6-teraflow-topology.json) using the webui. +First load the [teraflow configuration file](~/tfs-ctrl/src/te/tests/topology-descriptors.json) using the webui. The first time the configuration is loaded may partialy fail because it tries to load the topology before the devices and links, but a second load should work. In first console: $ cd ~/testbed - $ ../tfs-ctrl/tutorial/2-6-te-demo-start-testbed.sh + $ ../tfs-ctrl/src/te/tests/start-testbed.sh Then in second console: $ sudo -i diff --git a/tutorial/2-6-teraflow-service.json b/tutorial/2-6-teraflow-service.json deleted file mode 100644 index 6f8e1d52d..000000000 --- a/tutorial/2-6-teraflow-service.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "services": [ - { - "service_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "service_uuid": {"uuid": "2c025055-bf6c-4250-8560-cf62f2d29e72"}}, - "service_type": 4, - "service_status": {"service_status": 2}, - "service_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid":"RT1"}}, "endpoint_uuid": {"uuid":"eth-src"}}, - {"device_id": {"device_uuid": {"uuid":"RT6"}}, "endpoint_uuid": {"uuid":"eth-dst"}} - ], - "service_constraints": [], - "service_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "/lsp[foo]", "resource_value": "{\"src\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT1\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-src\"}}, \"dest\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT6\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-dst\"}}, \"binding_label\": 1111}"}}, - {"action": 1, "custom": {"resource_key": "/lsp[bar]", "resource_value": "{\"src\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT6\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-dst\"}}, \"dest\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT1\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-src\"}}, \"binding_label\": 6666}"}} - ]} - } - ] -} \ No newline at end of file diff --git a/tutorial/2-6-teraflow-topology.json b/tutorial/2-6-teraflow-topology.json deleted file mode 100644 index 8b5188866..000000000 --- a/tutorial/2-6-teraflow-topology.json +++ /dev/null @@ -1,210 +0,0 @@ -{ - "contexts": [ - { - "context_id": {"context_uuid": {"uuid": "admin"}}, - "topology_ids": [], - "service_ids": [] - } - ], - "topologies": [ - { - "topology_id": {"topology_uuid": {"uuid": "tetestbed"}, "context_id": {"context_uuid": {"uuid": "admin"}}}, - "device_ids": [ - {"device_uuid": {"uuid": "SW1"}}, - {"device_uuid": {"uuid": "RT1"}}, - {"device_uuid": {"uuid": "RT2"}}, - {"device_uuid": {"uuid": "RT3"}}, - {"device_uuid": {"uuid": "RT4"}}, - {"device_uuid": {"uuid": "RT5"}}, - {"device_uuid": {"uuid": "RT6"}} - ], - "link_ids": [ - {"link_uuid": {"uuid": "RT1/SW1"}}, - {"link_uuid": {"uuid": "RT2/SW1"}}, - {"link_uuid": {"uuid": "RT3/SW1"}}, - {"link_uuid": {"uuid": "RT2/RT4/1"}}, - {"link_uuid": {"uuid": "RT2/RT4/2"}}, - {"link_uuid": {"uuid": "RT3/RT5/1"}}, - {"link_uuid": {"uuid": "RT3/RT5/2"}}, - {"link_uuid": {"uuid": "RT4/RT5"}}, - {"link_uuid": {"uuid": "RT4/RT6"}}, - {"link_uuid": {"uuid": "RT5/RT6"}} - ] - } - ], - "devices": [ - { - "device_id": {"device_uuid": {"uuid": "SW1"}}, - "device_type": "emu-packet-switch", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": - "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"df8bb169-2013-4b82-9455-69777f7a01d6\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"061119c1-2aa4-48e9-be64-3ddf465fc80a\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"495ea3f8-e67f-46a0-84bd-a230a4b7067d\"}]}"}} - ]}, - "device_operational_status": 1, - "device_drivers": [0], - "device_endpoints": [] - }, - { - "device_id": {"device_uuid": {"uuid": "RT1"}}, - "device_type": "emu-packet-router", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16010"}}, - {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "1.1.1.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "1.1.1.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": - "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-src\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}]}"}} - ]}, - "device_operational_status": 1, - "device_drivers": [0], - "device_endpoints": [] - }, - { - "device_id": {"device_uuid": {"uuid": "RT2"}}, - "device_type": "emu-packet-router", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16020"}}, - {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "2.2.2.2"}}, - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "2.2.2.2"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": - "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4-2\"}]}"}} - ]}, - "device_operational_status": 1, - "device_drivers": [0], - "device_endpoints": [] - }, - { - "device_id": {"device_uuid": {"uuid": "RT3"}}, - "device_type": "emu-packet-router", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16030"}}, - {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "3.3.3.3"}}, - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "3.3.3.3"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": - "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5-2\"}]}"}} - ]}, - "device_operational_status": 1, - "device_drivers": [0], - "device_endpoints": [] - }, - { - "device_id": {"device_uuid": {"uuid": "RT4"}}, - "device_type": "emu-packet-router", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16040"}}, - {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "4.4.4.4"}}, - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "4.4.4.4"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": - "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt2-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt2-2\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt6\"}]}"}} - ]}, - "device_operational_status": 1, - "device_drivers": [0], - "device_endpoints": [] - }, - { - "device_id": {"device_uuid": {"uuid": "RT5"}}, - "device_type": "emu-packet-router", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16050"}}, - {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "5.5.5.5"}}, - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "5.5.5.5"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": - "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt3-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt3-2\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt6\"}]}"}} - ]}, - "device_operational_status": 1, - "device_drivers": [0], - "device_endpoints": [] - }, - { - "device_id": {"device_uuid": {"uuid": "RT6"}}, - "device_type": "emu-packet-router", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16060"}}, - {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "6.6.6.6"}}, - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "6.6.6.6"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": - "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-dst\"}]}"}} - ]}, - "device_operational_status": 1, - "device_drivers": [0], - "device_endpoints": [] - } - ], - "links": [ - { - "link_id": {"link_uuid": {"uuid": "RT1/SW1"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT1"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, - {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "df8bb169-2013-4b82-9455-69777f7a01d6"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT2/SW1"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, - {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "061119c1-2aa4-48e9-be64-3ddf465fc80a"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT3/SW1"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, - {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "495ea3f8-e67f-46a0-84bd-a230a4b7067d"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT2/RT4/1"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-rt4-1"}}, - {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt2-1"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT2/RT4/2"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-rt4-2"}}, - {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt2-2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT3/RT5/1"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-rt5-1"}}, - {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt3-1"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT3/RT5/2"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-rt5-2"}}, - {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt3-2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT4/RT5"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt5"}}, - {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt4"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT4/RT6"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt6"}}, - {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-rt4"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT5/RT6"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt6"}}, - {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-rt5"}} - ] - } - ] -} -- GitLab From 880eafa7325a8545e26feb1468d7c0c9a47e8eee Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 23 Nov 2022 19:12:26 +0000 Subject: [PATCH 08/63] Common: - added TE service constants --- src/common/Constants.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/Constants.py b/src/common/Constants.py index ed1c1475a..423f2558b 100644 --- a/src/common/Constants.py +++ b/src/common/Constants.py @@ -56,6 +56,7 @@ class ServiceNameEnum(Enum): OPTICALATTACKDETECTOR = 'opticalattackdetector' OPTICALATTACKMITIGATOR = 'opticalattackmitigator' CACHING = 'caching' + TE = 'te' # Used for test and debugging only DLT_GATEWAY = 'dltgateway' @@ -80,6 +81,7 @@ DEFAULT_SERVICE_GRPC_PORTS = { ServiceNameEnum.OPTICALATTACKMANAGER .value : 10005, ServiceNameEnum.INTERDOMAIN .value : 10010, ServiceNameEnum.PATHCOMP .value : 10020, + ServiceNameEnum.TE .value : 10030, # Used for test and debugging only ServiceNameEnum.DLT_GATEWAY .value : 50051, -- GitLab From e163a6067a4cb595cacb0ce1fb2287ed57f258f5 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 23 Nov 2022 19:13:28 +0000 Subject: [PATCH 09/63] Service component: - added TEServiceClient - added branch to redirect requests to TE component for TE services --- src/service/client/TEServiceClient.py | 67 ++++++++++++ .../service/ServiceServiceServicerImpl.py | 101 +++++++++++++----- 2 files changed, 139 insertions(+), 29 deletions(-) create mode 100644 src/service/client/TEServiceClient.py diff --git a/src/service/client/TEServiceClient.py b/src/service/client/TEServiceClient.py new file mode 100644 index 000000000..8bbbe8f9b --- /dev/null +++ b/src/service/client/TEServiceClient.py @@ -0,0 +1,67 @@ +# 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, logging +from common.Constants import ServiceNameEnum +from common.Settings import get_service_host, get_service_port_grpc +from common.proto.context_pb2 import Empty, Service, ServiceId, ServiceStatus +from common.proto.te_pb2_grpc import TEServiceStub +from common.tools.client.RetryDecorator import retry, delay_exponential +from common.tools.grpc.Tools import grpc_message_to_json_string + +LOGGER = logging.getLogger(__name__) +MAX_RETRIES = 15 +DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0) +RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') + +class TEServiceClient: + def __init__(self, host=None, port=None): + if not host: host = get_service_host(ServiceNameEnum.TE) + if not port: port = get_service_port_grpc(ServiceNameEnum.TE) + self.endpoint = '{:s}:{:s}'.format(str(host), str(port)) + LOGGER.debug('Creating channel to {:s}...'.format(str(self.endpoint))) + self.channel = None + self.stub = None + self.connect() + LOGGER.debug('Channel created') + + def connect(self): + self.channel = grpc.insecure_channel(self.endpoint) + self.stub = TEServiceStub(self.channel) + + def close(self): + if self.channel is not None: self.channel.close() + self.channel = None + self.stub = None + + @RETRY_DECORATOR + def RequestLSP(self, request : Service) -> ServiceStatus: + LOGGER.debug('RequestLSP request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.RequestLSP(request) + LOGGER.debug('RequestLSP result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def UpdateLSP(self, request : ServiceId) -> ServiceStatus: + LOGGER.debug('UpdateLSP request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.UpdateLSP(request) + LOGGER.debug('UpdateLSP result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def DeleteLSP(self, request : ServiceId) -> Empty: + LOGGER.debug('DeleteLSP request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.DeleteLSP(request) + LOGGER.debug('DeleteLSP result: {:s}'.format(grpc_message_to_json_string(response))) + return response diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py index 6d23fd4ce..54303448b 100644 --- a/src/service/service/ServiceServiceServicerImpl.py +++ b/src/service/service/ServiceServiceServicerImpl.py @@ -26,6 +26,7 @@ from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_s from context.client.ContextClient import ContextClient from pathcomp.frontend.client.PathCompClient import PathCompClient from service.service.tools.ConnectionToString import connection_to_string +from service.client.TEServiceClient import TEServiceClient from .service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory from .task_scheduler.TaskScheduler import TasksScheduler @@ -119,37 +120,71 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): context_client, service_id_with_uuids, rw_copy=False, include_config_rules=True, include_constraints=True, include_endpoint_ids=True) - num_disjoint_paths = 0 - for constraint in request.service_constraints: - if constraint.WhichOneof('constraint') == 'sla_availability': - num_disjoint_paths = constraint.sla_availability.num_disjoint_paths - break - - num_disjoint_paths = 1 if num_disjoint_paths is None or num_disjoint_paths == 0 else num_disjoint_paths - num_expected_endpoints = num_disjoint_paths * 2 + if service.service_type == ServiceTypeEnum.SERVICETYPE_TE: + # TE service: + te_service_client = TEServiceClient() + + # Note: TE should update the service in Context. + # By now we update it manually for debugging purposes + service = Service() + service.CopyFrom(request) + service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED + context_client.SetService(request) + + service_status = te_service_client.RequestLSP(service) + + if service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE: + _service : Optional[Service] = get_service(context_client, request.service_id) + service = Service() + service.CopyFrom(_service) + service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_ACTIVE + context_client.SetService(service) + else: + MSG = 'RequestLSP for Service({:s}) returned ServiceStatus({:s})' + context_uuid = request.service_id.context_id.context_uuid.uuid + service_uuid = request.service_id.service_uuid.uuid + service_key = '{:s}/{:s}'.format(context_uuid, service_uuid) + str_service_status = ServiceStatusEnum.Name(service_status.service_status) + raise Exception(MSG.format(service_key, str_service_status)) + else: + # Normal service: + num_disjoint_paths = None + for constraint in request.service_constraints: + if constraint.WhichOneof('constraint') == 'sla_availability': + num_disjoint_paths = constraint.sla_availability.num_disjoint_paths + break + + num_disjoint_paths = 1 if num_disjoint_paths is None or num_disjoint_paths == 0 else num_disjoint_paths + num_expected_endpoints = num_disjoint_paths * 2 tasks_scheduler = TasksScheduler(self.service_handler_factory) if len(service_with_uuids.service_endpoint_ids) >= num_expected_endpoints: pathcomp_request = PathCompRequest() pathcomp_request.services.append(service_with_uuids) # pylint: disable=no-member - if num_disjoint_paths is None or num_disjoint_paths in {0, 1}: - pathcomp_request.shortest_path.Clear() # pylint: disable=no-member - else: - pathcomp_request.k_disjoint_path.num_disjoint = num_disjoint_paths # pylint: disable=no-member + tasks_scheduler = TasksScheduler(self.service_handler_factory) + if len(service_with_uuids.service_endpoint_ids) >= num_expected_endpoints: + pathcomp_request = PathCompRequest() + pathcomp_request.services.append(service_with_uuids) # pylint: disable=no-member - LOGGER.debug('pathcomp_request={:s}'.format(grpc_message_to_json_string(pathcomp_request))) - pathcomp = PathCompClient() - pathcomp_reply = pathcomp.Compute(pathcomp_request) - pathcomp.close() - LOGGER.debug('pathcomp_reply={:s}'.format(grpc_message_to_json_string(pathcomp_reply))) + if num_disjoint_paths is None or num_disjoint_paths in {0, 1}: + pathcomp_request.shortest_path.Clear() # pylint: disable=no-member + else: + pathcomp_request.k_disjoint_path.num_disjoint = num_disjoint_paths # pylint: disable=no-member - # Feed TaskScheduler with this path computation reply. TaskScheduler identifies inter-dependencies among - # the services and connections retrieved and produces a schedule of tasks (an ordered list of tasks to be - # executed) to implement the requested create/update operation. - tasks_scheduler.compose_from_pathcompreply(pathcomp_reply, is_delete=False) + LOGGER.info('pathcomp_request={:s}'.format(grpc_message_to_json_string(pathcomp_request))) + pathcomp = PathCompClient() + pathcomp_reply = pathcomp.Compute(pathcomp_request) + pathcomp.close() + LOGGER.info('pathcomp_reply={:s}'.format(grpc_message_to_json_string(pathcomp_reply))) + + # Feed TaskScheduler with this path computation reply. TaskScheduler identifies inter-dependencies among + # the services and connections retrieved and produces a schedule of tasks (an ordered list of tasks to be + # executed) to implement the requested create/update operation. + tasks_scheduler.compose_from_pathcompreply(pathcomp_reply, is_delete=False) + + tasks_scheduler.execute_all() - tasks_scheduler.execute_all() return service_with_uuids.service_id @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) @@ -164,12 +199,20 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PENDING_REMOVAL context_client.SetService(service) - # Feed TaskScheduler with this service and the sub-services and sub-connections related to this service. - # TaskScheduler identifies inter-dependencies among them and produces a schedule of tasks (an ordered list of - # tasks to be executed) to implement the requested delete operation. - tasks_scheduler = TasksScheduler(self.service_handler_factory) - tasks_scheduler.compose_from_service(service, is_delete=True) - tasks_scheduler.execute_all() + if service.service_type == ServiceTypeEnum.SERVICETYPE_TE: + # TE service + te_service_client = TEServiceClient() + te_service_client.DeleteLSP(request) + context_client.RemoveService(request) + else: + # Normal service + # Feed TaskScheduler with this service and the sub-services and sub-connections related to this service. + # TaskScheduler identifies inter-dependencies among them and produces a schedule of tasks (an ordered list of + # tasks to be executed) to implement the requested delete operation. + tasks_scheduler = TasksScheduler(self.service_handler_factory) + tasks_scheduler.compose_from_service(service, is_delete=True) + tasks_scheduler.execute_all() + return Empty() @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) @@ -327,4 +370,4 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): updated_service_with_uuids, old_connection, new_connection) tasks_scheduler.execute_all() - return Empty() + return Empty() \ No newline at end of file -- GitLab From 13b24f0717a50df3c13df0c4a89a62e09ae7187d Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 23 Nov 2022 19:29:12 +0000 Subject: [PATCH 10/63] Service component: - increased Log level to DEBUG --- manifests/serviceservice.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifests/serviceservice.yaml b/manifests/serviceservice.yaml index 7d7bdaa4e..3865fd6c0 100644 --- a/manifests/serviceservice.yaml +++ b/manifests/serviceservice.yaml @@ -36,7 +36,7 @@ spec: - containerPort: 9192 env: - name: LOG_LEVEL - value: "INFO" + value: "DEBUG" readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:3030"] -- GitLab From 2836bc9e6972ba390a755e906ec79d29ae58176e Mon Sep 17 00:00:00 2001 From: merle Date: Fri, 25 Nov 2022 04:09:17 -0800 Subject: [PATCH 11/63] Details a bit more the Erlang environment setup documentation --- tutorial/1-6-setup-erlang-environmnet.md | 24 ++++++++++++++++-------- tutorial/2-6-te-demo.md | 4 ++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/tutorial/1-6-setup-erlang-environmnet.md b/tutorial/1-6-setup-erlang-environmnet.md index 11e65a49a..2d1519a6f 100644 --- a/tutorial/1-6-setup-erlang-environmnet.md +++ b/tutorial/1-6-setup-erlang-environmnet.md @@ -1,6 +1,9 @@ # 1.5. Setup Erlang Environment -First we need to install Erlang. There is multiple way, for development we will be using asdf. +First we need to install Erlang. There is multiple way, for development we will +be using *ASDF*, a tool that allows the installation of multiple version of Erlang +at the same time, and switch from one version to the other at will. + ## 1.5.1. Setup Erlang using asdf @@ -8,31 +11,36 @@ First, install any missing dependencies: sudo apt install curl git autoconf libncurses-dev build-essential m4 libssl-dev -Download asdf: +Download *ASDF* tool to the local account: git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.10.2 -Make asdf activate on login by adding these lines at the end of the `~/.bashrc` file: +Make *ASDF* activate on login by adding these lines at the end of the `~/.bashrc` file: . $HOME/.asdf/asdf.sh . $HOME/.asdf/completions/asdf.bash -Logout and log back in to activate asdf. +Logout and log back in to activate *ASDF*. -Install asdf plugin for Erlang: +*ASDF* supports multiple tools by installing there corresponding plugins. +Install *ASDF* plugin for Erlang: asdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git -Install Erlang: +Install a version of Erlang: asdf install erlang 24.3.4.2 -Activate Erlang locally for TFS controller: +Activate Erlang locally for TFS controller. This will create a local file +called `.tool-versions` defining which version of the tools to use when +running under the current directory: cd tfs-ctrl/ asdf local erlang 24.3.4.2 -Install rebar3: +Erlang projects uses a build tool called rebar3. It is used to manager project +dependenecies, compile a project and generate project releases. +Install rebar3 localy from source: cd ~ git clone https://github.com/erlang/rebar3.git diff --git a/tutorial/2-6-te-demo.md b/tutorial/2-6-te-demo.md index 9bfd7881c..778d62e06 100644 --- a/tutorial/2-6-te-demo.md +++ b/tutorial/2-6-te-demo.md @@ -1,4 +1,4 @@ -# 2.6. Traffic Engineering Demo (PENDING) +# 2.6. Traffic Engineering Demo (WIP) ## Setup Test-Bed @@ -20,7 +20,7 @@ ### Setup Free Range Routing $ sudo apt update - $ sudo apt-get install git autoconf automake libtool make libreadline-dev texinfo pkg-config libpam0g-dev libjson-c-dev bison flex libc-ares-dev python3-dev python3-sphinx install-info build-essential libsnmp-dev perl libcap-dev python2 libelf-dev libunwind-dev protobuf-c-compiler libprotobuf-c-dev libsystemd-dev + $ sudo apt-get install git autoconf automake libtool make libreadline-dev texinfo pkg-config libpam0g-dev libjson-c-dev bison flex libc-ares-dev python3-dev python3-sphinx install-info build-essential libsnmp-dev perl libcap-dev python2 libelf-dev libunwind-dev protobuf-c-compiler libprotobuf-c-dev libsystemd-dev $ mkdir -p ~/testbed $ cd ~/testbed $ git clone git@github.com:opensourcerouting/frr.git -- GitLab From 54e96c8188a4678ed8360f42e78662f75760fc76 Mon Sep 17 00:00:00 2001 From: merle Date: Thu, 1 Dec 2022 08:41:19 -0800 Subject: [PATCH 12/63] te: Fix dependencies --- src/te/Dockerfile | 1 + src/te/apps/epce/src/epce_server.erl | 3 ++- src/te/apps/tfte/src/tfte_app.erl | 2 +- src/te/rebar.config | 2 +- src/te/rebar.lock | 10 +++++----- src/te/tests/deploy_specs.sh | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/te/Dockerfile b/src/te/Dockerfile index f580ccf4e..98194f254 100644 --- a/src/te/Dockerfile +++ b/src/te/Dockerfile @@ -35,6 +35,7 @@ FROM alpine # Install some libs RUN apk add --no-cache libgcc libstdc++ && \ apk add --no-cache openssl && \ + apk add --no-cache libcrypto1.1 && \ apk add --no-cache ncurses-libs # Install the released application diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl index 507255385..03f1dacb2 100644 --- a/src/te/apps/epce/src/epce_server.erl +++ b/src/te/apps/epce/src/epce_server.erl @@ -234,7 +234,8 @@ compute_path(From, To) -> logger:debug("Route from ~p to ~p: ~p", [From, To, Labels]), {ok, Labels}; {error, Reason} -> - logger:warning("Failed to find a route from ~p to ~p", [From, To]), + logger:warning("Failed to find a route from ~p to ~p: ~p", + [From, To, Reason]), {error, route_not_found} end. diff --git a/src/te/apps/tfte/src/tfte_app.erl b/src/te/apps/tfte/src/tfte_app.erl index 96724904d..12f8cd4fc 100644 --- a/src/te/apps/tfte/src/tfte_app.erl +++ b/src/te/apps/tfte/src/tfte_app.erl @@ -57,7 +57,7 @@ add_services([{Name, EndpointsSpecs, GrpcOpts} | Rest]) -> end catch throw:{Name, Reason, Extra} -> - ?LOG_WARNING("Failed to resolve ~s service configuration: ~s ~p ~p", + ?LOG_WARNING("Failed to resolve ~s service configuration: ~s ~p", [Name, Reason, Extra]) end, add_services(Rest). diff --git a/src/te/rebar.config b/src/te/rebar.config index 3451955c2..776a80249 100644 --- a/src/te/rebar.config +++ b/src/te/rebar.config @@ -2,7 +2,7 @@ {deps, [ grpcbox, - {pcep_server, {git, "git@github.com:stritzinger/pcep_server.git", {branch, "master"}}} + {pcep_server, {git, "https://github.com/stritzinger/pcep_server.git", {branch, "master"}}} ]}. {shell, [ diff --git a/src/te/rebar.lock b/src/te/rebar.lock index ef9747fe4..446884e6b 100644 --- a/src/te/rebar.lock +++ b/src/te/rebar.lock @@ -2,7 +2,7 @@ [{<<"acceptor_pool">>,{pkg,<<"acceptor_pool">>,<<"1.0.0">>},1}, {<<"chatterbox">>,{pkg,<<"ts_chatterbox">>,<<"0.12.0">>},1}, {<<"codec_sequencer">>, - {git,"git@github.com:stritzinger/codec_sequencer.git", + {git,"https://github.com/stritzinger/codec_sequencer.git", {ref,"fc8760894f7962ef1497bf6ce4247eb75db9d5ca"}}, 2}, {<<"ctx">>,{pkg,<<"ctx">>,<<"0.6.0">>},1}, @@ -10,12 +10,12 @@ {<<"grpcbox">>,{pkg,<<"grpcbox">>,<<"0.15.0">>},0}, {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}, {<<"pcep_codec">>, - {git,"git@github.com:stritzinger/pcep_codec.git", - {ref,"84dcc430b2aa427984c100cc19dd35d946b22ff9"}}, + {git,"https://github.com/stritzinger/pcep_codec.git", + {ref,"ca5eb0822d9971ec4bcfb427a49b2e516081a126"}}, 1}, {<<"pcep_server">>, - {git,"git@github.com:stritzinger/pcep_server.git", - {ref,"3910bf5546879cb91a3483008b6aed11753deaa6"}}, + {git,"https://github.com/stritzinger/pcep_server.git", + {ref,"ea751fa807f4c1f5635f781431fe384610166b0a"}}, 0}, {<<"ranch">>,{pkg,<<"ranch">>,<<"2.0.0">>},1}]}. [ diff --git a/src/te/tests/deploy_specs.sh b/src/te/tests/deploy_specs.sh index 17dd44dd5..cd8871a05 100644 --- a/src/te/tests/deploy_specs.sh +++ b/src/te/tests/deploy_specs.sh @@ -7,7 +7,7 @@ export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" # interdomain slice pathcomp dlt # dbscanserving opticalattackmitigator opticalattackdetector # l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector -export TFS_COMPONENTS="context device pathcomp service slice webui" # te +export TFS_COMPONENTS="context device pathcomp service slice webui te" # Set the tag you want to use for your images. export TFS_IMAGE_TAG="dev" -- GitLab From c79f932d0e51c2f94d38252f0b835fb764bf921f Mon Sep 17 00:00:00 2001 From: merle Date: Thu, 15 Dec 2022 06:23:41 -0800 Subject: [PATCH 13/63] First simple integration with the service component --- src/te/apps/epce/src/epce_server.erl | 68 ++++++++++++++++-------- src/te/apps/epce/src/epce_ted.erl | 20 +++++++ src/te/apps/tfte/src/tfte.app.src | 1 + src/te/apps/tfte/src/tfte_server.erl | 59 ++++++++++++++++++++ src/te/apps/tfte/src/tfte_te_service.erl | 23 +++++--- src/te/apps/tfte/src/tfte_topology.erl | 36 ++++++------- src/te/apps/tfte/src/tfte_util.erl | 27 ++++++++++ src/te/rebar.config | 2 + src/te/rebar.lock | 3 ++ 9 files changed, 188 insertions(+), 51 deletions(-) create mode 100644 src/te/apps/tfte/src/tfte_util.erl diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl index 03f1dacb2..8996d3b35 100644 --- a/src/te/apps/epce/src/epce_server.erl +++ b/src/te/apps/epce/src/epce_server.erl @@ -67,8 +67,8 @@ get_flows() -> update_flow(FlowId, LabelStack) -> gen_server:call(?MODULE, {update_flow, FlowId, LabelStack}). -initiate_flow(Name, FromAddr, ToAddr, BindingLabel) -> - gen_server:call(?MODULE, {initiate_flow, Name, FromAddr, ToAddr, +initiate_flow(Name, From, To, BindingLabel) -> + gen_server:call(?MODULE, {initiate_flow, Name, From, To, BindingLabel}, ?LARGE_TIMEOUT). @@ -111,19 +111,26 @@ handle_call({update_flow, FlowId, Labels}, From, {noreply, State} end end; -handle_call({initiate_flow, Name, FromAddr, ToAddr, Binding}, From, +handle_call({initiate_flow, Name, FromKey, ToKey, Binding}, From, #state{sessions = SessMap} = State) -> - case maps:find(FromAddr, SessMap) of - error -> {reply, {error, session_not_found}, State}; - {ok, #sess{pid = Pid}} -> - case compute_path(FromAddr, ToAddr) of - {error, Reason} -> - {reply, {error, Reason}, State}; - {ok, Labels} -> - InitRoute = routeinit_from_labels(Name, FromAddr, ToAddr, - [], Binding, Labels), - session_initiate_flow(State, Pid, InitRoute, From), - {noreply, State} + case {pcc_address(FromKey), pcc_address(ToKey)} of + {{error, Reason}, _} -> + {reply, {error, Reason}, State}; + {_, {error, Reason}} -> + {reply, {error, Reason}, State}; + {{ok, FromAddr}, {ok, ToAddr}} -> + case maps:find(FromAddr, SessMap) of + error -> {reply, {error, session_not_found}, State}; + {ok, #sess{pid = Pid}} -> + case compute_path(FromAddr, ToAddr) of + {error, Reason} -> + {reply, {error, Reason}, State}; + {ok, Labels} -> + InitRoute = routeinit_from_labels(Name, FromAddr, + ToAddr, [], Binding, Labels), + session_initiate_flow(State, Pid, InitRoute, From), + {noreply, State} + end end end; handle_call({session_opened, Id, Caps, Pid}, _From, @@ -227,18 +234,33 @@ terminate(_Reason, _State) -> %%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -compute_path(From, To) -> - case epce_ted:compute_path(pcc_address, From, To) of - {ok, Devices} -> - Labels = tl([L || #{mpls_label := L} <- Devices, L =/= undefined]), - logger:debug("Route from ~p to ~p: ~p", [From, To, Labels]), - {ok, Labels}; +ted_index(Id) when is_binary(Id) -> id; +ted_index({_, _, _, _}) -> pcc_address. + +pcc_address(Key) -> + case epce_ted:lookup(ted_index(Key), Key) of {error, Reason} -> - logger:warning("Failed to find a route from ~p to ~p: ~p", - [From, To, Reason]), - {error, route_not_found} + logger:warning("Failed to find a PCC address for router ~p: ~p", + [Key, Reason]), + {error, router_not_found}; + {ok, #{pcc_address := Addr}} -> + {ok, Addr} end. +compute_path(From, To) when is_binary(From), is_binary(To) -> + compute_path_result(From, To, epce_ted:compute_path(id, From, To)); +compute_path({_, _, _, _} = From, {_, _, _, _} = To) -> + compute_path_result(From, To, epce_ted:compute_path(pcc_address, From, To)). + +compute_path_result(From, To, {error, Reason}) -> + logger:warning("Failed to find a route from ~p to ~p: ~p", + [From, To, Reason]), + {error, route_not_found}; +compute_path_result(From, To, {ok, Devices}) -> + Labels = tl([L || #{mpls_label := L} <- Devices, L =/= undefined]), + logger:debug("Route from ~p to ~p: ~p", [From, To, Labels]), + {ok, Labels}. + routereq_from_labels(Source, Destination, Constraints, Labels) -> #{ source => Source, diff --git a/src/te/apps/epce/src/epce_ted.erl b/src/te/apps/epce/src/epce_ted.erl index aaf5a4e9a..879b92920 100644 --- a/src/te/apps/epce/src/epce_ted.erl +++ b/src/te/apps/epce/src/epce_ted.erl @@ -19,6 +19,7 @@ -export([link_updated/2]). -export([link_deleted/1]). -export([compute_path/3]). +-export([lookup/2]). -export([get_graph/0]). @@ -69,6 +70,12 @@ compute_path(Index, From, To) compute_path(Index, _From, _To) -> {error, {invalid_index, Index}}. +lookup(Index, Key) + when Index =:= id; Index =:= pcc_address -> + gen_server:call(?MODULE, {lookup, Index, Key}); +lookup(Index, _Key) -> + {error, {invalid_index, Index}}. + get_graph() -> gen_server:call(?MODULE, get_graph). @@ -106,6 +113,13 @@ handle_call({compute_path, Index, From, To}, _From, #state{graph = G} = State) - {error, Reason} -> {reply, {error, Reason}, State} end; +handle_call({lookup, Index, Key}, _From, #state{graph = G} = State) -> + case as_ids(State, Index, [Key]) of + {ok, [Id]} -> + {reply, do_lookup(G, Id), State}; + {error, Reason} -> + {reply, {error, Reason}, State} + end; handle_call(get_graph, _From, #state{graph = G} = State) -> {reply, G, State}; handle_call(Request, _From, State) -> @@ -193,6 +207,12 @@ do_compute_path(G, FromId, ToId) -> Ids -> {ok, retrieve_devices(G, Ids, [])} end. +do_lookup(G, Id) -> + case digraph:vertex(G, Id) of + {_, Info} -> {ok, Info}; + false -> {error, not_found} + end. + retrieve_devices(_G, [], Acc) -> lists:reverse(Acc); retrieve_devices(G, [Id | Rest], Acc) -> diff --git a/src/te/apps/tfte/src/tfte.app.src b/src/te/apps/tfte/src/tfte.app.src index 06adeb848..64fffcb61 100644 --- a/src/te/apps/tfte/src/tfte.app.src +++ b/src/te/apps/tfte/src/tfte.app.src @@ -7,6 +7,7 @@ [kernel, stdlib, tfpb, + jsx, epce ]}, {env,[]}, diff --git a/src/te/apps/tfte/src/tfte_server.erl b/src/te/apps/tfte/src/tfte_server.erl index 29dddf3d1..ea80848f7 100644 --- a/src/te/apps/tfte/src/tfte_server.erl +++ b/src/te/apps/tfte/src/tfte_server.erl @@ -16,6 +16,8 @@ -export([context_event/1]). -export([topology_ready/1]). -export([topology_event/1]). +-export([request_lsp/1]). +-export([delete_lsp/1]). % Behaviour gen_statem functions -export([init/1]). @@ -28,6 +30,7 @@ %%% Records %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -record(data, { + services = #{} }). @@ -51,6 +54,12 @@ topology_ready(Topology) -> topology_event(Event) -> gen_statem:cast(?MODULE, {topology_event, Event}). +request_lsp(ServiceMap) -> + gen_statem:cast(?MODULE, {request_lsp, ServiceMap}). + +delete_lsp(ServiceId) -> + gen_statem:cast(?MODULE, {delete_lsp, ServiceId}). + %%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -83,6 +92,17 @@ handle_event(cast, {topology_ready, _Topology}, ready, _Data) -> handle_event(cast, {topology_event, _Event}, ready, _Data) -> ?LOG_DEBUG("Teraflow topology event: ~p", [_Event]), keep_state_and_data; +handle_event({call, _From}, {request_lsp, ServiceMap}, ready, Data) -> + #{service_id := ServiceId} = ServiceMap, + ?LOG_DEBUG("Teraflow service ~s requested its LSPs", + [format_service_id(ServiceId)]), + {Result, Data2} = do_request_lsp(Data, ServiceMap), + {keep_state, Data2, [{reply, Result}]}; +handle_event(cast, {delete_lsp, ServiceId}, ready, Data) -> + ?LOG_DEBUG("Teraflow service ~s delete its LSPs", + [format_service_id(ServiceId)]), + {Result, Data2} = do_delete_lsp(Data, ServiceId), + {keep_state, Data2, [{reply, Result}]}; %-- ANY STATE ------------------------------------------------------------------ handle_event(EventType, EventContent, State, Data) -> ?LOG_WARNING(Data, "Unexpected ~w event in state ~w: ~w", @@ -98,3 +118,42 @@ code_change(_OldVsn, OldState, OldData, _Extra) -> %%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +format_service_id(#{context_id := #{context_uuid := #{uuid := ContextName}}, + service_uuid := #{uuid := ServiceUUID}}) -> + iolist_to_binary(io_lib:format("~s:~s", [ContextName, ServiceUUID])). + +do_request_lsp(#data{services = Services} = Data, + #{service_type := 'SERVICETYPE_TE'} = ServiceMap) -> + #{service_config := Config, + service_endpoint_ids := Endpoints, + service_id := ServiceId} = ServiceMap, + #{binding_label := BindingLabel1, symbolic_name := SymbolicName1} + = tfte_util:custom_config(Config, <<"/lsp-fw">>), + #{binding_label := BindingLabel2, symbolic_name := SymbolicName2} + = tfte_util:custom_config(Config, <<"/lsp-bw">>), + [#{device_id := #{device_uuid := #{uuid := Id1}}}, + #{device_id := #{device_uuid := #{uuid := Id2}}}] = Endpoints, + case epce_server:initiate_flow(SymbolicName1, Id1, Id2, BindingLabel1) of + {error, Reason} -> + ?LOG_ERROR("Error while setting up service ~s forward LSP: ~p", + [format_service_id(ServiceId), Reason]), + {'SERVICESTATUS_UNDEFINED', Data}; + {ok, ForwardFlow} -> + case epce_server:initiate_flow(SymbolicName2, Id2, Id1, BindingLabel2) of + {error, Reason} -> + ?LOG_ERROR("Error while setting up service ~s backward LSP: ~p", + [format_service_id(ServiceId), Reason]), + %TODO: Cleanup forward flow ? + {'SERVICESTATUS_UNDEFINED', Data}; + {ok, BackwardFlow} -> + ServiceData = {ServiceMap, ForwardFlow, BackwardFlow}, + Services2 = Services#{ServiceId => ServiceData}, + Data2 = Data#data{services = Services2}, + {'SERVICESTATUS_ACTIVE', Data2} + end + end. + +do_delete_lsp(Data, ServiceId) -> + ?LOG_INFO("LSP DELETION REQUESTED ~p", [ServiceId]), + {'SERVICESTATUS_UNDEFINED', Data}. \ No newline at end of file diff --git a/src/te/apps/tfte/src/tfte_te_service.erl b/src/te/apps/tfte/src/tfte_te_service.erl index 1cadd7aad..2f230fd6b 100644 --- a/src/te/apps/tfte/src/tfte_te_service.erl +++ b/src/te/apps/tfte/src/tfte_te_service.erl @@ -6,6 +6,7 @@ %%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -include_lib("grpcbox/include/grpcbox.hrl"). +-include_lib("kernel/include/logger.hrl"). %%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -18,14 +19,22 @@ %%% BEHAVIOUR te_te_service_bhvr CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%% -request_lsp(_Ctx, _Service) -> - {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, - #{headers => #{}, trailers => #{}}}. +request_lsp(_Ctx, Service) -> + ?LOG_ERROR("Requesting LSP: ~p", [Service]), + try tfte_server:request_lsp(Service) + catch E:R:S -> + ?LOG_ERROR("Error while requesting LSP: ~p:~p ~p", [E, R, S]), + 'SERVICESTATUS_UNDEFINED' + end. -update_lsp(_Ctx, _Service) -> +update_lsp(_Ctx, _ServiceId) -> {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, #{headers => #{}, trailers => #{}}}. -delete_lsp(_Ctx, _Service) -> - {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, - #{headers => #{}, trailers => #{}}}. +delete_lsp(_Ctx, ServiceId) -> + ?LOG_ERROR("Deleting LSP: ~p", [ServiceId]), + try tfte_server:delete_lsp(ServiceId) + catch E:R:S -> + ?LOG_ERROR("Error while deleting LSP: ~p:~p ~p", [E, R, S]), + 'SERVICESTATUS_UNDEFINED' + end. diff --git a/src/te/apps/tfte/src/tfte_topology.erl b/src/te/apps/tfte/src/tfte_topology.erl index d2c6fc0d9..54825c409 100644 --- a/src/te/apps/tfte/src/tfte_topology.erl +++ b/src/te/apps/tfte/src/tfte_topology.erl @@ -281,37 +281,31 @@ device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_ENABLED'}) enabled. device_mpls_label(Device) -> - case device_config_value(<<"mpls_label">>, Device) of + case device_config_value(<<"/te_data/mpls_label">>, Device) of undefined -> undefined; - LabelBin -> - try binary_to_integer(LabelBin) + LabelJson -> + try jsx:decode(LabelJson) catch error:badarg -> undefined end end. device_pcc_address(Device) -> - case device_config_value(<<"pcc_address">>, Device) of + case device_config_value(<<"/te_data/pcc_address">>, Device) of undefined -> undefined; - AddressBin -> - case inet_parse:address(binary_to_list(AddressBin)) of - {ok, Address} -> Address; - {error,einval} -> undefined + AddressJson -> + try jsx:decode(AddressJson) of + AddressBin -> + case inet_parse:address(binary_to_list(AddressBin)) of + {ok, Address} -> Address; + {error,einval} -> undefined + end + catch + error:badarg -> undefined end end. -device_config_value(Key, #{device_config := #{config_rules := Rules}}) -> - device_config_value(Key, Rules); -device_config_value(_Key, []) -> - undefined; -device_config_value(Key, [#{action := 'CONFIGACTION_SET', - config_rule := {custom, Rule}} | Rest]) -> - case Rule of - #{resource_key := Key, resource_value := Value} -> Value; - _ -> device_config_value(Key, Rest) - end; -device_config_value(Key, [_Rule | Rest]) -> - device_config_value(Key, Rest). - +device_config_value(Key, #{device_config := Config}) -> + tfte_util:custom_config(Config, Key). device_endpoints(Device) -> device_endpoints(Device, []). diff --git a/src/te/apps/tfte/src/tfte_util.erl b/src/te/apps/tfte/src/tfte_util.erl new file mode 100644 index 000000000..7b766b5ba --- /dev/null +++ b/src/te/apps/tfte/src/tfte_util.erl @@ -0,0 +1,27 @@ +-module(tfte_util). + +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include_lib("kernel/include/logger.hrl"). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API functions +-export([custom_config/2]). + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +custom_config(#{config_rules := Rules}, Key) -> + custom_config(Rules, Key); +custom_config([], _Key) -> + undefined; +custom_config([#{action := 'CONFIGACTION_SET', + config_rule := {custom, Rule}} | Rest], Key) -> + case Rule of + #{resource_key := Key, resource_value := Value} -> jsx:decode(Value); + _ -> custom_config(Rest, Key) + end; +custom_config([_Rule | Rest], Key) -> + custom_config(Rest, Key). diff --git a/src/te/rebar.config b/src/te/rebar.config index 776a80249..06bff13c7 100644 --- a/src/te/rebar.config +++ b/src/te/rebar.config @@ -2,6 +2,7 @@ {deps, [ grpcbox, + jsx, {pcep_server, {git, "https://github.com/stritzinger/pcep_server.git", {branch, "master"}}} ]}. @@ -17,6 +18,7 @@ runtime_tools, epce, grpcbox, + jsx, tfpb, tfte ]}, diff --git a/src/te/rebar.lock b/src/te/rebar.lock index 446884e6b..c435b0456 100644 --- a/src/te/rebar.lock +++ b/src/te/rebar.lock @@ -9,6 +9,7 @@ {<<"gproc">>,{pkg,<<"gproc">>,<<"0.8.0">>},1}, {<<"grpcbox">>,{pkg,<<"grpcbox">>,<<"0.15.0">>},0}, {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}, + {<<"jsx">>,{pkg,<<"jsx">>,<<"3.1.0">>},0}, {<<"pcep_codec">>, {git,"https://github.com/stritzinger/pcep_codec.git", {ref,"ca5eb0822d9971ec4bcfb427a49b2e516081a126"}}, @@ -26,6 +27,7 @@ {<<"gproc">>, <<"CEA02C578589C61E5341FCE149EA36CCEF236CC2ECAC8691FBA408E7EA77EC2F">>}, {<<"grpcbox">>, <<"97C7126296A091602D372EBF5860A04F7BC795B45B33A984CAD2B8E362774FD8">>}, {<<"hpack">>, <<"17670F83FF984AE6CD74B1C456EDDE906D27FF013740EE4D9EFAA4F1BF999633">>}, + {<<"jsx">>, <<"D12516BAA0BB23A59BB35DCCAF02A1BD08243FCBB9EFE24F2D9D056CCFF71268">>}, {<<"ranch">>, <<"FBF3D79661C071543256F9051CAF19D65DAA6DF1CF6824D8F37A49B19A66F703">>}]}, {pkg_hash_ext,[ {<<"acceptor_pool">>, <<"0CBCD83FDC8B9AD2EEE2067EF8B91A14858A5883CB7CD800E6FCD5803E158788">>}, @@ -34,5 +36,6 @@ {<<"gproc">>, <<"580ADAFA56463B75263EF5A5DF4C86AF321F68694E7786CB057FD805D1E2A7DE">>}, {<<"grpcbox">>, <<"161ABE9E17E7D1982EFA6488ADEAA13C3E847A07984A6E6B224E553368918647">>}, {<<"hpack">>, <<"06F580167C4B8B8A6429040DF36CC93BBA6D571FAEAEC1B28816523379CBB23A">>}, + {<<"jsx">>, <<"0C5CC8FDC11B53CC25CF65AC6705AD39E54ECC56D1C22E4ADB8F5A53FB9427F3">>}, {<<"ranch">>, <<"C20A4840C7D6623C19812D3A7C828B2F1BD153EF0F124CB69C54FE51D8A42AE0">>}]} ]. -- GitLab From d7e2040147988a7fc06248331cb83bf7e517525e Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 25 Jan 2023 09:01:32 -0800 Subject: [PATCH 14/63] Fix some error handling --- src/service/service/__main__.py | 2 ++ src/te/apps/epce/src/epce_server.erl | 2 +- src/te/apps/tfte/src/tfte_server.erl | 8 ++++---- src/te/apps/tfte/src/tfte_te_service.erl | 24 ++++++++++++++++------- src/te/apps/tfte/src/tfte_topology.erl | 25 +++++++++--------------- 5 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/service/service/__main__.py b/src/service/service/__main__.py index f2b6e38d6..18f91bf1a 100644 --- a/src/service/service/__main__.py +++ b/src/service/service/__main__.py @@ -43,6 +43,8 @@ def main(): get_env_var_name(ServiceNameEnum.DEVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC), get_env_var_name(ServiceNameEnum.PATHCOMP, ENVVAR_SUFIX_SERVICE_HOST ), get_env_var_name(ServiceNameEnum.PATHCOMP, ENVVAR_SUFIX_SERVICE_PORT_GRPC), + get_env_var_name(ServiceNameEnum.TE, ENVVAR_SUFIX_SERVICE_HOST ), + get_env_var_name(ServiceNameEnum.TE, ENVVAR_SUFIX_SERVICE_PORT_GRPC), ]) signal.signal(signal.SIGINT, signal_handler) diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl index 8996d3b35..c7b404fca 100644 --- a/src/te/apps/epce/src/epce_server.erl +++ b/src/te/apps/epce/src/epce_server.erl @@ -36,7 +36,7 @@ %%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --define(LARGE_TIMEOUT, 20000). +-define(LARGE_TIMEOUT, infinity). %%% RECORDS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/te/apps/tfte/src/tfte_server.erl b/src/te/apps/tfte/src/tfte_server.erl index ea80848f7..a6c8c4b71 100644 --- a/src/te/apps/tfte/src/tfte_server.erl +++ b/src/te/apps/tfte/src/tfte_server.erl @@ -138,22 +138,22 @@ do_request_lsp(#data{services = Services} = Data, {error, Reason} -> ?LOG_ERROR("Error while setting up service ~s forward LSP: ~p", [format_service_id(ServiceId), Reason]), - {'SERVICESTATUS_UNDEFINED', Data}; + {{error, Reason}, Data}; {ok, ForwardFlow} -> case epce_server:initiate_flow(SymbolicName2, Id2, Id1, BindingLabel2) of {error, Reason} -> ?LOG_ERROR("Error while setting up service ~s backward LSP: ~p", [format_service_id(ServiceId), Reason]), %TODO: Cleanup forward flow ? - {'SERVICESTATUS_UNDEFINED', Data}; + {{error, Reason}, Data}; {ok, BackwardFlow} -> ServiceData = {ServiceMap, ForwardFlow, BackwardFlow}, Services2 = Services#{ServiceId => ServiceData}, Data2 = Data#data{services = Services2}, - {'SERVICESTATUS_ACTIVE', Data2} + {{ok, 'SERVICESTATUS_ACTIVE'}, Data2} end end. do_delete_lsp(Data, ServiceId) -> ?LOG_INFO("LSP DELETION REQUESTED ~p", [ServiceId]), - {'SERVICESTATUS_UNDEFINED', Data}. \ No newline at end of file + {{error, not_implemented}, Data}. \ No newline at end of file diff --git a/src/te/apps/tfte/src/tfte_te_service.erl b/src/te/apps/tfte/src/tfte_te_service.erl index 2f230fd6b..f0e7116d2 100644 --- a/src/te/apps/tfte/src/tfte_te_service.erl +++ b/src/te/apps/tfte/src/tfte_te_service.erl @@ -19,22 +19,32 @@ %%% BEHAVIOUR te_te_service_bhvr CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%% -request_lsp(_Ctx, Service) -> - ?LOG_ERROR("Requesting LSP: ~p", [Service]), - try tfte_server:request_lsp(Service) +request_lsp(Ctx, Service) -> + ?LOG_INFO("Requesting LSP: ~p", [Service]), + try tfte_server:request_lsp(Service) of + {ok, Status} -> + {ok, Status, Ctx}; + {error, Reason} -> + ?LOG_INFO("Error while requesting LSP: ~p", [Reason]), + {ok, 'SERVICESTATUS_UNDEFINED', Ctx} catch E:R:S -> ?LOG_ERROR("Error while requesting LSP: ~p:~p ~p", [E, R, S]), - 'SERVICESTATUS_UNDEFINED' + {ok, 'SERVICESTATUS_UNDEFINED', Ctx} end. update_lsp(_Ctx, _ServiceId) -> {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, #{headers => #{}, trailers => #{}}}. -delete_lsp(_Ctx, ServiceId) -> +delete_lsp(Ctx, ServiceId) -> ?LOG_ERROR("Deleting LSP: ~p", [ServiceId]), - try tfte_server:delete_lsp(ServiceId) + try tfte_server:delete_lsp(ServiceId) of + {ok, Status} -> + {ok, Status, Ctx}; + {error, Reason} -> + ?LOG_INFO("Error while deleting LSP: ~p", [Reason]), + {ok, 'SERVICESTATUS_UNDEFINED', Ctx} catch E:R:S -> ?LOG_ERROR("Error while deleting LSP: ~p:~p ~p", [E, R, S]), - 'SERVICESTATUS_UNDEFINED' + {ok, 'SERVICESTATUS_UNDEFINED', Ctx} end. diff --git a/src/te/apps/tfte/src/tfte_topology.erl b/src/te/apps/tfte/src/tfte_topology.erl index 54825c409..886fd926d 100644 --- a/src/te/apps/tfte/src/tfte_topology.erl +++ b/src/te/apps/tfte/src/tfte_topology.erl @@ -281,27 +281,20 @@ device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_ENABLED'}) enabled. device_mpls_label(Device) -> - case device_config_value(<<"/te_data/mpls_label">>, Device) of - undefined -> undefined; - LabelJson -> - try jsx:decode(LabelJson) - catch error:badarg -> undefined - end + try device_config_value(<<"/te_data/mpls_label">>, Device) + catch error:badarg -> undefined end. device_pcc_address(Device) -> - case device_config_value(<<"/te_data/pcc_address">>, Device) of + try device_config_value(<<"/te_data/pcc_address">>, Device) of undefined -> undefined; - AddressJson -> - try jsx:decode(AddressJson) of - AddressBin -> - case inet_parse:address(binary_to_list(AddressBin)) of - {ok, Address} -> Address; - {error,einval} -> undefined - end - catch - error:badarg -> undefined + AddressBin -> + case inet_parse:address(binary_to_list(AddressBin)) of + {ok, Address} -> Address; + {error,einval} -> undefined end + catch + error:badarg -> undefined end. device_config_value(Key, #{device_config := Config}) -> -- GitLab From 0bc00361ed212c7535bb13653dd5591e4979e2b0 Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 8 Feb 2023 11:00:30 -0800 Subject: [PATCH 15/63] Fix TE service manifest --- manifests/teservice.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifests/teservice.yaml b/manifests/teservice.yaml index 3f7bd1f04..701a5f52f 100644 --- a/manifests/teservice.yaml +++ b/manifests/teservice.yaml @@ -29,7 +29,7 @@ spec: shareProcessNamespace: true containers: - name: server - image: registry.gitlab.com/teraflow-h2020/controller/te:latest + image: labs.etsi.org:5050/tfs/controller/te:latest imagePullPolicy: Always ports: - containerPort: 11010 -- GitLab From 84aa2f542e6402074f2e49c06278725ec193fcc2 Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 8 Feb 2023 11:04:24 -0800 Subject: [PATCH 16/63] Add the tutorials locally to the TE service --- {tutorial => src/te/tutorial}/1-6-setup-erlang-environmnet.md | 0 {tutorial => src/te/tutorial}/2-6-te-demo.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {tutorial => src/te/tutorial}/1-6-setup-erlang-environmnet.md (100%) rename {tutorial => src/te/tutorial}/2-6-te-demo.md (100%) diff --git a/tutorial/1-6-setup-erlang-environmnet.md b/src/te/tutorial/1-6-setup-erlang-environmnet.md similarity index 100% rename from tutorial/1-6-setup-erlang-environmnet.md rename to src/te/tutorial/1-6-setup-erlang-environmnet.md diff --git a/tutorial/2-6-te-demo.md b/src/te/tutorial/2-6-te-demo.md similarity index 100% rename from tutorial/2-6-te-demo.md rename to src/te/tutorial/2-6-te-demo.md -- GitLab From 9283fcb07e3fef7afbc34b71c2f2e56306f15f8b Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 8 Feb 2023 11:58:16 -0800 Subject: [PATCH 17/63] Update TE service configuration format --- src/te/apps/tfte/src/tfte_topology.erl | 21 +++++++++++++-------- src/te/config/sys.config.src | 2 +- src/te/tests/topology-descriptors.json | 20 +++++++------------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/te/apps/tfte/src/tfte_topology.erl b/src/te/apps/tfte/src/tfte_topology.erl index 886fd926d..d9505e914 100644 --- a/src/te/apps/tfte/src/tfte_topology.erl +++ b/src/te/apps/tfte/src/tfte_topology.erl @@ -281,18 +281,23 @@ device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_ENABLED'}) enabled. device_mpls_label(Device) -> - try device_config_value(<<"/te_data/mpls_label">>, Device) + try device_config_value(<<"/te_data">>, Device) of + Map when is_map(Map) -> maps:get(<<"mpls_label">>, Map, undefined); + _ -> undefined catch error:badarg -> undefined end. device_pcc_address(Device) -> - try device_config_value(<<"/te_data/pcc_address">>, Device) of - undefined -> undefined; - AddressBin -> - case inet_parse:address(binary_to_list(AddressBin)) of - {ok, Address} -> Address; - {error,einval} -> undefined - end + try device_config_value(<<"/te_data">>, Device) of + Map when is_map(Map) -> + case maps:get(<<"pcc_address">>, Map, undefined) of + AddressBin -> + case inet_parse:address(binary_to_list(AddressBin)) of + {ok, Address} -> Address; + {error,einval} -> undefined + end + end; + _ -> undefined catch error:badarg -> undefined end. diff --git a/src/te/config/sys.config.src b/src/te/config/sys.config.src index f3c185641..b1640c109 100644 --- a/src/te/config/sys.config.src +++ b/src/te/config/sys.config.src @@ -2,7 +2,7 @@ {tfte, [ {context, <<"admin">>}, - {topology, <<"tetestbed">>}, + {topology, <<"admin">>}, {services, [ {te, [ {http, {env, "TESERVICE_SERVICE_HOST"}, {env, "TESERVICE_SERVICE_PORT_GRPC"}, []} diff --git a/src/te/tests/topology-descriptors.json b/src/te/tests/topology-descriptors.json index 82f37ce29..a34d8ce09 100644 --- a/src/te/tests/topology-descriptors.json +++ b/src/te/tests/topology-descriptors.json @@ -8,7 +8,7 @@ ], "topologies": [ { - "topology_id": {"topology_uuid": {"uuid": "tetestbed"}, "context_id": {"context_uuid": {"uuid": "admin"}}}, + "topology_id": {"topology_uuid": {"uuid": "admin"}, "context_id": {"context_uuid": {"uuid": "admin"}}}, "device_ids": [ {"device_uuid": {"uuid": "SW1"}}, {"device_uuid": {"uuid": "RT1"}}, @@ -54,8 +54,7 @@ {"sample_types": [], "type": "copper", "uuid": "eth-src"}, {"sample_types": [], "type": "copper", "uuid": "eth-sw1"} ]}}}, - {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16010"}}, - {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "1.1.1.1"}} + {"action": 1, "custom": {"resource_key": "/te_data", "resource_value": {"mpls_label": 16010, "pcc_address": "1.1.1.1"}}} ]} }, { @@ -69,8 +68,7 @@ {"sample_types": [], "type": "copper", "uuid": "eth-rt4-1"}, {"sample_types": [], "type": "copper", "uuid": "eth-rt4-2"} ]}}}, - {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16020"}}, - {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "2.2.2.2"}} + {"action": 1, "custom": {"resource_key": "/te_data", "resource_value": {"mpls_label": 16020, "pcc_address": "2.2.2.2"}}} ]} }, { @@ -84,8 +82,7 @@ {"sample_types": [], "type": "copper", "uuid": "eth-rt5-1"}, {"sample_types": [], "type": "copper", "uuid": "eth-rt5-2"} ]}}}, - {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16030"}}, - {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "3.3.3.3"}} + {"action": 1, "custom": {"resource_key": "/te_data", "resource_value": {"mpls_label": 16030, "pcc_address": "3.3.3.3"}}} ]} }, { @@ -100,8 +97,7 @@ {"sample_types": [], "type": "copper", "uuid": "eth-rt5"}, {"sample_types": [], "type": "copper", "uuid": "eth-rt6"} ]}}}, - {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16040"}}, - {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "4.4.4.4"}} + {"action": 1, "custom": {"resource_key": "/te_data", "resource_value": {"mpls_label": 16040, "pcc_address": "4.4.4.4"}}} ]} }, { @@ -116,8 +112,7 @@ {"sample_types": [], "type": "copper", "uuid": "eth-rt4"}, {"sample_types": [], "type": "copper", "uuid": "eth-rt6"} ]}}}, - {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16050"}}, - {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "5.5.5.5"}} + {"action": 1, "custom": {"resource_key": "/te_data", "resource_value": {"mpls_label": 16050, "pcc_address": "5.5.5.5"}}} ]} }, { @@ -131,8 +126,7 @@ {"sample_types": [], "type": "copper", "uuid": "eth-rt5"}, {"sample_types": [], "type": "copper", "uuid": "eth-dst"} ]}}}, - {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16060"}}, - {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "6.6.6.6"}} + {"action": 1, "custom": {"resource_key": "/te_data", "resource_value": {"mpls_label": 16060, "pcc_address": "6.6.6.6"}}} ]} } ], -- GitLab From f6e975daab7d64cdf4728a2c39e1de9b5b31911d Mon Sep 17 00:00:00 2001 From: merle Date: Mon, 10 Jul 2023 01:48:24 -0700 Subject: [PATCH 18/63] Update documentation and fix identation --- manifests/teservice.yaml | 6 ++++-- src/te/apps/epce/src/epce_sup.erl | 8 ++++---- src/te/tutorial/2-6-te-demo.md | 1 - 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/manifests/teservice.yaml b/manifests/teservice.yaml index 701a5f52f..d27bd42c7 100644 --- a/manifests/teservice.yaml +++ b/manifests/teservice.yaml @@ -22,6 +22,8 @@ spec: app: teservice template: metadata: + annotations: + config.linkerd.io/skip-inbound-ports: "4189" labels: app: teservice spec: @@ -50,8 +52,8 @@ spec: exec: command: ["/tfte/bin/tfte", "status"] livenessProbe: - grpc: - port: 11010 + exec: + command: ["/tfte/bin/tfte", "status"] resources: requests: cpu: 250m diff --git a/src/te/apps/epce/src/epce_sup.erl b/src/te/apps/epce/src/epce_sup.erl index f04e9697e..9341201c0 100644 --- a/src/te/apps/epce/src/epce_sup.erl +++ b/src/te/apps/epce/src/epce_sup.erl @@ -23,10 +23,10 @@ start_link() -> init([]) -> SupFlags = #{ - strategy => one_for_all, - intensity => 0, - period => 1 - }, + strategy => one_for_all, + intensity => 0, + period => 1 + }, TEDSpec = #{ id => ?TED_WORKER, start => {?TED_WORKER, start_link, []}, diff --git a/src/te/tutorial/2-6-te-demo.md b/src/te/tutorial/2-6-te-demo.md index 778d62e06..9b345203b 100644 --- a/src/te/tutorial/2-6-te-demo.md +++ b/src/te/tutorial/2-6-te-demo.md @@ -79,7 +79,6 @@ ### Run the Test-Bed First load the [teraflow configuration file](~/tfs-ctrl/src/te/tests/topology-descriptors.json) using the webui. -The first time the configuration is loaded may partialy fail because it tries to load the topology before the devices and links, but a second load should work. In first console: $ cd ~/testbed -- GitLab From 3bb700f6f66cd6d8d0fa083eb46d57e0f1ab07cb Mon Sep 17 00:00:00 2001 From: merle Date: Mon, 10 Jul 2023 03:24:38 -0700 Subject: [PATCH 19/63] Change port and documentation --- manifests/teservice.yaml | 6 +++--- src/te/Dockerfile | 2 +- src/te/README.md | 4 ++-- src/te/config/dev.config.template | 4 ++-- src/te/tutorial/2-6-te-demo.md | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifests/teservice.yaml b/manifests/teservice.yaml index d27bd42c7..30002bc52 100644 --- a/manifests/teservice.yaml +++ b/manifests/teservice.yaml @@ -34,7 +34,7 @@ spec: image: labs.etsi.org:5050/tfs/controller/te:latest imagePullPolicy: Always ports: - - containerPort: 11010 + - containerPort: 10030 env: - name: ERLANG_LOGGER_LEVEL value: "debug" @@ -73,8 +73,8 @@ spec: ports: - name: grpc protocol: TCP - port: 11010 - targetPort: 11010 + port: 10030 + targetPort: 10030 - name: pcep protocol: TCP port: 4189 diff --git a/src/te/Dockerfile b/src/te/Dockerfile index 98194f254..446936ca5 100644 --- a/src/te/Dockerfile +++ b/src/te/Dockerfile @@ -42,7 +42,7 @@ RUN apk add --no-cache libgcc libstdc++ && \ COPY --from=0 /var/teraflow/src/te/_build/prod/rel/tfte /tfte # Expose relevant ports -EXPOSE 11010 +EXPOSE 10030 EXPOSE 4189 ARG ERLANG_LOGGER_LEVEL_DEFAULT=debug diff --git a/src/te/README.md b/src/te/README.md index a316571bf..d6f1952ac 100644 --- a/src/te/README.md +++ b/src/te/README.md @@ -64,9 +64,9 @@ The docker image must be built from the root of the Teraflow project: ### Open a Console - $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -- /tfte/bin/tfte remote_console + $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -c server -- /tfte/bin/tfte remote_console ## Show Logs - $ kubectl --namespace tfs logs $(kubectl --namespace tfs get pods --selector=app=teservice -o name) + $ kubectl --namespace tfs logs $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -c server diff --git a/src/te/config/dev.config.template b/src/te/config/dev.config.template index 5897c4e46..6ca3af506 100644 --- a/src/te/config/dev.config.template +++ b/src/te/config/dev.config.template @@ -1,7 +1,7 @@ [ {tfte, [ {services, [ - {te, [{http, "localhost", 11010, []}], #{}} + {te, [{http, "localhost", 10030, []}], #{}} ]} ]}, @@ -30,7 +30,7 @@ %cacertfile => "" }, listen_opts => #{ - port => 11010, + port => 10030, ip => {0,0,0,0} }, pool_opts => #{ diff --git a/src/te/tutorial/2-6-te-demo.md b/src/te/tutorial/2-6-te-demo.md index 9b345203b..d514adf07 100644 --- a/src/te/tutorial/2-6-te-demo.md +++ b/src/te/tutorial/2-6-te-demo.md @@ -94,7 +94,7 @@ Then in second console: We will setup two unidirectional flow between router 1 and 6. We will use the binding label 1111 for the flow from router 1 to router 6, and the binding label 6666 for the flow from router 6 to router 1. - $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -- /tfte/bin/tfte remote_console + $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -c server -- /tfte/bin/tfte remote_console 1> {ok, Flow1to6} = epce_server:initiate_flow(<<"foo">>, {1, 1, 1, 1}, {6, 6, 6, 6}, 1111). 2> {ok, Flow6to1} = epce_server:initiate_flow(<<"bar">>, {6, 6, 6, 6}, {1, 1, 1, 1}, 6666). -- GitLab From 9ae02c230f5b750be0fbfd17d43fdd3dc8bc916b Mon Sep 17 00:00:00 2001 From: merle Date: Mon, 10 Jul 2023 03:28:49 -0700 Subject: [PATCH 20/63] Change port --- src/te/config/sys.config.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/te/config/sys.config.src b/src/te/config/sys.config.src index b1640c109..df373de74 100644 --- a/src/te/config/sys.config.src +++ b/src/te/config/sys.config.src @@ -53,7 +53,7 @@ %cacertfile => "" }, listen_opts => #{ - port => 11010, + port => 10030, ip => {0,0,0,0} }, pool_opts => #{ -- GitLab From 4290e9822576deb75618e44ad45f3ec6aeaa5575 Mon Sep 17 00:00:00 2001 From: merle Date: Tue, 11 Jul 2023 05:54:41 -0700 Subject: [PATCH 21/63] Use Empty defined in the context to prevent duplication --- proto/l3_centralizedattackdetector.proto | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proto/l3_centralizedattackdetector.proto b/proto/l3_centralizedattackdetector.proto index ed99435aa..5fbed424c 100644 --- a/proto/l3_centralizedattackdetector.proto +++ b/proto/l3_centralizedattackdetector.proto @@ -18,13 +18,13 @@ import "context.proto"; service L3Centralizedattackdetector { // Analyze single input to the ML model in the CAD component - rpc AnalyzeConnectionStatistics (L3CentralizedattackdetectorMetrics) returns (Empty) {} + rpc AnalyzeConnectionStatistics (L3CentralizedattackdetectorMetrics) returns (context.Empty) {} // Analyze a batch of inputs to the ML model in the CAD component - rpc AnalyzeBatchConnectionStatistics (L3CentralizedattackdetectorBatchInput) returns (Empty) {} + rpc AnalyzeBatchConnectionStatistics (L3CentralizedattackdetectorBatchInput) returns (context.Empty) {} // Get the list of features used by the ML model in the CAD component - rpc GetFeaturesIds (Empty) returns (AutoFeatures) {} + rpc GetFeaturesIds (context.Empty) returns (AutoFeatures) {} } message Feature { @@ -63,6 +63,6 @@ message L3CentralizedattackdetectorBatchInput { repeated L3CentralizedattackdetectorMetrics metrics = 1; } -message Empty { - string message = 1; -} +// message Empty { +// string message = 1; +// } -- GitLab From 2a7eb5455b51f017fbb2acc719f8f094595937a0 Mon Sep 17 00:00:00 2001 From: merle Date: Tue, 11 Jul 2023 05:59:01 -0700 Subject: [PATCH 22/63] Fix for topology API changes, add test script and update demo documentation. --- src/te/apps/tfte/src/tfte_context.erl | 11 ++- src/te/apps/tfte/src/tfte_server.erl | 37 ++++++--- src/te/apps/tfte/src/tfte_te_service.erl | 10 +-- src/te/apps/tfte/src/tfte_topology.erl | 101 ++++++++++++----------- src/te/apps/tfte/src/tfte_util.erl | 2 +- src/te/tests/test_te_service.py | 94 +++++++++++++++++++++ src/te/tutorial/2-6-te-demo.md | 23 ++++++ 7 files changed, 209 insertions(+), 69 deletions(-) create mode 100644 src/te/tests/test_te_service.py diff --git a/src/te/apps/tfte/src/tfte_context.erl b/src/te/apps/tfte/src/tfte_context.erl index ee0fafc07..1acb1befe 100644 --- a/src/te/apps/tfte/src/tfte_context.erl +++ b/src/te/apps/tfte/src/tfte_context.erl @@ -12,6 +12,7 @@ % API functions -export([start_link/0]). +-export([is_ready/0]). % Behaviour gen_statem functions -export([init/1]). @@ -39,7 +40,13 @@ %%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start_link() -> - gen_statem:start_link(?MODULE, [], []). + gen_statem:start_link({local, ?MODULE}, ?MODULE, [], []). + +is_ready() -> + case whereis(?MODULE) of + undefined -> false; + _ -> gen_statem:call(?MODULE, is_ready) + end. %%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -129,6 +136,8 @@ handle_event(info, {'DOWN', Ref, process, Pid, Reason}, ready, ?LOG_ERROR("Context subscription error: ~p", [Info]), {next_state, subscribe, Data2}; %-- ANY STATE ------------------------------------------------------------------ +handle_event({call, _From}, is_ready, State, _Data) -> + {keep_state_and_data, [{reply, State =:= ready}]}; handle_event(info, Msg, StateName, _Data) -> ?LOG_WARNING("Unexpected context message in state ~w: ~p", [StateName, Msg]), keep_state_and_data. diff --git a/src/te/apps/tfte/src/tfte_server.erl b/src/te/apps/tfte/src/tfte_server.erl index a6c8c4b71..85441bcb1 100644 --- a/src/te/apps/tfte/src/tfte_server.erl +++ b/src/te/apps/tfte/src/tfte_server.erl @@ -40,7 +40,7 @@ %%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start_link() -> - gen_statem:start_link(?MODULE, [], []). + gen_statem:start_link({local, ?MODULE}, ?MODULE, [], []). context_ready(Context) -> gen_statem:cast(?MODULE, {context_ready, Context}). @@ -55,24 +55,27 @@ topology_event(Event) -> gen_statem:cast(?MODULE, {topology_event, Event}). request_lsp(ServiceMap) -> - gen_statem:cast(?MODULE, {request_lsp, ServiceMap}). + gen_statem:call(?MODULE, {request_lsp, ServiceMap}). delete_lsp(ServiceId) -> - gen_statem:cast(?MODULE, {delete_lsp, ServiceId}). + gen_statem:call(?MODULE, {delete_lsp, ServiceId}). %%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init([]) -> ?LOG_INFO("Starting server...", []), - {ok, wait_context, #data{}}. + case tfte_context:is_ready() of + false -> {ok, wait_context, #data{}}; + true -> {ok, ready, #data{}} + end. callback_mode() -> [handle_event_function, state_enter]. %-- WAIT_CONTEXT STATE --------------------------------------------------------- handle_event(enter, _, wait_context, _Data) -> keep_state_and_data; -handle_event(cast, {context_ready, _Context}, wait_contex, Data) -> +handle_event(cast, {context_ready, _Context}, wait_context, Data) -> ?LOG_DEBUG("Teraflow context initialized: ~p", [_Context]), tfte_topology:context_updated(), {next_state, ready, Data}; @@ -92,20 +95,20 @@ handle_event(cast, {topology_ready, _Topology}, ready, _Data) -> handle_event(cast, {topology_event, _Event}, ready, _Data) -> ?LOG_DEBUG("Teraflow topology event: ~p", [_Event]), keep_state_and_data; -handle_event({call, _From}, {request_lsp, ServiceMap}, ready, Data) -> +handle_event({call, From}, {request_lsp, ServiceMap}, ready, Data) -> #{service_id := ServiceId} = ServiceMap, ?LOG_DEBUG("Teraflow service ~s requested its LSPs", [format_service_id(ServiceId)]), {Result, Data2} = do_request_lsp(Data, ServiceMap), - {keep_state, Data2, [{reply, Result}]}; -handle_event(cast, {delete_lsp, ServiceId}, ready, Data) -> + {keep_state, Data2, [{reply, From, Result}]}; +handle_event({call, From}, {delete_lsp, ServiceId}, ready, Data) -> ?LOG_DEBUG("Teraflow service ~s delete its LSPs", [format_service_id(ServiceId)]), {Result, Data2} = do_delete_lsp(Data, ServiceId), - {keep_state, Data2, [{reply, Result}]}; + {keep_state, Data2, [{reply, From, Result}]}; %-- ANY STATE ------------------------------------------------------------------ -handle_event(EventType, EventContent, State, Data) -> - ?LOG_WARNING(Data, "Unexpected ~w event in state ~w: ~w", +handle_event(EventType, EventContent, State, _Data) -> + ?LOG_WARNING("Unexpected tfte_server ~w event in state ~w: ~w", [EventType, State, EventContent]), keep_state_and_data. @@ -125,12 +128,14 @@ format_service_id(#{context_id := #{context_uuid := #{uuid := ContextName}}, do_request_lsp(#data{services = Services} = Data, #{service_type := 'SERVICETYPE_TE'} = ServiceMap) -> + try + #{service_config := Config, service_endpoint_ids := Endpoints, service_id := ServiceId} = ServiceMap, - #{binding_label := BindingLabel1, symbolic_name := SymbolicName1} + #{<<"binding_label">> := BindingLabel1, <<"symbolic_name">> := SymbolicName1} = tfte_util:custom_config(Config, <<"/lsp-fw">>), - #{binding_label := BindingLabel2, symbolic_name := SymbolicName2} + #{<<"binding_label">> := BindingLabel2, <<"symbolic_name">> := SymbolicName2} = tfte_util:custom_config(Config, <<"/lsp-bw">>), [#{device_id := #{device_uuid := #{uuid := Id1}}}, #{device_id := #{device_uuid := #{uuid := Id2}}}] = Endpoints, @@ -152,6 +157,12 @@ do_request_lsp(#data{services = Services} = Data, Data2 = Data#data{services = Services2}, {{ok, 'SERVICESTATUS_ACTIVE'}, Data2} end + end + + catch T:E:S -> + ?LOG_ERROR("Error while requesintg LSP: ~p:~p", [T, E]), + ?LOG_ERROR("Stacktrace: ~p", [S]), + erlang:raise(T, E, S) end. do_delete_lsp(Data, ServiceId) -> diff --git a/src/te/apps/tfte/src/tfte_te_service.erl b/src/te/apps/tfte/src/tfte_te_service.erl index f0e7116d2..29aea875a 100644 --- a/src/te/apps/tfte/src/tfte_te_service.erl +++ b/src/te/apps/tfte/src/tfte_te_service.erl @@ -23,13 +23,13 @@ request_lsp(Ctx, Service) -> ?LOG_INFO("Requesting LSP: ~p", [Service]), try tfte_server:request_lsp(Service) of {ok, Status} -> - {ok, Status, Ctx}; + {ok, #{service_status => Status}, Ctx}; {error, Reason} -> ?LOG_INFO("Error while requesting LSP: ~p", [Reason]), - {ok, 'SERVICESTATUS_UNDEFINED', Ctx} + {ok, #{service_status => 'SERVICESTATUS_UNDEFINED'}, Ctx} catch E:R:S -> ?LOG_ERROR("Error while requesting LSP: ~p:~p ~p", [E, R, S]), - {ok, 'SERVICESTATUS_UNDEFINED', Ctx} + {ok, #{service_status => 'SERVICESTATUS_UNDEFINED'}, Ctx} end. update_lsp(_Ctx, _ServiceId) -> @@ -43,8 +43,8 @@ delete_lsp(Ctx, ServiceId) -> {ok, Status, Ctx}; {error, Reason} -> ?LOG_INFO("Error while deleting LSP: ~p", [Reason]), - {ok, 'SERVICESTATUS_UNDEFINED', Ctx} + {ok, #{service_status => 'SERVICESTATUS_UNDEFINED'}, Ctx} catch E:R:S -> ?LOG_ERROR("Error while deleting LSP: ~p:~p ~p", [E, R, S]), - {ok, 'SERVICESTATUS_UNDEFINED', Ctx} + {ok, #{service_status => 'SERVICESTATUS_UNDEFINED'}, Ctx} end. diff --git a/src/te/apps/tfte/src/tfte_topology.erl b/src/te/apps/tfte/src/tfte_topology.erl index d9505e914..22fc50932 100644 --- a/src/te/apps/tfte/src/tfte_topology.erl +++ b/src/te/apps/tfte/src/tfte_topology.erl @@ -29,7 +29,8 @@ sub :: term() | undefined, obj :: map() | undefined, devices = #{} :: map(), - links = #{} :: map() + links = #{} :: map(), + names = #{} :: map() }). @@ -42,7 +43,7 @@ %%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start_link() -> - gen_statem:start_link(?MODULE, [], []). + gen_statem:start_link({local, ?MODULE}, ?MODULE, [], []). context_updated() -> gen_statem:cast(?MODULE, context_updated). @@ -69,9 +70,15 @@ handle_event(state_timeout, do_retrieve, retrieve, #data{uuid = UUID} = Data) -> case get_object(UUID) of error -> {keep_state_and_data, [{state_timeout, ?RETRIEVE_RETRY_TIMEOUT, do_retrieve}]}; - {ok, Topology} -> - ?LOG_DEBUG("Got topology: ~p", [Topology]), - {next_state, subscribe, Data#data{obj = Topology}} + {ok, #{device_ids := Devices, link_ids := Links } = Topology} -> + case {length(Devices), length(Links)} of + {D, L} when D =:= 0; L =:= 0 -> + ?LOG_WARNING("Got topology, but there is missing devices or links", []), + {keep_state_and_data, [{state_timeout, 1000, do_retrieve}]}; + _ -> + ?LOG_DEBUG("Got topology: ~p", [Topology]), + {next_state, subscribe, Data#data{obj = Topology}} + end end; handle_event(cast, context_updated, retrieve, _Data) -> {keep_state_and_data, [{state_timeout, 0, do_retrieve}]}; @@ -197,79 +204,74 @@ post_topology_event({link_deleted, Id}) -> epce_ted:link_deleted(Id). update_devices(#data{devices = OldDevices} = Data, DeviceIds, Events) -> - {NewDevices, Events2} = update_devices(OldDevices, #{}, DeviceIds, Events), - {Data#data{devices = NewDevices}, Events2}. + update_devices(Data, OldDevices, #{}, DeviceIds, Events). -update_devices(OldDevices, NewDevices, [], Events) -> - Events2 = [{device_deleted, post_process_device_id(I)} +update_devices(Data, OldDevices, NewDevices, [], Events) -> + #data{names = Names} = Data, + Events2 = [{device_deleted, maps:get(I, Names, undefined)} || I <- maps:keys(OldDevices)] ++ Events, - {NewDevices, Events2}; -update_devices(OldDevices, NewDevices, [Id | Rest], Events) -> - case get_device(Id) of - error -> throw({device_retrieval_error, Id}); + {Data#data{devices = NewDevices}, Events2}; +update_devices(Data, OldDevices, NewDevices, [GivenId | Rest], Events) -> + case get_device(GivenId) of + error -> throw({device_retrieval_error, GivenId}); {ok, Device} -> - Id2 = post_process_device_id(Id), - Device2 = post_process_device(Device), + Device2 = #{id := Id, real_id := RealId} = post_process_device(Device), + #data{names = Names} = Data, + Data2 = Data#data{names = Names#{RealId => Id}}, NewDevices2 = NewDevices#{Id => Device}, case maps:take(Id, OldDevices) of error -> % New device - Events2 = [{device_added, Id2, Device2} | Events], - update_devices(OldDevices, NewDevices2, Rest, Events2); + Events2 = [{device_added, Id, Device2} | Events], + update_devices(Data2, OldDevices, NewDevices2, Rest, Events2); {Device, OldDevices2} -> % Device did not change - update_devices(OldDevices2, NewDevices2, Rest, Events); + update_devices(Data2, OldDevices2, NewDevices2, Rest, Events); {_OldDevice, OldDevices2} -> % Device changed - Events2 = [{device_updated, Id2, Device2} | Events], - update_devices(OldDevices2, NewDevices2, Rest, Events2) + Events2 = [{device_updated, Id, Device2} | Events], + update_devices(Data2, OldDevices2, NewDevices2, Rest, Events2) end end. update_links(#data{links = OldLinks} = Data, LinksIds, Events) -> - {NewLinks, Events2} = update_links(OldLinks, #{}, LinksIds, Events), - {Data#data{links = NewLinks}, Events2}. + update_links(Data, OldLinks, #{}, LinksIds, Events). -update_links(OldLinks, NewLinks, [], Events) -> +update_links(Data, OldLinks, NewLinks, [], Events) -> Events2 = [{link_deleted, post_process_link_id(I)} || I <- maps:keys(OldLinks)] ++ Events, - {NewLinks, Events2}; -update_links(OldLinks, NewLinks, [Id | Rest], Events) -> + {Data#data{links = NewLinks}, Events2}; +update_links(Data, OldLinks, NewLinks, [Id | Rest], Events) -> case get_link(Id) of error -> throw({link_retrieval_error, Id}); {ok, Link} -> Id2 = post_process_link_id(Id), - Link2 = post_process_link(Link), + Link2 = post_process_link(Data, Link), NewLinks2 = NewLinks#{Id => Link}, case maps:take(Id, OldLinks) of error -> % New Link Events2 = [{link_added, Id2, Link2} | Events], - update_links(OldLinks, NewLinks2, Rest, Events2); + update_links(Data, OldLinks, NewLinks2, Rest, Events2); {Link, OldLinks2} -> % Link did not change - update_links(OldLinks2, NewLinks2, Rest, Events); + update_links(Data, OldLinks2, NewLinks2, Rest, Events); {_OldLink, OldLinks2} -> % Link changed Events2 = [{link_updated, Id2, Link2} | Events], - update_links(OldLinks2, NewLinks2, Rest, Events2) + update_links(Data, OldLinks2, NewLinks2, Rest, Events2) end end. -post_process_device_id(#{device_uuid := #{uuid := Name}}) -> - Name. - -post_process_device(Device) -> - #{id => device_id(Device), +post_process_device(#{device_id := Id, name := Name} = Device) -> + #{id => Name, + real_id => Id, type => device_type(Device), pcc_address => device_pcc_address(Device), mpls_label => device_mpls_label(Device), status => device_status(Device), endpoints => device_endpoints(Device)}. -device_id(#{device_id := Id}) -> - post_process_device_id(Id). - device_type(#{device_type := Type}) -> Type. @@ -312,33 +314,34 @@ device_endpoints(#{device_endpoints := Endpoints}, Acc) -> device_endpoints(Endpoints, Acc); device_endpoints([], Acc) -> lists:reverse(Acc); -device_endpoints([#{endpoint_id := #{endpoint_uuid := #{uuid := Name}}} | Rest], Acc) -> +device_endpoints([#{name := Name} | Rest], Acc) -> device_endpoints(Rest, [Name | Acc]). post_process_link_id(#{link_uuid := #{uuid := Name}}) -> Name. -post_process_link(Link) -> +post_process_link(Data, Link) -> #{id => link_id(Link), - endpoints => link_endpoints(Link)}. + endpoints => link_endpoints(Data, Link)}. link_id(#{link_id := Id}) -> post_process_link_id(Id). -link_endpoints(Link) -> - link_endpoints(Link, []). +link_endpoints(Data, Link) -> + link_endpoints(Data, Link, []). -link_endpoints(#{link_endpoint_ids := Endpoints}, Acc) -> - link_endpoints(Endpoints, Acc); -link_endpoints([], Acc) -> +link_endpoints(Data, #{link_endpoint_ids := Endpoints}, Acc) -> + link_endpoints(Data, Endpoints, Acc); +link_endpoints(_Data, [], Acc) -> lists:reverse(Acc); -link_endpoints([#{device_id := #{device_uuid := #{uuid := DevName}}, - endpoint_uuid := #{uuid := EndpointName}} | Rest], Acc) -> +link_endpoints(Data, [#{device_id := RealId, + endpoint_uuid := #{uuid := EndpointName}} | Rest], Acc) -> + #data{names = Names} = Data, Endpoint = #{ - device => DevName, + device => maps:get(RealId, Names, undefined), endpoint => EndpointName }, - link_endpoints(Rest, [Endpoint | Acc]). + link_endpoints(Data, Rest, [Endpoint | Acc]). %-- GRPC UNTILITY FUNCTION ----------------------------------------------------- diff --git a/src/te/apps/tfte/src/tfte_util.erl b/src/te/apps/tfte/src/tfte_util.erl index 7b766b5ba..444828d5c 100644 --- a/src/te/apps/tfte/src/tfte_util.erl +++ b/src/te/apps/tfte/src/tfte_util.erl @@ -18,7 +18,7 @@ custom_config(#{config_rules := Rules}, Key) -> custom_config([], _Key) -> undefined; custom_config([#{action := 'CONFIGACTION_SET', - config_rule := {custom, Rule}} | Rest], Key) -> + config_rule := {custom, Rule}} | Rest], Key) -> case Rule of #{resource_key := Key, resource_value := Value} -> jsx:decode(Value); _ -> custom_config(Rest, Key) diff --git a/src/te/tests/test_te_service.py b/src/te/tests/test_te_service.py new file mode 100644 index 000000000..282aa8592 --- /dev/null +++ b/src/te/tests/test_te_service.py @@ -0,0 +1,94 @@ +# Simple script to test GRPC calls to the TE service. +# First get the TE service IP using: +# > kubectl -n tfs get services +# Change it in this script and run with: +# > PYTHONPATH=./src python test_te_service.py + +import json, sys +from common.proto.context_pb2 import ConfigActionEnum, Service, ServiceStatusEnum, ServiceTypeEnum +from common.tools.grpc.Tools import grpc_message_to_json_string +from service.client.TEServiceClient import TEServiceClient + +# {"name": "", +# "service_config": { +# "config_rules": [ +# { +# "action": "CONFIGACTION_SET", +# "custom": { +# "resource_key": "/lsp-fw", +# "resource_value": "{\n\"binding_label\": 1111,\n\"symbolic_name\": \"foo\"\n}"}}, +# { +# "action": "CONFIGACTION_SET", +# "custom": { +# "resource_key": "/lsp-bw", +# "resource_value": "{\n\"binding_label\": 6666,\n\"symbolic_name\": \"bar\"\n}"}}]}, +# "service_constraints": [], +# "service_endpoint_ids": [ +# {"device_id": {"device_uuid": {"uuid": "RT1"}}, "endpoint_uuid": {"uuid": "eth-src"}}, +# {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-dst"}}], +# "service_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, +# "service_uuid": {"uuid": "2c025055-bf6c-4250-8560-cf62f2d29e72"}}, +# "service_status": {"service_status": "SERVICESTATUS_PLANNED"}, +# "service_type": "SERVICETYPE_TE"} + +service = Service() +service.service_id.context_id.context_uuid.uuid = 'admin' +service.service_id.service_uuid.uuid = 'test-te-service' + +service.service_type = ServiceTypeEnum.SERVICETYPE_TE +service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED + +# SRC Endpoint: +src_endpoint_id = service.service_endpoint_ids.add() +src_endpoint_id.device_id.device_uuid.uuid = 'RT1' +src_endpoint_id.endpoint_uuid.uuid = 'eth-src' + +# DST Endpoint: +dst_endpoint_id = service.service_endpoint_ids.add() +dst_endpoint_id.device_id.device_uuid.uuid = 'RT6' +dst_endpoint_id.endpoint_uuid.uuid = 'eth-dst' + +# # Capacity SLA +# sla_capacity = service.service_constraints.add() +# sla_capacity.sla_capacity.capacity_gbps = 10.0 + +# # Latency SLA +# sla_latency = service.service_constraints.add() +# sla_latency.sla_latency.e2e_latency_ms = 20.0 + +# Example config rules: +config_rule_1 = service.service_config.config_rules.add() +config_rule_1.action = ConfigActionEnum.CONFIGACTION_SET +config_rule_1.custom.resource_key = '/lsp-fw' +config_rule_1.custom.resource_value = json.dumps({ + 'binding_label': 1111, 'symbolic_name': "foo" +}) + +config_rule_2 = service.service_config.config_rules.add() +config_rule_2.action = ConfigActionEnum.CONFIGACTION_SET +config_rule_2.custom.resource_key = '/lsp-bw' +config_rule_2.custom.resource_value = json.dumps({ + 'binding_label': 6666, 'symbolic_name': "bar" +}) + +def main(): + # Connect: + te_service_client = TEServiceClient(host='XXX.XXX.XXX.XXX', port=10030) + + # RequestLSP + print('request:', grpc_message_to_json_string(service)) + service_status = te_service_client.RequestLSP(service) + print('response:', grpc_message_to_json_string(service_status)) + + # DeleteLSP + #print('request:', grpc_message_to_json_string(service)) + #service_status = te_service_client.DeleteLSP(service) + #print('response:', grpc_message_to_json_string(service_status)) + + # Close: + te_service_client.close() + + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/te/tutorial/2-6-te-demo.md b/src/te/tutorial/2-6-te-demo.md index d514adf07..c652b68bd 100644 --- a/src/te/tutorial/2-6-te-demo.md +++ b/src/te/tutorial/2-6-te-demo.md @@ -89,6 +89,11 @@ Then in second console: # cd /tmp/negen # ./tmux.sh +Be sure that both PCC connected to the TE service before going further. +This can be done by looking at the TE service log: + + $ kubectl --namespace tfs logs $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -c server + ### Setup a flow from the Erlang console We will setup two unidirectional flow between router 1 and 6. @@ -98,6 +103,11 @@ We will use the binding label 1111 for the flow from router 1 to router 6, and t 1> {ok, Flow1to6} = epce_server:initiate_flow(<<"foo">>, {1, 1, 1, 1}, {6, 6, 6, 6}, 1111). 2> {ok, Flow6to1} = epce_server:initiate_flow(<<"bar">>, {6, 6, 6, 6}, {1, 1, 1, 1}, 6666). +Another option is to use the router names: + + 1> {ok, Flow1to6} = epce_server:initiate_flow(<<"foo">>, <<"RT1">>, <<"RT6">>, 1111). + 2> {ok, Flow6to1} = epce_server:initiate_flow(<<"bar">>, <<"RT6">>, <<"RT1">>, 6666). + Now if we go to the tmux session src (Ctrl-B 0) we can ping dst: $ ping 9.9.9.2 @@ -105,3 +115,16 @@ Now if we go to the tmux session src (Ctrl-B 0) we can ping dst: From the Erlang console we can update the initiated flows to change the path the packets are flowing through: 3> epce_server:update_flow(Flow6to1, [16050, 16030, 16010]). + +### Setup a flow using the GRPC test script + +This does the same as the the setup from the Erlang console, but through GRPC. +After deploying Teraflow (with te), get the te service ip using: + + $ kubectl -n tfs get services + +Replace the IP in the python script `src/te/tests/test_te_service.py`. +Be sure the topology as been loaded, and netgen started as described previously, +and run the following command from the root of the teraflow controller: + + $ PYTHONPATH=./src python src/te/tests/test_te_service.py -- GitLab From 743362a19409330f2bddc352671e09ed6ff5bab5 Mon Sep 17 00:00:00 2001 From: merle Date: Tue, 11 Jul 2023 06:22:36 -0700 Subject: [PATCH 23/63] Update readme for Teraflow deployment --- my_deploy.sh | 3 +++ src/te/README.md | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/my_deploy.sh b/my_deploy.sh index e3ad5e71a..ac25e6a2c 100755 --- a/my_deploy.sh +++ b/my_deploy.sh @@ -22,6 +22,9 @@ export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/" # Set the list of components, separated by spaces, you want to build images for, and deploy. export TFS_COMPONENTS="context device pathcomp service slice compute webui load_generator" +# Uncoment to activate TE +#export TFS_COMPONENTS="${TFS_COMPONENTS} te" + # Uncoment to activate Monitoring #export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring" diff --git a/src/te/README.md b/src/te/README.md index d6f1952ac..dc5570e00 100644 --- a/src/te/README.md +++ b/src/te/README.md @@ -67,6 +67,13 @@ The docker image must be built from the root of the Teraflow project: $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -c server -- /tfte/bin/tfte remote_console -## Show Logs +### Show Logs $ kubectl --namespace tfs logs $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -c server + + +## Teraflow + +To build and deploy the TE service as part of Teraflow, the following line must be added or uncomented in your `my_deploy.sh`: + + export TFS_COMPONENTS="${TFS_COMPONENTS} te" -- GitLab From c16334074f9393dc266cdab04e5b5b9cb190ea6f Mon Sep 17 00:00:00 2001 From: merle Date: Tue, 11 Jul 2023 06:36:41 -0700 Subject: [PATCH 24/63] Update readme --- src/te/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/te/README.md b/src/te/README.md index dc5570e00..5da478e61 100644 --- a/src/te/README.md +++ b/src/te/README.md @@ -1,6 +1,8 @@ TeraFlow Traffic Engineering Service ==================================== +This service is mean as an example of a Teraflow Service made in Erlang. + The Traffic Engineering service is tested on Ubuntu 20.04. Follow the instructions below to build, test, and run this service on your local environment. -- GitLab From 57b173ee6c5d9d6f798a067c4b2b385fd650e257 Mon Sep 17 00:00:00 2001 From: merle Date: Tue, 11 Jul 2023 08:03:09 -0700 Subject: [PATCH 25/63] Fix missing application descriptor --- proto/src/erlang/.gitignore | 1 + proto/src/erlang/src/tfpb.app.src | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 proto/src/erlang/src/tfpb.app.src diff --git a/proto/src/erlang/.gitignore b/proto/src/erlang/.gitignore index 9b34c9cdb..b583d287f 100644 --- a/proto/src/erlang/.gitignore +++ b/proto/src/erlang/.gitignore @@ -1,4 +1,5 @@ * !rebar.config !rebar.lock +!src/ !src/tfpb.app.src diff --git a/proto/src/erlang/src/tfpb.app.src b/proto/src/erlang/src/tfpb.app.src new file mode 100644 index 000000000..abbe6bbcc --- /dev/null +++ b/proto/src/erlang/src/tfpb.app.src @@ -0,0 +1,10 @@ +{application, tfpb, + [{description, "Teraflow Erlang Protocol Buffers"}, + {vsn, "0.1.0"}, + {registered, []}, + {applications, [kernel, stdlib, grpcbox]}, + {env, []}, + {modules, []}, + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. -- GitLab From 5cebd158a3a0178afbf6421b683ad7c8a62c205e Mon Sep 17 00:00:00 2001 From: merle Date: Thu, 28 Jul 2022 03:05:08 -0700 Subject: [PATCH 26/63] Add TE service skeleton --- .gitignore | 3 + manifests/teservice.yaml | 79 ++++++++++++++++++++++++ src/te/.dockerignore | 4 ++ src/te/.gitignore | 21 +++++++ src/te/Dockerfile | 55 +++++++++++++++++ src/te/README.md | 50 +++++++++++++++ src/te/apps/tfte/src/tfte.app.src | 15 +++++ src/te/apps/tfte/src/tfte_app.erl | 18 ++++++ src/te/apps/tfte/src/tfte_sup.erl | 35 +++++++++++ src/te/config/dev.config.template | 19 ++++++ src/te/config/sys.config.src | 19 ++++++ src/te/config/vm.args.src | 4 ++ src/te/rebar.config | 28 +++++++++ src/te/rebar.lock | 1 + tutorial/1-6-setup-erlang-environmnet.md | 48 ++++++++++++++ 15 files changed, 399 insertions(+) create mode 100644 manifests/teservice.yaml create mode 100644 src/te/.dockerignore create mode 100644 src/te/.gitignore create mode 100644 src/te/Dockerfile create mode 100644 src/te/README.md create mode 100644 src/te/apps/tfte/src/tfte.app.src create mode 100644 src/te/apps/tfte/src/tfte_app.erl create mode 100644 src/te/apps/tfte/src/tfte_sup.erl create mode 100644 src/te/config/dev.config.template create mode 100644 src/te/config/sys.config.src create mode 100644 src/te/config/vm.args.src create mode 100644 src/te/rebar.config create mode 100644 src/te/rebar.lock create mode 100644 tutorial/1-6-setup-erlang-environmnet.md diff --git a/.gitignore b/.gitignore index 0a116f850..a9144d669 100644 --- a/.gitignore +++ b/.gitignore @@ -168,5 +168,8 @@ delete_local_deployment.sh local_docker_deployment.sh local_k8s_deployment.sh +# asdf configuration +.tool-versions + # Other logs **/logs/*.log.* diff --git a/manifests/teservice.yaml b/manifests/teservice.yaml new file mode 100644 index 000000000..2fca50879 --- /dev/null +++ b/manifests/teservice.yaml @@ -0,0 +1,79 @@ +# 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. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: teservice +spec: + selector: + matchLabels: + app: teservice + template: + metadata: + labels: + app: teservice + spec: + terminationGracePeriodSeconds: 5 + shareProcessNamespace: true + containers: + - name: server + image: registry.gitlab.com/teraflow-h2020/controller/te:latest + imagePullPolicy: Always + ports: + - containerPort: 11010 + env: + - name: ERLANG_LOGGER_LEVEL + value: "debug" + - name: ERLANG_COOKIE + value: "tfte-unsafe-cookie" + - name: ERLANG_NODE_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: ERLANG_NODE_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + exec: + command: ["/tfte/bin/tfte", "status"] + livenessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + exec: + command: ["/tfte/bin/tfte", "status"] + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: 700m + memory: 1024Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: teservice +spec: + type: ClusterIP + selector: + app: teservice + ports: + - name: grpc + protocol: TCP + port: 11010 + targetPort: 11010 diff --git a/src/te/.dockerignore b/src/te/.dockerignore new file mode 100644 index 000000000..558d2f3ec --- /dev/null +++ b/src/te/.dockerignore @@ -0,0 +1,4 @@ +Dockerfile +_build +README.md +.tool-versions diff --git a/src/te/.gitignore b/src/te/.gitignore new file mode 100644 index 000000000..eeb7cfc4a --- /dev/null +++ b/src/te/.gitignore @@ -0,0 +1,21 @@ +.tool-versions +.rebar3 +_* +.eunit +*.o +*.beam +*.plt +*.swp +*.swo +.erlang.cookie +ebin +log +erl_crash.dump +.rebar +logs +_build +.idea +*.iml +rebar3.crashdump +*~ +config/dev.config diff --git a/src/te/Dockerfile b/src/te/Dockerfile new file mode 100644 index 000000000..d8745ac74 --- /dev/null +++ b/src/te/Dockerfile @@ -0,0 +1,55 @@ +# 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 + +# Build stage 0 +FROM erlang:24.3-alpine + +RUN mkdir /buildroot +WORKDIR /buildroot + +# Copy our Erlang application +COPY . tfte + +# And build the release +WORKDIR tfte +RUN rebar3 as prod release + +# Build stage 1 +FROM alpine + +# Install some libs +RUN apk add --no-cache libgcc libstdc++ && \ + apk add --no-cache openssl && \ + apk add --no-cache ncurses-libs + +# Install the released application +COPY --from=0 /buildroot/tfte/_build/prod/rel/tfte /tfte + +# Expose relevant ports +#EXPOSE ???? + +ARG ERLANG_LOGGER_LEVEL_DEFAULT=debug +ARG ERLANG_COOKIE_DEFAULT=tfte-unsafe-cookie +ARG ERLANG_NODE_IP_DEFAULT=127.0.0.1 +ARG ERLANG_NODE_NAME_DEFAULT=tfte + +ENV ERLANG_LOGGER_LEVEL=$ERLANG_LOGGER_LEVEL_DEFAULT +ENV ERLANG_COOKIE=$ERLANG_COOKIE_DEFAULT +ENV ERLANG_NODE_IP=$ERLANG_NODE_IP_DEFAULT +ENV ERLANG_NODE_NAME=$ERLANG_NODE_NAME_DEFAULT + +ENTRYPOINT ["/tfte/bin/tfte"] +CMD ["foreground"] diff --git a/src/te/README.md b/src/te/README.md new file mode 100644 index 000000000..764021a4f --- /dev/null +++ b/src/te/README.md @@ -0,0 +1,50 @@ +TeraFlow Traffic Engineering Service +==================================== + +The Traffic Engineering service is tested on Ubuntu 20.04. Follow the instructions below to build, test, and run this service on your local environment. + + +## Build + + $ rebar3 compile + + +## Execute Unit Tests + + $ rebar3 eunit + + +## Run Service Console + +First you need to crete a configuration file if not already done, and customize it if required: + + $ cp config/dev.config.template config/dev.config + +Then you can start the service in console mode: + + $ rebar3 shell + + +## Build Docker Image + + $ docker build -t te:dev . + + +## Run Docker Container + + $ docker run -d --name te --init te:dev + + +## Open a Console to a Docker Container + + $ docker exec -it te /tfte/bin/tfte remote_console + + +## Open a Console to a Kubernetes Pod + + $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -- /tfte/bin/tfte remote_console + + +## Show Logs + + $ docker logs te \ No newline at end of file diff --git a/src/te/apps/tfte/src/tfte.app.src b/src/te/apps/tfte/src/tfte.app.src new file mode 100644 index 000000000..f1ed6cc80 --- /dev/null +++ b/src/te/apps/tfte/src/tfte.app.src @@ -0,0 +1,15 @@ +{application, tfte, + [{description, "Teraflow Traffic Engineering Service"}, + {vsn, "0.1.0"}, + {registered, []}, + {mod, {tfte_app, []}}, + {applications, + [kernel, + stdlib + ]}, + {env,[]}, + {modules, []}, + + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. diff --git a/src/te/apps/tfte/src/tfte_app.erl b/src/te/apps/tfte/src/tfte_app.erl new file mode 100644 index 000000000..aafcb2bcd --- /dev/null +++ b/src/te/apps/tfte/src/tfte_app.erl @@ -0,0 +1,18 @@ +%%%------------------------------------------------------------------- +%% @doc tfte public API +%% @end +%%%------------------------------------------------------------------- + +-module(tfte_app). + +-behaviour(application). + +-export([start/2, stop/1]). + +start(_StartType, _StartArgs) -> + tfte_sup:start_link(). + +stop(_State) -> + ok. + +%% internal functions diff --git a/src/te/apps/tfte/src/tfte_sup.erl b/src/te/apps/tfte/src/tfte_sup.erl new file mode 100644 index 000000000..a6f0a21f8 --- /dev/null +++ b/src/te/apps/tfte/src/tfte_sup.erl @@ -0,0 +1,35 @@ +%%%------------------------------------------------------------------- +%% @doc tfte top level supervisor. +%% @end +%%%------------------------------------------------------------------- + +-module(tfte_sup). + +-behaviour(supervisor). + +-export([start_link/0]). + +-export([init/1]). + +-define(SERVER, ?MODULE). + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%% sup_flags() = #{strategy => strategy(), % optional +%% intensity => non_neg_integer(), % optional +%% period => pos_integer()} % optional +%% child_spec() = #{id => child_id(), % mandatory +%% start => mfargs(), % mandatory +%% restart => restart(), % optional +%% shutdown => shutdown(), % optional +%% type => worker(), % optional +%% modules => modules()} % optional +init([]) -> + SupFlags = #{strategy => one_for_all, + intensity => 0, + period => 1}, + ChildSpecs = [], + {ok, {SupFlags, ChildSpecs}}. + +%% internal functions diff --git a/src/te/config/dev.config.template b/src/te/config/dev.config.template new file mode 100644 index 000000000..d6a4644ad --- /dev/null +++ b/src/te/config/dev.config.template @@ -0,0 +1,19 @@ +[ + {tfte, [ + ]}, + + {kernel, [ + {logger_level, debug}, + {logger, [ + {handler, default, logger_std_h, #{ + level => debug, + filter_default => log, + config => #{type => standard_io}, + formatter => {logger_formatter, #{ + legacy_header => false, + single_line => true + }} + }} + ]} + ]} +]. \ No newline at end of file diff --git a/src/te/config/sys.config.src b/src/te/config/sys.config.src new file mode 100644 index 000000000..70003158b --- /dev/null +++ b/src/te/config/sys.config.src @@ -0,0 +1,19 @@ +[ + {tfte, [ + ]}, + + {kernel, [ + {logger_level, ${ERLANG_LOGGER_LEVEL}}, + {logger, [ + {handler, default, logger_std_h, #{ + level => info, + filter_default => log, + config => #{type => standard_io}, + formatter => {logger_formatter, #{ + legacy_header => false, + single_line => true + }} + }} + ]} + ]} +]. \ No newline at end of file diff --git a/src/te/config/vm.args.src b/src/te/config/vm.args.src new file mode 100644 index 000000000..899705ce1 --- /dev/null +++ b/src/te/config/vm.args.src @@ -0,0 +1,4 @@ ++C multi_time_warp ++sbwt none +-name ${ERLANG_NODE_NAME}@${ERLANG_NODE_IP} +-setcookie ${ERLANG_COOKIE} diff --git a/src/te/rebar.config b/src/te/rebar.config new file mode 100644 index 000000000..b7b5e3e69 --- /dev/null +++ b/src/te/rebar.config @@ -0,0 +1,28 @@ +{erl_opts, [debug_info]}. + +{deps, [ +]}. + +{shell, [ + {config, "config/dev.config"}, + {apps, [tfte]} +]}. + +{relx, [ + {release, {tfte, "1.0.0"}, [tfte]}, + {vm_args_src, "config/vm.args.src"}, + {sys_config_src, "config/sys.config.src"}, + {dev_mode, true}, + {include_erts, false}, + {extended_start_script, true} +]}. + +{profiles, [ + {prod, [ + {relx, [ + {dev_mode, false}, + {include_erts, true}, + {include_src, false} + ]} + ]} +]}. diff --git a/src/te/rebar.lock b/src/te/rebar.lock new file mode 100644 index 000000000..57afcca04 --- /dev/null +++ b/src/te/rebar.lock @@ -0,0 +1 @@ +[]. diff --git a/tutorial/1-6-setup-erlang-environmnet.md b/tutorial/1-6-setup-erlang-environmnet.md new file mode 100644 index 000000000..11e65a49a --- /dev/null +++ b/tutorial/1-6-setup-erlang-environmnet.md @@ -0,0 +1,48 @@ +# 1.5. Setup Erlang Environment + +First we need to install Erlang. There is multiple way, for development we will be using asdf. + +## 1.5.1. Setup Erlang using asdf + +First, install any missing dependencies: + + sudo apt install curl git autoconf libncurses-dev build-essential m4 libssl-dev + +Download asdf: + + git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.10.2 + +Make asdf activate on login by adding these lines at the end of the `~/.bashrc` file: + + . $HOME/.asdf/asdf.sh + . $HOME/.asdf/completions/asdf.bash + +Logout and log back in to activate asdf. + +Install asdf plugin for Erlang: + + asdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git + +Install Erlang: + + asdf install erlang 24.3.4.2 + +Activate Erlang locally for TFS controller: + + cd tfs-ctrl/ + asdf local erlang 24.3.4.2 + +Install rebar3: + + cd ~ + git clone https://github.com/erlang/rebar3.git + cd rebar3 + asdf local erlang 24.3.4.2 + ./bootstrap + ./rebar3 local install + +Update `~/.bashrc` to use rebar3 by adding this line at the end: + + export PATH=$HOME/.cache/rebar3/bin:$PATH + +Logout and log back in. -- GitLab From 8651b1263e1f653ae8862ff1b36d90f168e36e3d Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 3 Aug 2022 07:34:43 -0700 Subject: [PATCH 27/63] te: Add support for protocol buffer --- manifests/teservice.yaml | 8 +-- proto/.gitignore | 3 + proto/generate_code_erlang.sh | 74 ++++++++++++++++++++ proto/src/erlang/.gitignore | 4 ++ proto/src/erlang/rebar.config | 7 ++ proto/src/erlang/rebar.lock | 23 +++++++ src/te/Dockerfile | 18 ++--- src/te/README.md | 34 +++++++-- src/te/apps/tfte/src/tfte.app.src | 3 +- src/te/apps/tfte/src/tfte_app.erl | 88 ++++++++++++++++++++++-- src/te/apps/tfte/src/tfte_sup.erl | 29 ++++---- src/te/apps/tfte/src/tfte_te_service.erl | 31 +++++++++ src/te/config/dev.config.template | 37 ++++++++++ src/te/config/sys.config.src | 58 ++++++++++++++++ src/te/rebar.config | 9 +-- src/te/rebar.lock | 24 ++++++- 16 files changed, 407 insertions(+), 43 deletions(-) create mode 100755 proto/generate_code_erlang.sh create mode 100644 proto/src/erlang/.gitignore create mode 100644 proto/src/erlang/rebar.config create mode 100644 proto/src/erlang/rebar.lock create mode 100644 src/te/apps/tfte/src/tfte_te_service.erl diff --git a/manifests/teservice.yaml b/manifests/teservice.yaml index 2fca50879..313acd273 100644 --- a/manifests/teservice.yaml +++ b/manifests/teservice.yaml @@ -47,15 +47,11 @@ spec: fieldRef: fieldPath: status.podIP readinessProbe: - initialDelaySeconds: 10 - periodSeconds: 10 exec: command: ["/tfte/bin/tfte", "status"] livenessProbe: - initialDelaySeconds: 10 - periodSeconds: 10 - exec: - command: ["/tfte/bin/tfte", "status"] + grpc: + port: 11010 resources: requests: cpu: 250m diff --git a/proto/.gitignore b/proto/.gitignore index d1dea37b3..4d6f9cbd7 100644 --- a/proto/.gitignore +++ b/proto/.gitignore @@ -3,5 +3,8 @@ src/*/* # used to prevent breaking symbolic links from source code folders !src/*/.gitignore !src/python/__init__.py +!src/erlang/rebar.config +!src/erlang/rebar.lock +!src/erlang/src/tfpb.app.src uml/generated diff --git a/proto/generate_code_erlang.sh b/proto/generate_code_erlang.sh new file mode 100755 index 000000000..471b654f9 --- /dev/null +++ b/proto/generate_code_erlang.sh @@ -0,0 +1,74 @@ +#!/bin/bash -eu +# 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. + +set -e + +FORCE=0 +DEFAULT_ACTION="generate" + +usage() { + echo "Usage: $0 [-f] [clean|generate]" 1>&2 + echo "Options:" + echo " -f: Force regeneration of all protocol buffers" + exit 1; +} + +while getopts "fc" o; do + case "${o}" in + f) + FORCE=1 + ;; + *) + usage + ;; + esac +done +shift $((OPTIND-1)) + +ACTION=${1:-$DEFAULT_ACTION} +cd $(dirname $0) +ROOT=$(pwd) +ERLANG_PROTO_DIR="$ROOT/src/erlang" +BUILD_CHECK="$ERLANG_PROTO_DIR/.generated" + +tfpb_clean() { + rm -f "$BUILD_CHECK" + rm -rf "$ERLANG_PROTO_DIR/src/"*.erl + rm -rf "$ERLANG_PROTO_DIR/src/erlang/_build" +} + +tfpb_generate() { + if [[ -f "$BUILD_CHECK" && $FORCE != 1 ]]; then + echo "Protocol buffer code for Erlang already generated, use -f to force" + exit 0 + fi + + tfpb_clean + mkdir -p "$ERLANG_PROTO_DIR" + cd "$ERLANG_PROTO_DIR" + rebar3 compile + rebar3 grpc gen + rebar3 compile + touch "$BUILD_CHECK" + + echo "Protocol buffer code for Erlang generated" +} + +case "$ACTION" in + clean) tfpb_clean;; + generate) tfpb_generate;; + *) usage;; +esac + diff --git a/proto/src/erlang/.gitignore b/proto/src/erlang/.gitignore new file mode 100644 index 000000000..9b34c9cdb --- /dev/null +++ b/proto/src/erlang/.gitignore @@ -0,0 +1,4 @@ +* +!rebar.config +!rebar.lock +!src/tfpb.app.src diff --git a/proto/src/erlang/rebar.config b/proto/src/erlang/rebar.config new file mode 100644 index 000000000..31ec32a36 --- /dev/null +++ b/proto/src/erlang/rebar.config @@ -0,0 +1,7 @@ +{erl_opts, [debug_info]}. +{deps, [grpcbox]}. + +{grpc, [{protos, "../.."}, + {gpb_opts, [{i, "../.."}, {strbin, true}, {descriptor, true}, {module_name_suffix, "_pb"}]}]}. + +{plugins, [grpcbox_plugin]}. diff --git a/proto/src/erlang/rebar.lock b/proto/src/erlang/rebar.lock new file mode 100644 index 000000000..d353eaf34 --- /dev/null +++ b/proto/src/erlang/rebar.lock @@ -0,0 +1,23 @@ +{"1.2.0", +[{<<"acceptor_pool">>,{pkg,<<"acceptor_pool">>,<<"1.0.0">>},1}, + {<<"chatterbox">>,{pkg,<<"ts_chatterbox">>,<<"0.12.0">>},1}, + {<<"ctx">>,{pkg,<<"ctx">>,<<"0.6.0">>},1}, + {<<"gproc">>,{pkg,<<"gproc">>,<<"0.8.0">>},1}, + {<<"grpcbox">>,{pkg,<<"grpcbox">>,<<"0.15.0">>},0}, + {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}]}. +[ +{pkg_hash,[ + {<<"acceptor_pool">>, <<"43C20D2ACAE35F0C2BCD64F9D2BDE267E459F0F3FD23DAB26485BF518C281B21">>}, + {<<"chatterbox">>, <<"4E54F199E15C0320B85372A24E35554A2CCFC4342E0B7CD8DAED9A04F9B8EF4A">>}, + {<<"ctx">>, <<"8FF88B70E6400C4DF90142E7F130625B82086077A45364A78D208ED3ED53C7FE">>}, + {<<"gproc">>, <<"CEA02C578589C61E5341FCE149EA36CCEF236CC2ECAC8691FBA408E7EA77EC2F">>}, + {<<"grpcbox">>, <<"97C7126296A091602D372EBF5860A04F7BC795B45B33A984CAD2B8E362774FD8">>}, + {<<"hpack">>, <<"17670F83FF984AE6CD74B1C456EDDE906D27FF013740EE4D9EFAA4F1BF999633">>}]}, +{pkg_hash_ext,[ + {<<"acceptor_pool">>, <<"0CBCD83FDC8B9AD2EEE2067EF8B91A14858A5883CB7CD800E6FCD5803E158788">>}, + {<<"chatterbox">>, <<"6478C161BC60244F41CD5847CC3ACCD26D997883E9F7FACD36FF24533B2FA579">>}, + {<<"ctx">>, <<"A14ED2D1B67723DBEBBE423B28D7615EB0BDCBA6FF28F2D1F1B0A7E1D4AA5FC2">>}, + {<<"gproc">>, <<"580ADAFA56463B75263EF5A5DF4C86AF321F68694E7786CB057FD805D1E2A7DE">>}, + {<<"grpcbox">>, <<"161ABE9E17E7D1982EFA6488ADEAA13C3E847A07984A6E6B224E553368918647">>}, + {<<"hpack">>, <<"06F580167C4B8B8A6429040DF36CC93BBA6D571FAEAEC1B28816523379CBB23A">>}]} +]. diff --git a/src/te/Dockerfile b/src/te/Dockerfile index d8745ac74..d9d561d4d 100644 --- a/src/te/Dockerfile +++ b/src/te/Dockerfile @@ -17,14 +17,16 @@ # Build stage 0 FROM erlang:24.3-alpine -RUN mkdir /buildroot -WORKDIR /buildroot +RUN apk add --no-cache bash -# Copy our Erlang application -COPY . tfte +RUN mkdir /var/teraflow +WORKDIR /var/teraflow -# And build the release -WORKDIR tfte +COPY proto proto +RUN bash -c proto/generate_code_erlang.sh +RUN mkdir src +COPY src/te src/te +WORKDIR src/te RUN rebar3 as prod release # Build stage 1 @@ -36,10 +38,10 @@ RUN apk add --no-cache libgcc libstdc++ && \ apk add --no-cache ncurses-libs # Install the released application -COPY --from=0 /buildroot/tfte/_build/prod/rel/tfte /tfte +COPY --from=0 /var/teraflow/src/te/_build/prod/rel/tfte /tfte # Expose relevant ports -#EXPOSE ???? +EXPOSE 11010 ARG ERLANG_LOGGER_LEVEL_DEFAULT=debug ARG ERLANG_COOKIE_DEFAULT=tfte-unsafe-cookie diff --git a/src/te/README.md b/src/te/README.md index 764021a4f..a316571bf 100644 --- a/src/te/README.md +++ b/src/te/README.md @@ -6,6 +6,12 @@ The Traffic Engineering service is tested on Ubuntu 20.04. Follow the instructio ## Build +First the TeraFlow protocol buffer code must have been generated: + + $ ../../proto/generate_code_erlang.sh + +Then the TE service can be built: + $ rebar3 compile @@ -25,26 +31,42 @@ Then you can start the service in console mode: $ rebar3 shell -## Build Docker Image +## Docker + +### Build Image + +The docker image must be built from the root of the Teraflow project: + + $ docker build -t te:dev -f src/te/Dockerfile . - $ docker build -t te:dev . +### Run a shell from inside the container -## Run Docker Container + $ docker run -ti --rm --entrypoint sh te:dev + + +### Run Docker Container $ docker run -d --name te --init te:dev -## Open a Console to a Docker Container +### Open a Console to a Docker Container's Service $ docker exec -it te /tfte/bin/tfte remote_console -## Open a Console to a Kubernetes Pod +### Show Logs + + $ docker logs te + + +## Kubernetes + +### Open a Console $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -- /tfte/bin/tfte remote_console ## Show Logs - $ docker logs te \ No newline at end of file + $ kubectl --namespace tfs logs $(kubectl --namespace tfs get pods --selector=app=teservice -o name) diff --git a/src/te/apps/tfte/src/tfte.app.src b/src/te/apps/tfte/src/tfte.app.src index f1ed6cc80..76d397939 100644 --- a/src/te/apps/tfte/src/tfte.app.src +++ b/src/te/apps/tfte/src/tfte.app.src @@ -5,7 +5,8 @@ {mod, {tfte_app, []}}, {applications, [kernel, - stdlib + stdlib, + tfpb ]}, {env,[]}, {modules, []}, diff --git a/src/te/apps/tfte/src/tfte_app.erl b/src/te/apps/tfte/src/tfte_app.erl index aafcb2bcd..159197fdf 100644 --- a/src/te/apps/tfte/src/tfte_app.erl +++ b/src/te/apps/tfte/src/tfte_app.erl @@ -1,18 +1,98 @@ -%%%------------------------------------------------------------------- +%%%----------------------------------------------------------------------------- %% @doc tfte public API %% @end -%%%------------------------------------------------------------------- +%%%----------------------------------------------------------------------------- -module(tfte_app). -behaviour(application). + +%--- Includes ------------------------------------------------------------------ + +-include_lib("kernel/include/logger.hrl"). + + +%--- Exports ------------------------------------------------------------------- + +% Behaviour application callback functions -export([start/2, stop/1]). + +%--- Behaviour application Callback Functions ---------------------------------- + start(_StartType, _StartArgs) -> - tfte_sup:start_link(). + case tfte_sup:start_link() of + {ok, Pid} -> + add_services(), + {ok, Pid}; + Other -> + Other + end. stop(_State) -> ok. -%% internal functions + +%--- Internal Functions -------------------------------------------------------- + +add_services() -> + case application:get_env(tfte, services) of + {ok, Services} -> add_services(Services); + _ -> ok + end. + +add_services([]) -> ok; +add_services([{Name, EndpointsSpecs, GrpcOpts} | Rest]) -> + try resolve_endpoints(Name, EndpointsSpecs, []) of + Endpoints -> + case grpcbox_channel_sup:start_child(Name, Endpoints, GrpcOpts) of + {ok, _Pid} -> + ?LOG_INFO("GRPC channel to ~s service started", [Name]), + ok; + {error, Reason} -> + ?LOG_WARNING("GRPC channel to ~s service failed to start: ~p", + [Name, Reason]), + ok + end + catch + throw:{Name, Reason, Extra} -> + ?LOG_WARNING("Failed to resolve ~s service configuration: ~s ~p ~p", + [Name, Reason, Extra]) + end, + add_services(Rest). + +resolve_endpoints(_Name, [], Acc) -> + lists:reverse(Acc); +resolve_endpoints(Name, [{Transport, HostSpec, PortSpec, SslOpts} | Rest], Acc) -> + Acc2 = [{Transport, resolve_host_spec(Name, HostSpec), + resolve_port_spec(Name, PortSpec), SslOpts} | Acc], + resolve_endpoints(Name, Rest, Acc2). + +resolve_host_spec(_Name, Hostname) when is_list(Hostname) -> Hostname; +resolve_host_spec(Name, {env, Key}) when is_list(Key) -> + ?LOG_DEBUG("????? HOST ~s ~s -> ~p", [Name, Key, os:getenv(Key)]), + try os:getenv(Key) of + false -> throw({Name, service_hostname_not_found, Key}); + Hostname -> Hostname + catch + _:Reason -> + throw({Name, service_hostname_error, Reason}) + end. + +resolve_port_spec(_Name, Port) when is_integer(Port) -> Port; +resolve_port_spec(Name, {env, Key}) when is_list(Key) -> + ?LOG_DEBUG("????? PORT ~s ~s -> ~p", [Name, Key, os:getenv(Key)]), + try os:getenv(Key) of + false -> throw({Name, service_port_not_found, Key}); + PortStr -> + try list_to_integer(PortStr) of + Port -> Port + catch + _:Reason -> + throw({Name, service_port_error, Reason}) + end + catch + _:Reason -> + throw({Name, service_port_error, Reason}) + end. diff --git a/src/te/apps/tfte/src/tfte_sup.erl b/src/te/apps/tfte/src/tfte_sup.erl index a6f0a21f8..2944889cb 100644 --- a/src/te/apps/tfte/src/tfte_sup.erl +++ b/src/te/apps/tfte/src/tfte_sup.erl @@ -1,35 +1,38 @@ -%%%------------------------------------------------------------------- +%%%----------------------------------------------------------------------------- %% @doc tfte top level supervisor. %% @end -%%%------------------------------------------------------------------- +%%%----------------------------------------------------------------------------- -module(tfte_sup). -behaviour(supervisor). + +%--- Exports ------------------------------------------------------------------- + +% API Functions -export([start_link/0]). +% Behaviour supervisor callback functions -export([init/1]). + +%--- Macros -------------------------------------------------------------------- + -define(SERVER, ?MODULE). + +%--- API Functions ------------------------------------------------------------- + start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). -%% sup_flags() = #{strategy => strategy(), % optional -%% intensity => non_neg_integer(), % optional -%% period => pos_integer()} % optional -%% child_spec() = #{id => child_id(), % mandatory -%% start => mfargs(), % mandatory -%% restart => restart(), % optional -%% shutdown => shutdown(), % optional -%% type => worker(), % optional -%% modules => modules()} % optional + +%--- Behaviour supervisor Callback Functions ----------------------------------- + init([]) -> SupFlags = #{strategy => one_for_all, intensity => 0, period => 1}, ChildSpecs = [], {ok, {SupFlags, ChildSpecs}}. - -%% internal functions diff --git a/src/te/apps/tfte/src/tfte_te_service.erl b/src/te/apps/tfte/src/tfte_te_service.erl new file mode 100644 index 000000000..7c2a7225b --- /dev/null +++ b/src/te/apps/tfte/src/tfte_te_service.erl @@ -0,0 +1,31 @@ +-module(tfte_te_service). + +-behaviour(te_te_service_bhvr). + + +%--- Includes ------------------------------------------------------------------ + +-include_lib("grpcbox/include/grpcbox.hrl"). + + +%--- Exports ------------------------------------------------------------------- + +% Behaviour te_te_service_bhvr callback functions +-export([request_lsp/2]). +-export([update_lsp/2]). +-export([delete_lsp/2]). + + +%--- Behaviour te_te_service_bhvr Callback Functions --------------------------- + +request_lsp(_Ctx, _Service) -> + {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, + #{headers => #{}, trailers => #{}}}. + +update_lsp(_Ctx, _Service) -> + {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, + #{headers => #{}, trailers => #{}}}. + +delete_lsp(_Ctx, _Service) -> + {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, + #{headers => #{}, trailers => #{}}}. diff --git a/src/te/config/dev.config.template b/src/te/config/dev.config.template index d6a4644ad..658bf13f8 100644 --- a/src/te/config/dev.config.template +++ b/src/te/config/dev.config.template @@ -1,5 +1,42 @@ [ {tfte, [ + {services, [ + {te, [{http, "localhost", 11010, []}], #{}}, + ]}, + + {grpcbox, [ + {servers, [#{ + grpc_opts => #{ + service_protos => [te_pb, grpcbox_health_pb, grpcbox_reflection_pb], + %client_cert_dir => "", + services => #{ + 'te.TEService' => tfte_te_service, + 'grpc.health.v1.Health' => grpcbox_health_service, + 'grpc.reflection.v1alpha.ServerReflection' => grpcbox_reflection_service + } + }, + transport_opts => #{ + ssl => false + %keyfile => "", + %certfile => "", + %cacertfile => "" + }, + listen_opts => #{ + port => 11010, + ip => {0,0,0,0} + }, + pool_opts => #{ + size => 10 + }, + server_opts => #{ + header_table_size => 4096, + enable_push => 1, + max_concurrent_streams => unlimited, + initial_window_size => 65535, + max_frame_size => 16384, + max_header_list_size => unlimited + } + }]} ]}, {kernel, [ diff --git a/src/te/config/sys.config.src b/src/te/config/sys.config.src index 70003158b..2bd5cf178 100644 --- a/src/te/config/sys.config.src +++ b/src/te/config/sys.config.src @@ -1,5 +1,63 @@ [ {tfte, [ + {services, [ + {te, [ + {http, {env, "TESERVICE_SERVICE_HOST"}, {env, "TESERVICE_SERVICE_PORT_GRPC"}, []} + ], #{}}, + {service, [ + {http, {env, "SERVICESERVICE_SERVICE_HOST"}, {env, "SERVICESERVICE_SERVICE_PORT_GRPC"}, []} + ], #{}}, + {monitoring, [ + {http, {env, "MONITORINGSERVICE_SERVICE_HOST"}, {env, "MONITORINGSERVICE_SERVICE_PORT_GRPC"}, []} + ], #{}}, + {compute, [ + {http, {env, "COMPUTESERVICE_SERVICE_HOST"}, {env, "COMPUTESERVICE_SERVICE_PORT_GRPC"}, []} + ], #{}}, + {device, [ + {http, {env, "DEVICESERVICE_SERVICE_HOST"}, {env, "DEVICESERVICE_SERVICE_PORT_GRPC"}, []} + ], #{}}, + {context, [ + {http, {env, "CONTEXTSERVICE_SERVICE_HOST"}, {env, "CONTEXTSERVICE_SERVICE_PORT_GRPC"}, []} + ], #{}}, + {automation, [ + {http, {env, "AUTOMATIONSERVICE_SERVICE_HOST"}, {env, "AUTOMATIONSERVICE_SERVICE_PORT_GRPC"}, []} + ], #{}} + ]} + ]}, + + {grpcbox, [ + {servers, [#{ + grpc_opts => #{ + service_protos => [te_pb, grpcbox_health_pb, grpcbox_reflection_pb], + %client_cert_dir => "", + services => #{ + 'te.TEService' => tfte_te_service, + 'grpc.health.v1.Health' => grpcbox_health_service, + 'grpc.reflection.v1alpha.ServerReflection' => grpcbox_reflection_service + } + }, + transport_opts => #{ + ssl => false + %keyfile => "", + %certfile => "", + %cacertfile => "" + }, + listen_opts => #{ + port => 11010, + ip => {0,0,0,0} + }, + pool_opts => #{ + size => 10 + }, + server_opts => #{ + header_table_size => 4096, + enable_push => 1, + max_concurrent_streams => unlimited, + initial_window_size => 65535, + max_frame_size => 16384, + max_header_list_size => unlimited + } + }]} ]}, {kernel, [ diff --git a/src/te/rebar.config b/src/te/rebar.config index b7b5e3e69..1063fd38c 100644 --- a/src/te/rebar.config +++ b/src/te/rebar.config @@ -1,15 +1,16 @@ {erl_opts, [debug_info]}. -{deps, [ -]}. +{deps, [grpcbox]}. {shell, [ {config, "config/dev.config"}, - {apps, [tfte]} + {apps, [tfte, tfpb, grpcbox]} ]}. +{project_app_dirs, ["apps/*", "../../proto/src/erlang"]}. + {relx, [ - {release, {tfte, "1.0.0"}, [tfte]}, + {release, {tfte, "1.0.0"}, [tfte, tfpb, grpcbox]}, {vm_args_src, "config/vm.args.src"}, {sys_config_src, "config/sys.config.src"}, {dev_mode, true}, diff --git a/src/te/rebar.lock b/src/te/rebar.lock index 57afcca04..d353eaf34 100644 --- a/src/te/rebar.lock +++ b/src/te/rebar.lock @@ -1 +1,23 @@ -[]. +{"1.2.0", +[{<<"acceptor_pool">>,{pkg,<<"acceptor_pool">>,<<"1.0.0">>},1}, + {<<"chatterbox">>,{pkg,<<"ts_chatterbox">>,<<"0.12.0">>},1}, + {<<"ctx">>,{pkg,<<"ctx">>,<<"0.6.0">>},1}, + {<<"gproc">>,{pkg,<<"gproc">>,<<"0.8.0">>},1}, + {<<"grpcbox">>,{pkg,<<"grpcbox">>,<<"0.15.0">>},0}, + {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}]}. +[ +{pkg_hash,[ + {<<"acceptor_pool">>, <<"43C20D2ACAE35F0C2BCD64F9D2BDE267E459F0F3FD23DAB26485BF518C281B21">>}, + {<<"chatterbox">>, <<"4E54F199E15C0320B85372A24E35554A2CCFC4342E0B7CD8DAED9A04F9B8EF4A">>}, + {<<"ctx">>, <<"8FF88B70E6400C4DF90142E7F130625B82086077A45364A78D208ED3ED53C7FE">>}, + {<<"gproc">>, <<"CEA02C578589C61E5341FCE149EA36CCEF236CC2ECAC8691FBA408E7EA77EC2F">>}, + {<<"grpcbox">>, <<"97C7126296A091602D372EBF5860A04F7BC795B45B33A984CAD2B8E362774FD8">>}, + {<<"hpack">>, <<"17670F83FF984AE6CD74B1C456EDDE906D27FF013740EE4D9EFAA4F1BF999633">>}]}, +{pkg_hash_ext,[ + {<<"acceptor_pool">>, <<"0CBCD83FDC8B9AD2EEE2067EF8B91A14858A5883CB7CD800E6FCD5803E158788">>}, + {<<"chatterbox">>, <<"6478C161BC60244F41CD5847CC3ACCD26D997883E9F7FACD36FF24533B2FA579">>}, + {<<"ctx">>, <<"A14ED2D1B67723DBEBBE423B28D7615EB0BDCBA6FF28F2D1F1B0A7E1D4AA5FC2">>}, + {<<"gproc">>, <<"580ADAFA56463B75263EF5A5DF4C86AF321F68694E7786CB057FD805D1E2A7DE">>}, + {<<"grpcbox">>, <<"161ABE9E17E7D1982EFA6488ADEAA13C3E847A07984A6E6B224E553368918647">>}, + {<<"hpack">>, <<"06F580167C4B8B8A6429040DF36CC93BBA6D571FAEAEC1B28816523379CBB23A">>}]} +]. -- GitLab From de244d299b91cbc8a513e6984f348f63a661a560 Mon Sep 17 00:00:00 2001 From: merle Date: Tue, 6 Sep 2022 04:49:32 -0700 Subject: [PATCH 28/63] Add basic PCE server and virtual testbed setup --- deploy/tfs.sh | 1 + manifests/teservice.yaml | 4 + src/te/Dockerfile | 3 +- src/te/apps/epce/src/epce.app.src | 16 + src/te/apps/epce/src/epce_app.erl | 18 + .../epce/src/epce_pcep_server_handler.erl | 69 +++ src/te/apps/epce/src/epce_server.erl | 238 ++++++++ src/te/apps/epce/src/epce_sup.erl | 37 ++ src/te/apps/tfte/src/tfte.app.src | 3 +- src/te/config/dev.config.template | 10 +- src/te/config/sys.config.src | 8 + src/te/rebar.config | 15 +- src/te/rebar.lock | 21 +- tutorial/2-6-netgen-config.yml | 101 ++++ tutorial/2-6-netgen-topology.yml.template | 545 ++++++++++++++++++ tutorial/2-6-te-demo-start-testbed.sh | 43 ++ tutorial/2-6-te-demo.md | 97 ++++ 17 files changed, 1220 insertions(+), 9 deletions(-) create mode 100644 src/te/apps/epce/src/epce.app.src create mode 100644 src/te/apps/epce/src/epce_app.erl create mode 100644 src/te/apps/epce/src/epce_pcep_server_handler.erl create mode 100644 src/te/apps/epce/src/epce_server.erl create mode 100644 src/te/apps/epce/src/epce_sup.erl create mode 100644 tutorial/2-6-netgen-config.yml create mode 100644 tutorial/2-6-netgen-topology.yml.template create mode 100755 tutorial/2-6-te-demo-start-testbed.sh create mode 100644 tutorial/2-6-te-demo.md diff --git a/deploy/tfs.sh b/deploy/tfs.sh index e6a0c0c10..ddc0d979c 100755 --- a/deploy/tfs.sh +++ b/deploy/tfs.sh @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +set -e ######################################################################################################################## # Read deployment settings diff --git a/manifests/teservice.yaml b/manifests/teservice.yaml index 313acd273..3f7bd1f04 100644 --- a/manifests/teservice.yaml +++ b/manifests/teservice.yaml @@ -73,3 +73,7 @@ spec: protocol: TCP port: 11010 targetPort: 11010 + - name: pcep + protocol: TCP + port: 4189 + targetPort: 4189 diff --git a/src/te/Dockerfile b/src/te/Dockerfile index d9d561d4d..f580ccf4e 100644 --- a/src/te/Dockerfile +++ b/src/te/Dockerfile @@ -17,7 +17,7 @@ # Build stage 0 FROM erlang:24.3-alpine -RUN apk add --no-cache bash +RUN apk add --no-cache bash git RUN mkdir /var/teraflow WORKDIR /var/teraflow @@ -42,6 +42,7 @@ COPY --from=0 /var/teraflow/src/te/_build/prod/rel/tfte /tfte # Expose relevant ports EXPOSE 11010 +EXPOSE 4189 ARG ERLANG_LOGGER_LEVEL_DEFAULT=debug ARG ERLANG_COOKIE_DEFAULT=tfte-unsafe-cookie diff --git a/src/te/apps/epce/src/epce.app.src b/src/te/apps/epce/src/epce.app.src new file mode 100644 index 000000000..92bf06111 --- /dev/null +++ b/src/te/apps/epce/src/epce.app.src @@ -0,0 +1,16 @@ +{application, epce, + [{description, "An Erlang PCE"}, + {vsn, "0.1.0"}, + {registered, []}, + {mod, {epce_app, []}}, + {applications, + [kernel, + stdlib, + pcep_server + ]}, + {env,[]}, + {modules, []}, + + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. diff --git a/src/te/apps/epce/src/epce_app.erl b/src/te/apps/epce/src/epce_app.erl new file mode 100644 index 000000000..022043b35 --- /dev/null +++ b/src/te/apps/epce/src/epce_app.erl @@ -0,0 +1,18 @@ +-module(epce_app). + +-behaviour(application). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Behaviour application functions +-export([start/2, stop/1]). + + +%%% BEHAVIOUR application FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start(_StartType, _StartArgs) -> + epce_sup:start_link(). + +stop(_State) -> + ok. diff --git a/src/te/apps/epce/src/epce_pcep_server_handler.erl b/src/te/apps/epce/src/epce_pcep_server_handler.erl new file mode 100644 index 000000000..210395885 --- /dev/null +++ b/src/te/apps/epce/src/epce_pcep_server_handler.erl @@ -0,0 +1,69 @@ +-module(epce_pcep_server_handler). + +-behaviour(gen_pcep_handler). + + +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include_lib("kernel/include/logger.hrl"). +-include_lib("pcep_codec/include/pcep_codec_te.hrl"). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API functions + +% Behaviour gen_pcep_handler functions +-export([init/1]). +-export([opened/4]). +-export([flow_added/2]). +-export([ready/1]). +-export([request_route/2]). +-export([flow_delegated/2]). +-export([flow_status_changed/3]). +-export([terminate/2]). + + +%%% RECORDS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(state, {}). + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +%%% BEHAVIOUR gen_pcep_handler FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([]) -> + {ok, #{}, #state{}}. + +opened(Id, Caps, Sess, State) -> + case epce_server:session_opened(Id, Caps, Sess) of + ok -> {ok, State}; + {error, Reason} -> {error, Reason} + end. + +flow_added(Flow, State) -> + case epce_server:flow_added(Flow) of + {error, _Reason} = Error -> Error; + ok -> {ok, State} + end. + +ready(State) -> + {ok, State}. + +request_route(RouteReq, State) -> + case epce_server:request_route(RouteReq) of + {error, _Reason} = Error -> Error; + {ok, Route} -> {ok, Route, State} + end. + +flow_delegated(_Flow, State) -> + {ok, State}. + +flow_status_changed(FlowId, NewStatus, State) -> + epce_server:flow_status_changed(FlowId, NewStatus), + {ok, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl new file mode 100644 index 000000000..bd3a13f44 --- /dev/null +++ b/src/te/apps/epce/src/epce_server.erl @@ -0,0 +1,238 @@ +-module(epce_server). + +-behaviour(gen_server). + + +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include_lib("kernel/include/logger.hrl"). +-include_lib("pcep_server/include/pcep_server.hrl"). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API Functions +-export([start_link/0]). +-export([get_flows/0]). +-export([update_flow/2]). + +% Handler Functions +-export([session_opened/3]). +-export([flow_added/1]). +-export([request_route/1]). +-export([flow_status_changed/2]). + +% Behaviour gen_server functions +-export([init/1]). +-export([handle_call/3]). +-export([handle_cast/2]). +-export([handle_continue/2]). +-export([handle_info/2]). +-export([code_change/3]). +-export([terminate/2]). + + +%%% RECORDS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(sess, { + id, + caps, + monref, + pid +}). + +-record(state, { + bouncer, + sessions = #{}, + sess_pids = #{}, + flows = #{} +}). + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +get_flows() -> + gen_server:call(?MODULE, get_flows). + +update_flow(FlowId, LabelStack) -> + gen_server:call(?MODULE, {update_flow, FlowId, LabelStack}). + + +%%% HANDLER FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +session_opened(Id, Caps, Pid) -> + gen_server:call(?MODULE, {session_opened, Id, Caps, Pid}). + +flow_added(Flow) -> + gen_server:call(?MODULE, {flow_added, Flow}). + +request_route(RouteReq) -> + gen_server:call(?MODULE, {request_route, RouteReq}). + +flow_status_changed(FlowId, NewStatus) -> + gen_server:call(?MODULE, {flow_status_changed, FlowId, NewStatus}). + + +%%% BEHAVIOUR gen_server FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([]) -> + {ok, bouncer_start(#state{})}. + +handle_call(get_flows, _From, #state{flows = Flows} = State) -> + {reply, {ok, Flows}, State}; +handle_call({update_flow, FlowId, Labels}, From, + #state{flows = Flows, sessions = SessMap} = State) -> + case maps:find(FlowId, Flows) of + error -> {reply, {error, flow_not_found}, State}; + {ok, #{owner := Owner, route := #{} = R}} -> + case maps:find(Owner, SessMap) of + error -> {reply, {error, session_not_found}, State}; + {ok, #sess{pid = Pid}} -> + #{source := S, destination := D, constraints := C} = R, + ReqRoute = route_from_labels(S, D, C, Labels), + session_update_flow(State, Pid, FlowId, ReqRoute, From), + {noreply, State} + end + end; +handle_call({session_opened, Id, Caps, Pid}, _From, + #state{sessions = SessMap, sess_pids = SessPids} = State) -> + logger:debug("Session with capabilities ~w open to ~w", [Caps, Id]), + case maps:find(Id, SessMap) of + {ok, _} -> {reply, {error, already_opened}, State}; + error -> + MonRef = erlang:monitor(process, Pid), + SessRec = #sess{id = Id, caps = Caps, monref = MonRef, pid = Pid}, + {reply, ok, State#state{ + sessions = SessMap#{Id => SessRec}, + sess_pids = SessPids#{Pid => SessRec} + }} + end; +handle_call({flow_added, #{id := Id, route := Route} = Flow}, + _From, #state{flows = Flows} = State) -> + logger:debug("Flow ~w with route ~w added", [Id, route_to_labels(Route)]), + {reply, ok, State#state{flows = Flows#{Id => Flow}}}; +handle_call({request_route, RouteReq}, _From, State) -> + logger:info("Route from ~w to ~w requested", + [maps:get(source, RouteReq), maps:get(destination, RouteReq)]), + #{source := S, destination := D, constraints := C} = RouteReq, + case compute_path(S, D) of + {error, _Reason} = Error -> + {reply, Error, State}; + {ok, Labels} -> + Route = route_from_labels(S, D, C, Labels), + {reply, {ok, Route}, State} + end; +handle_call({flow_status_changed, FlowId, NewStatus}, _From, + #state{flows = Flows} = State) -> + logger:info("Flow ~w status changed to ~w", [FlowId, NewStatus]), + Flow = maps:get(FlowId, Flows), + {reply, ok, State#state{ + flows = maps:put(FlowId, Flow#{status := NewStatus}, Flows)}}; +handle_call(Request, _From, State) -> + logger:warning("Unexpected request ~w", [Request]), + {reply, {error, unexpected_call}, State}. + + +handle_cast(_Request, State) -> + {noreply, State}. + +handle_continue(_Continue, State) -> + {noreply, State}. + +handle_info({flow_updated, FlowId, NewRoute, From}, + #state{flows = Flows} = State) -> + logger:info("Flow ~w updated to ~w", [FlowId, route_to_labels(NewRoute)]), + case maps:find(FlowId, Flows) of + error -> {noreply, State}; + {ok, Flow} -> + Flows2 = Flows#{FlowId => Flow#{route => NewRoute}}, + gen_server:reply(From, ok), + {noreply, State#state{flows = Flows2}} + end; +handle_info({flow_update_error, FlowId, Reason, From}, State) -> + logger:error("Flow ~w updated error: ~w", [FlowId, Reason]), + gen_server:reply(From, {error, Reason}), + {noreply, State}; +handle_info({'DOWN', MonRef, process, Pid, _Reason}, + #state{sessions = SessMap, sess_pids = PidMap} = State) -> + case maps:take(Pid, PidMap) of + {#sess{id = Id, monref = MonRef}, PidMap2} -> + SessMap2 = maps:remove(Id, SessMap), + %TODO: Do something about the flows from this session ? + {noreply, State#state{ + sessions = SessMap2, + sess_pids = PidMap2 + }}; + _X -> + {noreply, State} + end; +handle_info(_Info, State) -> + {noreply, State}. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +terminate(_Reason, _State) -> + ok. + + +%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +compute_path({1, 1, 1, 1}, {6, 6, 6, 6}) -> + {ok, [16020, 16040, 16060]}; +compute_path({6, 6, 6, 6}, {1, 1, 1, 1}) -> + {ok, [16040, 16020, 16010]}; +compute_path(_Src, _Dst) -> + {error, nopath}. + +route_from_labels(Source, Destination, Constraints, Labels) -> + #{ + source => Source, + destination => Destination, + constraints => Constraints, + steps => [ + #{ + is_loose => false, + nai_type => absent, + sid => #mpls_stack_entry{label = L} + } + || L <- Labels + ] + }. + +route_to_labels(#{steps := Steps}) -> + [Sid#mpls_stack_entry.label || #{sid := Sid} <- Steps]. + + +%-- Session Interface Functions ------------------------------------------------ + +session_update_flow(#state{bouncer = Pid}, SessPid, FlowId, Route, Args) -> + Pid ! {update_flow, SessPid, FlowId, Route, Args}. + +bouncer_start(#state{bouncer = undefined} = State) -> + Self = self(), + Pid = erlang:spawn_link(fun() -> + bouncer_bootstrap(Self) + end), + receive bouncer_ready -> ok end, + State#state{bouncer = Pid}. + +bouncer_bootstrap(Parent) -> + Parent ! bouncer_ready, + bouncer_loop(Parent). + +bouncer_loop(Parent) -> + receive + {update_flow, SessPid, FlowId, ReqRoute, Args} -> + case pcep_server_session:update_flow(SessPid, FlowId, ReqRoute) of + {ok, NewRoute} -> + Parent ! {flow_updated, FlowId, NewRoute, Args}, + bouncer_loop(Parent); + {error, Reason} -> + Parent ! {flow_update_error, FlowId, Reason, Args}, + bouncer_loop(Parent) + end + end. diff --git a/src/te/apps/epce/src/epce_sup.erl b/src/te/apps/epce/src/epce_sup.erl new file mode 100644 index 000000000..21a6a56b2 --- /dev/null +++ b/src/te/apps/epce/src/epce_sup.erl @@ -0,0 +1,37 @@ +-module(epce_sup). + +-behaviour(supervisor). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Behaviour supervisor functions +-export([start_link/0]). +-export([init/1]). + + +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(SERVER, ?MODULE). +-define(PCE_SERVER, epce_server). + + +%%% BEHAVIOUR SUPERVISOR FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +init([]) -> + SupFlags = #{ + strategy => one_for_all, + intensity => 0, + period => 1 + }, + ServerSpec = #{ + id => ?PCE_SERVER, + start => {?PCE_SERVER, start_link, []}, + restart => permanent, + shutdown => brutal_kill + }, + {ok, {SupFlags, [ServerSpec]}}. + diff --git a/src/te/apps/tfte/src/tfte.app.src b/src/te/apps/tfte/src/tfte.app.src index 76d397939..06adeb848 100644 --- a/src/te/apps/tfte/src/tfte.app.src +++ b/src/te/apps/tfte/src/tfte.app.src @@ -6,7 +6,8 @@ {applications, [kernel, stdlib, - tfpb + tfpb, + epce ]}, {env,[]}, {modules, []}, diff --git a/src/te/config/dev.config.template b/src/te/config/dev.config.template index 658bf13f8..5897c4e46 100644 --- a/src/te/config/dev.config.template +++ b/src/te/config/dev.config.template @@ -1,9 +1,17 @@ [ {tfte, [ {services, [ - {te, [{http, "localhost", 11010, []}], #{}}, + {te, [{http, "localhost", 11010, []}], #{}} + ]} + ]}, + + {pcep_server, [ + {handler, {epce_pcep_server_handler, []}} ]}, + {epce, [ + ]}, + {grpcbox, [ {servers, [#{ grpc_opts => #{ diff --git a/src/te/config/sys.config.src b/src/te/config/sys.config.src index 2bd5cf178..93bfbafb1 100644 --- a/src/te/config/sys.config.src +++ b/src/te/config/sys.config.src @@ -1,4 +1,5 @@ [ + {tfte, [ {services, [ {te, [ @@ -25,6 +26,13 @@ ]} ]}, + {pcep_server, [ + {handler, {epce_pcep_server_handler, []}} + ]}, + + {epce, [ + ]}, + {grpcbox, [ {servers, [#{ grpc_opts => #{ diff --git a/src/te/rebar.config b/src/te/rebar.config index 1063fd38c..3451955c2 100644 --- a/src/te/rebar.config +++ b/src/te/rebar.config @@ -1,16 +1,25 @@ {erl_opts, [debug_info]}. -{deps, [grpcbox]}. +{deps, [ + grpcbox, + {pcep_server, {git, "git@github.com:stritzinger/pcep_server.git", {branch, "master"}}} +]}. {shell, [ {config, "config/dev.config"}, - {apps, [tfte, tfpb, grpcbox]} + {apps, [epce, tfte, tfpb, grpcbox]} ]}. {project_app_dirs, ["apps/*", "../../proto/src/erlang"]}. {relx, [ - {release, {tfte, "1.0.0"}, [tfte, tfpb, grpcbox]}, + {release, {tfte, "1.0.0"}, [ + runtime_tools, + epce, + grpcbox, + tfpb, + tfte + ]}, {vm_args_src, "config/vm.args.src"}, {sys_config_src, "config/sys.config.src"}, {dev_mode, true}, diff --git a/src/te/rebar.lock b/src/te/rebar.lock index d353eaf34..6eb067ecd 100644 --- a/src/te/rebar.lock +++ b/src/te/rebar.lock @@ -1,10 +1,23 @@ {"1.2.0", [{<<"acceptor_pool">>,{pkg,<<"acceptor_pool">>,<<"1.0.0">>},1}, {<<"chatterbox">>,{pkg,<<"ts_chatterbox">>,<<"0.12.0">>},1}, + {<<"codec_sequencer">>, + {git,"git@github.com:stritzinger/codec_sequencer.git", + {ref,"fc8760894f7962ef1497bf6ce4247eb75db9d5ca"}}, + 2}, {<<"ctx">>,{pkg,<<"ctx">>,<<"0.6.0">>},1}, {<<"gproc">>,{pkg,<<"gproc">>,<<"0.8.0">>},1}, {<<"grpcbox">>,{pkg,<<"grpcbox">>,<<"0.15.0">>},0}, - {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}]}. + {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}, + {<<"pcep_codec">>, + {git,"git@github.com:stritzinger/pcep_codec.git", + {ref,"3d1623fdf0c62d3daf400ac65aaf985f8bd40835"}}, + 1}, + {<<"pcep_server">>, + {git,"git@github.com:stritzinger/pcep_server.git", + {ref,"2cf692e7e5fa2e9ac0fd54e5aa64ffb17f4f1b4a"}}, + 0}, + {<<"ranch">>,{pkg,<<"ranch">>,<<"2.0.0">>},1}]}. [ {pkg_hash,[ {<<"acceptor_pool">>, <<"43C20D2ACAE35F0C2BCD64F9D2BDE267E459F0F3FD23DAB26485BF518C281B21">>}, @@ -12,12 +25,14 @@ {<<"ctx">>, <<"8FF88B70E6400C4DF90142E7F130625B82086077A45364A78D208ED3ED53C7FE">>}, {<<"gproc">>, <<"CEA02C578589C61E5341FCE149EA36CCEF236CC2ECAC8691FBA408E7EA77EC2F">>}, {<<"grpcbox">>, <<"97C7126296A091602D372EBF5860A04F7BC795B45B33A984CAD2B8E362774FD8">>}, - {<<"hpack">>, <<"17670F83FF984AE6CD74B1C456EDDE906D27FF013740EE4D9EFAA4F1BF999633">>}]}, + {<<"hpack">>, <<"17670F83FF984AE6CD74B1C456EDDE906D27FF013740EE4D9EFAA4F1BF999633">>}, + {<<"ranch">>, <<"FBF3D79661C071543256F9051CAF19D65DAA6DF1CF6824D8F37A49B19A66F703">>}]}, {pkg_hash_ext,[ {<<"acceptor_pool">>, <<"0CBCD83FDC8B9AD2EEE2067EF8B91A14858A5883CB7CD800E6FCD5803E158788">>}, {<<"chatterbox">>, <<"6478C161BC60244F41CD5847CC3ACCD26D997883E9F7FACD36FF24533B2FA579">>}, {<<"ctx">>, <<"A14ED2D1B67723DBEBBE423B28D7615EB0BDCBA6FF28F2D1F1B0A7E1D4AA5FC2">>}, {<<"gproc">>, <<"580ADAFA56463B75263EF5A5DF4C86AF321F68694E7786CB057FD805D1E2A7DE">>}, {<<"grpcbox">>, <<"161ABE9E17E7D1982EFA6488ADEAA13C3E847A07984A6E6B224E553368918647">>}, - {<<"hpack">>, <<"06F580167C4B8B8A6429040DF36CC93BBA6D571FAEAEC1B28816523379CBB23A">>}]} + {<<"hpack">>, <<"06F580167C4B8B8A6429040DF36CC93BBA6D571FAEAEC1B28816523379CBB23A">>}, + {<<"ranch">>, <<"C20A4840C7D6623C19812D3A7C828B2F1BD153EF0F124CB69C54FE51D8A42AE0">>}]} ]. diff --git a/tutorial/2-6-netgen-config.yml b/tutorial/2-6-netgen-config.yml new file mode 100644 index 000000000..7f1de319f --- /dev/null +++ b/tutorial/2-6-netgen-config.yml @@ -0,0 +1,101 @@ +# Directory used for Netgen's operation. +# Default: "/tmp/netgen" +# netgen_runstatedir: + +# Clean exit. +# Default: 'false' +# clean_exit: + +# Valgrind parameters. +# Default: "--tool=memcheck" +# valgrind_params: "--tool=memcheck --leak-check=full --trace-children=yes" +# valgrind_params: "--tool=memcheck --leak-check=full" +# valgrind_params: "--tool=memcheck --leak-check=full --show-leak-kinds=all" +# valgrind_params: "--tool=callgrind --dump-instr=yes --collect-jumps=yes" + +# Perf directory +# Default: [netgen_runstatedir]/perf +# perf_dir: + +# Plugins configuration. +plugins: + frr: + # FRR's sysconfdir (--sysconfdir). + # Default: "/etc/frr" + # sysconfdir: + + # FRR's localstatedir (--localstatedir). + # Default: "/var/run/frr" + # localstatedir: + + # FRR's user (--enable-user). + # Default: "frr" + # user: + user: "root" + + # FRR's group (--enable-group). + # Default: "frr" + # group: + group: "root" + + # Directory to store FRR logs. + # Default: [netgen_runstatedir]/frrlogs + # logdir: + + tcpdump: + # Directory to store tcpdump captures. + # Default: [netgen_runstatedir]/pcaps + # pcap_dir: + + # Filter on which nodes tcpdump should run. + # Default: [] + # whitelist: + + # Filter on which nodes tcpdump should not run. + # Default: [] + # blacklist: + + tmux: + # Path of tmux script used to open a shell on all routers. + # Default: [netgen_runstatedir]/tmux.sh + # file: + + # Panels per node. + # Default: 1 + # panels-per-node: + + bird: + # BIRD's sysconfdir (--sysconfdir). + # Default: "/etc/bird" + # sysconfdir: + + # BIRD's localstatedir (--localstatedir). + # Default: "/var/run/bird" + # localstatedir: + + # BIRD's user (--enable-user). + # Default: "bird" + # user: + + # BIRD's group (--enable-group). + # Default: "bird" + # group: + + # Directory to store BIRD logs. + # Default: [netgen_runstatedir]/birdlogs + # logdir: + + bgpsimple: + # Path to bgp_simple script + # Default: "bgp_simple.pl" + # path: + + iou: + # IOU working directory. + # Default: [netgen_runstatedir]/iou + # dir: + + dynamips: + # dynamips working directory. + # Default: [netgen_runstatedir]/dynamips + # dir: diff --git a/tutorial/2-6-netgen-topology.yml.template b/tutorial/2-6-netgen-topology.yml.template new file mode 100644 index 000000000..286f40005 --- /dev/null +++ b/tutorial/2-6-netgen-topology.yml.template @@ -0,0 +1,545 @@ +# +# +---------+ +# | | +# | SRC | +# | 9.9.9.1 | +# | | +# +---------+ +# |eth-rt1 (.1) +# | +# |10.0.10.0/24 +# | +# |eth-src (.2) +# +---------+ . +# | | . +# | RT1 |eth-rt1-pce (???) . +# | 1.1.1.1 +----------------------------------+ +# | 16010 | . ??? | +# +---------+ . | +# |eth-sw1 . | +# | . | +# | . | +# | . | +# +---------+ | +---------+ . | +# | | | | | . | +# | RT2 |eth-sw1 | eth-sw1| RT3 | . | +# | 2.2.2.2 +----------+----------+ 3.3.3.3 | . | +# | 16020 | 10.0.1.0/24 | 16030 | . | +# +---------+ +---------+ .eth-pce-rt1|(???) +# eth-rt4-1| |eth-rt4-2 eth-rt5-1| |eth-rt5-2. +----+----+ +# | | | | . | | +# 10.0.2.0/24| |10.0.3.0/24 10.0.4.0/24| |10.0.5.0/24 | PCE | +# | | | | . | ???? | +# eth-rt2-1| |eth-rt2-2 eth-rt3-1| |eth-rt3-2. | | +# +---------+ +---------+ . +----+----+ +# | | | | .eth-pce-rt6|(???) +# | RT4 | 10.0.6.0/24 | RT5 | . | +# | 4.4.4.4 +---------------------+ 5.5.5.5 | . | +# | 16040 |eth-rt5 eth-rt4| 16050 | . | +# +---------+ +---------+ . | +# eth-rt6| |eth-rt6 . | +# | | . | +# 10.0.7.0/24| |10.0.8.0/24 | +# | +---------+ | . | +# +----------| |-----------+ . | +# eth-rt4| RT6 |eth-rt5 . | +# | 6.6.6.6 |eth-rt6-pce (????) . ???? | +# | 16060 +----------------------------------+ +# +---------+ . +# |eth-dst (.1) . +# | . +# |10.0.11.0/24 +# | +# |eth-rt6 (.2) +# +---------+ +# | | +# | DST | +# | 9.9.9.2 | +# | | +# +---------+ +# + +--- + +routers: + + src: + links: + lo: + ipv4: 9.9.9.1/32 + ipv6: 2001:db8:1066::1/128 + mpls: yes + eth-rt1: + peer: [rt1, eth-src] + ipv4: 10.0.10.1/24 + mpls: yes + frr: + zebra: + run: yes + config: + shell: | + ip route add 9.9.9.2/32 encap mpls 1111 via inet 10.0.10.2 src 9.9.9.1 + + rt1: + links: + lo: + ipv4: 1.1.1.1/32 + mpls: yes + eth-sw1: + peer: [sw1, sw1-rt1] + ipv4: 10.0.1.1/24 + mpls: yes + eth-src: + peer: [src, eth-rt1] + ipv4: 10.0.10.2/24 + mpls: yes + frr: + zebra: + run: yes + config: + pathd: + args: "-M pathd_pcep" + config: | + debug pathd pcep basic + segment-routing + traffic-eng + mpls-te on + policy color 1 endpoint 6.6.6.6 + name DEFAULT + binding-sid 1111 + candidate-path preference 100 name RUNTIME dynamic + ! + ! + pcep + pce-config CONFIG + source-address ip 1.1.1.1 + pce PCE + address ip ${PCE_IP} + config CONFIG + pcc + peer PCE + ! + isisd: + run: yes + config: | + interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive + ! + interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + ! + router isis 1 + net 49.0000.0000.0000.0001.00 + is-type level-1 + redistribute ipv4 static level-1 + redistribute ipv4 connected level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.1/32 index 10 explicit-null + segment-routing prefix 2001:db8:1000::1/128 index 11 explicit-null + ! + shell: | + ip route add 9.9.9.1/32 dev eth-src + ip link add eth-rt1-pce type veth peer name eth-pce-rt1 + ip addr add ${RT1_PCE_INT_IF_IP}/24 dev eth-rt1-pce + ip link set eth-pce-rt1 netns ${PCE_NETNS} + ip -n ${PCE_NETNS} addr add ${RT1_PCE_EXT_IF_IP}/24 dev eth-pce-rt1 + ip link set eth-rt1-pce up + ip -n ${PCE_NETNS} link set eth-pce-rt1 up + ip route add ${RT1_PCE_EXT_IF_IP}/24 via ${RT1_PCE_INT_IF_IP} dev eth-rt1-pce src 1.1.1.1 + ip -n ${PCE_NETNS} route add ${RT1_PCE_INT_IF_IP}/32 via ${RT1_PCE_EXT_IF_IP} dev eth-pce-rt1 + ip -n ${PCE_NETNS} route add 1.1.1.1/32 via ${RT1_PCE_EXT_IF_IP} dev eth-pce-rt1 + + rt2: + links: + lo: + ipv4: 2.2.2.2/32 + ipv6: 2001:db8:1000::2/128 + mpls: yes + eth-sw1: + peer: [sw1, sw1-rt2] + ipv4: 10.0.1.2/24 + mpls: yes + eth-rt4-1: + peer: [rt4, eth-rt2-1] + ipv4: 10.0.2.2/24 + mpls: yes + eth-rt4-2: + peer: [rt4, eth-rt2-2] + ipv4: 10.0.3.2/24 + mpls: yes + frr: + zebra: + run: yes + config: + isisd: + run: yes + config: | + interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive + ! + interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + ! + interface eth-rt4-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt4-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + router isis 1 + net 49.0000.0000.0000.0002.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 2.2.2.2/32 index 20 no-php-flag + segment-routing prefix 2001:db8:1000::2/128 index 21 no-php-flag + ! + + rt3: + links: + lo: + ipv4: 3.3.3.3/32 + ipv6: 2001:db8:1000::3/128 + mpls: yes + eth-sw1: + peer: [sw1, sw1-rt3] + ipv4: 10.0.1.3/24 + mpls: yes + eth-rt5-1: + peer: [rt5, eth-rt3-1] + ipv4: 10.0.4.3/24 + mpls: yes + eth-rt5-2: + peer: [rt5, eth-rt3-2] + ipv4: 10.0.5.3/24 + mpls: yes + frr: + zebra: + run: yes + config: + isisd: + run: yes + config: | + interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive + ! + interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + ! + interface eth-rt5-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt5-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + router isis 1 + net 49.0000.0000.0000.0003.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 3.3.3.3/32 index 30 no-php-flag + segment-routing prefix 2001:db8:1000::3/128 index 31 no-php-flag + ! + + rt4: + links: + lo: + ipv4: 4.4.4.4/32 + ipv6: 2001:db8:1000::4/128 + mpls: yes + eth-rt2-1: + peer: [rt2, eth-rt4-1] + ipv4: 10.0.2.4/24 + mpls: yes + eth-rt2-2: + peer: [rt2, eth-rt4-2] + ipv4: 10.0.3.4/24 + mpls: yes + eth-rt5: + peer: [rt5, eth-rt4] + ipv4: 10.0.6.4/24 + mpls: yes + eth-rt6: + peer: [rt6, eth-rt4] + ipv4: 10.0.7.4/24 + mpls: yes + frr: + zebra: + run: yes + config: + isisd: + run: yes + config: | + interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive + ! + interface eth-rt2-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt2-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt6 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + router isis 1 + net 49.0000.0000.0000.0004.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 4.4.4.4/32 index 40 no-php-flag + segment-routing prefix 2001:db8:1000::4/128 index 41 no-php-flag + ! + + rt5: + links: + lo: + ipv4: 5.5.5.5/32 + ipv6: 2001:db8:1000::5/128 + mpls: yes + eth-rt3-1: + peer: [rt3, eth-rt5-1] + ipv4: 10.0.4.5/24 + mpls: yes + eth-rt3-2: + peer: [rt3, eth-rt5-2] + ipv4: 10.0.5.5/24 + mpls: yes + eth-rt4: + peer: [rt4, eth-rt5] + ipv4: 10.0.6.5/24 + mpls: yes + eth-rt6: + peer: [rt6, eth-rt5] + ipv4: 10.0.8.5/24 + mpls: yes + frr: + zebra: + run: yes + config: + isisd: + run: yes + config: | + interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive + ! + interface eth-rt3-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt3-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt6 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + router isis 1 + net 49.0000.0000.0000.0005.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 5.5.5.5/32 index 50 no-php-flag + segment-routing prefix 2001:db8:1000::5/128 index 51 no-php-flag + ! + + rt6: + links: + lo: + ipv4: 6.6.6.6/32 + ipv6: 2001:db8:1000::6/128 + mpls: yes + eth-rt4: + peer: [rt4, eth-rt6] + ipv4: 10.0.7.6/24 + mpls: yes + eth-rt5: + peer: [rt5, eth-rt6] + ipv4: 10.0.8.6/24 + mpls: yes + eth-dst: + peer: [dst, eth-rt6] + ipv4: 10.0.11.1/24 + mpls: yes + frr: + zebra: + run: yes + config: + pathd: + args: "-M pathd_pcep" + config: | + debug pathd pcep + segment-routing + traffic-eng + policy color 1 endpoint 1.1.1.1 + name DEFAULT + binding-sid 6666 + candidate-path preference 200 name RUNTIME dynamic + ! + ! + pcep + pce-config CONFIG + source-address ip 6.6.6.6 + pce PCE + address ip ${PCE_IP} + config CONFIG + pcc + peer PCE + ! + isisd: + run: yes + config: | + interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive + ! + interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 + ! + router isis 1 + net 49.0000.0000.0000.0006.00 + is-type level-1 + redistribute ipv4 static level-1 + redistribute ipv4 connected level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 6.6.6.6/32 index 60 explicit-null + segment-routing prefix 2001:db8:1000::6/128 index 61 explicit-null + ! + shell: | + ip route add 9.9.9.2/32 dev eth-dst + ip link add eth-rt6-pce type veth peer name eth-pce-rt6 + ip addr add ${RT6_PCE_INT_IF_IP}/24 dev eth-rt6-pce + ip link set eth-pce-rt6 netns ${PCE_NETNS} + ip -n ${PCE_NETNS} addr add ${RT6_PCE_EXT_IF_IP}/24 dev eth-pce-rt6 + ip link set eth-rt6-pce up + ip -n ${PCE_NETNS} link set eth-pce-rt6 up + ip route add ${RT6_PCE_EXT_IF_IP}/24 via ${RT6_PCE_INT_IF_IP} dev eth-rt6-pce src 6.6.6.6 + ip -n ${PCE_NETNS} route add ${RT6_PCE_INT_IF_IP}/32 via ${RT6_PCE_EXT_IF_IP} dev eth-pce-rt6 + ip -n ${PCE_NETNS} route add 6.6.6.6/32 via ${RT6_PCE_EXT_IF_IP} dev eth-pce-rt6 + + dst: + links: + lo: + ipv4: 9.9.9.2/32 + ipv6: 2001:db8:1066::2/128 + mpls: yes + eth-rt6: + peer: [rt6, eth-dst] + ipv4: 10.0.11.2/24 + mpls: yes + frr: + zebra: + run: yes + config: + shell: | + ip route add 9.9.9.1/32 encap mpls 6666 via inet 10.0.11.1 + +switches: + sw1: + links: + sw1-rt1: + peer: [rt1, rt1-sw1] + sw1-rt2: + peer: [rt2, rt2-sw1] + sw1-rt3: + peer: [rt3, rt3-sw1] + +frr: + #perf: yes + #valgrind: yes + base-configs: + all: | + hostname %(node) + password 1 + log file %(logdir)/%(node)-%(daemon).log + log commands + zebra: | + debug zebra kernel + debug zebra packet + debug zebra mpls + isisd: | + debug isis events + debug isis route-events + debug isis spf-events + debug isis sr-events + debug isis lsp-gen diff --git a/tutorial/2-6-te-demo-start-testbed.sh b/tutorial/2-6-te-demo-start-testbed.sh new file mode 100755 index 000000000..dea41a71a --- /dev/null +++ b/tutorial/2-6-te-demo-start-testbed.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +set -e + +ROOTDIR="$( cd $( dirname $0 ); pwd )" +RUNDIR="$( pwd )" +NEGENDIR="${RUNDIR}/netgen" + +if [[ ! -f "${NEGENDIR}/exe/netgen" ]]; then + echo "Failed to find Netgen binary at ${NEGENDIR}/exe/netgen" + exit 1 +fi + +export PCE_NETNS="$1" +export PCE_IP="$2" +RT1_INT_IP="$3" +RT1_EXT_IP="$4" +RT6_INT_IP="$5" +RT6_EXT_IP="$6" + + +if [[ -z $PCE_NETNS || -z $PCE_IP || -z RT1_INT_IP || -z RT1_EXT_IP || -z RT6_INT_IP || -z RT6_EXT_IP ]]; then + echo "USAGE: $0 PCE_NETNS PCE_IP RT1_INT_IP RT1_EXT_IP RT6_INT_IP RT6_EXT_IP" + echo " e.g: $0 cni-588a2d06-e64f-907b-d51b-bed0307007c9 10.1.103.133 10 11 12 13" + exit 1 +fi + +IFS=. read PCE_IP1 PCE_IP2 PCE_IP3 PCE_IP4 <<< "$PCE_IP" + +export RT1_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT1_INT_IP" +export RT1_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT1_EXT_IP" +export RT6_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT6_INT_IP" +export RT6_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT6_EXT_IP" + +cp "${ROOTDIR}/2-6-netgen-config.yml" "${RUNDIR}/config.yml" +cat "${ROOTDIR}/2-6-netgen-topology.yml.template" | envsubst > "${RUNDIR}/topology.yml" + +sudo -i bash -c "\ + cd ${RUNDIR}/netgen;\ + sysctl -w net.ipv6.conf.all.disable_ipv6=1;\ + sysctl -w net.ipv6.conf.default.disable_ipv6=1;\ + sysctl -w net.ipv4.conf.all.rp_filter=0;\ + PATH=/usr/lib/frr:\$PATH ./exe/netgen ../topology.yml -c ../config.yml" diff --git a/tutorial/2-6-te-demo.md b/tutorial/2-6-te-demo.md new file mode 100644 index 000000000..6a99007b4 --- /dev/null +++ b/tutorial/2-6-te-demo.md @@ -0,0 +1,97 @@ +# 2.6. Traffic Engineering Demo (PENDING) + +## Setup Test-Bed + +### Setup libyang + + $ sudo apt update + $ sudo apt-get install cmake libpcre2-dev git make build-essential + $ mkdir -p ~/testbed + $ cd ~/testbed + $ git clone git@github.com:CESNET/libyang.git + $ cd libyang + $ git checkout v2.0.0 + $ mkdir build; cd build + $ cmake -D CMAKE_INSTALL_PREFIX:PATH=/usr -D CMAKE_BUILD_TYPE:String="Release" .. + $ make + $ sudo make install + + +### Setup Free Range Routing + + $ sudo apt update + $ sudo apt-get install git autoconf automake libtool make libreadline-dev texinfo pkg-config libpam0g-dev libjson-c-dev bison flex libc-ares-dev python3-dev python3-sphinx install-info build-essential libsnmp-dev perl libcap-dev python2 libelf-dev libunwind-dev protobuf-c-compiler libprotobuf-c-dev libsystemd-dev + $ mkdir -p ~/testbed + $ cd ~/testbed + $ git clone git@github.com:opensourcerouting/frr.git + $ cd frr + $ curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py + $ sudo python2 ./get-pip.py + $ export CFLAGS="-I /usr/local/include -g -O2" + $ sudo rm -rf /usr/lib/frr + $ sudo rm -rf /var/run/frr + $ sudo mkdir -p /etc/frr + $ sudo mkdir -p /var/run/frr + $ sudo chown -R root:root /etc/frr + $ ./bootstrap.sh + $ ./configure \ + --prefix=/usr \ + --includedir=\${prefix}/include \ + --enable-exampledir=\${prefix}/share/doc/frr/examples \ + --bindir=\${prefix}/bin \ + --sbindir=\${prefix}/lib/frr \ + --libdir=\${prefix}/lib/frr \ + --libexecdir=\${prefix}/lib/frr \ + --localstatedir=/var/run/frr \ + --sysconfdir=/etc/frr \ + --with-moduledir=\${prefix}/lib/frr/modules \ + --enable-configfile-mask=0640 \ + --enable-logfile-mask=0640 \ + --enable-snmp=agentx \ + --enable-multipath=64 \ + --enable-user=root \ + --enable-group=root \ + --enable-vty-group=root \ + --enable-vtysh \ + --with-pkg-git-version \ + --with-pkg-extra-version=-MyOwnFRRVersion \ + --enable-systemd=yes \ + --enable-config-rollbacks \ + --enable-pathd \ + --enable-pcep + $ make + $ sudo make install + + +### Setup NetGen + + $ sudo apt update + $ sudo apt-get install git ruby ruby-dev tmux gettext-base + $ mkdir -p ~/testbed + $ cd ~/testbed + $ git clone git@github.com:sylane/netgen.git + $ cd netgen + $ git checkout teraflow + $ sudo gem install bundler:1.15 + $ bundle _1.15_ install + + +### Run the Test-Bed + +To start the testbed, we need to figure out the IP of the TE service: + $ kubectl --namespace tfs get $(kubectl --namespace tfs get pods --selector=app=teservice -o name) --template '{{.status.podIP}}' + +And select the network namespace that defines it by looking into each of them until you find the correct one: + $ ip netns list + $ sudo ip -n XXX addr list + +When we have the IP and namespace we can start the testbed. + +In first console: + $ cd ~/testbed + $ ../tfs-ctrl/tutorial/2-6-te-demo-start-testbed.sh NETWORK_NAMESPACE TE_SERVICE_IP 10 11 12 13 + +Then in second console: + $ sudo -i + # cd /tmp/negen + # ./tmux.sh \ No newline at end of file -- GitLab From b428cc8ab950d087d06a5306310977f9e39eddec Mon Sep 17 00:00:00 2001 From: merle Date: Fri, 23 Sep 2022 09:37:50 -0700 Subject: [PATCH 29/63] Retrieve the topology from Context service and add a TED - Add _connect/mpls_label and _connect/pcc_address to the allowed device configuration. --- proto/context.proto | 1 + .../database/models/enums/ServiceType.py | 1 + src/te/apps/epce/src/epce_server.erl | 16 +- src/te/apps/epce/src/epce_sup.erl | 18 +- src/te/apps/epce/src/epce_ted.erl | 203 +++++++++ src/te/apps/tfte/src/tfte_app.erl | 10 +- src/te/apps/tfte/src/tfte_context.erl | 162 +++++++ src/te/apps/tfte/src/tfte_server.erl | 100 +++++ src/te/apps/tfte/src/tfte_service_sup.erl | 50 +++ src/te/apps/tfte/src/tfte_sup.erl | 24 +- src/te/apps/tfte/src/tfte_te_service.erl | 6 +- src/te/apps/tfte/src/tfte_topology.erl | 394 ++++++++++++++++++ src/te/config/sys.config.src | 4 +- tutorial/2-6-te-demo-start-testbed.sh | 36 +- tutorial/2-6-te-demo.md | 14 +- tutorial/2-6-teraflow-topology.json | 210 ++++++++++ 16 files changed, 1196 insertions(+), 53 deletions(-) create mode 100644 src/te/apps/epce/src/epce_ted.erl create mode 100644 src/te/apps/tfte/src/tfte_context.erl create mode 100644 src/te/apps/tfte/src/tfte_server.erl create mode 100644 src/te/apps/tfte/src/tfte_service_sup.erl create mode 100644 src/te/apps/tfte/src/tfte_topology.erl create mode 100644 tutorial/2-6-teraflow-topology.json diff --git a/proto/context.proto b/proto/context.proto index 9f779d8db..55a80470d 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -273,6 +273,7 @@ enum ServiceTypeEnum { SERVICETYPE_L3NM = 1; SERVICETYPE_L2NM = 2; SERVICETYPE_TAPI_CONNECTIVITY_SERVICE = 3; + SERVICETYPE_TE = 4; } enum ServiceStatusEnum { diff --git a/src/context/service/database/models/enums/ServiceType.py b/src/context/service/database/models/enums/ServiceType.py index 668133abf..3937eaa11 100644 --- a/src/context/service/database/models/enums/ServiceType.py +++ b/src/context/service/database/models/enums/ServiceType.py @@ -21,6 +21,7 @@ class ORM_ServiceTypeEnum(enum.Enum): L3NM = ServiceTypeEnum.SERVICETYPE_L3NM L2NM = ServiceTypeEnum.SERVICETYPE_L2NM TAPI_CONNECTIVITY_SERVICE = ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE + TE = ServiceTypeEnum.SERVICETYPE_TE grpc_to_enum__service_type = functools.partial( grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum) diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl index bd3a13f44..11a1625ef 100644 --- a/src/te/apps/epce/src/epce_server.erl +++ b/src/te/apps/epce/src/epce_server.erl @@ -181,12 +181,16 @@ terminate(_Reason, _State) -> %%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -compute_path({1, 1, 1, 1}, {6, 6, 6, 6}) -> - {ok, [16020, 16040, 16060]}; -compute_path({6, 6, 6, 6}, {1, 1, 1, 1}) -> - {ok, [16040, 16020, 16010]}; -compute_path(_Src, _Dst) -> - {error, nopath}. +compute_path(From, To) -> + case epce_ted:compute_path(pcc_address, From, To) of + {ok, Devices} -> + Labels = tl([L || #{mpls_label := L} <- Devices, L =/= undefined]), + logger:debug("Route from ~p to ~p: ~p", [From, To, Labels]), + {ok, Labels}; + {error, Reason} -> + logger:warning("Failed to find a route from ~p to ~p", [From, To]), + {error, Reason} + end. route_from_labels(Source, Destination, Constraints, Labels) -> #{ diff --git a/src/te/apps/epce/src/epce_sup.erl b/src/te/apps/epce/src/epce_sup.erl index 21a6a56b2..f04e9697e 100644 --- a/src/te/apps/epce/src/epce_sup.erl +++ b/src/te/apps/epce/src/epce_sup.erl @@ -12,14 +12,14 @@ %%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --define(SERVER, ?MODULE). --define(PCE_SERVER, epce_server). +-define(TED_WORKER, epce_ted). +-define(PCE_WORKER, epce_server). %%% BEHAVIOUR SUPERVISOR FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start_link() -> - supervisor:start_link({local, ?SERVER}, ?MODULE, []). + supervisor:start_link({local, ?MODULE}, ?MODULE, []). init([]) -> SupFlags = #{ @@ -27,11 +27,17 @@ init([]) -> intensity => 0, period => 1 }, + TEDSpec = #{ + id => ?TED_WORKER, + start => {?TED_WORKER, start_link, []}, + restart => permanent, + shutdown => brutal_kill + }, ServerSpec = #{ - id => ?PCE_SERVER, - start => {?PCE_SERVER, start_link, []}, + id => ?PCE_WORKER, + start => {?PCE_WORKER, start_link, []}, restart => permanent, shutdown => brutal_kill }, - {ok, {SupFlags, [ServerSpec]}}. + {ok, {SupFlags, [TEDSpec, ServerSpec]}}. diff --git a/src/te/apps/epce/src/epce_ted.erl b/src/te/apps/epce/src/epce_ted.erl new file mode 100644 index 000000000..aaf5a4e9a --- /dev/null +++ b/src/te/apps/epce/src/epce_ted.erl @@ -0,0 +1,203 @@ +-module(epce_ted). + +-behaviour(gen_server). + + +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include_lib("kernel/include/logger.hrl"). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API Functions +-export([start_link/0]). +-export([device_added/2]). +-export([device_updated/2]). +-export([device_deleted/1]). +-export([link_added/2]). +-export([link_updated/2]). +-export([link_deleted/1]). +-export([compute_path/3]). + +-export([get_graph/0]). + +% Behaviour gen_server functions +-export([init/1]). +-export([handle_call/3]). +-export([handle_cast/2]). +-export([handle_continue/2]). +-export([handle_info/2]). +-export([code_change/3]). +-export([terminate/2]). + + +%%% RECORDS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(state, { + graph :: diagraph:graph(), + pcc_address_to_id = #{} :: map() +}). + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +device_added(Id, Device) -> + gen_server:call(?MODULE, {device_added, Id, Device}). + +device_updated(Id, Device) -> + gen_server:call(?MODULE, {device_updated, Id, Device}). + +device_deleted(Id) -> + gen_server:call(?MODULE, {device_deleted, Id}). + +link_added(Id, Link) -> + gen_server:call(?MODULE, {link_added, Id, Link}). + +link_updated(Id, Link) -> + gen_server:call(?MODULE, {link_updated, Id, Link}). + +link_deleted(Id) -> + gen_server:call(?MODULE, {link_deleted, Id}). + +compute_path(Index, From, To) + when Index =:= id; Index =:= pcc_address -> + gen_server:call(?MODULE, {compute_path, Index, From, To}); +compute_path(Index, _From, _To) -> + {error, {invalid_index, Index}}. + + +get_graph() -> + gen_server:call(?MODULE, get_graph). + + +%%% BEHAVIOUR gen_server FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([]) -> + ?LOG_INFO("Starting TED process...", []), + % {ok, #state{graph = digraph:new([private, cyclic])}}. + {ok, #state{graph = digraph:new([protected, cyclic])}}. + +handle_call({device_added, Id, Device}, _From, State) -> + ?LOG_DEBUG("Adding TED device ~p: ~p", [Id, Device]), + {reply, ok, do_update_device(State, Id, Device)}; +handle_call({device_updated, Id, Device}, _From, State) -> + ?LOG_DEBUG("Updating TED device ~p: ~p", [Id, Device]), + {reply, ok, do_update_device(State, Id, Device)}; +handle_call({device_deleted, Id}, _From, State) -> + ?LOG_DEBUG("Deleting TED device ~p", [Id]), + {reply, ok, do_delete_device(State, Id)}; +handle_call({link_added, Id, Link}, _From, State) -> + ?LOG_DEBUG("Adding TED link ~p: ~p", [Id, Link]), + {reply, ok, do_update_link(State, Id, Link)}; +handle_call({link_updated, Id, Link}, _From, State) -> + ?LOG_DEBUG("Updating TED link ~p: ~p", [Id, Link]), + {reply, ok, do_update_link(State, Id, Link)}; +handle_call({link_deleted, Id}, _From, State) -> + ?LOG_DEBUG("Deleting TED link ~p", [Id]), + {reply, ok, do_delete_link(State, Id)}; +handle_call({compute_path, Index, From, To}, _From, #state{graph = G} = State) -> + case as_ids(State, Index, [From, To]) of + {ok, [FromId, ToId]} -> + {reply, do_compute_path(G, FromId, ToId), State}; + {error, Reason} -> + {reply, {error, Reason}, State} + end; +handle_call(get_graph, _From, #state{graph = G} = State) -> + {reply, G, State}; +handle_call(Request, _From, State) -> + logger:warning("Unexpected call to TED process ~w", [Request]), + {reply, {error, unexpected_call}, State}. + +handle_cast(Request, State) -> + logger:warning("Unexpected cast to TED process ~w", [Request]), + {noreply, State}. + +handle_continue(_Continue, State) -> + {noreply, State}. + +handle_info(Info, State) -> + logger:warning("Unexpected message to TED process ~w", [Info]), + {noreply, State}. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +terminate(_Reason, _State) -> + ?LOG_INFO("Terminating TED process...", []), + ok. + + +%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +as_ids(_State, id, Keys) -> + {ok, Keys}; +as_ids(State, IndexType, Keys) -> + as_ids(State, IndexType, Keys, []). + +as_ids(_State, _IndexType, [], Acc) -> + {ok, lists:reverse(Acc)}; +as_ids(#state{pcc_address_to_id = Index} = State, pcc_address, [Key | Rest], Acc) -> + case maps:find(Key, Index) of + error -> {error, {unknown_key, Key}}; + {ok, Id} -> as_ids(State, pcc_address, Rest, [Id | Acc]) + end. + +do_update_device(#state{graph = G} = State, Id, NewDevice) -> + State2 = case digraph:vertex(G, Id) of + false -> State; + {Id, OldDevice} -> index_remove_device(State, OldDevice) + end, + digraph:add_vertex(G, Id, NewDevice), + index_add_device(State2, NewDevice). + +do_delete_device(#state{graph = G} = State, Id) -> + case digraph:vertex(G, Id) of + false -> State; + {Id, OldDevice} -> + digraph:del_vertex(G, Id), + index_remove_device(State, OldDevice) + end. + +index_remove_device(#state{pcc_address_to_id = Index} = State, + #{pcc_address := OldAddress}) -> + Index2 = maps:remove(OldAddress, Index), + State#state{pcc_address_to_id = Index2}. + +index_add_device(State, #{pcc_address := undefined}) -> + State; +index_add_device(#state{pcc_address_to_id = Index} = State, + #{id := Id, pcc_address := NewAddress}) -> + Index2 = Index#{NewAddress => Id}, + State#state{pcc_address_to_id = Index2}. + +do_update_link(#state{graph = G} = State, Id, Link) -> + #{endpoints := [EP1, EP2]} = Link, + #{device := D1} = EP1, + #{device := D2} = EP2, + digraph:add_edge(G, {Id, a}, D1, D2, Link), + digraph:add_edge(G, {Id, b}, D2, D1, Link), + State. + +do_delete_link(#state{graph = G} = State, Id) -> + digraph:del_edge(G, {Id, a}), + digraph:del_edge(G, {Id, b}), + State. + +do_compute_path(G, FromId, ToId) -> + case digraph:get_short_path(G, FromId, ToId) of + false -> {error, not_found}; + Ids -> {ok, retrieve_devices(G, Ids, [])} + end. + +retrieve_devices(_G, [], Acc) -> + lists:reverse(Acc); +retrieve_devices(G, [Id | Rest], Acc) -> + case digraph:vertex(G, Id) of + false -> {error, invalid_path}; + {Id, Device} -> + retrieve_devices(G, Rest, [Device | Acc]) + end. diff --git a/src/te/apps/tfte/src/tfte_app.erl b/src/te/apps/tfte/src/tfte_app.erl index 159197fdf..96724904d 100644 --- a/src/te/apps/tfte/src/tfte_app.erl +++ b/src/te/apps/tfte/src/tfte_app.erl @@ -8,18 +8,18 @@ -behaviour(application). -%--- Includes ------------------------------------------------------------------ +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -include_lib("kernel/include/logger.hrl"). -%--- Exports ------------------------------------------------------------------- +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Behaviour application callback functions -export([start/2, stop/1]). -%--- Behaviour application Callback Functions ---------------------------------- +%%% BEHAVIOUR applicaation CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start(_StartType, _StartArgs) -> case tfte_sup:start_link() of @@ -34,7 +34,7 @@ stop(_State) -> ok. -%--- Internal Functions -------------------------------------------------------- +%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% add_services() -> case application:get_env(tfte, services) of @@ -71,7 +71,6 @@ resolve_endpoints(Name, [{Transport, HostSpec, PortSpec, SslOpts} | Rest], Acc) resolve_host_spec(_Name, Hostname) when is_list(Hostname) -> Hostname; resolve_host_spec(Name, {env, Key}) when is_list(Key) -> - ?LOG_DEBUG("????? HOST ~s ~s -> ~p", [Name, Key, os:getenv(Key)]), try os:getenv(Key) of false -> throw({Name, service_hostname_not_found, Key}); Hostname -> Hostname @@ -82,7 +81,6 @@ resolve_host_spec(Name, {env, Key}) when is_list(Key) -> resolve_port_spec(_Name, Port) when is_integer(Port) -> Port; resolve_port_spec(Name, {env, Key}) when is_list(Key) -> - ?LOG_DEBUG("????? PORT ~s ~s -> ~p", [Name, Key, os:getenv(Key)]), try os:getenv(Key) of false -> throw({Name, service_port_not_found, Key}); PortStr -> diff --git a/src/te/apps/tfte/src/tfte_context.erl b/src/te/apps/tfte/src/tfte_context.erl new file mode 100644 index 000000000..ee0fafc07 --- /dev/null +++ b/src/te/apps/tfte/src/tfte_context.erl @@ -0,0 +1,162 @@ +-module(tfte_context). + +-behaviour(gen_statem). + + +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include_lib("kernel/include/logger.hrl"). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API functions +-export([start_link/0]). + +% Behaviour gen_statem functions +-export([init/1]). +-export([callback_mode/0]). +-export([handle_event/4]). +-export([terminate/3]). +-export([code_change/4]). + + +%%% Records %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(data, { + uuid :: map(), + sub :: term() | undefined, + obj :: map() | undefined +}). + + +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(SUBSCRIBE_RETRY_TIMEOUT, 1000). +-define(RETRIEVE_RETRY_TIMEOUT, 10000). + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_link() -> + gen_statem:start_link(?MODULE, [], []). + + +%%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([]) -> + {ok, Name} = application:get_env(tfte, context), + ?LOG_INFO("Starting context ~s service handler...", [Name]), + UUID = #{context_uuid => #{uuid => Name}}, + {ok, subscribe, #data{uuid = UUID}}. + +callback_mode() -> [handle_event_function, state_enter]. + +%-- SUBSCRIBE STATE ------------------------------------------------------------ +handle_event(enter, _, subscribe, #data{sub = undefined}) -> + {keep_state_and_data, [{state_timeout, 0, do_suscribe}]}; +handle_event(enter, _, subscribe, Data) -> + % We already have a context subscription + {next_state, ready, Data}; +handle_event(state_timeout, do_suscribe, subscribe, Data) -> + ?LOG_DEBUG("Subscribing to context events...", []), + case do_subscribe() of + {ok, Sub} -> + ?LOG_INFO("Subscribed to context events", []), + Data2 = Data#data{sub = Sub}, + {next_state, retrieve, Data2}; + {error, Reason} -> + ?LOG_ERROR("Failed to subscribe to context service events: ~p", [Reason]), + {keep_state_and_data, [{state_timeout, ?SUBSCRIBE_RETRY_TIMEOUT, do_suscribe}]} + end; +%-- RETRIEVE STATE ------------------------------------------------------------- +handle_event(enter, _, retrieve, _Data) -> + {keep_state_and_data, [{state_timeout, 0, do_retrieve}]}; +handle_event(state_timeout, do_retrieve, retrieve, #data{uuid = UUID} = Data) -> + ?LOG_DEBUG("Retrieving context ~p...", [UUID]), + case get_object(UUID) of + error -> + {keep_state_and_data, [{state_timeout, ?RETRIEVE_RETRY_TIMEOUT, do_retrieve}]}; + {ok, Context} -> + ?LOG_DEBUG("Got context: ~p", [Context]), + tfte_server:context_ready(Context), + {next_state, ready, Data#data{obj = Context}} + end; +handle_event(info, {headers, Id, Value}, retrieve, + #data{sub = #{stream_id := Id}}) -> + %TODO: Handle HTTP errors ??? + ?LOG_DEBUG("Received context stream header: ~p", [Value]), + keep_state_and_data; +handle_event(info, {data, Id, Value}, retrieve, + #data{sub = #{stream_id := Id}}) -> + ?LOG_DEBUG("Received context event, retrying context: ~p", [Value]), + {keep_state_and_data, [{state_timeout, 0, do_retrieve}]}; +handle_event(info, {'DOWN', Ref, process, Pid, Reason}, retrieve, + #data{sub = #{stream_id := Id, monitor_ref := Ref, stream_pid := Pid}} = Data) -> + ?LOG_DEBUG("Context subscription is down: ~p", [Reason]), + Data2 = Data#data{sub = undefined}, + Info = receive + {trailers, Id, {Status, Message, Metadata}} -> + {Reason, Status, Message, Metadata} + after 0 -> + Reason + end, + ?LOG_ERROR("Context subscription error: ~p", [Info]), + {next_state, subscribe, Data2}; +%-- READY STATE ---------------------------------------------------------------- +handle_event(enter, _, ready, _Data) -> + keep_state_and_data; +handle_event(info, {headers, Id, Value}, ready, + #data{sub = #{stream_id := Id}}) -> + %TODO: Handle HTTP errors ??? + ?LOG_DEBUG("Received context stream header: ~p", [Value]), + keep_state_and_data; +handle_event(info, {data, Id, #{context_id := UUID, event := Event}}, ready, + #data{uuid = UUID, sub = #{stream_id := Id}}) -> + ?LOG_DEBUG("Received context event: ~p", [Event]), + tfte_server:context_event(Event), + keep_state_and_data; +handle_event(info, {'DOWN', Ref, process, Pid, Reason}, ready, + #data{sub = #{stream_id := Id, monitor_ref := Ref, stream_pid := Pid}} = Data) -> + ?LOG_DEBUG("Context subscription is down: ~p", [Reason]), + Data2 = Data#data{sub = undefined}, + Info = receive + {trailers, Id, {Status, Message, Metadata}} -> + {Reason, Status, Message, Metadata} + after 0 -> + Reason + end, + ?LOG_ERROR("Context subscription error: ~p", [Info]), + {next_state, subscribe, Data2}; +%-- ANY STATE ------------------------------------------------------------------ +handle_event(info, Msg, StateName, _Data) -> + ?LOG_WARNING("Unexpected context message in state ~w: ~p", [StateName, Msg]), + keep_state_and_data. + +terminate(Reason, _State, _Data) -> + ?LOG_INFO("Context service handler terminated: ~p", [Reason]), + ok. + +code_change(_OldVsn, OldState, OldData, _Extra) -> + {ok, OldState, OldData}. + + +%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +grpc_opts() -> + #{channel => context}. + +do_subscribe() -> + context_context_service_client:get_context_events(#{}, grpc_opts()). + +get_object(UUID) -> + case context_context_service_client:get_context(UUID, grpc_opts()) of + {error, Reason} -> + ?LOG_ERROR("Local error while retrieving the context object: ~p", [Reason]), + error; + {error, Reason, _Headers} -> + ?LOG_ERROR("Remote error while retrieving the context object: ~p", [Reason]), + error; + {ok, Result, _Headers} -> + {ok, Result} + end. \ No newline at end of file diff --git a/src/te/apps/tfte/src/tfte_server.erl b/src/te/apps/tfte/src/tfte_server.erl new file mode 100644 index 000000000..29dddf3d1 --- /dev/null +++ b/src/te/apps/tfte/src/tfte_server.erl @@ -0,0 +1,100 @@ +-module(tfte_server). + +-behaviour(gen_statem). + + +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include_lib("kernel/include/logger.hrl"). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API functions +-export([start_link/0]). +-export([context_ready/1]). +-export([context_event/1]). +-export([topology_ready/1]). +-export([topology_event/1]). + +% Behaviour gen_statem functions +-export([init/1]). +-export([callback_mode/0]). +-export([handle_event/4]). +-export([terminate/3]). +-export([code_change/4]). + + +%%% Records %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(data, { +}). + + +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_link() -> + gen_statem:start_link(?MODULE, [], []). + +context_ready(Context) -> + gen_statem:cast(?MODULE, {context_ready, Context}). + +context_event(Event) -> + gen_statem:cast(?MODULE, {context_event, Event}). + +topology_ready(Topology) -> + gen_statem:cast(?MODULE, {topology_ready, Topology}). + +topology_event(Event) -> + gen_statem:cast(?MODULE, {topology_event, Event}). + + +%%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([]) -> + ?LOG_INFO("Starting server...", []), + {ok, wait_context, #data{}}. + +callback_mode() -> [handle_event_function, state_enter]. + +%-- WAIT_CONTEXT STATE --------------------------------------------------------- +handle_event(enter, _, wait_context, _Data) -> + keep_state_and_data; +handle_event(cast, {context_ready, _Context}, wait_contex, Data) -> + ?LOG_DEBUG("Teraflow context initialized: ~p", [_Context]), + tfte_topology:context_updated(), + {next_state, ready, Data}; +%-- READY STATE ---------------------------------------------------------------- +handle_event(enter, _, ready, _Data) -> + keep_state_and_data; +handle_event(cast, {context_ready, _Context}, ready, _Data) -> + ?LOG_DEBUG("Teraflow context updated: ~p", [_Context]), + tfte_topology:context_updated(), + keep_state_and_data; +handle_event(cast, {context_event, _Event}, ready, _Data) -> + ?LOG_DEBUG("Teraflow context event: ~p", [_Event]), + keep_state_and_data; +handle_event(cast, {topology_ready, _Topology}, ready, _Data) -> + ?LOG_DEBUG("Teraflow topology updated: ~p", [_Topology]), + keep_state_and_data; +handle_event(cast, {topology_event, _Event}, ready, _Data) -> + ?LOG_DEBUG("Teraflow topology event: ~p", [_Event]), + keep_state_and_data; +%-- ANY STATE ------------------------------------------------------------------ +handle_event(EventType, EventContent, State, Data) -> + ?LOG_WARNING(Data, "Unexpected ~w event in state ~w: ~w", + [EventType, State, EventContent]), + keep_state_and_data. + +terminate(Reason, _State, _Data) -> + ?LOG_INFO("Server terminated: ~p", [Reason]), + ok. + +code_change(_OldVsn, OldState, OldData, _Extra) -> + {ok, OldState, OldData}. + + +%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/te/apps/tfte/src/tfte_service_sup.erl b/src/te/apps/tfte/src/tfte_service_sup.erl new file mode 100644 index 000000000..2223589e2 --- /dev/null +++ b/src/te/apps/tfte/src/tfte_service_sup.erl @@ -0,0 +1,50 @@ +%%%----------------------------------------------------------------------------- +%% @doc tfte service supervisor. +%% @end +%%%----------------------------------------------------------------------------- + +-module(tfte_service_sup). + +-behaviour(supervisor). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API Functions +-export([start_link/0]). + +% Behaviour supervisor callback functions +-export([init/1]). + + +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(SERVER, ?MODULE). + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + + +%%% BEHAVIOUR supervisor CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([]) -> + SupFlags = #{strategy => one_for_one, + intensity => 0, + period => 1}, + ContextSpec = #{ + id => tfte_context, + start => {tfte_context, start_link, []}, + restart => permanent, + shutdown => brutal_kill + }, + TopologySpec = #{ + id => tfte_topology, + start => {tfte_topology, start_link, []}, + restart => permanent, + shutdown => brutal_kill + }, + ChildSpecs = [ContextSpec, TopologySpec], + {ok, {SupFlags, ChildSpecs}}. diff --git a/src/te/apps/tfte/src/tfte_sup.erl b/src/te/apps/tfte/src/tfte_sup.erl index 2944889cb..57c95483e 100644 --- a/src/te/apps/tfte/src/tfte_sup.erl +++ b/src/te/apps/tfte/src/tfte_sup.erl @@ -8,7 +8,7 @@ -behaviour(supervisor). -%--- Exports ------------------------------------------------------------------- +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % API Functions -export([start_link/0]). @@ -17,22 +17,36 @@ -export([init/1]). -%--- Macros -------------------------------------------------------------------- +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -define(SERVER, ?MODULE). +-define(ROOT_SERVER, tfte_server). -%--- API Functions ------------------------------------------------------------- +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). -%--- Behaviour supervisor Callback Functions ----------------------------------- +%%% BEHAVIOUR supervisor CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init([]) -> SupFlags = #{strategy => one_for_all, intensity => 0, period => 1}, - ChildSpecs = [], + ServiceSupSpec = #{ + id => service_sup, + start => {tfte_service_sup, start_link, []}, + restart => permanent, + type => supervisor, + shutdown => brutal_kill + }, + ServerSpec = #{ + id => ?ROOT_SERVER, + start => {?ROOT_SERVER, start_link, []}, + restart => permanent, + shutdown => brutal_kill + }, + ChildSpecs = [ServerSpec, ServiceSupSpec], {ok, {SupFlags, ChildSpecs}}. diff --git a/src/te/apps/tfte/src/tfte_te_service.erl b/src/te/apps/tfte/src/tfte_te_service.erl index 7c2a7225b..1cadd7aad 100644 --- a/src/te/apps/tfte/src/tfte_te_service.erl +++ b/src/te/apps/tfte/src/tfte_te_service.erl @@ -3,12 +3,12 @@ -behaviour(te_te_service_bhvr). -%--- Includes ------------------------------------------------------------------ +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -include_lib("grpcbox/include/grpcbox.hrl"). -%--- Exports ------------------------------------------------------------------- +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Behaviour te_te_service_bhvr callback functions -export([request_lsp/2]). @@ -16,7 +16,7 @@ -export([delete_lsp/2]). -%--- Behaviour te_te_service_bhvr Callback Functions --------------------------- +%%% BEHAVIOUR te_te_service_bhvr CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%% request_lsp(_Ctx, _Service) -> {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, diff --git a/src/te/apps/tfte/src/tfte_topology.erl b/src/te/apps/tfte/src/tfte_topology.erl new file mode 100644 index 000000000..d2c6fc0d9 --- /dev/null +++ b/src/te/apps/tfte/src/tfte_topology.erl @@ -0,0 +1,394 @@ +-module(tfte_topology). + +-behaviour(gen_statem). + + +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include_lib("kernel/include/logger.hrl"). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API functions +-export([start_link/0]). +-export([context_updated/0]). + +% Behaviour gen_statem functions +-export([init/1]). +-export([callback_mode/0]). +-export([handle_event/4]). +-export([terminate/3]). +-export([code_change/4]). + + +%%% Records %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(data, { + uuid :: map(), + sub :: term() | undefined, + obj :: map() | undefined, + devices = #{} :: map(), + links = #{} :: map() +}). + + +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(SUBSCRIBE_RETRY_TIMEOUT, 1000). +-define(RETRIEVE_RETRY_TIMEOUT, 10000). + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start_link() -> + gen_statem:start_link(?MODULE, [], []). + +context_updated() -> + gen_statem:cast(?MODULE, context_updated). + + +%%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([]) -> + {ok, ContextName} = application:get_env(tfte, context), + {ok, TopoName} = application:get_env(tfte, topology), + ContextUUID = #{context_uuid => #{uuid => ContextName}}, + TopoUUID = #{context_id => ContextUUID, + topology_uuid => #{uuid => TopoName}}, + ?LOG_INFO("Starting topology ~s service handler...", [TopoName]), + {ok, retrieve, #data{uuid = TopoUUID}}. + +callback_mode() -> [handle_event_function, state_enter]. + +%-- RETRIEVE STATE ------------------------------------------------------------- +handle_event(enter, _, retrieve, _Data) -> + {keep_state_and_data, [{state_timeout, 0, do_retrieve}]}; +handle_event(state_timeout, do_retrieve, retrieve, #data{uuid = UUID} = Data) -> + ?LOG_DEBUG("Retrieving topology ~p...", [UUID]), + case get_object(UUID) of + error -> + {keep_state_and_data, [{state_timeout, ?RETRIEVE_RETRY_TIMEOUT, do_retrieve}]}; + {ok, Topology} -> + ?LOG_DEBUG("Got topology: ~p", [Topology]), + {next_state, subscribe, Data#data{obj = Topology}} + end; +handle_event(cast, context_updated, retrieve, _Data) -> + {keep_state_and_data, [{state_timeout, 0, do_retrieve}]}; +%-- SUBSCRIBE STATE ------------------------------------------------------------ +handle_event(enter, _, subscribe, #data{sub = undefined}) -> + {keep_state_and_data, [{state_timeout, 0, do_suscribe}]}; +handle_event(enter, _, subscribe, Data) -> + % We already have a topology subscription + {next_state, ready, Data}; +handle_event(state_timeout, do_suscribe, subscribe, #data{uuid = UUID} = Data) -> + ?LOG_DEBUG("Subscribing to topology events...", []), + case do_subscribe(UUID) of + {ok, Sub} -> + ?LOG_INFO("Subscribed to topology events", []), + Data2 = #data{obj = Obj} = Data#data{sub = Sub}, + #{device_ids := DeviceIds, link_ids := LinkIds} = Obj, + case update_topology(Data2, DeviceIds, LinkIds) of + {ok, Data3} -> + tfte_server:topology_ready(Obj), + {next_state, ready, Data3}; + {error, Reason} -> + ?LOG_ERROR("Failed to load topology: ~p", [Reason]), + statem_rollback_to_retrieve(Data2) + end; + {error, Reason} -> + ?LOG_ERROR("Failed to subscribe to topology service events: ~p", [Reason]), + {next_state, retrieve, [{state_timeout, ?SUBSCRIBE_RETRY_TIMEOUT, do_retrieve}]} + end; +%-- READY STATE ---------------------------------------------------------------- +handle_event(enter, _, ready, _Data) -> + keep_state_and_data; +handle_event(info, {headers, Id, Value}, ready, + #data{sub = #{stream_id := Id}}) -> + %TODO: Handle HTTP errors ??? + ?LOG_DEBUG("Received topology stream header: ~p", [Value]), + keep_state_and_data; +handle_event(info, {data, Id, #{event := Event}}, ready, + #data{sub = #{stream_id := Id}} = Data) -> + ?LOG_DEBUG("Received topology event: ~p", [Event]), + handle_topology_event(Data, Event); +handle_event(info, {'DOWN', Ref, process, Pid, Reason}, ready, + #data{sub = #{stream_id := Id, monitor_ref := Ref, stream_pid := Pid}} = Data) -> + ?LOG_DEBUG("Topology subscription is down: ~p", [Reason]), + Data2 = Data#data{sub = undefined}, + Info = receive + {trailers, Id, {Status, Message, Metadata}} -> + {Reason, Status, Message, Metadata} + after 0 -> + Reason + end, + ?LOG_ERROR("Topology subscription error: ~p", [Info]), + {next_state, retrieve, Data2}; +handle_event(cast, context_updated, ready, _Data) -> + keep_state_and_data; +%-- ANY STATE ------------------------------------------------------------------ +handle_event(info, Msg, StateName, _Data) -> + ?LOG_WARNING("Unexpected topology message in state ~w: ~p", [StateName, Msg]), + keep_state_and_data. + +terminate(Reason, _State, _Data) -> + ?LOG_INFO("Topology service handler terminated: ~p", [Reason]), + ok. + +code_change(_OldVsn, OldState, OldData, _Extra) -> + {ok, OldState, OldData}. + + +%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +statem_rollback_to_retrieve(#data{sub = undefined} = Data) -> + {next_state, retrieve, Data, [{state_timeout, ?RETRIEVE_RETRY_TIMEOUT, do_retrieve}]}; +statem_rollback_to_retrieve(#data{sub = Sub} = Data) -> + grpcbox_client:close_send_and_recv(Sub), + Data2 = Data#data{sub = undefined}, + {next_state, retrieve, Data2, [{state_timeout, ?RETRIEVE_RETRY_TIMEOUT, do_retrieve}]}. + +handle_topology_event(#data{uuid = UUID} = Data, + #{event_type := 'EVENTTYPE_UPDATE'} = Event) -> + case get_object(UUID) of + error -> + statem_rollback_to_retrieve(Data); + {ok, #{device_ids := DeviceIds, link_ids := LinkIds} = Topology} -> + ?LOG_DEBUG("Got new topology: ~p", [Topology]), + Data2 = Data#data{obj = Topology}, + case update_topology(Data2, DeviceIds, LinkIds) of + {ok, Data3} -> + tfte_server:topology_event(Event), + {keep_state, Data3}; + {error, Reason} -> + ?LOG_ERROR("Failed to update topology: ~p", [Reason]), + statem_rollback_to_retrieve(Data2) + end + end; +handle_topology_event(_Data, Event) -> + tfte_server:topology_event(Event), + keep_state_and_data. + +update_topology(Data, DeviceIds, LinkIds) -> + try + {Data2, Events} = update_devices(Data, DeviceIds, []), + {Data3, Events2} = update_links(Data2, LinkIds, Events), + post_topology_events(lists:reverse(Events2)), + {ok, Data3} + catch + throw:Reason -> + {error, Reason} + end. + +post_topology_events(Events) -> + lists:foreach(fun post_topology_event/1, Events). + +post_topology_event({device_added, Id, Device}) -> + epce_ted:device_added(Id, Device); +post_topology_event({device_updated, Id, Device}) -> + epce_ted:device_updated(Id, Device); +post_topology_event({device_deleted, Id}) -> + epce_ted:device_deleted(Id); +post_topology_event({link_added, Id, Link}) -> + epce_ted:link_added(Id, Link); +post_topology_event({link_updated, Id, Link}) -> + epce_ted:link_updated(Id, Link); +post_topology_event({link_deleted, Id}) -> + epce_ted:link_deleted(Id). + +update_devices(#data{devices = OldDevices} = Data, DeviceIds, Events) -> + {NewDevices, Events2} = update_devices(OldDevices, #{}, DeviceIds, Events), + {Data#data{devices = NewDevices}, Events2}. + +update_devices(OldDevices, NewDevices, [], Events) -> + Events2 = [{device_deleted, post_process_device_id(I)} + || I <- maps:keys(OldDevices)] ++ Events, + {NewDevices, Events2}; +update_devices(OldDevices, NewDevices, [Id | Rest], Events) -> + case get_device(Id) of + error -> throw({device_retrieval_error, Id}); + {ok, Device} -> + Id2 = post_process_device_id(Id), + Device2 = post_process_device(Device), + NewDevices2 = NewDevices#{Id => Device}, + case maps:take(Id, OldDevices) of + error -> + % New device + Events2 = [{device_added, Id2, Device2} | Events], + update_devices(OldDevices, NewDevices2, Rest, Events2); + {Device, OldDevices2} -> + % Device did not change + update_devices(OldDevices2, NewDevices2, Rest, Events); + {_OldDevice, OldDevices2} -> + % Device changed + Events2 = [{device_updated, Id2, Device2} | Events], + update_devices(OldDevices2, NewDevices2, Rest, Events2) + end + end. + +update_links(#data{links = OldLinks} = Data, LinksIds, Events) -> + {NewLinks, Events2} = update_links(OldLinks, #{}, LinksIds, Events), + {Data#data{links = NewLinks}, Events2}. + +update_links(OldLinks, NewLinks, [], Events) -> + Events2 = [{link_deleted, post_process_link_id(I)} + || I <- maps:keys(OldLinks)] ++ Events, + {NewLinks, Events2}; +update_links(OldLinks, NewLinks, [Id | Rest], Events) -> + case get_link(Id) of + error -> throw({link_retrieval_error, Id}); + {ok, Link} -> + Id2 = post_process_link_id(Id), + Link2 = post_process_link(Link), + NewLinks2 = NewLinks#{Id => Link}, + case maps:take(Id, OldLinks) of + error -> + % New Link + Events2 = [{link_added, Id2, Link2} | Events], + update_links(OldLinks, NewLinks2, Rest, Events2); + {Link, OldLinks2} -> + % Link did not change + update_links(OldLinks2, NewLinks2, Rest, Events); + {_OldLink, OldLinks2} -> + % Link changed + Events2 = [{link_updated, Id2, Link2} | Events], + update_links(OldLinks2, NewLinks2, Rest, Events2) + end + end. + +post_process_device_id(#{device_uuid := #{uuid := Name}}) -> + Name. + +post_process_device(Device) -> + #{id => device_id(Device), + type => device_type(Device), + pcc_address => device_pcc_address(Device), + mpls_label => device_mpls_label(Device), + status => device_status(Device), + endpoints => device_endpoints(Device)}. + +device_id(#{device_id := Id}) -> + post_process_device_id(Id). + +device_type(#{device_type := Type}) -> + Type. + +device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_UNDEFINED'}) -> + undefined; +device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_DISABLED'}) -> + disabled; +device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_ENABLED'}) -> + enabled. + +device_mpls_label(Device) -> + case device_config_value(<<"mpls_label">>, Device) of + undefined -> undefined; + LabelBin -> + try binary_to_integer(LabelBin) + catch error:badarg -> undefined + end + end. + +device_pcc_address(Device) -> + case device_config_value(<<"pcc_address">>, Device) of + undefined -> undefined; + AddressBin -> + case inet_parse:address(binary_to_list(AddressBin)) of + {ok, Address} -> Address; + {error,einval} -> undefined + end + end. + +device_config_value(Key, #{device_config := #{config_rules := Rules}}) -> + device_config_value(Key, Rules); +device_config_value(_Key, []) -> + undefined; +device_config_value(Key, [#{action := 'CONFIGACTION_SET', + config_rule := {custom, Rule}} | Rest]) -> + case Rule of + #{resource_key := Key, resource_value := Value} -> Value; + _ -> device_config_value(Key, Rest) + end; +device_config_value(Key, [_Rule | Rest]) -> + device_config_value(Key, Rest). + + +device_endpoints(Device) -> + device_endpoints(Device, []). + +device_endpoints(#{device_endpoints := Endpoints}, Acc) -> + device_endpoints(Endpoints, Acc); +device_endpoints([], Acc) -> + lists:reverse(Acc); +device_endpoints([#{endpoint_id := #{endpoint_uuid := #{uuid := Name}}} | Rest], Acc) -> + device_endpoints(Rest, [Name | Acc]). + +post_process_link_id(#{link_uuid := #{uuid := Name}}) -> + Name. + +post_process_link(Link) -> + #{id => link_id(Link), + endpoints => link_endpoints(Link)}. + +link_id(#{link_id := Id}) -> + post_process_link_id(Id). + +link_endpoints(Link) -> + link_endpoints(Link, []). + +link_endpoints(#{link_endpoint_ids := Endpoints}, Acc) -> + link_endpoints(Endpoints, Acc); +link_endpoints([], Acc) -> + lists:reverse(Acc); +link_endpoints([#{device_id := #{device_uuid := #{uuid := DevName}}, + endpoint_uuid := #{uuid := EndpointName}} | Rest], Acc) -> + Endpoint = #{ + device => DevName, + endpoint => EndpointName + }, + link_endpoints(Rest, [Endpoint | Acc]). + + +%-- GRPC UNTILITY FUNCTION ----------------------------------------------------- + +grpc_opts() -> + #{channel => context}. + +do_subscribe(UUID) -> + context_context_service_client:get_topology_events(UUID, grpc_opts()). + +get_object(UUID) -> + case context_context_service_client:get_topology(UUID, grpc_opts()) of + {error, Reason} -> + ?LOG_ERROR("Local error while retrieving the topology object: ~p", [Reason]), + error; + {error, Reason, _Headers} -> + ?LOG_ERROR("Remote error while retrieving the topology object: ~p", [Reason]), + error; + {ok, Result, _Headers} -> + {ok, Result} + end. + +get_device(UUID) -> + case context_context_service_client:get_device(UUID, grpc_opts()) of + {error, Reason} -> + ?LOG_ERROR("Local error while retrieving a device object: ~p", [Reason]), + error; + {error, Reason, _Headers} -> + ?LOG_ERROR("Remote error while retrieving a device object: ~p", [Reason]), + error; + {ok, Result, _Headers} -> + {ok, Result} + end. + +get_link(UUID) -> + case context_context_service_client:get_link(UUID, grpc_opts()) of + {error, Reason} -> + ?LOG_ERROR("Local error while retrieving a link object: ~p", [Reason]), + error; + {error, Reason, _Headers} -> + ?LOG_ERROR("Remote error while retrieving a link object: ~p", [Reason]), + error; + {ok, Result, _Headers} -> + {ok, Result} + end. diff --git a/src/te/config/sys.config.src b/src/te/config/sys.config.src index 93bfbafb1..f3c185641 100644 --- a/src/te/config/sys.config.src +++ b/src/te/config/sys.config.src @@ -1,6 +1,8 @@ [ {tfte, [ + {context, <<"admin">>}, + {topology, <<"tetestbed">>}, {services, [ {te, [ {http, {env, "TESERVICE_SERVICE_HOST"}, {env, "TESERVICE_SERVICE_PORT_GRPC"}, []} @@ -72,7 +74,7 @@ {logger_level, ${ERLANG_LOGGER_LEVEL}}, {logger, [ {handler, default, logger_std_h, #{ - level => info, + level => ${ERLANG_LOGGER_LEVEL}, filter_default => log, config => #{type => standard_io}, formatter => {logger_formatter, #{ diff --git a/tutorial/2-6-te-demo-start-testbed.sh b/tutorial/2-6-te-demo-start-testbed.sh index dea41a71a..3266d1141 100755 --- a/tutorial/2-6-te-demo-start-testbed.sh +++ b/tutorial/2-6-te-demo-start-testbed.sh @@ -11,26 +11,30 @@ if [[ ! -f "${NEGENDIR}/exe/netgen" ]]; then exit 1 fi -export PCE_NETNS="$1" -export PCE_IP="$2" -RT1_INT_IP="$3" -RT1_EXT_IP="$4" -RT6_INT_IP="$5" -RT6_EXT_IP="$6" - - -if [[ -z $PCE_NETNS || -z $PCE_IP || -z RT1_INT_IP || -z RT1_EXT_IP || -z RT6_INT_IP || -z RT6_EXT_IP ]]; then - echo "USAGE: $0 PCE_NETNS PCE_IP RT1_INT_IP RT1_EXT_IP RT6_INT_IP RT6_EXT_IP" - echo " e.g: $0 cni-588a2d06-e64f-907b-d51b-bed0307007c9 10.1.103.133 10 11 12 13" - exit 1 +PCE_IP=$( kubectl --namespace tfs get $(kubectl --namespace tfs get pods --selector=app=teservice -o name) --template '{{.status.podIP}}' ) +echo "Teraflow PCE IP address: $PCE_IP" +NAMESPACES=$( ip netns list | cut -d' ' -f1 ) +PCE_NETNS="" +for n in $NAMESPACES; do + if sudo ip -n $n addr list | grep $PCE_IP > /dev/null; then + echo "Teraflow TE service namespace: $n" + PCE_NETNS=$n + break + fi +done +if [[ -z $PCE_NETNS ]]; then + echo "Teraflow network namespace for TE service not found" + exit1 fi IFS=. read PCE_IP1 PCE_IP2 PCE_IP3 PCE_IP4 <<< "$PCE_IP" -export RT1_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT1_INT_IP" -export RT1_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT1_EXT_IP" -export RT6_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT6_INT_IP" -export RT6_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT6_EXT_IP" +export PCE_IP +export PCE_NETNS +export RT1_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.10" +export RT1_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.11" +export RT6_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.12" +export RT6_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.13" cp "${ROOTDIR}/2-6-netgen-config.yml" "${RUNDIR}/config.yml" cat "${ROOTDIR}/2-6-netgen-topology.yml.template" | envsubst > "${RUNDIR}/topology.yml" diff --git a/tutorial/2-6-te-demo.md b/tutorial/2-6-te-demo.md index 6a99007b4..774a38bab 100644 --- a/tutorial/2-6-te-demo.md +++ b/tutorial/2-6-te-demo.md @@ -78,20 +78,14 @@ ### Run the Test-Bed -To start the testbed, we need to figure out the IP of the TE service: - $ kubectl --namespace tfs get $(kubectl --namespace tfs get pods --selector=app=teservice -o name) --template '{{.status.podIP}}' - -And select the network namespace that defines it by looking into each of them until you find the correct one: - $ ip netns list - $ sudo ip -n XXX addr list - -When we have the IP and namespace we can start the testbed. +First load the [teraflow configuration file](./2-6-teraflow-topology.json) using the webui. +The first time the configuration is loaded may partialy fail because it tries to load the topology before the devices and links, but a second load should work. In first console: $ cd ~/testbed - $ ../tfs-ctrl/tutorial/2-6-te-demo-start-testbed.sh NETWORK_NAMESPACE TE_SERVICE_IP 10 11 12 13 + $ ../tfs-ctrl/tutorial/2-6-te-demo-start-testbed.sh Then in second console: $ sudo -i # cd /tmp/negen - # ./tmux.sh \ No newline at end of file + # ./tmux.sh diff --git a/tutorial/2-6-teraflow-topology.json b/tutorial/2-6-teraflow-topology.json new file mode 100644 index 000000000..8b5188866 --- /dev/null +++ b/tutorial/2-6-teraflow-topology.json @@ -0,0 +1,210 @@ +{ + "contexts": [ + { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_ids": [], + "service_ids": [] + } + ], + "topologies": [ + { + "topology_id": {"topology_uuid": {"uuid": "tetestbed"}, "context_id": {"context_uuid": {"uuid": "admin"}}}, + "device_ids": [ + {"device_uuid": {"uuid": "SW1"}}, + {"device_uuid": {"uuid": "RT1"}}, + {"device_uuid": {"uuid": "RT2"}}, + {"device_uuid": {"uuid": "RT3"}}, + {"device_uuid": {"uuid": "RT4"}}, + {"device_uuid": {"uuid": "RT5"}}, + {"device_uuid": {"uuid": "RT6"}} + ], + "link_ids": [ + {"link_uuid": {"uuid": "RT1/SW1"}}, + {"link_uuid": {"uuid": "RT2/SW1"}}, + {"link_uuid": {"uuid": "RT3/SW1"}}, + {"link_uuid": {"uuid": "RT2/RT4/1"}}, + {"link_uuid": {"uuid": "RT2/RT4/2"}}, + {"link_uuid": {"uuid": "RT3/RT5/1"}}, + {"link_uuid": {"uuid": "RT3/RT5/2"}}, + {"link_uuid": {"uuid": "RT4/RT5"}}, + {"link_uuid": {"uuid": "RT4/RT6"}}, + {"link_uuid": {"uuid": "RT5/RT6"}} + ] + } + ], + "devices": [ + { + "device_id": {"device_uuid": {"uuid": "SW1"}}, + "device_type": "emu-packet-switch", + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": + "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"df8bb169-2013-4b82-9455-69777f7a01d6\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"061119c1-2aa4-48e9-be64-3ddf465fc80a\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"495ea3f8-e67f-46a0-84bd-a230a4b7067d\"}]}"}} + ]}, + "device_operational_status": 1, + "device_drivers": [0], + "device_endpoints": [] + }, + { + "device_id": {"device_uuid": {"uuid": "RT1"}}, + "device_type": "emu-packet-router", + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16010"}}, + {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "1.1.1.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "1.1.1.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": + "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-src\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}]}"}} + ]}, + "device_operational_status": 1, + "device_drivers": [0], + "device_endpoints": [] + }, + { + "device_id": {"device_uuid": {"uuid": "RT2"}}, + "device_type": "emu-packet-router", + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16020"}}, + {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "2.2.2.2"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "2.2.2.2"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": + "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4-2\"}]}"}} + ]}, + "device_operational_status": 1, + "device_drivers": [0], + "device_endpoints": [] + }, + { + "device_id": {"device_uuid": {"uuid": "RT3"}}, + "device_type": "emu-packet-router", + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16030"}}, + {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "3.3.3.3"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "3.3.3.3"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": + "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5-2\"}]}"}} + ]}, + "device_operational_status": 1, + "device_drivers": [0], + "device_endpoints": [] + }, + { + "device_id": {"device_uuid": {"uuid": "RT4"}}, + "device_type": "emu-packet-router", + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16040"}}, + {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "4.4.4.4"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "4.4.4.4"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": + "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt2-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt2-2\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt6\"}]}"}} + ]}, + "device_operational_status": 1, + "device_drivers": [0], + "device_endpoints": [] + }, + { + "device_id": {"device_uuid": {"uuid": "RT5"}}, + "device_type": "emu-packet-router", + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16050"}}, + {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "5.5.5.5"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "5.5.5.5"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": + "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt3-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt3-2\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt6\"}]}"}} + ]}, + "device_operational_status": 1, + "device_drivers": [0], + "device_endpoints": [] + }, + { + "device_id": {"device_uuid": {"uuid": "RT6"}}, + "device_type": "emu-packet-router", + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16060"}}, + {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "6.6.6.6"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "6.6.6.6"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": + "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-dst\"}]}"}} + ]}, + "device_operational_status": 1, + "device_drivers": [0], + "device_endpoints": [] + } + ], + "links": [ + { + "link_id": {"link_uuid": {"uuid": "RT1/SW1"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT1"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, + {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "df8bb169-2013-4b82-9455-69777f7a01d6"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT2/SW1"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, + {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "061119c1-2aa4-48e9-be64-3ddf465fc80a"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT3/SW1"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, + {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "495ea3f8-e67f-46a0-84bd-a230a4b7067d"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT2/RT4/1"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-rt4-1"}}, + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt2-1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT2/RT4/2"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-rt4-2"}}, + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt2-2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT3/RT5/1"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-rt5-1"}}, + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt3-1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT3/RT5/2"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-rt5-2"}}, + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt3-2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT4/RT5"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt5"}}, + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt4"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT4/RT6"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt6"}}, + {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-rt4"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT5/RT6"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt6"}}, + {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-rt5"}} + ] + } + ] +} -- GitLab From 0fbf935cdf73d7f017270a6cd5b97f9cd53ad2cc Mon Sep 17 00:00:00 2001 From: merle Date: Fri, 30 Sep 2022 09:42:44 -0700 Subject: [PATCH 30/63] Add support for PCE initiated LSPs --- .../epce/src/epce_pcep_server_handler.erl | 5 ++ src/te/apps/epce/src/epce_server.erl | 83 +++++++++++++++++-- src/te/rebar.lock | 4 +- tutorial/2-6-netgen-topology.yml.template | 15 +--- tutorial/2-6-te-demo-start-testbed.sh | 2 - tutorial/2-6-te-demo.md | 17 ++++ 6 files changed, 100 insertions(+), 26 deletions(-) diff --git a/src/te/apps/epce/src/epce_pcep_server_handler.erl b/src/te/apps/epce/src/epce_pcep_server_handler.erl index 210395885..df786bfc6 100644 --- a/src/te/apps/epce/src/epce_pcep_server_handler.erl +++ b/src/te/apps/epce/src/epce_pcep_server_handler.erl @@ -17,6 +17,7 @@ -export([init/1]). -export([opened/4]). -export([flow_added/2]). +-export([flow_initiated/2]). -export([ready/1]). -export([request_route/2]). -export([flow_delegated/2]). @@ -49,6 +50,10 @@ flow_added(Flow, State) -> ok -> {ok, State} end. +flow_initiated(Flow, State) -> + ok = epce_server:flow_initiated(Flow), + {ok, State}. + ready(State) -> {ok, State}. diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl index 11a1625ef..507255385 100644 --- a/src/te/apps/epce/src/epce_server.erl +++ b/src/te/apps/epce/src/epce_server.erl @@ -15,10 +15,12 @@ -export([start_link/0]). -export([get_flows/0]). -export([update_flow/2]). +-export([initiate_flow/4]). % Handler Functions -export([session_opened/3]). -export([flow_added/1]). +-export([flow_initiated/1]). -export([request_route/1]). -export([flow_status_changed/2]). @@ -32,6 +34,11 @@ -export([terminate/2]). +%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(LARGE_TIMEOUT, 20000). + + %%% RECORDS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -record(sess, { @@ -60,6 +67,10 @@ get_flows() -> update_flow(FlowId, LabelStack) -> gen_server:call(?MODULE, {update_flow, FlowId, LabelStack}). +initiate_flow(Name, FromAddr, ToAddr, BindingLabel) -> + gen_server:call(?MODULE, {initiate_flow, Name, FromAddr, ToAddr, + BindingLabel}, ?LARGE_TIMEOUT). + %%% HANDLER FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -69,6 +80,9 @@ session_opened(Id, Caps, Pid) -> flow_added(Flow) -> gen_server:call(?MODULE, {flow_added, Flow}). +flow_initiated(Flow) -> + gen_server:call(?MODULE, {flow_initiated, Flow}). + request_route(RouteReq) -> gen_server:call(?MODULE, {request_route, RouteReq}). @@ -92,11 +106,26 @@ handle_call({update_flow, FlowId, Labels}, From, error -> {reply, {error, session_not_found}, State}; {ok, #sess{pid = Pid}} -> #{source := S, destination := D, constraints := C} = R, - ReqRoute = route_from_labels(S, D, C, Labels), + ReqRoute = routereq_from_labels(S, D, C, Labels), session_update_flow(State, Pid, FlowId, ReqRoute, From), {noreply, State} end end; +handle_call({initiate_flow, Name, FromAddr, ToAddr, Binding}, From, + #state{sessions = SessMap} = State) -> + case maps:find(FromAddr, SessMap) of + error -> {reply, {error, session_not_found}, State}; + {ok, #sess{pid = Pid}} -> + case compute_path(FromAddr, ToAddr) of + {error, Reason} -> + {reply, {error, Reason}, State}; + {ok, Labels} -> + InitRoute = routeinit_from_labels(Name, FromAddr, ToAddr, + [], Binding, Labels), + session_initiate_flow(State, Pid, InitRoute, From), + {noreply, State} + end + end; handle_call({session_opened, Id, Caps, Pid}, _From, #state{sessions = SessMap, sess_pids = SessPids} = State) -> logger:debug("Session with capabilities ~w open to ~w", [Caps, Id]), @@ -114,6 +143,11 @@ handle_call({flow_added, #{id := Id, route := Route} = Flow}, _From, #state{flows = Flows} = State) -> logger:debug("Flow ~w with route ~w added", [Id, route_to_labels(Route)]), {reply, ok, State#state{flows = Flows#{Id => Flow}}}; +handle_call({flow_initiated, #{id := Id, route := Route} = Flow}, + _From, #state{flows = Flows} = State) -> + logger:debug("Flow ~w with route ~p initiated", + [Id, route_to_labels(Route)]), + {reply, ok, State#state{flows = Flows#{Id => Flow}}}; handle_call({request_route, RouteReq}, _From, State) -> logger:info("Route from ~w to ~w requested", [maps:get(source, RouteReq), maps:get(destination, RouteReq)]), @@ -122,7 +156,7 @@ handle_call({request_route, RouteReq}, _From, State) -> {error, _Reason} = Error -> {reply, Error, State}; {ok, Labels} -> - Route = route_from_labels(S, D, C, Labels), + Route = routereq_from_labels(S, D, C, Labels), {reply, {ok, Route}, State} end; handle_call({flow_status_changed, FlowId, NewStatus}, _From, @@ -131,12 +165,13 @@ handle_call({flow_status_changed, FlowId, NewStatus}, _From, Flow = maps:get(FlowId, Flows), {reply, ok, State#state{ flows = maps:put(FlowId, Flow#{status := NewStatus}, Flows)}}; -handle_call(Request, _From, State) -> - logger:warning("Unexpected request ~w", [Request]), +handle_call(Request, From, State) -> + logger:warning("Unexpected call from ~w: ~p", [From, Request]), {reply, {error, unexpected_call}, State}. -handle_cast(_Request, State) -> +handle_cast(Request, State) -> + logger:warning("Unexpected cast: ~p", [Request]), {noreply, State}. handle_continue(_Continue, State) -> @@ -153,7 +188,17 @@ handle_info({flow_updated, FlowId, NewRoute, From}, {noreply, State#state{flows = Flows2}} end; handle_info({flow_update_error, FlowId, Reason, From}, State) -> - logger:error("Flow ~w updated error: ~w", [FlowId, Reason]), + logger:error("Flow ~w updated error: ~p", [FlowId, Reason]), + gen_server:reply(From, {error, Reason}), + {noreply, State}; +handle_info({flow_initiated, #{id := FlowId, route := Route} = Flow, From}, + #state{flows = Flows} = State) -> + logger:info("Flow ~w initiated to ~p", + [FlowId, route_to_labels(Route)]), + gen_server:reply(From, {ok, FlowId}), + {noreply, State#state{flows = Flows#{FlowId => Flow}}}; +handle_info({flow_init_error, Reason, From}, State) -> + logger:error("Flow initialisation error: ~p", [Reason]), gen_server:reply(From, {error, Reason}), {noreply, State}; handle_info({'DOWN', MonRef, process, Pid, _Reason}, @@ -169,7 +214,8 @@ handle_info({'DOWN', MonRef, process, Pid, _Reason}, _X -> {noreply, State} end; -handle_info(_Info, State) -> +handle_info(Info, State) -> + logger:warning("Unexpected message: ~p", [Info]), {noreply, State}. code_change(_OldVsn, State, _Extra) -> @@ -189,10 +235,10 @@ compute_path(From, To) -> {ok, Labels}; {error, Reason} -> logger:warning("Failed to find a route from ~p to ~p", [From, To]), - {error, Reason} + {error, route_not_found} end. -route_from_labels(Source, Destination, Constraints, Labels) -> +routereq_from_labels(Source, Destination, Constraints, Labels) -> #{ source => Source, destination => Destination, @@ -207,6 +253,13 @@ route_from_labels(Source, Destination, Constraints, Labels) -> ] }. +routeinit_from_labels(Name, Source, Destination, Constraints, Binding, Labels) -> + Route = routereq_from_labels(Source, Destination, Constraints, Labels), + Route#{ + name => Name, + binding_label => Binding + }. + route_to_labels(#{steps := Steps}) -> [Sid#mpls_stack_entry.label || #{sid := Sid} <- Steps]. @@ -216,6 +269,9 @@ route_to_labels(#{steps := Steps}) -> session_update_flow(#state{bouncer = Pid}, SessPid, FlowId, Route, Args) -> Pid ! {update_flow, SessPid, FlowId, Route, Args}. +session_initiate_flow(#state{bouncer = Pid}, SessPid, Route, Args) -> + Pid ! {initiate_flow, SessPid, Route, Args}. + bouncer_start(#state{bouncer = undefined} = State) -> Self = self(), Pid = erlang:spawn_link(fun() -> @@ -238,5 +294,14 @@ bouncer_loop(Parent) -> {error, Reason} -> Parent ! {flow_update_error, FlowId, Reason, Args}, bouncer_loop(Parent) + end; + {initiate_flow, SessPid, InitRoute, Args} -> + case pcep_server_session:initiate_flow(SessPid, InitRoute) of + {ok, Flow} -> + Parent ! {flow_initiated, Flow, Args}, + bouncer_loop(Parent); + {error, Reason} -> + Parent ! {flow_init_error, Reason, Args}, + bouncer_loop(Parent) end end. diff --git a/src/te/rebar.lock b/src/te/rebar.lock index 6eb067ecd..ef9747fe4 100644 --- a/src/te/rebar.lock +++ b/src/te/rebar.lock @@ -11,11 +11,11 @@ {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}, {<<"pcep_codec">>, {git,"git@github.com:stritzinger/pcep_codec.git", - {ref,"3d1623fdf0c62d3daf400ac65aaf985f8bd40835"}}, + {ref,"84dcc430b2aa427984c100cc19dd35d946b22ff9"}}, 1}, {<<"pcep_server">>, {git,"git@github.com:stritzinger/pcep_server.git", - {ref,"2cf692e7e5fa2e9ac0fd54e5aa64ffb17f4f1b4a"}}, + {ref,"3910bf5546879cb91a3483008b6aed11753deaa6"}}, 0}, {<<"ranch">>,{pkg,<<"ranch">>,<<"2.0.0">>},1}]}. [ diff --git a/tutorial/2-6-netgen-topology.yml.template b/tutorial/2-6-netgen-topology.yml.template index 286f40005..d16e34841 100644 --- a/tutorial/2-6-netgen-topology.yml.template +++ b/tutorial/2-6-netgen-topology.yml.template @@ -103,17 +103,11 @@ routers: debug pathd pcep basic segment-routing traffic-eng - mpls-te on - policy color 1 endpoint 6.6.6.6 - name DEFAULT - binding-sid 1111 - candidate-path preference 100 name RUNTIME dynamic - ! - ! pcep pce-config CONFIG source-address ip 1.1.1.1 pce PCE + pce-initiated address ip ${PCE_IP} config CONFIG pcc @@ -438,16 +432,11 @@ routers: debug pathd pcep segment-routing traffic-eng - policy color 1 endpoint 1.1.1.1 - name DEFAULT - binding-sid 6666 - candidate-path preference 200 name RUNTIME dynamic - ! - ! pcep pce-config CONFIG source-address ip 6.6.6.6 pce PCE + pce-initiated address ip ${PCE_IP} config CONFIG pcc diff --git a/tutorial/2-6-te-demo-start-testbed.sh b/tutorial/2-6-te-demo-start-testbed.sh index 3266d1141..5ba48479c 100755 --- a/tutorial/2-6-te-demo-start-testbed.sh +++ b/tutorial/2-6-te-demo-start-testbed.sh @@ -41,7 +41,5 @@ cat "${ROOTDIR}/2-6-netgen-topology.yml.template" | envsubst > "${RUNDIR}/topolo sudo -i bash -c "\ cd ${RUNDIR}/netgen;\ - sysctl -w net.ipv6.conf.all.disable_ipv6=1;\ - sysctl -w net.ipv6.conf.default.disable_ipv6=1;\ sysctl -w net.ipv4.conf.all.rp_filter=0;\ PATH=/usr/lib/frr:\$PATH ./exe/netgen ../topology.yml -c ../config.yml" diff --git a/tutorial/2-6-te-demo.md b/tutorial/2-6-te-demo.md index 774a38bab..da822e3a3 100644 --- a/tutorial/2-6-te-demo.md +++ b/tutorial/2-6-te-demo.md @@ -89,3 +89,20 @@ Then in second console: $ sudo -i # cd /tmp/negen # ./tmux.sh + +### Setup a flow from the Erlang console + +We will setup two unidirectional flow between router 1 and 6. +We will use the binding label 1111 for the flow from router 1 to router 6, and the binding label 6666 for the flow from router 6 to router 1. + + $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -- /tfte/bin/tfte remote_console + 1> {ok, Flow1to6} = epce_server:initiate_flow(<<"foo">>, {1, 1, 1, 1}, {6, 6, 6, 6}, 1111). + 2> {ok, Flow6to1} = epce_server:initiate_flow(<<"bar">>, {6, 6, 6, 6}, {1, 1, 1, 1}, 6666). + +Now if we go to the tmux session src (Ctrl-B 0) we can ping dst: + + $ ping 9.9.9.2 + +From the Erlang console we can update the initiated flows to change the path the packets are flowing through: + + 3> epce_server:update_flow(Flow6to1, [16050, 16030, 16010]). -- GitLab From a85a7257552217d92e0e138cedd0e887d3b89b95 Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 5 Oct 2022 07:25:10 -0700 Subject: [PATCH 31/63] Add skeleton for TE service handler --- .../service_handler_api/FilterFields.py | 3 +- .../service/service_handlers/__init__.py | 7 + .../service_handlers/te/ConfigRules.py | 44 +++++ .../service_handlers/te/TEServiceHandler.py | 161 ++++++++++++++++++ .../service/service_handlers/te/__init__.py | 14 ++ tutorial/2-6-teraflow-service.json | 18 ++ 6 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 src/service/service/service_handlers/te/ConfigRules.py create mode 100644 src/service/service/service_handlers/te/TEServiceHandler.py create mode 100644 src/service/service/service_handlers/te/__init__.py create mode 100644 tutorial/2-6-teraflow-service.json diff --git a/src/service/service/service_handler_api/FilterFields.py b/src/service/service/service_handler_api/FilterFields.py index f86412a8c..1b22c5c42 100644 --- a/src/service/service/service_handler_api/FilterFields.py +++ b/src/service/service/service_handler_api/FilterFields.py @@ -23,7 +23,8 @@ SERVICE_TYPE_VALUES = { ServiceTypeEnum.SERVICETYPE_UNKNOWN, ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM, - ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE + ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, + ServiceTypeEnum.SERVICETYPE_TE, } DEVICE_DRIVER_VALUES = { diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py index 7ea0d4f62..48d88944d 100644 --- a/src/service/service/service_handlers/__init__.py +++ b/src/service/service/service_handlers/__init__.py @@ -24,6 +24,7 @@ from .microwave.MicrowaveServiceHandler import MicrowaveServiceHandler from .p4.p4_service_handler import P4ServiceHandler from .tapi_tapi.TapiServiceHandler import TapiServiceHandler from .tapi_xr.TapiXrServiceHandler import TapiXrServiceHandler +from .te.TEServiceHandler import TEServiceHandler SERVICE_HANDLERS = [ (L2NMEmulatedServiceHandler, [ @@ -86,4 +87,10 @@ SERVICE_HANDLERS = [ FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN], } ]), + (TEServiceHandler, [ + { + FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_TE, + FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_UNDEFINED, + } + ]), ] diff --git a/src/service/service/service_handlers/te/ConfigRules.py b/src/service/service/service_handlers/te/ConfigRules.py new file mode 100644 index 000000000..7b79fca5a --- /dev/null +++ b/src/service/service/service_handlers/te/ConfigRules.py @@ -0,0 +1,44 @@ +# 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 logging +from typing import Dict, List +from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set +from service.service.service_handler_api.AnyTreeTools import TreeNode + +LOGGER = logging.getLogger(__name__) + +def setup_config_rules( + service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, + service_settings : TreeNode, endpoint_settings : TreeNode +) -> List[Dict]: + + json_settings : Dict = {} if service_settings is None else service_settings.value + json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value + + json_config_rules = [ + ] + return json_config_rules + +def teardown_config_rules( + service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, + service_settings : TreeNode, endpoint_settings : TreeNode +) -> List[Dict]: + + json_settings : Dict = {} if service_settings is None else service_settings.value + json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value + + json_config_rules = [ + ] + return json_config_rules diff --git a/src/service/service/service_handlers/te/TEServiceHandler.py b/src/service/service/service_handlers/te/TEServiceHandler.py new file mode 100644 index 000000000..d12d642f0 --- /dev/null +++ b/src/service/service/service_handlers/te/TEServiceHandler.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 anytree, json, logging +from typing import Any, List, Optional, Tuple, Union +from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service +from common.tools.object_factory.Device import json_device_id +from common.type_checkers.Checkers import chk_length, chk_type +from service.service.service_handler_api._ServiceHandler import _ServiceHandler +from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value +from service.service.task_scheduler.TaskExecutor import TaskExecutor +from .ConfigRules import setup_config_rules, teardown_config_rules + +LOGGER = logging.getLogger(__name__) + +class TEServiceHandler(_ServiceHandler): + def __init__( # pylint: disable=super-init-not-called + self, service : Service, task_executor : TaskExecutor, **settings + ) -> None: + self.__service = service + self.__task_executor = task_executor # pylint: disable=unused-private-member + self.__resolver = anytree.Resolver(pathattr='name') + self.__config = TreeNode('.') + for config_rule in service.service_config.config_rules: + action = config_rule.action + if config_rule.WhichOneof('config_rule') != 'custom': continue + resource_key = config_rule.custom.resource_key + resource_value = config_rule.custom.resource_value + if action == ConfigActionEnum.CONFIGACTION_SET: + try: + resource_value = json.loads(resource_value) + except: # pylint: disable=bare-except + pass + set_subnode_value(self.__resolver, self.__config, resource_key, resource_value) + elif action == ConfigActionEnum.CONFIGACTION_DELETE: + delete_subnode(self.__resolver, self.__config, resource_key) + + def SetEndpoint( + self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None + ) -> List[Union[bool, Exception]]: + chk_type('endpoints', endpoints, list) + if len(endpoints) == 0: return [] + + service_uuid = self.__service.service_id.service_uuid.uuid + settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None) + + results = [] + for endpoint in endpoints: + try: + chk_type('endpoint', endpoint, (tuple, list)) + chk_length('endpoint', endpoint, min_length=2, max_length=3) + device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now + + endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid) + endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None) + + json_config_rules = setup_config_rules( + service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings) + + device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + for json_config_rule in json_config_rules: + device.device_config.config_rules.append(ConfigRule(**json_config_rule)) + self.__task_executor.configure_device(device) + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint))) + results.append(e) + + return results + + def DeleteEndpoint( + self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None + ) -> List[Union[bool, Exception]]: + chk_type('endpoints', endpoints, list) + if len(endpoints) == 0: return [] + + service_uuid = self.__service.service_id.service_uuid.uuid + settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None) + + results = [] + for endpoint in endpoints: + try: + chk_type('endpoint', endpoint, (tuple, list)) + chk_length('endpoint', endpoint, min_length=2, max_length=3) + device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now + + endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid) + endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None) + + json_config_rules = teardown_config_rules( + service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings) + + device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + for json_config_rule in json_config_rules: + device.device_config.config_rules.append(ConfigRule(**json_config_rule)) + self.__task_executor.configure_device(device) + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint))) + results.append(e) + + return results + + def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('constraints', constraints, list) + if len(constraints) == 0: return [] + + msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.' + LOGGER.warning(msg.format(str(constraints))) + return [True for _ in range(len(constraints))] + + def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('constraints', constraints, list) + if len(constraints) == 0: return [] + + msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.' + LOGGER.warning(msg.format(str(constraints))) + return [True for _ in range(len(constraints))] + + def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('resources', resources, list) + if len(resources) == 0: return [] + + results = [] + for resource in resources: + try: + resource_key, resource_value = resource + resource_value = json.loads(resource_value) + set_subnode_value(self.__resolver, self.__config, resource_key, resource_value) + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource))) + results.append(e) + + return results + + def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('resources', resources, list) + if len(resources) == 0: return [] + + results = [] + for resource in resources: + try: + resource_key, _ = resource + delete_subnode(self.__resolver, self.__config, resource_key) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource))) + results.append(e) + + return results diff --git a/src/service/service/service_handlers/te/__init__.py b/src/service/service/service_handlers/te/__init__.py new file mode 100644 index 000000000..70a332512 --- /dev/null +++ b/src/service/service/service_handlers/te/__init__.py @@ -0,0 +1,14 @@ +# 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. + diff --git a/tutorial/2-6-teraflow-service.json b/tutorial/2-6-teraflow-service.json new file mode 100644 index 000000000..6f8e1d52d --- /dev/null +++ b/tutorial/2-6-teraflow-service.json @@ -0,0 +1,18 @@ +{ + "services": [ + { + "service_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "service_uuid": {"uuid": "2c025055-bf6c-4250-8560-cf62f2d29e72"}}, + "service_type": 4, + "service_status": {"service_status": 2}, + "service_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid":"RT1"}}, "endpoint_uuid": {"uuid":"eth-src"}}, + {"device_id": {"device_uuid": {"uuid":"RT6"}}, "endpoint_uuid": {"uuid":"eth-dst"}} + ], + "service_constraints": [], + "service_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "/lsp[foo]", "resource_value": "{\"src\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT1\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-src\"}}, \"dest\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT6\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-dst\"}}, \"binding_label\": 1111}"}}, + {"action": 1, "custom": {"resource_key": "/lsp[bar]", "resource_value": "{\"src\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT6\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-dst\"}}, \"dest\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT1\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-src\"}}, \"binding_label\": 6666}"}} + ]} + } + ] +} \ No newline at end of file -- GitLab From 0cbc2779e2493f749a4e6d9a6f9f78c86994cfb4 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 23 Nov 2022 19:12:08 +0000 Subject: [PATCH 32/63] TE component: - arranged tests folder --- src/te/tests/deploy_specs.sh | 26 +++ .../te/tests/netgen-config.yml | 0 .../te/tests/netgen-topology.yml.template | 0 src/te/tests/service-descriptors.json | 24 ++ .../te/tests/start-testbed.sh | 10 +- src/te/tests/topology-descriptors.json | 201 +++++++++++++++++ tutorial/2-6-te-demo.md | 4 +- tutorial/2-6-teraflow-service.json | 18 -- tutorial/2-6-teraflow-topology.json | 210 ------------------ 9 files changed, 258 insertions(+), 235 deletions(-) create mode 100644 src/te/tests/deploy_specs.sh rename tutorial/2-6-netgen-config.yml => src/te/tests/netgen-config.yml (100%) rename tutorial/2-6-netgen-topology.yml.template => src/te/tests/netgen-topology.yml.template (100%) create mode 100644 src/te/tests/service-descriptors.json rename tutorial/2-6-te-demo-start-testbed.sh => src/te/tests/start-testbed.sh (79%) create mode 100644 src/te/tests/topology-descriptors.json delete mode 100644 tutorial/2-6-teraflow-service.json delete mode 100644 tutorial/2-6-teraflow-topology.json diff --git a/src/te/tests/deploy_specs.sh b/src/te/tests/deploy_specs.sh new file mode 100644 index 000000000..17dd44dd5 --- /dev/null +++ b/src/te/tests/deploy_specs.sh @@ -0,0 +1,26 @@ +# Set the URL of your local Docker registry where the images will be uploaded to. +export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" + +# Set the list of components, separated by spaces, you want to build images for, and deploy. +# Supported components are: +# context device automation policy service compute monitoring webui +# interdomain slice pathcomp dlt +# dbscanserving opticalattackmitigator opticalattackdetector +# l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector +export TFS_COMPONENTS="context device pathcomp service slice webui" # te + +# Set the tag you want to use for your images. +export TFS_IMAGE_TAG="dev" + +# Set the name of the Kubernetes namespace to deploy to. +export TFS_K8S_NAMESPACE="tfs" + +# Set additional manifest files to be applied after the deployment +export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml" + +# Set the new Grafana admin password +export TFS_GRAFANA_PASSWORD="admin123+" + +# If not already set, disable skip-build flag. +# If TFS_SKIP_BUILD is "YES", the containers are not rebuilt-retagged-repushed and existing ones are used. +export TFS_SKIP_BUILD=${TFS_SKIP_BUILD:-""} diff --git a/tutorial/2-6-netgen-config.yml b/src/te/tests/netgen-config.yml similarity index 100% rename from tutorial/2-6-netgen-config.yml rename to src/te/tests/netgen-config.yml diff --git a/tutorial/2-6-netgen-topology.yml.template b/src/te/tests/netgen-topology.yml.template similarity index 100% rename from tutorial/2-6-netgen-topology.yml.template rename to src/te/tests/netgen-topology.yml.template diff --git a/src/te/tests/service-descriptors.json b/src/te/tests/service-descriptors.json new file mode 100644 index 000000000..15023ac9d --- /dev/null +++ b/src/te/tests/service-descriptors.json @@ -0,0 +1,24 @@ +{ + "services": [ + { + "service_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "service_uuid": {"uuid": "2c025055-bf6c-4250-8560-cf62f2d29e72"} + }, + "service_type": 4, "service_status": {"service_status": 1}, + "service_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid":"RT1"}}, "endpoint_uuid": {"uuid":"eth-src"}}, + {"device_id": {"device_uuid": {"uuid":"RT6"}}, "endpoint_uuid": {"uuid":"eth-dst"}} + ], + "service_constraints": [], + "service_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "/lsp-fw", "resource_value": { + "symbolic_name": "foo", "binding_label": 1111 + }}}, + {"action": 1, "custom": {"resource_key": "/lsp-bw", "resource_value": { + "symbolic_name": "bar", "binding_label": 6666 + }}} + ]} + } + ] +} diff --git a/tutorial/2-6-te-demo-start-testbed.sh b/src/te/tests/start-testbed.sh similarity index 79% rename from tutorial/2-6-te-demo-start-testbed.sh rename to src/te/tests/start-testbed.sh index 5ba48479c..c480b3e75 100755 --- a/tutorial/2-6-te-demo-start-testbed.sh +++ b/src/te/tests/start-testbed.sh @@ -4,10 +4,10 @@ set -e ROOTDIR="$( cd $( dirname $0 ); pwd )" RUNDIR="$( pwd )" -NEGENDIR="${RUNDIR}/netgen" +NETGENDIR="${RUNDIR}/netgen" -if [[ ! -f "${NEGENDIR}/exe/netgen" ]]; then - echo "Failed to find Netgen binary at ${NEGENDIR}/exe/netgen" +if [[ ! -f "${NETGENDIR}/exe/netgen" ]]; then + echo "Failed to find Netgen binary at ${NETGENDIR}/exe/netgen" exit 1 fi @@ -36,8 +36,8 @@ export RT1_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.11" export RT6_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.12" export RT6_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.13" -cp "${ROOTDIR}/2-6-netgen-config.yml" "${RUNDIR}/config.yml" -cat "${ROOTDIR}/2-6-netgen-topology.yml.template" | envsubst > "${RUNDIR}/topology.yml" +cp "${ROOTDIR}/netgen-config.yml" "${RUNDIR}/config.yml" +cat "${ROOTDIR}/netgen-topology.yml.template" | envsubst > "${RUNDIR}/topology.yml" sudo -i bash -c "\ cd ${RUNDIR}/netgen;\ diff --git a/src/te/tests/topology-descriptors.json b/src/te/tests/topology-descriptors.json new file mode 100644 index 000000000..82f37ce29 --- /dev/null +++ b/src/te/tests/topology-descriptors.json @@ -0,0 +1,201 @@ +{ + "contexts": [ + { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_ids": [], + "service_ids": [] + } + ], + "topologies": [ + { + "topology_id": {"topology_uuid": {"uuid": "tetestbed"}, "context_id": {"context_uuid": {"uuid": "admin"}}}, + "device_ids": [ + {"device_uuid": {"uuid": "SW1"}}, + {"device_uuid": {"uuid": "RT1"}}, + {"device_uuid": {"uuid": "RT2"}}, + {"device_uuid": {"uuid": "RT3"}}, + {"device_uuid": {"uuid": "RT4"}}, + {"device_uuid": {"uuid": "RT5"}}, + {"device_uuid": {"uuid": "RT6"}} + ], + "link_ids": [ + {"link_uuid": {"uuid": "RT1/SW1"}}, + {"link_uuid": {"uuid": "RT2/SW1"}}, + {"link_uuid": {"uuid": "RT3/SW1"}}, + {"link_uuid": {"uuid": "RT2/RT4/1"}}, + {"link_uuid": {"uuid": "RT2/RT4/2"}}, + {"link_uuid": {"uuid": "RT3/RT5/1"}}, + {"link_uuid": {"uuid": "RT3/RT5/2"}}, + {"link_uuid": {"uuid": "RT4/RT5"}}, + {"link_uuid": {"uuid": "RT4/RT6"}}, + {"link_uuid": {"uuid": "RT5/RT6"}} + ] + } + ], + "devices": [ + { + "device_id": {"device_uuid": {"uuid": "SW1"}}, "device_type": "emu-packet-switch", + "device_operational_status": 2, "device_drivers": [0], "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "df8bb169-2013-4b82-9455-69777f7a01d6"}, + {"sample_types": [], "type": "copper", "uuid": "061119c1-2aa4-48e9-be64-3ddf465fc80a"}, + {"sample_types": [], "type": "copper", "uuid": "495ea3f8-e67f-46a0-84bd-a230a4b7067d"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "RT1"}}, "device_type": "emu-packet-router", + "device_operational_status": 2, "device_drivers": [0], "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "1.1.1.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "eth-src"}, + {"sample_types": [], "type": "copper", "uuid": "eth-sw1"} + ]}}}, + {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16010"}}, + {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "1.1.1.1"}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "RT2"}}, "device_type": "emu-packet-router", + "device_operational_status": 2, "device_drivers": [0], "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "2.2.2.2"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "eth-sw1"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt4-1"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt4-2"} + ]}}}, + {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16020"}}, + {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "2.2.2.2"}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "RT3"}}, "device_type": "emu-packet-router", + "device_operational_status": 2, "device_drivers": [0], "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "3.3.3.3"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "eth-sw1"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt5-1"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt5-2"} + ]}}}, + {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16030"}}, + {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "3.3.3.3"}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "RT4"}}, "device_type": "emu-packet-router", + "device_operational_status": 2, "device_drivers": [0], "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "4.4.4.4"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "eth-rt2-1"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt2-2"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt5"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt6"} + ]}}}, + {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16040"}}, + {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "4.4.4.4"}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "RT5"}}, "device_type": "emu-packet-router", + "device_operational_status": 2, "device_drivers": [0], "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "5.5.5.5"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "eth-rt3-1"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt3-2"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt4"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt6"} + ]}}}, + {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16050"}}, + {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "5.5.5.5"}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "RT6"}}, "device_type": "emu-packet-router", + "device_operational_status": 2, "device_drivers": [0], "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "6.6.6.6"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "eth-rt4"}, + {"sample_types": [], "type": "copper", "uuid": "eth-rt5"}, + {"sample_types": [], "type": "copper", "uuid": "eth-dst"} + ]}}}, + {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16060"}}, + {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "6.6.6.6"}} + ]} + } + ], + "links": [ + { + "link_id": {"link_uuid": {"uuid": "RT1/SW1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT1"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, + {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "df8bb169-2013-4b82-9455-69777f7a01d6"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT2/SW1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, + {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "061119c1-2aa4-48e9-be64-3ddf465fc80a"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT3/SW1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, + {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "495ea3f8-e67f-46a0-84bd-a230a4b7067d"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT2/RT4/1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-rt4-1"}}, + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt2-1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT2/RT4/2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-rt4-2"}}, + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt2-2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT3/RT5/1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-rt5-1"}}, + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt3-1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT3/RT5/2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-rt5-2"}}, + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt3-2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT4/RT5"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt5"}}, + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt4"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT4/RT6"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt6"}}, + {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-rt4"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "RT5/RT6"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt6"}}, + {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-rt5"}} + ] + } + ] +} diff --git a/tutorial/2-6-te-demo.md b/tutorial/2-6-te-demo.md index da822e3a3..9bfd7881c 100644 --- a/tutorial/2-6-te-demo.md +++ b/tutorial/2-6-te-demo.md @@ -78,12 +78,12 @@ ### Run the Test-Bed -First load the [teraflow configuration file](./2-6-teraflow-topology.json) using the webui. +First load the [teraflow configuration file](~/tfs-ctrl/src/te/tests/topology-descriptors.json) using the webui. The first time the configuration is loaded may partialy fail because it tries to load the topology before the devices and links, but a second load should work. In first console: $ cd ~/testbed - $ ../tfs-ctrl/tutorial/2-6-te-demo-start-testbed.sh + $ ../tfs-ctrl/src/te/tests/start-testbed.sh Then in second console: $ sudo -i diff --git a/tutorial/2-6-teraflow-service.json b/tutorial/2-6-teraflow-service.json deleted file mode 100644 index 6f8e1d52d..000000000 --- a/tutorial/2-6-teraflow-service.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "services": [ - { - "service_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "service_uuid": {"uuid": "2c025055-bf6c-4250-8560-cf62f2d29e72"}}, - "service_type": 4, - "service_status": {"service_status": 2}, - "service_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid":"RT1"}}, "endpoint_uuid": {"uuid":"eth-src"}}, - {"device_id": {"device_uuid": {"uuid":"RT6"}}, "endpoint_uuid": {"uuid":"eth-dst"}} - ], - "service_constraints": [], - "service_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "/lsp[foo]", "resource_value": "{\"src\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT1\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-src\"}}, \"dest\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT6\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-dst\"}}, \"binding_label\": 1111}"}}, - {"action": 1, "custom": {"resource_key": "/lsp[bar]", "resource_value": "{\"src\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT6\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-dst\"}}, \"dest\": {\"device_id\": {\"device_uuid\": {\"uuid\":\"RT1\"}}, \"endpoint_uuid\": {\"uuid\":\"eth-src\"}}, \"binding_label\": 6666}"}} - ]} - } - ] -} \ No newline at end of file diff --git a/tutorial/2-6-teraflow-topology.json b/tutorial/2-6-teraflow-topology.json deleted file mode 100644 index 8b5188866..000000000 --- a/tutorial/2-6-teraflow-topology.json +++ /dev/null @@ -1,210 +0,0 @@ -{ - "contexts": [ - { - "context_id": {"context_uuid": {"uuid": "admin"}}, - "topology_ids": [], - "service_ids": [] - } - ], - "topologies": [ - { - "topology_id": {"topology_uuid": {"uuid": "tetestbed"}, "context_id": {"context_uuid": {"uuid": "admin"}}}, - "device_ids": [ - {"device_uuid": {"uuid": "SW1"}}, - {"device_uuid": {"uuid": "RT1"}}, - {"device_uuid": {"uuid": "RT2"}}, - {"device_uuid": {"uuid": "RT3"}}, - {"device_uuid": {"uuid": "RT4"}}, - {"device_uuid": {"uuid": "RT5"}}, - {"device_uuid": {"uuid": "RT6"}} - ], - "link_ids": [ - {"link_uuid": {"uuid": "RT1/SW1"}}, - {"link_uuid": {"uuid": "RT2/SW1"}}, - {"link_uuid": {"uuid": "RT3/SW1"}}, - {"link_uuid": {"uuid": "RT2/RT4/1"}}, - {"link_uuid": {"uuid": "RT2/RT4/2"}}, - {"link_uuid": {"uuid": "RT3/RT5/1"}}, - {"link_uuid": {"uuid": "RT3/RT5/2"}}, - {"link_uuid": {"uuid": "RT4/RT5"}}, - {"link_uuid": {"uuid": "RT4/RT6"}}, - {"link_uuid": {"uuid": "RT5/RT6"}} - ] - } - ], - "devices": [ - { - "device_id": {"device_uuid": {"uuid": "SW1"}}, - "device_type": "emu-packet-switch", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": - "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"df8bb169-2013-4b82-9455-69777f7a01d6\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"061119c1-2aa4-48e9-be64-3ddf465fc80a\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"495ea3f8-e67f-46a0-84bd-a230a4b7067d\"}]}"}} - ]}, - "device_operational_status": 1, - "device_drivers": [0], - "device_endpoints": [] - }, - { - "device_id": {"device_uuid": {"uuid": "RT1"}}, - "device_type": "emu-packet-router", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16010"}}, - {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "1.1.1.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "1.1.1.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": - "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-src\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}]}"}} - ]}, - "device_operational_status": 1, - "device_drivers": [0], - "device_endpoints": [] - }, - { - "device_id": {"device_uuid": {"uuid": "RT2"}}, - "device_type": "emu-packet-router", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16020"}}, - {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "2.2.2.2"}}, - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "2.2.2.2"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": - "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4-2\"}]}"}} - ]}, - "device_operational_status": 1, - "device_drivers": [0], - "device_endpoints": [] - }, - { - "device_id": {"device_uuid": {"uuid": "RT3"}}, - "device_type": "emu-packet-router", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16030"}}, - {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "3.3.3.3"}}, - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "3.3.3.3"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": - "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5-2\"}]}"}} - ]}, - "device_operational_status": 1, - "device_drivers": [0], - "device_endpoints": [] - }, - { - "device_id": {"device_uuid": {"uuid": "RT4"}}, - "device_type": "emu-packet-router", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16040"}}, - {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "4.4.4.4"}}, - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "4.4.4.4"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": - "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt2-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt2-2\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt6\"}]}"}} - ]}, - "device_operational_status": 1, - "device_drivers": [0], - "device_endpoints": [] - }, - { - "device_id": {"device_uuid": {"uuid": "RT5"}}, - "device_type": "emu-packet-router", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16050"}}, - {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "5.5.5.5"}}, - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "5.5.5.5"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": - "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt3-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt3-2\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt6\"}]}"}} - ]}, - "device_operational_status": 1, - "device_drivers": [0], - "device_endpoints": [] - }, - { - "device_id": {"device_uuid": {"uuid": "RT6"}}, - "device_type": "emu-packet-router", - "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16060"}}, - {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "6.6.6.6"}}, - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "6.6.6.6"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": - "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-dst\"}]}"}} - ]}, - "device_operational_status": 1, - "device_drivers": [0], - "device_endpoints": [] - } - ], - "links": [ - { - "link_id": {"link_uuid": {"uuid": "RT1/SW1"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT1"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, - {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "df8bb169-2013-4b82-9455-69777f7a01d6"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT2/SW1"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, - {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "061119c1-2aa4-48e9-be64-3ddf465fc80a"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT3/SW1"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-sw1"}}, - {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "495ea3f8-e67f-46a0-84bd-a230a4b7067d"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT2/RT4/1"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-rt4-1"}}, - {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt2-1"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT2/RT4/2"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-rt4-2"}}, - {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt2-2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT3/RT5/1"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-rt5-1"}}, - {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt3-1"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT3/RT5/2"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-rt5-2"}}, - {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt3-2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT4/RT5"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt5"}}, - {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt4"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT4/RT6"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt6"}}, - {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-rt4"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "RT5/RT6"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt6"}}, - {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-rt5"}} - ] - } - ] -} -- GitLab From dfd1f21e4f1bb6961f88e6d07661d46f0e7779bf Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 23 Nov 2022 19:12:26 +0000 Subject: [PATCH 33/63] Common: - added TE service constants --- src/common/Constants.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/Constants.py b/src/common/Constants.py index ed1c1475a..423f2558b 100644 --- a/src/common/Constants.py +++ b/src/common/Constants.py @@ -56,6 +56,7 @@ class ServiceNameEnum(Enum): OPTICALATTACKDETECTOR = 'opticalattackdetector' OPTICALATTACKMITIGATOR = 'opticalattackmitigator' CACHING = 'caching' + TE = 'te' # Used for test and debugging only DLT_GATEWAY = 'dltgateway' @@ -80,6 +81,7 @@ DEFAULT_SERVICE_GRPC_PORTS = { ServiceNameEnum.OPTICALATTACKMANAGER .value : 10005, ServiceNameEnum.INTERDOMAIN .value : 10010, ServiceNameEnum.PATHCOMP .value : 10020, + ServiceNameEnum.TE .value : 10030, # Used for test and debugging only ServiceNameEnum.DLT_GATEWAY .value : 50051, -- GitLab From d0291925c26a1605879ecc1da8b10f7f3c727d54 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 23 Nov 2022 19:13:28 +0000 Subject: [PATCH 34/63] Service component: - added TEServiceClient - added branch to redirect requests to TE component for TE services --- src/service/client/TEServiceClient.py | 67 ++++++++++++ .../service/ServiceServiceServicerImpl.py | 103 +++++++++++++----- 2 files changed, 140 insertions(+), 30 deletions(-) create mode 100644 src/service/client/TEServiceClient.py diff --git a/src/service/client/TEServiceClient.py b/src/service/client/TEServiceClient.py new file mode 100644 index 000000000..8bbbe8f9b --- /dev/null +++ b/src/service/client/TEServiceClient.py @@ -0,0 +1,67 @@ +# 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, logging +from common.Constants import ServiceNameEnum +from common.Settings import get_service_host, get_service_port_grpc +from common.proto.context_pb2 import Empty, Service, ServiceId, ServiceStatus +from common.proto.te_pb2_grpc import TEServiceStub +from common.tools.client.RetryDecorator import retry, delay_exponential +from common.tools.grpc.Tools import grpc_message_to_json_string + +LOGGER = logging.getLogger(__name__) +MAX_RETRIES = 15 +DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0) +RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') + +class TEServiceClient: + def __init__(self, host=None, port=None): + if not host: host = get_service_host(ServiceNameEnum.TE) + if not port: port = get_service_port_grpc(ServiceNameEnum.TE) + self.endpoint = '{:s}:{:s}'.format(str(host), str(port)) + LOGGER.debug('Creating channel to {:s}...'.format(str(self.endpoint))) + self.channel = None + self.stub = None + self.connect() + LOGGER.debug('Channel created') + + def connect(self): + self.channel = grpc.insecure_channel(self.endpoint) + self.stub = TEServiceStub(self.channel) + + def close(self): + if self.channel is not None: self.channel.close() + self.channel = None + self.stub = None + + @RETRY_DECORATOR + def RequestLSP(self, request : Service) -> ServiceStatus: + LOGGER.debug('RequestLSP request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.RequestLSP(request) + LOGGER.debug('RequestLSP result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def UpdateLSP(self, request : ServiceId) -> ServiceStatus: + LOGGER.debug('UpdateLSP request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.UpdateLSP(request) + LOGGER.debug('UpdateLSP result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def DeleteLSP(self, request : ServiceId) -> Empty: + LOGGER.debug('DeleteLSP request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.DeleteLSP(request) + LOGGER.debug('DeleteLSP result: {:s}'.format(grpc_message_to_json_string(response))) + return response diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py index 141c6cb3c..2a6f88152 100644 --- a/src/service/service/ServiceServiceServicerImpl.py +++ b/src/service/service/ServiceServiceServicerImpl.py @@ -27,6 +27,7 @@ from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_s from context.client.ContextClient import ContextClient from pathcomp.frontend.client.PathCompClient import PathCompClient from service.service.tools.ConnectionToString import connection_to_string +from service.client.TEServiceClient import TEServiceClient from .service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory from .task_scheduler.TaskScheduler import TasksScheduler from .tools.GeodesicDistance import gps_distance @@ -168,37 +169,71 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): context_client, service_id_with_uuids, rw_copy=False, include_config_rules=True, include_constraints=True, include_endpoint_ids=True) - num_disjoint_paths = 0 - for constraint in request.service_constraints: - if constraint.WhichOneof('constraint') == 'sla_availability': - num_disjoint_paths = constraint.sla_availability.num_disjoint_paths - break - - num_disjoint_paths = 1 if num_disjoint_paths is None or num_disjoint_paths == 0 else num_disjoint_paths - num_expected_endpoints = num_disjoint_paths * 2 + if service.service_type == ServiceTypeEnum.SERVICETYPE_TE: + # TE service: + te_service_client = TEServiceClient() + + # Note: TE should update the service in Context. + # By now we update it manually for debugging purposes + service = Service() + service.CopyFrom(request) + service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED + context_client.SetService(request) + + service_status = te_service_client.RequestLSP(service) + + if service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE: + _service : Optional[Service] = get_service(context_client, request.service_id) + service = Service() + service.CopyFrom(_service) + service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_ACTIVE + context_client.SetService(service) + else: + MSG = 'RequestLSP for Service({:s}) returned ServiceStatus({:s})' + context_uuid = request.service_id.context_id.context_uuid.uuid + service_uuid = request.service_id.service_uuid.uuid + service_key = '{:s}/{:s}'.format(context_uuid, service_uuid) + str_service_status = ServiceStatusEnum.Name(service_status.service_status) + raise Exception(MSG.format(service_key, str_service_status)) + else: + # Normal service: + num_disjoint_paths = None + for constraint in request.service_constraints: + if constraint.WhichOneof('constraint') == 'sla_availability': + num_disjoint_paths = constraint.sla_availability.num_disjoint_paths + break + + num_disjoint_paths = 1 if num_disjoint_paths is None or num_disjoint_paths == 0 else num_disjoint_paths + num_expected_endpoints = num_disjoint_paths * 2 tasks_scheduler = TasksScheduler(self.service_handler_factory) if len(service_with_uuids.service_endpoint_ids) >= num_expected_endpoints: pathcomp_request = PathCompRequest() pathcomp_request.services.append(service_with_uuids) # pylint: disable=no-member - if num_disjoint_paths is None or num_disjoint_paths in {0, 1}: - pathcomp_request.shortest_path.Clear() # pylint: disable=no-member - else: - pathcomp_request.k_disjoint_path.num_disjoint = num_disjoint_paths # pylint: disable=no-member - - LOGGER.debug('pathcomp_request={:s}'.format(grpc_message_to_json_string(pathcomp_request))) - pathcomp = PathCompClient() - pathcomp_reply = pathcomp.Compute(pathcomp_request) - pathcomp.close() - LOGGER.debug('pathcomp_reply={:s}'.format(grpc_message_to_json_string(pathcomp_reply))) + tasks_scheduler = TasksScheduler(self.service_handler_factory) + if len(service_with_uuids.service_endpoint_ids) >= num_expected_endpoints: + pathcomp_request = PathCompRequest() + pathcomp_request.services.append(service_with_uuids) # pylint: disable=no-member + + if num_disjoint_paths is None or num_disjoint_paths in {0, 1}: + pathcomp_request.shortest_path.Clear() # pylint: disable=no-member + else: + pathcomp_request.k_disjoint_path.num_disjoint = num_disjoint_paths # pylint: disable=no-member + + LOGGER.info('pathcomp_request={:s}'.format(grpc_message_to_json_string(pathcomp_request))) + pathcomp = PathCompClient() + pathcomp_reply = pathcomp.Compute(pathcomp_request) + pathcomp.close() + LOGGER.info('pathcomp_reply={:s}'.format(grpc_message_to_json_string(pathcomp_reply))) + + # Feed TaskScheduler with this path computation reply. TaskScheduler identifies inter-dependencies among + # the services and connections retrieved and produces a schedule of tasks (an ordered list of tasks to be + # executed) to implement the requested create/update operation. + tasks_scheduler.compose_from_pathcompreply(pathcomp_reply, is_delete=False) - # Feed TaskScheduler with this path computation reply. TaskScheduler identifies inter-dependencies among - # the services and connections retrieved and produces a schedule of tasks (an ordered list of tasks to be - # executed) to implement the requested create/update operation. - tasks_scheduler.compose_from_pathcompreply(pathcomp_reply, is_delete=False) + tasks_scheduler.execute_all() - tasks_scheduler.execute_all() return service_with_uuids.service_id @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) @@ -213,12 +248,20 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PENDING_REMOVAL context_client.SetService(service) - # Feed TaskScheduler with this service and the sub-services and sub-connections related to this service. - # TaskScheduler identifies inter-dependencies among them and produces a schedule of tasks (an ordered list of - # tasks to be executed) to implement the requested delete operation. - tasks_scheduler = TasksScheduler(self.service_handler_factory) - tasks_scheduler.compose_from_service(service, is_delete=True) - tasks_scheduler.execute_all() + if service.service_type == ServiceTypeEnum.SERVICETYPE_TE: + # TE service + te_service_client = TEServiceClient() + te_service_client.DeleteLSP(request) + context_client.RemoveService(request) + else: + # Normal service + # Feed TaskScheduler with this service and the sub-services and sub-connections related to this service. + # TaskScheduler identifies inter-dependencies among them and produces a schedule of tasks (an ordered list of + # tasks to be executed) to implement the requested delete operation. + tasks_scheduler = TasksScheduler(self.service_handler_factory) + tasks_scheduler.compose_from_service(service, is_delete=True) + tasks_scheduler.execute_all() + return Empty() @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) @@ -376,4 +419,4 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): updated_service_with_uuids, old_connection, new_connection) tasks_scheduler.execute_all() - return Empty() + return Empty() \ No newline at end of file -- GitLab From 9134f14b4fdeb8d7ef1b2ab4079542a7989eb7ad Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 23 Nov 2022 19:29:12 +0000 Subject: [PATCH 35/63] Service component: - increased Log level to DEBUG --- manifests/serviceservice.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifests/serviceservice.yaml b/manifests/serviceservice.yaml index 7d7bdaa4e..3865fd6c0 100644 --- a/manifests/serviceservice.yaml +++ b/manifests/serviceservice.yaml @@ -36,7 +36,7 @@ spec: - containerPort: 9192 env: - name: LOG_LEVEL - value: "INFO" + value: "DEBUG" readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:3030"] -- GitLab From 8bcedb8dae80a00b106999e37bafb3edc36c486d Mon Sep 17 00:00:00 2001 From: merle Date: Fri, 25 Nov 2022 04:09:17 -0800 Subject: [PATCH 36/63] Details a bit more the Erlang environment setup documentation --- tutorial/1-6-setup-erlang-environmnet.md | 24 ++++++++++++++++-------- tutorial/2-6-te-demo.md | 4 ++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/tutorial/1-6-setup-erlang-environmnet.md b/tutorial/1-6-setup-erlang-environmnet.md index 11e65a49a..2d1519a6f 100644 --- a/tutorial/1-6-setup-erlang-environmnet.md +++ b/tutorial/1-6-setup-erlang-environmnet.md @@ -1,6 +1,9 @@ # 1.5. Setup Erlang Environment -First we need to install Erlang. There is multiple way, for development we will be using asdf. +First we need to install Erlang. There is multiple way, for development we will +be using *ASDF*, a tool that allows the installation of multiple version of Erlang +at the same time, and switch from one version to the other at will. + ## 1.5.1. Setup Erlang using asdf @@ -8,31 +11,36 @@ First, install any missing dependencies: sudo apt install curl git autoconf libncurses-dev build-essential m4 libssl-dev -Download asdf: +Download *ASDF* tool to the local account: git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.10.2 -Make asdf activate on login by adding these lines at the end of the `~/.bashrc` file: +Make *ASDF* activate on login by adding these lines at the end of the `~/.bashrc` file: . $HOME/.asdf/asdf.sh . $HOME/.asdf/completions/asdf.bash -Logout and log back in to activate asdf. +Logout and log back in to activate *ASDF*. -Install asdf plugin for Erlang: +*ASDF* supports multiple tools by installing there corresponding plugins. +Install *ASDF* plugin for Erlang: asdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git -Install Erlang: +Install a version of Erlang: asdf install erlang 24.3.4.2 -Activate Erlang locally for TFS controller: +Activate Erlang locally for TFS controller. This will create a local file +called `.tool-versions` defining which version of the tools to use when +running under the current directory: cd tfs-ctrl/ asdf local erlang 24.3.4.2 -Install rebar3: +Erlang projects uses a build tool called rebar3. It is used to manager project +dependenecies, compile a project and generate project releases. +Install rebar3 localy from source: cd ~ git clone https://github.com/erlang/rebar3.git diff --git a/tutorial/2-6-te-demo.md b/tutorial/2-6-te-demo.md index 9bfd7881c..778d62e06 100644 --- a/tutorial/2-6-te-demo.md +++ b/tutorial/2-6-te-demo.md @@ -1,4 +1,4 @@ -# 2.6. Traffic Engineering Demo (PENDING) +# 2.6. Traffic Engineering Demo (WIP) ## Setup Test-Bed @@ -20,7 +20,7 @@ ### Setup Free Range Routing $ sudo apt update - $ sudo apt-get install git autoconf automake libtool make libreadline-dev texinfo pkg-config libpam0g-dev libjson-c-dev bison flex libc-ares-dev python3-dev python3-sphinx install-info build-essential libsnmp-dev perl libcap-dev python2 libelf-dev libunwind-dev protobuf-c-compiler libprotobuf-c-dev libsystemd-dev + $ sudo apt-get install git autoconf automake libtool make libreadline-dev texinfo pkg-config libpam0g-dev libjson-c-dev bison flex libc-ares-dev python3-dev python3-sphinx install-info build-essential libsnmp-dev perl libcap-dev python2 libelf-dev libunwind-dev protobuf-c-compiler libprotobuf-c-dev libsystemd-dev $ mkdir -p ~/testbed $ cd ~/testbed $ git clone git@github.com:opensourcerouting/frr.git -- GitLab From b80968828d22db72552e9082a03f6254419d4f06 Mon Sep 17 00:00:00 2001 From: merle Date: Thu, 1 Dec 2022 08:41:19 -0800 Subject: [PATCH 37/63] te: Fix dependencies --- src/te/Dockerfile | 1 + src/te/apps/epce/src/epce_server.erl | 3 ++- src/te/apps/tfte/src/tfte_app.erl | 2 +- src/te/rebar.config | 2 +- src/te/rebar.lock | 10 +++++----- src/te/tests/deploy_specs.sh | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/te/Dockerfile b/src/te/Dockerfile index f580ccf4e..98194f254 100644 --- a/src/te/Dockerfile +++ b/src/te/Dockerfile @@ -35,6 +35,7 @@ FROM alpine # Install some libs RUN apk add --no-cache libgcc libstdc++ && \ apk add --no-cache openssl && \ + apk add --no-cache libcrypto1.1 && \ apk add --no-cache ncurses-libs # Install the released application diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl index 507255385..03f1dacb2 100644 --- a/src/te/apps/epce/src/epce_server.erl +++ b/src/te/apps/epce/src/epce_server.erl @@ -234,7 +234,8 @@ compute_path(From, To) -> logger:debug("Route from ~p to ~p: ~p", [From, To, Labels]), {ok, Labels}; {error, Reason} -> - logger:warning("Failed to find a route from ~p to ~p", [From, To]), + logger:warning("Failed to find a route from ~p to ~p: ~p", + [From, To, Reason]), {error, route_not_found} end. diff --git a/src/te/apps/tfte/src/tfte_app.erl b/src/te/apps/tfte/src/tfte_app.erl index 96724904d..12f8cd4fc 100644 --- a/src/te/apps/tfte/src/tfte_app.erl +++ b/src/te/apps/tfte/src/tfte_app.erl @@ -57,7 +57,7 @@ add_services([{Name, EndpointsSpecs, GrpcOpts} | Rest]) -> end catch throw:{Name, Reason, Extra} -> - ?LOG_WARNING("Failed to resolve ~s service configuration: ~s ~p ~p", + ?LOG_WARNING("Failed to resolve ~s service configuration: ~s ~p", [Name, Reason, Extra]) end, add_services(Rest). diff --git a/src/te/rebar.config b/src/te/rebar.config index 3451955c2..776a80249 100644 --- a/src/te/rebar.config +++ b/src/te/rebar.config @@ -2,7 +2,7 @@ {deps, [ grpcbox, - {pcep_server, {git, "git@github.com:stritzinger/pcep_server.git", {branch, "master"}}} + {pcep_server, {git, "https://github.com/stritzinger/pcep_server.git", {branch, "master"}}} ]}. {shell, [ diff --git a/src/te/rebar.lock b/src/te/rebar.lock index ef9747fe4..446884e6b 100644 --- a/src/te/rebar.lock +++ b/src/te/rebar.lock @@ -2,7 +2,7 @@ [{<<"acceptor_pool">>,{pkg,<<"acceptor_pool">>,<<"1.0.0">>},1}, {<<"chatterbox">>,{pkg,<<"ts_chatterbox">>,<<"0.12.0">>},1}, {<<"codec_sequencer">>, - {git,"git@github.com:stritzinger/codec_sequencer.git", + {git,"https://github.com/stritzinger/codec_sequencer.git", {ref,"fc8760894f7962ef1497bf6ce4247eb75db9d5ca"}}, 2}, {<<"ctx">>,{pkg,<<"ctx">>,<<"0.6.0">>},1}, @@ -10,12 +10,12 @@ {<<"grpcbox">>,{pkg,<<"grpcbox">>,<<"0.15.0">>},0}, {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}, {<<"pcep_codec">>, - {git,"git@github.com:stritzinger/pcep_codec.git", - {ref,"84dcc430b2aa427984c100cc19dd35d946b22ff9"}}, + {git,"https://github.com/stritzinger/pcep_codec.git", + {ref,"ca5eb0822d9971ec4bcfb427a49b2e516081a126"}}, 1}, {<<"pcep_server">>, - {git,"git@github.com:stritzinger/pcep_server.git", - {ref,"3910bf5546879cb91a3483008b6aed11753deaa6"}}, + {git,"https://github.com/stritzinger/pcep_server.git", + {ref,"ea751fa807f4c1f5635f781431fe384610166b0a"}}, 0}, {<<"ranch">>,{pkg,<<"ranch">>,<<"2.0.0">>},1}]}. [ diff --git a/src/te/tests/deploy_specs.sh b/src/te/tests/deploy_specs.sh index 17dd44dd5..cd8871a05 100644 --- a/src/te/tests/deploy_specs.sh +++ b/src/te/tests/deploy_specs.sh @@ -7,7 +7,7 @@ export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" # interdomain slice pathcomp dlt # dbscanserving opticalattackmitigator opticalattackdetector # l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector -export TFS_COMPONENTS="context device pathcomp service slice webui" # te +export TFS_COMPONENTS="context device pathcomp service slice webui te" # Set the tag you want to use for your images. export TFS_IMAGE_TAG="dev" -- GitLab From a01a91e54d640304f81adf75b85cf40ddfec46fe Mon Sep 17 00:00:00 2001 From: merle Date: Thu, 15 Dec 2022 06:23:41 -0800 Subject: [PATCH 38/63] First simple integration with the service component --- src/te/apps/epce/src/epce_server.erl | 68 ++++++++++++++++-------- src/te/apps/epce/src/epce_ted.erl | 20 +++++++ src/te/apps/tfte/src/tfte.app.src | 1 + src/te/apps/tfte/src/tfte_server.erl | 59 ++++++++++++++++++++ src/te/apps/tfte/src/tfte_te_service.erl | 23 +++++--- src/te/apps/tfte/src/tfte_topology.erl | 36 ++++++------- src/te/apps/tfte/src/tfte_util.erl | 27 ++++++++++ src/te/rebar.config | 2 + src/te/rebar.lock | 3 ++ 9 files changed, 188 insertions(+), 51 deletions(-) create mode 100644 src/te/apps/tfte/src/tfte_util.erl diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl index 03f1dacb2..8996d3b35 100644 --- a/src/te/apps/epce/src/epce_server.erl +++ b/src/te/apps/epce/src/epce_server.erl @@ -67,8 +67,8 @@ get_flows() -> update_flow(FlowId, LabelStack) -> gen_server:call(?MODULE, {update_flow, FlowId, LabelStack}). -initiate_flow(Name, FromAddr, ToAddr, BindingLabel) -> - gen_server:call(?MODULE, {initiate_flow, Name, FromAddr, ToAddr, +initiate_flow(Name, From, To, BindingLabel) -> + gen_server:call(?MODULE, {initiate_flow, Name, From, To, BindingLabel}, ?LARGE_TIMEOUT). @@ -111,19 +111,26 @@ handle_call({update_flow, FlowId, Labels}, From, {noreply, State} end end; -handle_call({initiate_flow, Name, FromAddr, ToAddr, Binding}, From, +handle_call({initiate_flow, Name, FromKey, ToKey, Binding}, From, #state{sessions = SessMap} = State) -> - case maps:find(FromAddr, SessMap) of - error -> {reply, {error, session_not_found}, State}; - {ok, #sess{pid = Pid}} -> - case compute_path(FromAddr, ToAddr) of - {error, Reason} -> - {reply, {error, Reason}, State}; - {ok, Labels} -> - InitRoute = routeinit_from_labels(Name, FromAddr, ToAddr, - [], Binding, Labels), - session_initiate_flow(State, Pid, InitRoute, From), - {noreply, State} + case {pcc_address(FromKey), pcc_address(ToKey)} of + {{error, Reason}, _} -> + {reply, {error, Reason}, State}; + {_, {error, Reason}} -> + {reply, {error, Reason}, State}; + {{ok, FromAddr}, {ok, ToAddr}} -> + case maps:find(FromAddr, SessMap) of + error -> {reply, {error, session_not_found}, State}; + {ok, #sess{pid = Pid}} -> + case compute_path(FromAddr, ToAddr) of + {error, Reason} -> + {reply, {error, Reason}, State}; + {ok, Labels} -> + InitRoute = routeinit_from_labels(Name, FromAddr, + ToAddr, [], Binding, Labels), + session_initiate_flow(State, Pid, InitRoute, From), + {noreply, State} + end end end; handle_call({session_opened, Id, Caps, Pid}, _From, @@ -227,18 +234,33 @@ terminate(_Reason, _State) -> %%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -compute_path(From, To) -> - case epce_ted:compute_path(pcc_address, From, To) of - {ok, Devices} -> - Labels = tl([L || #{mpls_label := L} <- Devices, L =/= undefined]), - logger:debug("Route from ~p to ~p: ~p", [From, To, Labels]), - {ok, Labels}; +ted_index(Id) when is_binary(Id) -> id; +ted_index({_, _, _, _}) -> pcc_address. + +pcc_address(Key) -> + case epce_ted:lookup(ted_index(Key), Key) of {error, Reason} -> - logger:warning("Failed to find a route from ~p to ~p: ~p", - [From, To, Reason]), - {error, route_not_found} + logger:warning("Failed to find a PCC address for router ~p: ~p", + [Key, Reason]), + {error, router_not_found}; + {ok, #{pcc_address := Addr}} -> + {ok, Addr} end. +compute_path(From, To) when is_binary(From), is_binary(To) -> + compute_path_result(From, To, epce_ted:compute_path(id, From, To)); +compute_path({_, _, _, _} = From, {_, _, _, _} = To) -> + compute_path_result(From, To, epce_ted:compute_path(pcc_address, From, To)). + +compute_path_result(From, To, {error, Reason}) -> + logger:warning("Failed to find a route from ~p to ~p: ~p", + [From, To, Reason]), + {error, route_not_found}; +compute_path_result(From, To, {ok, Devices}) -> + Labels = tl([L || #{mpls_label := L} <- Devices, L =/= undefined]), + logger:debug("Route from ~p to ~p: ~p", [From, To, Labels]), + {ok, Labels}. + routereq_from_labels(Source, Destination, Constraints, Labels) -> #{ source => Source, diff --git a/src/te/apps/epce/src/epce_ted.erl b/src/te/apps/epce/src/epce_ted.erl index aaf5a4e9a..879b92920 100644 --- a/src/te/apps/epce/src/epce_ted.erl +++ b/src/te/apps/epce/src/epce_ted.erl @@ -19,6 +19,7 @@ -export([link_updated/2]). -export([link_deleted/1]). -export([compute_path/3]). +-export([lookup/2]). -export([get_graph/0]). @@ -69,6 +70,12 @@ compute_path(Index, From, To) compute_path(Index, _From, _To) -> {error, {invalid_index, Index}}. +lookup(Index, Key) + when Index =:= id; Index =:= pcc_address -> + gen_server:call(?MODULE, {lookup, Index, Key}); +lookup(Index, _Key) -> + {error, {invalid_index, Index}}. + get_graph() -> gen_server:call(?MODULE, get_graph). @@ -106,6 +113,13 @@ handle_call({compute_path, Index, From, To}, _From, #state{graph = G} = State) - {error, Reason} -> {reply, {error, Reason}, State} end; +handle_call({lookup, Index, Key}, _From, #state{graph = G} = State) -> + case as_ids(State, Index, [Key]) of + {ok, [Id]} -> + {reply, do_lookup(G, Id), State}; + {error, Reason} -> + {reply, {error, Reason}, State} + end; handle_call(get_graph, _From, #state{graph = G} = State) -> {reply, G, State}; handle_call(Request, _From, State) -> @@ -193,6 +207,12 @@ do_compute_path(G, FromId, ToId) -> Ids -> {ok, retrieve_devices(G, Ids, [])} end. +do_lookup(G, Id) -> + case digraph:vertex(G, Id) of + {_, Info} -> {ok, Info}; + false -> {error, not_found} + end. + retrieve_devices(_G, [], Acc) -> lists:reverse(Acc); retrieve_devices(G, [Id | Rest], Acc) -> diff --git a/src/te/apps/tfte/src/tfte.app.src b/src/te/apps/tfte/src/tfte.app.src index 06adeb848..64fffcb61 100644 --- a/src/te/apps/tfte/src/tfte.app.src +++ b/src/te/apps/tfte/src/tfte.app.src @@ -7,6 +7,7 @@ [kernel, stdlib, tfpb, + jsx, epce ]}, {env,[]}, diff --git a/src/te/apps/tfte/src/tfte_server.erl b/src/te/apps/tfte/src/tfte_server.erl index 29dddf3d1..ea80848f7 100644 --- a/src/te/apps/tfte/src/tfte_server.erl +++ b/src/te/apps/tfte/src/tfte_server.erl @@ -16,6 +16,8 @@ -export([context_event/1]). -export([topology_ready/1]). -export([topology_event/1]). +-export([request_lsp/1]). +-export([delete_lsp/1]). % Behaviour gen_statem functions -export([init/1]). @@ -28,6 +30,7 @@ %%% Records %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -record(data, { + services = #{} }). @@ -51,6 +54,12 @@ topology_ready(Topology) -> topology_event(Event) -> gen_statem:cast(?MODULE, {topology_event, Event}). +request_lsp(ServiceMap) -> + gen_statem:cast(?MODULE, {request_lsp, ServiceMap}). + +delete_lsp(ServiceId) -> + gen_statem:cast(?MODULE, {delete_lsp, ServiceId}). + %%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -83,6 +92,17 @@ handle_event(cast, {topology_ready, _Topology}, ready, _Data) -> handle_event(cast, {topology_event, _Event}, ready, _Data) -> ?LOG_DEBUG("Teraflow topology event: ~p", [_Event]), keep_state_and_data; +handle_event({call, _From}, {request_lsp, ServiceMap}, ready, Data) -> + #{service_id := ServiceId} = ServiceMap, + ?LOG_DEBUG("Teraflow service ~s requested its LSPs", + [format_service_id(ServiceId)]), + {Result, Data2} = do_request_lsp(Data, ServiceMap), + {keep_state, Data2, [{reply, Result}]}; +handle_event(cast, {delete_lsp, ServiceId}, ready, Data) -> + ?LOG_DEBUG("Teraflow service ~s delete its LSPs", + [format_service_id(ServiceId)]), + {Result, Data2} = do_delete_lsp(Data, ServiceId), + {keep_state, Data2, [{reply, Result}]}; %-- ANY STATE ------------------------------------------------------------------ handle_event(EventType, EventContent, State, Data) -> ?LOG_WARNING(Data, "Unexpected ~w event in state ~w: ~w", @@ -98,3 +118,42 @@ code_change(_OldVsn, OldState, OldData, _Extra) -> %%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +format_service_id(#{context_id := #{context_uuid := #{uuid := ContextName}}, + service_uuid := #{uuid := ServiceUUID}}) -> + iolist_to_binary(io_lib:format("~s:~s", [ContextName, ServiceUUID])). + +do_request_lsp(#data{services = Services} = Data, + #{service_type := 'SERVICETYPE_TE'} = ServiceMap) -> + #{service_config := Config, + service_endpoint_ids := Endpoints, + service_id := ServiceId} = ServiceMap, + #{binding_label := BindingLabel1, symbolic_name := SymbolicName1} + = tfte_util:custom_config(Config, <<"/lsp-fw">>), + #{binding_label := BindingLabel2, symbolic_name := SymbolicName2} + = tfte_util:custom_config(Config, <<"/lsp-bw">>), + [#{device_id := #{device_uuid := #{uuid := Id1}}}, + #{device_id := #{device_uuid := #{uuid := Id2}}}] = Endpoints, + case epce_server:initiate_flow(SymbolicName1, Id1, Id2, BindingLabel1) of + {error, Reason} -> + ?LOG_ERROR("Error while setting up service ~s forward LSP: ~p", + [format_service_id(ServiceId), Reason]), + {'SERVICESTATUS_UNDEFINED', Data}; + {ok, ForwardFlow} -> + case epce_server:initiate_flow(SymbolicName2, Id2, Id1, BindingLabel2) of + {error, Reason} -> + ?LOG_ERROR("Error while setting up service ~s backward LSP: ~p", + [format_service_id(ServiceId), Reason]), + %TODO: Cleanup forward flow ? + {'SERVICESTATUS_UNDEFINED', Data}; + {ok, BackwardFlow} -> + ServiceData = {ServiceMap, ForwardFlow, BackwardFlow}, + Services2 = Services#{ServiceId => ServiceData}, + Data2 = Data#data{services = Services2}, + {'SERVICESTATUS_ACTIVE', Data2} + end + end. + +do_delete_lsp(Data, ServiceId) -> + ?LOG_INFO("LSP DELETION REQUESTED ~p", [ServiceId]), + {'SERVICESTATUS_UNDEFINED', Data}. \ No newline at end of file diff --git a/src/te/apps/tfte/src/tfte_te_service.erl b/src/te/apps/tfte/src/tfte_te_service.erl index 1cadd7aad..2f230fd6b 100644 --- a/src/te/apps/tfte/src/tfte_te_service.erl +++ b/src/te/apps/tfte/src/tfte_te_service.erl @@ -6,6 +6,7 @@ %%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -include_lib("grpcbox/include/grpcbox.hrl"). +-include_lib("kernel/include/logger.hrl"). %%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -18,14 +19,22 @@ %%% BEHAVIOUR te_te_service_bhvr CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%% -request_lsp(_Ctx, _Service) -> - {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, - #{headers => #{}, trailers => #{}}}. +request_lsp(_Ctx, Service) -> + ?LOG_ERROR("Requesting LSP: ~p", [Service]), + try tfte_server:request_lsp(Service) + catch E:R:S -> + ?LOG_ERROR("Error while requesting LSP: ~p:~p ~p", [E, R, S]), + 'SERVICESTATUS_UNDEFINED' + end. -update_lsp(_Ctx, _Service) -> +update_lsp(_Ctx, _ServiceId) -> {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, #{headers => #{}, trailers => #{}}}. -delete_lsp(_Ctx, _Service) -> - {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, - #{headers => #{}, trailers => #{}}}. +delete_lsp(_Ctx, ServiceId) -> + ?LOG_ERROR("Deleting LSP: ~p", [ServiceId]), + try tfte_server:delete_lsp(ServiceId) + catch E:R:S -> + ?LOG_ERROR("Error while deleting LSP: ~p:~p ~p", [E, R, S]), + 'SERVICESTATUS_UNDEFINED' + end. diff --git a/src/te/apps/tfte/src/tfte_topology.erl b/src/te/apps/tfte/src/tfte_topology.erl index d2c6fc0d9..54825c409 100644 --- a/src/te/apps/tfte/src/tfte_topology.erl +++ b/src/te/apps/tfte/src/tfte_topology.erl @@ -281,37 +281,31 @@ device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_ENABLED'}) enabled. device_mpls_label(Device) -> - case device_config_value(<<"mpls_label">>, Device) of + case device_config_value(<<"/te_data/mpls_label">>, Device) of undefined -> undefined; - LabelBin -> - try binary_to_integer(LabelBin) + LabelJson -> + try jsx:decode(LabelJson) catch error:badarg -> undefined end end. device_pcc_address(Device) -> - case device_config_value(<<"pcc_address">>, Device) of + case device_config_value(<<"/te_data/pcc_address">>, Device) of undefined -> undefined; - AddressBin -> - case inet_parse:address(binary_to_list(AddressBin)) of - {ok, Address} -> Address; - {error,einval} -> undefined + AddressJson -> + try jsx:decode(AddressJson) of + AddressBin -> + case inet_parse:address(binary_to_list(AddressBin)) of + {ok, Address} -> Address; + {error,einval} -> undefined + end + catch + error:badarg -> undefined end end. -device_config_value(Key, #{device_config := #{config_rules := Rules}}) -> - device_config_value(Key, Rules); -device_config_value(_Key, []) -> - undefined; -device_config_value(Key, [#{action := 'CONFIGACTION_SET', - config_rule := {custom, Rule}} | Rest]) -> - case Rule of - #{resource_key := Key, resource_value := Value} -> Value; - _ -> device_config_value(Key, Rest) - end; -device_config_value(Key, [_Rule | Rest]) -> - device_config_value(Key, Rest). - +device_config_value(Key, #{device_config := Config}) -> + tfte_util:custom_config(Config, Key). device_endpoints(Device) -> device_endpoints(Device, []). diff --git a/src/te/apps/tfte/src/tfte_util.erl b/src/te/apps/tfte/src/tfte_util.erl new file mode 100644 index 000000000..7b766b5ba --- /dev/null +++ b/src/te/apps/tfte/src/tfte_util.erl @@ -0,0 +1,27 @@ +-module(tfte_util). + +%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include_lib("kernel/include/logger.hrl"). + + +%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% API functions +-export([custom_config/2]). + + +%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +custom_config(#{config_rules := Rules}, Key) -> + custom_config(Rules, Key); +custom_config([], _Key) -> + undefined; +custom_config([#{action := 'CONFIGACTION_SET', + config_rule := {custom, Rule}} | Rest], Key) -> + case Rule of + #{resource_key := Key, resource_value := Value} -> jsx:decode(Value); + _ -> custom_config(Rest, Key) + end; +custom_config([_Rule | Rest], Key) -> + custom_config(Rest, Key). diff --git a/src/te/rebar.config b/src/te/rebar.config index 776a80249..06bff13c7 100644 --- a/src/te/rebar.config +++ b/src/te/rebar.config @@ -2,6 +2,7 @@ {deps, [ grpcbox, + jsx, {pcep_server, {git, "https://github.com/stritzinger/pcep_server.git", {branch, "master"}}} ]}. @@ -17,6 +18,7 @@ runtime_tools, epce, grpcbox, + jsx, tfpb, tfte ]}, diff --git a/src/te/rebar.lock b/src/te/rebar.lock index 446884e6b..c435b0456 100644 --- a/src/te/rebar.lock +++ b/src/te/rebar.lock @@ -9,6 +9,7 @@ {<<"gproc">>,{pkg,<<"gproc">>,<<"0.8.0">>},1}, {<<"grpcbox">>,{pkg,<<"grpcbox">>,<<"0.15.0">>},0}, {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}, + {<<"jsx">>,{pkg,<<"jsx">>,<<"3.1.0">>},0}, {<<"pcep_codec">>, {git,"https://github.com/stritzinger/pcep_codec.git", {ref,"ca5eb0822d9971ec4bcfb427a49b2e516081a126"}}, @@ -26,6 +27,7 @@ {<<"gproc">>, <<"CEA02C578589C61E5341FCE149EA36CCEF236CC2ECAC8691FBA408E7EA77EC2F">>}, {<<"grpcbox">>, <<"97C7126296A091602D372EBF5860A04F7BC795B45B33A984CAD2B8E362774FD8">>}, {<<"hpack">>, <<"17670F83FF984AE6CD74B1C456EDDE906D27FF013740EE4D9EFAA4F1BF999633">>}, + {<<"jsx">>, <<"D12516BAA0BB23A59BB35DCCAF02A1BD08243FCBB9EFE24F2D9D056CCFF71268">>}, {<<"ranch">>, <<"FBF3D79661C071543256F9051CAF19D65DAA6DF1CF6824D8F37A49B19A66F703">>}]}, {pkg_hash_ext,[ {<<"acceptor_pool">>, <<"0CBCD83FDC8B9AD2EEE2067EF8B91A14858A5883CB7CD800E6FCD5803E158788">>}, @@ -34,5 +36,6 @@ {<<"gproc">>, <<"580ADAFA56463B75263EF5A5DF4C86AF321F68694E7786CB057FD805D1E2A7DE">>}, {<<"grpcbox">>, <<"161ABE9E17E7D1982EFA6488ADEAA13C3E847A07984A6E6B224E553368918647">>}, {<<"hpack">>, <<"06F580167C4B8B8A6429040DF36CC93BBA6D571FAEAEC1B28816523379CBB23A">>}, + {<<"jsx">>, <<"0C5CC8FDC11B53CC25CF65AC6705AD39E54ECC56D1C22E4ADB8F5A53FB9427F3">>}, {<<"ranch">>, <<"C20A4840C7D6623C19812D3A7C828B2F1BD153EF0F124CB69C54FE51D8A42AE0">>}]} ]. -- GitLab From 3a1cf47f76238d39353995f4cf257334d8ada1c2 Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 25 Jan 2023 09:01:32 -0800 Subject: [PATCH 39/63] Fix some error handling --- src/service/service/__main__.py | 2 ++ src/te/apps/epce/src/epce_server.erl | 2 +- src/te/apps/tfte/src/tfte_server.erl | 8 ++++---- src/te/apps/tfte/src/tfte_te_service.erl | 24 ++++++++++++++++------- src/te/apps/tfte/src/tfte_topology.erl | 25 +++++++++--------------- 5 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/service/service/__main__.py b/src/service/service/__main__.py index f2b6e38d6..18f91bf1a 100644 --- a/src/service/service/__main__.py +++ b/src/service/service/__main__.py @@ -43,6 +43,8 @@ def main(): get_env_var_name(ServiceNameEnum.DEVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC), get_env_var_name(ServiceNameEnum.PATHCOMP, ENVVAR_SUFIX_SERVICE_HOST ), get_env_var_name(ServiceNameEnum.PATHCOMP, ENVVAR_SUFIX_SERVICE_PORT_GRPC), + get_env_var_name(ServiceNameEnum.TE, ENVVAR_SUFIX_SERVICE_HOST ), + get_env_var_name(ServiceNameEnum.TE, ENVVAR_SUFIX_SERVICE_PORT_GRPC), ]) signal.signal(signal.SIGINT, signal_handler) diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl index 8996d3b35..c7b404fca 100644 --- a/src/te/apps/epce/src/epce_server.erl +++ b/src/te/apps/epce/src/epce_server.erl @@ -36,7 +36,7 @@ %%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --define(LARGE_TIMEOUT, 20000). +-define(LARGE_TIMEOUT, infinity). %%% RECORDS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/te/apps/tfte/src/tfte_server.erl b/src/te/apps/tfte/src/tfte_server.erl index ea80848f7..a6c8c4b71 100644 --- a/src/te/apps/tfte/src/tfte_server.erl +++ b/src/te/apps/tfte/src/tfte_server.erl @@ -138,22 +138,22 @@ do_request_lsp(#data{services = Services} = Data, {error, Reason} -> ?LOG_ERROR("Error while setting up service ~s forward LSP: ~p", [format_service_id(ServiceId), Reason]), - {'SERVICESTATUS_UNDEFINED', Data}; + {{error, Reason}, Data}; {ok, ForwardFlow} -> case epce_server:initiate_flow(SymbolicName2, Id2, Id1, BindingLabel2) of {error, Reason} -> ?LOG_ERROR("Error while setting up service ~s backward LSP: ~p", [format_service_id(ServiceId), Reason]), %TODO: Cleanup forward flow ? - {'SERVICESTATUS_UNDEFINED', Data}; + {{error, Reason}, Data}; {ok, BackwardFlow} -> ServiceData = {ServiceMap, ForwardFlow, BackwardFlow}, Services2 = Services#{ServiceId => ServiceData}, Data2 = Data#data{services = Services2}, - {'SERVICESTATUS_ACTIVE', Data2} + {{ok, 'SERVICESTATUS_ACTIVE'}, Data2} end end. do_delete_lsp(Data, ServiceId) -> ?LOG_INFO("LSP DELETION REQUESTED ~p", [ServiceId]), - {'SERVICESTATUS_UNDEFINED', Data}. \ No newline at end of file + {{error, not_implemented}, Data}. \ No newline at end of file diff --git a/src/te/apps/tfte/src/tfte_te_service.erl b/src/te/apps/tfte/src/tfte_te_service.erl index 2f230fd6b..f0e7116d2 100644 --- a/src/te/apps/tfte/src/tfte_te_service.erl +++ b/src/te/apps/tfte/src/tfte_te_service.erl @@ -19,22 +19,32 @@ %%% BEHAVIOUR te_te_service_bhvr CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%% -request_lsp(_Ctx, Service) -> - ?LOG_ERROR("Requesting LSP: ~p", [Service]), - try tfte_server:request_lsp(Service) +request_lsp(Ctx, Service) -> + ?LOG_INFO("Requesting LSP: ~p", [Service]), + try tfte_server:request_lsp(Service) of + {ok, Status} -> + {ok, Status, Ctx}; + {error, Reason} -> + ?LOG_INFO("Error while requesting LSP: ~p", [Reason]), + {ok, 'SERVICESTATUS_UNDEFINED', Ctx} catch E:R:S -> ?LOG_ERROR("Error while requesting LSP: ~p:~p ~p", [E, R, S]), - 'SERVICESTATUS_UNDEFINED' + {ok, 'SERVICESTATUS_UNDEFINED', Ctx} end. update_lsp(_Ctx, _ServiceId) -> {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, #{headers => #{}, trailers => #{}}}. -delete_lsp(_Ctx, ServiceId) -> +delete_lsp(Ctx, ServiceId) -> ?LOG_ERROR("Deleting LSP: ~p", [ServiceId]), - try tfte_server:delete_lsp(ServiceId) + try tfte_server:delete_lsp(ServiceId) of + {ok, Status} -> + {ok, Status, Ctx}; + {error, Reason} -> + ?LOG_INFO("Error while deleting LSP: ~p", [Reason]), + {ok, 'SERVICESTATUS_UNDEFINED', Ctx} catch E:R:S -> ?LOG_ERROR("Error while deleting LSP: ~p:~p ~p", [E, R, S]), - 'SERVICESTATUS_UNDEFINED' + {ok, 'SERVICESTATUS_UNDEFINED', Ctx} end. diff --git a/src/te/apps/tfte/src/tfte_topology.erl b/src/te/apps/tfte/src/tfte_topology.erl index 54825c409..886fd926d 100644 --- a/src/te/apps/tfte/src/tfte_topology.erl +++ b/src/te/apps/tfte/src/tfte_topology.erl @@ -281,27 +281,20 @@ device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_ENABLED'}) enabled. device_mpls_label(Device) -> - case device_config_value(<<"/te_data/mpls_label">>, Device) of - undefined -> undefined; - LabelJson -> - try jsx:decode(LabelJson) - catch error:badarg -> undefined - end + try device_config_value(<<"/te_data/mpls_label">>, Device) + catch error:badarg -> undefined end. device_pcc_address(Device) -> - case device_config_value(<<"/te_data/pcc_address">>, Device) of + try device_config_value(<<"/te_data/pcc_address">>, Device) of undefined -> undefined; - AddressJson -> - try jsx:decode(AddressJson) of - AddressBin -> - case inet_parse:address(binary_to_list(AddressBin)) of - {ok, Address} -> Address; - {error,einval} -> undefined - end - catch - error:badarg -> undefined + AddressBin -> + case inet_parse:address(binary_to_list(AddressBin)) of + {ok, Address} -> Address; + {error,einval} -> undefined end + catch + error:badarg -> undefined end. device_config_value(Key, #{device_config := Config}) -> -- GitLab From 5147086066bffcca6df2459b96fd9b86fa07fb4c Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 8 Feb 2023 11:00:30 -0800 Subject: [PATCH 40/63] Fix TE service manifest --- manifests/teservice.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifests/teservice.yaml b/manifests/teservice.yaml index 3f7bd1f04..701a5f52f 100644 --- a/manifests/teservice.yaml +++ b/manifests/teservice.yaml @@ -29,7 +29,7 @@ spec: shareProcessNamespace: true containers: - name: server - image: registry.gitlab.com/teraflow-h2020/controller/te:latest + image: labs.etsi.org:5050/tfs/controller/te:latest imagePullPolicy: Always ports: - containerPort: 11010 -- GitLab From f8b3ec9d1773a1d8f42a5aaf9b639a742948a9d9 Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 8 Feb 2023 11:04:24 -0800 Subject: [PATCH 41/63] Add the tutorials locally to the TE service --- {tutorial => src/te/tutorial}/1-6-setup-erlang-environmnet.md | 0 {tutorial => src/te/tutorial}/2-6-te-demo.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {tutorial => src/te/tutorial}/1-6-setup-erlang-environmnet.md (100%) rename {tutorial => src/te/tutorial}/2-6-te-demo.md (100%) diff --git a/tutorial/1-6-setup-erlang-environmnet.md b/src/te/tutorial/1-6-setup-erlang-environmnet.md similarity index 100% rename from tutorial/1-6-setup-erlang-environmnet.md rename to src/te/tutorial/1-6-setup-erlang-environmnet.md diff --git a/tutorial/2-6-te-demo.md b/src/te/tutorial/2-6-te-demo.md similarity index 100% rename from tutorial/2-6-te-demo.md rename to src/te/tutorial/2-6-te-demo.md -- GitLab From c89d240b0f7f7e600037ab238fd236bfecca59c1 Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 8 Feb 2023 11:58:16 -0800 Subject: [PATCH 42/63] Update TE service configuration format --- src/te/apps/tfte/src/tfte_topology.erl | 21 +++++++++++++-------- src/te/config/sys.config.src | 2 +- src/te/tests/topology-descriptors.json | 20 +++++++------------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/te/apps/tfte/src/tfte_topology.erl b/src/te/apps/tfte/src/tfte_topology.erl index 886fd926d..d9505e914 100644 --- a/src/te/apps/tfte/src/tfte_topology.erl +++ b/src/te/apps/tfte/src/tfte_topology.erl @@ -281,18 +281,23 @@ device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_ENABLED'}) enabled. device_mpls_label(Device) -> - try device_config_value(<<"/te_data/mpls_label">>, Device) + try device_config_value(<<"/te_data">>, Device) of + Map when is_map(Map) -> maps:get(<<"mpls_label">>, Map, undefined); + _ -> undefined catch error:badarg -> undefined end. device_pcc_address(Device) -> - try device_config_value(<<"/te_data/pcc_address">>, Device) of - undefined -> undefined; - AddressBin -> - case inet_parse:address(binary_to_list(AddressBin)) of - {ok, Address} -> Address; - {error,einval} -> undefined - end + try device_config_value(<<"/te_data">>, Device) of + Map when is_map(Map) -> + case maps:get(<<"pcc_address">>, Map, undefined) of + AddressBin -> + case inet_parse:address(binary_to_list(AddressBin)) of + {ok, Address} -> Address; + {error,einval} -> undefined + end + end; + _ -> undefined catch error:badarg -> undefined end. diff --git a/src/te/config/sys.config.src b/src/te/config/sys.config.src index f3c185641..b1640c109 100644 --- a/src/te/config/sys.config.src +++ b/src/te/config/sys.config.src @@ -2,7 +2,7 @@ {tfte, [ {context, <<"admin">>}, - {topology, <<"tetestbed">>}, + {topology, <<"admin">>}, {services, [ {te, [ {http, {env, "TESERVICE_SERVICE_HOST"}, {env, "TESERVICE_SERVICE_PORT_GRPC"}, []} diff --git a/src/te/tests/topology-descriptors.json b/src/te/tests/topology-descriptors.json index 82f37ce29..a34d8ce09 100644 --- a/src/te/tests/topology-descriptors.json +++ b/src/te/tests/topology-descriptors.json @@ -8,7 +8,7 @@ ], "topologies": [ { - "topology_id": {"topology_uuid": {"uuid": "tetestbed"}, "context_id": {"context_uuid": {"uuid": "admin"}}}, + "topology_id": {"topology_uuid": {"uuid": "admin"}, "context_id": {"context_uuid": {"uuid": "admin"}}}, "device_ids": [ {"device_uuid": {"uuid": "SW1"}}, {"device_uuid": {"uuid": "RT1"}}, @@ -54,8 +54,7 @@ {"sample_types": [], "type": "copper", "uuid": "eth-src"}, {"sample_types": [], "type": "copper", "uuid": "eth-sw1"} ]}}}, - {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16010"}}, - {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "1.1.1.1"}} + {"action": 1, "custom": {"resource_key": "/te_data", "resource_value": {"mpls_label": 16010, "pcc_address": "1.1.1.1"}}} ]} }, { @@ -69,8 +68,7 @@ {"sample_types": [], "type": "copper", "uuid": "eth-rt4-1"}, {"sample_types": [], "type": "copper", "uuid": "eth-rt4-2"} ]}}}, - {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16020"}}, - {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "2.2.2.2"}} + {"action": 1, "custom": {"resource_key": "/te_data", "resource_value": {"mpls_label": 16020, "pcc_address": "2.2.2.2"}}} ]} }, { @@ -84,8 +82,7 @@ {"sample_types": [], "type": "copper", "uuid": "eth-rt5-1"}, {"sample_types": [], "type": "copper", "uuid": "eth-rt5-2"} ]}}}, - {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16030"}}, - {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "3.3.3.3"}} + {"action": 1, "custom": {"resource_key": "/te_data", "resource_value": {"mpls_label": 16030, "pcc_address": "3.3.3.3"}}} ]} }, { @@ -100,8 +97,7 @@ {"sample_types": [], "type": "copper", "uuid": "eth-rt5"}, {"sample_types": [], "type": "copper", "uuid": "eth-rt6"} ]}}}, - {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16040"}}, - {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "4.4.4.4"}} + {"action": 1, "custom": {"resource_key": "/te_data", "resource_value": {"mpls_label": 16040, "pcc_address": "4.4.4.4"}}} ]} }, { @@ -116,8 +112,7 @@ {"sample_types": [], "type": "copper", "uuid": "eth-rt4"}, {"sample_types": [], "type": "copper", "uuid": "eth-rt6"} ]}}}, - {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16050"}}, - {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "5.5.5.5"}} + {"action": 1, "custom": {"resource_key": "/te_data", "resource_value": {"mpls_label": 16050, "pcc_address": "5.5.5.5"}}} ]} }, { @@ -131,8 +126,7 @@ {"sample_types": [], "type": "copper", "uuid": "eth-rt5"}, {"sample_types": [], "type": "copper", "uuid": "eth-dst"} ]}}}, - {"action": 1, "custom": {"resource_key": "/te_data/mpls_label", "resource_value": "16060"}}, - {"action": 1, "custom": {"resource_key": "/te_data/pcc_address", "resource_value": "6.6.6.6"}} + {"action": 1, "custom": {"resource_key": "/te_data", "resource_value": {"mpls_label": 16060, "pcc_address": "6.6.6.6"}}} ]} } ], -- GitLab From 646fbd7ad02bc210add25575453d2293ec4116d4 Mon Sep 17 00:00:00 2001 From: merle Date: Mon, 10 Jul 2023 01:48:24 -0700 Subject: [PATCH 43/63] Update documentation and fix identation --- manifests/teservice.yaml | 6 ++++-- src/te/apps/epce/src/epce_sup.erl | 8 ++++---- src/te/tutorial/2-6-te-demo.md | 1 - 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/manifests/teservice.yaml b/manifests/teservice.yaml index 701a5f52f..d27bd42c7 100644 --- a/manifests/teservice.yaml +++ b/manifests/teservice.yaml @@ -22,6 +22,8 @@ spec: app: teservice template: metadata: + annotations: + config.linkerd.io/skip-inbound-ports: "4189" labels: app: teservice spec: @@ -50,8 +52,8 @@ spec: exec: command: ["/tfte/bin/tfte", "status"] livenessProbe: - grpc: - port: 11010 + exec: + command: ["/tfte/bin/tfte", "status"] resources: requests: cpu: 250m diff --git a/src/te/apps/epce/src/epce_sup.erl b/src/te/apps/epce/src/epce_sup.erl index f04e9697e..9341201c0 100644 --- a/src/te/apps/epce/src/epce_sup.erl +++ b/src/te/apps/epce/src/epce_sup.erl @@ -23,10 +23,10 @@ start_link() -> init([]) -> SupFlags = #{ - strategy => one_for_all, - intensity => 0, - period => 1 - }, + strategy => one_for_all, + intensity => 0, + period => 1 + }, TEDSpec = #{ id => ?TED_WORKER, start => {?TED_WORKER, start_link, []}, diff --git a/src/te/tutorial/2-6-te-demo.md b/src/te/tutorial/2-6-te-demo.md index 778d62e06..9b345203b 100644 --- a/src/te/tutorial/2-6-te-demo.md +++ b/src/te/tutorial/2-6-te-demo.md @@ -79,7 +79,6 @@ ### Run the Test-Bed First load the [teraflow configuration file](~/tfs-ctrl/src/te/tests/topology-descriptors.json) using the webui. -The first time the configuration is loaded may partialy fail because it tries to load the topology before the devices and links, but a second load should work. In first console: $ cd ~/testbed -- GitLab From 54dc702ae5aeb969c20131eb5b2257e8342514eb Mon Sep 17 00:00:00 2001 From: merle Date: Mon, 10 Jul 2023 03:24:38 -0700 Subject: [PATCH 44/63] Change port and documentation --- manifests/teservice.yaml | 6 +++--- src/te/Dockerfile | 2 +- src/te/README.md | 4 ++-- src/te/config/dev.config.template | 4 ++-- src/te/tutorial/2-6-te-demo.md | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifests/teservice.yaml b/manifests/teservice.yaml index d27bd42c7..30002bc52 100644 --- a/manifests/teservice.yaml +++ b/manifests/teservice.yaml @@ -34,7 +34,7 @@ spec: image: labs.etsi.org:5050/tfs/controller/te:latest imagePullPolicy: Always ports: - - containerPort: 11010 + - containerPort: 10030 env: - name: ERLANG_LOGGER_LEVEL value: "debug" @@ -73,8 +73,8 @@ spec: ports: - name: grpc protocol: TCP - port: 11010 - targetPort: 11010 + port: 10030 + targetPort: 10030 - name: pcep protocol: TCP port: 4189 diff --git a/src/te/Dockerfile b/src/te/Dockerfile index 98194f254..446936ca5 100644 --- a/src/te/Dockerfile +++ b/src/te/Dockerfile @@ -42,7 +42,7 @@ RUN apk add --no-cache libgcc libstdc++ && \ COPY --from=0 /var/teraflow/src/te/_build/prod/rel/tfte /tfte # Expose relevant ports -EXPOSE 11010 +EXPOSE 10030 EXPOSE 4189 ARG ERLANG_LOGGER_LEVEL_DEFAULT=debug diff --git a/src/te/README.md b/src/te/README.md index a316571bf..d6f1952ac 100644 --- a/src/te/README.md +++ b/src/te/README.md @@ -64,9 +64,9 @@ The docker image must be built from the root of the Teraflow project: ### Open a Console - $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -- /tfte/bin/tfte remote_console + $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -c server -- /tfte/bin/tfte remote_console ## Show Logs - $ kubectl --namespace tfs logs $(kubectl --namespace tfs get pods --selector=app=teservice -o name) + $ kubectl --namespace tfs logs $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -c server diff --git a/src/te/config/dev.config.template b/src/te/config/dev.config.template index 5897c4e46..6ca3af506 100644 --- a/src/te/config/dev.config.template +++ b/src/te/config/dev.config.template @@ -1,7 +1,7 @@ [ {tfte, [ {services, [ - {te, [{http, "localhost", 11010, []}], #{}} + {te, [{http, "localhost", 10030, []}], #{}} ]} ]}, @@ -30,7 +30,7 @@ %cacertfile => "" }, listen_opts => #{ - port => 11010, + port => 10030, ip => {0,0,0,0} }, pool_opts => #{ diff --git a/src/te/tutorial/2-6-te-demo.md b/src/te/tutorial/2-6-te-demo.md index 9b345203b..d514adf07 100644 --- a/src/te/tutorial/2-6-te-demo.md +++ b/src/te/tutorial/2-6-te-demo.md @@ -94,7 +94,7 @@ Then in second console: We will setup two unidirectional flow between router 1 and 6. We will use the binding label 1111 for the flow from router 1 to router 6, and the binding label 6666 for the flow from router 6 to router 1. - $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -- /tfte/bin/tfte remote_console + $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -c server -- /tfte/bin/tfte remote_console 1> {ok, Flow1to6} = epce_server:initiate_flow(<<"foo">>, {1, 1, 1, 1}, {6, 6, 6, 6}, 1111). 2> {ok, Flow6to1} = epce_server:initiate_flow(<<"bar">>, {6, 6, 6, 6}, {1, 1, 1, 1}, 6666). -- GitLab From 891e20acdd3e484f5889b9c67569be92bf9ebe02 Mon Sep 17 00:00:00 2001 From: merle Date: Mon, 10 Jul 2023 03:28:49 -0700 Subject: [PATCH 45/63] Change port --- src/te/config/sys.config.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/te/config/sys.config.src b/src/te/config/sys.config.src index b1640c109..df373de74 100644 --- a/src/te/config/sys.config.src +++ b/src/te/config/sys.config.src @@ -53,7 +53,7 @@ %cacertfile => "" }, listen_opts => #{ - port => 11010, + port => 10030, ip => {0,0,0,0} }, pool_opts => #{ -- GitLab From 71198a61a1be9825aacd511c35a569a8b0c010a5 Mon Sep 17 00:00:00 2001 From: merle Date: Tue, 11 Jul 2023 05:54:41 -0700 Subject: [PATCH 46/63] Use Empty defined in the context to prevent duplication --- proto/l3_centralizedattackdetector.proto | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proto/l3_centralizedattackdetector.proto b/proto/l3_centralizedattackdetector.proto index ed99435aa..5fbed424c 100644 --- a/proto/l3_centralizedattackdetector.proto +++ b/proto/l3_centralizedattackdetector.proto @@ -18,13 +18,13 @@ import "context.proto"; service L3Centralizedattackdetector { // Analyze single input to the ML model in the CAD component - rpc AnalyzeConnectionStatistics (L3CentralizedattackdetectorMetrics) returns (Empty) {} + rpc AnalyzeConnectionStatistics (L3CentralizedattackdetectorMetrics) returns (context.Empty) {} // Analyze a batch of inputs to the ML model in the CAD component - rpc AnalyzeBatchConnectionStatistics (L3CentralizedattackdetectorBatchInput) returns (Empty) {} + rpc AnalyzeBatchConnectionStatistics (L3CentralizedattackdetectorBatchInput) returns (context.Empty) {} // Get the list of features used by the ML model in the CAD component - rpc GetFeaturesIds (Empty) returns (AutoFeatures) {} + rpc GetFeaturesIds (context.Empty) returns (AutoFeatures) {} } message Feature { @@ -63,6 +63,6 @@ message L3CentralizedattackdetectorBatchInput { repeated L3CentralizedattackdetectorMetrics metrics = 1; } -message Empty { - string message = 1; -} +// message Empty { +// string message = 1; +// } -- GitLab From 6c2965147ad28c2958c09b80dc7b54177e785efa Mon Sep 17 00:00:00 2001 From: merle Date: Tue, 11 Jul 2023 05:59:01 -0700 Subject: [PATCH 47/63] Fix for topology API changes, add test script and update demo documentation. --- src/te/apps/tfte/src/tfte_context.erl | 11 ++- src/te/apps/tfte/src/tfte_server.erl | 37 ++++++--- src/te/apps/tfte/src/tfte_te_service.erl | 10 +-- src/te/apps/tfte/src/tfte_topology.erl | 101 ++++++++++++----------- src/te/apps/tfte/src/tfte_util.erl | 2 +- src/te/tests/test_te_service.py | 94 +++++++++++++++++++++ src/te/tutorial/2-6-te-demo.md | 23 ++++++ 7 files changed, 209 insertions(+), 69 deletions(-) create mode 100644 src/te/tests/test_te_service.py diff --git a/src/te/apps/tfte/src/tfte_context.erl b/src/te/apps/tfte/src/tfte_context.erl index ee0fafc07..1acb1befe 100644 --- a/src/te/apps/tfte/src/tfte_context.erl +++ b/src/te/apps/tfte/src/tfte_context.erl @@ -12,6 +12,7 @@ % API functions -export([start_link/0]). +-export([is_ready/0]). % Behaviour gen_statem functions -export([init/1]). @@ -39,7 +40,13 @@ %%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start_link() -> - gen_statem:start_link(?MODULE, [], []). + gen_statem:start_link({local, ?MODULE}, ?MODULE, [], []). + +is_ready() -> + case whereis(?MODULE) of + undefined -> false; + _ -> gen_statem:call(?MODULE, is_ready) + end. %%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -129,6 +136,8 @@ handle_event(info, {'DOWN', Ref, process, Pid, Reason}, ready, ?LOG_ERROR("Context subscription error: ~p", [Info]), {next_state, subscribe, Data2}; %-- ANY STATE ------------------------------------------------------------------ +handle_event({call, _From}, is_ready, State, _Data) -> + {keep_state_and_data, [{reply, State =:= ready}]}; handle_event(info, Msg, StateName, _Data) -> ?LOG_WARNING("Unexpected context message in state ~w: ~p", [StateName, Msg]), keep_state_and_data. diff --git a/src/te/apps/tfte/src/tfte_server.erl b/src/te/apps/tfte/src/tfte_server.erl index a6c8c4b71..85441bcb1 100644 --- a/src/te/apps/tfte/src/tfte_server.erl +++ b/src/te/apps/tfte/src/tfte_server.erl @@ -40,7 +40,7 @@ %%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start_link() -> - gen_statem:start_link(?MODULE, [], []). + gen_statem:start_link({local, ?MODULE}, ?MODULE, [], []). context_ready(Context) -> gen_statem:cast(?MODULE, {context_ready, Context}). @@ -55,24 +55,27 @@ topology_event(Event) -> gen_statem:cast(?MODULE, {topology_event, Event}). request_lsp(ServiceMap) -> - gen_statem:cast(?MODULE, {request_lsp, ServiceMap}). + gen_statem:call(?MODULE, {request_lsp, ServiceMap}). delete_lsp(ServiceId) -> - gen_statem:cast(?MODULE, {delete_lsp, ServiceId}). + gen_statem:call(?MODULE, {delete_lsp, ServiceId}). %%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init([]) -> ?LOG_INFO("Starting server...", []), - {ok, wait_context, #data{}}. + case tfte_context:is_ready() of + false -> {ok, wait_context, #data{}}; + true -> {ok, ready, #data{}} + end. callback_mode() -> [handle_event_function, state_enter]. %-- WAIT_CONTEXT STATE --------------------------------------------------------- handle_event(enter, _, wait_context, _Data) -> keep_state_and_data; -handle_event(cast, {context_ready, _Context}, wait_contex, Data) -> +handle_event(cast, {context_ready, _Context}, wait_context, Data) -> ?LOG_DEBUG("Teraflow context initialized: ~p", [_Context]), tfte_topology:context_updated(), {next_state, ready, Data}; @@ -92,20 +95,20 @@ handle_event(cast, {topology_ready, _Topology}, ready, _Data) -> handle_event(cast, {topology_event, _Event}, ready, _Data) -> ?LOG_DEBUG("Teraflow topology event: ~p", [_Event]), keep_state_and_data; -handle_event({call, _From}, {request_lsp, ServiceMap}, ready, Data) -> +handle_event({call, From}, {request_lsp, ServiceMap}, ready, Data) -> #{service_id := ServiceId} = ServiceMap, ?LOG_DEBUG("Teraflow service ~s requested its LSPs", [format_service_id(ServiceId)]), {Result, Data2} = do_request_lsp(Data, ServiceMap), - {keep_state, Data2, [{reply, Result}]}; -handle_event(cast, {delete_lsp, ServiceId}, ready, Data) -> + {keep_state, Data2, [{reply, From, Result}]}; +handle_event({call, From}, {delete_lsp, ServiceId}, ready, Data) -> ?LOG_DEBUG("Teraflow service ~s delete its LSPs", [format_service_id(ServiceId)]), {Result, Data2} = do_delete_lsp(Data, ServiceId), - {keep_state, Data2, [{reply, Result}]}; + {keep_state, Data2, [{reply, From, Result}]}; %-- ANY STATE ------------------------------------------------------------------ -handle_event(EventType, EventContent, State, Data) -> - ?LOG_WARNING(Data, "Unexpected ~w event in state ~w: ~w", +handle_event(EventType, EventContent, State, _Data) -> + ?LOG_WARNING("Unexpected tfte_server ~w event in state ~w: ~w", [EventType, State, EventContent]), keep_state_and_data. @@ -125,12 +128,14 @@ format_service_id(#{context_id := #{context_uuid := #{uuid := ContextName}}, do_request_lsp(#data{services = Services} = Data, #{service_type := 'SERVICETYPE_TE'} = ServiceMap) -> + try + #{service_config := Config, service_endpoint_ids := Endpoints, service_id := ServiceId} = ServiceMap, - #{binding_label := BindingLabel1, symbolic_name := SymbolicName1} + #{<<"binding_label">> := BindingLabel1, <<"symbolic_name">> := SymbolicName1} = tfte_util:custom_config(Config, <<"/lsp-fw">>), - #{binding_label := BindingLabel2, symbolic_name := SymbolicName2} + #{<<"binding_label">> := BindingLabel2, <<"symbolic_name">> := SymbolicName2} = tfte_util:custom_config(Config, <<"/lsp-bw">>), [#{device_id := #{device_uuid := #{uuid := Id1}}}, #{device_id := #{device_uuid := #{uuid := Id2}}}] = Endpoints, @@ -152,6 +157,12 @@ do_request_lsp(#data{services = Services} = Data, Data2 = Data#data{services = Services2}, {{ok, 'SERVICESTATUS_ACTIVE'}, Data2} end + end + + catch T:E:S -> + ?LOG_ERROR("Error while requesintg LSP: ~p:~p", [T, E]), + ?LOG_ERROR("Stacktrace: ~p", [S]), + erlang:raise(T, E, S) end. do_delete_lsp(Data, ServiceId) -> diff --git a/src/te/apps/tfte/src/tfte_te_service.erl b/src/te/apps/tfte/src/tfte_te_service.erl index f0e7116d2..29aea875a 100644 --- a/src/te/apps/tfte/src/tfte_te_service.erl +++ b/src/te/apps/tfte/src/tfte_te_service.erl @@ -23,13 +23,13 @@ request_lsp(Ctx, Service) -> ?LOG_INFO("Requesting LSP: ~p", [Service]), try tfte_server:request_lsp(Service) of {ok, Status} -> - {ok, Status, Ctx}; + {ok, #{service_status => Status}, Ctx}; {error, Reason} -> ?LOG_INFO("Error while requesting LSP: ~p", [Reason]), - {ok, 'SERVICESTATUS_UNDEFINED', Ctx} + {ok, #{service_status => 'SERVICESTATUS_UNDEFINED'}, Ctx} catch E:R:S -> ?LOG_ERROR("Error while requesting LSP: ~p:~p ~p", [E, R, S]), - {ok, 'SERVICESTATUS_UNDEFINED', Ctx} + {ok, #{service_status => 'SERVICESTATUS_UNDEFINED'}, Ctx} end. update_lsp(_Ctx, _ServiceId) -> @@ -43,8 +43,8 @@ delete_lsp(Ctx, ServiceId) -> {ok, Status, Ctx}; {error, Reason} -> ?LOG_INFO("Error while deleting LSP: ~p", [Reason]), - {ok, 'SERVICESTATUS_UNDEFINED', Ctx} + {ok, #{service_status => 'SERVICESTATUS_UNDEFINED'}, Ctx} catch E:R:S -> ?LOG_ERROR("Error while deleting LSP: ~p:~p ~p", [E, R, S]), - {ok, 'SERVICESTATUS_UNDEFINED', Ctx} + {ok, #{service_status => 'SERVICESTATUS_UNDEFINED'}, Ctx} end. diff --git a/src/te/apps/tfte/src/tfte_topology.erl b/src/te/apps/tfte/src/tfte_topology.erl index d9505e914..22fc50932 100644 --- a/src/te/apps/tfte/src/tfte_topology.erl +++ b/src/te/apps/tfte/src/tfte_topology.erl @@ -29,7 +29,8 @@ sub :: term() | undefined, obj :: map() | undefined, devices = #{} :: map(), - links = #{} :: map() + links = #{} :: map(), + names = #{} :: map() }). @@ -42,7 +43,7 @@ %%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start_link() -> - gen_statem:start_link(?MODULE, [], []). + gen_statem:start_link({local, ?MODULE}, ?MODULE, [], []). context_updated() -> gen_statem:cast(?MODULE, context_updated). @@ -69,9 +70,15 @@ handle_event(state_timeout, do_retrieve, retrieve, #data{uuid = UUID} = Data) -> case get_object(UUID) of error -> {keep_state_and_data, [{state_timeout, ?RETRIEVE_RETRY_TIMEOUT, do_retrieve}]}; - {ok, Topology} -> - ?LOG_DEBUG("Got topology: ~p", [Topology]), - {next_state, subscribe, Data#data{obj = Topology}} + {ok, #{device_ids := Devices, link_ids := Links } = Topology} -> + case {length(Devices), length(Links)} of + {D, L} when D =:= 0; L =:= 0 -> + ?LOG_WARNING("Got topology, but there is missing devices or links", []), + {keep_state_and_data, [{state_timeout, 1000, do_retrieve}]}; + _ -> + ?LOG_DEBUG("Got topology: ~p", [Topology]), + {next_state, subscribe, Data#data{obj = Topology}} + end end; handle_event(cast, context_updated, retrieve, _Data) -> {keep_state_and_data, [{state_timeout, 0, do_retrieve}]}; @@ -197,79 +204,74 @@ post_topology_event({link_deleted, Id}) -> epce_ted:link_deleted(Id). update_devices(#data{devices = OldDevices} = Data, DeviceIds, Events) -> - {NewDevices, Events2} = update_devices(OldDevices, #{}, DeviceIds, Events), - {Data#data{devices = NewDevices}, Events2}. + update_devices(Data, OldDevices, #{}, DeviceIds, Events). -update_devices(OldDevices, NewDevices, [], Events) -> - Events2 = [{device_deleted, post_process_device_id(I)} +update_devices(Data, OldDevices, NewDevices, [], Events) -> + #data{names = Names} = Data, + Events2 = [{device_deleted, maps:get(I, Names, undefined)} || I <- maps:keys(OldDevices)] ++ Events, - {NewDevices, Events2}; -update_devices(OldDevices, NewDevices, [Id | Rest], Events) -> - case get_device(Id) of - error -> throw({device_retrieval_error, Id}); + {Data#data{devices = NewDevices}, Events2}; +update_devices(Data, OldDevices, NewDevices, [GivenId | Rest], Events) -> + case get_device(GivenId) of + error -> throw({device_retrieval_error, GivenId}); {ok, Device} -> - Id2 = post_process_device_id(Id), - Device2 = post_process_device(Device), + Device2 = #{id := Id, real_id := RealId} = post_process_device(Device), + #data{names = Names} = Data, + Data2 = Data#data{names = Names#{RealId => Id}}, NewDevices2 = NewDevices#{Id => Device}, case maps:take(Id, OldDevices) of error -> % New device - Events2 = [{device_added, Id2, Device2} | Events], - update_devices(OldDevices, NewDevices2, Rest, Events2); + Events2 = [{device_added, Id, Device2} | Events], + update_devices(Data2, OldDevices, NewDevices2, Rest, Events2); {Device, OldDevices2} -> % Device did not change - update_devices(OldDevices2, NewDevices2, Rest, Events); + update_devices(Data2, OldDevices2, NewDevices2, Rest, Events); {_OldDevice, OldDevices2} -> % Device changed - Events2 = [{device_updated, Id2, Device2} | Events], - update_devices(OldDevices2, NewDevices2, Rest, Events2) + Events2 = [{device_updated, Id, Device2} | Events], + update_devices(Data2, OldDevices2, NewDevices2, Rest, Events2) end end. update_links(#data{links = OldLinks} = Data, LinksIds, Events) -> - {NewLinks, Events2} = update_links(OldLinks, #{}, LinksIds, Events), - {Data#data{links = NewLinks}, Events2}. + update_links(Data, OldLinks, #{}, LinksIds, Events). -update_links(OldLinks, NewLinks, [], Events) -> +update_links(Data, OldLinks, NewLinks, [], Events) -> Events2 = [{link_deleted, post_process_link_id(I)} || I <- maps:keys(OldLinks)] ++ Events, - {NewLinks, Events2}; -update_links(OldLinks, NewLinks, [Id | Rest], Events) -> + {Data#data{links = NewLinks}, Events2}; +update_links(Data, OldLinks, NewLinks, [Id | Rest], Events) -> case get_link(Id) of error -> throw({link_retrieval_error, Id}); {ok, Link} -> Id2 = post_process_link_id(Id), - Link2 = post_process_link(Link), + Link2 = post_process_link(Data, Link), NewLinks2 = NewLinks#{Id => Link}, case maps:take(Id, OldLinks) of error -> % New Link Events2 = [{link_added, Id2, Link2} | Events], - update_links(OldLinks, NewLinks2, Rest, Events2); + update_links(Data, OldLinks, NewLinks2, Rest, Events2); {Link, OldLinks2} -> % Link did not change - update_links(OldLinks2, NewLinks2, Rest, Events); + update_links(Data, OldLinks2, NewLinks2, Rest, Events); {_OldLink, OldLinks2} -> % Link changed Events2 = [{link_updated, Id2, Link2} | Events], - update_links(OldLinks2, NewLinks2, Rest, Events2) + update_links(Data, OldLinks2, NewLinks2, Rest, Events2) end end. -post_process_device_id(#{device_uuid := #{uuid := Name}}) -> - Name. - -post_process_device(Device) -> - #{id => device_id(Device), +post_process_device(#{device_id := Id, name := Name} = Device) -> + #{id => Name, + real_id => Id, type => device_type(Device), pcc_address => device_pcc_address(Device), mpls_label => device_mpls_label(Device), status => device_status(Device), endpoints => device_endpoints(Device)}. -device_id(#{device_id := Id}) -> - post_process_device_id(Id). - device_type(#{device_type := Type}) -> Type. @@ -312,33 +314,34 @@ device_endpoints(#{device_endpoints := Endpoints}, Acc) -> device_endpoints(Endpoints, Acc); device_endpoints([], Acc) -> lists:reverse(Acc); -device_endpoints([#{endpoint_id := #{endpoint_uuid := #{uuid := Name}}} | Rest], Acc) -> +device_endpoints([#{name := Name} | Rest], Acc) -> device_endpoints(Rest, [Name | Acc]). post_process_link_id(#{link_uuid := #{uuid := Name}}) -> Name. -post_process_link(Link) -> +post_process_link(Data, Link) -> #{id => link_id(Link), - endpoints => link_endpoints(Link)}. + endpoints => link_endpoints(Data, Link)}. link_id(#{link_id := Id}) -> post_process_link_id(Id). -link_endpoints(Link) -> - link_endpoints(Link, []). +link_endpoints(Data, Link) -> + link_endpoints(Data, Link, []). -link_endpoints(#{link_endpoint_ids := Endpoints}, Acc) -> - link_endpoints(Endpoints, Acc); -link_endpoints([], Acc) -> +link_endpoints(Data, #{link_endpoint_ids := Endpoints}, Acc) -> + link_endpoints(Data, Endpoints, Acc); +link_endpoints(_Data, [], Acc) -> lists:reverse(Acc); -link_endpoints([#{device_id := #{device_uuid := #{uuid := DevName}}, - endpoint_uuid := #{uuid := EndpointName}} | Rest], Acc) -> +link_endpoints(Data, [#{device_id := RealId, + endpoint_uuid := #{uuid := EndpointName}} | Rest], Acc) -> + #data{names = Names} = Data, Endpoint = #{ - device => DevName, + device => maps:get(RealId, Names, undefined), endpoint => EndpointName }, - link_endpoints(Rest, [Endpoint | Acc]). + link_endpoints(Data, Rest, [Endpoint | Acc]). %-- GRPC UNTILITY FUNCTION ----------------------------------------------------- diff --git a/src/te/apps/tfte/src/tfte_util.erl b/src/te/apps/tfte/src/tfte_util.erl index 7b766b5ba..444828d5c 100644 --- a/src/te/apps/tfte/src/tfte_util.erl +++ b/src/te/apps/tfte/src/tfte_util.erl @@ -18,7 +18,7 @@ custom_config(#{config_rules := Rules}, Key) -> custom_config([], _Key) -> undefined; custom_config([#{action := 'CONFIGACTION_SET', - config_rule := {custom, Rule}} | Rest], Key) -> + config_rule := {custom, Rule}} | Rest], Key) -> case Rule of #{resource_key := Key, resource_value := Value} -> jsx:decode(Value); _ -> custom_config(Rest, Key) diff --git a/src/te/tests/test_te_service.py b/src/te/tests/test_te_service.py new file mode 100644 index 000000000..282aa8592 --- /dev/null +++ b/src/te/tests/test_te_service.py @@ -0,0 +1,94 @@ +# Simple script to test GRPC calls to the TE service. +# First get the TE service IP using: +# > kubectl -n tfs get services +# Change it in this script and run with: +# > PYTHONPATH=./src python test_te_service.py + +import json, sys +from common.proto.context_pb2 import ConfigActionEnum, Service, ServiceStatusEnum, ServiceTypeEnum +from common.tools.grpc.Tools import grpc_message_to_json_string +from service.client.TEServiceClient import TEServiceClient + +# {"name": "", +# "service_config": { +# "config_rules": [ +# { +# "action": "CONFIGACTION_SET", +# "custom": { +# "resource_key": "/lsp-fw", +# "resource_value": "{\n\"binding_label\": 1111,\n\"symbolic_name\": \"foo\"\n}"}}, +# { +# "action": "CONFIGACTION_SET", +# "custom": { +# "resource_key": "/lsp-bw", +# "resource_value": "{\n\"binding_label\": 6666,\n\"symbolic_name\": \"bar\"\n}"}}]}, +# "service_constraints": [], +# "service_endpoint_ids": [ +# {"device_id": {"device_uuid": {"uuid": "RT1"}}, "endpoint_uuid": {"uuid": "eth-src"}}, +# {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-dst"}}], +# "service_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, +# "service_uuid": {"uuid": "2c025055-bf6c-4250-8560-cf62f2d29e72"}}, +# "service_status": {"service_status": "SERVICESTATUS_PLANNED"}, +# "service_type": "SERVICETYPE_TE"} + +service = Service() +service.service_id.context_id.context_uuid.uuid = 'admin' +service.service_id.service_uuid.uuid = 'test-te-service' + +service.service_type = ServiceTypeEnum.SERVICETYPE_TE +service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED + +# SRC Endpoint: +src_endpoint_id = service.service_endpoint_ids.add() +src_endpoint_id.device_id.device_uuid.uuid = 'RT1' +src_endpoint_id.endpoint_uuid.uuid = 'eth-src' + +# DST Endpoint: +dst_endpoint_id = service.service_endpoint_ids.add() +dst_endpoint_id.device_id.device_uuid.uuid = 'RT6' +dst_endpoint_id.endpoint_uuid.uuid = 'eth-dst' + +# # Capacity SLA +# sla_capacity = service.service_constraints.add() +# sla_capacity.sla_capacity.capacity_gbps = 10.0 + +# # Latency SLA +# sla_latency = service.service_constraints.add() +# sla_latency.sla_latency.e2e_latency_ms = 20.0 + +# Example config rules: +config_rule_1 = service.service_config.config_rules.add() +config_rule_1.action = ConfigActionEnum.CONFIGACTION_SET +config_rule_1.custom.resource_key = '/lsp-fw' +config_rule_1.custom.resource_value = json.dumps({ + 'binding_label': 1111, 'symbolic_name': "foo" +}) + +config_rule_2 = service.service_config.config_rules.add() +config_rule_2.action = ConfigActionEnum.CONFIGACTION_SET +config_rule_2.custom.resource_key = '/lsp-bw' +config_rule_2.custom.resource_value = json.dumps({ + 'binding_label': 6666, 'symbolic_name': "bar" +}) + +def main(): + # Connect: + te_service_client = TEServiceClient(host='XXX.XXX.XXX.XXX', port=10030) + + # RequestLSP + print('request:', grpc_message_to_json_string(service)) + service_status = te_service_client.RequestLSP(service) + print('response:', grpc_message_to_json_string(service_status)) + + # DeleteLSP + #print('request:', grpc_message_to_json_string(service)) + #service_status = te_service_client.DeleteLSP(service) + #print('response:', grpc_message_to_json_string(service_status)) + + # Close: + te_service_client.close() + + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/te/tutorial/2-6-te-demo.md b/src/te/tutorial/2-6-te-demo.md index d514adf07..c652b68bd 100644 --- a/src/te/tutorial/2-6-te-demo.md +++ b/src/te/tutorial/2-6-te-demo.md @@ -89,6 +89,11 @@ Then in second console: # cd /tmp/negen # ./tmux.sh +Be sure that both PCC connected to the TE service before going further. +This can be done by looking at the TE service log: + + $ kubectl --namespace tfs logs $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -c server + ### Setup a flow from the Erlang console We will setup two unidirectional flow between router 1 and 6. @@ -98,6 +103,11 @@ We will use the binding label 1111 for the flow from router 1 to router 6, and t 1> {ok, Flow1to6} = epce_server:initiate_flow(<<"foo">>, {1, 1, 1, 1}, {6, 6, 6, 6}, 1111). 2> {ok, Flow6to1} = epce_server:initiate_flow(<<"bar">>, {6, 6, 6, 6}, {1, 1, 1, 1}, 6666). +Another option is to use the router names: + + 1> {ok, Flow1to6} = epce_server:initiate_flow(<<"foo">>, <<"RT1">>, <<"RT6">>, 1111). + 2> {ok, Flow6to1} = epce_server:initiate_flow(<<"bar">>, <<"RT6">>, <<"RT1">>, 6666). + Now if we go to the tmux session src (Ctrl-B 0) we can ping dst: $ ping 9.9.9.2 @@ -105,3 +115,16 @@ Now if we go to the tmux session src (Ctrl-B 0) we can ping dst: From the Erlang console we can update the initiated flows to change the path the packets are flowing through: 3> epce_server:update_flow(Flow6to1, [16050, 16030, 16010]). + +### Setup a flow using the GRPC test script + +This does the same as the the setup from the Erlang console, but through GRPC. +After deploying Teraflow (with te), get the te service ip using: + + $ kubectl -n tfs get services + +Replace the IP in the python script `src/te/tests/test_te_service.py`. +Be sure the topology as been loaded, and netgen started as described previously, +and run the following command from the root of the teraflow controller: + + $ PYTHONPATH=./src python src/te/tests/test_te_service.py -- GitLab From 90947f1a6a438731175e5e86edb406ae14bf4b89 Mon Sep 17 00:00:00 2001 From: merle Date: Tue, 11 Jul 2023 06:22:36 -0700 Subject: [PATCH 48/63] Update readme for Teraflow deployment --- my_deploy.sh | 3 +++ src/te/README.md | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/my_deploy.sh b/my_deploy.sh index 7e8abb128..5a830cda4 100755 --- a/my_deploy.sh +++ b/my_deploy.sh @@ -22,6 +22,9 @@ export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/" # Set the list of components, separated by spaces, you want to build images for, and deploy. export TFS_COMPONENTS="context device pathcomp service slice compute webui load_generator" +# Uncomment to activate TE +#export TFS_COMPONENTS="${TFS_COMPONENTS} te" + # Uncomment to activate Monitoring #export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring" diff --git a/src/te/README.md b/src/te/README.md index d6f1952ac..dc5570e00 100644 --- a/src/te/README.md +++ b/src/te/README.md @@ -67,6 +67,13 @@ The docker image must be built from the root of the Teraflow project: $ kubectl --namespace tfs exec -ti $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -c server -- /tfte/bin/tfte remote_console -## Show Logs +### Show Logs $ kubectl --namespace tfs logs $(kubectl --namespace tfs get pods --selector=app=teservice -o name) -c server + + +## Teraflow + +To build and deploy the TE service as part of Teraflow, the following line must be added or uncomented in your `my_deploy.sh`: + + export TFS_COMPONENTS="${TFS_COMPONENTS} te" -- GitLab From c97e2b244ba151e327e5aa9b602696f6fe8a2865 Mon Sep 17 00:00:00 2001 From: merle Date: Tue, 11 Jul 2023 06:36:41 -0700 Subject: [PATCH 49/63] Update readme --- src/te/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/te/README.md b/src/te/README.md index dc5570e00..5da478e61 100644 --- a/src/te/README.md +++ b/src/te/README.md @@ -1,6 +1,8 @@ TeraFlow Traffic Engineering Service ==================================== +This service is mean as an example of a Teraflow Service made in Erlang. + The Traffic Engineering service is tested on Ubuntu 20.04. Follow the instructions below to build, test, and run this service on your local environment. -- GitLab From dc30993896f41ae5664bbcdcaf55afa0e9a4cf63 Mon Sep 17 00:00:00 2001 From: merle Date: Tue, 11 Jul 2023 08:03:09 -0700 Subject: [PATCH 50/63] Fix missing application descriptor --- proto/src/erlang/.gitignore | 1 + proto/src/erlang/src/tfpb.app.src | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 proto/src/erlang/src/tfpb.app.src diff --git a/proto/src/erlang/.gitignore b/proto/src/erlang/.gitignore index 9b34c9cdb..b583d287f 100644 --- a/proto/src/erlang/.gitignore +++ b/proto/src/erlang/.gitignore @@ -1,4 +1,5 @@ * !rebar.config !rebar.lock +!src/ !src/tfpb.app.src diff --git a/proto/src/erlang/src/tfpb.app.src b/proto/src/erlang/src/tfpb.app.src new file mode 100644 index 000000000..abbe6bbcc --- /dev/null +++ b/proto/src/erlang/src/tfpb.app.src @@ -0,0 +1,10 @@ +{application, tfpb, + [{description, "Teraflow Erlang Protocol Buffers"}, + {vsn, "0.1.0"}, + {registered, []}, + {applications, [kernel, stdlib, grpcbox]}, + {env, []}, + {modules, []}, + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. -- GitLab From 6e26b7907323560343292d2a6df3369b3498a490 Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 12 Jul 2023 03:07:09 -0700 Subject: [PATCH 51/63] Better handling of bad GRPC arguments --- src/te/apps/tfte/src/tfte_server.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/te/apps/tfte/src/tfte_server.erl b/src/te/apps/tfte/src/tfte_server.erl index 85441bcb1..1185a3f2f 100644 --- a/src/te/apps/tfte/src/tfte_server.erl +++ b/src/te/apps/tfte/src/tfte_server.erl @@ -96,9 +96,8 @@ handle_event(cast, {topology_event, _Event}, ready, _Data) -> ?LOG_DEBUG("Teraflow topology event: ~p", [_Event]), keep_state_and_data; handle_event({call, From}, {request_lsp, ServiceMap}, ready, Data) -> - #{service_id := ServiceId} = ServiceMap, ?LOG_DEBUG("Teraflow service ~s requested its LSPs", - [format_service_id(ServiceId)]), + [format_service_id(maps:get(service_id, ServiceMap, undefined))]), {Result, Data2} = do_request_lsp(Data, ServiceMap), {keep_state, Data2, [{reply, From, Result}]}; handle_event({call, From}, {delete_lsp, ServiceId}, ready, Data) -> @@ -122,6 +121,7 @@ code_change(_OldVsn, OldState, OldData, _Extra) -> %%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +format_service_id(undefined) -> <<"undefined">>; format_service_id(#{context_id := #{context_uuid := #{uuid := ContextName}}, service_uuid := #{uuid := ServiceUUID}}) -> iolist_to_binary(io_lib:format("~s:~s", [ContextName, ServiceUUID])). @@ -162,8 +162,11 @@ do_request_lsp(#data{services = Services} = Data, catch T:E:S -> ?LOG_ERROR("Error while requesintg LSP: ~p:~p", [T, E]), ?LOG_ERROR("Stacktrace: ~p", [S]), - erlang:raise(T, E, S) - end. + {{error, internal_error}, Data} + end; +do_request_lsp(Data, ServiceMap) -> + ?LOG_ERROR("Invalid arguments to RequestLSP call: ~p", [ServiceMap]), + {{error, badarg}, Data}. do_delete_lsp(Data, ServiceId) -> ?LOG_INFO("LSP DELETION REQUESTED ~p", [ServiceId]), -- GitLab From 28b6adc4e6321e31ddf6a3848998b9c88d6c12cd Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 12 Jul 2023 15:56:07 +0000 Subject: [PATCH 52/63] Pre-merge header fixing and code cleanup --- deploy/tfs.sh | 1 - manifests/serviceservice.yaml | 2 +- manifests/teservice.yaml | 2 +- my_deploy.sh | 6 +++--- src/service/client/TEServiceClient.py | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/deploy/tfs.sh b/deploy/tfs.sh index ddc0d979c..e6a0c0c10 100755 --- a/deploy/tfs.sh +++ b/deploy/tfs.sh @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -set -e ######################################################################################################################## # Read deployment settings diff --git a/manifests/serviceservice.yaml b/manifests/serviceservice.yaml index 3865fd6c0..7d7bdaa4e 100644 --- a/manifests/serviceservice.yaml +++ b/manifests/serviceservice.yaml @@ -36,7 +36,7 @@ spec: - containerPort: 9192 env: - name: LOG_LEVEL - value: "DEBUG" + value: "INFO" readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:3030"] diff --git a/manifests/teservice.yaml b/manifests/teservice.yaml index 30002bc52..15f1619df 100644 --- a/manifests/teservice.yaml +++ b/manifests/teservice.yaml @@ -1,4 +1,4 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/my_deploy.sh b/my_deploy.sh index f6664a858..888fc9890 100755 --- a/my_deploy.sh +++ b/my_deploy.sh @@ -22,9 +22,6 @@ export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/" # Set the list of components, separated by spaces, you want to build images for, and deploy. export TFS_COMPONENTS="context device pathcomp service slice compute webui load_generator" -# Uncomment to activate TE -export TFS_COMPONENTS="${TFS_COMPONENTS} te" - # Uncomment to activate Monitoring #export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring" @@ -37,6 +34,9 @@ export TFS_COMPONENTS="${TFS_COMPONENTS} te" # Uncomment to activate L3 CyberSecurity #export TFS_COMPONENTS="${TFS_COMPONENTS} l3_attackmitigator l3_centralizedattackdetector" +# Uncomment to activate TE +#export TFS_COMPONENTS="${TFS_COMPONENTS} te" + # Set the tag you want to use for your images. export TFS_IMAGE_TAG="dev" diff --git a/src/service/client/TEServiceClient.py b/src/service/client/TEServiceClient.py index 8bbbe8f9b..19ca95bce 100644 --- a/src/service/client/TEServiceClient.py +++ b/src/service/client/TEServiceClient.py @@ -1,4 +1,4 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. -- GitLab From ececb384b7850f9812e4eb251bc6571435536a20 Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 12 Jul 2023 09:15:50 -0700 Subject: [PATCH 53/63] Add and update copyright headers --- proto/generate_code_erlang.sh | 2 +- proto/src/erlang/src/tfpb.app.src | 14 ++++++++++++++ .../service/service_handlers/te/ConfigRules.py | 2 +- .../service_handlers/te/TEServiceHandler.py | 2 +- .../service/service_handlers/te/__init__.py | 2 +- src/te/Dockerfile | 2 +- src/te/apps/epce/src/epce.app.src | 14 ++++++++++++++ src/te/apps/epce/src/epce_app.erl | 16 ++++++++++++++++ .../apps/epce/src/epce_pcep_server_handler.erl | 16 ++++++++++++++++ src/te/apps/epce/src/epce_server.erl | 16 ++++++++++++++++ src/te/apps/epce/src/epce_sup.erl | 16 ++++++++++++++++ src/te/apps/epce/src/epce_ted.erl | 16 ++++++++++++++++ src/te/apps/tfte/src/tfte.app.src | 14 ++++++++++++++ src/te/apps/tfte/src/tfte_app.erl | 14 ++++++++++++++ src/te/apps/tfte/src/tfte_context.erl | 16 ++++++++++++++++ src/te/apps/tfte/src/tfte_server.erl | 16 ++++++++++++++++ src/te/apps/tfte/src/tfte_service_sup.erl | 14 ++++++++++++++ src/te/apps/tfte/src/tfte_sup.erl | 14 ++++++++++++++ src/te/apps/tfte/src/tfte_te_service.erl | 16 ++++++++++++++++ src/te/apps/tfte/src/tfte_topology.erl | 16 ++++++++++++++++ src/te/apps/tfte/src/tfte_util.erl | 16 ++++++++++++++++ src/te/rebar.config | 14 ++++++++++++++ src/te/tests/deploy_specs.sh | 14 ++++++++++++++ src/te/tests/start-testbed.sh | 13 +++++++++++++ src/te/tests/test_te_service.py | 14 ++++++++++++++ 25 files changed, 304 insertions(+), 5 deletions(-) diff --git a/proto/generate_code_erlang.sh b/proto/generate_code_erlang.sh index 471b654f9..80fb977e4 100755 --- a/proto/generate_code_erlang.sh +++ b/proto/generate_code_erlang.sh @@ -1,5 +1,5 @@ #!/bin/bash -eu -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/proto/src/erlang/src/tfpb.app.src b/proto/src/erlang/src/tfpb.app.src index abbe6bbcc..097bdc597 100644 --- a/proto/src/erlang/src/tfpb.app.src +++ b/proto/src/erlang/src/tfpb.app.src @@ -1,3 +1,17 @@ +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + {application, tfpb, [{description, "Teraflow Erlang Protocol Buffers"}, {vsn, "0.1.0"}, diff --git a/src/service/service/service_handlers/te/ConfigRules.py b/src/service/service/service_handlers/te/ConfigRules.py index 7b79fca5a..1e82751d0 100644 --- a/src/service/service/service_handlers/te/ConfigRules.py +++ b/src/service/service/service_handlers/te/ConfigRules.py @@ -1,4 +1,4 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/src/service/service/service_handlers/te/TEServiceHandler.py b/src/service/service/service_handlers/te/TEServiceHandler.py index d12d642f0..f03ca2cc8 100644 --- a/src/service/service/service_handlers/te/TEServiceHandler.py +++ b/src/service/service/service_handlers/te/TEServiceHandler.py @@ -1,4 +1,4 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/src/service/service/service_handlers/te/__init__.py b/src/service/service/service_handlers/te/__init__.py index 70a332512..1549d9811 100644 --- a/src/service/service/service_handlers/te/__init__.py +++ b/src/service/service/service_handlers/te/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/src/te/Dockerfile b/src/te/Dockerfile index 446936ca5..aaca9fe87 100644 --- a/src/te/Dockerfile +++ b/src/te/Dockerfile @@ -1,4 +1,4 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/src/te/apps/epce/src/epce.app.src b/src/te/apps/epce/src/epce.app.src index 92bf06111..13324fd2f 100644 --- a/src/te/apps/epce/src/epce.app.src +++ b/src/te/apps/epce/src/epce.app.src @@ -1,3 +1,17 @@ +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + {application, epce, [{description, "An Erlang PCE"}, {vsn, "0.1.0"}, diff --git a/src/te/apps/epce/src/epce_app.erl b/src/te/apps/epce/src/epce_app.erl index 022043b35..19f574f38 100644 --- a/src/te/apps/epce/src/epce_app.erl +++ b/src/te/apps/epce/src/epce_app.erl @@ -1,3 +1,19 @@ +%%%----------------------------------------------------------------------------- +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%%----------------------------------------------------------------------------- + -module(epce_app). -behaviour(application). diff --git a/src/te/apps/epce/src/epce_pcep_server_handler.erl b/src/te/apps/epce/src/epce_pcep_server_handler.erl index df786bfc6..dea88b5d0 100644 --- a/src/te/apps/epce/src/epce_pcep_server_handler.erl +++ b/src/te/apps/epce/src/epce_pcep_server_handler.erl @@ -1,3 +1,19 @@ +%%%----------------------------------------------------------------------------- +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%%----------------------------------------------------------------------------- + -module(epce_pcep_server_handler). -behaviour(gen_pcep_handler). diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl index c7b404fca..d1d86b576 100644 --- a/src/te/apps/epce/src/epce_server.erl +++ b/src/te/apps/epce/src/epce_server.erl @@ -1,3 +1,19 @@ +%%%----------------------------------------------------------------------------- +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%%----------------------------------------------------------------------------- + -module(epce_server). -behaviour(gen_server). diff --git a/src/te/apps/epce/src/epce_sup.erl b/src/te/apps/epce/src/epce_sup.erl index 9341201c0..79c17c9a8 100644 --- a/src/te/apps/epce/src/epce_sup.erl +++ b/src/te/apps/epce/src/epce_sup.erl @@ -1,3 +1,19 @@ +%%%----------------------------------------------------------------------------- +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%%----------------------------------------------------------------------------- + -module(epce_sup). -behaviour(supervisor). diff --git a/src/te/apps/epce/src/epce_ted.erl b/src/te/apps/epce/src/epce_ted.erl index 879b92920..8313bec1c 100644 --- a/src/te/apps/epce/src/epce_ted.erl +++ b/src/te/apps/epce/src/epce_ted.erl @@ -1,3 +1,19 @@ +%%%----------------------------------------------------------------------------- +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%%----------------------------------------------------------------------------- + -module(epce_ted). -behaviour(gen_server). diff --git a/src/te/apps/tfte/src/tfte.app.src b/src/te/apps/tfte/src/tfte.app.src index 64fffcb61..abebf1169 100644 --- a/src/te/apps/tfte/src/tfte.app.src +++ b/src/te/apps/tfte/src/tfte.app.src @@ -1,3 +1,17 @@ +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + {application, tfte, [{description, "Teraflow Traffic Engineering Service"}, {vsn, "0.1.0"}, diff --git a/src/te/apps/tfte/src/tfte_app.erl b/src/te/apps/tfte/src/tfte_app.erl index 12f8cd4fc..a629a1b13 100644 --- a/src/te/apps/tfte/src/tfte_app.erl +++ b/src/te/apps/tfte/src/tfte_app.erl @@ -1,4 +1,18 @@ %%%----------------------------------------------------------------------------- +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% %% @doc tfte public API %% @end %%%----------------------------------------------------------------------------- diff --git a/src/te/apps/tfte/src/tfte_context.erl b/src/te/apps/tfte/src/tfte_context.erl index 1acb1befe..453852f34 100644 --- a/src/te/apps/tfte/src/tfte_context.erl +++ b/src/te/apps/tfte/src/tfte_context.erl @@ -1,3 +1,19 @@ +%%%----------------------------------------------------------------------------- +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%%----------------------------------------------------------------------------- + -module(tfte_context). -behaviour(gen_statem). diff --git a/src/te/apps/tfte/src/tfte_server.erl b/src/te/apps/tfte/src/tfte_server.erl index 1185a3f2f..002bda810 100644 --- a/src/te/apps/tfte/src/tfte_server.erl +++ b/src/te/apps/tfte/src/tfte_server.erl @@ -1,3 +1,19 @@ +%%%----------------------------------------------------------------------------- +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%%----------------------------------------------------------------------------- + -module(tfte_server). -behaviour(gen_statem). diff --git a/src/te/apps/tfte/src/tfte_service_sup.erl b/src/te/apps/tfte/src/tfte_service_sup.erl index 2223589e2..6ec5d09b0 100644 --- a/src/te/apps/tfte/src/tfte_service_sup.erl +++ b/src/te/apps/tfte/src/tfte_service_sup.erl @@ -1,4 +1,18 @@ %%%----------------------------------------------------------------------------- +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% %% @doc tfte service supervisor. %% @end %%%----------------------------------------------------------------------------- diff --git a/src/te/apps/tfte/src/tfte_sup.erl b/src/te/apps/tfte/src/tfte_sup.erl index 57c95483e..4630511d6 100644 --- a/src/te/apps/tfte/src/tfte_sup.erl +++ b/src/te/apps/tfte/src/tfte_sup.erl @@ -1,4 +1,18 @@ %%%----------------------------------------------------------------------------- +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% %% @doc tfte top level supervisor. %% @end %%%----------------------------------------------------------------------------- diff --git a/src/te/apps/tfte/src/tfte_te_service.erl b/src/te/apps/tfte/src/tfte_te_service.erl index 29aea875a..b9911ee37 100644 --- a/src/te/apps/tfte/src/tfte_te_service.erl +++ b/src/te/apps/tfte/src/tfte_te_service.erl @@ -1,3 +1,19 @@ +%%%----------------------------------------------------------------------------- +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%%----------------------------------------------------------------------------- + -module(tfte_te_service). -behaviour(te_te_service_bhvr). diff --git a/src/te/apps/tfte/src/tfte_topology.erl b/src/te/apps/tfte/src/tfte_topology.erl index 22fc50932..39897caa8 100644 --- a/src/te/apps/tfte/src/tfte_topology.erl +++ b/src/te/apps/tfte/src/tfte_topology.erl @@ -1,3 +1,19 @@ +%%%----------------------------------------------------------------------------- +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%%----------------------------------------------------------------------------- + -module(tfte_topology). -behaviour(gen_statem). diff --git a/src/te/apps/tfte/src/tfte_util.erl b/src/te/apps/tfte/src/tfte_util.erl index 444828d5c..fb058c7cf 100644 --- a/src/te/apps/tfte/src/tfte_util.erl +++ b/src/te/apps/tfte/src/tfte_util.erl @@ -1,3 +1,19 @@ +%%%----------------------------------------------------------------------------- +%% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%%----------------------------------------------------------------------------- + -module(tfte_util). %%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/te/rebar.config b/src/te/rebar.config index 06bff13c7..01f7a899e 100644 --- a/src/te/rebar.config +++ b/src/te/rebar.config @@ -1,3 +1,17 @@ +% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + {erl_opts, [debug_info]}. {deps, [ diff --git a/src/te/tests/deploy_specs.sh b/src/te/tests/deploy_specs.sh index cd8871a05..13f831c47 100644 --- a/src/te/tests/deploy_specs.sh +++ b/src/te/tests/deploy_specs.sh @@ -1,3 +1,17 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Set the URL of your local Docker registry where the images will be uploaded to. export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" diff --git a/src/te/tests/start-testbed.sh b/src/te/tests/start-testbed.sh index c480b3e75..07a30e091 100755 --- a/src/te/tests/start-testbed.sh +++ b/src/te/tests/start-testbed.sh @@ -1,4 +1,17 @@ #!/bin/bash +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. set -e diff --git a/src/te/tests/test_te_service.py b/src/te/tests/test_te_service.py index 282aa8592..6237049d4 100644 --- a/src/te/tests/test_te_service.py +++ b/src/te/tests/test_te_service.py @@ -1,3 +1,17 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Simple script to test GRPC calls to the TE service. # First get the TE service IP using: # > kubectl -n tfs get services -- GitLab From 6f8e1c1a57d4c04dcbc463c3856835623dc4e2a6 Mon Sep 17 00:00:00 2001 From: merle Date: Wed, 12 Jul 2023 10:01:21 -0700 Subject: [PATCH 54/63] Add missing copyright header --- proto/src/erlang/rebar.config | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/proto/src/erlang/rebar.config b/proto/src/erlang/rebar.config index 31ec32a36..55e139eab 100644 --- a/proto/src/erlang/rebar.config +++ b/proto/src/erlang/rebar.config @@ -1,3 +1,17 @@ +% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + {erl_opts, [debug_info]}. {deps, [grpcbox]}. -- GitLab From 39504ce49e7afb2b1f5c5fbfe89c3a9361562cde Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 12 Jul 2023 17:21:52 +0000 Subject: [PATCH 55/63] Removed Empty message in L3 Centralized Attack Detector to prevent collision with Context.Empty --- proto/l3_centralizedattackdetector.proto | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proto/l3_centralizedattackdetector.proto b/proto/l3_centralizedattackdetector.proto index 5fbed424c..17fc1553e 100644 --- a/proto/l3_centralizedattackdetector.proto +++ b/proto/l3_centralizedattackdetector.proto @@ -18,10 +18,10 @@ import "context.proto"; service L3Centralizedattackdetector { // Analyze single input to the ML model in the CAD component - rpc AnalyzeConnectionStatistics (L3CentralizedattackdetectorMetrics) returns (context.Empty) {} + rpc AnalyzeConnectionStatistics (L3CentralizedattackdetectorMetrics) returns (StatusMessage) {} // Analyze a batch of inputs to the ML model in the CAD component - rpc AnalyzeBatchConnectionStatistics (L3CentralizedattackdetectorBatchInput) returns (context.Empty) {} + rpc AnalyzeBatchConnectionStatistics (L3CentralizedattackdetectorBatchInput) returns (StatusMessage) {} // Get the list of features used by the ML model in the CAD component rpc GetFeaturesIds (context.Empty) returns (AutoFeatures) {} @@ -63,6 +63,6 @@ message L3CentralizedattackdetectorBatchInput { repeated L3CentralizedattackdetectorMetrics metrics = 1; } -// message Empty { -// string message = 1; -// } +message StatusMessage { + string message = 1; +} -- GitLab From db81efc68d4e7d33bd20cd97c1879a89395546d5 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 12 Jul 2023 17:32:56 +0000 Subject: [PATCH 56/63] Pre-merge code cleanup --- .../service/ServiceServiceServicerImpl.py | 80 +++++++++---------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py index 2a6f88152..f7b2c74b0 100644 --- a/src/service/service/ServiceServiceServicerImpl.py +++ b/src/service/service/ServiceServiceServicerImpl.py @@ -117,6 +117,28 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): service.service_type = request.service_type # pylint: disable=no-member service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED # pylint: disable=no-member + if service.service_type == ServiceTypeEnum.SERVICETYPE_TE: + # TE service: + context_client.SetService(request) + + te_service_client = TEServiceClient() + service_status = te_service_client.RequestLSP(service) + + if service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE: + _service : Optional[Service] = get_service_by_id( + context_client, request.service_id, rw_copy=True, + include_config_rules=False, include_constraints=False, include_endpoint_ids=False) + _service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_ACTIVE + service_id = context_client.SetService(_service) + return service_id + else: + MSG = 'RequestLSP for Service({:s}) returned ServiceStatus({:s})' + context_uuid = request.service_id.context_id.context_uuid.uuid + service_uuid = request.service_id.service_uuid.uuid + service_key = '{:s}/{:s}'.format(context_uuid, service_uuid) + str_service_status = ServiceStatusEnum.Name(service_status.service_status) + raise Exception(MSG.format(service_key, str_service_status)) + del service.service_endpoint_ids[:] # pylint: disable=no-member for endpoint_id in request.service_endpoint_ids: service.service_endpoint_ids.add().CopyFrom(endpoint_id) # pylint: disable=no-member @@ -169,42 +191,14 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): context_client, service_id_with_uuids, rw_copy=False, include_config_rules=True, include_constraints=True, include_endpoint_ids=True) - if service.service_type == ServiceTypeEnum.SERVICETYPE_TE: - # TE service: - te_service_client = TEServiceClient() - - # Note: TE should update the service in Context. - # By now we update it manually for debugging purposes - service = Service() - service.CopyFrom(request) - service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED - context_client.SetService(request) - - service_status = te_service_client.RequestLSP(service) - - if service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE: - _service : Optional[Service] = get_service(context_client, request.service_id) - service = Service() - service.CopyFrom(_service) - service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_ACTIVE - context_client.SetService(service) - else: - MSG = 'RequestLSP for Service({:s}) returned ServiceStatus({:s})' - context_uuid = request.service_id.context_id.context_uuid.uuid - service_uuid = request.service_id.service_uuid.uuid - service_key = '{:s}/{:s}'.format(context_uuid, service_uuid) - str_service_status = ServiceStatusEnum.Name(service_status.service_status) - raise Exception(MSG.format(service_key, str_service_status)) - else: - # Normal service: - num_disjoint_paths = None - for constraint in request.service_constraints: - if constraint.WhichOneof('constraint') == 'sla_availability': - num_disjoint_paths = constraint.sla_availability.num_disjoint_paths - break + num_disjoint_paths = None + for constraint in request.service_constraints: + if constraint.WhichOneof('constraint') == 'sla_availability': + num_disjoint_paths = constraint.sla_availability.num_disjoint_paths + break - num_disjoint_paths = 1 if num_disjoint_paths is None or num_disjoint_paths == 0 else num_disjoint_paths - num_expected_endpoints = num_disjoint_paths * 2 + num_disjoint_paths = 1 if num_disjoint_paths is None or num_disjoint_paths == 0 else num_disjoint_paths + num_expected_endpoints = num_disjoint_paths * 2 tasks_scheduler = TasksScheduler(self.service_handler_factory) if len(service_with_uuids.service_endpoint_ids) >= num_expected_endpoints: @@ -253,15 +247,15 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): te_service_client = TEServiceClient() te_service_client.DeleteLSP(request) context_client.RemoveService(request) - else: - # Normal service - # Feed TaskScheduler with this service and the sub-services and sub-connections related to this service. - # TaskScheduler identifies inter-dependencies among them and produces a schedule of tasks (an ordered list of - # tasks to be executed) to implement the requested delete operation. - tasks_scheduler = TasksScheduler(self.service_handler_factory) - tasks_scheduler.compose_from_service(service, is_delete=True) - tasks_scheduler.execute_all() + return Empty() + # Normal service + # Feed TaskScheduler with this service and the sub-services and sub-connections related to this service. + # TaskScheduler identifies inter-dependencies among them and produces a schedule of tasks (an ordered list of + # tasks to be executed) to implement the requested delete operation. + tasks_scheduler = TasksScheduler(self.service_handler_factory) + tasks_scheduler.compose_from_service(service, is_delete=True) + tasks_scheduler.execute_all() return Empty() @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) -- GitLab From 2d933ddf3a72ca01a43c5ff9566ff8ced0dc6d0b Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 12 Jul 2023 17:36:59 +0000 Subject: [PATCH 57/63] Pre-merge code cleanup --- .../service/service_handlers/__init__.py | 9 +- .../service_handlers/te/ConfigRules.py | 44 ----- .../service_handlers/te/TEServiceHandler.py | 161 ------------------ .../service/service_handlers/te/__init__.py | 14 -- 4 files changed, 1 insertion(+), 227 deletions(-) delete mode 100644 src/service/service/service_handlers/te/ConfigRules.py delete mode 100644 src/service/service/service_handlers/te/TEServiceHandler.py delete mode 100644 src/service/service/service_handlers/te/__init__.py diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py index 48d88944d..491097a46 100644 --- a/src/service/service/service_handlers/__init__.py +++ b/src/service/service/service_handlers/__init__.py @@ -24,7 +24,6 @@ from .microwave.MicrowaveServiceHandler import MicrowaveServiceHandler from .p4.p4_service_handler import P4ServiceHandler from .tapi_tapi.TapiServiceHandler import TapiServiceHandler from .tapi_xr.TapiXrServiceHandler import TapiXrServiceHandler -from .te.TEServiceHandler import TEServiceHandler SERVICE_HANDLERS = [ (L2NMEmulatedServiceHandler, [ @@ -86,11 +85,5 @@ SERVICE_HANDLERS = [ FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_L2NM, FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN], } - ]), - (TEServiceHandler, [ - { - FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_TE, - FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_UNDEFINED, - } - ]), + ]) ] diff --git a/src/service/service/service_handlers/te/ConfigRules.py b/src/service/service/service_handlers/te/ConfigRules.py deleted file mode 100644 index 1e82751d0..000000000 --- a/src/service/service/service_handlers/te/ConfigRules.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -from typing import Dict, List -from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set -from service.service.service_handler_api.AnyTreeTools import TreeNode - -LOGGER = logging.getLogger(__name__) - -def setup_config_rules( - service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, - service_settings : TreeNode, endpoint_settings : TreeNode -) -> List[Dict]: - - json_settings : Dict = {} if service_settings is None else service_settings.value - json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value - - json_config_rules = [ - ] - return json_config_rules - -def teardown_config_rules( - service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, - service_settings : TreeNode, endpoint_settings : TreeNode -) -> List[Dict]: - - json_settings : Dict = {} if service_settings is None else service_settings.value - json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value - - json_config_rules = [ - ] - return json_config_rules diff --git a/src/service/service/service_handlers/te/TEServiceHandler.py b/src/service/service/service_handlers/te/TEServiceHandler.py deleted file mode 100644 index f03ca2cc8..000000000 --- a/src/service/service/service_handlers/te/TEServiceHandler.py +++ /dev/null @@ -1,161 +0,0 @@ -# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import anytree, json, logging -from typing import Any, List, Optional, Tuple, Union -from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service -from common.tools.object_factory.Device import json_device_id -from common.type_checkers.Checkers import chk_length, chk_type -from service.service.service_handler_api._ServiceHandler import _ServiceHandler -from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value -from service.service.task_scheduler.TaskExecutor import TaskExecutor -from .ConfigRules import setup_config_rules, teardown_config_rules - -LOGGER = logging.getLogger(__name__) - -class TEServiceHandler(_ServiceHandler): - def __init__( # pylint: disable=super-init-not-called - self, service : Service, task_executor : TaskExecutor, **settings - ) -> None: - self.__service = service - self.__task_executor = task_executor # pylint: disable=unused-private-member - self.__resolver = anytree.Resolver(pathattr='name') - self.__config = TreeNode('.') - for config_rule in service.service_config.config_rules: - action = config_rule.action - if config_rule.WhichOneof('config_rule') != 'custom': continue - resource_key = config_rule.custom.resource_key - resource_value = config_rule.custom.resource_value - if action == ConfigActionEnum.CONFIGACTION_SET: - try: - resource_value = json.loads(resource_value) - except: # pylint: disable=bare-except - pass - set_subnode_value(self.__resolver, self.__config, resource_key, resource_value) - elif action == ConfigActionEnum.CONFIGACTION_DELETE: - delete_subnode(self.__resolver, self.__config, resource_key) - - def SetEndpoint( - self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None - ) -> List[Union[bool, Exception]]: - chk_type('endpoints', endpoints, list) - if len(endpoints) == 0: return [] - - service_uuid = self.__service.service_id.service_uuid.uuid - settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None) - - results = [] - for endpoint in endpoints: - try: - chk_type('endpoint', endpoint, (tuple, list)) - chk_length('endpoint', endpoint, min_length=2, max_length=3) - device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now - - endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid) - endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None) - - json_config_rules = setup_config_rules( - service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings) - - device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) - for json_config_rule in json_config_rules: - device.device_config.config_rules.append(ConfigRule(**json_config_rule)) - self.__task_executor.configure_device(device) - results.append(True) - except Exception as e: # pylint: disable=broad-except - LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint))) - results.append(e) - - return results - - def DeleteEndpoint( - self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None - ) -> List[Union[bool, Exception]]: - chk_type('endpoints', endpoints, list) - if len(endpoints) == 0: return [] - - service_uuid = self.__service.service_id.service_uuid.uuid - settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None) - - results = [] - for endpoint in endpoints: - try: - chk_type('endpoint', endpoint, (tuple, list)) - chk_length('endpoint', endpoint, min_length=2, max_length=3) - device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now - - endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid) - endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None) - - json_config_rules = teardown_config_rules( - service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings) - - device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) - for json_config_rule in json_config_rules: - device.device_config.config_rules.append(ConfigRule(**json_config_rule)) - self.__task_executor.configure_device(device) - results.append(True) - except Exception as e: # pylint: disable=broad-except - LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint))) - results.append(e) - - return results - - def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: - chk_type('constraints', constraints, list) - if len(constraints) == 0: return [] - - msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.' - LOGGER.warning(msg.format(str(constraints))) - return [True for _ in range(len(constraints))] - - def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: - chk_type('constraints', constraints, list) - if len(constraints) == 0: return [] - - msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.' - LOGGER.warning(msg.format(str(constraints))) - return [True for _ in range(len(constraints))] - - def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: - chk_type('resources', resources, list) - if len(resources) == 0: return [] - - results = [] - for resource in resources: - try: - resource_key, resource_value = resource - resource_value = json.loads(resource_value) - set_subnode_value(self.__resolver, self.__config, resource_key, resource_value) - results.append(True) - except Exception as e: # pylint: disable=broad-except - LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource))) - results.append(e) - - return results - - def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: - chk_type('resources', resources, list) - if len(resources) == 0: return [] - - results = [] - for resource in resources: - try: - resource_key, _ = resource - delete_subnode(self.__resolver, self.__config, resource_key) - except Exception as e: # pylint: disable=broad-except - LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource))) - results.append(e) - - return results diff --git a/src/service/service/service_handlers/te/__init__.py b/src/service/service/service_handlers/te/__init__.py deleted file mode 100644 index 1549d9811..000000000 --- a/src/service/service/service_handlers/te/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -- GitLab From f80ef527c090c880fa553ab4ebe2a102be0bf95f Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 12 Jul 2023 17:37:45 +0000 Subject: [PATCH 58/63] Pre-merge code cleanup --- src/service/service/service_handlers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py index 491097a46..7ea0d4f62 100644 --- a/src/service/service/service_handlers/__init__.py +++ b/src/service/service/service_handlers/__init__.py @@ -85,5 +85,5 @@ SERVICE_HANDLERS = [ FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_L2NM, FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN], } - ]) + ]), ] -- GitLab From 1b5ba6ae608f607c46565e3bb583fb59682cdcd6 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 12 Jul 2023 17:47:04 +0000 Subject: [PATCH 59/63] Pre-merge code cleanup --- .../service/ServiceServiceServicerImpl.py | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py index 823122c01..e2c236ce3 100644 --- a/src/service/service/ServiceServiceServicerImpl.py +++ b/src/service/service/ServiceServiceServicerImpl.py @@ -206,27 +206,21 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): pathcomp_request = PathCompRequest() pathcomp_request.services.append(service_with_uuids) # pylint: disable=no-member - tasks_scheduler = TasksScheduler(self.service_handler_factory) - if len(service_with_uuids.service_endpoint_ids) >= num_expected_endpoints: - pathcomp_request = PathCompRequest() - pathcomp_request.services.append(service_with_uuids) # pylint: disable=no-member - - if num_disjoint_paths is None or num_disjoint_paths in {0, 1}: - pathcomp_request.shortest_path.Clear() # pylint: disable=no-member - else: - pathcomp_request.k_disjoint_path.num_disjoint = num_disjoint_paths # pylint: disable=no-member - - LOGGER.info('pathcomp_request={:s}'.format(grpc_message_to_json_string(pathcomp_request))) - pathcomp = PathCompClient() - pathcomp_reply = pathcomp.Compute(pathcomp_request) - pathcomp.close() - LOGGER.info('pathcomp_reply={:s}'.format(grpc_message_to_json_string(pathcomp_reply))) - - # Feed TaskScheduler with this path computation reply. TaskScheduler identifies inter-dependencies among - # the services and connections retrieved and produces a schedule of tasks (an ordered list of tasks to be - # executed) to implement the requested create/update operation. - tasks_scheduler.compose_from_pathcompreply(pathcomp_reply, is_delete=False) - + if num_disjoint_paths is None or num_disjoint_paths in {0, 1}: + pathcomp_request.shortest_path.Clear() # pylint: disable=no-member + else: + pathcomp_request.k_disjoint_path.num_disjoint = num_disjoint_paths # pylint: disable=no-member + + LOGGER.info('pathcomp_request={:s}'.format(grpc_message_to_json_string(pathcomp_request))) + pathcomp = PathCompClient() + pathcomp_reply = pathcomp.Compute(pathcomp_request) + pathcomp.close() + LOGGER.info('pathcomp_reply={:s}'.format(grpc_message_to_json_string(pathcomp_reply))) + + # Feed TaskScheduler with this path computation reply. TaskScheduler identifies inter-dependencies among + # the services and connections retrieved and produces a schedule of tasks (an ordered list of tasks to be + # executed) to implement the requested create/update operation. + tasks_scheduler.compose_from_pathcompreply(pathcomp_reply, is_delete=False) tasks_scheduler.execute_all() return service_with_uuids.service_id @@ -414,4 +408,4 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): updated_service_with_uuids, old_connection, new_connection) tasks_scheduler.execute_all() - return Empty() \ No newline at end of file + return Empty() -- GitLab From 394c29be0614fb445e52937ce4a5a53ca66bfb66 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 12 Jul 2023 17:49:51 +0000 Subject: [PATCH 60/63] Pre-merge code cleanup --- src/service/service/ServiceServiceServicerImpl.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py index e2c236ce3..55d0b593b 100644 --- a/src/service/service/ServiceServiceServicerImpl.py +++ b/src/service/service/ServiceServiceServicerImpl.py @@ -192,7 +192,7 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): context_client, service_id_with_uuids, rw_copy=False, include_config_rules=True, include_constraints=True, include_endpoint_ids=True) - num_disjoint_paths = None + num_disjoint_paths = 0 for constraint in request.service_constraints: if constraint.WhichOneof('constraint') == 'sla_availability': num_disjoint_paths = constraint.sla_availability.num_disjoint_paths @@ -211,18 +211,18 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): else: pathcomp_request.k_disjoint_path.num_disjoint = num_disjoint_paths # pylint: disable=no-member - LOGGER.info('pathcomp_request={:s}'.format(grpc_message_to_json_string(pathcomp_request))) + LOGGER.debug('pathcomp_request={:s}'.format(grpc_message_to_json_string(pathcomp_request))) pathcomp = PathCompClient() pathcomp_reply = pathcomp.Compute(pathcomp_request) pathcomp.close() - LOGGER.info('pathcomp_reply={:s}'.format(grpc_message_to_json_string(pathcomp_reply))) + LOGGER.debug('pathcomp_reply={:s}'.format(grpc_message_to_json_string(pathcomp_reply))) # Feed TaskScheduler with this path computation reply. TaskScheduler identifies inter-dependencies among # the services and connections retrieved and produces a schedule of tasks (an ordered list of tasks to be # executed) to implement the requested create/update operation. tasks_scheduler.compose_from_pathcompreply(pathcomp_reply, is_delete=False) - tasks_scheduler.execute_all() + tasks_scheduler.execute_all() return service_with_uuids.service_id @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) -- GitLab From dc76bee3ce2f0a754ba902bd50f710734f27076a Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 12 Jul 2023 17:53:13 +0000 Subject: [PATCH 61/63] Pre-merge code cleanup --- src/service/service/__main__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/service/service/__main__.py b/src/service/service/__main__.py index 18f91bf1a..f2b6e38d6 100644 --- a/src/service/service/__main__.py +++ b/src/service/service/__main__.py @@ -43,8 +43,6 @@ def main(): get_env_var_name(ServiceNameEnum.DEVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC), get_env_var_name(ServiceNameEnum.PATHCOMP, ENVVAR_SUFIX_SERVICE_HOST ), get_env_var_name(ServiceNameEnum.PATHCOMP, ENVVAR_SUFIX_SERVICE_PORT_GRPC), - get_env_var_name(ServiceNameEnum.TE, ENVVAR_SUFIX_SERVICE_HOST ), - get_env_var_name(ServiceNameEnum.TE, ENVVAR_SUFIX_SERVICE_PORT_GRPC), ]) signal.signal(signal.SIGINT, signal_handler) -- GitLab From 95ff26e6b2143d8c27cd0760f4fb24c6761da630 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 12 Jul 2023 18:00:09 +0000 Subject: [PATCH 62/63] Pre-merge header fixing and code cleanup --- src/te/config/dev.config.template | 14 +++ src/te/config/sys.config.src | 14 +++ src/te/tests/deploy_specs.sh | 131 ++++++++++++++++++++-- src/te/tests/netgen-config.yml | 14 +++ src/te/tests/netgen-topology.yml.template | 14 +++ 5 files changed, 175 insertions(+), 12 deletions(-) diff --git a/src/te/config/dev.config.template b/src/te/config/dev.config.template index 6ca3af506..6e21dc0d2 100644 --- a/src/te/config/dev.config.template +++ b/src/te/config/dev.config.template @@ -1,3 +1,17 @@ +% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + [ {tfte, [ {services, [ diff --git a/src/te/config/sys.config.src b/src/te/config/sys.config.src index df373de74..edcd4384a 100644 --- a/src/te/config/sys.config.src +++ b/src/te/config/sys.config.src @@ -1,3 +1,17 @@ +% Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. + [ {tfte, [ diff --git a/src/te/tests/deploy_specs.sh b/src/te/tests/deploy_specs.sh index 13f831c47..818fb2b0d 100644 --- a/src/te/tests/deploy_specs.sh +++ b/src/te/tests/deploy_specs.sh @@ -12,29 +12,136 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Set the URL of your local Docker registry where the images will be uploaded to. -export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" + +# ----- TeraFlowSDN ------------------------------------------------------------ + +# Set the URL of the internal MicroK8s Docker registry where the images will be uploaded to. +export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/" # Set the list of components, separated by spaces, you want to build images for, and deploy. -# Supported components are: -# context device automation policy service compute monitoring webui -# interdomain slice pathcomp dlt -# dbscanserving opticalattackmitigator opticalattackdetector -# l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector -export TFS_COMPONENTS="context device pathcomp service slice webui te" +#export TFS_COMPONENTS="context device pathcomp service slice compute webui load_generator" +export TFS_COMPONENTS="context device pathcomp service slice webui" + +# Uncomment to activate Monitoring +#export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring" + +# Uncomment to activate Automation and Policy Manager +#export TFS_COMPONENTS="${TFS_COMPONENTS} automation policy" + +# Uncomment to activate Optical CyberSecurity +#export TFS_COMPONENTS="${TFS_COMPONENTS} dbscanserving opticalattackmitigator opticalattackdetector opticalattackmanager" + +# Uncomment to activate L3 CyberSecurity +#export TFS_COMPONENTS="${TFS_COMPONENTS} l3_attackmitigator l3_centralizedattackdetector" + +# Uncomment to activate TE +export TFS_COMPONENTS="${TFS_COMPONENTS} te" # Set the tag you want to use for your images. export TFS_IMAGE_TAG="dev" -# Set the name of the Kubernetes namespace to deploy to. +# Set the name of the Kubernetes namespace to deploy TFS to. export TFS_K8S_NAMESPACE="tfs" # Set additional manifest files to be applied after the deployment export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml" +# Uncomment to monitor performance of components +#export TFS_EXTRA_MANIFESTS="${TFS_EXTRA_MANIFESTS} manifests/servicemonitors.yaml" + +# Uncomment when deploying Optical CyberSecurity +#export TFS_EXTRA_MANIFESTS="${TFS_EXTRA_MANIFESTS} manifests/cachingservice.yaml" + # Set the new Grafana admin password export TFS_GRAFANA_PASSWORD="admin123+" -# If not already set, disable skip-build flag. -# If TFS_SKIP_BUILD is "YES", the containers are not rebuilt-retagged-repushed and existing ones are used. -export TFS_SKIP_BUILD=${TFS_SKIP_BUILD:-""} +# Disable skip-build flag to rebuild the Docker images. +export TFS_SKIP_BUILD="" + + +# ----- CockroachDB ------------------------------------------------------------ + +# Set the namespace where CockroackDB will be deployed. +export CRDB_NAMESPACE="crdb" + +# Set the external port CockroackDB Postgre SQL interface will be exposed to. +export CRDB_EXT_PORT_SQL="26257" + +# Set the external port CockroackDB HTTP Mgmt GUI interface will be exposed to. +export CRDB_EXT_PORT_HTTP="8081" + +# Set the database username to be used by Context. +export CRDB_USERNAME="tfs" + +# Set the database user's password to be used by Context. +export CRDB_PASSWORD="tfs123" + +# Set the database name to be used by Context. +export CRDB_DATABASE="tfs" + +# Set CockroachDB installation mode to 'single'. This option is convenient for development and testing. +# See ./deploy/all.sh or ./deploy/crdb.sh for additional details +export CRDB_DEPLOY_MODE="single" + +# Disable flag for dropping database, if it exists. +export CRDB_DROP_DATABASE_IF_EXISTS="YES" + +# Disable flag for re-deploying CockroachDB from scratch. +export CRDB_REDEPLOY="" + + +# ----- NATS ------------------------------------------------------------------- + +# Set the namespace where NATS will be deployed. +export NATS_NAMESPACE="nats" + +# Set the external port NATS Client interface will be exposed to. +export NATS_EXT_PORT_CLIENT="4222" + +# Set the external port NATS HTTP Mgmt GUI interface will be exposed to. +export NATS_EXT_PORT_HTTP="8222" + +# Disable flag for re-deploying NATS from scratch. +export NATS_REDEPLOY="" + + +# ----- QuestDB ---------------------------------------------------------------- + +# Set the namespace where QuestDB will be deployed. +export QDB_NAMESPACE="qdb" + +# Set the external port QuestDB Postgre SQL interface will be exposed to. +export QDB_EXT_PORT_SQL="8812" + +# Set the external port QuestDB Influx Line Protocol interface will be exposed to. +export QDB_EXT_PORT_ILP="9009" + +# Set the external port QuestDB HTTP Mgmt GUI interface will be exposed to. +export QDB_EXT_PORT_HTTP="9000" + +# Set the database username to be used for QuestDB. +export QDB_USERNAME="admin" + +# Set the database user's password to be used for QuestDB. +export QDB_PASSWORD="quest" + +# Set the table name to be used by Monitoring for KPIs. +export QDB_TABLE_MONITORING_KPIS="tfs_monitoring_kpis" + +# Set the table name to be used by Slice for plotting groups. +export QDB_TABLE_SLICE_GROUPS="tfs_slice_groups" + +# Disable flag for dropping tables if they exist. +export QDB_DROP_TABLES_IF_EXIST="YES" + +# Disable flag for re-deploying QuestDB from scratch. +export QDB_REDEPLOY="" + + +# ----- K8s Observability ------------------------------------------------------ + +# Set the external port Prometheus Mgmt HTTP GUI interface will be exposed to. +export PROM_EXT_PORT_HTTP="9090" + +# Set the external port Grafana HTTP Dashboards will be exposed to. +export GRAF_EXT_PORT_HTTP="3000" diff --git a/src/te/tests/netgen-config.yml b/src/te/tests/netgen-config.yml index 7f1de319f..d037088ce 100644 --- a/src/te/tests/netgen-config.yml +++ b/src/te/tests/netgen-config.yml @@ -1,3 +1,17 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Directory used for Netgen's operation. # Default: "/tmp/netgen" # netgen_runstatedir: diff --git a/src/te/tests/netgen-topology.yml.template b/src/te/tests/netgen-topology.yml.template index d16e34841..fd21c4363 100644 --- a/src/te/tests/netgen-topology.yml.template +++ b/src/te/tests/netgen-topology.yml.template @@ -1,3 +1,17 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # # +---------+ # | | -- GitLab From e679c52e5c9173717bf479cd11fba59c012cdf57 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 12 Jul 2023 18:05:13 +0000 Subject: [PATCH 63/63] Pre-merge code cleanup --- src/te/tutorial/2-6-te-demo.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/te/tutorial/2-6-te-demo.md b/src/te/tutorial/2-6-te-demo.md index c652b68bd..c53a60c51 100644 --- a/src/te/tutorial/2-6-te-demo.md +++ b/src/te/tutorial/2-6-te-demo.md @@ -1,4 +1,4 @@ -# 2.6. Traffic Engineering Demo (WIP) +# 2.6. Traffic Engineering Demo ## Setup Test-Bed -- GitLab