int bdb_purge_media_record(JCR *jcr, MEDIA_DBR *mr);
int bdb_delete_snapshot_record(JCR *jcr, SNAPSHOT_DBR *sr);
int bdb_delete_client_record(JCR *jcr, CLIENT_DBR *cr);
+ int bdb_delete_tag_record(JCR *jcr, TAG_DBR *tr);
/* sql_find.c */
bool bdb_find_last_job_end_time(JCR *jcr, JOB_DBR *jr, POOLMEM **etime, char *job);
bool bdb_find_failed_job_since(JCR *jcr, JOB_DBR *jr, POOLMEM *stime, int &JobLevel);
/* sql_create.c */
+ bool bdb_create_tag_record(JCR *jcr, TAG_DBR *tr);
bool bdb_create_log_record(JCR *jcr, JobId_t jobid, utime_t mtime, char *msg);
int bdb_create_events_record(JCR *jcr, EVENTS_DBR *rec);
int bdb_create_path_record(JCR *jcr, ATTR_DBR *ar);
void bdb_list_client_records(JCR *jcr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
void bdb_list_copies_records(JCR *jcr, uint32_t limit, char *jobids, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
void bdb_list_events_records(JCR *jcr, EVENTS_DBR *rec, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
+ void bdb_list_tag_records(JCR *jcr, TAG_DBR *rec, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
void bdb_list_base_files_for_job(JCR *jcr, JobId_t jobid, DB_LIST_HANDLER *sendit, void *ctx);
void bdb_list_restore_objects(JCR *jcr, ROBJECT_DBR *rr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
void bdb_list_plugin_objects(JCR *jcr, OBJECT_DBR *obj_r, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
uint64_t Size; /* Snapshot Size */
};
+class TAG_DBR
+{
+public:
+ char Client[MAX_NAME_LENGTH]; /* Client name */
+ char Job[MAX_NAME_LENGTH]; /* Job name */
+ char Pool[MAX_NAME_LENGTH]; /* Pool name */
+ char Volume[MAX_NAME_LENGTH]; /* Volume name */
+ char Comment[MAX_NAME_LENGTH]; /* Comment */
+ char Name[MAX_NAME_LENGTH]; /* Name */
+ char Object[MAX_NAME_LENGTH]; /* Object name */
+ JobId_t JobId; /* JobId */
+
+ bool all;
+
+ TAG_DBR() {};
+ void zero() {
+ JobId = 0;
+ all = false;
+ *Object = *Client = *Job = *Pool = *Volume = *Comment = *Name = '\0';
+ };
+ virtual ~TAG_DBR(){};
+
+ /* Scan a TAG_DBR object to help with SQL queries */
+ void gen_sql(JCR *jcr, BDB *db,
+ const char **table, /* Table name (Client, Job, Media...) */
+ const char **name, /* Name of the record (Name, VolumeName, JobId) */
+ const char **id, /* Id of the record (ClientId, JobId, MediaId) */
+ char *esc, /* Escaped name of the resource */
+ char *esc_name, /* Escaped name of the tag */
+ uint64_t *aclbits, /* ACL used */
+ uint64_t *aclbits_extra); /* Extra ACL used */
+};
+
/* Call back context for getting a 32/64 bit value from the database */
class db_int64_ctx {
public:
mdb->bdb_create_log_record(jcr, jobid, mtime, msg)
#define db_create_events_record(jcr, mdb, rec) \
mdb->bdb_create_events_record(jcr, rec)
-#define db_create_path_record(jcr, mdb, ar) \
+#define db_create_tag_record(jcr, mdb, rec) \
+ mdb->bdb_create_tag_record(jcr, rec)
+#define db_create_path_record(jcr, mdb, ar) \
mdb->bdb_create_path_record(jcr, ar)
#define db_create_file_attributes_record(jcr, mdb, ar) \
mdb->bdb_create_file_attributes_record(jcr, ar)
mdb->bdb_delete_snapshot_record(jcr, sr)
#define db_delete_client_record(jcr, mdb, cr) \
mdb->bdb_delete_client_record(jcr, cr)
+#define db_delete_tag_record(jcr, mdb, tr) \
+ mdb->bdb_delete_tag_record(jcr, tr)
/* sql_find.c */
mdb->bdb_list_snapshot_records(jcr, snapdbr, sendit, ua, llist)
#define db_list_files(jcr, mdb, filedbr, sendit, ua) \
mdb->bdb_list_files(jcr, filedbr, sendit, ua);
+#define db_list_tag_records(jcr, mdb, tagdbr, sendit, ua, llist) \
+ mdb->bdb_list_tag_records(jcr, tagdbr, sendit, ua, llist);
/* sql_update.c */
#define db_update_job_start_record(jcr, mdb, jr) \
mdb->print_lock_info(fp);
}
+
+/* Scan a TAG_DBR object to help with SQL queries */
+void TAG_DBR::gen_sql(JCR *jcr, BDB *db,
+ const char **table_, /* Table name (Client, Job, Media...) */
+ const char **name_, /* Name of the record (Name, VolumeName, JobId) */
+ const char **id_, /* Id of the record (ClientId, MediaId, JobId) */
+ char *esc, /* Escaped name of the resource */
+ char *esc_name, /* Escaped name of the tag */
+ uint64_t *aclbits_, /* ACL used */
+ uint64_t *aclbits_extra_ /* Extra ACLs (not already included in the join list) */)
+{
+ db->bdb_lock();
+ *esc = 0;
+ *esc_name = 0;
+ const char *name = NT_("Name");
+ const char *table = NULL;
+ const char *id = NULL;
+ uint64_t aclbits=0; /* Used in the WHERE */
+ uint64_t aclbits_extra=0; /* Used in the JOIN */
+
+ if (*Client) {
+ table = "Client";
+ id = "ClientId";
+ db->bdb_escape_string(jcr, esc, Client, strlen(Client));
+ aclbits |= DB_ACL_BIT(DB_ACL_CLIENT);
+
+ } else if (*Job) {
+ table = "Job";
+ name = "Name";
+ id = "JobId";
+ int len = strlen(Job); /* 01234567890123456789012 */
+ if (len > 23) { /* Can be a full job name .2020-08-28_15.34.32_03 */
+ int idx=len-23;
+ if (Job[idx] == '.' &&
+ B_ISDIGIT(Job[idx+1]) &&
+ B_ISDIGIT(Job[idx+2]) &&
+ B_ISDIGIT(Job[idx+3]) &&
+ B_ISDIGIT(Job[idx+4]) &&
+ Job[idx+5] == '-' &&
+ B_ISDIGIT(Job[idx+6]) &&
+ B_ISDIGIT(Job[idx+7]) &&
+ Job[idx+8] == '-' &&
+ B_ISDIGIT(Job[idx+9]) &&
+ B_ISDIGIT(Job[idx+10]) &&
+ Job[idx+11] == '_' &&
+ B_ISDIGIT(Job[idx+12]) &&
+ B_ISDIGIT(Job[idx+13]) &&
+ Job[idx+14] == '.' &&
+ B_ISDIGIT(Job[idx+15]) &&
+ B_ISDIGIT(Job[idx+16]) &&
+ Job[idx+17] == '.' &&
+ B_ISDIGIT(Job[idx+18]) &&
+ B_ISDIGIT(Job[idx+19]) &&
+ Job[idx+20] == '_' &&
+ B_ISDIGIT(Job[idx+21]) &&
+ B_ISDIGIT(Job[idx+22]) &&
+ B_ISDIGIT(Job[idx+23]) == 0)
+ {
+ name = "Job";
+ }
+ }
+ db->bdb_escape_string(jcr, esc, Job, strlen(Job));
+ aclbits |= DB_ACL_BIT(DB_ACL_JOB);
+
+ } else if (*Volume) {
+ table = "Media";
+ name = "VolumeName";
+ id = "MediaId";
+ db->bdb_escape_string(jcr, esc, Volume, strlen(Volume));
+ aclbits_extra |= DB_ACL_BIT(DB_ACL_POOL);
+ aclbits |= DB_ACL_BIT(DB_ACL_POOL);
+
+ } else if (*Pool) {
+ table = "Pool";
+ id = "PoolId";
+ db->bdb_escape_string(jcr, esc, Pool, strlen(Pool));
+ aclbits_extra |= DB_ACL_BIT(DB_ACL_POOL);
+ aclbits |= DB_ACL_BIT(DB_ACL_POOL);
+
+ } else if (*Object) {
+ table = "Object";
+ name = "ObjectName";
+ id = "ObjectId";
+ db->bdb_escape_string(jcr, esc, Object, strlen(Object));
+ 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));
+ }
+ db->bdb_unlock();
+
+ if (JobId > 0) {
+ table = "Job";
+ name = "JobId";
+ id = "JobId";
+ edit_uint64(JobId, esc);
+ aclbits |= DB_ACL_BIT(DB_ACL_JOB);
+ }
+
+ *table_ = table;
+ *name_ = name;
+ *id_ = id;
+ *aclbits_ = aclbits;
+ *aclbits_extra_ = aclbits_extra;
+}
+
#ifdef COMMUNITY
bool BDB::bdb_check_settings(JCR *jcr, int64_t *starttime, int val, int64_t val2)
{
return ret;
}
+bool BDB::bdb_create_tag_record(JCR *jcr, TAG_DBR *tag)
+{
+ uint64_t aclbits, aclbits_extra;
+ char esc[MAX_ESCAPE_NAME_LENGTH];
+ char esc_name[MAX_ESCAPE_NAME_LENGTH];
+ const char *name;
+ const char *table;
+ const char *id;
+ bool ret=false;
+
+ tag->gen_sql(jcr, this, &table, &name, &id, esc, esc_name, &aclbits, &aclbits_extra);
+
+ bdb_lock();
+ /* TODO: Need a special kind of ACL */
+ const char *whereand = get_acls(aclbits, false);
+ const char *join = get_acl_join_filter(aclbits_extra);
+
+ if (*esc_name && *esc) { /* We have a tag name */
+ Mmsg(cmd, "INSERT INTO Tag%s (Tag, %s) VALUES ('%s', (SELECT %s FROM %s %s WHERE %s = '%s' %s))",
+ table, id, esc_name, id, table, join, (tag->JobId>0)?id:name, esc, whereand);
+ ret = bdb_sql_query(cmd, NULL, (void *)NULL);
+
+ } else {
+ Dmsg2(DT_SQL|50, "Tag invalid esc_name='%s' esc='%s'\n", esc_name, esc);
+ }
+ bdb_unlock();
+ return ret;
+}
+
#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */
Mmsg(cmd, "DELETE FROM Media WHERE MediaId=%lu", mr->MediaId);
bdb_sql_query(cmd, NULL, (void *)NULL);
+
+ Mmsg(cmd, "DELETE FROM TagMedia WHERE MediaId=%lu", mr->MediaId);
+ bdb_sql_query(cmd, NULL, (void *)NULL);
bdb_unlock();
return 1;
}
Mmsg(cmd, "DELETE FROM Client WHERE ClientId=%d", cr->ClientId);
bdb_sql_query(cmd, NULL, (void *)NULL);
+
+ Mmsg(cmd, "DELETE FROM TagClient WHERE ClientId=%d", cr->ClientId);
+ bdb_sql_query(cmd, NULL, (void *)NULL);
bdb_unlock();
return 1;
}
+/* Delete Tag record */
+int BDB::bdb_delete_tag_record(JCR *jcr, TAG_DBR *tag)
+{
+ char esc[MAX_ESCAPE_NAME_LENGTH];
+ char esc_name[MAX_ESCAPE_NAME_LENGTH];
+ uint64_t aclbits, aclbits_extra;
+ const char *name;
+ const char *table;
+ const char *id;
+ bool ret = false;
+
+ tag->gen_sql(jcr, this, &table, &name, &id, esc, esc_name, &aclbits, &aclbits_extra);
+
+ bdb_lock();
+ /* TODO: Need a special kind of ACL */
+ const char *join = get_acl_join_filter(aclbits_extra);
+ const char *whereand = get_acls(aclbits, false);
+
+ if (*esc_name) { /* We have a tag name */
+ if (tag->all) {
+ Mmsg(cmd, "DELETE FROM Tag%s WHERE Tag = '%s'", table, esc_name);
+
+ } else {
+ Mmsg(cmd, "DELETE FROM Tag%s WHERE Tag = '%s' AND %s IN (SELECT W.%sId FROM %s AS W %s WHERE W.%s = '%s' %s)",
+ table, esc_name, id, table, table, join, name, esc, whereand);
+ }
+ } else {
+ Mmsg(cmd, "DELETE FROM Tag%s WHERE %sId IN (SELECT W.%s FROM %s AS W %s WHERE W.%s = '%s' %s)",
+ table, table, id, table, join, name, esc, whereand);
+ }
+ Dmsg1(DT_SQL|50, "q=%s\n", cmd);
+ ret = bdb_sql_query(cmd, NULL, (void *)NULL);
+ bdb_unlock();
+ return ret;
+}
+
#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */
bdb_lock();
bdb_escape_string(jcr, esc, mdbr->VolumeName, strlen(mdbr->VolumeName));
- const char *join = get_acl_join_filter(DB_ACL_BIT(DB_ACL_POOL));
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 (mdbr->VolumeName[0] != 0) {
bdb_lock();
/* Get some extra SQL parameters if needed */
- const char *join = get_acl_join_filter(DB_ACL_BIT(DB_ACL_JOB) |
- DB_ACL_BIT(DB_ACL_FILESET) |
- DB_ACL_BIT(DB_ACL_CLIENT));
const char *where = get_acls(DB_ACL_BIT(DB_ACL_JOB) |
DB_ACL_BIT(DB_ACL_FILESET) |
DB_ACL_BIT(DB_ACL_CLIENT), (JobId == 0));
+ const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_JOB) |
+ DB_ACL_BIT(DB_ACL_FILESET) |
+ DB_ACL_BIT(DB_ACL_CLIENT)) : "";
+
if (type == VERT_LIST) {
if (JobId > 0) { /* do by JobId */
Mmsg(cmd, "SELECT JobMediaId,JobId,Media.MediaId,Media.VolumeName,"
}
bdb_lock();
- const char *join = get_acl_join_filter(DB_ACL_BIT(DB_ACL_CLIENT));
const char *where = get_acls(DB_ACL_BIT(DB_ACL_JOB) | DB_ACL_BIT(DB_ACL_CLIENT), false);
+ const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_CLIENT)) : "";
Mmsg(cmd,
"SELECT DISTINCT Job.PriorJobId AS JobId, Job.Job, "
}
bdb_lock();
- const char *join = get_acl_join_filter(DB_ACL_BIT(DB_ACL_JOB) |
- DB_ACL_BIT(DB_ACL_FILESET) |
- DB_ACL_BIT(DB_ACL_CLIENT));
const char *where = get_acls(DB_ACL_BIT(DB_ACL_JOB) |
DB_ACL_BIT(DB_ACL_FILESET) |
DB_ACL_BIT(DB_ACL_CLIENT), false);
+ const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_JOB) |
+ DB_ACL_BIT(DB_ACL_FILESET) |
+ DB_ACL_BIT(DB_ACL_CLIENT)) : "";
+
if (type == VERT_LIST) {
Mmsg(cmd, "SELECT Time,LogText FROM Log %s "
"WHERE Log.JobId=%s %s ORDER BY LogId ASC",
POOLMEM *tmp = get_pool_memory(PM_MESSAGE);
const char *order = "ASC";
const char *join = "";
+ const char *where_tmp = "";
*where = 0;
bdb_lock();
append_filter(where, tmp);
}
- pm_strcat(where, get_acls(DB_ACL_BIT(DB_ACL_CLIENT) |
- DB_ACL_BIT(DB_ACL_JOB) |
- DB_ACL_BIT(DB_ACL_FILESET),
- where[0] == 0));
+ where_tmp = get_acls(DB_ACL_BIT(DB_ACL_CLIENT) |
+ DB_ACL_BIT(DB_ACL_JOB) |
+ DB_ACL_BIT(DB_ACL_FILESET),
+ where[0] == 0);
+ pm_strcat(where, where_tmp);
- join = get_acl_join_filter(DB_ACL_BIT(DB_ACL_CLIENT) |
- DB_ACL_BIT(DB_ACL_FILESET));
+ if (*where_tmp) {
+ join = get_acl_join_filter(DB_ACL_BIT(DB_ACL_CLIENT) |
+ DB_ACL_BIT(DB_ACL_FILESET));
+ }
switch (type) {
case VERT_LIST:
void BDB::bdb_list_job_totals(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit, void *ctx)
{
bdb_lock();
- const char *join = get_acl_join_filter(DB_ACL_BIT(DB_ACL_CLIENT));
const char *where = get_acls(DB_ACL_BIT(DB_ACL_CLIENT) | DB_ACL_BIT(DB_ACL_JOB), true);
+ const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_CLIENT)) : "";
/* List by Job */
Mmsg(cmd, "SELECT count(*) AS Jobs,sum(JobFiles) "
bdb_lock();
/* Get optional filters for the SQL query */
- const char *join = get_acl_join_filter(DB_ACL_BIT(DB_ACL_JOB) |
- DB_ACL_BIT(DB_ACL_CLIENT) |
- DB_ACL_BIT(DB_ACL_FILESET));
-
const char *where = get_acls(DB_ACL_BIT(DB_ACL_JOB) |
DB_ACL_BIT(DB_ACL_CLIENT) |
DB_ACL_BIT(DB_ACL_FILESET), true);
+ const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_JOB) |
+ DB_ACL_BIT(DB_ACL_CLIENT) |
+ DB_ACL_BIT(DB_ACL_FILESET)) : "";
+
/*
* MySQL is different with no || operator
*/
bdb_unlock();
}
+void BDB::bdb_list_tag_records(JCR *jcr, TAG_DBR *tag, DB_LIST_HANDLER *result_handler, void *ctx, e_list_type type)
+{
+ POOL_MEM tmp, where;
+ char esc[MAX_ESCAPE_NAME_LENGTH];
+ char esc_name[MAX_ESCAPE_NAME_LENGTH];
+ uint64_t aclbits, aclbits_extra;
+
+ const char *name;
+ const char *id;
+ const char *table;
+
+ tag->gen_sql(jcr, this, &table, &name, &id, esc, esc_name, &aclbits, &aclbits_extra);
+
+ bdb_lock();
+ pm_strcpy(where, get_acls(aclbits, true)); /* get_acls() uses a specific object buffer */
+ const char *whereand = get_acls(aclbits, false);
+ const char *join = get_acl_join_filter(aclbits_extra);
+
+ if (table) {
+ if (tag->all) {
+ if (*esc_name) {
+ /* Display all resource for a tag */
+ Mmsg(tmp, "SELECT %s, %s AS %s FROM Tag%s AS T JOIN %s USING (%s) %s WHERE T.Tag = '%s' %s",
+ id, name, table, table, table, id, join, esc_name, whereand);
+ } else {
+ /* Display all tags for a resource type */
+ Mmsg(tmp, "SELECT DISTINCT T.Tag, %s AS %s, %s AS %s FROM Tag%s AS T JOIN %s USING (%s) %s %s",
+ id, id, name, table, table, table, id, join, where.c_str());
+ }
+
+ } else {
+ if (*esc_name) {
+ Mmsg(tmp, "SELECT T.Tag, %s as %s, %s AS %s FROM Tag%s AS T JOIN %s USING (%s) %s WHERE %s = '%s' AND T.Tag = '%s' %s",
+ id, id, name, table, table, table, id, join, name, esc, esc_name, whereand);
+
+ } else {
+ /* Display all tags for a client */
+ Mmsg(tmp, "SELECT Tag, %s as %s, %s as %s FROM Tag%s AS T JOIN %s USING (%s) %s WHERE %s = '%s' %s",
+ id, id, (tag->JobId>0)?"Name":name, table, table, table, id, join, name, esc, whereand);
+ }
+ }
+ Dmsg1(DT_SQL|50, "q=%s\n", tmp.c_str());
+ bdb_list_sql_query(jcr, tmp.c_str(), result_handler, ctx, 0, type);
+ }
+ bdb_unlock();
+}
+
#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */
{"DeleteConsolidatedJobs", store_bool, ITEM(res_job.DeleteConsolidatedJobs), 0, ITEM_DEFAULT, false},
{"PluginOptions", store_str, ITEM(res_job.PluginOptions), 0, 0, 0},
{"Base", store_alist_res, ITEM(res_job.base), R_JOB, 0, 0},
+ {"Tag", store_alist_str, ITEM(res_job.tag), 0, 0, 0},
{NULL, NULL, {0}, 0, 0, 0}
};
free_runscripts(res->res_job.RunScripts);
delete res->res_job.RunScripts;
}
+ if (res->res_job.tag) {
+ delete res->res_job.tag;
+ }
break;
case R_MSGS:
if (res->res_msgs.mail_cmd) {
res->res_job.jobdefs = res_all.res_job.jobdefs;
res->res_job.run_cmds = res_all.res_job.run_cmds;
res->res_job.RunScripts = res_all.res_job.RunScripts;
-
+ res->res_job.tag = res_all.res_job.tag;
/* TODO: JobDefs where/regexwhere doesn't work well (but this
* is not very useful)
* We have to set_bit(index, res_all.hdr.item_present);
bool PurgeMigrateJob; /* Purges source job on completion */
bool DeleteConsolidatedJobs; /* Delete or not consolidated Virtual Full jobs */
+ alist *tag; /* tags defined for this Job */
alist *base; /* Base jobs */
int64_t max_bandwidth; /* Speed limit on this job */
goto bail_out;
}
jcr->JobId = jcr->jr.JobId;
+ if (jcr->job->tag) {
+ char *tag;
+ TAG_DBR Tag;
+ Tag.JobId = jcr->JobId;
+ foreach_alist(tag, jcr->job->tag) {
+ bstrncpy(Tag.Name, tag, sizeof(Tag.Name)-1);
+ db_create_tag_record(jcr, jcr->db, &Tag);
+ }
+ }
Dmsg4(100, "Created job record JobId=%d Name=%s Type=%c Level=%c\n",
jcr->JobId, jcr->Job, jcr->jr.JobType, jcr->jr.JobLevel);
bool acl_access_console_ok(UAContext *ua, const char *name);
/* ua_cmds.c */
+int tag_cmd(UAContext *ua, const char *cmd);
bool get_uid_gid_from_acl(UAContext *ua, alist **uid, alist **gid, alist **dir);
bool do_a_command(UAContext *ua);
bool do_a_dot_command(UAContext *ua);
{ NT_("label"), label_cmd, _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool> slot=<slot> drive=<nb> barcodes"), false},
{ NT_("list"), list_cmd, _("List objects from catalog"),
- NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [level=<l>] [jobtype=<t>] [limit=<n>] [order=<asc|desc>]|\n"
+ NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [tag=<name>] [joberrors] [jobstatus=<s>] [level=<l>] [jobtype=<t>] [limit=<n>] [order=<asc|desc>]|\n"
"\tjobtotals | pools | volume | media <pool=pool-name> | files [type=<deleted|all>] jobid=<nn> | copies jobid=<nn> |\n"
"\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot | \n"
"\tfilemedia jobid=<nn> fileindex=<mm> | clients\n"
), false},
{ NT_("llist"), llist_cmd, _("Full or long list like list command"),
- NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [level=<l>] [jobtype=<t>] [order=<asc/desc>] [limit=<n>]|\n"
+ NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name> [tag=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [level=<l>] [jobtype=<t>] [order=<asc/desc>] [limit=<n>]|\n"
"\tjobtotals | pools | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn> |\n"
"\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot |\n"
"\tfilemedia jobid=<nn> fileindex=<mm> | clients\n"), false},
{ NT_("sqlquery"), sqlquery_cmd, _("Use SQL to query catalog"), NT_(""), false},
{ NT_("statistics"), collect_cmd, _("Display daemon statistics data"), NT_("[ simple | full | json ] all | <metric>\n"
"\t[ client=<client-name> | storage=<storage-name> ]"), true},
+ { NT_("tag"), tag_cmd, _("Manage tags"), NT_("[add|list|delete] [name=<str>] [job=<job>|jobid=<jobid>|client=<cli>|volume=<vol>]"), false},
{ NT_("time"), time_cmd, _("Print current time"), NT_(""), true},
{ NT_("trace"), trace_cmd, _("Turn on/off trace to file"), NT_("on | off"), true},
{ NT_("truncate"), truncate_cmd, _("Truncate one or more Volumes"), NT_("volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
return 1;
}
+class tag_mngt: public TAG_DBR
+{
+public:
+ int action; /* 1: create, 2: delete, 3: list, 0: nothing */
+ int target; /* 1: client, 2: job, 3: volume */
+
+ tag_mngt() {
+ zero();
+ action = 0;
+ target = 0;
+ };
+
+ int scan_command(UAContext *ua) {
+ for(int i = 0 ; i < ua->argc ; i++) {
+ if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
+ if (is_name_valid(ua->argv[i], NULL)) {
+ bstrncpy(Client, ua->argv[i], sizeof(Client));
+
+ } else {
+ all = 1;
+ bstrncpy(Client, "*", sizeof(Client));
+ }
+ target = 1;
+
+ } else if (strcasecmp(ua->argk[i], NT_("job")) == 0 ||
+ strcasecmp(ua->argk[i], NT_("jobs")) == 0) /* Compatible with list jobs tag= */
+ {
+ if (is_name_valid(ua->argv[i], NULL)) {
+ bstrncpy(Job, ua->argv[i], sizeof(Job));
+
+ } else {
+ all = 1;
+ bstrncpy(Job, "*", sizeof(Job));
+ }
+ target = 2;
+#if 0
+ } else if (strcasecmp(ua->argk[i], NT_("pool")) == 0) {
+ if (is_name_valid(ua->argv[i], NULL)) {
+ bstrncpy(Pool, ua->argv[i], sizeof(Pool));
+
+ } else {
+ all = 1;
+ bstrncpy(Pool, "*", sizeof(Pool));
+ }
+#endif
+
+ } else if (strcasecmp(ua->argk[i], NT_("volume")) == 0) {
+ if (is_name_valid(ua->argv[i], NULL)) {
+ bstrncpy(Volume, ua->argv[i], sizeof(Volume));
+
+ } else {
+ all = 1;
+ bstrncpy(Volume, "*", sizeof(Volume));
+ }
+ target = 3;
+
+ } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0 && is_a_number(ua->argv[i])) {
+ JobId = str_to_uint64(ua->argv[i]);
+ target = 2;
+
+ } else if (strcasecmp(ua->argk[i], NT_("name")) == 0 && is_name_valid(ua->argv[i], NULL, "#")) {
+ bstrncpy(Name, ua->argv[i], sizeof(Name));
+
+ } else if (strcasecmp(ua->argk[i], NT_("tag")) == 0 && is_name_valid(ua->argv[i], NULL, "#")) {
+ bstrncpy(Name, ua->argv[i], sizeof(Name));
+
+ } else if (strcasecmp(ua->argk[i], NT_("add")) == 0) {
+ action = 1;
+
+ } else if (strcasecmp(ua->argk[i], NT_("delete")) == 0) {
+ action = 2;
+
+ } else if (strcasecmp(ua->argk[i], NT_("list")) == 0) {
+ action = 3;
+ }
+ }
+ return 1;
+ };
+};
+
+/*
+ * tag management
+ */
+static int delete_tag(UAContext *ua)
+{
+ return 1;
+}
+
+/*
+ * tag management
+ */
+int tag_cmd(UAContext *ua, const char *cmd)
+{
+ tag_mngt t;
+ t.scan_command(ua);
+
+ if (!open_client_db(ua)) {
+ return 1;
+ }
+ if (t.action == 0) {
+ /*
+ * We didn't find an appropriate keyword above, so
+ * prompt the user.
+ */
+ start_prompt(ua, _("Available Tag operations:\n"));
+ add_prompt(ua, _("Add"));
+ add_prompt(ua, _("Delete"));
+ add_prompt(ua, _("List"));
+
+ t.action = 1 + do_prompt(ua, "", _("Select Tag operation"), NULL, 0);
+
+ if (t.action == 0) {
+ return 1;
+ }
+ }
+ if (t.target == 0) {
+ start_prompt(ua, _("Available Tag target:\n"));
+ add_prompt(ua, _("Client")); /* 1 */
+ add_prompt(ua, _("Job")); /* 2 */
+ add_prompt(ua, _("Volume")); /* 3 */
+
+ t.target = 1 + do_prompt(ua, "", _("Select Tag target"), NULL, 0);
+
+ switch(t.target) {
+ case 1:
+ {
+ CLIENT *cl = get_client_resource(ua, JT_BACKUP_RESTORE);
+ if (cl) {
+ bstrncpy(t.Client, cl->name(), sizeof(t.Client)-1);
+ } else {
+ return 1;
+ }
+ break;
+ }
+ case 2:
+ {
+ JOB_DBR jr;
+ bmemset(&jr, 0, sizeof(jr));
+ t.JobId = get_job_dbr(ua, &jr);
+
+ if (t.JobId == 0) {
+ return 1;
+ }
+ break;
+ }
+ case 3:
+ {
+ MEDIA_DBR mr;
+ if (!select_media_dbr(ua, &mr)) {
+ return 1;
+ }
+ bstrncpy(t.Volume, mr.VolumeName, sizeof(t.Volume)-1);
+ break;
+ }
+ default:
+ return 1;
+ }
+ }
+
+ if (t.target == 0) {
+ return 1;
+ }
+
+ switch(t.action) {
+ case 1: /* create */
+ if (*t.Name == 0) {
+ if (!get_cmd(ua, _("Enter the Tag value: "), false)) {
+ return false;
+ }
+ if (strlen(ua->cmd) > sizeof(t.Name)-1) {
+ ua->error_msg(_("Invalid tag\n"));
+ return false;
+ }
+ bstrncpy(t.Name, ua->cmd, sizeof(t.Name)-1);
+ }
+ if (db_create_tag_record(ua->jcr, ua->db, &t)) {
+ ua->send_msg(_("1000 Tag added\n"));
+ } else {
+ ua->error_msg(_("1009 Unable to add specified Tag.\n"));
+ }
+ break;
+
+ case 2: /* delete */
+ if (db_delete_tag_record(ua->jcr, ua->db, &t)) {
+ ua->send_msg(_("1000 Tag deleted\n"));
+ } else {
+ ua->error_msg(_("1009 Unable to delete specified Tag\n"));
+ }
+ break;
+
+ case 3: /* list */
+ db_list_tag_records(ua->jcr, ua->db, &t, prtit, ua, HORZ_LIST);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
/*
* print time
*/
NT_("jobid"),
NT_("snapshot"),
NT_("client"),
+ NT_("tag"),
NULL};
/* Deleting large jobs can take time! */
case 4:
delete_client(ua);
return 1;
+ case 5:
+ /* We don't want to manage tag deletion via the delete command, an
+ * accident can come very quickly (we have client, volume, etc... in the
+ * same) command line */
+
+ /* failback wanted */
default:
break;
}
case 4:
delete_client(ua);
return 1;
+ case 5:
+ delete_tag(ua);
+ return 1;
default:
ua->warning_msg(_("Nothing done.\n"));
break;
}
db_list_events_records(ua->jcr,ua->db, &event, prtit, ua, llist);
+ } else if (strcasecmp(ua->argk[i], NT_("tag")) == 0) {
+ return tag_cmd(ua, cmd);
+
} else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
|| strcasecmp(ua->argk[i], NT_("days")) == 0
|| strcasecmp(ua->argk[i], NT_("joberrors")) == 0
|| strcasecmp(ua->argk[i], NT_("long")) == 0
|| strcasecmp(ua->argk[i], NT_("start")) == 0
|| strcasecmp(ua->argk[i], NT_("end")) == 0
+ || strcasecmp(ua->argk[i], NT_("name")) == 0
) {
/* Ignore it */
} else if (strcasecmp(ua->argk[i], NT_("snapshot")) == 0 ||
{
POOL_MEM query(PM_MESSAGE);
+ Mmsg(query, "DELETE FROM TagJob WHERE JobId IN (%s)", jobs);
+ db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
+ Dmsg1(050, "Delete TagJob sql=%s\n", query.c_str());
+
Mmsg(query, "DELETE FROM File WHERE JobId IN (%s)", jobs);
db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
Dmsg1(050, "Delete File sql=%s\n", query.c_str());