1
1
#!/usr/bin/env python3
2
2
3
- '''
4
- Story Teller:
5
- Utility to help analyze log for certain sequence of events.
6
- e.g.: reboot (including warm/fast reboot), interface flapping, etc.
3
+ '''Story Teller: Utility to help analyze log for certain sequence of events.
4
+
5
+ e.g.: reboot (including warm/fast reboot), interface flapping, etc.
7
6
'''
8
7
9
8
import argparse
9
+ import os
10
10
import subprocess
11
+ import sys
12
+
13
+ from shlex import quote
11
14
12
15
regex_dict = {
16
+ 'acl' : r'acl\|ACL\|Acl' ,
13
17
'bgp' : 'bgpcfgd' ,
14
- 'crash' : 'what\|unexpected exception\|notify_OA_about_syncd_exception\|SIG\|not expected' ,
15
- 'interface' : 'updatePortOperStatus\|Configure .* to' ,
16
- 'lag' : 'link becomes\|addLag' ,
17
- 'reboot' : 'BOOT\|rc.local\|old_config\|minigraph.xml\|Rebooting\|reboot\|executeOperationsOnAsic\|getAsicView\|dumpVidToAsicOperatioId\|neighbor_adv\|Pausing\|shutdown\|warm' ,
18
- 'service' : 'Starting\|Stopping\|Started\|Stopped' ,
18
+ 'crash' : r 'what\|unexpected exception\|notify_OA_about_syncd_exception\|SIG\|not expected' ,
19
+ 'interface' : r 'updatePortOperStatus\|Configure .* to' ,
20
+ 'lag' : r 'link becomes\|addLag' ,
21
+ 'reboot' : r 'BOOT\|rc.local\|old_config\|minigraph.xml\|Rebooting\|reboot\|executeOperationsOnAsic\|getAsicView\|dumpVidToAsicOperatioId\|neighbor_adv\|Pausing\|shutdown\|warm' ,
22
+ 'service' : r 'Starting\|Stopping\|Started\|Stopped' ,
19
23
}
20
24
21
25
26
+ reference_file = '/tmp/storyteller_time_reference'
27
+
22
28
def exec_cmd (cmd ):
23
29
# Use universal_newlines (instead of text) so that this tool can work with any python versions.
24
30
out = subprocess .Popen (cmd , stdout = subprocess .PIPE , stderr = subprocess .STDOUT , shell = True , universal_newlines = True )
@@ -40,13 +46,12 @@ def build_options(after=0, before=0, context=0):
40
46
41
47
def find_log (logpath , log , regex , after = 0 , before = 0 , context = 0 ):
42
48
options = build_options (after , before , context )
43
- cmd = 'ls -rt {}/{}* | xargs zgrep -a {} "{}"' .format (logpath , log , options , regex )
49
+ cmd = 'find -L {}/{}* -newer {} | xargs zgrep -a {} "{}"' .format (logpath , log , reference_file , options , regex )
44
50
_ , out , _ = exec_cmd (cmd )
45
51
'''
46
52
Opportunity to improve:
47
53
output (out) can be split to lines and send to a filter to
48
54
decide if a line should be printed out or not.
49
- e.g. limited to a certain time span.
50
55
'''
51
56
print (out )
52
57
@@ -57,10 +62,22 @@ def build_regex(category):
57
62
# if c is not found, add c to grep list directly
58
63
regex .append (regex_dict [c ] if c in regex_dict else c )
59
64
60
- return '\|' .join (x for x in regex )
65
+ return r'\|' .join (x for x in regex )
66
+
67
+
68
+ def configure_time_filter (since ):
69
+ ret_code , _ , _ = exec_cmd ('date --date {}' .format (since ))
70
+ if ret_code :
71
+ print ('invalid date "{}"' .format (since ))
72
+ sys .exit (1 )
73
+
74
+ exec_cmd ('touch --date "{}" {}' .format (since , reference_file ))
61
75
62
76
63
77
def main ():
78
+ if os .geteuid () != 0 :
79
+ exit ("Root privileges are required for this operation" )
80
+
64
81
parser = argparse .ArgumentParser (description = 'Story Teller' )
65
82
66
83
parser .add_argument ('-l' , '--log' , help = 'log file prefix, e.g. syslog; default: syslog' ,
@@ -75,13 +92,21 @@ def main():
75
92
type = int , required = False , default = 0 )
76
93
parser .add_argument ('-C' , '--context' , help = 'Show N lines before and after match' ,
77
94
type = int , required = False , default = 0 )
95
+ parser .add_argument ('-S' , '--since' , help = 'Filter logs since the given date' ,
96
+ type = str , required = False , default = "@0" )
78
97
79
98
args = parser .parse_args ()
80
99
81
- log = args .log
82
- reg = build_regex (args .category )
100
+ # sanitize all string inputs
101
+ log = quote (args .log )
102
+ log_path = quote (args .logpath )
103
+ category = quote (args .category )
104
+ since = quote (args .since )
105
+
106
+ reg = build_regex (category )
107
+ configure_time_filter (since )
83
108
84
- find_log (args . logpath , log , reg , args .after , args .before , args .context )
109
+ find_log (log_path , log , reg , args .after , args .before , args .context )
85
110
86
111
87
112
if __name__ == '__main__' :
0 commit comments