From: Michal Rakowski Date: Tue, 27 Jul 2021 20:58:16 +0000 (+0200) Subject: Introduce 'FreeSpace' storage policy X-Git-Tag: Beta-15.0.0~901 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c3398eebbd864236a27b9b9618f6032c350d2ced;p=thirdparty%2Fbacula.git Introduce 'FreeSpace' storage policy This policy queries each SD in the list for its FreeSpace (as a sum of devices specified in the SD config) and sort the list by FreeSpace returned. --- diff --git a/bacula/src/dird/job.c b/bacula/src/dird/job.c index 160da65b3..274913d5f 100644 --- a/bacula/src/dird/job.c +++ b/bacula/src/dird/job.c @@ -1661,6 +1661,12 @@ static void init_store_manager(JCR *jcr, const char *policy) } else if (strcmp(policy, "ListedOrder") == 0) { Dmsg1(dbglvl_store_mngr, "Setting ListedOrder storage group policy for JobId: %d\n", jcr->JobId); jcr->store_mngr = New(ListedOrderStore()); + } else if (strcmp(policy, "FreeSpace") == 0) { + Dmsg1(dbglvl_store_mngr, "Setting FreeSpace storage group policy for JobId: %d\n", jcr->JobId); + jcr->store_mngr = New(FreeSpaceStore()); + } else { + Dmsg1(dbglvl_store_mngr, "Invalid policy for JobId: %d, setting default (ListedOrder)\n", jcr->JobId); + jcr->store_mngr = New(ListedOrderStore()); } } else { Dmsg1(dbglvl_store_mngr, "Setting ListedOrder storage group policy for JobId: %d\n", jcr->JobId); diff --git a/bacula/src/dird/store_mngr.c b/bacula/src/dird/store_mngr.c index 032e14824..141dd7845 100644 --- a/bacula/src/dird/store_mngr.c +++ b/bacula/src/dird/store_mngr.c @@ -20,6 +20,7 @@ #include "bacula.h" #include "dird.h" #include "lib/lockmgr.h" +#include "lib/store_mngr_cmds.h" static const int dbglvl = 200; @@ -331,6 +332,131 @@ static void swapit(uint32_t *v1, uint32_t *v2) *v2 = temp; } +void QueryStore::apply_policy(bool write_store) { + alist *list = write_store ? wstore.get_list() : rstore.get_list(); + STORE *store = NULL; + JCR *tmp_jcr = new_jcr(sizeof(JCR), dird_free_jcr); // temporary jcr needed for connecting to the SD + BSOCK *sd = tmp_jcr->store_bsock; + sm_ctx *context = NULL; + dlist *d_list = New(dlist(context, &context->link)); + + /* Init store mngr so that we can assing current storage to the jcr - needed for the connect call below */ + tmp_jcr->store_mngr = New(ListedOrderStore()); + + /* Query Storage daemon for needed information for each storage in the list */ + foreach_alist(store, list) { + if (write_store) { + tmp_jcr->store_mngr->set_wstorage(store, "qeuerystore_applypolicy"); + } else { + tmp_jcr->store_mngr->set_rstore(store, "qeuerystore_applypolicy"); + } + + if (!connect_to_storage_daemon(tmp_jcr, 1, 1, 0)) { + Dmsg1(dbglvl, "Failed to connect to the Storage: %s during policy quering\n", store->name()); + continue; + } + + sd = tmp_jcr->store_bsock; + + /* Init storage querying */ + if (!sd->fsend(store_query)) { + Dmsg2(dbglvl, "Failed to send \"%s\" command to the %s Storage\n", store_query, store->name()); + continue; + } + + sm_ctx *context = New(sm_ctx(store)); + + /* Execute the policy-specific querying */ + int ret = query(sd, d_list, context); + + sd->signal(BNET_TERMINATE); + sd->close(); + + if(!ret) { + Dmsg1(dbglvl, "Failed to query '\%s\' Storage\n", store->name()); + delete context; /* Free context since it was not added to the list */ + + /* Continue in case some more processing is needed - we don't want to work on + invalid data */ + continue; + } + } + + /* Let the policy reorder storage list according to the information gathered */ + reorder_list(list, d_list); + + if (d_list) { + delete d_list; + } + + if (tmp_jcr) { + free_jcr(tmp_jcr); + } +} + +void FreeSpaceStore::reorder_list(alist *list, dlist *d_list) { + sm_ctx *ctx; + STORE *store; + + list->destroy(); + list->init(10, not_owned_by_alist); + + Dmsg0(dbglvl, "Storage List after applying FreeSpace policy:\n"); + foreach_dlist(ctx, d_list) { + store = ctx->store; + list->append(store); + Dmsg2(dbglvl, "*** store: %s size: %llu\n", ctx->store->name(), ctx->number); + } +} + +bool FreeSpaceStore::query(BSOCK *sd, dlist *d_list, sm_ctx *context) { + STORE *store = context->store; + bool ret = false; + POOL_MEM device_name; + DEVICE *dev; + uint64_t fs; + uint64_t *size = (uint64_t *) &context->number; + + if (sd->recv() >= 0) { + Dmsg1(dbglvl, "msg); + if (strncmp(sd->msg, OK_store_query, strlen(OK_store_query)) != 0) { + Dmsg2(dbglvl, "Failed to query storage: %s msg=%s\n", store->name(), sd->msg); + goto bail_out; + } + } else { + Dmsg1(dbglvl, "Failed to query storage: %s\n", store->name()); + goto bail_out; + } + + /* Now go through the list of devices for the storage resource and + * query each for available storage space */ + foreach_alist(dev, store->device) { + pm_strcpy(device_name, dev->name()); + bash_spaces(device_name.c_str()); + sd->fsend(store_query_freespace, device_name.c_str()); + } + + sd->signal(BNET_EOD); + + if (sd->recv() >= 0) { + ret = sscanf(sd->msg, OK_store_size, &fs) == 1; + if (!ret) { + Dmsg1(dbglvl, "Failed to get size for storage: %s\n", store->name()); + goto bail_out; + } + + *size = fs; + } + + // Insert store size to the list + d_list->binary_insert_multiple(context, &FreeSpaceStore::cmp); + + ret = true; + +bail_out: + return ret; +} + 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); diff --git a/bacula/src/dird/store_mngr.h b/bacula/src/dird/store_mngr.h index 5f7c1a230..2b93894fc 100644 --- a/bacula/src/dird/store_mngr.h +++ b/bacula/src/dird/store_mngr.h @@ -29,7 +29,6 @@ /* Forward delcaration */ class STORE; - /* * 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 @@ -246,4 +245,87 @@ class ListedOrderStore : public StorageManager { } }; +/* Context with per-policy specific data (as of now there's only single uint64_t value available for each policy)*/ +class sm_ctx : public SMARTALLOC { + public: + STORE *store; + dlink link; + uint64_t number; /* Per-policy specific number (e.g. free space, cpu usage...) */ + + sm_ctx(STORE *s) { + store = s; + number = 0; + } +}; + +/* + * Abstract class for policies which require DIR<->SD querying. + * Each querying policy has to implement query() and reorder() methods - theese act + * as a callbacks for QueryStore's generic methods. + */ +class QueryStore : public StorageManager { + protected: + /* Policy-specific query method */ + virtual bool query(BSOCK *sd, dlist *d_list, sm_ctx *context) = 0; + + /* Reorder storage list after querying all storages in the list for needed information */ + virtual void reorder_list(alist *list, dlist *d_list) = 0; + + public: + void apply_policy(bool write_store); + + QueryStore (const char *policy="VirtualPolicy_QueryStore"): StorageManager(policy) { + } + + ~QueryStore() { + } +}; + +/* + * Policy which reorders storage list based on FreeSpace available on each Storage + */ +class FreeSpaceStore : public QueryStore { + private: + /* Private helper struct */ + struct store_free_size { + STORE *store; + uint64_t free_size; + dlink link; + }; + + bool query(BSOCK *sd, dlist *d_list, sm_ctx *context); + + /* Comparator for easy list ordering */ + static int cmp(void *item1, void *item2) { + sm_ctx *ctx1 = (sm_ctx *) item1; + sm_ctx *ctx2 = (sm_ctx *) item2; + uint64_t s1 = ctx1->number; + uint64_t s2 = ctx2->number; + + if (s1 < s2) { + return 1; + } else if (s1 > s2) { + return -1; + } else { + return 0; + } + } + + void reorder_list(alist *list, dlist *d_list); + + public: + void apply_write_policy() { + return apply_policy(true); + } + void apply_read_policy() { + return apply_policy(false); + } + + FreeSpaceStore(): QueryStore("FreeSpace") { + } + + ~FreeSpaceStore() { + } +}; + #endif // STORE_MNGR_H diff --git a/bacula/src/lib/parse_conf.c b/bacula/src/lib/parse_conf.c index b878657c6..17ef721ad 100644 --- a/bacula/src/lib/parse_conf.c +++ b/bacula/src/lib/parse_conf.c @@ -1017,6 +1017,7 @@ void store_coll_type(LEX *lc, RES_ITEM *item, int index, int pass) static char const *storage_mngmt_policy[] = { "LeastUsed", "ListedOrder", + "FreeSpace", NULL }; diff --git a/bacula/src/lib/store_mngr_cmds.h b/bacula/src/lib/store_mngr_cmds.h new file mode 100644 index 000000000..083a35c31 --- /dev/null +++ b/bacula/src/lib/store_mngr_cmds.h @@ -0,0 +1,32 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2023 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. +*/ +/* + * Commands defined for the Storage Manager, for communication between the DIR and SD. + */ + +#ifndef STORE_MNGR_CMDS_H +#define STORE_MNGR_CMDS_H 1 + +static const char store_query[] = "store_mngr query\n"; +static const char store_query_freespace[] = "store_mngr freespace device=%s\n"; +static char OK_store_query[] = "3000 OK query store\n"; +static char store_query_unsupp_policy[] = "3200 unsupported policy\n"; +static char OK_store_size[] = "3000 OK size=%llu\n"; + +#endif // STORE_MNGR_CMDS_H diff --git a/bacula/src/stored/Makefile.in b/bacula/src/stored/Makefile.in index 13cc5bdea..064f607f6 100644 --- a/bacula/src/stored/Makefile.in +++ b/bacula/src/stored/Makefile.in @@ -49,7 +49,7 @@ SDCORE_OBJS = \ # bacula-sd SDOBJS = \ - stored.o append.o authenticate.o dircmd.o fd_cmds.o job.o \ + stored.o append.o authenticate.o store_mngr.o dircmd.o fd_cmds.o job.o \ hello.o status.o vbackup.o \ sdcollect.o $(SDCORE_OBJS) $(EXTRA_STORED_OBJS) diff --git a/bacula/src/stored/dircmd.c b/bacula/src/stored/dircmd.c index 46b6f121d..6bb2a89d2 100644 --- a/bacula/src/stored/dircmd.c +++ b/bacula/src/stored/dircmd.c @@ -62,6 +62,7 @@ extern bool run_cmd(JCR *jcr); extern bool status_cmd(JCR *sjcr); extern bool qstatus_cmd(JCR *jcr); extern bool collect_cmd(JCR *jcr); +extern bool query_store(JCR *jcr); //extern bool query_cmd(JCR *jcr); /* Forward referenced functions */ @@ -146,6 +147,7 @@ static struct dir_cmds cmds[] = { #endif {"run", run_cmd, 0}, {"statistics", collect_cmd, 0}, + {"store_mngr", query_store, 0}, // {"query", query_cmd, 0}, {NULL, NULL} /* list terminator */ }; diff --git a/bacula/src/stored/store_mngr.c b/bacula/src/stored/store_mngr.c new file mode 100644 index 000000000..27998972b --- /dev/null +++ b/bacula/src/stored/store_mngr.c @@ -0,0 +1,141 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2023 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 daemon's part of Storage Manager - responsible for retrieving needed + * information in response to Director's query. + */ + +#include "bacula.h" +#include "stored.h" +#include "store_mngr.h" +#include "lib/store_mngr_cmds.h" + +static const int dbglvl = 200; + +/* Helper Compare method for the FreeSpace policy. + * It's purpose is to have list with unique devices. */ +static int query_size_cmp(void *item1, void *item2) { + struct query_size *qs1 = (struct query_size *) item1; + struct query_size *qs2 = (struct query_size *) item2; + + /* We only want to avoid duplicates, order does not matter at all */ + if (qs1->devno == qs2->devno) { + return 0; + } + + return 1; +} + +static bool query_freespace(JCR *jcr, const char *dev_name) { + BSOCK *dir = jcr->dir_bsock; + DEVRES *device; + AUTOCHANGER *changer; + uint64_t ts, fs, free_sum = 0; + struct query_size *qs; + alist dev_list(10, not_owned_by_alist); + dlist size_list; + bool found; + + /* Device sent can be either single device or an autochanger */ + foreach_res(changer, R_AUTOCHANGER) { + if (strcmp(changer->hdr.name, dev_name) == 0) { + /* For now we sum free size of all autochanger's unique devices and report it as a single value + * back to the DIR in response to the FreeSpace policy querying. + * We may want to change it at some point if we see that some other calculations/reporting make more sense */ + foreach_alist(device, changer->device) { + /* devno == 0 means that it's just an different directory of a device we already have */ + if (device && device->dev->devno) { + device->dev->get_freespace(&fs, &ts); + /* Alloc helper struct, set correct size for this dev and add it to list + * so that later we can sum size of all unique devices */ + qs = (struct query_size *) malloc(sizeof(struct query_size)); + if (!qs) { + Dmsg1(dbglvl, "Failed to alloc query_size for dev: %s, policy querying stopped\n", device->device_name); + break; + } else { + qs->size = fs; + qs->devno = device->dev->devno; + dev_list.append(qs); + } + } + } + + found = true; /* No need to go through the device resource list now */ + break; + } + } + + if (!found) { + /* Device sent to query is a single device instead of an autochanger */ + foreach_res(device, R_DEVICE) { + if (strcmp(device->hdr.name, dev_name) == 0) { + device->dev->get_freespace(&fs, &ts); + qs = (struct query_size *) malloc(sizeof(struct query_size)); + if (!qs) { + Dmsg1(dbglvl, "Failed to alloc query_size for dev: %s, policy querying stopped\n", device->device_name); + break; + } else { + qs->size = fs; + qs->devno = device->dev->devno; + } + dev_list.append(qs); + found = true; + break; + } + } + } + + /* Add free size for each unique device */ + foreach_alist(qs, &dev_list) { + struct query_size *tmp = (struct query_size *) size_list.binary_insert(qs, query_size_cmp); + if (tmp) { + free_sum+=tmp->size; + } + } + + dir->fsend(OK_store_size, free_sum); + + return found; +} + +/* Handler for DIR's 'store_mngr query' command */ +bool query_store(JCR *jcr) { + BSOCK *dir = jcr->dir_bsock; + POOL_MEM dev_name; + bool ok = false; + + if (strncmp(dir->msg, store_query, strlen(store_query)) != 0) { + return false; + } + + dir->fsend(OK_store_query); + + /* Return parameters asked by the DIR */ + if (dir->recv() >= 0) { + if (sscanf(dir->msg, store_query_freespace, dev_name.c_str()) == 1) { + unbash_spaces(dev_name.c_str()); + + ok = query_freespace(jcr, dev_name.c_str()); + } else { + dir->fsend(store_query_unsupp_policy); + } + } + + return ok; +} diff --git a/bacula/src/stored/store_mngr.h b/bacula/src/stored/store_mngr.h new file mode 100644 index 000000000..67c4daac0 --- /dev/null +++ b/bacula/src/stored/store_mngr.h @@ -0,0 +1,40 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2023 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 Daemon's part of Storage Manager - responsible for retrieving needed + * information in response to Director's query. + */ + +#ifndef STORE_MNGR_H +#define STORE_MNGR_H 1 + +#include "bacula.h" + + +/* FreeSpace policy struct to easily get rid of device duplicates in the list */ +struct query_size { + struct dlink link; + uint64_t devno; /* Unique device number */ + uint64_t size; /* Device's freespace in bytes */ +}; + +/* Main FreeSpace policy handler */ +bool query_store(JCR *jcr); + +#endif // STORE_MNGR_H diff --git a/bacula/src/stored/stored.h b/bacula/src/stored/stored.h index ceb021444..dde26f7fd 100644 --- a/bacula/src/stored/stored.h +++ b/bacula/src/stored/stored.h @@ -95,6 +95,7 @@ const int sd_dbglvl = 300; #include "vtape_dev.h" #include "cloud_dev.h" #include "aligned_dev.h" +#include "store_mngr.h" #ifdef SD_DEDUP_SUPPORT #include "dedup_dev.h" #endif