This is the mail archive of the cluster-cvs@sourceware.org mailing list for the cluster.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

fence-agents: master - fence_cisco: Added fence agent for Cisco MDS9124/9134


Gitweb:        http://git.fedorahosted.org/git/fence-agents.git?p=fence-agents.git;a=commitdiff;h=a6d00b47d451a71e5d9efcd173829fdaf503d14d
Commit:        a6d00b47d451a71e5d9efcd173829fdaf503d14d
Parent:        99ea762d501929b9157a6196c0cff3760c48ba8f
Author:        Jan Friesse <jfriesse@redhat.com>
AuthorDate:    Mon Mar 2 15:18:48 2009 +0100
Committer:     Jan Friesse <jfriesse@redhat.com>
CommitterDate: Mon Mar 2 15:20:00 2009 +0100

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.
---
 fence/agents/cisco_mds/Makefile           |    5 +
 fence/agents/cisco_mds/fence_cisco_mds.py |  111 ++++++++++++++++++++++++
 fence/agents/lib/Makefile                 |    2 +-
 fence/agents/lib/fencing.py.py            |   60 +++++++++++++
 fence/agents/lib/fencing_snmp.py.py       |  109 ++++++++++++++++++++++++
 fence/man/Makefile                        |    1 +
 fence/man/fence_cisco_mds.8               |  132 +++++++++++++++++++++++++++++
 7 files changed, 419 insertions(+), 1 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..d5de816
--- /dev/null
+++ b/fence/agents/cisco_mds/fence_cisco_mds.py
@@ -0,0 +1,111 @@
+#!/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
+from fencing import *
+from fencing_snmp import *
+
+#BEGIN_VERSION_GENERATION
+RELEASE_VERSION="Cisco MDS 9xxx SNMP fence agent"
+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", "separator", "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)
+
+	port_oid=cisco_port2oid(options["-n"])
+
+	# Operate the fencing device
+	fence_action(FencingSnmp(options), options, set_power_status, get_power_status, get_outlets_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 a4548fd..bdf7da2 100644
--- a/fence/agents/lib/fencing.py.py
+++ b/fence/agents/lib/fencing.py.py
@@ -206,6 +206,63 @@ all_opt = {
 		"required" : "0",
 		"shortdesc" : "Show only machines in specified datacenter",
 		"order" : 2 },
+	"snmp_version" : {
+		"getopt" : "d:",
+		"longopt" : "snmp-version",
+		"help" : "-d, --snmp-version=<ver>       Specifies SNMP version to use",
+		"required" : "0",
+		"shortdesc" : "Specifies SNMP version to use (1,2c,3)",
+		"order" : 1 },
+	"community" : {
+		"getopt" : "c:",
+		"longopt" : "community",
+		"help" : "-c, --community=<community>    Set the community string",
+		"required" : "0",
+		"shortdesc" : "Set the community string",
+		"order" : 1},
+	"snmp_auth_prot" : {
+		"getopt" : "b:",
+		"longopt" : "snmp-auth-prot",
+		"help" : "-b, --snmp-auth-prot=<prot>    Set authentication protocol (MD5|SHA)",
+		"required" : "0",
+		"shortdesc" : "Set authentication protocol (MD5|SHA)",
+		"order" : 1},
+	"snmp_sec_level" : {
+		"getopt" : "E:",
+		"longopt" : "snmp-sec-level",
+		"help" : "-E, --snmp-sec-level=<level>   Set security level\n"+
+		"                                  (noAuthNoPriv|authNoPriv|authPriv)",
+		"required" : "0",
+		"shortdesc" : "Set security level (noAuthNoPriv|authNoPriv|authPriv)",
+		"order" : 1},
+	"snmp_priv_prot" : {
+		"getopt" : "B:",
+		"longopt" : "snmp-priv-prot",
+		"help" : "-B, --snmp-priv-prot=<prot>    Set privacy protocol (DES|AES)",
+		"required" : "0",
+		"shortdesc" : "Set privacy protocol (DES|AES)",
+		"order" : 1},
+	"snmp_priv_passwd" : {
+		"getopt" : "P:",
+		"longopt" : "snmp-priv-passwd",
+		"help" : "-P, --snmp-priv-passwd=<pass>  Set privacy protocol password",
+		"required" : "0",
+		"shortdesc" : "Set privacy protocol password",
+		"order" : 1},
+	"snmp_priv_passwd_script" : {
+		"getopt" : "R:",
+		"longopt" : "snmp-priv-passwd-script",
+		"help" : "-R, --snmp-priv-passwd-script  Script to run to retrieve privacy password",
+		"required" : "0",
+		"shortdesc" : "Script to run to retrieve privacy password",
+		"order" : 1},
+	"udpport" : {
+		"getopt" : "u:",
+		"longopt" : "udpport",
+		"help" : "-u, --udpport                  UDP/TCP port to use",
+		"required" : "0",
+		"shortdesc" : "UDP/TCP port to use for connection with device",
+		"order" : 1},
 	"separator" : {
 		"getopt" : "C:",
 		"longopt" : "separator",
@@ -465,6 +522,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()
+
 	return options
 	
 def wait_power_status(tn, options, get_power_fn):
diff --git a/fence/agents/lib/fencing_snmp.py.py b/fence/agents/lib/fencing_snmp.py.py
new file mode 100644
index 0000000..e2a1760
--- /dev/null
+++ b/fence/agents/lib/fencing_snmp.py.py
@@ -0,0 +1,109 @@
+#!/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
+RELEASE_VERSION = ""
+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]]))
+
+		cmd+=" '%s%s'"%(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 ceca579..b35745f 100644
--- a/fence/man/Makefile
+++ b/fence/man/Makefile
@@ -5,6 +5,7 @@ TARGET=	fence_ack_manual.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)


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]