Selaa lähdekoodia

Merge branch 'stats_command#1' into 'master'

Added a NAS on Listener

See merge request interlink-sa/flowdat3/modules/stats_command!3
Jean Sumara Leopoldo 5 vuotta sitten
vanhempi
commit
775a4c9507
5 muutettua tiedostoa jossa 1011 lisäystä ja 0 poistoa
  1. 3 0
      bin/console
  2. 212 0
      src/Command/NAS/NasOnuOctetsCommand.php
  3. 40 0
      src/SNMP/NAS/MIB.php
  4. 28 0
      src/SNMP/NAS/MIBS/OIDSBase.php
  5. 728 0
      src/SNMP/NAS/SNMP.php

+ 3 - 0
bin/console

@@ -6,6 +6,7 @@ use Flowdat\Stats\Command\Fiberhome\FiberhomeOltScanCommand;
 use Flowdat\Stats\Command\Fiberhome\FiberhomeOnuScanCommand;
 use Flowdat\Stats\Command\Huawei\HuaweiOltScanCommand;
 use Flowdat\Stats\Command\Huawei\HuaweiOnuScanCommand;
+use Flowdat\Stats\Command\NAS\NasOnuOctetsCommand;
 use Flowdat\Stats\Command\Zte\ZteOltScanCommand;
 use Flowdat\Stats\Command\Zte\ZteOnuScanCommand;
 use Symfony\Component\Console\Application;
@@ -19,4 +20,6 @@ $application->add(new FiberhomeOnuScanCommand());
 
 $application->add(new ZteOltScanCommand());
 $application->add(new ZteOnuScanCommand());
+
+$application->add(new NasOnuOctetsCommand());
 $application->run();

+ 212 - 0
src/Command/NAS/NasOnuOctetsCommand.php

