From 4ce80b8a580fd8910b05bfb94b320b5d2fc2cc6a Mon Sep 17 00:00:00 2001 From: Philipp Storz Date: Sat, 4 May 2013 13:15:04 +0200 Subject: [PATCH] Added scheduler preview The new command "status scheduler" shows the status of the bareos scheduler. The output first shows what schedule triggers what jobs, then the scheduler executions of the next 14 days are displayed including the overrides. By giving the param "schedule=" the display can be limited to a certain schedule and by giving "days=" the timeframe for the preview can be set. Also, the dotcmd schedule was added, so that the tab-completion works for giving schedules. Fixes #14: Implement a scheduler preview for up to one year. --- src/console/console.c | 36 +++++----- src/dird/protos.h | 1 + src/dird/scheduler.c | 2 +- src/dird/ua_cmds.c | 3 +- src/dird/ua_dotcmds.c | 15 ++++ src/dird/ua_status.c | 156 ++++++++++++++++++++++++++++++++++++++++-- src/lib/btime.c | 11 +++ src/lib/btime.h | 1 + 8 files changed, 201 insertions(+), 24 deletions(-) diff --git a/src/console/console.c b/src/console/console.c index a8d5db59002..fcb08dcf170 100644 --- a/src/console/console.c +++ b/src/console/console.c @@ -586,24 +586,24 @@ struct cpl_keywords_t { }; static struct cpl_keywords_t cpl_keywords[] = { - {"pool=", ".pool" }, - {"fileset=", ".fileset" }, - {"client=", ".client" }, - {"job=", ".jobs" }, - {"restore_job=",".jobs type=R" }, - {"level=", ".level" }, - {"storage=", ".storage" }, - {"schedule=", ".schedule" }, - {"volume=", ".media" }, - {"oldvolume=", ".media" }, - {"volstatus=", ".volstatus" }, - {"ls", ".ls" }, - {"cd", ".lsdir" }, - {"mark", ".ls" }, - {"m", ".ls" }, - {"unmark", ".lsmark" }, - {"catalog=", ".catalogs" }, - {"actiononpurge=", ".actiononpurge" } + { "pool=", ".pool" }, + { "fileset=", ".fileset" }, + { "client=", ".client" }, + { "job=", ".jobs" }, + { "restore_job=",".jobs type=R" }, + { "level=", ".level" }, + { "storage=", ".storage" }, + { "schedule=", ".schedule" }, + { "volume=", ".media" }, + { "oldvolume=", ".media" }, + { "volstatus=", ".volstatus" }, + { "ls", ".ls" }, + { "cd", ".lsdir" }, + { "mark", ".ls" }, + { "m", ".ls" }, + { "unmark", ".lsmark" }, + { "catalog=", ".catalogs" }, + { "actiononpurge=", ".actiononpurge" } }; #define key_size ((int)(sizeof(cpl_keywords)/sizeof(struct cpl_keywords_t))) diff --git a/src/dird/protos.h b/src/dird/protos.h index 13f2bd0a3e8..2a9d76903d1 100644 --- a/src/dird/protos.h +++ b/src/dird/protos.h @@ -216,6 +216,7 @@ bool do_autochanger_volume_operation(UAContext *ua, STORERES *store, /* scheduler.c */ JCR *wait_for_next_job(char *one_shot_job_to_run); +bool is_doy_in_last_week(int year, int doy); void term_scheduler(); /* ua_acl.c */ diff --git a/src/dird/scheduler.c b/src/dird/scheduler.c index dad10f6b19a..81234c7a977 100644 --- a/src/dird/scheduler.c +++ b/src/dird/scheduler.c @@ -255,7 +255,7 @@ void term_scheduler() * depending if the year is leap year or not, the doy of the last day of the month * is varying one day. */ -static bool is_doy_in_last_week(int year, int doy) +bool is_doy_in_last_week(int year, int doy) { int i; int *last_dom; diff --git a/src/dird/ua_cmds.c b/src/dird/ua_cmds.c index 12f1e580261..74b1f758d6c 100644 --- a/src/dird/ua_cmds.c +++ b/src/dird/ua_cmds.c @@ -187,7 +187,8 @@ static struct cmdstruct commands[] = { "\twhen=\n" "\tcomment= yes"), false }, { NT_("status"), status_cmd, _("Report status"), - NT_("all | dir= | director | client= | storage= slots | days=nnn"), true }, + NT_("all | dir= | director | client= | storage= slots |\n" + "\tdays=nnn | scheduler | schedule="), true }, { NT_("setbandwidth"), setbwlimit_cmd, _("Sets bandwidth"), NT_("limit= client= jobid= job= ujobid="), true }, { NT_("setdebug"), setdebug_cmd, _("Sets debug level"), diff --git a/src/dird/ua_dotcmds.c b/src/dird/ua_dotcmds.c index 67a30742abe..a9df7633bd3 100644 --- a/src/dird/ua_dotcmds.c +++ b/src/dird/ua_dotcmds.c @@ -67,6 +67,7 @@ static bool locationscmd(UAContext *ua, const char *cmd); static bool mediacmd(UAContext *ua, const char *cmd); static bool aopcmd(UAContext *ua, const char *cmd); static bool catalogscmd(UAContext *ua, const char *cmd); +static bool schedulecmd(UAContext *ua, const char *cmd); static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd); static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd); @@ -107,6 +108,7 @@ static struct cmdstruct commands[] = { { NT_(".pools"), poolscmd, NULL, true }, { NT_(".quit"), dot_quit_cmd, NULL, false }, { NT_(".sql"), sql_cmd, NULL, false }, + { NT_(".schedule"), schedulecmd, NULL, false }, { NT_(".status"), dot_status_cmd, NULL, false }, { NT_(".storage"), storagecmd, NULL, true }, { NT_(".volstatus"), volstatuscmd, NULL, true }, @@ -1052,6 +1054,19 @@ static bool mediacmd(UAContext *ua, const char *cmd) return true; } +static bool schedulecmd(UAContext *ua, const char *cmd) +{ + SCHEDRES *sched; + + LockRes(); + foreach_res(sched, R_SCHEDULE) { + ua->send_msg("%s\n", sched->hdr.name); + } + UnlockRes(); + return true; +} + + static bool locationscmd(UAContext *ua, const char *cmd) { if (!open_client_db(ua)) { diff --git a/src/dird/ua_status.c b/src/dird/ua_status.c index 8617654ebbc..25433690260 100644 --- a/src/dird/ua_status.c +++ b/src/dird/ua_status.c @@ -35,6 +35,7 @@ static void list_scheduled_jobs(UAContext *ua); static void list_running_jobs(UAContext *ua); static void list_terminated_jobs(UAContext *ua); static void do_director_status(UAContext *ua); +static void do_scheduler_status(UAContext *ua); static void do_all_status(UAContext *ua); static void status_slots(UAContext *ua, STORERES *store); static void status_content(UAContext *ua, STORERES *store); @@ -148,12 +149,11 @@ int status_cmd(UAContext *ua, const char *cmd) Dmsg1(20, "status:%s:\n", cmd); - for (i=1; iargc; i++) { + for (i = 1; i < ua->argc; i++) { if (bstrcasecmp(ua->argk[i], NT_("all"))) { do_all_status(ua); return 1; - } else if (bstrcasecmp(ua->argk[i], NT_("dir")) || - bstrcasecmp(ua->argk[i], NT_("director"))) { + } else if (bstrncasecmp(ua->argk[i], NT_("dir"), 3)) { do_director_status(ua); return 1; } else if (bstrcasecmp(ua->argk[i], NT_("client"))) { @@ -171,6 +171,9 @@ int status_cmd(UAContext *ua, const char *cmd) } } return 1; + } else if (bstrncasecmp(ua->argk[i], NT_("sched"), 5)) { + do_scheduler_status(ua); + return 1; } else { store = get_storage_resource(ua, false/*no default*/); if (store) { @@ -202,7 +205,7 @@ int status_cmd(UAContext *ua, const char *cmd) } /* If no args, ask for status type */ if (ua->argc == 1) { - char prmt[MAX_NAME_LENGTH]; + char prmt[MAX_NAME_LENGTH]; start_prompt(ua, _("Status available for:\n")); add_prompt(ua, NT_("Director")); @@ -382,6 +385,151 @@ void list_dir_status_header(UAContext *ua) } } +static void do_scheduler_status(UAContext *ua) +{ + int i, hour, mday, wday, month, wom, woy, yday; + int days = 14; /* Default days for preview */ + bool is_last_week = false, /* Are we in the last week of a month? */ + schedulegiven = false; + char dt[MAX_TIME_LENGTH]; + time_t time_to_check, now, runtime; + char level[15]; + char schedulename[MAX_NAME_LENGTH]; + const int seconds_per_day = 86400; /* Number of seconds in one day */ + struct tm tm; + SCHEDRES *sched; + JOBRES *job; + RUNRES *run; + + now = time(NULL); /* Initialize to now */ + time_to_check = now; + + i = find_arg_with_value(ua, NT_("days")); + if (i >= 0) { + days = atoi(ua->argv[i]); + if (((days < 0) || (days > 366)) && !ua->api) { + ua->send_msg(_("Ignoring invalid value for days. Max is 366.\n")); + days = 14; + } + } + + /* + * Schedule given? + */ + i = find_arg_with_value(ua, NT_("schedule")); + if (i >= 0) { + bstrncpy(schedulename, ua->argv[i], sizeof(schedulename)); + schedulegiven = true; + } + + ua->send_msg("Scheduler Jobs:\n\n"); + ua->send_msg("Schedule Jobs Triggered\n"); + ua->send_msg("===========================================================\n"); + + LockRes(); + foreach_res(sched, R_SCHEDULE) { + if (schedulegiven) { + if (!bstrcmp(sched->hdr.name, schedulename)) { + continue; + } + } + ua->send_msg("%s\n", sched->hdr.name); + foreach_res(job, R_JOB) { + if (job->schedule && bstrcmp(sched->hdr.name, job->schedule->hdr.name)) { + ua->send_msg(" %s\n", job->name()); + } + } + ua->send_msg("\n"); + } + UnlockRes(); + + ua->send_msg("====\n\n"); + ua->send_msg("Scheduler Preview for %d days:\n\n", days); + ua->send_msg("Date Schedule Overrides\n"); + ua->send_msg("==============================================================\n"); + + while (time_to_check < (now + (days * seconds_per_day))) { + (void)localtime_r(&time_to_check, &tm); + hour = tm.tm_hour; + mday = tm.tm_mday - 1; + wday = tm.tm_wday; + month = tm.tm_mon; + wom = mday / 7; + woy = tm_woy(time_to_check); /* Get week of year */ + yday = tm.tm_yday; /* Get day of year */ + + is_last_week = is_doy_in_last_week(tm.tm_year + 1900 , yday); + + LockRes(); + foreach_res(sched, R_SCHEDULE) { + if (schedulegiven) { + if (!bstrcmp(sched->hdr.name, schedulename)) { + continue; + } + } + + for (run = sched->run; run; run = run->next) { + bool run_now; + + run_now = bit_is_set(hour, run->hour) && + bit_is_set(mday, run->mday) && + bit_is_set(wday, run->wday) && + bit_is_set(month, run->month) && + (bit_is_set(wom, run->wom) || + (run->last_set && is_last_week)) && + bit_is_set(woy, run->woy); + + if (run_now) { + /* + * Find time (time_t) job is to be run + */ + (void)localtime_r(&time_to_check, &tm); /* Reset tm structure */ + tm.tm_min = run->minute; /* Set run minute */ + tm.tm_sec = 0; /* Zero secs */ + runtime = mktime(&tm); + bstrftime_wd(dt, sizeof(dt), runtime); + + ua->send_msg(dt); + ua->send_msg(" %-22.22s ", sched->hdr.name); + + bstrncpy(level, level_to_str(run->level), sizeof(level)); + ua->send_msg("Level=%s ", level); + + if (run->Priority) { + ua->send_msg("Priority=%d ", run->Priority); + } + + if (run->spool_data_set) { + ua->send_msg("Spool Data=%d ", run->spool_data); + } + + if (run->accurate_set) { + ua->send_msg("Accurate=%d ", run->accurate); + } + + if (run->pool) { + ua->send_msg("Pool=%s ", run->pool->name()); + } + + if (run->storage) { + ua->send_msg("Storage=%s ", run->storage->name()); + } + + if (run->msgs) { + ua->send_msg("Messages=%s ", run->msgs->name()); + } + + ua->send_msg("\n"); + } + } + } + UnlockRes(); + + time_to_check += 3600; /* next hour */ + } + ua->send_msg("====\n"); +} + static void do_director_status(UAContext *ua) { list_dir_status_header(ua); diff --git a/src/lib/btime.c b/src/lib/btime.c index 132100da259..5d2875017ae 100644 --- a/src/lib/btime.c +++ b/src/lib/btime.c @@ -79,6 +79,17 @@ char *bstrftime_ny(char *dt, int maxlen, utime_t utime) return dt; } +/* Formatted time for user display with weekday: weekday dd-Mon hh:mm */ +char *bstrftime_wd(char *dt, int maxlen, utime_t utime) +{ + time_t time = (time_t)utime; + struct tm tm; + + /* ***FIXME**** the format and localtime_r() should be user configurable */ + (void)localtime_r(&time, &tm); + strftime(dt, maxlen, "%a %d-%b %H:%M", &tm); + return dt; +} /* Formatted time for user display: dd-Mon-yy hh:mm (no century) */ char *bstrftime_nc(char *dt, int maxlen, utime_t utime) diff --git a/src/lib/btime.h b/src/lib/btime.h index fd9010680f7..f502c2b7b70 100644 --- a/src/lib/btime.h +++ b/src/lib/btime.h @@ -35,6 +35,7 @@ char *bstrftime(char *dt, int maxlen, utime_t tim); char *bstrftimes(char *dt, int maxlen, utime_t tim); char *bstrftime_ny(char *dt, int maxlen, utime_t tim); char *bstrftime_nc(char *dt, int maxlen, utime_t tim); +char *bstrftime_wd(char *dt, int maxlen, utime_t tim); utime_t str_to_utime(const char *str);