Use lua report script

This commit is contained in:
Martin 2017-04-14 21:49:08 +02:00
parent fee73c869c
commit b17fe1fde0
23 changed files with 634 additions and 78 deletions

File diff suppressed because one or more lines are too long

View File

@ -19,6 +19,128 @@ public:
IEMailFunction* mail_func;
};
struct Param
{
typedef std::map<Param, Param> params_map;
Param()
: tag(PARAM_VEC)
{
u.params = new params_map();
}
Param(const std::string& pstr)
: tag(PARAM_STR)
{
u.str = new std::string(pstr);
}
Param(const char* pstr)
: tag(PARAM_STR)
{
u.str = new std::string(pstr);
}
Param(double num)
:tag(PARAM_NUM) {
u.num = num;
}
Param(int i)
: tag(PARAM_INT) {
u.i = i;
}
Param(int64 i)
: tag(PARAM_NUM) {
u.num = static_cast<double>(i);
}
Param(bool b)
: tag(PARAM_BOOL) {
u.b = b;
}
~Param() {
if (tag == PARAM_VEC) {
delete u.params;
}
else if (tag == PARAM_STR) {
delete u.str;
}
}
Param(const Param& other)
: tag(other.tag) {
if (tag == PARAM_VEC) {
u.params = new params_map(*other.u.params);
}
else if (tag == PARAM_STR) {
u.str = new std::string(*other.u.str);
}
else if (tag == PARAM_NUM) {
u.num = other.u.num;
}
else if (tag == PARAM_BOOL) {
u.b = other.u.b;
}
else if (tag == PARAM_INT) {
u.i = other.u.i;
}
}
void swap(Param& first, Param& second) {
std::swap(first.tag, second.tag);
std::swap(first.u, second.u);
}
Param& operator=(Param other)
{
swap(*this, other);
return *this;
}
bool operator<(const Param& other) const {
if (tag != other.tag) {
return false;
}
if (tag == PARAM_VEC) {
return *u.params < *other.u.params;
}
else if (tag == PARAM_STR) {
return *u.str < *other.u.str;
}
else if (tag == PARAM_NUM) {
return u.num < other.u.num;
}
else if (tag == PARAM_BOOL) {
return u.b < other.u.b;
}
else if (tag == PARAM_INT) {
return u.i < other.u.i;
}
return false;
}
union
{
params_map* params;
std::string* str;
double num;
bool b;
int i;
} u;
enum
{
PARAM_VEC = 1,
PARAM_STR,
PARAM_NUM,
PARAM_BOOL,
PARAM_INT
} tag;
};
virtual std::string compileScript(const std::string& script) = 0;
virtual int64 runScript(const std::string& script, const str_map& params, int64& ret2, std::string& state, std::string& global_data, const SInterpreterFunctions& funcs) = 0;
virtual int64 runScript(const std::string& script, const Param& params, int64& ret2, std::string& state, std::string& global_data, const SInterpreterFunctions& funcs) = 0;
};

View File

