Skip to content

Commit 6521b70

Browse files
committed
DpkgHistory.__add__() done and small bugfixes.
Adding two DpkgHistory instances combines their operation lists relying on their chronological relationship to choose what to say for each package. e.g. x installed and then upgraded -> just say installed and put latest version number. Assumes there is no overlap between the two. Normally there won't be.
1 parent 685b9af commit 6521b70

File tree

4 files changed

+72
-19
lines changed

4 files changed

+72
-19
lines changed

TODO

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
Roadmap in order of planned implementation
22

3-
status command line option
4-
-gives date of last snapshot and lists recent package changes
5-
63
pickle
7-
-in create_snapshot and set_default
4+
-in create_snapshot
5+
-add function to combine two DpkgHistory objects
6+
-set_default
87
-testing
98

9+
DpkgHistory
10+
-change default for since (currently 30d)
11+
1012
delete_snapshot
1113
-should consolidate package change info, unless there is no parent
1214

apt-btrfs-snapshot

+1-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ if __name__ == "__main__":
144144
elif args.command == "list":
145145
res = apt_btrfs.list(args.how_many)
146146
elif args.command == "status":
147-
res = apt_btrfs.status(args.how_many)
147+
res = apt_btrfs.status()
148148
elif args.command == "set-default":
149149
res = apt_btrfs.set_default(args.snapshot, args.tag)
150150
elif args.command == "rollback":

dpkg_history.py

+48-14
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,52 @@
3232

3333
class DpkgHistory(dict):
3434
""" Parser for the dpkg history logs """
35-
def __init__(self, var_location="/var/", since = None):
35+
def __init__(self, var_location="/var/", since = None, do_parse=True):
3636
super(DpkgHistory, self).__init__()
3737

3838
self["install"] = []
3939
self["auto-install"] = []
4040
self["upgrade"] = []
4141
self["remove"] = []
4242
self["purge"] = []
43-
43+
4444
self.var_location = var_location
4545
self.since = self._get_date_from_string(since)
46-
self._get_dpkg_history()
47-
if len(self['install']) > 0:
48-
self.auto = self._find_auto_installs()
49-
self._split_installs_by_auto()
50-
self._sort_lists()
46+
self.auto = []
47+
48+
if do_parse:
49+
self._get_dpkg_history()
50+
if len(self['install']) > 0:
51+
self.auto = self._find_auto_installs()
52+
self._split_installs_by_auto()
53+
self._sort_lists()
54+
55+
def __add__(self, other):
56+
if self.since < other.since:
57+
order = self, other
58+
else:
59+
order = other, self
60+
61+
ops_by_package = defaultdict(list)
62+
versions = defaultdict(list)
63+
for history in order:
64+
for op in history.keys():
65+
for package, version in history[op]:
66+
ops_by_package[package].append(op)
67+
if ", " in version:
68+
version = version.split(", ")
69+
else:
70+
version = [version]
71+
versions[package].append(version)
72+
73+
combined = DpkgHistory(since = order[0].since, do_parse=False)
74+
combined._distill_ops(ops_by_package, versions)
75+
if len(combined['install']) > 0:
76+
combined.auto = order[1].auto
77+
combined._split_installs_by_auto()
78+
combined._sort_lists()
79+
80+
return combined
5181

5282
def _get_date_from_string(self, since):
5383
if isinstance(since, basestring):
@@ -61,7 +91,8 @@ def _get_dpkg_history(self):
6191
""" Read dpkg.log's and return dictionary of ops
6292
"""
6393
logfiles = self._logfiles_to_check()
64-
self._parse(logfiles)
94+
ops_by_package, versions = self._parse_by_package(logfiles)
95+
self._distill_ops(ops_by_package, versions)
6596

6697
def _logfiles_to_check(self):
6798
""" Return an ordered list of opened logfiles young enough to be
@@ -99,13 +130,13 @@ def _read_files(self, files):
99130
for line in f:
100131
yield line.strip()
101132

102-
def _parse(self, logfiles):
133+
def _parse_by_package(self, logfiles):
103134
""" reads in the opened logfiles and makes lists of the ops mentioned.
104135
Returns a dictionary of lists, each list contains (pkg, version)
105136
tuples.
106137
"""
107138
ops_by_package = defaultdict(list)
108-
version = defaultdict(list)
139+
versions = defaultdict(list)
109140
# List ops per package
110141
for line in self._read_files(logfiles):
111142
if line == "":
@@ -123,15 +154,18 @@ def _parse(self, logfiles):
123154

124155
package = bits[3]
125156
ops_by_package[package].append(linetype)
126-
version[package].append(bits[4:6])
127-
157+
versions[package].append(bits[4:6])
158+
159+
return ops_by_package, versions
160+
161+
def _distill_ops(self, ops_by_package, versions):
128162
instup = ("install", "upgrade")
129163
remurge = ("remove", "purge")
130164
# Decide which op to remember for each package
131165
for package, ops in ops_by_package.iteritems():
132166

133-
oldest_version = version[package][0][0]
134-
newest_version = version[package][-1][1]
167+
oldest_version = versions[package][0][0]
168+
newest_version = versions[package][-1][-1]
135169
version_change = "%s, %s" % (oldest_version, newest_version)
136170

137171
if ops[0] == "install" and ops[-1] not in remurge:

test/test_dpkg_history.py

+17
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,23 @@ def test_auto_installed_separation(self):
130130
expected = [(u'lib32asound2', u'1.0.25-1ubuntu10.2'), (u'lib32z1', u'1:1.2.3.4.dfsg-3ubuntu4'), (u'libc6-i386', u'2.15-0ubuntu10.4'), (u'linux-headers-3.8.0-27', u'3.8.0-27.40~precise3'), (u'linux-headers-3.8.0-27-generic', u'3.8.0-27.40~precise3'), (u'lynx-cur', u'2.8.8dev.9-2ubuntu0.12.04.1'), (u'python-gpgme', u'0.2-1')]
131131
self.assertEqual(log['auto-install'], expected)
132132

133+
def test_add(self):
134+
log1 = DpkgHistory(do_parse=False)
135+
log2 = DpkgHistory(do_parse=False)
136+
log1['install'] = [('one', '1'), ('two', '2'), ('three', '3')]
137+
log1['upgrade'] = [('four', '4, 4.1'), ('five', '5')]
138+
log1['remove'] = [('six', '6'), ('seven', '7')]
139+
log2['install'] = [('eight', '8'), ('six', '6.1')]
140+
log2['remove'] = [('two', '2'), ('five', '5')]
141+
log2['upgrade'] = [('one', '1, 1.2'), ('four', '4.1, 4.2')]
142+
log3 = log1 + log2
143+
self.assertEqual(log3['install'], [('eight', '8'),
144+
('one', u'1.2'), ('three', '3')])
145+
self.assertEqual(log3['upgrade'], [('four', '4, 4.2'),
146+
('six', '6, 6.1')])
147+
self.assertEqual(log3['remove'], [('five', '5'),
148+
('seven', '7')])
149+
self.assertEqual(log3['purge'], log3['auto-install'], [])
133150

134151
if __name__ == "__main__":
135152
unittest.main()

0 commit comments

Comments
 (0)