#!/usr/bin/env python """ Icinga/Nagios plugin to check if pip upgrade is available Copyright (c) 2022 Benjamin Renard This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. """ import argparse import json import logging import os import subprocess # nosec import sys import traceback # nagios exit code STATUS = {"OK": 0, "WARNING": 1, "CRITICAL": 2, "UNKNOWN": 3} parser = argparse.ArgumentParser() parser.add_argument("-d", "--debug", action="store_true", dest="debug", default=False) parser.add_argument("-b", "--bin", action="store", dest="bin", help="Python binary path", type=str) parser.add_argument( action="store", dest="packages", help=("Python package(s) to check. By default, all installed" "packages are checked."), nargs="*", default=[], ) options = parser.parse_args() logging.basicConfig( level=logging.DEBUG if options.debug else logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s", ) if options.bin and not os.path.exists(options.bin): print(f'UNKNOWN - python executable "{options.bin}" not found') sys.exit(STATUS["UNKNOWN"]) cmd = [ options.bin if options.bin else sys.executable, "-m", "pip", "list", "--format", "json", "--outdated", ] logging.debug("Execute external command: %s", " ".join(cmd)) output = subprocess.check_output(cmd) # nosec logging.debug("Output:\n%s", output) try: outdated_packages = { package["name"]: package for package in json.loads(output) if not options.packages or package["name"] in options.packages } except Exception as exc: # pylint: disable=broad-except print("UNKNOWN - Exception occurred parsing pip output") traceback.print_exc(exc) sys.exit(STATUS["UNKNOWN"]) if outdated_packages: if len(outdated_packages) > 1: print(f"WARNING - {len(outdated_packages)} upgrades available") print( "\n".join( [ f' - {name} ({p["version"]} => {p["latest_version"]})' for name, p in outdated_packages.items() ] ) ) else: name = next(iter(outdated_packages)) p = outdated_packages[name] print( f"WARNING - available upgrade for {name} " f'({p["version"]} => {p["latest_version"]})' ) sys.exit(STATUS["WARNING"]) if options.packages and len(options.packages) == 1: print(f"OK - {options.packages[0]} is up-to-date") else: print("OK - all packages is up-to-date") sys.exit(STATUS["OK"])