f40ef6b7c0
Align Cloudly with the current typedserver, smartconfig, smartstate, and Docker tooling releases so builds and Docker output stay compatible with the upgraded stack.
181 lines
5.6 KiB
TypeScript
181 lines
5.6 KiB
TypeScript
import * as plugins from '../plugins.js';
|
|
import { Cloudly } from '../classes.cloudly.js';
|
|
import { BareMetal } from './classes.baremetal.js';
|
|
import { logger } from '../logger.js';
|
|
|
|
export class CloudlyBaremetalManager {
|
|
public cloudlyRef: Cloudly;
|
|
public typedRouter = new plugins.typedrequest.TypedRouter();
|
|
|
|
public hetznerAccount?: plugins.hetznercloud.HetznerAccount;
|
|
|
|
public get db() {
|
|
return this.cloudlyRef.mongodbConnector.smartdataDb;
|
|
}
|
|
public CBareMetal = plugins.smartdata.setDefaultManagerForDoc(this, BareMetal);
|
|
|
|
constructor(cloudlyRefArg: Cloudly) {
|
|
this.cloudlyRef = cloudlyRefArg;
|
|
this.cloudlyRef.typedrouter.addTypedRouter(this.typedRouter);
|
|
|
|
// API endpoint to get baremetal servers
|
|
this.typedRouter.addTypedHandler(
|
|
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.baremetal.IRequest_Any_Cloudly_GetBaremetalServers>(
|
|
'getBaremetalServers',
|
|
async (requestData) => {
|
|
const baremetals = await this.getAllBaremetals();
|
|
return {
|
|
baremetals: await Promise.all(
|
|
baremetals.map((baremetal) => baremetal.createSavableObject())
|
|
),
|
|
};
|
|
},
|
|
),
|
|
);
|
|
|
|
// API endpoint to control baremetal via IPMI
|
|
this.typedRouter.addTypedHandler(
|
|
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.baremetal.IRequest_Any_Cloudly_ControlBaremetal>(
|
|
'controlBaremetal',
|
|
async (requestData) => {
|
|
const baremetal = await this.CBareMetal.getInstance({
|
|
id: requestData.baremetalId,
|
|
});
|
|
|
|
if (!baremetal) {
|
|
return {
|
|
success: false,
|
|
message: 'BareMetal not found',
|
|
};
|
|
}
|
|
|
|
let success = false;
|
|
switch (requestData.action) {
|
|
case 'powerOn':
|
|
success = await baremetal.powerOn();
|
|
break;
|
|
case 'powerOff':
|
|
success = await baremetal.powerOff();
|
|
break;
|
|
case 'reset':
|
|
success = await baremetal.reset();
|
|
break;
|
|
}
|
|
|
|
return {
|
|
success,
|
|
message: success ? `Action ${requestData.action} completed` : `Action ${requestData.action} failed`,
|
|
};
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
public async start() {
|
|
const hetznerToken = await this.cloudlyRef.settingsManager.getSetting('hetznerToken');
|
|
|
|
if (hetznerToken) {
|
|
this.hetznerAccount = new plugins.hetznercloud.HetznerAccount(hetznerToken);
|
|
}
|
|
|
|
logger.log('info', 'BareMetal manager started');
|
|
}
|
|
|
|
public async stop() {
|
|
logger.log('info', 'BareMetal manager stopped');
|
|
}
|
|
|
|
/**
|
|
* Get all baremetal servers
|
|
*/
|
|
public async getAllBaremetals(): Promise<BareMetal[]> {
|
|
const baremetals = await this.CBareMetal.getInstances({});
|
|
return baremetals;
|
|
}
|
|
|
|
/**
|
|
* Get baremetal by ID
|
|
*/
|
|
public async getBaremetalById(id: string): Promise<BareMetal | null> {
|
|
const baremetal = await this.CBareMetal.getInstance({
|
|
id,
|
|
});
|
|
return baremetal;
|
|
}
|
|
|
|
/**
|
|
* Get baremetals by provider
|
|
*/
|
|
public async getBaremetalsByProvider(provider: 'hetzner' | 'aws' | 'digitalocean' | 'onpremise'): Promise<BareMetal[]> {
|
|
const baremetals = await this.CBareMetal.getInstances({
|
|
data: {
|
|
provider,
|
|
},
|
|
});
|
|
return baremetals;
|
|
}
|
|
|
|
/**
|
|
* Create baremetal from Hetzner server
|
|
*/
|
|
public async createBaremetalFromHetznerServer(hetznerServer: plugins.hetznercloud.HetznerServer): Promise<BareMetal> {
|
|
const serverData = hetznerServer.data;
|
|
if (!serverData) {
|
|
throw new Error('Hetzner server response is missing server data');
|
|
}
|
|
// Check if baremetal already exists for this Hetzner server
|
|
const existingBaremetals = await this.CBareMetal.getInstances({});
|
|
for (const baremetal of existingBaremetals) {
|
|
if (baremetal.data.providerMetadata?.hetznerServerId === serverData.id) {
|
|
logger.log('info', `BareMetal already exists for Hetzner server ${serverData.id}`);
|
|
return baremetal;
|
|
}
|
|
}
|
|
|
|
// Create new baremetal
|
|
const newBaremetal = await BareMetal.createFromHetznerServer(hetznerServer);
|
|
logger.log('success', `Created new BareMetal ${newBaremetal.id} from Hetzner server ${serverData.id}`);
|
|
return newBaremetal;
|
|
}
|
|
|
|
/**
|
|
* Sync baremetals with Hetzner
|
|
*/
|
|
public async syncWithHetzner() {
|
|
if (!this.hetznerAccount) {
|
|
logger.log('warn', 'Cannot sync with Hetzner - no account configured');
|
|
return;
|
|
}
|
|
|
|
const hetznerServers = await this.hetznerAccount.getServers();
|
|
|
|
for (const hetznerServer of hetznerServers) {
|
|
await this.createBaremetalFromHetznerServer(hetznerServer);
|
|
}
|
|
|
|
logger.log('success', `Synced ${hetznerServers.length} servers from Hetzner`);
|
|
}
|
|
|
|
/**
|
|
* Provision a new baremetal server
|
|
*/
|
|
public async provisionBaremetal(options: {
|
|
provider: 'hetzner' | 'aws' | 'digitalocean';
|
|
location: any; // TODO: Import proper type from hetznercloud when available
|
|
type: any; // TODO: Import proper type from hetznercloud when available
|
|
}): Promise<BareMetal> {
|
|
if (options.provider === 'hetzner' && this.hetznerAccount) {
|
|
const hetznerServer = await this.hetznerAccount.createServer({
|
|
name: plugins.smartunique.uniSimple('baremetal'),
|
|
location: options.location,
|
|
type: options.type,
|
|
});
|
|
|
|
const baremetal = await this.createBaremetalFromHetznerServer(hetznerServer);
|
|
return baremetal;
|
|
}
|
|
|
|
throw new Error(`Provider ${options.provider} not supported or not configured`);
|
|
}
|
|
}
|