From 80ee4b29f5881a3b7dc2654f5ea1de203a7ea575 Mon Sep 17 00:00:00 2001 From: licanwei Date: Thu, 14 Dec 2017 00:15:14 -0800 Subject: [PATCH] reset job interval when audit was updated when we update a existing audit's interval, the interval of 'execute_audit' job is still the old value. We need to update the interval of 'execute_audit' job. Change-Id: I402efaa6b2fd3a454717c3df9746c827927ffa91 Closes-Bug: #1738140 --- watcher/decision_engine/audit/continuous.py | 23 +++++++++---- .../audit/test_audit_handlers.py | 34 +++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/watcher/decision_engine/audit/continuous.py b/watcher/decision_engine/audit/continuous.py index 0439eb8e7..9272ac4b7 100644 --- a/watcher/decision_engine/audit/continuous.py +++ b/watcher/decision_engine/audit/continuous.py @@ -129,14 +129,25 @@ class ContinuousAuditHandler(base.AuditHandler): audits = objects.Audit.list( audit_context, filters=audit_filters, eager=True) scheduler_job_args = [ - job.args for job in self.scheduler.get_jobs() + (job.args[0].uuid, job) for job + in self.scheduler.get_jobs() if job.name == 'execute_audit'] - for args in scheduler_job_args: - if self._is_audit_inactive(args[0]): - scheduler_job_args.remove(args) + scheduler_jobs = dict(scheduler_job_args) + # if audit isn't in active states, audit's job should be removed + for job in scheduler_jobs.values(): + if self._is_audit_inactive(job.args[0]): + scheduler_jobs.pop(job.args[0].uuid) for audit in audits: - # if audit is not presented in scheduled audits yet. - if audit.uuid not in [arg[0].uuid for arg in scheduler_job_args]: + existing_job = scheduler_jobs.get(audit.uuid, None) + # if audit is not presented in scheduled audits yet, + # just add a new audit job. + # if audit is already in the job queue, and interval has changed, + # we need to remove the old job and add a new one. + if (existing_job is None) or ( + existing_job and + audit.interval != existing_job.args[0].interval): + if existing_job: + self.scheduler.remove_job(existing_job.id) # if interval is provided with seconds if utils.is_int_like(audit.interval): # if audit has already been provided and we need diff --git a/watcher/tests/decision_engine/audit/test_audit_handlers.py b/watcher/tests/decision_engine/audit/test_audit_handlers.py index eb63e304d..1923f585b 100644 --- a/watcher/tests/decision_engine/audit/test_audit_handlers.py +++ b/watcher/tests/decision_engine/audit/test_audit_handlers.py @@ -383,3 +383,37 @@ class TestContinuousAuditHandler(base.DbTestCase): audit_handler.execute_audit(self.audits[0], self.context) m_execute.assert_called_once_with(self.audits[0], self.context) self.assertIsNotNone(self.audits[0].next_run_time) + + @mock.patch.object(objects.service.Service, 'list') + @mock.patch.object(sq_api, 'get_engine') + @mock.patch.object(scheduling.BackgroundSchedulerService, 'remove_job') + @mock.patch.object(scheduling.BackgroundSchedulerService, 'add_job') + @mock.patch.object(scheduling.BackgroundSchedulerService, 'get_jobs') + @mock.patch.object(objects.audit.Audit, 'list') + def test_launch_audits_periodically_with_diff_interval( + self, mock_list, mock_jobs, m_add_job, m_remove_job, + m_engine, m_service): + audit_handler = continuous.ContinuousAuditHandler() + mock_list.return_value = self.audits + self.audits[0].next_run_time = (datetime.datetime.now() - + datetime.timedelta(seconds=1800)) + m_job1 = mock.MagicMock() + m_job1.name = 'execute_audit' + m_audit = mock.MagicMock() + m_audit.uuid = self.audits[0].uuid + m_audit.interval = 60 + m_job1.args = [m_audit] + mock_jobs.return_value = [m_job1] + m_engine.return_value = mock.MagicMock() + m_add_job.return_value = mock.MagicMock() + + audit_handler.launch_audits_periodically() + m_service.assert_called() + m_engine.assert_called() + m_add_job.assert_called() + mock_jobs.assert_called() + self.assertIsNotNone(self.audits[0].next_run_time) + self.assertIsNone(self.audits[1].next_run_time) + + audit_handler.launch_audits_periodically() + m_remove_job.assert_called()