This is the mail archive of the
cluster-cvs@sourceware.org
mailing list for the cluster.
fence-agents: master - fence_ifmib: Brand new implementation of thisagent
- From: Jan Friesse <honzaf at fedoraproject dot org>
- To: cluster-cvs-relay at redhat dot com
- Date: Wed, 4 Mar 2009 14:54:31 +0000 (UTC)
- Subject: fence-agents: master - fence_ifmib: Brand new implementation of thisagent
Gitweb: http://git.fedorahosted.org/git/fence-agents.git?p=fence-agents.git;a=commitdiff;h=21a0c39b079dd529c75267b2cf465305b0471c7b
Commit: 21a0c39b079dd529c75267b2cf465305b0471c7b
Parent: 01dd364e60046856b3d46d0d6c9df822d3178aff
Author: Jan Friesse <jfriesse@redhat.com>
AuthorDate: Wed Mar 4 15:54:09 2009 +0100
Committer: Jan Friesse <jfriesse@redhat.com>
CommitterDate: Wed Mar 4 15:54:09 2009 +0100
fence_ifmib: Brand new implementation of this agent
Current version of agent is using unified Python library.
Main new features:
- Support for list/monitor action
- Support for metadata
- Support for SNMP v1/2c/3 (old has only 2c)
- Port can be entered not only as ifIndex, but port name
as well (ex. fc1/1).
To update, user needs change ifindex parameter to much
common port parameter.
---
fence/agents/ifmib/README | 10 +-
fence/agents/ifmib/fence_ifmib.py | 350 +++++++++++++------------------------
fence/man/fence_ifmib.8 | 123 ++++++++++---
3 files changed, 224 insertions(+), 259 deletions(-)
diff --git a/fence/agents/ifmib/README b/fence/agents/ifmib/README
index b6e7123..cd2a3c1 100644
--- a/fence/agents/ifmib/README
+++ b/fence/agents/ifmib/README
@@ -2,9 +2,9 @@ Intro:
------
This is an SNMP-based fencing agent for RHCS. It was designed with the use-case
of disabling ethernet ports on an iSCSI SAN, but could be used to disable any
-port on any SNMP v2c device that implementes the IF-MIB.
+port on any SNMP v1/2c/3 device that implementes the IF-MIB.
-The script requires PySNMP version 2 to be installed and working on all nodes
+The script requires NetSNMP to be installed and working on all nodes
in the cluster. There are no requirements for any MIBs to be setup --- all of
the required OIDs are hard-coded into the script. Since the IF-MIB is an IETF
standard, these identifiers are very widely supported and will not change.
@@ -16,7 +16,7 @@ To use this agen with the switch used on the iSCSI network, you'll require:
1) A managed switch running SNMP.
2) An SNMP community with write privileges.
3) Permission to send SNMP through any ACLs or firewalls from the nodes.
- 4) The ifIndex associated with the ports being used by the cluster nodes.
+ 4) The ifIndex or ifPort associated with the ports being used by the cluster nodes.
Consider a three-node cluster composed of A, B, and C. Each node has two
network interfaces - one used for network and cluster communication, the second
@@ -37,9 +37,9 @@ need something like this cluster.conf
In a node's fencing methods, you'll include a line like this:
-<device name="myswitch" ifindex="43" option="off"/>
+<device name="myswitch" port="43" option="off"/>
This node will be fenced by disabling the port with ifIndex 43 on the host sw1.
In SNMP speak, we set IF-MIB::ifAdminStatus.43 = down(2).
-
+If you will use port name (like fc1/1), script will try to find ifIndex.
diff --git a/fence/agents/ifmib/fence_ifmib.py b/fence/agents/ifmib/fence_ifmib.py
index 42345ff..766a96d 100644
--- a/fence/agents/ifmib/fence_ifmib.py
+++ b/fence/agents/ifmib/fence_ifmib.py
@@ -1,233 +1,131 @@
#!/usr/bin/python
-# fence_ifmib.py: fabric fencing for RHCS based on setting a network interface
-# to admin down. Intended to be used for iSCSI connections, can be used with
-# anything that supports the IF-MIB and SNMP v2c.
-#
-# Written by Ross Vandegrift <ross@kallisti.us>
-# Copyright (C) 2008-2009 Ross Vandegrift
-# 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.
-
-COPYRIGHT="Copyright (C) 2008-2009 Ross Vandegrift <ross@kallisti.us>"
+
+# 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)
+# - Partially with APC PDU (Network Management Card AOS v2.7.0, Rack PDU APP v2.7.3)
+# Only lance if is visible
+
+import sys, re, pexpect
+from fencing import *
+from fencing_snmp import *
#BEGIN_VERSION_GENERATION
-RELEASE_VERSION="New fence_ifmib"
-BUILD_DATE="March, 2008"
+RELEASE_VERSION="IF:MIB SNMP fence agent"
+REDHAT_COPYRIGHT=""
+BUILD_DATE=""
#END_VERSION_GENERATION
-import os
-os.environ['PYSNMP_API_VERSION'] = 'v2'
-import sys, getopt, random, socket
-import atexit
-from pysnmp import role, v2c, asn1
-
-ifAdminStatus = '.1.3.6.1.2.1.2.2.1.7.'
-up = 1
-down = 2
-testing = 3
-
-def usage():
- line = '\t%s\t%s'
- print ''
- print 'This script fences a node by sending a command via SNMP to set'
- print 'ifAdminStatus to down. It is designed to kill node access'
- print 'to the shared storage. It only supports SNMP v2c.'
- print ''
- print RELEASE_VERSION, BUILD_DATE
- print COPYRIGHT
- print ''
- print 'Usage: fence_ifmib [options]'
- print line % ('-h', '\tPrint usage')
- print line % ('-V', '\tRun verbosely')
- print line % ('-c [private]', 'Write community string to use')
- print line % ('-a [hostname]', 'IP/hostname of SNMP agent')
- print line % ('-i [index]', 'ifIndex entry of the port ')
- print line % ('-o [action]', 'One of down, up, or status')
-
-
-def vprint(v, s):
- if v:
- print s
-
-
-def parseargs():
- try:
- opt, arg = getopt.getopt (sys.argv[1:], 'hVc:v:a:i:o:')
- except getopt.GetoptError, e:
- print str (e)
- usage ()
- sys.exit (-1)
-
- comm = ipaddr = ifindex = option = verbose = None
-
- for o, a in opt:
- if o == '-h':
- usage ()
- sys.exit (-1)
- if o == '-V':
- verbose = True
- if o == '-c':
- comm = a
- if o == '-a':
- ipaddr = a
- if o == '-i':
- try:
- ifindex = int(a)
- except:
- sys.stderr.write ('fence_ifmib: ifIndex must be an integer\n')
- usage ()
- sys.exit (-1)
- if o == '-o':
- option = a
- if option not in ('on', 'off', 'status'):
- sys.stderr.write ('fence_ifmib: option must be one of on, off, or status\n')
- usage ()
- sys.exit (-1)
-
- if comm == None or ipaddr == None or ifindex == None \
- or option == None:
- sts.stderr.write ('All args are madatory!\n')
- usage ()
- sys.exit (-1)
-
- return (comm, ipaddr, ifindex, option, verbose)
-
-
-def parsestdin():
- params = {}
- for line in sys.stdin:
- val = line.split('=')
- if len (val) == 2:
- params[val[0].strip ()] = val[1].strip ()
-
- try:
- comm = params['comm']
- except:
- sys.stdout.write ('fence_ifmib: Error reading community string\n')
- sys.exit (1)
-
- try:
- ipaddr = params['ipaddr']
- except:
- sys.stdout.write ('fence_ifmib: Error reading destination IP/host\n')
- sys.exit (1)
-
- try:
- ifindex = params['ifindex']
- except:
- sys.stdout.write ('fence_ifmib: Error reading ifindex\n')
- sys.exit (1)
-
- try:
- option = params['option']
- except:
- option = 'off'
-
- return (comm, ipaddr, ifindex, option)
-
-
-def snmpget (host, comm, oid):
- req = v2c.GETREQUEST ()
- encoded_oids = map (asn1.OBJECTID().encode, [oid,])
- req['community'] = comm
- tr = role.manager ((host, 161))
- rsp = v2c.RESPONSE ()
- (rawrsp, src) = tr.send_and_receive (req.encode (encoded_oids=encoded_oids))
- rsp.decode (rawrsp)
- if rsp['error_status']:
- raise IOError('SNMP error while reading')
- oids = map (lambda x: x[0], map(asn1.OBJECTID ().decode, rsp['encoded_oids']))
- vals = map (lambda x: x[0] (), map(asn1.decode, rsp['encoded_vals']))
- return vals[0]
-
-
-def snmpset (host, comm, oid, type, value):
- req = v2c.SETREQUEST (request_id=random.randint (1,2**16-1))
- req['community'] = comm
- tr = role.manager ((host, 161))
- rsp = v2c.RESPONSE ()
- encoded_oids = map (asn1.OBJECTID ().encode, [oid,])
- encoded_vals = []
- encoded_vals.append (eval ('asn1.' + type + '()').encode (value))
- (rawrsp, src) = tr.send_and_receive (req.encode (encoded_oids=encoded_oids, encoded_vals=encoded_vals))
- rsp.decode(rawrsp)
- if rsp['error_status']:
- raise IOError('SNMP error while setting')
- oids = map (lambda x: x[0], map (asn1.OBJECTID().decode, rsp['encoded_oids']))
- vals = map (lambda x: x[0] (), map (asn1.decode, rsp['encoded_vals']))
- if vals[0] == value:
- return vals[0]
- else:
- raise IOError('SNMP error while setting')
-
-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)
+### CONSTANTS ###
+# 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"
+STATUSES_OID=".1.3.6.1.2.1.2.2.1.7"
+
+# Status constants returned as value from SNMP
+STATUS_UP=1
+STATUS_DOWN=2
+STATUS_TESTING=3
+
+### GLOBAL VARIABLES ###
+# Port number converted from port name or index
+port_num=None
+
+### FUNCTIONS ###
+
+# Convert port index or name to port index
+def port2index(conn,port):
+ res=None
+
+ if (port.isdigit()):
+ res=int(port)
+ else:
+ ports=conn.walk(PORTS_OID,30)
+
+ for x in ports:
+ if (x[1].strip('"')==port):
+ res=int(x[0].split('.')[-1])
+ break
+
+ if (res==None):
+ fail_usage("Can't find port with name %s!"%(port))
+
+ return res
+
+def get_power_status(conn,options):
+ global port_num
+
+ if (port_num==None):
+ port_num=port2index(conn,options["-n"])
+
+ (oid,status)=conn.get("%s.%d"%(STATUSES_OID,port_num))
+ return (status==str(STATUS_UP) and "on" or "off")
+
+def set_power_status(conn, options):
+ global port_num
+
+ if (port_num==None):
+ port_num=port2index(conn,options["-n"])
+
+ conn.set("%s.%d"%(STATUSES_OID,port_num),(options["-o"]=="on" and STATUS_UP or STATUS_DOWN))
+# 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))
+
+ for x in res_fc:
+ 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
+
+# Define new options
+def ifmib_define_defaults():
+ all_opt["snmp_version"]["default"]="2c"
+
+# Main agent method
def main():
- atexit.register(atexit_handler)
-
- if len (sys.argv) > 1:
- (comm, host, index, option, verbose) = parseargs ()
- else:
- verbose = False
- (comm, host, index, option) = parsestdin ()
-
- try:
- switch = socket.gethostbyname (host)
- except socket.gaierror, err:
- vprint (verbose, 'fence_ifmib: %s' % str (err[1]))
- sys.exit(1)
-
- if option == 'on':
- value = up
- elif option == 'off':
- value = down
- elif option == 'status':
- value = None
-
- if value:
- # For option in (on, off) - write and verify
- try:
- r = snmpset (switch, comm, ifAdminStatus + str (index), 'INTEGER', value)
- except:
- sys.stderr.write ('fence_ifmib: Error during snmp write\n')
- sys.exit (1)
-
- try:
- s = int (snmpget (switch, comm, ifAdminStatus + str (index)))
- except:
- sys.stderr.write ('fence_ifmib: Error during fence verification\n')
- sys.exit (1)
-
- if s == value:
- vprint (verbose, 'fence_ifmib: action %s sucessful' % option)
- sys.exit (0)
- else:
- vprint (verbose, 'fence_ifmib: action %s failed' % option)
- sys.exit (1)
- else: # status
- try:
- r = int (snmpget (switch, comm, ifAdminStatus + str (index)))
- except:
- sys.stderr.write ('fence_ifmib: Error during snmp read\n')
- sys.exit (1)
-
- if r == up:
- vprint (verbose, 'fence_ifmib: Port is admin up')
- sys.exit (0)
- elif r == down:
- vprint (verbose, 'fence_ifmib: Port is admin down')
- sys.exit (2)
- elif r == testing:
- vprint (verbose, 'fence_ifmib: Port is admin testing')
- sys.exit (2)
-
-
-if __name__ == '__main__':
- 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"]
+
+ atexit.register(atexit_handler)
+
+ 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)
+
+ # 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/man/fence_ifmib.8 b/fence/man/fence_ifmib.8
index 93e8b4b..38cd223 100644
--- a/fence/man/fence_ifmib.8
+++ b/fence/man/fence_ifmib.8
@@ -1,69 +1,136 @@
-.\" Copyright (C) 2008-2009 Ross Vandegrift. 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.
-
.TH fence_ifmib 8
.SH NAME
fence_ifmib - I/O Fencing agent for IF-MIB capable SNMP devices
.SH SYNOPSIS
-.B
+.B
fence_ifmib
[\fIOPTION\fR]...
.SH DESCRIPTION
-fence_ifmib is an I/O Fencing agent which can be used with any IF-MIB capable
-SNMP device. It was written with managed ethernet switches in mind, in order
+fence_ifmib is an I/O Fencing agent which can be used with any SNMP IF-MIB capable
+device. Agent internally uses snmpget, snmpset and snmpwalk command.
+
+It was written with managed ethernet switches in mind, in order
to fence iSCSI SAN connections. However, there are many devices that support
the IF-MIB interface. The agent uses IF-MIB::ifAdminStatus to control the
state of an interface.
-fence_ifmib accepts options on the command line as well as from stdin.
-Fenced sends parameters through stdin when it execs the agent. fence_ifmib
+fence_ifmib accepts options on the command line as well as from stdin.
+Fenced sends parameters through stdin when it execs the agent. fence_ifmib
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 SNMP agent to be written.
+IP address or hostname of the SNMP device. Can be used any syntax supported by snmpget.
.TP
-\fB-h\fP
+\fB-h\fP
Print out a help message describing available options, then exit.
.TP
\fB-c\fP \fIcommunity\fR
-The write community string to be used in the request.
+The read/write community string to be used in the request.
+.TP
+\fB-n\fP \fIname\fR
+Name of port to fence (fc1/1) or ifIndex (43).
+.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-i\fP \fIiIindex\fR
-The ifIndex of the interface to be acted upon. This will need to be determined
-manually prior to configuration.
+\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, or status. off sets ifAdminStatus
-down, on sets ifAdminStatus up, and status returns the current state.
+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
-Verbose. Print informational messages to standard out.
+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_ifmib.
.TP
-\fIipaddr = < hostname | ip >\fR
-IP address or hostname of the device.
+\fIipaddr = < param >\fR
+IP address or hostname of the SNMP 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) or ifIndex (43).
+.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
-\fIcomm = < param >\fR
-Write community string to be used in the request.
+\fIudpport = < param >\fR
+UDP/TCP port to use.
.TP
-\fIifindex = < param >\fR
-The ifIndex of the interface to be acted upon.
+\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
-\fIoption = < param >\fR
-The action required. off (default), on, or status.
+\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)