fence-agents: RHEL6 - fence_rsb: Fence agent ported to fencing library

Marek GrĂ¡c marx at fedoraproject.org
Thu Jan 12 15:45:20 UTC 2012


Gitweb:        http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=8a2a70414d7e287f801d31d7b89c9a1de269f936
Commit:        8a2a70414d7e287f801d31d7b89c9a1de269f936
Parent:        49a5baea19ac2a371439454d1cf01559612b6efe
Author:        Marek 'marx' Grac <mgrac at redhat.com>
AuthorDate:    Fri Nov 25 11:41:05 2011 +0100
Committer:     Marek 'marx' Grac <mgrac at redhat.com>
CommitterDate: Thu Jan 12 16:44:34 2012 +0100

fence_rsb: Fence agent ported to fencing library

Fence agent was rewritten to use fencing library. Main benefits are
support for ssh connection, XML metadata, automatic man page generation.

Resolves: rhbz#742003
---
 fence/agents/lib/fencing.py.py |    2 +-
 fence/agents/rsb/Makefile.am   |    5 +-
 fence/agents/rsb/fence_rsb.8   |   76 ------
 fence/agents/rsb/fence_rsb.py  |  491 ++++++++--------------------------------
 4 files changed, 102 insertions(+), 472 deletions(-)

diff --git a/fence/agents/lib/fencing.py.py b/fence/agents/lib/fencing.py.py
index ec84bbc..8be0a61 100644
--- a/fence/agents/lib/fencing.py.py
+++ b/fence/agents/lib/fencing.py.py
@@ -871,7 +871,7 @@ def fence_login(options):
 
 	try:
 		re_login = re.compile("(login\s*: )|(Login Name:  )|(username: )|(User Name :)", re.IGNORECASE)
-		re_pass  = re.compile("password", re.IGNORECASE)
+		re_pass  = re.compile("(password)|(pass phrase)", re.IGNORECASE)
 
 		if options.has_key("-z"):
 			command = '%s %s %s %s' % (SSL_PATH, force_ipvx, options["-a"], options["-u"])
diff --git a/fence/agents/rsb/Makefile.am b/fence/agents/rsb/Makefile.am
index e8193be..939db98 100644
--- a/fence/agents/rsb/Makefile.am
+++ b/fence/agents/rsb/Makefile.am
@@ -8,9 +8,10 @@ EXTRA_DIST		= $(SRC)
 
 sbin_SCRIPTS		= $(TARGET)
 
-dist_man_MANS		= $(TARGET).8
+man_MANS		= $(TARGET).8
 
 include $(top_srcdir)/make/fencebuild.mk
+include $(top_srcdir)/make/fenceman.mk
 
-clean-local:
+clean-local: clean-man
 	rm -f $(TARGET)
diff --git a/fence/agents/rsb/fence_rsb.8 b/fence/agents/rsb/fence_rsb.8
deleted file mode 100644
index d278271..0000000
--- a/fence/agents/rsb/fence_rsb.8
+++ /dev/null
@@ -1,76 +0,0 @@
-.TH fence_rsb 8
-
-.SH NAME
-fence_rsb - I/O Fencing agent for Fujitsu-Siemens RSB
-
-.SH SYNOPSIS
-.B 
-fence_rsb
-[\fIOPTION\fR]...
-
-.SH DESCRIPTION
-fence_rsb is an I/O Fencing agent which can be used with the Fujitsu-Siemens
-RSB management interface.  It logs into an RSB device via telnet and reboots
-the associated machine. Lengthy telnet connections to the RSB device 
-should be avoided while a GFS cluster is running because the connection 
-will block any necessary fencing actions.
-
-fence_rsb accepts options on the command line as well as from stdin.  
-Fenced sends parameters through stdin when it execs the agent.  fence_rsb
-can be run by itself with command line options.  This is useful for testing.
-
-Vendor URL: http://www.fujitsu.com
-.SH OPTIONS
-.TP
-\fB-a\fP \fIIPaddress\fR
-IP address or hostname of the RSB device.
-.TP
-\fB-h\fP 
-Print out a help message describing available options, then exit.
-.TP
-\fB-l\fP \fIlogin\fR
-Login name.
-.TP
-\fB-n\fP \fItelnet_port\fR
-The port number on which the telnet service listens.
-.TP
-\fB-o\fP \fIaction\fR
-The action required.  This can be reboot (default), off, on, or status.
-.TP
-\fB-p\fP \fIpassword\fR
-Password for login.
-.TP
-\fB-S\fP \fIpath\fR
-Full path to an executable to generate the password for login.
-.TP
-\fB-v\fP
-Verbose.  Print informational messages to standard out.
-.TP
-\fB-V\fP
-Print out a version message, then exit.
-
-.SH STDIN PARAMETERS
-.TP
-\fIagent = < param >\fR
-This option is used by fence_node(8) and is ignored by fence_rsb.
-.TP
-\fIipaddr = < hostname | ip >\fR
-IP address or hostname of the device.
-.TP
-\fItelnet_port = < port number >\fR
-The port number on which the telnet service listens.
-.TP
-\fIlogin = < param >\fR
-Login name.
-.TP
-\fIoption = < param >\fR
-The action required.  This can be reboot (default), off, on, or status.
-.TP
-\fIpasswd = < param >\fR
-Password for login.
-.TP
-\fIpasswd_script = < param >\fR
-Full path to an executable to generate the password for login.
-
-.SH SEE ALSO
-fence(8), fence_node(8)
diff --git a/fence/agents/rsb/fence_rsb.py b/fence/agents/rsb/fence_rsb.py
old mode 100644
new mode 100755
index 759ae2d..fe1ec60
--- a/fence/agents/rsb/fence_rsb.py
+++ b/fence/agents/rsb/fence_rsb.py
@@ -1,18 +1,8 @@
 #!/usr/bin/python
 
