Browse Source

chore(emqx_management): abstract data backup

zhanghongtong 5 years ago
parent
commit
033f8619eb

+ 0 - 408
lib-ce/emqx_management/src/emqx_mgmt.erl

@@ -36,15 +36,6 @@
 %% Metrics and Stats
 -export([ get_metrics/0
         , get_metrics/1
-        , get_all_topic_metrics/0
-        , get_topic_metrics/1
-        , get_topic_metrics/2
-        , register_topic_metrics/1
-        , register_topic_metrics/2
-        , unregister_topic_metrics/1
-        , unregister_topic_metrics/2
-        , unregister_all_topic_metrics/0
-        , unregister_all_topic_metrics/1
         , get_stats/0
         , get_stats/1
         ]).
@@ -91,14 +82,6 @@
         , reload_plugin/2
         ]).
 
-%% Modules
--export([ list_modules/0
-        , list_modules/1
-        , load_module/2
-        , unload_module/2
-        , reload_module/2
-        ]).
-
 %% Listeners
 -export([ list_listeners/0
         , list_listeners/1
@@ -118,26 +101,6 @@
         , delete_banned/1
         ]).
 
-%% Export/Import
--export([ export_rules/0
-        , export_resources/0
-        , export_blacklist/0
-        , export_applications/0
-        , export_users/0
-        , export_auth_mnesia/0
-        , export_acl_mnesia/0
-        , import_rules/1
-        , import_resources/1
-        , import_resources_and_rules/3
-        , import_blacklist/1
-        , import_applications/1
-        , import_users/1
-        , import_auth_clientid/1 %% BACKW: 4.1.x
-        , import_auth_username/1 %% BACKW: 4.1.x
-        , import_auth_mnesia/2
-        , import_acl_mnesia/2
-        , to_version/1
-        ]).
 
 -export([ enable_telemetry/0
         , disable_telemetry/0
@@ -217,73 +180,6 @@ get_metrics(Node) when Node =:= node() ->
 get_metrics(Node) ->
     rpc_call(Node, get_metrics, [Node]).
 
-get_all_topic_metrics() ->
-    lists:foldl(fun(Topic, Acc) ->
-                    case get_topic_metrics(Topic) of
-                        {error, _Reason} ->
-                            Acc;
-                        Metrics ->
-                            [#{topic => Topic, metrics => Metrics} | Acc]
-                    end
-                end, [], emqx_mod_topic_metrics:all_registered_topics()).
-
-get_topic_metrics(Topic) ->
-    lists:foldl(fun(Node, Acc) ->
-                    case get_topic_metrics(Node, Topic) of
-                        {error, _Reason} ->
-                            Acc;
-                        Metrics ->
-                            case Acc of
-                                [] -> Metrics;
-                                _ ->
-                                    lists:foldl(fun({K, V}, Acc0) ->
-                                                    [{K, V + proplists:get_value(K, Metrics, 0)} | Acc0]
-                                                end, [], Acc)
-                            end
-                    end
-                end, [], ekka_mnesia:running_nodes()).
-
-get_topic_metrics(Node, Topic) when Node =:= node() ->
-    emqx_mod_topic_metrics:metrics(Topic);
-get_topic_metrics(Node, Topic) ->
-    rpc_call(Node, get_topic_metrics, [Node, Topic]).
-
-register_topic_metrics(Topic) ->
-    Results = [register_topic_metrics(Node, Topic) || Node <- ekka_mnesia:running_nodes()],
-    case lists:any(fun(Item) -> Item =:= ok end, Results) of
-        true  -> ok;
-        false -> lists:last(Results)
-    end.
-
-register_topic_metrics(Node, Topic) when Node =:= node() ->
-    emqx_mod_topic_metrics:register(Topic);
-register_topic_metrics(Node, Topic) ->
-    rpc_call(Node, register_topic_metrics, [Node, Topic]).
-
-unregister_topic_metrics(Topic) ->
-    Results = [unregister_topic_metrics(Node, Topic) || Node <- ekka_mnesia:running_nodes()],
-    case lists:any(fun(Item) -> Item =:= ok end, Results) of
-        true  -> ok;
-        false -> lists:last(Results)
-    end.
-
-unregister_topic_metrics(Node, Topic) when Node =:= node() ->
-    emqx_mod_topic_metrics:unregister(Topic);
-unregister_topic_metrics(Node, Topic) ->
-    rpc_call(Node, unregister_topic_metrics, [Node, Topic]).
-
-unregister_all_topic_metrics() ->
-    Results = [unregister_all_topic_metrics(Node) || Node <- ekka_mnesia:running_nodes()],
-    case lists:any(fun(Item) -> Item =:= ok end, Results) of
-        true  -> ok;
-        false -> lists:last(Results)
-    end.
-
-unregister_all_topic_metrics(Node) when Node =:= node() ->
-    emqx_mod_topic_metrics:unregister_all();
-unregister_all_topic_metrics(Node) ->
-    rpc_call(Node, unregister_topic_metrics, [Node]).
-
 get_stats() ->
     [{Node, get_stats(Node)} || Node <- ekka_mnesia:running_nodes()].
 
@@ -504,33 +400,6 @@ reload_plugin(Node, Plugin) when Node =:= node() ->
 reload_plugin(Node, Plugin) ->
     rpc_call(Node, reload_plugin, [Node, Plugin]).
 
-
-%%--------------------------------------------------------------------
-%% Modules
-%%--------------------------------------------------------------------
-
-list_modules() ->
-    [{Node, list_modules(Node)} || Node <- ekka_mnesia:running_nodes()].
-
-list_modules(Node) when Node =:= node() ->
-    emqx_modules:list();
-list_modules(Node) ->
-    rpc_call(Node, list_modules, [Node]).
-
-load_module(Node, Module) when Node =:= node() ->
-    emqx_modules:load(Module);
-load_module(Node, Module) ->
-    rpc_call(Node, load_module, [Node, Module]).
-
-unload_module(Node, Module) when Node =:= node() ->
-    emqx_modules:unload(Module);
-unload_module(Node, Module) ->
-    rpc_call(Node, unload_module, [Node, Module]).
-
-reload_module(Node, Module) when Node =:= node() ->
-    emqx_modules:reload(Module);
-reload_module(Node, Module) ->
-    rpc_call(Node, reload_module, [Node, Module]).
 %%--------------------------------------------------------------------
 %% Listeners
 %%--------------------------------------------------------------------
@@ -613,282 +482,6 @@ create_banned(Banned) ->
 delete_banned(Who) ->
     emqx_banned:delete(Who).
 
-%%--------------------------------------------------------------------
-%% Data Export and Import
-%%--------------------------------------------------------------------
-
-export_rules() ->
-    lists:map(fun({_, RuleId, _, RawSQL, _, _, _, _, _, _, Actions, Enabled, Desc}) ->
-                   [{id, RuleId},
-                     {rawsql, RawSQL},
-                     {actions, actions_to_prop_list(Actions)},
-                     {enabled, Enabled},
-                     {description, Desc}]
-              end, emqx_rule_registry:get_rules()).
-
-export_resources() ->
-    lists:map(fun({_, Id, Type, Config, CreatedAt, Desc}) ->
-                   NCreatedAt = case CreatedAt of
-                                    undefined -> null;
-                                    _ -> CreatedAt
-                                end,
-                   [{id, Id},
-                    {type, Type},
-                    {config, maps:to_list(Config)},
-                    {created_at, NCreatedAt},
-                    {description, Desc}]
-              end, emqx_rule_registry:get_resources()).
-
-export_blacklist() ->
-    lists:map(fun(#banned{who = Who, by = By, reason = Reason, at = At, until = Until}) ->
-                  NWho = case Who of
-                             {peerhost, Peerhost} -> {peerhost, inet:ntoa(Peerhost)};
-                             _ -> Who
-                         end,
-                  [{who, [NWho]}, {by, By}, {reason, Reason}, {at, At}, {until, Until}]
-              end, ets:tab2list(emqx_banned)).
-
-export_applications() ->
-    lists:map(fun({_, AppID, AppSecret, Name, Desc, Status, Expired}) ->
-                  [{id, AppID}, {secret, AppSecret}, {name, Name}, {desc, Desc}, {status, Status}, {expired, Expired}]
-              end, ets:tab2list(mqtt_app)).
-
-export_users() ->
-    lists:map(fun({_, Username, Password, Tags}) ->
-                  [{username, Username}, {password, base64:encode(Password)}, {tags, Tags}]
-              end, ets:tab2list(mqtt_admin)).
-
-export_auth_mnesia() ->
-    case ets:info(emqx_user) of
-        undefined -> [];
-        _ ->
-            lists:map(fun({_, {Type, Login}, Password, CreatedAt}) ->
-                          [{login, Login}, {type, Type}, {password, base64:encode(Password)}, {created_at, CreatedAt}]
-                      end, ets:tab2list(emqx_user))
-    end.
-
-export_acl_mnesia() ->
-    case ets:info(emqx_acl) of
-        undefined -> [];
-        _ ->
-            lists:map(fun({_, Filter, Action, Access, CreatedAt}) ->
-                          Filter1 = case Filter of
-                              {{Type, TypeValue}, Topic} ->
-                                  [{type, Type}, {type_value, TypeValue}, {topic, Topic}];
-                              {Type, Topic} ->
-                                  [{type, Type}, {topic, Topic}]
-                          end,
-                          Filter1 ++ [{action, Action}, {access, Access}, {created_at, CreatedAt}]
-                      end, ets:tab2list(emqx_acl))
-    end.
-
-import_rules(Rules) ->
-    lists:foreach(fun(Rule) ->
-                      import_rule(Rule)
-                  end, Rules).
-
-import_resources(Reources) ->
-    lists:foreach(fun(Resource) ->
-                      import_resource(Resource)
-                  end, Reources).
-
-import_rule(#{<<"id">> := RuleId,
-              <<"rawsql">> := RawSQL,
-              <<"actions">> := Actions,
-              <<"enabled">> := Enabled,
-              <<"description">> := Desc}) ->
-    Rule = #{id => RuleId,
-             rawsql => RawSQL,
-             actions => map_to_actions(Actions),
-             enabled => Enabled,
-             description => Desc},
-    try emqx_rule_engine:create_rule(Rule)
-    catch throw:{resource_not_initialized, _ResId} ->
-        emqx_rule_engine:create_rule(Rule#{enabled => false})
-    end.
-
-import_resource(#{<<"id">> := Id,
-                  <<"type">> := Type,
-                  <<"config">> := Config,
-                  <<"created_at">> := CreatedAt,
-                  <<"description">> := Desc}) ->
-    NCreatedAt = case CreatedAt of
-                     null -> undefined;
-                     _ -> CreatedAt
-                 end,
-    emqx_rule_engine:create_resource(#{id => Id,
-                                       type => any_to_atom(Type),
-                                       config => Config,
-                                       created_at => NCreatedAt,
-                                       description => Desc}).
-
-import_resources_and_rules(Resources, Rules, FromVersion)
-  when FromVersion =:= "4.0" orelse FromVersion =:= "4.1" orelse FromVersion =:= "4.2" ->
-    Configs = lists:foldl(fun(#{<<"id">> := ID,
-                                <<"type">> := <<"web_hook">>,
-                                <<"config">> := #{<<"content_type">> := ContentType,
-                                                  <<"headers">> := Headers,
-                                                  <<"method">> := Method,
-                                                  <<"url">> := URL}} = Resource, Acc) ->
-                              NConfig = #{<<"connect_timeout">> => 5,
-                                          <<"request_timeout">> => 5,
-                                          <<"cacertfile">> => <<>>,
-                                          <<"certfile">> => <<>>,
-                                          <<"keyfile">> => <<>>,
-                                          <<"pool_size">> => 8,
-                                          <<"url">> => URL,
-                                          <<"verify">> => true},
-                              NResource = Resource#{<<"config">> := NConfig},
-                              import_resource(NResource),
-                              NHeaders = maps:put(<<"content-type">>, ContentType, Headers),
-                              [{ID, #{headers => NHeaders, method => Method}} | Acc];
-                             (Resource, Acc) ->
-                              import_resource(Resource),
-                              Acc
-                          end, [], Resources),
-    lists:foreach(fun(#{<<"actions">> := Actions} = Rule) ->
-                      NActions = apply_new_config(Actions, Configs),
-                      import_rule(Rule#{<<"actions">> := NActions})
-                  end, Rules);
-import_resources_and_rules(Resources, Rules, _FromVersion) ->
-    import_resources(Resources),
-    import_rules(Rules).
-
-apply_new_config(Actions, Configs) ->
-    apply_new_config(Actions, Configs, []).
-
-apply_new_config([], _Configs, Acc) ->
-    Acc;
-apply_new_config([Action = #{<<"name">> := <<"data_to_webserver">>,
-                             <<"args">> := #{<<"$resource">> := ID,
-                                            <<"path">> := Path,
-                                            <<"payload_tmpl">> := PayloadTmpl}} | More], Configs, Acc) ->
-    case proplists:get_value(ID, Configs, undefined) of
-        undefined ->
-            apply_new_config(More, Configs, [Action | Acc]);
-        #{headers := Headers, method := Method} ->
-            Args = #{<<"$resource">> => ID,
-                     <<"body">> => PayloadTmpl,
-                     <<"headers">> => Headers,
-                     <<"method">> => Method,
-                     <<"path">> => Path},
-            apply_new_config(More, Configs, [Action#{<<"args">> := Args} | Acc])
-    end.
-
-import_blacklist(Blacklist) ->
-    lists:foreach(fun(#{<<"who">> := Who,
-                        <<"by">> := By,
-                        <<"reason">> := Reason,
-                        <<"at">> := At,
-                        <<"until">> := Until}) ->
-                      NWho = case Who of
-                                 #{<<"peerhost">> := Peerhost} ->
-                                     {ok, NPeerhost} = inet:parse_address(Peerhost),
-                                     {peerhost, NPeerhost};
-                                 #{<<"clientid">> := ClientId} -> {clientid, ClientId};
-                                 #{<<"username">> := Username} -> {username, Username}
-                             end,
-                     emqx_banned:create(#banned{who = NWho, by = By, reason = Reason, at = At, until = Until})
-                  end, Blacklist).
-
-import_applications(Apps) ->
-    lists:foreach(fun(#{<<"id">> := AppID,
-                        <<"secret">> := AppSecret,
-                        <<"name">> := Name,
-                        <<"desc">> := Desc,
-                        <<"status">> := Status,
-                        <<"expired">> := Expired}) ->
-                      NExpired = case is_integer(Expired) of
-                                     true -> Expired;
-                                     false -> undefined
-                                 end,
-                      emqx_mgmt_auth:force_add_app(AppID, Name, AppSecret, Desc, Status, NExpired)
-                  end, Apps).
-
-import_users(Users) ->
-    lists:foreach(fun(#{<<"username">> := Username,
-                        <<"password">> := Password,
-                        <<"tags">> := Tags}) ->
-                      NPassword = base64:decode(Password),
-                      emqx_dashboard_admin:force_add_user(Username, NPassword, Tags)
-                  end, Users).
-
-import_auth_clientid(Lists) ->
-    case ets:info(emqx_user) of
-        undefined -> ok;
-        _ ->
-            [ mnesia:dirty_write({emqx_user, {clientid, Clientid}, base64:decode(Password), erlang:system_time(millisecond)})
-              || #{<<"clientid">> := Clientid, <<"password">> := Password} <- Lists ]
-    end.
-
-import_auth_username(Lists) ->
-    case ets:info(emqx_user) of
-        undefined -> ok;
-        _ ->
-            [ mnesia:dirty_write({emqx_user, {username, Username}, base64:decode(Password), erlang:system_time(millisecond)})
-              || #{<<"username">> := Username, <<"password">> := Password} <- Lists ]
-    end.
-
-import_auth_mnesia(Auths, FromVersion) when FromVersion =:= "4.0" orelse
-                                            FromVersion =:= "4.1" ->
-    case ets:info(emqx_user) of
-        undefined -> ok;
-        _ ->
-            CreatedAt = erlang:system_time(millisecond),
-            [ begin
-                mnesia:dirty_write({emqx_user, {username, Login}, base64:decode(Password), CreatedAt})
-              end
-              || #{<<"login">> := Login,
-                   <<"password">> := Password} <- Auths ]
-
-    end;
-
-import_auth_mnesia(Auths, _) ->
-    case ets:info(emqx_user) of
-        undefined -> ok;
-        _ ->
-            [ mnesia:dirty_write({emqx_user, {any_to_atom(Type), Login}, base64:decode(Password), CreatedAt})
-              || #{<<"login">> := Login,
-                   <<"type">> := Type,
-                   <<"password">> := Password,
-                   <<"created_at">> := CreatedAt } <- Auths ]
-    end.
-
-import_acl_mnesia(Acls, FromVersion) when FromVersion =:= "4.0" orelse
-                                          FromVersion =:= "4.1" ->
-    case ets:info(emqx_acl) of
-        undefined -> ok;
-        _ ->
-            CreatedAt = erlang:system_time(millisecond),
-            [begin
-                 Allow1 = case any_to_atom(Allow) of
-                              true -> allow;
-                              false -> deny
-                          end,
-                 mnesia:dirty_write({emqx_acl, {{username, Login}, Topic}, any_to_atom(Action), Allow1, CreatedAt})
-             end || #{<<"login">> := Login,
-                      <<"topic">> := Topic,
-                      <<"allow">> := Allow,
-                      <<"action">> := Action} <- Acls]
-    end;
-
-import_acl_mnesia(Acls, _) ->
-    case ets:info(emqx_acl) of
-        undefined -> ok;
-        _ ->
-            [ begin
-              Filter = case maps:get(<<"type_value">>, Map, undefined) of
-                  undefined ->
-                      {any_to_atom(maps:get(<<"type">>, Map)), maps:get(<<"topic">>, Map)};
-                  Value ->
-                      {{any_to_atom(maps:get(<<"type">>, Map)), Value}, maps:get(<<"topic">>, Map)}
-              end,
-              mnesia:dirty_write({emqx_acl ,Filter, any_to_atom(Action), any_to_atom(Access), CreatedAt})
-              end
-              || Map = #{<<"action">> := Action,
-                         <<"access">> := Access,
-                         <<"created_at">> := CreatedAt} <- Acls ]
-    end.
 
 any_to_atom(L) when is_list(L) -> list_to_atom(L);
 any_to_atom(B) when is_binary(B) -> binary_to_atom(B, utf8);
