mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
Use lua report script
This commit is contained in:
parent
fee73c869c
commit
b17fe1fde0
File diff suppressed because one or more lines are too long
@ -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;
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
145
urbackupserver/LogReport.cpp
Normal file
145
urbackupserver/LogReport.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
12
urbackupserver/LogReport.h
Normal file
12
urbackupserver/LogReport.h
Normal 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);
|
||||
@ -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
|
||||
|
||||
@ -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
64
urbackupserver/report.lua
Normal 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
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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_
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
@ -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" />
|
||||
|
||||
@ -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
@ -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"
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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}
|
||||
|
||||
17
urbackupserver/www/templates/report_script_edit.htm
Normal file
17
urbackupserver/www/templates/report_script_edit.htm
Normal 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>
|
||||
Loading…
Reference in New Issue
Block a user