#!/usr/bin/env python
# vim:ts=4 sw=4 et:
#
# vulnerability_report.py
#
# dumps a vulnerability report, but will also do it based on full errata, not just what
# is available in the clone channel a system is tied to.
# This does assume satellite >= 5.4.0. I have no idea if 5.3 will work yet.
import sys
import xmlrpclib
import pprint
import ConfigParser
from optparse import OptionParser
def getArch(arch):
'''
This is a function because the API doesn't return good data.
Specifically system.listPackages() returns AMD64 for the arch instead
of x86_64. If you try to use the arch value directly in packages.findByNvrea()
it will blow up in your face.
'''
if arch == 'AMD64':
return 'x86_64'
else:
return arch
parser = OptionParser()
parser.add_option("-c", "--configfile", action="store", type="string",
dest="configfile", help="Alternate config file to set defaults")
parser.add_option("-d", "--debug", action="store_true", dest="debug", help="Turn on Debug mode")
parser.add_option("-l", "--login", dest="login", help="Satellite Server Login Name")
parser.add_option("-p", "--password", dest="password", help="Satellite Server Password")
parser.add_option("-u", "--url", dest="url", help="URL of Satellite Server")
parser.set_defaults(configfile=False)
parser.set_defaults(debug=False)
parser.set_defaults(url='http://localhost/rpc/api')
(options, args) = parser.parse_args()
if __name__ == '__main__':
login = False
password = False
# Read config file for options
if options.configfile:
config = ConfigParser.ConfigParser()
config.read(options.configfile)
try:
url = config.get('config','url')
except ConfigParser.NoOptionError:
pass
except ConfigParser.NoSectionError:
parser.print_help()
print
print >>sys.stderr, 'No config section found in your config file.'
print >>sys.stderr, 'Please verify that %s exists and has the correct syntax.' % (options.configfile)
sys.exit(1)
try:
login = config.get('config','login')
except ConfigParser.NoOptionError:
pass
try:
password = config.get('config','password')
except ConfigParser.NoOptionError:
pass
url = options.url
debug = options.debug
if options.login:
login = options.login
if options.password:
password = options.password
# Various checks up front to save time
if login and password:
client = xmlrpclib.Server(url, verbose=0)
authKey = client.auth.login(login, password)
else:
parser.print_help()
parser.error("Login or Password have not been defined on the commandline or in a config file")
# Establish which system arch types to look for
rhel5_x64 = False
rhel4_x64 = False
rhel3_x64 = False
rhel5_i386 = False
rhel4_i386 = False
rhel3_i386 = False
# get a list of servers to iterate over
if debug:
print >>sys.stderr, "Starting collection of system names"
active_systems = []
if args:
args.sort()
for system in args:
try:
data = client.system.getId(authKey,system)[0]
cpu = client.system.getCpu(authKey,data['id'])
detail = client.system.getDetails(authKey,data['id'])
if cpu['arch'] == 'x86_64':
if detail['release'] == '5Server':
rhel5_x64 = True
if detail['release'] == '4AS':
rhel4_x64 = True
if detail['release'] == '3AS':
rhel3_x64 = True
if cpu['arch'] == 'i386' or cpu['arch'] == 'i686':
if detail['release'] == '5Server':
rhel5_i386 = True
if detail['release'] == '4AS':
rhel4_i386 = True
if detail['release'] == '3AS':
rhel3_i386 = True
data['arch'] = cpu['arch']
data['release'] = detail['release']
active_systems.append(data)
except:
print >>sys.stderr, "ERROR: Could not identify servername: %s" % system
else:
systems = sorted(client.system.listActiveSystems(authKey), key=lambda k: k['name'])
for system in systems:
data = system
cpu = client.system.getCpu(authKey,data['id'])
detail = client.system.getDetails(authKey,data['id'])
data['arch'] = cpu['arch']
data['release'] = detail['release']
active_systems.append(data)
rhel5_x64 = True
rhel4_x64 = True
rhel3_x64 = True
rhel5_i386 = True
rhel4_i386 = True
rhel3_i386 = True
# ALL RHEL3-64 Packages
if rhel3_x64:
if debug:
print >>sys.stderr, "Starting collection of RHEL3 64bit"
rhel3 = client.channel.software.listAllPackages(authKey,'rhel-x86_64-as-3')
rhel3_tools = client.channel.software.listAllPackages(authKey,'rhn-tools-rhel-3-as-x86_64')
rhel3_x64_all = rhel3 + rhel3_tools
# ALL RHEL3-32 Packages
if rhel3_i386:
if debug:
print >>sys.stderr, "Starting collection of RHEL3 32bit"
rhel3 = client.channel.software.listAllPackages(authKey,'rhel-i386-as-3')
rhel3_tools = client.channel.software.listAllPackages(authKey,'rhn-tools-rhel-3-as-i386')
rhel3_i386_all = rhel3 + rhel3_tools
# ALL RHEL4-64 Packages
if rhel4_x64:
if debug:
print >>sys.stderr, "Starting collection of RHEL4 64bit"
rhel4 = client.channel.software.listAllPackages(authKey,'rhel-x86_64-as-4')
rhel4_tools = client.channel.software.listAllPackages(authKey,'rhn-tools-rhel-4-as-x86_64')
rhel4_cluster = client.channel.software.listAllPackages(authKey,'rhel-x86_64-as-4-cluster')
rhel4_gfs = client.channel.software.listAllPackages(authKey,'rhel-x86_64-as-4-gfs-6.1')
rhel4_proxy = client.channel.software.listAllPackages(authKey,'redhat-rhn-proxy-5.1-as-x86_64-4')
rhel4_extras = client.channel.software.listAllPackages(authKey,'rhel-x86_64-as-4-extras')
rhel4_x64_all = rhel4 + rhel4_tools + rhel4_cluster + rhel4_gfs + rhel4_proxy + rhel4_extras
# ALL RHEL4-32 Packages
if rhel4_i386:
if debug:
print >>sys.stderr, "Starting collection of RHEL4 32bit"
rhel4 = client.channel.software.listAllPackages(authKey,'rhel-i386-as-4')
rhel4_tools = client.channel.software.listAllPackages(authKey,'rhn-tools-rhel-4-as-i386')
rhel4_cluster = client.channel.software.listAllPackages(authKey,'rhel-i386-as-4-cluster')
rhel4_gfs = client.channel.software.listAllPackages(authKey,'rhel-i386-as-4-gfs-6.1')
rhel4_proxy = client.channel.software.listAllPackages(authKey,'redhat-rhn-proxy-5.1-as-i386-4')
rhel4_extras = client.channel.software.listAllPackages(authKey,'rhel-i386-as-4-extras')
rhel4_i386_all = rhel4 + rhel4_tools + rhel4_cluster + rhel4_gfs + rhel4_proxy + rhel4_extras
# ALL RHEL5-64 Packages
if rhel5_x64:
if debug:
print >>sys.stderr, "Starting collection of RHEL5 64bit"
rhel5 = client.channel.software.listAllPackages(authKey,'rhel-x86_64-server-5')
rhel5_tools = client.channel.software.listAllPackages(authKey,'rhn-tools-rhel-x86_64-server-5')
rhel5_cluster_storage = client.channel.software.listAllPackages(authKey,'rhel-x86_64-server-cluster-storage-5')
rhel5_cluster = client.channel.software.listAllPackages(authKey,'rhel-x86_64-server-cluster-5')
rhel5_prod = client.channel.software.listAllPackages(authKey,'rhel-x86_64-server-productivity-5')
rhel5_supp = client.channel.software.listAllPackages(authKey,'rhel-x86_64-server-supplementary-5')
rhel5_virt = client.channel.software.listAllPackages(authKey,'rhel-x86_64-server-vt-5')
rhel5_x64_all = rhel5 + rhel5_tools + rhel5_cluster_storage + rhel5_cluster + rhel5_prod + rhel5_supp + rhel5_virt
# ALL RHEL5-32 Packages
if rhel5_i386:
if debug:
print >>sys.stderr, "Starting collection of RHEL5 32bit"
rhel5 = client.channel.software.listAllPackages(authKey,'rhel-i386-server-5')
rhel5_proxy = client.channel.software.listAllPackages(authKey,'redhat-rhn-proxy-5.3-server-i386-5')
rhel5_tools = client.channel.software.listAllPackages(authKey,'rhn-tools-rhel-i386-server-5')
rhel5_cluster_storage = client.channel.software.listAllPackages(authKey,'rhel-i386-server-cluster-storage-5')
rhel5_cluster = client.channel.software.listAllPackages(authKey,'rhel-i386-server-cluster-5')
rhel5_prod = client.channel.software.listAllPackages(authKey,'rhel-i386-server-productivity-5')
rhel5_supp = client.channel.software.listAllPackages(authKey,'rhel-i386-server-supplementary-5')
rhel5_i386_all = rhel5 + rhel5_proxy + rhel5_tools + rhel5_cluster_storage + rhel5_cluster + rhel5_prod + rhel5_supp
for system in active_systems:
# re-auth every system to avoid timeouts
authKey = client.auth.login(login, password)
system_package_list = client.system.listPackages(authKey,system['id'])
package_dict = {}
unmatched_packages = []
try:
if system['arch'] == 'x86_64':
if system['release'] == '5Server':
if debug:
print >>sys.stderr, "RHEL5 64bit collected"
base_package_list = rhel5_x64_all
elif system['release'] == '4AS':
if debug:
print >>sys.stderr, "RHEL4 64bit collected"
base_package_list = rhel4_x64_all
else:
if debug:
print >>sys.stderr, "RHEL3 64bit collected"
base_package_list = rhel3_x64_all
if system['arch'] == 'i386' or system['arch'] == 'i686':
if system['release'] == '5Server':
if debug:
print >>sys.stderr, "RHEL5 32bit collected"
base_package_list = rhel5_i386_all
elif system['release'] == '4AS':
if debug:
print >>sys.stderr, "RHEL4 32bit collected"
base_package_list = rhel4_i386_all
else:
if debug:
print >>sys.stderr, "RHEL3 32bit collected"
base_package_list = rhel3_i386_all
except:
base_package_list = []
print 'WARNING, Unable to find packages -- System: %s, Arch: %s, Release: %s' % (system['name'],
system['arch'],
system['release'],)
continue
for package in system_package_list:
arch = getArch(package['arch'])
matched = False
for base_package in base_package_list:
pname = package['name'] == base_package['name']
parch = arch == base_package['arch_label']
release = package['release'] == base_package['release']
version = package['version'] == base_package['version']
if pname:
matched = True
pkey = base_package['name']+base_package['arch_label']
package_dict.setdefault(pkey,{})
package_dict[pkey][base_package['id']] = {}
package_dict[pkey][base_package['id']]['data'] = base_package
if release and version:
package_dict[pkey][base_package['id']]['current'] = True
else:
package_dict[pkey][base_package['id']]['current'] = False
if not matched:
unmatched_packages.append(package['name']+'.'+package['arch'])
bugfix_list = {}
security_list = {}
enhancement_list = {}
package_keys = package_dict.keys()
package_keys.sort()
for package in package_keys:
get_advisory = False
current = 0
pkg = package_dict[package]
pkg_keys = pkg.keys()
pkg_keys.sort()
for pkey in pkg_keys:
if pkg[pkey]['current']:
if not pkey == pkg_keys[-1]:
get_advisory = True
current = pkg[pkey]['data']['id']
continue
if get_advisory:
advisories = client.packages.listProvidingErrata(authKey,pkg[pkey]['data']['id'])
for advisory in advisories:
if 'RH' in advisory['advisory']:
if 'Bug Fix Advisory' in advisory['type']:
try:
bugfix_list[advisory['advisory']].append(pkg[pkey]['data'])
except:
bugfix_list[advisory['advisory']] = []
bugfix_list[advisory['advisory']].append(pkg[pkey]['data'])
if 'Security Advisory' in advisory['type']:
try:
security_list[advisory['advisory']].append(pkg[pkey]['data'])
except:
security_list[advisory['advisory']] = []
security_list[advisory['advisory']].append(pkg[pkey]['data'])
if 'Product Enhancement Advisory' in advisory['type']:
try:
enhancement_list[advisory['advisory']].append(pkg[pkey]['data'])
except:
enhancement_list[advisory['advisory']] = []
enhancement_list[advisory['advisory']].append(pkg[pkey]['data'])
#print 'ADVISORY ALERT %s:%s -- Package: %s %s %s, Base Package: %s %s %s' % (advisory['advisory'],
# advisory['type'],
# pkg[current]['data']['name'],
# pkg[current]['data']['version'],
# pkg[current]['data']['release'],
# pkg[pkey]['data']['name'],
# pkg[pkey]['data']['version'],
# pkg[pkey]['data']['release'])
print 'System: %s, Arch: %s, Release: %s, Bugfixes: %d, Security Fixes: %d, Enhancements: %d' % (system['name'],
system['arch'],
system['release'],
len(bugfix_list),
len(security_list),
len(enhancement_list))
if debug:
print
print 'UNMATCHED PACKAGES'
pprint.pprint(unmatched_packages)
print
print 'BUGFIX LIST'
pprint.pprint(bugfix_list)
print
print 'SECURITY LIST'
pprint.pprint(security_list)
print
print 'ENHANCEMENT LIST'
pprint.pprint(enhancement_list)
sys.stdout.flush()
client.auth.logout(authKey)
Comments