Created
August 6, 2016 00:42
-
-
Save gojun077/eef1cf74fa340863c74899104f99d25b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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