-
Notifications
You must be signed in to change notification settings - Fork 0
/
apthist.py
153 lines (93 loc) · 3.67 KB
/
apthist.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
"""
Parses the apt history log on Ubuntu/Debian (/var/log/apt/history.log)
with different output options.
Use cases:
The `list` action outputs for each entry:
(index, date and time key, invoked command)
python apthist.py list
python apthist.py list --file /path/to/custom/history.log
The `describe` actions provides a detailed description
of a particular entry indentified by its index from `list`
python apthist.py describe --index 10
python apthist.py describe --index 10 --file /path/to/custom/history.log
The `packages` action prints a list of packages installed (one per line)
for an entry identified by index from `list`
python apthist.py packages --index 10
"""
import re
import argparse
from pprint import pprint
DEFAULT_FILENAME = '/var/log/apt/history.log'
def find_matches(s, pattern):
match = re.search(pattern, s)
return match.groupdict()
def update_entry(entry, line, pattern):
d = find_matches(line, pattern)
entry.update(d)
def parse_packages(packages_unparsed):
res = []
for s in packages_unparsed.split(')')[:-1]:
pkg_arch, version_info = s.split('(')
idx = 0
if pkg_arch.startswith(','):
idx = 1
pkg, arch = pkg_arch.split(' ')[idx].split(':')
res.append((pkg, arch, version_info))
return res
def parse(fname):
f = open(fname)
entries = dict()
first_time = True
idx = 0
for line in f:
if line.startswith('Start-Date:'):
if first_time:
first_time = False
else:
entries[idx] = current_entry
idx += 1
current_entry = dict()
update_entry(current_entry, line, '(Start-Date:) (?P<date>.*)')
elif line.startswith('Commandline:'):
update_entry(current_entry, line, '(Commandline:) (?P<command>.*)')
elif line.startswith('Requested-By:'):
update_entry(current_entry, line, '(Requested-By:) (?P<username>[a-zA-Z]+) \((?P<userid>[0-9]+)\)')
elif line.startswith('Install:') or line.startswith('Upgrade:'):
packages_match_dict = find_matches(line, '(?P<operation>Install|Upgrade):\s(?P<packages_unparsed>.*)')
current_entry['operation'] = packages_match_dict['operation']
packages_unparsed = packages_match_dict['packages_unparsed']
current_entry['packages'] = parse_packages(packages_unparsed)
else:
pass
entries[idx] = current_entry
return entries
if __name__ == '__main__':
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('--file', default=DEFAULT_FILENAME)
arg_parser.add_argument('action')
arg_parser.add_argument('--index', type=int)
args = arg_parser.parse_args()
entries = parse(args.file)
if args.action == 'list':
for k in sorted(entries.keys(), reverse=True):
v = entries[k]
print(k, v['date'])
if 'command' in v:
print(v['command'])
else:
print('Operation by user', v['username'])
print()
elif args.action == 'describe':
if args.index is not None:
pprint(entries[args.index])
else:
print('No index is provided')
elif args.action == 'packages':
if args.index is not None:
# TODO Allow this only for entries with install
for p, _, _ in entries[args.index]['packages']:
print(p)
else:
print('No index is provided')
else:
print('Not valid action')