Skip to content

Instantly share code, notes, and snippets.

@gojun077
Created August 6, 2016 00:42
Show Gist options
  • Save gojun077/eef1cf74fa340863c74899104f99d25b to your computer and use it in GitHub Desktop.
Save gojun077/eef1cf74fa340863c74899104f99d25b to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# rhel-baseline-diff.py
# Jun Go [email protected]
# Last updated: 2016-08-03
# Given a baseline file that contains a list of rpm's
# from a RHEL installation DVD, compare this rpm list with
# the list of rpm's currently installed on a RHEL 5.X or
# RHEL 6.X machine and write any differences to a file.
# Note that the Python 2 version on RHEL 5.11 is 2.4.3
# while the version on RHEL 6.8 is 2.6.6
# This python script should work in both python 2 and 3
# as it does not use any 2-or-3-specific syntax or libraries
# and only uses language features available up to Python 2.4
# USAGE: ./rhel-baseline-diff.py [baseline file]
# This script will send output to the file 'rhel-diff.txt'
import optparse
import subprocess
import re
# Although 'optparse' has been deprecated in favor of 'argparse'
# starting from Python 2.7.X, the RHEL machines this script
# will be executed on use Python 2.6.X and 2.4.X
def chk_rhel_rel():
"""
-> string
Search /etc/redat-release for 'Tikanga' (RHEL 5),
'Santiago' (RHEL 6), or 'Maipo' (RHEL 7) and return
string with release codename
"""
rhel5 = r"Tikanga"
rhel6 = r"Santiago"
rhel7 = r"Maipo"
rhel5_obj = re.compile(rhel5)
rhel6_obj = re.compile(rhel6)
rhel7_obj = re.compile(rhel7)
rhrel = open("/etc/redhat-release", 'r')
try:
for line in rhrel:
match5 = rhel5_obj.search(line)
match6 = rhel6_obj.search(line)
match7 = rhel7_obj.search(line)
if match5:
return "Tikanga"
elif match6:
return "Santiago"
elif match7:
return "Maipo"
finally:
rhrel.close()
def check_rpm_macro():
"""
-> string
Look for %query_all_fmt line in /usr/lib/rpm/macros and verify
that 'architecture' is printed. The format for this setting
differs between RHEL 5.X or 6.X, however, so the search patterns
are different. If the pattern is not found, the function returns
the strings '5' or '6'. If the pattern is found, the function
returns blank string ''.
TODO: need to handle edge case in which search pattern is found
but the entire line is commented out with '#'
"""
# /usr/lib/rpm/macros pattern for RHEL5.X
pat1 = r'%%{name}-%%{version}-%%{release}.%%{arch}'
# /usr/lib/rpm/macros pattern for RHEL6.X
pat2 = r'%%{nvra}'
pat1_obj = re.compile(pat1)
pat2_obj = re.compile(pat2)
write_flag = ''
match_count = 0
# RHEL version string
rhel_ver = chk_rhel_rel()
f = open("/usr/lib/rpm/macros", 'r')
try:
if rhel_ver == "Tikanga":
for line in f:
match5 = pat1_obj.search(line)
if match5:
match_count += 1
if match_count == 0:
write_flag = '5'
return write_flag
elif rhel_ver == "Santiago":
for line in f:
match6 = pat2_obj.search(line)
if match6:
match_count += 1
if match_count == 0:
write_flag = '6'
return write_flag
finally:
f.close()
def write_rpm_macro(write_flag):
"""
String -> Write to file
Depending on the string 'write_flag', the proper '%query_all_fmt'
string for RHEL5.X / RHEL6.X will be appended to
/usr/lib/rpm/macros or not written at all
"""
f = open("/usr/lib/rpm/macros", 'a')
try:
if write_flag == '5':
f.write(
"\n%_query_all_fmt %%{name}-%%{version}-%%{release}.%%{arch}\n"
)
elif write_flag == '6':
f.write("\n%%{nvra}\n")
finally:
f.close
def load_baseline(filename):
"""
file -> LoS
Given a LF-delimited file containing rpm package names, this
function will read each line into a list and strip out newlines
"""
f = open(filename, 'r')
try:
baselineL = [line.strip() for line in f]
return baselineL
finally:
f.close()
def write_file(los, filename):
"""
ListOfString, text -> file
Given a list of strings and a filename, write each list element
to 'filename' and add a newline char with each element.
"""
g = open(filename, 'w')
try:
for elem in los:
g.write(elem + "\n")
finally:
g.close()
def current_rpmlist():
"""
No input -> LoS
This function will execute 'rpm -qa' on the target system
(which must be a RHEL variant) and insert the output into
a ListOfString while stripping out `\n' chars.
"""
rpmqa = subprocess.Popen("rpm -qa", shell=True, stdout=subprocess.PIPE)
out = rpmqa.stdout.readlines()
currentL = [line.strip() for line in out]
return currentL
def main():
p = optparse.OptionParser(description="",
prog="rhel-baseline-diff.py",
version="0.1",
usage="%prog [baseline-file]")
options, arguments = p.parse_args()
if len(arguments) == 1:
# Check if /usr/lib/rpm/macros is correctly set up for rpm -q
write_rpm_macro(check_rpm_macro())
# Filename of rpm baseline pkg list
filename = arguments[0]
# List of baseline rpm pkg's
baseL = load_baseline(filename)
# List of rpm pkg's currently installed on machine
currL = current_rpmlist()
# List of rpm's on machine that are identical to baseline
intersectL = list(set(currL).intersection(baseL))
# List of rpm's on machine that are different from baseline
diffL = list(set(currL) - set(intersectL))
print("No. of local pkgs identical to baseline:\n %s"
% len(intersectL))
print("No. of local pkgs different from baseline:\n %s"
% len(diffL))
# Sanity check - the number of installed pkg's must equal
# no. of identical pkg's + no. of different pkg's
assert len(intersectL) + len(diffL) == len(currL)
# print("Local pkgs identical to baseline:\n %s" % intersectL)
# print("Local packages different from baseline: \n %s" % diffL)
write_file(intersectL, "pkg-intersect.txt")
write_file(diffL, "pkg-diff.txt")
# print help message if no arguments are passed
else:
p.print_help()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment