From: Eric Bollengier Date: Fri, 24 Apr 2020 16:01:19 +0000 (+0200) Subject: BEE Backport bacula/src/dird/ua_cmds.c X-Git-Tag: Release-11.3.2~1744 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a035967f03372d6d9dbc67a2d9852252cf1e43d7;p=thirdparty%2Fbacula.git BEE Backport bacula/src/dird/ua_cmds.c This commit is the result of the squash of the following main commits: Author: Eric Bollengier Date: Fri Apr 24 10:27:09 2020 +0200 Add update jobid=x reviewed=y command Author: Eric Bollengier Date: Wed Mar 4 15:41:15 2020 +0100 Fix compilation of community version + move dedup configuration functions to external file Author: Eric Bollengier Date: Wed Jan 8 17:08:18 2020 +0100 Fix #5833 Modify 'status schedule' to display only jobs starting after the 'time' parameter if set Author: Eric Bollengier Date: Mon Jan 6 10:29:11 2020 +0100 Fix #5834 Do not prompt for the job status when jobid is provided in the resume command Author: Eric Bollengier Date: Mon Jul 8 18:19:00 2019 +0200 Add RTT estimation to the 'status network' command Author: Eric Bollengier Date: Tue Jun 18 15:12:54 2019 +0200 Fix #5173 about incorrect behavior of the "delete pool" command Author: Eric Bollengier Date: Wed May 1 15:24:21 2019 +0200 Accept |script in the Client Address directive Author: Eric Bollengier Date: Mon Jan 7 09:50:23 2019 +0100 cloud: Fix #4457 about an harmless message "Catalog VolCloudParts mismatch" with the cloud list command Author: Eric Bollengier Date: Wed Oct 31 10:26:57 2018 +0100 Fix #3574 Add "clients" option to the "help list" output Author: Eric Bollengier Date: Fri Oct 19 16:33:13 2018 +0200 Make "scan" keyword in update help command optional Author: Radosław Korzeniewski Date: Fri Sep 7 11:20:03 2018 +0200 Change 'Collector' resource name into 'Statistics'. The patch introduce a Statistics resource in place of Collector one used before. It changes the 'collect' command into 'statistics' one and '.status collector' into '.status statistics'. No other functionalities were changed. Author: Eric Bollengier Date: Fri Jul 13 14:00:14 2018 +0200 Fix GCC 8 compiler warnings with memset() on objects Author: Radosław Korzeniewski Date: Fri May 25 14:25:00 2018 +0200 Add two new collect command output. Now the collect support a three output formats: - simple - as $metric=$value - full - with all metric attributes in single line - json - with all metric attributes as JSON array of metric objects Author: Eric Bollengier Date: Wed May 16 16:38:30 2018 +0200 Fix #3824 about incorrect setdebug command description Author: Norbert Bizet Date: Mon May 22 16:36:40 2017 +0200 cloud: Add "Manual" cloud upload option and add truncate option to the upload command. Author: Eric Bollengier Date: Thu May 11 09:57:26 2017 +0200 Fix #2822 about list jobs order=xxx description in the help command Author: Kern Sibbald Date: Sun Feb 2 18:47:08 2014 +0100 Removed Dir Python module --- diff --git a/bacula/src/dird/ua_cmds.c b/bacula/src/dird/ua_cmds.c index f181f2e0cf..ffb69ec953 100644 --- a/bacula/src/dird/ua_cmds.c +++ b/bacula/src/dird/ua_cmds.c @@ -17,9 +17,11 @@ Bacula(R) is a registered trademark of Kern Sibbald. */ /* + * * Bacula Director -- User Agent Commands * * Kern Sibbald, September MM + * */ #include "bacula.h" @@ -96,6 +98,13 @@ struct cmdstruct { const char *usage; /* all arguments to build usage */ const bool use_in_rs; /* Can use it in Console RunScript */ }; + +#if BEEF +# include "bee_dird_dde.h" +#else +# define DEDUP_KW +#endif + static struct cmdstruct commands[] = { /* Can use it in Console RunScript*/ { NT_("add"), add_cmd, _("Add media to a pool"), NT_("pool= storage= jobid="), false}, { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"), false}, @@ -114,24 +123,24 @@ static struct cmdstruct commands[] = { /* C { NT_("exit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false}, { NT_("gui"), gui_cmd, _("Non-interactive gui mode"), NT_("on | off"), false}, { NT_("help"), help_cmd, _("Print help on specific command"), - NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist" - "\n\tmessages memory mount prune purge quit query\n\trestore relabel release reload run status statistics" + NT_("add autodisplay automount cancel collect create delete disable\n\tenable estimate exit gui label list llist" + "\n\tmessages memory mount prune purge quit query\n\trestore relabel release reload run status" "\n\tsetbandwidth setdebug setip show sqlquery time trace unmount\n\tumount update use var version wait" - "\n\tsnapshot"), false}, + "\n\tsnapshot cloud " DEDUP_KW), false}, { NT_("label"), label_cmd, _("Label a tape"), NT_("storage= volume= pool= slot= drive= barcodes"), false}, { NT_("list"), list_cmd, _("List objects from catalog"), - NT_("jobs [client=] [jobid=] [ujobid=] [job=] [joberrors] [jobstatus=] [level=] [jobtype=] [limit=]|\n" + NT_("jobs [client=] [jobid=] [ujobid=] [job=] [joberrors] [jobstatus=] [level=] [jobtype=] [limit=] [order=]|\n" "\tjobtotals | pools | volume | media | files [type=] jobid= | copies jobid= |\n" "\tjoblog jobid= | pluginrestoreconf jobid= restoreobjectid= | snapshot | \n" - "\tfileindex= | clients\n" + "\tfilemedia jobid= fileindex= | clients\n" ), false}, { NT_("llist"), llist_cmd, _("Full or long list like list command"), NT_("jobs [client=] [jobid=] [ujobid=] [job=] [joberrors] [jobstatus=] [level=] [jobtype=] [order=] [limit=]|\n" "\tjobtotals | pools | volume | media | files jobid= | copies jobid= |\n" "\tjoblog jobid= | pluginrestoreconf jobid= restoreobjectid= | snapshot |\n" - "\tjobid= fileindex= | clients\n"), false}, + "\tfilemedia jobid= fileindex= | clients\n"), false}, { NT_("messages"), messagescmd, _("Display pending messages"), NT_(""), false}, { NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true}, @@ -146,6 +155,9 @@ static struct cmdstruct commands[] = { /* C { NT_("purge"), purge_cmd, _("Purge records from catalog"), NT_("files jobs volume= [mediatype= pool= allpools storage= drive=]"), true}, { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false}, { NT_("query"), query_cmd, _("Query catalog"), NT_("[]"), false}, +#if BEEF + { NT_(DEDUP_KW), dedupcmd, _("Manage Global Deduplication Engine"), NT_(DEDUP_USAGE), true}, +#endif { NT_("restore"), restore_cmd, _("Restore files"), NT_("where= client= storage= bootstrap= " "restorejob= restoreclient= noautoparent" @@ -166,18 +178,18 @@ static struct cmdstruct commands[] = { /* C "when=\n\tcomment= spooldata= jobid="), false}, { NT_("resume"), restart_cmd, _("Resume a job"), - NT_("incomplete job= client=\n\tfileset= level=\n\tstorage=" + NT_("[incomplete|canceled|failed] job= client=\n\tfileset= level=\n\tstorage=" "when=\n\tcomment= spooldata= jobid="), false}, { NT_("status"), status_cmd, _("Report status"), - NT_("all | network [bytes=] | dir= | director | client= |\n" + NT_("all | network [bytes= rtt=] | dir= | director | client= |\n" "\tstorage= slots |\n" "\tschedule [job=] [client=] [schedule=] [days=] [limit=]\n" - "\t\t[time=]"), true}, + "\t\t[time] [time=]"), true}, { NT_("stop"), cancel_cmd, _("Stop a job"), NT_("jobid= job= ujobid= all"), false}, { NT_("setdebug"), setdebug_cmd, _("Sets debug level"), - NT_("level= tags= trace=0/1 options=<0tTc> tags= | client= | dir | storage= | all"), true}, + NT_("level= trace=0/1 options=<0tTc> tags= | client= | dir | storage= | all"), true}, { NT_("setbandwidth"), setbwlimit_cmd, _("Sets bandwidth"), NT_("limit= client= jobid= job= ujobid="), true}, @@ -201,13 +213,15 @@ static struct cmdstruct commands[] = { /* C { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"), NT_("storage= [ drive= ] [ device= ]| jobid= | job="), false}, - { NT_("update"), update_cmd, _("Update volume, pool or stats"), - NT_("stats\n\tsnapshot\n\tpool=\n\tslots storage= scan" - "\n\tvolume= volstatus= volretention= cacheretention=" + { NT_("update"), update_cmd, _("Update volume, pool, job or stats"), + NT_("stats\n\tsnapshot\n\tpool=\n\tslots storage= [scan]" + "\nvolume= volstatus= volretention= cacheretention=" "\n\t pool= recycle= slot=\n\t inchanger=" "\n\t maxvolbytes= maxvolfiles= maxvoljobs=" "\n\t enabled= recyclepool= actiononpurge=" - "\n\t allfrompool= fromallpools frompool"),true}, + "\n\t allfrompool= fromallpools frompool" + "\njobid= [client= | starttime= | priority= |" + "\n\t pool= | comment= | reviewed="),true}, { NT_("use"), use_cmd, _("Use catalog xxx"), NT_("catalog="), false}, { NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false}, { NT_("version"), version_cmd, _("Print Director version"), NT_(""), true}, @@ -314,7 +328,7 @@ static int add_cmd(UAContext *ua, const char *cmd) return 1; } - memset(&pr, 0, sizeof(pr)); + bmemset(&pr, 0, sizeof(pr)); if (!get_pool_dbr(ua, &pr)) { return 1; @@ -533,9 +547,11 @@ void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op) pr->MaxVolJobs = pool->MaxVolJobs; pr->MaxVolFiles = pool->MaxVolFiles; pr->MaxVolBytes = pool->MaxVolBytes; + pr->MaxPoolBytes = pool->MaxPoolBytes; pr->AutoPrune = pool->AutoPrune; pr->ActionOnPurge = pool->action_on_purge; pr->Recycle = pool->Recycle; + pr->MaxPoolBytes = pool->MaxPoolBytes; if (pool->label_format) { bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat)); } else { @@ -559,7 +575,7 @@ int update_pool_references(JCR *jcr, BDB *db, POOL *pool) return 1; } - memset(&pr, 0, sizeof(POOL_DBR)); + bmemset(&pr, 0, sizeof(POOL_DBR)); bstrncpy(pr.Name, pool->name(), sizeof(pr.Name)); /* Don't compute NumVols here */ @@ -589,7 +605,7 @@ bool set_pooldbr_references(JCR *jcr, BDB *db, POOL_DBR *pr, POOL *pool) bool ret = true; if (pool->RecyclePool) { - memset(&rpool, 0, sizeof(POOL_DBR)); + bmemset(&rpool, 0, sizeof(POOL_DBR)); bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name)); if (db_get_pool_record(jcr, db, &rpool)) { @@ -607,7 +623,7 @@ bool set_pooldbr_references(JCR *jcr, BDB *db, POOL_DBR *pr, POOL *pool) } if (pool->ScratchPool) { - memset(&rpool, 0, sizeof(POOL_DBR)); + bmemset(&rpool, 0, sizeof(POOL_DBR)); bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name)); if (db_get_pool_record(jcr, db, &rpool)) { @@ -638,7 +654,7 @@ bool set_pooldbr_references(JCR *jcr, BDB *db, POOL_DBR *pr, POOL *pool) int create_pool(JCR *jcr, BDB *db, POOL *pool, e_pool_op op) { POOL_DBR pr; - memset(&pr, 0, sizeof(POOL_DBR)); + bmemset(&pr, 0, sizeof(POOL_DBR)); bstrncpy(pr.Name, pool->name(), sizeof(pr.Name)); if (db_get_pool_record(jcr, db, &pr)) { @@ -716,7 +732,7 @@ static int setbwlimit_client(UAContext *ua, CLIENT *client, char *Job, int64_t l /* Try to connect for 15 seconds */ ua->send_msg(_("Connecting to Client %s at %s:%d\n"), - client->name(), client->address(buf.addr()), client->FDport); + client->name(), get_client_address(ua->jcr, client, buf.addr()), client->FDport); if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) { ua->error_msg(_("Failed to connect to Client.\n")); goto bail_out; @@ -825,7 +841,7 @@ static int setip_cmd(UAContext *ua, const char *cmd) sizeof(ua->UA_sock->client_addr), addr, sizeof(addr)); client->setAddress(addr); ua->send_msg(_("Client \"%s\" address set to %s\n"), - client->name(), addr); + client->name(), addr); get_out: UnlockRes(); return 1; @@ -1006,7 +1022,7 @@ static void do_client_setdebug(UAContext *ua, CLIENT *client, ua->jcr->client = client; /* Try to connect for 15 seconds */ ua->send_msg(_("Connecting to Client %s at %s:%d\n"), - client->name(), client->address(buf.addr()), client->FDport); + client->name(), get_client_address(ua->jcr, client, buf.addr()), client->FDport); if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) { ua->error_msg(_("Failed to connect to Client.\n")); @@ -1459,7 +1475,7 @@ static int estimate_cmd(UAContext *ua, const char *cmd) get_level_since_time(ua->jcr, since, sizeof(since)); ua->send_msg(_("Connecting to Client %s at %s:%d\n"), - jcr->client->name(), jcr->client->address(buf.addr()), jcr->client->FDport); + jcr->client->name(), get_client_address(jcr, jcr->client, buf.addr()), jcr->client->FDport); if (!connect_to_file_daemon(jcr, 1, 15, 0)) { ua->error_msg(_("Failed to connect to Client.\n")); return 1; @@ -1659,27 +1675,24 @@ static void do_job_delete(UAContext *ua, JobId_t JobId) ua->send_msg(_("JobId=%s and associated records deleted from the catalog.\n"), ed1); } + /* * Delete media records from database -- dangerous */ -static int delete_volume(UAContext *ua) +static int delete_a_volume(UAContext *ua, MEDIA_DBR *mr) { - MEDIA_DBR mr; char buf[1000]; db_list_ctx lst; - if (!select_media_dbr(ua, &mr)) { - return 1; - } ua->warning_msg(_("\nThis command will delete volume %s\n" "and all Jobs saved on that volume from the Catalog\n"), - mr.VolumeName); + mr->VolumeName); if (find_arg(ua, "yes") >= 0) { ua->pint32_val = 1; /* Have "yes" on command line already" */ } else { bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "), - mr.VolumeName); + mr->VolumeName); if (!get_yesno(ua, buf)) { return 1; } @@ -1689,8 +1702,8 @@ static int delete_volume(UAContext *ua) } /* If not purged, do it */ - if (strcmp(mr.VolStatus, "Purged") != 0) { - if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) { + if (strcmp(mr->VolStatus, "Purged") != 0) { + if (!db_get_volume_jobids(ua->jcr, ua->db, mr, &lst)) { ua->error_msg(_("Can't list jobs on this volume\n")); return 1; } @@ -1699,10 +1712,22 @@ static int delete_volume(UAContext *ua) } } - db_delete_media_record(ua->jcr, ua->db, &mr); + db_delete_media_record(ua->jcr, ua->db, mr); return 1; } +/* + * Delete media records from database -- dangerous + */ +static int delete_volume(UAContext *ua) +{ + MEDIA_DBR mr; + if (!select_media_dbr(ua, &mr)) { + return 1; + } + return delete_a_volume(ua, &mr); +} + /* * Delete a pool record from the database -- dangerous * TODO: Check if the resource is still defined? @@ -1710,20 +1735,73 @@ static int delete_volume(UAContext *ua) static int delete_pool(UAContext *ua) { POOL_DBR pr; + POOL *pool; char buf[200]; - - memset(&pr, 0, sizeof(pr)); + bmemset(&pr, 0, sizeof(pr)); if (!get_pool_dbr(ua, &pr)) { return 1; } - bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "), - pr.Name); + + pool = (POOL*) GetResWithName(R_POOL, pr.Name); + + if (pool) { + ua->error_msg(_("Unable to delete Pool \"%s\", the resource is still defined in the configuration.\n"), pr.Name); + return 1; + } + + if (pr.NumVols > 0) { + bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\" and delete %d volume(s)? (yes/no): "), + pr.Name, pr.NumVols); + } else { + bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "), + pr.Name); + } if (!get_yesno(ua, buf)) { return 1; } if (ua->pint32_val) { - db_delete_pool_record(ua->jcr, ua->db, &pr); + if (pr.NumVols > 0) { + int num_ids=0; + uint32_t *ids=NULL; + MEDIA_DBR mr, mr2; /* Automatically memset() */ + mr.PoolId = pr.PoolId; + mr.Enabled=-1; + mr.Recycle=-1; + /* Find each volumes from the pool and purge it */ + if (!db_get_media_ids(ua->jcr, ua->jcr->db, &mr, &num_ids, &ids)) { + ua->error_msg(_("Unable to list volumes that belong to Pool \"%s\"\n"), pr.Name); + return 1; + } + for (int i = 0 ; i < num_ids ; i++) { + mr2.clear(); + mr2.MediaId = ids[i]; + if (db_get_media_record(ua->jcr, ua->jcr->db, &mr2)) { + delete_a_volume(ua, &mr2); + + } else { + ua->error_msg(_("Unable to get catalog record for volume \"MediaId=%ld\" in the Pool \"%s\"\n"), + mr2.MediaId, pr.Name); + if (ids) { + free(ids); + } + return 1; + } + } + if (ids) { + free(ids); + } + } + if (!get_pool_dbr(ua, &pr)) { + return 1; + } + if (pr.NumVols == 0) { + db_delete_pool_record(ua->jcr, ua->db, &pr); + } else { + ua->error_msg(_("Unable to delete Pool catalog record \"%s\". Pool still have %d volume(s).\n"), + pr.Name, pr.NumVols); + return 1; + } } return 1; } @@ -1738,7 +1816,7 @@ static int delete_client(UAContext *ua) char buf[200]; db_list_ctx lst; - memset(&cr, 0, sizeof(cr)); + bmemset(&cr, 0, sizeof(cr)); if (!get_client_dbr(ua, &cr, 0)) { return 1; @@ -1812,8 +1890,8 @@ static void do_storage_cmd(UAContext *ua, const char *command) drive = get_storage_drive(ua, store.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) { + strcasecmp(command, "enable") == 0 || + strcasecmp(command, "unmount") == 0) { slot = 0; } else { slot = get_storage_slot(ua, store.store); @@ -1892,8 +1970,9 @@ int cloud_volumes_cmd(UAContext *ua, const char *cmd, const char *mode) POOL_DBR pr; BSOCK *sd = NULL; char storage[MAX_NAME_LENGTH]; + char truncate_option[MAX_NAME_LENGTH]; const char *action = mode; - memset(&pr, 0, sizeof(pr)); + bmemset(&pr, 0, sizeof(pr)); /* * Look for all volumes that are enabled and @@ -1913,6 +1992,10 @@ int cloud_volumes_cmd(UAContext *ua, const char *cmd, const char *mode) goto bail_out; } + if (!scan_truncate_cmd(ua, cmd, truncate_option)) { + goto bail_out; + } + if ((sd=open_sd_bsock(ua)) == NULL) { Dmsg0(100, "Can't open connection to sd\n"); goto bail_out; @@ -1934,16 +2017,18 @@ int cloud_volumes_cmd(UAContext *ua, const char *cmd, const char *mode) bash_spaces(mr.MediaType); bash_spaces(pr.Name); bash_spaces(storage); + bash_spaces(truncate_option); sd->fsend("%s Storage=%s Volume=%s PoolName=%s MediaType=%s " - "Slot=%d drive=%d CacheRetention=%lld\n", + "Slot=%d drive=%d truncate=%s CacheRetention=%lld\n", action, storage, mr.VolumeName, pr.Name, mr.MediaType, - mr.Slot, drive, mr.CacheRetention); + mr.Slot, drive, truncate_option, mr.CacheRetention); unbash_spaces(mr.VolumeName); unbash_spaces(mr.MediaType); unbash_spaces(pr.Name); unbash_spaces(storage); + unbash_spaces(truncate_option); /* Check for valid response */ while (bget_dirmsg(sd) >= 0) { @@ -1994,8 +2079,8 @@ static int cloud_list_cmd(UAContext *ua, const char *cmd) bool first=true; uint32_t maxpart=0, part; uint64_t maxpart_size=0; - memset(&pr, 0, sizeof(pr)); - memset(&mr, 0, sizeof(mr)); + bmemset(&pr, 0, sizeof(pr)); + bmemset(&mr, 0, sizeof(mr)); /* Look at arguments */ for (int i=1; iargc; i++) { @@ -2070,9 +2155,9 @@ static int cloud_list_cmd(UAContext *ua, const char *cmd) pm_strcpy(errmsg, tmpmsg.c_str()); } if (mr.VolCloudParts != maxpart) { - Mmsg(tmpmsg, "Error on volume \"%s\". Catalog VolCloudParts mismatch %ld != %ld\n", + /* The VolCloudParts is not yet fully synchronized with the catalog */ + Dmsg3(500, "Issue on volume \"%s\". Catalog VolCloudParts mismatch %ld != %ld\n", mr.VolumeName, mr.VolCloudParts, maxpart); - pm_strcpy(errmsg, tmpmsg.c_str()); } if (strlen(errmsg.c_str()) > 0) { ua->error_msg("\n%s", errmsg.c_str()); @@ -2094,7 +2179,7 @@ static int cloud_list_cmd(UAContext *ua, const char *cmd) mr.MediaId = 0; if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) { - memset(&pr, 0, sizeof(POOL_DBR)); + bmemset(&pr, 0, sizeof(POOL_DBR)); pr.PoolId = mr.PoolId; if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { strcpy(pr.Name, "?"); @@ -2124,6 +2209,8 @@ bail_out: } /* Ask client to create/prune/delete a snapshot via the command line */ +/* allow multiple commands b.e. : "cloud upload truncate" */ +/* will upload to the cloud then truncate the cache */ static int cloud_cmd(UAContext *ua, const char *cmd) { for (int i=0; iargc; i++) {