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
108 * going to be migrated.
109 * prev_job refers to the job resource of the Job that is
110 * going to be migrated.
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 * No Volume or File data is associated with this control
116 * mig_jcr refers to the newly migrated job that is run by
117 * the current jcr. It is a backup job that moves (migrates) the
118 * data written for the previous_jr into the new pool. This
119 * job (mig_jcr) becomes the new backup job that replaces
120 * the original backup job.
122 if (jcr->previous_jr.JobId == 0 || jcr->ExpectedFiles == 0) {
123 set_jcr_job_status(jcr, JS_Terminated);
124 migration_cleanup(jcr, jcr->JobStatus);
125 return true; /* no work */
128 Dmsg4(dbglevel, "Previous: Name=%s JobId=%d Type=%c Level=%c\n",
129 jcr->previous_jr.Name, jcr->previous_jr.JobId,
130 jcr->previous_jr.JobType, jcr->previous_jr.JobLevel);
132 Dmsg4(dbglevel, "Current: Name=%s JobId=%d Type=%c Level=%c\n",
133 jcr->jr.Name, jcr->jr.JobId,
134 jcr->jr.JobType, jcr->jr.JobLevel);
137 job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
138 prev_job = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
140 if (!job || !prev_job) {
144 /* Create a migation jcr */
145 mig_jcr = jcr->mig_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
146 memcpy(&mig_jcr->previous_jr, &jcr->previous_jr, sizeof(mig_jcr->previous_jr));
149 * Turn the mig_jcr into a "real" job that takes on the aspects of
150 * the previous backup job "prev_job".
152 set_jcr_defaults(mig_jcr, prev_job);
153 if (!setup_job(mig_jcr)) {
157 /* Now reset the job record from the previous job */
158 memcpy(&mig_jcr->jr, &jcr->previous_jr, sizeof(mig_jcr->jr));
159 /* Update the jr to reflect the new values of PoolId, FileSetId, and JobId. */
160 mig_jcr->jr.PoolId = jcr->jr.PoolId;
161 mig_jcr->jr.FileSetId = jcr->jr.FileSetId;
162 mig_jcr->jr.JobId = mig_jcr->JobId;
164 Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
165 mig_jcr->jr.Name, mig_jcr->jr.JobId,
166 mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
169 * Get the PoolId used with the original job. Then
170 * find the pool name from the database record.
172 memset(&pr, 0, sizeof(pr));
173 pr.PoolId = mig_jcr->previous_jr.PoolId;
174 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
175 Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
176 edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
179 /* Get the pool resource corresponding to the original job */
180 pool = (POOL *)GetResWithName(R_POOL, pr.Name);
182 Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
186 /* If pool storage specified, use it for restore */
187 copy_rstorage(mig_jcr, pool->storage, _("Pool resource"));
188 copy_rstorage(jcr, pool->storage, _("Pool resource"));
191 * If the original backup pool has a NextPool, make sure a
192 * record exists in the database. Note, in this case, we
193 * will be migrating from pool to pool->NextPool.
195 if (pool->NextPool) {
196 jcr->jr.PoolId = get_or_create_pool_record(jcr, pool->NextPool->hdr.name);
197 if (jcr->jr.PoolId == 0) {
201 * put the "NextPool" resource pointer in our jcr so that we
202 * can pull the Storage reference from it.
204 mig_jcr->pool = jcr->pool = pool->NextPool;
205 mig_jcr->jr.PoolId = jcr->jr.PoolId;
206 pm_strcpy(jcr->pool_source, _("NextPool in Pool resource"));
209 /* If pool storage specified, use it instead of job storage for backup */
210 copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
212 /* Print Job Start message */
213 Jmsg(jcr, M_INFO, 0, _("Start Migration JobId %s, Job=%s\n"),
214 edit_uint64(jcr->JobId, ed1), jcr->Job);
216 set_jcr_job_status(jcr, JS_Running);
217 set_jcr_job_status(mig_jcr, JS_Running);
218 Dmsg2(dbglevel, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
220 /* Update job start record for this migration control job */
221 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
222 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
226 Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
227 mig_jcr->jr.Name, mig_jcr->jr.JobId,
228 mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
230 /* Update job start record for the real migration backup job */
231 if (!db_update_job_start_record(mig_jcr, mig_jcr->db, &mig_jcr->jr)) {
232 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(mig_jcr->db));
238 * Open a message channel connection with the Storage
239 * daemon. This is to let him know that our client
240 * will be contacting him for a backup session.
243 Dmsg0(110, "Open connection with storage daemon\n");
244 set_jcr_job_status(jcr, JS_WaitSD);
245 set_jcr_job_status(mig_jcr, JS_WaitSD);
247 * Start conversation with Storage daemon
249 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
252 sd = jcr->store_bsock;
254 * Now start a job with the Storage daemon
256 Dmsg2(dbglevel, "Read store=%s, write store=%s\n",
257 ((STORE *)jcr->rstorage->first())->name(),
258 ((STORE *)jcr->wstorage->first())->name());
259 if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage)) {
262 Dmsg0(150, "Storage daemon connection OK\n");
264 if (!send_bootstrap_file(jcr, sd) ||
265 !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
269 if (!bnet_fsend(sd, "run")) {
274 * Now start a Storage daemon message thread
276 if (!start_storage_daemon_message_thread(jcr)) {
281 set_jcr_job_status(jcr, JS_Running);
282 set_jcr_job_status(mig_jcr, JS_Running);
284 /* Pickup Job termination data */
285 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
286 wait_for_storage_daemon_termination(jcr);
288 set_jcr_job_status(jcr, jcr->SDJobStatus);
289 if (jcr->JobStatus != JS_Terminated) {
292 migration_cleanup(jcr, jcr->JobStatus);
294 UAContext *ua = new_ua_context(jcr);
295 purge_files_from_job(ua, jcr->previous_jr.JobId);
307 * Callback handler make list of DB Ids
309 static int dbid_handler(void *ctx, int num_fields, char **row)
311 idpkt *ids = (idpkt *)ctx;
313 Dmsg3(dbglevel, "count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
314 if (ids->count == 0) {
317 pm_strcat(ids->list, ",");
319 pm_strcat(ids->list, row[0]);
330 static int item_compare(void *item1, void *item2)
332 uitem *i1 = (uitem *)item1;
333 uitem *i2 = (uitem *)item2;
334 return strcmp(i1->item, i2->item);
337 static int unique_name_handler(void *ctx, int num_fields, char **row)
339 dlist *list = (dlist *)ctx;
341 uitem *new_item = (uitem *)malloc(sizeof(uitem));
344 memset(new_item, 0, sizeof(uitem));
345 new_item->item = bstrdup(row[0]);
346 Dmsg1(dbglevel, "Item=%s\n", row[0]);
347 item = (uitem *)list->binary_insert((void *)new_item, item_compare);
348 if (item != new_item) { /* already in list */
349 free(new_item->item);
350 free((char *)new_item);
356 /* Get Job names in Pool */
357 const char *sql_job =
358 "SELECT DISTINCT Job.Name from Job,Pool"
359 " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
361 /* Get JobIds from regex'ed Job names */
362 const char *sql_jobids_from_job =
363 "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
364 " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
365 " ORDER by Job.StartTime";
367 /* Get Client names in Pool */
368 const char *sql_client =
369 "SELECT DISTINCT Client.Name from Client,Pool,Job"
370 " WHERE Pool.Name='%s' AND Job.ClientId=Client.ClientId AND"
371 " Job.PoolId=Pool.PoolId";
373 /* Get JobIds from regex'ed Client names */
374 const char *sql_jobids_from_client =
375 "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
376 " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
377 " AND Job.ClientId=Client.ClientId "
378 " ORDER by Job.StartTime";
380 /* Get Volume names in Pool */
381 const char *sql_vol =
382 "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
383 " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
384 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
386 /* Get JobIds from regex'ed Volume names */
387 const char *sql_jobids_from_vol =
388 "SELECT DISTINCT Job.JobId,Job.StartTime FROM Media,JobMedia,Job"
389 " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId"
390 " AND JobMedia.JobId=Job.JobId"
391 " ORDER by Job.StartTime";
394 const char *sql_smallest_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 VolBytes ASC LIMIT 1";
400 const char *sql_oldest_vol =
401 "SELECT MediaId FROM Media,Pool WHERE"
402 " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
403 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
404 " ORDER BY LastWritten ASC LIMIT 1";
406 /* Get JobIds when we have selected MediaId */
407 const char *sql_jobids_from_mediaid =
408 "SELECT DISTINCT Job.JobId,Job.StartTime FROM JobMedia,Job"
409 " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=%s"
410 " ORDER by Job.StartTime";
412 const char *sql_pool_bytes =
413 "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
414 " VolStatus in ('Full','Used','Error','Append') AND Media.Enabled=1 AND"
415 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
417 const char *sql_vol_bytes =
418 "SELECT MediaId FROM Media,Pool WHERE"
419 " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
420 " Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
421 " VolBytes<%s ORDER BY LastWritten ASC LIMIT 1";
424 const char *sql_ujobid =
425 "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
426 " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
427 " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
432 * Returns: false on error
433 * true if OK and jcr->previous_jr filled in
435 static bool get_job_to_migrate(JCR *jcr)
438 POOL_MEM query(PM_MESSAGE);
444 ids.list = get_pool_memory(PM_MESSAGE);
445 Dmsg1(dbglevel, "list=%p\n", ids.list);
450 * If MigrateJobId is set, then we migrate only that Job,
451 * otherwise, we go through the full selection of jobs to
454 if (jcr->MigrateJobId != 0) {
455 Dmsg1(dbglevel, "At Job start previous jobid=%u\n", jcr->MigrateJobId);
456 edit_uint64(jcr->MigrateJobId, ids.list);
459 switch (jcr->job->selection_type) {
461 if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
466 if (!regex_find_jobids(jcr, &ids, sql_client, sql_jobids_from_client, "Client")) {
471 if (!regex_find_jobids(jcr, &ids, sql_vol, sql_jobids_from_vol, "Volume")) {
476 if (!jcr->job->selection_pattern) {
477 Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
480 Dmsg1(dbglevel, "SQL=%s\n", jcr->job->selection_pattern);
481 if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
482 dbid_handler, (void *)&ids)) {
483 Jmsg(jcr, M_FATAL, 0,
484 _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
488 case MT_SMALLEST_VOL:
489 if (!find_mediaid_then_jobids(jcr, &ids, sql_smallest_vol, "Smallest Volume")) {
494 if (!find_mediaid_then_jobids(jcr, &ids, sql_oldest_vol, "Oldest Volume")) {
499 /***** Below not implemented yet *********/
500 case MT_POOL_OCCUPANCY:
504 Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
505 if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
506 Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
509 if (ctx.count == 0) {
510 Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
513 if (ctx.value > (int64_t)jcr->pool->MigrationHighBytes) {
514 Dmsg2(dbglevel, "highbytes=%d pool=%d\n", (int)jcr->pool->MigrationHighBytes,
520 Dmsg0(dbglevel, "Pool time not implemented\n");
523 Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
529 * Loop over all jobids except the last one, sending
530 * them to start_migration_job(), which will start a job
531 * for each of them. For the last JobId, we handle it below.
534 for (int i=1; i < (int)ids.count; i++) {
536 stat = get_next_jobid_from_list(&p, &JobId);
537 Dmsg2(dbglevel, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
538 jcr->MigrateJobId = JobId;
539 start_migration_job(jcr);
541 Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
543 } else if (stat == 0) {
544 Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
549 /* Now get the last JobId and handle it in the current job */
551 stat = get_next_jobid_from_list(&p, &JobId);
552 Dmsg2(dbglevel, "Last get_next_jobid stat=%d JobId=%u\n", stat, JobId);
554 Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
556 } else if (stat == 0) {
557 Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
561 jcr->previous_jr.JobId = JobId;
562 Dmsg1(100, "Previous jobid=%d\n", jcr->previous_jr.JobId);
564 if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
565 Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
566 edit_int64(jcr->previous_jr.JobId, ed1),
567 db_strerror(jcr->db));
570 Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
571 jcr->previous_jr.JobId, jcr->previous_jr.Job);
574 free_pool_memory(ids.list);
578 free_pool_memory(ids.list);
582 static void start_migration_job(JCR *jcr)
584 UAContext *ua = new_ua_context(jcr);
587 Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name,
588 edit_uint64(jcr->MigrateJobId, ed1));
589 Dmsg1(dbglevel, "=============== Migration cmd=%s\n", ua->cmd);
590 parse_ua_args(ua); /* parse command */
591 int stat = run_cmd(ua, ua->cmd);
593 Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
595 Jmsg(jcr, M_INFO, 0, _("Migration JobId %d started.\n"), stat);
600 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
604 POOL_MEM query(PM_MESSAGE);
607 /* Basic query for MediaId */
608 Mmsg(query, query1, jcr->pool->hdr.name);
609 if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
610 Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
613 if (ids->count == 0) {
614 Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
616 if (ids->count != 1) {
617 Jmsg(jcr, M_FATAL, 0, _("SQL logic error. Count should be 1 but is %d\n"),
621 Dmsg1(dbglevel, "Smallest Vol Jobids=%s\n", ids->list);
623 ok = find_jobids_from_mediaid_list(jcr, ids, type);
629 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type)
632 POOL_MEM query(PM_MESSAGE);
634 Mmsg(query, sql_jobids_from_mediaid, ids->list);
636 if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
637 Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
640 if (ids->count == 0) {
641 Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
648 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
649 const char *query2, const char *type)
653 uitem *last_item = NULL;
658 POOL_MEM query(PM_MESSAGE);
660 item_chain = New(dlist(item, &item->link));
661 if (!jcr->job->selection_pattern) {
662 Jmsg(jcr, M_FATAL, 0, _("No Migration %s selection pattern specified.\n"),
666 Dmsg1(dbglevel, "regex=%s\n", jcr->job->selection_pattern);
667 /* Compile regex expression */
668 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
670 regerror(rc, &preg, prbuf, sizeof(prbuf));
671 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
672 jcr->job->selection_pattern, prbuf);
675 /* Basic query for names */
676 Mmsg(query, query1, jcr->pool->hdr.name);
677 Dmsg1(dbglevel, "query1=%s\n", query.c_str());
678 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
679 (void *)item_chain)) {
680 Jmsg(jcr, M_FATAL, 0,
681 _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
684 /* Now apply the regex to the names and remove any item not matched */
685 foreach_dlist(item, item_chain) {
686 const int nmatch = 30;
687 regmatch_t pmatch[nmatch];
689 Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
690 free(last_item->item);
691 item_chain->remove(last_item);
693 Dmsg1(dbglevel, "Item=%s\n", item->item);
694 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
696 last_item = NULL; /* keep this one */
702 free(last_item->item);
703 Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
704 item_chain->remove(last_item);
708 * At this point, we have a list of items in item_chain
709 * that have been matched by the regex, so now we need
710 * to look up their jobids.
713 foreach_dlist(item, item_chain) {
714 Dmsg2(dbglevel, "Got %s: %s\n", type, item->item);
715 Mmsg(query, query2, item->item, jcr->pool->hdr.name);
716 Dmsg1(dbglevel, "query2=%s\n", query.c_str());
717 if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
718 Jmsg(jcr, M_FATAL, 0,
719 _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
723 if (ids->count == 0) {
724 Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
728 Dmsg2(dbglevel, "Count=%d Jobids=%s\n", ids->count, ids->list);
730 Dmsg0(dbglevel, "After delete item_chain\n");
736 * Release resources allocated during backup.
738 void migration_cleanup(JCR *jcr, int TermCode)
740 char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
741 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
742 char ec6[50], ec7[50], ec8[50];
743 char term_code[100], sd_term_msg[100];
744 const char *term_msg;
749 JCR *mig_jcr = jcr->mig_jcr;
750 POOL_MEM query(PM_MESSAGE);
752 Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
753 dequeue_messages(jcr); /* display any queued messages */
754 memset(&mr, 0, sizeof(mr));
755 set_jcr_job_status(jcr, TermCode);
756 update_job_end_record(jcr); /* update database */
759 * Check if we actually did something.
760 * mig_jcr is jcr of the newly migrated job.
763 mig_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
764 mig_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
765 mig_jcr->VolSessionId = jcr->VolSessionId;
766 mig_jcr->VolSessionTime = jcr->VolSessionTime;
767 mig_jcr->jr.RealEndTime = 0;
768 mig_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
770 set_jcr_job_status(mig_jcr, TermCode);
773 update_job_end_record(mig_jcr);
775 /* Update final items to set them to the previous job's values */
776 Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
777 "JobTDate=%s WHERE JobId=%s",
778 jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime,
779 edit_uint64(jcr->previous_jr.JobTDate, ec1),
780 edit_uint64(mig_jcr->jr.JobId, ec2));
781 db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
783 /* Now marke the previous job as migrated */
784 Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
785 (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
786 db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
788 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
789 Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
790 db_strerror(jcr->db));
791 set_jcr_job_status(jcr, JS_ErrorTerminated);
794 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
795 if (!db_get_media_record(jcr, jcr->db, &mr)) {
796 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
797 mr.VolumeName, db_strerror(jcr->db));
798 set_jcr_job_status(jcr, JS_ErrorTerminated);
801 update_bootstrap_file(mig_jcr);
803 if (!db_get_job_volume_names(mig_jcr, mig_jcr->db, mig_jcr->jr.JobId, &mig_jcr->VolumeName)) {
805 * Note, if the job has erred, most likely it did not write any
806 * tape, so suppress this "error" message since in that case
807 * it is normal. Or look at it the other way, only for a
808 * normal exit should we complain about this error.
810 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
811 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(mig_jcr->db));
813 mig_jcr->VolumeName[0] = 0; /* none */
817 msg_type = M_INFO; /* by default INFO message */
818 switch (jcr->JobStatus) {
820 if (jcr->Errors || jcr->SDErrors) {
821 term_msg = _("%s OK -- with warnings");
823 term_msg = _("%s OK");
827 case JS_ErrorTerminated:
828 term_msg = _("*** %s Error ***");
829 msg_type = M_ERROR; /* Generate error message */
830 if (jcr->store_bsock) {
831 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
832 if (jcr->SD_msg_chan) {
833 pthread_cancel(jcr->SD_msg_chan);
838 term_msg = _("%s Canceled");
839 if (jcr->store_bsock) {
840 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
841 if (jcr->SD_msg_chan) {
842 pthread_cancel(jcr->SD_msg_chan);
847 term_msg = _("Inappropriate %s term code");
850 bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
851 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
852 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
853 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
857 kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
861 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
863 Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
864 " Prev Backup JobId: %s\n"
865 " New Backup JobId: %s\n"
866 " Migration JobId: %s\n"
867 " Migration Job: %s\n"
868 " Backup Level: %s%s\n"
870 " FileSet: \"%s\" %s\n"
871 " Pool: \"%s\" (From %s)\n"
872 " Storage: \"%s\" (From %s)\n"
875 " Elapsed time: %s\n"
877 " SD Files Written: %s\n"
878 " SD Bytes Written: %s (%sB)\n"
880 " Volume name(s): %s\n"
881 " Volume Session Id: %d\n"
882 " Volume Session Time: %d\n"
883 " Last Volume Bytes: %s (%sB)\n"
885 " SD termination status: %s\n"
886 " Termination: %s\n\n"),
890 mig_jcr ? edit_uint64(jcr->previous_jr.JobId, ec6) : "0",
891 mig_jcr ? edit_uint64(mig_jcr->jr.JobId, ec7) : "0",
892 edit_uint64(jcr->jr.JobId, ec8),
894 level_to_str(jcr->JobLevel), jcr->since,
896 jcr->fileset->name(), jcr->FSCreateTime,
897 jcr->pool->name(), jcr->pool_source,
898 jcr->wstore->name(), jcr->storage_source,
901 edit_utime(RunTime, elapsed, sizeof(elapsed)),
903 edit_uint64_with_commas(jcr->SDJobFiles, ec1),
904 edit_uint64_with_commas(jcr->SDJobBytes, ec2),
905 edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
907 mig_jcr ? mig_jcr->VolumeName : "",
910 edit_uint64_with_commas(mr.VolBytes, ec4),
911 edit_uint64_with_suffix(mr.VolBytes, ec5),
916 Dmsg1(100, "migrate_cleanup() mig_jcr=0x%x\n", jcr->mig_jcr);
918 free_jcr(jcr->mig_jcr);
921 Dmsg0(100, "Leave migrate_cleanup()\n");