cluster: STABLE2 - fence_cisco: Added fence agent for Cisco MDS 9124/9134

Jan Friesse honzaf@fedoraproject.org
Mon Mar 30 12:44:00 GMT 2009


Gitweb:        http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=61a99bda889ccf696495b4095e93b04bfdee88e2
Commit:        61a99bda889ccf696495b4095e93b04bfdee88e2
Parent:        c097a807fcc3482ee0b899ef6311ca7cae2a278e
Author:        Jan Friesse <jfriesse@redhat.com>
AuthorDate:    Mon Mar 30 14:10:40 2009 +0200
Committer:     Jan Friesse <jfriesse@redhat.com>
CommitterDate: Mon Mar 30 14:43:21 2009 +0200

fence_cisco: Added fence agent for Cisco MDS 9124/9134

Agent uses SNMP, so Python fencing_snmp library is included
in this patch too. This library uses fencing.py library,
and add option to write small agents using SNMP. Library
contains class FencingSnmp, designed to be instanciate and
passed in agent's call of fence_action. After that, developer
has in conn variable this object, and can call functions
set/get/walk.

RHBZ#480836
---
 fence/agents/cisco_mds/Makefile           |    5 +
 fence/agents/cisco_mds/fence_cisco_mds.py |  113 ++++++++++++++++++++++++
 fence/agents/lib/Makefile                 |    2 +-
 fence/agents/lib/fencing.py.py            |   37 ++++++++-
 fence/agents/lib/fencing_snmp.py.py       |  117 +++++++++++++++++++++++++
 fence/man/Makefile                        |    1 +
 fence/man/fence_cisco_mds.8               |  132 +++++++++++++++++++++++++++++
 7 files changed, 405 insertions(+), 2 deletions(-)

