]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
Introduce Storage Manager and Storage Groups feature.
authorMichal Rakowski <michal.rakowski@baculasystems.com>
Mon, 29 Mar 2021 20:33:59 +0000 (22:33 +0200)
committerEric Bollengier <eric@baculasystems.com>
Thu, 24 Mar 2022 08:03:00 +0000 (09:03 +0100)
Description:
------------
1. All of the director's storage-related fields were moved from the
   jcr struct into the new Storage Manager class.
   From now one all of the storage operations (get, set, ...) should be performed
   using manager.
2. Introduced Storage Groups along with different policies (for now only
   the default one and LeastUsed). Storage now can be defined as a list
   inside the Job or Pool resource, along with its policy.

Big part of this commit is just an refactoring (moving fields from jcr
to the class members, adding some helper methods).

34 files changed:
bacula/src/dird/Makefile.in
bacula/src/dird/admin.c
bacula/src/dird/backup.c
bacula/src/dird/catreq.c
bacula/src/dird/dir_plugins.c
bacula/src/dird/dird.c
bacula/src/dird/dird.h
bacula/src/dird/dird_conf.c
bacula/src/dird/dird_conf.h
bacula/src/dird/expand.c
bacula/src/dird/fd_cmds.c
bacula/src/dird/job.c
bacula/src/dird/jobq.c
bacula/src/dird/mac.c
bacula/src/dird/msgchan.c
bacula/src/dird/newvol.c
bacula/src/dird/next_vol.c
bacula/src/dird/protos.h
bacula/src/dird/restore.c
bacula/src/dird/store_mngr.c [new file with mode: 0644]
bacula/src/dird/store_mngr.h [new file with mode: 0644]
bacula/src/dird/ua_cmds.c
bacula/src/dird/ua_collect.c
bacula/src/dird/ua_dotcmds.c
bacula/src/dird/ua_label.c
bacula/src/dird/ua_purge.c
bacula/src/dird/ua_restore.c
bacula/src/dird/ua_run.c
bacula/src/dird/ua_select.c
bacula/src/dird/ua_status.c
bacula/src/dird/vbackup.c
bacula/src/dird/verify.c
bacula/src/jcr.h
bacula/src/stored/reserve.c

index db40602729e1fea806c5a88ca59867f6bf29936a..929b1f5336f0ff2dbd6e56d331a6700aaca0b3f1 100644 (file)
@@ -40,7 +40,7 @@ SVRSRCS = dird.c admin.c authenticate.c \
          jobq.c mac.c mac_sql.c \
          mountreq.c msgchan.c next_vol.c newvol.c \
          recycle.c restore.c run_conf.c \
-         scheduler.c \
+         scheduler.c store_mngr.c \
          ua_acl.c ua_cmds.c ua_dotcmds.c \
          ua_query.c ua_collect.c \
          ua_input.c ua_label.c ua_output.c ua_prune.c \
index 08f296cff073b2180dbcc0637bdb56adac0d155a..141025daae756dcbbeff825d9a3a3cd01b5d3f45 100644 (file)
@@ -34,7 +34,7 @@
 
 bool do_admin_init(JCR *jcr)
 {
-   free_rstorage(jcr);
+   jcr->store_mngr->reset_rstorage();
    if (!allow_duplicate_job(jcr)) {
       return false;
    }
index 6849dd40a403b66ad3bba4c4f072a7efbddce047..8a9b7fed44f6e4caa11d23339dff7f30dc72ed91 100644 (file)
@@ -107,12 +107,12 @@ bool do_backup_init(JCR *jcr)
      return do_vbackup_init(jcr);
    }
 
-   free_rstorage(jcr);                   /* we don't read so release */
+   jcr->store_mngr->reset_rstorage();
 
    /* If pool storage specified, use it instead of job storage */
-   copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
+   jcr->store_mngr->set_wstorage(jcr->pool->storage, _("Pool resource"));
 
-   if (!jcr->wstorage) {
+   if (!jcr->store_mngr->get_wstore()) {
       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
       return false;
    }
@@ -452,7 +452,8 @@ bool run_storage_and_start_message_thread(JCR *jcr, BSOCK *sd)
  */
 bool do_backup(JCR *jcr)
 {
-   int stat;
+   bool sd_job_started = false, wstore_group = false;
+   int stat, iter_no = 1;
    BSOCK   *fd, *sd;
    STORE *store;
    char *store_address;
@@ -533,18 +534,89 @@ bool do_backup(JCR *jcr)
     */
    Dmsg0(110, "Open connection with storage daemon\n");
    jcr->setJobStatus(JS_WaitSD);
-   /*
-    * Start conversation with Storage daemon
-    */
-   if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
-      return false;
+
+   if (jcr->store_mngr->get_wstore_list()->size() != 1) {
+      wstore_group = true;
    }
-   /*
-    * Now start a job with the Storage daemon
+
+   if (wstore_group) {
+      /* Apply policy for the write storage list */
+      jcr->store_mngr->apply_policy(true);
+      Jmsg(jcr, M_INFO, 0, _("Possible storage choices: %s\n"),
+            jcr->store_mngr->print_wlist());
+      iter_no = 2;
+   }
+
+   /* We are doing two iterations here:
+    *    - First one with 'wait' set to false in start_storage_dameon_job() call
+    *      to check if we can use any device from the list without waiting at all
+    *    - Second iteration is performed when no device was avalable - this time we wait for a device to
+    *      become available (hence setting wait to true in start_storage_daemon())
+    * Each iteration traverses storage list and try to reserve each. If any of the storages is ok to use,
+    * we stop the loops.
     */
-   if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
-      return false;
+   for (int iter=0; iter<iter_no; iter++) {
+      if (wstore_group) {
+         if (iter == 0) {
+            Jmsg(jcr, M_INFO, 0, _("Trying to start job on any storage from the list without "
+                     "waiting for busy devices first.\n"));
+         } else {
+            Jmsg(jcr, M_INFO, 0, _("All devices are currently busy, need to wait at each try of storage reservation.\n"));
+         }
+      }
+
+      foreach_alist(store, jcr->store_mngr->get_wstore_list()) {
+         jcr->store_mngr->set_current_wstorage(store);
+
+         if (jcr->store_bsock) {
+            jcr->store_bsock->close();
+         }
+
+         /*
+          * Start conversation with Storage daemon
+          */
+         if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
+            Jmsg(jcr, M_INFO, 0, _("Failed connect to the storage: %s\n"),
+                  jcr->store_mngr->get_wstore()->name());
+            continue;
+         } else {
+            Jmsg(jcr, M_INFO, 0, _("Connected to the storage: %s\n"),
+                  jcr->store_mngr->get_wstore()->name());
+         }
+
+         alist wlist;
+         wlist.init(10, not_owned_by_alist);
+         wlist.append(store);
+
+         /*
+          * Now start a job with the Storage daemon with temporary, single-item list.
+          * We wait on the storage if there is only 1 storage defined,
+          * else 'iter' is used as a bool in start_storage_daemon_job(),
+          * because we have only 2 iterations (so 'iter' variable is either 0 or 1)
+          */
+         if (!start_storage_daemon_job(jcr, NULL, &wlist, wstore_group ? (bool)iter : true)) {
+            Jmsg(jcr, M_INFO, 0, _("Failed to start job on the storage: %s\n"),
+                  jcr->store_mngr->get_wstore()->name());
+            continue;
+         } else {
+            Jmsg(jcr, M_INFO, 0, _("Started job on storage: %s\n"),
+                  jcr->store_mngr->get_wstore()->name());
+            sd_job_started = true;
+            break;
+         }
+      }
+
+      if(sd_job_started) {
+         /* Now break from the outer loop as well */
+         break;
+      }
    }
