3 * Bacula Director -- migrate.c -- responsible for doing
6 * Kern Sibbald, September MMIV
8 * Basic tasks done here:
9 * Open DB and create records for this job.
10 * Open Message Channel with Storage daemon to tell him a job will be starting.
11 * Open connection with Storage daemon and pass him commands
13 * When the Storage daemon finishes the job, update the DB.
18 Copyright (C) 2004-2006 Kern Sibbald
20 This program is free software; you can redistribute it and/or
21 modify it under the terms of the GNU General Public License
22 version 2 as amended with additional clauses defined in the
23 file LICENSE in the main source directory.
25 This program is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 the file LICENSE for additional details.
36 #include "lib/bregex.h"
41 static const int dbglevel = 100;
43 static char OKbootstrap[] = "3000 OK bootstrap\n";
44 static bool get_job_to_migrate(JCR *jcr);
46 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
47 const char *query2, const char *type);
48 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
50 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type);
51 static void start_migration_job(JCR *jcr);
54 * Called here before the job is run to do the job
57 bool do_migration_init(JCR *jcr)
59 /* If we find a job to migrate it is previous_jr.JobId */
60 if (!get_job_to_migrate(jcr)) {
64 if (jcr->previous_jr.JobId == 0) {
65 return true; /* no work */
68 if (!get_or_create_fileset_record(jcr)) {
72 apply_pool_overrides(jcr);
74 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->hdr.name);
75 if (jcr->jr.PoolId == 0) {
79 /* If pool storage specified, use it instead of job storage */
80 copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
83 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
87 create_restore_bootstrap_file(jcr);
92 * Do a Migration of a previous job
94 * Returns: false on failure
97 bool do_migration(JCR *jcr)
104 JCR *mig_jcr; /* newly migrated job */
107 * previous_jr refers to the job DB record of the Job that is
109 * prev_job refers to the job resource of the Job that is
111 * jcr is the jcr for the current "migration" job. It is a
112 * control job that is put in the DB as a migration job, which
113 * means that this job migrated a previous job to a new job.
114 * mig_jcr refers to the newly migrated job that is run by
115 * the current jcr. It is a backup job that moves (migrates) the
116 * data written for the previous_jr into the new pool. This
117 * job (mig_jcr) becomes the new backup job that replaces
118 * the original backup job.
120 if (jcr->previous_jr.JobId == 0 || jcr->ExpectedFiles == 0) {
121 set_jcr_job_status(jcr, JS_Terminated);
122 migration_cleanup(jcr, jcr->JobStatus);
123 return true; /* no work */
126 Dmsg4(dbglevel, "Previous: Name=%s JobId=%d Type=%c Level=%c\n",
127 jcr->previous_jr.Name, jcr->previous_jr.JobId,
128 jcr->previous_jr.JobType, jcr->previous_jr.JobLevel);
130 Dmsg4(dbglevel, "Current: Name=%s JobId=%d Type=%c Level=%c\n",
131 jcr->jr.Name, jcr->jr.JobId,
132 jcr->jr.JobType, jcr->jr.JobLevel);
135 job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
136 prev_job = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
138 if (!job || !prev_job) {
142 mig_jcr = jcr->mig_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
143 memcpy(&mig_jcr->previous_jr, &jcr->previous_jr, sizeof(mig_jcr->previous_jr));
145 /* Turn the mig_jcr into a "real" job that takes on the aspects of
146 * the previous backup job "prev_job".
148 set_jcr_defaults(mig_jcr, prev_job);
149 if (!setup_job(mig_jcr)) {
153 /* Now reset the job record from the previous job */
154 memcpy(&mig_jcr->jr, &jcr->previous_jr, sizeof(mig_jcr->jr));
155 /* Update the jr to reflect the new values of PoolId, FileSetId, and JobId. */
156 mig_jcr->jr.PoolId = jcr->jr.PoolId;
157 mig_jcr->jr.FileSetId = jcr->jr.FileSetId;
158 mig_jcr->jr.JobId = mig_jcr->JobId;
160 Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
161 mig_jcr->jr.Name, mig_jcr->jr.JobId,
162 mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
165 * Get the PoolId used with the original job. Then
166 * find the pool name from the database record.
168 memset(&pr, 0, sizeof(pr));
169 pr.PoolId = mig_jcr->previous_jr.PoolId;
170 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
171 Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
172 edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
175 /* Get the pool resource corresponding to the original job */
176 pool = (POOL *)GetResWithName(R_POOL, pr.Name);
178 Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
182 /* If pool storage specified, use it for restore */
183 copy_rstorage(mig_jcr, pool->storage, _("Pool resource"));
184 copy_rstorage(jcr, pool->storage, _("Pool resource"));
186 /* If the original backup pool has a NextPool, make sure a
187 * record exists in the database.
189 if (pool->NextPool) {
190 jcr->jr.PoolId = get_or_create_pool_record(jcr, pool->NextPool->hdr.name);
191 if (jcr->jr.PoolId == 0) {
195 * put the "NextPool" resource pointer in our jcr so that we
196 * can pull the Storage reference from it.
198 mig_jcr->pool = jcr->pool = pool->NextPool;
199 mig_jcr->jr.PoolId = jcr->jr.PoolId;
200 pm_strcpy(jcr->pool_source, _("NextPool in Pool resource"));
203 /* If pool storage specified, use it instead of job storage for backup */
204 copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
206 /* Print Job Start message */
207 Jmsg(jcr, M_INFO, 0, _("Start Migration JobId %s, Job=%s\n"),
208 edit_uint64(jcr->JobId, ed1), jcr->Job);
210 set_jcr_job_status(jcr, JS_Running);
211 set_jcr_job_status(mig_jcr, JS_Running);
212 Dmsg2(dbglevel, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
214 /* Update job start record for this migration job */
215 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
216 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
220 Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
221 mig_jcr->jr.Name, mig_jcr->jr.JobId,
222 mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
224 /* Update job start record for migrated job */
225 if (!db_update_job_start_record(mig_jcr, mig_jcr->db, &mig_jcr->jr)) {
226 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(mig_jcr->db));
232 * Open a message channel connection with the Storage
233 * daemon. This is to let him know that our client
234 * will be contacting him for a backup session.
237 Dmsg0(110, "Open connection with storage daemon\n");
238 set_jcr_job_status(jcr, JS_WaitSD);
239 set_jcr_job_status(mig_jcr, JS_WaitSD);
241 * Start conversation with Storage daemon
243 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
246 sd = jcr->store_bsock;
248 * Now start a job with the Storage daemon
250 Dmsg2(dbglevel, "Read store=%s, write store=%s\n",
251 ((STORE *)jcr->rstorage->first())->name(),
252 ((STORE *)jcr->wstorage->first())->name());
253 if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage)) {
256 Dmsg0(150, "Storage daemon connection OK\n");
258 if (!send_bootstrap_file(jcr, sd) ||
259 !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
263 if (!bnet_fsend(sd, "run")) {
268 * Now start a Storage daemon message thread
270 if (!start_storage_daemon_message_thread(jcr)) {
275 set_jcr_job_status(jcr, JS_Running);
276 set_jcr_job_status(mig_jcr, JS_Running);
278 /* Pickup Job termination data */
279 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
280 wait_for_storage_daemon_termination(jcr);
282 set_jcr_job_status(jcr, jcr->SDJobStatus);
283 if (jcr->JobStatus != JS_Terminated) {
286 migration_cleanup(jcr, jcr->JobStatus);
288 UAContext *ua = new_ua_context(jcr);
289 purge_files_from_job(ua, jcr->previous_jr.JobId);
301 * Callback handler make list of DB Ids
303 static int dbid_handler(void *ctx, int num_fields, char **row)
305 idpkt *ids = (idpkt *)ctx;
307 Dmsg3(dbglevel, "count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
308 if (ids->count == 0) {
311 pm_strcat(ids->list, ",");
313 pm_strcat(ids->list, row[0]);
324 static int item_compare(void *item1, void *item2)
326 uitem *i1 = (uitem *)item1;
327 uitem *i2 = (uitem *)item2;
328 return strcmp(i1->item, i2->item);
331 static int unique_name_handler(void *ctx, int num_fields, char **row)
333 dlist *list = (dlist *)ctx;
335 uitem *new_item = (uitem *)malloc(sizeof(uitem));
338 memset(new_item, 0, sizeof(uitem));
339 new_item->item = bstrdup(row[0]);
340 Dmsg1(dbglevel, "Item=%s\n", row[0]);
341 item = (uitem *)list->binary_insert((void *)new_item, item_compare);
342 if (item != new_item) { /* already in list */
343 free(new_item->item);
344 free((char *)new_item);
350 /* Get Job names in Pool */
351 const char *sql_job =
352 "SELECT DISTINCT Job.Name from Job,Pool"
353 " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
355 /* Get JobIds from regex'ed Job names */
356 const char *sql_jobids_from_job =
357 "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
358 " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
359 " ORDER by Job.StartTime";
361 /* Get Client names in Pool */
362 const char *sql_client =
363 "SELECT DISTINCT Client.Name from Client,Pool,Job"
364 " WHERE Pool.Name='%s' AND Job.ClientId=Client.ClientId AND"
365 " Job.PoolId=Pool.PoolId";
367 /* Get JobIds from regex'ed Client names */
368 const char *sql_jobids_from_client =
369 "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
370 " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
371 " AND Job.ClientId=Client.ClientId "
372 " ORDER by Job.StartTime";
374 /* Get Volume names in Pool */
375 const char *sql_vol =
376 "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
377 " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
378 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
380 /* Get JobIds from regex'ed Volume names */
381 const char *sql_jobids_from_vol =
382 "SELECT DISTINCT Job.JobId,Job.StartTime FROM Media,JobMedia,Job"
383 " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId"
384 " AND JobMedia.JobId=Job.JobId"
385 " ORDER by Job.StartTime";
388 const char *sql_smallest_vol =
389 "SELECT MediaId FROM Media,Pool WHERE"
390 " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
391 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
392 " ORDER BY VolBytes ASC LIMIT 1";
394 const char *sql_oldest_vol =
395 "SELECT MediaId FROM Media,Pool WHERE"
396 " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
397 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
398 " ORDER BY LastWritten ASC LIMIT 1";
400 /* Get JobIds when we have selected MediaId */
401 const char *sql_jobids_from_mediaid =
402 "SELECT DISTINCT Job.JobId,Job.StartTime FROM JobMedia,Job"
403 " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=%s"
404 " ORDER by Job.StartTime";
406 const char *sql_pool_bytes =
407 "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
408 " VolStatus in ('Full','Used','Error','Append') AND Media.Enabled=1 AND"
409 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
411 const char *sql_vol_bytes =
412 "SELECT MediaId FROM Media,Pool WHERE"
413 " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
414 " Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
415 " VolBytes<%s ORDER BY LastWritten ASC LIMIT 1";
418 const char *sql_ujobid =
419 "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
420 " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
421 " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
426 * Returns: false on error
427 * true if OK and jcr->previous_jr filled in
429 static bool get_job_to_migrate(JCR *jcr)
432 POOL_MEM query(PM_MESSAGE);
438 ids.list = get_pool_memory(PM_MESSAGE);
439 Dmsg1(dbglevel, "list=%p\n", ids.list);
443 if (jcr->MigrateJobId != 0) {
444 Dmsg1(000, "At Job start previous jobid=%u\n", jcr->MigrateJobId);
445 edit_uint64(jcr->MigrateJobId, ids.list);
448 switch (jcr->job->selection_type) {
450 if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
455 if (!regex_find_jobids(jcr, &ids, sql_client, sql_jobids_from_client, "Client")) {
460 if (!regex_find_jobids(jcr, &ids, sql_vol, sql_jobids_from_vol, "Volume")) {
465 if (!jcr->job->selection_pattern) {
466 Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
469 Dmsg1(dbglevel, "SQL=%s\n", jcr->job->selection_pattern);
470 if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
471 dbid_handler, (void *)&ids)) {
472 Jmsg(jcr, M_FATAL, 0,
473 _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
477 case MT_SMALLEST_VOL:
478 if (!find_mediaid_then_jobids(jcr, &ids, sql_smallest_vol, "Smallest Volume")) {
483 if (!find_mediaid_then_jobids(jcr, &ids, sql_oldest_vol, "Oldest Volume")) {
488 /***** Below not implemented yet *********/
489 case MT_POOL_OCCUPANCY:
493 Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
494 if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
495 Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
498 if (ctx.count == 0) {
499 Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
502 if (ctx.value > (int64_t)jcr->pool->MigrationHighBytes) {
503 Dmsg2(000, "highbytes=%d pool=%d\n", (int)jcr->pool->MigrationHighBytes,
509 Dmsg0(000, "Pool time not implemented\n");
512 Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
518 * Loop over all jobids except the last one, sending
519 * them to start_migration_job(), which will start a job
520 * for each of them. For the last JobId, we handle it below.
523 for (int i=1; i < (int)ids.count; i++) {
525 stat = get_next_jobid_from_list(&p, &JobId);
526 Dmsg2(000, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
527 jcr->MigrateJobId = JobId;
528 start_migration_job(jcr);
530 Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
532 } else if (stat == 0) {
533 Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
538 /* Now get the last JobId and handle it in the current job */
540 stat = get_next_jobid_from_list(&p, &JobId);
541 Dmsg2(000, "Last get_next_jobid stat=%d JobId=%u\n", stat, JobId);
543 Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
545 } else if (stat == 0) {
546 Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
550 jcr->previous_jr.JobId = JobId;
551 Dmsg1(100, "Previous jobid=%d\n", jcr->previous_jr.JobId);
553 if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
554 Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
555 edit_int64(jcr->previous_jr.JobId, ed1),
556 db_strerror(jcr->db));
559 Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
560 jcr->previous_jr.JobId, jcr->previous_jr.Job);
563 free_pool_memory(ids.list);
567 free_pool_memory(ids.list);
571 static void start_migration_job(JCR *jcr)
573 UAContext *ua = new_ua_context(jcr);
576 Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name,
577 edit_uint64(jcr->MigrateJobId, ed1));
578 Dmsg1(dbglevel, "=============== Migration cmd=%s\n", ua->cmd);
579 parse_ua_args(ua); /* parse command */
580 int stat = run_cmd(ua, ua->cmd);
581 // int stat = (int)jcr->MigrateJobId;
583 Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
585 Jmsg(jcr, M_INFO, 0, _("Migration JobId %d started.\n"), stat);
590 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
594 POOL_MEM query(PM_MESSAGE);
597 /* Basic query for MediaId */
598 Mmsg(query, query1, jcr->pool->hdr.name);
599 if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
600 Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
603 if (ids->count == 0) {
604 Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
606 if (ids->count != 1) {
607 Jmsg(jcr, M_FATAL, 0, _("SQL logic error. Count should be 1 but is %d\n"),
611 Dmsg1(000, "Smallest Vol Jobids=%s\n", ids->list);
613 ok = find_jobids_from_mediaid_list(jcr, ids, type);
619 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type)
622 POOL_MEM query(PM_MESSAGE);
624 Mmsg(query, sql_jobids_from_mediaid, ids->list);
626 if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
627 Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
630 if (ids->count == 0) {
631 Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
638 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
639 const char *query2, const char *type)
643 uitem *last_item = NULL;
648 POOL_MEM query(PM_MESSAGE);
650 item_chain = New(dlist(item, &item->link));
651 if (!jcr->job->selection_pattern) {
652 Jmsg(jcr, M_FATAL, 0, _("No Migration %s selection pattern specified.\n"),
656 Dmsg1(dbglevel, "regex=%s\n", jcr->job->selection_pattern);
657 /* Compile regex expression */
658 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
660 regerror(rc, &preg, prbuf, sizeof(prbuf));
661 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
662 jcr->job->selection_pattern, prbuf);
665 /* Basic query for names */
666 Mmsg(query, query1, jcr->pool->hdr.name);
667 Dmsg1(dbglevel, "query1=%s\n", query.c_str());
668 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
669 (void *)item_chain)) {
670 Jmsg(jcr, M_FATAL, 0,
671 _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
674 /* Now apply the regex to the names and remove any item not matched */
675 foreach_dlist(item, item_chain) {
676 const int nmatch = 30;
677 regmatch_t pmatch[nmatch];
679 Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
680 free(last_item->item);
681 item_chain->remove(last_item);
683 Dmsg1(dbglevel, "Item=%s\n", item->item);
684 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
686 last_item = NULL; /* keep this one */
692 free(last_item->item);
693 Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
694 item_chain->remove(last_item);
698 * At this point, we have a list of items in item_chain
699 * that have been matched by the regex, so now we need
700 * to look up their jobids.
703 foreach_dlist(item, item_chain) {
704 Dmsg2(dbglevel, "Got %s: %s\n", type, item->item);
705 Mmsg(query, query2, item->item, jcr->pool->hdr.name);
706 Dmsg1(dbglevel, "query2=%s\n", query.c_str());
707 if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
708 Jmsg(jcr, M_FATAL, 0,
709 _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
713 if (ids->count == 0) {
714 Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
718 Dmsg2(dbglevel, "Count=%d Jobids=%s\n", ids->count, ids->list);
720 Dmsg0(dbglevel, "After delete item_chain\n");
726 * Release resources allocated during backup.
728 void migration_cleanup(JCR *jcr, int TermCode)
730 char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
731 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
732 char ec6[50], ec7[50], ec8[50];
733 char term_code[100], sd_term_msg[100];
734 const char *term_msg;
739 JCR *mig_jcr = jcr->mig_jcr;
740 POOL_MEM query(PM_MESSAGE);
742 Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
743 dequeue_messages(jcr); /* display any queued messages */
744 memset(&mr, 0, sizeof(mr));
745 set_jcr_job_status(jcr, TermCode);
746 update_job_end_record(jcr); /* update database */
749 * Check if we actually did something.
750 * mig_jcr is jcr of the newly migrated job.
753 mig_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
754 mig_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
755 mig_jcr->VolSessionId = jcr->VolSessionId;
756 mig_jcr->VolSessionTime = jcr->VolSessionTime;
757 mig_jcr->jr.RealEndTime = 0;
758 mig_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
760 set_jcr_job_status(mig_jcr, TermCode);
763 update_job_end_record(mig_jcr);
765 /* Update final items to set them to the previous job's values */
766 Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
767 "JobTDate=%s WHERE JobId=%s",
768 jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime,
769 edit_uint64(jcr->previous_jr.JobTDate, ec1),
770 edit_uint64(mig_jcr->jr.JobId, ec2));
771 db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
773 /* Now marke the previous job as migrated */
774 Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
775 (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
776 db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
778 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
779 Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
780 db_strerror(jcr->db));
781 set_jcr_job_status(jcr, JS_ErrorTerminated);
784 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
785 if (!db_get_media_record(jcr, jcr->db, &mr)) {
786 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
787 mr.VolumeName, db_strerror(jcr->db));
788 set_jcr_job_status(jcr, JS_ErrorTerminated);
791 update_bootstrap_file(mig_jcr);
793 if (!db_get_job_volume_names(mig_jcr, mig_jcr->db, mig_jcr->jr.JobId, &mig_jcr->VolumeName)) {
795 * Note, if the job has erred, most likely it did not write any
796 * tape, so suppress this "error" message since in that case
797 * it is normal. Or look at it the other way, only for a
798 * normal exit should we complain about this error.
800 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
801 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(mig_jcr->db));
803 mig_jcr->VolumeName[0] = 0; /* none */
807 msg_type = M_INFO; /* by default INFO message */
808 switch (jcr->JobStatus) {
810 if (jcr->Errors || jcr->SDErrors) {
811 term_msg = _("%s OK -- with warnings");
813 term_msg = _("%s OK");
817 case JS_ErrorTerminated:
818 term_msg = _("*** %s Error ***");
819 msg_type = M_ERROR; /* Generate error message */
820 if (jcr->store_bsock) {
821 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
822 if (jcr->SD_msg_chan) {
823 pthread_cancel(jcr->SD_msg_chan);
828 term_msg = _("%s Canceled");
829 if (jcr->store_bsock) {
830 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
831 if (jcr->SD_msg_chan) {
832 pthread_cancel(jcr->SD_msg_chan);
837 term_msg = _("Inappropriate %s term code");
840 bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
841 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
842 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
843 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
847 kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
851 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
853 Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
854 " Prev Backup JobId: %s\n"
855 " New Backup JobId: %s\n"
856 " Migration JobId: %s\n"
857 " Migration Job: %s\n"
858 " Backup Level: %s%s\n"
860 " FileSet: \"%s\" %s\n"
861 " Pool: \"%s\" (From %s)\n"
862 " Storage: \"%s\" (From %s)\n"
865 " Elapsed time: %s\n"
867 " SD Files Written: %s\n"
868 " SD Bytes Written: %s (%sB)\n"
870 " Volume name(s): %s\n"
871 " Volume Session Id: %d\n"
872 " Volume Session Time: %d\n"
873 " Last Volume Bytes: %s (%sB)\n"
875 " SD termination status: %s\n"
876 " Termination: %s\n\n"),
880 mig_jcr ? edit_uint64(jcr->previous_jr.JobId, ec6) : "0",
881 mig_jcr ? edit_uint64(mig_jcr->jr.JobId, ec7) : "0",
882 edit_uint64(jcr->jr.JobId, ec8),
884 level_to_str(jcr->JobLevel), jcr->since,
886 jcr->fileset->name(), jcr->FSCreateTime,
887 jcr->pool->name(), jcr->pool_source,
888 jcr->wstore->name(), jcr->storage_source,
891 edit_utime(RunTime, elapsed, sizeof(elapsed)),
893 edit_uint64_with_commas(jcr->SDJobFiles, ec1),
894 edit_uint64_with_commas(jcr->SDJobBytes, ec2),
895 edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
897 mig_jcr ? mig_jcr->VolumeName : "",
900 edit_uint64_with_commas(mr.VolBytes, ec4),
901 edit_uint64_with_suffix(mr.VolBytes, ec5),
906 Dmsg1(100, "migrate_cleanup() mig_jcr=0x%x\n", jcr->mig_jcr);
908 free_jcr(jcr->mig_jcr);
911 Dmsg0(100, "Leave migrate_cleanup()\n");