SME Monitoring System
Agentless, dual-protocol (SNMP v2c + IPMI RMCP+) real-time monitoring for SME PCs and servers, delivered as a Docker Compose stack.
Features
- Real-time monitoring via Socket.IO push notifications
- Multi-protocol support:
- Ping (ICMP) for availability and latency
- SNMP v2c for Windows/Linux/Firewall/Switch metrics
- IPMI RMCP+ for hardware sensors (fan, power, temperature)
- Device types: Windows, Linux, Firewall, Switch, BMC
- Vendor-specific OIDs for Firewalls (FortiGate, Cisco, Huawei) and Servers (HPE, Dell, Lenovo)
- Session-based authentication with Flask-Login and bcrypt password hashing
- Network scanning for device discovery
- Dark theme dashboard using AdminLTE 3.2
- Thread-safe SQLite database with automatic metric retention cleanup
- Async polling engine with concurrent device polling
Architecture
┌─────────────────────────────────────────────────────┐
│ Docker Host (VPS) │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Nginx │◄──────►│ Flask Dashboard │ │
│ │ (Port 80) │ │ (Port 5000) │ │
│ └──────────────┘ └──────────┬───────────┘ │
│ │ │
│ ┌──────▼───────┐ │
│ │ Polling Engine│ │
│ │ Ping+SNMP+IPMI│ │
│ └──────┬───────┘ │
│ │ │
│ ┌──────▼───────┐ │
│ │ SQLite DB │ │
│ └───────────────┘ │
└─────────────────────────────────────────────────────┘
Technology Stack
| Layer |
Choice |
Notes |
| Runtime |
Python 3.12.3 |
python:3.12.3-slim Docker image |
| Web Framework |
Flask + Flask-SocketIO |
Socket.IO transport for real-time push |
| Async Runtime |
asyncio |
Used inside polling modules |
| SNMP |
Net-SNMP tools |
snmpget, snmpwalk via subprocess |
| IPMI |
pyghmi |
RMCP+ SDR sensor reads |
| Database |
SQLite via sqlite3 stdlib |
Thread-safe via threading.Lock() |
| Frontend |
AdminLTE 3.2 (dark theme) |
CDN-loaded; no Node.js build step |
| Reverse Proxy |
Nginx (stable-alpine) |
HTTP proxy + WebSocket support |
| Container |
Docker Compose v2 |
Services: dashboard, nginx |
| Config |
config.yaml + env vars |
YAML parsed by PyYAML |
Quick Start
Local Development
For local development and review (without Docker):
# 1. Create and activate virtual environment
python3 -m venv .venv
source .venv/bin/activate # Linux/Mac
# .venv\Scripts\activate # Windows
# 2. Install dependencies
pip install -r requirements.txt
# 3. Run the development server
python run.py
# Or with custom options
python run.py --port 8080 # Custom port
python run.py --host 0.0.0.0 # Accessible from network
python run.py --no-polling # Disable background polling (faster for review)
python run.py --help # Show all options
The server will start at http://127.0.0.1:5000 with:
- Username:
admin
- Password:
admin
Note: Make sure you're using the virtual environment (source .venv/bin/activate) before running the server.
Production Deployment with Docker
1. Prerequisites
- Docker Engine 20.10+
- Docker Compose v2
- WireGuard VPN (for remote device access)
2. Clone and Setup
# Clone the repository
git clone <repository-url>
cd sme-monitoring
# Create required directories
mkdir -p data config logs
# Set up environment variables (optional)
cp .env.example .env # if exists
# Or set directly in docker-compose.yml
3. Start the System
# Build and start containers
docker compose up -d
# View logs
docker compose logs -f
4. Access the Dashboard
Open your browser and navigate to: http://your-server-ip:8080
Default credentials:
- Username:
admin
- Password:
admin
⚠️ Change the default password immediately!
Configuration
Environment Variables
| Variable |
Default |
Description |
SECRET_KEY |
change-me-in-production |
Flask secret key for session encryption |
DASHBOARD_USERNAME |
admin |
Default admin username |
DASHBOARD_PASSWORD |
admin |
Default admin password (plaintext, hashed on startup) |
POLL_INTERVAL |
10 |
Polling interval in seconds |
POLL_TIMEOUT |
5 |
SNMP/IPMI timeout in seconds |
POLL_RETRIES |
2 |
Number of retry attempts for SNMP/IPMI |
PING_TIMEOUT |
2 |
Ping timeout in seconds |
PING_PACKETS |
1 |
Number of ping packets to send |
DB_PATH |
/app/data/monitoring.db |
SQLite database path |
CONFIG_YAML_PATH |
/app/config/config.yaml |
Device configuration YAML path |
LOG_LEVEL |
INFO |
Logging level (DEBUG/INFO/WARNING/ERROR) |
SESSION_DURATION_HOURS |
12 |
Session timeout in hours |
CLEANUP_DAYS |
30 |
Metric retention period in days |
Adding Devices
Devices can be added via the web UI or API:
Via Web UI
- Log in to the dashboard
- Navigate to Devices page
- Click Add Device
- Fill in device details:
- Name: Display name
- IP: IP address (via WireGuard tunnel or network)
- Type: Windows/Linux/Firewall/Switch/BMC
- Vendor: (for Firewalls/Switches)
- SNMP Settings: Community string (default: public)
- IPMI Settings: (optional) BMC credentials
Via API
curl -X POST http://localhost:8080/api/devices \
-H "Content-Type: application/json" \
-u admin:admin \
-d '{
"name": "My Server",
"ip": "10.0.0.10",
"device_type": "linux",
"snmp_enabled": true,
"snmp_community": "public",
"ipmi_enabled": false
}'
Network Scanning
- Click Scan Network on the Devices page
- Enter network range (e.g.,
10.0.0.0/24)
- Enter SNMP community string
- Click Start Scan
- Add discovered devices with one click
Note: Network scans are limited to /16 prefix to prevent accidental large scans.
Supported Metrics
Ping (All Devices)
| Metric |
Description |
| Online/Offline |
Device reachability status |
| Latency (RTT) |
Round-trip time in milliseconds |
SNMP Metrics
| Category |
Windows |
Linux |
Firewall |
Switch |
| CPU Usage |
✅ hrProcessorLoad |
✅ UCD-SNMP |
⚠️ Vendor-specific |
⚠️ Vendor-specific |
| Memory Usage |
✅ hrMemorySize |
✅ memTotalReal |
⚠️ Vendor-specific |
⚠️ Vendor-specific |
| Hostname |
✅ sysName |
✅ sysName |
✅ sysName |
✅ sysName |
| Uptime |
✅ hrSystemUptime |
✅ sysUpTime |
✅ sysUpTime |
✅ sysUpTime |
| MAC Addresses |
✅ ifPhysAddress |
✅ ifPhysAddress |
✅ ifPhysAddress |
✅ ifPhysAddress |
Vendor-Specific OIDs
| Vendor |
CPU OID |
Memory OID |
| FortiGate |
fgSysCpuUsage |
fgSysMemUsage |
| Cisco |
ciscoMemoryPool |
memPoolUsed |
| Huawei |
Vendor-specific |
Vendor-specific |
Server Hardware OIDs
| Vendor |
Fan OID |
Power OID |
| HPE |
probesAndSensors |
probesAndSensors |
| Dell |
oem Dell Mgmt |
oem Dell Mgmt |
| Lenovo |
lmFanSensors |
lmFanSensors |
IPMI Metrics (when enabled)
| Metric |
Description |
| CPU Temperature |
Processor temperature in °C |
| Board Temperature |
System/board temperature in °C |
| Fan Speed (RPM) |
Fan rotation speed |
| Power Consumption |
Power draw in Watts |
Project Structure
sme-monitoring/
├── app.py # Main entry point (production)
├── run.py # Development server launcher
├── app/
│ └── __init__.py # Flask app factory, Socket.IO setup
├── api/
│ ├── __init__.py
│ ├── devices.py # Device CRUD API + web routes
│ ├── metrics.py # Metrics API
│ └── dashboard.py # Dashboard data API
├── auth/
│ └── __init__.py # Authentication, Flask-Login setup
├── config/
│ └── config.yaml # Device configuration (optional)
├── config.py # Configuration loader (env vars + YAML)
├── database.py # SQLite database layer
├── models/
│ ├── __init__.py
│ ├── device.py # Device dataclass
│ └── user.py # User dataclass
├── polling/
│ ├── __init__.py
│ ├── ping_poller.py # ICMP ping implementation
│ ├── snmp_poller.py # SNMP v2c polling
│ ├── ipmi_poller.py # IPMI RMCP+ polling
│ └── scheduler.py # Polling cycle coordinator
├── templates/
│ ├── index.html # Login page
│ ├── dashboard.html # Main dashboard
│ ├── devices.html # Device management
│ └── user.html # User settings
├── data/ # SQLite database (runtime)
├── logs/ # Log files (runtime)
├── certs/ # TLS certificates (optional)
├── Dockerfile
├── docker-compose.yml
├── nginx.conf
├── requirements.txt
└── README.md
API Endpoints
All API endpoints require authentication (Basic Auth or session).
Authentication
| Method |
Endpoint |
Description |
| GET |
/ |
Login page |
| POST |
/api/auth/login |
Authenticate |
| POST |
/api/auth/logout |
Logout |
| POST |
/api/auth/change-password |
Change password |
Dashboard
| Method |
Endpoint |
Description |
| GET |
/dashboard |
Dashboard page |
| GET |
/api/devices-status |
Get all devices with latest metrics |
Devices
| Method |
Endpoint |
Description |
| GET |
/devices |
Device management page |
| GET |
/api/devices |
List all devices |
| POST |
/api/devices |
Add device |
| GET |
/api/devices/<id> |
Get specific device |
| PUT |
/api/devices/<id> |
Update device |
| DELETE |
/api/devices/<id> |
Delete device |
| GET |
/api/devices/scan?range=<CIDR> |
Network scan |
Metrics
| Method |
Endpoint |
Description |
| GET |
/api/metrics/<id> |
Get device metrics (limit=50) |
| GET |
/api/metrics/<id>/latest |
Get latest device metrics |
System
| Method |
Endpoint |
Description |
| GET |
/health |
Health check endpoint |
Socket.IO Events
| Event |
Direction |
Description |
connect |
Client→Server |
Client connection established |
disconnect |
Client→Server |
Client disconnection |
metrics_update |
Server→Client |
Push notification when metrics updated |
Database Schema
Tables
users
| Column |
Type |
Description |
| id |
INTEGER |
Primary key, autoincrement |
| username |
TEXT |
Unique username |
| password_hash |
TEXT |
bcrypt hashed password |
| created_at |
TIMESTAMP |
Creation timestamp |
| updated_at |
TIMESTAMP |
Last update timestamp |
devices
| Column |
Type |
Description |
| id |
INTEGER |
Primary key, autoincrement |
| name |
TEXT |
Display name |
| ip |
TEXT |
IP address |
| device_type |
TEXT |
Type (windows, linux, firewall, switch, bmc) |
| vendor |
TEXT |
Vendor name (for firewall/switch) |
| snmp_enabled |
BOOLEAN |
Enable SNMP polling |
| snmp_community |
TEXT |
SNMP community string |
| snmp_version |
TEXT |
SNMP version (default: 2c) |
| ipmi_enabled |
BOOLEAN |
Enable IPMI polling |
| ipmi_username |
TEXT |
IPMI username |
| ipmi_password |
TEXT |
IPMI password |
| status |
TEXT |
Status (online, offline, unknown) |
| last_seen |
TIMESTAMP |
Last seen timestamp |
| created_at |
TIMESTAMP |
Creation timestamp |
metrics
| Column |
Type |
Description |
| id |
INTEGER |
Primary key, autoincrement |
| device_id |
INTEGER |
Foreign key to devices |
| timestamp |
TIMESTAMP |
Measurement timestamp |
| ping_ok |
BOOLEAN |
Ping success status |
| latency_ms |
REAL |
Ping latency in ms |
| device_name |
TEXT |
Hostname from SNMP |
| sys_up_time |
TEXT |
System uptime string |
| cpu_model |
TEXT |
CPU model |
| mac_addresses |
TEXT |
JSON string of MAC addresses |
| cpu_percent |
REAL |
CPU usage percentage |
| mem_total_kb |
REAL |
Total memory in KB |
| mem_percent |
REAL |
Memory usage percentage |
| fan_speed_rpm |
REAL |
Fan speed RPM |
| power_watts |
REAL |
Power consumption in Watts |
| fdb_mac_port_map |
TEXT |
JSON string of MAC-to-port mapping |
Docker Configuration
Dockerfile
The Dockerfile creates a production-ready container:
- Base image:
python:3.12.3-slim
- Non-root user (
appuser, UID 1000)
- Health check every 30 seconds
- Installs curl and iputils-ping for network utilities
- Exposes port 5000
Docker Compose Services
dashboard
- Flask application service
- Environment variables for configuration
- Volume mounts for data persistence
- Health check for service monitoring
nginx
- Reverse proxy on port 80
- WebSocket proxy for Socket.IO
- Depends on healthy dashboard service
Nginx Configuration
- Proxies all requests to Flask on
dashboard:5000
- Special handling for
/socket.io/ with WebSocket upgrade
- Disabled buffering for real-time communication
- Long timeouts (86400s) for persistent connections
Troubleshooting
Devices showing as offline
- Verify network connectivity:
ping <device-ip>
- Check device IP is reachable
- Verify SNMP is enabled on device
- Check firewall allows UDP port 161
SNMP polling errors
- Verify community string is correct
- Check SNMP service is running on target
- Review logs:
docker compose logs dashboard
- Test manually:
snmpget -v2c -c public <device-ip> 1.3.6.1.2.1.1.5.0
IPMI polling errors
- Verify BMC is reachable on port 623
- Check IPMI credentials are correct
- Review logs:
docker compose logs dashboard
- Test manually:
ipmi-raw -H <bmc-ip> -U <user> -P <pass> 0x06 0x20
Socket.IO connection issues
- Check nginx configuration
- Verify WebSocket proxy settings
- Check browser console for errors
- Ensure nginx is running and proxying correctly
Database locked errors
- Check if multiple instances are running
- Verify file permissions on data directory
- Increase
DB_TIMEOUT in environment if needed
Security Notes
- Change default admin credentials immediately after deployment
- Use strong passwords (minimum 8 characters, recommend 12+)
- Deploy behind TLS/SSL in production (add SSL termination to nginx)
- SNMP community strings are stored in the database (TODO: encryption)
- IPMI credentials are stored in the database (TODO: encryption)
- Restrict network access to the dashboard port
- Regularly update the system and dependencies
- Monitor logs for suspicious activity
Security TODOs
Development
Running Tests
# Run pytest
pytest tests/
# Run with coverage
pytest --cov=. tests/
Code Style
The project follows PEP 8 standards. Use ruff for linting:
ruff check .
ruff format .
Adding a New Device Type
- Add the device type to
models/device.py DeviceType enum
- Add OID mappings in
config.py DEVICE_TYPE_OIDS
- Update
polling/snmp_poller.py with type-specific polling logic
- Update dashboard template