+
+   if(!sd_job_started) {
+      Jmsg(jcr, M_FATAL, 0, _("Failed to start job on any of the storages defined!\n"));
+      goto bail_out;
+   }
+
    sd = jcr->store_bsock;
    if (jcr->client) {
       jcr->sd_calls_client = jcr->client->sd_calls_client;
@@ -591,7 +663,7 @@ bool do_backup(JCR *jcr)
 
    send_snapshot_retention(jcr, jcr->snapshot_retention);
 
-   store = jcr->wstore;
+   store = jcr->store_mngr->get_wstore();
 
    if (jcr->sd_calls_client) {
       if (jcr->FDVersion < 10) {
@@ -606,7 +678,7 @@ bool do_backup(JCR *jcr)
          goto bail_out;
       }
 
-      store_address = jcr->wstore->address;  /* dummy */
+      store_address = store->address;  /* dummy */
       store_port = 0;           /* flag that SD calls FD */
    } else {
       /*
@@ -903,6 +975,7 @@ void backup_cleanup(JCR *jcr, int TermCode)
    utime_t RunTime;
    POOL_MEM base_info;
    POOL_MEM vol_info;
+   STORE *wstore = jcr->store_mngr->get_wstore();
 
    remove_dummy_jobmedia_records(jcr);
 
@@ -1104,7 +1177,7 @@ void backup_cleanup(JCR *jcr, int TermCode)
         jcr->fileset->name(), jcr->FSCreateTime,
         jcr->pool->name(), jcr->pool_source,
         jcr->catalog->name(), jcr->catalog_source,
-        jcr->wstore->name(), jcr->wstore_source,
+        wstore->name(), jcr->store_mngr->get_wsource(),
         schedt,
         sdt,
         edt,
index ff64d5bc0bc54330eff16c2cf4119dc45a8f2090..da11056b3d411accd7d97dff8f6ae4a9bf221328 100644 (file)
@@ -139,6 +139,7 @@ void catalog_request(JCR *jcr, BSOCK *bs)
    int can_create=0;
    int Enabled, Recycle;
    JobId_t JobId = 0;
+   STORE *wstore = jcr->store_mngr->get_wstore();
 
    bmemset(&sdmr, 0, sizeof(sdmr));
    bmemset(&jm, 0, sizeof(jm));
@@ -169,7 +170,7 @@ void catalog_request(JCR *jcr, BSOCK *bs)
       ok = db_get_pool_record(jcr, jcr->db, &pr);
       if (ok) {
          mr.PoolId = pr.PoolId;
-         set_storageid_in_mr(jcr->wstore, &mr);
+         set_storageid_in_mr(wstore, &mr);
          mr.ScratchPoolId = pr.ScratchPoolId;
          ok = find_next_volume_for_append(jcr, &mr, index,
                                           can_create?fnv_create_vol : fnv_no_create_vol,
@@ -219,7 +220,7 @@ void catalog_request(JCR *jcr, BSOCK *bs)
              */
             if (mr.PoolId != jcr->jr.PoolId) {
                reason = _("not in Pool");
-            } else if (strcmp(mr.MediaType, jcr->wstore->media_type) != 0) {
+            } else if (strcmp(mr.MediaType, jcr->store_mngr->get_wmedia_type()) != 0) {
                reason = _("not correct MediaType");
             } else {
                /*
@@ -322,11 +323,11 @@ void catalog_request(JCR *jcr, BSOCK *bs)
        *   However, do so only if we are writing the tape, i.e.
        *   the number of VolWrites has increased.
        */
-      if (jcr->wstore && sdmr.VolWrites > mr.VolWrites) {
+      if (wstore && sdmr.VolWrites > mr.VolWrites) {
          Dmsg2(050, "Update StorageId old=%d new=%d\n",
-               mr.StorageId, jcr->wstore->StorageId);
+               mr.StorageId, wstore->StorageId);
          /* Update StorageId after write */
-         set_storageid_in_mr(jcr->wstore, &mr);
+         set_storageid_in_mr(wstore, &mr);
       } else {
          /* Nothing written, reset same StorageId */
          set_storageid_in_mr(NULL, &mr);
index 5fee8e76943a79b3beb456812400cc62a37aa941..65b120003d19af27a736d7c53f83fe880908e210 100644 (file)
@@ -310,6 +310,9 @@ static bRC baculaGetValue(bpContext *ctx, brDirVariable var, void *value)
    if (!value) {
       return bRC_Error;
    }
+
+   STORE *rstore = jcr->store_mngr->get_rstore();
+   STORE *wstore = jcr->store_mngr->get_wstore();
    switch (var) {
    case bDirVarJobId:
       *((int *)value) = jcr->JobId;
@@ -348,10 +351,10 @@ static bRC baculaGetValue(bpContext *ctx, brDirVariable var, void *value)
       Dmsg1(dbglvl, "Bacula: return bDirVarPool=%s\n", jcr->pool->hdr.name);
       break;
    case bDirVarStorage:
-      if (jcr->wstore) {
-         *((char **)value) = jcr->wstore->hdr.name;
-      } else if (jcr->rstore) {
-         *((char **)value) = jcr->rstore->hdr.name;
+      if (wstore) {
+         *((char **)value) = wstore->hdr.name;
+      } else if (rstore) {
+         *((char **)value) = rstore->hdr.name;
       } else {
          *((char **)value) = NULL;
          ret=bRC_Error;
@@ -359,8 +362,8 @@ static bRC baculaGetValue(bpContext *ctx, brDirVariable var, void *value)
       Dmsg1(dbglvl, "Bacula: return bDirVarStorage=%s\n", NPRT(*((char **)value)));
       break;
    case bDirVarWriteStorage:
-      if (jcr->wstore) {
-         *((char **)value) = jcr->wstore->hdr.name;
+      if (wstore) {
+         *((char **)value) = wstore->hdr.name;
       } else {
          *((char **)value) = NULL;
          ret=bRC_Error;
@@ -368,8 +371,8 @@ static bRC baculaGetValue(bpContext *ctx, brDirVariable var, void *value)
       Dmsg1(dbglvl, "Bacula: return bDirVarWriteStorage=%s\n", NPRT(*((char **)value)));
       break;
    case bDirVarReadStorage:
-      if (jcr->rstore) {
-         *((char **)value) = jcr->rstore->hdr.name;
+      if (rstore) {
+         *((char **)value) = rstore->hdr.name;
       } else {
          *((char **)value) = NULL;
          ret=bRC_Error;
@@ -381,10 +384,10 @@ static bRC baculaGetValue(bpContext *ctx, brDirVariable var, void *value)
       Dmsg1(dbglvl, "Bacula: return bDirVarCatalog=%s\n", jcr->catalog->hdr.name);
       break;
    case bDirVarMediaType:
-      if (jcr->wstore) {
-         *((char **)value) = jcr->wstore->media_type;
-      } else if (jcr->rstore) {
-         *((char **)value) = jcr->rstore->media_type;
+      if (wstore) {
+         *((char **)value) = wstore->media_type;
+      } else if (rstore) {
+         *((char **)value) = rstore->media_type;
       } else {
          *((char **)value) = NULL;
          ret=bRC_Error;
index 02a9933c43afd97464a0ff18180093937922e0cc..fa43b7236f11b8131363b1143d6acfb2355070cc 100644 (file)
@@ -203,7 +203,7 @@ static void usage()
 static void dir_debug_print(JCR *jcr, FILE *fp)
 {
    fprintf(fp, "\twstore=%p rstore=%p wjcr=%p client=%p reschedule_count=%d SD_msg_chan_started=%d\n",
-           jcr->wstore, jcr->rstore, jcr->wjcr, jcr->client, jcr->reschedule_count, (int)jcr->SD_msg_chan_started);
+           jcr->store_mngr->get_wstore(), jcr->store_mngr->get_rstore(), jcr->wjcr, jcr->client, jcr->reschedule_count, (int)jcr->SD_msg_chan_started);
 }
 
 /*********************************************************************
index 3bd20d8f204e0cbd9a5c7de4931073a25b5e0c78..fad0fbf6043c2025656de8e544a8fe86628cc9a9 100644 (file)
@@ -26,6 +26,7 @@
 #include "lib/runscript.h"
 #include "lib/breg.h"
 #include "dird_conf.h"
+#include "store_mngr.h"
 
 #define DIRECTOR_DAEMON 1
 
index aa002ac7785930cde96d2d394479401827c0a0ea..0f0f8d9e7a14f1da64cdddebc737ef925029faac 100644 (file)
@@ -134,6 +134,28 @@ BSOCK *CLIENT::getBSOCK(int timeout)
    return globals->socket->get(timeout);
 }
 
+/* Store a storage group policy */
+void store_storage_mngr(LEX *lc, RES_ITEM *item, int index, int pass)
+{
+   lex_get_token(lc, T_STRING);
+   if (pass == 1) {
+      if (*(item->value)) {
+         scan_err5(lc, _("Attempt to redefine \"%s\" from \"%s\" to \"%s\" referenced on line %d : %s\n"),
+            item->name, *(item->value), lc->str, lc->line_no, lc->line);
+         return;
+      }
+
+      if (!StorageManager::check_policy(lc->str)) {
+         scan_err0(lc, _("Invalid storage policy!\n"));
+         return;
+      }
+
+      *(item->value) = bstrdup(lc->str);
+   }
+   scan_to_eol(lc);
+   set_bit(index, res_all.hdr.item_present);
+}
+
 bool CLIENT::getBSOCK_state(POOLMEM *&buf)
 {
    P(globals_mutex);
@@ -580,6 +602,7 @@ RES_ITEM job_items[] = {
    {"Level",     store_level,   ITEM(res_job.JobLevel),    0, 0, 0},
    {"Messages",  store_res,     ITEM(res_job.messages),  R_MSGS, ITEM_REQUIRED, 0},
    {"Storage",   store_alist_res, ITEM(res_job.storage),  R_STORAGE, 0, 0},
+   {"StoragePolicy",   store_storage_mngr, ITEM(res_job.storage_policy),  0, 0, 0},
    {"Pool",      store_res,     ITEM(res_job.pool),      R_POOL, ITEM_REQUIRED, 0},
    {"NextPool",  store_res,     ITEM(res_job.next_pool), R_POOL, 0, 0},
    {"FullBackupPool",  store_res, ITEM(res_job.full_pool),   R_POOL, 0, 0},
@@ -1859,6 +1882,9 @@ void free_resource(RES *rres, int type)
       if (res->res_job.run_cmds) {
          delete res->res_job.run_cmds;
       }
+      if (res->res_job.storage_policy) {
+         free(res->res_job.storage_policy);
+      }
       if (res->res_job.storage) {
          delete res->res_job.storage;
       }
@@ -2765,10 +2791,13 @@ extern "C" char *job_code_callback_director(JCR *jcr, const char* param, char *b
          }
          break;
       case 'w':
-         if (jcr->wstore) {
-            return jcr->wstore->name();
+         {
+            STORE *wstore = jcr->store_mngr->get_wstore();
+            if (wstore) {
+               return wstore->name();
+            }
+            break;
          }
-         break;
       case 'x':
          return jcr->spool_data ? yes : no;
       case 'D':
index dbb4e11801cb08df8c8a44ad6d4299e4183905cc..7f54ea4604e2e32498b1f5fa233c1607a7b77d31 100644 (file)
@@ -395,8 +395,10 @@ public:
    POOLMEM *store_source;
 
    /* Methods */
-   USTORE() { store = NULL; store_source = get_pool_memory(PM_MESSAGE);
-              *store_source = 0; };
+   USTORE() { store = NULL;
+              store_source = get_pool_memory(PM_MESSAGE);
+              pm_strcpy(store_source, _("Unknown source"));
+   }
    ~USTORE() { destroy(); }
    void set_source(const char *where);
    void destroy();
@@ -480,6 +482,7 @@ public:
    CLIENT    *client;                 /* Who to backup */
    FILESET   *fileset;                /* What to backup -- Fileset */
    alist     *storage;                /* Where is device -- list of Storage to be used */
+   char      *storage_policy;         /* Storage policy (e.g. round robin, least used...) */
    POOL      *pool;                   /* Where is media -- Media Pool */
    POOL      *next_pool;              /* Next Pool for Copy/Migrate/VirtualFull */
    POOL      *full_pool;              /* Pool for Full backups */
@@ -780,6 +783,9 @@ public:
 #define GetFileSetResWithName(x) ((FILESET *)GetResWithName(R_FILESET, (x)))
 #define GetCatalogResWithName(x) ((CAT *)GetResWithName(R_CATALOG, (x)))
 
+/* Director daemon's specific */
+void store_storage_mngr(LEX *lc, RES_ITEM *item, int index, int pass);
+
 /* Imported subroutines */
 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass);
 void store_level(LEX *lc, RES_ITEM *item, int index, int pass);
index fff10f9e06355000d14d0938f27236b7a74cc05a..f43b9d86553955548a9b0284b2f92e6e690be974 100644 (file)
@@ -74,6 +74,8 @@ static int job_item(JCR *jcr, int code,
    const char *str = " ";
    char buf[20];
 
+   STORE *rstore = jcr->store_mngr->get_rstore();
+   STORE *wstore = jcr->store_mngr->get_wstore();
    switch (code) {
    case 1:                            /* Job */
       str = jcr->job->name();
@@ -105,20 +107,20 @@ static int job_item(JCR *jcr, int code,
       str = jcr->pool->name();
       break;
    case 9:                            /* Storage */
-      if (jcr->wstore) {
-         str = jcr->wstore->name();
+      if (wstore) {
+         str = wstore->name();
       } else {
-         str = jcr->rstore->name();
+         str = rstore->name();
       }
       break;
    case 10:                           /* Catalog */
       str = jcr->catalog->name();
       break;
    case 11:                           /* MediaType */
-      if (jcr->wstore) {
-         str = jcr->wstore->media_type;
+      if (wstore) {
+         str = wstore->media_type;
       } else {
-         str = jcr->rstore->media_type;
+         str = rstore->media_type;
       }
       break;
    case 12:                           /* JobName */
index b16c5c87ac79e718a3661333f66754355dd559af..2ecbbb65fe6077167aaf455273689b83b0729a30 100644 (file)
@@ -455,7 +455,7 @@ static bool send_fileset(JCR *jcr)
 {
    FILESET *fileset = jcr->fileset;
    BSOCK   *fd = jcr->file_bsock;
-   STORE   *store = jcr->wstore;
+   STORE   *store = jcr->store_mngr->get_wstore();
    int num;
    bool include = true;
 
index 0e9519a39d3f6ac9554d8a09058c064437a7b8e8..2f128d2e9ef50026b5dc7595f4c9e1eb86502938 100644 (file)
@@ -34,6 +34,7 @@ static void job_monitor_destructor(watchdog_t *self);
 static bool job_check_maxwaittime(JCR *jcr);
 static bool job_check_maxruntime(JCR *jcr);
 static bool job_check_maxrunschedtime(JCR *jcr);
+static void set_jcr_default_store(JCR *jcr, JOB *job);
 
 /* Imported subroutines and variables */
 extern void term_scheduler();
@@ -93,6 +94,7 @@ JobId_t run_job(JCR *jcr)
 bool setup_job(JCR *jcr)
 {
    int errstat;
+   alist *rlist;
 
    jcr->lock();
    Dsm_check(100);
@@ -197,15 +199,12 @@ bool setup_job(JCR *jcr)
       goto bail_out;
    }
 
-   if (jcr->JobReads() && !jcr->rstorage) {
-      if (jcr->job->storage) {
-         copy_rwstorage(jcr, jcr->job->storage, _("Job resource"));
-      } else {
-         copy_rwstorage(jcr, jcr->job->pool->storage, _("Pool resource"));
-      }
+   rlist = jcr->store_mngr->get_rstore_list();
+   if (jcr->JobReads() && rlist->empty()) {
+      set_jcr_default_store(jcr, jcr->job);
    }
    if (!jcr->JobReads()) {
-      free_rstorage(jcr);
+      jcr->store_mngr->reset_rstorage();
    }
 
    /*
@@ -574,23 +573,24 @@ static bool cancel_file_daemon_job(UAContext *ua, const char *cmd, JCR *jcr)
 static bool cancel_sd_job(UAContext *ua, const char *cmd, JCR *jcr)
 {
    if (jcr->store_bsock) {
-      if (jcr->rstorage) {
-         copy_wstorage(ua->jcr, jcr->rstorage, _("Job resource"));
+      if (jcr->store_mngr->get_rstore()) {
+         ua->jcr->store_mngr->set_wstorage(jcr->store_mngr->get_rstore_list(), _("Job resource"));
       } else {
-         copy_wstorage(ua->jcr, jcr->wstorage, _("Job resource"));
+         ua->jcr->store_mngr->set_wstorage(jcr->store_mngr->get_wstore_list(), _("Job resource"));
       }
    } else {
-      USTORE store;
-      if (jcr->rstorage) {
-         store.store = jcr->rstore;
+      STORE *store;
+      if (jcr->store_mngr->get_rstore()) {
+         store = jcr->store_mngr->get_rstore();
       } else {
-         store.store = jcr->wstore;
+         store = jcr->store_mngr->get_wstore();
       }
-      set_wstorage(ua->jcr, &store);
+      ua->jcr->store_mngr->set_wstorage(store, "Job resource");
    }
 
-   if (!ua->jcr->wstore) {
-      ua->error_msg(_("Failed to select Storage daemon.\n"));
+   STORE *wstore = ua->jcr->store_mngr->get_wstore();
+   if (!wstore) {
+      ua->error_msg(_("Failed to select Storage daemon for jobid: %d.\n"), jcr->JobId);
       return false;
    }
 
@@ -600,7 +600,7 @@ static bool cancel_sd_job(UAContext *ua, const char *cmd, JCR *jcr)
    }
 
    Dmsg3(10, "Connected to storage daemon %s for cancel ua.jcr=%p jcr=%p\n",
-         ua->jcr->wstore->name(), ua->jcr, jcr);
+         wstore->name(), ua->jcr, jcr);
 
    BSOCK *sd = ua->jcr->store_bsock;
    sd->fsend("%s Job=%s\n", cmd, jcr->Job);
@@ -678,12 +678,11 @@ int cancel_inactive_job(UAContext *ua)
    /* At this time, we can't really guess the storage name from
     * the job record
     */
-   store.store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
-   if (!store.store) {
+   if (get_storage_resource(ua, &store, false/*no default*/, true/*unique*/)) {
       goto bail_out;
    }
 
-   set_wstorage(jcr, &store);
+   jcr->store_mngr->set_wstorage(store.store, store.store_source);
    cancel_sd_job(ua, "cancel", jcr);
 
 bail_out:
@@ -828,20 +827,22 @@ void cancel_storage_daemon_job(JCR *jcr)
 
    ua->jcr = control_jcr;
    if (jcr->store_bsock) {
-      if (!ua->jcr->wstorage) {
-         if (jcr->rstorage) {
-            copy_wstorage(ua->jcr, jcr->rstorage, _("Job resource"));
+      alist *wlist = ua->jcr->store_mngr->get_wstore_list();
+      if (wlist->empty()) {
+         alist *rlist = jcr->store_mngr->get_rstore_list();
+         if (!rlist->empty()) {
+            ua->jcr->store_mngr->set_wstorage(jcr->store_mngr->get_rstore_list(), _("Job resource"));
          } else {
-            copy_wstorage(ua->jcr, jcr->wstorage, _("Job resource"));
+            ua->jcr->store_mngr->set_wstorage(jcr->store_mngr->get_wstore_list(), _("Job resource"));
          }
       } else {
-         USTORE store;
-         if (jcr->rstorage) {
-            store.store = jcr->rstore;
+         STORE *store;
+         if (jcr->store_mngr->get_rstore()) {
+            store = jcr->store_mngr->get_rstore();
          } else {
-            store.store = jcr->wstore;
+            store = jcr->store_mngr->get_wstore();
          }
-         set_wstorage(ua->jcr, &store);
+         ua->jcr->store_mngr->set_wstorage(store, "Job cancellation");
       }
 
       if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
@@ -1539,16 +1540,13 @@ void dird_free_jcr(JCR *jcr)
    free_and_null_pool_memory(jcr->next_pool_source);
    free_and_null_pool_memory(jcr->catalog_source);
    free_and_null_pool_memory(jcr->rpool_source);
-   free_and_null_pool_memory(jcr->wstore_source);
-   free_and_null_pool_memory(jcr->rstore_source);
    free_and_null_pool_memory(jcr->next_vol_list);
    free_and_null_pool_memory(jcr->component_fname);
 
-   /* Delete lists setup to hold storage pointers */
-   free_rwstorage(jcr);
-
    jcr->job_end_push.destroy();
 
+   delete jcr->store_mngr;
+
    if (jcr->JobId != 0)
       write_state_file(director->working_directory, "bacula-dir", get_first_port_host_order(director->DIRaddrs));
 
@@ -1565,9 +1563,7 @@ void dird_free_jcr(JCR *jcr)
 }
 
 /*
- * The Job storage definition must be either in the Job record
- *  or in the Pool record.  The Pool record overrides the Job
- *  record.
+ * Override storage from run parameters if set.
  */
 void get_job_storage(USTORE *store, JOB *job, RUN *run)
 {
@@ -1581,12 +1577,13 @@ void get_job_storage(USTORE *store, JOB *job, RUN *run)
       pm_strcpy(store->store_source, _("Run storage override"));
       return;
    }
-   if (job->pool->storage) {
-      store->store = (STORE *)job->pool->storage->first();
-      pm_strcpy(store->store_source, _("Pool resource"));
+}
+
+static void set_jcr_default_store(JCR *jcr, JOB *job) {
+   if (job->storage) {
+      copy_rwstorage(jcr, job->storage, _("Job resource"));
    } else {
-      store->store = (STORE *)job->storage->first();
-      pm_strcpy(store->store_source, _("Job resource"));
+      copy_rwstorage(jcr, job->pool->storage, _("Pool resource"));
    }
 }
 
@@ -1628,12 +1625,17 @@ void set_jcr_defaults(JCR *jcr, JOB *job)
    }
 
    jcr->JobPriority = job->Priority;
-   /* Copy storage definitions -- deleted in dir_free_jcr above */
-   if (job->storage) {
-      copy_rwstorage(jcr, job->storage, _("Job resource"));
+
+   if (job->storage_policy) {
+      if (strcmp(job->storage_policy, "LeastUsed") == 0) {
+         jcr->store_mngr = New(LeastUsedStore());
+      }
    } else {
-      copy_rwstorage(jcr, job->pool->storage, _("Pool resource"));
+      jcr->store_mngr = New(SimpleStoreMngr());
    }
+
+   set_jcr_default_store(jcr, job);
+
    /* check if we run a restore */
    if (jcr->getJobType() == JT_RESTORE && job->RestoreClient){
       jcr->client = GetClientResWithName(jcr->job->RestoreClient);
@@ -1709,9 +1711,9 @@ void set_jcr_defaults(JCR *jcr, JOB *job)
 void copy_rwstorage(JCR *jcr, alist *storage, const char *where)
 {
    if (jcr->JobReads()) {
-      copy_rstorage(jcr, storage, where);
+      jcr->store_mngr->set_rstore(storage, where);
    }
-   copy_wstorage(jcr, storage, where);
+   jcr->store_mngr->set_wstorage(storage, where);
 }
 
 
@@ -1723,142 +1725,9 @@ void set_rwstorage(JCR *jcr, USTORE *store)
       return;
    }
    if (jcr->JobReads()) {
-      set_rstorage(jcr, store);
-   }
-   set_wstorage(jcr, store);
-}
-
-void free_rwstorage(JCR *jcr)
-{
-   free_rstorage(jcr);
-   free_wstorage(jcr);
-}
-
-/*
- * Copy the storage definitions from an alist to the JCR
- */
-void copy_rstorage(JCR *jcr, alist *storage, const char *where)
-{
-   if (storage) {
-      STORE *st;
-      if (jcr->rstorage) {
-         delete jcr->rstorage;
-      }
-      jcr->rstorage = New(alist(10, not_owned_by_alist));
-      foreach_alist(st, storage) {
-         jcr->rstorage->append(st);
-      }
-      if (!jcr->rstore_source) {
-         jcr->rstore_source = get_pool_memory(PM_MESSAGE);
-      }
-      pm_strcpy(jcr->rstore_source, where);
-      if (jcr->rstorage) {
-         jcr->rstore = (STORE *)jcr->rstorage->first();
-      }
-   }
-}
-
-
-/* Set storage override.  Remove all previous storage */
-void set_rstorage(JCR *jcr, USTORE *store)
-{
-   STORE *storage;
-
-   if (!store->store) {
-      return;
-   }
-   if (jcr->rstorage) {
-      free_rstorage(jcr);
-   }
-   if (!jcr->rstorage) {
-      jcr->rstorage = New(alist(10, not_owned_by_alist));
-   }
-   jcr->rstore = store->store;
-   if (!jcr->rstore_source) {
-      jcr->rstore_source = get_pool_memory(PM_MESSAGE);
-   }
-   pm_strcpy(jcr->rstore_source, store->store_source);
-   foreach_alist(storage, jcr->rstorage) {
-      if (store->store == storage) {
-         return;
-      }
-   }
-   /* Store not in list, so add it */
-   jcr->rstorage->prepend(store->store);
-}
-
-void free_rstorage(JCR *jcr)
-{
-   if (jcr->rstorage) {
-      delete jcr->rstorage;
-      jcr->rstorage = NULL;
-   }
-   jcr->rstore = NULL;
-}
-
-/*
- * Copy the storage definitions from an alist to the JCR
- */
-void copy_wstorage(JCR *jcr, alist *storage, const char *where)
-{
-   if (storage) {
-      STORE *st;
-      if (jcr->wstorage) {
-         delete jcr->wstorage;
-      }
-      jcr->wstorage = New(alist(10, not_owned_by_alist));
-      foreach_alist(st, storage) {
-         Dmsg1(100, "wstorage=%s\n", st->name());
-         jcr->wstorage->append(st);
-      }
-      if (!jcr->wstore_source) {
-         jcr->wstore_source = get_pool_memory(PM_MESSAGE);
-      }
-      pm_strcpy(jcr->wstore_source, where);
-      if (jcr->wstorage) {
-         jcr->wstore = (STORE *)jcr->wstorage->first();
-         Dmsg2(100, "wstore=%s where=%s\n", jcr->wstore->name(), jcr->wstore_source);
-      }
-   }
-}
-
-
-/* Set storage override. Remove all previous storage */
-void set_wstorage(JCR *jcr, USTORE *store)
-{
-   STORE *storage;
-
-   if (!store->store) {
-      return;
-   }
-   if (jcr->wstorage) {
-      free_wstorage(jcr);
-   }
-   if (!jcr->wstorage) {
-      jcr->wstorage = New(alist(10, not_owned_by_alist));
-   }
-   jcr->wstore = store->store;
-   if (!jcr->wstore_source) {
-      jcr->wstore_source = get_pool_memory(PM_MESSAGE);
-   }
-   pm_strcpy(jcr->wstore_source, store->store_source);
-   Dmsg2(50, "wstore=%s where=%s\n", jcr->wstore->name(), jcr->wstore_source);
-   foreach_alist(storage, jcr->wstorage) {
-      if (store->store == storage) {
-         return;
-      }
-   }
-   /* Store not in list, so add it */
-   jcr->wstorage->prepend(store->store);
-}
-
-void free_wstorage(JCR *jcr)
-{
-   if (jcr->wstorage) {
-      delete jcr->wstorage;
-      jcr->wstorage = NULL;
+      jcr->store_mngr->set_rstore(store->store, store->store_source);
    }
-   jcr->wstore = NULL;
+   jcr->store_mngr->set_wstorage(store->store, store->store_source);
 }
 
 void create_clones(JCR *jcr)
index 18dc1321ca7ef2085c769d5a0f54bfdc20bbc7fb..0623c76fe9a03ffa8544ccc829f17ba30bba304e 100644 (file)
@@ -47,7 +47,6 @@ extern "C" void *sched_wait(void *arg);
 static int  start_server(jobq_t *jq);
 static bool acquire_resources(JCR *jcr);
 static bool reschedule_job(JCR *jcr, jobq_t *jq, jobq_item_t *je);
-static void dec_write_store(JCR *jcr);
 
 /*
  * Initialize a job queue
@@ -482,8 +481,8 @@ void *jobq_server(void *arg)
           *  put into the ready queue.
           */
          if (jcr->acquired_resource_locks) {
-            dec_read_store(jcr);
-            dec_write_store(jcr);
+            jcr->store_mngr->dec_read_stores();
+            jcr->store_mngr->dec_write_stores();
             update_client_numconcurrentjobs(jcr, -1);
             jcr->job->incNumConcurrentJobs(-1);
             jcr->acquired_resource_locks = false;
@@ -629,6 +628,7 @@ void *jobq_server(void *arg)
 static bool reschedule_job(JCR *jcr, jobq_t *jq, jobq_item_t *je)
 {
    bool resched = false;
+   alist *store;
    /*
     * Reschedule the job if requested and possible
     */
@@ -743,15 +743,17 @@ static bool reschedule_job(JCR *jcr, jobq_t *jq, jobq_item_t *je)
       njcr->diff_pool = jcr->diff_pool;
       njcr->JobStatus = -1;
       njcr->setJobStatus(jcr->JobStatus);
-      if (jcr->rstore) {
-         copy_rstorage(njcr, jcr->rstorage, _("previous Job"));
+      if (jcr->store_mngr->get_rstore()) {
+         store = jcr->store_mngr->get_rstore_list();
+         njcr->store_mngr->set_rstore(store, _("previous Job"));
       } else {
-         free_rstorage(njcr);
+         njcr->store_mngr->reset_rstorage();
       }
-      if (jcr->wstore) {
-         copy_wstorage(njcr, jcr->wstorage, _("previous Job"));
+      if (jcr->store_mngr->get_wstore()) {
+         store = jcr->store_mngr->get_wstore_list();
+         njcr->store_mngr->set_wstorage(store, _("previous Job"));
       } else {
-         free_wstorage(njcr);
+         njcr->store_mngr->reset_wstorage();
       }
       njcr->messages = jcr->messages;
       njcr->spool_data = jcr->spool_data;
@@ -793,29 +795,23 @@ static bool acquire_resources(JCR *jcr)
       return false;
    }
 #endif
-   if (jcr->rstore) {
-      Dmsg1(200, "Rstore=%s\n", jcr->rstore->name());
-      if (!inc_read_store(jcr)) {
-         Dmsg1(200, "Fail rncj=%d\n", jcr->rstore->getNumConcurrentJobs());
+   STORE *rstore = jcr->store_mngr->get_rstore();
+   if (rstore) {
+      Dmsg1(200, "Rstore=%s\n", rstore->name());
+      if (!jcr->store_mngr->inc_read_stores(jcr)) {
+         Dmsg1(200, "Fail rncj=%d\n", rstore->getNumConcurrentJobs());
          jcr->setJobStatus(JS_WaitStoreRes);
          return false;
       }
    }
 
-   if (jcr->wstore) {
-      Dmsg1(200, "Wstore=%s\n", jcr->wstore->name());
-      int num = jcr->wstore->getNumConcurrentJobs();
-      if (num < jcr->wstore->MaxConcurrentJobs) {
-         num = jcr->wstore->incNumConcurrentJobs(1);
-         Dmsg1(200, "Inc wncj=%d\n", num);
-      } else if (jcr->rstore) {
-         dec_read_store(jcr);
-         skip_this_jcr = true;
-      } else {
-         Dmsg1(200, "Fail wncj=%d\n", num);
-         skip_this_jcr = true;
+   if (!jcr->store_mngr->inc_write_stores(jcr)) {
+      if (jcr->store_mngr->get_rstore()) {
+         jcr->store_mngr->dec_read_stores();
       }
+      skip_this_jcr = true;
    }
+
    if (skip_this_jcr) {
       jcr->setJobStatus(JS_WaitStoreRes);
       return false;
@@ -826,8 +822,8 @@ static bool acquire_resources(JCR *jcr)
          update_client_numconcurrentjobs(jcr, 1);
       } else {
          /* Back out previous locks */
-         dec_write_store(jcr);
-         dec_read_store(jcr);
+         jcr->store_mngr->dec_write_stores();
+         jcr->store_mngr->dec_read_stores();
          jcr->setJobStatus(JS_WaitClientRes);
          return false;
       }
@@ -836,8 +832,8 @@ static bool acquire_resources(JCR *jcr)
       jcr->job->incNumConcurrentJobs(1);
    } else {
       /* Back out previous locks */
-      dec_write_store(jcr);
-      dec_read_store(jcr);
+      jcr->store_mngr->dec_write_stores();
+      jcr->store_mngr->dec_read_stores();
       update_client_numconcurrentjobs(jcr, -1);
       jcr->setJobStatus(JS_WaitJobRes);
       return false;
@@ -846,50 +842,3 @@ static bool acquire_resources(JCR *jcr)
    jcr->acquired_resource_locks = true;
    return true;
 }
-
-static pthread_mutex_t rstore_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-/*
- * Note: inc_read_store() and dec_read_store() are
- *   called from select_rstore() in src/dird/restore.c
- */
-bool inc_read_store(JCR *jcr)
-{
-   P(rstore_mutex);
-   int num = jcr->rstore->getNumConcurrentJobs();
-   int numread = jcr->rstore->getNumConcurrentReadJobs();
-   int maxread = jcr->rstore->MaxConcurrentReadJobs;
-   if (num < jcr->rstore->MaxConcurrentJobs &&
-       (jcr->getJobType() == JT_RESTORE ||
-        numread == 0     ||
-        maxread == 0     ||     /* No limit set */
-        numread < maxread))     /* Below the limit */
-   {
-      numread = jcr->rstore->incNumConcurrentReadJobs(1);
-      num = jcr->rstore->incNumConcurrentJobs(1);
-      Dmsg1(200, "Inc rncj=%d\n", num);
-      V(rstore_mutex);
-      return true;
-   }
-   V(rstore_mutex);
-   return false;
-}
-
-void dec_read_store(JCR *jcr)
-{
-   if (jcr->rstore) {
-      P(rstore_mutex);
-      jcr->rstore->incNumConcurrentReadJobs(-1);
-      int num = jcr->rstore->incNumConcurrentJobs(-1);
-      Dmsg1(200, "Dec rncj=%d\n", num);
-      V(rstore_mutex);
-   }
-}
-
-static void dec_write_store(JCR *jcr)
-{
-   if (jcr->wstore) {
-      int num = jcr->wstore->incNumConcurrentJobs(-1);
-      Dmsg1(200, "Dec wncj=%d\n", num);
-   }
-}
index e40a13fa617937caa3073051579dbbd83af2f707..625492945da27347bdbef38b5f8391cc7f35a73a 100644 (file)
@@ -256,8 +256,8 @@ bool do_mac_init(JCR *jcr)
 
    if (set_mac_next_pool(jcr, &pool)) {
       /* If pool storage specified, use it for restore */
-      copy_rstorage(wjcr, pool->storage, _("Pool resource"));
-      copy_rstorage(jcr, pool->storage, _("Pool resource"));
+      wjcr->store_mngr->set_rstore(pool->storage, _("Pool resource"));
+      jcr->store_mngr->set_rstore(pool->storage, _("Pool resource"));
 
       wjcr->pool = jcr->pool;
       wjcr->next_pool = jcr->next_pool;
@@ -358,6 +358,8 @@ bool do_mac(JCR *jcr)
    STORE *store;
    char *store_address;
    uint32_t store_port;
+   STORE *rstore = NULL;
+   STORE *wstore = NULL;
 
    /*
     * If wjcr is NULL, there is nothing to do for this job,
@@ -402,12 +404,9 @@ bool do_mac(JCR *jcr)
     * Now separate the read and write storages. jcr has no wstor...
     *  they all go into wjcr.
     */
-   free_rwstorage(wjcr);
-   wjcr->rstore = NULL;
-   wjcr->wstore = jcr->wstore;
-   jcr->wstore = NULL;
-   wjcr->wstorage = jcr->wstorage;
-   jcr->wstorage = NULL;
+   wjcr->store_mngr->reset_rwstorage();
+   wjcr->store_mngr->set_wstorage(jcr->store_mngr->get_wstore_list(), _("MAC JOB"));
+   jcr->store_mngr->reset_wstorage();
 
    /* TODO: See priority with bandwidth parameter */
    if (jcr->job->max_bandwidth > 0) {
@@ -450,16 +449,18 @@ bool do_mac(JCR *jcr)
       jcr->sd_calls_client = jcr->client->sd_calls_client;
    }
 
+   rstore = jcr->store_mngr->get_rstore();
+   wstore = wjcr->store_mngr->get_wstore();
    Dmsg2(dbglevel, "Read store=%s, write store=%s\n",
-      ((STORE *)jcr->rstorage->first())->name(),
-      ((STORE *)wjcr->wstorage->first())->name());
+      rstore->name(),
+      wstore->name());
 
    /*
     * Now start a job with the read Storage daemon sending the bsr.
     *  This call returns the sd_auth_key
     */
    Dmsg1(200, "Start job with read (jcr) storage daemon. Jid=%d\n", jcr->JobId);
-   if (!start_storage_daemon_job(jcr, jcr->rstorage, NULL, /*send_bsr*/true)) {
+   if (!start_storage_daemon_job(jcr, jcr->store_mngr->get_rstore_list(), NULL, /*send_bsr*/true)) {
       goto bail_out;
    }
    Dmsg0(150, "Read storage daemon connection OK\n");
@@ -476,7 +477,7 @@ bool do_mac(JCR *jcr)
     * Now start a job with the write Storage daemon sending.
     */
    Dmsg1(200, "Start Job with write (wjcr) storage daemon. Jid=%d\n", jcr->JobId);
-   if (!start_storage_daemon_job(wjcr, NULL, wjcr->wstorage, /*no_send_bsr*/false)) {
+   if (!start_storage_daemon_job(wjcr, NULL, wjcr->store_mngr->get_wstore_list(), /*no_send_bsr*/false)) {
       goto bail_out;
    }
    Dmsg0(150, "Write storage daemon connection OK\n");
@@ -536,7 +537,7 @@ bool do_mac(JCR *jcr)
       }
 
       /* Setup the storage address and port */
-      store = wjcr->wstore;
+      store = wjcr->store_mngr->get_wstore();
       if (store->SDDport == 0) {
          store->SDDport = store->SDport;
       }
@@ -572,7 +573,7 @@ bool do_mac(JCR *jcr)
        *
        * Send Storage daemon address to the writing SD
        */
-      store = jcr->rstore;
+      store = jcr->store_mngr->get_rstore();
       if (store->SDDport == 0) {
          store->SDDport = store->SDport;
       }
@@ -613,10 +614,8 @@ bool do_mac(JCR *jcr)
 
 bail_out:
    /* Put back jcr write storages for proper cleanup */
-   jcr->wstorage = wjcr->wstorage;
-   jcr->wstore = wjcr->wstore;
-   wjcr->wstore = NULL;
-   wjcr->wstorage = NULL;
+   jcr->store_mngr->set_wstorage(wjcr->store_mngr->get_wstore_list(), "rollback");
+   wjcr->store_mngr->reset_wstorage();
    wjcr->file_bsock = NULL;
 
    if (ok) {
@@ -948,12 +947,12 @@ void mac_cleanup(JCR *jcr, int TermCode, int writeTermCode)
         jcr->FSCreateTime,
         jcr->rpool?jcr->rpool->name():"*None*",
         jcr->rpool_source,
-        jcr->rstore?jcr->rstore->name():"*None*",
-        NPRT(jcr->rstore_source),
+        jcr->store_mngr->get_rstore()?jcr->store_mngr->get_rstore()->name():"*None*",
+        NPRT(jcr->store_mngr->get_rsource()),
         jcr->pool?jcr->pool->name():"*None*",
         jcr->pool_source,
-        jcr->wstore?jcr->wstore->name():"*None*",
-        NPRT(jcr->wstore_source),
+        jcr->store_mngr->get_wstore()?jcr->store_mngr->get_wstore()->name():"*None*",
+        NPRT(jcr->store_mngr->get_wsource()),
         jcr->catalog?jcr->catalog->name():"*None*",
         jcr->catalog_source,
         sdt,
@@ -996,7 +995,7 @@ bool set_mac_wstorage(UAContext *ua, JCR *jcr, POOL *pool, POOL *next_pool,
    }
 
    /* If pool storage specified, use it instead of job storage for backup */
-   copy_wstorage(jcr, next_pool->storage, source);
+   jcr->store_mngr->set_wstorage(next_pool->storage, source);
 
    return true;
 }
index c7a5beb0c1b87347552cea6f1484b23db2129e2a..dd235abc6eee16929f9075c731f37893bd72e76c 100644 (file)
@@ -45,7 +45,7 @@ static char jobcmd[] = "JobId=%s job=%s job_name=%s client_name=%s "
    "rerunning=%d VolSessionId=%d VolSessionTime=%d sd_client=%d "
    "Authorization=%s\n";
 static char use_storage[] = "use storage=%s media_type=%s pool_name=%s "
-   "pool_type=%s append=%d copy=%d stripe=%d\n";
+   "pool_type=%s append=%d copy=%d stripe=%d wait=%d\n";
 static char use_device[] = "use device=%s\n";
 //static char query_device[] = _("query device=%s");
 
@@ -64,7 +64,7 @@ extern "C" void *msg_thread(void *arg);
 
 BSOCK *open_sd_bsock(UAContext *ua)
 {
-   STORE *store = ua->jcr->wstore;
+   STORE *store = ua->jcr->store_mngr->get_wstore();
 
    if (!is_bsock_open(ua->jcr->store_bsock)) {
       ua->send_msg(_("Connecting to Storage daemon %s at %s:%d ...\n"),
@@ -95,6 +95,7 @@ bool connect_to_storage_daemon(JCR *jcr, int retry_interval,
    BSOCK *sd = jcr->store_bsock;
    STORE *store;
    utime_t heart_beat;
+   STORE *wstore = jcr->store_mngr->get_wstore();
 
    if (is_bsock_open(sd)) {
       return true;                    /* already connected */
@@ -104,10 +105,15 @@ bool connect_to_storage_daemon(JCR *jcr, int retry_interval,
    }
 
    /* If there is a write storage use it */
-   if (jcr->wstore) {
-      store = jcr->wstore;
+   if (wstore) {
+      store = wstore;
    } else {
-      store = jcr->rstore;
+      store = jcr->store_mngr->get_rstore();
+   }
+
+   if (!store) {
+      Dmsg0(100, "No storage resource found in jcr!\n");
+      return false;
    }
 
    if (store->heartbeat_interval) {
@@ -174,7 +180,7 @@ static char OKbootstrap[] = "3000 OK bootstrap\n";
 /*
  * Start a job with the Storage daemon
  */
-bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore, bool send_bsr)
+bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore, bool wait, bool send_bsr)
 {
    bool ok = true;
    STORE *storage;
@@ -184,6 +190,7 @@ bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore, bool send_
    POOL_MEM job_name, client_name, fileset_name;
    int copy = 0;
    int stripe = 0;
+   int sleep = wait;    /* 'wait' arg needs to be passed as an int through the network */
    char ed1[30], ed2[30];
    int sd_client;
 
@@ -290,7 +297,7 @@ bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore, bool send_
          }
          bash_spaces(media_type);
          sd->fsend(use_storage, store_name.c_str(), media_type.c_str(),
-                   pool_name.c_str(), pool_type.c_str(), 0, copy, stripe);
+                   pool_name.c_str(), pool_type.c_str(), 0, copy, stripe, sleep);
          Dmsg1(100, "rstore >stored: %s", sd->msg);
          DEVICE *dev;
          /* Loop over alternative storage Devices until one is OK */
@@ -328,7 +335,7 @@ bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore, bool send_
          pm_strcpy(media_type, storage->media_type);
          bash_spaces(media_type);
          sd->fsend(use_storage, store_name.c_str(), media_type.c_str(),
-                   pool_name.c_str(), pool_type.c_str(), 1, copy, stripe);
+               pool_name.c_str(), pool_type.c_str(), 1, copy, stripe, sleep);
 
          Dmsg1(100, "wstore >stored: %s", sd->msg);
          DEVICE *dev;
@@ -357,11 +364,11 @@ bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore, bool send_
       POOL_MEM err_msg;
       if (sd->msg[0]) {
          pm_strcpy(err_msg, sd->msg); /* save message */
-         Jmsg(jcr, M_FATAL, 0, _("\n"
+         Jmsg(jcr, M_INFO, 0, _("\n"
               "     Storage daemon didn't accept Device \"%s\" because:\n     %s"),
               device_name.c_str(), err_msg.c_str()/* sd->msg */);
       } else {
-         Jmsg(jcr, M_FATAL, 0, _("\n"
+         Jmsg(jcr, M_INFO, 0, _("\n"
               "     Storage daemon didn't accept Device \"%s\" command.\n"),
               device_name.c_str());
       }
index 7cf7c1fc4bc64dcbe4fbe2306a36d218053542d3..0c6cab3c1194c684d14b6b42758dea26c71d848c 100644 (file)
@@ -47,6 +47,7 @@ static bool perform_full_name_substitution(JCR *jcr, MEDIA_DBR *mr, POOL_DBR *pr
 bool newVolume(JCR *jcr, MEDIA_DBR *mr, STORE *store, POOL_MEM &errmsg)
 {
    POOL_DBR pr;
+   STORE *wstore = jcr->store_mngr->get_wstore();
 
    bmemset(&pr, 0, sizeof(pr));
 
@@ -74,7 +75,7 @@ bool newVolume(JCR *jcr, MEDIA_DBR *mr, STORE *store, POOL_MEM &errmsg)
    mr->clear();
    set_pool_dbr_defaults_in_media_dbr(mr, &pr);
    jcr->VolumeName[0] = 0;
-   bstrncpy(mr->MediaType, jcr->wstore->media_type, sizeof(mr->MediaType));
+   bstrncpy(mr->MediaType, wstore->media_type, sizeof(mr->MediaType));
    generate_plugin_event(jcr, bDirEventNewVolume); /* return void... */
    if (jcr->VolumeName[0] && is_volume_name_legal(NULL, jcr->VolumeName)) {
       bstrncpy(mr->VolumeName, jcr->VolumeName, sizeof(mr->VolumeName));
index e6adc8a3f9fa28dac9de719a5ec14ad0e852cb0a..25136d41f78019dce62a4a558f005dadea30ce6b 100644 (file)
@@ -104,7 +104,7 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,
    int retry = 0;
    bool ok;
    bool InChanger;
-   STORE *store = jcr->wstore;
+   STORE *store = jcr->store_mngr->get_wstore();
 
    bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
    Dmsg6(dbglvl, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s index=%d create=%d prune=%d\n",
index 35536dd455b284b414057112305297ff6969db9c..2848f7a40cc61580b910f9d15c33747de52c2528 100644 (file)
@@ -138,13 +138,6 @@ extern void init_jcr_job_record(JCR *jcr);
 extern void update_job_end(JCR *jcr, int TermCode);
 extern void copy_rwstorage(JCR *jcr, alist *storage, const char *where);
 extern void set_rwstorage(JCR *jcr, USTORE *store);
-extern void free_rwstorage(JCR *jcr);
-extern void copy_wstorage(JCR *jcr, alist *storage, const char *where);
-extern void set_wstorage(JCR *jcr, USTORE *store);
-extern void free_wstorage(JCR *jcr);
-extern void copy_rstorage(JCR *jcr, alist *storage, const char *where);
-extern void set_rstorage(JCR *jcr, USTORE *store);
-extern void free_rstorage(JCR *jcr);
 extern bool setup_job(JCR *jcr);
 extern void create_clones(JCR *jcr);
 extern int create_restore_bootstrap_file(JCR *jcr);
@@ -177,7 +170,7 @@ extern BSOCK *open_sd_bsock(UAContext *ua);
 extern void close_sd_bsock(UAContext *ua);
 extern bool connect_to_storage_daemon(JCR *jcr, int retry_interval,
                               int max_retry_time, int verbose);
-extern bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore,
+extern bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore, bool wait,
               bool send_bsr=false);
 extern bool start_storage_daemon_message_thread(JCR *jcr);
 extern int bget_dirmsg(BSOCK *bs);
@@ -295,7 +288,7 @@ int     do_prompt(UAContext *ua, const char *automsg, const char *msg, char *pro
 int     do_alist_prompt(UAContext *ua, const char *automsg, const char *msg,
               alist *selected);
 CAT    *get_catalog_resource(UAContext *ua);
-STORE  *get_storage_resource(UAContext *ua, bool use_default, bool unique=false);
+bool    get_storage_resource(UAContext *ua, USTORE *ustore, bool use_default, bool unique=false);
 int     get_storage_drive(UAContext *ua, STORE *store);
 int     get_storage_slot(UAContext *ua, STORE *store);
 int     get_media_type(UAContext *ua, char *MediaType, int max_media);
index f5e562ddb9e5962d21032d6a7ac45281f2d41599..ddaba6ba722c8354072de31d405f1f69f00800dd 100644 (file)
@@ -147,7 +147,7 @@ static bool open_bootstrap_file(JCR *jcr, bootstrap_info &info)
    if (!jcr->RestoreBootstrap) {
       return false;
    }
-   bstrncpy(info.storage, jcr->rstore->name(), MAX_NAME_LENGTH);
+   bstrncpy(info.storage, jcr->store_mngr->get_rstore()->name(), MAX_NAME_LENGTH);
 
    bs = bfopen(jcr->RestoreBootstrap, "rb");
    if (!bs) {
@@ -194,7 +194,7 @@ static bool is_on_same_storage(JCR *jcr, char *new_one)
       return true;
    }
    /* same name */
-   if (!strcmp(new_one, jcr->rstore->name())) {
+   if (!strcmp(new_one, jcr->store_mngr->get_rstore()->name())) {
       return true;
    }
    new_store = (STORE *)GetResWithName(R_STORAGE, new_one);
@@ -207,8 +207,9 @@ static bool is_on_same_storage(JCR *jcr, char *new_one)
    /* if Port and Hostname/IP are same, we are talking to the same
     * Storage Daemon
     */
-   if (jcr->rstore->SDport != new_store->SDport ||
-       strcmp(jcr->rstore->address, new_store->address))
+   STORE *rstore = jcr->store_mngr->get_rstore();
+   if (rstore->SDport != new_store->SDport ||
+       strcmp(rstore->address, new_store->address))
    {
       return false;
    }
@@ -282,15 +283,16 @@ static bool send_bootstrap_file(JCR *jcr, BSOCK *sock,
  */
 static bool select_rstore(JCR *jcr, bootstrap_info &info)
 {
-   USTORE ustore;
+   STORE *store;
    int i;
 
 
-   if (!strcmp(jcr->rstore->name(), info.storage)) {
+   STORE *rstore = jcr->store_mngr->get_rstore();
+   if (!strcmp(rstore->name(), info.storage)) {
       return true;                 /* same SD nothing to change */
    }
 
-   if (!(ustore.store = (STORE *)GetResWithName(R_STORAGE,info.storage))) {
+   if (!(store = (STORE *)GetResWithName(R_STORAGE,info.storage))) {
       Jmsg(jcr, M_FATAL, 0,
            _("Could not get storage resource '%s'.\n"), info.storage);
       jcr->setJobStatus(JS_ErrorTerminated);
@@ -306,27 +308,26 @@ static bool select_rstore(JCR *jcr, bootstrap_info &info)
    /*
     * release current read storage and get a new one
     */
-   dec_read_store(jcr);
-   free_rstorage(jcr);
-   set_rstorage(jcr, &ustore);
+   jcr->store_mngr->dec_read_stores();
+   jcr->store_mngr->set_rstore(store, _("Job resource"));
    jcr->setJobStatus(JS_WaitSD);
    /*
     * Wait for up to 6 hours to increment read stoage counter
     */
    for (i=0; i < MAX_TRIES; i++) {
       /* try to get read storage counter incremented */
-      if (inc_read_store(jcr)) {
+      if (jcr->store_mngr->inc_read_stores(jcr)) {
          jcr->setJobStatus(JS_Running);
          return true;
       }
       bmicrosleep(10, 0);       /* sleep 10 secs */
       if (job_canceled(jcr)) {
-         free_rstorage(jcr);
+         jcr->store_mngr->reset_rstorage();
          return false;
       }
    }
    /* Failed to inc_read_store() */
-   free_rstorage(jcr);
+   jcr->store_mngr->reset_rstorage();
    Jmsg(jcr, M_FATAL, 0,
       _("Could not acquire read storage lock for \"%s\""), info.storage);
    return false;
@@ -396,7 +397,7 @@ bool restore_bootstrap(JCR *jcr)
       /*
        * Now start a job with the Storage daemon
        */
-      if (!start_storage_daemon_job(jcr, jcr->rstorage, NULL)) {
+      if (!start_storage_daemon_job(jcr, jcr->store_mngr->get_rstore_list(), NULL, true /* wait */)) {
          goto bail_out;
       }
 
@@ -423,6 +424,8 @@ bool restore_bootstrap(JCR *jcr)
          goto bail_out;
       }
 
+      STORE *rstore = jcr->store_mngr->get_rstore();
+
       if (jcr->sd_calls_client) {
          /*
           * SD must call "client" i.e. FD
@@ -438,7 +441,7 @@ bool restore_bootstrap(JCR *jcr)
             goto bail_out;
          }
 
-         store_address = jcr->rstore->address;  /* dummy */
+         store_address = rstore->address;  /* dummy */
          store_port = 0;                        /* flag that SD calls FD */
 
       } else {
@@ -454,17 +457,17 @@ bool restore_bootstrap(JCR *jcr)
           *   then wait for File daemon to make connection
           *   with Storage daemon.
           */
-         if (jcr->rstore->SDDport == 0) {
-            jcr->rstore->SDDport = jcr->rstore->SDport;
+         if (rstore->SDDport == 0) {
+            rstore->SDDport = rstore->SDport;
          }
 
-         store_address = get_storage_address(jcr->client, jcr->rstore);
-         store_port = jcr->rstore->SDDport;
+         store_address = get_storage_address(jcr->client, rstore);
+         store_port = rstore->SDDport;
       }
 
       /* TLS Requirement */
-      if (jcr->rstore->tls_enable) {
-         if (jcr->rstore->tls_require) {
+      if (rstore->tls_enable) {
+         if (rstore->tls_require) {
             tls_need = BNET_TLS_REQUIRED;
          } else {
             tls_need = BNET_TLS_OK;
@@ -544,7 +547,7 @@ bool do_restore(JCR *jcr)
    JOB_DBR rjr;                       /* restore job record */
    int stat;
 
-   free_wstorage(jcr);                /* we don't write */
+   jcr->store_mngr->reset_wstorage();
 
    if (!allow_duplicate_job(jcr)) {
       goto bail_out;
@@ -628,7 +631,7 @@ bool do_restore_init(JCR *jcr)
       delete jcr->plugin_config;
       jcr->plugin_config = NULL;
    }
-   free_wstorage(jcr);
+   jcr->store_mngr->reset_wstorage();
    return true;
 }
 
diff --git a/bacula/src/dird/store_mngr.c b/bacula/src/dird/store_mngr.c
new file mode 100644 (file)
index 0000000..0e34cde
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2020 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+
+#include "bacula.h"
+#include "dird.h"
+
+storage::storage() {
+   list = New(alist(10, not_owned_by_alist));
+   source = get_pool_memory(PM_MESSAGE);
+   list_str = get_pool_memory(PM_MESSAGE);
+   *source = 0;
+   store = NULL;
+   mutex = PTHREAD_MUTEX_INITIALIZER;
+}
+
+storage::~storage() {
+   store = NULL;
+   if (list) {
+      delete list;
+      list = NULL;
+   }
+   if (source) {
+      free_and_null_pool_memory(source);
+   }
+   if (list_str) {
+      free_and_null_pool_memory(list_str);
+   }
+}
+
+void storage::set_rw(bool write) {
+   P(mutex);
+   this->write = write;
+   V(mutex);
+}
+
+alist *storage::get_list() {
+   return list;
+}
+
+const char *storage::get_source() const {
+   return source;
+}
+
+const char *storage::get_media_type() const {
+   return store->media_type;
+}
+
+void storage::set(STORE *storage, const char *source) {
+   if (!storage) {
+      return;
+   }
+
+   P(mutex);
+
+   reset();
+
+   list->append(storage);
+
+   store = storage;
+   if (!source) {
+      pm_strcpy(this->source, _("Not specified"));
+   } else {
+      pm_strcpy(this->source, source);
+   }
+
+   V(mutex);
+}
+
+/* Set storage override. Remove all previous storage */
+void storage::set(alist *storage, const char *source) {
+   if (!storage) {
+      return;
+   }
+
+   P(mutex);
+
+   reset();
+
+   STORE *s;
+   foreach_alist(s, storage) {
+      list->append(s);
+   }
+
+   store = (STORE *)list->first();
+   if (!source) {
+      pm_strcpy(this->source, _("Not specified"));
+   } else {
+      pm_strcpy(this->source, source);
+   }
+
+   V(mutex);
+}
+
+void storage::reset() {
+   store = NULL;
+   while (list->size()) {
+      list->remove(0);
+   }
+   *source = 0;
+   *list_str = 0;
+}
+
+/* Set custom storage for next usage (it needs to be an item from the current store list) */
+bool storage::set_current_storage(STORE *storage) {
+   if (!storage) {
+      return false;
+   }
+
+   P(mutex);
+
+   STORE *s;
+   foreach_alist(s, list) {
+      if (s == storage) {
+         store = storage;
+         V(mutex);
+         return true;
+      }
+   }
+
+   V(mutex);
+   return false;
+}
+
+bool storage::inc_rstores(JCR *jcr) {
+   if (list->empty()) {
+      return true;
+   }
+
+   P(mutex);
+   int num = store->getNumConcurrentJobs();
+   int numread = store->getNumConcurrentReadJobs();
+   int maxread = store->MaxConcurrentReadJobs;
+   if (num < store->MaxConcurrentJobs &&
+         (jcr->getJobType() == JT_RESTORE ||
+          numread == 0     ||
+          maxread == 0     ||     /* No limit set */
+          numread < maxread))     /* Below the limit */
+   {
+      numread = store->incNumConcurrentReadJobs(1);
+      num = store->incNumConcurrentJobs(1);
+      Dmsg1(200, "Inc rncj=%d\n", num);
+      V(mutex);
+      return true;
+   }
+   V(mutex);
+   return false;
+}
+
+bool storage::inc_wstores(JCR *jcr) {
+   STORE *store;
+   bool ret;
+   if (list->empty()) {
+      return true;
+   }
+
+   P(mutex);
+
+   /* Create a temp copy of wstore list */
+   alist tmp_list(10, not_owned_by_alist);
+   foreach_alist(store, list) {
+      tmp_list.append(store);
+   }
+
+   /* Reset write list */
+   list->destroy();
+   list->init(10, not_owned_by_alist);
+
+   foreach_alist(store, &tmp_list) {
+      Dmsg1(200, "Wstore=%s\n", store->name());
+      int num = store->getNumConcurrentJobs();
+      if (num < store->MaxConcurrentJobs) {
+         num = store->incNumConcurrentJobs(1);
+         Dmsg1(200, "Inc wncj=%d\n", num);
+         list->append(store);
+      }
+   }
+
+   if (!list->empty()) {
+      ret = true;
+   } else {
+      /* Failed to increment counter in at least one storage */
+      ret = false;
+   }
+
+   V(mutex);
+   return ret;
+}
+
+bool storage::inc_stores(JCR *jcr) {
+   if (write) {
+      return inc_wstores(jcr);
+   } else {
+      return inc_rstores(jcr);
+   }
+}
+
+void storage::dec_stores() {
+   if (list->empty()) {
+      return;
+   }
+
+   P(mutex);
+   STORE *store;
+   foreach_alist(store, list) {
+      int num = store->incNumConcurrentJobs(-1);
+      Dmsg1(200, "Dec wncj=%d\n", num);
+   }
+   V(mutex);
+}
+
+const char *storage::print_list() {
+   P(mutex);
+
+   *list_str = 0;
+   STORE *store;
+   POOL_MEM tmp;
+   bool first = true;
+
+   foreach_alist(store, list) {
+      if (first) {
+         first = false;
+      } else {
+         pm_strcat(tmp.addr(), ", ");
+      }
+      pm_strcat(tmp.addr(), store->name());
+   }
+
+   V(mutex);
+   return quote_string(list_str, tmp.addr());
+}
+
+
+void LeastUsedStore::apply_policy(bool write_store) {
+   alist *store = write_store ? wstore.get_list() : rstore.get_list();
+   alist tmp_list(10, not_owned_by_alist);
+   uint32_t store_count = store->size();
+   uint32_t i, j, swap;
+   //TODO arrays below limit store list to 64 items currently...
+   uint32_t conc_arr[64];
+   uint32_t idx_arr[64];
+
+
+   for (uint32_t i=0; i<store_count; i++) {
+      tmp_list.append(store->get(i));
+   }
+
+   /* Reset list */
+   store->destroy();
+   store->init(10, not_owned_by_alist);
+
+   STORE *storage;
+   foreach_alist_index(i, storage, &tmp_list) {
+      idx_arr[i] = i;
+      conc_arr[i] = storage->getNumConcurrentJobs();
+   }
+
+   /* Simple bubble sort */
+   for (i = 0; i<store_count - 1; i++) {
+      for (j =0; j<store_count - i -1; j++) {
+         if (conc_arr[i] > conc_arr[i+1]) {
+            swap = conc_arr[i];
+            conc_arr[i] = conc_arr[i+1];
+            conc_arr[i+1] = swap;
+            swap = idx_arr[i];
+            idx_arr[i] = idx_arr[i+1];
+            idx_arr[i+1] = swap;
+         }
+      }
+   }
+
+   for (i=0; i<store_count; i++) {
+      storage = (STORE *)tmp_list.get(idx_arr[i]);
+      store->append(storage);
+   }
+}
+
+
+StorageManager::StorageManager() {
+   rstore.set_rw(false);
+   wstore.set_rw(true);
+};
+
+STORE *StorageManager::get_rstore() {
+   return rstore.get_store();
+}
+
+alist *StorageManager::get_rstore_list() {
+   return rstore.get_list();
+}
+
+const char *StorageManager::get_rsource() const {
+   return rstore.get_source();
+}
+
+const char *StorageManager::get_rmedia_type() const {
+   return rstore.get_media_type();
+}
+
+alist *StorageManager::get_wstore_list() {
+   return wstore.get_list();
+}
+
+const char *StorageManager::get_wsource() const {
+   return wstore.get_source();
+}
+
+const char *StorageManager::get_wmedia_type() const {
+   return wstore.get_media_type();
+}
+
+void StorageManager::set_rstore(STORE *storage, const char *source) {
+   rstore.set(storage, source);
+}
+
+void StorageManager::set_rstore(alist *storage, const char *source) {
+   rstore.set(storage, source);
+}
+
+void StorageManager::reset_rstorage() {
+   rstore.reset();
+}
+
+const char *StorageManager::print_rlist() {
+   return rstore.print_list();
+}
+
+bool StorageManager::set_current_wstorage(STORE *storage) {
+   return wstore.set_current_storage(storage);
+}
+
+void StorageManager::set_wstorage(STORE *storage, const char *source) {
+   wstore.set(storage, source);
+}
+
+void StorageManager::set_wstorage(alist *storage, const char *source) {
+   wstore.set(storage, source);
+}
+
+void StorageManager::reset_wstorage() {
+   wstore.reset();
+}
+
+const char *StorageManager::print_wlist() {
+   return wstore.print_list();
+}
+
+void StorageManager::reset_rwstorage() {
+   rstore.reset();
+   wstore.reset();
+}
+
+bool StorageManager::inc_read_stores(JCR *jcr) {
+   return rstore.inc_stores(jcr);
+}
+
+void StorageManager::dec_read_stores() {
+   return rstore.dec_stores();
+}
+
+bool StorageManager::inc_write_stores(JCR *jcr) {
+   return wstore.inc_stores(jcr);
+
+}
+
+void StorageManager::dec_write_stores() {
+   return wstore.dec_stores();
+}
diff --git a/bacula/src/dird/store_mngr.h b/bacula/src/dird/store_mngr.h
new file mode 100644 (file)
index 0000000..95b4dcb
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2020 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+/*
+ * Storage manager classes.
+ * All of this code is intented to make managing
+ * (accessing, setting, incrementing counters, applying storage policy...) storage easier
+ * from the code perspective.
+ */
+
+#ifndef STORE_MNGR_H
+#define STORE_MNGR_H 1
+
+/* Forward delcaration */
+class STORE;
+
+/* Not so many policies for now...
+ * Some ideas for next ones:
+ *    - Round robin
+ *    - Most free space
+ *    - Least CPU usage
+ *    - Least network usage
+ *    - A lot more
+ *
+ * Some protocol/interface changes will be needed to query all of the storages from
+ * the list about it's current status, depending of policy used.
+ */
+static char const *storage_mngmt_policy[] = {
+   "LeastUsed",
+   NULL
+};
+
+/*
+ * Helper class to make managing each storage type (write/read) easier.
+ * It contains storage resource ('store' member) which is currently used as well as list of all
+ * possible storage resource choices.
+ */
+class storage {
+    private:
+      bool write;            /* Write or read storage */
+      STORE *store;          /* Selected storage to be used */
+      alist *list;           /* Storage possibilities */
+      POOLMEM *source;       /* Where the storage came from */
+      POOLMEM *list_str;     /* List of storage names in the list */
+      pthread_mutex_t mutex; /* Mutex for accessing items */
+
+      /* Only when we are a read storage - increment concurrent read counters for all storages on the list */
+      bool inc_rstores(JCR *jcr);
+
+      /* Only when we are a write storage - increment concurrent write counters for all storages on the list */
+      bool inc_wstores(JCR *jcr);
+   public:
+      storage();
+
+      ~storage();
+
+      /* Determine if we are write or read storage */
+      void set_rw(bool write);
+
+      /* Get storage which will be used next */
+      STORE *get_store() {
+         return store;
+      }
+
+      /* Get list of all possible storages */
+      alist *get_list();
+
+      /* Get source of the storage (pool, job, commandline, unknown, ...) */
+      const char *get_source() const;
+
+      /* Get media type of current storage */
+      const char *get_media_type() const;
+
+      /* Set storage override. Remove all previous storage.
+       * Can be called for single storage - list consists only one, specified storage then.
+       * Can be called for setting a list - internal list consists of same elemets as the list passed
+       * as an arg. First item from the list becames storage currently used.
+       */
+      void set(STORE *storage, const char *source);
+      void set(alist *storage, const char *source);
+
+      /* Reset class, remove all items from list, unset storage currently used, clean source */
+      void reset();
+
+      /* Set custom storage for next usage (it needs to be an item from the current store list) */
+      bool set_current_storage(STORE *storage);
+
+      /* Increment concurrent read/write counters for all storages on the list */
+      bool inc_stores(JCR *jcr);
+
+      /* Decrement concurrent read/write counters for all storages on the list */
+      void dec_stores();
+
+      /* Print all elements of the list (sample result of print_list() -> "File1, File2, File3" */
+      const char *print_list();
+};
+
+
+/*
+ * Storage Manager class responsible for managing all of the storage used by the JCR.
+ * It's holds read as well as write storage resources assigned to the JCR.
+ * It is a base class for Storage Policy (hence virtual 'apply_policy' method).
+ * Most of member functions are just wrappers around the storage class to make accessing
+ * and managin read/write storage in a bit more friendly way.
+ *
+ */
+class StorageManager : public SMARTALLOC {
+
+   protected:
+      storage rstore;   /* Read storage */
+      storage wstore;   /* Write storage */
+
+   public:
+      virtual void apply_policy(bool write_store) = 0;
+
+      virtual ~StorageManager() {
+         reset_rwstorage();
+      };
+
+      StorageManager();
+
+      /* Helper to validate if policy user wants to use is a valid one */
+      static bool check_policy(const char *policy) {
+         int i = 0;
+         while (storage_mngmt_policy[i]) {
+            if (strcmp(policy, storage_mngmt_policy[i]) == 0) {
+               return true;
+            }
+            i++;
+         }
+         return false;
+      }
+
+      /************ READ STORAGE HELPERS ************/
+      STORE *get_rstore();
+
+      alist *get_rstore_list();
+
+      const char *get_rsource() const;
+
+      const char *get_rmedia_type() const;
+
+      void set_rstore(STORE *storage, const char *source);
+
+      void set_rstore(alist *storage, const char *source);
+
+      void reset_rstorage();
+
+      bool inc_read_stores(JCR *jcr);
+
+      void dec_read_stores();
+
+      const char *print_rlist();
+
+      /************ WRITE STORAGE HELPERS ************/
+      STORE *get_wstore() {
+         return wstore.get_store();
+      }
+
+      alist *get_wstore_list();
+
+      const char *get_wsource() const;
+
+      const char *get_wmedia_type() const;
+
+      bool set_current_wstorage(STORE *storage);
+
+      void set_wstorage(STORE *storage, const char *source);
+
+      void set_wstorage(alist *storage, const char *source);
+
+      void reset_wstorage();
+
+      const char *print_wlist();
+
+      bool inc_write_stores(JCR *jcr);
+
+      void dec_write_stores();
+
+      /************ GENERIC STORAGE HELPERS ************/
+      void reset_rwstorage();
+};
+
+/*
+ * Least used policy chooses storage from the list which has the least concurrent jobs number.
+ */
+class LeastUsedStore : public StorageManager {
+   public:
+      void apply_policy(bool write_store);
+
+   LeastUsedStore() {
+   }
+
+   ~LeastUsedStore() {
+   }
+};
+
+/*
+ * Default policy for the storage group. It uses first available storage from the list.
+ */
+class SimpleStoreMngr : public StorageManager {
+   private:
+
+   public:
+      void apply_policy(bool write_store) {
+         /* Do nothing for now */
+      }
+
+   SimpleStoreMngr() {
+   }
+
+   ~SimpleStoreMngr() {
+   }
+};
+
+#endif // STORE_MNGR_H
index 1d54c8f2547984f28567f0853197179bf1eccbaf..229d03bc17b7b670a93617d77e5cb0c490509f6b 100644 (file)
@@ -262,9 +262,10 @@ bool do_a_command(UAContext *ua)
       return false;
    }
 
-   if (ua->jcr->wstorage) {
-      while (ua->jcr->wstorage->size()) {
-         ua->jcr->wstorage->remove(0);
+   alist *wstorage = ua->jcr->store_mngr->get_wstore_list();
+   if (!wstorage->empty()) {
+      while (wstorage->size()) {
+         wstorage->remove(0);
       }
    }
 
@@ -335,7 +336,7 @@ static int add_cmd(UAContext *ua, const char *cmd)
    MEDIA_DBR mr;
    int num, i, max, startnum;
    char name[MAX_NAME_LENGTH];
-   STORE *store;
+   USTORE ustore;
    int Slot = 0, InChanger = 0;
 
    ua->send_msg(_(
@@ -365,8 +366,8 @@ static int add_cmd(UAContext *ua, const char *cmd)
    }
 
    /* Get media type */
-   if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
-      bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
+   if (get_storage_resource(ua, &ustore, false/*no default*/)) {
+      bstrncpy(mr.MediaType, ustore.store->media_type, sizeof(mr.MediaType));
    } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
       return 1;
    }
@@ -435,7 +436,7 @@ static int add_cmd(UAContext *ua, const char *cmd)
       num = 1;
    }
 
-   if (store && store->autochanger) {
+   if (ustore.store && ustore.store->autochanger) {
       if (!get_pint(ua, _("Enter slot (0 for none): "))) {
          return 1;
       }
@@ -452,7 +453,7 @@ static int add_cmd(UAContext *ua, const char *cmd)
       mr.Slot = Slot++;
       mr.InChanger = InChanger;
       mr.Enabled = 1;
-      set_storageid_in_mr(store, &mr);
+      set_storageid_in_mr(ustore.store, &mr);
       Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
       if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
          ua->error_msg("%s", db_strerror(ua->db));
@@ -1048,11 +1049,8 @@ static void do_storage_setdebug(UAContext *ua, STORE *store,
                char *options, char *tags)
 {
    BSOCK *sd;
-   USTORE lstore;
 
-   lstore.store = store;
-   pm_strcpy(lstore.store_source, _("unknown source"));
-   set_wstorage(ua->jcr, &lstore);
+   ua->jcr->store_mngr->set_wstorage(store, _("unknown source"));
    /* Try connecting for up to 15 seconds */
    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
       store->name(), store->address, store->SDport);
@@ -1211,7 +1209,7 @@ static void do_all_setdebug(UAContext *ua, int64_t level,
  */
 static int setdebug_cmd(UAContext *ua, const char *cmd)
 {
-   STORE *store;
+   USTORE ustore;
    CLIENT *client;
    int64_t level=0, tags=0;
    int trace_flag = -1;
@@ -1306,18 +1304,16 @@ static int setdebug_cmd(UAContext *ua, const char *cmd)
       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
-         store = NULL;
          if (ua->argv[i]) {
-            store = GetStoreResWithName(ua->argv[i]);
-            if (store) {
-               do_storage_setdebug(ua, store, level, trace_flag,
+            ustore.store = GetStoreResWithName(ua->argv[i]);
+            if (ustore.store) {
+               do_storage_setdebug(ua, ustore.store, level, trace_flag,
                   hangup, blowup, options, tags_str);
                return 1;
             }
          }
-         store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
-         if (store) {
-            do_storage_setdebug(ua, store, level, trace_flag,
+         if (get_storage_resource(ua, &ustore, false/*no default*/, true/*unique*/)) {
+            do_storage_setdebug(ua, ustore.store, level, trace_flag,
                hangup, blowup, options, tags_str);
             return 1;
          }
@@ -1337,9 +1333,8 @@ static int setdebug_cmd(UAContext *ua, const char *cmd)
       do_dir_setdebug(ua, level, trace_flag, options, tags_str);
       break;
    case 1:
-      store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
-      if (store) {
-         do_storage_setdebug(ua, store, level, trace_flag, hangup, blowup,
+      if (get_storage_resource(ua, &ustore, false/*no default*/, true/*unique*/)) {
+         do_storage_setdebug(ua, ustore.store, level, trace_flag, hangup, blowup,
             options, tags_str);
       }
       break;
@@ -2392,7 +2387,7 @@ int memory_cmd(UAContext *ua, const char *cmd)
 
 static void do_storage_cmd(UAContext *ua, const char *command)
 {
-   USTORE store;
+   USTORE ustore;
    BSOCK *sd;
    JCR *jcr = ua->jcr;
    char dev_name[MAX_NAME_LENGTH];
@@ -2404,20 +2399,19 @@ static void do_storage_cmd(UAContext *ua, const char *command)
    }
    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
 
-   store.store = get_storage_resource(ua, true/*arg is storage*/);
-   if (!store.store) {
+   if (!get_storage_resource(ua, &ustore, true/*arg is storage*/)) {
       return;
    }
-   pm_strcpy(store.store_source, _("unknown source"));
-   set_wstorage(jcr, &store);
-   drive = get_storage_drive(ua, store.store);
+
+   jcr->store_mngr->set_wstorage(ustore.store, ustore.store_source);
+   drive = get_storage_drive(ua, ustore.store);
    /* For the disable/enable/unmount commands, the slot is not mandatory */
    if (strcasecmp(command, "disable") == 0 ||
        strcasecmp(command, "enable")  == 0 ||
        strcasecmp(command, "unmount")  == 0) {
       slot = 0;
    } else {
-      slot = get_storage_slot(ua, store.store);
+      slot = get_storage_slot(ua, ustore.store);
    }
    /* Users may set a device name directly on the command line */
    if ((i = find_arg_with_value(ua, "device")) > 0) {
@@ -2431,11 +2425,11 @@ static void do_storage_cmd(UAContext *ua, const char *command)
       bstrncpy(dev_name, ua->argv[i], sizeof(dev_name));
 
    } else {                     /* We take the default device name */
-      bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
+      bstrncpy(dev_name, ustore.store->dev_name(), sizeof(dev_name));
    }
 
    Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
-      store.store->media_type, store.store->dev_name(), drive);
+      ustore.store->media_type, ustore.store->dev_name(), drive);
    Dmsg4(120, "Cmd: %s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
 
    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
@@ -2445,7 +2439,7 @@ static void do_storage_cmd(UAContext *ua, const char *command)
 
    /* Keep track of this important event */
    ua->send_events("DC0013", EVENTS_TYPE_COMMAND, "%s storage=%s dev=%s",
-                  command, store.store->name(), dev_name);
+                  command, ustore.store->name(), dev_name);
 
    sd = jcr->store_bsock;
    bash_spaces(dev_name);
@@ -2588,7 +2582,7 @@ int cloud_volumes_cmd(UAContext *ua, const char *cmd, const char *mode)
 bail_out:
    close_db(ua);
    close_sd_bsock(ua);
-   ua->jcr->wstore = NULL;
+   ua->jcr->store_mngr->reset_wstorage();
    if (results) {
       free(results);
    }
@@ -2602,7 +2596,7 @@ static int cloud_list_cmd(UAContext *ua, const char *cmd)
 {
    int drive = -1;
    int64_t size, mtime;
-   STORE *store = NULL;
+   USTORE ustore;
    MEDIA_DBR mr;
    POOL_DBR pr;
    BSOCK *sd = NULL;
@@ -2630,12 +2624,12 @@ static int cloud_list_cmd(UAContext *ua, const char *cmd)
    }
 
    /* Choose storage */
-   ua->jcr->wstore = store = get_storage_resource(ua, false);
-   if (!store) {
+   if (!get_storage_resource(ua, &ustore, false)) {
       goto bail_out;
    }
-   bstrncpy(storage, store->dev_name(), sizeof(storage));
-   bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
+   ua->jcr->store_mngr->set_wstorage(ustore.store, ustore.store_source);
+   bstrncpy(storage, ustore.store->dev_name(), sizeof(storage));
+   bstrncpy(mr.MediaType, ustore.store->media_type, sizeof(mr.MediaType));
 
    if ((sd=open_sd_bsock(ua)) == NULL) {
       Dmsg0(100, "Can't open connection to SD\n");
@@ -2736,7 +2730,7 @@ static int cloud_list_cmd(UAContext *ua, const char *cmd)
 bail_out:
    close_db(ua);
    close_sd_bsock(ua);
-   ua->jcr->wstore = NULL;
+   ua->jcr->store_mngr->reset_wstorage();
    return 1;
 }
 
index 92765e78627528b8dc32a889fb56440a69d0a503..78820078e796f231f78394b2dcd3b5279dff6816 100644 (file)
@@ -413,7 +413,6 @@ void do_collect_storage(UAContext *ua, STORE *store, bool doall, display_format_
 {
    int i;
    BSOCK *sd;
-   USTORE lstore;
    POOL_MEM buf;
 
    if (!acl_access_ok(ua, Storage_ACL, store->name())) {
@@ -430,9 +429,8 @@ void do_collect_storage(UAContext *ua, STORE *store, bool doall, display_format_
       ua->error_msg(_("Restricted Client or Job does not permit access to  Storage daemons\n"));
       return;
    }
-   lstore.store = store;
-   pm_strcpy(lstore.store_source, _("unknown source"));
-   set_wstorage(ua->jcr, &lstore);
+
+   ua->jcr->store_mngr->set_wstorage(store, _("unknown source"));
    /* Try connecting for up to 15 seconds */
    if (!ua->api) ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
       store->name(), store->address, store->SDport);
@@ -483,7 +481,7 @@ int collect_cmd(UAContext *ua, const char *cmd)
    bool doall = true;
    display_format_t format = COLLECT_SIMPLE;
    char **margk;
-   STORE *store = NULL;
+   USTORE ustore;
    CLIENT *client = NULL;
 
    Dmsg1(20, "cmd:%s:\n", cmd);
@@ -519,7 +517,7 @@ int collect_cmd(UAContext *ua, const char *cmd)
          Dmsg0(20, "statistics format JSON\n");
          continue;
       }
-      if (strcasecmp(ua->argk[i], "client") == 0 && store == NULL) {
+      if (strcasecmp(ua->argk[i], "client") == 0 && ustore.store == NULL) {
          client = get_client_resource(ua, JT_BACKUP_RESTORE);
          if (!client) {
             goto bailout;
@@ -527,8 +525,7 @@ int collect_cmd(UAContext *ua, const char *cmd)
          continue;
       }
       if (strcasecmp(ua->argk[i], "storage") == 0 && client == NULL) {
-         store = get_storage_resource(ua, false /*no default*/, true/*unique*/);
-         if (!store) {
+         if (!get_storage_resource(ua, &ustore, false /*no default*/, true/*unique*/)) {
             goto bailout;
          }
          continue;
@@ -560,8 +557,8 @@ int collect_cmd(UAContext *ua, const char *cmd)
       case 0:                         /* Director, the default behavior */
          break;
       case 1:
-         store = select_storage_resource(ua, true/*unique*/);
-         if (!store) {
+         ustore.store = select_storage_resource(ua, true/*unique*/);
+         if (!ustore.store) {
             goto bailout;
          }
          break;
@@ -580,9 +577,9 @@ int collect_cmd(UAContext *ua, const char *cmd)
       /* do collect client */
       do_collect_client(ua, client, doall, format, margc, margk);
    } else
-   if (store){
+   if (ustore.store){
       /* do collect storage */
-      do_collect_storage(ua, store, doall, format, margc, margk);
+      do_collect_storage(ua, ustore.store, doall, format, margc, margk);
    } else {
       /* it is simpler to handle JSON array here */
       if (format == COLLECT_JSON){
index 6500861755dcc3cc0cd8c7d91bbada707d12c53b..f8f06eb0102bd90ebedff864628a713a2b429da0 100644 (file)
@@ -1665,11 +1665,8 @@ static void do_storage_cmd(UAContext *ua, STORE *store, const char *cmd)
 {
    BSOCK *sd;
    JCR *jcr = ua->jcr;
-   USTORE lstore;
 
-   lstore.store = store;
-   pm_strcpy(lstore.store_source, _("unknown source"));
-   set_wstorage(jcr, &lstore);
+   jcr->store_mngr->set_wstorage(store, _("unknown source"));
    /* Try connecting for up to 15 seconds */
    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
       store->name(), store->address, store->SDport);
@@ -1721,7 +1718,7 @@ static void do_client_cmd(UAContext *ua, CLIENT *client, const char *cmd)
 static bool admin_cmds(UAContext *ua, const char *cmd)
 {
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-   STORE *store=NULL;
+   USTORE ustore;
    CLIENT *client=NULL;
    bool dir=false;
    bool do_deadlock=false;
@@ -1764,17 +1761,16 @@ static bool admin_cmds(UAContext *ua, const char *cmd)
       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
-         store = NULL;
          if (ua->argv[i]) {
-            store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
+            ustore.store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
          }
-         if (!store) {
-            store = get_storage_resource(ua, false/*no default*/);
+         if (!ustore.store) {
+            get_storage_resource(ua, &ustore, false/*no default*/);
          }
       }
    }
 
-   if (!dir && !store && !client) {
+   if (!dir && !ustore.store && !client) {
       /*
        * We didn't find an appropriate keyword above, so
        * prompt the user.
@@ -1788,7 +1784,7 @@ static bool admin_cmds(UAContext *ua, const char *cmd)
          dir=true;
          break;
       case 1:
-         store = get_storage_resource(ua, false/*no default*/);
+         get_storage_resource(ua, &ustore, false/*no default*/);
          break;
       case 2:
          client = select_client_resource(ua, JT_BACKUP_RESTORE);
@@ -1798,8 +1794,8 @@ static bool admin_cmds(UAContext *ua, const char *cmd)
       }
    }
 
-   if (store) {
-      do_storage_cmd(ua, store, remote_cmd);
+   if (ustore.store) {
+      do_storage_cmd(ua, ustore.store, remote_cmd);
    }
 
    if (client) {
index 5c146dbbf1ff0dcd6b050621eb0cb6106f46f4fe..ce0505b5e6e8b11eb2fac13e4bc1e68d9c127ae8 100644 (file)
@@ -158,7 +158,7 @@ bail_out:
  */
 void update_slots(UAContext *ua)
 {
-   USTORE store;
+   USTORE ustore;
    vol_list_t *vl, *vol_list = NULL;
    MEDIA_DBR mr;
    char *slot_list;
@@ -173,13 +173,12 @@ void update_slots(UAContext *ua)
    if (!open_client_db(ua)) {
       return;
    }
-   store.store = get_storage_resource(ua, true/*arg is storage*/);
-   if (!store.store) {
+
+   if (!get_storage_resource(ua, &ustore, true/*arg is storage*/)) {
       return;
    }
-   pm_strcpy(store.store_source, _("Command input"));
-   set_wstorage(ua->jcr, &store);
-   drive = get_storage_drive(ua, store.store);
+   ua->jcr->store_mngr->set_wstorage(ustore.store, ustore.store_source);
+   drive = get_storage_drive(ua, ustore.store);
 
    scan = find_arg(ua, NT_("scan")) >= 0;
    if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) {
@@ -244,7 +243,7 @@ void update_slots(UAContext *ua)
       } else {
          mr.VolumeName[0] = 0;
       }
-      set_storageid_in_mr(store.store, &mr);
+      set_storageid_in_mr(ustore.store, &mr);
       Dmsg4(100, "Before make unique: Vol=%s slot=%d inchanger=%d sid=%d\n",
             mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
       db_lock(ua->db);
@@ -265,13 +264,13 @@ void update_slots(UAContext *ua)
          Dmsg4(100, "After get MR: Vol=%s slot=%d inchanger=%d sid=%d\n",
             mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId);
          /* If Slot, Inchanger, and StorageId have changed, update the Media record */
-         if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) {
+         if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != ustore.store->StorageId) {
             mr.Slot = vl->Slot;
             mr.InChanger = 1;
             if (have_enabled) {
                mr.Enabled = Enabled;
             }
-            set_storageid_in_mr(store.store, &mr);
+            set_storageid_in_mr(ustore.store, &mr);
             if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
                ua->error_msg("%s", db_strerror(ua->db));
             } else {
@@ -293,7 +292,7 @@ void update_slots(UAContext *ua)
    }
    mr.clear();
    mr.InChanger = 1;
-   set_storageid_in_mr(store.store, &mr);
+   set_storageid_in_mr(ustore.store, &mr);
    db_lock(ua->db);
    for (int i=1; i <= max_slots; i++) {
       if (slot_list[i]) {
@@ -320,7 +319,7 @@ bail_out:
  */
 static int do_label(UAContext *ua, const char *cmd, int relabel)
 {
-   USTORE store;
+   USTORE ustore;
    BSOCK *sd;
    char dev_name[MAX_NAME_LENGTH];
    MEDIA_DBR mr, omr;
@@ -351,13 +350,11 @@ static int do_label(UAContext *ua, const char *cmd, int relabel)
       label_barcodes = true;
    }
 
-   store.store = get_storage_resource(ua, true/*use default*/);
-   if (!store.store) {
+   if (!get_storage_resource(ua, &ustore, true/*use default*/)) {
       return 1;
    }
-   pm_strcpy(store.store_source, _("Command input"));
-   set_wstorage(ua->jcr, &store);
-   drive = get_storage_drive(ua, store.store);
+   ua->jcr->store_mngr->set_wstorage(ustore.store, ustore.store_source);
+   drive = get_storage_drive(ua, ustore.store);
 
    if (label_barcodes) {
       label_from_barcodes(ua, drive);
@@ -430,7 +427,7 @@ checkName:
          mr.Slot = 0;
       }
       mr.InChanger = mr.Slot > 0;  /* if slot give assume in changer */
-   } else if (store.store->autochanger) {
+   } else if (ustore.store->autochanger) {
       if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) {
          return 1;
       }
@@ -440,9 +437,9 @@ checkName:
       }
       mr.InChanger = mr.Slot > 0;  /* if slot give assume in changer */
    }
-   set_storageid_in_mr(store.store, &mr);
+   set_storageid_in_mr(ustore.store, &mr);
 
-   bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType));
+   bstrncpy(mr.MediaType, ustore.store->media_type, sizeof(mr.MediaType));
 
    /* Must select Pool if not already done */
    if (pr.PoolId == 0) {
@@ -472,7 +469,7 @@ checkName:
          }
       }
       if (ua->automount) {
-         bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
+         bstrncpy(dev_name, ustore.store->dev_name(), sizeof(dev_name));
          ua->info_msg(_("Requesting to mount %s ...\n"), dev_name);
          bash_spaces(dev_name);
          sd->fsend("mount %s drive=%d slot=%d", dev_name, drive, mr.Slot);
@@ -508,7 +505,7 @@ checkName:
  */
 static void label_from_barcodes(UAContext *ua, int drive)
 {
-   STORE *store = ua->jcr->wstore;
+   STORE *store = ua->jcr->store_mngr->get_wstore();
    POOL_DBR pr;
    MEDIA_DBR mr, omr;
    vol_list_t *vl, *vol_list = NULL;
@@ -679,11 +676,12 @@ static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
    uint64_t VolBytes = 0;
    uint64_t VolABytes = 0;
    uint32_t VolType = 0;
+   STORE *wstore = ua->jcr->store_mngr->get_wstore();
 
    if (!(sd=open_sd_bsock(ua))) {
       return false;
    }
-   bstrncpy(dev_name, ua->jcr->wstore->dev_name(), sizeof(dev_name));
+   bstrncpy(dev_name, wstore->dev_name(), sizeof(dev_name));
    bash_spaces(dev_name);
    bash_spaces(mr->VolumeName);
    bash_spaces(mr->MediaType);
@@ -717,7 +715,7 @@ static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
             mr->VolABytes = VolABytes;
             mr->VolType = VolType;
             mr->InChanger = mr->Slot > 0;  /* if slot give assume in changer */
-            set_storageid_in_mr(ua->jcr->wstore, mr);
+            set_storageid_in_mr(wstore, mr);
             if (!db_update_media_record(ua->jcr, ua->db, mr)) {
                ua->error_msg("%s", db_strerror(ua->db));
                ok = false;
@@ -729,7 +727,7 @@ static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
             mr->VolType = VolType;
             mr->InChanger = mr->Slot > 0;  /* if slot give assume in changer */
             mr->Enabled = 1;
-            set_storageid_in_mr(ua->jcr->wstore, mr);
+            set_storageid_in_mr(wstore, mr);
             if (db_create_media_record(ua->jcr, ua->db, mr)) {
                ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d  successfully created.\n"),
                             mr->VolumeName, mr->Slot);
@@ -753,7 +751,7 @@ static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr,
 
 static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
 {
-   STORE *store = ua->jcr->wstore;
+   STORE *store = ua->jcr->store_mngr->get_wstore();
    BSOCK *sd;
    char dev_name[MAX_NAME_LENGTH];
    char *VolName = NULL;
@@ -794,7 +792,7 @@ static char *get_volume_name_from_SD(UAContext *ua, int Slot, int drive)
  */
 static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan)
 {
-   STORE *store = ua->jcr->wstore;
+   STORE *store = ua->jcr->store_mngr->get_wstore();
    char dev_name[MAX_NAME_LENGTH];
    BSOCK *sd;
    vol_list_t *vl;
@@ -922,7 +920,7 @@ static void free_vol_list(vol_list_t *vol_list)
  */
 static int get_num_slots_from_SD(UAContext *ua)
 {
-   STORE *store = ua->jcr->wstore;
+   STORE *store = ua->jcr->store_mngr->get_wstore();
    char dev_name[MAX_NAME_LENGTH];
    BSOCK *sd;
    int slots = 0;
@@ -954,7 +952,7 @@ static int get_num_slots_from_SD(UAContext *ua)
  */
 int get_num_drives_from_SD(UAContext *ua)
 {
-   STORE *store = ua->jcr->wstore;
+   STORE *store = ua->jcr->store_mngr->get_wstore();
    char dev_name[MAX_NAME_LENGTH];
    BSOCK *sd;
    int drives = 0;
@@ -1166,7 +1164,7 @@ void status_content(UAContext *ua, STORE *store)
  */
 void status_slots(UAContext *ua, STORE *store_r)
 {
-   USTORE store;
+   STORE *store;
    POOL_DBR pr;
    vol_list_t *vl, *vol_list = NULL;
    MEDIA_DBR mr;
@@ -1184,11 +1182,10 @@ void status_slots(UAContext *ua, STORE *store_r)
    if (!open_client_db(ua)) {
       return;
    }
-   store.store = store_r;
+   store = store_r;
 
-   pm_strcpy(store.store_source, _("Command input"));
-   set_wstorage(ua->jcr, &store);
-   get_storage_drive(ua, store.store);
+   ua->jcr->store_mngr->set_wstorage(store, _("Command input"));
+   get_storage_drive(ua, store);
 
    max_slots = get_num_slots_from_SD(ua);
 
index 5bbdae134f493daf4aa745a13f2ebb29cf3bb316..0a529813c776f53dd6e1228717e618077bf19c79 100644 (file)
@@ -775,7 +775,7 @@ int truncate_cmd(UAContext *ua, const char *cmd)
 bail_out:
    close_db(ua);
    close_sd_bsock(ua);
-   ua->jcr->wstore = NULL;
+   ua->jcr->store_mngr->reset_wstorage();
    if (results) {
       free(results);
    }
index 55eb10c4dc95c60edc9aedde57e7d48767e8cd5f..6b3cad27e48e52bd0077fa3f8b41be9c04eb37d5 100644 (file)
@@ -1969,6 +1969,7 @@ static void free_name_list(NAME_LIST *name_list)
 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType)
 {
    STORE *store;
+   USTORE ustore;
 
    if (rx.store) {
       Dmsg1(200, "Already have store=%s\n", rx.store->name());
@@ -2040,9 +2041,10 @@ void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *
    }
 
    /* Take command line arg, or ask user if none */
-   rx.store = get_storage_resource(ua, false /* don't use default */);
-   if (rx.store) {
-      Dmsg1(200, "Set store=%s\n", rx.store->name());
+
+   if (get_storage_resource(ua, &ustore, false /* don't use default */)) {
+      Dmsg2(200, "Set store=%s store source=%s\n", ustore.store->name(), ustore.store_source);
    }
 
+   rx.store = ustore.store;
 }
index 27fad7d596a38799aca55665db3231476b4c9928..9408db90a2a47a4a830740f83d56b4b0cacc4111 100644 (file)
@@ -512,36 +512,9 @@ static bool get_fileset(UAContext *ua, run_ctx &rc)
 /*
  * Fill in storage data according to what is setup
  *  in the run context, and make sure the user
- *  has authorized access to it.
+ *  has authorized access to it. If no storage is found, list from job/pool is used later (see set_jcr_default_store()).
  */
 static bool get_storage(UAContext *ua, run_ctx &rc)
-{
-   if (rc.store_name) {
-      rc.store->store = GetStoreResWithName(rc.store_name);
-      pm_strcpy(rc.store->store_source, _("Command input"));
-      if (!rc.store->store) {
-         if (*rc.store_name != 0) {
-            ua->warning_msg(_("Storage \"%s\" not found.\n"), rc.store_name);
-         }
-         rc.store->store = select_storage_resource(ua);
-         pm_strcpy(rc.store->store_source, _("user selection"));
-      }
-   } else if (!rc.store->store) {
-      get_job_storage(rc.store, rc.job, NULL);      /* use default */
-   }
-   if (!rc.store->store) {
-      ua->error_msg(_("No storage specified.\n"));
-      return false;
-   } else if (!acl_access_ok(ua, Storage_ACL, rc.store->store->name())) {
-      ua->error_msg(_("No authorization. Storage \"%s\".\n"),
-               rc.store->store->name());
-      return false;
-   }
-   Dmsg1(800, "Using storage=%s\n", rc.store->store->name());
-   return true;
-}
-
-static bool get_storage_from_job_record(UAContext *ua, run_ctx &rc)
 {
    if (rc.restart) {
       char name[MAX_NAME_LENGTH];
@@ -554,16 +527,36 @@ static bool get_storage_from_job_record(UAContext *ua, run_ctx &rc)
             Dmsg1(50, "Found Storage resource related to the JobId=%ld\n",
                   rc.JobId);
             return true;
-         }
+         } else {
          Dmsg1(50, "Could not find any Storage resource related to the one refered by JobId=%ld\n",
                rc.JobId);
+         }
 
       } else {
          Dmsg1(50, "Could not find any Storage record in catalog related to the one refered by JobId=%ld\n",
                rc.JobId);
       }
+   } else if (rc.store_name) {
+      rc.store->store = GetStoreResWithName(rc.store_name);
+      pm_strcpy(rc.store->store_source, _("Command input"));
+      if (!rc.store->store) {
+         if (*rc.store_name != 0) {
+            ua->warning_msg(_("Storage \"%s\" not found.\n"), rc.store_name);
+         }
+         rc.store->store = select_storage_resource(ua);
+         pm_strcpy(rc.store->store_source, _("user selection"));
+      }
+   }
+
+   if (!rc.store->store) {
+      get_job_storage(rc.store, rc.job, NULL);      /* use default */
    }
-   get_job_storage(rc.store, rc.job, NULL);
+   if (rc.store->store && !acl_access_ok(ua, Storage_ACL, rc.store->store->name())) {
+      ua->error_msg(_("No authorization. Storage \"%s\".\n"),
+               rc.store->store->name());
+      return false;
+   }
+
    return true;
 }
 
@@ -699,8 +692,6 @@ static bool get_jobid_from_list(UAContext *ua, sellist &sl, run_ctx &rc)
       return false;
    }
 
-   get_storage_from_job_record(ua, rc);
-
    bmemset(&fr, 0, sizeof(fr));
    fr.FileSetId = rc.jr.FileSetId;
    if (!db_get_fileset_record(ua->jcr, ua->db, &fr)) {
@@ -1876,8 +1867,10 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
 {
    char ec1[30], edl[50];
    char dt[MAX_TIME_LENGTH];
+   STORE *wstore = jcr->store_mngr->get_wstore();
 
    Dmsg1(800, "JobType=%c\n", jcr->getJobType());
+   STORE *rstore = jcr->store_mngr->get_rstore();
    switch (jcr->getJobType()) {
    case JT_ADMIN:
       if (ua->api) {
@@ -1893,7 +1886,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
                  job->name(),
                  jcr->fileset->name(),
                  NPRT(jcr->client->name()),
-                 jcr->wstore?jcr->wstore->name():"*None*",
+                 wstore?wstore->name():"*None*",
                  bstrutime(dt, sizeof(dt), jcr->sched_time),
                  jcr->JobPriority);
       } else {
@@ -1907,7 +1900,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
                  job->name(),
                  jcr->fileset->name(),
                  NPRT(jcr->client->name()),
-                 jcr->wstore?jcr->wstore->name():"*None*",
+                 wstore?wstore->name():"*None*",
                  bstrutime(dt, sizeof(dt), jcr->sched_time),
                  jcr->JobPriority);
       }
@@ -1942,7 +1935,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
                  jcr->fileset->name(),
                  NPRT(jcr->pool->name()),
                  next_pool,
-                 jcr->wstore?jcr->wstore->name():"*None*",
+                 wstore?wstore->name():"*None*",
                  bstrutime(dt, sizeof(dt), jcr->sched_time),
                  jcr->JobPriority,
                  jcr->plugin_options?"Plugin Options: ":"",
@@ -1972,7 +1965,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
                  jcr->fileset->name(),
                  NPRT(jcr->pool->name()), jcr->pool_source,
                  next_pool,
-                 jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
+                 wstore?wstore->name():"*None*", jcr->store_mngr->get_wsource(),
                  bstrutime(dt, sizeof(dt), jcr->sched_time),
                  jcr->JobPriority,
                  jcr->plugin_options?"Plugin Options: ":"",
@@ -2021,7 +2014,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
               jcr->client->name(),
               jcr->fileset->name(),
               NPRT(jcr->pool->name()), jcr->pool_source,
-              jcr->rstore->name(), jcr->rstore_source,
+              rstore->name(), jcr->store_mngr->get_rsource(),
               Name,
               verify_list,
               bstrutime(dt, sizeof(dt), jcr->sched_time),
@@ -2043,7 +2036,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
               jcr->client->name(),
               jcr->fileset->name(),
               NPRT(jcr->pool->name()), jcr->pool_source,
-              jcr->rstore->name(), jcr->rstore_source,
+              rstore->name(), jcr->store_mngr->get_rsource(),
               Name,
               verify_list,
               bstrutime(dt, sizeof(dt), jcr->sched_time),
@@ -2090,7 +2083,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
                  jcr->fileset->name(),
                  client_name,
                  jcr->client->name(),
-                 jcr->rstore->name(),
+                 rstore->name(),
                  bstrutime(dt, sizeof(dt), jcr->sched_time),
                  jcr->catalog->name(),
                  jcr->JobPriority,
@@ -2117,7 +2110,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
                  jcr->fileset->name(),
                  client_name,
                  jcr->client->name(),
-                 jcr->rstore->name(),
+                 rstore->name(),
                  bstrutime(dt, sizeof(dt), jcr->sched_time),
                  jcr->catalog->name(),
                  jcr->JobPriority,
@@ -2148,7 +2141,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
                  jcr->fileset->name(),
                  client_name,
                  jcr->client->name(),
-                 jcr->rstore->name(),
+                 rstore->name(),
                  bstrutime(dt, sizeof(dt), jcr->sched_time),
                  jcr->catalog->name(),
                  jcr->JobPriority,
@@ -2175,7 +2168,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
                  jcr->fileset->name(),
                  client_name,
                  jcr->client->name(),
-                 jcr->rstore->name(),
+                 rstore->name(),
                  bstrutime(dt, sizeof(dt), jcr->sched_time),
                  jcr->catalog->name(),
                  jcr->JobPriority,
@@ -2212,7 +2205,7 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
                         "Plugin Options:  %s\n"),
               replace,
               jcr->client->name(),
-              jcr->rstore->name(),
+              rstore->name(),
               jcr->RestoreJobId==0?"*None*":edit_uint64(jcr->RestoreJobId, ec1),
               bstrutime(dt, sizeof(dt), jcr->sched_time),
               jcr->catalog->name(),
@@ -2252,8 +2245,8 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
            jcr->fileset->name(),
            NPRT(jcr->pool->name()),
            jcr->next_pool?jcr->next_pool->name():"*None*",
-           jcr->rstore->name(),
-           jcr->wstore?jcr->wstore->name():"*None*",
+           rstore->name(),
+           wstore?wstore->name():"*None*",
            jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1),
            bstrutime(dt, sizeof(dt), jcr->sched_time),
            jcr->catalog->name(),
@@ -2285,8 +2278,8 @@ static bool display_job_parameters(UAContext *ua, JCR *jcr, JOB *job, const char
            NPRT(jcr->pool->name()), jcr->pool_source,
            jcr->next_pool?jcr->next_pool->name():"*None*",
                NPRT(jcr->next_pool_source),
-           jcr->rstore->name(), jcr->rstore_source,
-           jcr->wstore?jcr->wstore->name():"*None*", jcr->wstore_source,
+           rstore->name(), jcr->store_mngr->get_rsource(),
+           wstore?wstore->name():"*None*", jcr->store_mngr->get_wsource(),
            jcr->MigrateJobId==0?"*None*":edit_uint64(jcr->MigrateJobId, ec1),
            bstrutime(dt, sizeof(dt), jcr->sched_time),
            jcr->catalog->name(),
index cb6f929d8f189a65b76f9098307f9c6c4569bb1e..d03a7711e2bc7fc66358214668aee85278173df1 100644 (file)
@@ -1111,15 +1111,16 @@ done:
  * If use_default is set, we assume that any keyword without a value
  *   is the name of the Storage resource wanted.
  */
-STORE *get_storage_resource(UAContext *ua, bool use_default, bool unique)
+bool get_storage_resource(UAContext *ua, USTORE *ustore, bool use_default, bool unique)
 {
    char store_name[MAX_NAME_LENGTH];
-   STORE *store = NULL;
    int jobid;
    JCR *jcr;
    int i;
    char ed1[50];
+
    *store_name = 0;
+   ustore->store = NULL;
 
    for (i=1; i<ua->argc; i++) {
       if (use_default && !ua->argv[i]) {
@@ -1133,30 +1134,35 @@ STORE *get_storage_resource(UAContext *ua, bool use_default, bool unique)
          /* Default argument is storage (except in enable/disable command) */
          if (store_name[0]) {
             ua->error_msg(_("Storage name given twice.\n"));
-            return NULL;
+            goto bail_out;
          }
          bstrncpy(store_name, ua->argk[i], sizeof(store_name));
          if (store_name[0] == '?') {
             *store_name = 0;
             break;
          }
+
+         pm_strcpy(ustore->store_source, _("Command line"));
       } else {
          if (strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
              strcasecmp(ua->argk[i], NT_("sd")) == 0) {
             bstrncpy(store_name, NPRTB(ua->argv[i]), sizeof(store_name));
+            pm_strcpy(ustore->store_source, _("Command line (storage parameter)"));
 
          } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
             jobid = str_to_int64(ua->argv[i]);
             if (jobid <= 0) {
                ua->error_msg(_("Expecting jobid=nn command, got: %s\n"), ua->argk[i]);
-               return NULL;
+               goto bail_out;
             }
             if (!(jcr=get_jcr_by_id(jobid))) {
                ua->error_msg(_("JobId %s is not running.\n"), edit_int64(jobid, ed1));
-               return NULL;
+               goto bail_out;
             }
-            if (jcr->wstore) {
-               bstrncpy(store_name, jcr->wstore->name(), sizeof(store_name));
+            STORE *wstore = jcr->store_mngr->get_wstore();
+            if (wstore) {
+               bstrncpy(store_name, wstore->name(), sizeof(store_name));
+               pm_strcpy(ustore->store_source, _("Command line (jobid parameter)"));
             }
             free_jcr(jcr);
 
@@ -1164,25 +1170,29 @@ STORE *get_storage_resource(UAContext *ua, bool use_default, bool unique)
                     strcasecmp(ua->argk[i], NT_("jobname")) == 0) {
             if (!ua->argv[i]) {
                ua->error_msg(_("Expecting job=xxx, got: %s.\n"), ua->argk[i]);
-               return NULL;
+               goto bail_out;
             }
             if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
                ua->error_msg(_("Job \"%s\" is not running.\n"), ua->argv[i]);
-               return NULL;
+               goto bail_out;
             }
-            if (jcr->wstore) {
-               bstrncpy(store_name, jcr->wstore->name(), sizeof(store_name));
+            STORE *wstore = jcr->store_mngr->get_wstore();
+            if (wstore) {
+               bstrncpy(store_name, wstore->name(), sizeof(store_name));
+               pm_strcpy(ustore->store_source, _("Command line (job/jobname parameter)"));
             }
             free_jcr(jcr);
 
          } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
             if (!ua->argv[i]) {
                ua->error_msg(_("Expecting ujobid=xxx, got: %s.\n"), ua->argk[i]);
-               return NULL;
+               goto bail_out;
             }
             if ((jcr=get_jcr_by_full_name(ua->argv[i]))) {
-               if (jcr->wstore) {
-                  bstrncpy(store_name, jcr->wstore->name(), sizeof(store_name));
+               STORE *wstore = jcr->store_mngr->get_wstore();
+               if (wstore) {
+                  bstrncpy(store_name, wstore->name(), sizeof(store_name));
+                  pm_strcpy(ustore->store_source, _("Command line (ujobid parameter)"));
                }
                free_jcr(jcr);
             }
@@ -1194,22 +1204,25 @@ STORE *get_storage_resource(UAContext *ua, bool use_default, bool unique)
    }
 
    if (store_name[0] != 0) {
-      store = (STORE *)GetResWithName(R_STORAGE, store_name);
-      if (!store && strcmp(store_name, "storage") != 0) {
+      ustore->store = (STORE *)GetResWithName(R_STORAGE, store_name);
+      if (!ustore->store && strcmp(store_name, "storage") != 0) {
          /* Looks that the first keyword of the line was not a storage name, make
           * sure that it's not "storage=" before we print the following message
           */
          ua->error_msg(_("Storage resource \"%s\": not found\n"), store_name);
       }
    }
-   if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
-      store = NULL;
+   if (ustore->store && !acl_access_ok(ua, Storage_ACL, ustore->store->name())) {
+      ustore->store = NULL;
    }
    /* No keywords found, so present a selection list */
-   if (!store) {
-      store = select_storage_resource(ua, unique);
+   if (!ustore->store) {
+      ustore->store = select_storage_resource(ua, unique);
+      pm_strcpy(ustore->store_source, _("User selection"));
    }
-   return store;
+
+bail_out:
+   return ustore->store != NULL;
 }
 
 /* Get drive that we are working with for this storage */
@@ -1545,7 +1558,7 @@ int scan_storage_cmd(UAContext *ua, const char *cmd,
                      uint32_t **results)  /* List of MediaId */
 {
    bool allpools=false, has_vol = false;;
-   STORE *store;
+   USTORE ustore;
 
    *nb = 0;
    *results = NULL;
@@ -1595,12 +1608,12 @@ int scan_storage_cmd(UAContext *ua, const char *cmd,
 
    if (storage) {
       /* Choose storage */
-      ua->jcr->wstore = store =  get_storage_resource(ua, false);
-      if (!store) {
+      if (!get_storage_resource(ua, &ustore, false)) {
          goto bail_out;
       }
-      bstrncpy(storage, store->dev_name(), MAX_NAME_LENGTH);
-      set_storageid_in_mr(store, mr);
+      ua->jcr->store_mngr->set_wstorage(ustore.store, ustore.store_source);
+      bstrncpy(storage, ustore.store->dev_name(), MAX_NAME_LENGTH);
+      set_storageid_in_mr(ustore.store, mr);
    }
 
    if (!open_db(ua)) {
@@ -1663,7 +1676,7 @@ bail_out:
    }
 
    close_db(ua);
-   ua->jcr->wstore = NULL;
+   ua->jcr->store_mngr->reset_wstorage();
    if (*results) {
       free(*results);
       *results = NULL;
index b13304c4951c265e956219fdebba22b58d35b898..eb257010a6f1dec4d365fef020033fea93f9a8d6 100644 (file)
@@ -52,7 +52,7 @@ static char DotStatusJob[] = "JobId=%s JobStatus=%c JobErrors=%d\n";
 
 bool dot_status_cmd(UAContext *ua, const char *cmd)
 {
-   STORE *store;
+   USTORE ustore;
    CLIENT *client;
    JCR* njcr = NULL;
    s_last_job* job;
@@ -105,12 +105,11 @@ bool dot_status_cmd(UAContext *ua, const char *cmd)
          do_client_status(ua, client, ua->argk[2]);
       }
    } else if (strcasecmp(ua->argk[1], "storage") == 0) {
-      store = get_storage_resource(ua, false /*no default*/, true/*unique*/);
-      if (!store) {
+      if (!get_storage_resource(ua, &ustore, false /*no default*/, true/*unique*/)) {
          ua->send_msg("1900 Bad .status command, wrong argument.\n");
          return false;
       }
-      do_storage_status(ua, store, ua->argk[2]);
+      do_storage_status(ua, ustore.store, ua->argk[2]);
    } else {
       ua->send_msg("1900 Bad .status command, wrong argument.\n");
       return false;
@@ -123,7 +122,7 @@ bool dot_status_cmd(UAContext *ua, const char *cmd)
 static int do_network_status(UAContext *ua)
 {
    CLIENT *client = NULL;
-   USTORE  store;
+   USTORE ustore;
    JCR *jcr = ua->jcr;
    char *store_address, ed1[50];
    uint32_t store_port;
@@ -147,17 +146,16 @@ static int do_network_status(UAContext *ua)
       return 1;
    }
 
-   store.store = get_storage_resource(ua, false, true);
-   if (!store.store) {
+   if (!get_storage_resource(ua, &ustore, false, true)) {
       return 1;
    }
 
    jcr->client = client;
-   set_wstorage(jcr, &store);
+   jcr->store_mngr->set_wstorage(ustore.store, NULL);
 
    if (!ua->api) {
       ua->send_msg(_("Connecting to Storage %s at %s:%d\n"),
-                   store.store->name(), store.store->address, store.store->SDport);
+                   ustore.store->name(), ustore.store->address, ustore.store->SDport);
    }
 
    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
@@ -165,7 +163,7 @@ static int do_network_status(UAContext *ua)
       goto bail_out;
    }
 
-   if (!start_storage_daemon_job(jcr, NULL, NULL)) {
+   if (!start_storage_daemon_job(jcr, NULL, NULL, true /* wait */ )) {
       goto bail_out;
    }
 
@@ -204,7 +202,7 @@ static int do_network_status(UAContext *ua)
          goto bail_out;
       }
 
-      store_address = store.store->address;  /* dummy */
+      store_address = ustore.store->address;  /* dummy */
       store_port = 0;                        /* flag that SD calls FD */
 
    } else {
@@ -213,21 +211,21 @@ static int do_network_status(UAContext *ua)
        *   then wait for File daemon to make connection
        *   with Storage daemon.
        */
-      if (store.store->SDDport == 0) {
-         store.store->SDDport = store.store->SDport;
+      if (ustore.store->SDDport == 0) {
+         ustore.store->SDDport = ustore.store->SDport;
       }
 
-      store_address = get_storage_address(jcr->client, store.store);
-      store_port = store.store->SDDport;
+      store_address = get_storage_address(jcr->client, ustore.store);
+      store_port = ustore.store->SDDport;
    }
 
-   if (!send_store_addr_to_fd(jcr, store.store, store_address, store_port)) {
+   if (!send_store_addr_to_fd(jcr, ustore.store, store_address, store_port)) {
       goto bail_out;
    }
 
    if (!ua->api) {
       ua->info_msg(_("Running network test between Client=%s and Storage=%s with %sB ...\n"),
-                   client->name(), store.store->name(), edit_uint64_with_suffix(nb, ed1));
+                   client->name(), ustore.store->name(), edit_uint64_with_suffix(nb, ed1));
    }
 
    if (!jcr->file_bsock->fsend("testnetwork bytes=%llu rtt=%llu\n", nb, nbrtt)) {
@@ -249,7 +247,7 @@ bail_out:
    free_bsock(jcr->store_bsock);
 
    jcr->client = NULL;
-   free_wstorage(jcr);
+   jcr->store_mngr->reset_wstorage();    /* we don't read so release */
    return 1;
 }
 
@@ -267,7 +265,7 @@ int qstatus_cmd(UAContext *ua, const char *cmd)
  */
 int status_cmd(UAContext *ua, const char *cmd)
 {
-   STORE *store;
+   USTORE ustore;
    CLIENT *client;
    int item, i;
 
@@ -295,12 +293,11 @@ int status_cmd(UAContext *ua, const char *cmd)
          }
          return 1;
       } else {
-         store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
-         if (store) {
+         if (get_storage_resource(ua, &ustore, false/*no default*/, true/*unique*/)) {
             if (find_arg(ua, NT_("slots")) > 0) {
-               status_slots(ua, store);
+               status_slots(ua, ustore.store);
             } else {
-               do_storage_status(ua, store, NULL);
+               do_storage_status(ua, ustore.store, NULL);
             }
          }
          return 1;
@@ -327,9 +324,9 @@ int status_cmd(UAContext *ua, const char *cmd)
          do_director_status(ua);
          break;
       case 1:
-         store = select_storage_resource(ua, true/*unique*/);
-         if (store) {
-            do_storage_status(ua, store, NULL);
+         ustore.store = select_storage_resource(ua, true/*unique*/);
+         if (ustore.store) {
+            do_storage_status(ua, ustore.store, NULL);
          }
          break;
       case 2:
@@ -546,7 +543,6 @@ static void do_director_status(UAContext *ua)
 static void do_storage_status(UAContext *ua, STORE *store, char *cmd)
 {
    BSOCK *sd;
-   USTORE lstore;
    int i;
 
    if (!acl_access_ok(ua, Storage_ACL, store->name())) {
@@ -563,9 +559,7 @@ static void do_storage_status(UAContext *ua, STORE *store, char *cmd)
       ua->error_msg(_("Restricted Client or Job does not permit access to  Storage daemons\n"));
       return;
    }
-   lstore.store = store;
-   pm_strcpy(lstore.store_source, _("unknown source"));
-   set_wstorage(ua->jcr, &lstore);
+   ua->jcr->store_mngr->set_wstorage(store, _("unknown source"));
    /* Try connecting for up to 15 seconds */
    if (!ua->api) ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
       store->name(), store->address, store->SDport);
@@ -725,8 +719,8 @@ static void prt_runtime(UAContext *ua, sched_pkt *sp, OutputWriter *ow)
       }
       if (ok) {
          mr.PoolId = jcr->jr.PoolId;
-         jcr->wstore = sp->store;
-         set_storageid_in_mr(jcr->wstore, &mr);
+         jcr->store_mngr->set_wstorage(sp->store, "PRT runtime");
+         set_storageid_in_mr(jcr->store_mngr->get_wstore(), &mr);
          Dmsg0(250, "call find_next_volume_for_append\n");
          /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
          ok = find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_no_prune, errmsg);
@@ -754,7 +748,7 @@ static void prt_runtime(UAContext *ua, sched_pkt *sp, OutputWriter *ow)
 
    } else if (ua->api > 1) {
       POOL  *p = jcr->pool;
-      STORE *s = jcr->wstore;
+      STORE *s = jcr->store_mngr->get_wstore();
       ua->send_msg("%s",
                    ow->get_output(OT_CLEAR,
                       OT_START_OBJ,
@@ -1256,6 +1250,8 @@ static void list_running_jobs(UAContext *ua)
          }
       }
       status = jcr->JobStatus;
+      STORE *rstore = jcr->store_mngr->get_rstore();
+      STORE *wstore = jcr->store_mngr->get_wstore();
       switch (status) {
       case JS_Created:
          msg = _("is waiting execution");
@@ -1302,10 +1298,10 @@ static void list_running_jobs(UAContext *ua)
          break;
       case JS_WaitSD:
          emsg = (char *) get_pool_memory(PM_FNAME);
-         if (jcr->wstore) {
-            Mmsg(emsg, _("is waiting on Storage \"%s\""), jcr->wstore->name());
-         } else if (jcr->rstore) {
-            Mmsg(emsg, _("is waiting on Storage \"%s\""), jcr->rstore->name());
+         if (wstore) {
+            Mmsg(emsg, _("is waiting on Storage \"%s\""), wstore->name());
+         } else if (rstore) {
+            Mmsg(emsg, _("is waiting on Storage \"%s\""), rstore->name());
          } else {
             Mmsg(emsg, _("is waiting on Storage"));
          }
@@ -1384,11 +1380,11 @@ static void list_running_jobs(UAContext *ua)
                emsg = (char *)get_pool_memory(PM_FNAME);
                pool_mem = true;
             }
-            if (!jcr->client || !jcr->wstore) {
+            if (!jcr->client || !wstore) {
                Mmsg(emsg, _("is waiting for Client to connect to Storage daemon"));
             } else {
                Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
-                    jcr->client->name(), jcr->wstore->name());
+                    jcr->client->name(), wstore->name());
             }
             msg = emsg;
          }
@@ -1437,8 +1433,8 @@ static void list_running_jobs(UAContext *ua)
       } else if (ua->api > 1) {
          CLIENT   *c = jcr->client;
          FILESET  *f = jcr->fileset;
-         STORE    *w = jcr->wstore;
-         STORE    *r = jcr->rstore;
+         STORE    *w = wstore;
+         STORE    *r = rstore;
          JOB      *j = jcr->job;
          ua->send_msg("%s", ow.get_output(OT_CLEAR,
                          OT_START_OBJ,
index 06fe83eab4d04f497af478f392394ca8f2c25618..1e0c0a30248d8d41658d19f8e4a7d5e5f70b8f9d 100644 (file)
@@ -65,7 +65,7 @@ bool do_vbackup_init(JCR *jcr)
    pm_strcpy(jcr->rpool_source, jcr->pool_source);
 
    /* If pool storage specified, use it for virtual full */
-   copy_rstorage(jcr, jcr->pool->storage, _("Pool resource"));
+   jcr->store_mngr->set_rstore(jcr->pool->storage, _("Pool resource"));
 
    Dmsg2(dbglevel, "Read pool=%s (From %s)\n", jcr->rpool->name(), jcr->rpool_source);
 
@@ -102,10 +102,10 @@ bool do_vbackup(JCR *jcr)
    db_list_ctx jobids;
    UAContext *ua;
 
-   Dmsg2(100, "rstorage=%p wstorage=%p\n", jcr->rstorage, jcr->wstorage);
+   Dmsg2(100, "rstorage=%p wstorage=%p\n", jcr->store_mngr->get_rstore_list(), jcr->store_mngr->get_wstore_list());
    Dmsg2(100, "Read store=%s, write store=%s\n",
-      ((STORE *)jcr->rstorage->first())->name(),
-      ((STORE *)jcr->wstorage->first())->name());
+      ((STORE *)jcr->store_mngr->get_rstore_list()->first())->name(),
+      ((STORE *)jcr->store_mngr->get_wstore_list()->first())->name());
 
    jcr->wasVirtualFull = true;        /* remember where we came from */
 
@@ -308,7 +308,7 @@ _("This Job is not an Accurate backup so is not equivalent to a Full backup.\n")
    /*
     * Now start a job with the Storage daemon
     */
-   if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage, /*send_bsr*/true)) {
+   if (!start_storage_daemon_job(jcr, jcr->store_mngr->get_rstore_list(), jcr->store_mngr->get_wstore_list(), /*send_bsr*/true)) {
       return false;
    }
    Dmsg0(100, "Storage daemon connection OK\n");
@@ -396,6 +396,7 @@ void vbackup_cleanup(JCR *jcr, int TermCode)
    double kbps, compression;
    utime_t RunTime;
    POOL_MEM query(PM_MESSAGE);
+   STORE *wstore = jcr->store_mngr->get_wstore();
 
    Dmsg2(100, "Enter vbackup_cleanup %d %c\n", TermCode, TermCode);
    memset(&cr, 0, sizeof(cr));
@@ -527,7 +528,7 @@ void vbackup_cleanup(JCR *jcr, int TermCode)
         jcr->fileset->name(), jcr->FSCreateTime,
         jcr->pool->name(), jcr->pool_source,
         jcr->catalog->name(), jcr->catalog_source,
-        jcr->wstore->name(), jcr->wstore_source,
+        wstore->name(), jcr->store_mngr->get_wsource(),
         schedt,
         sdt,
         edt,
index 287f584bafe2ea8578dfd9a8379aafdcb1734771..780426320e263e9338d2b4c91c8d6d122b2ad23a 100644 (file)
@@ -62,12 +62,11 @@ bool do_verify_init(JCR *jcr)
    case L_VERIFY_INIT:
    case L_VERIFY_CATALOG:
    case L_VERIFY_DISK_TO_CATALOG:
-      free_rstorage(jcr);
-      free_wstorage(jcr);
+      jcr->store_mngr->reset_rwstorage();
       break;
    case L_VERIFY_DATA:
    case L_VERIFY_VOLUME_TO_CATALOG:
-      free_wstorage(jcr);
+      jcr->store_mngr->reset_wstorage();
       break;
    default:
       Jmsg2(jcr, M_FATAL, 0, _("Unimplemented Verify level %d(%c)\n"), jcr->getJobLevel(),
@@ -95,8 +94,9 @@ bool do_verify(JCR *jcr)
    char *store_address;
    uint32_t store_port;
    const char *Name;
+   STORE *rstore = jcr->store_mngr->get_rstore();
 
-   free_wstorage(jcr);                   /* we don't write */
+   jcr->store_mngr->reset_wstorage();
 
    memset(&jcr->previous_jr, 0, sizeof(jcr->previous_jr));
 
@@ -258,7 +258,7 @@ bool do_verify(JCR *jcr)
       /*
        * Now start a job with the Storage daemon
        */
-      if (!start_storage_daemon_job(jcr, jcr->rstorage, NULL)) {
+      if (!start_storage_daemon_job(jcr, jcr->store_mngr->get_rstore_list(), NULL, true /* wait */)) {
          return false;
       }
       sd = jcr->store_bsock;
@@ -327,21 +327,21 @@ bool do_verify(JCR *jcr)
          if (!run_storage_and_start_message_thread(jcr, jcr->store_bsock)) {
             return false;
          }
-         store_address = jcr->rstore->address;  /* dummy */
+         store_address = rstore->address;  /* dummy */
          store_port = 0;           /* flag that SD calls FD */
       } else {
          /*
           * send Storage daemon address to the File daemon
           */
-         if (jcr->rstore->SDDport == 0) {
-            jcr->rstore->SDDport = jcr->rstore->SDport;
+         if (rstore->SDDport == 0) {
+            rstore->SDDport = rstore->SDport;
          }
 
-         store_address = get_storage_address(jcr->client, jcr->rstore);
-         store_port = jcr->rstore->SDDport;
+         store_address = get_storage_address(jcr->client, rstore);
+         store_port = rstore->SDDport;
       }
 
-      if (!send_store_addr_to_fd(jcr, jcr->rstore, store_address, store_port)) {
+      if (!send_store_addr_to_fd(jcr, rstore, store_address, store_port)) {
          goto bail_out;
       }
 
index 87f6757b921b6b54e9894aa762e95839c83aa6ff..af457ace95156cc76b087fd3133216a866a9c8c7 100644 (file)
@@ -337,10 +337,7 @@ public:
    JOB *job;                          /* Job resource */
    JOB *verify_job;                   /* Job resource of verify previous job */
    alist *plugin_config;              /* List of ConfigFile needed for restore */
-   alist *rstorage;                   /* Read storage possibilities */
-   STORE *rstore;                     /* Selected read storage */
-   alist *wstorage;                   /* Write storage possibilities */
-   STORE *wstore;                     /* Selected write storage */
+   StorageManager *store_mngr;
    CLIENT *client;                    /* Client resource */
    POOL *pool;                        /* Pool resource = write for migration */
    POOL *next_pool;                   /* Next pool override */
@@ -379,8 +376,6 @@ public:
    POOLMEM *pool_source;              /* Where pool came from */
    POOLMEM *next_pool_source;         /* Where next pool came from */
    POOLMEM *rpool_source;             /* Where migrate read pool came from */
-   POOLMEM *rstore_source;            /* Where read storage came from */
-   POOLMEM *wstore_source;            /* Where write storage came from */
    POOLMEM *catalog_source;           /* Where catalog came from */
    POOLMEM *next_vol_list;            /* Volumes previously requested */
    rblist  *bsr_list;                 /* Bootstrap that can be needed during restore */
index e8068cf74820c8a69aeb64cfc216c09273e53c82..79d272428d0d41a177bac1c461fcb8d560aabe72 100644 (file)
@@ -47,7 +47,7 @@ static int is_pool_ok(DCR *dcr);
 
 /* Requests from the Director daemon */
 static char use_storage[]  = "use storage=%127s media_type=%127s "
-    "pool_name=%127s pool_type=%127s append=%d copy=%d stripe=%d\n";
+    "pool_name=%127s pool_type=%127s append=%d copy=%d stripe=%d wait=%d\n";
 static char use_device[]  = "use device=%127s\n";
 
 /* Responses sent to Director daemon */
@@ -269,7 +269,7 @@ static bool use_device_cmd(JCR *jcr)
    BSOCK *dir = jcr->dir_bsock;
    int32_t append;
    bool ok;
-   int32_t Copy, Stripe;
+   int32_t Copy, Stripe, Wait;
    DIRSTORE *store;
    RCTX rctx;
    alist *dirstore;
@@ -286,7 +286,7 @@ static bool use_device_cmd(JCR *jcr)
       Dmsg1(dbglvl, "<dird: %s", dir->msg);
       ok = sscanf(dir->msg, use_storage, store_name.c_str(),
                   media_type.c_str(), pool_name.c_str(),
-                  pool_type.c_str(), &append, &Copy, &Stripe) == 7;
+                  pool_type.c_str(), &append, &Copy, &Stripe, &Wait) == 8;
       if (!ok) {
          break;
       }
@@ -394,6 +394,13 @@ static bool use_device_cmd(JCR *jcr)
          if (ok) {
             break;
          }
+         if (!Wait) {
+            /* Director does some kind of 'quick round' now, does not want to wait for device to become available.
+             * If none of the devices is ready now, director will probably do a second use_device call,
+             * this time with 'wait' set to 1 */
+            break;
+         }
+
          /* Keep reservations locked *except* during wait_for_device() */
          unlock_reservations();
          /*
@@ -421,12 +428,7 @@ static bool use_device_cmd(JCR *jcr)
           *  means nothing configured.  If a device is suitable but busy
           *  with another Volume, we will not come here.
           */
-         unbash_spaces(dir->msg);
-         pm_strcpy(jcr->errmsg, dir->msg);
-         Jmsg(jcr, M_FATAL, 0, _("Device reservation failed for JobId=%d: %s\n"),
-              jcr->JobId, jcr->errmsg);
          dir->fsend(NO_device, dev_name.c_str());
-
          Dmsg1(dbglvl, ">dird: %s", dir->msg);
       }
    } else {