X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fdird%2Fmigrate.c;h=357250b3348bd826814674f494d8bae965cdf0a8;hb=04d2e746550ef971dcbd3253026b1c97979f3bbe;hp=07d90fe75da53cd62c44500c73d3070f890cbf6d;hpb=bfa001f12e03fb616e294bf6a232272708dc4d82;p=bacula%2Fbacula diff --git a/bacula/src/dird/migrate.c b/bacula/src/dird/migrate.c index 07d90fe75d..357250b334 100644 --- a/bacula/src/dird/migrate.c +++ b/bacula/src/dird/migrate.c @@ -1,12 +1,12 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2004-2008 Free Software Foundation Europe e.V. + Copyright (C) 2004-2010 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. This program is Free Software; you can redistribute it and/or - modify it under the terms of version two of the GNU General Public + modify it under the terms of version three of the GNU Affero General Public License as published by the Free Software Foundation and included in the file LICENSE. @@ -15,7 +15,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Affero General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -41,7 +41,6 @@ * to do the backup. * When the Storage daemon finishes the job, update the DB. * - * Version $Id$ */ #include "bacula.h" @@ -55,16 +54,17 @@ static const int dbglevel = 10; -static char OKbootstrap[] = "3000 OK bootstrap\n"; -static int get_job_to_migrate(JCR *jcr); +static int getJob_to_migrate(JCR *jcr); struct idpkt; static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1, const char *query2, const char *type); static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1, const char *type); static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type); +static bool find_jobids_of_pool_uncopied_jobs(JCR *jcr, idpkt *ids); static void start_migration_job(JCR *jcr); static int get_next_dbid_from_list(char **p, DBId_t *DBId); +static bool set_migration_next_pool(JCR *jcr, POOL **pool); /* * Called here before the job is run to do the job @@ -96,9 +96,7 @@ static int get_next_dbid_from_list(char **p, DBId_t *DBId); */ bool do_migration_init(JCR *jcr) { - POOL_DBR pr; - POOL *pool; - char ed1[100]; + POOL *pool = NULL; JOB *job, *prev_job; JCR *mig_jcr; /* newly migrated job */ int count; @@ -128,39 +126,45 @@ bool do_migration_init(JCR *jcr) Dmsg2(dbglevel, "Read pool=%s (From %s)\n", jcr->rpool->name(), jcr->rpool_source); + if (!get_or_create_fileset_record(jcr)) { + Dmsg1(dbglevel, "JobId=%d no FileSet\n", (int)jcr->JobId); + Jmsg(jcr, M_FATAL, 0, _("Could not get or create the FileSet record.\n")); + return false; + } + /* If we find a job or jobs to migrate it is previous_jr.JobId */ - count = get_job_to_migrate(jcr); + count = getJob_to_migrate(jcr); if (count < 0) { return false; } if (count == 0) { - return true; + set_migration_next_pool(jcr, &pool); + return true; /* no work */ } - Dmsg1(dbglevel, "Back from get_job_to_migrate JobId=%d\n", (int)jcr->JobId); + Dmsg1(dbglevel, "Back from getJob_to_migrate JobId=%d\n", (int)jcr->JobId); if (jcr->previous_jr.JobId == 0) { Dmsg1(dbglevel, "JobId=%d no previous JobId\n", (int)jcr->JobId); - Jmsg(jcr, M_INFO, 0, _("No previous Job found to migrate.\n")); + Jmsg(jcr, M_INFO, 0, _("No previous Job found to %s.\n"), jcr->get_ActionName(0)); + set_migration_next_pool(jcr, &pool); return true; /* no work */ } - if (!get_or_create_fileset_record(jcr)) { - Dmsg1(dbglevel, "JobId=%d no FileSet\n", (int)jcr->JobId); - Jmsg(jcr, M_FATAL, 0, _("Could not get or create the FileSet record.\n")); + if (create_restore_bootstrap_file(jcr) < 0) { + Jmsg(jcr, M_FATAL, 0, _("Create bootstrap file failed.\n")); return false; } - create_restore_bootstrap_file(jcr); - if (jcr->previous_jr.JobId == 0 || jcr->ExpectedFiles == 0) { set_jcr_job_status(jcr, JS_Terminated); Dmsg1(dbglevel, "JobId=%d expected files == 0\n", (int)jcr->JobId); if (jcr->previous_jr.JobId == 0) { - Jmsg(jcr, M_INFO, 0, _("No previous Job found to migrate.\n")); + Jmsg(jcr, M_INFO, 0, _("No previous Job found to %s.\n"), jcr->get_ActionName(0)); } else { - Jmsg(jcr, M_INFO, 0, _("Previous Job has no data to migrate.\n")); + Jmsg(jcr, M_INFO, 0, _("Previous Job has no data to %s.\n"), jcr->get_ActionName(0)); } + set_migration_next_pool(jcr, &pool); return true; /* no work */ } @@ -186,7 +190,7 @@ bool do_migration_init(JCR *jcr) jcr->spool_data = job->spool_data; /* turn on spooling if requested in job */ - /* Create a migation jcr */ + /* Create a migration jcr */ mig_jcr = jcr->mig_jcr = new_jcr(sizeof(JCR), dird_free_jcr); memcpy(&mig_jcr->previous_jr, &jcr->previous_jr, sizeof(mig_jcr->previous_jr)); @@ -206,16 +210,44 @@ bool do_migration_init(JCR *jcr) mig_jcr->jr.PoolId = jcr->jr.PoolId; mig_jcr->jr.JobId = mig_jcr->JobId; + /* Don't let WatchDog checks Max*Time value on this Job */ + mig_jcr->no_maxtime = true; + Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n", mig_jcr->jr.Name, (int)mig_jcr->jr.JobId, mig_jcr->jr.JobType, mig_jcr->jr.JobLevel); + if (set_migration_next_pool(jcr, &pool)) { + /* If pool storage specified, use it for restore */ + copy_rstorage(mig_jcr, pool->storage, _("Pool resource")); + copy_rstorage(jcr, pool->storage, _("Pool resource")); + + mig_jcr->pool = jcr->pool; + mig_jcr->jr.PoolId = jcr->jr.PoolId; + } + + return true; +} + + +/* + * set_migration_next_pool() called by do_migration_init() + * at differents stages. + * The idea here is tofactorize the NextPool's search code and + * to permit do_migration_init() to return with NextPool set in jcr struct. + */ +static bool set_migration_next_pool(JCR *jcr, POOL **retpool) +{ + POOL_DBR pr; + POOL *pool; + char ed1[100]; + /* * Get the PoolId used with the original job. Then * find the pool name from the database record. */ memset(&pr, 0, sizeof(pr)); - pr.PoolId = mig_jcr->previous_jr.PoolId; + pr.PoolId = jcr->jr.PoolId; if (!db_get_pool_record(jcr, jcr->db, &pr)) { Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"), edit_int64(pr.PoolId, ed1), db_strerror(jcr->db)); @@ -223,15 +255,12 @@ bool do_migration_init(JCR *jcr) } /* Get the pool resource corresponding to the original job */ pool = (POOL *)GetResWithName(R_POOL, pr.Name); + *retpool = pool; if (!pool) { Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name); return false; } - /* If pool storage specified, use it for restore */ - copy_rstorage(mig_jcr, pool->storage, _("Pool resource")); - copy_rstorage(jcr, pool->storage, _("Pool resource")); - /* * If the original backup pool has a NextPool, make sure a * record exists in the database. Note, in this case, we @@ -246,14 +275,15 @@ bool do_migration_init(JCR *jcr) if (!set_migration_wstorage(jcr, pool)) { return false; } - mig_jcr->pool = jcr->pool = pool->NextPool; + jcr->pool = pool->NextPool; pm_strcpy(jcr->pool_source, _("Job Pool's NextPool resource")); - mig_jcr->jr.PoolId = jcr->jr.PoolId; Dmsg2(dbglevel, "Write pool=%s read rpool=%s\n", jcr->pool->name(), jcr->rpool->name()); + return true; } + /* * Do a Migration of a previous job * @@ -277,15 +307,21 @@ bool do_migration(JCR *jcr) } if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) { - Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"), + Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to %s. ERR=%s"), edit_int64(jcr->previous_jr.JobId, ed1), + jcr->get_ActionName(0), db_strerror(jcr->db)); set_jcr_job_status(jcr, JS_Terminated); migration_cleanup(jcr, jcr->JobStatus); return true; } /* Make sure this job was not already migrated */ - if (jcr->previous_jr.JobType != JT_BACKUP) { + if (jcr->previous_jr.JobType != JT_BACKUP && + jcr->previous_jr.JobType != JT_JOB_COPY) { + Jmsg(jcr, M_INFO, 0, _("JobId %s already %s probably by another Job. %s stopped.\n"), + edit_int64(jcr->previous_jr.JobId, ed1), + jcr->get_ActionName(1), + jcr->get_OperationName()); set_jcr_job_status(jcr, JS_Terminated); migration_cleanup(jcr, jcr->JobStatus); return true; @@ -293,8 +329,7 @@ bool do_migration(JCR *jcr) /* Print Job Start message */ Jmsg(jcr, M_INFO, 0, _("Start %s JobId %s, Job=%s\n"), - jcr->get_JobType() == JT_MIGRATE ? "Migration" : "Copy", - edit_uint64(jcr->JobId, ed1), jcr->Job); + jcr->get_OperationName(), edit_uint64(jcr->JobId, ed1), jcr->Job); /* * Open a message channel connection with the Storage @@ -318,20 +353,12 @@ bool do_migration(JCR *jcr) Dmsg2(dbglevel, "Read store=%s, write store=%s\n", ((STORE *)jcr->rstorage->first())->name(), ((STORE *)jcr->wstorage->first())->name()); - if (((STORE *)jcr->rstorage->first())->name() == ((STORE *)jcr->wstorage->first())->name()) { - Jmsg(jcr, M_FATAL, 0, _("Read storage \"%s\" same as write storage.\n"), - ((STORE *)jcr->rstorage->first())->name()); - return false; - } - if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage)) { + + if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage, /*send_bsr*/true)) { return false; } Dmsg0(150, "Storage daemon connection OK\n"); - if (!send_bootstrap_file(jcr, sd) || - !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) { - return false; - } /* * We re-update the job start record so that the start @@ -392,7 +419,7 @@ bool do_migration(JCR *jcr) set_jcr_job_status(mig_jcr, JS_Running); /* Pickup Job termination data */ - /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */ + /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */ wait_for_storage_daemon_termination(jcr); set_jcr_job_status(jcr, jcr->SDJobStatus); db_write_batch_file_records(jcr); /* used by bulk batch file insert */ @@ -401,14 +428,7 @@ bool do_migration(JCR *jcr) } migration_cleanup(jcr, jcr->JobStatus); - if (jcr->get_JobType() == JT_MIGRATE && mig_jcr) { - char jobid[50]; - UAContext *ua = new_ua_context(jcr); - edit_uint64(jcr->previous_jr.JobId, jobid); - /* Purge all old file records, but leave Job record */ - purge_files_from_jobs(ua, jobid); - free_ua_context(ua); - } + return true; } @@ -460,6 +480,12 @@ static int unique_dbid_handler(void *ctx, int num_fields, char **row) { idpkt *ids = (idpkt *)ctx; + /* Sanity check */ + if (!row || !row[0]) { + Dmsg0(dbglevel, "dbid_hdlr error empty row\n"); + return 1; /* stop calling us */ + } + add_unique_id(ids, row[0]); Dmsg3(dbglevel, "dbid_hdlr count=%d Ids=%p %s\n", ids->count, ids->list, ids->list); return 0; @@ -518,7 +544,8 @@ const char *sql_client = const char *sql_jobids_from_client = "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool,Client" " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId" - " AND Job.ClientId=Client.ClientId AND Job.Type='B'" + " AND Job.ClientId=Client.ClientId AND Job.Type IN ('B','C')" + " AND Job.JobStatus IN ('T','W')" " ORDER by Job.StartTime"; /* Get Volume names in Pool */ @@ -531,10 +558,10 @@ const char *sql_vol = const char *sql_jobids_from_vol = "SELECT DISTINCT Job.JobId,Job.StartTime FROM Media,JobMedia,Job" " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId" - " AND JobMedia.JobId=Job.JobId AND Job.Type='B'" + " AND JobMedia.JobId=Job.JobId AND Job.Type IN ('B','C')" + " AND Job.JobStatus IN ('T','W') AND Media.Enabled=1" " ORDER by Job.StartTime"; - const char *sql_smallest_vol = "SELECT Media.MediaId FROM Media,Pool,JobMedia WHERE" " Media.MediaId in (SELECT DISTINCT MediaId from JobMedia) AND" @@ -553,20 +580,22 @@ const char *sql_oldest_vol = const char *sql_jobids_from_mediaid = "SELECT DISTINCT Job.JobId,Job.StartTime FROM JobMedia,Job" " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId IN (%s)" - " AND Job.Type='B'" + " AND Job.Type IN ('B','C') AND Job.JobStatus IN ('T','W')" " ORDER by Job.StartTime"; -/* Get tne number of bytes in the pool */ +/* Get the number of bytes in the pool */ const char *sql_pool_bytes = - "SELECT SUM(VolBytes) FROM Media,Pool WHERE" + "SELECT SUM(JobBytes) FROM Job WHERE JobId IN" + " (SELECT DISTINCT Job.JobId from Pool,Job,Media,JobMedia WHERE" + " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND" " VolStatus in ('Full','Used','Error','Append') AND Media.Enabled=1 AND" - " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"; + " Job.Type IN ('B','C') AND Job.JobStatus IN ('T','W') AND" + " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId)"; -/* Get tne number of bytes in the Jobs */ +/* Get the number of bytes in the Jobs */ const char *sql_job_bytes = "SELECT SUM(JobBytes) FROM Job WHERE JobId IN (%s)"; - /* Get Media Ids in Pool */ const char *sql_mediaids = "SELECT MediaId FROM Media,Pool WHERE" @@ -575,13 +604,25 @@ const char *sql_mediaids = /* Get JobIds in Pool longer than specified time */ const char *sql_pool_time = - "SELECT DISTINCT Job.JobId from Pool,Job,Media,JobMedia WHERE" + "SELECT DISTINCT Job.JobId FROM Pool,Job,Media,JobMedia WHERE" " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND" - " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND" - " Job.Type='B' AND" + " VolStatus IN ('Full','Used','Error') AND Media.Enabled=1 AND" + " Job.Type IN ('B','C') AND Job.JobStatus IN ('T','W') AND" " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId" " AND Job.RealEndTime<='%s'"; +/* Get JobIds from successfully completed backup jobs which have not been copied before */ +const char *sql_jobids_of_pool_uncopied_jobs = + "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool" + " WHERE Pool.Name = '%s' AND Pool.PoolId = Job.PoolId" + " AND Job.Type = 'B' AND Job.JobStatus IN ('T','W')" + " AND Job.jobBytes > 0" + " AND Job.JobId NOT IN" + " (SELECT PriorJobId FROM Job WHERE" + " Type IN ('B','C') AND Job.JobStatus IN ('T','W')" + " AND PriorJobId != 0)" + " ORDER by Job.StartTime"; + /* * const char *sql_ujobid = * "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia " @@ -589,8 +630,6 @@ const char *sql_pool_time = * " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId"; */ - - /* * * This is the central piece of code that finds a job or jobs @@ -608,7 +647,7 @@ const char *sql_pool_time = * 0 if no jobs to migrate * 1 if OK and jcr->previous_jr filled in */ -static int get_job_to_migrate(JCR *jcr) +static int getJob_to_migrate(JCR *jcr) { char ed1[30], ed2[30]; POOL_MEM query(PM_MESSAGE); @@ -663,7 +702,7 @@ static int get_job_to_migrate(JCR *jcr) break; case MT_SQLQUERY: if (!jcr->job->selection_pattern) { - Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n")); + Jmsg(jcr, M_FATAL, 0, _("No %s SQL selection pattern specified.\n"), jcr->get_OperationName()); goto bail_out; } Dmsg1(dbglevel, "SQL=%s\n", jcr->job->selection_pattern); @@ -684,7 +723,6 @@ static int get_job_to_migrate(JCR *jcr) goto bail_out; } break; - case MT_POOL_OCCUPANCY: ctx.count = 0; /* Find count of bytes in pool */ @@ -694,14 +732,14 @@ static int get_job_to_migrate(JCR *jcr) goto bail_out; } if (ctx.count == 0) { - Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n")); + Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0)); goto ok_out; } pool_bytes = ctx.value; Dmsg2(dbglevel, "highbytes=%lld pool=%lld\n", jcr->rpool->MigrationHighBytes, pool_bytes); if (pool_bytes < (int64_t)jcr->rpool->MigrationHighBytes) { - Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n")); + Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0)); goto ok_out; } Dmsg0(dbglevel, "We should do Occupation migration.\n"); @@ -715,12 +753,12 @@ static int get_job_to_migrate(JCR *jcr) goto bail_out; } if (ids.count == 0) { - Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n")); + Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0)); goto ok_out; } Dmsg2(dbglevel, "Pool Occupancy ids=%d MediaIds=%s\n", ids.count, ids.list); - if (!find_jobids_from_mediaid_list(jcr, &ids, "Volumes")) { + if (!find_jobids_from_mediaid_list(jcr, &ids, "Volume")) { goto bail_out; } /* ids == list of jobs */ @@ -751,10 +789,10 @@ static int get_job_to_migrate(JCR *jcr) goto bail_out; } pool_bytes -= ctx.value; - Dmsg1(dbglevel, "Total migrate Job bytes=%s\n", edit_int64(ctx.value, ed1)); + Dmsg2(dbglevel, "Total %s Job bytes=%s\n", jcr->get_ActionName(0), edit_int64_with_commas(ctx.value, ed1)); Dmsg2(dbglevel, "lowbytes=%s poolafter=%s\n", - edit_int64(jcr->rpool->MigrationLowBytes, ed1), - edit_int64(pool_bytes, ed2)); + edit_int64_with_commas(jcr->rpool->MigrationLowBytes, ed1), + edit_int64_with_commas(pool_bytes, ed2)); if (pool_bytes <= (int64_t)jcr->rpool->MigrationLowBytes) { Dmsg0(dbglevel, "We should be done.\n"); break; @@ -765,7 +803,6 @@ static int get_job_to_migrate(JCR *jcr) pm_strcpy(ids.list, jids.list); Dmsg2(dbglevel, "Pool Occupancy ids=%d JobIds=%s\n", ids.count, ids.list); break; - case MT_POOL_TIME: ttime = time(NULL) - (time_t)jcr->rpool->MigrationTime; (void)localtime_r(&ttime, &tm); @@ -779,14 +816,18 @@ static int get_job_to_migrate(JCR *jcr) goto bail_out; } if (ids.count == 0) { - Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n")); + Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0)); goto ok_out; } Dmsg2(dbglevel, "PoolTime ids=%d JobIds=%s\n", ids.count, ids.list); break; - + case MT_POOL_UNCOPIED_JOBS: + if (!find_jobids_of_pool_uncopied_jobs(jcr, &ids)) { + goto bail_out; + } + break; default: - Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n")); + Jmsg(jcr, M_FATAL, 0, _("Unknown %s Selection Type.\n"), jcr->get_OperationName()); goto bail_out; } } @@ -798,28 +839,29 @@ static int get_job_to_migrate(JCR *jcr) */ p = ids.list; if (ids.count == 0) { - Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n")); + Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName(0)); goto ok_out; } - Jmsg(jcr, M_INFO, 0, _("The following %u JobId%s were chosen to be migrated: %s\n"), - ids.count, ids.count==0?"":"s", ids.list); + Jmsg(jcr, M_INFO, 0, _("The following %u JobId%s chosen to be %s: %s\n"), + ids.count, (ids.count < 2) ? _(" was") : _("s were"), + jcr->get_ActionName(1), ids.list); Dmsg2(dbglevel, "Before loop count=%d ids=%s\n", ids.count, ids.list); for (int i=1; i < (int)ids.count; i++) { JobId = 0; stat = get_next_jobid_from_list(&p, &JobId); - Dmsg3(dbglevel, "get_jobid_no=%d stat=%d JobId=%u\n", i, stat, JobId); - jcr->MigrateJobId = JobId; - start_migration_job(jcr); - Dmsg0(dbglevel, "Back from start_migration_job\n"); + Dmsg3(dbglevel, "getJobid_no=%d stat=%d JobId=%u\n", i, stat, JobId); if (stat < 0) { Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n")); goto bail_out; } else if (stat == 0) { - Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n")); + Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName(0)); goto ok_out; } + jcr->MigrateJobId = JobId; + start_migration_job(jcr); + Dmsg0(dbglevel, "Back from start_migration_job\n"); } /* Now get the last JobId and handle it in the current job */ @@ -830,7 +872,7 @@ static int get_job_to_migrate(JCR *jcr) Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n")); goto bail_out; } else if (stat == 0) { - Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n")); + Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName(0)); goto ok_out; } @@ -838,14 +880,17 @@ static int get_job_to_migrate(JCR *jcr) Dmsg1(dbglevel, "Previous jobid=%d\n", (int)jcr->previous_jr.JobId); if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) { - Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"), + Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to %s. ERR=%s"), edit_int64(jcr->previous_jr.JobId, ed1), + jcr->get_ActionName(0), db_strerror(jcr->db)); goto bail_out; } - Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%s Job=%s\n"), + Jmsg(jcr, M_INFO, 0, _("%s using JobId=%s Job=%s\n"), + jcr->get_OperationName(), edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job); - Dmsg3(dbglevel, "Migration JobId=%d using JobId=%s Job=%s\n", + Dmsg4(dbglevel, "%s JobId=%d using JobId=%s Job=%s\n", + jcr->get_OperationName(), jcr->JobId, edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job); count = 1; @@ -868,15 +913,15 @@ static void start_migration_job(JCR *jcr) UAContext *ua = new_ua_context(jcr); char ed1[50]; ua->batch = true; - Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name, + Mmsg(ua->cmd, "run job=\"%s\" jobid=%s", jcr->job->name(), edit_uint64(jcr->MigrateJobId, ed1)); - Dmsg1(dbglevel, "=============== Migration cmd=%s\n", ua->cmd); + Dmsg2(dbglevel, "=============== %s cmd=%s\n", jcr->get_OperationName(), ua->cmd); parse_ua_args(ua); /* parse command */ JobId_t jobid = run_cmd(ua, ua->cmd); if (jobid == 0) { Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n")); } else { - Jmsg(jcr, M_INFO, 0, _("Migration JobId %d started.\n"), (int)jobid); + Jmsg(jcr, M_INFO, 0, _("%s JobId %d started.\n"), jcr->get_OperationName(), (int)jobid); } free_ua_context(ua); } @@ -895,7 +940,7 @@ static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1, goto bail_out; } if (ids->count == 0) { - Jmsg(jcr, M_INFO, 0, _("No %s found to migrate.\n"), type); + Jmsg(jcr, M_INFO, 0, _("No %s found to %s.\n"), type, jcr->get_ActionName(0)); ok = true; /* Not an error */ goto bail_out; } else if (ids->count != 1) { @@ -928,7 +973,39 @@ static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type goto bail_out; } if (ids->count == 0) { - Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type); + Jmsg(jcr, M_INFO, 0, _("No %ss found to %s.\n"), type, jcr->get_ActionName(0)); + } + ok = true; + +bail_out: + return ok; +} + +/* + * This routine returns: + * false if an error occurred + * true otherwise + * ids.count number of jobids found (may be zero) + */ +static bool find_jobids_of_pool_uncopied_jobs(JCR *jcr, idpkt *ids) +{ + bool ok = false; + POOL_MEM query(PM_MESSAGE); + + /* Only a copy job is allowed */ + if (jcr->getJobType() != JT_COPY) { + Jmsg(jcr, M_FATAL, 0, + _("Selection Type 'pooluncopiedjobs' only applies to Copy Jobs")); + goto bail_out; + } + + Dmsg1(dbglevel, "copy selection pattern=%s\n", jcr->rpool->name()); + Mmsg(query, sql_jobids_of_pool_uncopied_jobs, jcr->rpool->name()); + Dmsg1(dbglevel, "get uncopied jobs query=%s\n", query.c_str()); + if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) { + Jmsg(jcr, M_FATAL, 0, + _("SQL to get uncopied jobs failed. ERR=%s\n"), db_strerror(jcr->db)); + goto bail_out; } ok = true; @@ -950,8 +1027,8 @@ static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1, item_chain = New(dlist(item, &item->link)); if (!jcr->job->selection_pattern) { - Jmsg(jcr, M_FATAL, 0, _("No Migration %s selection pattern specified.\n"), - type); + Jmsg(jcr, M_FATAL, 0, _("No %s %s selection pattern specified.\n"), + jcr->get_OperationName(), type); goto bail_out; } Dmsg1(dbglevel, "regex-sel-pattern=%s\n", jcr->job->selection_pattern); @@ -966,8 +1043,8 @@ static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1, } Dmsg1(dbglevel, "query1 returned %d names\n", item_chain->size()); if (item_chain->size() == 0) { - Jmsg(jcr, M_INFO, 0, _("Query of Pool \"%s\" returned no Jobs to migrate.\n"), - jcr->rpool->name()); + Jmsg(jcr, M_INFO, 0, _("Query of Pool \"%s\" returned no Jobs to %s.\n"), + jcr->rpool->name(), jcr->get_ActionName(0)); ok = true; goto bail_out; /* skip regex match */ } else { @@ -1004,7 +1081,7 @@ static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1, regfree(&preg); } if (item_chain->size() == 0) { - Jmsg(jcr, M_INFO, 0, _("Regex pattern matched no Jobs to migrate.\n")); + Jmsg(jcr, M_INFO, 0, _("Regex pattern matched no Jobs to %s.\n"), jcr->get_ActionName(0)); ok = true; goto bail_out; /* skip regex match */ } @@ -1026,7 +1103,7 @@ static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1, } } if (ids->count == 0) { - Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type); + Jmsg(jcr, M_INFO, 0, _("No %ss found to %s.\n"), type, jcr->get_ActionName(0)); } ok = true; @@ -1039,7 +1116,6 @@ bail_out: return ok; } - /* * Release resources allocated during backup. */ @@ -1066,6 +1142,11 @@ void migration_cleanup(JCR *jcr, int TermCode) * mig_jcr is jcr of the newly migrated job. */ if (mig_jcr) { + char old_jobid[50], new_jobid[50]; + + edit_uint64(jcr->previous_jr.JobId, old_jobid); + edit_uint64(mig_jcr->jr.JobId, new_jobid); + mig_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles; mig_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes; mig_jcr->VolSessionId = jcr->VolSessionId; @@ -1080,13 +1161,42 @@ void migration_cleanup(JCR *jcr, int TermCode) "JobTDate=%s WHERE JobId=%s", jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, edit_uint64(jcr->previous_jr.JobTDate, ec1), - edit_uint64(mig_jcr->jr.JobId, ec2)); + new_jobid); db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL); - /* Now mark the previous job as migrated if it terminated normally */ - if (jcr->get_JobType() == JT_MIGRATE && jcr->JobStatus == JS_Terminated) { + /* + * If we terminated a migration normally: + * - mark the previous job as migrated + * - move any Log records to the new JobId + * - Purge the File records from the previous job + */ + if (jcr->getJobType() == JT_MIGRATE && jcr->JobStatus == JS_Terminated) { Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s", - (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1)); + (char)JT_MIGRATED_JOB, old_jobid); + db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL); + UAContext *ua = new_ua_context(jcr); + /* Move JobLog to new JobId */ + Mmsg(query, "UPDATE Log SET JobId=%s WHERE JobId=%s", + new_jobid, old_jobid); + db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL); + /* Purge all old file records, but leave Job record */ + purge_files_from_jobs(ua, old_jobid); + free_ua_context(ua); + } + + /* + * If we terminated a Copy (rather than a Migration) normally: + * - copy any Log records to the new JobId + * - set type="Job Copy" for the new job + */ + if (jcr->getJobType() == 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); + 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, new_jobid); db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL); } @@ -1128,7 +1238,7 @@ void migration_cleanup(JCR *jcr, int TermCode) switch (jcr->JobStatus) { case JS_Terminated: - if (jcr->Errors || jcr->SDErrors) { + if (jcr->JobErrors || jcr->SDErrors) { term_msg = _("%s OK -- with warnings"); } else { term_msg = _("%s OK"); @@ -1158,17 +1268,17 @@ void migration_cleanup(JCR *jcr, int TermCode) term_msg = _("Inappropriate %s term code"); break; } - } else { - if (jcr->get_JobType() == JT_MIGRATE && jcr->previous_jr.JobId != 0) { - /* Mark previous job as migrated */ - Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s", - (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1)); - db_sql_query(jcr->db, query.c_str(), NULL, NULL); - } - term_msg = _("%s -- no files to migrate"); - } - - bsnprintf(term_code, sizeof(term_code), term_msg, "Migration"); + } else { + if (jcr->getJobType() == JT_MIGRATE && jcr->previous_jr.JobId != 0) { + /* Mark previous job as migrated */ + Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s", + (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1)); + db_sql_query(jcr->db, query.c_str(), NULL, NULL); + } + term_msg = _("%s -- no files to %s"); + } + + bsnprintf(term_code, sizeof(term_code), term_msg, jcr->get_OperationName(), jcr->get_ActionName(0)); bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime); bstrftimes(edt, sizeof(edt), jcr->jr.EndTime); RunTime = jcr->jr.EndTime - jcr->jr.StartTime; @@ -1178,12 +1288,12 @@ void migration_cleanup(JCR *jcr, int TermCode) kbps = (double)jcr->SDJobBytes / (1000 * RunTime); } - jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg)); - Jmsg(jcr, msg_type, 0, _("Bacula %s %s (%s): %s\n" + Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n" " Build OS: %s %s %s\n" " Prev Backup JobId: %s\n" +" Prev Backup Job: %s\n" " New Backup JobId: %s\n" " Current JobId: %s\n" " Current Job: %s\n" @@ -1209,13 +1319,14 @@ void migration_cleanup(JCR *jcr, int TermCode) " SD Errors: %d\n" " SD termination status: %s\n" " Termination: %s\n\n"), - my_name, VERSION, LSMDATE, edt, + BACULA, my_name, VERSION, LSMDATE, HOST_OS, DISTNAME, DISTVER, edit_uint64(jcr->previous_jr.JobId, ec6), + jcr->previous_jr.Job, mig_jcr ? edit_uint64(mig_jcr->jr.JobId, ec7) : "0", edit_uint64(jcr->jr.JobId, ec8), jcr->jr.Job, - level_to_str(jcr->get_JobLevel()), jcr->since, + level_to_str(jcr->getJobLevel()), jcr->since, jcr->client->name(), jcr->fileset->name(), jcr->FSCreateTime, jcr->rpool->name(), jcr->rpool_source,