From 1ae5ffd76215d09b86a4afa3a3e212c8627900dc Mon Sep 17 00:00:00 2001 From: Guillaume Raffy Date: Mon, 18 Apr 2011 09:32:19 +0000 Subject: [PATCH] =?UTF-8?q?d=C3=A9placement=20des=20scripts=20python=20pou?= =?UTF-8?q?r=20les=20rendre=20partag=C3=A9s=20par=20tous=20les=20admins=20?= =?UTF-8?q?(j'en=20ai=20besoin=20pour=20d'autres=20applis=20que=20clusterc?= =?UTF-8?q?ontroller)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ipmi/ClusterNodeSensorsReadings.py | 42 +++++++++ Ipmi/IpmiTool202Parser.py | 81 +++++++++++++++++ Ipmi/IpmiTool218Parser.py | 39 ++++++++ Ipmi/Sensor.py | 23 +++++ Ipmi/__init__.py | 1 + IsMachineResponding.py | 0 SimpaDbUtil.py | 139 +++++++++++++++++++++++++++++ Util.py | 69 ++++++++++++++ __init__.py | 1 + wol.py | 42 +++++++++ 10 files changed, 437 insertions(+) create mode 100644 Ipmi/ClusterNodeSensorsReadings.py create mode 100644 Ipmi/IpmiTool202Parser.py create mode 100644 Ipmi/IpmiTool218Parser.py create mode 100644 Ipmi/Sensor.py create mode 100644 Ipmi/__init__.py create mode 100644 IsMachineResponding.py create mode 100644 SimpaDbUtil.py create mode 100644 Util.py create mode 100644 __init__.py create mode 100644 wol.py diff --git a/Ipmi/ClusterNodeSensorsReadings.py b/Ipmi/ClusterNodeSensorsReadings.py new file mode 100644 index 0000000..1d7d35b --- /dev/null +++ b/Ipmi/ClusterNodeSensorsReadings.py @@ -0,0 +1,42 @@ +import Sensor + +class ClusterNodeSensorsReadings: + """ + + """ + """ + POWERSTATE_UNKNOWN=0 + POWERSTATE_OFF=1 + POWERSTATE_ON=2 + POWERSTATE_SLEEP=3 + """ + def __init__(self, clusterNodeName): + self.m_clusterNodeName = clusterNodeName + self.m_sensors = {} + #self.m_powerState = ClusterNodeStatus.POWERSTATE_UNKNOWN + return + def addSensor(self, sensor): + self.m_sensors[sensor.m_name] = sensor + def dump(self): + for key,sensor in self.m_sensors.iteritems(): + sensor.dump() + return + #def getPowerState(self): + # return self.m_powerState + def getLowestTemperature( self ): + #log('ClusterNodeSensorsReadings::getLowestTemperature : start') + lowestTemperature = 0.0 + lowestTemperatureIsDefined = False + for key,sensor in self.m_sensors.iteritems(): + #log('ClusterNodeSensorsReadings::getLowestTemperature : start') + if sensor.typeName() == 'Temperature': + sensor.m_temperature + if lowestTemperatureIsDefined: + if sensor.m_temperature < lowestTemperature: + lowestTemperature = sensor.m_temperature + else: + lowestTemperature = sensor.m_temperature + lowestTemperatureIsDefined = True + assert( lowestTemperatureIsDefined ) + #log('ClusterNodeSensorsReadings::getLowestTemperature : end') + return lowestTemperature diff --git a/Ipmi/IpmiTool202Parser.py b/Ipmi/IpmiTool202Parser.py new file mode 100644 index 0000000..ef32b29 --- /dev/null +++ b/Ipmi/IpmiTool202Parser.py @@ -0,0 +1,81 @@ +import StringIO +import re +from Sensor import FanSensor, TemperatureSensor +from ClusterNodeSensorsReadings import ClusterNodeSensorsReadings + +class IpmiTool202Parser: + def parseSensorOutput( self, strOutput, clusterNodeName ): + sensorReadings=ClusterNodeSensorsReadings(clusterNodeName) + f = StringIO.StringIO(strOutput) + line = f.readline() + while( len(line) > 0 ): + #print line, + matchObj = re.match( '^Sensor ID[ ]*\: \'(?P[a-zA-Z 0-9]+)\'', line ) + if matchObj: + sensorName = matchObj.group('sensorName') + # print sensorName + # read the entity id + line = f.readline() + matchObj = re.match( '^ Entity ID[ ]*\: (?P[0-9\.]+)', line ) + assert(matchObj) + entityId = matchObj.group('entityId') + # print entityId + # read the sensor type + line = f.readline() + matchObj = re.match( '^ Sensor Type[\(\)a-zA-Z ]*\: (?P[a-zA-Z \(\)]+)', line ) + assert(matchObj) + sensorType = matchObj.group('sensorType') + #print sensorType + if sensorType == 'Fan': + rpms = self.parseFanSensorOutput(f) + if temperature != None: + sensor = FanSensor(sensorName) + sensor.m_rpms = rpms + elif sensorType == 'Temperature': + temperature = self.parseTemperatureSensorOutput(f) + if temperature != None: + sensor = TemperatureSensor(sensorName) + sensor.m_temperature = temperature + else: + #ignoring other sensors + sensor = None + if sensor: + sensorReadings.addSensor( sensor ) + else: + None + #assert(False) + line = f.readline() + f.close() + def parseFanSensorOutput(self, file): + """ + reads the fan specific ipdmitool output + """ + line = file.readline() + #print line + matchObj = re.match( '^ Sensor Reading[ ]*\: (?P[0-9]+) \(\+/\- (?P[0-9]+)\) RPM', line ) + if(matchObj): + numRpms = matchObj.group('numRpms') + #print numRpms + rpms = float( numRpms ) + return rpms + else: + matchObj = re.match( '^ Sensor Reading[ ]*\: Not Present', line ) + assert(matchObj) + return None + + def parseTemperatureSensorOutput(self, file): + """ + reads the temperature specific ipdmitool output + """ + # Sensor Reading : 36 (+/- 0) degrees C + line = file.readline() + #print line + matchObj = re.match( '^ Sensor Reading[ ]*\: (?P[0-9]+) \(\+/\- (?P[0-9]+)\) degrees C', line ) + if(matchObj): + temperature = matchObj.group('temperature') + temperature = float( temperature ) + return temperature + else: + matchObj = re.match( '^ Sensor Reading[ ]*\: Not Present', line ) + assert(matchObj) + return None diff --git a/Ipmi/IpmiTool218Parser.py b/Ipmi/IpmiTool218Parser.py new file mode 100644 index 0000000..d4d0f91 --- /dev/null +++ b/Ipmi/IpmiTool218Parser.py @@ -0,0 +1,39 @@ +import StringIO +import re +from Sensor import FanSensor, TemperatureSensor +from ClusterNodeSensorsReadings import ClusterNodeSensorsReadings + +class IpmiTool218Parser: + def parseSensorOutput( self, strOutput, clusterNodeName ): + sensorReadings=ClusterNodeSensorsReadings(clusterNodeName) + f = StringIO.StringIO(strOutput) + line = f.readline() + while( len(line) > 0 ): + #print line, + matchObj = re.match( '^(?P[a-zA-Z 0-9]+[a-zA-Z 0-9]*[a-zA-Z0-9])[ ]*\| (?P[\.0-9]+)[ ]*\| (?P[a-zA-Z0-9][a-zA-Z 0-9]*[a-zA-Z0-9])[?]*', line ) + if matchObj: + #log('readClusterNodeSensorsIpmiTool2_1_8 : sensorName = '+matchObj.group('sensorName')) + #log('readClusterNodeSensorsIpmiTool2_1_8 : sensorValue = '+matchObj.group('sensorValue')) + #log('readClusterNodeSensorsIpmiTool2_1_8 : sensorUnit = "'+matchObj.group('sensorUnit')+'"') + sensorName = matchObj.group('sensorName') + sensorValue = matchObj.group('sensorValue') + sensorUnit = matchObj.group('sensorUnit') + sensor = None + if sensorUnit == 'degrees C': + sensor = TemperatureSensor(sensorName) + sensor.m_temperature = float( sensorValue ) + elif sensorUnit == 'RPM': + sensor = FanSensor(sensorName) + sensor.m_rpms = float( sensorValue ) + else: + None + if sensor: + #log('readClusterNodeSensorsIpmiTool2_1_8 : adding sensor') + sensorReadings.addSensor( sensor ) + else: + None + #assert(False) + line = f.readline() + f.close() + return sensorReadings + \ No newline at end of file diff --git a/Ipmi/Sensor.py b/Ipmi/Sensor.py new file mode 100644 index 0000000..d73b93a --- /dev/null +++ b/Ipmi/Sensor.py @@ -0,0 +1,23 @@ +class Sensor: + def __init__(self, sensorName): + self.m_name = sensorName + self.m_isValid = True # false if this sensor is not actually present on the target machine + return + def dump(self): + print self.m_name + +class FanSensor(Sensor): + def __init__(self, sensorName): + Sensor.__init__(self, sensorName) + def dump(self): + print 'Fan \'', self.m_name, '\' rpm=',self.m_rpms + def typeName(self): + return 'Fan' + +class TemperatureSensor(Sensor): + def __init__(self, sensorName): + Sensor.__init__(self, sensorName) + def dump(self): + print 'Temperature \'', self.m_name, '\' temperature=',self.m_temperature + def typeName(self): + return 'Temperature' diff --git a/Ipmi/__init__.py b/Ipmi/__init__.py new file mode 100644 index 0000000..5a9ecee --- /dev/null +++ b/Ipmi/__init__.py @@ -0,0 +1 @@ +# this file is here just so that the containing directory is treated as a python package \ No newline at end of file diff --git a/IsMachineResponding.py b/IsMachineResponding.py new file mode 100644 index 0000000..e69de29 diff --git a/SimpaDbUtil.py b/SimpaDbUtil.py new file mode 100644 index 0000000..9363c47 --- /dev/null +++ b/SimpaDbUtil.py @@ -0,0 +1,139 @@ +import MySQLdb +import time +import subprocess +import StringIO +import re +from wol import * +import os +import signal +from Util import * + +def isMachineResponding(machineName): + (returnCode, stdout, stderr) = executeProgram( [ 'ping', '-o', '-t', '1', machineName ] ) + #log( 'isMachineResponding : result of command %s : %d' % (command, returnCode) ) + + if returnCode == 0: + return True + else: + bMachineNameIsNotKnown = (returnCode == 68) + bMachineIsNotResponding = (returnCode == 2) + if bMachineIsNotResponding == False: + bBUG_00000004_IS_STILL_ALIVE = True + if bBUG_00000004_IS_STILL_ALIVE == True and returnCode == 142: + log('isMachineResponding : bug00000004 Unexpected return code : returnCode=%d, stdout="%s", stderr="%s" , machineName = %s' % (returnCode, stdout, stderr, machineName) ) + # don't stop the program until we understand bug00000004 + elif bBUG_00000004_IS_STILL_ALIVE == True and returnCode == -14: # I had this error code on 07/09/2009 20:38 but I don't know yet what that means + log('isMachineResponding : bug00000004 Unexpected return code : returnCode=%d, stdout="%s", stderr="%s" , machineName = %s' % (returnCode, stdout, stderr, machineName) ) + # don't stop the program until we understand bug00000004 + else: + log('isMachineResponding : Unexpected return code : returnCode=%d, stdout="%s", stderr="%s" , machineName = %s' % (returnCode, stdout, stderr, machineName) ) + assert(False) + return False + + +def machineNameToMacAddress( machineName ): + conn = MySQLdb.connect('simpatix10', 'root', '', 'simpadb') + assert(conn) + sqlQuery = """SELECT mac_address FROM ethernet_cards WHERE machine_name='"""+machineName+"""' AND type='normal'""" + #print sqlQuery + conn.query( sqlQuery ) + r=conn.store_result() + row = r.fetch_row(0) + assert( len(row) == 1 ) + #print 'row =', row + macAddress = row[0][0] + #print macAddress + conn.close() + return macAddress + +def getLightOutManagementIpAddress( machineName ): + """ + the light out management ip of servers allows to talk to the server even when it's asleep + """ + conn = MySQLdb.connect('simpatix10', 'root', '', 'simpadb') + assert(conn) + sqlQuery = """SELECT ip_address_1,ip_address_2,ip_address_3,ip_address_4 FROM ethernet_cards WHERE machine_name='"""+machineName+"""' AND type='light_out_management'""" + #print sqlQuery + conn.query( sqlQuery ) + r=conn.store_result() + row = r.fetch_row(0) + assert( len(row) == 1 ) + #print 'row =', row + ipAddress = ('%s.%s.%s.%s') % (row[0][0], row[0][1], row[0][2], row[0][3]) + #print macAddress + conn.close() + return ipAddress + + +def getClusterMachinesNames(): + clusterMachinesNames = [] + conn = MySQLdb.connect('simpatix10', 'root', '', 'simpadb') + assert(conn) + sqlQuery = """SELECT name FROM machines WHERE affectation='cluster'""" + #print sqlQuery + conn.query( sqlQuery ) + r=conn.store_result() + rows = r.fetch_row(0) + for row in rows: + #print row + clusterMachinesNames.append( row[0] ) + conn.close() + return clusterMachinesNames + +def machineSupportsIpmi( machineName ): + if (machineName == 'simpatix') or (machineName == 'simpatix01' or (machineName == 'simpa-mac2')): + # the command ipmitool sensor on simpatix doesn't work : + # Unabled to establish a session with the BMC. + # Command failed due to Unknown (0xFFFEF921) (0xFFFEF921) + return False + return True + +def putToSleep( machineName ): + # note : pmset must be executed as root + (returnCode, stdout, stderr) = executeCommand([ 'ssh', machineName, 'pmset sleepnow' ]) + """ + print returnCode + print 'stdout :' + print stdout + print 'stderr :' + print stderr + """ + assert( returnCode == 0 ) + # check if the command succeeded by looking at the output (that's the only way I found) + f = StringIO.StringIO(stdout) + line = f.readline() + f.close() + matchObj = re.match('^Sleeping now...', line) + if matchObj: + return True + else: + return False + +def wakeUp(machineName): + macAddress = machineNameToMacAddress( machineName ) + wake_on_lan(macAddress) + return True + + +def isNonRespondingMachineSleeping(machineName): + """ + note : crappy method to detect if the machine is sleeping (if other methods are available, I would be very interested) + """ + wakeUp(machineName) + time.sleep(120) + if isMachineResponding( machineName ): + putToSleep( machineName ) + time.sleep(30) # allow a little time to make sure the machine is ready to receive other wake on lan messages + return True + else: + return False + +if __name__ == '__main__': + """ + for i in range(30): + machineName = 'simpatix%d' % (i+10) + print 'lom ip of %s is %s' % (machineName, getLightOutManagementIpAddress(machineName)) + """ + wakeUp('simpatix21') + #print putToSleep('simpatix13') + #print isNonRespondingMachineSleeping('simpatix13') \ No newline at end of file diff --git a/Util.py b/Util.py new file mode 100644 index 0000000..33e856a --- /dev/null +++ b/Util.py @@ -0,0 +1,69 @@ +import time +import subprocess +import StringIO +import re +from wol import * +import os +import signal +import smtplib +from email.MIMEText import MIMEText + +def sendTextMail(strFrom, to, strSubject, text ): + #from = "SimpaCluster " + mail = MIMEText(text) + mail['From'] = strFrom + mail['Subject'] = strSubject + mail['To'] = to + smtp = smtplib.SMTP('smtp.univ-rennes1.fr', 25) + #smtp.connect() + #smtp.login('guillaume.raffy@univ-rennes1.fr', 'password') + smtp.sendmail(strFrom, [to], mail.as_string()) + smtp.close() + +def log( message ): + print time.asctime(time.localtime())+' : '+ message + +def executeProgram( astrArguments ): + + #log('executeProgram : program [%s]' % (','.join(astrArguments))) + popen = subprocess.Popen( astrArguments, bufsize=1, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) # bufsize=1 seems to prevent deadlocks that happen 50% the time + stdout, stderr = popen.communicate() + #popen.wait() + result = (popen.returncode, stdout, stderr) + #log('executeProgram : command %s popen.pid = %d' % (astrArguments[0], popen.pid)) + #os.kill(popen.pid, signal.SIGTERM) + return result + +def executeCommand( command ): + """ + executes the shell command such as 'set x=1; myprog $x' + """ + popen = subprocess.Popen( [ command ], bufsize=1, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) # bufsize=1 seems to prevent deadlocks that happen 50% the time + stdout, stderr = popen.communicate() + #popen.wait() + result = (popen.returncode, stdout, stderr) + return result + + +def getUpsStatus(): + try: + url = 'http://Net Vision:public@129.20.27.119/PageMonComprehensive.html' + f = urllib.urlopen(url) + res = f.read() + f.close() + except: + print "bad read" + return + h = MyHTMLParser() + h.feed(res) + tokensList = h.GetTokenList() + +if __name__ == '__main__': + """ + for i in range(30): + machineName = 'simpatix%d' % (i+10) + print 'lom ip of %s is %s' % (machineName, getLightOutManagementIpAddress(machineName)) + """ + wakeUp('simpatix21') + #print putToSleep('simpatix13') + #print isNonRespondingMachineSleeping('simpatix13') \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..5a9ecee --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +# this file is here just so that the containing directory is treated as a python package \ No newline at end of file diff --git a/wol.py b/wol.py new file mode 100644 index 0000000..e5d3730 --- /dev/null +++ b/wol.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# wol.py + +import socket +import struct + +def wake_on_lan(macaddress): + """ Switches on remote computers using WOL. """ + + # Check macaddress format and try to compensate. + if len(macaddress) == 12: + pass + elif len(macaddress) == 12 + 5: + sep = macaddress[2] + macaddress = macaddress.replace(sep, '') + else: + raise ValueError('Incorrect MAC address format') + + # Pad the synchronization stream. + data = ''.join(['FFFFFFFFFFFF', macaddress * 20]) + send_data = '' + + # Split up the hex values and pack. + for i in range(0, len(data), 2): + send_data = ''.join([send_data, + struct.pack('B', int(data[i: i + 2], 16))]) + + # Broadcast it to the LAN. + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + sock.sendto(send_data, ('', 7)) + + +if __name__ == '__main__': + + # Use macaddresses with any seperators. + wake_on_lan('00:1E:52:F3:61:60') # simpatix28 + #wake_on_lan('00:24:36:F2:D0:FA') # simpatix33 + #wake_on_lan('0F:0F:DF:0F:BF:EF') + #wake_on_lan('0F-0F-DF-0F-BF-EF') + # or without any seperators. + #wake_on_lan('0F0FDF0FBFEF')