ovsdb client hadler now working for startup sequence

This commit is contained in:
Helge
2020-11-20 12:13:57 -08:00
parent c599a05f14
commit ccab98a820
4 changed files with 219 additions and 63 deletions

View File

@@ -17,7 +17,7 @@
-ifdef(debug).
-define(DBGTRC(Msg), io:format("~s:~s (~p) => ~s~n",[?MODULE,?FUNCTION_NAME,?LINE,Msg])).
-define(DBGSTR(Msg), io_lib:format("~s:~s (~B) => ~s~n",[?MODULE,?FUNCTION_NAME,?LINE,Msg])).
-define(DBGSTR(FMsg,Args), io_lib:format("~s:~s (~B) => " FMsg "~n",[?MODULE,?FUNCTION_NAME,?LINE] ++ [Args])).
-define(DBGSTR(FMsg,Args), io_lib:format("~s:~s (~B) => " FMsg "~n",[?MODULE,?FUNCTION_NAME,?LINE] ++ Args)).
-else.
-define(DBGTRC(Msg), true).
-define(DBGSTR(Msg), Msg).

View File

@@ -29,7 +29,7 @@
%% API
-export([start/2]).
-export([start_link/3]).
-export([uuid/1]).
@@ -40,14 +40,17 @@
%% data structures
-type ap_status() :: init | ready | running | paused | stopped.
-export_type([ap_status/0]).
-record(ap_state, {
sim_node :: pid(), % controlling simnode
sim_manager :: pid(), % manager to be contacted to get configuration
serial :: string(), % serial number of the access point
type :: binary(), % device type e.g. EA8300
uuid :: term(), % unique ID for this AP node
serial = "" :: string(), % serial number of the access point
type = <<"">> :: binary(), % device type e.g. EA8300
uuid :: string(), % unique ID for this AP node
timer :: owls_timers:tms(),
status = init :: init | ready | running | stopped, % internal status
status = init :: ap_status(), % internal status
config = #{} :: map(),
statistics = #{} :: map()
}).
@@ -60,14 +63,15 @@
%%%============================================================================
-spec start (Handler, Spec) -> {ok, Pid} | {error, Reason} when
-spec start_link (Handler, Id, SimMan) -> {ok, Pid} | {error, Reason} when
Handler :: pid(),
Spec :: proplists:proplist(),
Id :: UUID::string(),
SimMan :: pid(),
Pid :: pid(),
Reason :: term().
start (Handler, Spec) ->
gen_server:start(?MODULE, {Handler, Spec}, []).
start_link (Handler, Id, SimMan) ->
gen_server:start_link(?MODULE, {Handler, Id, SimMan}, []).
@@ -86,14 +90,15 @@ uuid (Node) ->
%%% GEN_SERVER callbacks
%%%============================================================================
-spec init ({Handler, Spec}) -> {ok, State} when
-spec init ({Handler, Id, SimMan}) -> {ok, State} when
Handler :: pid(),
Spec :: proplists:proplist(),
Id :: UUID::string(),
SimMan :: pid(),
State :: #ap_state{}.
init ({Handler,Spec}) ->
init ({Handler,Id,SimMan}) ->
process_flag(trap_exit, true),
InitialState = prepare_state(Handler,Spec),
InitialState = prepare_state(Handler,Id,SimMan),
gen_server:cast(self(),start_up),
{ok, InitialState}.
@@ -181,20 +186,19 @@ code_change (_,OldState,_) ->
%%%============================================================================
%---------prepare_state/2----------------convert Spec proplist into internal state
%---------prepare_state/3----------------convert Spec proplist into internal state
-spec prepare_state (Node, Spec) -> State when
Node :: pid(),
Spec :: proplists:proplist(),
-spec prepare_state (Handler, ID, SimMan) -> State when
Handler :: pid(),
ID :: UUID::string(),
SimMan :: pid(),
State :: #ap_state{}.
prepare_state (Node,Spec) ->
prepare_state (Handler, ID, SimMan) ->
#ap_state{
sim_node = Node,
sim_manager = proplists:get_value(manager,Spec,Node),
serial = proplists:get_value(ap_serial,Spec,"1P000000000"),
type = proplists:get_value(ap_type,Spec,<<"EA8300">>),
uuid = proplists:get_value(uuid,Spec,erlang:phash2({node(),erlang:system_time()})),
sim_node = Handler,
sim_manager = SimMan,
uuid = ID,
timer = owls_timers:new(millisecond)
}.

View File

