Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
cf231e9785 | |||
edce110c8a | |||
5eefe8cf40 | |||
ecfd171f97 | |||
70c16fa0a6 | |||
7ef38cf961 |
20
changelog.md
20
changelog.md
@ -1,5 +1,25 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-03-25 - 2.0.1 - fix(cli/systemd)
|
||||||
|
Fix status command to pass debug flag and improve systemd status logging output
|
||||||
|
|
||||||
|
- ts/cli.ts: Now extracts debug options from process arguments and passes debug mode to getStatus.
|
||||||
|
- ts/systemd.ts: Updated getStatus to accept a debugMode parameter, enabling detailed SNMP debug logging, explicitly reloading configuration, and printing connection details.
|
||||||
|
|
||||||
|
## 2025-03-25 - 2.0.0 - BREAKING CHANGE(snmp)
|
||||||
|
refactor: update SNMP type definitions and interface names for consistency
|
||||||
|
|
||||||
|
- Renamed SnmpConfig to ISnmpConfig, OIDSet to IOidSet, UpsStatus to IUpsStatus, and UpsModel to TUpsModel in ts/snmp/types.ts.
|
||||||
|
- Updated internal references in ts/daemon.ts, ts/snmp/index.ts, ts/snmp/manager.ts, ts/snmp/oid-sets.ts, ts/snmp/packet-creator.ts, and ts/snmp/packet-parser.ts to use the new interface names.
|
||||||
|
|
||||||
|
## 2025-03-25 - 1.10.1 - fix(systemd/readme)
|
||||||
|
Improve README documentation and fix UPS status retrieval in systemd service
|
||||||
|
|
||||||
|
- Updated README features and installation instructions to clarify SNMP version support, UPS models, and configuration
|
||||||
|
- Modified default SNMP host to '192.168.1.100' and added 'upsModel' property in configuration examples
|
||||||
|
- Enhanced instructions for real-time log viewing and update process in README
|
||||||
|
- Fixed systemd.ts to use a test configuration with an appropriate timeout when fetching UPS status
|
||||||
|
|
||||||
## 2025-03-25 - 1.10.0 - feat(core)
|
## 2025-03-25 - 1.10.0 - feat(core)
|
||||||
Add update checking and version logging across startup components
|
Add update checking and version logging across startup components
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@serve.zone/nupst",
|
"name": "@serve.zone/nupst",
|
||||||
"version": "1.10.0",
|
"version": "2.0.1",
|
||||||
"description": "Node.js UPS Shutdown Tool for SNMP-enabled UPS devices",
|
"description": "Node.js UPS Shutdown Tool for SNMP-enabled UPS devices",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
75
readme.md
75
readme.md
@ -4,10 +4,14 @@ NUPST is a command-line tool that monitors SNMP-enabled UPS devices and initiate
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Monitors UPS devices using SNMP
|
- Monitors UPS devices using SNMP (v1, v2c, and v3 supported)
|
||||||
- Automatic shutdown when battery level falls below threshold
|
- Automatic shutdown when battery level falls below threshold
|
||||||
- Automatic shutdown when runtime remaining falls below threshold
|
- Automatic shutdown when runtime remaining falls below threshold
|
||||||
|
- Supports multiple UPS brands (CyberPower, APC, Eaton, TrippLite, Liebert/Vertiv)
|
||||||
- Simple systemd service integration
|
- Simple systemd service integration
|
||||||
|
- Regular status logging for monitoring
|
||||||
|
- Real-time log viewing with journalctl
|
||||||
|
- Version checking and automatic updates
|
||||||
- Self-contained - includes its own Node.js runtime
|
- Self-contained - includes its own Node.js runtime
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@ -66,12 +70,18 @@ Usage:
|
|||||||
nupst enable - Install and enable the systemd service (requires root)
|
nupst enable - Install and enable the systemd service (requires root)
|
||||||
nupst disable - Stop and uninstall the systemd service (requires root)
|
nupst disable - Stop and uninstall the systemd service (requires root)
|
||||||
nupst daemon-start - Start the daemon process directly
|
nupst daemon-start - Start the daemon process directly
|
||||||
nupst logs - Show logs of the systemd service
|
nupst logs - Show logs of the systemd service in real-time
|
||||||
nupst stop - Stop the systemd service
|
nupst stop - Stop the systemd service
|
||||||
nupst start - Start the systemd service
|
nupst start - Start the systemd service
|
||||||
nupst status - Show status of the systemd service and UPS status
|
nupst status - Show status of the systemd service and UPS status
|
||||||
nupst setup - Run the interactive setup to configure SNMP settings
|
nupst setup - Run the interactive setup to configure SNMP settings
|
||||||
|
nupst test - Test the current configuration by connecting to the UPS
|
||||||
|
nupst update - Update NUPST from repository and refresh systemd service (requires root)
|
||||||
nupst help - Show this help message
|
nupst help - Show this help message
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--debug, -d - Enable debug mode for detailed SNMP logging
|
||||||
|
(Example: nupst test --debug)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
@ -93,11 +103,12 @@ Alternatively, you can manually edit the configuration file at `/etc/nupst/confi
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"snmp": {
|
"snmp": {
|
||||||
"host": "127.0.0.1",
|
"host": "192.168.1.100",
|
||||||
"port": 161,
|
"port": 161,
|
||||||
"community": "public",
|
"community": "public",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"timeout": 5000
|
"timeout": 5000,
|
||||||
|
"upsModel": "cyberpower"
|
||||||
},
|
},
|
||||||
"thresholds": {
|
"thresholds": {
|
||||||
"battery": 60,
|
"battery": 60,
|
||||||
@ -112,6 +123,7 @@ Alternatively, you can manually edit the configuration file at `/etc/nupst/confi
|
|||||||
- `port`: SNMP port (default: 161)
|
- `port`: SNMP port (default: 161)
|
||||||
- `version`: SNMP version (1, 2, or 3)
|
- `version`: SNMP version (1, 2, or 3)
|
||||||
- `timeout`: Timeout in milliseconds (default: 5000)
|
- `timeout`: Timeout in milliseconds (default: 5000)
|
||||||
|
- `upsModel`: The UPS model ('cyberpower', 'apc', 'eaton', 'tripplite', 'liebert', or 'custom')
|
||||||
- For SNMPv1/v2c:
|
- For SNMPv1/v2c:
|
||||||
- `community`: SNMP community string (default: public)
|
- `community`: SNMP community string (default: public)
|
||||||
- For SNMPv3:
|
- For SNMPv3:
|
||||||
@ -121,6 +133,11 @@ Alternatively, you can manually edit the configuration file at `/etc/nupst/confi
|
|||||||
- `authKey`: Authentication password/key
|
- `authKey`: Authentication password/key
|
||||||
- `privProtocol`: Privacy/encryption protocol ('DES' or 'AES')
|
- `privProtocol`: Privacy/encryption protocol ('DES' or 'AES')
|
||||||
- `privKey`: Privacy password/key
|
- `privKey`: Privacy password/key
|
||||||
|
- For custom UPS models:
|
||||||
|
- `customOIDs`: Object containing custom OIDs for your UPS:
|
||||||
|
- `POWER_STATUS`: OID for power status
|
||||||
|
- `BATTERY_CAPACITY`: OID for battery capacity percentage
|
||||||
|
- `BATTERY_RUNTIME`: OID for runtime remaining in minutes
|
||||||
- `thresholds`: When to trigger shutdown
|
- `thresholds`: When to trigger shutdown
|
||||||
- `battery`: Battery percentage threshold (default: 60%)
|
- `battery`: Battery percentage threshold (default: 60%)
|
||||||
- `runtime`: Runtime minutes threshold (default: 20 minutes)
|
- `runtime`: Runtime minutes threshold (default: 20 minutes)
|
||||||
@ -141,6 +158,56 @@ To check the status:
|
|||||||
nupst status
|
nupst status
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To view logs in real-time:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nupst logs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Updating NUPST
|
||||||
|
|
||||||
|
NUPST checks for updates automatically and will notify you when an update is available. To update to the latest version:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nupst update
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
1. Pull the latest changes from the git repository
|
||||||
|
2. Run the installation scripts
|
||||||
|
3. Refresh the systemd service configuration
|
||||||
|
4. Restart the service if it was running
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
NUPST was designed with security in mind:
|
||||||
|
|
||||||
|
### Minimal Dependencies
|
||||||
|
|
||||||
|
- **Zero Runtime NPM Dependencies**: NUPST is built without any external NPM packages to minimize the attack surface and avoid supply chain risks.
|
||||||
|
- **Self-contained Node.js**: NUPST ships with its own Node.js binary, isolated from the system's Node.js installation. This ensures:
|
||||||
|
- No dependency on system Node.js versions
|
||||||
|
- Zero external libraries that could become compromised
|
||||||
|
- Consistent, tested environment for execution
|
||||||
|
- Reduced risk of dependency-based attacks
|
||||||
|
|
||||||
|
### Implementation Security
|
||||||
|
|
||||||
|
- **Privilege Separation**: Only specific commands that require elevated permissions (`enable`, `disable`, `update`) check for root access; all other functionality runs with minimal privileges.
|
||||||
|
- **Limited Network Access**: NUPST only communicates with the UPS device over SNMP and contacts npmjs.org only to check for updates.
|
||||||
|
- **Secure SNMPv3 Support**: Supports encrypted authentication and privacy for secure communication with the UPS device.
|
||||||
|
- **Isolated Execution**: The application runs in its working directory (`/opt/nupst`) or specified installation location, minimizing the impact on the rest of the system.
|
||||||
|
|
||||||
|
### Installation Security
|
||||||
|
|
||||||
|
- The installation script can be reviewed before execution (`curl -sSL [url] | less`)
|
||||||
|
- All setup scripts download only verified versions and check integrity
|
||||||
|
- Installation is transparent and places files in standard locations (`/opt/nupst`, `/usr/local/bin`, `/etc/systemd/system`)
|
||||||
|
|
||||||
|
### Audit and Review
|
||||||
|
|
||||||
|
The codebase is small, focused, and designed to be easily auditable. All code is open source and available for review.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT
|
MIT
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/nupst',
|
name: '@serve.zone/nupst',
|
||||||
version: '1.10.0',
|
version: '2.0.1',
|
||||||
description: 'Node.js UPS Shutdown Tool for SNMP-enabled UPS devices'
|
description: 'Node.js UPS Shutdown Tool for SNMP-enabled UPS devices'
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,9 @@ export class NupstCli {
|
|||||||
* Show status of the systemd service and UPS
|
* Show status of the systemd service and UPS
|
||||||
*/
|
*/
|
||||||
private async status(): Promise<void> {
|
private async status(): Promise<void> {
|
||||||
await this.nupst.getSystemd().getStatus();
|
// Extract debug options from args array
|
||||||
|
const debugOptions = this.extractDebugOptions(process.argv);
|
||||||
|
await this.nupst.getSystemd().getStatus(debugOptions.debugMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
16
ts/daemon.ts
16
ts/daemon.ts
@ -1,13 +1,13 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { NupstSnmp, type SnmpConfig } from './snmp.js';
|
import { NupstSnmp, type ISnmpConfig } from './snmp.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration interface for the daemon
|
* Configuration interface for the daemon
|
||||||
*/
|
*/
|
||||||
export interface NupstConfig {
|
export interface INupstConfig {
|
||||||
/** SNMP configuration settings */
|
/** SNMP configuration settings */
|
||||||
snmp: SnmpConfig;
|
snmp: ISnmpConfig;
|
||||||
/** Threshold settings for initiating shutdown */
|
/** Threshold settings for initiating shutdown */
|
||||||
thresholds: {
|
thresholds: {
|
||||||
/** Shutdown when battery below this percentage */
|
/** Shutdown when battery below this percentage */
|
||||||
@ -28,7 +28,7 @@ export class NupstDaemon {
|
|||||||
private readonly CONFIG_PATH = '/etc/nupst/config.json';
|
private readonly CONFIG_PATH = '/etc/nupst/config.json';
|
||||||
|
|
||||||
/** Default configuration */
|
/** Default configuration */
|
||||||
private readonly DEFAULT_CONFIG: NupstConfig = {
|
private readonly DEFAULT_CONFIG: INupstConfig = {
|
||||||
snmp: {
|
snmp: {
|
||||||
host: '127.0.0.1',
|
host: '127.0.0.1',
|
||||||
port: 161,
|
port: 161,
|
||||||
@ -52,7 +52,7 @@ export class NupstDaemon {
|
|||||||
checkInterval: 30000, // Check every 30 seconds
|
checkInterval: 30000, // Check every 30 seconds
|
||||||
};
|
};
|
||||||
|
|
||||||
private config: NupstConfig;
|
private config: INupstConfig;
|
||||||
private snmp: NupstSnmp;
|
private snmp: NupstSnmp;
|
||||||
private isRunning: boolean = false;
|
private isRunning: boolean = false;
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ export class NupstDaemon {
|
|||||||
* Load configuration from file
|
* Load configuration from file
|
||||||
* @throws Error if configuration file doesn't exist
|
* @throws Error if configuration file doesn't exist
|
||||||
*/
|
*/
|
||||||
public async loadConfig(): Promise<NupstConfig> {
|
public async loadConfig(): Promise<INupstConfig> {
|
||||||
try {
|
try {
|
||||||
// Check if config file exists
|
// Check if config file exists
|
||||||
const configExists = fs.existsSync(this.CONFIG_PATH);
|
const configExists = fs.existsSync(this.CONFIG_PATH);
|
||||||
@ -95,7 +95,7 @@ export class NupstDaemon {
|
|||||||
/**
|
/**
|
||||||
* Save configuration to file
|
* Save configuration to file
|
||||||
*/
|
*/
|
||||||
public async saveConfig(config: NupstConfig): Promise<void> {
|
public async saveConfig(config: INupstConfig): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const configDir = path.dirname(this.CONFIG_PATH);
|
const configDir = path.dirname(this.CONFIG_PATH);
|
||||||
if (!fs.existsSync(configDir)) {
|
if (!fs.existsSync(configDir)) {
|
||||||
@ -125,7 +125,7 @@ export class NupstDaemon {
|
|||||||
/**
|
/**
|
||||||
* Get the current configuration
|
* Get the current configuration
|
||||||
*/
|
*/
|
||||||
public getConfig(): NupstConfig {
|
public getConfig(): INupstConfig {
|
||||||
return this.config;
|
return this.config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Re-export all public types
|
// Re-export all public types
|
||||||
export type { UpsStatus, OIDSet, UpsModel, SnmpConfig } from './types.js';
|
export type { IUpsStatus, IOidSet, TUpsModel, ISnmpConfig } from './types.js';
|
||||||
|
|
||||||
// Re-export the SNMP manager class
|
// Re-export the SNMP manager class
|
||||||
export { NupstSnmp } from './manager.js';
|
export { NupstSnmp } from './manager.js';
|
@ -1,7 +1,7 @@
|
|||||||
import { exec } from 'child_process';
|
import { exec } from 'child_process';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import * as dgram from 'dgram';
|
import * as dgram from 'dgram';
|
||||||
import type { OIDSet, SnmpConfig, UpsModel, UpsStatus } from './types.js';
|
import type { IOidSet, ISnmpConfig, TUpsModel, IUpsStatus } from './types.js';
|
||||||
import { UpsOidSets } from './oid-sets.js';
|
import { UpsOidSets } from './oid-sets.js';
|
||||||
import { SnmpPacketCreator } from './packet-creator.js';
|
import { SnmpPacketCreator } from './packet-creator.js';
|
||||||
import { SnmpPacketParser } from './packet-parser.js';
|
import { SnmpPacketParser } from './packet-parser.js';
|
||||||
@ -14,12 +14,12 @@ const execAsync = promisify(exec);
|
|||||||
*/
|
*/
|
||||||
export class NupstSnmp {
|
export class NupstSnmp {
|
||||||
// Active OID set
|
// Active OID set
|
||||||
private activeOIDs: OIDSet;
|
private activeOIDs: IOidSet;
|
||||||
// Reference to the parent Nupst instance
|
// Reference to the parent Nupst instance
|
||||||
private nupst: any; // Type 'any' to avoid circular dependency
|
private nupst: any; // Type 'any' to avoid circular dependency
|
||||||
|
|
||||||
// Default SNMP configuration
|
// Default SNMP configuration
|
||||||
private readonly DEFAULT_CONFIG: SnmpConfig = {
|
private readonly DEFAULT_CONFIG: ISnmpConfig = {
|
||||||
host: '127.0.0.1', // Default to localhost
|
host: '127.0.0.1', // Default to localhost
|
||||||
port: 161, // Default SNMP port
|
port: 161, // Default SNMP port
|
||||||
community: 'public', // Default community string for v1/v2c
|
community: 'public', // Default community string for v1/v2c
|
||||||
@ -64,7 +64,7 @@ export class NupstSnmp {
|
|||||||
* Set active OID set based on UPS model
|
* Set active OID set based on UPS model
|
||||||
* @param config SNMP configuration
|
* @param config SNMP configuration
|
||||||
*/
|
*/
|
||||||
private setActiveOIDs(config: SnmpConfig): void {
|
private setActiveOIDs(config: ISnmpConfig): void {
|
||||||
// If custom OIDs are provided, use them
|
// If custom OIDs are provided, use them
|
||||||
if (config.upsModel === 'custom' && config.customOIDs) {
|
if (config.upsModel === 'custom' && config.customOIDs) {
|
||||||
this.activeOIDs = config.customOIDs;
|
this.activeOIDs = config.customOIDs;
|
||||||
@ -206,7 +206,7 @@ export class NupstSnmp {
|
|||||||
* @param config SNMP configuration
|
* @param config SNMP configuration
|
||||||
* @returns Promise resolving to the UPS status
|
* @returns Promise resolving to the UPS status
|
||||||
*/
|
*/
|
||||||
public async getUpsStatus(config = this.DEFAULT_CONFIG): Promise<UpsStatus> {
|
public async getUpsStatus(config = this.DEFAULT_CONFIG): Promise<IUpsStatus> {
|
||||||
try {
|
try {
|
||||||
// Set active OID set based on UPS model in config
|
// Set active OID set based on UPS model in config
|
||||||
this.setActiveOIDs(config);
|
this.setActiveOIDs(config);
|
||||||
@ -408,12 +408,12 @@ export class NupstSnmp {
|
|||||||
* @param config SNMP configuration
|
* @param config SNMP configuration
|
||||||
* @returns Promise resolving to the discovered engine ID
|
* @returns Promise resolving to the discovered engine ID
|
||||||
*/
|
*/
|
||||||
public async discoverEngineId(config: SnmpConfig): Promise<Buffer> {
|
public async discoverEngineId(config: ISnmpConfig): Promise<Buffer> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const socket = dgram.createSocket('udp4');
|
const socket = dgram.createSocket('udp4');
|
||||||
|
|
||||||
// Create a proper discovery message (SNMPv3 with noAuthNoPriv)
|
// Create a proper discovery message (SNMPv3 with noAuthNoPriv)
|
||||||
const discoveryConfig: SnmpConfig = {
|
const discoveryConfig: ISnmpConfig = {
|
||||||
...config,
|
...config,
|
||||||
securityLevel: 'noAuthNoPriv',
|
securityLevel: 'noAuthNoPriv',
|
||||||
username: '', // Empty username for discovery
|
username: '', // Empty username for discovery
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { OIDSet, UpsModel } from './types.js';
|
import type { IOidSet, TUpsModel } from './types.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OID sets for different UPS models
|
* OID sets for different UPS models
|
||||||
@ -8,7 +8,7 @@ export class UpsOidSets {
|
|||||||
/**
|
/**
|
||||||
* OID sets for different UPS models
|
* OID sets for different UPS models
|
||||||
*/
|
*/
|
||||||
private static readonly UPS_OID_SETS: Record<UpsModel, OIDSet> = {
|
private static readonly UPS_OID_SETS: Record<TUpsModel, IOidSet> = {
|
||||||
// Cyberpower OIDs for RMCARD205 (based on CyberPower_MIB_v2.11)
|
// Cyberpower OIDs for RMCARD205 (based on CyberPower_MIB_v2.11)
|
||||||
cyberpower: {
|
cyberpower: {
|
||||||
POWER_STATUS: '1.3.6.1.4.1.3808.1.1.1.4.1.1.0', // upsBaseOutputStatus (2=online, 3=on battery)
|
POWER_STATUS: '1.3.6.1.4.1.3808.1.1.1.4.1.1.0', // upsBaseOutputStatus (2=online, 3=on battery)
|
||||||
@ -57,7 +57,7 @@ export class UpsOidSets {
|
|||||||
* @param model UPS model name
|
* @param model UPS model name
|
||||||
* @returns OID set for the model
|
* @returns OID set for the model
|
||||||
*/
|
*/
|
||||||
public static getOidSet(model: UpsModel): OIDSet {
|
public static getOidSet(model: TUpsModel): IOidSet {
|
||||||
return this.UPS_OID_SETS[model];
|
return this.UPS_OID_SETS[model];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
import type { SnmpConfig, SnmpV3SecurityParams } from './types.js';
|
import type { ISnmpConfig, ISnmpV3SecurityParams } from './types.js';
|
||||||
import { SnmpEncoder } from './encoder.js';
|
import { SnmpEncoder } from './encoder.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,7 +118,7 @@ export class SnmpPacketCreator {
|
|||||||
*/
|
*/
|
||||||
public static createSnmpV3GetRequest(
|
public static createSnmpV3GetRequest(
|
||||||
oid: string,
|
oid: string,
|
||||||
config: SnmpConfig,
|
config: ISnmpConfig,
|
||||||
engineID: Buffer,
|
engineID: Buffer,
|
||||||
engineBoots: number,
|
engineBoots: number,
|
||||||
engineTime: number,
|
engineTime: number,
|
||||||
@ -145,7 +145,7 @@ export class SnmpPacketCreator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create security parameters
|
// Create security parameters
|
||||||
const securityParams: SnmpV3SecurityParams = {
|
const securityParams: ISnmpV3SecurityParams = {
|
||||||
msgAuthoritativeEngineID: engineID,
|
msgAuthoritativeEngineID: engineID,
|
||||||
msgAuthoritativeEngineBoots: engineBoots,
|
msgAuthoritativeEngineBoots: engineBoots,
|
||||||
msgAuthoritativeEngineTime: engineTime,
|
msgAuthoritativeEngineTime: engineTime,
|
||||||
@ -366,7 +366,7 @@ export class SnmpPacketCreator {
|
|||||||
* @param config SNMP configuration
|
* @param config SNMP configuration
|
||||||
* @returns Encrypted data
|
* @returns Encrypted data
|
||||||
*/
|
*/
|
||||||
private static simulateEncryption(data: Buffer, config: SnmpConfig): Buffer {
|
private static simulateEncryption(data: Buffer, config: ISnmpConfig): Buffer {
|
||||||
// This is a placeholder - in a real implementation, you would:
|
// This is a placeholder - in a real implementation, you would:
|
||||||
// 1. Generate an initialization vector (IV)
|
// 1. Generate an initialization vector (IV)
|
||||||
// 2. Use the privacy key derived from the privKey
|
// 2. Use the privacy key derived from the privKey
|
||||||
@ -427,7 +427,7 @@ export class SnmpPacketCreator {
|
|||||||
* @param authParamsBuf Authentication parameters buffer
|
* @param authParamsBuf Authentication parameters buffer
|
||||||
* @returns Authenticated message
|
* @returns Authenticated message
|
||||||
*/
|
*/
|
||||||
private static addAuthentication(message: Buffer, config: SnmpConfig, authParamsBuf: Buffer): Buffer {
|
private static addAuthentication(message: Buffer, config: ISnmpConfig, authParamsBuf: Buffer): Buffer {
|
||||||
// In a real implementation, this would:
|
// In a real implementation, this would:
|
||||||
// 1. Zero out the authentication parameters field
|
// 1. Zero out the authentication parameters field
|
||||||
// 2. Calculate HMAC-MD5 or HMAC-SHA1 over the entire message
|
// 2. Calculate HMAC-MD5 or HMAC-SHA1 over the entire message
|
||||||
@ -548,7 +548,7 @@ export class SnmpPacketCreator {
|
|||||||
* @param requestID Request ID
|
* @param requestID Request ID
|
||||||
* @returns Discovery message
|
* @returns Discovery message
|
||||||
*/
|
*/
|
||||||
public static createDiscoveryMessage(config: SnmpConfig, requestID: number): Buffer {
|
public static createDiscoveryMessage(config: ISnmpConfig, requestID: number): Buffer {
|
||||||
// Basic SNMPv3 header for discovery
|
// Basic SNMPv3 header for discovery
|
||||||
const msgIdBuf = Buffer.concat([
|
const msgIdBuf = Buffer.concat([
|
||||||
Buffer.from([0x02, 0x04]), // ASN.1 Integer, length 4
|
Buffer.from([0x02, 0x04]), // ASN.1 Integer, length 4
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { SnmpConfig } from './types.js';
|
import type { ISnmpConfig } from './types.js';
|
||||||
import { SnmpEncoder } from './encoder.js';
|
import { SnmpEncoder } from './encoder.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -13,7 +13,7 @@ export class SnmpPacketParser {
|
|||||||
* @param debug Whether to enable debug output
|
* @param debug Whether to enable debug output
|
||||||
* @returns Parsed value or null if parsing failed
|
* @returns Parsed value or null if parsing failed
|
||||||
*/
|
*/
|
||||||
public static parseSnmpResponse(buffer: Buffer, config: SnmpConfig, debug: boolean = false): any {
|
public static parseSnmpResponse(buffer: Buffer, config: ISnmpConfig, debug: boolean = false): any {
|
||||||
// Check if we have a response packet
|
// Check if we have a response packet
|
||||||
if (buffer[0] !== 0x30) {
|
if (buffer[0] !== 0x30) {
|
||||||
throw new Error('Invalid SNMP response format');
|
throw new Error('Invalid SNMP response format');
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
/**
|
/**
|
||||||
* UPS status interface
|
* UPS status interface
|
||||||
*/
|
*/
|
||||||
export interface UpsStatus {
|
export interface IUpsStatus {
|
||||||
/** Current power status */
|
/** Current power status */
|
||||||
powerStatus: 'online' | 'onBattery' | 'unknown';
|
powerStatus: 'online' | 'onBattery' | 'unknown';
|
||||||
/** Battery capacity percentage */
|
/** Battery capacity percentage */
|
||||||
@ -19,7 +19,7 @@ export interface UpsStatus {
|
|||||||
/**
|
/**
|
||||||
* SNMP OID Sets for different UPS brands
|
* SNMP OID Sets for different UPS brands
|
||||||
*/
|
*/
|
||||||
export interface OIDSet {
|
export interface IOidSet {
|
||||||
/** OID for power status */
|
/** OID for power status */
|
||||||
POWER_STATUS: string;
|
POWER_STATUS: string;
|
||||||
/** OID for battery capacity */
|
/** OID for battery capacity */
|
||||||
@ -31,12 +31,12 @@ export interface OIDSet {
|
|||||||
/**
|
/**
|
||||||
* Supported UPS model types
|
* Supported UPS model types
|
||||||
*/
|
*/
|
||||||
export type UpsModel = 'cyberpower' | 'apc' | 'eaton' | 'tripplite' | 'liebert' | 'custom';
|
export type TUpsModel = 'cyberpower' | 'apc' | 'eaton' | 'tripplite' | 'liebert' | 'custom';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SNMP Configuration interface
|
* SNMP Configuration interface
|
||||||
*/
|
*/
|
||||||
export interface SnmpConfig {
|
export interface ISnmpConfig {
|
||||||
/** SNMP server host */
|
/** SNMP server host */
|
||||||
host: string;
|
host: string;
|
||||||
/** SNMP server port (default 161) */
|
/** SNMP server port (default 161) */
|
||||||
@ -66,15 +66,15 @@ export interface SnmpConfig {
|
|||||||
|
|
||||||
// UPS model and custom OIDs
|
// UPS model and custom OIDs
|
||||||
/** UPS model for OID selection */
|
/** UPS model for OID selection */
|
||||||
upsModel?: UpsModel;
|
upsModel?: TUpsModel;
|
||||||
/** Custom OIDs when using custom UPS model */
|
/** Custom OIDs when using custom UPS model */
|
||||||
customOIDs?: OIDSet;
|
customOIDs?: IOidSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SNMPv3 security parameters
|
* SNMPv3 security parameters
|
||||||
*/
|
*/
|
||||||
export interface SnmpV3SecurityParams {
|
export interface ISnmpV3SecurityParams {
|
||||||
/** Engine ID for the SNMP server */
|
/** Engine ID for the SNMP server */
|
||||||
msgAuthoritativeEngineID: Buffer;
|
msgAuthoritativeEngineID: Buffer;
|
||||||
/** Engine boots counter */
|
/** Engine boots counter */
|
||||||
|
@ -126,9 +126,18 @@ WantedBy=multi-user.target
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get status of the systemd service and UPS
|
* Get status of the systemd service and UPS
|
||||||
|
* @param debugMode Whether to enable debug mode for SNMP
|
||||||
*/
|
*/
|
||||||
public async getStatus(): Promise<void> {
|
public async getStatus(debugMode: boolean = false): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
// Enable debug mode if requested
|
||||||
|
if (debugMode) {
|
||||||
|
console.log('┌─ Debug Mode ─────────────────────────────┐');
|
||||||
|
console.log('│ SNMP debugging enabled - detailed logs will be shown');
|
||||||
|
console.log('└──────────────────────────────────────────┘');
|
||||||
|
this.daemon.getNupstSnmp().enableDebug();
|
||||||
|
}
|
||||||
|
|
||||||
// Display version information
|
// Display version information
|
||||||
this.daemon.getNupstSnmp().getNupst().logVersionInfo();
|
this.daemon.getNupstSnmp().getNupst().logVersionInfo();
|
||||||
|
|
||||||
@ -170,9 +179,23 @@ WantedBy=multi-user.target
|
|||||||
*/
|
*/
|
||||||
private async displayUpsStatus(): Promise<void> {
|
private async displayUpsStatus(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const upsStatus = await this.daemon.getConfig().snmp;
|
// Explicitly load the configuration first to ensure it's up-to-date
|
||||||
|
await this.daemon.loadConfig();
|
||||||
|
const config = this.daemon.getConfig();
|
||||||
const snmp = this.daemon.getNupstSnmp();
|
const snmp = this.daemon.getNupstSnmp();
|
||||||
const status = await snmp.getUpsStatus(upsStatus);
|
|
||||||
|
// Create a test config with appropriate timeout, similar to the test command
|
||||||
|
const snmpConfig = {
|
||||||
|
...config.snmp,
|
||||||
|
timeout: Math.min(config.snmp.timeout, 10000) // Use at most 10 seconds for status check
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('┌─ Connecting to UPS... ────────────────────┐');
|
||||||
|
console.log(`│ Host: ${config.snmp.host}:${config.snmp.port}`);
|
||||||
|
console.log(`│ UPS Model: ${config.snmp.upsModel || 'cyberpower'}`);
|
||||||
|
console.log('└──────────────────────────────────────────┘');
|
||||||
|
|
||||||
|
const status = await snmp.getUpsStatus(snmpConfig);
|
||||||
|
|
||||||
console.log('┌─ UPS Status ───────────────────────────────┐');
|
console.log('┌─ UPS Status ───────────────────────────────┐');
|
||||||
console.log(`│ Power Status: ${status.powerStatus}`);
|
console.log(`│ Power Status: ${status.powerStatus}`);
|
||||||
|
Reference in New Issue
Block a user