so I went ahead and built a python script which does the trick
it's my first python so have mercy
the script has to run as root, depends on apt, apt-listchanges being installed and makes only sense if you run lxc, but i guess the lxc-attach could be switched in favor of running the command over ssh.
it lists each package only once and which hosts are affected, also it lists the combined changes in detail.
output:
apt-monitor on ***
2017-04-16 20:28:25.394625 UTC
----- packages -----
linux-image-4.9.0-0.bpo.2-amd64
version old: 4.9.13-1~bpo8+1
version new: 4.9.18-1~bpo8+1
affected hosts:
- master
tzdata
version old: 2017a-0+deb8u1
version new: 2017b-0+deb8u1
affected hosts:
- master
- service-mysql
- www-stage
----- changes -----
linux-signed (4.4~bpo8+1) jessie-backports; urgency=medium
* Rebuild for jessie-backports
* Update to linux version 4.9.18-1~bpo8+1
-- Ben Hutchings <ben@decadent.org.uk> Wed, 12 Apr 2017 15:50:11 +0100
linux-signed (4.4) unstable; urgency=medium
* Update to linux version 4.9.18-1
-- Ben Hutchings <ben@decadent.org.uk> Thu, 30 Mar 2017 17:43:56 +0100
tzdata (2017b-0+deb8u1) stable; urgency=medium
* New upstream version, affecting the following future timestamp:
- Haiti resumed observance of DST in 2017.
-- Aurelien Jarno <aurel32@debian.org> Thu, 06 Apr 2017 11:53:44 +0200
code:
#!/usr/bin/python
import re
import string
import subprocess
import sys
import datetime
import socket
# extract updates from apt output
def collect_upgrades(host, changes, upgrades, is_lxc):
changes = changes.split('\n')
pattern = re.compile('^Inst ([\S]+) \[([^\]]+)\] \(([\S]+) [^\[]*\[([^\]]+)\]\)')
for change in changes:
if pattern.match(change):
matches = pattern.search(change)
upgrade = {'name' : matches.group(1), 'version_old' : matches.group(2), 'version_new' : matches.group(3), 'suffix' : matches.group(4), 'hosts' : [], 'is_lxc' : is_lxc}
key = upgrade['name']+'_'+upgrade['version_new']
if not key in upgrades:
upgrades[key] = upgrade
upgrades[key]['hosts'] += [host]
# distinct package update lookup
upgrades = {}
# gather changes from host and containers
changes = subprocess.check_output(['apt-get', '--just-print', 'dist-upgrade'])
collect_upgrades('master', changes, upgrades, False)
containers = subprocess.check_output(['lxc-ls', '-1']).strip().split('\n')
for container in containers:
changes = subprocess.check_output(['lxc-attach', '-n', container, '--', 'apt-get', '--just-print', 'dist-upgrade'])
collect_upgrades(container, changes, upgrades, True)
# if upgrades is empty we can exit
if not bool(upgrades):
sys.exit(0)
# collect details apt-listchanges output for each package update
# also print upgrades
# print header
print 'apt-monitor on '+socket.gethostname()
print str(datetime.datetime.utcnow())+' UTC'
print ''
print '----- packages -----'
apt_changes = ''
for key, upgrade in upgrades.iteritems():
deb_file = '/var/cache/apt/archives/'+upgrade['name']+'_'+upgrade['version_new']+'_'+upgrade['suffix']+'.deb'
if upgrade['is_lxc']:
container = upgrade['hosts'][0]
apt_changes += subprocess.check_output(['lxc-attach', '-n', container, '--', 'apt-listchanges', '-f', 'text', deb_file])
else:
apt_changes += subprocess.check_output(['apt-listchanges', '-f', 'text', deb_file])
print ''
print upgrade['name']
print ' version old: '+upgrade['version_old']
print ' version new: '+upgrade['version_new']
print ' affected hosts:'
for host in upgrade['hosts']:
print ' - '+host
print ''
print '----- changes -----'
print apt_changes