-import getopt, sys
-import os
-import socket
-import time
-import atexit
-
-from telnetlib import Telnet
-
-TELNET_TIMEOUT=30 #How long to wait for a response from a telnet try
-MAX_TRIES = 20
-
-# WARNING!! Do not add code bewteen "#BEGIN_VERSION_GENERATION" and
-# "#END_VERSION_GENERATION"  It is generated by the Makefile
+import sys, re, pexpect, exceptions
+sys.path.append("@FENCEAGENTSLIBDIR@")
+from fencing import *
 
 #BEGIN_VERSION_GENERATION
 RELEASE_VERSION=""
@@ -20,389 +10,104 @@ REDHAT_COPYRIGHT=""
 BUILD_DATE=""
 #END_VERSION_GENERATION
 
-def usage():
-  print "Usage:"
-  print "fence_rsb [options]"
-  print "Options:"
-  print "   -a <ipaddress>           ip or hostname of rsb"
-  print "   -h                       print out help"
-  print "   -l [login]               login name"
-  print "   -n [telnet port]         telnet port"
-  print "   -p [password]            password"
-  print "   -S [path]                script to run to retrieve password"
-  print "   -o [action]              reboot (default), off, on, or status"
-  print "   -v Verbose               Verbose mode"
-  print "   -V                       Print Version, then exit"
-
-  sys.exit (0)
-
-def version():
-  print "fence_rsb %s  %s\n" % (RELEASE_VERSION, BUILD_DATE)
-  print "%s\n" % REDHAT_COPYRIGHT
-  sys.exit(0)
+def get_power_status(conn, options):
+	try:
+		conn.send("2")
+		conn.log_expect(options, options["-c"], int(options["-Y"]))
+		status = re.compile("Power Status : (on|off)", re.IGNORECASE).search(conn.before).group(1)
+		conn.send("0")
+		conn.log_expect(options, options["-c"], int(options["-Y"]))
+
+		return (status.lower().strip())
+	except pexpect.EOF:
+		fail(EC_CONNECTION_LOST)
+	except pexpect.TIMEOUT:
+		fail(EC_TIMED_OUT)
+
+def set_power_status(conn, options):
+	action = {
+		'on' : "4",
+		'off': "1"
+	}[options["-o"]]
 
-def atexit_handler():
 	try:
-		sys.stdout.close()
-		os.close(1)
-	except IOError:
-		sys.stderr.write("%s failed to close standard output\n"%(sys.argv[0]))
-		sys.exit(1)
+		conn.send("2")
+		conn.log_expect(options, options["-c"], int(options["-Y"]))
+		conn.sendline(action)
+		conn.log_expect(options, ["want to power off", "'yes' or 'no'"], int(options["-Y"]))
+		conn.send("yes\r")
+		conn.log_expect(options, "any key to continue", int(options["-g"]))
+		conn.send("\r")
+		conn.log_expect(options, options["-c"], int(options["-Y"]))
+		conn.sendline("0")
+		conn.log_expect(options, options["-c"], int(options["-Y"]))
+	except pexpect.EOF:
+		fail(EC_CONNECTION_LOST)
+	except pexpect.TIMEOUT:
+		fail(EC_TIMED_OUT)
 
 def main():
