From: Eric Bollengier Date: Wed, 31 Mar 2021 15:38:43 +0000 (+0200) Subject: Add bconsole .jlist command to get JSON output from regular list commands X-Git-Tag: Release-11.3.2~646 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a95d72cef373531475dcf551a22c37b887efeb29;p=thirdparty%2Fbacula.git Add bconsole .jlist command to get JSON output from regular list commands --- diff --git a/bacula/src/cats/cats.h b/bacula/src/cats/cats.h index 9e8eefcba..31fdf0b0d 100644 --- a/bacula/src/cats/cats.h +++ b/bacula/src/cats/cats.h @@ -665,6 +665,7 @@ enum e_list_type { HORZ_LIST, /* list */ VERT_LIST, /* llist */ ARG_LIST, /* key1=v1 key2=v2 key3=v3 */ + JSON_LIST, FAILED_JOBS, INCOMPLETE_JOBS }; diff --git a/bacula/src/cats/sql.c b/bacula/src/cats/sql.c index 57e35ce5d..e07985a25 100644 --- a/bacula/src/cats/sql.c +++ b/bacula/src/cats/sql.c @@ -796,7 +796,10 @@ int list_result(void *vctx, int nb_col, char **row) if (type == ARG_LIST) { goto arg_list; } - + if (type == JSON_LIST) { + goto json_list; + } + Dmsg1(800, "list_result starts second loop looking at %d fields\n", mdb->sql_num_fields()); @@ -882,7 +885,33 @@ arg_list: } send(ctx, "\n"); return 0; - + +json_list: + POOL_MEM tmp, qval, qkey; + bool first=true; + + Dmsg1(800, "list_result starts json list at %d fields\n", mdb->sql_num_fields()); + mdb->sql_field_seek(0); + + send(ctx, "{"); + for (i = 0; i < mdb->sql_num_fields(); i++) { + field = mdb->sql_fetch_field(); + if (!field) { + break; + } + quote_string(qkey.addr(), field->name); + lcase(qkey.c_str()); + if (!mdb->sql_field_is_numeric(field->type)) { + quote_string(qval.addr(), NPRTB(row[i])); + } else { + pm_strcpy(qval, row[i]); + } + Mmsg(tmp, "%s%s: %s", first?"":",", qkey.c_str(), qval.c_str()); + send(ctx, tmp.c_str()); + first = false; + } + send(ctx, "}\n"); + return 0; } /* @@ -899,8 +928,12 @@ list_result(JCR *jcr, BDB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type ty char buf[2000], ewc[30]; Dmsg0(800, "list_result starts\n"); - if (mdb->sql_num_rows() == 0) { - send(ctx, _("No results to list.\n")); + if (mdb->sql_num_rows() == 0) { + if (type == JSON_LIST) { + send(ctx, "[]\n"); + } else { + send(ctx, _("No results to list.\n")); + } return mdb->sql_num_rows(); } @@ -939,7 +972,10 @@ list_result(JCR *jcr, BDB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type ty if (type == ARG_LIST) { goto arg_list; } - + if (type == JSON_LIST) { + goto json_list; + } + Dmsg1(800, "list_result starts second loop looking at %d fields\n", mdb->sql_num_fields()); list_dashes(mdb, send, ctx); send(ctx, "|"); @@ -983,8 +1019,7 @@ list_result(JCR *jcr, BDB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type ty list_dashes(mdb, send, ctx); return mdb->sql_num_rows(); -vertical_list: - +vertical_list: Dmsg1(800, "list_result starts vertical list at %d fields\n", mdb->sql_num_fields()); while ((row = mdb->sql_fetch_row()) != NULL) { mdb->sql_field_seek(0); @@ -1007,8 +1042,7 @@ vertical_list: send(ctx, "\n"); } -arg_list: - +arg_list: Dmsg1(800, "list_result starts arg list at %d fields\n", mdb->sql_num_fields()); while ((row = mdb->sql_fetch_row()) != NULL) { mdb->sql_field_seek(0); @@ -1027,7 +1061,39 @@ arg_list: } send(ctx, "\n"); } - return mdb->sql_num_rows(); + return mdb->sql_num_rows(); + +json_list: + Dmsg1(800, "list_result starts json list at %d fields\n", mdb->sql_num_fields()); + POOL_MEM tmp, qval, qkey; + bool rfirst=true; + send(ctx, "["); + while ((row = mdb->sql_fetch_row()) != NULL) { + bool first=true; + send(ctx, rfirst?"{" : ",{"); + rfirst = false; + + mdb->sql_field_seek(0); + for (i = 0; i < mdb->sql_num_fields(); i++) { + field = mdb->sql_fetch_field(); + if (!field) { + break; + } + quote_string(qkey.addr(), field->name); + lcase(qkey.c_str()); + if (!mdb->sql_field_is_numeric(field->type)) { + quote_string(qval.addr(), NPRTB(row[i])); + } else { + pm_strcpy(qval, row[i]); + } + Mmsg(tmp, "%s%s: %s", first?"":",", qkey.c_str(), qval.c_str()); + send(ctx, tmp.c_str()); + first = false; + } + send(ctx, "}"); + } + send(ctx, "]\n"); + return mdb->sql_num_rows(); } /* @@ -1164,7 +1230,6 @@ void TAG_DBR::gen_sql(JCR *jcr, BDB *db, aclbits |= DB_ACL_BIT(DB_ACL_JOB); aclbits_extra |= DB_ACL_BIT(DB_ACL_JOB); } - if (*Name) { db->bdb_escape_string(jcr, esc_name, Name, strlen(Name)); } diff --git a/bacula/src/cats/sql_list.c b/bacula/src/cats/sql_list.c index a7de244ea..24ef902c2 100644 --- a/bacula/src/cats/sql_list.c +++ b/bacula/src/cats/sql_list.c @@ -66,7 +66,7 @@ void BDB::bdb_list_pool_records(JCR *jcr, POOL_DBR *pdbr, bdb_lock(); bdb_escape_string(jcr, esc, pdbr->Name, strlen(pdbr->Name)); - if (type == VERT_LIST) { + if (type == VERT_LIST || type == JSON_LIST) { if (pdbr->Name[0] != 0) { Mmsg(cmd, "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog," "AcceptAnyVolume,VolRetention,VolUseDuration,MaxVolJobs,MaxVolBytes," @@ -109,7 +109,7 @@ void BDB::bdb_list_pool_records(JCR *jcr, POOL_DBR *pdbr, void BDB::bdb_list_client_records(JCR *jcr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type) { bdb_lock(); - if (type == VERT_LIST) { + if (type == VERT_LIST || type == JSON_LIST) { Mmsg(cmd, "SELECT ClientId,Name,Uname,AutoPrune,FileRetention," "JobRetention " "FROM Client %s ORDER BY ClientId", get_acl(DB_ACL_CLIENT, true)); @@ -180,6 +180,7 @@ void BDB::bdb_list_plugin_objects(JCR *jcr, OBJECT_DBR *obj_r, DB_LIST_HANDLER * } switch (type) { + case JSON_LIST: case VERT_LIST: Mmsg(cmd, "SELECT Object.ObjectId, Object.JobId, Object.Path, Object.Filename, Object.PluginName, Object.ObjectCategory, " @@ -216,6 +217,7 @@ void BDB::bdb_list_plugin_objects_ids(JCR *jcr, char* id_list, DB_LIST_HANDLER * POOL_MEM msg; switch (type) { + case JSON_LIST: case VERT_LIST: Mmsg(cmd, "SELECT Object.ObjectId, Object.JobId, Object.Path, Object.Filename, Object.PluginName, Object.ObjectCategory, " @@ -273,7 +275,7 @@ void BDB::bdb_list_restore_objects(JCR *jcr, ROBJECT_DBR *rr, DB_LIST_HANDLER *s } bdb_lock(); - if (type == VERT_LIST) { + if (type == VERT_LIST || type == JSON_LIST) { Mmsg(cmd, "SELECT JobId, RestoreObjectId, ObjectName, " "PluginName, ObjectType " "FROM RestoreObject JOIN Job USING (JobId) WHERE JobId IN (%s) %s " @@ -314,7 +316,7 @@ void BDB::bdb_list_media_records(JCR *jcr, MEDIA_DBR *mdbr, const char *where = get_acl(DB_ACL_POOL, false); const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_POOL)) : ""; - if (type == VERT_LIST) { + if (type == VERT_LIST || type == JSON_LIST) { if (mdbr->VolumeName[0] != 0) { Mmsg(cmd, "SELECT MediaId,VolumeName,Slot,PoolId," "MediaType,MediaTypeId,FirstWritten,LastWritten,LabelDate,VolJobs," @@ -402,7 +404,7 @@ void BDB::bdb_list_jobmedia_records(JCR *jcr, uint32_t JobId, DB_ACL_BIT(DB_ACL_FILESET) | DB_ACL_BIT(DB_ACL_CLIENT)) : ""; - if (type == VERT_LIST) { + if (type == VERT_LIST || type == JSON_LIST) { if (JobId > 0) { /* do by JobId */ Mmsg(cmd, "SELECT JobMediaId,JobId,Media.MediaId,Media.VolumeName," "FirstIndex,LastIndex,StartFile,JobMedia.EndFile,StartBlock," @@ -465,7 +467,7 @@ void BDB::bdb_list_filemedia_records(JCR *jcr, uint32_t JobId, uint32_t FileInde } bdb_lock(); - if (type == VERT_LIST) { + if (type == VERT_LIST || type == JSON_LIST) { Mmsg(cmd, "SELECT JobId,FileIndex,Media.MediaId,Media.VolumeName," "BlockAddress,RecordNo,FileOffset " "FROM FileMedia,Media WHERE Media.MediaId=FileMedia.MediaId " @@ -632,7 +634,7 @@ void BDB::bdb_list_joblog_records(JCR *jcr, uint32_t JobId, DB_ACL_BIT(DB_ACL_FILESET) | DB_ACL_BIT(DB_ACL_CLIENT)) : ""; - if (type == VERT_LIST) { + if (type == VERT_LIST || type == JSON_LIST) { Mmsg(cmd, "SELECT Time,LogText FROM Log %s " "WHERE Log.JobId=%s %s ORDER BY LogId ASC", join, @@ -748,6 +750,7 @@ alist *BDB::bdb_list_job_records(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit, } switch (type) { + case JSON_LIST: case VERT_LIST: Mmsg(cmd, "SELECT JobId,Job,Job.Name,PurgedFiles,Type,Level," @@ -1014,7 +1017,7 @@ void BDB::bdb_list_snapshot_records(JCR *jcr, SNAPSHOT_DBR *sdbr, pm_strcat(filter, " ORDER BY SnapshotId DESC"); } - if (type == VERT_LIST || type == ARG_LIST) { + if (type == VERT_LIST || type == ARG_LIST || type == JSON_LIST) { Mmsg(cmd, "SELECT SnapshotId, Snapshot.Name, CreateDate, Client.Name AS Client, " "FileSet.FileSet AS FileSet, JobId, Volume, Device, Type, Retention, Comment " "FROM Snapshot JOIN Client USING (ClientId) LEFT JOIN FileSet USING (FileSetId) %s %s", filter, where); diff --git a/bacula/src/dird/ua_dotcmds.c b/bacula/src/dird/ua_dotcmds.c index f8f06eb01..d876b0e9b 100644 --- a/bacula/src/dird/ua_dotcmds.c +++ b/bacula/src/dird/ua_dotcmds.c @@ -43,6 +43,7 @@ extern int quit_cmd(UAContext *ua, const char *cmd); extern int qhelp_cmd(UAContext *ua, const char *cmd); extern bool dot_status_cmd(UAContext *ua, const char *cmd); extern void bvfs_set_acl(UAContext *ua, Bvfs *bvfs); +extern bool jlist_cmd(UAContext *ua, const char *cmd); /* Forward referenced functions */ static bool admin_cmds(UAContext *ua, const char *cmd); @@ -110,6 +111,7 @@ static struct cmdstruct commands[] = { /* help */ /* can be used in runscript * { NT_(".help"), dot_help_cmd, NULL, false}, { NT_(".jobs"), jobscmd, NULL, true}, { NT_(".estimate"), dotestimatecmd, NULL, false}, + { NT_(".jlist"), jlist_cmd, NULL, false}, { NT_(".levels"), levelscmd, NULL, false}, { NT_(".messages"), getmsgscmd, NULL, false}, { NT_(".msgs"), msgscmd, NULL, false}, diff --git a/bacula/src/dird/ua_output.c b/bacula/src/dird/ua_output.c index 9f4835252..a0912914c 100644 --- a/bacula/src/dird/ua_output.c +++ b/bacula/src/dird/ua_output.c @@ -335,6 +335,11 @@ bail_out: */ /* Do long or full listing */ +bool jlist_cmd(UAContext *ua, const char *cmd) +{ + return do_list_cmd(ua, cmd, JSON_LIST); +} + int llist_cmd(UAContext *ua, const char *cmd) { return do_list_cmd(ua, cmd, VERT_LIST); @@ -359,8 +364,9 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) POOL_DBR pr; MEDIA_DBR mr; - if (!open_new_client_db(ua)) + if (!open_new_client_db(ua)) { return 1; + } bmemset(&jr, 0, sizeof(jr)); bmemset(&pr, 0, sizeof(pr)); @@ -407,9 +413,6 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) } else if (B_ISALPHA(ua->argv[j][0])) { jr.JobLevel = ua->argv[j][0]; /* TODO: Check if the code is correct */ } - } else if (strcasecmp(ua->argk[j], NT_("level")) == 0) { - - } else if (strcasecmp(ua->argk[j], NT_("client")) == 0) { if (is_name_valid(ua->argv[j], NULL)) { CLIENT_DBR cr; @@ -435,10 +438,15 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) /* List JOBID=nn */ } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) { if (ua->argv[i]) { - jobid = str_to_int64(ua->argv[i]); - if (jobid > 0) { - jr.JobId = jobid; - db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist); + /* .jlist joblog jobid=1 should display only one json struct */ + if (llist == JSON_LIST && i > 1) { + /* nop */ + } else { + jobid = str_to_int64(ua->argv[i]); + if (jobid > 0) { + jr.JobId = jobid; + db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist); + } } }