@@ -0,0 +1,212 @@
+<?php
+
+
+namespace Flowdat\Stats\Command\NAS;
+
+
+use Doctrine\DBAL\DBALException;
+use Flowdat\Stats\App\Helper\RedisHelper;
+use Flowdat\Stats\App\Service\Doctrine\DoctrineService;
+use Flowdat\Stats\App\Service\SNMP\SNMPService;
+use Flowdat\Stats\App\Service\StatsD\StatsDService;
+use Flowdat\Stats\SNMP\NAS\SNMP;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class NasOnuOctetsCommand extends Command
+{
+    protected $nasDeviceId;
+    protected $nasServerId;
+    protected $nasIp;
+    protected $nasCommunity;
+    protected $nasSnmpLibrary;
+    protected $flag;
+    protected $apiSNMP;
+    protected $inicio;
+    protected $fin;
+    private $redisHelper;
+    private $snmpService;
+
+    /**
+     * NasOnuOctetsCommand constructor.
+     */
+    public function __construct()
+    {
+        parent::__construct();
+
+        $this->redisHelper = new RedisHelper();
+    }
+
+    protected function configure()
+    {
+        $this
+            ->setName('nas:onu:octets')
+            ->setDescription('Bandwidth ONTs de NAS')
+            ->setHelp('Se requieren parámetros para poder realizar la correcta consulta. El comando requiere Redis.')
+            ->setDefinition(array(
+                new InputOption('nas-device-id', null, InputOption::VALUE_OPTIONAL, "DeviceId del NAS",1),
+                new InputOption('nas-server-id', null, InputOption::VALUE_OPTIONAL, "ServerDevice del NAS",1),
+                new InputOption('nas-ip', false, InputOption::VALUE_OPTIONAL, "IP del NAS"),
+                new InputOption('nas-community', false, InputOption::VALUE_OPTIONAL, "COMMUNITY del NAS"),
+                new InputOption('nas-snmp-library', false, InputOption::VALUE_OPTIONAL, "Versión de librería SNMP"),
+                new InputOption('save-historic', null, InputOption::VALUE_OPTIONAL, "Send data to StatsD",1)
+            ))
+        ;
+    }
+
+    public function execute(InputInterface $input, OutputInterface $output)
+    {
+        $this->inicio = microtime(true);
+        $this->output = $output;
+
+        $msg = PHP_EOL."################################################# (".date("Y-m-d H:i:s").")";
+        $this->output->writeln($msg);
+
+        $this->nasDeviceId = (int) $input->getOption('nas-device-id');
+        $this->nasServerId = (int) $input->getOption('nas-server-id');
+        $this->nasIp = $input->getOption('nas-ip');
+        $this->nasCommunity = $input->getOption('nas-community');
+        $this->nasSnmpLibrary = $input->getOption('nas-snmp-library');
+        $this->d_s = "d_{$this->nasDeviceId}_s_{$this->nasServerId}";
+
+        $cmd = str_replace(":","_",$this->getName());
+        $this->output->writeln("INICIO COMANDO {$cmd} | {$this->d_s}");
+
+        $this->flag = "{$this->d_s}_cmd_$cmd";
+
+        if (!RedisHelper::lock($this->flag)) {
+            exit(1);
+        }
+
+        $this->snmpService = new SNMPService($this->flag);
+
+        //$key_nas_scan = "nas_scan_{$this->d_s}";
+        $key_nas_onu_bandwidth = "nas_bandwidth_onu_{$this->d_s}";
+
+        $saveHistoric = (int) $input->getOption('save-historic');
+        $inicio = microtime(true);
+
+        $SNMP = new SNMP($this->nasIp, $this->nasCommunity);
+        $library = "use".$this->nasSnmpLibrary;
+        $this->apiSNMP = $SNMP->$library();
+
+        $bandwidthCached = $this->redisHelper->getData($key_nas_onu_bandwidth, true);
+
+
+        $onus = $this->snmpService->getSNMP($this->apiSNMP, "onuSerialNumber","onuSerialNumber", $this-$output);
+
+        //counter64
+        $inOctets = $this->snmpService->getSNMP($this->apiSNMP, "onuInOctets","onuInOctets", $this->output);
+        $outOctets = $this->snmpService->getSNMP($this->apiSNMP, "onuOutOctets","onuOutOctets", $this->output);
+
+        $consumptionData = $sendData = array();
+
+        $subId = $this->d_s;
+
+        $t1 = time();
+        $totalConsOut = $totalConsIn = $totalIn = $totalOut = 0;
+        foreach($onus as $index => $onu) {
+
+            if(preg_match("/^<pppoe-([^-]+)[-\d]*>$/",$onu, $match)){
+                $sn = strtolower($match[1]);
+            } else {
+                continue;
+            }
+
+            (isset($inOctets[$index]))? $in1 = $inOctets[$index] : $in1 = 0;
+            (isset($outOctets[$index]))? $out1 = $outOctets[$index] : $out1 = 0;
+
+            if(isset($bandwidthCached[$index])) {
+                $t0 = $bandwidthCached[$index]['t'];
+                $in0 = $bandwidthCached[$index]['inOct'];
+                $out0 = $bandwidthCached[$index]['outOct'];
+                $inAcc = $bandwidthCached[$index]['inAcc'];
+                $outAcc = $bandwidthCached[$index]['outAcc'];
+
+                $inDiff = $outDiff = $inBandwidth = $outBandwidth = 0;
+
+                if(($in1 >= $in0) && ($t1 > $t0)) {
+                    $inDiff = $diff = $in1 - $in0;
+                    $inAcc += $diff;
+                    $inBandwidth = ($diff / ($t1 - $t0)) * 8;
+                }
+
+                if(($out1 >= $out0) && ($t1 > $t0)) {
+                    $outDiff = $diff = $out1 - $out0;
+                    $outAcc += $diff;
+                    $outBandwidth = ($diff / ($t1 - $t0)) * 8;
+                }
+
+                $totalIn += $inBandwidth;
+                $totalOut += $outBandwidth;
+
+                $sendData["inbandwidth_onu_{$sn}"] = "{$inBandwidth}|g";
+                $sendData["outbandwidth_onu_{$sn}"] = "{$outBandwidth}|g";
+
+                $div = 1073741824; //bytes => giga
+                $consIn = number_format(($inAcc / $div),3,'.','');
+                $consOut = number_format(($outAcc / $div),3,'.','');
+
+                $sendData["inconsumption_onu_{$sn}"] = "{$consIn}|g";
+                $sendData["outconsumption_onu_{$sn}"] = "{$consOut}|g";
+
+                $div = 1073741824; //bytes => giga
+                $inGB =  number_format($inDiff / $div, 5,".","");
+                $outGB = number_format($outDiff / $div, 5,".","");
+                $fatherDevice = $this->nasDeviceId;
+                $server = $this->nasServerId;
+                $fatherDeviceType = 3;
+                $device = $sn;
+                $date = date("Y-m-d");
+                $consumptionData[] = "({$server},{$fatherDevice},{$fatherDeviceType},'{$device}','{$date}',{$inGB},{$outGB})";
+
+                if(date("n",$t0) != date("n",$t1)) {
+                    $inAcc = $outAcc = 0;
+                }
+
+                $totalConsOut += $outAcc;
+                $totalConsIn += $inAcc;
+
+                $bandwidthCached[$index] = array('sn' => $sn, 't' => $t1, 'inOct' => $in1, 'outOct' => $out1, 'inAcc' => $inAcc, 'outAcc' => $outAcc, 'inBand' => $inBandwidth, 'outBand' => $outBandwidth);
+            } else {
+                $bandwidthCached[$index] = array('sn' => $sn, 't' => $t1, 'inOct' => $in1, 'outOct' => $out1, 'inAcc' => 0, 'outAcc' => 0, 'inBand' => 0, 'outBand' => 0);
+            }
+
+        }
+
+        $this->redisHelper->setData($key_nas_onu_bandwidth, $bandwidthCached, true);
+
+        $this->saveConsumption($consumptionData);
+
+        if($sendData && $saveHistoric) {
+            $t_start_script = microtime(true);
+            (new StatsDService())->send($sendData);
+            $t_end_script = microtime(true);
+            $time = $t_end_script - $t_start_script;
+            print_r("Tiempo de envío al StatsD: {$time} ms / Cantidad: ".count($sendData).PHP_EOL);
+        }
+
+
+        /* Fin de bloqueo */
+        RedisHelper::removeLock($this->flag);
+
+        $fin = microtime(true);
+        $time = $fin - $inicio;
+        $this->output->writeln("Tiempo: $time segundos");
+
+    }
+
+    private function saveConsumption($data)
+    {
+        if ($data) {
+            $query = "INSERT LOW_PRIORITY INTO `device_consumption` (`server_id`, `father_device_id`, `father_device_type`, `device`, `date`,`cons_in`,`cons_out`) VALUES " . implode(",", $data) . " ON DUPLICATE KEY UPDATE cons_out = cons_out + VALUES(cons_out), cons_in = cons_in + VALUES(cons_in);";
+            try {
+                (new DoctrineService())->executeQueries(array($query));
+            } catch (DBALException $e) {
+
+            }
+        }
+    }
+}

