[SOLVED] Fencing using SNMP - eMAA12

hotwired007

Member
Sep 19, 2011
533
7
16
UK
i have recently bought an eaton eMAA12 ePDU to use as a fencing device - it was advertised as supporting IPMI but after speaking directly to EATON IPMI is only supported on bespoke versions (only available in orders of over 1000 units) :S

It will how ever accept SNMP commands - i have no experience in dealing with SNMP could anyone offer me any advice on how to get this working?

I have the MIB and i assume the commands needed for proxmox are:

outletControlOffCmd,
outletControlOnCmd,
outletControlPowerOnState,
outletControlRebootCmd,
outletControlRebootOffTime,
outletControlSequenceDelay,
outletControlStatus,
 
Last edited:
Re: Fencing using SNMP

thanks e100, i've tried to get this working but i think im missing something:

Code:
root@node1:/usr/sbin# fence_eaton_snmp -a 192.168.15.120 -l admin -p admin -n A2 status -v
/usr/bin/snmpwalk -m '' -Oeqn  -v '1' -c 'private' '192.168.15.120:161' '.1.3.6.1.2.1.1.2.0'
.1.3.6.1.2.1.1.2.0 .1.3.6.1.4.1.534.6.6.7

Traceback (most recent call last):
  File "/usr/sbin/fence_eaton_snmp", line 250, in <module>
    main()
  File "/usr/sbin/fence_eaton_snmp", line 246, in main
    result = fence_action(FencingSnmp(options), options, set_power_status, get_power_status, get_outlets_status)
  File "/usr/share/fence/fencing.py", line 793, in fence_action
    status = get_power_fn(tn, options)
  File "/usr/sbin/fence_eaton_snmp", line 123, in get_power_status
    eaton_resolv_port_id(conn,options)
  File "/usr/sbin/fence_eaton_snmp", line 86, in eaton_resolv_port_id
    eaton_set_device(conn,options)
  File "/usr/sbin/fence_eaton_snmp", line 78, in eaton_set_device
    device=agents_dir[eaton_type[0][1]]
KeyError: None

any suggestions what im doing wrong?
 
Re: Fencing using SNMP

Pointing you in the direction of fence_eaton_snmp is about all the knowledge I have when it comes to Eaton PDUs
So not sure if I can be of much more help.....

I did use fence_apc_snmp with a very old APC Masterswitch I have, documented that here:
http://pve.proxmox.com/wiki/Fencing#APC_Master_Switch

snmp does not use username and password, it uses a community string.
Try setting the community string and see if that works.
You might also need to enable snmp in the Eaton and maybe set the version in the fence_eaton_snmp

No idea if Eaton is like this, but my Masterswitch will only let one person manage it at a time.
If someone is logged into it using telnet or the web gui, the fence agent fails when trying to control it using snmp.
Had me scratching my head trying to figure out why the fence agent was not working, after I logged out from the web GUI it magically started working
 
Re: Fencing using SNMP

i've figured out the CLI commands for powering on and off and checking the staus of the outlets - how can i get this to work with proxmox:

power on outlet 1
Code:
snmpset -v 1 -c private 192.168.15.120 .1.3.6.1.4.1.534.6.6.7.6.6.1.4.0.1 i 1

power off outlet 1
Code:
snmpset -v 1 -c private 192.168.15.120 .1.3.6.1.4.1.534.6.6.7.6.6.1.3.0.1 i 1

check status outlet 1
Code:
snmpwalk -v 1 -c private 192.168.15.120 .1.3.6.1.4.1.534.6.6.7.6.6.1.2.0.1

reboot outlet 1
Code:
snmpset -v 1 -c private 192.168.15.120 .1.3.6.1.4.1.534.6.6.7.6.6.1.5.0.1 i 0

to change outlet you just change the last digit - ie outlet 13 is .1.3.6.1.4.1.534.6.6.7.6.6.1.2.0.13
 
Last edited:
Re: Fencing using SNMP

it results in this:

Code:
root@node1:/usr/bin# fence_eaton_snmp -a 192.168.15.120 -n 1 -o status -v -c 'private'
/usr/bin/snmpwalk -m '' -Oeqn  -v '1' -c 'private' '192.168.15.120:161' '.1.3.6.1.2.1.1.2.0'
.1.3.6.1.2.1.1.2.0 .1.3.6.1.4.1.534.6.6.7

Traceback (most recent call last):
  File "/usr/sbin/fence_eaton_snmp", line 250, in <module>
    main()
  File "/usr/sbin/fence_eaton_snmp", line 246, in main
    result = fence_action(FencingSnmp(options), options, set_power_status, get_power_status, get_outlets_status)
  File "/usr/share/fence/fencing.py", line 793, in fence_action
    status = get_power_fn(tn, options)
  File "/usr/sbin/fence_eaton_snmp", line 123, in get_power_status
    eaton_resolv_port_id(conn,options)
  File "/usr/sbin/fence_eaton_snmp", line 86, in eaton_resolv_port_id
    eaton_set_device(conn,options)
  File "/usr/sbin/fence_eaton_snmp", line 78, in eaton_set_device
    device=agents_dir[eaton_type[0][1]]
