diff --git a/openscap_daemon/config.py b/openscap_daemon/config.py index 8f284a7..8257fab 100644 --- a/openscap_daemon/config.py +++ b/openscap_daemon/config.py @@ -44,6 +44,8 @@ def __init__(self): self.cve_feeds_dir = \ os.path.join("/", "var", "lib", "oscapd", "cve_feeds") self.jobs = 4 + # -2 means never prune old results + self.max_results_to_keep = -2 # Tools section self.oscap_path = "" @@ -224,6 +226,11 @@ def absolutize(path): except (configparser.NoOptionError, configparser.NoSectionError): pass + try: + self.max_results_to_keep = config.getint("General", "max-results-to-keep") + except (configparser.NoOptionError, configparser.NoSectionError): + pass + # Tools section try: self.oscap_path = absolutize(config.get("Tools", "oscap")) @@ -295,6 +302,7 @@ def save_as(self, config_file): config.set("General", "work-in-progress-dir", str(self.work_in_progress_dir)) config.set("General", "cve-feeds-dir", str(self.cve_feeds_dir)) config.set("General", "jobs", str(self.jobs)) + config.set("General", "max-results-to-keep", str(self.max_results_to_keep)) config.add_section("Tools") config.set("Tools", "oscap", str(self.oscap_path)) diff --git a/openscap_daemon/task.py b/openscap_daemon/task.py index 9786d6f..6d43f42 100644 --- a/openscap_daemon/task.py +++ b/openscap_daemon/task.py @@ -169,6 +169,11 @@ def __init__(self): self.title = None self.evaluation_spec = evaluation_spec.EvaluationSpec() + # How many results should we keep before pruning old results + # -1 means use the default from config + # -2 means never prune any results + self.max_results_to_keep = -1 + self.schedule = Schedule() # If True, this task will be evaluated once without affecting the # schedule. This feature is important for test runs. This variable does @@ -183,6 +188,9 @@ def __str__(self): ret += "- ID: \t%i\n" % (self.id_) ret += "- title: \t%s\n" % (self.title) ret += str(self.evaluation_spec) + "\n" + ret += "- max results to keep: \t%s\n" % \ + ("default" if self.max_results_to_keep == -1 + else str(self.max_results_to_keep)) ret += "- schedule:\n" ret += " - not before: \t%s\n" % (self.schedule.not_before) ret += " - repeat after: \t%s\n" % (self.schedule.repeat_after) @@ -205,6 +213,7 @@ def is_equivalent_to(self, other): return \ self.evaluation_spec.is_equivalent_to(other.evaluation_spec) and \ self.title == other.title and \ + self.max_results_to_keep == other.max_results_to_keep and \ self.schedule.is_equivalent_to(other.schedule) and \ self.run_outside_schedule_once == other.run_outside_schedule_once @@ -229,6 +238,9 @@ def load_from_xml_element(self, root, config_file): et_helpers.get_element(root, "evaluation_spec") ) + self.max_results_to_keep = \ + int(et_helpers.get_element_text(root, "max-results-to-keep", "-1")) + self.schedule = Schedule() self.schedule.load_from_xml_element( et_helpers.get_element(root, "schedule") @@ -259,6 +271,11 @@ def to_xml_element(self): evaluation_spec_element = self.evaluation_spec.to_xml_element() root.append(evaluation_spec_element) + if self.max_results_to_keep != -1: + max_results_element = ElementTree.Element("max-results-to-keep") + max_results_element.text = str(self.max_results_to_keep) + root.append(max_results_element) + schedule_element = self.schedule.to_xml_element() root.append(schedule_element) @@ -356,12 +373,14 @@ def remove_result(self, result_id, config): ) logging.debug( - "Removing ARF of result '%i' of task '%i', expected path '%s'.", - result_id, self.id_, result_path + "Removing ARF of result '%s' of task '%i', expected path '%s'.", + str(result_id), self.id_, result_path ) shutil.rmtree(result_path, False) - logging.info("Removed result '%i' of task '%i'.", result_id, self.id_) + logging.info( + "Removed result '%s' of task '%i'.", str(result_id), self.id_ + ) def get_next_update_time(self, reference_datetime, log=False): if not self.enabled: @@ -404,6 +423,24 @@ def should_be_updated(self, reference_datetime, log=False): return False + def prune_old_results(self, config): + max_results_to_keep = self.max_results_to_keep + if max_results_to_keep == -1: + max_results_to_keep = config.max_results_to_keep + + if max_results_to_keep < 0: + # pruning is disabled + return + + result_ids = self.list_result_ids(config.results_dir) + result_ids_to_remove = result_ids[max_results_to_keep:] + + if result_ids_to_remove: + logging.info("Pruning old results of task '%i'...", self.id_) + + for result_id in reversed(result_ids_to_remove): + self.remove_result(result_id, config) + def update(self, reference_datetime, config): """Figures out if the task should be run right now, alters the schedule values accordingly. @@ -447,6 +484,9 @@ def update(self, reference_datetime, config): else: self.run_outside_schedule_once = False + # we have one extra result, let's prune old results + self.prune_old_results(config) + def generate_guide(self, config): return self.evaluation_spec.generate_guide(config) diff --git a/tests/data_dir_template/config.ini b/tests/data_dir_template/config.ini index c8bb683..a593bff 100644 --- a/tests/data_dir_template/config.ini +++ b/tests/data_dir_template/config.ini @@ -4,6 +4,7 @@ results-dir=./results work-in-progress-dir=./work_in_progress cve-feeds-dir=./cve_feeds jobs=4 +max-results-to-keep=100 [Tools] oscap=