@@ -985,4 +578,3 @@ action_to_prop_list({action_instance, ActionInstId, Name, FallbackActions, Args}
      {name, Name},
      {fallbacks, actions_to_prop_list(FallbackActions)},
      {args, Args}].
-

+ 4 - 71
lib-ce/emqx_management/src/emqx_mgmt_api_data.erl

@@ -75,48 +75,8 @@
         ]).
 
 export(_Bindings, _Params) ->
-    Rules = emqx_mgmt:export_rules(),
-    Resources = emqx_mgmt:export_resources(),
-    Blacklist = emqx_mgmt:export_blacklist(),
-    Apps = emqx_mgmt:export_applications(),
-    Users = emqx_mgmt:export_users(),
-    AuthMnesia = emqx_mgmt:export_auth_mnesia(),
-    AclMnesia = emqx_mgmt:export_acl_mnesia(),
-    Seconds = erlang:system_time(second),
-    {{Y, M, D}, {H, MM, S}} = emqx_mgmt_util:datetime(Seconds),
-    Filename = io_lib:format("emqx-export-~p-~p-~p-~p-~p-~p.json", [Y, M, D, H, MM, S]),
-    NFilename = filename:join([emqx:get_env(data_dir), Filename]),
-    Version = string:sub_string(emqx_sys:version(), 1, 3),
-    Data = [{version, erlang:list_to_binary(Version)},
-            {date, erlang:list_to_binary(emqx_mgmt_util:strftime(Seconds))},
-            {rules, Rules},
-            {resources, Resources},
-            {blacklist, Blacklist},
-            {apps, Apps},
-            {users, Users},
-            {auth_mnesia, AuthMnesia},
-            {acl_mnesia, AclMnesia}
-           ],
-
-    Bin = emqx_json:encode(Data),
-    ok = filelib:ensure_dir(NFilename),
-    case file:write_file(NFilename, Bin) of
-        ok ->
-            case file:read_file_info(NFilename) of
-                {ok, #file_info{size = Size, ctime = {{Y0, M0, D0}, {H0, MM0, S0}}}} ->
-                    CreatedAt = io_lib:format("~p-~p-~p ~p:~p:~p", [Y0, M0, D0, H0, MM0, S0]),
-                    return({ok, [{filename, list_to_binary(Filename)},
-                                 {size, Size},
-                                 {created_at, list_to_binary(CreatedAt)},
-                                 {node, node()}
-                                ]});
-                {error, Reason} ->
-                    return({error, Reason})
-            end;
-        {error, Reason} ->
-            return({error, Reason})
-    end.
-
+    return(emqx_mgmt_data_backup:export()).
+    
 list_exported(_Bindings, _Params) ->
     List = [ rpc:call(Node, ?MODULE, get_list_exported, []) || Node <- ekka_mnesia:running_nodes() ],
     NList = lists:map(fun({_, FileInfo}) -> FileInfo end, lists:keysort(1, lists:append(List))),
@@ -158,7 +118,7 @@ import(_Bindings, Params) ->
                     case lists:member(Node,
                           [ erlang:atom_to_binary(N, utf8) || N <- ekka_mnesia:running_nodes() ]
                          ) of
-                        true -> rpc:call(erlang:binary_to_atom(Node, utf8), ?MODULE, do_import, [Filename]);
+                        true -> return(rpc:call(erlang:binary_to_atom(Node, utf8), ?MODULE, do_import, [Filename]));
                         false -> return({error, no_existent_node})
                     end
             end,
@@ -167,34 +127,7 @@ import(_Bindings, Params) ->
 
 do_import(Filename) ->
     FullFilename = filename:join([emqx:get_env(data_dir), Filename]),
-    case file:read_file(FullFilename) of
-        {ok, Json} ->
-            Data = emqx_json:decode(Json, [return_maps]),
-            Version = emqx_mgmt:to_version(maps:get(<<"version">>, Data)),
-            case lists:member(Version, ?VERSIONS) of
-                true  ->
-                    try
-                        emqx_mgmt:import_resources_and_rules(maps:get(<<"resources">>, Data, []), maps:get(<<"rules">>, Data, []), Version),
-                        emqx_mgmt:import_blacklist(maps:get(<<"blacklist">>, Data, [])),
-                        emqx_mgmt:import_applications(maps:get(<<"apps">>, Data, [])),
-                        emqx_mgmt:import_users(maps:get(<<"users">>, Data, [])),
-                        _ = emqx_mgmt:import_auth_clientid(maps:get(<<"auth_clientid">>, Data, [])),
-                        _ = emqx_mgmt:import_auth_username(maps:get(<<"auth_username">>, Data, [])),
-                        _ = emqx_mgmt:import_auth_mnesia(maps:get(<<"auth_mnesia">>, Data, []), Version),
-                        _ = emqx_mgmt:import_acl_mnesia(maps:get(<<"acl_mnesia">>, Data, []), Version),
-                        logger:debug("The emqx data has been imported successfully"),
-                        ok
-                    catch Class:Reason:Stack ->
-                        logger:error("The emqx data import failed: ~0p", [{Class,Reason,Stack}]),
-                        {error, import_failed}
-                    end;
-                false ->
-                    logger:error("Unsupported version: ~p", [Version]),
-                    {error, unsupported_version}
-            end;
-        {error, Reason} ->
-            {error, Reason}
-    end.
+    emqx_mgmt_data_backup:import(FullFilename).    
 
 download(#{filename := Filename}, _Params) ->
     FullFilename = filename:join([emqx:get_env(data_dir), Filename]),

+ 10 - 87
lib-ce/emqx_management/src/emqx_mgmt_cli.erl

@@ -41,7 +41,6 @@
         , log/1
         , mgmt/1
         , data/1
-        , modules/1
         ]).
 
 -define(PROC_INFOKEYS, [status,
@@ -322,44 +321,6 @@ plugins(_) ->
                     {"plugins reload <Plugin>", "Reload plugin"}
                    ]).
 
