Imported Upstream version 3.3.1
[debian/amanda] / server-src / planner.c
index 031e36b6fe0a0ae7a6353fd947560a84c7343b3a..c8501e2f35aced2ab93db61374600e0fd921c0ef 100644 (file)
@@ -30,6 +30,7 @@
  */
 #include "amanda.h"
 #include "arglist.h"
+#include "find.h"
 #include "conffile.h"
 #include "diskfile.h"
 #include "tapefile.h"
@@ -189,6 +190,13 @@ main(
     char *cfg_opt = NULL;
     int    planner_setuid;
     int exit_status = EXIT_SUCCESS;
+    gboolean no_taper = FALSE;
+    gboolean from_client = FALSE;
+
+    if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
+       printf("planner-%s\n", VERSION);
+       return (0);
+    }
 
     /*
      * Configure program for internationalization:
@@ -255,12 +263,22 @@ main(
        g_fprintf(stderr, _("%s: %s"), get_pname(), version_info[i]);
 
     diskarg_offset = 2;
-    if (argc > 3 && strcmp(argv[2], "--starttime") == 0) {
-       planner_timestamp = stralloc(argv[3]);
+    if (argc - diskarg_offset > 1 && strcmp(argv[diskarg_offset], "--starttime") == 0) {
+       planner_timestamp = stralloc(argv[diskarg_offset+1]);
        diskarg_offset += 2;
     }
+    if (argc - diskarg_offset > 0 && strcmp(argv[diskarg_offset], "--no-taper") == 0) {
+       no_taper = TRUE;
+       diskarg_offset += 1;
+    }
+    if (argc - diskarg_offset > 0 && strcmp(argv[diskarg_offset], "--from-client") == 0) {
+       from_client = TRUE;
+       diskarg_offset += 1;
+    }
 
 
+    run_server_global_scripts(EXECUTE_ON_PRE_ESTIMATE, get_config_name());
+
     /*
      * 1. Networking Setup
      *
@@ -309,7 +327,7 @@ main(
     conf_tapecycle = getconf_int(CNF_TAPECYCLE);
     conf_etimeout = (time_t)getconf_int(CNF_ETIMEOUT);
     conf_reserve  = getconf_int(CNF_RESERVE);
-    conf_autoflush = getconf_boolean(CNF_AUTOFLUSH);
+    conf_autoflush = getconf_no_yes_all(CNF_AUTOFLUSH);
     conf_usetimestamps = getconf_boolean(CNF_USETIMESTAMPS);
 
     today = time(0);
@@ -335,6 +353,19 @@ main(
        g_fprintf(stderr,"%s",errstr);
         exit_status = EXIT_FAILURE;
     }
+
+    for (dp = origq.head; dp != NULL; dp = dp->next) {
+       if (dp->todo) {
+           if (from_client) {
+               if (!dp->dump_limit || !dp->dump_limit->same_host)
+                   dp->todo = 0;
+           } else {
+               if (dp->dump_limit && !dp->dump_limit->server)
+                   dp->todo = 0;
+           }
+       }
+    }
+
     nb_disk = 0;
     for (dp = origq.head; dp != NULL; dp = dp->next) {
        if (dp->todo) {
@@ -409,12 +440,13 @@ main(
 
     g_fprintf(stderr,_("\nSENDING FLUSHES...\n"));
 
-    if(conf_autoflush) {
+    if(conf_autoflush && !no_taper) {
        dumpfile_t  file;
        GSList *holding_list, *holding_file;
        char *qdisk, *qhname;
 
-       /* get *all* flushable files in holding */
+       /* get *all* flushable files in holding, without checking against
+        * the disklist (which may not contain some of the dumps) */
        holding_list = holding_get_files_for_flush(NULL);
        for(holding_file=holding_list; holding_file != NULL;
                                       holding_file = holding_file->next) {
@@ -428,6 +460,13 @@ main(
                continue;
            }
 
+           /* see if this matches the command-line arguments */
+           if (conf_autoflush == 1 &&
+               !match_dumpfile(&file, argc-diskarg_offset,
+                                      argv+diskarg_offset)) {
+               continue;
+           }
+
            qdisk = quote_string(file.disk);
            qhname = quote_string((char *)holding_file->data);
            log_add(L_DISK, "%s %s", file.name, qdisk);
@@ -449,7 +488,7 @@ main(
            amfree(qhname);
            dumpfile_free_data(&file);
        }
-       g_slist_free_full(holding_list);
+       slist_free_full(holding_list, g_free);
        holding_list = NULL;
     }
     g_fprintf(stderr, _("ENDFLUSH\n"));
