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