From a1fe76fb5ba506f23382a1c219e9dab1fd88f68a Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 27 May 2009 17:29:43 +0000 Subject: [PATCH] Support to select what modules to update. Split large function in smaller ones. SVN Revision: 2109 --- src/ejabberd_update.erl | 111 +++++++++++++++++++++++---------- src/web/ejabberd_web_admin.erl | 71 +++++++++++++++++++-- 2 files changed, 145 insertions(+), 37 deletions(-) diff --git a/src/ejabberd_update.erl b/src/ejabberd_update.erl index 7d5ae36c9..de2333558 100644 --- a/src/ejabberd_update.erl +++ b/src/ejabberd_update.erl @@ -28,13 +28,15 @@ -author('alexey@process-one.net'). %% API --export([update/0, update_info/0]). +-export([update/0, update/1, update_info/0]). -include("ejabberd.hrl"). %%==================================================================== %% API %%==================================================================== + +%% Update all the modified modules update() -> case update_info() of {ok, Dir, _UpdatedBeams, _Script, LowLevelScript, _Check} -> @@ -48,48 +50,91 @@ update() -> {error, Reason} end. -update_info() -> - Dir = filename:dirname(code:which(ejabberd)), - case file:list_dir(Dir) of - {ok, Files} -> - Beams = [list_to_atom(filename:rootname(FN)) || - FN <- Files, lists:suffix(".beam", FN)], - UpdatedBeams = - lists:filter( - fun(Module) -> - {ok, {Module, NewVsn}} = - beam_lib:version(code:which(Module)), - case code:is_loaded(Module) of - {file, _} -> - Attrs = Module:module_info(attributes), - {value, {vsn, CurVsn}} = - lists:keysearch(vsn, 1, Attrs), - NewVsn /= CurVsn; - false -> - false - end - end, Beams), - ?INFO_MSG("beam files: ~p~n", [UpdatedBeams]), - Script = make_script(UpdatedBeams), - ?INFO_MSG("script: ~p~n", [Script]), - LowLevelScript = make_low_level_script(UpdatedBeams, Script), - ?INFO_MSG("low level script: ~p~n", [LowLevelScript]), - Check = - release_handler_1:check_script( - LowLevelScript, +%% Update only the specified modules +update(ModulesToUpdate) -> + case update_info() of + {ok, Dir, UpdatedBeamsAll, _Script, _LowLevelScript, _Check} -> + UpdatedBeamsNow = + [A || A <- UpdatedBeamsAll, B <- ModulesToUpdate, A == B], + {_, LowLevelScript, _} = build_script(Dir, UpdatedBeamsNow), + Eval = + release_handler_1:eval_script( + LowLevelScript, [], [{ejabberd, "", filename:join(Dir, "..")}]), - ?INFO_MSG("check: ~p~n", [Check]), - {ok, Dir, UpdatedBeams, Script, LowLevelScript, Check}; + ?INFO_MSG("eval: ~p~n", [Eval]), + Eval; {error, Reason} -> {error, Reason} end. +%% Get information about the modified modules +update_info() -> + Dir = filename:dirname(code:which(ejabberd)), + case file:list_dir(Dir) of + {ok, Files} -> + update_info(Dir, Files); + {error, Reason} -> + {error, Reason} + end. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -%% From systools.hrl +update_info(Dir, Files) -> + Beams = lists:sort(get_beams(Files)), + UpdatedBeams = get_updated_beams(Beams), + ?INFO_MSG("beam files: ~p~n", [UpdatedBeams]), + {Script, LowLevelScript, Check} = build_script(Dir, UpdatedBeams), + {ok, Dir, UpdatedBeams, Script, LowLevelScript, Check}. + +get_beams(Files) -> + [list_to_atom(filename:rootname(FN)) + || FN <- Files, lists:suffix(".beam", FN)]. + +%% Return only the beams that have different version +get_updated_beams(Beams) -> + lists:filter( + fun(Module) -> + NewVsn = get_new_version(Module), + case code:is_loaded(Module) of + {file, _} -> + CurVsn = get_current_version(Module), + (NewVsn /= CurVsn + andalso NewVsn /= unknown_version); + false -> + false + end + end, Beams). + +get_new_version(Module) -> + Path = code:which(Module), + VersionRes = beam_lib:version(Path), + case VersionRes of + {ok, {Module, NewVsn}} -> NewVsn; + %% If a m1.erl has -module("m2"): + _ -> unknown_version + end. + +get_current_version(Module) -> + Attrs = Module:module_info(attributes), + {value, {vsn, CurVsn}} = lists:keysearch(vsn, 1, Attrs), + CurVsn. + +%% @spec(Dir::string(), UpdatedBeams::[atom()]) -> {Script,LowLevelScript,Check} +build_script(Dir, UpdatedBeams) -> + Script = make_script(UpdatedBeams), + ?INFO_MSG("script: ~p~n", [Script]), + LowLevelScript = make_low_level_script(UpdatedBeams, Script), + ?INFO_MSG("low level script: ~p~n", [LowLevelScript]), + Check = + release_handler_1:check_script( + LowLevelScript, + [{ejabberd, "", filename:join(Dir, "..")}]), + ?INFO_MSG("check: ~p~n", [Check]), + {Script, LowLevelScript, Check}. + +%% Copied from Erlang/OTP file: lib/sasl/src/systools.hrl -record(application, {name, %% Name of the application, atom(). type = permanent, %% Application start type, atom(). diff --git a/src/web/ejabberd_web_admin.erl b/src/web/ejabberd_web_admin.erl index 484c9b393..77b69b933 100644 --- a/src/web/ejabberd_web_admin.erl +++ b/src/web/ejabberd_web_admin.erl @@ -39,6 +39,12 @@ -include("ejabberd_http.hrl"). -include("ejabberd_web_admin.hrl"). +-define(INPUTATTRS(Type, Name, Value, Attrs), + ?XA("input", Attrs ++ + [?XMLATTR('type', Type), + ?XMLATTR('name', Name), + ?XMLATTR('value', Value)])). + process(["doc", LocalFile], _Request) -> DocPath = case os:getenv("EJABBERD_DOC_PATH") of @@ -149,6 +155,12 @@ make_xhtml(Els, Host, Node, Lang) -> #xmlel{ns = ?NS_XHTML, name = 'meta', attrs = [ ?XMLATTR('http-equiv', <<"Content-Type">>), ?XMLATTR('content', <<"text/html; charset=utf-8">>)]}, + #xmlel{ns = ?NS_XHTML, name = 'script', + %% This children is to ensure exmpp puts: + children = [?C(".")], + attrs = [ + ?XMLATTR('src', Base ++ "additions.js"), + ?XMLATTR('type', <<"text/javascript">>)]}, #xmlel{ns = ?NS_XHTML, name = 'link', attrs = [ ?XMLATTR('href', Base ++ "favicon.ico"), ?XMLATTR('type', <<"image/x-icon">>), @@ -190,6 +202,24 @@ get_base_path(Host, cluster) -> "/admin/server/" ++ Host ++ "/"; get_base_path(global, Node) -> "/admin/node/" ++ atom_to_list(Node) ++ "/"; get_base_path(Host, Node) -> "/admin/server/" ++ Host ++ "/node/" ++ atom_to_list(Node) ++ "/". +additions_js() -> +" +function selectAll() { + for(i=0;i Base = get_base_path(Host, cluster), " @@ -525,6 +555,10 @@ h3 { padding-left: 10px; } +#content ul.noliststyle > li { + list-style-type: none; +} + #content li.big { font-size: 10pt; } @@ -646,6 +680,9 @@ process_admin(_Host, #request{path = ["logo.png"]}) -> process_admin(_Host, #request{path = ["logo-fill.png"]}) -> {200, [{"Content-Type", "image/png"}, last_modified(), cache_control_public()], logo_fill()}; +process_admin(_Host, #request{path = ["additions.js"]}) -> + {200, [{"Content-Type", "text/javascript"}, last_modified(), cache_control_public()], additions_js()}; + process_admin(Host, #request{path = ["acls-raw"], q = Query, @@ -1953,9 +1990,32 @@ get_node(global, Node, ["update"], Query, Lang) -> [] -> ?CT("None"); _ -> - ?XE('ul', - [?LI([?C(atom_to_list(Beam))]) || - Beam <- UpdatedBeams]) + BeamsLis = + lists:map( + fun(Beam) -> + BeamString = atom_to_list(Beam), + ?LI([ + ?INPUT("checkbox", "selected", BeamString), + %% If we want checkboxes selected by default: + %%?XA("input", [{"checked", ""}, + %% {"type", "checkbox"}, + %% {"name", "selected"}, + %% {"value", BeamString}]), + ?C(BeamString)]) + end, + UpdatedBeams), + SelectButtons = + [?BR, + ?INPUTATTRS(<<"button">>, <<"selectall">>, + <<"Select All">>, + [?XMLATTR('onClick', <<"selectAll()">>)]), + ?C(" "), + ?INPUTATTRS(<<"button">>, <<"unselectall">>, + <<"Unselect All">>, + [?XMLATTR('onClick', <<"unSelectAll()">>)])], + %%?XE("ul", BeamsLis) + ?XAE('ul', [?XMLATTR('class', <<"noliststyle">>)], + BeamsLis ++ SelectButtons) end, FmtScript = ?XC('pre', io_lib:format("~p", [Script])), FmtLowLevelScript = ?XC('pre', io_lib:format("~p", [LowLevelScript])), @@ -1972,6 +2032,7 @@ get_node(global, Node, ["update"], Query, Lang) -> ?XCT('h3', "Update script"), FmtScript, ?XCT('h3', "Low level update script"), FmtLowLevelScript, ?XCT('h3', "Script check"), ?XC("pre", atom_to_list(Check)), + ?BR, ?INPUTT("submit", "update", "Update") ]) ]; @@ -2294,7 +2355,9 @@ node_modules_parse_query(Host, Node, Modules, Query) -> node_update_parse_query(Node, Query) -> case lists:keysearch("update", 1, Query) of {value, _} -> - case rpc:call(Node, ejabberd_update, update, []) of + ModulesToUpdateStrings = proplists:get_all_values("selected",Query), + ModulesToUpdate = [list_to_atom(M) || M <- ModulesToUpdateStrings], + case rpc:call(Node, ejabberd_update, update, [ModulesToUpdate]) of {ok, _} -> ok; {error, Error} ->