From 82f4e388dc074fdc4fc401796353e868ca81fc8e Mon Sep 17 00:00:00 2001 From: Eric Bollengier Date: Sat, 20 Dec 2008 22:02:31 +0000 Subject: [PATCH] ebl Work on copy jobs - Add "list copies" command - Add JT_JOB_COPY type for job copies - Don't allow copy jobs in automatic restore - Promote next copy job as backup when original job is deleted git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@8209 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/src/cats/protos.h | 1 + bacula/src/cats/sql_cmds.c | 21 +++++++------- bacula/src/cats/sql_list.c | 37 ++++++++++++++++++++++++ bacula/src/dird/migrate.c | 6 +++- bacula/src/dird/ua_cmds.c | 2 +- bacula/src/dird/ua_output.c | 14 +++++++++ bacula/src/dird/ua_purge.c | 55 +++++++++++++++++++++++++++++++++++- bacula/src/dird/ua_restore.c | 29 ++++++++++++++----- bacula/src/jcr.h | 5 ++-- bacula/src/lib/edit.c | 21 ++++++++++++++ bacula/src/lib/protos.h | 1 + bacula/src/lib/util.c | 3 ++ bacula/technotes-2.5 | 5 ++++ 13 files changed, 178 insertions(+), 22 deletions(-) diff --git a/bacula/src/cats/protos.h b/bacula/src/cats/protos.h index cf9f8be7ff..ea03a3cc54 100644 --- a/bacula/src/cats/protos.h +++ b/bacula/src/cats/protos.h @@ -124,6 +124,7 @@ void db_list_jobmedia_records(JCR *jcr, B_DB *mdb, JobId_t JobId, DB_LIST_HANDLE void db_list_joblog_records(JCR *jcr, B_DB *mdb, JobId_t JobId, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); int db_list_sql_query(JCR *jcr, B_DB *mdb, const char *query, DB_LIST_HANDLER *sendit, void *ctx, int verbose, e_list_type type); void db_list_client_records(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); +void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *jobids, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); /* sql_update.c */ bool db_update_job_start_record(JCR *jcr, B_DB *db, JOB_DBR *jr); diff --git a/bacula/src/cats/sql_cmds.c b/bacula/src/cats/sql_cmds.c index 87c16b5044..26b9744051 100644 --- a/bacula/src/cats/sql_cmds.c +++ b/bacula/src/cats/sql_cmds.c @@ -286,7 +286,7 @@ const char *uar_last_full = "FROM Client,Job,JobMedia,Media,FileSet WHERE Client.ClientId=%s " "AND Job.ClientId=%s " "AND Job.StartTime<'%s' " - "AND Level='F' AND JobStatus='T' AND Type='B' " + "AND Level='F' AND JobStatus='T' " "AND JobMedia.JobId=Job.JobId " "AND Media.Enabled=1 " "AND JobMedia.MediaId=Media.MediaId " @@ -297,13 +297,14 @@ const char *uar_last_full = const char *uar_full = "INSERT INTO temp SELECT Job.JobId,Job.JobTDate," - "Job.ClientId,Job.Level,Job.JobFiles,Job.JobBytes," - "StartTime,VolumeName,JobMedia.StartFile,VolSessionId,VolSessionTime " - "FROM temp1,Job,JobMedia,Media WHERE temp1.JobId=Job.JobId " - "AND Level='F' AND JobStatus='T' AND Type='B' " - "AND Media.Enabled=1 " - "AND JobMedia.JobId=Job.JobId " - "AND JobMedia.MediaId=Media.MediaId"; + "Job.ClientId,Job.Level,Job.JobFiles,Job.JobBytes," + "StartTime,VolumeName,JobMedia.StartFile,VolSessionId,VolSessionTime " + "FROM temp1,Job,JobMedia,Media " + "WHERE temp1.JobId=Job.JobId " + "AND Level='F' AND JobStatus='T' " + "AND Media.Enabled=1 " + "AND JobMedia.JobId=Job.JobId " + "AND JobMedia.MediaId=Media.MediaId"; const char *uar_dif = "INSERT INTO temp SELECT Job.JobId,Job.JobTDate,Job.ClientId," @@ -316,7 +317,7 @@ const char *uar_dif = "AND JobMedia.JobId=Job.JobId " "AND Media.Enabled=1 " "AND JobMedia.MediaId=Media.MediaId " - "AND Job.Level='D' AND JobStatus='T' AND Type='B' " + "AND Job.Level='D' AND JobStatus='T' " "AND Job.FileSetId=FileSet.FileSetId " "AND FileSet.FileSet='%s' " "%s" @@ -333,7 +334,7 @@ const char *uar_inc = "AND Media.Enabled=1 " "AND JobMedia.JobId=Job.JobId " "AND JobMedia.MediaId=Media.MediaId " - "AND Job.Level='I' AND JobStatus='T' AND Type='B' " + "AND Job.Level='I' AND JobStatus='T' " "AND Job.FileSetId=FileSet.FileSetId " "AND FileSet.FileSet='%s' " "%s"; diff --git a/bacula/src/cats/sql_list.c b/bacula/src/cats/sql_list.c index 8e7f1b2873..a7feae7b54 100644 --- a/bacula/src/cats/sql_list.c +++ b/bacula/src/cats/sql_list.c @@ -242,6 +242,43 @@ void db_list_jobmedia_records(JCR *jcr, B_DB *mdb, uint32_t JobId, } +void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *JobIds, + DB_LIST_HANDLER *sendit, void *ctx, e_list_type type) +{ + POOL_MEM str_limit(PM_MESSAGE); + POOL_MEM str_jobids(PM_MESSAGE); + + if (limit > 0) { + Mmsg(str_limit, " LIMIT %d", limit); + } + + if (JobIds && JobIds[0]) { + Mmsg(str_jobids, " AND (C.PriorJobId IN (%s) OR C.JobId IN (%s)) ", + JobIds, JobIds); + } + + db_lock(mdb); + Mmsg(mdb->cmd, + "SELECT DISTINCT C.PriorJobId AS JobId, C.Job, " + "C.JobId AS CopyJobId, M.MediaType " + "FROM Job AS C " + "JOIN JobMedia USING (JobId) " + "JOIN Media AS M USING (MediaId) " + "WHERE C.Type = '%c' %s ORDER BY C.PriorJobId DESC %s", + (char) JT_JOB_COPY, str_jobids.c_str(), str_limit.c_str()); + + if (!QUERY_DB(jcr, mdb, mdb->cmd)) { + goto bail_out; + } + + list_result(jcr, mdb, sendit, ctx, type); + + sql_free_result(mdb); + +bail_out: + db_unlock(mdb); +} + void db_list_joblog_records(JCR *jcr, B_DB *mdb, uint32_t JobId, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type) { diff --git a/bacula/src/dird/migrate.c b/bacula/src/dird/migrate.c index 5e361c6205..8ab5923e45 100644 --- a/bacula/src/dird/migrate.c +++ b/bacula/src/dird/migrate.c @@ -1158,12 +1158,16 @@ void migration_cleanup(JCR *jcr, int TermCode) /* * If we terminated a copy normally: * - copy any Log records to the new JobId + * - set type="Job Copy" for the new job */ if (jcr->get_JobType() == JT_COPY && jcr->JobStatus == JS_Terminated) { /* Copy JobLog to new JobId */ Mmsg(query, "INSERT INTO Log (JobId, Time, LogText ) " "SELECT %s, Time, LogText FROM Log WHERE JobId=%s", - new_jobid, old_jobid); + edit_uint64(mig_jcr->jr.JobId, ec7), old_jobid); + db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL); + Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s", + (char)JT_JOB_COPY, ec7); db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL); } diff --git a/bacula/src/dird/ua_cmds.c b/bacula/src/dird/ua_cmds.c index 7ae9ae8bae..920cbfe87f 100644 --- a/bacula/src/dird/ua_cmds.c +++ b/bacula/src/dird/ua_cmds.c @@ -123,7 +123,7 @@ static struct cmdstruct commands[] = { /* C { NT_("exit"), quit_cmd, _("exit = quit"), false}, { NT_("gui"), gui_cmd, _("gui [on|off] -- non-interactive gui mode"), false}, { NT_("help"), help_cmd, _("print this command"), false}, - { NT_("list"), list_cmd, _("list [pools | jobs | jobtotals | media | files ]; from catalog"), true}, + { NT_("list"), list_cmd, _("list [pools | jobs | jobtotals | media | files | copies ]; from catalog"), true}, { NT_("label"), label_cmd, _("label a tape"), false}, { NT_("llist"), llist_cmd, _("full or long list like list command"), true}, { NT_("messages"), messagescmd, _("messages"), false}, diff --git a/bacula/src/dird/ua_output.c b/bacula/src/dird/ua_output.c index 1afe0484f1..1efb461a8f 100644 --- a/bacula/src/dird/ua_output.c +++ b/bacula/src/dird/ua_output.c @@ -223,6 +223,7 @@ bail_out: * list clients - list clients * list nextvol job=xx - list the next vol to be used by job * list nextvolume job=xx - same as above. + * list copies jobid=x,y,z * */ @@ -454,6 +455,19 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) } } list_nextvol(ua, n); + } else if (strcasecmp(ua->argk[i], NT_("copies")) == 0) { + char *jobids=NULL; + uint32_t limit=0; + for (j=i+1; jargc; j++) { + if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) { + if (is_a_number_list(ua->argv[j])) { + jobids = ua->argv[j]; + } + } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) { + limit = atoi(ua->argv[j]); + } + } + db_list_copies_records(ua->jcr,ua->db,limit,jobids,prtit,ua,llist); } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0 || strcasecmp(ua->argk[i], NT_("days")) == 0) { /* Ignore it */ diff --git a/bacula/src/dird/ua_purge.c b/bacula/src/dird/ua_purge.c index 9d2276801f..8bdc1d683c 100644 --- a/bacula/src/dird/ua_purge.c +++ b/bacula/src/dird/ua_purge.c @@ -359,6 +359,57 @@ void purge_files_from_job_list(UAContext *ua, del_ctx &del) } } +/* + * Change the type of the next copy job to backup. + * We need to upgrade the next copy of a normal job, + * and also upgrade the next copy when the normal job + * already have been purged. + * + * JobId: 1 PriorJobId: 0 (original) + * JobId: 2 PriorJobId: 1 (first copy) + * JobId: 3 PriorJobId: 1 (second copy) + * + * JobId: 2 PriorJobId: 1 (first copy, now regular backup) + * JobId: 3 PriorJobId: 1 (second copy) + * + * => Search through PriorJobId in jobid and + * PriorJobId in PriorJobId (jobid) + */ +void upgrade_copies(UAContext *ua, char *jobs) +{ + POOL_MEM query(PM_MESSAGE); + + db_lock(ua->db); + /* Do it in two times for mysql */ + Mmsg(query, "CREATE TEMPORARY TABLE cpy_tmp AS " + "SELECT MIN(JobId) AS JobId FROM Job " /* Choose the oldest job */ + "WHERE Type='%c' " + "AND ( PriorJobId IN (%s) " + "OR " + " PriorJobId IN ( " + "SELECT PriorJobId " + "FROM Job " + "WHERE JobId IN (%s) " + " AND Type='B' " + ") " + ") " + "GROUP BY PriorJobId ", /* one result per copy */ + JT_JOB_COPY, jobs, jobs); + db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL); + + /* Now upgrade first copy to Backup */ + Mmsg(query, "UPDATE Job SET Type='B' " /* JT_JOB_COPY => JT_BACKUP */ + "WHERE JobId IN ( SELECT JobId FROM cpy_tmp )"); + + db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL); + + Mmsg(query, "DROP TABLE cpy_tmp"); + db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL); + + db_unlock(ua->db); + Dmsg1(00, "Upgrade copies Log sql=%s\n", query.c_str()); +} + /* * Remove all records from catalog for a list of JobIds */ @@ -377,13 +428,15 @@ void purge_jobs_from_catalog(UAContext *ua, char *jobs) db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL); Dmsg1(050, "Delete Log sql=%s\n", query.c_str()); + upgrade_copies(ua, jobs); + /* Now remove the Job record itself */ Mmsg(query, "DELETE FROM Job WHERE JobId IN (%s)", jobs); db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL); + Dmsg1(050, "Delete Job sql=%s\n", query.c_str()); } - void purge_files_from_volume(UAContext *ua, MEDIA_DBR *mr ) {} /* ***FIXME*** implement */ diff --git a/bacula/src/dird/ua_restore.c b/bacula/src/dird/ua_restore.c index 7e4d8d3183..f9430b9dad 100644 --- a/bacula/src/dird/ua_restore.c +++ b/bacula/src/dird/ua_restore.c @@ -444,6 +444,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) "add_suffix", /* 17 */ "regexwhere", /* 18 */ "restoreclient", /* 19 */ + "copies", /* 20 */ NULL }; @@ -1138,9 +1139,10 @@ static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *dat bool ok = false; FILESET_DBR fsr; CLIENT_DBR cr; + POOL_MEM other_filter(PM_MESSAGE); + POOL_MEM temp_filter(PM_MESSAGE); char fileset_name[MAX_NAME_LENGTH]; char ed1[50], ed2[50]; - char pool_select[MAX_NAME_LENGTH]; int i; /* Create temp tables */ @@ -1196,23 +1198,32 @@ static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *dat } /* If Pool specified, add PoolId specification */ - pool_select[0] = 0; if (rx->pool) { POOL_DBR pr; memset(&pr, 0, sizeof(pr)); bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name)); if (db_get_pool_record(ua->jcr, ua->db, &pr)) { - bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ", - edit_int64(pr.PoolId, ed1)); + Mmsg(other_filter, " AND Media.PoolId=%s ", + edit_int64(pr.PoolId, ed1)); } else { ua->warning_msg(_("Pool \"%s\" not found, using any pool.\n"), pr.Name); } } + /* include copies or not in job selection */ + if (find_arg(ua, NT_("copies")) > 0) { + Mmsg(temp_filter, "%s AND Job.Type IN ('%c', '%c') ", + other_filter.c_str(), (char)JT_BACKUP, (char)JT_JOB_COPY); + } else { + Mmsg(temp_filter, "%s AND Job.Type = '%c' ", other_filter.c_str(), + (char)JT_BACKUP); + } + pm_strcpy(other_filter, temp_filter.c_str()); /* Find JobId of last Full backup for this client, fileset */ edit_int64(cr.ClientId, ed1); Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet, - pool_select); + other_filter.c_str()); + Dmsg1(0, "sql=%s\n", rx->query); if (!db_sql_query(ua->db, rx->query, NULL, NULL)) { ua->error_msg("%s\n", db_strerror(ua->db)); goto bail_out; @@ -1238,12 +1249,13 @@ static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *dat /* Now find most recent Differental Job after Full save, if any */ Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date, - edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select); + edit_int64(cr.ClientId, ed2), fsr.FileSet, other_filter.c_str()); if (!db_sql_query(ua->db, rx->query, NULL, NULL)) { ua->warning_msg("%s\n", db_strerror(ua->db)); } /* Now update JobTDate to lock onto Differental, if any */ rx->JobTDate = 0; + Dmsg1(0, "sql=%s\n", rx->query); if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) { ua->warning_msg("%s\n", db_strerror(ua->db)); } @@ -1254,7 +1266,8 @@ static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *dat /* Now find all Incremental Jobs after Full/dif save */ Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date, - edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select); + edit_int64(cr.ClientId, ed2), fsr.FileSet, other_filter.c_str()); + Dmsg1(0, "sql=%s\n", rx->query); if (!db_sql_query(ua->db, rx->query, NULL, NULL)) { ua->warning_msg("%s\n", db_strerror(ua->db)); } @@ -1267,6 +1280,8 @@ static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *dat } if (rx->JobIds[0] != 0) { + /* Display a list of all copies */ + db_list_copies_records(ua->jcr, ua->db, 0, rx->JobIds, prtit, ua, HORZ_LIST); /* Display a list of Jobs selected for this restore */ db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST); ok = true; diff --git a/bacula/src/jcr.h b/bacula/src/jcr.h index fcda30cdb7..d0a0cb63f9 100644 --- a/bacula/src/jcr.h +++ b/bacula/src/jcr.h @@ -60,11 +60,12 @@ #define JT_MIGRATED_JOB 'M' /* A previous backup job that was migrated */ #define JT_VERIFY 'V' /* Verify Job */ #define JT_RESTORE 'R' /* Restore Job */ -#define JT_CONSOLE 'c' /* console program */ +#define JT_CONSOLE 'U' /* console program */ #define JT_SYSTEM 'I' /* internal system "job" */ #define JT_ADMIN 'D' /* admin job */ #define JT_ARCHIVE 'A' /* Archive Job */ -#define JT_COPY 'C' /* Copy Job */ +#define JT_JOB_COPY 'C' /* Copy of a Job */ +#define JT_COPY 'c' /* Copy Job */ #define JT_MIGRATE 'g' /* Migration Job */ #define JT_SCAN 'S' /* Scan Job */ diff --git a/bacula/src/lib/edit.c b/bacula/src/lib/edit.c index 480253920f..8b13435f5f 100644 --- a/bacula/src/lib/edit.c +++ b/bacula/src/lib/edit.c @@ -406,6 +406,27 @@ bool is_a_number(const char *n) return digit_seen && *n==0; } +/* + * Check if specified string is a list of number or not + */ +bool is_a_number_list(const char *n) +{ + bool previous_digit = false; + bool digit_seen = false; + while (*n) { + if (B_ISDIGIT(*n)) { + previous_digit=true; + digit_seen = true; + } else if (*n == ',' && previous_digit) { + previous_digit = false; + } else { + return false; + } + n++; + } + return digit_seen && *n==0; +} + /* * Check if the specified string is an integer */ diff --git a/bacula/src/lib/protos.h b/bacula/src/lib/protos.h index fa662a4881..d2efd2c9fa 100644 --- a/bacula/src/lib/protos.h +++ b/bacula/src/lib/protos.h @@ -186,6 +186,7 @@ bool duration_to_utime (char *str, utime_t *value); bool size_to_uint64(char *str, int str_len, uint64_t *rtn_value); char *edit_utime (utime_t val, char *buf, int buf_len); bool is_a_number (const char *num); +bool is_a_number_list (const char *n); bool is_an_integer (const char *n); bool is_name_valid (char *name, POOLMEM **msg); diff --git a/bacula/src/lib/util.c b/bacula/src/lib/util.c index 1d93d1b542..a19ba34737 100644 --- a/bacula/src/lib/util.c +++ b/bacula/src/lib/util.c @@ -361,6 +361,9 @@ const char *job_type_to_str(int type) case JT_COPY: str = _("Copy"); break; + case JT_JOB_COPY: + str = _("Job Copy"); + break; case JT_CONSOLE: str = _("Console"); break; diff --git a/bacula/technotes-2.5 b/bacula/technotes-2.5 index 3210297dc0..f6cf7f6ecd 100644 --- a/bacula/technotes-2.5 +++ b/bacula/technotes-2.5 @@ -11,6 +11,11 @@ mixed priorities General: 20Dec08 +ebl Work on copy jobs + - Add "list copies" command + - Add JT_JOB_COPY type for job copies + - Don't allow copy jobs in automatic restore + - Promote next copy job as backup when original job is deleted kes Closed bug #1207 -- 2.4.4-b1 strange volume/device handling kes Closed bug #1204 -- Undescriptive help options kes Closed bug #1202 -- Revise documentation -- 2.39.2