diff options
Diffstat (limited to 'lib/plugins/stonith/external/ibmrsa-telnet')
-rw-r--r-- | lib/plugins/stonith/external/ibmrsa-telnet | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/lib/plugins/stonith/external/ibmrsa-telnet b/lib/plugins/stonith/external/ibmrsa-telnet new file mode 100644 index 0000000..4d75d9a --- /dev/null +++ b/lib/plugins/stonith/external/ibmrsa-telnet @@ -0,0 +1,320 @@ +#!/usr/bin/python +# vim: set filetype=python +####################################################################### +# +# ibmrsa-telnet - External stonith plugin for HAv2 (http://linux-ha.org/wiki) +# Connects to IBM RSA Board via telnet and switches power +# of server appropriately. +# +# Author: Andreas Mock (andreas.mock@web.de) +# +# History: +# 2007-10-19 Fixed bad commandline handling in case of stonithing +# 2007-10-11 First release. +# +# Comment: Please send bug fixes and enhancements. +# I hope the functionality of communicating via telnet is encapsulated +# enough so that someone can use it for similar purposes. +# +# Description: IBM offers Remote Supervisor Adapters II for several +# servers. These RSA boards can be accessed in different ways. +# One of that is via telnet. Once logged in you can use 'help' to +# show all available commands. With 'power' you can reset, power on and +# off the controlled server. This command is used in combination +# with python's standard library 'telnetlib' to do it automatically. +# +# cib-snippet: Please see README.ibmrsa-telnet for examples. +# +# Copyright (c) 2007 Andreas Mock (andreas.mock@web.de) +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of version 2 or later of the GNU General Public +# License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Further, this software is distributed without any warranty that it is +# free of the rightful claim of any third person regarding infringement +# or the like. Any license provided herein, whether implied or +# otherwise, applies only to this software file. Patent licenses, if +# any, provided herein do not apply to combinations of this program with +# other software, or any other product whatsoever. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. +# +####################################################################### +import sys +import os +import time +import telnetlib +import subprocess + +class TimeoutException(Exception): + def __init__(self, value=None): + Exception.__init__(self) + self.value = value + + def __str__(self): + return repr(self.value) + +class RSABoard(telnetlib.Telnet): + def __init__(self, *args, **kwargs): + telnetlib.Telnet.__init__(self, *args, **kwargs) + self._timeout = 10 + self._loggedin = 0 + self._history = [] + self._appl = os.path.basename(sys.argv[0]) + + def _get_timestamp(self): + ct = time.time() + msecs = (ct - long(ct)) * 1000 + return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S", + time.localtime(ct)), msecs) + + def write(self, buffer, nolog = False): + self._history.append(self._get_timestamp() + ': WRITE: ' + + (nolog and '******' or repr(buffer))) + telnetlib.Telnet.write(self, buffer) + + def expect(self, what, timeout=20): + line = telnetlib.Telnet.expect(self, what, timeout) + self._history.append(self._get_timestamp() + ': READ : ' + repr(line)) + if not line: + raise TimeoutException("Timeout while waiting for '%s'." % (what, )) + return line + + def login(self, user, passwd): + time.sleep(1) + line = self.expect(['\nlogin : ', '\nusername: '], self._timeout) + self.write(user) + self.write('\r') + line = self.expect(['\nPassword: ', '\npassword: '], self._timeout) + self.write(passwd, nolog = True) + self.write('\r') + line = self.expect(['\nsystem>', '> '], self._timeout) + + def reset(self): + self.write('power cycle\r') + line = self.expect(['\nok'], self._timeout) + line = self.expect(['\nsystem>', '> '], self._timeout) + + def on(self): + self.write('power on\r') + line = self.expect(['\nok'], self._timeout) + line = self.expect(['\nsystem>', '> '], self._timeout) + + def off(self): + self.write('power off\r') + line = self.expect(['\nok'], self._timeout) + line = self.expect(['\nsystem>', '> '], self._timeout) + + def exit(self): + self.write('exit\r') + + def get_history(self): + return "\n".join(self._history) + + +class RSAStonithPlugin: + def __init__(self): + # define the external stonith plugin api + self._required_cmds = \ + 'reset gethosts status getconfignames getinfo-devid ' \ + 'getinfo-devname getinfo-devdescr getinfo-devurl ' \ + 'getinfo-xml' + self._optional_cmds = 'on off' + self._required_cmds_list = self._required_cmds.split() + self._optional_cmds_list = self._optional_cmds.split() + + # who am i + self._appl = os.path.basename(sys.argv[0]) + + # telnet connection object + self._connection = None + + # the list of configuration names + self._confignames = ['nodename', 'ip_address', 'username', 'password'] + + # catch the parameters provided by environment + self._parameters = {} + for name in self._confignames: + try: + self._parameters[name] = os.environ.get(name, '').split()[0] + except IndexError: + self._parameters[name] = '' + + def _get_timestamp(self): + ct = time.time() + msecs = (ct - long(ct)) * 1000 + return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S", + time.localtime(ct)), msecs) + + def _echo_debug(self, *args): + self.echo_log('debug', *args) + + def echo(self, *args): + what = ''.join([str(x) for x in args]) + sys.stdout.write(what) + sys.stdout.write('\n') + sys.stdout.flush() + self._echo_debug("STDOUT:", what) + + def echo_log(self, level, *args): + subprocess.call(('ha_log.sh', level) + args) + + def _get_connection(self): + if not self._connection: + c = RSABoard() + self._echo_debug("Connect to '%s'" % + (self._parameters['ip_address'],)) + c.open(self._parameters['ip_address']) + self._echo_debug("Connection established") + c.login(self._parameters['username'], + self._parameters['password']) + self._connection = c + + def _end_connection(self): + if self._connection: + self._connection.exit() + self._connection.close() + + def reset(self): + self._get_connection() + self._connection.reset() + self._end_connection() + self._echo_debug(self._connection.get_history()) + self.echo_log("info", "Reset of node '%s' done" % + (self._parameters['nodename'],)) + return(0) + + def on(self): + self._get_connection() + self._connection.on() + self._end_connection() + self._echo_debug(self._connection.get_history()) + self.echo_log("info", "Switched node '%s' ON" % + (self._parameters['nodename'],)) + return(0) + + def off(self): + self._get_connection() + self._connection.off() + self._end_connection() + self._echo_debug(self._connection.get_history()) + self.echo_log("info", "Switched node '%s' OFF" % + (self._parameters['nodename'],)) + return(0) + + def gethosts(self): + self.echo(self._parameters['nodename']) + return(0) + + def status(self): + self._get_connection() + self._end_connection() + self._echo_debug(self._connection.get_history()) + return(0) + + def getconfignames(self): + for name in ['nodename', 'ip_address', 'username', 'password']: + self.echo(name) + return(0) + + def getinfo_devid(self): + self.echo("External Stonith Plugin for IBM RSA Boards") + return(0) + + def getinfo_devname(self): + self.echo("External Stonith Plugin for IBM RSA Boards connecting " + "via Telnet") + return(0) + + def getinfo_devdescr(self): + self.echo("External stonith plugin for HAv2 which connects to " + "a RSA board on IBM servers via telnet. Commands to " + "turn on/off power and to reset server are sent " + "appropriately. " + "(c) 2007 by Andreas Mock (andreas.mock@web.de)") + return(0) + + def getinfo_devurl(self): + self.echo("http://www.ibm.com/Search/?q=remote+supervisor+adapter") + + def getinfo_xml(self): + info = """<parameters> + <parameter name="nodename" unique="1" required="1"> + <content type="string" /> + <shortdesc lang="en">nodename to shoot</shortdesc> + <longdesc lang="en"> + Name of the node which has to be stonithed in case. + </longdesc> + </parameter> + <parameter name="ip_address" unique="1" required="1"> + <content type="string" /> + <shortdesc lang="en">hostname or ip address of RSA</shortdesc> + <longdesc lang="en"> + Hostname or ip address of RSA board used to reset node. + </longdesc> + </parameter> + <parameter name="username" unique="1" required="1"> + <content type="string" /> + <shortdesc lang="en">username to login on RSA board</shortdesc> + <longdesc lang="en"> + Username to login on RSA board. + </longdesc> + </parameter> + <parameter name="password" unique="1" required="1"> + <content type="string" /> + <shortdesc lang="en">password to login on RSA board</shortdesc> + <longdesc lang="en"> + Password to login on RSA board. + </longdesc> + </parameter> + </parameters> + """ + self.echo(info) + return(0) + + def not_implemented(self, cmd): + self.echo_log("err", "Command '%s' not implemented." % (cmd,)) + return(1) + + def usage(self): + usage = "Call me with one of the allowed commands: %s, %s" % ( + ', '.join(self._required_cmds_list), + ', '.join(self._optional_cmds_list)) + return usage + + def process(self, argv): + self._echo_debug("========== Start =============") + if len(argv) < 1: + self.echo_log("err", 'At least one commandline argument required.') + return(1) + cmd = argv[0] + self._echo_debug("cmd:", cmd) + if cmd not in self._required_cmds_list and \ + cmd not in self._optional_cmds_list: + self.echo_log("err", "Command '%s' not supported." % (cmd,)) + return(1) + try: + cmd = cmd.lower().replace('-', '_') + func = getattr(self, cmd, self.not_implemented) + rc = func() + return(rc) + except Exception, args: + self.echo_log("err", 'Exception raised:', str(args)) + if self._connection: + self.echo_log("err", self._connection.get_history()) + self._connection.close() + return(1) + + +if __name__ == '__main__': + stonith = RSAStonithPlugin() + rc = stonith.process(sys.argv[1:]) + sys.exit(rc) |