markabrahams / node-net-snmp

JavaScript implementation of the Simple Network Management Protocol (SNMP)
206 stars 97 forks source link

Subagent Stops Processing Requests #224

Closed mcian2 closed 1 year ago

mcian2 commented 1 year ago

Hi folks,

This isn't necessarily an issue but more of a question:

I have a react app that implements a subagent. It works fine on my dev platform, able to handle hundreds of snmpwalk requests without problem. However when installed on a production platform (which is only slightly more loaded, but still, both systems lower than 6% cpu), the subagent will soon stop processing requests. Snmpwalk queries produce "No Such Object available on this agent at this OID". Restarting the app resolves the problem but only temporarily.

Has anyone else experienced anything similar? Is there some event I could listen for in order to somehow reset the registration?

Thanks!

entry: "./src/app.tsx"

` app.tsx:

async function boot() {
  let error : any = null;
  try {
    let app = new App("./config");
    let server = SNMPServer.getInstance();
    if (server.subagent != null) {
      server.openSubAgent(app.configurationBaseDirectory);
    }   
  } catch (e) {
    console.log('Error: ',e);
  }
}

boot();

agent.ts:

let snmp = require("net-snmp");
import * as path from "path";
let filesys = require("fs");
import { parseString } from "xml2js";

type SacSicT = { sac: number, sic: number };
export const SG3_AMCS_TABLE = "Sg3AmcsPtsTable";

export class SNMPServer {
  private static instance: SNMPServer;
  sitesXml: string;
  subagent: null | any;

  private constructor() {
    this.sitesXml = '';
    this.subagent = snmp.createSubagent(suboptions);
  }

  public static getInstance(): SNMPServer {
    if(!SNMPServer.instance) {
        SNMPServer.instance = new SNMPServer();
    }
    return SNMPServer.instance;
  }

  // Retrieve all the sac & sic pairs 
  getSacSicList( fileName: string ) : SacSicT[] {
    let xmlString: string = (filesys.readFileSync(fileName)).toString("utf-8");
    let list: SacSicT[] = [];
    parseString( xmlString, ( error, result ) => {
      if( error ) {
        console.error('getSacSicList(): ', error );
      }
      else {
        for( let site of result.sites.site ) {
          let ss: SacSicT = { sac: site.$.sac, sic: site.$.sic };
          list.push( ss );
        }
      }
    });
    return list;
  }

  public openSubAgent( configBaseDir: string ) {
    this.sitesXml = path.join( configBaseDir, "sites.xml");
    this.subagent.open( this.openCallback );
  }

  openCallback = ( error: any, data: any ) => {
    if( error ) {
        console.error('subagent.open() ERROR:' + error);
    } else {
        console.log('subagent.open() OK...' + data);
        this.subagent.registerProvider( summariesTableProvider, registerProviderCallback );
        this.subagent.registerProvider( sg3_to_AmcsTableProvider, registerProviderCallback );
         // Pre-populate SNMP Table or calls to net-snmp.setTableSingleCell() will hace erratic behavior (each
         // field in the table MUST have a value beforehand)
        let sacSicList: SacSicT[] = this.getSacSicList( this.sitesXml );
        for( let site of sacSicList) {
            this.subagent.getMib().addTableRow( SG3_AMCS_TABLE, [site.sac, site.sic,
                SG3_AMCS_SNMP_VALUES.UNINITIALISED,
                SG3_AMCS_SNMP_VALUES.UNINITIALISED,
                SG3_AMCS_SNMP_VALUES.UNINITIALISED,
                SG3_AMCS_SNMP_VALUES.UNINITIALISED,
                SG3_AMCS_SNMP_VALUES.UNINITIALISED,
                SG3_AMCS_SNMP_VALUES.UNINITIALISED,
                SG3_AMCS_SNMP_VALUES.UNINITIALISED,
                SG3_AMCS_SNMP_VALUES.UNINITIALISED,
                SG3_AMCS_SNMP_VALUES.UNINITIALISED,
                SG3_AMCS_SNMP_VALUES.UNINITIALISED,
                SG3_AMCS_SNMP_VALUES.UNINITIALISED]);
            console.log('SNMP added placeholder row sac sic ',site.sac, site.sic);
        }
    }
  }

  public closeSubAgent() {
    this.subagent.unregisterProvider( summariesTableProvider, ()=>{} );
    this.subagent.unregisterProvider( sg3_to_AmcsTableProvider, ()=>{} );
    this.subagent.close(() => {});
    console.log('Snmp Sub-Agent closeSubAgent()');
  }
}

// Default suboptions
var suboptions = {
    master: "localhost",
    masterPort: 705,
    backwardsGetNexts: true,
    description: "Node net-snmp AgentX sub-agent"
};

var registerProviderCallback = function(error: any, data: any) {
    if( error ) {
        console.error('CREATE SUBAGENT ERROR '+error);
    } else {
        console.log('OK SNMP CREATE SUBAGENT '+JSON.stringify(data, null, 2));
    }
};

var summariesTableProvider = {
    name: "summaryPtsTable",
    type: snmp.MibProviderType.Table,
    oid: "1.3.6.1.4.1.12345.3.3.2.1",
    tableColumns: [
        {
            number: 1,
            name: "snmpIndex",
            type: snmp.ObjectType.Integer
        },
        {
            number: 2,
            name: "SAC",
            type: snmp.ObjectType.Integer
        },
        {
            number: 3,
            name: "SIC",
            type: snmp.ObjectType.Integer
        },
        {
            number: 4,
            name: "Site",
            type: snmp.ObjectType.OctetString
        },
        {
            number: 5,
            name: "PointId",
            type: snmp.ObjectType.OctetString
        },
        {
            number: 6,
            name: "UpdateTime",
            type: snmp.ObjectType.Integer
        },
        {
            number: 7,
            name: "Level",
            type: snmp.ObjectType.Integer
        }
    ],
    tableIndex:  [{ columnName: "snmpIndex" }],
    handler: function(mibRequest: any) {
        console.log('snmp Sub-Agent Handler summaryPtsTable ', mibRequest.oid );
      // Can update the table here before responding to the request here if needed.
      mibRequest.done();
    }
};

export enum SG3_AMCS_SNMP_FIELDS {
    SAC = 1,
    SIC = 2,
    MssrA = 3,
    MssrB = 4,
    EncoderA = 5,
    EncoderB = 6,
    PedestalA = 7,
    PedestalB = 8,
    upsStatus = 9,
    upsAlarm = 10,
    generatorStatus = 11,
    generatorAlarm = 12,
    UpdateTime = 13
  }

export enum SG3_AMCS_SNMP_VALUES {
    MSSR_ONLINE = 0,
    MSSR_OFFLINE = 2,
    ENCODER_OK = 0,
    ENCODER_WARNING = 1,
    ENCODER_FAIL = 2,
    PEDESTAL_ROTATING = 0,
    PEDESTAL_WARNING = 1,
    PEDESTAL_STOPPED = 2,
    UPS_STATUS_ONLINE = 0,
    UPS_STATUS_BATTERY = 1,
    UPS_STATUS_BYPASSED = 2,
    UPS_ALARM_OK = 0,
    UPS_ALARM = 2,
    GENERATOR_STATUS_OFF = 0,
    GENERATOR_STATUS_RUNNING = 1,
    GENERATOR_ALARM_OK = 0,
    GENERATOR_ALARM_FAIL = 2,
    UNINITIALISED = 99
}

var sg3_to_AmcsTableProvider = {
    name: SG3_AMCS_TABLE,
    type: snmp.MibProviderType.Table,
    oid: "1.3.6.1.4.1.12345.3.3.2.2",
    tableColumns: [
        {
            number: SG3_AMCS_SNMP_FIELDS.SAC,
            name: "SAC",
            type: snmp.ObjectType.Integer
        },
        {
            number: SG3_AMCS_SNMP_FIELDS.SIC,
            name: "SIC",
            type: snmp.ObjectType.Integer
        },
        {
            number: SG3_AMCS_SNMP_FIELDS.MssrA,
            name: "MssrA",
            type: snmp.ObjectType.Integer
        },
        {
            number: SG3_AMCS_SNMP_FIELDS.MssrB,
            name: "MssrB",
            type: snmp.ObjectType.Integer
        },
        {
            number: SG3_AMCS_SNMP_FIELDS.EncoderA,
            name: "EncoderA",
            type: snmp.ObjectType.Integer
        },
        {
            number: SG3_AMCS_SNMP_FIELDS.EncoderB,
            name: "EncoderB",
            type: snmp.ObjectType.Integer
        },
        {
            number: SG3_AMCS_SNMP_FIELDS.PedestalA,
            name: "PedestalA",
            type: snmp.ObjectType.Integer
        },
        {
            number: SG3_AMCS_SNMP_FIELDS.PedestalB,
            name: "PedestalB",
            type: snmp.ObjectType.Integer
        },
        {
            number: SG3_AMCS_SNMP_FIELDS.upsStatus,
            name: "upsStatus",
            type: snmp.ObjectType.Integer
        },
        {
            number: SG3_AMCS_SNMP_FIELDS.upsAlarm,
            name: "upsAlarm",
            type: snmp.ObjectType.Integer
        },
        {
            number: SG3_AMCS_SNMP_FIELDS.generatorStatus,
            name: "generatorStatus",
            type: snmp.ObjectType.Integer
        },
        {
            number: SG3_AMCS_SNMP_FIELDS.generatorAlarm,
            name: "generatorAlarm",
            type: snmp.ObjectType.Integer
        },
        {
            number: SG3_AMCS_SNMP_FIELDS.UpdateTime,
            name: "UpdateTime",
            type: snmp.ObjectType.Integer
        }
    ],
    tableIndex:  [{ columnName: "SAC" }, { columnName: "SIC" }],
     //Optional:
      handler: function( mibRequest: any ) {
        console.log('snmp Sub-Agent Handler sg3_to_AmcsTableProvider ', mibRequest.oid );
         // Can update the table here before responding to the request here if needed.
         mibRequest.done();
    }
};`
markabrahams commented 1 year ago

Hi @mcian2 - I've just released version 3.9.3 of the npm, which allows you troubleshoot this scenario further by subscribing to a close event on the subagent's TCP socket like this:

subagent.on("close", function() {
    // do whatever you want here
    console.log ("Subagent socket closed");
});

In your case, a "No Such Object" error is being returned by the master agent, which indicates that it doesn't have that OID registered to the subagent any more.

Logging the aforementioned close event in combination with examining a packet capture between master and subagent would be able to determine if it was: (a) your subagent code inadvertently unregistering the OID, (b) a bug in the library incorrectly unregistering the OID from the subagent side, or (c) your master agent losing the OID registration separately from any subagent interaction e.g. through restarting

If I was a betting man, my money would be on (c).

mcian2 commented 1 year ago

Thanks Mark,

I will try this out soon-ish :-)

Ian

mcian2 commented 1 year ago

Upgrading from 3.5.2 to 3.9.3 has resolved this problem.

Thanks again Mark Ian

markabrahams commented 1 year ago

Thanks for the update @mcian2. With version 3.5.2 being a couple of years old, there is a chance of this being a bug that has been fixed in the intervening period. Closing as resolved.