+ 40 - 0
src/SNMP/NAS/MIB.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace Flowdat\Stats\SNMP\NAS;
+
+/**
+ * Parent class for all "MIB" extensions.
+ *
+ * @copyright Copyright (c) 2012, Open Source Solutions Limited, Dublin, Ireland
+ * @author Barry O'Donovan <barry@opensolutions.ie>
+ */
+class MIB
+{
+    /**
+     * Instance for the SNMP object
+     */
+    private $_snmp = null;
+
+
+    /**
+     * Set the SNMP instance
+     *
+     * @param SNMP $snmp the SNMP instance
+     * @return MIB An instance of this class for fluent interfaces
+     */
+    public function setSNMP( $snmp )
+    {
+        $this->_snmp = $snmp;
+    }
+
+    /**
+     * Get the SNMP instance
+     *
+     * @return SNMP Instance of the SNMP object
+     */
+    public function getSNMP()
+    {
+        return $this->_snmp;
+    }
+
+}

+ 28 - 0
src/SNMP/NAS/MIBS/OIDSBase.php

@@ -0,0 +1,28 @@
+<?php
+
+// MikroTik - http://www.oidview.com/mibs/14988/MIKROTIK-MIB.html
+namespace Flowdat\Stats\SNMP\NAS\MIBS;
+
+use Flowdat\Stats\SNMP\NAS\MIB;
+
+class OIDSBase extends MIB {
+    
+    const OID_mtxrQueueSimpleName      = "1.3.6.1.4.1.14988.1.1.2.1.1.2";
+    
+    const OID_mtxrQueueSimpleBytesIn    = "1.3.6.1.4.1.14988.1.1.2.1.1.8";
+    const OID_mtxrQueueSimpleBytesOut   = "1.3.6.1.4.1.14988.1.1.2.1.1.9";
+    
+
+    public function onuSerialNumber($index = null) {
+        return $this->getSNMP()->lastOidWalk(self::OID_mtxrQueueSimpleName,14);
+    }
+    
+    public function onuInOctets() {
+        return $this->getSNMP()->lastOidWalk(self::OID_mtxrQueueSimpleBytesIn,14);
+    }
+    
+    public function onuOutOctets() {
+        return $this->getSNMP()->lastOidWalk(self::OID_mtxrQueueSimpleBytesOut,14);
+    }
+    
+}

