diff --git a/deploy.sh b/deploy.sh index b79d7e143db6ca473fcf020b0fed4e15634c0801..de6ed0c27cf6379c4154cd85068725f812472097 100755 --- a/deploy.sh +++ b/deploy.sh @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +set -e ######################################################################################################################## # Read deployment settings @@ -54,7 +55,7 @@ TMP_LOGS_FOLDER="$TMP_FOLDER/logs" mkdir -p $TMP_LOGS_FOLDER echo "Deleting and Creating a new namespace..." -kubectl delete namespace $TFS_K8S_NAMESPACE +kubectl delete --ignore-not-found=true namespace $TFS_K8S_NAMESPACE kubectl create namespace $TFS_K8S_NAMESPACE printf "\n" diff --git a/manifests/teservice.yaml b/manifests/teservice.yaml index 313acd27328f9d9a238bf8214aa9b6da52468581..3f7bd1f046152c8a86925880827d0cd3db0af24d 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 d9d561d4d8000c15a19108cf13de7f7834361362..f580ccf4e5db0fe714633c9a5b295b68e68ad283 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 0000000000000000000000000000000000000000..92bf061115285fae58663bf81368d81939ae0705 --- /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 0000000000000000000000000000000000000000..022043b35cf97ce35aa5a079cfa95f2fc3b30ba1 --- /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 0000000000000000000000000000000000000000..2103958857efbe49e1005dfaf302c997b5a75eeb --- /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 0000000000000000000000000000000000000000..bd3a13f4429590268346d4569a95eb6a09fd3a06 --- /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 0000000000000000000000000000000000000000..21a6a56b2ff39b05b2bc440f1089fb18024fe8ef --- /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 76d397939f51f0d9d548407da0574aed4c566113..06adeb848ba96963c734f1c2bc6de2f3771eba8d 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 658bf13f8e69db439147732d607299110491c960..5897c4e468e830736e579d7649f8350365ab2ead 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 2bd5cf1784625c02f4a71c32506e92c5b23946e6..93bfbafb1cf9ee5d457fc41dc2e97389deb731f0 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 1063fd38cc74dee1d4a628785547d0202facdaf7..3451955c2f59e22eed47b475c96e64120ea2f894 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 d353eaf344ffe261be7200c1cb3a1ad76bf80703..6eb067ecd1c93bbf3aa900a4f602cdd9f7405d4a 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-0-run-experiments.md b/tutorial/2-0-run-experiments.md index f87d00e98a66449f5fa6d267c527565b145722b2..ad24131c13d3e67989eeb922fa9b65ad20e2462f 100644 --- a/tutorial/2-0-run-experiments.md +++ b/tutorial/2-0-run-experiments.md @@ -10,3 +10,4 @@ commands you might need, configuring the network topology, and executing differe - [2.3. OECC/PSC'22 Demo (WORK IN PROGRESS)](./2-3-oeccpsc22.md) - [2.4. ECOC'22 Demo (PENDING)](./2-4-ecoc22.md) - [2.5. NFV-SDN'22 Demo (PENDING)](./2-5-nfvsdn22.md) +- [2.6. Traffic Engineering Demo (PENDING)](./2-6-te-demo.md) diff --git a/tutorial/2-6-netgen-config.yml b/tutorial/2-6-netgen-config.yml new file mode 100644 index 0000000000000000000000000000000000000000..7f1de319f3b3e7a3b1da10dfff8458ae443de838 --- /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 0000000000000000000000000000000000000000..286f4000573e9b1a469c65121820f1c4792d8feb --- /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 0000000000000000000000000000000000000000..dea41a71a4371384b32d91f74e2ec90af171d30f --- /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 0000000000000000000000000000000000000000..6a99007b4c5d481cc7692ea963c138ff51234c0d --- /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