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

Reply via email to