]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/dird/migrate.c
Simplify the code path in migration and copy jobs
[bacula/bacula] / bacula / src / dird / migrate.c
index fc49cb74ef23fa7d18e07152343f56636b8007f9..2ee0e0d15b81e473bf5a97060fd828ae27155976 100644 (file)
@@ -1,12 +1,12 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2004-2009 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,8 +54,7 @@
 
 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);
@@ -135,7 +133,7 @@ bool do_migration_init(JCR *jcr)
    }
 
    /* 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;
    }
@@ -144,7 +142,7 @@ bool do_migration_init(JCR *jcr)
       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);
@@ -153,10 +151,13 @@ bool do_migration_init(JCR *jcr)
       return true;                    /* no work */
    }
 
-   create_restore_bootstrap_file(jcr);
+   if (create_restore_bootstrap_file(jcr) < 0) {
+      Jmsg(jcr, M_FATAL, 0, _("Create bootstrap file failed.\n"));
+      return false;
+   }
 
    if (jcr->previous_jr.JobId == 0 || jcr->ExpectedFiles == 0) {
-      set_jcr_job_status(jcr, JS_Terminated);
+      jcr->setJobStatus(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 %s.\n"), jcr->get_ActionName(0));
@@ -209,6 +210,9 @@ 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);
@@ -297,7 +301,7 @@ bool do_migration(JCR *jcr)
     *  so set a normal status, cleanup and return OK.
     */
    if (!mig_jcr) {
-      set_jcr_job_status(jcr, JS_Terminated);
+      jcr->setJobStatus(JS_Terminated);
       migration_cleanup(jcr, jcr->JobStatus);
       return true;
    }
@@ -307,17 +311,18 @@ bool do_migration(JCR *jcr)
            edit_int64(jcr->previous_jr.JobId, ed1),
            jcr->get_ActionName(0),
            db_strerror(jcr->db));
-      set_jcr_job_status(jcr, JS_Terminated);
+      jcr->setJobStatus(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);
+      jcr->setJobStatus(JS_Terminated);
       migration_cleanup(jcr, jcr->JobStatus);
       return true;
    }
@@ -333,8 +338,8 @@ bool do_migration(JCR *jcr)
     *
     */
    Dmsg0(110, "Open connection with storage daemon\n");
-   set_jcr_job_status(jcr, JS_WaitSD);
-   set_jcr_job_status(mig_jcr, JS_WaitSD);
+   jcr->setJobStatus(JS_WaitSD);
+   mig_jcr->setJobStatus(JS_WaitSD);
    /*
     * Start conversation with Storage daemon
     */
@@ -348,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
@@ -376,7 +373,7 @@ bool do_migration(JCR *jcr)
    jcr->start_time = time(NULL);
    jcr->jr.StartTime = jcr->start_time;
    jcr->jr.JobTDate = jcr->start_time;
-   set_jcr_job_status(jcr, JS_Running);
+   jcr->setJobStatus(JS_Running);
 
    /* Update job start record for this migration control job */
    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
@@ -388,7 +385,7 @@ bool do_migration(JCR *jcr)
    mig_jcr->start_time = time(NULL);
    mig_jcr->jr.StartTime = mig_jcr->start_time;
    mig_jcr->jr.JobTDate = mig_jcr->start_time;
-   set_jcr_job_status(mig_jcr, JS_Running);
+   mig_jcr->setJobStatus(JS_Running);
 
    /* Update job start record for the real migration backup job */
    if (!db_update_job_start_record(mig_jcr, mig_jcr->db, &mig_jcr->jr)) {
@@ -418,13 +415,13 @@ bool do_migration(JCR *jcr)
    }
 
 
-   set_jcr_job_status(jcr, JS_Running);
-   set_jcr_job_status(mig_jcr, JS_Running);
+   jcr->setJobStatus(JS_Running);
+   mig_jcr->setJobStatus(JS_Running);
 
    /* Pickup Job termination data */
    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
    wait_for_storage_daemon_termination(jcr);
-   set_jcr_job_status(jcr, jcr->SDJobStatus);
+   jcr->setJobStatus(jcr->SDJobStatus);
    db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
    if (jcr->JobStatus != JS_Terminated) {
       return false;
@@ -483,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;
@@ -644,7 +647,7 @@ const char *sql_jobids_of_pool_uncopied_jobs =
  *           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);
@@ -670,7 +673,6 @@ static int get_job_to_migrate(JCR *jcr)
    jids.list[0] = 0;
    jids.count = 0;
 
