#!/usr/local/bin/python
#
# Copyright (c) 2002 Neil Blakey-Milner
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#

from twisted.internet import defer, protocol, reactor, udp

from pysnmp.proto import v1, v2c

class SNMPv1Boss:
    def __init__(self, server, port = 161, community = "public"):
        self.snmp = v1
        self.community = community

        self.queries = {}
        self.server = server
        self.port = port

    def gotResponse(self, peerinfo, data):
        if self.queries.has_key(peerinfo):
            (snmp, deferred) = self.queries[peerinfo]
            rsp = snmp.GetResponse()
            rsp.decode(data)
            deferred.callback(rsp)
            del self.queries[peerinfo]

    def makeRequest(self, oids):
        d = defer.Deferred()
        req = SNMPRequest()
        req.boss = self
        reactor.connectUDP(self.server, self.port, req)
        req.id = req.transport.getHost()
        self.queries[req.id] = (self.snmp, d)
        req.query(self.snmp, self.community, oids)
        d.setTimeout(5)
        return d

class SNMPv2Boss(SNMPv1Boss):
    def __init__(self, server, port = 161, community = "public"):
        SNMPv1Boss.__init__(self, server, port, community)
        self.snmp = v2c

class SNMPRequest(protocol.ConnectedDatagramProtocol):
    def query(self, snmp, community, oids):
        self.snmp = snmp
        self.req = snmp.GetRequest()
        self.req['community'].set(community)
        self.req['pdu']['get_request']['variable_bindings'].extend(map(lambda x: snmp.VarBind(name=snmp.ObjectName(x)), oids))
        self.transport.write(self.req.encode())

    def datagramReceived(self, answer):
        self.boss.gotResponse(self.id, answer)
        self.transport.loseConnection()

def success(rsp):
    oids = map(lambda x: x['name'].get(), \
        rsp['pdu'].values()[0]['variable_bindings'])
    vals = map(lambda x: x['value'], \
        rsp['pdu'].values()[0]['variable_bindings'])
    # Print out results
    for (oid, val) in map(None, oids, vals):
        print oid, ' ---> ', val.values()[0]

def failed(reason):
    print reason

def makeQuery(boss):
    d = boss.makeRequest(["1.3.6.1.2.1.2.2.1.10.2"])
    d2 = boss.makeRequest(["1.3.6.1.2.1.2.2.1.10.3"])
    d.addCallbacks(success, failed)
    d2.addCallbacks(success, failed)
    d3 = boss.makeRequest(["1.3.6.1.2.1.2.2.1.10.2", "1.3.6.1.2.1.2.2.1.10.3"])
    d3.addCallbacks(success, failed)
    reactor.callLater(5, makeQuery, (boss))

def main():
    boss = SNMPv2Boss("localhost", 161)
    reactor.callLater(1, makeQuery, (boss))
    reactor.run()

if __name__ == "__main__":
    main()
