Skip to main content

Overview

Custom device models allow you to extend NMIS monitoring for:
  • Unsupported device vendors
  • Proprietary MIBs
  • Custom monitoring requirements
  • Override default model behavior

Model Directory Structure

nmis9/
├── models-default/       # Opmantek-provided models (do not modify)
│   ├── Model-*.nmis
│   ├── Common-*.nmis
│   └── Graph-*.nmis
└── models-custom/        # Your custom models (safe from upgrades)
    ├── Model-MyDevice.nmis
    ├── Common-MyFeature.nmis
    └── Graph-MyGraph.nmis
Important: Always create custom models in models-custom/ directory. Files in models-default/ are overwritten during NMIS upgrades.

Model Precedence

NMIS loads models in this order (later files override earlier ones):
  1. models-default/ - Base models from Opmantek
  2. models-custom/ - Your custom models (highest precedence)
To override a default model, copy it to models-custom/ and modify.

Using model_tool.pl

The model_tool.pl script helps discover SNMP capabilities of devices:

Location

/usr/local/nmis9/admin/model_tool.pl

Basic Usage

# Discover what a device supports
./model_tool.pl node=devicename debug=true

# Check model syntax
./model_tool.pl check=true

# Generate discovery report
./model_tool.pl node=devicename file=/tmp/discovery.txt

# Create a new model automatically
./model_tool.pl node=devicename model=MyNewModel

# Exclude specific common models
./model_tool.pl node=devicename model=MyDevice common_exclude="(cbqos|netflow)"

Discovery Output

From model_tool.pl:1342-1377:
usage: ./model_tool.pl node=<nodename> [model=name for new model]
                      [file=/path/to/file_for_details.txt] 
                      [debug=true|false]

Params:
  node: NMIS nodename
  check: (true|false), check the models structure and for errors
  model: Name of new model and the result file to be generated
  common_exclude: A regular expression for the Common models
               to exclude in the auto generated model
  file: Where to save the results to, TAB delimited CSV
  errors: Display models errors found or not
  schema: Check the models structure against the schema
  make_schema: Make the model schema file

Discovery Process

From model_tool.pl:369-501:
  1. Load node configuration - Gets SNMP credentials
  2. Test SNMP connectivity - Verifies device is reachable
  3. Walk MIB trees - Tests each OID from models
  4. Report findings - Shows supported/unsupported MIBs
  5. Generate model - Creates basic model structure

Creating a New Model

Step 1: Discover Device Capabilities

# First, add the device to NMIS with Default model
# Then run discovery
./model_tool.pl node=mydevice model=MyCustomDevice debug=true
This generates:
  • models-custom/Model-MyCustomDevice.nmis
  • Console output showing supported MIBs
  • Common models to include

Step 2: Basic Model Template

# models-custom/Model-MyCustomDevice.nmis
%hash = (
  'system' => {
    'nodeModel' => 'MyCustomDevice',
    'nodeVendor' => 'VendorName',
    'nodetype' => 'router',  # or switch, firewall, server, generic
    'sys' => {
      'standard' => {
        'snmp' => {
          'sysLocation' => {
            'oid' => 'sysLocation',
            'title' => 'Location'
          },
          'sysDescr' => {
            'oid' => 'sysDescr',
            'title' => 'Description'
          },
          'sysObjectID' => {
            'oid' => 'sysObjectID'
          },
          'sysContact' => {
            'oid' => 'sysContact',
            'title' => 'Contact'
          },
          'sysName' => {
            'oid' => 'sysName',
            'title' => 'System Name'
          },
          'sysUpTime' => {
            'oid' => 'sysUpTime',
            'title' => 'Uptime'
          }
        }
      }
    }
  },
  'interface' => {
    'sys' => {
      'standard' => {
        'indexed' => 'true',
        'snmp' => {
          'ifDescr' => {
            'oid' => 'ifDescr',
            'title' => 'Description'
          },
          'ifSpeed' => {
            'oid' => 'ifSpeed',
            'title' => 'Speed'
          },
          'ifAdminStatus' => {
            'oid' => 'ifAdminStatus',
            'title' => 'Admin Status'
          },
          'ifOperStatus' => {
            'oid' => 'ifOperStatus',
            'title' => 'Oper Status'
          }
        }
      }
    },
    'rrd' => {
      'interface' => {
        'indexed' => 'true',
        'graphtype' => 'bits,abits,maxbits,util,autil',
        'snmp' => {
          'ifInOctets' => {
            'oid' => 'ifInOctets',
            'option' => 'counter,0:U'
          },
          'ifOutOctets' => {
            'oid' => 'ifOutOctets',
            'option' => 'counter,0:U'
          }
        }
      }
    }
  },
  'systemHealth' => {
    'sections' => 'entityMib,temperature,fanStatus',
  },
  '-common-' => {
    'class' => {
      'entityMib' => {
        'common-model' => 'entityMib'
      }
    }
  }
);

Step 3: Add Custom SNMP Collection