KeyError: None
 
Re: Fencing using SNMP

Take a look at the code, it is python so just open up a copy of /usr/sbin/fence_eaton_snmp in your favorite editor.

Take a look at the function eaton_set_device
That detects what device you have.
This is identified by the snmpwalk command that is output when running "fence_eaton_snmp -a 192.168.15.120 -n 1 -o status -v -c 'private'"

Note that the snmpwalk command returns ".1.3.6.1.4.1.534.6.6.7"
That that value does not match any known eaton devices in the source.

You can likely patch this yourself.
Seems like copying the class named "EatonManagedePDU" into some other name and editing the oid's to match your PDU is step #1.
Then in the function eaton_set_device edit agents_dir to include the proper oid:newclassname
 
Re: Fencing using SNMP

i've managed to get it to check the status ok and i cna get it to either power on OR power off the socket.

The code seems to accept that the power on and off command is the same oid with a 0 or 1 to indicate on and off, whereas on the eMAA12 there is a command for on and a command for off.
 
Re: Fencing using SNMP

i have managed to cobble together a fix for the snmp bit - i've added to the code a bit so it will probably not work with the original PDUs, so i built a new copy of the fence_eaton_snmp called fence_emaa12_snmp, just going to strip the rest of details for the other ePDUs and then i'll load up the file on here...

i've tested it with SNMP v1 and it works.
 
Re: Fencing using SNMP

here is a copy/paste of the contents of the fence_emaa12_snmp file, when you upload it, it needs chmoding to 755

Code:
#!/usr/bin/python

# The Following agent has been tested on:
# - Eaton eMAA12 ePDU - SNMP v1

# - Modified by Hotwired007

import sys, re, pexpect
sys.path.append("/usr/share/fence")
from fencing import *
from fencing_snmp import *

#BEGIN_VERSION_GENERATION
RELEASE_VERSION="3.1.8.6-723b"
BUILD_DATE="(built Mon Jul 9 07:55:22 CEST 2012)"
REDHAT_COPYRIGHT="Copyright (C) Red Hat, Inc. 2004-2010 All rights reserved."
#END_VERSION_GENERATION

### CONSTANTS ###
# oid defining fence device
OID_SYS_OBJECT_ID='.1.3.6.1.2.1.1.2.0'

### GLOBAL VARIABLES ###
# Device - see EatonManagedePDU, EatonSwitchedePDU
device=None

# Port ID
port_id=None
# Switch ID
switch_id=None

# Did we issue a set before get (to adjust OID with Switched ePDU)
after_set=False

# Classes describing Device params
    
# eMAA12 ePDU
class EatoneMAA12ePDU:
    status_oid=    '.1.3.6.1.4.1.534.6.6.7.6.6.1.2.0.%d'
    control_oid=None
    control_oid_on='.1.3.6.1.4.1.534.6.6.7.6.6.1.3.0.%d'
    control_oid_off='.1.3.6.1.4.1.534.6.6.7.6.6.1.4.0.%d'
    outlet_table_oid='.1.3.6.1.4.1.534.6.6.7.6.6'
    ident_str="Eaton eMAA12 ePDU"
    state_off=0
    state_on=1
    state_cycling=2    # FIXME: not usable with fence-agents
    turn_off=1 
    turn_on=1
    turn_cycle=2    # FIXME: not usable with fence-agents
    has_switches=False

### FUNCTIONS ###
def eaton_set_device(conn,options):
    global device

    agents_dir={'.1.3.6.1.4.1.534.6.6.7':EatoneMAA12ePDU,}

    # First resolve type of Eaton
    eaton_type=conn.walk(OID_SYS_OBJECT_ID)

    if (not ((len(eaton_type)==1) and (agents_dir.has_key(eaton_type[0][1])))):
        eaton_type=[[None,None]]

    device=agents_dir[eaton_type[0][1]]

    conn.log_command("Trying %s"%(device.ident_str))

def eaton_resolv_port_id(conn,options):
    global port_id,switch_id,device

    if (device==None):
        eaton_set_device(conn,options)

    # Restore the increment, that was removed in main for ePDU Managed
    if (device.ident_str != "Eaton Managed ePDU"):
        options["-n"] = str(int(options["-n"]) + 1)
        
    # Now we resolv port_id/switch_id
    if ((options["-n"].isdigit()) and ((not device.has_switches) or (options["-s"].isdigit()))):
        port_id=int(options["-n"])

        if (device.has_switches):
            switch_id=int(options["-s"])
    else:
        table=conn.walk(device.outlet_table_oid,30)

        for x in table:
            if (x[1].strip('"')==options["-n"]):
                t=x[0].split('.')
                if (device.has_switches):
                    port_id=int(t[len(t)-1])
                    switch_id=int(t[len(t)-3])
                else:
                    if (device.ident_str == "Eaton Switched ePDU"):
                        port_id=int(t[len(t)-3])
                    else:
                        port_id=int(t[len(t)-1])

    if (port_id==None):
        # Restore index offset, to provide a valid error output on Managed ePDU
        if (device.ident_str != "Eaton Switched ePDU"):
            options["-n"] = str(int(options["-n"]) + 1)
        fail_usage("Can't find port with name %s!"%(options["-n"]))