+ 728 - 0
src/SNMP/NAS/SNMP.php

@@ -0,0 +1,728 @@
+<?php
+
+namespace Flowdat\Stats\SNMP\NAS;
+use \Exception;
+use OSS_SNMP\Cache\Basic;
+
+class SNMP
+{
+    /**
+     * The SNMP community to use when polling SNMP services. Defaults to 'public' by the constructor.
+     *
+     * @var string The SNMP community to use when polling SNMP services. Defaults to 'public' by the constructor.
+     */
+    protected $_community;
+
+    /**
+     * The SNMP host to query. Defaults to '127.0.0.1'
+     * @var string The SNMP host to query. Defaults to '127.0.0.1' by the constructor.
+     */
+    protected $_host;
+
+    /**
+     * The SNMP query timeout value (microseconds). Default: 1000000
+     * @var int The SNMP query timeout value (microseconds). Default: 1000000
+     */
+    protected $_timeout = 100000000;
+
+    /**
+     * The SNMP query retry count. Default: 5
+     * @var int The SNMP query retry count. Default: 5
+     */
+    protected $_retry = 5;
+
+
+    /**
+     * A variable to hold the last unaltered result of an SNMP query
+     * @var mixed The last unaltered result of an SNMP query
+     */
+    protected $_lastResult = null;
+
+    /**
+     * The cache object to use as the cache
+     * @var \OSS_SNMP\Cache\Basic The cache object to use
+     */
+    protected $_cache = null;
+
+    /**
+     * Set to true to disable local cache lookup and force SNMP queries
+     *
+     * Results are still stored. If you need to force a SNMP query, you can:
+     *
+     * $snmp = new OSS_SNMP( ... )'
+     * ...
+     * $snmp->disableCache();
+     * $snmp->get( ... );
+     * $snmp->enableCache();
+     */
+    protected $_disableCache = false;
+
+    /**
+     * SNMP output constants to mirror those of PHP
+     * @var SNMP output constants to mirror those of PHP
+     */
+    const OID_OUTPUT_FULL    = SNMP_OID_OUTPUT_FULL;
+
+    /**
+     * SNMP output constants to mirror those of PHP
+     * @var SNMP output constants to mirror those of PHP
+     */
+    const OID_OUTPUT_NUMERIC = SNMP_OID_OUTPUT_NUMERIC;
+
+
+    /**
+     * Definition of an SNMP return type 'TruthValue'
+     * @var Definition of an SNMP return type 'TruthValue'
+     */
+    const SNMP_TRUTHVALUE_TRUE  = 1;
+
+    /**
+     * Definition of an SNMP return type 'TruthValue'
+     * @var Definition of an SNMP return type 'TruthValue'
+     */
+    const SNMP_TRUTHVALUE_FALSE = 2;
+
+    /**
+     * PHP equivalents of SNMP return type TruthValue
+     * @var array PHP equivalents of SNMP return type TruthValue
+     */
+    public static $SNMP_TRUTHVALUES = array(
+        self::SNMP_TRUTHVALUE_TRUE  => true,
+        self::SNMP_TRUTHVALUE_FALSE => false
+    );
+
+    /**
+     * The constructor.
+     *
+     * @param string $host The target host for SNMP queries.
+     * @param string $community The community to use for SNMP queries.
+     */
+    public function __construct( $host = '127.0.0.1', $community = 'public' )
+    {
+        return $this->setHost( $host )
+                    ->setCommunity( $community )
+                    ->setOidOutputFormat( self::OID_OUTPUT_NUMERIC );
+    }
+
+
+    /**
+     * Proxy to the snmp2_real_walk command
+     *
+     * @param string $oid The OID to walk
+     * @return array The results of the walk
+     */
+    public function realWalk( $oid )
+    {
+        return $this->_lastResult = @snmp2_real_walk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
+    }
+
+
+    /**
+     * Get a single SNMP value
+     *
+     * @throws \Exception On *any* SNMP error, warnings are supressed and a generic exception is thrown
+     * @param string $oid The OID to get
+     * @return mixed The resultant value
+     */
+    public function get( $oid )
+    {
+        if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
+            return $rtn;
+
+        $this->_lastResult = @snmp2_get( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
+
+        if( $this->_lastResult === false )
+            throw new Exception( 'Cound not perform walk for OID ' . $oid );
+
+        return $this->getCache()->save( $oid, $this->parseSnmpValue( $this->_lastResult ) );
+    }
+
+    /**
+     * Get indexed SNMP values (first degree)
+     *
+     * Walks the SNMP tree returning an array of key => value pairs.
+     *
+     * This is a first degree walk and it will throw an exception if there is more that one degree of values.
+     *
+     * I.e. the following query with sample results:
+     *
+     * walk1d( '.1.0.8802.1.1.2.1.3.7.1.4' )
+     *
+     *       .1.0.8802.1.1.2.1.3.7.1.4.1 = STRING: "GigabitEthernet1/0/1"
+     *       .1.0.8802.1.1.2.1.3.7.1.4.2 = STRING: "GigabitEthernet1/0/2"
+     *       .1.0.8802.1.1.2.1.3.7.1.4.3 = STRING: "GigabitEthernet1/0/3"
+     *       .....
+     *
+     * would yield an array:
+     *
+     *      1 => GigabitEthernet1/0/1
+     *      2 => GigabitEthernet1/0/2
+     *      3 => GigabitEthernet1/0/3
+     *
+     * @param string $oid The OID to walk
+     * @return array The resultant values
+     * @throws \Exception On *any* SNMP error, warnings are supressed and a generic exception is thrown
+     */
+    public function walk1d( $oid )
+    {
+        if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
+            return $rtn;
+
+        $this->_lastResult = @snmp2_real_walk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
+
+        if( $this->_lastResult === false )
+            throw new Exception( 'Cound not perform walk for OID ' . $oid );
+
+        $result = array();
+
+        $oidPrefix = null;
+        foreach( $this->_lastResult as $_oid => $value )
+        {
+            if( $oidPrefix !== null && $oidPrefix != substr( $_oid, 0, strrpos( $_oid, '.' ) ) )
+                throw new Exception( 'Requested OID tree is not a first degree indexed SNMP value' );
+            else
+                $oidPrefix = substr( $_oid, 0, strrpos( $_oid, '.' ) );
+
+            $result[ substr( $_oid, strrpos( $_oid, '.' ) + 1 ) ] = $this->parseSnmpValue( $value );
+        }
+
+        return $this->getCache()->save( $oid, $result );
+    }
+
+    /**
+     * Get indexed SNMP values where the array key is the given position of the OID
+     *
+     * I.e. the following query with sample results:
+     *
+     * subOidWalk( '.1.3.6.1.4.1.9.9.23.1.2.1.1.9', 15 )
+     *
+     *
+     *       .1.3.6.1.4.1.9.9.23.1.2.1.1.9.10101.5 = Hex-STRING: 00 00 00 01
+     *       .1.3.6.1.4.1.9.9.23.1.2.1.1.9.10105.2 = Hex-STRING: 00 00 00 01
+     *       .1.3.6.1.4.1.9.9.23.1.2.1.1.9.10108.4 = Hex-STRING: 00 00 00 01
+     *
+     * would yield an array:
+     *
+     *      10101 => Hex-STRING: 00 00 00 01
+     *      10105 => Hex-STRING: 00 00 00 01
+     *      10108 => Hex-STRING: 00 00 00 01
+     *
+     * @throws \Exception On *any* SNMP error, warnings are supressed and a generic exception is thrown
+     * @param string $oid The OID to walk
+     * @param int $position The position of the OID to use as the key
+     * @return array The resultant values
+     */
+    public function subOidWalk( $oid, $position )
+    {
+        if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
+            return $rtn;
+
+        $this->_lastResult = @snmp2_real_walk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
+
+        if( $this->_lastResult === false )
+            throw new Exception( 'Cound not perform walk for OID ' . $oid );
+
+        $result = array();
+
+        foreach( $this->_lastResult as $_oid => $value )
+        {
+            $oids = explode( '.', $_oid );
+
+            $result[ $oids[ $position] ] = $this->parseSnmpValue( $value );
+        }
+
+        return $this->getCache()->save( $oid, $result );
+    }
+
+
+
+    /**
+     * Get indexed SNMP values where they are indexed by IPv4 addresses
+     *
+     * I.e. the following query with sample results:
+     *
+     * subOidWalk( '.1.3.6.1.2.1.15.3.1.1. )
+     *
+     *
+     *       .1.3.6.1.2.1.15.3.1.1.10.20.30.4 = IpAddress: 192.168.10.10
+     *       ...
+     *
+     * would yield an array:
+     *
+     *      [10.20.30.4] => "192.168.10.10"
+     *      ....
+     *
+     * @throws \Exception On *any* SNMP error, warnings are supressed and a generic exception is thrown
+     * @param string $oid The OID to walk
+     * @return array The resultant values
+     */
+    public function walkIPv4( $oid )
+    {
+        if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
+            return $rtn;
+
+        $this->_lastResult = @snmp2_real_walk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
+
+        if( $this->_lastResult === false )
+            throw new Exception( 'Cound not perform walk for OID ' . $oid );
+
+        $result = array();
+
+        foreach( $this->_lastResult as $_oid => $value )
+        {
+            $oids = explode( '.', $_oid );
+
+            $len = count( $oids );
+
+            $result[ $oids[ $len - 4 ] . '.' . $oids[ $len - 3 ] . '.' . $oids[ $len - 2 ] . '.' . $oids[ $len - 1 ]  ] = $this->parseSnmpValue( $value );
+        }
+
+        return $this->getCache()->save( $oid, $result );
+    }
+
+
+
+    /**
+     * Parse the result of an SNMP query into a PHP type
+     *
+     * For example, [STRING: "blah"] is parsed to a PHP string containing: blah
+     *
+     * @param string $v The value to parse
+     * @return mixed The parsed value
+     * @throws Exception
+     */
+    public function parseSnmpValue( $v )
+    {
+        // first, rule out an empty string
+        if( $v == '""' || $v == '' )
+            return "";
+
+        $type = substr( $v, 0, strpos( $v, ':' ) );
+        $value = trim( substr( $v, strpos( $v, ':' ) + 1 ) );
+
+        switch( $type )
+        {
+            case 'STRING':
+                if($value == '"-"') return "";
+                    
+                if( substr( $value, 0, 1 ) == '"' )
+                    $rtn = (string)trim( substr( substr( $value, 1 ), 0, -1 ) );
+                else
+                    $rtn = (string)$value;
+                break;
+
+            case 'INTEGER': //case : INTEGER : someText(intValue) // INTEGER : -3.15 Unidad
+                if( strpos( $value, '(' ) !== false and substr($value, 0,1) != '-'){
+                    $rtn = (float)substr( substr( $value, strpos( $value, '(' ) + 1 ), 0);
+                }else{
+                    $rtn = (float)$value;
+		}
+
+		if($rtn == (int)$rtn) $rtn = (int) $rtn;
+                break;
+
+            case 'Counter32':
+            case 'Counter64':
+                $rtn = (int)$value;
+                break;
+
+            case 'Gauge32':
+            case 'Gauge64':
+                $rtn = (int)$value;
+                break;
+
+            case 'Hex-STRING':
+                $rtn = (string)implode( '', explode( ' ', $value ) );
+                break;
+
+            case 'IpAddress':
+                $rtn = (string)$value;
+                break;
+
+            case 'Timeticks':
+                $rtn = (int)substr( $value, 1, strrpos( $value, ')' ) - 1 );
+                break;
+
+            default:
+                throw new Exception( "ERR: Unhandled SNMP return type: $type\n" );
+        }
+
+        return $rtn;
+    }
+
+    /**
+     * Utility function to convert TruthValue SNMP responses to true / false
+     *
+     * @param integer $value The TruthValue ( 1 => true, 2 => false) to convert
+     * @return boolean
+     */
+    public static function ppTruthValue( $value )
+    {
+        if( is_array( $value ) )
+            foreach( $value as $k => $v )
+                $value[$k] = self::$SNMP_TRUTHVALUES[ $v ];
+        else
+            $value = self::$SNMP_TRUTHVALUES[ $value ];
+
+        return $value;
+    }
+
+    /**
+     * Utility function to translate one value(s) to another via an associated array
+     *
+     * I.e. all elements '$value' will be replaced with $translator( $value ) where
+     * $translator is an associated array.
+     *
+     * Huh? Just read the code below!
+     *
+     * @param mixed $values A scalar or array or values to translate
+     * @param array $translator An associated array to use to translate the values
+     * @return mixed The translated scalar or array
+     */
+    public static function translate( $values, $translator )
+    {
+        if( !is_array( $values ) )
+        {
+            if( isset( $translator[ $values ] ) )
+                return $translator[ $values ];
+            else
+                return "*** UNKNOWN ***";
+        }
+
+        foreach( $values as $k => $v )
+        {
+            if( isset( $translator[ $v ] ) )
+                $values[$k] = $translator[ $v ];
+            else
+                $values[$k] = "*** UNKNOWN ***";
+        }
+
+        return $values;
+    }
+
+    /**
+     * Sets the output format for SNMP queries.
+     *
+     * Should be one of the class OID_OUTPUT_* constants
+     *
+     * @param int $f The fomat to use
+     * @return SNMP An instance of $this (for fluent interfaces)
+     */
+    public function setOidOutputFormat( $f )
+    {
+        snmp_set_oid_output_format( $f );
+        return $this;
+    }
+
+
+    /**
+     * Sets the target host for SNMP queries.
+     *
+     * @param string $h The target host for SNMP queries.
+     * @return SNMP An instance of $this (for fluent interfaces)
+     */
+    public function setHost( $h )
+    {
+        $this->_host = $h;
+
+        // clear the temporary result cache and last result
+        $this->_lastResult = null;
+        unset( $this->_resultCache );
+        $this->_resultCache = array();
+
+        return $this;
+    }
+
+    /**
+     * Returns the target host as currently configured for SNMP queries
+     *
+     * @return string The target host as currently configured for SNMP queries
+     */
+    public function getHost()
+    {
+        return $this->_host;
+    }
+
+    /**
+     * Sets the community string to use for SNMP queries.
+     *
+     * @param string $c The community to use for SNMP queries.
+     * @return SNMP An instance of $this (for fluent interfaces)
+     */
+    public function setCommunity( $c )
+    {
+        $this->_community = $c;
+        return $this;
+    }
+
+    /**
+     * Returns the community string currently in use.
+     *
+     * @return string The community string currently in use.
+     */
+    public function getCommunity()
+    {
+        return $this->_community;
+    }
+
+    /**
+     * Sets the timeout to use for SNMP queries (microseconds).
+     *
+     * @param int $t The timeout to use for SNMP queries (microseconds).
+     * @return SNMP An instance of $this (for fluent interfaces)
+     */
+    public function setTimeout( $t )
+    {
+        $this->_timeout = $t;
+        return $this;
+    }
+
+    /**
+     * Returns the SNMP query timeout (microseconds).
+     *
+     * @return int The the SNMP query timeout (microseconds)
+     */
+    public function getTimeout()
+    {
+        return $this->_timeout;
+    }
+
+    /**
+     * Sets the SNMP query retry count.
+     *
+     * @param int $r The SNMP query retry count
+     * @return SNMP An instance of $this (for fluent interfaces)
+     */
+    public function setRetry( $r )
+    {
+        $this->_retry = $r;
+        return $this;
+    }
+
+    /**
+     * Returns the SNMP query retry count
+     *
+     * @return string The SNMP query retry count
+     */
+    public function getRetry()
+    {
+        return $this->_retry;
+    }
+
+    /**
+     * Returns the unaltered original last SNMP result
+     *
+     * @return mixed The unaltered original last SNMP result
+     */
+    public function getLastResult()
+    {
+        return $this->_lastResult;
+    }
+
+    /**
+     * Returns the internal result cache
+     *
+     * @return array The internal result cache
+     */
+    public function getResultCache()
+    {
+        return $this->_resultCache;
+    }
+
+
+    /**
+     * Disable lookups of the local cache
+     *
+     * @return SNMP An instance of this for fluent interfaces
+     */
+    public function disableCache()
+    {
+        $this->_disableCache = true;
+        return $this;
+    }
+
+
+    /**
+     * Enable lookups of the local cache
+     *
+     * @return SNMP An instance of this for fluent interfaces
+     */
+    public function enableCache()
+    {
+        $this->_disableCache = false;
+        return $this;
+    }
+
+    /**
+     * Query whether we are using the cache or not
+     *
+     * @return boolean True of the local lookup cache is enabled. Otherwise false.
+     */
+    public function cache()
+    {
+        return !$this->_disableCache;
+    }
+
+    /**
+     * Set the cache to use
+     *
+     * @param \OSS_SNMP\Cache $c The cache to use
+     * @return SNMP For fluent interfaces
+     */
+    public function setCache( $c )
+    {
+        $this->_cache = $c;
+        return $this;
+    }
+
+    /**
+     * Get the cache in use (or create a Cache\Basic instance
+     *
+     * We kind of mandate the use of a cache as the code is written with a cache in mind.
+     * You are free to disable it via disableCache() but your machines may be hammered!
+     *
+     * We would suggest disableCache() / enableCache() used in pairs only when really needed.
+     *
+     * @return \OSS_SNMP\Cache The cache object
+     */
+    public function getCache()
+    {
+        if( $this->_cache === null )
+            $this->_cache = new Basic();
+
+        return $this->_cache;
+    }
+
+
+    /**
+     * Magic method for generic function calls
+     *
+     * @param string $method
+     * @param array $args
+     * @throws Exception
+     */
+    public function __call( $method, $args )
+    {
+        if( substr( $method, 0, 3 ) == 'use' )
+            return $this->useExtension( substr( $method, 3 ), $args );
+
+        throw new Exception( "ERR: Unknown method requested in magic __call(): $method\n" );
+    }
+
+
+    /**
+     * This is the MIB Extension magic
+     *
+     * Calling $this->useXXX_YYY_ZZZ()->fn() will instantiate
+     * an extension MIB class is the given name and this $this SNMP
+     * instance and then call fn().
+     *
+     * See the examples for more information.
+     *
+     * @param string $mib The extension class to use
+     * @param array $args
+     */
+    public function useExtension( $mib, $args )
+    {
+        $mib = 'Flowdat\\Stats\\SNMP\\NAS\\MIBS\\' . str_replace( '_', '\\', $mib );
+        $m = new $mib();
+        $m->setSNMP( $this );
+        return $m;
+    }
+    
+    /**
+     * Retorna la última parte del oid, iniciando desde $position
+     *
+     * I.e. the following query with sample results:
+     *
+     * lastOidWalk( '1.3.6.1.4.1.13464.1.11.3.1.1.2', 14 )
+     *
+     * tener en cuenta que retorna con . por delante y realiza explode por .
+     * 
+     *      .1.3.6.1.4.1.13464.1.11.3.1.1.2.0.1 2
+     *      .1.3.6.1.4.1.13464.1.11.3.1.1.2.0.2 2
+     *      .1.3.6.1.4.1.13464.1.11.3.1.1.2.0.3 2
+     *      .1.3.6.1.4.1.13464.1.11.3.1.1.2.0.4 2
+     *      
+     * Posiciones:
+     *        .  1  .  3  .  6  .  1  .  4  .  1  .  13464  .  1  .  11  .  3  .  1  .  1  .  2  .  0  .  4   2 
+     *      0    1     2     3     4     5     6       7       8     9      10    11    12    13    14    15  value
+     *
+     * Would yield an array:
+     *
+     *      [0.1] => 2
+     *      [0.2] => 2
+     *      [0.3] => 2
+     *      [0.4] => 2
+     *
+     *
+     * @throws \Exception On *any* SNMP error, warnings are supressed and a generic exception is thrown
+     * @param string $oid The OID to walk
+     * @param int $position The position of the OID to use as the key
+     * @return array The resultant values
+     */
+    public function lastOidWalk( $oid, $position)
+    {
+        if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
+            return $rtn;
+
+        $this->_lastResult = @snmp2_real_walk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
+
+        if( $this->_lastResult === false )
+            throw new Exception( 'Cound not perform walk for OID ' . $oid );
+
+        $result = array();
+
+        foreach( $this->_lastResult as $_oid => $value )
+        {
+            $oids = explode( '.', $_oid );
+
+            $_oids = array_slice($oids, $position);
+            $oid = implode(".", $_oids);
+            
+            $result[ $oid ] = $this->parseSnmpValue( $value );
+        }
+
+        return $this->getCache()->save( $oid, $result );
+    }
+    
+    /**
+     * Proxy to the snmprealwalk command
+     *
+     * @param string $oid The OID to walk
+     * @return array The results of the walk
+     */
+    public function realWalkV1( $oid )
+    {
+        return $this->_lastResult = @snmprealwalk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
+    }
+    
+    public function lastOidWalkV1( $oid, $position )
+    {
+        if( $this->cache() && ( $rtn = $this->getCache()->load( $oid ) ) !== null )
+            return $rtn;
+
+        $this->_lastResult = @snmprealwalk( $this->getHost(), $this->getCommunity(), $oid, $this->getTimeout(), $this->getRetry() );
+
+        if( $this->_lastResult === false )
+            throw new Exception( 'Cound not perform walk for OID ' . $oid );
+
+        $result = array();
+
+        foreach( $this->_lastResult as $_oid => $value )
+        {
+            $oids = explode( '.', $_oid );
+
+            $_oids = array_slice($oids, $position);
+            $oid = implode(".", $_oids);
+            
+            $result[ $oid ] = $this->parseSnmpValue( $value );
+        }
+
+        return $this->getCache()->save( $oid, $result );
+        
+    }
+    
+}
+
+