System Architecture
NMIS 9 is a distributed network management system with a modular architecture:
Core Components
NMIS Daemon (nmisd)
The central polling and data collection engine.
Location: /usr/local/nmis9/bin/nmisd
Responsibilities:
- SNMP polling of network devices
- Data collection and storage
- Threshold evaluation
- Event generation
- Job queue management
Process Management:
# Start daemon
systemctl start nmisd
# Check status
systemctl status nmisd
# View process
ps aux | grep nmisd
The daemon runs continuously and spawns worker processes for polling:
# From nmisd source
my $config = NMISNG::Util::loadConfTable();
my $nmisng = NMISNG->new(
config => $config,
log => $log
);
The daemon automatically restarts failed workers and manages resource limits.
Web Interface (nmisx)
Mojolicious-based web application providing the user interface.
Location: /usr/local/nmis9/script/nmisx
Port: 8080 (default)
Features:
- Real-time device dashboards
- Performance graphs
- Event management
- Configuration interface
- RESTful API
Startup:
# Production mode
./nmisx daemon -m production -l http://*:8080
# Development mode with auto-reload
./nmisx daemon -m development -l http://*:8080
Technology Stack:
- Framework: Mojolicious 8.11+
- Template Engine: Embedded Perl (EP)
- JavaScript: jQuery, Chart.js
- CSS: Bootstrap-based custom theme
Database (MongoDB)
Document database storing configuration and dynamic data.
Version: 6.0 or 7.0
Collections:
| Collection | Purpose | Typical Size |
|---|
| nodes | Node configuration | ~50 KB per node |
| inventory | Interface and component inventory | ~10 KB per interface |
| events | Active and historical events | ~2 KB per event |
| opstatus | Operational status cache | ~5 KB per node |
| queue | Job queue | Variable |
| logs | Activity logs | Variable |
Connection:
# From NMISNG::DB
my $client = MongoDB::MongoClient->new(
host => "mongodb://localhost:27017",
username => $config->{db_username},
password => $config->{db_password},
auth_mechanism => 'SCRAM-SHA-256'
);
Indexes:
NMIS creates indexes automatically on startup:
// nodes collection
db.nodes.createIndex({"name": 1}, {unique: true})
db.nodes.createIndex({"cluster_id": 1, "name": 1})
db.nodes.createIndex({"group": 1})
// inventory collection
db.inventory.createIndex({"node_uuid": 1, "concept": 1})
db.inventory.createIndex({"cluster_id": 1, "enabled": 1})
// events collection
db.events.createIndex({"node_uuid": 1, "event": 1})
db.events.createIndex({"time": -1})
db.events.createIndex({"level": 1, "current": 1})
RRD Storage
Round-Robin Database files store time-series performance data.
Location: /usr/local/nmis9/var
Directory Structure:
var/
├── nmis_system/ # System-level RRDs
│ └── health/
├── router1/ # Per-node directories
│ ├── interface/ # Interface metrics
│ │ ├── GigabitEthernet0-0.rrd
│ │ └── GigabitEthernet0-1.rrd
│ ├── health/ # System health
│ │ ├── cpu.rrd
│ │ └── memory.rrd
│ └── pkts/ # Packet statistics
└── router2/
RRD Schema:
NMIS creates RRDs with multiple RRAs (Round-Robin Archives):
# From rrdfunc.pm
my @rra = (
"RRA:AVERAGE:0.5:1:600", # 5 min for 2 days
"RRA:AVERAGE:0.5:6:700", # 30 min for 14 days
"RRA:AVERAGE:0.5:24:775", # 2 hour for 2 months
"RRA:AVERAGE:0.5:288:1460", # 1 day for 4 years
"RRA:MAX:0.5:1:600",
"RRA:MAX:0.5:6:700",
"RRA:MAX:0.5:24:775",
"RRA:MAX:0.5:288:1460"
);
This provides:
- Raw data: 2 days at 5-minute resolution
- 30-minute averages: 14 days
- 2-hour averages: 2 months
- Daily averages: 4 years
Configuration Files
NMIS uses a hierarchical configuration system.
Main Config: /usr/local/nmis9/conf/Config.nmis
%hash = (
'system' => {
'db_name' => 'nmis',
'db_server' => 'localhost:27017',
'db_username' => 'nmis',
},
'directories' => {
'base' => '/usr/local/nmis9',
'var' => '/usr/local/nmis9/var',
'database' => '/usr/local/nmis9/database',
},
'polling' => {
'poll_interval' => 300, # 5 minutes
'max_threads' => 10,
}
);
Precedence:
conf/Config.nmis (custom)
conf-default/Config.nmis (defaults)
Custom configuration overrides defaults.
Data Flow
Polling Cycle
Step-by-Step:
-
Job Scheduling: Scheduler adds poll job to queue every 5 minutes (default)
-
Job Pickup:
nmisd worker picks up job from queue
-
SNMP Collection:
# Simplified polling code
my $snmp = Compat::NMIS::snmp_open(
host => $node->configuration->{host},
version => $node->configuration->{version},
community => $node->configuration->{community}
);
my $result = $snmp->get_request(-varbindlist => \@oids);
-
Data Storage:
- Performance metrics → RRD files
- Configuration/inventory → MongoDB
-
Threshold Evaluation:
if ($cpu_utilization > $threshold->{high}) {
$nmisng->events->eventAdd(
event => "High CPU",
level => "Major",
element => $node->name,
details => "CPU at $cpu_utilization%"
);
}
Web Request Flow
Authentication:
# From NMISNG::Auth
my $auth = NMISNG::Auth->new(config => $config);
my $user = $auth->authenticate(
username => $username,
password => $password
);
Authorization:
Privilege levels (0-5) control access:
- 5: View only
- 3: Operator (acknowledge events)
- 1: Administrator (full access)
- 0: Super admin
Event Flow
Event Lifecycle:
- Creation: Threshold exceeded or manual creation
- Storage: Written to
events collection
- Escalation: Matched against escalation policies
- Notification: Email, SMS, webhook, etc.
- Acknowledgment: User acknowledges event
- Closure: Event resolved or auto-closed
Distributed Architecture
NMIS supports distributed polling for large networks:
Roles:
-
Master Server:
- Central MongoDB database
- Web interface
- Event aggregation
- Reporting
-
Poller Nodes:
- SNMP polling only
- Local RRD storage
- Send inventory updates to master
- Can operate independently if master is unavailable
Configuration:
# Master server
'server' => {
'server_name' => 'master',
'server_role' => 'MASTER'
}
# Poller node
'server' => {
'server_name' => 'poller1',
'server_role' => 'POLLER',
'server_master' => 'master.example.com'
}
Process Management
Daemon Processes
# Check all NMIS processes
ps aux | grep nmis
# Typical output
nmis 1234 nmisd # Main daemon
nmis 1235 nmisd worker # Polling worker 1
nmis 1236 nmisd worker # Polling worker 2
nmis 1240 nmisx # Web interface
Resource Limits
NMIS respects system limits:
# From nmisd
my $max_workers = $config->{polling}->{max_threads} // 10;
my $memory_limit = $config->{system}->{memory_limit} // '2G';
Tuning:
# Config.nmis
'polling' => {
'max_threads' => 20, # Concurrent pollers
'max_repetitions' => 25, # SNMP GETBULK size
'snmp_timeout' => 5, # SNMP timeout (seconds)
'max_msg_size' => 65535, # Max SNMP message size
}
Scalability
Vertical Scaling
Single Server Capacity:
- Up to 5,000 devices with:
- 16 CPU cores
- 32 GB RAM
- SSD storage
- 5-minute polling interval
Bottlenecks:
- CPU: SNMP processing and RRD updates
- Disk I/O: RRD file writes
- Network: SNMP polling throughput
Horizontal Scaling
For > 5,000 devices:
- Add Poller Nodes: Each poller handles 2,000-5,000 devices
- Dedicated Database Server: Move MongoDB to dedicated hardware
- Load Balancer: Distribute web interface traffic
High Availability
Database Replication
MongoDB replica set:
# MongoDB replica set config
replication:
replSetName: nmis-rs
// Initialize replica set
rs.initiate({
_id: "nmis-rs",
members: [
{ _id: 0, host: "nmis1:27017" },
{ _id: 1, host: "nmis2:27017" },
{ _id: 2, host: "nmis3:27017" }
]
})
Daemon Failover
Use keepalived or Pacemaker for daemon failover:
# keepalived config
vrrp_script check_nmisd {
script "/usr/local/nmis9/bin/check_daemon.sh"
interval 10
}
Internal Metrics
NMIS monitors its own performance:
# View polling statistics
/usr/local/nmis9/admin/polling_summary9.pl
# Output
Total Polls: 1,234
Successful: 1,230 (99.7%)
Failed: 4 (0.3%)
Avg Duration: 12.3 seconds
Max Duration: 45.2 seconds
Database Statistics
// MongoDB performance
db.nodes.stats()
db.events.stats()
// Query profiling
db.setProfilingLevel(1, { slowms: 100 })
db.system.profile.find().limit(5).sort({ ts: -1 })
Security Architecture
Authentication Flow
Supported Methods:
- Local htpasswd file
- LDAP/Active Directory
- RADIUS
- TACACS+
- PAM
- Atlassian Crowd
- ConnectWise
Data Encryption
- SNMP: SNMPv3 with AES-256
- Web: HTTPS with TLS 1.2+
- Database: MongoDB encryption at rest
- Passwords: bcrypt hashing
See Also