diff --git a/fence/agents/cisco_mds/Makefile b/fence/agents/cisco_mds/Makefile
new file mode 100644
index 0000000..e4e8c9f
--- /dev/null
+++ b/fence/agents/cisco_mds/Makefile
@@ -0,0 +1,5 @@
+include ../../../make/defines.mk
+
+TARGET= fence_cisco_mds
+
+include $(OBJDIR)/make/fencebuild.mk
diff --git a/fence/agents/cisco_mds/fence_cisco_mds.py b/fence/agents/cisco_mds/fence_cisco_mds.py
new file mode 100644
index 0000000..bda66bb
--- /dev/null
+++ b/fence/agents/cisco_mds/fence_cisco_mds.py
@@ -0,0 +1,113 @@
+#!/usr/bin/python
+
+# The Following agent has been tested on:
+# - Cisco MDS UROS 9134 FC (1 Slot) Chassis ("1/2/4 10 Gbps FC/Supervisor-2") Motorola, e500v2
+#   with BIOS 1.0.16, kickstart 4.1(1c), system 4.1(1c)
+# - Cisco MDS 9124 (1 Slot) Chassis ("1/2/4 Gbps FC/Supervisor-2") Motorola, e500
+#   with BIOS 1.0.16, kickstart 4.1(1c), system 4.1(1c)
+
+import sys, re, pexpect
+sys.path.append("@FENCEAGENTSLIBDIR@")
+from fencing import *
+from fencing_snmp import *
+
+#BEGIN_VERSION_GENERATION
+RELEASE_VERSION=""
+REDHAT_COPYRIGHT=""
+BUILD_DATE=""
+#END_VERSION_GENERATION
+
+### CONSTANTS ###
+# Cisco admin status
+PORT_ADMIN_STATUS_OID=".1.3.6.1.2.1.75.1.2.2.1.1"
+
+# IF-MIB trees for alias, status and port
+ALIASES_OID=".1.3.6.1.2.1.31.1.1.1.18"
+PORTS_OID=".1.3.6.1.2.1.2.2.1.2"
+
+### GLOBAL VARIABLES ###
+# OID converted from fc port name (fc(x)/(y))
+port_oid=""
+
+### FUNCTIONS ###
+
+# Convert cisco port name (fc(x)/(y)) to OID
+def cisco_port2oid(port):
+	port=port.lower()
+
+	nums=re.match('^fc(\d+)/(\d+)$',port)
+
+	if ((nums) and (len(nums.groups()))==2):
+		return "%s.%d.%d"%(PORT_ADMIN_STATUS_OID,int(nums.group(1))+21,int(nums.group(2))-1)
+	else:
+		fail_usage("Mangled port number: %s"%(port))
+
+def get_power_status(conn,options):
+	global port_oid
+
+	(oid,status)=conn.get(port_oid)
+	return (status=="1" and "on" or "off")
+
+def set_power_status(conn, options):
+	global port_oid
+
+	conn.set(port_oid,(options["-o"]=="on" and 1 or 2))
+
+# Convert array of format [[key1, value1], [key2, value2], ... [keyN, valueN]] to dict, where key is
+# in format a.b.c.d...z and returned dict has key only z
+def array_to_dict(ar):
+	return dict(map(lambda y:[y[0].split('.')[-1],y[1]],ar))
+
+def get_outlets_status(conn, options):
+	result={}
+
+	res_fc=conn.walk(PORTS_OID,30)
+	res_aliases=array_to_dict(conn.walk(ALIASES_OID,30))
+
+	fc_re=re.compile('^"fc\d+/\d+"$')
+
+	for x in res_fc:
+		if fc_re.match(x[1]):
+			port_num=x[0].split('.')[-1]
+
+			port_name=x[1].strip('"')
+			port_alias=(res_aliases.has_key(port_num) and res_aliases[port_num].strip('"') or "")
+			port_status=""
+			result[port_name]=(port_alias,port_status)
+
+	return result
+
+# Main agent method
+def main():
+	global port_oid
+
+	device_opt = [ "help", "version", "agent", "quiet", "verbose", "debug",
+		       "action", "ipaddr", "login", "passwd", "passwd_script",
+		       "test", "port", "no_login", "no_password",
+		       "snmp_version", "community", "snmp_auth_prot", "snmp_sec_level",
+		       "snmp_priv_prot", "snmp_priv_passwd", "snmp_priv_passwd_script",
+		       "udpport"]
+
+	options=process_input(device_opt)
+
+	# Emulate enable/disable functionality
+	if (options.has_key("-o")):
+		options["-o"]=options["-o"].lower()
+
+		if (options["-o"]=="enable"):
+			options["-o"]="on"
+		if (options["-o"]=="disable"):
+			options["-o"]="off"
+	else:
+		options["-o"]="off"
+
+	options = check_input(device_opt, options)
+
+	if (not (options["-o"] in ["list","monitor"])):
+		port_oid=cisco_port2oid(options["-n"])
+
+	# Operate the fencing device
+	fence_action(FencingSnmp(options), options, set_power_status, get_power_status)
+
+if __name__ == "__main__":
+	main()
diff --git a/fence/agents/lib/Makefile b/fence/agents/lib/Makefile
index 00666fa..049ff6e 100644
--- a/fence/agents/lib/Makefile
+++ b/fence/agents/lib/Makefile
@@ -1,6 +1,6 @@
 include ../../../make/defines.mk
 
-TARGET= fencing.py telnet_ssl
+TARGET= fencing.py telnet_ssl fencing_snmp.py
 
 FENCEAGENTSLIB= $(TARGET)
 