@ -2,6 +2,11 @@
#include "src/lua.hpp"
#include "../Interface/Server.h"
#include "../common/data.h"
#include <assert.h>
extern "C" {
LUAMOD_API int luaopen_os_custom(lua_State *L);
}
namespace
{
@ -188,6 +193,7 @@ namespace
{ LUA_STRLIBNAME, luaopen_string },
{ LUA_MATHLIBNAME, luaopen_math },
{ LUA_UTF8LIBNAME, luaopen_utf8 },
{ LUA_OSLIBNAME, luaopen_os_custom},
{ NULL, NULL }
};
@ -228,6 +234,41 @@ namespace
lua_pushboolean(L, b ? 1 : 0);
return 1;
}
void set_param(lua_State* state, const ILuaInterpreter::Param& param)
{
if (param.tag == ILuaInterpreter::Param::PARAM_VEC)
{
lua_newtable(state);
for (ILuaInterpreter::Param::params_map::const_iterator it = param.u.params->begin();
it!=param.u.params->end();++it)
{
set_param(state, it->first);
set_param(state, it->second);
lua_rawset(state, -3);
}
}
else if (param.tag == ILuaInterpreter::Param::PARAM_STR)
{
lua_pushstring(state, param.u.str->c_str());
}
else if (param.tag == ILuaInterpreter::Param::PARAM_NUM)
{
lua_pushnumber(state, param.u.num);
}
else if (param.tag == ILuaInterpreter::Param::PARAM_INT)
{
lua_pushnumber(state, param.u.i);
}
else if (param.tag == ILuaInterpreter::Param::PARAM_BOOL)
{
lua_pushboolean(state, param.u.b);
}
else
{
assert(false);
}
}
}
std::string LuaInterpreter::compileScript(const std::string & script)
@ -257,7 +298,7 @@ std::string LuaInterpreter::compileScript(const std::string & script)
return ret;
}
int64 LuaInterpreter::runScript(const std::string& script, const str_map& params, int64& ret2,
int64 LuaInterpreter::runScript(const std::string& script, const ILuaInterpreter::Param& params, int64& ret2,
std::string& state_data, std::string& global_data, const ILuaInterpreter::SInterpreterFunctions& funcs)
{
ret2 = -1;
@ -272,20 +313,13 @@ int64 LuaInterpreter::runScript(const std::string& script, const str_map& params
luaL_openlibs_custom(state);
int rc = luaL_loadbuffer(state, script.c_str(), script.size(), "foo");
int rc = luaL_loadbuffer(state, script.c_str(), script.size(), "script");
if (rc) {
Server->Log(std::string("Error loading lua script: ") + lua_tostring(state, -1), LL_ERROR);
return -1;
}
lua_newtable(state);
for (str_map::const_iterator it = params.begin(); it != params.end(); ++it)
{
lua_pushstring(state, it->first.c_str());
lua_pushstring(state, it->second.c_str());
lua_rawset(state, -3);
}
set_param(state, params);
lua_setglobal(state, "params");
lua_pushlightuserdata(state, const_cast<ILuaInterpreter::SInterpreterFunctions*>(&funcs));
@ -339,6 +373,14 @@ int64 LuaInterpreter::runScript(const std::string& script, const str_map& params
{
Server->Log("Error serializing state data", LL_WARNING);
}
lua_getglobal(state, "global");
global_data = serialize_table(state);
if (global_data.empty())
{
Server->Log("Error serializing global data", LL_WARNING);
}
return ret;
}

View File

@ -8,7 +8,7 @@ class LuaInterpreter : public ILuaInterpreter
public:
virtual std::string compileScript(const std::string& script);
virtual int64 runScript(const std::string& script, const str_map& params, int64& ret2,
virtual int64 runScript(const std::string& script, const Param& params, int64& ret2,
std::string& state_data, std::string& global_data, const SInterpreterFunctions& funcs);
};

View File

@ -37,3 +37,17 @@
#include "lcorolib.c"
#include "lutf8lib.c"
#include "lctype.c"
static const luaL_Reg syslib_custom[] = {
{ "clock", os_clock },
{ "date", os_date },
{ "difftime", os_difftime },
{ "time", os_time },
{ NULL, NULL }
};
LUAMOD_API int luaopen_os_custom(lua_State *L) {
luaL_newlib(L, syslib_custom);
return 1;
}

View File

@ -7,6 +7,8 @@
#include "../luaplugin/ILuaInterpreter.h"
#include "Mailer.h"
extern ILuaInterpreter* lua_interpreter;
namespace
{
struct SScriptParam
@ -43,7 +45,7 @@ namespace
if (!ret.code.empty())
{
ret.code = lua_interpreter->compileScript(ret.code);
//ret.code = lua_interpreter->compileScript(ret.code);
}
if (ret.code.empty())
@ -75,11 +77,8 @@ namespace
void Alerts::operator()()
{
str_map params;
ILuaInterpreter* lua_interpreter = dynamic_cast<ILuaInterpreter*>(Server->getPlugin(Server->getThreadID(), Server->StartPlugin("lua", params)));
if (lua_interpreter == NULL)
{
Server->Log("Lua plugin missing. Alerts won't work.", LL_WARNING);
return;
}
@ -89,7 +88,7 @@ void Alerts::operator()()
IQuery* q_get_alert_clients = db->Prepare("SELECT id, name, file_ok, image_ok, alerts_state, strftime('%s', lastbackup) AS lastbackup, "
"strftime('%s', lastseen) AS lastseen, strftime('%s', lastbackup_image) AS lastbackup_image, created, os_simple "
"FROM clients WHERE alerts_next_check IS NULL OR alerts_next_check>=?");
"FROM clients WHERE alerts_next_check IS NULL OR alerts_next_check<=?");
IQuery* q_update_client = db->Prepare("UPDATE clients SET file_ok=?, image_ok=?, alerts_next_check=?, alerts_state=? WHERE id=?");
std::map<int, SScript> alert_scripts;
@ -99,7 +98,7 @@ void Alerts::operator()()
while (true)
{
Server->wait(1000);
Server->wait(60*1000);
q_get_alert_clients->Bind(Server->getTimeMS());
db_results res = q_get_alert_clients->Read();
@ -119,15 +118,16 @@ void Alerts::operator()()
if (!it->second.code.empty())
{
str_map params;
params["clientid"] = convert(clientid);
ILuaInterpreter::Param params_raw;
ILuaInterpreter::Param::params_map& params = *params_raw.u.params;
params["clientid"] = clientid;
params["clientname"] = res[i]["name"];
params["incr_file_interval"] = convert(server_settings.getUpdateFreqFileIncr());
params["full_file_interval"] = convert(server_settings.getUpdateFreqFileFull());
params["incr_image_interval"] = convert(server_settings.getUpdateFreqImageIncr());
params["full_image_interval"] = convert(server_settings.getUpdateFreqImageFull());
params["no_images"] = server_settings.getSettings()->no_images ? "1" : "0";
params["no_file_backups"] = server_settings.getSettings()->no_file_backups ? "1" : "0";
params["incr_file_interval"] = server_settings.getUpdateFreqFileIncr();
params["full_file_interval"] = server_settings.getUpdateFreqFileFull();
params["incr_image_interval"] = server_settings.getUpdateFreqImageIncr();
params["full_image_interval"] = server_settings.getUpdateFreqImageFull();
params["no_images"] = server_settings.getSettings()->no_images;
params["no_file_backups"] = server_settings.getSettings()->no_file_backups;
params["os_simple"] = res[i]["os_simple"];
int64 times = Server->getTimeSeconds();
@ -135,11 +135,11 @@ void Alerts::operator()()
int64 lastbackup_file = watoi64(res[i]["lastbackup"]);
int64 lastbackup_image = watoi64(res[i]["lastbackup_image"]);
params["passed_time_lastseen"] = convert(times - watoi64(res[i]["lastseen"]));
params["passed_time_lastbackup_file"] = convert((std::min)(times - lastbackup_file, times - created) );
params["passed_time_lastbackup_image"] = convert((std::min)(times - lastbackup_image, times - created) );
params["lastbackup_file"] = convert(lastbackup_file);
params["lastbackup_image"] = convert(lastbackup_image);
params["passed_time_lastseen"] = times - watoi64(res[i]["lastseen"]);
params["passed_time_lastbackup_file"] = (std::min)(times - lastbackup_file, times - created);
params["passed_time_lastbackup_image"] = (std::min)(times - lastbackup_image, times - created);
params["lastbackup_file"] = lastbackup_file;
params["lastbackup_image"] = lastbackup_image;
SSettings* settings = server_settings.getSettings();
@ -148,16 +148,16 @@ void Alerts::operator()()
|| settings->update_freq_image_full.find(";") != std::string::npos
|| settings->update_freq_image_incr.find(";") != std::string::npos)
{
params["complex_interval"] = "1";
params["complex_interval"] = true;
}
else
{
params["complex_interval"] = "0";
params["complex_interval"] = false;
}
std::string file_ok = res[i]["file_ok"];
bool file_ok = res[i]["file_ok"] == "1";
params["file_ok"] = file_ok;
std::string image_ok = res[i]["image_ok"];
bool image_ok = res[i]["image_ok"] == "1";
params["image_ok"] = image_ok;
str_map nondefault_params;
@ -179,16 +179,16 @@ void Alerts::operator()()
std::string state = res[i]["alerts_state"];
int64 ret2;
int64 ret = lua_interpreter->runScript(it->second.code, params, ret2, state, it->second.global, funcs);
int64 ret = lua_interpreter->runScript(it->second.code, params_raw, ret2, state, it->second.global, funcs);
bool needs_update = false;
if (ret>=0)
{
file_ok = (ret & 1) ? "0" : "1";
image_ok = (ret & 2) ? "0" : "1";
file_ok = !(ret & 1);
image_ok = !(ret & 2);
if (file_ok != params["file_ok"]
|| image_ok != params["image_ok"])
if (file_ok != params["file_ok"].u.b
|| image_ok != params["image_ok"].u.b)
{
needs_update = true;
}
@ -206,8 +206,8 @@ void Alerts::operator()()
}
else
{
if (file_ok == "0"
&& image_ok == "0")
if (!file_ok
&& !image_ok)
{
next_check = Server->getTimeMS() + 1*60*60*1000;
needs_update = true;

View File

@ -27,6 +27,7 @@
#include "../urlplugin/IUrlFactory.h"
#include "server_status.h"
#include "server_cleanup.h"
#include "LogReport.h"
extern IUrlFactory *url_fak;
@ -242,6 +243,12 @@ void Backup::sendLogdataMail(bool r_success, int image, int incremental, bool re
( report_sendonly==2 && r_success) ||
( report_sendonly==3 && !r_success && !has_timeout_error) ) )
{
if (run_report_script(incremental, resumed, image, infos, warnings,
errors, r_success, report_mail, data, clientname))
{
return;
}
std::vector<std::string> to_addrs;
Tokenize(report_mail, to_addrs, ",;");
@ -348,4 +355,4 @@ std::string Backup::getUserRights(int userid, std::string domain)
{
return "none";
}
}
}

View File

@ -0,0 +1,145 @@
#include "LogReport.h"
#include "../Interface/Server.h"
#include "../Interface/Mutex.h"
#include "../Interface/Database.h"
#include "../stringtools.h"
#include "database.h"
#include "../luaplugin/ILuaInterpreter.h"
#include "Mailer.h"
extern ILuaInterpreter* lua_interpreter;
namespace
{
IMutex* mutex;
std::string script;
class MailBridge : public ILuaInterpreter::IEMailFunction
{
public:
// Inherited via IEMailFunction
virtual bool mail(const std::string & send_to, const std::string & subject, const std::string & message)
{
return Mailer::sendMail(send_to, subject, message);
}
};
ILuaInterpreter::SInterpreterFunctions funcs;
IMutex* global_mutex;
std::string global_data;
}
std::string load_report_script()
{
IScopedLock lock(mutex);
if (!script.empty())
{
return script;
}
if (FileExists("urbackupserver/report.lua"))
{
script = getFile("urbackupserver/report.lua");
if (!script.empty())
{
return script;
}
}
IDatabase* db = Server->getDatabase(Server->getThreadID(), URBACKUPDB_SERVER);
db_results res = db->Read("SELECT tvalue FROM misc WHERE tkey='report_script'");
if (!res.empty())
{
return res[0]["tvalue"];
}
return std::string();
}
std::string get_report_script()
{
std::string script = load_report_script();
if (script.empty())
return script;
std::string ret = lua_interpreter->compileScript(script);
return ret;
}
void init_log_report()
{
mutex = Server->createMutex();
funcs.mail_func = new MailBridge;
global_mutex = Server->createMutex();
}
void reload_report_script()
{
IScopedLock lock(mutex);
script.clear();
}
bool run_report_script(int incremental, bool resumed, int image,
int infos, int warnings, int errors, bool success, const std::string& report_mail,
const std::string & data, const std::string& clientname)
{
std::string script;
{
IScopedLock lock(mutex);
script = get_report_script();
}
if (script.empty())
return false;
ILuaInterpreter::Param param_raw;
ILuaInterpreter::Param::params_map& param = *param_raw.u.params;
param["incremental"] = incremental;
param["resumed"] = resumed;
param["image"] = image;
param["infos"] = infos;
param["warnings"] = warnings;
param["warnings"] = errors;
param["report_mail"] = report_mail;
param["clientname"] = clientname;
ILuaInterpreter::Param::params_map& pdata = *param["data"].u.params;
std::vector<std::string> msgs;
TokenizeMail(data, msgs, "\n");
for (size_t j = 0; j<msgs.size(); ++j)
{
ILuaInterpreter::Param::params_map& obj = *pdata[static_cast<int>(j + 1)].u.params;
std::string ll;
if (!msgs[j].empty()) ll = msgs[j][0];
int li = watoi(ll);
msgs[j].erase(0, 2);
std::string tt = getuntil("-", msgs[j]);
std::string m = getafter("-", msgs[j]);
obj["time"] = watoi64(tt);
obj["msg"] = m;
obj["ll"] = li;
}
int64 ret2;
std::string state;
int64 ret;
{
IScopedLock lock(global_mutex);
ret = lua_interpreter->runScript(script, param_raw, ret2, state, global_data, funcs);
}
if (ret<0)
{
return false;
}
return true;
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <string>
void init_log_report();
std::string load_report_script();
void reload_report_script();
bool run_report_script(int incremental, bool resumed, int image, int infos,
int warnings, int errors, bool success, const std::string& report_mail, const std::string& data,
const std::string& clientname);

View File

@ -1,13 +1,13 @@
--find largest (maximum of incr and full interval) interval for file and image backups
local file_interval = tonumber(params.incr_file_interval)
local full_file_interval = tonumber(params.full_file_interval)
local file_interval = params.incr_file_interval
local full_file_interval = params.full_file_interval
if full_file_interval>0 and full_file_interval<file_interval
then
file_interval = full_file_interval
end
local image_interval = tonumber(params.incr_image_interval)
local full_image_interval = tonumber(params.full_image_interval)
local image_interval = params.incr_image_interval
local full_image_interval = params.full_image_interval
if full_image_interval>0 and full_image_interval<image_interval
then
image_interval = full_image_interval
@ -56,21 +56,32 @@ function fail_mail(image, passed_time, last_time, alert_time)
then
return
end
if alert_time<0
then
return
end
local btype = "file"
if image
then
btype="image"
if params.no_images=="1"
if params.no_images
then
return
end
elseif params.no_file_backups=="1"
elseif params.no_file_backups
then
return
end
local subj = "[UrBackup] " .. params.clientname .. ": No recent " .. btype .. " backup"
local important=" "
if params.alert_important~="1"
then
important = "[Important] "
end
local subj = "[UrBackup]" .. important .. params.clientname .. ": No recent " .. btype .. " backup"
local lastbackup = "Last " .. btype .." backup was " .. pretty_time(passed_time) .. " ago"
if last_time==0
then
@ -85,17 +96,22 @@ function fail_mail(image, passed_time, last_time, alert_time)
mail(params.alert_emails, subj, msg)
end
local all_fail = 1
if not state.mailed
then
state.mailed = true
fail_mail(false, params.passed_time_lastbackup_file, params.lastbackup_file, file_interval*tonumber(params.alert_file_mult) )
end
--Time in seconds till file backup status is not ok
local file_backup_nok = file_interval*tonumber(params.alert_file_mult) - tonumber(params.passed_time_lastbackup_file)
if file_backup_nok<0 and file_interval>0
local file_backup_nok = file_interval*tonumber(params.alert_file_mult) - params.passed_time_lastbackup_file
if file_backup_nok<0
then
ret=ret + 1
--Send warning mail only once on status becoming not ok
if params.file_ok=="1"
if params.file_ok
then
fail_mail(false, tonumber(params.passed_time_lastbackup_file), tonumber(params.lastbackup_file), file_interval*tonumber(params.alert_file_mult) )
fail_mail(false, params.passed_time_lastbackup_file, params.lastbackup_file, file_interval*tonumber(params.alert_file_mult) )
end
else
next_check_ms = math.min(next_check_ms, file_backup_nok*1000)
@ -103,26 +119,27 @@ end
if string.len(params.os_simple)==0 or params.os_simple=="windows"
then
all_fail = all_fail + 2
--Time in seconds till image backup status is not ok
local image_backup_nok = image_interval*tonumber(params.alert_image_mult) - tonumber(params.passed_time_lastbackup_image)
if image_backup_nok<0 and image_interval>0
local image_backup_nok = image_interval*tonumber(params.alert_image_mult) - params.passed_time_lastbackup_image
if image_backup_nok<0
then
ret= ret + 2
if params.image_ok=="1"
if params.image_ok
then
fail_mail(true, tonumber(params.passed_time_lastbackup_image), tonumber(params.lastbackup_image), file_interval*tonumber(params.alert_image_mult) )
fail_mail(true, params.passed_time_lastbackup_image, params.lastbackup_image, image_interval*tonumber(params.alert_image_mult) )
end
else
next_check_ms = math.min(next_check_ms, image_backup_nok*1000)
end
else
ret = ret + 2
end
--Second parameter returns the number of milliseconds to wait till the next status check
--If the interval is complex (not a single number, but different numbers depending on window) we cannot return this time reliably
--The alert script is automatically run after a backup so no need to return the wait time if both image and file backup status is not ok
if params.complex_interval=="1" or ret==all_fail
if params.complex_interval or ret==3
then
return ret
else

View File

@ -53,6 +53,7 @@ extern IServer* Server;
#include "../fsimageplugin/IFSImageFactory.h"
#include "../cryptoplugin/ICryptoFactory.h"
#include "../urlplugin/IUrlFactory.h"
#include "../luaplugin/ILuaInterpreter.h"
#include "database.h"
#include "actions.h"
@ -96,6 +97,7 @@ SStartupStatus startup_status;
#include "../urbackupcommon/WalCheckpointThread.h"
#include "FileMetadataDownloadThread.h"
#include "../urbackupcommon/chunk_hasher.h"
#include "LogReport.h"
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
#include "../common/miniz.h"
@ -141,6 +143,7 @@ IFSImageFactory *image_fak;
ICryptoFactory *crypto_fak;
IUrlFactory *url_fak=NULL;
IFileServ* fileserv=NULL;
ILuaInterpreter* lua_interpreter = NULL;
std::string server_identity;
std::string server_token;
@ -636,6 +639,15 @@ DLLEXPORT void LoadActions(IServer* pServer)
}
}
{
str_map params;
lua_interpreter = dynamic_cast<ILuaInterpreter*>(Server->getPlugin(Server->getThreadID(), Server->StartPlugin("lua", params)));
if (lua_interpreter == NULL)
{
Server->Log("Lua plugin missing. Alerts won't work.", LL_WARNING);
}
}
open_server_database(true);
@ -644,6 +656,7 @@ DLLEXPORT void LoadActions(IServer* pServer)
ServerSettings::init_mutex();
ClientMain::init_mutex();
DataplanDb::init();
init_log_report();
open_settings_database();
@ -1981,6 +1994,7 @@ bool upgrade54_55()
b &= db->Write("INSERT INTO alert_script_params (script_id, idx, name, label, default_value, has_translation) VALUES (1, 0, 'alert_file_mult', 'alert_file_mult', '3', 1)");
b &= db->Write("INSERT INTO alert_script_params (script_id, idx, name, label, default_value, has_translation) VALUES (1, 1, 'alert_image_mult', 'alert_image_mult', '3', 1)");
b &= db->Write("INSERT INTO alert_script_params (script_id, idx, name, label, default_value, has_translation) VALUES (1, 2, 'alert_emails', 'alert_emails', '', 1)");
b &= db->Write("INSERT INTO alert_script_params (script_id, idx, name, label, default_value, has_translation) VALUES (1, 3, 'alert_important', 'alert_important', '0', 1)");
b &= db->Write("CREATE TABLE mail_queue (id INTEGER PRIMARY KEY, send_to TEXT, subject TEXT, message TEXT, next_try INTEGER, retry_count INTEGER DEFAULT 0)");

64
urbackupserver/report.lua Normal file
View File

@ -0,0 +1,64 @@
local subj = "UrBackup: "
local msg = "UrBackup just did "
if params.incremental>0
then
if params.resumed
then
msg = msg .. "a resumed incremental "
subj = subj .. "Resumed incremental "
else
msg = msg .. "an incremental "
subj = subj .. "Incremental "
end
else
if params.resumed
then
msg = msg .. "a resumed full "
subj = subj .. "Resumed full "
else
msg = msg .. "a full "
subj = subj .. "Full "
end
end
if params.image>0
then
msg = msg .. "image "
subj = subj .. "image "
else
msg = msg .. "file "
subj = subj .. "file "
end
subj = subj .. "backup of \"" .. params.clientname .. "\"\n"
msg = msg .. backup of \"" .. params.clientname .. "\".\n"
msg = msg .. "\nReport:\n"
msg = msg .. "( " .. params.infos
if params.infos!=1 then msg = msg .. " infos, "
else msg = msg .. " info, " end
msg = msg .. params.warnings
if params.warnings!=1 then msg = msg .. " warnings, "
else msg = msg .. " warning, " end
msg = msg .. params.errors
if params.errors!=1 then msg = msg .. " errors)\n\n"
else msg = msg .. " error)\n\n" end
for i, v in ipairs(params.data)
do
local ll = "(info)"
if v.ll==1 then ll="(warning)"
elseif v.ll==2 then ll="(error)" end
msg = msg .. os.date("%x %X", v.time) .. ll .. ": " .. v.msg .. "\n"
end
if params.success
then
subj = subj .. " - success"
else
subj = subj .. " - failed"
end
mail(params.report_mail, subj, msg)
return 0

View File

@ -206,6 +206,11 @@ ACTION_IMPL(logs)
ret.set("HAS_MAIL_START", "<!--");
ret.set("HAS_MAIL_STOP", "-->");
}
if (helper.getRights(RIGHT_REPORT_SCRIPT) == RIGHT_ALL)
{
ret.set("can_report_script_edit", true);
}
}
else
{

View File

@ -7,6 +7,7 @@ namespace
const char* RIGHT_SETTINGS="settings";
const char* RIGHT_BROWSE_BACKUPS = "browse_backups";
const char* RIGHT_ALERT_SCRIPTS = "alert_scripts";
const char* RIGHT_REPORT_SCRIPT = "report_script";
}
#endif //_RIGHTS_H_

View File

@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/
#include "action_header.h"
#include "../LogReport.h"
ACTION_IMPL(scripts)
{
@ -79,6 +80,8 @@ ACTION_IMPL(scripts)
q->Write();
q->Reset();
}
ret.set("saved_ok", true);
}
else if (sa == "rm_alert"
&& id != 1)
@ -122,4 +125,26 @@ ACTION_IMPL(scripts)
ret.set("params", params);
helper.Write(ret.stringify(false));
}
else if (sa == "get_report"
|| sa == "set_report")
{
if (helper.getRights(RIGHT_REPORT_SCRIPT) != RIGHT_ALL)
{
return;
}
JSON::Object ret;
if (sa == "set_report")
{
db->Write("DELETE FROM misc WHERE tkey='report_script'");
IQuery* q = db->Prepare("INSERT INTO misc (tkey, tvalue) VALUES ('report_script', ?)");
q->Bind(POST["script"]);
q->Write();
q->Reset();
ret.set("saved_ok", true);
}
ret.set("script", load_report_script());
helper.Write(ret.stringify(false));
}
}

