diff --git a/proto/context.proto b/proto/context.proto
index f5dec30796a8426f512947d369b8db5f5889471a..74132e7377c0097c1febb0024b969e843f6370a5 100644
--- a/proto/context.proto
+++ b/proto/context.proto
@@ -239,6 +239,7 @@ enum ServiceTypeEnum {
   SERVICETYPE_L3NM = 1;
   SERVICETYPE_L2NM = 2;
   SERVICETYPE_TAPI_CONNECTIVITY_SERVICE = 3;
+  SERVICETYPE_TE = 4;
 }
 
 enum ServiceStatusEnum {
diff --git a/src/common/DeviceTypes.py b/src/common/DeviceTypes.py
index c5ea4c54fef7b739a4ad33dd3759c3bdef124038..1cda1fa21a8a1ba996f5a7797059ebe0ace19318 100644
--- a/src/common/DeviceTypes.py
+++ b/src/common/DeviceTypes.py
@@ -18,6 +18,7 @@ class DeviceTypeEnum(Enum):
     EMULATED_DATACENTER       = 'emu-datacenter'
     EMULATED_OPEN_LINE_SYSTEM = 'emu-open-line-system'
     EMULATED_PACKET_ROUTER    = 'emu-packet-router'
+    EMULATED_PACKET_SWITCH    = 'emu-packet-switch'
     DATACENTER                = 'datacenter'
     MICROVAWE_RADIO_SYSTEM    = 'microwave-radio-system'
     OPTICAL_ROADM             = 'optical-roadm'
diff --git a/src/context/service/database/ServiceModel.py b/src/context/service/database/ServiceModel.py
index 8b32d1cc9eeec248d1097f972df93dbd2c0882fa..2bddc09a5c090d0a153417134ff4802a599001ee 100644
--- a/src/context/service/database/ServiceModel.py
+++ b/src/context/service/database/ServiceModel.py
@@ -34,6 +34,7 @@ class ORM_ServiceTypeEnum(Enum):
     L3NM                      = ServiceTypeEnum.SERVICETYPE_L3NM
     L2NM                      = ServiceTypeEnum.SERVICETYPE_L2NM
     TAPI_CONNECTIVITY_SERVICE = ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE
+    TE                        = ServiceTypeEnum.SERVICETYPE_TE
 
 grpc_to_enum__service_type = functools.partial(
     grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum)
diff --git a/src/device/service/DeviceServiceServicerImpl.py b/src/device/service/DeviceServiceServicerImpl.py
index 9ffd028a67a34cfcce7a737a5817128126941759..69784738bcad29fa002f44b47a6d20e1b434802e 100644
--- a/src/device/service/DeviceServiceServicerImpl.py
+++ b/src/device/service/DeviceServiceServicerImpl.py
@@ -157,6 +157,16 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
             config_rule = (ORM_ConfigActionEnum.SET, resource_key, json.dumps(resource_value, sort_keys=True))
             running_config_rules.append(config_rule)
 
+        mpls_label = connection_config_rules.pop('mpls_label', None)
+        if mpls_label != None:
+            mpls_label_config_rule = (ORM_ConfigActionEnum.SET, "mpls_label", mpls_label)
+            running_config_rules.append(mpls_label_config_rule)
+
+        pcc_address = connection_config_rules.pop('pcc_address', None)
+        if pcc_address != None:
+            pcc_address_config_rule = (ORM_ConfigActionEnum.SET, "pcc_address", pcc_address)
+            running_config_rules.append(pcc_address_config_rule)
+
         #for running_config_rule in running_config_rules:
         #    LOGGER.info('[AddDevice] running_config_rule: {:s}'.format(str(running_config_rule)))
         update_config(self.database, device_uuid, 'running', running_config_rules)
diff --git a/src/service/service/database/ServiceModel.py b/src/service/service/database/ServiceModel.py
index cf756af60a8178a9ae2fda2a5fa5ddeebc73912c..fd8cfc71ed8783509db7a93424f4e4d65fc15663 100644
--- a/src/service/service/database/ServiceModel.py
+++ b/src/service/service/database/ServiceModel.py
@@ -34,6 +34,7 @@ class ORM_ServiceTypeEnum(Enum):
     L3NM                      = ServiceTypeEnum.SERVICETYPE_L3NM
     L2NM                      = ServiceTypeEnum.SERVICETYPE_L2NM
     TAPI_CONNECTIVITY_SERVICE = ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE
+    TE                        = ServiceTypeEnum.SERVICETYPE_TE
 
 grpc_to_enum__service_type = functools.partial(
     grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum)
diff --git a/src/te/apps/epce/src/epce_server.erl b/src/te/apps/epce/src/epce_server.erl
index bd3a13f4429590268346d4569a95eb6a09fd3a06..11a1625efa0e961658a98d01223481f7512fdbf7 100644
--- a/src/te/apps/epce/src/epce_server.erl
+++ b/src/te/apps/epce/src/epce_server.erl
@@ -181,12 +181,16 @@ terminate(_Reason, _State) ->
 
 %%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-compute_path({1, 1, 1, 1}, {6, 6, 6, 6}) ->
-    {ok, [16020, 16040, 16060]};
-compute_path({6, 6, 6, 6}, {1, 1, 1, 1}) ->
-    {ok, [16040, 16020, 16010]};
-compute_path(_Src, _Dst) ->
-    {error, nopath}.
+compute_path(From, To) ->
+    case epce_ted:compute_path(pcc_address, From, To) of
+        {ok, Devices} ->
+            Labels = tl([L || #{mpls_label := L} <- Devices, L =/= undefined]),
+            logger:debug("Route from ~p to ~p: ~p", [From, To, Labels]),
+            {ok, Labels};
+        {error, Reason} ->
+            logger:warning("Failed to find a route from ~p to ~p", [From, To]),
+            {error, Reason}
+    end.
 
 route_from_labels(Source, Destination, Constraints, Labels) ->
     #{
diff --git a/src/te/apps/epce/src/epce_sup.erl b/src/te/apps/epce/src/epce_sup.erl
index 21a6a56b2ff39b05b2bc440f1089fb18024fe8ef..f04e9697e0dec0759dbdb512e8b37dcda5546408 100644
--- a/src/te/apps/epce/src/epce_sup.erl
+++ b/src/te/apps/epce/src/epce_sup.erl
@@ -12,14 +12,14 @@
 
 %%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
--define(SERVER, ?MODULE).
--define(PCE_SERVER, epce_server).
+-define(TED_WORKER, epce_ted).
+-define(PCE_WORKER, epce_server).
 
 
 %%% BEHAVIOUR SUPERVISOR FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
 
 start_link() ->
-    supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+    supervisor:start_link({local, ?MODULE}, ?MODULE, []).
 
 init([]) ->
     SupFlags = #{
@@ -27,11 +27,17 @@ init([]) ->
 		intensity => 0,
 		period => 1
 	},
+    TEDSpec = #{
+        id => ?TED_WORKER,
+        start => {?TED_WORKER, start_link, []},
+        restart => permanent,
+        shutdown => brutal_kill
+    },
     ServerSpec = #{
-        id => ?PCE_SERVER,
-        start => {?PCE_SERVER, start_link, []},
+        id => ?PCE_WORKER,
+        start => {?PCE_WORKER, start_link, []},
         restart => permanent,
         shutdown => brutal_kill
     },
-    {ok, {SupFlags, [ServerSpec]}}.
+    {ok, {SupFlags, [TEDSpec, ServerSpec]}}.
 