diff --git a/fence/agents/lib/fencing.py.py b/fence/agents/lib/fencing.py.py
index 64a13aa..1cf1220 100644
--- a/fence/agents/lib/fencing.py.py
+++ b/fence/agents/lib/fencing.py.py
@@ -152,7 +152,39 @@ all_opt = {
 	"vmpasswd_script" : {
 		"getopt" : "B:",
 		"help" : "-B <script>    Script to run to retrieve VMware ESX management password",
-		"order" : 2 }
+		"order" : 2 },
+	"snmp_version" : {
+		"getopt" : "d:",
+		"help" : "-d <ver>       Specifies SNMP version to use",
+		"order" : 1 },
+	"community" : {
+		"getopt" : "c:",
+		"help" : "-c <community> Set the community string",
+		"order" : 1},
+	"snmp_auth_prot" : {
+		"getopt" : "b:",
+		"help" : "-b <prot>      Set authentication protocol (MD5|SHA)",
+		"order" : 1},
+	"snmp_sec_level" : {
+		"getopt" : "E:",
+		"help" : "-E <level>     Set security level (noAuthNoPriv|authNoPriv|authPriv)",
+		"order" : 1},
+	"snmp_priv_prot" : {
+		"getopt" : "B:",
+		"help" : "-B <prot>      Set privacy protocol (DES|AES)",
+		"order" : 1},
+	"snmp_priv_passwd" : {
+		"getopt" : "P:",
+		"help" : "-P <pass>      Set privacy protocol password",
+		"order" : 1},
+	"snmp_priv_passwd_script" : {
+		"getopt" : "R:",
+		"help" : "-R <script>    Script to run to retrieve privacy password",
+		"order" : 1},
+	"udpport" : {
+		"getopt" : "u:",
+		"help" : "-u <port>      UDP/TCP port to use",
+		"order" : 1}
 }
 
 class fspawn(pexpect.spawn):
@@ -336,6 +368,9 @@ def check_input(device_opt, opt):
 	if options.has_key("-v") and options.has_key("debug_fh") == 0:
 		options["debug_fh"] = sys.stderr
 
+	if options.has_key("-R"):
+		options["-P"] = os.popen(options["-R"]).read().rstrip()
+
 	## VMware
 	#######
 	if options.has_key("-B"):
