I've attached the script that one of our temps wrote to remove systems
that have not checked in over a year (but it is configurable) and warn
if systems have been offline for over a given period of time. Maybe it
is what you are looking for.
We just run it out of cron with a number of command line switches.
Dave
On 07/22/2011 02:02 PM, [email protected] wrote:
Is there a script (supported or otherwise) to remove stale system
entries from a spacewalk/RHN server?
Say I have a testing environment with limited RHEL entitlements, and
people are constantly registering servers, do what they need to do,
and then re-image the server or shut it down and move on to something
else without manually removing the system entry from RHN before moving
on. These stale entries not only populate the database needlessly, but
also take up an entitlement.
Would be great if I could run a nightly cron job where server entries
that have not checked in for X days are removed automatically...
#!/usr/bin/python
import xmlrpclib
import sys
import getopt
from string import atoi, atoi_error
import datetime
import os
##############################################################################################
# Variable descriptions:
#
# SATELLITE_URL: The URL of the RHEL Satellite server.
# DATE_FORMAT: Format of the date and time. The format must coincide with the
date format from
# xmlrpclib, and should only be changed if that changes.
# EMAIL_SUBJECT: Subject heading of the email.
# MAIL_EXEC: The command used to send the email. The assumption is that
sendmail is used, but
# any Linux command line mail sender will likely work.
#
# Copyright 2011, Regents of the University of Michigan. Free for
# non-profit use. Any redistribution of this code must be accompanied
# by this copyright notice.
##############################################################################################
SATELLITE_URL = "http://<server>/rpc/api"
DATE_FORMAT = "%Y%m%dT%H:%M:%S"
EMAIL_SUBJECT = "RHN Satellite Automatic Removal of Inactive Profiles"
EMAIL_ADDRESS = ["<user>@umich.edu"]
EMAIL_EXEC = "/usr/sbin/sendmail"
EMAIL_MESSAGE = """The following is an overview of the inactive profiles on the
Satellite,
and details when inactive profiles will be deleted. You can add the word
\"HIATUS\" or \"DUALBOOT\"
(case-insensitive) to the end of the name of any profile on the server to have
it excluded from
this script.
Any questions should be sent to LSA RSG ([email protected]).
"""
DELETE_DAYS = 365
CAUTION_DAYS = [100, 200, 300]
WARNING_DAYS = [DELETE_DAYS - 14, DELETE_DAYS - 7]
VERBOSE = False
LOG_FILE = "/var/log/remove-inactive-profiles.log"
INCLUDE_VIRTUAL = False
EXCLUDE = []
DRY = False
SEND = False
SAFE_WORD = ["hiatus", "dualboot", "duelboot"]
GRACE_PERIOD = 7
def usage():
print "Use this script to automatically remove old profiles from"
print "the satellite server."
print ""
print "Required Arguments:"
print " -l, --login=FILE FILE should be a two-line file, where
the first line is"
print " the account name used to log into the
satellite, and"
print " the second line is the password
associated with the"
print " account."
print ""
print "Control Arguments:"
print " -c, --caution-days=[100,200,300]"
print " Takes a comma separated list of
integers. Profiles inactive for"
print " each of these numbers of days (within
grace period) will be listed."
print " -w, --warning-days=[351,358]"
print " Takes a comma separated list of
integers. Profiles inactive for"
print " each of these numbers of days receive
a warning."
print " -d, --delete-days=[365] The threshold for which a profile is
considered old."
print " Profiles that have been inactive for
at least this long"
print " will be deleted."
print " -g, --grace=[7] The size of the window for machines
that will receive cautions, in"
print " days. Does not affect warnings or
deletions. It is recommended this"
print " be set to the frequency this script is
run."
print " -s --send=[[email protected]]"
print " Send an email to the user specified."
print " By default, no emails will be sent."
print " -f, --log-file=[/var/log/remove-inactive-profiles.log]"
print " The log file where output is stored."
print " -e, --exclude=KEYWORD Use this option to prevent this script
from deleting"
print " profile names which contain the given
KEYWORD. This check is"
print " case-insensitive."
print " -i, --include-virtual Include this argument if you would
also like to delete"
print " old profiles of virtual systems. By
default, virtualized"
print " systems will NOT be deleted."
print ""
print "Other Arguments:"
print " -h, --help Display this help and exit"
print " -v, --verbose Display the logging messages to stdout
as well."
print " -D, --dry Do output as normal, except make no
changes."
print ""
return
def email(fromName, fromAddress, ccList, bccList, toAddresses, subject,
message):
outFileName = "/tmp/mail-parse-data-to-mailer"
cmd = "rm -f " + outFileName
os.system(cmd)
outFile = open(outFileName, 'a')
addressees = toAddresses[0]
for i in toAddresses[1:]:
addressees += ", "
addressees += i
outFile.write("To: " + addressees + "\n")
outFile.write("From: \"" + fromName + "\" <" + fromAddress + ">\n")
if ccList != "":
outFile.write("Cc: " + ccList + "\n")
if bccList != "":
outFile.write("Bcc: " + bccList + "\n")
outFile.write("Subject: " + subject + "\n")
#outFile.write("Content-Type: text/html; charset=\"us-ascii\"\n\n")
#outFile.write("<html>\n")
#outFile.write("<body>\n")
for line in message:
outFile.write(line)
outFile.write("\n\n")
#outFile.write("</body>\n")
#outFile.write("</html>\n")
outFile.close()
#cmd = EMAIL_EXEC + " -s \"%s\" %s < \"%s\"" % (subject, address,
message)
cmd = EMAIL_EXEC + " -i -t < %s" % outFileName
#print cmd
#system("cat " + outFileName)
rc = os.system(cmd)
return rc
#### Handle Command-line Arguments ####
try:
opts, args = getopt.getopt(sys.argv[1:], "l:f:e:c:w:d:g:ihvsD",
["login=",
"log-file=",
"exclude=",
"caution-days=",
"warning-days=",
"delete-days=",
"grace=",
"include-virtual",
"help",
"verbose",
"send",
"dry"])
except getopt.GetoptError:
usage()
sys.exit(1)
for opt, arg in opts:
if opt in ("-h", "--help"):
usage()
sys.exit(0)
elif opt in ("-v", "--verbose"):
VERBOSE = True
elif opt in ("-l", "--login"):
credentialsFileName = arg
elif opt in ("--caution-days"):
CAUTION_DAYS = []
s = str(arg)
s = s.strip()
list = s.split(',')
for i in list:
try:
CAUTION_DAYS.append(atoi(arg))
except:
print "\nValue for caution-days must be
comma-separated list of integers\n"
sys.exit(1)
elif opt in ("--warning-days"):
WARNING_DAYS = []
s = str(arg)
s = s.strip()
list = s.split(',')
for i in list:
try:
WARNING_DAYS.append(atoi(arg))
except:
print "\nValue for warning-days must be
comma-separated list of integers\n"
sys.exit(1)
elif opt in ("--delete-days"):
DELETE_DAYS = []
s = str(arg)
s = s.strip()
try:
DELETE_DAYS = atoi(arg)
except:
print "\nValue for delete-days must be a positive
integer\n"
sys.exit(1)
elif opt in ("-f", "--log-file"):
LOG_FILE = arg
elif opt in ("-i", "--include-virtual"):
INCLUDE_VIRTUAL = True
elif opt in ("-e", "--exclude"):
SAFE_WORD.append(arg)
elif opt in ("-D", "--dry"):
DRY = True
elif opt in ("-s", "--send"):
SEND = True
elif opt in ("-g", "--grace"):
try:
GRACE_PERIOD = atoi(arg)
except:
print "\nValue for grace must be a positive integer\n"
sys.exit(1)
login = ""
password = ""
credentialsFile = open(credentialsFileName, 'r')
login = credentialsFile.readline()
password = credentialsFile.readline()
credentialsFile.close()
login = login.strip()
password = password.strip()
if login == "" or password == "":
print "\nPlease supply a login name and password."
usage()
sys.exit(4)
###end argument processing
log = open(LOG_FILE, 'a', 0) #append, unbuffered
log.write("\n\n")
log.write(" ".join(sys.argv[0:]))
out = ""
if DRY == True:
out = "Doing a DRY RUN------Profiles will NOT be deleted!"
log.write("\n" + out)
if VERBOSE : print out
###Connect to the server
try:
client = xmlrpclib.Server(SATELLITE_URL, verbose=0)
out = "Connected to %s successfully." % SATELLITE_URL
log.write("\n" + out)
if VERBOSE : print out
except:
out = "Unable to connect to server %s\nExiting\n" % SATELLITE_URL
log.write("\n" + out)
if VERBOSE : print out
sys.exit(2)
###Open a session
try:
session = client.auth.login(login, password)
out = "Session created with login = %s and supplied password" % login
log.write("\n" + out)
if VERBOSE : print out
except:
out = "Unable to create session with login = %s and supplied
password\n" % login
log.write("\n" + out)
if VERBOSE : print out
sys.exit(3)
##### rawr
allGroupsRaw = client.systemgroup.listAllGroups(session) #list of all system
groups visible to user
allGroups = []
for group in allGroupsRaw:
allGroups.append(group.get('name'))
cautionDates = {}
warningDates = {}
cautionSystems = {}
removeFromCautionSystems = {}
warningSystems = {}
deleteSystems = {}
today = datetime.datetime.today()
out = "Today is %s" % today.strftime(DATE_FORMAT)
deletionDate = today - datetime.timedelta(days=DELETE_DAYS)
for number in CAUTION_DAYS:
cautionDates[number] = today - datetime.timedelta(days=number) #gets
the dates
out += "\nProfiles inactive for %s days (since %s) are marked for
future deletion." % (number, cautionDates[number].strftime(DATE_FORMAT))
cautionSystems[number] = {}
removeFromCautionSystems[number] = {}
for group in allGroups:
cautionSystems[number][group] =
client.systemgroup.listInactiveSystemsInGroup(session, group, number) #gets
systems
removeFromCautionSystems[number][group] =
client.systemgroup.listInactiveSystemsInGroup(session, group, number +
GRACE_PERIOD) #gets systems
for number in WARNING_DAYS:
warningDates[number] = today - datetime.timedelta(days=number) #gets
the dates
out += "\nProfiles inactive for %s days (since %s) will be deleted on
%s." % (number, warningDates[number].strftime(DATE_FORMAT),
deletionDate.strftime(DATE_FORMAT))
warningSystems[number] = {}
for group in allGroups:
warningSystems[number][group] =
client.systemgroup.listInactiveSystemsInGroup(session, group, number) #gets
systems
out += "\nProfiles inactive for %s days (since %s) will be deleted." %
(DELETE_DAYS, deletionDate.strftime(DATE_FORMAT))
for group in allGroups:
deleteSystems[group] =
client.systemgroup.listInactiveSystemsInGroup(session, group, DELETE_DAYS)
log.write("\n" + out)
if VERBOSE : print out
del allGroups
###Construct sets of systems by sysid -- note that "sys" here is a sysid
cautionIds = {}
warningIds = {}
deleteIds = {}
names = {}
working = 0
for number in cautionSystems.keys():
for group in cautionSystems[number].keys():
for sys in cautionSystems[number][group]:
working += 1
if VERBOSE and working % 25 == 1:
print "."
sysName = client.system.getName(session, sys)
names[sys] = sysName.get('name')
isSafe = True
for word in SAFE_WORD:
if word.lower() in str(sysName).lower():
isSafe = False
break
if isSafe == True:
if number not in cautionIds:
cautionIds[number] = {}
if group not in cautionIds[number]:
cautionIds[number][group] = set()
cautionIds[number][group].add(sys)
for number in warningSystems.keys():
for group in warningSystems[number].keys():
for sys in warningSystems[number][group]:
working += 1
if VERBOSE and working % 25 == 1:
print "."
sysName = client.system.getName(session, sys)
names[sys] = sysName.get('name')
isSafe = True
for word in SAFE_WORD:
if word.lower() in str(sysName).lower():
isSafe = False
break
if isSafe == True:
if number not in warningIds:
warningIds[number] = {}
if group not in warningIds[number]:
warningIds[number][group] = set()
warningIds[number][group].add(sys)
for group in deleteSystems.keys():
for sys in deleteSystems[group]:
working += 1
if VERBOSE and working % 25 == 1:
print "."
sysName = client.system.getName(session, sys)
names[sys] = sysName.get('name')
isSafe = True
for word in SAFE_WORD:
if word.lower() in str(sysName).lower():
isSafe = False
break
if isSafe == True:
if group not in deleteIds:
deleteIds[group] = set()
deleteIds[group].add(sys)
print "\n"
del cautionSystems
del warningSystems
del deleteSystems
if INCLUDE_VIRTUAL == False:
#Get the list
virtHosts = client.system.listVirtualHosts(session)
virtIdList = set()
for host in virtHosts:
guestsOnHost = client.system.listVirtualGuests(session,
host.get('id'))
for guest in guestsOnHost:
virtIdList.add(guest.get('id'))
guestsOnHost = []
###Remove any overlap
numbers = sorted(cautionIds.keys())
for i in range(len(numbers)):
groups = cautionIds[numbers[i]].keys()
for j in range(i+1, len(cautionIds)):
for group in groups:
if group in cautionIds[numbers[j]].keys():
cautionIds[numbers[i]][group] =
cautionIds[numbers[i]][group] - cautionIds[numbers[j]][group]
for warningNumber in warningIds.keys():
for group in groups:
if group in warningIds[warningNumber].keys():
cautionIds[numbers[i]][group] =
cautionIds[numbers[i]][group] - warningIds[warningNumber][group]
for group in groups:
if group in deleteIds.keys():
cautionIds[numbers[i]][group] =
cautionIds[numbers[i]][group] - deleteIds[group]
#cautionIds[numbers[i]][group] = cautionIds[numbers[i]][group]
- virtHostIds
if INCLUDE_VIRTUAL == False:
cautionIds[numbers[i]][group] =
cautionIds[numbers[i]][group] - virtIdList
numbers = sorted(warningIds.keys())
for i in range(len(numbers)):
groups = warningIds[numbers[i]].keys()
for j in range(i+1, len(warningIds)):
for group in groups:
if group in warningIds[numbers[j]].keys():
warningIds[numbers[i]][group] =
warningIds[numbers[i]][group] - warningIds[numbers[j]][group]
for group in groups:
if group in deleteIds.keys():
warningIds[numbers[i]][group] =
warningIds[numbers[i]][group] - deleteIds[group]
#warningIds[numbers[i]][group] = warningIds[numbers[i]][group]
- virtHostIds
if INCLUDE_VIRTUAL == False:
warningIds[numbers[i]][group] =
warningIds[numbers[i]][group] - virtIdList
for group in deleteIds.keys():
#deleteIds[group] = deleteIds[group] - virtHostIds
if INCLUDE_VIRTUAL == False:
deleteIds[group] = deleteIds[group] - virtIdList
### Here we get a list of all VM hosts, and remove those WITH guests from the
above lists
### This way we'll never remove an inactive host profile that has active guests
# for number in cautionIds.keys():
# for group in cautionIds[number].keys():
# if host.get('id') in cautionIds[number][group]:
# cautionIds[number][group].remove(id)
# for number in warningIds.keys():
# for group in warningIds[number].keys():
# if host.get('id') in warningIds[number][group]:
# warningIds[number][group].remove(id)
# for group in deleteIds.keys():
# if host.get('id') in deleteIds[group]:
# deleteIds[group].remove(id)
#virtHosts= []
#Get a list of all the virtual profiles/systems, and remove them
#from the list of profiles to delete
# for id in virtIdList:
# for number in cautionIds.keys():
# for group in cautionIds[number].keys():
# if id in cautionIds[number][group]:
# cautionIds[number][group].remove(id)
# for number in warningIds.keys():
# for group in warningIds[number].keys():
# if id in warningIds[number][group]:
# warningIds[number][group].remove(id)
# for group in deleteIds.keys():
# if id in deleteIds[group]:
# deleteIds.remove(id)
###Generate the caution output
cautionOutput = ""
for number in sorted(cautionIds.keys()):
cautionOutput += "\nThe following systems have now been inactive for %s
days:\n" % number
for group in cautionIds[number].keys():
if len(cautionIds[number][group]) > 0:
cautionOutput += "---%s\n" % str(group).upper()
for id in cautionIds[number][group]:
system = client.system.getName(session, id)
cautionOutput += "------Profile with name=%s, id=%s,
and last_checkin=%s\n" % (system.get('name'), system.get('id'),
system.get('last_checkin'))
warningOutput = ""
for number in sorted(warningIds.keys()):
warningOutput += "\nThe following systems have been inactive for %s
days\n" % number
warningOutput += "They WILL BE DELETED in %s days on %s\n" %
(DELETE_DAYS - number, (today +
datetime.timedelta(days=number)).strftime(DATE_FORMAT))
for group in warningIds[number].keys():
if len(warningIds[number][group]) > 0:
warningOutput += "---%s\n" % str(group).upper()
for id in warningIds[number][group]:
system = client.system.getName(session, id)
warningOutput += "------Profile with name=%s, id=%s,
and last_checkin=%s\n" % (system.get('name'), system.get('id'),
system.get('last_checkin'))
###Generate the first final warning output
deleteOutput = "The following systems have been DELETED because they were
inactive for %s days\n" % DELETE_DAYS
for group in deleteIds.keys():
if len(deleteIds[group]) > 0:
deleteOutput += "---%s\n" % str(group).upper()
for id in deleteIds[group]:
system = client.system.getName(session, id)
deleteOutput += "------Profile with name=%s, id=%s, and
last_checkin=%s\n" % (system.get('name'), system.get('id'),
system.get('last_checkin'))
###Construct user email
emailOutput = ""
if DRY == True:
emailOutput += "This is a DRY RUN. No profiles have been deleted."
emailOutput += "\n\n" + deleteOutput
emailOutput += "\n\n" + warningOutput
emailOutput += "\n\n" + cautionOutput
log.write("\n\n\nThe contents of the email:\n" + EMAIL_MESSAGE + emailOutput +
"\n\n\n")
if VERBOSE : print "\n\nThe contents of the email:\n" + EMAIL_MESSAGE +
emailOutput + "\n\n"
###Prepare for output
if DRY == False:
#Really delete them
try:
for group in deleteIds.keys():
client.system.deleteSystems(session,
list(deleteIds[group]))
out = "Profiles deleted successfully."
log.write("\n" + out)
if VERBOSE: print out
except:
out = "The Satellite could not delete the profiles.\nUnexpected
error:", sys.exc_info()[0]
log.write("\n" + out)
if VERBOSE: print out
sys.exit(42)
###Send the email
if SEND == True:
out = "Sending email to the following addresses: %s" % EMAIL_ADDRESS
rc = email("GROUP", "<user>@umich.edu", "<user>@umich.edu", "",
EMAIL_ADDRESS, EMAIL_SUBJECT, EMAIL_MESSAGE + emailOutput)
if rc == 0:
out += "\nSent successfully."
else:
out += "\nCould not send the email. Error code: %s" % rc
else:
out = "User opted to not send email."
log.write("\n" + out)
if VERBOSE : print out
###Close the session
client.auth.logout(session)
out = "Exited normally.\nSession closed."
log.write("\n" + out)
if VERBOSE : print out
log.close()
_______________________________________________
Spacewalk-list mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/spacewalk-list