diff --git a/src/te/apps/epce/src/epce_ted.erl b/src/te/apps/epce/src/epce_ted.erl
new file mode 100644
index 0000000000000000000000000000000000000000..aaf5a4e9a09183e51686457613f89a1804f5276c
--- /dev/null
+++ b/src/te/apps/epce/src/epce_ted.erl
@@ -0,0 +1,203 @@
+-module(epce_ted).
+
+-behaviour(gen_server).
+
+
+%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-include_lib("kernel/include/logger.hrl").
+
+
+%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% API Functions
+-export([start_link/0]).
+-export([device_added/2]).
+-export([device_updated/2]).
+-export([device_deleted/1]).
+-export([link_added/2]).
+-export([link_updated/2]).
+-export([link_deleted/1]).
+-export([compute_path/3]).
+
+-export([get_graph/0]).
+
+% Behaviour gen_server functions
+-export([init/1]).
+-export([handle_call/3]).
+-export([handle_cast/2]).
+-export([handle_continue/2]).
+-export([handle_info/2]).
+-export([code_change/3]).
+-export([terminate/2]).
+
+
+%%% RECORDS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-record(state, {
+    graph :: diagraph:graph(),
+    pcc_address_to_id = #{} :: map()
+}).
+
+
+%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+start_link() ->
+    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+device_added(Id, Device) ->
+    gen_server:call(?MODULE, {device_added, Id, Device}).
+
+device_updated(Id, Device) ->
+    gen_server:call(?MODULE, {device_updated, Id, Device}).
+
+device_deleted(Id) ->
+    gen_server:call(?MODULE, {device_deleted, Id}).
+
+link_added(Id, Link) ->
+    gen_server:call(?MODULE, {link_added, Id, Link}).
+
+link_updated(Id, Link) ->
+    gen_server:call(?MODULE, {link_updated, Id, Link}).
+
+link_deleted(Id) ->
+    gen_server:call(?MODULE, {link_deleted, Id}).
+
+compute_path(Index, From, To)
+  when Index =:= id; Index =:= pcc_address ->
+    gen_server:call(?MODULE, {compute_path, Index, From, To});
+compute_path(Index, _From, _To) ->
+    {error, {invalid_index, Index}}.
+
+
+get_graph() ->
+    gen_server:call(?MODULE, get_graph).
+
+
+%%% BEHAVIOUR gen_server FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+init([]) ->
+    ?LOG_INFO("Starting TED process...", []),
+    % {ok, #state{graph = digraph:new([private, cyclic])}}.
+    {ok, #state{graph = digraph:new([protected, cyclic])}}.
+
+handle_call({device_added, Id, Device}, _From, State) ->
+    ?LOG_DEBUG("Adding TED device ~p: ~p", [Id, Device]),
+    {reply, ok, do_update_device(State, Id, Device)};
+handle_call({device_updated, Id, Device}, _From, State) ->
+    ?LOG_DEBUG("Updating TED device ~p: ~p", [Id, Device]),
+    {reply, ok, do_update_device(State, Id, Device)};
+handle_call({device_deleted, Id}, _From, State) ->
+    ?LOG_DEBUG("Deleting TED device ~p", [Id]),
+    {reply, ok, do_delete_device(State, Id)};
+handle_call({link_added, Id, Link}, _From, State) ->
+    ?LOG_DEBUG("Adding TED link ~p: ~p", [Id, Link]),
+    {reply, ok, do_update_link(State, Id, Link)};
+handle_call({link_updated, Id, Link}, _From, State) ->
+    ?LOG_DEBUG("Updating TED link ~p: ~p", [Id, Link]),
+    {reply, ok, do_update_link(State, Id, Link)};
+handle_call({link_deleted, Id}, _From, State) ->
+    ?LOG_DEBUG("Deleting TED link ~p", [Id]),
+    {reply, ok, do_delete_link(State, Id)};
+handle_call({compute_path, Index, From, To}, _From, #state{graph = G} = State) ->
+    case as_ids(State, Index, [From, To]) of
+        {ok, [FromId, ToId]} ->
+            {reply, do_compute_path(G, FromId, ToId), State};
+        {error, Reason} ->
+            {reply, {error, Reason}, State}
+    end;
+handle_call(get_graph, _From, #state{graph = G} = State) ->
+    {reply, G, State};
+handle_call(Request, _From, State) ->
+    logger:warning("Unexpected call to TED process ~w", [Request]),
+    {reply, {error, unexpected_call}, State}.
+
+handle_cast(Request, State) ->
+    logger:warning("Unexpected cast to TED process ~w", [Request]),
+    {noreply, State}.
+
+handle_continue(_Continue, State) ->
+    {noreply, State}.
+
+handle_info(Info, State) ->
+    logger:warning("Unexpected message to TED process ~w", [Info]),
+    {noreply, State}.
+
+code_change(_OldVsn, State, _Extra) ->
+    {ok, State}.
+
+terminate(_Reason, _State) ->
+    ?LOG_INFO("Terminating TED process...", []),
+    ok.
+
+
+%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+as_ids(_State, id, Keys) ->
+    {ok, Keys};
+as_ids(State, IndexType, Keys) ->
+    as_ids(State, IndexType, Keys, []).
+
+as_ids(_State, _IndexType, [], Acc) ->
+    {ok, lists:reverse(Acc)};
+as_ids(#state{pcc_address_to_id = Index} = State, pcc_address, [Key | Rest], Acc) ->
+    case maps:find(Key, Index) of
+        error -> {error, {unknown_key, Key}};
+        {ok, Id} -> as_ids(State, pcc_address, Rest, [Id | Acc])
+    end.
+
+do_update_device(#state{graph = G} = State, Id, NewDevice) ->
+    State2 = case digraph:vertex(G, Id) of
+        false -> State;
+        {Id, OldDevice} -> index_remove_device(State, OldDevice)
+    end,
+    digraph:add_vertex(G, Id, NewDevice),
+    index_add_device(State2, NewDevice).
+
+do_delete_device(#state{graph = G} = State, Id) ->
+    case digraph:vertex(G, Id) of
+        false -> State;
+        {Id, OldDevice} ->
+            digraph:del_vertex(G, Id),
+            index_remove_device(State, OldDevice)
+    end.
+
+index_remove_device(#state{pcc_address_to_id = Index} = State,
+                    #{pcc_address := OldAddress}) ->
+    Index2 = maps:remove(OldAddress, Index),
+    State#state{pcc_address_to_id = Index2}.
+
+index_add_device(State, #{pcc_address := undefined}) ->
+    State;
+index_add_device(#state{pcc_address_to_id = Index} = State,
+                 #{id := Id, pcc_address := NewAddress}) ->
+    Index2 = Index#{NewAddress => Id},
+    State#state{pcc_address_to_id = Index2}.
+
+do_update_link(#state{graph = G} = State, Id, Link) ->
+    #{endpoints := [EP1, EP2]} = Link,
+    #{device := D1} = EP1,
+    #{device := D2} = EP2,
+    digraph:add_edge(G, {Id, a}, D1, D2, Link),
+    digraph:add_edge(G, {Id, b}, D2, D1, Link),
+    State.
+
+do_delete_link(#state{graph = G} = State, Id) ->
+    digraph:del_edge(G, {Id, a}),
+    digraph:del_edge(G, {Id, b}),
+    State.
+
+do_compute_path(G, FromId, ToId) ->
+    case digraph:get_short_path(G, FromId, ToId) of
+        false -> {error, not_found};
+        Ids -> {ok, retrieve_devices(G, Ids, [])}
+    end.
+
+retrieve_devices(_G, [], Acc) ->
+    lists:reverse(Acc);
+retrieve_devices(G, [Id | Rest], Acc) ->
+    case digraph:vertex(G, Id) of
+        false -> {error, invalid_path};
+        {Id, Device} ->
+            retrieve_devices(G, Rest, [Device | Acc])
+    end.
diff --git a/src/te/apps/tfte/src/tfte_app.erl b/src/te/apps/tfte/src/tfte_app.erl
index 159197fdfa95043bb72beef2007e0c766f983b15..96724904dd7a044442c5568b13d64a2e2a72df44 100644
--- a/src/te/apps/tfte/src/tfte_app.erl
+++ b/src/te/apps/tfte/src/tfte_app.erl
@@ -8,18 +8,18 @@
 -behaviour(application).
 
 
-%--- Includes ------------------------------------------------------------------
+%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 -include_lib("kernel/include/logger.hrl").
 
 
-%--- Exports -------------------------------------------------------------------
+%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 % Behaviour application callback functions
 -export([start/2, stop/1]).
 
 
-%--- Behaviour application Callback Functions ----------------------------------
+%%% BEHAVIOUR applicaation CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 start(_StartType, _StartArgs) ->
     case tfte_sup:start_link() of
@@ -34,7 +34,7 @@ stop(_State) ->
     ok.
 
 
-%--- Internal Functions --------------------------------------------------------
+%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 add_services() ->
     case application:get_env(tfte, services) of
@@ -71,7 +71,6 @@ resolve_endpoints(Name, [{Transport, HostSpec, PortSpec, SslOpts} | Rest], Acc)
 
 resolve_host_spec(_Name, Hostname) when is_list(Hostname) -> Hostname;
 resolve_host_spec(Name, {env, Key}) when is_list(Key) ->
-    ?LOG_DEBUG("????? HOST ~s ~s -> ~p", [Name, Key, os:getenv(Key)]),
     try os:getenv(Key) of
         false -> throw({Name, service_hostname_not_found, Key});
         Hostname -> Hostname
@@ -82,7 +81,6 @@ resolve_host_spec(Name, {env, Key}) when is_list(Key) ->
 
 resolve_port_spec(_Name, Port) when is_integer(Port) -> Port;
 resolve_port_spec(Name, {env, Key}) when is_list(Key) ->
-    ?LOG_DEBUG("????? PORT ~s ~s -> ~p", [Name, Key, os:getenv(Key)]),
     try os:getenv(Key) of
         false -> throw({Name, service_port_not_found, Key});
         PortStr ->
diff --git a/src/te/apps/tfte/src/tfte_context.erl b/src/te/apps/tfte/src/tfte_context.erl
new file mode 100644
index 0000000000000000000000000000000000000000..ee0fafc07ed46fe85c4c884da9dac061298c0ab0
--- /dev/null
+++ b/src/te/apps/tfte/src/tfte_context.erl
@@ -0,0 +1,162 @@
+-module(tfte_context).
+
+-behaviour(gen_statem).
+
+
+%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-include_lib("kernel/include/logger.hrl").
+
+
+%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% API functions
+-export([start_link/0]).
+
+% Behaviour gen_statem functions
+-export([init/1]).
+-export([callback_mode/0]).
+-export([handle_event/4]).
+-export([terminate/3]).
+-export([code_change/4]).
+
+
+%%% Records %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-record(data, {
+    uuid :: map(),
+    sub :: term() | undefined,
+    obj :: map() | undefined
+}).
+
+
+%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(SUBSCRIBE_RETRY_TIMEOUT, 1000).
+-define(RETRIEVE_RETRY_TIMEOUT, 10000).
+
+
+%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+start_link() ->
+    gen_statem:start_link(?MODULE, [], []).
+
+
+%%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+init([]) ->
+    {ok, Name} = application:get_env(tfte, context),
+    ?LOG_INFO("Starting context ~s service handler...", [Name]),
+    UUID = #{context_uuid => #{uuid => Name}},
+    {ok, subscribe, #data{uuid = UUID}}.
+
+callback_mode() -> [handle_event_function, state_enter].
+
+%-- SUBSCRIBE STATE ------------------------------------------------------------
+handle_event(enter, _, subscribe, #data{sub = undefined}) ->
+    {keep_state_and_data, [{state_timeout, 0, do_suscribe}]};
+handle_event(enter, _, subscribe, Data) ->
+    % We already have a context subscription
+    {next_state, ready, Data};
+handle_event(state_timeout, do_suscribe, subscribe, Data) ->
+    ?LOG_DEBUG("Subscribing to context events...", []),
+    case do_subscribe() of
+        {ok, Sub} ->
+            ?LOG_INFO("Subscribed to context events", []),
+            Data2 = Data#data{sub = Sub},
+            {next_state, retrieve, Data2};
+        {error, Reason} ->
+            ?LOG_ERROR("Failed to subscribe to context service events: ~p", [Reason]),
+            {keep_state_and_data, [{state_timeout, ?SUBSCRIBE_RETRY_TIMEOUT, do_suscribe}]}
+    end;
+%-- RETRIEVE STATE -------------------------------------------------------------
+handle_event(enter, _, retrieve, _Data) ->
+    {keep_state_and_data, [{state_timeout, 0, do_retrieve}]};
+handle_event(state_timeout, do_retrieve, retrieve, #data{uuid = UUID} = Data) ->
+    ?LOG_DEBUG("Retrieving context ~p...", [UUID]),
+    case get_object(UUID) of
+        error ->
+            {keep_state_and_data, [{state_timeout, ?RETRIEVE_RETRY_TIMEOUT, do_retrieve}]};
+        {ok, Context} ->
+            ?LOG_DEBUG("Got context: ~p", [Context]),
+            tfte_server:context_ready(Context),
+            {next_state, ready, Data#data{obj = Context}}
+    end;
+handle_event(info, {headers, Id, Value}, retrieve,
+             #data{sub = #{stream_id := Id}}) ->
+    %TODO: Handle HTTP errors ???
+    ?LOG_DEBUG("Received context stream header: ~p", [Value]),
+    keep_state_and_data;
+handle_event(info, {data, Id, Value}, retrieve,
+             #data{sub = #{stream_id := Id}}) ->
+    ?LOG_DEBUG("Received context event, retrying context: ~p", [Value]),
+    {keep_state_and_data, [{state_timeout, 0, do_retrieve}]};
+handle_event(info, {'DOWN', Ref, process, Pid, Reason}, retrieve,
+             #data{sub = #{stream_id := Id, monitor_ref := Ref, stream_pid := Pid}} = Data) ->
+    ?LOG_DEBUG("Context subscription is down: ~p", [Reason]),
+    Data2 = Data#data{sub = undefined},
+    Info = receive
+        {trailers, Id, {Status, Message, Metadata}} ->
+            {Reason, Status, Message, Metadata}
+    after 0 ->
+        Reason
+    end,
+    ?LOG_ERROR("Context subscription error: ~p", [Info]),
+    {next_state, subscribe, Data2};
+%-- READY STATE ----------------------------------------------------------------
+handle_event(enter, _, ready, _Data) ->
+    keep_state_and_data;
+handle_event(info, {headers, Id, Value}, ready,
+             #data{sub = #{stream_id := Id}}) ->
+    %TODO: Handle HTTP errors ???
+    ?LOG_DEBUG("Received context stream header: ~p", [Value]),
+    keep_state_and_data;
+handle_event(info, {data, Id, #{context_id := UUID, event := Event}}, ready,
+             #data{uuid = UUID, sub = #{stream_id := Id}}) ->
+    ?LOG_DEBUG("Received context event: ~p", [Event]),
+    tfte_server:context_event(Event),
+    keep_state_and_data;
+handle_event(info, {'DOWN', Ref, process, Pid, Reason}, ready,
+             #data{sub = #{stream_id := Id, monitor_ref := Ref, stream_pid := Pid}} = Data) ->
+    ?LOG_DEBUG("Context subscription is down: ~p", [Reason]),
+    Data2 = Data#data{sub = undefined},
+    Info = receive
+        {trailers, Id, {Status, Message, Metadata}} ->
+            {Reason, Status, Message, Metadata}
+    after 0 ->
+        Reason
+    end,
+    ?LOG_ERROR("Context subscription error: ~p", [Info]),
+    {next_state, subscribe, Data2};
+%-- ANY STATE ------------------------------------------------------------------
+handle_event(info, Msg, StateName, _Data) ->
+    ?LOG_WARNING("Unexpected context message in state ~w: ~p", [StateName, Msg]),
+    keep_state_and_data.
+
+terminate(Reason, _State, _Data) ->
+    ?LOG_INFO("Context service handler terminated: ~p", [Reason]),
+    ok.
+
+code_change(_OldVsn, OldState, OldData, _Extra) ->
+    {ok, OldState, OldData}.
+
+
+%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+grpc_opts() ->
+    #{channel => context}.
+
+do_subscribe() ->
+    context_context_service_client:get_context_events(#{}, grpc_opts()).
+
+get_object(UUID) ->
+    case context_context_service_client:get_context(UUID, grpc_opts()) of
+        {error, Reason} -> 
+            ?LOG_ERROR("Local error while retrieving the context object: ~p", [Reason]),
+            error;
+        {error, Reason, _Headers} ->
+            ?LOG_ERROR("Remote error while retrieving the context object: ~p", [Reason]),
+            error;
+        {ok, Result, _Headers} ->
+            {ok, Result}
+    end.
\ No newline at end of file
diff --git a/src/te/apps/tfte/src/tfte_server.erl b/src/te/apps/tfte/src/tfte_server.erl
new file mode 100644
index 0000000000000000000000000000000000000000..29dddf3d1dab6b08810746c5f5a2ef2636c5ad99
--- /dev/null
+++ b/src/te/apps/tfte/src/tfte_server.erl
@@ -0,0 +1,100 @@
+-module(tfte_server).
+
+-behaviour(gen_statem).
+
+
+%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-include_lib("kernel/include/logger.hrl").
+
+
+%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% API functions
+-export([start_link/0]).
+-export([context_ready/1]).
+-export([context_event/1]).
+-export([topology_ready/1]).
+-export([topology_event/1]).
+
+% Behaviour gen_statem functions
+-export([init/1]).
+-export([callback_mode/0]).
+-export([handle_event/4]).
+-export([terminate/3]).
+-export([code_change/4]).
+
+
+%%% Records %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-record(data, {
+}).
+
+
+%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+start_link() ->
+    gen_statem:start_link(?MODULE, [], []).
+
+context_ready(Context) ->
+    gen_statem:cast(?MODULE, {context_ready, Context}).
+
+context_event(Event) ->
+    gen_statem:cast(?MODULE, {context_event, Event}).
+
+topology_ready(Topology) ->
+    gen_statem:cast(?MODULE, {topology_ready, Topology}).
+
+topology_event(Event) ->
+    gen_statem:cast(?MODULE, {topology_event, Event}).
+
+
+%%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+init([]) ->
+    ?LOG_INFO("Starting server...", []),
+    {ok, wait_context, #data{}}.
+
+callback_mode() -> [handle_event_function, state_enter].
+
+%-- WAIT_CONTEXT STATE ---------------------------------------------------------
+handle_event(enter, _, wait_context, _Data) ->
+    keep_state_and_data;
+handle_event(cast, {context_ready, _Context}, wait_contex, Data) ->
+    ?LOG_DEBUG("Teraflow context initialized: ~p", [_Context]),
+    tfte_topology:context_updated(),
+    {next_state, ready, Data};
+%-- READY STATE ----------------------------------------------------------------
+handle_event(enter, _, ready, _Data) ->
+    keep_state_and_data;
+handle_event(cast, {context_ready, _Context}, ready, _Data) ->
+    ?LOG_DEBUG("Teraflow context updated: ~p", [_Context]),
+    tfte_topology:context_updated(),
+    keep_state_and_data;
+handle_event(cast, {context_event, _Event}, ready, _Data) ->
+    ?LOG_DEBUG("Teraflow context event: ~p", [_Event]),
+    keep_state_and_data;
+handle_event(cast, {topology_ready, _Topology}, ready, _Data) ->
+    ?LOG_DEBUG("Teraflow topology updated: ~p", [_Topology]),
+    keep_state_and_data;
+handle_event(cast, {topology_event, _Event}, ready, _Data) ->
+    ?LOG_DEBUG("Teraflow topology event: ~p", [_Event]),
+    keep_state_and_data;
+%-- ANY STATE ------------------------------------------------------------------
+handle_event(EventType, EventContent, State, Data) ->
+    ?LOG_WARNING(Data, "Unexpected ~w event in state ~w: ~w",
+                 [EventType, State, EventContent]),
+    keep_state_and_data.
+
+terminate(Reason, _State, _Data) ->
+    ?LOG_INFO("Server terminated: ~p", [Reason]),
+    ok.
+
+code_change(_OldVsn, OldState, OldData, _Extra) ->
+    {ok, OldState, OldData}.
+
+
+%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/src/te/apps/tfte/src/tfte_service_sup.erl b/src/te/apps/tfte/src/tfte_service_sup.erl
new file mode 100644
index 0000000000000000000000000000000000000000..2223589e2350d51b8aa32004bf45e7b24b76b939
--- /dev/null
+++ b/src/te/apps/tfte/src/tfte_service_sup.erl
@@ -0,0 +1,50 @@
+%%%-----------------------------------------------------------------------------
+%% @doc tfte service supervisor.
+%% @end
+%%%-----------------------------------------------------------------------------
+
+-module(tfte_service_sup).
+
+-behaviour(supervisor).
+
+
+%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% API Functions
+-export([start_link/0]).
+
+% Behaviour supervisor callback functions
+-export([init/1]).
+
+
+%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(SERVER, ?MODULE).
+
+
+%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+start_link() ->
+    supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+
+%%% BEHAVIOUR supervisor CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+init([]) ->
+    SupFlags = #{strategy => one_for_one,
+                 intensity => 0,
+                 period => 1},
+    ContextSpec = #{
+        id => tfte_context,
+        start => {tfte_context, start_link, []},
+        restart => permanent,
+        shutdown => brutal_kill
+    },
+    TopologySpec = #{
+        id => tfte_topology,
+        start => {tfte_topology, start_link, []},
+        restart => permanent,
+        shutdown => brutal_kill
+    },
+    ChildSpecs = [ContextSpec, TopologySpec],
+    {ok, {SupFlags, ChildSpecs}}.
diff --git a/src/te/apps/tfte/src/tfte_sup.erl b/src/te/apps/tfte/src/tfte_sup.erl
index 2944889cb2f302992cc3d72164730e375665d783..57c95483ea74e20df048990b92d3950ab48a216e 100644
--- a/src/te/apps/tfte/src/tfte_sup.erl
+++ b/src/te/apps/tfte/src/tfte_sup.erl
@@ -8,7 +8,7 @@
 -behaviour(supervisor).
 
 
-%--- Exports -------------------------------------------------------------------
+%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 % API Functions
 -export([start_link/0]).
@@ -17,22 +17,36 @@
 -export([init/1]).
 
 
-%--- Macros --------------------------------------------------------------------
+%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 -define(SERVER, ?MODULE).
+-define(ROOT_SERVER, tfte_server).
 
 
-%--- API Functions -------------------------------------------------------------
+%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 start_link() ->
     supervisor:start_link({local, ?SERVER}, ?MODULE, []).
 
 
-%--- Behaviour supervisor Callback Functions -----------------------------------
+%%% BEHAVIOUR supervisor CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 init([]) ->
     SupFlags = #{strategy => one_for_all,
                  intensity => 0,
                  period => 1},
-    ChildSpecs = [],
+    ServiceSupSpec = #{
+        id => service_sup,
+        start => {tfte_service_sup, start_link, []},
+        restart => permanent,
+        type => supervisor,
+        shutdown => brutal_kill
+    },
+    ServerSpec = #{
+        id => ?ROOT_SERVER,
+        start => {?ROOT_SERVER, start_link, []},
+        restart => permanent,
+        shutdown => brutal_kill
+    },
+    ChildSpecs = [ServerSpec, ServiceSupSpec],
     {ok, {SupFlags, ChildSpecs}}.
diff --git a/src/te/apps/tfte/src/tfte_te_service.erl b/src/te/apps/tfte/src/tfte_te_service.erl
index 7c2a7225b497ef5ba5b56ba7aac419e4f5bacbe6..1cadd7aad0b7f512445fae62fbcdb380d92bf544 100644
--- a/src/te/apps/tfte/src/tfte_te_service.erl
+++ b/src/te/apps/tfte/src/tfte_te_service.erl
@@ -3,12 +3,12 @@
 -behaviour(te_te_service_bhvr).
 
 
-%--- Includes ------------------------------------------------------------------
+%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 -include_lib("grpcbox/include/grpcbox.hrl").
 
 
-%--- Exports -------------------------------------------------------------------
+%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 % Behaviour te_te_service_bhvr callback functions
 -export([request_lsp/2]).
@@ -16,7 +16,7 @@
 -export([delete_lsp/2]).
 
 
-%--- Behaviour te_te_service_bhvr Callback Functions ---------------------------
+%%% BEHAVIOUR te_te_service_bhvr CALLBACK FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 request_lsp(_Ctx, _Service) ->
     {error, {?GRPC_STATUS_UNIMPLEMENTED, <<"Not yet implemented">>},
diff --git a/src/te/apps/tfte/src/tfte_topology.erl b/src/te/apps/tfte/src/tfte_topology.erl
new file mode 100644
index 0000000000000000000000000000000000000000..d2c6fc0d9cdb2ceb097bc21dff5211308ed7bd9e
--- /dev/null
+++ b/src/te/apps/tfte/src/tfte_topology.erl
@@ -0,0 +1,394 @@
+-module(tfte_topology).
+
+-behaviour(gen_statem).
+
+
+%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-include_lib("kernel/include/logger.hrl").
+
+
+%%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% API functions
+-export([start_link/0]).
+-export([context_updated/0]).
+
+% Behaviour gen_statem functions
+-export([init/1]).
+-export([callback_mode/0]).
+-export([handle_event/4]).
+-export([terminate/3]).
+-export([code_change/4]).
+
+
+%%% Records %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-record(data, {
+    uuid :: map(),
+    sub :: term() | undefined,
+    obj :: map() | undefined,
+    devices = #{} :: map(),
+    links = #{} :: map()
+}).
+
+
+%%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(SUBSCRIBE_RETRY_TIMEOUT, 1000).
+-define(RETRIEVE_RETRY_TIMEOUT, 10000).
+
+
+%%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+start_link() ->
+    gen_statem:start_link(?MODULE, [], []).
+
+context_updated() ->
+    gen_statem:cast(?MODULE, context_updated).
+
+
+%%% BEHAVIOUR gen_statem FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+init([]) ->
+    {ok, ContextName} = application:get_env(tfte, context),
+    {ok, TopoName} = application:get_env(tfte, topology),
+    ContextUUID = #{context_uuid => #{uuid => ContextName}},
+    TopoUUID = #{context_id => ContextUUID,
+                 topology_uuid => #{uuid => TopoName}},
+    ?LOG_INFO("Starting topology ~s service handler...", [TopoName]),
+    {ok, retrieve, #data{uuid = TopoUUID}}.
+
+callback_mode() -> [handle_event_function, state_enter].
+
+%-- RETRIEVE STATE -------------------------------------------------------------
+handle_event(enter, _, retrieve, _Data) ->
+    {keep_state_and_data, [{state_timeout, 0, do_retrieve}]};
+handle_event(state_timeout, do_retrieve, retrieve, #data{uuid = UUID} = Data) ->
+    ?LOG_DEBUG("Retrieving topology ~p...", [UUID]),
+    case get_object(UUID) of
+        error ->
+            {keep_state_and_data, [{state_timeout, ?RETRIEVE_RETRY_TIMEOUT, do_retrieve}]};
+        {ok, Topology} ->
+            ?LOG_DEBUG("Got topology: ~p", [Topology]),
+            {next_state, subscribe, Data#data{obj = Topology}}
+    end;
+handle_event(cast, context_updated, retrieve, _Data) ->
+    {keep_state_and_data, [{state_timeout, 0, do_retrieve}]};
+%-- SUBSCRIBE STATE ------------------------------------------------------------
+handle_event(enter, _, subscribe, #data{sub = undefined}) ->
+    {keep_state_and_data, [{state_timeout, 0, do_suscribe}]};
+handle_event(enter, _, subscribe, Data) ->
+    % We already have a topology subscription
+    {next_state, ready, Data};
+handle_event(state_timeout, do_suscribe, subscribe, #data{uuid = UUID} = Data) ->
+    ?LOG_DEBUG("Subscribing to topology events...", []),
+    case do_subscribe(UUID) of
+        {ok, Sub} ->
+            ?LOG_INFO("Subscribed to topology events", []),
+            Data2 = #data{obj = Obj} = Data#data{sub = Sub},
+            #{device_ids := DeviceIds, link_ids := LinkIds} = Obj,
+            case update_topology(Data2, DeviceIds, LinkIds) of
+                {ok, Data3} ->
+                    tfte_server:topology_ready(Obj),
+                    {next_state, ready, Data3};
+                {error, Reason} ->
+                    ?LOG_ERROR("Failed to load topology: ~p", [Reason]),
+                    statem_rollback_to_retrieve(Data2)
+            end;
+        {error, Reason} ->
+            ?LOG_ERROR("Failed to subscribe to topology service events: ~p", [Reason]),
+            {next_state, retrieve, [{state_timeout, ?SUBSCRIBE_RETRY_TIMEOUT, do_retrieve}]}
+    end;
+%-- READY STATE ----------------------------------------------------------------
+handle_event(enter, _, ready, _Data) ->
+    keep_state_and_data;
+handle_event(info, {headers, Id, Value}, ready,
+             #data{sub = #{stream_id := Id}}) ->
+    %TODO: Handle HTTP errors ???
+    ?LOG_DEBUG("Received topology stream header: ~p", [Value]),
+    keep_state_and_data;
+handle_event(info, {data, Id, #{event := Event}}, ready,
+             #data{sub = #{stream_id := Id}} = Data) ->
+    ?LOG_DEBUG("Received topology event: ~p", [Event]),
+    handle_topology_event(Data, Event);
+handle_event(info, {'DOWN', Ref, process, Pid, Reason}, ready,
+             #data{sub = #{stream_id := Id, monitor_ref := Ref, stream_pid := Pid}} = Data) ->
+    ?LOG_DEBUG("Topology subscription is down: ~p", [Reason]),
+    Data2 = Data#data{sub = undefined},
+    Info = receive
+        {trailers, Id, {Status, Message, Metadata}} ->
+            {Reason, Status, Message, Metadata}
+    after 0 ->
+        Reason
+    end,
+    ?LOG_ERROR("Topology subscription error: ~p", [Info]),
+    {next_state, retrieve, Data2};
+handle_event(cast, context_updated, ready, _Data) ->
+    keep_state_and_data;
+%-- ANY STATE ------------------------------------------------------------------
+handle_event(info, Msg, StateName, _Data) ->
+    ?LOG_WARNING("Unexpected topology message in state ~w: ~p", [StateName, Msg]),
+    keep_state_and_data.
+
+terminate(Reason, _State, _Data) ->
+    ?LOG_INFO("Topology service handler terminated: ~p", [Reason]),
+    ok.
+
+code_change(_OldVsn, OldState, OldData, _Extra) ->
+    {ok, OldState, OldData}.
+
+
+%%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+statem_rollback_to_retrieve(#data{sub = undefined} = Data) ->
+    {next_state, retrieve, Data, [{state_timeout, ?RETRIEVE_RETRY_TIMEOUT, do_retrieve}]};
+statem_rollback_to_retrieve(#data{sub = Sub} = Data) ->
+    grpcbox_client:close_send_and_recv(Sub),
+    Data2 = Data#data{sub = undefined},
+    {next_state, retrieve, Data2, [{state_timeout, ?RETRIEVE_RETRY_TIMEOUT, do_retrieve}]}.
+
+handle_topology_event(#data{uuid = UUID} = Data,
+                      #{event_type := 'EVENTTYPE_UPDATE'} = Event) ->
+    case get_object(UUID) of
+        error ->
+            statem_rollback_to_retrieve(Data);
+        {ok, #{device_ids := DeviceIds, link_ids := LinkIds} = Topology} ->
+            ?LOG_DEBUG("Got new topology: ~p", [Topology]),
+            Data2 = Data#data{obj = Topology},
+            case update_topology(Data2, DeviceIds, LinkIds) of
+                {ok, Data3} ->
+                    tfte_server:topology_event(Event),
+                    {keep_state, Data3};
+                {error, Reason} ->
+                    ?LOG_ERROR("Failed to update topology: ~p", [Reason]),
+                    statem_rollback_to_retrieve(Data2)
+            end
+    end;
+handle_topology_event(_Data, Event) ->
+    tfte_server:topology_event(Event),
+    keep_state_and_data.
+
+update_topology(Data, DeviceIds, LinkIds) ->
+    try
+        {Data2, Events} = update_devices(Data, DeviceIds, []),
+        {Data3, Events2} = update_links(Data2, LinkIds, Events),
+        post_topology_events(lists:reverse(Events2)),
+        {ok, Data3}
+    catch
+        throw:Reason ->
+            {error, Reason}
+    end.
+
+post_topology_events(Events) ->
+    lists:foreach(fun post_topology_event/1, Events).
+
+post_topology_event({device_added, Id, Device}) ->
+    epce_ted:device_added(Id, Device);
+post_topology_event({device_updated, Id, Device}) ->
+    epce_ted:device_updated(Id, Device);
+post_topology_event({device_deleted, Id}) ->
+    epce_ted:device_deleted(Id);
+post_topology_event({link_added, Id, Link}) ->
+    epce_ted:link_added(Id, Link);
+post_topology_event({link_updated, Id, Link}) ->
+    epce_ted:link_updated(Id, Link);
+post_topology_event({link_deleted, Id}) ->
+    epce_ted:link_deleted(Id).
+
+update_devices(#data{devices = OldDevices} = Data, DeviceIds, Events) ->
+    {NewDevices, Events2} = update_devices(OldDevices, #{}, DeviceIds, Events),
+    {Data#data{devices = NewDevices}, Events2}.
+
+update_devices(OldDevices, NewDevices, [], Events) ->
+    Events2 = [{device_deleted, post_process_device_id(I)}
+               || I <- maps:keys(OldDevices)] ++ Events,
+    {NewDevices, Events2};
+update_devices(OldDevices, NewDevices, [Id | Rest], Events) ->
+    case get_device(Id) of
+        error -> throw({device_retrieval_error, Id});
+        {ok, Device} ->
+            Id2 = post_process_device_id(Id),
+            Device2 = post_process_device(Device),
+            NewDevices2 = NewDevices#{Id => Device},
+            case maps:take(Id, OldDevices) of
+                error ->
+                    % New device
+                    Events2 = [{device_added, Id2, Device2} | Events],
+                    update_devices(OldDevices, NewDevices2, Rest, Events2);
+                {Device, OldDevices2} ->
+                    % Device did not change
+                    update_devices(OldDevices2, NewDevices2, Rest, Events);
+                {_OldDevice, OldDevices2} ->
+                    % Device changed
+                    Events2 = [{device_updated, Id2, Device2} | Events],
+                    update_devices(OldDevices2, NewDevices2, Rest, Events2)
+            end
+    end.
+
+update_links(#data{links = OldLinks} = Data, LinksIds, Events) ->
+    {NewLinks, Events2} = update_links(OldLinks, #{}, LinksIds, Events),
+    {Data#data{links = NewLinks}, Events2}.
+
+update_links(OldLinks, NewLinks, [], Events) ->
+    Events2 = [{link_deleted, post_process_link_id(I)}
+               || I <- maps:keys(OldLinks)] ++ Events,
+    {NewLinks, Events2};
+update_links(OldLinks, NewLinks, [Id | Rest], Events) ->
+    case get_link(Id) of
+        error -> throw({link_retrieval_error, Id});
+        {ok, Link} ->
+            Id2 = post_process_link_id(Id),
+            Link2 = post_process_link(Link),
+            NewLinks2 = NewLinks#{Id => Link},
+            case maps:take(Id, OldLinks) of
+                error ->
+                    % New Link
+                    Events2 = [{link_added, Id2, Link2} | Events],
+                    update_links(OldLinks, NewLinks2, Rest, Events2);
+                {Link, OldLinks2} ->
+                    % Link did not change
+                    update_links(OldLinks2, NewLinks2, Rest, Events);
+                {_OldLink, OldLinks2} ->
+                    % Link changed
+                    Events2 = [{link_updated, Id2, Link2} | Events],
+                    update_links(OldLinks2, NewLinks2, Rest, Events2)
+            end
+    end.
+
+post_process_device_id(#{device_uuid := #{uuid := Name}}) ->
+    Name.
+
+post_process_device(Device) ->
+    #{id => device_id(Device),
+      type => device_type(Device),
+      pcc_address => device_pcc_address(Device),
+      mpls_label => device_mpls_label(Device),
+      status => device_status(Device),
+      endpoints => device_endpoints(Device)}.
+
+device_id(#{device_id := Id}) ->
+    post_process_device_id(Id).
+
+device_type(#{device_type := Type}) ->
+    Type.
+
+device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_UNDEFINED'}) ->
+    undefined;
+device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_DISABLED'}) ->
+    disabled;
+device_status(#{device_operational_status := 'DEVICEOPERATIONALSTATUS_ENABLED'}) ->
+    enabled.
+
+device_mpls_label(Device) ->
+    case device_config_value(<<"mpls_label">>, Device) of
+        undefined -> undefined;
+        LabelBin ->
+            try binary_to_integer(LabelBin)
+            catch error:badarg -> undefined
+            end
+    end.
+
+device_pcc_address(Device) ->
+    case device_config_value(<<"pcc_address">>, Device) of
+        undefined -> undefined;
+        AddressBin ->
+            case inet_parse:address(binary_to_list(AddressBin)) of
+                {ok, Address} -> Address;
+                {error,einval} -> undefined
+            end
+    end.
+
+device_config_value(Key, #{device_config := #{config_rules := Rules}}) ->
+    device_config_value(Key, Rules);
+device_config_value(_Key, []) ->
+    undefined;
+device_config_value(Key, [#{action := 'CONFIGACTION_SET',
+                          config_rule := {custom, Rule}} | Rest]) ->
+    case Rule of
+        #{resource_key := Key, resource_value := Value} -> Value;
+        _ -> device_config_value(Key, Rest)
+    end;
+device_config_value(Key, [_Rule | Rest]) ->
+    device_config_value(Key, Rest).
+
+
+device_endpoints(Device) ->
+    device_endpoints(Device, []).
+
+device_endpoints(#{device_endpoints := Endpoints}, Acc) ->
+    device_endpoints(Endpoints, Acc);
+device_endpoints([], Acc) ->
+    lists:reverse(Acc);
+device_endpoints([#{endpoint_id := #{endpoint_uuid := #{uuid := Name}}} | Rest], Acc) ->
+    device_endpoints(Rest, [Name | Acc]).
+
+post_process_link_id(#{link_uuid := #{uuid := Name}}) ->
+    Name.
+
+post_process_link(Link) ->
+    #{id => link_id(Link),
+      endpoints => link_endpoints(Link)}.
+
+link_id(#{link_id := Id}) ->
+    post_process_link_id(Id).
+
+link_endpoints(Link) ->
+    link_endpoints(Link, []).
+
+link_endpoints(#{link_endpoint_ids := Endpoints}, Acc) ->
+    link_endpoints(Endpoints, Acc);
+link_endpoints([], Acc) ->
+    lists:reverse(Acc);
+link_endpoints([#{device_id := #{device_uuid := #{uuid := DevName}},
+                  endpoint_uuid := #{uuid := EndpointName}} | Rest], Acc) ->
+    Endpoint = #{
+        device => DevName,
+        endpoint => EndpointName
+    },
+    link_endpoints(Rest, [Endpoint | Acc]).
+
+
+%-- GRPC UNTILITY FUNCTION -----------------------------------------------------
+
+grpc_opts() ->
+    #{channel => context}.
+
+do_subscribe(UUID) ->
+    context_context_service_client:get_topology_events(UUID, grpc_opts()).
+
+get_object(UUID) ->
+    case context_context_service_client:get_topology(UUID, grpc_opts()) of
+        {error, Reason} -> 
+            ?LOG_ERROR("Local error while retrieving the topology object: ~p", [Reason]),
+            error;
+        {error, Reason, _Headers} ->
+            ?LOG_ERROR("Remote error while retrieving the topology object: ~p", [Reason]),
+            error;
+        {ok, Result, _Headers} ->
+            {ok, Result}
+    end.
+
+get_device(UUID) ->
+    case context_context_service_client:get_device(UUID, grpc_opts()) of
+        {error, Reason} -> 
+            ?LOG_ERROR("Local error while retrieving a device object: ~p", [Reason]),
+            error;
+        {error, Reason, _Headers} ->
+            ?LOG_ERROR("Remote error while retrieving a device object: ~p", [Reason]),
+            error;
+        {ok, Result, _Headers} ->
+            {ok, Result}
+    end.
+
+get_link(UUID) ->
+    case context_context_service_client:get_link(UUID, grpc_opts()) of
+        {error, Reason} -> 
+            ?LOG_ERROR("Local error while retrieving a link object: ~p", [Reason]),
+            error;
+        {error, Reason, _Headers} ->
+            ?LOG_ERROR("Remote error while retrieving a link object: ~p", [Reason]),
+            error;
+        {ok, Result, _Headers} ->
+            {ok, Result}
+    end.
diff --git a/src/te/config/sys.config.src b/src/te/config/sys.config.src
index 93bfbafb1cf9ee5d457fc41dc2e97389deb731f0..f3c18564177e11bcf21f4fab4d45e905f806060c 100644
--- a/src/te/config/sys.config.src
+++ b/src/te/config/sys.config.src
@@ -1,6 +1,8 @@
 [
 
     {tfte, [
+        {context, <<"admin">>},
+        {topology, <<"tetestbed">>},
         {services, [
             {te, [
                 {http, {env, "TESERVICE_SERVICE_HOST"}, {env, "TESERVICE_SERVICE_PORT_GRPC"}, []}
@@ -72,7 +74,7 @@
         {logger_level, ${ERLANG_LOGGER_LEVEL}},
         {logger, [
             {handler, default, logger_std_h, #{
-                level => info,
+                level => ${ERLANG_LOGGER_LEVEL},
                 filter_default => log,
                 config => #{type => standard_io},
                 formatter => {logger_formatter, #{
diff --git a/tutorial/2-6-te-demo-start-testbed.sh b/tutorial/2-6-te-demo-start-testbed.sh
index dea41a71a4371384b32d91f74e2ec90af171d30f..3266d1141a246fd7bfd4dd3efd155128a792114f 100755
--- a/tutorial/2-6-te-demo-start-testbed.sh
+++ b/tutorial/2-6-te-demo-start-testbed.sh
@@ -11,26 +11,30 @@ if [[ ! -f "${NEGENDIR}/exe/netgen" ]]; then
     exit 1
 fi
 
-export PCE_NETNS="$1"
-export PCE_IP="$2"
-RT1_INT_IP="$3"
-RT1_EXT_IP="$4"
-RT6_INT_IP="$5"
-RT6_EXT_IP="$6"
-
-
-if [[ -z $PCE_NETNS || -z $PCE_IP || -z  RT1_INT_IP || -z  RT1_EXT_IP || -z  RT6_INT_IP || -z  RT6_EXT_IP ]]; then
-    echo "USAGE: $0 PCE_NETNS PCE_IP RT1_INT_IP RT1_EXT_IP RT6_INT_IP RT6_EXT_IP"
-    echo "  e.g: $0 cni-588a2d06-e64f-907b-d51b-bed0307007c9 10.1.103.133 10 11 12 13"
-    exit 1
+PCE_IP=$( kubectl --namespace tfs get $(kubectl --namespace tfs get pods --selector=app=teservice -o name) --template '{{.status.podIP}}' )
+echo "Teraflow PCE IP address: $PCE_IP"
+NAMESPACES=$( ip netns list | cut -d' ' -f1 )
+PCE_NETNS=""
+for n in $NAMESPACES; do
+    if sudo ip -n $n addr list | grep $PCE_IP > /dev/null; then
+        echo "Teraflow TE service namespace: $n"
+        PCE_NETNS=$n
+        break
+    fi    
+done
+if [[ -z $PCE_NETNS ]]; then
+    echo "Teraflow network namespace for TE service not found"
+    exit1
 fi
 
 IFS=. read PCE_IP1 PCE_IP2 PCE_IP3 PCE_IP4 <<< "$PCE_IP"
 
-export RT1_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT1_INT_IP"
-export RT1_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT1_EXT_IP"
-export RT6_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT6_INT_IP"
-export RT6_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.$RT6_EXT_IP"
+export PCE_IP
+export PCE_NETNS
+export RT1_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.10"
+export RT1_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.11"
+export RT6_PCE_INT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.12"
+export RT6_PCE_EXT_IF_IP="$PCE_IP1.$PCE_IP2.$PCE_IP3.13"
 
 cp "${ROOTDIR}/2-6-netgen-config.yml" "${RUNDIR}/config.yml"
 cat "${ROOTDIR}/2-6-netgen-topology.yml.template" | envsubst > "${RUNDIR}/topology.yml"
diff --git a/tutorial/2-6-te-demo.md b/tutorial/2-6-te-demo.md
index 6a99007b4c5d481cc7692ea963c138ff51234c0d..774a38babe9f0fea53296cbbd83467c55f303e59 100644
--- a/tutorial/2-6-te-demo.md
+++ b/tutorial/2-6-te-demo.md
@@ -78,20 +78,14 @@
 
 ### Run the Test-Bed
 
-To start the testbed, we need to figure out the IP of the TE service:
-    $ kubectl --namespace tfs get $(kubectl --namespace tfs get pods --selector=app=teservice -o name) --template '{{.status.podIP}}'
-
-And select the network namespace that defines it by looking into each of them until you find the correct one:
-    $ ip netns list
-    $ sudo ip -n XXX addr list
-
-When we have the IP and namespace we can start the testbed.
+First load the [teraflow configuration file](./2-6-teraflow-topology.json) using the webui.
+The first time the configuration is loaded may partialy fail because it tries to load the topology before the devices and links, but a second load should work.
 
 In first console:
     $ cd ~/testbed
-    $ ../tfs-ctrl/tutorial/2-6-te-demo-start-testbed.sh NETWORK_NAMESPACE TE_SERVICE_IP 10 11 12 13
+    $ ../tfs-ctrl/tutorial/2-6-te-demo-start-testbed.sh
 
 Then in second console:
     $ sudo -i
     # cd /tmp/negen
-    # ./tmux.sh
\ No newline at end of file
+    # ./tmux.sh
diff --git a/tutorial/2-6-teraflow-topology.json b/tutorial/2-6-teraflow-topology.json
new file mode 100644
index 0000000000000000000000000000000000000000..8b5188866c4e2d2b5dff26b6b3bf26c7eb56b2ec
--- /dev/null
+++ b/tutorial/2-6-teraflow-topology.json
@@ -0,0 +1,210 @@
+{
+    "contexts": [
+        {
+            "context_id": {"context_uuid": {"uuid": "admin"}},
+            "topology_ids": [],
+            "service_ids": []
+        }
+    ],
+    "topologies": [
+        {
+            "topology_id": {"topology_uuid": {"uuid": "tetestbed"}, "context_id": {"context_uuid": {"uuid": "admin"}}},
+            "device_ids": [
+                {"device_uuid": {"uuid": "SW1"}},
+                {"device_uuid": {"uuid": "RT1"}},
+                {"device_uuid": {"uuid": "RT2"}},
+                {"device_uuid": {"uuid": "RT3"}},
+                {"device_uuid": {"uuid": "RT4"}},
+                {"device_uuid": {"uuid": "RT5"}},
+                {"device_uuid": {"uuid": "RT6"}}
+            ],
+            "link_ids": [
+                {"link_uuid": {"uuid": "RT1/SW1"}},
+                {"link_uuid": {"uuid": "RT2/SW1"}},
+                {"link_uuid": {"uuid": "RT3/SW1"}},
+                {"link_uuid": {"uuid": "RT2/RT4/1"}},
+                {"link_uuid": {"uuid": "RT2/RT4/2"}},
+                {"link_uuid": {"uuid": "RT3/RT5/1"}},
+                {"link_uuid": {"uuid": "RT3/RT5/2"}},
+                {"link_uuid": {"uuid": "RT4/RT5"}},
+                {"link_uuid": {"uuid": "RT4/RT6"}},
+                {"link_uuid": {"uuid": "RT5/RT6"}}
+            ]
+        }
+    ],
+    "devices": [
+        {
+            "device_id": {"device_uuid": {"uuid": "SW1"}},
+            "device_type": "emu-packet-switch",
+            "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value":
+                    "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"df8bb169-2013-4b82-9455-69777f7a01d6\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"061119c1-2aa4-48e9-be64-3ddf465fc80a\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"495ea3f8-e67f-46a0-84bd-a230a4b7067d\"}]}"}}
+            ]},
+            "device_operational_status": 1,
+            "device_drivers": [0],
+            "device_endpoints": []
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "RT1"}},
+            "device_type": "emu-packet-router",
+            "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16010"}},
+                {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "1.1.1.1"}},
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "1.1.1.1"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value":
+                    "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-src\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}]}"}}
+            ]},
+            "device_operational_status": 1,
+            "device_drivers": [0],
+            "device_endpoints": []
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "RT2"}},
+            "device_type": "emu-packet-router",
+            "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16020"}},
+                {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "2.2.2.2"}},
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "2.2.2.2"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value":
+                    "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4-2\"}]}"}}
+            ]},
+            "device_operational_status": 1,
+            "device_drivers": [0],
+            "device_endpoints": []
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "RT3"}},
+            "device_type": "emu-packet-router",
+            "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16030"}},
+                {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "3.3.3.3"}},
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "3.3.3.3"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value":
+                    "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-sw1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5-2\"}]}"}}
+            ]},
+            "device_operational_status": 1,
+            "device_drivers": [0],
+            "device_endpoints": []
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "RT4"}},
+            "device_type": "emu-packet-router",
+            "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16040"}},
+                {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "4.4.4.4"}},
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "4.4.4.4"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value":
+                    "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt2-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt2-2\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt6\"}]}"}}
+            ]},
+            "device_operational_status": 1,
+            "device_drivers": [0],
+            "device_endpoints": []
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "RT5"}},
+            "device_type": "emu-packet-router",
+            "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16050"}},
+                {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "5.5.5.5"}},
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "5.5.5.5"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value":
+                "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt3-1\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt3-2\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt6\"}]}"}}
+            ]},
+            "device_operational_status": 1,
+            "device_drivers": [0],
+            "device_endpoints": []
+        },
+        {
+            "device_id": {"device_uuid": {"uuid": "RT6"}},
+            "device_type": "emu-packet-router",
+            "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/mpls_label", "resource_value": "16060"}},
+                {"action": 1, "custom": {"resource_key": "_connect/pcc_address", "resource_value": "6.6.6.6"}},
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "6.6.6.6"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value":
+                "{\"endpoints\": [{\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt4\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-rt5\"}, {\"sample_types\": [], \"type\": \"copper\", \"uuid\": \"eth-dst\"}]}"}}
+            ]},
+            "device_operational_status": 1,
+            "device_drivers": [0],
+            "device_endpoints": []
+        }
+    ],
+    "links": [
+        {
+            "link_id": {"link_uuid": {"uuid": "RT1/SW1"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "RT1"}}, "endpoint_uuid": {"uuid": "eth-sw1"}},
+                {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "df8bb169-2013-4b82-9455-69777f7a01d6"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "RT2/SW1"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-sw1"}},
+                {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "061119c1-2aa4-48e9-be64-3ddf465fc80a"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "RT3/SW1"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-sw1"}},
+                {"device_id": {"device_uuid": {"uuid": "SW1"}}, "endpoint_uuid": {"uuid": "495ea3f8-e67f-46a0-84bd-a230a4b7067d"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "RT2/RT4/1"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-rt4-1"}},
+                {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt2-1"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "RT2/RT4/2"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "RT2"}}, "endpoint_uuid": {"uuid": "eth-rt4-2"}},
+                {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt2-2"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "RT3/RT5/1"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-rt5-1"}},
+                {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt3-1"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "RT3/RT5/2"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "RT3"}}, "endpoint_uuid": {"uuid": "eth-rt5-2"}},
+                {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt3-2"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "RT4/RT5"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt5"}},
+                {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt4"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "RT4/RT6"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "RT4"}}, "endpoint_uuid": {"uuid": "eth-rt6"}},
+                {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-rt4"}}
+            ]
+        },
+        {
+            "link_id": {"link_uuid": {"uuid": "RT5/RT6"}},
+            "link_endpoint_ids": [
+                {"device_id": {"device_uuid": {"uuid": "RT5"}}, "endpoint_uuid": {"uuid": "eth-rt6"}},
+                {"device_id": {"device_uuid": {"uuid": "RT6"}}, "endpoint_uuid": {"uuid": "eth-rt5"}}
+            ]
+        }
+    ]
+}