def get_power_status(conn,options):
    global port_id,switch_id,device,after_set

    if (port_id==None):
        eaton_resolv_port_id(conn,options)

    # Ajust OID for Switched ePDU when the get is after a set
    if ((after_set == True) and (device.ident_str == "Eaton Switched ePDU")):
        port_id-=1
        after_set=False

    oid=((device.has_switches) and device.status_oid%(switch_id,port_id) or device.status_oid%(port_id))

    try:
        (oid,status)=conn.get(oid)
        if (status==str(device.state_on)):
            return "on"
        elif (status==str(device.state_off)):
            return "off"
        else:
            return None
    except:
        return None

def set_power_status(conn, options):
    global port_id,switch_id,device,after_set

    after_set = True

    if (port_id==None):
        eaton_resolv_port_id(conn,options)

    #eMAA12 has seperarte off and on oid, so we have to define the control_oid using an if statement
    if (options["-o"]=="on"):
        device.control_oid = '.1.3.6.1.4.1.534.6.6.7.6.6.1.4.0.%d'
    else:    
        device.control_oid = '.1.3.6.1.4.1.534.6.6.7.6.6.1.3.0.%d'
        
    oid=((device.has_switches) and device.control_oid%(switch_id,port_id) or device.control_oid%(port_id))

    conn.set(oid,(options["-o"]=="on" and device.turn_on or device.turn_off))


def get_outlets_status(conn, options):
    global device

    outletCount = 0
    result={}

    if (device==None):
        eaton_set_device(conn,options)

    res_ports=conn.walk(device.outlet_table_oid,30)

    for x in res_ports:
        outletCount+=1
        status=x[1]
        t=x[0].split('.')

        # Plug indexing start from zero, so we substract '1' from the
        # user's given plug number
        if (device.ident_str == "Eaton Managed ePDU"):
            port_num=str(int(((device.has_switches) and "%s:%s"%(t[len(t)-3],t[len(t)-1]) or "%s"%(t[len(t)-1]))) + 1)

            # Plug indexing start from zero, so we add '1'
            # for the user's exposed plug number
            port_name=str(int(x[1].strip('"')) + 1)
            port_status=""
            result[port_num]=(port_name,port_status)
        else:
            # Switched ePDU do not propose an outletCount OID!
            # Invalid status (ie value == '0'), retrieved via the walk,
            # means the outlet is absent
            port_num=str(outletCount)
            port_name=str(outletCount)
            port_status=""
            if (status != '0'):
                result[port_num]=(port_name,port_status)

    return result

# Define new options
def eaton_snmp_define_defaults():
    all_opt["snmp_version"]["default"]="1"
    all_opt["community"]["default"]="private"

# Main agent method
def main():
    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","inet4_only","inet6_only",
               "power_timeout", "shell_timeout", "login_timeout", "power_wait" ]

    atexit.register(atexit_handler)

    snmp_define_defaults ()
    eaton_snmp_define_defaults()

    options=check_input(device_opt,process_input(device_opt))

    ## Support for -n [switch]:[plug] notation that was used before
    if ((options.has_key("-n")) and (-1 != options["-n"].find(":"))):
        (switch, plug) = options["-n"].split(":", 1)
        if ((switch.isdigit()) and (plug.isdigit())):
                options["-s"] = switch
            options["-n"] = plug

    if (not (options.has_key("-s"))):
        options["-s"]="1"

    # Plug indexing start from zero on ePDU Managed, so we substract '1' from
    # the user's given plug number.
    # For Switched ePDU, we will add this back again later.
    if ((options.has_key("-n")) and (options["-n"].isdigit())):
        options["-n"] = str(int(options["-n"]) - 1)

    docs = { }
    docs["shortdesc"] = "Fence agent for Eaton over SNMP"
    docs["longdesc"] = "fence_eaton_snmp is an I/O Fencing agent \
which can be used with the Eaton network power switch. It logs \
into a device via SNMP and reboots a specified outlet. It supports \
SNMP v1 and v3 with all combinations of  authenticity/privacy settings."
    docs["vendorurl"] = "http://powerquality.eaton.com"
    show_docs(options, docs)

    # Operate the fencing device
    result = fence_action(FencingSnmp(options), options, set_power_status, get_power_status, get_outlets_status)

    sys.exit(result)
if __name__ == "__main__":
    main()
 

About

The Proxmox community has been around for many years and offers help and support for Proxmox VE, Proxmox Backup Server, and Proxmox Mail Gateway.
We think our community is one of the best thanks to people like you!

Get your subscription!

The Proxmox team works very hard to make sure you are running the best software and getting stable updates and security enhancements, as well as quick enterprise support. Tens of thousands of happy customers have a Proxmox subscription. Get yours easily in our online shop.

Buy now!