@@ -12,7 +12,10 @@
-behaviour(gen_server).
-behaviour(gen_sim_client).
-include("../include/common.hrl").
-define(SERVER, ?MODULE).
-define(MAX_STARTUP_TIME,10000).
%% API
-export([start_link/0]).
@@ -25,14 +28,22 @@
%% data structures
-type client_ref() :: {UUID::string(), available | dead | pid()}.
-type client_states() :: #{UUID::string() := available | ovsdb_ap:ap_status()}.
-type client_refs() :: #{pid() := UUID::string()}.
-record(hdl_state, {
clients_avail = [] :: [client_ref()],
clients_started = [] :: [client_ref()],
clients_running = [] :: [client_ref()],
clients_paused = [] :: [client_ref()],
config = #{} :: #{}
-record (command,{
cmd :: start | stop | pause | cancel,
refs :: [UUID::string()]
}).
-type command() :: #command{}.
-record (hdl_state, {
client_states = #{} :: client_states(),
client_pids = #{} :: client_refs(),
cmd_queue = [] :: [command()],
config = #{} :: #{},
timer :: owls_timers:tms()
}).
@@ -65,7 +76,7 @@ set_configuration (Cfg) ->
Reason :: term().
start (What) ->
gen_server:call(?SERVER,{start_sim, What}).
gen_server:call(?SERVER,{cmd_start, What}).
@@ -124,7 +135,7 @@ report () ->
init (_) ->
process_flag(trap_exit, true),
{ok, #hdl_state{}}.
{ok, #hdl_state{timer=owls_timers:new(millisecond)}}.
@@ -135,6 +146,9 @@ init (_) ->
NewState :: #hdl_state{},
Reason :: term().
handle_cast (execute, State) ->
{noreply, execute_cmd(State)};
handle_cast (_,State) ->
{noreply, State}.
@@ -150,16 +164,10 @@ handle_cast (_,State) ->
NewState :: #hdl_state{}.
handle_call ({set_config, Cfg},_,State) ->
NewState = apply_config(State,Cfg),
{reply, ok, NewState};
{reply, ok, apply_config(State,Cfg)};
handle_call ({start_sim, How},_,State) ->
case start_simulation(State,How) of
{ok, NewState} ->
{reply, ok, NewState};
Error ->
{reply, Error, State}
end;
handle_call ({cmd_start, How},_,State) ->
{reply, ok, cmd_startup_sim(State,How)};
handle_call (_, _, State) ->
{reply, invalid, State}.
@@ -212,24 +220,144 @@ code_change (_,OldState,_) ->
NewState :: #hdl_state{}.
apply_config (State, _Cfg) ->
State#hdl_state{clients_avail=[{"a68d41fa-dd12-4fb7-bc44-e834667280b4",available}]}.
State#hdl_state{client_states=#{"a68d41fa-dd12-4fb7-bc44-e834667280b4" => available}}.
%--------start_simulation/2--------------start a simulation of designated clients
%--------get_clients_in_state/3----------filter all clients with state
-spec start_simulation (State, How) -> {ok, NewState} | {error, Reason} when
-spec get_clients_in_state (State, Clients, Candidates) -> Available when
State :: available | ovsdb_ap:ap_status(),
Clients :: client_states(),
Candidates :: all | [UUID::string()],
Available :: [UUID::string()].
get_clients_in_state (State, Clients, all) ->
[K || {K,V} <- maps:to_list(Clients), (V=:=State)];
get_clients_in_state (State, Clients, Candidates) ->
[K || {K,V} <- maps:to_list(Clients), (V=:=State) and lists:member(K,Candidates)].
%%-----------------------------------------------------------------------------
%% command queue handling
%--------execute_cmd/1-------------------executes the first command in queue
-spec execute_cmd (State) -> NewState when
State :: #hdl_state{},
NewState :: #hdl_state{}.
execute_cmd (#hdl_state{cmd_queue=[]}=State) ->
State;
execute_cmd (#hdl_state{cmd_queue=Q}=State) ->
[Cmd|RemCmds] = lists:reverse(Q),
AltState = State#hdl_state{cmd_queue=lists:reverse(RemCmds)},
case Cmd of
#command{cmd=start, refs=R} ->
cmd_start_clients(R, AltState);
#command{cmd=Ukn} ->
?L_E(?DBGSTR("Unknown command '~s' in queue",[Ukn])),
AltState
end.
%--------cmd_startup_sim/2--------------lauches the start-up sequence of simulation clients
-spec cmd_startup_sim (State, How) -> NewState when
State :: #hdl_state{},
How :: all | [UUID::string()],
NewState :: #hdl_state{},
Reason :: string().
NewState :: #hdl_state{}.
start_simulation (#hdl_state{clients_avail=[]}, _) ->
{error, "no clients available"};
cmd_startup_sim (State, How) ->
NewState = #hdl_state{timer=owls_timers:mark("startup",State#hdl_state.timer)},
ToLaunch = get_clients_in_state (available,State#hdl_state.client_states, How),
ToStart = get_clients_in_state (ready,State#hdl_state.client_states, How),
if
length(ToLaunch) + length(ToStart) > 0 ->
gen_server:cast(self(),execute),
Cmds = [#command{cmd=start,refs=ToStart ++ ToLaunch} | NewState#hdl_state.cmd_queue],
cmd_lauch(ToLaunch,NewState#hdl_state{cmd_queue=Cmds});
true ->
?L_E(?DBGSTR("start command issued with no clients available or ready to start")),
NewState
end.
start_simulation (#hdl_state{clients_avail=Cl} = State, all) ->
start_simulation(State,Cl);
start_simulation (State, _Clients) ->
{ok, State}.
%--------cmd_launch/2--------------------lauch processes for clients (synchrounsly)
-spec cmd_lauch (ToLauch,State) -> NewState when
ToLauch :: [UUID::string()],
State :: #hdl_state{},
NewState :: #hdl_state{}.
cmd_lauch (ToLauch, State) ->
%% @TODO: pass in the real manager
L = [ {V, ovsdb_ap:start_link(self(),V,self())} || V <- ToLauch],
C = maps:merge(State#hdl_state.client_states,maps:from_list([{V,init} || {V, {ok, _}} <- L])),
P = maps:from_list([{Pid,Uuid}||{Uuid,Pid}<-[{V,P} || {V, {ok, P}} <- L]]),
T = owls_timers:mark("launched",State#hdl_state.timer),
State#hdl_state{client_states=C,client_pids=P,timer=T}.
%--------cmd_start_clients/2-------------starts the simulation of the cliens (in ready state)
-spec cmd_start_clients (Refs, State) -> NewState when
Refs :: [UUID::string()],
State :: #hdl_state{},
NewState :: #hdl_state{}.
cmd_start_clients (Refs, #hdl_state{timer=T,cmd_queue=Q}=State) ->
{Elapsed, T2} = get_elapsed("start_sim",T),
if
Elapsed > ?MAX_STARTUP_TIME * length(Refs) ->
?L_E(?DBGSTR("Getting ~B clients ready took too long (~s)",[length(Refs),owls_timers:fmt_duration(Elapsed,T)])),
?L_I(?DBGSTR("Cancelling simulation start request")),
Cmds = lists:reverse([#command{cmd=cancel, refs=Refs}|lists:reverse(Q)]),
gen_server:cast(self(),execute),
State#hdl_state{cmd_queue=Cmds, timer=T2};
true ->
Ready = get_clients_in_state (ready,State#hdl_state.client_states,Refs),
check_client_startup(Ready,Refs,State#hdl_state{timer=T2})
end.
-spec check_client_startup (Ready, ToStart, State) -> NewState when
Ready :: [UUID::string()],
ToStart :: [UUID::string()],
State :: #hdl_state{},
NewState :: #hdl_state{}.
check_client_startup (_Ready, _ToStart, State) ->
State.
-spec get_elapsed (Mark,Timer) -> {Elapsed,NewTimer} when
Mark :: string(),
Timer :: owls_timers:tms(),
Elapsed :: integer(),
NewTimer :: owls_timers:tms().
get_elapsed (Mark,T) ->
case owls_timers:delta(Mark,default,T) of
invalid ->
{0,owls_timers:mark("start_sim",T)};
Stamp ->
{Stamp,T}
end.

View File

@@ -37,7 +37,7 @@
new (R) ->
Ups = erlang:convert_time_unit(1, second, native),
#tms{resolution=R,stamps=[{"start",os:system_time()}],units_per_sec=Ups}.
#tms{resolution=R,stamps=[{"*start*",os:system_time()}],units_per_sec=Ups}.
@@ -53,28 +53,49 @@ mark (Name,Timer) ->
%--------stamp/2-----------------------get the value of a specific stamp in units of resolution
-spec stamp (Name, Timer) -> Stamp when
-spec stamp (Name, Timer) -> invalid | Stamp when
Name :: string(),
Timer :: tms(),
Stamp :: integer().
stamp (Name,Timer) ->
{ok, Ts, _} = get_stamp(Name,Timer#tms.stamps),
erlang:convert_time_unit(Ts, native, Timer#tms.resolution).
case get_stamp(Name,Timer#tms.stamps) of
{ok, Ts, _} ->
erlang:convert_time_unit(Ts, native, Timer#tms.resolution);
_ ->
invalid
end.
%--------delta/3-------------------------gets time difference between two stamps in units of resolution
-spec delta (Earlier, Later, Timer) -> Delta when
Earlier :: string(), % Earlier must have been added before Later or in other words
Later :: string(), % Later must have occured after Earlier in the timeline
Earlier :: default | string(), % Earlier must have been added before Later (default means start) or in other words
Later :: default |string(), % Later must have occured after Earlier in the timeline (default means now)
Timer :: tms(),
Delta :: integer().
Delta :: invalid | integer().
delta (default,L,T) ->
delta("*start*",L,T);
delta (E,default,T) ->
delta(E,"now",T);
delta (default,default,T) ->
delta("start","now",T);
delta (E,L,T) ->
{ok, T2, R} = get_stamp(L,T#tms.stamps),
{ok, T1, _} = get_stamp(E,R),
erlang:convert_time_unit(T2 - T1, native, T#tms.resolution).
case get_stamp(L,T#tms.stamps) of
{ok, T2, R} ->
case get_stamp(E,R) of
{ok, T1, _} ->
erlang:convert_time_unit(T2 - T1, native, T#tms.resolution);
_ ->
invalid
end;
_ ->
invalid
end.
@@ -154,6 +175,9 @@ fmt_duration (Delta,Timer) ->
get_stamp (_,[]) ->
invalid;
get_stamp ("now",T) ->
{ok, os:system_time(), T};
get_stamp (Stamp,[{Stamp,Value}|T]) ->
{ok, Value, T};