-
-
Save yumminhuang/8b1502a49d8b20a6ae70 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python | |
#coding=utf-8 | |
import apt | |
import apt_pkg | |
from time import strftime | |
import os | |
import subprocess | |
import sys | |
""" | |
Following functions are used to return package info of available updates. | |
See: /usr/lib/update-notifier/apt_check.py | |
""" | |
SYNAPTIC_PINFILE = "/var/lib/synaptic/preferences" | |
DISTRO = subprocess.check_output(["lsb_release", "-c", "-s"], | |
universal_newlines=True).strip() | |
def clean(cache,depcache): | |
""" unmark (clean) all changes from the given depcache """ | |
# mvo: looping is too inefficient with the new auto-mark code | |
# for pkg in cache.Packages: | |
# depcache.MarkKeep(pkg) | |
depcache.init() | |
def saveDistUpgrade(cache,depcache): | |
""" this functions mimics a upgrade but will never remove anything """ | |
depcache.upgrade(True) | |
if depcache.del_count > 0: | |
clean(cache,depcache) | |
depcache.upgrade() | |
def get_update_packages(): | |
""" | |
Return a list of dict about package updates | |
""" | |
pkgs = [] | |
apt_pkg.init() | |
# force apt to build its caches in memory for now to make sure | |
# that there is no race when the pkgcache file gets re-generated | |
apt_pkg.config.set("Dir::Cache::pkgcache","") | |
try: | |
cache = apt_pkg.Cache(apt.progress.base.OpProgress()) | |
except SystemError as e: | |
sys.stderr.write("Error: Opening the cache (%s)" % e) | |
sys.exit(-1) | |
depcache = apt_pkg.DepCache(cache) | |
# read the pin files | |
depcache.read_pinfile() | |
# read the synaptic pins too | |
if os.path.exists(SYNAPTIC_PINFILE): | |
depcache.read_pinfile(SYNAPTIC_PINFILE) | |
# init the depcache | |
depcache.init() | |
try: | |
saveDistUpgrade(cache,depcache) | |
except SystemError as e: | |
sys.stderr.write("Error: Marking the upgrade (%s)" % e) | |
sys.exit(-1) | |
# use assignment here since apt.Cache() doesn't provide a __exit__ method | |
# on Ubuntu 12.04 it looks like | |
# aptcache = apt.Cache() | |
for pkg in cache.packages: | |
if not (depcache.marked_install(pkg) or depcache.marked_upgrade(pkg)): | |
continue | |
inst_ver = pkg.current_ver | |
cand_ver = depcache.get_candidate_ver(pkg) | |
if cand_ver == inst_ver: | |
continue | |
record = {"name": pkg.name, | |
"security": isSecurityUpgrade(pkg, depcache), | |
"section": pkg.section, | |
"current_version": inst_ver.ver_str if inst_ver else '-', | |
"candidate_version": cand_ver.ver_str if cand_ver else '-', | |
"priority": cand_ver.priority_str} | |
pkgs.append(record) | |
return pkgs | |
def isSecurityUpgrade(pkg, depcache): | |
def isSecurityUpgrade_helper(ver): | |
""" check if the given version is a security update (or masks one) """ | |
security_pockets = [("Ubuntu", "%s-security" % DISTRO), | |
("gNewSense", "%s-security" % DISTRO), | |
("Debian", "%s-updates" % DISTRO)] | |
for (file, index) in ver.file_list: | |
for origin, archive in security_pockets: | |
if (file.archive == archive and file.origin == origin): | |
return True | |
return False | |
inst_ver = pkg.current_ver | |
cand_ver = depcache.get_candidate_ver(pkg) | |
if isSecurityUpgrade_helper(cand_ver): | |
return True | |
# now check for security updates that are masked by a | |
# canidate version from another repo (-proposed or -updates) | |
for ver in pkg.version_list: | |
if (inst_ver and | |
apt_pkg.version_compare(ver.ver_str, inst_ver.ver_str) <= 0): | |
#print "skipping '%s' " % ver.VerStr | |
continue | |
if isSecurityUpgrade_helper(ver): | |
return True | |
return False | |
def print_result(pkgs): | |
""" | |
Print package updates in a table | |
""" | |
security_updates = filter(lambda x: x.get('security'), pkgs) | |
text = list() | |
text.append('Check Time: %s' % strftime('%m/%d/%Y %H:%M:%S')) | |
if not pkgs: | |
text.append('No available updates on this machine.') | |
else: | |
# Updates are available, build a table | |
text.append('%d packages can be updated.' % len(pkgs)) | |
text.append('%d updates are security updates.' % len(security_updates)) | |
text.append('-' * 100) | |
# List available security updates | |
text.append('Package Name'.ljust(30) + | |
'Current Version'.ljust(30) + | |
'Latest Version'.ljust(30) + | |
'Security'.ljust(10)) | |
text.append('-' * 100) | |
for pkg in pkgs: | |
text.append('{:<30}{:<30}{:<30}{:<10}'.format(pkg.get('name'), | |
pkg.get('current_version'), | |
pkg.get('candidate_version'), | |
'*' if pkg.get('security') else '')) | |
text.append('=' * 100) | |
return '\n'.join(text) | |
if __name__ == '__main__': | |
pkgs = get_update_packages() | |
print print_result(pkgs) |
Nice !
hello, i would like to start learning python , 1st thing that i think it will be simple and interesting to me to do is , to make a update-ubuntu.py with the apt-get update ... end the rest of the commands that i like to do my "py" file , can you help me understand ? ... or just tell me a place where can i read , so i can make it happen the ubntu-update.py file ? plssssss help
Hi @Seth666tl
You can try Python APT Library. Check out the documentation https://apt-team.pages.debian.net/python-apt/library/apt.cache.html https://apt.alioth.debian.org/python-apt-doc/library/apt.cache.html#apt.cache.Cache.update for apt-get update
command.
Very nice! I forked it and added small changes which sends the result to a given Slack Webhook URL. So I'm able to use it on multiple servers running as a cron: https://gist.github.com/tschwaerzl/2f7e92891e720643d9aa2b955d3c9e6e
This is awesome. Thanks for this !
I was modified fork from @tschwaerzl ( https://gist.github.com/tschwaerzl/2f7e92891e720643d9aa2b955d3c9e6e) with slack for Centos, with only package list in same form.
https://gist.github.com/petarkozic/19aa8f53dc4e00c2cfa85b84d54e9abd
You can try Python APT Library. Check out the documentation https://apt.alioth.debian.org/python-apt-doc/library/apt.cache.html#apt.cache.Cache.update for
apt-get update
command.
It seems as if the links do now work any more as of Feb. 2021
Also on pypi.org this might be dead as the last update was released Jul 12, 2012
Looks much better ;-)
Maybe you can edit your old post and strike the links?
@yumminhuang
Really great work and would save me a lot of time. Is it ok to use your code in a project under GPLv3 license?
Happy Coding!
Heiko.
Hi @FolkertsHeiko
I am not quite sure what are the restrictions on my code if it is used in a project under GPLv3 license. Since my code is based on /usr/lib/update-notifier/apt-check
, you can refer to its license.
You code is a complete monitoring solution. Looks good! 👍
Great, Thank you @yumminhuang !
Hi, thank you very much for the contribution, I am trying to run on kali linux 2021.1 or ubuntu 20.4 and it gives me a SyntaxError: invalid syntax error
File "/home/username/.config/polybar/scripts/package_updates_check.py", line 152
print print_result (pkgs)
can it be corrected?
Hi, thank you very much for the contribution, I am trying to run on kali linux 2021.1 or ubuntu 20.4 and it gives me a SyntaxError: invalid syntax error
File "/home/username/.config/polybar/scripts/package_updates_check.py", line 152
print print_result (pkgs)can it be corrected?
@panxos Which version of Python do you have?
@panxos Change print print_result(pkgs) to print(print_result(pkgs))
If there is nothing to do, the script works fine.
However, if there are some updates to do, I get this error:
Traceback (most recent call last):
File "package_updates_check.py", line 151, in
pkgs = get_update_packages()
File "package_updates_check.py", line 81, in get_update_packages
"section": pkg.section,
AttributeError: 'apt_pkg.Package' object has no attribute 'section'
Using 3.8.10 on Ubuntu 20.04
@tmatthews5511
No guarantee, but:
The issue is that the "section" attribute has been removed from apt_pkg.Package in version 1.6. The code should be updated to either use a newer version of apt_pkg or find an alternative way to get the package info.
An alternative for the "section" attribute could be the "candidate" attribute of the apt_pkg.Package object, which returns an apt_pkg.Version object which contains a "section" attribute. For example:
for pkg in cache.packages:
if not (depcache.marked_install(pkg) or depcache.marked_upgrade(pkg)):
continue
inst_ver = pkg.current_ver
cand_ver = depcache.get_candidate_ver(pkg)
if cand_ver == inst_ver:
continue
record = {"name": pkg.name,
"security": isSecurityUpgrade(pkg, depcache),
"section": cand_ver.section,
"current_version": inst_ver.ver_str if inst_ver else '-',
"candidate_version": cand_ver.ver_str if cand_ver else '-',
"priority": cand_ver.priority_str}
pkgs.append(record)
Ubuntu: 22.04.1 LTS
Anyone running into this TypeError:
Traceback (most recent call last):
File "<path>", line 152, in <module>
print (print_result(pkgs))
File "<path>", line 134, in print_result
text.append('%d updates are security updates.' % len(security_updates))
TypeError: object of type 'filter' has no len()
can update line 126
from:
security_updates = filter(lambda x: x.get('security'), pkgs)
to:
security_updates = list(filter(lambda x: x.get('security'), pkgs))
this should resolve that TypeError
I'm getting this error when executing the script and I cannot get what it's missing. Any ideas?
Traceback (most recent call last): File "package_updates_check.py", line 149, in <module> print (print_result(pkgs)) File "package_updates_check.py", line 123, in print_result security_updates = list(filter(lambda x: x.get('security'), pkgs)) TypeError: 'NoneType' object is not iterable
I fixed all errors I faced. But it's with Discord notification. So you have to edit the last lines if you don't want to use Discord.
https://gist.github.com/kankadev/05ee806815cfb059af69e7e56c1ef637
And you have to use this directory structure:
Copy config.temp.json
to ~/discord-config.json
and paste your webhook URL if you want to use Discord or comment / delete related lines.
I'm getting this error when executing the script and I cannot get what it's missing. Any ideas?
Traceback (most recent call last): File "package_updates_check.py", line 149, in <module> print (print_result(pkgs)) File "package_updates_check.py", line 123, in print_result security_updates = list(filter(lambda x: x.get('security'), pkgs)) TypeError: 'NoneType' object is not iterable
Nevermind, already fix it :).
I'm thinking on making a fork of this script. I've been able to adapt it to be used as a monitoring script for Nagios/Icinga or similars.
Example output: