/* * Pruning Jobs is a bit more complicated than purging Files * because we delete Job records only if there is a more current * backup of the FileSet. Otherwise, we keep the Job record. * In other words, we never delete the only Job record that * contains a current backup of a FileSet. This prevents the * Volume from being recycled and destroying a current backup. * * For Verify Jobs, we do not delete the last InitCatalog. * * For Restore Jobs there are no restrictions. */ int prune_jobs(UAContext *ua, CLIENTRES *client, POOLRES *pool, int JobType) { POOL_MEM query(PM_MESSAGE); POOL_MEM sql_where(PM_MESSAGE); POOL_MEM sql_from(PM_MESSAGE); utime_t period; char ed1[50]; alist *jobids_check=NULL; struct accurate_check_ctx *elt; db_list_ctx jobids, tempids; JOB_DBR jr; struct del_ctx del; memset(&del, 0, sizeof(del)); if (pool && pool->JobRetention > 0) { period = pool->JobRetention; } else if (client) { period = client->JobRetention; } else { /* should specify at least pool or client */ return false; } db_lock(ua->db); if (!prune_set_filter(ua, client, pool, period, &sql_from, &sql_where)) { goto bail_out; } /* Drop any previous temporary tables still there */ drop_temp_tables(ua); /* Create temp tables and indicies */ if (!create_temp_tables(ua)) { goto bail_out; } edit_utime(period, ed1, sizeof(ed1)); Jmsg(ua->jcr, M_INFO, 0, _("Begin pruning Jobs older than %s.\n"), ed1); del.max_ids = 100; del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids); del.PurgedFiles = (char *)malloc(del.max_ids); /* * Select all files that are older than the JobRetention period * and add them into the "DeletionCandidates" table. */ Mmsg(query, "INSERT INTO DelCandidates " "SELECT JobId,PurgedFiles,FileSetId,JobFiles,JobStatus " "FROM Job %s " /* JOIN Pool/Client */ "WHERE Type IN ('B', 'C', 'M', 'V', 'D', 'R', 'c', 'm', 'g') " " %s ", /* Pool/Client + JobTDate */ sql_from.c_str(), sql_where.c_str()); Dmsg1(050, "select sql=%s\n", query.c_str()); if (!db_sql_query(ua->db, query.c_str())) { if (ua->verbose) { ua->error_msg("%s", db_strerror(ua->db)); } goto bail_out; } /* Now, for the selection, we discard some of them in order to be always * able to restore files. (ie, last full, last diff, last incrs) * Note: The DISTINCT could be more useful if we don't get FileSetId */ jobids_check = New(alist(10, owned_by_alist)); Mmsg(query, "SELECT DISTINCT Job.Name, FileSet, Client.Name, Job.FileSetId, " "Job.ClientId, Job.Type " "FROM DelCandidates " "JOIN Job USING (JobId) " "JOIN Client USING (ClientId) " "JOIN FileSet ON (Job.FileSetId = FileSet.FileSetId) " "WHERE Job.Type IN ('B') " /* Look only Backup jobs */ "AND Job.JobStatus IN ('T', 'W') " /* Look only useful jobs */ ); /* The job_select_handler will skip jobs or filesets that are no longer * in the configuration file. Interesting ClientId/FileSetId will be * added to jobids_check (currently disabled in 6.0.7b) */ if (!db_sql_query(ua->db, query.c_str(), job_select_handler, jobids_check)) { ua->error_msg("%s", db_strerror(ua->db)); } /* For this selection, we exclude current jobs used for restore or * accurate. This will prevent to prune the last full backup used for * current backup & restore */ memset(&jr, 0, sizeof(jr)); /* To find useful jobs, we do like an incremental */ jr.JobLevel = L_INCREMENTAL; foreach_alist(elt, jobids_check) { jr.ClientId = elt->ClientId; /* should be always the same */ jr.FileSetId = elt->FileSetId; db_accurate_get_jobids(ua->jcr, ua->db, &jr, &tempids); jobids.add(tempids); }
/* * Pruning Jobs is a bit more complicated than purging Files * because we delete Job records only if there is a more current * backup of the FileSet. Otherwise, we keep the Job record. * In other words, we never delete the only Job record that * contains a current backup of a FileSet. This prevents the * Volume from being recycled and destroying a current backup. * * For Verify Jobs, we do not delete the last InitCatalog. * * For Restore Jobs there are no restrictions. */ int prune_jobs(UAContext *ua, CLIENT *client, int JobType) { struct del_ctx del; POOL_MEM query(PM_MESSAGE); utime_t now, period; CLIENT_DBR cr; char ed1[50], ed2[50]; db_lock(ua->db); memset(&cr, 0, sizeof(cr)); memset(&del, 0, sizeof(del)); bstrncpy(cr.Name, client->name(), sizeof(cr.Name)); if (!db_create_client_record(ua->jcr, ua->db, &cr)) { db_unlock(ua->db); return 0; } period = client->JobRetention; now = (utime_t)time(NULL); /* Drop any previous temporary tables still there */ drop_temp_tables(ua); /* Create temp tables and indicies */ if (!create_temp_tables(ua)) { goto bail_out; } /* * Select all files that are older than the JobRetention period * and stuff them into the "DeletionCandidates" table. */ edit_int64(now - period, ed1); Mmsg(query, insert_delcand, (char)JobType, ed1, edit_int64(cr.ClientId, ed2)); if (!db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL)) { if (ua->verbose) { ua->error_msg("%s", db_strerror(ua->db)); } Dmsg0(050, "insert delcand failed\n"); goto bail_out; } del.max_ids = 100; del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids); del.PurgedFiles = (char *)malloc(del.max_ids); /* ed1 = JobTDate */ edit_int64(cr.ClientId, ed2); switch (JobType) { case JT_BACKUP: Mmsg(query, select_backup_del, ed1, ed2); break; case JT_RESTORE: Mmsg(query, select_restore_del, ed1, ed2); break; case JT_VERIFY: Mmsg(query, select_verify_del, ed1, ed2); break; case JT_ADMIN: Mmsg(query, select_admin_del, ed1, ed2); break; case JT_COPY: Mmsg(query, select_copy_del, ed1, ed2); break; case JT_MIGRATE: Mmsg(query, select_migrate_del, ed1, ed2); break; } Dmsg1(150, "Query=%s\n", query.c_str()); if (!db_sql_query(ua->db, query.c_str(), job_delete_handler, (void *)&del)) { ua->error_msg("%s", db_strerror(ua->db)); } purge_job_list_from_catalog(ua, del); if (del.num_del > 0) { ua->info_msg(_("Pruned %d %s for client %s from catalog.\n"), del.num_del, del.num_del==1?_("Job"):_("Jobs"), client->name()); } else if (ua->verbose) { ua->info_msg(_("No Jobs found to prune.\n")); } bail_out: drop_temp_tables(ua); db_unlock(ua->db); if (del.JobId) { free(del.JobId); } if (del.PurgedFiles) { free(del.PurgedFiles); } return 1; }