Skip to content
This repository has been archived by the owner on Oct 3, 2020. It is now read-only.

Commit

Permalink
Release 0.3.5
Browse files Browse the repository at this point in the history
Fixed several bugs:
- custom info key values are now escaped to avoid aborts (added shared
function escape_string; see issue #49)
- when cloning channels, labels are added to the name (see issue #47)
- changed name format to „label-date.channel“ when cloning channels to
avoid using protected names (see issues #46 and #48)
- fixed a bug to avoid scheduling downtimes for non-monitored hosts
- fixed a bug where VM snapshots were not created
- fixed a bug where wrong reboot_required information were inserted
into patch reports
- fixed a bug where diff reports could not be created (see issue #44)
- verification log files are created if non-existent
- added support for Spacewalk 2.4
  • Loading branch information
Christian Stankowic authored and Christian Stankowic committed Dec 18, 2015
1 parent 2995a0d commit 9e478d5
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 70 deletions.
59 changes: 22 additions & 37 deletions satprep_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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")
Expand Down Expand Up @@ -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__))

Expand Down Expand Up @@ -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.")
Expand All @@ -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('- '))
Expand Down Expand Up @@ -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=""
Expand Down Expand Up @@ -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 = ""
Expand Down Expand Up @@ -392,19 +375,14 @@ 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$"
this_vmSnapNo = "$\CheckedBox$"
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"]])
Expand All @@ -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=""
Expand Down
2 changes: 1 addition & 1 deletion satprep_install_custominfos.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
40 changes: 20 additions & 20 deletions satprep_patch_freeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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)
Expand All @@ -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")
Expand All @@ -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")
Expand All @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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")
Expand Down
15 changes: 9 additions & 6 deletions satprep_prepare_maintenance.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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"]] + ")")

Expand Down Expand Up @@ -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")
Expand Down
9 changes: 8 additions & 1 deletion satprep_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from datetime import datetime, timedelta
import libvirt
from fnmatch import fnmatch
import string



Expand All @@ -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"]



Expand Down Expand Up @@ -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])
19 changes: 15 additions & 4 deletions satprep_snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)



Expand Down
Loading

0 comments on commit 9e478d5

Please sign in to comment.