diff --git a/fence/agents/lib/fencing_snmp.py.py b/fence/agents/lib/fencing_snmp.py.py
new file mode 100644
index 0000000..5b0233d
--- /dev/null
+++ b/fence/agents/lib/fencing_snmp.py.py
@@ -0,0 +1,117 @@
+#!/usr/bin/python
+
+# For example of use please see fence_cisco_mds
+
+import re,pexpect
+from fencing import *
+
+## do not add code here.
+#BEGIN_VERSION_GENERATION
+FENCE_RELEASE_NAME=""
+REDHAT_COPYRIGHT=""
+BUILD_DATE=""
+#END_VERSION_GENERATION
+
+class FencingSnmp:
+	def __init__(self,options):
+		self.options=options
+
+	# Log message if user set verbose option
+	def log_command(self, message):
+		if self.options["log"] >= LOG_MODE_VERBOSE:
+			self.options["debug_fh"].write(message+"\n")
+
+	def quote_for_run(self,str):
+		return ''.join(map(lambda x:x==r"'" and "'\\''" or x,str))
+
+	def complete_missed_params(self):
+		mapping=[
+			[['P','p','!E'],'self.options["-E"]="authPriv"'],
+			[['!d','c','!l','!P','!p'],'self.options["-d"]="2c"']
+			]
+
+		for val in mapping:
+			e=val[0]
+
+			res=True
+
+			for item in e:
+				if ((item[0]=='!') and (self.options.has_key("-"+item[1]))):
+					res=False
+					break
+
+				if ((item[0]!='!') and (not self.options.has_key("-"+item[0]))):
+					res=False
+					break
+
+			if res:
+				exec(val[1])
+
+	def prepare_cmd(self,command):
+		cmd="@SNMPBIN@/%s -m '' -Oeqn "%(command)
+
+		self.complete_missed_params()
+
+		#mapping from our option to snmpcmd option
+		mapping=(('d','v'), ('c','c'),('b','a'),('E','l'),('B','x'),('P','X'),('p','A'),('l','u'))
+
+		for item in mapping:
+			if (self.options.has_key("-"+item[0])):
+				cmd+=" -%s '%s'"%(item[1],self.quote_for_run(self.options["-"+item[0]]))
+
+		force_ipvx=""
+
+		if (self.options.has_key("-6")):
+			force_ipvx="udp6:"
+
+		if (self.options.has_key("-4")):
+			force_ipvx="udp:"
+
+		cmd+=" '%s%s%s'"%(force_ipvx, self.quote_for_run(self.options["-a"]),
+				self.options.has_key("-u") and self.quote_for_run(":"+self.options["-u"]) or "")
+		return cmd
+
+	def run_command(self,command,additional_timemout=0):
+		try:
+			self.log_command(command)
+
+			(res_output,res_code)=pexpect.run(command,SHELL_TIMEOUT+LOGIN_TIMEOUT+additional_timemout,True)
+
+			if (res_code==None):
+				fail(EC_TIMED_OUT)
+
+			self.log_command(res_output)
+			if (res_code!=0):
+				fail_usage("Returned %d: %s"%(res_code,res_output))
+		except pexpect.ExceptionPexpect:
+			fail_usage("Cannot run command %s"%(command))
+
+		return res_output
+
+	def get(self,oid,additional_timemout=0):
+		cmd="%s '%s'"%(self.prepare_cmd("snmpget"),self.quote_for_run(oid))
+
+		output=self.run_command(cmd,additional_timemout).splitlines()
+
+		return output[len(output)-1].split(None,1)
+
+	def set(self,oid,value,additional_timemout=0):
+		mapping=((int,'i'),(str,'s'))
+
+		type=''
+
+		for item in mapping:
+			if (isinstance(value,item[0])):
+				type=item[1]
+				break
+
+		cmd="%s '%s' %s '%s'"%(self.prepare_cmd("snmpset"),self.quote_for_run(oid),type,self.quote_for_run(str(value)))
+
+		self.run_command(cmd,additional_timemout)
+
+	def walk(self,oid,additional_timemout=0):
+		cmd="%s '%s'"%(self.prepare_cmd("snmpwalk"),self.quote_for_run(oid))
+
+		output=self.run_command(cmd,additional_timemout).splitlines()
+
+		return map(lambda x:x.split(None,1),filter(lambda y:len(y)>0 and y[0]=='.',output))
diff --git a/fence/man/Makefile b/fence/man/Makefile
index b4c93b2..e51d70c 100644
--- a/fence/man/Makefile
+++ b/fence/man/Makefile
@@ -7,6 +7,7 @@ TARGET= fence.8 \
 	fence_bladecenter.8 \
 	fence_brocade.8 \
 	fence_bullpap.8 \
+	fence_cisco_mds.8 \
 	fence_cpint.8 \
 	fence_drac.8 \
 	fence_egenera.8 \
