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

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


Gitweb:        http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=321445dff2be4d808eaf6dbc4c2fef70a6b0f53d
Commit:        321445dff2be4d808eaf6dbc4c2fef70a6b0f53d
Parent:        35f2b0df8c8acc5371a6d45c398bd7cb12937ab5
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:10:40 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/Makefile                     |    3 +
 fence/agents/cisco_mds/Makefile           |   38 ++++++++
 fence/agents/cisco_mds/fence_cisco_mds.py |  113 ++++++++++++++++++++++++
 fence/agents/lib/Makefile                 |   18 +++-
 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 +++++++++++++++++++++++++++++
 8 files changed, 455 insertions(+), 4 deletions(-)

diff --git a/fence/agents/Makefile b/fence/agents/Makefile
index 4ac3039..80655fb 100644
--- a/fence/agents/Makefile
+++ b/fence/agents/Makefile
@@ -21,6 +21,7 @@ all:
 	${MAKE} -C bladecenter all
 	${MAKE} -C brocade all
 	${MAKE} -C bullpap all
+	${MAKE} -C cisco_mds all
 	# ${MAKE} -C cpint all
 	${MAKE} -C drac all
 	${MAKE} -C egenera all
@@ -50,6 +51,7 @@ install: all
 	${MAKE} -C bladecenter install
 	${MAKE} -C brocade install
 	${MAKE} -C bullpap install
+	${MAKE} -C cisco_mds install
 	# ${MAKE} -C cpint install
 	${MAKE} -C drac install
 	${MAKE} -C egenera install
@@ -79,6 +81,7 @@ clean:
 	${MAKE} -C bladecenter clean
 	${MAKE} -C brocade clean
 	${MAKE} -C bullpap clean
+	${MAKE} -C cisco_mds clean
 	# ${MAKE} -C cpint clean
 	${MAKE} -C drac clean
 	${MAKE} -C egenera clean
diff --git a/fence/agents/cisco_mds/Makefile b/fence/agents/cisco_mds/Makefile
new file mode 100644
index 0000000..f61b83b
--- /dev/null
+++ b/fence/agents/cisco_mds/Makefile
@@ -0,0 +1,38 @@
+###############################################################################
+###############################################################################
+##
+##  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
+##  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+##  
+##  This copyrighted material is made available to anyone wishing to use,
+##  modify, copy, or redistribute it subject to the terms and conditions
+##  of the GNU General Public License v.2.
+##
+###############################################################################
+###############################################################################
+
+SOURCE= fence_cisco_mds.py
+TARGET= fence_cisco_mds
+
+top_srcdir=../..
+include ${top_srcdir}/make/defines.mk
+
+all: $(TARGET)
+
+fence_cisco_mds: fence_cisco_mds.py
+	: > $(TARGET)
+	awk "{print}(\$$1 ~ /#BEGIN_VERSION_GENERATION/){exit 0}" $(SOURCE) >> $(TARGET)
+	echo "FENCE_RELEASE_NAME=\"${RELEASE}\";" >> $(TARGET)
+	${top_srcdir}/scripts/define2var ${top_srcdir}/config/copyright.cf sh REDHAT_COPYRIGHT >> $(TARGET)
+	echo "BUILD_DATE=\"(built `date`)\";" >> $(TARGET)
+	awk -v p=0 "(\$$1 ~ /#END_VERSION_GENERATION/){p = 1} {if(p==1)print}" $(SOURCE) >> $(TARGET)
+	chmod +x $(TARGET)
+
+install: all
+	if [ ! -d ${sbindir} ]; then \
+		install -d ${sbindir}; \
+	fi
+	install -m755 ${TARGET} ${sbindir}
+
+clean:
+	rm -f $(TARGET)
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..40faf94
--- /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("/usr/lib/fence")
+from fencing import *
+from fencing_snmp import *
+
+#BEGIN_VERSION_GENERATION
+FENCE_RELEASE_NAME=""
+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 0f6063d..b4cf466 100644
--- a/fence/agents/lib/Makefile
+++ b/fence/agents/lib/Makefile
@@ -17,10 +17,13 @@ TARGET1= fencing.py
 SOURCE2= telnet_ssl.py
 TARGET2= telnet_ssl
 
+SOURCE3= fencing_snmp.py.py
+TARGET3= fencing_snmp.py
+
 top_srcdir=../..
 include ${top_srcdir}/make/defines.mk
 
-all: $(TARGET1) $(TARGET2)
+all: $(TARGET1) $(TARGET2) $(TARGET3)
 
 $(TARGET1): $(SOURCE1)
 	: > $(TARGET1)
@@ -40,11 +43,20 @@ $(TARGET2): $(SOURCE2)
 	awk -v p=0 "(\$$1 ~ /#END_VERSION_GENERATION/){p = 1} {if(p==1)print}" $(SOURCE2) >> $(TARGET2)
 	chmod +x $(TARGET2)
 
+$(TARGET3): $(SOURCE3)
+	: > $(TARGET3)
+	awk "{print}(\$$1 ~ /#BEGIN_VERSION_GENERATION/){exit 0}" $(SOURCE3) >> $(TARGET3)
+	echo "FENCE_RELEASE_NAME=\"${RELEASE}\";" >> $(TARGET3)
+	${top_srcdir}/scripts/define2var ${top_srcdir}/config/copyright.cf sh REDHAT_COPYRIGHT >> $(TARGET3)
+	echo "BUILD_DATE=\"(built `date`)\";" >> $(TARGET3)
+	awk -v p=0 "(\$$1 ~ /#END_VERSION_GENERATION/){p = 1} {if(p==1)print}" $(SOURCE3) >> $(TARGET3)
+	chmod +x $(TARGET3)
+
 install: all
 	if [ ! -d ${DESTDIR}/usr/lib/fence ]; then \
 		install -d ${DESTDIR}/usr/lib/fence; \
 	fi
-	install -m755 ${TARGET1} ${TARGET2} ${DESTDIR}/usr/lib/fence
+	install -m755 ${TARGET1} ${TARGET2} ${TARGET3} ${DESTDIR}/usr/lib/fence
 
 clean:
-	rm -f $(TARGET1) $(TARGET2)
+	rm -f $(TARGET1) $(TARGET2) $(TARGET3)
diff --git a/fence/agents/lib/fencing.py.py b/fence/agents/lib/fencing.py.py
index 6eacf6c..12e162c 100644
--- a/fence/agents/lib/fencing.py.py
+++ b/fence/agents/lib/fencing.py.py
@@ -157,7 +157,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):
@@ -339,6 +371,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..49e3c57
--- /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="/usr/bin/%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 e593980..6eb8cb4 100644
--- a/fence/man/Makefile
+++ b/fence/man/Makefile
@@ -18,6 +18,7 @@ TARGET8= \
 	fence_rsa.8 \
 	fence_bladecenter.8 \
 	fence_brocade.8 \
+	fence_cisco_mds.8 \
 	fence_drac.8 \
 	fence_egenera.8 \
 	fence_eps.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