134 lines
3.2 KiB
TypeScript
134 lines
3.2 KiB
TypeScript
import { Client } from 'ssh2';
|
|
|
|
export interface SSHConnectionConfig {
|
|
host: string;
|
|
port?: number;
|
|
username: string;
|
|
privateKey?: string;
|
|
password?: string;
|
|
}
|
|
|
|
export interface DockerContainer {
|
|
name: string;
|
|
image: string;
|
|
status: string;
|
|
ports: string[];
|
|
created: string;
|
|
}
|
|
|
|
export interface HostInfo {
|
|
name: string;
|
|
ip: string;
|
|
online: boolean;
|
|
containers: DockerContainer[];
|
|
cpuUsage?: number;
|
|
memoryUsage?: number;
|
|
uptime?: string;
|
|
error?: string;
|
|
}
|
|
|
|
async function connectSSH(config: SSHConnectionConfig): Promise<Client> {
|
|
return new Promise((resolve, reject) => {
|
|
const conn = new Client();
|
|
conn.on('ready', () => resolve(conn));
|
|
conn.on('error', (err) => reject(err));
|
|
conn.connect({
|
|
host: config.host,
|
|
port: config.port || 22,
|
|
username: config.username,
|
|
privateKey: config.privateKey,
|
|
password: config.password,
|
|
readyTimeout: 10000,
|
|
});
|
|
});
|
|
}
|
|
|
|
async function execSSH(conn: Client, command: string): Promise<string> {
|
|
return new Promise((resolve, reject) => {
|
|
conn.exec(command, (err, stream) => {
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
let output = '';
|
|
stream.on('close', () => resolve(output));
|
|
stream.on('data', (data: Buffer) => { output += data.toString(); });
|
|
stream.stderr.on('data', (data: Buffer) => { output += data.toString(); });
|
|
});
|
|
});
|
|
}
|
|
|
|
export async function discoverHostViaSSH(
|
|
hostName: string,
|
|
ip: string,
|
|
sshConfig?: SSHConnectionConfig
|
|
): Promise<HostInfo> {
|
|
const defaultConfig: SSHConnectionConfig = {
|
|
host: ip,
|
|
username: 'bear',
|
|
privateKey: require('fs').readFileSync(require('os').homedir() + '/.ssh/id_ed25519'),
|
|
};
|
|
|
|
const config = sshConfig || defaultConfig;
|
|
|
|
try {
|
|
const conn = await connectSSH(config);
|
|
|
|
const dockerPsCmd = `docker ps --format '{{.Names}}|{{.Image}}|{{.Status}}|{{.Ports}}|{{.CreatedAt}}'`;
|
|
const dockerPsOutput = await execSSH(conn, dockerPsCmd);
|
|
|
|
const containers: DockerContainer[] = dockerPsOutput
|
|
.trim()
|
|
.split('\n')
|
|
.filter(line => line.trim())
|
|
.map(line => {
|
|
const [name, image, status, ports, created] = line.split('|');
|
|
return {
|
|
name: name.trim(),
|
|
image: image.trim(),
|
|
status: status.trim(),
|
|
ports: ports.trim() ? ports.trim().split(', ') : [],
|
|
created: created.trim(),
|
|
};
|
|
});
|
|
|
|
conn.end();
|
|
|
|
return {
|
|
name: hostName,
|
|
ip: ip,
|
|
online: true,
|
|
containers,
|
|
};
|
|
} catch (error: any) {
|
|
return {
|
|
name: hostName,
|
|
ip: ip,
|
|
online: false,
|
|
containers: [],
|
|
error: error.message || 'Connection failed',
|
|
};
|
|
}
|
|
}
|
|
|
|
export async function discoverAllHostsSSH(
|
|
hosts: { name: string; ip: string }[]
|
|
): Promise<HostInfo[]> {
|
|
const results = await Promise.all(
|
|
hosts.map(host => discoverHostViaSSH(host.name, host.ip))
|
|
);
|
|
return results;
|
|
}
|
|
|
|
if (require.main === module) {
|
|
const hosts = [
|
|
{ name: 'ubuntu', ip: '192.168.50.61' },
|
|
{ name: 'grizzley', ip: '192.168.50.84' },
|
|
{ name: 'truenas', ip: '192.168.50.12' },
|
|
];
|
|
|
|
discoverAllHostsSSH(hosts).then(results => {
|
|
console.log(JSON.stringify(results, null, 2));
|
|
}).catch(console.error);
|
|
}
|