@@ -539,6 +578,8 @@ main(
     while(!empty(estq)) analyze_estimate(dequeue_disk(&estq));
     while(!empty(failq)) handle_failed(dequeue_disk(&failq));
 
+    run_server_global_scripts(EXECUTE_ON_POST_ESTIMATE, get_config_name());
+
     /*
      * At this point, all the disks are on schedq sorted by priority.
      * The total estimated size of the backups is in total_size.
@@ -759,8 +800,6 @@ setup_estimate(
            amfree(qname);
            return;
        } else if (dp->to_holdingdisk == HOLD_AUTO) {
-           log_add(L_INFO, _("Disabling holding disk for %s:%s."),
-                   dp->host->hostname, qname);
            g_fprintf(stderr,_("%s:%s Disabling holding disk\n"),
                      dp->host->hostname, qname);
            dp->to_holdingdisk = HOLD_NEVER;
@@ -796,21 +835,24 @@ setup_estimate(
             * hosed when that tape gets re-used next.  Disallow this for
             * now.
             */
-           log_add(L_ERROR,
+           log_add(L_WARNING,
                    _("Cannot force full dump of %s:%s with no-full option."),
                    dp->host->hostname, qname);
 
            /* clear force command */
            CLR(info.command, FORCE_FULL);
-           if(put_info(dp->host->hostname, dp->name, &info)) {
-               error(_("could not put info record for %s:%s: %s"),
-                     dp->host->hostname, qname, strerror(errno));
-               /*NOTREACHED*/
-           }
            ep->last_level = last_level(&info);
            ep->next_level0 = next_level0(dp, &info);
-       }
-       else {
+       } else if (dp->strategy == DS_INCRONLY) {
+           log_add(L_WARNING,
+                   _("Cannot force full dump of %s:%s with incronly option."),
+                   dp->host->hostname, qname);
+
+           /* clear force command */
+           CLR(info.command, FORCE_FULL);
+           ep->last_level = last_level(&info);
+           ep->next_level0 = next_level0(dp, &info);
+       } else {
            ep->degr_mesg = _("Skipping: force-full disk can't be dumped in degraded mode");
            ep->last_level = -1;
            ep->next_level0 = -conf_dumpcycle;
@@ -975,7 +1017,7 @@ setup_estimate(
         (!ISSET(info.command, FORCE_BUMP) ||
          dp->skip_incr ||
          ep->last_level == -1))) {
-       if(info.command & FORCE_BUMP && ep->last_level == -1) {
+       if(ISSET(info.command, FORCE_BUMP) && ep->last_level == -1) {
            log_add(L_INFO,
                  _("Remove force-bump command of %s:%s because it's a new disk."),
                    dp->host->hostname, qname);
@@ -984,12 +1026,16 @@ setup_estimate(
        case DS_STANDARD: 
        case DS_NOINC:
            askfor(ep, i++, 0, &info);
+           if (ep->last_level == -1)
+               ep->degr_mesg = _("Skipping: new disk can't be dumped in degraded mode");
+           else
+               ep->degr_mesg = _("Skipping: strategy NOINC can't be dumped in degraded mode");
            if(dp->skip_full) {
-               log_add(L_INFO, _("Ignoring skip_full for %s:%s "
+               log_add(L_INFO, _("Ignoring skip-full for %s:%s "
                        "because the strategy is NOINC."),
                        dp->host->hostname, qname);
            }
-           if(info.command & FORCE_BUMP) {
+           if(ISSET(info.command, FORCE_BUMP)) {
                log_add(L_INFO,
                 _("Ignoring FORCE_BUMP for %s:%s because the strategy is NOINC."),
                        dp->host->hostname, qname);
@@ -1168,7 +1214,7 @@ static gint64 est_tape_size(
     one_est_t *dump_est;
 
     dump_est = est_for_level(dp, level);
-    if (dump_est->csize <= -1)
+    if (dump_est->level >= 0 && dump_est->csize <= -1)
        est_csize(dp, dump_est);
     return dump_est->csize;
 }
@@ -1307,15 +1353,11 @@ static void get_estimates(void)
            hostp = dp->host;
            if(hostp->up == HOST_READY) {
                something_started = 1;
+               run_server_host_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE,
+                                       get_config_name(), hostp);
                for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
                    if (dp1->todo)
-                       run_server_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE,
-                                          get_config_name(), dp1,
-                                          est(dp1)->estimate[0].level);
-               }
-               for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
-                   if (dp1->todo)
-                       run_server_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE,
+                       run_server_dle_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE,
                                           get_config_name(), dp1,
                                           est(dp1)->estimate[0].level);
                }
@@ -1580,6 +1622,9 @@ static void getsize(
                    strappend(s, l);
                    s_len += strlen(l);
                    amfree(l);
+                   amfree(levelstr);
+                   amfree(spindlestr);
+                   amfree(o);
                } else if (strcmp(dp->program,"DUMP") != 0 &&
                           strcmp(dp->program,"GNUTAR") != 0) {
                    est(dp)->errstr = newvstrallocf(est(dp)->errstr,
@@ -1696,6 +1741,8 @@ static void getsize(
                    enqueue_disk(&failq, dp);
                }
            }
+           amfree(b64disk);
+           amfree(b64device);
            amfree(qname);
            amfree(qdevice);
        }
@@ -1724,6 +1771,7 @@ static void getsize(
        timeout = (time_t)getconf_int(CNF_CTIMEOUT);
     }
 
+    dbprintf(_("send request:\n----\n%s\n----\n\n"), req);
     secdrv = security_getdriver(hostp->disks->auth);
     if (secdrv == NULL) {
        hostp->up = HOST_DONE;
@@ -1789,8 +1837,12 @@ static void handle_result(
     hostp->up = HOST_READY;
 
     if (pkt == NULL) {
-       errbuf = vstrallocf(_("Request to %s failed: %s"),
+       if (strcmp(security_geterror(sech), "timeout waiting for REP") == 0) {
+           errbuf = vstrallocf("Some estimate timeout on %s, using server estimate if possible", hostp->hostname);
+       } else {
+           errbuf = vstrallocf(_("Request to %s failed: %s"),
                        hostp->hostname, security_geterror(sech));
+       }
        goto error_return;
     }
     if (pkt->type == P_NAK) {
@@ -1815,6 +1867,7 @@ static void handle_result(
        }
     }
 
+    dbprintf(_("got reply:\n----\n%s\n----\n\n"), pkt->body);
     s = pkt->body;
     ch = *s++;
     while(ch) {
@@ -1931,7 +1984,8 @@ static void handle_result(
                break;
            }
        }
-       if (i == MAX_LEVELS) {
+       if (i == MAX_LEVELS && level > 0) {
+                       /* client always report level 0 for some error */
            goto bad_msg;               /* this est wasn't requested */
        }
        est(dp)->got_estimate++;
@@ -2043,7 +2097,7 @@ static void handle_result(
                est(dp)->estimate[1].nsize > (gint64)0) &&
              (est(dp)->estimate[2].level == -1 ||
                est(dp)->estimate[2].nsize > (gint64)0)))) {
-           run_server_scripts(EXECUTE_ON_POST_DLE_ESTIMATE,
+           run_server_dle_scripts(EXECUTE_ON_POST_DLE_ESTIMATE,
                               get_config_name(), dp,
                                est(dp)->estimate[0].level);
            est(dp)->post_dle = 1;
@@ -2052,13 +2106,9 @@ static void handle_result(
     }
 
     if(hostp->up == HOST_DONE) {
-       for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
-           if (dp->todo)
-               if (pkt->type == P_REP) {
-                   run_server_scripts(EXECUTE_ON_POST_HOST_ESTIMATE,
-                                      get_config_name(), dp,
-                                       est(dp)->estimate[0].level);
-           }
+       if (pkt->type == P_REP) {
+           run_server_host_scripts(EXECUTE_ON_POST_HOST_ESTIMATE,
+                                   get_config_name(), hostp);
        }
     }
 
@@ -2148,7 +2198,7 @@ static void analyze_estimate(
     ep->degr_est = &default_one_est;
 
     if (ep->next_level0 <= 0 || (have_info && ep->last_level == 0
-       && (info.command & FORCE_NO_BUMP))) {
+       && (ISSET(info.command, FORCE_NO_BUMP)))) {
        if (ep->next_level0 <= 0) {
            g_fprintf(stderr,_("(due for level 0) "));
        }
@@ -2208,6 +2258,24 @@ static void analyze_estimate(
        }
     }
 
+    if (ep->dump_est->level < 0) {
+       int   i;
+       char *q = quote_string("no estimate");
+
+       g_fprintf(stderr,_("  no valid estimate\n"));
+       for(i=0; i<MAX_LEVELS; i++) {
+           if (est(dp)->estimate[i].level >= 0) {
+               g_fprintf(stderr,("    level: %d  nsize: %lld csize: %lld\n"),
+                         est(dp)->estimate[i].level,
+                         (long long)est(dp)->estimate[i].nsize,
+                         (long long)est(dp)->estimate[i].csize);
+           }
+       }
+       log_add(L_WARNING, _("%s %s %s 0 %s"), dp->host->hostname, qname,
+               planner_timestamp, q);
+       amfree(q);
+    }
+
     g_fprintf(stderr,_("  curr level %d nsize %lld csize %lld "),
               ep->dump_est->level, (long long)ep->dump_est->nsize,
               (long long)ep->dump_est->csize);
@@ -2224,7 +2292,15 @@ static void analyze_estimate(
        lev0size = est_tape_size(dp, 0);
        if(lev0size == (gint64)-1) lev0size = ep->last_lev0size;
 
-       balanced_size += (double)(lev0size / (gint64)runs_per_cycle);
+       if (dp->strategy == DS_NOINC) {
+           balanced_size += (double)lev0size;
+       } else if (dp->dumpcycle == 0) {
+           balanced_size += (double)(lev0size * conf_dumpcycle / (gint64)runs_per_cycle);
+       } else if (dp->dumpcycle != conf_dumpcycle) {
+           balanced_size += (double)(lev0size * (conf_dumpcycle / dp->dumpcycle) / (gint64)runs_per_cycle);
+       } else {
+           balanced_size += (double)(lev0size / (gint64)runs_per_cycle);
+       }
     }
 
     g_fprintf(stderr,_("total size %lld total_lev0 %1.0lf balanced-lev0size %1.0lf\n"),
@@ -2319,9 +2395,11 @@ static one_est_t *pick_inclevel(
 
     /* if we didn't get an estimate, we can't do an inc */
     if (base_est->nsize == (gint64)-1) {
-       bump_est = est_for_level(dp, base_est->level + 1);
-       if (bump_est->nsize > (gint64)0) /* FORCE_BUMP */
+       bump_est = est_for_level(dp, est(dp)->last_level + 1);
+       if (bump_est->nsize > (gint64)0) { /* FORCE_BUMP */
+           g_fprintf(stderr,_("   picklev: bumping to level %d\n"), bump_est->level);
            return bump_est;
+       }
        g_fprintf(stderr,_("   picklev: no estimate for level %d, so no incs\n"), base_est->level);
        return base_est;
     }
@@ -2423,7 +2501,7 @@ static void delay_dumps(void)
 
     for(dp = schedq.head; dp != NULL; dp = ndp) {
        int avail_tapes = 1;
-       if (dp->tape_splitsize > (gint64)0)
+       if (dp->splitsize > (gint64)0 || dp->allow_split)
            avail_tapes = conf_runtapes;
 
        ndp = dp->next; /* remove_disk zaps this */
@@ -2431,7 +2509,7 @@ static void delay_dumps(void)
        full_size = est_tape_size(dp, 0);
        if (full_size > tapetype_get_length(tape) * (gint64)avail_tapes) {
            char *qname = quote_string(dp->name);
-           if (conf_runtapes > 1 && dp->tape_splitsize == (gint64)0) {
+           if (conf_runtapes > 1 && dp->splitsize == (gint64)0) {
                log_add(L_WARNING, _("disk %s:%s, full dump (%lldKB) will be larger than available tape space"
                        ", you could define a splitsize"),
                        dp->host->hostname, qname,
@@ -2511,7 +2589,7 @@ static void delay_dumps(void)
        if(est(dp)->dump_est->level != 0) continue;
 
        get_info(dp->host->hostname, dp->name, &info);
-       if(info.command & FORCE_FULL) {
+       if(ISSET(info.command, FORCE_FULL)) {
            nb_forced_level_0 += 1;
            preserve = dp;
            continue;
@@ -2619,7 +2697,7 @@ static void delay_dumps(void)
        int avail_tapes = 1;
        nbi = bi->prev;
        dp = bi->dp;
-       if(dp->tape_splitsize > (gint64)0)
+       if(dp->splitsize > (gint64)0)
            avail_tapes = conf_runtapes;
 
        if(bi->deleted) {
@@ -2911,7 +2989,8 @@ static int promote_hills(void)
     }
 
     for(dp = schedq.head; dp != NULL; dp = dp->next) {
-       days = est(dp)->next_level0;   /* This is > 0 by definition */
+       days = est(dp)->next_level0;
+       if (days < 0) days = 0;
        if(days<my_dumpcycle && !dp->skip_full && dp->strategy != DS_NOFULL &&
           dp->strategy != DS_INCRONLY) {
            sp[days].disks++;