]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
Final changes
[bacula/bacula] / bacula / src / dird / migrate.c
1 /*
2  *
3  *   Bacula Director -- migrate.c -- responsible for doing
4  *     migration jobs.
5  *
6  *     Kern Sibbald, September MMIV
7  *
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
12  *       to do the backup.
13  *     When the Storage daemon finishes the job, update the DB.
14  *
15  *   Version $Id$
16  */
17 /*
18    Copyright (C) 2004-2006 Kern Sibbald
19
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.
24
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.
29
30  */
31
32 #include "bacula.h"
33 #include "dird.h"
34 #include "ua.h"
35 #ifndef HAVE_REGEX_H
36 #include "lib/bregex.h"
37 #else
38 #include <regex.h>
39 #endif
40
41 static const int dbglevel = 100;
42
43 static char OKbootstrap[] = "3000 OK bootstrap\n";
44 static bool get_job_to_migrate(JCR *jcr);
45 struct idpkt;
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,
49                  const char *type);
50 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type);
51 static void start_migration_job(JCR *jcr);
52 static int get_next_dbid_from_list(char **p, DBId_t *DBId);
53
54 /* 
55  * Called here before the job is run to do the job
56  *   specific setup.
57  */
58 bool do_migration_init(JCR *jcr)
59 {
60    /* If we find a job or jobs to migrate it is previous_jr.JobId */
61    if (!get_job_to_migrate(jcr)) {
62       return false;
63    }
64
65    if (jcr->previous_jr.JobId == 0) {
66       return true;                    /* no work */
67    }
68
69    if (!get_or_create_fileset_record(jcr)) {
70       return false;
71    }
72
73    apply_pool_overrides(jcr);
74
75    jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->hdr.name);
76    if (jcr->jr.PoolId == 0) {
77       return false;
78    }
79
80    /* If pool storage specified, use it instead of job storage */
81    copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
82
83    if (!jcr->wstorage) {
84       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
85       return false;
86    }
87
88    create_restore_bootstrap_file(jcr);
89    return true;
90 }
91
92 /*
93  * Do a Migration of a previous job
94  *
95  *  Returns:  false on failure
96  *            true  on success
97  */
98 bool do_migration(JCR *jcr)
99 {
100    POOL_DBR pr;
101    POOL *pool;
102    char ed1[100];
103    BSOCK *sd;
104    JOB *job, *prev_job;
105    JCR *mig_jcr;                   /* newly migrated job */
106
107    /* 
108     *  previous_jr refers to the job DB record of the Job that is
109     *    going to be migrated.
110     *  prev_job refers to the job resource of the Job that is
111     *    going to be migrated.
112     *  jcr is the jcr for the current "migration" job.  It is a
113     *    control job that is put in the DB as a migration job, which
114     *    means that this job migrated a previous job to a new job.
115     *    No Volume or File data is associated with this control
116     *    job.
117     *  mig_jcr refers to the newly migrated job that is run by
118     *    the current jcr.  It is a backup job that moves (migrates) the
119     *    data written for the previous_jr into the new pool.  This
120     *    job (mig_jcr) becomes the new backup job that replaces
121     *    the original backup job.
122     */
123    if (jcr->previous_jr.JobId == 0 || jcr->ExpectedFiles == 0) {
124       set_jcr_job_status(jcr, JS_Terminated);
125       migration_cleanup(jcr, jcr->JobStatus);
126       return true;                    /* no work */
127    }
128
129    Dmsg4(dbglevel, "Previous: Name=%s JobId=%d Type=%c Level=%c\n",
130       jcr->previous_jr.Name, jcr->previous_jr.JobId, 
131       jcr->previous_jr.JobType, jcr->previous_jr.JobLevel);
132
133    Dmsg4(dbglevel, "Current: Name=%s JobId=%d Type=%c Level=%c\n",
134       jcr->jr.Name, jcr->jr.JobId, 
135       jcr->jr.JobType, jcr->jr.JobLevel);
136
137    LockRes();
138    job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
139    prev_job = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
140    UnlockRes();
141    if (!job || !prev_job) {
142       return false;
143    }
144
145    /* Create a migation jcr */
146    mig_jcr = jcr->mig_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
147    memcpy(&mig_jcr->previous_jr, &jcr->previous_jr, sizeof(mig_jcr->previous_jr));
148
149    /*
150     * Turn the mig_jcr into a "real" job that takes on the aspects of
151     *   the previous backup job "prev_job".
152     */
153    set_jcr_defaults(mig_jcr, prev_job);
154    if (!setup_job(mig_jcr)) {
155       return false;
156    }
157
158    /* Now reset the job record from the previous job */
159    memcpy(&mig_jcr->jr, &jcr->previous_jr, sizeof(mig_jcr->jr));
160    /* Update the jr to reflect the new values of PoolId, FileSetId, and JobId. */
161    mig_jcr->jr.PoolId = jcr->jr.PoolId;
162    mig_jcr->jr.FileSetId = jcr->jr.FileSetId;
163    mig_jcr->jr.JobId = mig_jcr->JobId;
164
165    Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
166       mig_jcr->jr.Name, mig_jcr->jr.JobId, 
167       mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
168
169    /*
170     * Get the PoolId used with the original job. Then
171     *  find the pool name from the database record.
172     */
173    memset(&pr, 0, sizeof(pr));
174    pr.PoolId = mig_jcr->previous_jr.PoolId;
175    if (!db_get_pool_record(jcr, jcr->db, &pr)) {
176       Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
177             edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
178          return false;
179    }
180    /* Get the pool resource corresponding to the original job */
181    pool = (POOL *)GetResWithName(R_POOL, pr.Name);
182    if (!pool) {
183       Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
184       return false;
185    }
186
187    /* If pool storage specified, use it for restore */
188    copy_rstorage(mig_jcr, pool->storage, _("Pool resource"));
189    copy_rstorage(jcr, pool->storage, _("Pool resource"));
190
191    /*
192     * If the original backup pool has a NextPool, make sure a 
193     *  record exists in the database. Note, in this case, we
194     *  will be migrating from pool to pool->NextPool.
195     */
196    if (pool->NextPool) {
197       jcr->jr.PoolId = get_or_create_pool_record(jcr, pool->NextPool->hdr.name);
198       if (jcr->jr.PoolId == 0) {
199          return false;
200       }
201       /*
202        * put the "NextPool" resource pointer in our jcr so that we
203        * can pull the Storage reference from it.
204        */
205       mig_jcr->pool = jcr->pool = pool->NextPool;
206       mig_jcr->jr.PoolId = jcr->jr.PoolId;
207       pm_strcpy(jcr->pool_source, _("NextPool in Pool resource"));
208    }
209
210    /* If pool storage specified, use it instead of job storage for backup */
211    copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
212
213    /* Print Job Start message */
214    Jmsg(jcr, M_INFO, 0, _("Start Migration JobId %s, Job=%s\n"),
215         edit_uint64(jcr->JobId, ed1), jcr->Job);
216
217    set_jcr_job_status(jcr, JS_Running);
218    set_jcr_job_status(mig_jcr, JS_Running);
219    Dmsg2(dbglevel, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
220
221    /* Update job start record for this migration control job */
222    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
223       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
224       return false;
225    }
226
227    Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
228       mig_jcr->jr.Name, mig_jcr->jr.JobId, 
229       mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
230
231    /* Update job start record for the real migration backup job */
232    if (!db_update_job_start_record(mig_jcr, mig_jcr->db, &mig_jcr->jr)) {
233       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(mig_jcr->db));
234       return false;
235    }
236
237
238    /*
239     * Open a message channel connection with the Storage
240     * daemon. This is to let him know that our client
241     * will be contacting him for a backup  session.
242     *
243     */
244    Dmsg0(110, "Open connection with storage daemon\n");
245    set_jcr_job_status(jcr, JS_WaitSD);
246    set_jcr_job_status(mig_jcr, JS_WaitSD);
247    /*
248     * Start conversation with Storage daemon
249     */
250    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
251       return false;
252    }
253    sd = jcr->store_bsock;
254    /*
255     * Now start a job with the Storage daemon
256     */
257    Dmsg2(dbglevel, "Read store=%s, write store=%s\n", 
258       ((STORE *)jcr->rstorage->first())->name(),
259       ((STORE *)jcr->wstorage->first())->name());
260    if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage)) {
261       return false;
262    }
263    Dmsg0(150, "Storage daemon connection OK\n");
264
265    if (!send_bootstrap_file(jcr, sd) ||
266        !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
267       return false;
268    }
269
270    if (!bnet_fsend(sd, "run")) {
271       return false;
272    }
273
274    /*
275     * Now start a Storage daemon message thread
276     */
277    if (!start_storage_daemon_message_thread(jcr)) {
278       return false;
279    }
280
281
282    set_jcr_job_status(jcr, JS_Running);
283    set_jcr_job_status(mig_jcr, JS_Running);
284
285    /* Pickup Job termination data */
286    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
287    wait_for_storage_daemon_termination(jcr);
288
289    set_jcr_job_status(jcr, jcr->SDJobStatus);
290    if (jcr->JobStatus != JS_Terminated) {
291       return false;
292    }
293    migration_cleanup(jcr, jcr->JobStatus);
294    if (mig_jcr) {
295       UAContext *ua = new_ua_context(jcr);
296       purge_files_from_job(ua, jcr->previous_jr.JobId);
297       free_ua_context(ua);
298    }
299    return true;
300 }
301
302 struct idpkt {
303    POOLMEM *list;
304    uint32_t count;
305 };
306
307 /*
308  * Callback handler make list of DB Ids
309  */
310 static int dbid_handler(void *ctx, int num_fields, char **row)
311 {
312    idpkt *ids = (idpkt *)ctx;
313
314    Dmsg3(dbglevel, "count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
315    if (ids->count == 0) {
316       ids->list[0] = 0;
317    } else {
318       pm_strcat(ids->list, ",");
319    }
320    pm_strcat(ids->list, row[0]);
321    ids->count++;
322    return 0;
323 }
324
325
326 struct uitem {
327    dlink link;   
328    char *item;
329 };
330
331 static int item_compare(void *item1, void *item2)
332 {
333    uitem *i1 = (uitem *)item1;
334    uitem *i2 = (uitem *)item2;
335    return strcmp(i1->item, i2->item);
336 }
337
338 static int unique_name_handler(void *ctx, int num_fields, char **row)
339 {
340    dlist *list = (dlist *)ctx;
341
342    uitem *new_item = (uitem *)malloc(sizeof(uitem));
343    uitem *item;
344    
345    memset(new_item, 0, sizeof(uitem));
346    new_item->item = bstrdup(row[0]);
347    Dmsg1(dbglevel, "Item=%s\n", row[0]);
348    item = (uitem *)list->binary_insert((void *)new_item, item_compare);
349    if (item != new_item) {            /* already in list */
350       free(new_item->item);
351       free((char *)new_item);
352       return 0;
353    }
354    return 0;
355 }
356
357 /* Get Job names in Pool */
358 const char *sql_job =
359    "SELECT DISTINCT Job.Name from Job,Pool"
360    " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
361
362 /* Get JobIds from regex'ed Job names */
363 const char *sql_jobids_from_job =
364    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
365    " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
366    " ORDER by Job.StartTime";
367
368 /* Get Client names in Pool */
369 const char *sql_client =
370    "SELECT DISTINCT Client.Name from Client,Pool,Job"
371    " WHERE Pool.Name='%s' AND Job.ClientId=Client.ClientId AND"
372    " Job.PoolId=Pool.PoolId";
373
374 /* Get JobIds from regex'ed Client names */
375 const char *sql_jobids_from_client =
376    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool,Client"
377    " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
378    " AND Job.ClientId=Client.ClientId "
379    " ORDER by Job.StartTime";
380
381 /* Get Volume names in Pool */
382 const char *sql_vol = 
383    "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
384    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
385    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
386
387 /* Get JobIds from regex'ed Volume names */
388 const char *sql_jobids_from_vol =
389    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Media,JobMedia,Job"
390    " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId"
391    " AND JobMedia.JobId=Job.JobId" 
392    " ORDER by Job.StartTime";
393
394
395 const char *sql_smallest_vol = 
396    "SELECT MediaId FROM Media,Pool WHERE"
397    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
398    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
399    " ORDER BY VolBytes ASC LIMIT 1";
400
401 const char *sql_oldest_vol = 
402    "SELECT MediaId FROM Media,Pool WHERE"
403    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
404    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
405    " ORDER BY LastWritten ASC LIMIT 1";
406
407 /* Get JobIds when we have selected MediaId */
408 const char *sql_jobids_from_mediaid =
409    "SELECT DISTINCT Job.JobId,Job.StartTime FROM JobMedia,Job"
410    " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=%s"
411    " ORDER by Job.StartTime";
412
413 /* Get tne number of bytes in the pool */
414 const char *sql_pool_bytes =
415    "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
416    " VolStatus in ('Full','Used','Error','Append') AND Media.Enabled=1 AND"
417    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
418
419 /* Get tne number of bytes in the Jobs */
420 const char *sql_job_bytes =
421    "SELECT SUM(JobBytes) FROM Job WHERE JobId IN (%s)";
422
423
424 /* Get Media Ids in Pool */
425 const char *sql_mediaids =
426    "SELECT MediaId FROM Media,Pool WHERE"
427    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
428    " Media.PoolId=Pool.PoolId AND Pool.Name='%s' ORDER BY LastWritten ASC";
429
430 /* Get JobIds in Pool longer than specified time */
431 const char *sql_pool_time = 
432    "SELECT DISTINCT Job.JobId from Pool,Job,Media,JobMedia WHERE"
433    " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND"
434    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
435    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId"
436    " AND Job.RealEndTime<='%s'";
437
438 /*
439 * const char *sql_ujobid =
440 *   "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
441 *   " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
442 *   " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
443 */
444
445
446
447 /*
448  *
449  * This is the central piece of code that finds a job or jobs 
450  *   actually JobIds to migrate.  It first looks to see if one
451  *   has been "manually" specified in jcr->MigrateJobId, and if
452  *   so, it returns that JobId to be run.  Otherwise, it
453  *   examines the Selection Type to see what kind of migration
454  *   we are doing (Volume, Job, Client, ...) and applies any
455  *   Selection Pattern if appropriate to obtain a list of JobIds.
456  *   Finally, it will loop over all the JobIds found, except the last
457  *   one starting a new job with MigrationJobId set to that JobId, and
458  *   finally, it returns the last JobId to the caller.
459  *
460  * Returns: false on error
461  *          true  if OK and jcr->previous_jr filled in
462  */
463 static bool get_job_to_migrate(JCR *jcr)
464 {
465    char ed1[30];
466    POOL_MEM query(PM_MESSAGE);
467    JobId_t JobId;
468    DBId_t  MediaId = 0;
469    int stat;
470    char *p;
471    idpkt ids, mid, jids;
472    db_int64_ctx ctx;
473    int64_t pool_bytes;
474    bool ok;
475    time_t ttime;
476    struct tm tm;
477    char dt[MAX_TIME_LENGTH];
478
479    ids.list = get_pool_memory(PM_MESSAGE);
480    ids.list[0] = 0;
481    ids.count = 0;
482    mid.list = get_pool_memory(PM_MESSAGE);
483    mid.list[0] = 0;
484    mid.count = 0;
485    jids.list = get_pool_memory(PM_MESSAGE);
486    jids.list[0] = 0;
487    jids.count = 0;
488
489
490    /*
491     * If MigrateJobId is set, then we migrate only that Job,
492     *  otherwise, we go through the full selection of jobs to
493     *  migrate.
494     */
495    if (jcr->MigrateJobId != 0) {
496       Dmsg1(dbglevel, "At Job start previous jobid=%u\n", jcr->MigrateJobId);
497       edit_uint64(jcr->MigrateJobId, ids.list);
498       ids.count = 1;
499    } else {
500       switch (jcr->job->selection_type) {
501       case MT_JOB:
502          if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
503             goto bail_out;
504          } 
505          break;
506       case MT_CLIENT:
507          if (!regex_find_jobids(jcr, &ids, sql_client, sql_jobids_from_client, "Client")) {
508             goto bail_out;
509          } 
510          break;
511       case MT_VOLUME:
512          if (!regex_find_jobids(jcr, &ids, sql_vol, sql_jobids_from_vol, "Volume")) {
513             goto bail_out;
514          } 
515          break;
516       case MT_SQLQUERY:
517          if (!jcr->job->selection_pattern) {
518             Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
519             goto bail_out;
520          }
521          Dmsg1(dbglevel, "SQL=%s\n", jcr->job->selection_pattern);
522          if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
523               dbid_handler, (void *)&ids)) {
524             Jmsg(jcr, M_FATAL, 0,
525                  _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
526             goto bail_out;
527          }
528          break;
529       case MT_SMALLEST_VOL:
530          if (!find_mediaid_then_jobids(jcr, &ids, sql_smallest_vol, "Smallest Volume")) {
531             goto bail_out;
532          }
533          break;
534       case MT_OLDEST_VOL:
535          if (!find_mediaid_then_jobids(jcr, &ids, sql_oldest_vol, "Oldest Volume")) {
536             goto bail_out;
537          }
538          break;
539
540       case MT_POOL_OCCUPANCY:
541          ctx.count = 0;
542          /* Find count of bytes in pool */
543          Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
544          if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
545             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
546             goto bail_out;
547          }
548          if (ctx.count == 0) {
549             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
550             goto ok_out;
551          }
552          pool_bytes = ctx.value;
553          Dmsg2(dbglevel, "highbytes=%d pool=%d\n", (int)jcr->pool->MigrationHighBytes,
554                (int)pool_bytes);
555          if (pool_bytes < (int64_t)jcr->pool->MigrationHighBytes) {
556             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
557             goto ok_out;
558          }
559          Dmsg0(dbglevel, "We should do Occupation migration.\n");
560
561          ids.count = 0;
562          /* Find a list of MediaIds that could be migrated */
563          Mmsg(query, sql_mediaids, jcr->pool->hdr.name);
564 //       Dmsg1(dbglevel, "query=%s\n", query.c_str());
565          if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)&ids)) {
566             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
567             goto bail_out;
568          }
569          if (ids.count == 0) {
570             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
571             goto ok_out;
572          }
573          Dmsg2(dbglevel, "Pool Occupancy ids=%d MediaIds=%s\n", ids.count, ids.list);
574
575          /*
576           * Now loop over MediaIds getting more JobIds to migrate until
577           *  we reduce the pool occupancy below the low water mark.
578           */
579          p = ids.list;
580          for (int i=0; i < (int)ids.count; i++) {
581             stat = get_next_dbid_from_list(&p, &MediaId);
582             Dmsg2(dbglevel, "get_next_dbid stat=%d MediaId=%u\n", stat, MediaId);
583             if (stat < 0) {
584                Jmsg(jcr, M_FATAL, 0, _("Invalid MediaId found.\n"));
585                goto bail_out;
586             } else if (stat == 0) {
587                break;
588             }
589             mid.count = 1;
590             Mmsg(mid.list, "%s", edit_int64(MediaId, ed1));
591             ok = find_jobids_from_mediaid_list(jcr, &mid, "Volumes");
592             if (!ok) {
593                continue;
594             }
595             if (i != 0) {
596                pm_strcat(jids.list, ",");
597             }
598             pm_strcat(jids.list, mid.list);
599             jids.count += mid.count;
600
601             /* Now get the count of bytes added */
602             ctx.count = 0;
603             /* Find count of bytes from Jobs */
604             Mmsg(query, sql_job_bytes, mid.list);
605             if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
606                Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
607                goto bail_out;
608             }
609             pool_bytes -= ctx.value;
610             Dmsg1(dbglevel, "Job bytes=%d\n", (int)ctx.value);
611             Dmsg2(dbglevel, "lowbytes=%d pool=%d\n", (int)jcr->pool->MigrationLowBytes,
612                   (int)pool_bytes);
613             if (pool_bytes <= (int64_t)jcr->pool->MigrationLowBytes) {
614                Dmsg0(dbglevel, "We should be done.\n");
615                break;
616             }
617
618          }
619          Dmsg2(dbglevel, "Pool Occupancy ids=%d JobIds=%s\n", jids.count, jids.list);
620
621          break;
622
623       case MT_POOL_TIME:
624          ttime = time(NULL) - (time_t)jcr->pool->MigrationTime;
625          (void)localtime_r(&ttime, &tm);
626          strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
627
628          ids.count = 0;
629          Mmsg(query, sql_pool_time, jcr->pool->hdr.name, dt);
630 //       Dmsg1(000, "query=%s\n", query.c_str());
631          if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)&ids)) {
632             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
633             goto bail_out;
634          }
635          if (ids.count == 0) {
636             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
637             goto ok_out;
638          }
639          Dmsg2(dbglevel, "PoolTime ids=%d JobIds=%s\n", ids.count, ids.list);
640          break;
641
642       default:
643          Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
644          goto bail_out;
645       }
646    }
647
648    /*
649     * Loop over all jobids except the last one, sending
650     *  them to start_migration_job(), which will start a job
651     *  for each of them.  For the last JobId, we handle it below.
652     */
653    p = ids.list;
654    Jmsg(jcr, M_INFO, 0, _("The following %u JobIds will be migrated: %s\n"),
655       ids.count, ids.list);
656    for (int i=1; i < (int)ids.count; i++) {
657       JobId = 0;
658       stat = get_next_jobid_from_list(&p, &JobId);
659       Dmsg2(dbglevel, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
660       jcr->MigrateJobId = JobId;
661       start_migration_job(jcr);
662       if (stat < 0) {
663          Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
664          goto bail_out;
665       } else if (stat == 0) {
666          Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
667          goto ok_out;
668       }
669    }
670    
671    /* Now get the last JobId and handle it in the current job */
672    JobId = 0;
673    stat = get_next_jobid_from_list(&p, &JobId);
674    Dmsg2(dbglevel, "Last get_next_jobid stat=%d JobId=%u\n", stat, JobId);
675    if (stat < 0) {
676       Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
677       goto bail_out;
678    } else if (stat == 0) {
679       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
680       goto ok_out;
681    }
682
683    jcr->previous_jr.JobId = JobId;
684    Dmsg1(100, "Previous jobid=%d\n", jcr->previous_jr.JobId);
685
686    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
687       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
688            edit_int64(jcr->previous_jr.JobId, ed1),
689            db_strerror(jcr->db));
690       goto bail_out;
691    }
692    Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
693       jcr->previous_jr.JobId, jcr->previous_jr.Job);
694
695 ok_out:
696    free_pool_memory(ids.list);
697    free_pool_memory(mid.list);
698    free_pool_memory(jids.list);
699    return true;
700
701 bail_out:
702    free_pool_memory(ids.list);
703    free_pool_memory(mid.list);
704    free_pool_memory(jids.list);
705    return false;
706 }
707
708 static void start_migration_job(JCR *jcr)
709 {
710    UAContext *ua = new_ua_context(jcr);
711    char ed1[50];
712    ua->batch = true;
713    Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name, 
714         edit_uint64(jcr->MigrateJobId, ed1));
715    Dmsg1(dbglevel, "=============== Migration cmd=%s\n", ua->cmd);
716    parse_ua_args(ua);                 /* parse command */
717    int stat = run_cmd(ua, ua->cmd);
718    if (stat == 0) {
719       Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
720    } else {
721       Jmsg(jcr, M_INFO, 0, _("Migration JobId %d started.\n"), stat);
722    }
723    free_ua_context(ua);
724 }
725
726 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
727                  const char *type) 
728 {
729    bool ok = false;
730    POOL_MEM query(PM_MESSAGE);
731
732    ids->count = 0;
733    /* Basic query for MediaId */
734    Mmsg(query, query1, jcr->pool->hdr.name);
735    if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
736       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
737       goto bail_out;
738    }
739    if (ids->count == 0) {
740       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
741    }
742    if (ids->count != 1) {
743       Jmsg(jcr, M_FATAL, 0, _("SQL logic error. Count should be 1 but is %d\n"), 
744          ids->count);
745       goto bail_out;
746    }
747    Dmsg1(dbglevel, "Smallest Vol Jobids=%s\n", ids->list);
748
749    ok = find_jobids_from_mediaid_list(jcr, ids, type);
750
751 bail_out:
752    return ok;
753 }
754
755 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type) 
756 {
757    bool ok = false;
758    POOL_MEM query(PM_MESSAGE);
759
760    Mmsg(query, sql_jobids_from_mediaid, ids->list);
761    ids->count = 0;
762    if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
763       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
764       goto bail_out;
765    }
766    if (ids->count == 0) {
767       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
768    }
769    ok = true;
770 bail_out:
771    return ok;
772 }
773
774 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
775                  const char *query2, const char *type) 
776 {
777    dlist *item_chain;
778    uitem *item = NULL;
779    uitem *last_item = NULL;
780    regex_t preg;
781    char prbuf[500];
782    int rc;
783    bool ok = false;
784    POOL_MEM query(PM_MESSAGE);
785
786    item_chain = New(dlist(item, &item->link));
787    if (!jcr->job->selection_pattern) {
788       Jmsg(jcr, M_FATAL, 0, _("No Migration %s selection pattern specified.\n"),
789          type);
790       goto bail_out;
791    }
792    Dmsg1(dbglevel, "regex=%s\n", jcr->job->selection_pattern);
793    /* Compile regex expression */
794    rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
795    if (rc != 0) {
796       regerror(rc, &preg, prbuf, sizeof(prbuf));
797       Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
798            jcr->job->selection_pattern, prbuf);
799       goto bail_out;
800    }
801    /* Basic query for names */
802    Mmsg(query, query1, jcr->pool->hdr.name);
803    Dmsg1(dbglevel, "query1=%s\n", query.c_str());
804    if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
805         (void *)item_chain)) {
806       Jmsg(jcr, M_FATAL, 0,
807            _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
808       goto bail_out;
809    }
810    /* Now apply the regex to the names and remove any item not matched */
811    foreach_dlist(item, item_chain) {
812       const int nmatch = 30;
813       regmatch_t pmatch[nmatch];
814       if (last_item) {
815          Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
816          free(last_item->item);
817          item_chain->remove(last_item);
818       }
819       Dmsg1(dbglevel, "Item=%s\n", item->item);
820       rc = regexec(&preg, item->item, nmatch, pmatch,  0);
821       if (rc == 0) {
822          last_item = NULL;   /* keep this one */
823       } else {   
824          last_item = item;
825       }
826    }
827    if (last_item) {
828       free(last_item->item);
829       Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
830       item_chain->remove(last_item);
831    }
832    regfree(&preg);
833    /* 
834     * At this point, we have a list of items in item_chain
835     *  that have been matched by the regex, so now we need
836     *  to look up their jobids.
837     */
838    ids->count = 0;
839    foreach_dlist(item, item_chain) {
840       Dmsg2(dbglevel, "Got %s: %s\n", type, item->item);
841       Mmsg(query, query2, item->item, jcr->pool->hdr.name);
842       Dmsg1(dbglevel, "query2=%s\n", query.c_str());
843       if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
844          Jmsg(jcr, M_FATAL, 0,
845               _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
846          goto bail_out;
847       }
848    }
849    if (ids->count == 0) {
850       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
851    }
852    ok = true;
853 bail_out:
854    Dmsg2(dbglevel, "Count=%d Jobids=%s\n", ids->count, ids->list);
855    delete item_chain;
856    Dmsg0(dbglevel, "After delete item_chain\n");
857    return ok;
858 }
859
860
861 /*
862  * Release resources allocated during backup.
863  */
864 void migration_cleanup(JCR *jcr, int TermCode)
865 {
866    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
867    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
868    char ec6[50], ec7[50], ec8[50];
869    char term_code[100], sd_term_msg[100];
870    const char *term_msg;
871    int msg_type;
872    MEDIA_DBR mr;
873    double kbps;
874    utime_t RunTime;
875    JCR *mig_jcr = jcr->mig_jcr;
876    POOL_MEM query(PM_MESSAGE);
877
878    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
879    dequeue_messages(jcr);             /* display any queued messages */
880    memset(&mr, 0, sizeof(mr));
881    set_jcr_job_status(jcr, TermCode);
882    update_job_end_record(jcr);        /* update database */
883
884    /* 
885     * Check if we actually did something.  
886     *  mig_jcr is jcr of the newly migrated job.
887     */
888    if (mig_jcr) {
889       mig_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
890       mig_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
891       mig_jcr->VolSessionId = jcr->VolSessionId;
892       mig_jcr->VolSessionTime = jcr->VolSessionTime;
893       mig_jcr->jr.RealEndTime = 0; 
894       mig_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
895
896       set_jcr_job_status(mig_jcr, TermCode);
897
898   
899       update_job_end_record(mig_jcr);
900      
901       /* Update final items to set them to the previous job's values */
902       Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
903                   "JobTDate=%s WHERE JobId=%s", 
904          jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
905          edit_uint64(jcr->previous_jr.JobTDate, ec1),
906          edit_uint64(mig_jcr->jr.JobId, ec2));
907       db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
908
909       /* Now marke the previous job as migrated */
910       Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
911            (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
912       db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
913
914       if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
915          Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
916             db_strerror(jcr->db));
917          set_jcr_job_status(jcr, JS_ErrorTerminated);
918       }
919
920       bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
921       if (!db_get_media_record(jcr, jcr->db, &mr)) {
922          Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
923             mr.VolumeName, db_strerror(jcr->db));
924          set_jcr_job_status(jcr, JS_ErrorTerminated);
925       }
926
927       update_bootstrap_file(mig_jcr);
928
929       if (!db_get_job_volume_names(mig_jcr, mig_jcr->db, mig_jcr->jr.JobId, &mig_jcr->VolumeName)) {
930          /*
931           * Note, if the job has erred, most likely it did not write any
932           *  tape, so suppress this "error" message since in that case
933           *  it is normal.  Or look at it the other way, only for a
934           *  normal exit should we complain about this error.
935           */
936          if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
937             Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(mig_jcr->db));
938          }
939          mig_jcr->VolumeName[0] = 0;         /* none */
940       }
941   }
942
943    msg_type = M_INFO;                 /* by default INFO message */
944    switch (jcr->JobStatus) {
945    case JS_Terminated:
946       if (jcr->Errors || jcr->SDErrors) {
947          term_msg = _("%s OK -- with warnings");
948       } else {
949          term_msg = _("%s OK");
950       }
951       break;
952    case JS_FatalError:
953    case JS_ErrorTerminated:
954       term_msg = _("*** %s Error ***");
955       msg_type = M_ERROR;          /* Generate error message */
956       if (jcr->store_bsock) {
957          bnet_sig(jcr->store_bsock, BNET_TERMINATE);
958          if (jcr->SD_msg_chan) {
959             pthread_cancel(jcr->SD_msg_chan);
960          }
961       }
962       break;
963    case JS_Canceled:
964       term_msg = _("%s Canceled");
965       if (jcr->store_bsock) {
966          bnet_sig(jcr->store_bsock, BNET_TERMINATE);
967          if (jcr->SD_msg_chan) {
968             pthread_cancel(jcr->SD_msg_chan);
969          }
970       }
971       break;
972    default:
973       term_msg = _("Inappropriate %s term code");
974       break;
975    }
976    bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
977    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
978    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
979    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
980    if (RunTime <= 0) {
981       kbps = 0;
982    } else {
983       kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
984    }
985
986
987    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
988
989    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
990 "  Prev Backup JobId:      %s\n"
991 "  New Backup JobId:       %s\n"
992 "  Migration JobId:        %s\n"
993 "  Migration Job:          %s\n"
994 "  Backup Level:           %s%s\n"
995 "  Client:                 %s\n"
996 "  FileSet:                \"%s\" %s\n"
997 "  Pool:                   \"%s\" (From %s)\n"
998 "  Storage:                \"%s\" (From %s)\n"
999 "  Start time:             %s\n"
1000 "  End time:               %s\n"
1001 "  Elapsed time:           %s\n"
1002 "  Priority:               %d\n"
1003 "  SD Files Written:       %s\n"
1004 "  SD Bytes Written:       %s (%sB)\n"
1005 "  Rate:                   %.1f KB/s\n"
1006 "  Volume name(s):         %s\n"
1007 "  Volume Session Id:      %d\n"
1008 "  Volume Session Time:    %d\n"
1009 "  Last Volume Bytes:      %s (%sB)\n"
1010 "  SD Errors:              %d\n"
1011 "  SD termination status:  %s\n"
1012 "  Termination:            %s\n\n"),
1013    VERSION,
1014    LSMDATE,
1015         edt, 
1016         mig_jcr ? edit_uint64(jcr->previous_jr.JobId, ec6) : "0", 
1017         mig_jcr ? edit_uint64(mig_jcr->jr.JobId, ec7) : "0",
1018         edit_uint64(jcr->jr.JobId, ec8),
1019         jcr->jr.Job,
1020         level_to_str(jcr->JobLevel), jcr->since,
1021         jcr->client->name(),
1022         jcr->fileset->name(), jcr->FSCreateTime,
1023         jcr->pool->name(), jcr->pool_source,
1024         jcr->wstore->name(), jcr->storage_source,
1025         sdt,
1026         edt,
1027         edit_utime(RunTime, elapsed, sizeof(elapsed)),
1028         jcr->JobPriority,
1029         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
1030         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
1031         edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
1032         (float)kbps,
1033         mig_jcr ? mig_jcr->VolumeName : "",
1034         jcr->VolSessionId,
1035         jcr->VolSessionTime,
1036         edit_uint64_with_commas(mr.VolBytes, ec4),
1037         edit_uint64_with_suffix(mr.VolBytes, ec5),
1038         jcr->SDErrors,
1039         sd_term_msg,
1040         term_code);
1041
1042    Dmsg1(100, "migrate_cleanup() mig_jcr=0x%x\n", jcr->mig_jcr);
1043    if (jcr->mig_jcr) {
1044       free_jcr(jcr->mig_jcr);
1045       jcr->mig_jcr = NULL;
1046    }
1047    Dmsg0(100, "Leave migrate_cleanup()\n");
1048 }
1049
1050 /* 
1051  * Return next DBId from comma separated list   
1052  *
1053  * Returns:
1054  *   1 if next DBId returned
1055  *   0 if no more DBIds are in list
1056  *  -1 there is an error
1057  */
1058 static int get_next_dbid_from_list(char **p, DBId_t *DBId)
1059 {
1060    char id[30];
1061    char *q = *p;
1062
1063    id[0] = 0;
1064    for (int i=0; i<(int)sizeof(id); i++) {
1065       if (*q == 0) {
1066          break;
1067       } else if (*q == ',') {
1068          q++;
1069          break;
1070       }
1071       id[i] = *q++;
1072       id[i+1] = 0;
1073    }
1074    if (id[0] == 0) {
1075       return 0;
1076    } else if (!is_a_number(id)) {
1077       return -1;                      /* error */
1078    }
1079    *p = q;
1080    *DBId = str_to_int64(id);
1081    return 1;
1082 }