blob: a7d2b0babd39cca39499503df97f285865d804d3 [file] [log] [blame]
barfab@chromium.orgb6d29932012-04-11 09:46:43 +02001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Eric Lie7c4cab2011-01-05 14:39:19 -08002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging, os, re
barfab@chromium.orgb6d29932012-04-11 09:46:43 +02006
Eric Lie7c4cab2011-01-05 14:39:19 -08007import common
Eric Lie7c4cab2011-01-05 14:39:19 -08008from autotest_lib.client.bin import utils
9from autotest_lib.client.common_lib import error
barfab@chromium.orgb6d29932012-04-11 09:46:43 +020010from constants import CLEANUP_LOGS_PAUSED_FILE
Eric Lie7c4cab2011-01-05 14:39:19 -080011
12class LogReader(object):
13 """
14 A class to read system log files.
15 """
16
Eric Li4844a9a2011-04-03 14:42:49 -070017 def __init__(self, filename='/var/log/messages', include_rotated_logs=True):
Eric Lie7c4cab2011-01-05 14:39:19 -080018 self._start_line = 1
19 self._filename = filename
Eric Li4844a9a2011-04-03 14:42:49 -070020 self._include_rotated_logs = include_rotated_logs
Eric Lie7c4cab2011-01-05 14:39:19 -080021 if not os.path.exists(CLEANUP_LOGS_PAUSED_FILE):
22 raise error.TestError('LogReader created without ' +
23 CLEANUP_LOGS_PAUSED_FILE)
24
25
Eric Li4844a9a2011-04-03 14:42:49 -070026 def read_all_logs(self, start=0):
27 """Read all content from log files.
28
29 Generator function.
30 Return an iterator on the content of files.
31 """
32 log_files = []
33 line_number = 1
34 if self._include_rotated_logs:
35 log_files.extend(utils.system_output(
36 'ls -tr1 %s.*' % self._filename,
37 ignore_status=True).splitlines())
38 log_files.append(self._filename)
Eric Li4844a9a2011-04-03 14:42:49 -070039 for log_file in log_files:
40 f = open(log_file)
41 for line in f:
42 if line_number >= start:
43 yield line
44 line_number += 1
45 f.close()
46
47
Eric Lie7c4cab2011-01-05 14:39:19 -080048 def set_start_by_regexp(self, index, regexp):
49 """Set the start of logs based on a regular expression.
50
51 @param index: line matching regexp to start at, earliest log at 0.
52 Negative numbers indicate matches since end of log.
53 """
54 regexp_compiled = re.compile(regexp)
Eric Lie7c4cab2011-01-05 14:39:19 -080055 starts = []
56 line_number = 1
Eric Li4844a9a2011-04-03 14:42:49 -070057 for line in self.read_all_logs():
Eric Lie7c4cab2011-01-05 14:39:19 -080058 if regexp_compiled.match(line):
59 starts.append(line_number)
60 line_number += 1
61 if index < -len(starts):
62 self._start_line = 1
63 elif index >= len(starts):
Eric Li4844a9a2011-04-03 14:42:49 -070064 self._start_line = line_number
Eric Lie7c4cab2011-01-05 14:39:19 -080065 else:
66 self._start_line = starts[index]
67
68
69 def set_start_by_reboot(self, index):
70 """ Set the start of logs (must be system log) based on reboot.
71
72 @param index: reboot to start at, earliest log at 0. Negative
73 numbers indicate reboots since end of log.
74 """
75 return self.set_start_by_regexp(index,
76 r'.*000\] Linux version \d')
77
78
Eric Li4844a9a2011-04-03 14:42:49 -070079 def set_start_by_current(self, relative=0):
Eric Lie7c4cab2011-01-05 14:39:19 -080080 """ Set start of logs based on current last line.
81
82 @param relative: line relative to current to start at. 1 means
83 to start the log after this line.
84 """
Eric Li4844a9a2011-04-03 14:42:49 -070085 count = self._start_line + relative
86 for line in self.read_all_logs(start=self._start_line):
87 count += 1
88 self._start_line = count
Eric Lie7c4cab2011-01-05 14:39:19 -080089
90
91 def get_logs(self):
92 """ Get logs since the start line.
93
94 Start line is set by set_start_* functions or
95 since the start of the file if none were called.
96
97 @return string of contents of file since start line.
98 """
Eric Li4844a9a2011-04-03 14:42:49 -070099 logs = []
100 for line in self.read_all_logs(start=self._start_line):
101 logs.append(line)
102 return ''.join(logs)
103
Eric Lie7c4cab2011-01-05 14:39:19 -0800104
105 def can_find(self, string):
106 """ Try to find string in the logs.
107
108 @return boolean indicating if we found the string.
109 """
Jason Glasgow68cf52f2011-03-31 15:32:13 -0400110 return string in self.get_logs()
Eric Lie7c4cab2011-01-05 14:39:19 -0800111
112
113class LogRotationPauser(object):
114 """
115 Class to control when logs are rotated from either server or client.
116
117 Assumes all setting of CLEANUP_LOGS_PAUSED_FILE is done by this class
118 and that all calls to begin and end are properly
119 nested. For instance, [ a.begin(), b.begin(), b.end(), a.end() ] is
120 supported, but [ a.begin(), b.begin(), a.end(), b.end() ] is not.
121 We do support redundant calls to the same class, such as
122 [ a.begin(), a.begin(), a.end() ].
123 """
124 def __init__(self, host=None):
125 self._host = host
126 self._begun = False
127 self._is_nested = True
128
129
130 def _run(self, command, *args, **dargs):
131 if self._host:
132 return self._host.run(command, *args, **dargs).exit_status
133 else:
134 return utils.system(command, *args, **dargs)
135
136
137 def begin(self):
138 """Make sure that log rotation is disabled."""
139 if self._begun:
140 return
141 print "in begin " + str(self._begun)
142 self._is_nested = (self._run(('[ -r %s ]' %
143 CLEANUP_LOGS_PAUSED_FILE),
144 ignore_status=True) == 0)
145 print "in begin is nested: " + str(self._is_nested)
146 if self._is_nested:
147 print logging.__file__
148 logging.info('File %s was already present' %
149 CLEANUP_LOGS_PAUSED_FILE)
150 print 1
151 else:
152 self._run('touch ' + CLEANUP_LOGS_PAUSED_FILE)
153 print 2
154 self._begun = True
155
156
157 def end(self):
158 print "in end" + str(self._begun)
159 assert self._begun
160 if not self._is_nested:
161 self._run('rm -f ' + CLEANUP_LOGS_PAUSED_FILE)
162 else:
163 logging.info('Leaving existing %s file' % CLEANUP_LOGS_PAUSED_FILE)
164 self._begun = False