-  depth = 0
-  POWER_OFF = 0
-  POWER_ON = 1
-  POWER_STATUS = 2
-  POWER_REBOOT = 3
-
-  STATUS_ON = 0
-  STATUS_OFF = 2
-
-  power_command_issued = 0 
-
-  address = ""
-  login = ""
-  passwd = ""
-  passwd_script = ""
-  action = POWER_REBOOT   #default action
-  telnet_port = 3172
-  verbose = False
-  power_state = None
-
-  standard_err = 2
-
-  result = 0
-
-  completed_action = 0
-
-  #set up regex list
-  USERNAME = 0
-  PASSWORD = 1
-  PROMPT = 2
-  STATE = 3
-  ERROR = 4
-  CONT = 5
-  CONFIRM = 6
-  DONE = 7
-
-  regex_list = list()
-  regex_list.append("user name\s*:")
-  regex_list.append("pass phrase\s*:")
-  regex_list.append("[Ee]nter\s+[Ss]election[^\r\n]*:")
-  regex_list.append("[pP]ower Status\s*:")
-  regex_list.append("[Ee]rror\s*:")
-  regex_list.append("[Pp]ress any key to continue")
-  regex_list.append("really want to")
-  regex_list.append("CLOSING TELNET CONNECTION")
-
-  atexit.register(atexit_handler)
-
-  if len(sys.argv) > 1:
-    try:
-      opts, args = getopt.getopt(sys.argv[1:], "a:hl:n:o:p:S:vV", ["help", "output="])
-    except getopt.GetoptError:
-      #print help info and quit
-      usage()
-      sys.exit(2)
-
-    for o, a in opts:
-      if o == "-v":
-        verbose = True
-      if o == "-V":
-        version()
-      if o in ("-h", "--help"):
-        usage()
-        sys.exit(0)
-      if o == "-l":
-        login = a
-      if o == "-n":
-        telnet_port = a
-      if o == "-p":
-        passwd = a
-      if o == "-S":
-        passwd_script = a
-      if o  == "-o":
-        a_lower=a.lower()
-        if a_lower == "off":
-          action = POWER_OFF
-        elif a_lower == "on":
-          action = POWER_ON
-        elif a_lower == "status":
-          action = POWER_STATUS
-        elif a_lower == "reboot":
-          action = POWER_REBOOT
-        else:
-          usage()
-          sys.exit(1)
-      if o == "-a":
-        address = a
-    if address == "" or login == "" or (passwd == "" and passwd_script == ""):
-      usage()
-      sys.exit(1)
-
-  else: #Take args from stdin...
-    params = {}
-    #place params in dict
-    for line in sys.stdin:
-      val = line.split("=")
-      if len(val) == 2:
-        params[val[0].strip()] = val[1].strip()
-
-    try:
-      address = params["ipaddr"]
-    except KeyError, e:
-      os.write(standard_err, "FENCE: Missing ipaddr param for fence_rsb...exiting")
-      sys.exit(1)
-    
-    try:
-      login = params["login"]
-    except KeyError, e:
-      os.write(standard_err, "FENCE: Missing login param for fence_rsb...exiting")
-      sys.exit(1)
-    
-    try:
-      if 'passwd' in params:
-        passwd = params["passwd"]
-      if 'passwd_script' in params:
-        passwd_script = params['passwd_script']
-      if passwd == "" and passwd_script == "":
-        raise Exception, "missing password"
-    except KeyError, e:
-      os.write(standard_err, "FENCE: Missing passwd param for fence_rsb...exiting")
-      sys.exit(1)
-    
-    try:
-      telnet_port = params["telnet_port"]
-    except KeyError, e:
-      pass
-
-    try:
-      a = params["option"]
-      a_lower=a.lower()
-      if a_lower == "off":
-        action = POWER_OFF
-      elif a_lower == "on":
-        action = POWER_ON
-      elif a_lower == "reboot":
-        action = POWER_REBOOT
-    except KeyError, e:
-      action = POWER_REBOOT
-
-    ####End of stdin section
-  
-  
-  # retrieve passwd from passwd_script (if specified)
-  passwd_scr = ''
-  if len(passwd_script):
-    try:
-      if not os.access(passwd_script, os.X_OK):
-        raise Exception, 'script not executable'
-      p = os.popen(passwd_script, 'r', 1024)
-      passwd_scr = p.readline().strip()
-      if p.close() != None:
-        raise Exception, 'script failed'
-    except:
-      sys.stderr.write('password-script "%s" failed\n' % passwd_script)
-      passwd_scr = ''
-  
-  if passwd == "" and passwd_scr == "":
-    sys.stderr.write('password not available, exiting...')
-    sys.exit(1)
-  elif passwd == passwd_scr:
-    pass
-  elif passwd and passwd_scr:
-    # execute self, with password_scr as passwd,
-    # if that fails, continue with "passwd" argument as password
-    if len(sys.argv) > 1:
-      comm = sys.argv[0]
-      skip_next = False
-      for w in sys.argv[1:]:
-        if skip_next:
-          skip_next = False
-        elif w in ['-p', '-S']:
-          skip_next = True
-        else:
-          comm += ' ' + w
-      comm += ' -p ' + passwd_scr
-      ret = os.system(comm)
-      if ret != -1 and os.WIFEXITED(ret) and os.WEXITSTATUS(ret) == 0:
-        # success
-        sys.exit(0)
-      else:
-        sys.stderr.write('Use of password from "passwd_script" failed, trying "passwd" argument\n')
-    else: # use stdin
-      p = os.popen(sys.argv[0], 'w', 1024)
-      for par in params:
-        if par not in ['passwd', 'passwd_script']:
-          p.write(par + '=' + params[par] + '\n')
-      p.write('passwd=' + passwd_scr + '\n')
-      p.flush()
-      if p.close() == None:
-        # success
-        sys.exit(0)
-      else:
-        sys.stderr.write('Use of password from "passwd_script" failed, trying "passwd" argument\n')
-  elif passwd_scr:
-    passwd = passwd_scr
-  # passwd all set
-  
-  
-  
-  try:
-    telnet_port = int(telnet_port)
-  except:
-    os.write(standard_err, ("FENCE: Invalid telnet port: %s\n" % telnet_port))
-    sys.exit(1)
-    
-  ##Time to open telnet session and log in. 
-  try:
-    sock = Telnet(address.strip(), telnet_port)
-  except socket.error, (errno, msg):
-    my_msg = "FENCE: A problem was encountered opening a telnet session with " + address
-    os.write(standard_err, my_msg)
-    os.write(standard_err, ("FENCE: Error number: %d -- Message: %s\n" % (errno, msg)))
-    os.write(standard_err, "Firewall issue? Correct address?\n")
-    sys.exit(1)
-
-  if verbose:
-    #sock.set_debuglevel(10000)
-    print  "socket open to %s %d\n" % (address, telnet_port)
-
-  tries = MAX_TRIES
-  while 1:
-    i, mo, txt = sock.expect(regex_list, TELNET_TIMEOUT)
-    if i == ERROR:
-      os.write(standard_err,("FENCE: An error was encountered when communicating with the rsb device at %s" % address))
-      buf = sock.read_eager()
-      os.write(standard_err,("FENCE: The error message is - %s" % txt + " " + buf))
-      sock.close()
-      sys.exit(1)
-
-    try:
-      buf = sock.read_eager()
-    except EOFError:
-      if completed_action == 1:
-      	# action was completed succesfully, connection closed is OK
-        sys.exit(result)
-      else:
-        raise
-	         
-    if i == USERNAME:
-      if verbose:
-        print "Sending login: %s\n" % login
-      sock.write(login + "\r")
-
-    elif i == PASSWORD:
-      if verbose:
-        print "Sending password: %s\n" % passwd
-      sock.write(passwd + "\r")
-
-    elif i == CONT:
-      if verbose:
-        print "Sending continue char..."
-      sock.write("\r")
-      time.sleep(2)
-
-    elif i == CONFIRM:
-      if verbose:
-        print "Confirming..."
-      sock.write("yes\r")
-
-    elif i == PROMPT:
-      if verbose:
-        print "Evaluating prompt...\n"
-
-      if depth == 0:
-        sock.write("2\r")
-        depth += 1
-      elif depth == 1:
-        if action == POWER_OFF or action == POWER_REBOOT:
-          if power_command_issued == 0:
-            if verbose:
-              print "Sending power off %s" % address
-            sock.write("1\r")
-            power_command_issued += 1
-            time.sleep(2)
-          elif power_command_issued and power_state == 0:
-            if verbose:
-              print "Power off was successful"
-            if action == POWER_OFF:
-              completed_action = 1
-              depth += 1
-              sock.write("0\r")
-            else:
-              action = POWER_ON
-              power_command_issued = 0
-              sock.write("\r")
-          elif tries > 0:
-            if verbose:
-              print "Waiting for power off to complete"
-            tries -= 1
-            sock.write("\r")
-            time.sleep(2)
-          else:
-            os.write(standard_err, "FENCE: Unable to power off server")
-            depth += 1
-            sock.write("0\r")
-
-        elif action == POWER_ON:
-          if power_command_issued == 0:
-            if verbose:
-              print "Sending power on %s" % address
-            sock.write("4\r")
-            power_command_issued += 1
-            time.sleep(2)
-          elif power_command_issued and power_state == 1:
-            if verbose:
-              print "Power on was successful"
-            completed_action = 1
-            depth += 1
-            sock.write("0\r")
-          elif tries > 0:
-            if verbose:
-              print "Waiting for power on to complete"
-            tries -= 1
-            sock.write("\r")
-            time.sleep(2)
-          else:
-            os.write(standard_err, "FENCE: Unable to power on server")
-            depth += 1
-            sock.write("0\r")
-      else:
-        sock.write("0\r")
-
-    elif i == STATE:
-      if buf.find(" On") != (-1):
-        power_state = 1
-      elif buf.find(" Off") != (-1):
-        power_state = 0
-      else:
-        power_state = None
-
-      if action == POWER_STATUS:
-        if verbose:
-          print "Determining power state..."
-        if power_state == 1:
-          print "Server is On"
-          result = STATUS_ON
-        elif power_state == 0:
-          print "Server is Off"
-          result = STATUS_OFF
-        else:
-          os.write(standard_err, ("FENCE: Cannot determine power state: %s" % buf))
-          sys.exit(1)
-        depth = 2
-        completed_action = 1
-
-    elif i == DONE:
-      break
-
-    else:
-      sock.write("\r")
-
-  sock.close()
-  sys.exit(result)
+	device_opt = [  "help", "version", "agent", "quiet", "verbose", "debug",
+			"action", "ipaddr", "login", "passwd", "passwd_script",
+			"secure", "identity_file", "separator", "cmd_prompt",
+			"inet4_only", "inet6_only", "ipport", "telnet_port",
+			"power_timeout", "shell_timeout", "login_timeout", "power_wait" ]
+
+	atexit.register(atexit_handler)
+	all_opt["telnet_port"] = {
+		"getopt" : "n:",
+                "longopt" : "telnet_port",
+                "help" : "-n                             TCP port to use (deprecated, use -u)",
+                "required" : "0",
+                "shortdesc" : "TCP port to use for connection with device (default is 3172 for telnet)",
+                "order" : 1
+	}
+	all_opt["cmd_prompt"]["default"] = "to quit:"
+
+	opt = process_input(device_opt)
+	# option -n for backward compatibility (-n is normally port no)
+	if 1 == opt.has_key("-n"):
+		opt["-u"] = opt["-n"]
+
+	# set default port for telnet only
+	if 0 == opt.has_key("-x") and 0 == opt.has_key("-u"):
+		opt["-u"] = 3172
+	options = check_input(device_opt, opt)
+
+	docs = { }
+	docs["shortdesc"] = "I/O Fencing agent for Fujitsu-Siemens RSB"
+	docs["longdesc"] = "fence_rsb is an I/O Fencing agent \
+which can be used with the Fujitsu-Siemens RSB management interface. It logs \
+into device via telnet/ssh  and reboots a specified outlet. Lengthy telnet/ssh \
+connections should be avoided while a GFS cluster is running because the connection \
+will block any necessary fencing actions."
+	docs["vendorurl"] = "http://www.fujitsu.com"
+	show_docs(options, docs)
+
+	##
+	## Operate the fencing device
+	####
+	print options["-u"]
+
+	conn = fence_login(options)
+	result = fence_action(conn, options, set_power_status, get_power_status, None)
+
+	##
+	## Logout from system
+	##
+	## In some special unspecified cases it is possible that 
+	## connection will be closed before we run close(). This is not 
+	## a problem because everything is checked before.
+	######
+	try:
+		conn.sendline("0")
+		conn.close()
+	except exceptions.OSError:
+		pass
+	except pexpect.ExceptionPexpect:
+		pass
+	
+	sys.exit(result)
 
 if __name__ == "__main__":
-  main()
+	main()


More information about the cluster-commits mailing list