Add device-specific monitoring:
'systemHealth' => {
  'sections' => 'myCustomMetric',
  'sys' => {
    'myCustomMetric' => {
      'indexed' => 'true',
      'index_oid' => '1.3.6.1.4.1.9999.1.2.1.1',
      'index_regex' => '\.(\d+)$',
      'headers' => 'index,metricName,metricValue',
      'snmp' => {
        'metricName' => {
          'oid' => '1.3.6.1.4.1.9999.1.2.1.2',
          'title' => 'Metric Name'
        },
        'metricValue' => {
          'oid' => '1.3.6.1.4.1.9999.1.2.1.3',
          'title' => 'Value'
        }
      }
    }
  },
  'rrd' => {
    'myCustomMetric' => {
      'indexed' => 'true',
      'graphtype' => 'myCustomGraph',
      'snmp' => {
        'metricValue' => {
          'oid' => '1.3.6.1.4.1.9999.1.2.1.3',
          'option' => 'gauge,0:U'
        }
      }
    }
  }
}

Step 4: Device Identification

Add autodiscovery to models-custom/Model.nmis:
# Get the Enterprise ID from sysObjectID
# Example: 1.3.6.1.4.1.9999 (9999 is Enterprise ID)

'9999' => {  # Your vendor's Enterprise ID
  'order' => {
    '10' => {
      'MyCustomDevice' => 'my vendor.*device model'
    }
  }
}
The regex matches against the device’s sysDescr.

Model Inheritance and Overrides

Including Common Models

'-common-' => {
  'class' => {
    'database' => {
      'common-model' => 'database'
    },
    'entityMib' => {
      'common-model' => 'entityMib'
    },
    'lldp' => {
      'common-model' => 'lldp'
    }
  }
}

Overriding Default Behavior

Copy the default model to models-custom/ and modify:
cp models-default/Model-CiscoRouter.nmis models-custom/Model-CiscoRouter.nmis
Then edit models-custom/Model-CiscoRouter.nmis to add/change sections.

Creating Common Models

Reusable monitoring components:
# models-custom/Common-MyFeature.nmis
%hash = (
  'systemHealth' => {
    'sys' => {
      'myFeature' => {
        'indexed' => 'myFeatureIndex',
        'headers' => 'featureName,featureStatus',
        'snmp' => {
          'featureName' => {
            'oid' => '1.3.6.1.4.1.9999.2.1.1.2',
            'title' => 'Feature Name'
          },
          'featureStatus' => {
            'oid' => '1.3.6.1.4.1.9999.2.1.1.3',
            'title' => 'Status',
            'replace' => {
              '1' => 'enabled',
              '2' => 'disabled'
            }
          }
        }
      }
    }
  }
);
Include in other models:
'-common-' => {
  'class' => {
    'MyFeature' => {
      'common-model' => 'MyFeature'
    }
  }
}

Advanced Features

Value Calculations

Transform SNMP values:
'memoryUsed' => {
  'oid' => 'memoryTotal',
  'calculate' => 'CVAR1=memoryFree; return $r - $CVAR1;'
}

Control Statements

Conditionally collect data:
'env-temp' => {
  'control' => 'CVAR=tempType;$CVAR =~ /celsius/',
  'snmp' => {
    'currentTemp' => {
      'oid' => 'entSensorValue'
    }
  }
}

Value Replacement

Map numeric values to strings:
'interfaceStatus' => {
  'oid' => 'ifOperStatus',
  'replace' => {
    '1' => 'up',
    '2' => 'down',
    '3' => 'testing',
    '4' => 'unknown',
    '5' => 'dormant',
    '6' => 'notPresent',
    '7' => 'lowerLayerDown'
  }
}

Alert Thresholds

'alerts' => {
  'myCustomMetric' => {
    'highValue' => {
      'type' => 'threshold-rising',
      'threshold' => {
        'Warning' => '80',
        'Minor' => '85',
        'Major' => '90',
        'Critical' => '95',
        'Fatal' => '100'
      },
      'element' => 'metricName',
      'event' => 'High Metric Value',
      'level' => 'Major',
      'unit' => '%',
      'value' => 'CVAR1=metricValue;int($CVAR1)'
    }
  }
}

Model Testing

Syntax Check

./model_tool.pl check=true errors=true

Test SNMP Collection

# Update the device to use your model
# Then run a manual collect
/usr/local/nmis9/bin/nmis-cli act=collect node=mydevice debug=true

Check RRD Creation

ls -la /usr/local/nmis9/var/nmis/mydevice/health/
ls -la /usr/local/nmis9/var/nmis/mydevice/interface/

Verify in GUI

  1. Navigate to node view
  2. Check System Health sections
  3. Verify graphs are generated
  4. Check for alerts

Common Model Structure