-%%--------------------------------------------------------------------
-%% @doc Modules Command
-modules(["list"]) ->
-    foreach(fun(Module) -> print({module, Module}) end, emqx_modules:list());
-
-modules(["load", Name]) ->
-    case emqx_modules:load(list_to_atom(Name)) of
-        ok ->
-            emqx_ctl:print("Module ~s loaded successfully.~n", [Name]);
-        {error, Reason}   ->
-            emqx_ctl:print("Load module ~s error: ~p.~n", [Name, Reason])
-    end;
-
-modules(["unload", Name]) ->
-    case emqx_modules:unload(list_to_atom(Name)) of
-        ok ->
-            emqx_ctl:print("Module ~s unloaded successfully.~n", [Name]);
-        {error, Reason} ->
-            emqx_ctl:print("Unload module ~s error: ~p.~n", [Name, Reason])
-    end;
-
-modules(["reload", "emqx_mod_acl_internal" = Name]) ->
-    case emqx_modules:reload(list_to_atom(Name)) of
-        ok ->
-            emqx_ctl:print("Module ~s reloaded successfully.~n", [Name]);
-        {error, Reason} ->
-            emqx_ctl:print("Reload module ~s error: ~p.~n", [Name, Reason])
-    end;
-modules(["reload", Name]) ->
-    emqx_ctl:print("Module: ~p does not need to be reloaded.~n", [Name]);
-
-modules(_) ->
-    emqx_ctl:usage([{"modules list",            "Show loaded modules"},
-                    {"modules load <Module>",   "Load module"},
-                    {"modules unload <Module>", "Unload module"},
-                    {"modules reload <Module>", "Reload module"}
-                   ]).
-
 %%--------------------------------------------------------------------
 %% @doc vm command
 