diff --git a/fence/man/fence_cisco_mds.8 b/fence/man/fence_cisco_mds.8
new file mode 100644
index 0000000..8c58d63
--- /dev/null
+++ b/fence/man/fence_cisco_mds.8
@@ -0,0 +1,132 @@
+.TH fence_cisco_mds 8
+
+.SH NAME
+fence_cisco_mds - I/O Fencing agent for Cisco MDS 9000 series SNMP devices
+
+.SH SYNOPSIS
+.B
+fence_cisco_mds
+[\fIOPTION\fR]...
+
+.SH DESCRIPTION
+fence_cisco_mds is an I/O Fencing agent which can be used with any Cisco MDS
+9000 series with SNMP enabled device. Agent internally uses snmpget, snmpset
+and snmpwalk command.
+
+fence_cisco_mds accepts options on the command line as well as from stdin.
+Fenced sends parameters through stdin when it execs the agent.  fence_cisco_mds
+can be run by itself with command line options.  This is useful for testing.
+
+.SH OPTIONS
+.TP
+\fB-a\fP \fIIPaddress\fR
+IP address or hostname of the Cisco device. Can be used any syntax supported by snmpget.
+.TP
+\fB-h\fP
+Print out a help message describing available options, then exit.
+.TP
+\fB-c\fP \fIcommunity\fR
+The read/write community string to be used in the request.
+.TP
+\fB-n\fP \fIname\fR
+Name of port to fence (fc1/1)
+.TP
+\fB-p\fP \fIpassword\fR
+Password for login for SNMP v3 (authentication protocol pass phrase).
+.TP
+\fB-P\fP \fIpassword\fR
+Password for privacy for SNMP v3 (privacy protocol password).
+.TP
+\fB-S\fP \fIscript\fR
+Script to run to retrieve password for login for SNMP v3 (authentication protocol pass phrase).
+.TP
+\fB-R\fP \fIscript\fR
+Script to run to retrieve privacy for SNMP v3 (privacy protocol password).
+.TP
+\fB-l\fP \fIlogin\fR
+Login name for SNMP v3 (security name).
+.TP
+\fB-d\fP \fIversion\fR
+SNMP version (1,2c,3).
+.TP
+\fB-b\fP \fIauth_protocol\fR
+SNMP authentication protocol (MD5|SHA).
+.TP
+\fB-E\fP \fIsec_level\fR
+SNMP security level (noAuthNoPriv|authNoPriv|authPriv).
+.TP
+\fB-B\fP \fIpriv_protocol\fR
+SNMP privacy protocol (DES|AES).
+.TP
+\fB-u\fP \fIudp_port\fR
+UDP/TCP port to use.
+.TP
+\fB-o\fP \fIaction\fR
+The action required.  off (default), on, status, list or monitor. Deprecated
+options (enable -> on and disable -> off) can be used too.
+.TP
+\fB-v\fP
+Verbose. Record session to stdout, or debug file if specified (see -D).
+.TP
+\fB-D\fP
+Specifies file, where will be written debug messages from session.
+.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_cisco_mds.
+.TP
+\fIipaddr = < param >\fR
+IP address or hostname of the Cisco device. Can be used any syntax supported by snmpget.
+.TP
+\fIcommunity = < param >\fR
+The read/write community string to be used in the request.
+.TP
+\fIport = < param >\fR
+Name of port to fence (fc1/1)
+.TP
+\fIpasswd = < param >\fR
+Password for login for SNMP v3 (authentication protocol pass phrase).
+.TP
+\fIsnmp_priv_passwd\fR
+Password for privacy for SNMP v3 (privacy protocol password).
+.TP
+\fIpasswd_script = < param >\fR
+Script to run to retrieve password for login for SNMP v3 (authentication protocol pass phrase).
+.TP
+\fIsnmp_priv_passwd_script = < param>\fR
+Password for privacy for SNMP v3 (privacy protocol password).
+.TP
+\fIlogin = < param >\fR
+Login name for SNMP v3 (security name).
+.TP
+\fIsnmp_version = < param >\fR
+SNMP version (1,2c,3).
+.TP
+\fIsnmp_auth_prot = < param >\fR
+SNMP authentication protocol (MD5|SHA).
+.TP
+\fIsnmp_sec_level = < param >\fR
+SNMP security level (noAuthNoPriv|authNoPriv|authPriv).
+.TP
+\fIsnmp_priv_prot = < param >\fR
+SNMP privacy protocol (DES|AES).
+.TP
+\fIudpport = < param >\fR
+UDP/TCP port to use.
+.TP
+\fIaction = < param >\fR
+The action required.  off (default), on, status, list or monitor. Deprecated
+options (enable -> on and disable -> off) can be used too.
+.TP
+\fIverbose = < param >\fR
+Verbose.  Record session to stdout, or debug file if specified (see debug).
+.TP
+\fIdebug = < param >\fR
+Specifies file, where will be written debug messages from session.
+
+.SH SEE ALSO
+fence(8), fence_node(8)



More information about the Cluster-cvs mailing list