View File

@ -242,6 +242,7 @@
<ClCompile Include="lmdb\mdb.c" />
<ClCompile Include="lmdb\midl.c" />
<ClCompile Include="LMDBFileIndex.cpp" />
<ClCompile Include="LogReport.cpp" />
<ClCompile Include="Mailer.cpp" />
<ClCompile Include="PhashLoad.cpp" />
<ClCompile Include="restore_client.cpp" />
@ -359,6 +360,7 @@
<ClInclude Include="lmdb\lmdb.h" />
<ClInclude Include="lmdb\midl.h" />
<ClInclude Include="LMDBFileIndex.h" />
<ClInclude Include="LogReport.h" />
<ClInclude Include="Mailer.h" />
<ClInclude Include="PhashLoad.h" />
<ClInclude Include="restore_client.h" />

View File

@ -369,6 +369,9 @@
<ClCompile Include="serverinterface\scripts.cpp">
<Filter>serverinterface</Filter>
</ClCompile>
<ClCompile Include="LogReport.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="action_header.h">
@ -668,5 +671,8 @@
<ClInclude Include="Alerts.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="LogReport.h">
<Filter>Headerdateien</Filter>
</ClInclude>
</ItemGroup>
</Project>

File diff suppressed because one or more lines are too long

View File

@ -1587,7 +1587,8 @@ translations.en = {
"confirm_alert_script_remove": "Are you sure you want to remove this alert script?",
"alert_file_mult": "Number of times the file backup interval after which the file backup status is set to not ok",
"alert_image_mult": "Number of times the image backup interval after which the image backup status is set to not ok",
"alert_emails": "E-Mail addresses (separated by ';') to notify once the image/file backup status is not ok"
"alert_emails": "E-Mail addresses (separated by ';') to notify once the image/file backup status is not ok",
"alert_important": "Add [Important] tag to alert mails"
}
translations.en_US = {
"tInfos": "About"

View File

@ -4468,6 +4468,7 @@ function show_logs2(data)
td.sel_warn=(data.report_loglevel==1)?sel:"";
td.sel_error=(data.report_loglevel==2)?sel:"";
td.live_log_clients=live_log_clients;
td.can_report_script_edit = data.can_report_script_edit;
if(data.has_user)
{
td.has_user=true;
@ -5303,7 +5304,7 @@ function show_scripts2(data)
g.alert_script_idx.push(j);
}
var tdata = dustRender("alert_script_edit", {alert_scripts: script_options, mod_alert_params: params_html} );
var tdata = dustRender("alert_script_edit", {alert_scripts: script_options, mod_alert_params: params_html, saved_ok: data.saved_ok} );
if(g.data_f!=tdata)
{
@ -5311,6 +5312,11 @@ function show_scripts2(data)
g.data_f=tdata;
}
if(data.saved_ok)
{
setTimeout(remove_saved_ok, 5000);
}
require.config({ paths: { 'vs': 'js/vs' }});
require(['vs/editor/editor.main'], function() {
@ -5389,4 +5395,49 @@ function showAlertScript()
{
if(!startLoading()) return;
new getJSON("scripts", "sa=get_alert&id="+I("alert_script").value, show_scripts2);
}
function show_report_script1()
{
if(!startLoading()) return;
new getJSON("scripts", "sa=get_report", show_report_script2);
I('nav_pos').innerHTML="";
}
function remove_saved_ok()
{
if(I("saved_ok"))
{
I("saved_ok").remove();
}
}
function show_report_script2(data)
{
stopLoading();
var tdata = dustRender("report_script_edit", data);
if(g.data_f!=tdata)
{
I('data_f').innerHTML=tdata;
g.data_f=tdata;
}
if(data.saved_ok)
{
setTimeout(remove_saved_ok, 5000);
}
require.config({ paths: { 'vs': 'js/vs' }});
require(['vs/editor/editor.main'], function() {
g.editor = monaco.editor.create(document.getElementById('editor'), {
value: unescapeHTML(data.script),
language: 'lua'
});
});
}
function saveReportScript()
{
if(!startLoading()) return;
var params="&script="+encodeURIComponent(g.editor.getValue());
new getJSON("scripts", "sa=set_report"+params, show_report_script2);
}

View File

@ -41,5 +41,10 @@
</div>
<br />
<input type="button" class="btn btn-primary" style="margin-left: 20px" value="{tSave}" onClick="saveAlertScript()" id="save_alert_script_btn"/>
{?saved_ok}
<div id="saved_ok">
Saved script successfully.
</div>
{/saved_ok}
</div>
</div>

View File

@ -78,6 +78,11 @@
<div class="col-sm-offset-2">
<input type="button" class="btn btn-default" value="{tSave}" onClick="saveReportSettings()" />
</div>
{?can_report_script_edit}
<br />
<br />
<a href="javascript: show_report_script1()">{tEdit report script}</a>
{/can_report_script_edit}
</form>
{:else}
{tYou need to create a user to be able to send reports}

View File

@ -0,0 +1,17 @@
<div class="panel panel-default" style="margin-top:20px; height:100%">
<div class="panel-heading">{tEdit report script}</div>
<div class="panel-body">
<div>
<h3>{tReport script}</h3>
<div id="editor" style="clear:left; border:1px solid grey; height:600px">
</div>
</div>
<br />
<input type="button" class="btn btn-primary" style="margin-left: 20px" value="{tSave}" onClick="saveReportScript()"/>
{?saved_ok}
<div id="saved_ok">
Saved script successfully.
</div>
{/saved_ok}
</div>
</div>