Skip to content

Commit

Permalink
Change in swift-drive-audit handling log rotation.
Browse files Browse the repository at this point in the history
Change supports kern.log rotation in order to avoid loss
of significant information.

There is a year change functionality added as kern.log
does not keep record of year.

There is also backwards function added which allows
reading logs from the back to the front, speeding up the
execution along with the unit test for it

Fixes Bug 1080682

Change-Id: I93436c405aff5625396514000cab774b66022dd0
  • Loading branch information
jola-mirecka committed Feb 12, 2013
1 parent af3bd46 commit 902b66d
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 15 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -66,6 +66,7 @@ Paul McMillan (paul.mcmillan@nebula.com)
Ewan Mellor (ewan.mellor@citrix.com)
Samuel Merritt (sam@swiftstack.com)
Stephen Milton (milton@isomedia.com)
Jola Mirecka (jola.mirecka@hp.com)
Russ Nelson (russ@crynwr.com)
Maru Newby (mnewby@internap.com)
Colin Nicholson (colin.nicholson@iomart.com)
Expand Down
60 changes: 45 additions & 15 deletions bin/swift-drive-audit
Expand Up @@ -15,13 +15,14 @@
# limitations under the License.

import datetime
import glob
import os
import re
import subprocess
import sys
from ConfigParser import ConfigParser

from swift.common.utils import get_logger
from swift.common.utils import backward, get_logger


# To search for more types of errors, add the regex to the list below
Expand Down Expand Up @@ -61,27 +62,56 @@ def get_devices(device_dir, logger):


def get_errors(minutes):
# Assuming log rotation is being used, we need to examine
# recently rotated files in case the rotation occured
# just before the script is being run - the data we are
# looking for may have rotated.
log_files = [f for f in glob.glob('/var/log/kern.*[!.][!g][!z]')]
log_files.sort()

now_time = datetime.datetime.now()
end_time = now_time - datetime.timedelta(minutes=minutes)
# kern.log does not contain the year so we need to keep
# track of the year and month in case the year recently
# ticked over
year = now_time.year
prev_entry_month = now_time.month
errors = {}
start_time = datetime.datetime.now() - datetime.timedelta(minutes=minutes)
try:
for line in open('/var/log/kern.log'):
if '[ 0.000000]' in line:

reached_old_logs = False
for path in log_files:
try:
f = open(path)
except IOError:
logger.error("Error: Unable to open " + path)
print("Unable to open " + path)
sys.exit(1)
for line in backward(f):
if '[ 0.000000]' in line \
or 'KERNEL supported cpus:' in line \
or 'BIOS-provided physical RAM map:' in line:
# Ignore anything before the last boot
errors = {}
continue
log_time_string = '%s %s' % (start_time.year,
' '.join(line.split()[:3]))
reached_old_logs = True
break
# Solves the problem with year change - kern.log does not
# keep track of the year.
log_time_entry = line.split()[:3]
if log_time_entry[0] == 'Dec' and prev_entry_month == 'Jan':
year -= 1
prev_entry_month = log_time_entry[0]
log_time_string = '%s %s' % (year, ' '.join(log_time_entry))
log_time = datetime.datetime.strptime(
log_time_string, '%Y %b %d %H:%M:%S')
if log_time > start_time:
if log_time > end_time:
for err in error_re:
for device in err.findall(line):
errors[device] = errors.get(device, 0) + 1
return errors
except IOError:
logger.error("Error: Unable to open /var/log/kern.log")
print("Unable to open /var/log/kern.log")
sys.exit(1)
else:
reached_old_logs = True
break
if reached_old_logs:
break
return errors


def comment_fstab(mount_point):
Expand Down
33 changes: 33 additions & 0 deletions swift/common/utils.py
Expand Up @@ -84,6 +84,39 @@
except (NoSectionError, NoOptionError):
pass


def backward(f, blocksize=4096):
"""
A generator returning lines from a file starting with the last line,
then the second last line, etc. i.e., it reads lines backwards.
Stops when the first line (if any) is read.
This is useful when searching for recent activity in very
large files.
:param f: file object to read
:param blocksize: no of characters to go backwards at each block
"""
f.seek(0, os.SEEK_END)
if f.tell() == 0:
return
last_row = ''
while f.tell() != 0:
try:
f.seek(-blocksize, os.SEEK_CUR)
except IOError:
blocksize = f.tell()
f.seek(-blocksize, os.SEEK_CUR)
block = f.read(blocksize)
f.seek(-blocksize, os.SEEK_CUR)
rows = block.split('\n')
rows[-1] = rows[-1] + last_row
while rows:
last_row = rows.pop(-1)
if rows and last_row:
yield last_row
yield last_row


# Used when reading config values
TRUE_VALUES = set(('true', '1', 'yes', 'on', 't', 'y'))

Expand Down
30 changes: 30 additions & 0 deletions test/unit/common/test_utils.py
Expand Up @@ -147,6 +147,36 @@ def test_normalize_timestamp(self):
self.assertRaises(ValueError, utils.normalize_timestamp, '')
self.assertRaises(ValueError, utils.normalize_timestamp, 'abc')

def test_backwards(self):
""" Test swift.common.utils.backward """

# The lines are designed so that the function would encounter
# all of the boundary conditions and typical conditions.
# Block boundaries are marked with '<>' characters
blocksize = 25
lines = ['123456789x12345678><123456789\n', # block larger than rest
'123456789x123>\n', # block ends just before \n character
'123423456789\n',
'123456789x\n', # block ends at the end of line
'<123456789x123456789x123\n',
'<6789x123\n', # block ends at the beginning of the line
'6789x1234\n',
'1234><234\n', # block ends typically in the middle of line
'123456789x123456789\n']

with TemporaryFile('r+w') as f:
for line in lines:
f.write(line)

count = len(lines) - 1
for line in utils.backward(f, blocksize):
self.assertEquals(line, lines[count].split('\n')[0])
count -= 1

# Empty file case
with TemporaryFile('r') as f:
self.assertEquals([], list(utils.backward(f)))

def test_mkdirs(self):
testroot = os.path.join(os.path.dirname(__file__), 'mkdirs')
try:
Expand Down

0 comments on commit 902b66d

Please sign in to comment.