-
    /*
     * If MigrateJobId is set, then we migrate only that Job,
     *  otherwise, we go through the full selection of jobs to
@@ -678,8 +680,7 @@ static int get_job_to_migrate(JCR *jcr)
     */
    if (jcr->MigrateJobId != 0) {
       Dmsg1(dbglevel, "At Job start previous jobid=%u\n", jcr->MigrateJobId);
-      edit_uint64(jcr->MigrateJobId, ids.list);
-      ids.count = 1;
+      JobId = jcr->MigrateJobId;
    } else {
       switch (jcr->job->selection_type) {
       case MT_JOB:
@@ -827,31 +828,43 @@ static int get_job_to_migrate(JCR *jcr)
          Jmsg(jcr, M_FATAL, 0, _("Unknown %s Selection Type.\n"), jcr->get_OperationName());
          goto bail_out;
       }
-   }
-
-   /*
-    * Loop over all jobids except the last one, sending
-    *  them to start_migration_job(), which will start a job
-    *  for each of them.  For the last JobId, we handle it below.
-    */
-   p = ids.list;
-   if (ids.count == 0) {
-      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 chosen to be %s: %s\n"),
-      ids.count, (ids.count < 2) ? _(" was") : _("s were"),
-      jcr->get_ActionName(1), ids.list);
+      /*
+       * Loop over all jobids except the last one, sending
+       * them to start_migration_job(), which will start a job
+       * for each of them.  For the last JobId, we handle it below.
+       */
+      p = ids.list;
+      if (ids.count == 0) {
+         Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName(0));
+         goto ok_out;
+      }
 
-   Dmsg2(dbglevel, "Before loop count=%d ids=%s\n", ids.count, ids.list);
-   for (int i=1; i < (int)ids.count; i++) {
+      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, "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 %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 */
       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");
+      Dmsg2(dbglevel, "Last get_next_jobid stat=%d JobId=%u\n", stat, (int)JobId);
       if (stat < 0) {
          Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
          goto bail_out;
@@ -860,18 +873,6 @@ static int get_job_to_migrate(JCR *jcr)
          goto ok_out;
       }
    }
-   
-   /* Now get the last JobId and handle it in the current job */
-   JobId = 0;
-   stat = get_next_jobid_from_list(&p, &JobId);
-   Dmsg2(dbglevel, "Last get_next_jobid stat=%d JobId=%u\n", stat, (int)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 %s.\n"), jcr->get_ActionName(0));
-      goto ok_out;
-   }
 
    jcr->previous_jr.JobId = JobId;
    Dmsg1(dbglevel, "Previous jobid=%d\n", (int)jcr->previous_jr.JobId);
@@ -883,6 +884,7 @@ static int get_job_to_migrate(JCR *jcr)
            db_strerror(jcr->db));
       goto bail_out;
    }
+
    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);
@@ -910,7 +912,7 @@ 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 allowduplicates=yes", jcr->job->name(),
         edit_uint64(jcr->MigrateJobId, ed1));
    Dmsg2(dbglevel, "=============== %s cmd=%s\n", jcr->get_OperationName(), ua->cmd);
    parse_ua_args(ua);                 /* parse command */
@@ -990,7 +992,7 @@ static bool find_jobids_of_pool_uncopied_jobs(JCR *jcr, idpkt *ids)
    POOL_MEM query(PM_MESSAGE);
 
    /* Only a copy job is allowed */
-   if (jcr->get_JobType() != JT_COPY) {
+   if (jcr->getJobType() != JT_COPY) {
       Jmsg(jcr, M_FATAL, 0,
            _("Selection Type 'pooluncopiedjobs' only applies to Copy Jobs"));
       goto bail_out;
@@ -1167,7 +1169,7 @@ void migration_cleanup(JCR *jcr, int TermCode)
        *   - move any Log records to the new JobId
        *   - Purge the File records from the previous job
        */
-      if (jcr->get_JobType() == JT_MIGRATE && jcr->JobStatus == JS_Terminated) {
+      if (jcr->getJobType() == JT_MIGRATE && jcr->JobStatus == JS_Terminated) {
          Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
               (char)JT_MIGRATED_JOB, old_jobid);
          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
@@ -1176,17 +1178,24 @@ void migration_cleanup(JCR *jcr, int TermCode)
          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);
+
+         if (jcr->job->PurgeMigrateJob) {
+            /* Purge old Job record */
+            purge_jobs_from_catalog(ua, old_jobid);
+         } else {
+            /* 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 normally:
+       * 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->get_JobType() == JT_COPY && jcr->JobStatus == JS_Terminated) {
+      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",
@@ -1200,7 +1209,7 @@ void migration_cleanup(JCR *jcr, int TermCode)
       if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
          Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
             db_strerror(jcr->db));
-         set_jcr_job_status(jcr, JS_ErrorTerminated);
+         jcr->setJobStatus(JS_ErrorTerminated);
       }
 
       update_bootstrap_file(mig_jcr);
@@ -1266,7 +1275,7 @@ void migration_cleanup(JCR *jcr, int TermCode)
          break;
       }
    } else {
-      if (jcr->get_JobType() == JT_MIGRATE && jcr->previous_jr.JobId != 0) {
+      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));
@@ -1287,7 +1296,7 @@ void migration_cleanup(JCR *jcr, int TermCode)
 
    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
 
-   Jmsg(jcr, msg_type, 0, _("%s %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"
@@ -1316,14 +1325,14 @@ void migration_cleanup(JCR *jcr, int TermCode)
 "  SD Errors:              %d\n"
 "  SD termination status:  %s\n"
 "  Termination:            %s\n\n"),
-        BACULA, 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,