From 70e12d910b111a6d5284877a576fe5c59fa4071d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rgen?= Date: Tue, 17 Apr 2018 11:15:12 +0200 Subject: [PATCH] Alter the web interface of the CRE to provide status.json and cache.json --- src/cre.erl | 2 +- src/cre_cache_handler.erl | 6 +- src/cre_html.erl | 170 ------------------------------------- src/cre_index_handler.erl | 170 ------------------------------------- src/cre_master.erl | 133 ++++++++++------------------- src/cre_status_handler.erl | 66 ++++++++++++++ 6 files changed, 113 insertions(+), 434 deletions(-) delete mode 100644 src/cre_html.erl delete mode 100644 src/cre_index_handler.erl create mode 100644 src/cre_status_handler.erl diff --git a/src/cre.erl b/src/cre.erl index 27edf2d..ccff2be 100644 --- a/src/cre.erl +++ b/src/cre.erl @@ -92,7 +92,7 @@ start( _Type, _Args ) -> Dispatch = cowboy_router:compile( [{'_', [ - {"/[index.html]", cre_index_handler, []}, + {"/[status.json]", cre_status_handler, []}, {"/cache.json", cre_cache_handler, []} ]}] ), diff --git a/src/cre_cache_handler.erl b/src/cre_cache_handler.erl index fc91858..bddfa30 100644 --- a/src/cre_cache_handler.erl +++ b/src/cre_cache_handler.erl @@ -38,10 +38,8 @@ init( Req0, State ) -> - PairLst = cre_master:cache( cre_master ), - Cache = [#{ request => A, reply => Delta } || {A, Delta} <- PairLst], - - Doc = jsone:encode( #{ cache => Cache } ), + CacheMap = cre_master:get_cache( cre_master ), + Doc = jsone:encode( CacheMap ), Reply = cowboy_req:reply( diff --git a/src/cre_html.erl b/src/cre_html.erl deleted file mode 100644 index ca910d3..0000000 --- a/src/cre_html.erl +++ /dev/null @@ -1,170 +0,0 @@ -%% -*- erlang -*- -%% -%% Common runtime environment for distributed programming languages -%% -%% Copyright 2015-2018 Jörgen Brandt -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% ------------------------------------------------------------------- -%% @author Jörgen Brandt -%% @version 0.1.5 -%% @copyright 2015-2018 Jörgen Brandt -%% -%% -%% -%% -%% -%% @end -%% ------------------------------------------------------------------- --module( cre_html ). - --export( [html/2, head/1, body/1, section/2, subsection/2, p/1, a/2, - monospace/1, table/2, table/1, title/1, table_header/1, table_row/1, - progress_span/2] ). - --spec html( Head :: binary(), Body :: binary() ) -> binary(). - -html( Head, Body ) -when is_binary( Head ), - is_binary( Body ) -> - <<"\n", Head/binary, "\n", Body/binary, "">>. - - --spec head( Title :: string() ) -> binary(). - -head( Title ) when is_list( Title ) -> - B = list_to_binary( Title ), - <<"\n", B/binary, "\n\n">>. - - --spec body( BodyLst :: [binary()] ) -> binary(). - -body( BodyLst ) when is_list( BodyLst ) -> - - F = - fun( Element, Acc ) -> - <> - end, - - - B = lists:foldl( F, <<>>, BodyLst ), - - <<"\n", B/binary, "\n">>. - - --spec section( Title :: string(), Body :: binary() ) -> binary(). - -section( Title, Body ) -when is_list( Title ), - is_binary( Body ) -> - B = list_to_binary( Title ), - <<"
\n

", B/binary, "

\n", Body/binary, "
\n">>. - - --spec p( Content :: binary() ) -> binary(). - -p( Content ) when is_binary( Content ) -> - <<"

\n", Content/binary, "

\n">>. - - --spec progress_span( Ratio :: float(), Width :: pos_integer() ) -> binary(). - -progress_span( Ratio, Width ) -when is_float( Ratio ), Ratio >= 0, Ratio =< 1, - is_integer( Width ), Width > 0 -> - - NBar = trunc( Ratio*Width ), - NSpace = Width-NBar, - - B1 = list_to_binary( lists:duplicate( NBar, $| ) ), - B2 = list_to_binary( lists:duplicate( NSpace, " " ) ), - - <<"[", - "", - B1/binary, - "", - B2/binary, - "]">>. - - --spec title( Title :: string() ) -> binary(). - -title( Title ) when is_list( Title ) -> - B = list_to_binary( Title ), - <<"

", B/binary, "

\n">>. - - --spec table_header( HeaderLst :: [string()] ) -> binary(). - -table_header( HeaderLst ) -when is_list( HeaderLst ) -> - - F = - fun( Header, Acc ) -> - B = list_to_binary( Header ), - <", B/binary, "">> - end, - - Tr = lists:foldl( F, <<>>, HeaderLst ), - - <<"", Tr/binary, "\n">>. - - --spec table_row( DataLst :: [binary()] ) -> binary(). - -table_row( DataLst ) when is_list( DataLst ) -> - - F = - fun( Data, Acc ) -> - <", Data/binary, "">> - end, - - Tr = lists:foldl( F, <<>>, DataLst ), - - <<"", Tr/binary, "\n">>. - - --spec table( RowLst :: [binary()] ) -> binary(). - -table( RowLst ) when is_list( RowLst ) -> - - B = lists:foldl( fun( E, A ) -> <> end, <<>>, RowLst ), - - <<"\n", B/binary, "
">>. - - --spec table( Header :: binary(), RowLst :: [binary()] ) -> binary(). - -table( Header, RowLst ) -when is_binary( Header ), - is_list( RowLst ) -> - - B = lists:foldl( fun( E, A ) -> <
> end, <<>>, RowLst ), - - <<"\n", Header/binary, B/binary, "
">>. - -monospace( Content ) -> - <<"", - Content/binary, - "\n">>. - - -a( Href, Body ) when is_binary( Href ), is_binary( Body ) -> - <<"
", Body/binary, "">>. - -subsection( Title, Body ) -when is_list( Title ), - is_binary( Body ) -> - B = list_to_binary( Title ), - <<"

