From a01a91e54d640304f81adf75b85cf40ddfec46fe Mon Sep 17 00:00:00 2001 From: merle <sebastien.merle@stritzinger.com> Date: Thu, 15 Dec 2022 06:23:41 -0800 Subject: [PATCH] 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