%hash = (
  # Device identification and basic info
  'system' => {
    'nodeModel' => 'ModelName',
    'nodeVendor' => 'VendorName',
    'nodetype' => 'router|switch|firewall|server|generic',
    'sys' => {
      'standard' => {
        'snmp' => {
          # System MIB-II variables
        }
      }
    }
  },
  
  # Interface statistics
  'interface' => {
    'sys' => {
      'standard' => {
        'indexed' => 'true',
        'snmp' => {
          # Interface MIB variables (ifTable, ifXTable)
        }
      }
    },
    'rrd' => {
      'interface' => {
        'indexed' => 'true',
        'graphtype' => 'bits,abits,maxbits,util,autil',
        'snmp' => {
          # Time-series interface metrics
        }
      }
    }
  },
  
  # Device health metrics
  'systemHealth' => {
    'sections' => 'section1,section2,section3',
    'sys' => {
      'mySection' => {
        'indexed' => 'indexOID',
        'index_oid' => '1.3.6.1.x.x.x',
        'index_regex' => '\\.(\\d+)$',
        'headers' => 'col1,col2,col3',
        'snmp' => {
          # Display values in GUI
        }
      }
    },
    'rrd' => {
      'mySection' => {
        'indexed' => 'true',
        'graphtype' => 'myGraph',
        'threshold' => 'myThreshold',
        'control' => 'CVAR=var;$CVAR > 0',
        'snmp' => {
          # Store values in RRD
        }
      }
    }
  },
  
  # Alert definitions
  'alerts' => {
    'mySection' => {
      'myAlert' => {
        'type' => 'test|threshold-rising|threshold-falling',
        'threshold' => {
          'Warning' => 'value',
          'Minor' => 'value',
          'Major' => 'value',
          'Critical' => 'value',
          'Fatal' => 'value'
        },
        'element' => 'columnName',
        'event' => 'Event Description',
        'level' => 'Major',
        'test' => 'CVAR1=var;$CVAR1 > 10',
        'value' => 'CVAR1=var;int($CVAR1)',
        'unit' => 'units'
      }
    }
  },
  
  # RRD database definitions
  'database' => {
    'type' => {
      'mySection' => '/nodes/$node/health/mySection-$index.rrd'
    }
  },
  
  # Graph headings
  'heading' => {
    'graphtype' => {
      'myGraph' => 'My Graph Title'
    }
  },
  
  # Include common models
  '-common-' => {
    'class' => {
      'CommonModel' => {
        'common-model' => 'CommonModel'
      }
    }
  }
);

SNMP Data Types

Counter Types

# Counter32 - 32-bit counter (wraps at 2^32)
'ifInOctets' => {
  'oid' => 'ifInOctets',
  'option' => 'counter,0:U'
}

# Counter64 - 64-bit counter
'ifHCInOctets' => {
  'oid' => 'ifHCInOctets',
  'option' => 'counter,0:U'
}

Gauge Types

# Gauge32 - Instantaneous value
'cpuLoad' => {
  'oid' => 'cpuLoad',
  'option' => 'gauge,0:100'
}

RRD Options

  • counter,min:max - Counter that increments (calculates rate)
  • gauge,min:max - Instantaneous value
  • derive,min:max - Rate of change
  • nosave - Don’t store in RRD (display only)
  • 0:U - Min 0, max unlimited
  • 0:100 - Range 0-100

Enterprise ID Registration

If monitoring a new vendor:
  1. Find Enterprise ID from sysObjectID:
    1.3.6.1.4.1.9999.x.x.x
                 ^^^^ Enterprise ID
    
  2. Look up vendor at: https://www.iana.org/assignments/enterprise-numbers/
  3. Add to conf/Enterprise.nmis:
    '9999' => {
      'Enterprise' => 'Vendor Name',
      'OID' => '9999'
    }
    

Troubleshooting

Model Not Loading

# Check syntax errors
./model_tool.pl check=true

# Verify model file permissions
ls -la models-custom/Model-MyDevice.nmis

# Check NMIS logs
tail -f /usr/local/nmis9/logs/nmis.log

SNMP Collection Failing

# Test SNMP manually
snmpwalk -v2c -c public devicename 1.3.6.1.4.1.9999

# Check OID resolution
grep "1.3.6.1.4.1.9999" /usr/local/nmis9/mibs/nmis_mibs.oid

# Run collection with debug
/usr/local/nmis9/bin/nmis-cli act=collect node=mydevice debug=9

No Graphs Displayed

  • Verify graphtype exists in models-default/Graph-*.nmis
  • Check RRD file was created
  • Ensure data type (counter/gauge) is correct
  • Wait for 2+ collection cycles

Best Practices

  1. Start with model_tool.pl - Discover supported MIBs first
  2. Use Common models - Reuse existing monitoring definitions
  3. Document your models - Add comments explaining custom sections
  4. Test incrementally - Add one section at a time
  5. Version control - Keep custom models in git
  6. Follow naming conventions - Model-Vendor-Model.nmis
  7. Set proper thresholds - Tune alert levels for your environment
  8. Use control statements - Avoid collecting invalid data

Next Steps

Build docs developers (and LLMs) love