[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: [Linux-cluster] New fencing method



On Tue, May 20, 2008 at 03:35:18PM -0400, Lon Hohberger wrote:
> 
> On Tue, 2008-05-20 at 08:41 +0200, Darek Skorupa wrote:
> > > However, I'm having trouble finding how to integrate my script into
> > > the fence_node system.  Is there a config file somewhere, or will I
> > > need to build a custom version of fence_node?
> > >
> > >   
> > I think, you should copy fence_snmp script to /sbin folder and if script 
> > will exit with '0' status fencing is successful in otherwise is 
> > unsuccessful.
> > 
> 
> That's step one.
> 
> The agent as noted doesn't appear to take arguments from STDIN.  Try
> looking here for more information:
> 
>    http://sources.redhat.com/cluster/wiki/FenceAgentAPI

Awesome - thanks for the pointer.  That makes so much more sense and
explains how fence_tool can introspect the options passed to the
particular fencer.

I've attached an updated version that follows the specifications at
the above link.  I've got a two node cluster running configured with
it, though I haven't done any substantial testing yet.

Thanks for the help - feel free to use/distribute/forget about it :)
-- 
Ross Vandegrift
ross kallisti us

"The good Christian should beware of mathematicians, and all those who
make empty prophecies. The danger already exists that the mathematicians
have made a covenant with the devil to darken the spirit and to confine
man in the bonds of Hell."
	--St. Augustine, De Genesi ad Litteram, Book II, xviii, 37
#!/usr/bin/python
# fence_snmp.py: fabric fencing for RHCS based on setting a network interface
# to admin down.  Will be used for iSCSI connections.
#
# Written by Ross Vandegrift <ross kallisti us>
# Released into the public domain

import os
os.environ['PYSNMP_API_VERSION'] = 'v2'
import sys, getopt, random, socket
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 iss designed to kill node access'
    print 'to the shared storage.  It only supports SNMP v2c.'
    print ''
    print 'Usage: fence_snmp [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_snmp: 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_snmp: 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_snmp: Error reading community string\n')
        sys.exit (1)

    try:
        ipaddr = params['ipaddr']
    except:
        sys.stdout.write ('fence_snmp: Error reading destination IP/host\n')
        sys.exit (1)

    try:
        ifindex = params['ifindex']
    except:
        sys.stdout.write ('fence_snmp: 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 main():
    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_snmp: %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_snmp: Error during snmp write\n')
            sys.exit (1)
        
        try:
            s = int (snmpget (switch, comm, ifAdminStatus + str (index)))
        except:
            sys.stderr.write ('fence_snmp: Error during fence verification\n')
            sys.exit (1)

        if s == value:
            vprint (verbose, 'fence_snmp: action %s sucessful' % option)
            sys.exit (0)
        else:
            vprint (verbose, 'fence_snmp: action %s failed' % option)
            sys.exit (1)
    else: # status
        try: 
            r = int (snmpget (switch, comm, ifAdminStatus + str (index)))
        except:
            sys.stderr.write ('fence_snmp: Error during snmp read\n')
            sys.exit (1)

        if r == up:
            vprint (verbose, 'fence_snmp: Port is admin up')
            sys.exit (0)
        elif r == down:
            vprint (verbose, 'fence_snmp: Port is admin down')
            sys.exit (2)
        elif r == testing:
            vprint (verbose, 'fence_snmp: Port is admin testing')
            sys.exit (2)


if __name__ == '__main__':
    main()

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]