#!/usr/bin/env python
"""
   Retrieve or set  MIB variables associated with user specifed SNMP Object
   IDs from arbitrary SNMP agent.

   Copyright 1999-2004 by Ilya Etingof <ilya@glas.net>. See LICENSE for
   details.
"""
from sys import exit, argv, stderr
from string import find
from pysnmp.version import verifyVersionRequirement

verifyVersionRequirement(3, 4)

from pysnmp.mapping.udp.role import Manager
from pysnmp.proto.api import alpha
from pysnmpap.cli import options, ucd
from pysnmpap.error import SnmpApplicationError

class ConfSnmpVersion(options.ConfOptionValue):
    def help(self): return 'SNMP protocol version to use [%s]' % self
confSnmpVersion = ConfSnmpVersion(alpha.protoVersionId1)

class ConfReportType(options.ConfOptionFlag):
    def help(self): return 'report variables types [%s]' % self
confReportType = ConfReportType(0)

def formatUsage():
    tspDsp = Manager()
    ver = alpha.protoVersions[confSnmpVersion()]
    req = ver.Message()
    req.apiAlphaSetPdu(ver.pduTypes[pduType]())
    appUsage = '[ -h ] [ -v <version> ] [ -C <t> ]'
    appOpts =           ' -h             print this usage note.\n'
    appOpts = appOpts + ' -v <version>   ' + confSnmpVersion.help() + '\n'
    appOpts = appOpts + ' -C <t>         application specific options\n'
    appOpts = appOpts + '     t:         ' + confReportType.help() + '\n'
    usage = 'Usage: ' + argv[0] + ' ' + appUsage + ' ' + \
            tspDsp.cliUcdGetUsage() + ' ' + req.cliUcdGetUsage()
    options = 'Application options:\n' + appOpts + \
              'Transport options:\n' + tspDsp.cliUcdGetOptsUsage(' ') + \
              'Protocol options:\n' + req.cliUcdGetOptsUsage()
    return usage + '\n' + options

# Figure out operation mode (not GNUish)
if find(argv[0], 'pysnmpget') != -1:
    pduType = alpha.getRequestPduType
elif find(argv[0], 'pysnmpset') != -1:
    pduType = alpha.setRequestPduType
else:
    stderr.write('Cant determine operation mode (get/set) from tool name: %s'\
                 % argv[0])
    exit(1)

# Parse app options
idx = 1; newArgv = []
while idx < len(argv):
    if argv[idx] == '-h':
        stderr.write(formatUsage() + '\n')
        exit(1)
    if argv[idx] == '-v':
        if argv[idx+1][:1] != 'v': argv[idx+1] = 'v'+argv[idx+1]
        if argv[idx+1] == 'v1':
            confSnmpVersion(alpha.protoVersionId1)
        elif argv[idx+1] == 'v2c':
            confSnmpVersion(alpha.protoVersionId2c)
        else:
            stderr.write('Unsupported SNMP protocol version: %s\n%s\n' % \
                         (argv[idx+1], formatUsage()))
            exit(-1)
        idx = idx + 2
        continue
    if argv[idx] == '-C':
        try:
            for c in list(argv[idx+1]):
                if c == 't': confReportType(1)
        except IndexError, why:
            stderr.write('Missing value to %s\n%s\n' % \
                         (argv[idx], formatUsage()))
            exit(-1)
        idx = idx + 2
        continue
    newArgv.append(argv[idx])
    idx = idx + 1

# Proto & transport objects
tspDsp = Manager()
ver = alpha.protoVersions[confSnmpVersion()]
req = ver.Message(); rsp = ver.Message()
req.apiAlphaSetPdu(ver.pduTypes[pduType]())

# Parse transport and protocol options
try:
    newArgv = req.cliUcdSetArgs(tspDsp.cliUcdSetArgs(newArgv))
except SnmpApplicationError, why:
    stderr.write('Error: %s\n%s\n' % (why, formatUsage()))
    exit(-1)

if len(newArgv):
    stderr.write('Extra parameters given: %s\n%s\n' \
                 % (newArgv, formatUsage()))
    exit(-1)

def cbFun(wholeMsg, srcAddr, (req, rsp)):
    rsp.berDecode(wholeMsg)
    if not req.apiAlphaMatch(rsp): return
    
    reqPdu = req.apiAlphaGetPdu(); rspPdu = rsp.apiAlphaGetPdu()
    varBindList = rspPdu.apiAlphaGetVarBindList()
    
    # Handle errors
    errorStatus = rspPdu.apiAlphaGetErrorStatus()
    if errorStatus != 0:
        errorIndex = rspPdu.apiAlphaGetErrorIndex()-1
        raise SnmpApplicationError('%s at %s' % \
                                   (errorStatus, varBindList[errorIndex.get()]))

    for varBind in varBindList:
        oid, val = varBind.apiAlphaGetOidVal()
        if confReportType:
            print oid, '=', val
        else:
            print oid.get(), '=', repr(val.get())
                
    req.apiAlphaGetPdu().apiAlphaGetRequestId().inc(1)
    return 1

try:
    tspDsp.sendAndReceive(req.berEncode(), (None, 0), (cbFun, (req, rsp)))

except SnmpApplicationError, why:
    if why: stderr.write('Application error: %s\n' % why)
    exit(-1)

except Exception, why:
    stderr.write('pysnmp error: %s\n' % why)
    exit(-1)
