Skip to content
Snippets Groups Projects
Commit fd926bbb authored by Sebastien Merle's avatar Sebastien Merle
Browse files

Retrieve the topology from Context service and add a TED

 - Add _connect/mpls_label and _connect/pcc_address to the allowed
   device configuration.
parent 1d496136
No related branches found
No related tags found
2 merge requests!142Release TeraFlowSDN 2.1,!133Integration of TE component
Showing
with 1196 additions and 53 deletions
...@@ -272,6 +272,7 @@ enum ServiceTypeEnum { ...@@ -272,6 +272,7 @@ enum ServiceTypeEnum {
SERVICETYPE_L3NM = 1; SERVICETYPE_L3NM = 1;
SERVICETYPE_L2NM = 2; SERVICETYPE_L2NM = 2;
SERVICETYPE_TAPI_CONNECTIVITY_SERVICE = 3; SERVICETYPE_TAPI_CONNECTIVITY_SERVICE = 3;
SERVICETYPE_TE = 4;
} }
enum ServiceStatusEnum { enum ServiceStatusEnum {
......
...@@ -21,6 +21,7 @@ class ORM_ServiceTypeEnum(enum.Enum): ...@@ -21,6 +21,7 @@ class ORM_ServiceTypeEnum(enum.Enum):
L3NM = ServiceTypeEnum.SERVICETYPE_L3NM L3NM = ServiceTypeEnum.SERVICETYPE_L3NM
L2NM = ServiceTypeEnum.SERVICETYPE_L2NM L2NM = ServiceTypeEnum.SERVICETYPE_L2NM
TAPI_CONNECTIVITY_SERVICE = ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE TAPI_CONNECTIVITY_SERVICE = ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE
TE = ServiceTypeEnum.SERVICETYPE_TE
grpc_to_enum__service_type = functools.partial( grpc_to_enum__service_type = functools.partial(
grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum) grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum)
...@@ -181,12 +181,16 @@ terminate(_Reason, _State) -> ...@@ -181,12 +181,16 @@ terminate(_Reason, _State) ->
%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
compute_path({1, 1, 1, 1}, {6, 6, 6, 6}) -> compute_path(From, To) ->
{ok, [16020, 16040, 16060]}; case epce_ted:compute_path(pcc_address, From, To) of
compute_path({6, 6, 6, 6}, {1, 1, 1, 1}) -> {ok, Devices} ->
{ok, [16040, 16020, 16010]}; Labels = tl([L || #{mpls_label := L} <- Devices, L =/= undefined]),
compute_path(_Src, _Dst) -> logger:debug("Route from ~p to ~p: ~p", [From, To, Labels]),
{error, nopath}. {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) -> route_from_labels(Source, Destination, Constraints, Labels) ->
#{ #{
......
...@@ -12,14 +12,14 @@ ...@@ -12,14 +12,14 @@
%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-define(SERVER, ?MODULE). -define(TED_WORKER, epce_ted).
-define(PCE_SERVER, epce_server). -define(PCE_WORKER, epce_server).
%%% BEHAVIOUR SUPERVISOR FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% BEHAVIOUR SUPERVISOR FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start_link() -> start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []). supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) -> init([]) ->
SupFlags = #{ SupFlags = #{
...@@ -27,11 +27,17 @@ init([]) -> ...@@ -27,11 +27,17 @@ init([]) ->
intensity => 0, intensity => 0,
period => 1 period => 1
}, },
TEDSpec = #{
id => ?TED_WORKER,
start => {?TED_WORKER, start_link, []},
restart => permanent,
shutdown => brutal_kill
},
ServerSpec = #{ ServerSpec = #{
id => ?PCE_SERVER, id => ?PCE_WORKER,
start => {?PCE_SERVER, start_link, []}, start => {?PCE_WORKER, start_link, []},
restart => permanent, restart => permanent,
shutdown => brutal_kill shutdown => brutal_kill
}, },
{ok, {SupFlags, [ServerSpec]}}. {ok, {SupFlags, [TEDSpec, ServerSpec]}}.
-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.
...@@ -8,18 +8,18 @@ ...@@ -8,18 +8,18 @@
-behaviour(application). -behaviour(application).
%--- Includes ------------------------------------------------------------------ %%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-include_lib("kernel/include/logger.hrl"). -include_lib("kernel/include/logger.hrl").
%--- Exports ------------------------------------------------------------------- %%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Behaviour application callback functions % Behaviour application callback functions
-export([start/2, stop/1]). -export([start/2, stop/1]).
%--- Behaviour application Callback Functions ---------------------------------- %%% BEHAVIOUR applicaation CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start(_StartType, _StartArgs) -> start(_StartType, _StartArgs) ->
case tfte_sup:start_link() of case tfte_sup:start_link() of
...@@ -34,7 +34,7 @@ stop(_State) -> ...@@ -34,7 +34,7 @@ stop(_State) ->
ok. ok.
%--- Internal Functions -------------------------------------------------------- %%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
add_services() -> add_services() ->
case application:get_env(tfte, services) of case application:get_env(tfte, services) of
...@@ -71,7 +71,6 @@ resolve_endpoints(Name, [{Transport, HostSpec, PortSpec, SslOpts} | Rest], Acc) ...@@ -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, Hostname) when is_list(Hostname) -> Hostname;
resolve_host_spec(Name, {env, Key}) when is_list(Key) -> 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 try os:getenv(Key) of
false -> throw({Name, service_hostname_not_found, Key}); false -> throw({Name, service_hostname_not_found, Key});
Hostname -> Hostname Hostname -> Hostname
...@@ -82,7 +81,6 @@ resolve_host_spec(Name, {env, Key}) when is_list(Key) -> ...@@ -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, Port) when is_integer(Port) -> Port;
resolve_port_spec(Name, {env, Key}) when is_list(Key) -> 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 try os:getenv(Key) of
false -> throw({Name, service_port_not_found, Key}); false -> throw({Name, service_port_not_found, Key});
PortStr -> PortStr ->
......
-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
-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 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%-----------------------------------------------------------------------------
%% @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}}.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
-behaviour(supervisor). -behaviour(supervisor).
%--- Exports ------------------------------------------------------------------- %%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% API Functions % API Functions
-export([start_link/0]). -export([start_link/0]).
...@@ -17,22 +17,36 @@ ...@@ -17,22 +17,36 @@
-export([init/1]). -export([init/1]).
%--- Macros -------------------------------------------------------------------- %%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-define(SERVER, ?MODULE). -define(SERVER, ?MODULE).
-define(ROOT_SERVER, tfte_server).
%--- API Functions ------------------------------------------------------------- %%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start_link() -> start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []). supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%--- Behaviour supervisor Callback Functions ----------------------------------- %%% BEHAVIOUR supervisor CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init([]) -> init([]) ->
SupFlags = #{strategy => one_for_all, SupFlags = #{strategy => one_for_all,
intensity => 0, intensity => 0,
period => 1}, 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}}. {ok, {SupFlags, ChildSpecs}}.
...@@ -3,12 +3,12 @@ ...@@ -3,12 +3,12 @@
-behaviour(te_te_service_bhvr). -behaviour(te_te_service_bhvr).
%--- Includes ------------------------------------------------------------------ %%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-include_lib("grpcbox/include/grpcbox.hrl"). -include_lib("grpcbox/include/grpcbox.hrl").
%--- Exports ------------------------------------------------------------------- %%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Behaviour te_te_service_bhvr callback functions % Behaviour te_te_service_bhvr callback functions
-export([request_lsp/2]). -export([request_lsp/2]).
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
-export([delete_lsp/2]). -export([delete_lsp/2]).
%--- Behaviour te_te_service_bhvr Callback Functions --------------------------- %%% BEHAVIOUR te_te_service_bhvr CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%
request_lsp(_Ctx, _Service) -> request_lsp(_Ctx, _Service) ->
{error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>}, {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>},
......
-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.
[ [
{tfte, [ {tfte, [
{context, <<"admin">>},
{topology, <<"tetestbed">>},
{services, [ {services, [
{te, [ {te, [
{http, {env, "TESERVICE_SERVICE_HOST"}, {env, "TESERVICE_SERVICE_PORT_GRPC"}, []} {http, {env, "TESERVICE_SERVICE_HOST"}, {env, "TESERVICE_SERVICE_PORT_GRPC"}, []}
...@@ -72,7 +74,7 @@ ...@@ -72,7 +74,7 @@
{logger_level, ${ERLANG_LOGGER_LEVEL}}, {logger_level, ${ERLANG_LOGGER_LEVEL}},
{logger, [ {logger, [
{handler, default, logger_std_h, #{ {handler, default, logger_std_h, #{
level => info, level => ${ERLANG_LOGGER_LEVEL},
filter_default => log, filter_default => log,
config => #{type => standard_io}, config => #{type => standard_io},
formatter => {logger_formatter, #{ formatter => {logger_formatter, #{
......
...@@ -11,26 +11,30 @@ if [[ ! -f "${NEGENDIR}/exe/netgen" ]]; then ...@@ -11,26 +11,30 @@ if [[ ! -f "${NEGENDIR}/exe/netgen" ]]; then
exit 1 exit 1
fi fi
export PCE_NETNS="$1" PCE_IP=$( kubectl --namespace tfs get $(kubectl --namespace tfs get pods --selector=app=teservice -o name) --template '{{.status.podIP}}' )
export PCE_IP="$2" echo "Teraflow PCE IP address: $PCE_IP"
RT1_INT_IP="$3" NAMESPACES=$( ip netns list | cut -d' ' -f1 )
RT1_EXT_IP="$4" PCE_NETNS=""
RT6_INT_IP="$5" for n in $NAMESPACES; do
RT6_EXT_IP="$6" if sudo ip -n $n addr list | grep $PCE_IP > /dev/null; then
echo "Teraflow TE service namespace: $n"
PCE_NETNS=$n
if [[ -z $PCE_NETNS || -z $PCE_IP || -z RT1_INT_IP || -z RT1_EXT_IP || -z RT6_INT_IP || -z RT6_EXT_IP ]]; then break
echo "USAGE: $0 PCE_NETNS PCE_IP RT1_INT_IP RT1_EXT_IP RT6_INT_IP RT6_EXT_IP" fi
echo " e.g: $0 cni-588a2d06-e64f-907b-d51b-bed0307007c9 10.1.103.133 10 11 12 13" done
exit 1 if [[ -z $PCE_NETNS ]]; then
echo "Teraflow network namespace for TE service not found"
exit1
fi fi
IFS=. read PCE_IP1 PCE_IP2 PCE_IP3 PCE_IP4 <<< "$PCE_IP" 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 PCE_IP
export RT1_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT1_EXT_IP" export PCE_NETNS
export RT6_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT6_INT_IP" export RT1_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.10"
export RT6_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT6_EXT_IP" 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" cp "${ROOTDIR}/2-6-netgen-config.yml" "${RUNDIR}/config.yml"
cat "${ROOTDIR}/2-6-netgen-topology.yml.template" | envsubst > "${RUNDIR}/topology.yml" cat "${ROOTDIR}/2-6-netgen-topology.yml.template" | envsubst > "${RUNDIR}/topology.yml"
......
...@@ -78,20 +78,14 @@ ...@@ -78,20 +78,14 @@
### Run the Test-Bed ### Run the Test-Bed
To start the testbed, we need to figure out the IP of the TE service: First load the [teraflow configuration file](./2-6-teraflow-topology.json) using the webui.
$ kubectl --namespace tfs get $(kubectl --namespace tfs get pods --selector=app=teservice -o name) --template '{{.status.podIP}}' 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.
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: In first console:
$ cd ~/testbed $ 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: Then in second console:
$ sudo -i $ sudo -i
# cd /tmp/negen # cd /tmp/negen
# ./tmux.sh # ./tmux.sh
\ No newline at end of file
{
"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"}}
]
}
]
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment