From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 10707 invoked by alias); 4 Mar 2009 14:54:58 -0000 Received: (qmail 10700 invoked by alias); 4 Mar 2009 14:54:58 -0000 X-SWARE-Spam-Status: No, hits=1.1 required=5.0 tests=AWL,BAYES_50,J_CHICKENPOX_34,J_CHICKENPOX_36,J_CHICKENPOX_47,J_CHICKENPOX_61,J_CHICKENPOX_66,SPF_HELO_PASS X-Spam-Status: No, hits=1.1 required=5.0 tests=AWL,BAYES_50,J_CHICKENPOX_34,J_CHICKENPOX_36,J_CHICKENPOX_47,J_CHICKENPOX_61,J_CHICKENPOX_66,SPF_HELO_PASS X-Spam-Check-By: sourceware.org X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on bastion.fedora.phx.redhat.com Subject: fence-agents: master - fence_ifmib: Brand new implementation of this agent To: cluster-cvs-relay@redhat.com X-Project: Cluster Project X-Git-Module: fence-agents.git X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Oldrev: 01dd364e60046856b3d46d0d6c9df822d3178aff X-Git-Newrev: 21a0c39b079dd529c75267b2cf465305b0471c7b From: Jan Friesse Message-Id: <20090304145431.17E691201FA@lists.fedorahosted.org> Date: Wed, 04 Mar 2009 14:54:00 -0000 X-Scanned-By: MIMEDefang 2.58 on 172.16.52.254 Mailing-List: contact cluster-cvs-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Post: List-Help: , Sender: cluster-cvs-owner@sourceware.org X-SW-Source: 2009-q1/txt/msg00678.txt.bz2 Gitweb: http://git.fedorahosted.org/git/fence-agents.git?p=fence-agents.git;a=commitdiff;h=21a0c39b079dd529c75267b2cf465305b0471c7b Commit: 21a0c39b079dd529c75267b2cf465305b0471c7b Parent: 01dd364e60046856b3d46d0d6c9df822d3178aff Author: Jan Friesse AuthorDate: Wed Mar 4 15:54:09 2009 +0100 Committer: Jan Friesse 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: - + 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 -# 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 " + +# 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)