", B/binary, "

\n", Body/binary>>. \ No newline at end of file diff --git a/src/cre_index_handler.erl b/src/cre_index_handler.erl deleted file mode 100644 index 2e8a1f2..0000000 --- a/src/cre_index_handler.erl +++ /dev/null @@ -1,170 +0,0 @@ -%% -*- erlang -*- -%% -%% Common runtime environment for distributed programming languages -%% -%% Copyright 2015-2018 Jörgen Brandt -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% ------------------------------------------------------------------- -%% @author Jörgen Brandt -%% @version 0.1.5 -%% @copyright 2015-2018 Jörgen Brandt -%% -%% -%% -%% -%% -%% @end -%% ------------------------------------------------------------------- --module( cre_index_handler ). --behavior( cowboy_handler ). - - -%%==================================================================== -%% Exports -%%==================================================================== - --export( [init/2] ). - - -%%==================================================================== -%% Includes -%%==================================================================== - --include( "cre.hrl" ). - - -%%==================================================================== -%% Cowboy handler callback functions -%%==================================================================== - -init( Req0, State ) -> - - % cre info - - #{ total_load := TotalLoad, - n_wrk := NWrk } = cre_master:cre_info( cre_master ), - - CreInfo = - cre_html:table( [ - cre_html:table_row( [<<"Version:">>, <>] ), - cre_html:table_row( [<<"CRE node:">>, cre_html:monospace( atom_to_binary( node(), utf8 ) )] ), - cre_html:table_row( [<<"Total worker slots:">>, integer_to_binary( NWrk )] )] ), - - % cre load - - CreLoad = cre_html:p( cre_html:progress_span( TotalLoad, 64 ) ), - - % node table - - NodeInfoLst = cre_master:node_info( cre_master ), - - F = - fun( NodeInfo ) -> - #{ node := Node, n_wrk := NWrk, node_load := NodeLoad } = NodeInfo, - B1 = cre_html:monospace( atom_to_binary( Node, utf8 ) ), - B2 = integer_to_binary( NWrk ), - B3 = cre_html:progress_span( NodeLoad, 16 ), - cre_html:table_row( [B1, B2, B3] ) - end, - - NodeRowLst = [F( WrkInfo ) || WrkInfo <- NodeInfoLst], - NodeHeader = cre_html:table_header( ["Worker node", "Worker slots", "Load"] ), - - NodeTable = cre_html:table( NodeHeader, NodeRowLst ), - - % queue table - - #{ queued_lst := QueuedLst, - active_lst := ActiveLst, - complete_lst := CompleteLst } = cre_master:app_info( cre_master ), - - - G = - fun( T ) -> - #{ app_id := AppId, lambda_name := LambdaName } = T, - Skip = byte_size( AppId )-7, - <<_:Skip/binary, B/binary>> = AppId, - cre_html:table_row( [cre_html:monospace( B ), cre_html:monospace( LambdaName )] ) - end, - - QueuedRowLst = [G( T ) || T <- QueuedLst], - QueuedHeader = cre_html:table_header( ["App id", "Lambda name"] ), - - QueuedTable = cre_html:table( QueuedHeader, QueuedRowLst ), - QueuedSubsection = cre_html:subsection( "Queued", QueuedTable ), - - % active table - - H = - fun( T ) -> - #{ app_id := AppId, lambda_name := LambdaName, node := Node } = T, - Skip = byte_size( AppId )-7, - <<_:Skip/binary, B/binary>> = AppId, - cre_html:table_row( [cre_html:monospace( B ), cre_html:monospace( LambdaName ), cre_html:monospace( Node )] ) - end, - - ActiveRowLst = [H( T ) || T <- ActiveLst], - ActiveHeader = cre_html:table_header( ["App id", "Lambda name", "Worker node"] ), - - ActiveTable = cre_html:table( ActiveHeader, ActiveRowLst ), - ActiveSubsection = cre_html:subsection( "Active", ActiveTable ), - - - % complete table - - I = - fun( T ) -> - #{ app_id := AppId, lambda_name := LambdaName, node := Node, status := Status } = T, - Skip = byte_size( AppId )-7, - <<_:Skip/binary, B/binary>> = AppId, - cre_html:table_row( [cre_html:monospace( B ), - cre_html:monospace( LambdaName ), - cre_html:monospace( Node ), - cre_html:monospace( Status )] ) - end, - - CompleteHeader = cre_html:table_header( ["App id", "Lambda name", "Worker node", "Status"] ), - CompleteRowLst = [I( T ) || T <- CompleteLst], - CompleteTable = cre_html:table( CompleteHeader, CompleteRowLst ), - CompleteSubsection = cre_html:subsection( "Complete", CompleteTable ), - - Title = cre_html:title( "CRE status" ), - NodeSection = cre_html:section( "Worker nodes", NodeTable ), - TaskSection = cre_html:section( "Applications", <> ), - - Head = cre_html:head( "CRE status" ), - - Body = - cre_html:body( - [Title, - CreInfo, - CreLoad, - NodeSection, - TaskSection] ), - - Page = cre_html:html( Head, Body ), - - Reply = - cowboy_req:reply( - 200, - #{ <<"content-type">> => <<"text/html">>}, - Page, - Req0 ), - - {ok, Reply, State}. - - diff --git a/src/cre_master.erl b/src/cre_master.erl index 6c15067..95ab44d 100644 --- a/src/cre_master.erl +++ b/src/cre_master.erl @@ -37,8 +37,7 @@ %% API functions -export( [start_link/0, start_link/1, add_worker/2, worker_result/4, - cre_request/4, stop/1, cre_info/1, node_info/1, app_info/1, - cache/1] ). + cre_request/4, stop/1, get_status/1, get_cache/1] ). %% gen_server callback functions -export( [code_change/3, handle_call/3, handle_cast/2, handle_info/2, init/1, @@ -121,40 +120,34 @@ stop( CreName ) -> gen_server:stop( CreName ). --spec cre_info( CreName :: _ ) -> - #{ total_load => float(), - n_wrk => non_neg_integer() }. -cre_info( CreName ) -> - {ok, Info} = gen_server:call( CreName, cre_info ), +-spec get_status( CreName :: _ ) -> + #{ cre_info => #{ load => float(), + n_wrk => non_neg_integer() }, + node_info => [#{ node => binary(), + load => float(), + n_wrk => pos_integer() }], + app_info => #{ queued => [#{ app_id => binary(), + lambda_name => binary() }], + active => [#{ app_id => binary(), + lambda_name => binary(), + node => binary() }], + complete => [#{ app_id => binary(), + lambda_name => binary(), + node => binary(), + status => binary() }] } }. + +get_status( CreName ) -> + {ok, Info} = gen_server:call( CreName, get_status ), Info. --spec node_info( CreName :: _ ) -> - [#{ node => binary(), - n_wrk => pos_integer(), - node_load => float() }]. +-spec get_cache( CreName :: _ ) -> + #{ cache => [#{ app => _, delta => _ }] }. -node_info( CreName ) -> - {ok, Info} = gen_server:call( CreName, node_info ), - Info. - - --spec app_info( CreName :: _ ) -> - #{ queued_lst => [#{ atom() => _ }], - active_lst => [#{ atom() => _ }], - complete_lst => [#{ atom() => _ }] }. - -app_info( CreName ) -> - {ok, Info} = gen_server:call( CreName, app_info ), - Info. - - --spec cache( CreName :: _ ) -> [{_, _}]. - -cache( CreName ) -> - {ok, PairLst} = gen_server:call( CreName, cache ), - PairLst. +get_cache( CreName ) -> + {ok, CacheMap} = gen_server:call( CreName, get_cache ), + CacheMap. %%==================================================================== @@ -185,6 +178,7 @@ handle_cast( {add_worker, P}, CreState ) -> {application, cre}, {cre_master_pid, self()}, {worker_pid, Pid}, + {worker_node, node( Pid )}, {nworker, length( IdleLst )+maps:size( BusyMap )+1}] ), @@ -332,10 +326,12 @@ handle_info( _Info, CreState ) -> {noreply, CreState}. -handle_call( cre_info, _From, CreState ) -> +handle_call( get_status, _From, CreState ) -> #cre_state{ idle_lst = IdleLst, - busy_map = BusyMap } = CreState, + busy_map = BusyMap, + cache = Cache, + queue = Queue } = CreState, NIdle = length( IdleLst ), NBusy = maps:size( BusyMap ), @@ -347,14 +343,8 @@ handle_call( cre_info, _From, CreState ) -> _ -> NBusy/N end, - CreInfoMap = #{ total_load => Ratio, n_wrk => N}, + CreInfoMap = #{ load => Ratio, n_wrk => N }, - {reply, {ok, CreInfoMap}, CreState}; - -handle_call( node_info, _From, CreState ) -> - - #cre_state{ idle_lst = IdleLst, - busy_map = BusyMap } = CreState, PidLst = IdleLst++maps:values( BusyMap ), @@ -373,22 +363,15 @@ handle_call( node_info, _From, CreState ) -> end, G = - fun( Node, L ) -> - NWrk = length( L ), - NBusy = length( lists:filter( IsBusy, L ) ), + fun( Node, PLst ) -> + NWrk = length( PLst ), + NBusy = length( lists:filter( IsBusy, PLst ) ), NodeLoad = NBusy/NWrk, - #{ node => Node, n_wrk => NWrk, node_load => NodeLoad } + #{ node => Node, n_wrk => NWrk, load => NodeLoad } end, NodeInfoLst = maps:values( maps:map( G, NodeWrkMap ) ), - {reply, {ok, NodeInfoLst}, CreState}; - -handle_call( app_info, _From, CreState ) -> - - #cre_state{ cache = Cache, - busy_map = BusyMap, - queue = Queue } = CreState, BinaryToHexString = fun( X ) when is_binary( X ) -> @@ -443,52 +426,24 @@ handle_call( app_info, _From, CreState ) -> ActiveLst = [FormatActiveTask( App, Pid ) || {App, Pid} <- maps:to_list( BusyMap )], CompleteLst = [FormatCompleteTask( App ) || App <- maps:keys( Cache )], - SortFormattedTask = - fun( M1, M2 ) -> - #{ app_id := AppId1, lambda_name := LambdaName1 } = M1, - #{ app_id := AppId2, lambda_name := LambdaName2 } = M2, - if - LambdaName1 =/= LambdaName2 -> LambdaName1 =< LambdaName2; - true -> AppId1 =< AppId2 - end - end, - - SortFormattedActiveTask = - fun( M1, M2 ) -> - - #{ app_id := AppId1, lambda_name := LambdaName1, node := Node1 } = M1, - #{ app_id := AppId2, lambda_name := LambdaName2, node := Node2 } = M2, - - if - Node1 =/= Node2 -> - Node1 =< Node2; + AppInfoMap = #{ queued => QueuedLst, + active => ActiveLst, + complete => CompleteLst }, - true -> - if - - LambdaName1 =/= LambdaName2 -> - LambdaName1 =< LambdaName2; - - true -> - AppId1 =< AppId2 - - end + StatusMap = #{ cre_info => CreInfoMap, + node_info => NodeInfoLst, + app_info => AppInfoMap }, - end - - end, + {reply, {ok, StatusMap}, CreState}; - TaskInfoMap = #{ queued_lst => lists:sort( SortFormattedTask, QueuedLst ), - active_lst => lists:sort( SortFormattedActiveTask, ActiveLst ), - complete_lst => lists:sort( SortFormattedTask, CompleteLst ) }, - {reply, {ok, TaskInfoMap}, CreState}; -handle_call( cache, _From, CreState ) -> +handle_call( get_cache, _From, CreState ) -> #cre_state{ cache = Cache } = CreState, - {reply, {ok, maps:to_list( Cache )}, CreState}; + CacheMap = #{ cache => [#{ app => A, delta => R } || {A, R} <- maps:to_list( Cache )] }, + {reply, {ok, CacheMap}, CreState}; handle_call( _Request, _From, CreState ) -> {reply, {error, bad_msg}, CreState}. diff --git a/src/cre_status_handler.erl b/src/cre_status_handler.erl new file mode 100644 index 0000000..5bfeab7 --- /dev/null +++ b/src/cre_status_handler.erl @@ -0,0 +1,66 @@ +%% -*- erlang -*- +%% +%% Common runtime environment for distributed programming languages +%% +%% Copyright 2015-2018 Jörgen Brandt +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% ------------------------------------------------------------------- +%% @author Jörgen Brandt +%% @version 0.1.5 +%% @copyright 2015-2018 Jörgen Brandt +%% +%% +%% +%% +%% +%% @end +%% ------------------------------------------------------------------- +-module( cre_status_handler ). +-behavior( cowboy_handler ). + + +%%==================================================================== +%% Exports +%%==================================================================== + +-export( [init/2] ). + + +%%==================================================================== +%% Includes +%%==================================================================== + +-include( "cre.hrl" ). + + +%%==================================================================== +%% Cowboy handler callback functions +%%==================================================================== + +init( Req0, State ) -> + + StatusMap = cre_master:get_status( cre_master ), + Doc = jsone:encode( StatusMap ), + + Reply = + cowboy_req:reply( + 200, + #{ <<"content-type">> => <<"application/json">>}, + Doc, + Req0 ), + + {ok, Reply, State}. + +