blob: 9a55da6a1a2662ef3cfb9952138b42594dc898b3 [file] [log] [blame]
mblighf3294cc2009-04-08 21:17:38 +00001"""
2Autotest AFE Cleanup used by the scheduler
3"""
4
5
6import datetime, time, logging
7import common
8from autotest_lib.database import database_connection
9from autotest_lib.frontend.afe import models
10from autotest_lib.scheduler import email_manager, scheduler_config
11
12
13class PeriodicCleanup(object):
14
15
16 def __init__(self, db, clean_interval, run_at_initialize=False):
17 self._db = db
18 self.clean_interval = clean_interval
19 self._last_clean_time = time.time()
showard915958d2009-04-22 21:00:58 +000020 self._run_at_initialize = run_at_initialize
21
22
23 def initialize(self):
24 if self._run_at_initialize:
mblighf3294cc2009-04-08 21:17:38 +000025 self._cleanup()
26
27
28 def run_cleanup_maybe(self):
29 should_cleanup = (self._last_clean_time + self.clean_interval * 60
30 < time.time())
31 if should_cleanup:
32 self._cleanup()
33 self._last_clean_time = time.time()
34
35
36 def _cleanup(self):
37 """Abrstract cleanup method."""
38 raise NotImplementedError
39
40
41class UserCleanup(PeriodicCleanup):
42 """User cleanup that is controlled by the global config variable
43 clean_interval in the SCHEDULER section.
44 """
45
46
47 def __init__(self, db, clean_interval_minutes):
48 super(UserCleanup, self).__init__(db, clean_interval_minutes)
49
50
51 def _cleanup(self):
52 logging.info('Running periodic cleanup')
53 self._abort_timed_out_jobs()
54 self._abort_jobs_past_synch_start_timeout()
55 self._clear_inactive_blocks()
56 self._check_for_db_inconsistencies()
57
58
59 def _abort_timed_out_jobs(self):
60 msg = 'Aborting all jobs that have timed out and are not complete'
61 logging.info(msg)
62 query = models.Job.objects.filter(hostqueueentry__complete=False).extra(
63 where=['created_on + INTERVAL timeout HOUR < NOW()'])
64 for job in query.distinct():
65 logging.warning('Aborting job %d due to job timeout', job.id)
66 job.abort(None)
67
68
69 def _abort_jobs_past_synch_start_timeout(self):
70 """
71 Abort synchronous jobs that are past the start timeout (from global
72 config) and are holding a machine that's in everyone.
73 """
74 msg = 'Aborting synchronous jobs that are past the start timeout'
75 logging.info(msg)
76 timeout_delta = datetime.timedelta(
77 minutes=scheduler_config.config.synch_job_start_timeout_minutes)
78 timeout_start = datetime.datetime.now() - timeout_delta
79 query = models.Job.objects.filter(
80 created_on__lt=timeout_start,
81 hostqueueentry__status='Pending',
82 hostqueueentry__host__aclgroup__name='Everyone')
83 for job in query.distinct():
84 logging.warning('Aborting job %d due to start timeout', job.id)
85 entries_to_abort = job.hostqueueentry_set.exclude(
86 status=models.HostQueueEntry.Status.RUNNING)
87 for queue_entry in entries_to_abort:
88 queue_entry.abort(None)
89
90
91 def _check_for_db_inconsistencies(self):
92 logging.info('Checking for db inconsistencies')
93 query = models.HostQueueEntry.objects.filter(active=True, complete=True)
94 if query.count() != 0:
95 subject = ('%d queue entries found with active=complete=1'
96 % query.count())
97 message = '\n'.join(str(entry.get_object_dict())
98 for entry in query[:50])
99 if len(query) > 50:
100 message += '\n(truncated)\n'
101
102 logging.error(subject)
103 email_manager.manager.enqueue_notify_email(subject, message)
104
105
106 def _clear_inactive_blocks(self):
107 msg = 'Clear out blocks for all completed jobs.'
108 logging.info(msg)
109 # this would be simpler using NOT IN (subquery), but MySQL
110 # treats all IN subqueries as dependent, so this optimizes much
111 # better
112 self._db.execute("""
113 DELETE ihq FROM ineligible_host_queues ihq
114 LEFT JOIN (SELECT DISTINCT job_id FROM host_queue_entries
115 WHERE NOT complete) hqe
116 USING (job_id) WHERE hqe.job_id IS NULL""")
117
118
119class TwentyFourHourUpkeep(PeriodicCleanup):
120 """Cleanup that runs at the startup of monitor_db and every subsequent
121 twenty four hours.
122 """
123
124
125 def __init__(self, db, run_at_initialize=True):
126 clean_interval = 24 * 60 # 24 hours
127 super(TwentyFourHourUpkeep, self).__init__(
128 db, clean_interval, run_at_initialize=run_at_initialize)
129
130
131 def _cleanup(self):
132 logging.info('Running 24 hour clean up')
133 self._django_session_cleanup()
134
135
136 def _django_session_cleanup(self):
137 """Clean up django_session since django doesn't for us.
138 http://www.djangoproject.com/documentation/0.96/sessions/
139 """
140 logging.info('Deleting old sessions from django_session')
141 sql = 'DELETE FROM django_session WHERE expire_date < NOW()'
142 self._db.execute(sql)