diff --git a/satprep_diff.py b/satprep_diff.py index 9c96480..f44fc00 100755 --- a/satprep_diff.py +++ b/satprep_diff.py @@ -23,7 +23,6 @@ #define logger LOGGER = logging.getLogger('satprep_diff') -vlogInvalid=False #TODO: delete original snapshots after creating delta option #TODO: use pre-existing delta CSV instead of creating one (e.g. for testing purposes) @@ -42,7 +41,7 @@ def parse_options(args=None): desc='''%prog is used to create patch diff reports of systems managed with Spacewalk, Red Hat Satellite and SUSE Manager. The script needs TeXlive/LaTeX to create PDF reports. Defining your own templates is possible - the default template needs to be located in the same directory like this script. Checkout the GitHub page for updates: https://github.com/stdevel/satprep''' - parser = OptionParser(usage=usage, description=desc, version="%prog version 0.3.4") + parser = OptionParser(usage=usage, description=desc, version="%prog version 0.3.5") #define option groups genOpts = OptionGroup(parser, "Generic Options") repOpts = OptionGroup(parser, "Report Options") @@ -86,8 +85,6 @@ def parse_options(args=None): def main(options): - global vlogInvalid - #define folder of this script thisFolder = os.path.dirname(os.path.realpath(__file__)) @@ -161,14 +158,7 @@ def main(options): this_date = datetime.datetime.fromtimestamp(os.path.getmtime(args[1])).strftime('%Y-%m-%d') #set vlog if options.verificationLog == "": - if os.access(datetime.datetime.fromtimestamp(os.path.getmtime(args[0])).strftime('%Y%m%d')+"_satprep.vlog", os.W_OK): - options.verificationLog = datetime.datetime.fromtimestamp(os.path.getmtime(args[0])).strftime('%Y%m%d')+"_satprep.vlog" - elif os.access(options.verificationLog, os.W_OK) != True: - if os.access(datetime.datetime.fromtimestamp(os.path.getmtime(args[0])).strftime('%Y%m%d')+"_satprep.vlog", os.W_OK): - options.verificationLog = datetime.datetime.fromtimestamp(os.path.getmtime(args[0])).strftime('%Y%m%d')+"_satprep.vlog" - else: options.verificationLog = "" - if options.verificationLog != "": LOGGER.info(options.verificationLog + " seems to be our verification log") - else: LOGGER.info("Snapshot and monitoring checkboxes won't be pre-selected as we don't have a valid .vlog!") + options.verificationLog = datetime.datetime.fromtimestamp(os.path.getmtime(args[0])).strftime('%Y%m%d')+"_satprep.vlog" else: #file2 is bigger LOGGER.info("Assuming file2 ('"+args[1]+"') is the first snapshot.") @@ -180,23 +170,15 @@ def main(options): this_date = datetime.datetime.fromtimestamp(os.path.getmtime(args[0])).strftime('%Y-%m-%d') #set vlog if options.verificationLog == "": - if os.access(datetime.datetime.fromtimestamp(os.path.getmtime(args[1])).strftime('%Y%m%d')+"_satprep.vlog", os.W_OK): - options.verificationLog = datetime.datetime.fromtimestamp(os.path.getmtime(args[1])).strftime('%Y%m%d')+"_satprep.vlog" - elif os.access(options.verificationLog, os.W_OK) != True: - if os.access(datetime.datetime.fromtimestamp(os.path.getmtime(args[1])).strftime('%Y%m%d')+"_satprep.vlog", os.W_OK): - options.verificationLog = datetime.datetime.fromtimestamp(os.path.getmtime(args[1])).strftime('%Y%m%d')+"_satprep.vlog" - else: options.verificationLog = "" - if options.verificationLog != "": LOGGER.info(options.verificationLog + " seems to be our verification log") - else: - LOGGER.info("Snapshot and monitoring checkboxes won't be pre-selected as we don't have a valid .vlog!") - vlogInvalid=True + options.verificationLog = datetime.datetime.fromtimestamp(os.path.getmtime(args[1])).strftime('%Y%m%d')+"_satprep.vlog" #read vlog - if vlogInvalid == False: + if os.path.exists(options.verificationLog): f_log = open(options.verificationLog, 'r') - vlog = f_log.read() - LOGGER.debug("vlog is:\n" + vlog) - else: vlog="" + else: + f_log = open(options.verificationLog, 'w+') + vlog = f_log.read() + LOGGER.debug("vlog is:\n" + vlog) #create delta delta = ''.join(x[2:] for x in diff if x.startswith('- ')) @@ -249,9 +231,11 @@ def main(options): for host in hosts: #scan imported data - #flag for NoReboot Box and notes + #pre-setting some variables this_NoReboot="$\Box$" this_RebootNotes="" + this_monYes="$\Box$" + this_monNo="$\Box$" #LaTeX line including the the errata-relevant columns for this host thisColDescriptor="" @@ -338,14 +322,13 @@ def main(options): if "@" in tempHost: tempHost = tempHost[:tempHost.find("@")] else: tempHost = host if "MONOK;"+host in vlog: - LOGGER.debug("MONOK;"+tempHost + " in vlog!") + LOGGER.debug("MONOK;"+tempHost+" in vlog!") this_monYes = "$\CheckedBox$" this_monNo = "$\Box$" else: - LOGGER.debug("MONOK;"+tempHost + " NOT in vlog!") + LOGGER.debug("MONOK;"+tempHost+" NOT in vlog!") this_monYes = "$\Box$" - if vlogInvalid == False: this_monNo = "$\CheckedBox$" - else: this_monNo = "$\Box$" + this_monNo = "$\CheckedBox$" if repcols["system_monitoring_notes"] < 666 and len(line[repcols["system_monitoring_notes"]]) > 1: this_monNotes = line[repcols["system_monitoring_notes"]] else: this_monNotes = "" @@ -392,7 +375,7 @@ def main(options): else: LOGGER.debug("SNAPOK;"+tempHost + " NOT in vlog!") this_vmSnapYes = "$\Box$" - if vlogInvalid == False: this_vmSnapNo = "$\CheckedBox$" + this_vmSnapNo = "$\CheckedBox$" else: #set boxes and notes this_hwCheckNo = "$\Box$" @@ -400,11 +383,6 @@ def main(options): this_hwCheckNotes = "" this_vmSnapNotes ="not a virtual machine" - #set reboot box if specified and present in report - if repcols["errata_reboot"] < 666 and line[repcols["errata_reboot"]] != "reboot_suggested": - this_NoReboot="$\CheckedBox$" - this_RebootNotes="no reboot required" - #set errata information if repcols["errata_name"] < 666 and line[repcols['errata_name']] != "": this_errata_name.append(line[repcols["errata_name"]]) @@ -416,6 +394,13 @@ def main(options): this_errata_type.append(line[repcols["errata_type"]]) if repcols["errata_reboot"] < 666 and line[repcols["errata_reboot"]] != "": this_errata_reboot.append(line[repcols["errata_reboot"]]) + + #set reboot box if specified and present in report + rebootErrata = [s for e in this_errata_reboot if e == "1"] + print len(rebootErrata) + if len(rebootErrata) == 0: + this_NoReboot="$\CheckedBox$" + this_RebootNotes="no reboot required" #add errata tempRow="" diff --git a/satprep_install_custominfos.py b/satprep_install_custominfos.py index 12db41f..4b9e619 100755 --- a/satprep_install_custominfos.py +++ b/satprep_install_custominfos.py @@ -111,7 +111,7 @@ def parse_options(args=None): If you're not defining variables or an authfile you will be prompted to enter your login information. Checkout the GitHub page for updates: https://github.com/stdevel/satprep''' - parser = OptionParser(description=desc, version="%prog version 0.3.4") + parser = OptionParser(description=desc, version="%prog version 0.3.5") #define option groups genOpts = OptionGroup(parser, "Generic Options") srvOpts = OptionGroup(parser, "Server Options") diff --git a/satprep_patch_freeze.py b/satprep_patch_freeze.py index c50fe08..3dd0077 100755 --- a/satprep_patch_freeze.py +++ b/satprep_patch_freeze.py @@ -61,7 +61,7 @@ def getChannels(client, key): LOGGER.debug("This system's profile ID: {0}".format(hostId)) baseChannel = client.system.getSubscribedBaseChannel(key, hostId[0]["id"]) cleanBase = baseChannel["label"] - if ".sp" in cleanBase: cleanBase = cleanBase[:cleanBase.find(".sp")] + if "." in cleanBase: cleanBase = cleanBase[cleanBase.find(".")+1:] if cleanBase not in myChannels: #channel non-present LOGGER.debug("Adding channel '{0}'".format(cleanBase)) @@ -70,7 +70,7 @@ def getChannels(client, key): childChannels = client.system.listSubscribedChildChannels(key, hostId[0]["id"]) for channel in childChannels: cleanChild = channel["label"] - if ".sp" in cleanChild: cleanChild = cleanChild[:cleanChild.find(".sp")] + if "." in cleanChild: cleanChild = cleanChild[:cleanChild.find(".")+1:] if cleanChild not in myChannels[cleanBase]: LOGGER.debug("Adding child-channel '{0}'".format(cleanChild)) myChannels[cleanBase].append(cleanChild) @@ -94,34 +94,34 @@ def cloneChannels(client, key, date, label, unfreeze=False): for channel in myChannels: #remove child-channels for child in myChannels[channel]: - if options.dryrun: LOGGER.info("I'd like to remove cloned child-channel '{0}'".format(child+"."+options.targetLabel+"-"+options.targetDate)) + if options.dryrun: LOGGER.info("I'd like to remove cloned child-channel '{0}'".format(options.targetLabel+"-"+options.targetDate+"."+child)) else: try: - LOGGER.info("Deleting child-channel '{0}'".format(child+"-"+options.targetLabel+"-"+options.targetDate)) - result = client.channel.software.delete(key, child+"."+options.targetLabel+"-"+options.targetDate) + LOGGER.info("Deleting child-channel '{0}'".format(options.targetLabel+"-"+options.targetDate+"."+child)) + result = client.channel.software.delete(key, options.targetLabel+"-"+options.targetDate+"."+child) except xmlrpclib.Fault as e: - LOGGER.error("Unable to remove child-channel '{0}': '{1}'".format(child+"-"+options.targetLabel+"-"+options.targetDate, e.faultString)) + LOGGER.error("Unable to remove child-channel '{0}': '{1}'".format(options.targetLabel+"-"+options.targetDate+"."+child, e.faultString)) except xmlrpclib.ProtocolError as e: - LOGGER.error("Unable to remove child-channel '{0}': '{1}'".format(child+"-"+options.targetLabel+"-"+options.targetDate, e.errmsg)) + LOGGER.error("Unable to remove child-channel '{0}': '{1}'".format(options.targetLabel+"-"+options.targetDate+"."+child, e.errmsg)) except: - LOGGER.error("Unable to remove child-channel '{0}'!".format(child+"-"+options.targetLabel+"-"+options.targetDate)) + LOGGER.error("Unable to remove child-channel '{0}'!".format(options.targetLabel+"-"+options.targetDate+"."+child)) #remove base-channel if options.dryrun: LOGGER.info("I'd like to remove cloned base-channel '{0}'".format(channel+"."+options.targetLabel+"-"+options.targetDate)) else: try: LOGGER.info("Deleting base-channel '{0}'".format(channel+"."+options.targetLabel+"-"+options.targetDate)) - result = client.channel.software.delete(key, channel+"."+options.targetLabel+"-"+options.targetDate) + result = client.channel.software.delete(key, options.targetLabel+"-"+options.targetDate+"."+channel) except: LOGGER.error("Unable to remove base-channel '{0}'!".format(channel)) return True #clone channels for channel in myChannels: #clone base-channels - myargs={"name" : channel+" clone from "+options.targetDate, "label" : channel+"."+options.targetLabel+"-"+options.targetDate, "summary" : "Software channel cloned by Satprep"} + myargs={"name" : channel+" clone from "+options.targetDate+" ("+options.targetLabel+")", "label" : options.targetLabel+"-"+options.targetDate+"."+channel, "summary" : "Software channel cloned by Satprep"} if options.dryrun: - LOGGER.info("I'd like to clone base-channel '{0}' as '{1}'".format(channel, channel+"."+options.targetLabel+"-"+options.targetDate)) + LOGGER.info("I'd like to clone base-channel '{0}' as '{1}'".format(channel, options.targetLabel+"-"+options.targetDate+"."+channel)) else: - LOGGER.info("Cloning base-channel '{0}' as '{1}'".format(channel, channel+"."+options.targetLabel+"-"+options.targetDate)) + LOGGER.info("Cloning base-channel '{0}' as '{1}'".format(channel, options.targetLabel+"-"+options.targetDate+"."+channel)) try: result = client.channel.software.clone(key, channel, myargs, False) if result != 0: LOGGER.debug("Cloned base-channel") @@ -134,11 +134,11 @@ def cloneChannels(client, key, date, label, unfreeze=False): #clone child-channels for child in myChannels[channel]: - myargs={"name" : child+" clone from "+options.targetDate, "label" : child+"."+options.targetLabel+"-"+options.targetDate, "summary" : "Software channel cloned by Satprep", "parent_label": channel+"."+options.targetLabel+"-"+options.targetDate} + myargs={"name" : child+" clone from "+options.targetDate, "label" : options.targetLabel+"-"+options.targetDate+"."+child, "summary" : "Software channel cloned by Satprep", "parent_label": options.targetLabel+"-"+options.targetDate+"."+channel} if options.dryrun: - LOGGER.info("I'd like to clone child-channel '{0}' as '{1}'".format(child, child+"."+options.targetLabel+"-"+options.targetDate)) + LOGGER.info("I'd like to clone child-channel '{0}' as '{1}'".format(child, options.targetLabel+"-"+options.targetDate+"."+child)) else: - LOGGER.info("Cloning child-channel '{0}' as '{1}'".format(child, child+"."+options.targetLabel+"-"+options.targetDate)) + LOGGER.info("Cloning child-channel '{0}' as '{1}'".format(child, options.targetLabel+"-"+options.targetDate+"."+child)) try: result = client.channel.software.clone(key, child, myargs, False) if result != 0: LOGGER.debug("Cloned child-channel") @@ -161,8 +161,8 @@ def remapSystems(client, key, unfreeze=False): myBase = client.system.getSubscribedBaseChannel(key, hostId[0]["id"]) if options.unfreeze: myNewBase = myBase["label"] - myNewBase = myNewBase[:myNewBase.find(".sp")] - else: myNewBase = myBase["label"]+"."+options.targetLabel+"-"+options.targetDate + myNewBase = myNewBase[myNewBase.find(".")+1:] + else: myNewBase = options.targetLabel+"-"+options.targetDate+"."+myBase["label"] if options.dryrun: LOGGER.info("I'd like to remap {0}'s base-channel from {1} to {2}".format(system, myBase["label"], myNewBase)) else: @@ -181,10 +181,10 @@ def remapSystems(client, key, unfreeze=False): myNewChannel = channel["label"] if options.unfreeze: #switch back to non-cloned - myNewChannel = myNewChannel[:myNewChannel.find(".sp")] + myNewChannel = myNewChannel[myNewChannel.find(".")+1:] else: #switch to cloned - myNewChannel = channel["label"]+"."+options.targetLabel+"-"+options.targetDate + myNewChannel = options.targetLabel+"-"+options.targetDate+"."+channel["label"] tmpChannels.append(myNewChannel) if options.dryrun: LOGGER.info("I'd like to set the following child-channels for {0}: {1}".format(system, str(tmpChannels))) else: @@ -252,7 +252,7 @@ def parse_options(args=None): If you're not defining variables or an authfile you will be prompted to enter your login information. Checkout the GitHub page for updates: https://github.com/stdevel/satprep''' - parser = OptionParser(description=desc, version="%prog version 0.3.4") + parser = OptionParser(description=desc, version="%prog version 0.3.5") #define option groups genOpts = OptionGroup(parser, "Generic Options") srvOpts = OptionGroup(parser, "Server Options") diff --git a/satprep_prepare_maintenance.py b/satprep_prepare_maintenance.py index 30696c4..3a13bb6 100755 --- a/satprep_prepare_maintenance.py +++ b/satprep_prepare_maintenance.py @@ -48,8 +48,10 @@ def verify(): #check whether the output directory/file is writable if os.access(os.getcwd(), os.W_OK): LOGGER.debug("Output file/directory writable!") - myLog = open(myPrefix+"_satprep.vlog", "r+") - #myFile = myLog.readlines() + if os.path.exists(myPrefix+"_satprep.vlog"): + myLog = open(myPrefix+"_satprep.vlog", "r+") + else: + myLog = open(myPrefix+"_satprep.vlog", "w+") myFile = myLog.read().splitlines() LOGGER.debug("vlog before customization: ***\n" + str(myFile)) else: @@ -326,16 +328,17 @@ def readFile(file): LOGGER.debug("Script parameters are avoiding creating snapshot for '" + this_name + "' (P:" + row[repcols["system_prod"]] + ")") else: #add host to downtimeHosts if reboot required and monitoring flag set, add custom names if defined - if row[repcols["system_monitoring"]] == "1" and row[repcols["errata_reboot"]] == "1": + if repcols["errata_reboot"] < 666 and row[repcols["errata_reboot"]] == "1": #handle custom name if row[repcols["system_monitoring"]] == "1" and row[repcols["system_monitoring_name"]] != "": this_name = row[repcols["system_monitoring_name"]] - else: this_name = row[repcols["hostname"]] + elif row[repcols["system_monitoring"]] == "1": + this_name = row[repcols["hostname"]] #only add if prod/nonprod modes aren't avoiding it if (row[repcols["system_prod"]] == "1" and options.nonprodOnly == False) \ or (row[repcols["system_prod"]] != "1" and options.prodOnly == False) \ or (options.prodOnly == False and options.nonprodOnly == False): - if is_blacklisted(row[repcols["hostname"]], options.exclude) == False: downtimeHosts.append(this_name) + if is_blacklisted(row[repcols["hostname"]], options.exclude) == False and row[repcols["system_monitoring"]] == "1": downtimeHosts.append(this_name) LOGGER.debug("Downtime will be scheduled for '" + this_name + "' (P:" + row[repcols["system_prod"]] + ")") else: LOGGER.debug("Script parameters are avoiding scheduling downtime for '" + this_name + "' (P:" + row[repcols["system_prod"]] + ")") @@ -404,7 +407,7 @@ def parse_options(args=None): Check-out the GitHub documentation (https://github.com/stdevel/satprep) for further information. ''' - parser = OptionParser(usage=usage, description=desc, version="%prog version 0.3.4") + parser = OptionParser(usage=usage, description=desc, version="%prog version 0.3.5") #define option groups genOpts = OptionGroup(parser, "Generic Options") monOpts = OptionGroup(parser, "Monitoring Options") diff --git a/satprep_shared.py b/satprep_shared.py index f117514..db5ecdb 100755 --- a/satprep_shared.py +++ b/satprep_shared.py @@ -12,6 +12,7 @@ from datetime import datetime, timedelta import libvirt from fnmatch import fnmatch +import string @@ -20,7 +21,7 @@ LIBVIRT_PASSWORD="" LOGGER = logging.getLogger('satprep-shared') -SUPPORTED_API_LEVELS = ["11.1", "12", "13", "13.0", "14", "14.0", "15", "15.0", "16", "16.0"] +SUPPORTED_API_LEVELS = ["11.1", "12", "13", "13.0", "14", "14.0", "15", "15.0", "16", "16.0", "17", "17.0"] @@ -301,3 +302,9 @@ def get_systems_by_systemgroup(client, key, group): #get systems by system group systems = client.systemgroup.listSystems(key, group) LOGGER.debug(systems) + + + +def escape_string(str): + temp=filter(string.printable.__contains__,str) + return ''.join([c for c in temp if ord(c) > 31 or ord(c) == 9]) diff --git a/satprep_snapshot.py b/satprep_snapshot.py index 05299b1..036167a 100755 --- a/satprep_snapshot.py +++ b/satprep_snapshot.py @@ -17,7 +17,7 @@ import time import xmlrpclib from optparse import OptionParser, OptionGroup -from satprep_shared import check_if_api_is_supported, get_credentials +from satprep_shared import check_if_api_is_supported, get_credentials, escape_string from unidecode import unidecode @@ -47,7 +47,7 @@ def parse_options(args=None): If you're not defining variables or an authfile you will be prompted to enter your login information. Checkout the GitHub page for updates: https://github.com/stdevel/satprep''' - parser = OptionParser(description=desc, version="%prog version 0.3.4") + parser = OptionParser(description=desc, version="%prog version 0.3.5") #define option groups genOpts = OptionGroup(parser, "Generic Options") parser.add_option_group(genOpts) @@ -349,6 +349,8 @@ def process_errata(client, key, writer, system): ascii=unidecode(field) )) valueSet[i] = str(unidecode(field)) + #remove crap + valueSet[i] = escape_string(str(valueSet[i])) writer.writerow(valueSet) @@ -517,8 +519,17 @@ def process_patches(client, key, writer, system): else: valueSet.append("") - if valueSet: - writer.writerow(valueSet) + #replace unicodes + for i,field in enumerate(valueSet): + if type(field) is unicode: + LOGGER.debug("Converted to ascii: {ascii}".format( + ascii=unidecode(field) + )) + valueSet[i] = str(unidecode(field)) + #remove crap + valueSet[i] = escape_string(str(valueSet[i])) + + writer.writerow(valueSet) diff --git a/satprep_wa_vcvms.py b/satprep_wa_vcvms.py index 5a79aeb..dc20b06 100755 --- a/satprep_wa_vcvms.py +++ b/satprep_wa_vcvms.py @@ -204,7 +204,7 @@ def parse_options(args=None): It is also possible to create an authfile (permissions 0600) for usage with this script (parameters -a/-A). The first line needs to contain the username, the second line should consist of the appropriate password. If you're not defining variables or an authfile you will be prompted to enter your login information. Checkout the GitHub wiki for further information: https://github.com/stdevel/satprep/wiki''' - parser = OptionParser(description=desc, version="%prog version 0.3.4") + parser = OptionParser(description=desc, version="%prog version 0.3.5") #define option groups genOpts = OptionGroup(parser, "Generic Options")