#Python Inotifier for Perceus Clients #Author: Anthony Vuong #Department: OIT-RCS #University of California, Irvine #Contact: avuong1@uci.edu #Description: This script is written for our Perceus provisioned BDUC nodes. In order to access these nodes from the "outside world", we have to configure the named configuration #on bduc-login. This script essentially monitors the perceus dhcp log and inputs the newly modiified data into the named configuration files on bduc-login. Without this process, we cannot remotely access #the bduc nodes through hostnames. #Import os to make commandline calls, pyinotify libraries, and datetime for timestamps. import os, pyinotify, datetime #I actually don't understand why we have to import these since we've imported the full pyinotify library, but an error will occur if we do not specify these imports directly. from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent, IN_DELETE, IN_CREATE, IN_ATTRIB, ALL_EVENTS class MyEventHandler(ProcessEvent): #If this process is called, it means that an IN_ATTRIB event was triggered. IN_ATTRIB signals a change in metadata value aka the file was modified in some way. #If this was called, it means /usr/var/lib/perceus/dhcpd.leases was modified by perceus. We have to adjust the /var/named/chroot/var/named/*.db files to enable networking #and ssh by hostnames. def process_IN_ACCESS(self, event): print "ACCESS event:", event.pathname def process_IN_CLOSE_NOWRITE(self, event): print "CLOSE_NOWRITE event:", event.pathname def process_IN_CLOSE_WRITE(self, event): print "CLOSE_WRITE event:", event.pathname def process_IN_OPEN(self, event): print "OPEN event:", event.pathname #Still need to configure this to check for manually modified files. def process_IN_ATTRIB(self, event): print "ATTR event:", event.pathname #When perceus modifies its /usr/var/lib/perceus/dhcpd.leases file, it triggers a IN_MODIFY event. Here's how the process goes: #Modify Event 1. When a node starts up, perceus will pick up that node and its information(mac address) and assign an IP, but it does not define a hostname for the node(e.g. n100 n101, n102). It simply assigns a * in the log. #Modify Event 2. If the node is provisioned an image, then perceus will define a hostname for that node, otherwise it'll be left blank. #Causes: The causes of this problem seems to be kickstart. Perceus reads and finds every node on the network and is set to provision a default vnfs capsule. But kickstart interrupts this full perceus process. def process_IN_MODIFY(self, event): #Print the event print "MODIFY event:", event.pathname #This boolean variable is used to control the file writing process. #The process in which this is used is defined in the lower comments. perceusEndFlag = True #Get the date and time in which this event was triggered. currentDateTime = datetime.datetime.now() #Format: 2011-12-12 currentDate = str(currentDateTime.year) + '-' + str(currentDateTime.month) + '-' + str(currentDateTime.day) #Format: 10-10-1 currentTime = str(currentDateTime.hour) + '-' + str(currentDateTime.minute) + '-' + str(currentDateTime.second) #Format: 2011-12-12_10-10-1 datetimeStamp = currentDate + '_' + currentTime #Set directory variables so I don't have to type them later. bducDbBackUpDir = '/usr/var/lib/perceus/dnsbackup/bduc.dbbackup_' + datetimeStamp tenDbBackUpDir = '/usr/var/lib/perceus/dnsbackup/10.255.78.dbbackup_' + datetimeStamp bducDbNewDir = '/usr/var/lib/perceus/dnsnew/bduc.db_' + datetimeStamp tenDbNewDir = '/usr/var/lib/perceus/dnsnew/10.255.78.db_' + datetimeStamp #Shout to copy /var/named/chroot/var/named/10.255.78.db and rename it. execLine = 'scp root@bduc-login:/var/named/chroot/var/named/10.255.78.db ' + tenDbBackUpDir os.system(execLine) #Shout to copy /var/named/chroot/var/named/bduc.db and rename it. execLine = 'scp root@bduc-login:/var/named/chroot/var/named/bduc.db ' + bducDbBackUpDir os.system(execLine) #We're going to have to reread/restart the named server. To do this, we need to find its pid and kill it #Shout to copy /var/run/named.pid and copy it to claw1 so we can read it later. execLine = 'scp root@bduc-login:/var/run/named.pid named.pid' os.system(execLine) #Open files to read and write f2 = open(tenDbBackUpDir, 'r') f3 = open(bducDbBackUpDir, 'r') f4 = open(tenDbNewDir, 'w') f5 = open(bducDbNewDir, 'w') #Open the dhcpd.leases files which was modified. We'll be reading from this file twice since we're modifying it for two different formats. f = open('/usr/var/lib/perceus/dhcpd.leases', 'r') #This for loop starts with reading the /var/named/chroot/var/named/10.255.78.db file we just copied over. It'll read each line and write the new configurations #to a new file in which we'll shout and copy to bduc-login afterwards. #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #THE .DB FILES MUST HAVE THE FOLLOWING COMMENTS SOMEWHERE FOR THIS TO WORK!!!! #; Perceus Nodes #; Perceus Node End #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ for line in f2: #PerceusEndFlag simply checks whether we're reading between the perceus comments or not. If it reads "outside" of the perceus comments #then the script will simply dump the line as we don't want to modify anything. #This is the boolean control variable we've set earlier. If the value is set to true, then it means we'll simply dump the line onto the new file. We don't want to #modify anything outside of perceus. #This variable helps us modify only what we need and that's the perceus nodes. The value will be set to false after reading the first Perceus comment which will write #the new configurations found in dhcpd.leases. More description is described below. if perceusEndFlag: f4.write(str(line)) #If the current line is the first perceus comment, then we want to start dumping the new configurations from the dhcpd.leases file onto the 10.255.78.db file. if str(line).strip() == '; Perceus Nodes': #Iterate through the dhcpd.leases file and dump configurations according to the 10.255.78.db format. for a in f: #The dhcpd.leases files has a space-delimited format. Here's the format: #1309222119 00:d0:68:0d:03:83 10.255.78.127 n100 * lineSplit = str(a).split(' ') node = lineSplit[3] #As I stated in the earlier comments, Perceus will pick up any node on the network, but kickstart prevents Perceus from completing the provisioning process. We have to ignore all these #undefined entries. Here's the format of these undefined entries: #1309222119 00:d0:68:0d:03:83 10.255.78.127 * * #If the node is define, then dump the new configurations in a defined format. if not node == '*': #Here's the format #190 IN 1H PTR n190.bduc. modifiedLine = node[1:len(node)] + ' IN 1H PTR ' + node + '.bduc.\n' f4.write(modifiedLine) #At this point, all new configurations were made, so we need to read past the old perceus configuration found in the 10.255.78.db file. #Set this to false and it will simply iterate reading the files until the end of the perceus comment which signals the end of new configurations. perceusEndFlag = False #Once we've hit this comment, simply dump the rest of the configuration because its not part of perceus. Set the boolean control variable to true again to process this. if str(line).strip() == '; Perceus Node End': f4.write("\n") f4.write(str(line)) perceusEndFlag = True #Flush the output and close modified/read files. f4.flush() f2.close() f4.close() f.close() #This is the same process as the 10.255.78.db loop except we have to save the new configurations into a different format into bduc.db f = open('/usr/var/lib/perceus/dhcpd.leases', 'r') for line in f3: if perceusEndFlag: f5.write(str(line)) if str(line).strip() == '; Perceus Nodes': for a in f: lineSplit = str(a).split(' ') node = lineSplit[3] ipaddr = lineSplit[2] if not node == '*': #Here's the format for 10.255.78.db #n190 IN 1H A 10.255.78.242 modifiedLine = node + '\tIN\t1H\tA\t' + ipaddr + '\n' f5.write(modifiedLine) perceusEndFlag = False if str(line).strip() == '; Perceus Node End': f5.write("\n") f5.write(str(line)) perceusEndFlag = True f5.flush() f5.close() f3.close() f.close() #At this point, backup files are made in /usr/var/lib/perceus/dnsbackup and the new files with timestamps are found in /usr/var/lib/perceus/dnsnew. #You can do a "diff" to see the differences as the times are consistent with each other. #Now we'll need to shout our newly modified bduc.db and 10.255.78.db files onto bduc and restart the named service. execLine = 'scp ' + bducDbNewDir + ' root@bduc-login:/var/named/chroot/var/named/bduc.db' os.system(execLine) execLine = 'scp ' + tenDbNewDir + ' root@bduc-login:/var/named/chroot/var/named/10.255.78.db' os.system(execLine) #Read named.pid file from bduc-login to find named processId and kill it. f = open('named.pid', 'r') processId = str(f.readline()).strip() f.close() execLine = 'ssh bduc-login.nacs.uci.edu \"kill -HUP ' + processId + '\"' os.system(execLine) def main(): # watch manager wm = WatchManager() #Watch this file. If you want to watch more, simply replace the directory. wm.add_watch('/usr/var/lib/perceus/dhcpd.leases', ALL_EVENTS, rec=True) # event handler eh = MyEventHandler() # notifier notifier = Notifier(wm, eh) notifier.loop() if __name__ == '__main__': main()