#!/usr/local/bin/rxx /* (c) copyright 1995, Paul Zarnowski and Cornell University (see "Legal Information", below) daily.backup For each registered client node, if: 1) A successful backup has not occurred within the last N days, and 2) The node-admin wants to be notified, then send a mailfile to the node-admin indicating: 1) Their node has not been backed up within the last N days, and 2) Which filespaces, and 3) The last successful incremental backup of their node occured on X. If the "notification value" is negative, then we always notify the node-admin whether there is a recent backup or not. However, the content and subject of the E-mail will be different, depending on whether a recent backup has occured or not. The absolute value of the "notification value" is used to determine whether the last backup is "recent" or not. The "notification value" is stored as part of the "CONTACT" field for the ADSM node. The CONTACT field is parsed by the ParseContact function. Input: - qidfile: contains id & password of ADSM query administrator - QUERY NODE: nodename, owner, N, notify-flag, platform - QUERY FILESPACE: days-since-last-backup (by filespace) - QUERY EVENT: status Output: - Appends to history file - Summary email report to "mailto" address - Notification emails to node-owners (one per node-owner) Pre-req's: edate - external rexx function Make sure you set the parameters, below. Author Information: Paul Zarnowski Cornell Information Technologies Ithaca, NY 14853-2601 psz1@cornell.edu Update History: 1-Jul-94 psz1: Initial version 3-Apr-95 psz1: Added "notify always" option ($NOTIFY = -1) 19-Apr-95 psz1: Modified "notify always" option ($NOTIFY < 0) 8-Jun-95 psz1: Added special case for no backups (treat as failure) */ /* Parameters */ G.="" topdir="/u/adsm-mon" qidfile = topdir"/adm/qidfile" /* ADSM Query Administrator ID & PASSWORD */ FILENAME=edate(S,,-1,L) G.$MAILTO="adsm-maint" /* Send Summary Report to */ G.$MAILADMIN="adsm-maint@nodename1.cornell.edu" /* mail questions here */ G.$SUMMARY=topdir"/log/backlog/"FILENAME /* File for Summaries */ G.$MAILDIR="/tmp/adsm.daily.nodemail" /* Temp dir for E-mail */ G.$HISTORY=topdir"/log/adsm.backup.history" /* History file */ G.$HISTNB=topdir"/log/adsm.notbacked.history" /* Nobackup history file */ G.$WIDTH=79 G.$SEPARATOR1=copies("= ",G.$WIDTH % 2)"=" G.$SEPARATOR2=copies("- ",G.$WIDTH % 2)"-" G.$NOW = edate(U,,,L) time() /* Get current date & time */ G.$DEBUG = 0 /* Initialize statistics */ G.$NOCONTACT = "" /* list of nodes which never contacted server */ G.$LOCKED = "" /* list of locked nodes */ G.$IGNORED = "" /* list of nodes who don't want notification */ G.$NOTBACKED = "" /* list of nodes not backed up recently */ G.$EMAIL.0 = 0 /* Array of E-mail recipients */ EMAIL. = 0 /* = 1, if we have encountered this E-mail */ NL0. = "" /* NL0.email = list of ignored nodes for E-mail */ NL1. = "" /* NL1.email = list of neg notify, recent backup */ NL2. = "" /* NL2.email = list of pos notify, recent backup */ NL3. = "" /* NL3.email = list of nodes to be notified */ SUBJECT. = "" /* SUBJECT.email = subject for E-mail */ /* Parse qidfile */ /* The qidfile contains 1 line containing an ADSM admin-id & passwd */ rc = popen('/bin/head -1' qidfile) if (rc <> 0) then exit 12 parse pull id pswd . if (pswd = "") then do call log "Invalid ID file." exit 12 end G.$ADSMQID = id /* for use by osd_ADSMQstack */ G.$ADSMQPASS = pswd /* Remove temp files if they exist. */ "/bin/test -f" G.$SUMMARY if (rc=0) then "/bin/rm -f --" G.$SUMMARY call CleanTempFiles "/bin/mkdir" G.$MAILDIR if (rc <> 0) then do say "Unexpected RC" rc "from mkdir" G.$MAILDIR call finish end nodelist=GetNodeInfo() /* Get info from ADSM Q NODES FORMAT=DETAILED */ /* Get FileSpace info from ADSM Q FILESPACE. */ call GetFilespaceInfo() /* Get Event info from ADSM Q EVENTS. */ call GetEventInfo() /* Check each node to see if hasn't been backed up recently. */ nn = nodelist do while (nn <> "") parse value nn with node nn email = NC.node.$ADMINEMAIL if (email="") then email = NC.node.$OWNEREMAIL b = CheckBackup(node) if (G.$DEBUG) then say "CheckBackup("node") =" b if (^EMAIL.email) then do i = G.$EMAIL.0 i = i + 1 G.$EMAIL.i = email G.$EMAIL.0 = i EMAIL.email = 1 end select when (b=0) then NL0.email = NL0.email node when (b=1) then NL1.email = NL1.email node when (b=2) then NL2.email = NL2.email node when (b=3) then NL3.email = NL3.email node otherwise say "Invalid value returned from CheckBackup("node") -" b end end /* Prepare Summary Report */ if (G.$DEBUG) then say "Calling PrepSummary" call PrepSummary /* Prepare mailfile for each E-mail recipient */ n = G.$EMAIL.0 do i = 1 to n email = G.$EMAIL.i if (G.$DEBUG) then say "Calling PrepEmail("email")" if (PrepEmail(email) <> 0) then G.$EMAIL.i = "" end /* Append statistics to history log */ call UpdateHistory(G.$HISTORY) call UpdateNOBHistory(G.$HISTNB) /* Send E-mail to recipients (as indicated) */ n = G.$EMAIL.0 do i = 1 to n email = G.$EMAIL.i if (email <> "") then call SendEmail(email) end /* Mail Summary Report */ finish: call MailSummary /* Remove temporary files */ call CleanTempFiles /* Exit Program */ done: exit /* CheckBackup: See if node has been backed up recently. return: 0 - notification not wanted 1 - negative notification wanted & recent backup occurred 2 - positive notification wanted & recent backup occurred 3 - notification wanted & no recent backup occurred (0,1) -> no notification to user (2,3) -> notify user (differently) The following arrays are filled in by this procedure: G.$NOCONTACT - list of nodes which have never contacted the server G.$LOCKED - list of nodes which are locked G.$IGNORED - list of nodes not wanting notification G.$NNBACKED - list of nodes backed up recently, negative notify G.$PNBACKED - list of nodes backed up recently, positive notify G.$NOTBACKED - list of nodes not backed up recently, wanting notify The following 2 FS. arrays are only filled in if a successful scheduled event was not found. FS.$BKP.node = filespaces backed up recently FS.$NBKP.node = filespaces not backed up recently Notes: 1. The term "negative notification" is used to indicate that the node owner wants to be notified only if a recent backup has not occurred. 2. The term "positive notification" is used to indicate that the node owner wants to be notified always, whether a recent backup has occurred or not. However, the form of the notification will be different if a recent backup has occurred, than if a recent backup has not occurred. 3. The absolute value of NODE.node.$NOTIFY is used to determine whether a "recent" backup has occurred or not. Logic: 1. First we check EVENT.node.$COMPLETED to see if the last scheduled event for this node completed successfully or not. If it did, then we check EVENT.node.$COMPLETE to see whether it completed recently or not. If it occurred recently, then we return 1 or 2. 2. If we don't see a successful scheduled event, then we revert to looking at the output from QUERY FILESPACE, to see when each filespace was last successfully completed. At this point, we fill in the FS.$BKP and FS.$NBKP arrays. We return 1, 2, or 3. Note that if we revert to step 2, then we may flag the node as not being successfully backed up because an obsolete filespace was not backed up (and normally wouldn't be by a scheduled backup). Alas, we cannot determine this from the server, so we flag it anyway. */ CheckBackup: procedure expose G. F. FS. EVENT. NODE. NC. parse arg node . if (NODE.node.$PLATFORM="") then do /* node has never contacted server */ G.$NOCONTACT = G.$NOCONTACT node return 0 end if (NODE.node.$LOCKED) then do /* node is locked */ G.$LOCKED = G.$LOCKED node return 0 end if (NC.node.$NOTIFY = 0) then do /* node does not want to be notified */ G.$IGNORED = G.$IGNORED node return 0 end /* If scheduled backup completed recently, then return 1 or 2 */ /* If positive notification wanted, return 2 */ if (EVENT.node.$COMPLETED) then do parse value EVENT.node.$COMPLETE with cdate ctime . if (DeltaT(cdate ctime) < abs(NC.node.$NOTIFY)) then do if (NC.node.$NOTIFY < 0) then do G.$PNBACKED = G.$PNBACKED node return 2 end else do G.$NNBACKED = G.$NNBACKED node return 1 end end end /* If no scheduled backup completed recently, look at each filespace */ n = FS.node.0 do i = 1 to n /* process each filespace for this node */ f = FS.node.i /* If filesystem has not been backed up recently */ if (LateBackup(node,f)) then do j=FS.$NBKP.node.0 + 1; FS.$NBKP.node.0=j; FS.$NBKP.node.j=f end else do j=FS.$BKP.node.0 + 1; FS.$BKP.node.0=j; FS.$BKP.node.j=f end end if (FS.node.0 = 0) then do /* no filespaces exist; treat as */ G.$NOTBACKED = G.$NOTBACKED node /* if node was not backed up. */ return 3 end if (FS.$NBKP.node.0 > 0) then do /* node not backed up recently */ G.$NOTBACKED = G.$NOTBACKED node return 3 end else do /* node backed up recently */ if (NC.node.$NOTIFY > 0) then do /* negative notification wanted */ G.$NNBACKED = G.$NNBACKED node return 1 end else do /* positive notification wanted */ G.$PNBACKED = G.$PNBACKED node return 2 end end return ERROR /* LateBackup: determines if a backup is late or not. return: 0 - if backup is not late 1 - if backup is late */ LateBackup: procedure expose G. F. EVENT. NODE. NC. parse arg node, filespace node=strip(node); filespace=strip(filespace) if ^(datatype(NC.node.$NOTIFY,W)) then return 0 if (NC.node.$NOTIFY = 0) then return 0 if (F.$CDAYS.node.filespace = "") then return 1 if (F.$CDAYS.node.filespace <= abs(NC.node.$NOTIFY)) then return 0 return 1 /* DeltaT(date1,time1,date2,time2) date1,date2=mm/dd/yyyy time1,time2=hh:mm:ss returns #days between first date/time and second date/time. #days is not a whole number. if second date/time is not specified, the current date/time is used. */ DeltaT: procedure expose G. parse arg date1 time1 date2 time2 . if (date2="") then parse value G.$NOW with date2 time2 . if (time2="") then parse value G.$NOW with . time2 . parse value time1 with hh ":" mm ":" ss . if (ss="") then ss=0 sec1=ss+(60*mm)+(3600*hh) base1=edate(B,date1,,L)+(sec1/(24*60*60)) parse value time2 with hh ":" mm ":" ss . if (ss="") then ss=0 sec2=ss+(60*mm)+(3600*hh) base2=edate(B,date2,,L)+(sec2/(24*60*60)) return format(base2-base1,,2) /* CleanTempFiles: Remove any temporary files used by this script */ CleanTempFiles: procedure expose G. "/bin/test -d" G.$MAILDIR if (rc=0) then "/bin/rm -rf --" G.$MAILDIR return /* MailSummary: Mail Summary Report, if one was created. */ MailSummary: procedure expose G. call close G.$SUMMARY "/bin/test -f" G.$SUMMARY if (rc<>0) then return "/bin/cat" G.$SUMMARY , "| /bin/mail -s ""ADSM Backup Summary Report""" G.$MAILTO return /* PrepSummary: Prepare Backup Summary Report */ PrepSummary: procedure expose G. file = G.$SUMMARY call output file "Summary Information:" call output file " " call output file "Nodes backed up and not notified:" , format(words(G.$NNBACKED),3) call output file "Nodes backed up and notified: " , format(words(G.$PNBACKED),3) call output file "Nodes not backed up recently: " , format(words(G.$NOTBACKED),3) call output file "Nodes not wanting notification: " , format(words(G.$IGNORED),3) call output file "Nodes locked: " , format(words(G.$LOCKED),3) call output file "Nodes never contacted server: " , format(words(G.$NOCONTACT),3) call output file " " return /* PrepEmail: Prepare mailfile for a node or set of nodes */ PrepEmail: procedure expose G. F. FS. NODE. NC. NL0. NL1. NL2. NL3. SUBJECT. , EVENT. parse arg email /* Initialize some variables */ file = G.$MAILDIR"/"email sumfile = G.$SUMMARY Ngood = words(NL2.email) Nbad = words(NL3.email) /* If no notification is necessary, then we just return. */ if (Ngood = 0) & (Nbad = 0) then return 1 /* Set the "subject" */ select when (Nbad = 0) & (Ngood = 1) then do subject = "ADSM has a recent backup for '"strip(NL2.email)"'" end when (Nbad = 0) & (Ngood > 1) then do subject = "ADSM has current backup for" Ngood "systems" end when (Nbad = 1) then do subject = "ADSM backup FAILED for '"strip(NL3.email)"'" end when (Nbad > 1) then do subject = "ADSM backup FAILED for" Nbad "of" Nbad+Ngood "systems" end otherwise do subject = "ADSM Backup Report" end end SUBJECT.email = subject /* Generate output for summary file */ node = word(NL2.email NL3.email,1) name = NC.node.$ADMINNAME if (name = "") then name = NC.node.$OWNERNAME phone = NC.node.$ADMINPHONE if (phone = "") then phone = NC.node.$OWNERPHONE call output sumfile G.$SEPARATOR1 call output sumfile "Short version of mail sent to" name "("phone"):" /* Prepare header for mailfile */ l="" if ((Nbad + Ngood) = 1) then do this="this" system="system" is="is" end else do this="these" system="systems" is="are" end select when (Nbad = 1) & (Ngood = 0) then do n = strip(NL3.email) if (abs(NC.n.$NOTIFY) = 1) then x = "1 day" else x = abs(NC.n.$NOTIFY) "days" l=l "System '"n"' has NOT been backed up by the ADSM" l=l "Backup Server within the last" x"." l1 = "whenever this system is not backed up regularly." l2 = "this system" end when (Ngood = 1) & (Nbad = 0) then do n = strip(NL2.email) if (abs(NC.n.$NOTIFY) = 1) then x = "1 day" else x = abs(NC.n.$NOTIFY) "days" l=l "System '"n"' has been successfully backed up by the ADSM" l=l "Backup Server within the last" x"." l1 = "every day for this system." l2 = "this system" end when (Ngood = 0) then do l=l "The following" Nbad "computer systems have NOT been backed up" l=l "recently by the ADSM Backup Server." l1 = "whenever these systems are not backed up regularly." l2 = "these systems" end when (Nbad = 0) then do l=l "The following" Nbad "computer systems have been successfully" l=l "backed up recently by the ADSM Backup Server." l1 = "every day for these systems." l2 = "these systems" end otherwise do if (Ngood=1) then has="has"; else has="have" l=l Ngood "of the following computer systems" has "been successfully" if (Nbad=1) then has="has"; else has="have" l=l "backed up recently, and" Nbad "of the following computer systems" l=l has "NOT been backed up recently, by the ADSM Backup Server." l1 = "every day for at least one of these systems, or whenever some" l1=l1 "of these systems are not backed up regularly." l2 = "these systems" end end call paraout sumfile l l=l " You are listed as the administrator for" this system", and our" l=l "records indicate that you would like to receive this notification" l=l l1 l=l " If you want to change the notification policy for" l=l l2", please send E-mail to" G.$MAILADMIN"." call paraout file l nn = NL3.email NL2.email do while (nn <> "") parse value nn with n nn call output file " " call output2 file sumfile G.$SEPARATOR2 call output file " " rc = PrepNode(n, email, file, sumfile) end call output file " " call output file G.$SEPARATOR2 return 0 /* PrepNode: Prepare mailfile for node-admin */ PrepNode: procedure expose G. F. FS. NODE. NC. EVENT. parse arg node, email, file, sumfile l="System Name: " node if (NODE.node.$PLATFORM <> "") then l=l " ("NODE.node.$PLATFORM")" call output2 file sumfile l if (NC.node.$OWNERNAME<>"") & (NC.node.$ADMINNAME<>"") & , (NC.node.$OWNERNAME <> NC.node.$ADMINNAME) then do l="Owned by: " NC.node.$OWNERNAME ll="" if (NC.node.$OWNERPHONE <> "") then ll = NC.node.$OWNERPHONE if (NC.node.$OWNEREMAIL <> "") then do if (ll <> "") then ll=ll"," ll=ll NC.node.$OWNEREMAIL end if (ll<>"") then l=l "("ll")" call output2 file sumfile l end notify=NC.node.$NOTIFY anotify = abs(notify) if (abs(notify) > 1) then days = "days" else days = "day" if (notify > 0) then notifypolicy = "If not backed up for" notify days"." else notifypolicy = "Always notify." l="Notification:" notifypolicy call output2 file sumfile l platform = NODE.node.$PLATFORM select when (platform = "Mac") then do filespace = "volume" filespace1 = "Volume name" end when (wordpos(platform,"OS/2 Windows DOS NetWare") > 0) then do filespace = "drive" filespace1 = "Drive label" end when (wordpos(platform,"SunOS AIX ULTRIX HPUX SCO") > 0) then do filespace = "filesystem" filespace1 = "Filesystem name" end otherwise do filespace = "filespace" filespace1 = "Filespace name" end end /* Need to distinguish between successful scheduled events vs manual backup! A successful scheduled event will have FS.$BKP.node.0 = 0. For a successful scheduled event, refer to EVENT.node.$COMPLETE EVENT.node.$COMPLETED = 0 | 1 EVENT.node.$COMPLETE =