@@ -582,59 +543,21 @@ stop_listener(#{listen_on := ListenOn} = Listener, _Input) ->
 %% @doc data Command
 
 data(["export"]) ->
-    Rules = emqx_mgmt:export_rules(),
-    Resources = emqx_mgmt:export_resources(),
-    Blacklist = emqx_mgmt:export_blacklist(),
-    Apps = emqx_mgmt:export_applications(),
-    Users = emqx_mgmt:export_users(),
-    AuthMnesia = emqx_mgmt:export_auth_mnesia(),
-    AclMnesia = emqx_mgmt:export_acl_mnesia(),
-    Seconds = erlang:system_time(second),
-    {{Y, M, D}, {H, MM, S}} = emqx_mgmt_util:datetime(Seconds),
-    Filename = io_lib:format("emqx-export-~p-~p-~p-~p-~p-~p.json", [Y, M, D, H, MM, S]),
-    NFilename = filename:join([emqx:get_env(data_dir), Filename]),
-    Version = string:sub_string(emqx_sys:version(), 1, 3),
-    Data = [{version, erlang:list_to_binary(Version)},
-            {date, erlang:list_to_binary(emqx_mgmt_util:strftime(Seconds))},
-            {rules, Rules},
-            {resources, Resources},
-            {blacklist, Blacklist},
-            {apps, Apps},
-            {users, Users},
-            {auth_mnesia, AuthMnesia},
-            {acl_mnesia, AclMnesia}],
-    ok = filelib:ensure_dir(NFilename),
-    case file:write_file(NFilename, emqx_json:encode(Data)) of
-        ok ->
+    case emqx_mgmt_data_backup:export() of
+        {ok, _} ->
             emqx_ctl:print("The emqx data has been successfully exported to ~s.~n", [NFilename]);
         {error, Reason} ->
             emqx_ctl:print("The emqx data export failed due to ~p.~n", [Reason])
-    end;
+    end;    
 
 data(["import", Filename]) ->
-    case file:read_file(Filename) of
-        {ok, Json} ->
-            Data = emqx_json:decode(Json, [return_maps]),
-            Version = emqx_mgmt:to_version(maps:get(<<"version">>, Data)),
-            case lists:member(Version, ?VERSIONS) of
-                true  ->
-                    try
-                        emqx_mgmt:import_resources(maps:get(<<"resources">>, Data, [])),
-                        emqx_mgmt:import_rules(maps:get(<<"rules">>, Data, [])),
-                        emqx_mgmt:import_blacklist(maps:get(<<"blacklist">>, Data, [])),
-                        emqx_mgmt:import_applications(maps:get(<<"apps">>, Data, [])),
-                        emqx_mgmt:import_users(maps:get(<<"users">>, Data, [])),
-                        _ = emqx_mgmt:import_auth_clientid(maps:get(<<"auth_clientid">>, Data, [])),
-                        _ = emqx_mgmt:import_auth_username(maps:get(<<"auth_username">>, Data, [])),
-                        _ = emqx_mgmt:import_auth_mnesia(maps:get(<<"auth_mnesia">>, Data, []), Version),
-                        _ = emqx_mgmt:import_acl_mnesia(maps:get(<<"acl_mnesia">>, Data, []), Version),
-                        emqx_ctl:print("The emqx data has been imported successfully.~n")
-                    catch Class:Reason:Stack ->
-                        emqx_ctl:print("The emqx data import failed due: ~0p~n", [{Class,Reason,Stack}])
-                    end;
-                false ->
-                    emqx_ctl:print("Unsupported version: ~p~n", [Version])
-            end;
+    case emqx_mgmt_data_backup:import(Filename) of
+        ok ->
+            emqx_ctl:print("The emqx data has been imported successfully.~n");
+        {error, import_failed} -> 
+            emqx_ctl:print("The emqx data import failed due: ~0p~n", [{Class,Reason,Stack}]);
+        {error, unsupported_version} ->
+            emqx_ctl:print("Unsupported version: ~p~n", [Version]);
         {error, Reason} ->
             emqx_ctl:print("The emqx data import failed: ~0p while reading ~s.~n", [Reason, Filename])
     end;

+ 557 - 0
lib-ce/emqx_management/src/emqx_mgmt_data_backup.erl

@@ -0,0 +1,557 @@
+%%--------------------------------------------------------------------
+%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%
+%% 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.
+%%--------------------------------------------------------------------
+
+-module(emqx_mgmt_data_backup).
+
+-ifdef(EMQX_ENTERPISE).
+-export([ export_modules/0
+        , export_schemas/0
+        , export_confs/0
+        , import_modules/1
+        , import_schemas/1
+        , import_confs/2
+        ]).
+-else.
+-export([ import_resources_and_rules/3 ]).
+-endif.
+
+-export([ export_rules/0
+        , export_resources/0
+        , export_blacklist/0
+        , export_applications/0
+        , export_users/0
+        , export_auth_mnesia/0
+        , export_acl_mnesia/0
+        , import_rules/1
+        , import_resources/1
+        , import_blacklist/1
+        , import_applications/1
+        , import_users/1
+        , import_auth_clientid/1 %% BACKW: 4.1.x
+        , import_auth_username/1 %% BACKW: 4.1.x
+        , import_auth_mnesia/2
+        , import_acl_mnesia/2
+        , to_version/1
+        ]).
+
+-export([ export/0 
+        , import/1
+        ]).
+
+%%--------------------------------------------------------------------
+%% Data Export and Import
+%%--------------------------------------------------------------------
+
+export_rules() ->
+    lists:map(fun({_, RuleId, _, RawSQL, _, _, _, _, _, _, Actions, Enabled, Desc}) ->
+                   [{id, RuleId},
+                    {rawsql, RawSQL},
+                    {actions, actions_to_prop_list(Actions)},
+                    {enabled, Enabled},
+                    {description, Desc}]
+               end, emqx_rule_registry:get_rules()).
+
+export_resources() ->
+    lists:map(fun({_, Id, Type, Config, CreatedAt, Desc}) ->
+                   NCreatedAt = case CreatedAt of
+                                    undefined -> null;
+                                    _ -> CreatedAt
+                                end,
+                   [{id, Id},
+                    {type, Type},
+                    {config, maps:to_list(Config)},
+                    {created_at, NCreatedAt},
+                    {description, Desc}]
+               end, emqx_rule_registry:get_resources()).
+
+export_blacklist() ->
+    lists:map(fun(#banned{who = Who, by = By, reason = Reason, at = At, until = Until}) ->
+                  NWho = case Who of
+                             {peerhost, Peerhost} -> {peerhost, inet:ntoa(Peerhost)};
+                             _ -> Who
+                         end,
+                  [{who, [NWho]}, {by, By}, {reason, Reason}, {at, At}, {until, Until}]
+              end, ets:tab2list(emqx_banned)).
+
+export_applications() ->
+    lists:map(fun({_, AppID, AppSecret, Name, Desc, Status, Expired}) ->
+                  [{id, AppID}, {secret, AppSecret}, {name, Name}, {desc, Desc}, {status, Status}, {expired, Expired}]
+              end, ets:tab2list(mqtt_app)).
+
+export_users() ->
+    lists:map(fun({_, Username, Password, Tags}) ->
+                  [{username, Username}, {password, base64:encode(Password)}, {tags, Tags}]
+              end, ets:tab2list(mqtt_admin)).
+
+export_auth_mnesia() ->
+    case ets:info(emqx_user) of
+        undefined -> [];
+        _ ->
+            lists:map(fun({_, {Type, Login}, Password, CreatedAt}) ->
+                          [{login, Login}, {type, Type}, {password, base64:encode(Password)}, {created_at, CreatedAt}]
+                      end, ets:tab2list(emqx_user))
+    end.
+
+export_acl_mnesia() ->
+    case ets:info(emqx_acl) of
+        undefined -> [];
+        _ ->
+            lists:map(fun({_, Filter, Action, Access, CreatedAt}) ->
+                          Filter1 = case Filter of
+                              {{Type, TypeValue}, Topic} ->
+                                  [{type, Type}, {type_value, TypeValue}, {topic, Topic}];
+                              {Type, Topic} ->
+                                  [{type, Type}, {topic, Topic}]
+                          end,
+                          Filter1 ++ [{action, Action}, {access, Access}, {created_at, CreatedAt}]
+                      end, ets:tab2list(emqx_acl))
+    end.
+
+-ifdef(EMQX_ENTERPISE).
+export_modules() ->
+    case ets:info(emqx_modules) of
+        undefined -> [];
+        _ ->
+           lists:map(fun({_, Id, Type, Config, Enabled, CreatedAt, Description}) ->
+                          [{id, Id},
+                           {type, Type},
+                           {config, Config},
+                           {enabled, Enabled},
+                           {created_at, CreatedAt},
+                           {description, Description}
+                          ]
+                      end, ets:tab2list(emqx_modules))
+    end.
+
+export_schemas() ->
+    case ets:info(emqx_schema) of
+        undefined -> [];
+        _ ->
+            [emqx_schema_api:format_schema(Schema) || Schema <- emqx_schema_registry:get_all_schemas()]
+    end.
+
+export_confs() ->
+    case ets:info(emqx_conf_info) of
+        undefined -> {[], []};
+        _ ->
+            {lists:map(fun({_, Key, Confs}) ->
+                case Key of
+                    {_Zone, Name} ->
+                        [{zone, list_to_binary(Name)},
+                         {confs, confs_to_binary(Confs)}];
+                    {_Listener, Type, Name} ->
+                        [{type, list_to_binary(Type)},
+                         {name, list_to_binary(Name)},
+                         {confs, confs_to_binary(Confs)}];
+                    Name ->
+                        [{name, list_to_binary(Name)},
+                         {confs, confs_to_binary(Confs)}]
+                end
+            end, ets:tab2list(emqx_conf_b)),
+            lists:map(fun({_, {_Listener, Type, Name}, Status}) ->
+                [{type, list_to_binary(Type)},
+                 {name, list_to_binary(Name)},
+                 {status, Status}]
+            end, ets:tab2list(emqx_listeners_state))}
+    end.
+
+confs_to_binary(Confs) ->
+    [{list_to_binary(Key), list_to_binary(Val)} || {Key, Val} <-Confs].
+
+-endif.
+
+import_rules(Rules) ->
+    lists:foreach(fun(Resource) ->
+                      import_resource(Resource)
+                  end, Rules).
+import_rule(#{<<"id">> := RuleId,
+              <<"rawsql">> := RawSQL,
+              <<"actions">> := Actions,
+              <<"enabled">> := Enabled,
+              <<"description">> := Desc}) ->
+    Rule = #{id => RuleId,
+             rawsql => RawSQL,
+             actions => map_to_actions(Actions),
+             enabled => Enabled,
+             description => Desc},
+    try emqx_rule_engine:create_rule(Rule)
+    catch throw:{resource_not_initialized, _ResId} ->
+        emqx_rule_engine:create_rule(Rule#{enabled => false})
+    end.
+
+import_resources(Reources) ->
+    lists:foreach(fun(Resource) ->
+                      import_resource(Resource)
+                  end, Reources).
+
+import_resource(#{<<"id">> := Id,
+                  <<"type">> := Type,
+                  <<"config">> := Config,
+                  <<"created_at">> := CreatedAt,
+                  <<"description">> := Desc}) ->
+    NCreatedAt = case CreatedAt of
+                     null -> undefined;
+                     _ -> CreatedAt
+                 end,
+    emqx_rule_engine:create_resource(#{id => Id,
+                                       type => any_to_atom(Type),
+                                       config => Config,
+                                       created_at => NCreatedAt,
+                                       description => Desc}).
+
+-ifndef(EMQX_ENTERPRISE).
+import_resources_and_rules(Resources, Rules, FromVersion)
+  when FromVersion =:= "4.0" orelse FromVersion =:= "4.1" orelse FromVersion =:= "4.2" ->
+    Configs = lists:foldl(fun(#{<<"id">> := ID,
+                                <<"type">> := <<"web_hook">>,
+                                <<"config">> := #{<<"content_type">> := ContentType,
+                                                  <<"headers">> := Headers,
+                                                  <<"method">> := Method,
+                                                  <<"url">> := URL}} = Resource, Acc) ->
+                              NConfig = #{<<"connect_timeout">> => 5,
+                                          <<"request_timeout">> => 5,
+                                          <<"cacertfile">> => <<>>,
+                                          <<"certfile">> => <<>>,
+                                          <<"keyfile">> => <<>>,
+                                          <<"pool_size">> => 8,
+                                          <<"url">> => URL,
+                                          <<"verify">> => true},
+                              NResource = Resource#{<<"config">> := NConfig},
+                              import_resource(NResource),
+                              NHeaders = maps:put(<<"content-type">>, ContentType, Headers),
+                              [{ID, #{headers => NHeaders, method => Method}} | Acc];
+                             (Resource, Acc) ->
+                              import_resource(Resource),
+                              Acc
+                          end, [], Resources),
+    lists:foreach(fun(#{<<"actions">> := Actions} = Rule) ->
+                      NActions = apply_new_config(Actions, Configs),
+                      import_rule(Rule#{<<"actions">> := NActions})
+                  end, Rules);
+import_resources_and_rules(Resources, Rules, _FromVersion) ->
+    import_resources(Resources),
+    import_rules(Rules).
+
+apply_new_config(Actions, Configs) ->
+    apply_new_config(Actions, Configs, []).
+
+apply_new_config([], _Configs, Acc) ->
+    Acc;
+apply_new_config([Action = #{<<"name">> := <<"data_to_webserver">>,
+                             <<"args">> := #{<<"$resource">> := ID,
+                                            <<"path">> := Path,
+                                            <<"payload_tmpl">> := PayloadTmpl}} | More], Configs, Acc) ->
+    case proplists:get_value(ID, Configs, undefined) of
+        undefined ->
+            apply_new_config(More, Configs, [Action | Acc]);
+        #{headers := Headers, method := Method} ->
+            Args = #{<<"$resource">> => ID,
+                     <<"body">> => PayloadTmpl,
+                     <<"headers">> => Headers,
+                     <<"method">> => Method,
+                     <<"path">> => Path},
+            apply_new_config(More, Configs, [Action#{<<"args">> := Args} | Acc])
+    end.
+
+
+-endif.
+
+import_blacklist(Blacklist) ->
+    lists:foreach(fun(#{<<"who">> := Who,
+                        <<"by">> := By,
+                        <<"reason">> := Reason,
+                        <<"at">> := At,
+                        <<"until">> := Until}) ->
+                      NWho = case Who of
+                                 #{<<"peerhost">> := Peerhost} ->
+                                     {ok, NPeerhost} = inet:parse_address(Peerhost),
+                                     {peerhost, NPeerhost};
+                                 #{<<"clientid">> := ClientId} -> {clientid, ClientId};
+                                 #{<<"username">> := Username} -> {username, Username}
+                             end,
+                     emqx_banned:create(#banned{who = NWho, by = By, reason = Reason, at = At, until = Until})
+                  end, Blacklist).
+
+import_applications(Apps) ->
+    lists:foreach(fun(#{<<"id">> := AppID,
+                        <<"secret">> := AppSecret,
+                        <<"name">> := Name,
+                        <<"desc">> := Desc,
+                        <<"status">> := Status,
+                        <<"expired">> := Expired}) ->
+                      NExpired = case is_integer(Expired) of
+                                     true -> Expired;
+                                     false -> undefined
+                                 end,
+                      emqx_mgmt_auth:force_add_app(AppID, Name, AppSecret, Desc, Status, NExpired)
+                  end, Apps).
+
+import_users(Users) ->
+    lists:foreach(fun(#{<<"username">> := Username,
+                        <<"password">> := Password,
+                        <<"tags">> := Tags}) ->
+                      NPassword = base64:decode(Password),
+                      emqx_dashboard_admin:force_add_user(Username, NPassword, Tags)
+                  end, Users).
+
+import_auth_clientid(Lists) ->
+    case ets:info(emqx_user) of
+        undefined -> ok;
+        _ ->
+            lists:foreach(fun(#{<<"clientid">> := Clientid, <<"password">> := Password}) -> 
+                            mnesia:dirty_write({emqx_user, {clientid, Clientid}, base64:decode(Password), erlang:system_time(millisecond)})
+                          end, Lists)
+    end.
+
+import_auth_username(Lists) ->
+    case ets:info(emqx_user) of
+        undefined -> ok;
+        _ ->
+            lists:foreach(fun(#{<<"username">> := Username, <<"password">> := Password}) ->
+                            mnesia:dirty_write({emqx_user, {username, Username}, base64:decode(Password), erlang:system_time(millisecond)})           
+                          end, Lists)
+    end.
+
+import_auth_mnesia(Auths, FromVersion) when FromVersion =:= "4.0" orelse
+                                            FromVersion =:= "4.1" ->
+    case ets:info(emqx_user) of
+        undefined -> ok;
+        _ ->
+            CreatedAt = erlang:system_time(millisecond),
+            lists:foreach(fun(#{<<"login">> := Login,
+                                <<"password">> := Password}) -> 
+                            mnesia:dirty_write({emqx_user, {username, Login}, base64:decode(Password), CreatedAt})          
+                          end, Auths)
+    end;
+
+import_auth_mnesia(Auths, _) ->
+    case ets:info(emqx_user) of
+        undefined -> ok;
+        _ ->
+            lists:foreach(fun(#{<<"login">> := Login,
+                                <<"type">> := Type,
+                                <<"password">> := Password,
+                                <<"created_at">> := CreatedAt }) -> 
+                            mnesia:dirty_write({emqx_user, {any_to_atom(Type), Login}, base64:decode(Password), CreatedAt})
+                          end, Auths)
+    end.
+
+import_acl_mnesia(Acls, FromVersion) when FromVersion =:= "4.0" orelse
+                                          FromVersion =:= "4.1" ->
+    case ets:info(emqx_acl) of
+        undefined -> ok;
+        _ ->
+            CreatedAt = erlang:system_time(millisecond),
+            lists:foreach(fun(#{<<"login">> := Login,
+                                <<"topic">> := Topic,
+                                <<"allow">> := Allow,
+                                <<"action">> := Action}) ->
+                            Allow1 = case any_to_atom(Allow) of
+                                         true -> allow;
+                                         false -> deny
+                                     end,
+                            mnesia:dirty_write({emqx_acl, {{username, Login}, Topic}, any_to_atom(Action), Allow1, CreatedAt})         
+                          end, Acls)
+    end;
+
+import_acl_mnesia(Acls, _) ->
+    case ets:info(emqx_acl) of
+        undefined -> ok;
+        _ ->
+            lists:foreach(fun(Map = #{<<"action">> := Action,
+                                      <<"access">> := Access,
+                                      <<"created_at">> := CreatedAt}) -> 
+                            Filter = case maps:get(<<"type_value">>, Map, undefined) of
+                                undefined ->
+                                    {any_to_atom(maps:get(<<"type">>, Map)), maps:get(<<"topic">>, Map)};
+                                Value ->
+                                    {{any_to_atom(maps:get(<<"type">>, Map)), Value}, maps:get(<<"topic">>, Map)}
+                            end,
+                            mnesia:dirty_write({emqx_acl ,Filter, any_to_atom(Action), any_to_atom(Access), CreatedAt})            
+                          end, Acls)
+    end.
+
+-ifdef(EMQX_ENTERPRISE).
+import_modules(Modules) ->
+    case ets:info(emqx_modules) of
+        undefined -> [];
+        _ ->
+           lists:foreach(fun(#{<<"id">> := Id,
+                               <<"type">> := Type,
+                               <<"config">> := Config,
+                               <<"enabled">> := Enabled,
+                               <<"created_at">> := CreatedAt,
+                               <<"description">> := Description}) ->
+                            emqx_modules:import_module({Id, any_to_atom(Type), Config, Enabled, CreatedAt, Description})
+                         end, Modules)
+    end.
+
+
+import_schemas(Schemas) ->
+    case ets:info(emqx_schema) of
+        undefined -> ok;
+        _ -> [emqx_schema_registry:add_schema(emqx_schema_api:make_schema_params(Schema)) || Schema <- Schemas]
+    end.
+
+import_confs(Configs, ListenersState) ->
+    case ets:info(emqx_conf_info) of
+        undefined -> ok;
+        _ ->
+            emqx_conf:import_confs(Configs, ListenersState)
+    end.
+
+-endif.
+
+-ifdef(EMQX_ENTERPRISE).
+export() ->
+    Modules = export_modules(),
+    Rules = export_rules(),
+    Resources = export_resources(),
+    Blacklist = export_blacklist(),
+    Apps = export_applications(),
+    Users = export_users(),
+    AuthMnesia = export_auth_mnesia(),
+    AclMnesia = export_acl_mnesia(),
+    Schemas = export_schemas(),
+    {Configs, State} = export_confs(),
+    Seconds = erlang:system_time(second),
+    {{Y, M, D}, {H, MM, S}} = emqx_mgmt_util:datetime(Seconds),
+    Filename = io_lib:format("emqx-export-~p-~p-~p-~p-~p-~p.json", [Y, M, D, H, MM, S]),
+    NFilename = filename:join([emqx:get_env(data_dir), Filename]),
+    Version = string:sub_string(emqx_sys:version(), 1, 3),
+    Data = [{version, erlang:list_to_binary(Version)},
+            {date, erlang:list_to_binary(emqx_mgmt_util:strftime(Seconds))},
+            {modules, Modules},
+            {rules, Rules},
+            {resources, Resources},
+            {blacklist, Blacklist},
+            {apps, Apps},
+            {users, Users},
+            {auth_mnesia, AuthMnesia},
+            {acl_mnesia, AclMnesia},
+            {schemas, Schemas},
+            {configs, Configs},
+            {listeners_state, State}],
+    write_file(NFilename, Data). 
+
+import(Filename) ->
+    case file:read_file(FullFilename) of
+        {ok, Json} ->
+            Data = emqx_json:decode(Json, [return_maps]),
+            Version = to_version(maps:get(<<"version">>, Data)),
+            case lists:member(Version, ?VERSIONS) of
+                true  ->
+                    try
+                        import_confs(maps:get(<<"configs">>, Data, []), maps:get(<<"listeners_state">>, Data, [])),
+                        import_resources(maps:get(<<"resources">>, Data, [])),
+                        import_rules(maps:get(<<"rules">>, Data, [])),
+                        import_blacklist(maps:get(<<"blacklist">>, Data, [])),
+                        import_applications(maps:get(<<"apps">>, Data, [])),
+                        import_users(maps:get(<<"users">>, Data, [])),
+                        import_modules(maps:get(<<"modules">>, Data, [])),
+                        import_auth_clientid(maps:get(<<"auth_clientid">>, Data, [])),
+                        import_auth_username(maps:get(<<"auth_username">>, Data, [])),
+                        import_auth_mnesia(maps:get(<<"auth_mnesia">>, Data, []), Version),
+                        import_acl_mnesia(maps:get(<<"acl_mnesia">>, Data, []), Version),
+                        import_schemas(maps:get(<<"schemas">>, Data, [])),
+                        logger:debug("The emqx data has been imported successfully"),
+                        ok
+                    catch Class:Reason:Stack ->
+                        logger:error("The emqx data import failed: ~0p", [{Class,Reason,Stack}]),
+                        {error, import_failed}
+                    end;
+                false ->
+                    logger:error("Unsupported version: ~p", [Version]),
+                    {error, unsupported_version}
+            end;
+        {error, Reason} ->
+            {error, Reason}
+    end.
+
+-else.
+export() ->
+    Rules = export_rules(),
+    Resources = export_resources(),
+    Blacklist = export_blacklist(),
+    Apps = export_applications(),
+    Users = export_users(),
+    AuthMnesia = export_auth_mnesia(),
+    AclMnesia = export_acl_mnesia(),
+    Seconds = erlang:system_time(second),
+    {{Y, M, D}, {H, MM, S}} = emqx_mgmt_util:datetime(Seconds),
+    Filename = io_lib:format("emqx-export-~p-~p-~p-~p-~p-~p.json", [Y, M, D, H, MM, S]),
+    NFilename = filename:join([emqx:get_env(data_dir), Filename]),
+    Version = string:sub_string(emqx_sys:version(), 1, 3),
+    Data = [{version, erlang:list_to_binary(Version)},
+            {date, erlang:list_to_binary(emqx_mgmt_util:strftime(Seconds))},
+            {rules, Rules},
+            {resources, Resources},
+            {blacklist, Blacklist},
+            {apps, Apps},
+            {users, Users},
+            {auth_mnesia, AuthMnesia},
+            {acl_mnesia, AclMnesia}],
+    write_file(NFilename, Data). 
+    
+import(Filename) ->
+    case file:read_file(FullFilename) of
+        {ok, Json} ->
+            Data = emqx_json:decode(Json, [return_maps]),
+            Version = to_version(maps:get(<<"version">>, Data)),
+            case lists:member(Version, ?VERSIONS) of
+                true  ->
+                    try
+                        import_resources_and_rules(maps:get(<<"resources">>, Data, []), maps:get(<<"rules">>, Data, []), Version),
+                        import_blacklist(maps:get(<<"blacklist">>, Data, [])),
+                        import_applications(maps:get(<<"apps">>, Data, [])),
+                        import_users(maps:get(<<"users">>, Data, [])),
+                        import_auth_clientid(maps:get(<<"auth_clientid">>, Data, [])),
+                        import_auth_username(maps:get(<<"auth_username">>, Data, [])),
+                        import_auth_mnesia(maps:get(<<"auth_mnesia">>, Data, []), Version),
+                        import_acl_mnesia(maps:get(<<"acl_mnesia">>, Data, []), Version),
+                        logger:debug("The emqx data has been imported successfully"),
+                        ok
+                    catch Class:Reason:Stack ->
+                        logger:error("The emqx data import failed: ~0p", [{Class,Reason,Stack}]),
+                        {error, import_failed}
+                    end;
+                false ->
+                    logger:error("Unsupported version: ~p", [Version]),
+                    {error, unsupported_version}
+            end;
+        {error, Reason} ->
+            {error, Reason}
+    end.
+-endif.
+
+write_file(Filename, Data) ->
+    ok = filelib:ensure_dir(Filename),
+    case file:write_file(Filename, emqx_json:encode(Data)) of
+        ok ->
+            case file:read_file_info(Filename) of
+                {ok, #file_info{size = Size, ctime = {{Y, M, D}, {H, MM, S}}}} ->
+                    CreatedAt = io_lib:format("~p-~p-~p ~p:~p:~p", [Y, M, D, H, MM, S]),
+                    {ok, [{filename, list_to_binary(Filename)},
+                          {size, Size},
+                          {created_at, list_to_binary(CreatedAt)},
+                          {node, node()}
+                         ]};
+                {error, Reason} ->
+                    {error, Reason}
+            end;
+        {error, Reason} ->
+            {error, Reason}
+    end.