Skip to content

Commit

Permalink
Update Add Server / IPMI
Browse files Browse the repository at this point in the history
  • Loading branch information
cdgco committed Apr 4, 2024
1 parent b089661 commit a49b431
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 17 deletions.
79 changes: 74 additions & 5 deletions src/commands/server/add.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
import {Command, Flags} from '@oclif/core'
const { addServer, closeDb, openOrCreateDatabase } = require('../../db/index.ts')
const { addCredentials, addServer, closeDb, openOrCreateDatabase } = require('../../db/index.ts')
const { checkAndRefreshToken } = require('../../firebase/auth.ts')
const crypto = require("node:crypto");
const promptly = require("promptly");

// Validates if a value is a number within a specified range
function numberValidator(value: any, min: number, max: number) {
const number = Number.parseInt(value, 10);

if (!Number.isNaN(number) && number >= min && number <= max) {
return value;
}

throw new Error(`Value must be a number between ${min} and ${max}`);
}

function isValidNumber(value: number) {
try {
numberValidator(value, 0, 65_535);
return true;
} catch {
return false;
}
}

export default class Add extends Command {
static description = 'Add a server'

Expand All @@ -13,6 +33,11 @@ export default class Add extends Command {

static flags = {
interval: Flags.integer({char: 'i', description: 'Interval in minutes'}),
ipmiAddress: Flags.string({char: 'a', description: 'IPMI address'}),
ipmiFlags: Flags.string({char: 'f', description: 'Extra ipmitool flags'}),
ipmiPassword: Flags.string({char: 'P', description: 'IPMI password'}),
ipmiPort: Flags.integer({char: 'o', description: 'IPMI port'}),
ipmiUsername: Flags.string({char: 'u', description: 'IPMI username'}),
mode: Flags.string({char: 'm', description: 'Monitoring mode (tcp, udp, http, https)'}),
name: Flags.string({char: 'n', description: 'Name of the server'}),
port: Flags.integer({char: 'p', description: 'Port to monitor'}),
Expand All @@ -26,17 +51,61 @@ export default class Add extends Command {

const serverId = crypto.randomUUID();

if (flags.port && !isValidNumber(flags.port)) {
flags.port = undefined;
}

// Ask for address first
const server = flags.server || await promptly.prompt("Server address: ");
const name = flags.name || await promptly.prompt("Server name (optional): ", { default: "", retry: false});
const mode = flags.mode || await promptly.choose('Monitoring mode (tcp, udp, http, or https) [tcp]: ', ['http', 'https', 'tcp', 'udp'], { default: 'tcp' });
const defaultPort = mode === "http" || mode === "https" ? "80" : "0";
const port = flags.port || await promptly.prompt(`Port to monitor [${defaultPort}]: `, { default: defaultPort, validator: (value: any) => numberValidator(value, 0, 65_535) });

const data = {
id: serverId,
interval: flags.interval || await promptly.prompt("Interval in minutes [5]: ", {default: "5"}),
mode: flags.mode || await promptly.prompt("Monitoring mode (tcp, udp, http, https) [tcp]: ", {default: "tcp"}),
name: flags.name || await promptly.prompt("Name: ", {default: ""}),
port: flags.port || await promptly.prompt("Port to monitor [0]: ", {default: "0"}),
server: flags.server || await promptly.prompt("Server address: "),
mode,
name,
port,
server,
};

const setIpmi = await promptly.confirm("Do you want to configure IPMI credentials for this server? [y/N]: ", { default: "n" });

let credential : any = {};

if (flags.ipmiPort && !isValidNumber(flags.ipmiPort)) {
flags.ipmiPort = undefined;
}

const ipmiAddress = flags.ipmiAddress || await promptly.prompt("IPMI address: ");
const ipmiUsername = flags.ipmiUsername || await promptly.prompt("IPMI username: ");
const ipmiPassword = flags.ipmiPassword || await promptly.password("IPMI password: ");
const ipmiPort = flags.ipmiPort || await promptly.prompt("IPMI port [623]: ", { default: "623", validator: (value: any) => numberValidator(value, 1, 65_535) });
const ipmiFlags = flags.ipmiFlags || await promptly.prompt("Extra ipmitool flags (optional): ", { default: "", retry: false });

if (setIpmi) {
credential = {
address: ipmiAddress,
flags: ipmiFlags,
password: ipmiPassword,
port: ipmiPort,
username: ipmiUsername,
};
}

await addServer(db, data);

await addCredentials(db, {
address: credential.address,
flags: credential.flags,
password: credential.password,
port: credential.port,
serverId,
username: credential.username,
});

await closeDb(db);

console.log(`Server ${data.name} added with id ${serverId}`);
Expand Down
43 changes: 31 additions & 12 deletions src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const path = require("node:path");
const { dataPath, dbName, findDatabasePath } = require("./paths.ts");
const { getEffectiveUidGid, isAdmin } = require("../service/helpers/index.ts");
const { exit } = require("node:process");
const crypto = require("node:crypto");
const keytar = require("keytar");

function deleteWithRetry(filePath: string , maxRetries = 5, interval = 100, attempt = 0) {
try {
Expand Down Expand Up @@ -135,10 +137,14 @@ function initializeDatabase(db: any) {
});

db.run(`CREATE TABLE IF NOT EXISTS ipmi (
server TEXT PRIMARY KEY NOT NULL,
id INTEGER PRIMARY KEY AUTOINCREMENT,
server_id TEXT NOT NULL,
address TEXT NOT NULL,
username TEXT,
credential TEXT
credential TEXT,
port INTEGER DEFAULT 623,
flags TEXT DEFAULT '',
FOREIGN KEY (server_id) REFERENCES servers(id) ON DELETE CASCADE
)`, [], (err: Error) => {
if (err) {
console.error("Error creating ipmi table", err);
Expand All @@ -147,6 +153,13 @@ function initializeDatabase(db: any) {
resolve(null);
}
});

db.run("PRAGMA foreign_keys = ON", (err: Error) => {
if (err) {
console.error("Error enabling foreign key support", err);
reject(err);
}
});
});
});
}
Expand Down Expand Up @@ -189,22 +202,28 @@ function getServer(db: any, server: string) {

}

function addCredentials(db: any, ipmi: { address: string, credential: string, server: string, username: string }) {
function addCredentials(db: any, ipmi: { address: string, flags: string, password: string, port: string, serverId: string, username: string }) {
return new Promise((resolve, reject) => {
db.run(`REPLACE INTO ipmi (server, address, username, credential) VALUES (?, ?, ?, ?)`, [ipmi.server, ipmi.address, ipmi.username, ipmi.credential], (err: Error) => {
if (err) {
console.error("Error adding IPMI credentials", err);
reject(err);
} else {
resolve(null);
}
const accountId = "rackmanage_" + crypto.randomUUID();
keytar.setPassword("rackmanage", accountId, ipmi.password).then(() => {
db.run(`REPLACE INTO ipmi (server_id, address, username, credential, port, flags) VALUES (?, ?, ?, ?, ?, ?)`, [ipmi.serverId, ipmi.address, ipmi.username, accountId, ipmi.port, ipmi.flags], (err: Error) => {
if (err) {
console.error("Error adding IPMI credentials", err);
reject(err);
} else {
resolve(null);
}
}).catch((error: Error) => {
console.error("Error adding IPMI credentials", error);
reject(error);
});
});
});
}

function getCredential(db: any, server: string) {
function getCredential(db: any, serverId: string) {
return new Promise((resolve, reject) => {
db.get(`SELECT server, address, username, credential FROM ipmi WHERE server = ?`, [server], (err: Error, row: any) => {
db.get(`SELECT server_id, address, username, credential, port, flags FROM ipmi WHERE server_id = ?`, [serverId], (err: Error, row: any) => {
if (err) {
reject(err);
} else if (row) {
Expand Down

0 comments on commit a49b431

Please sign in to comment.