mirror of
https://github.com/unraid/api.git
synced 2026-01-03 15:09:48 -06:00
feat: hypervisor async imports
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
import { ConnectListAllDomainsFlags } from '@vmngr/libvirt';
|
|
||||||
import { getHypervisor } from '@app/core/utils/vms/get-hypervisor';
|
|
||||||
import { VmState, type VmDomain } from '@app/graphql/generated/api/types';
|
|
||||||
import { GraphQLError } from 'graphql';
|
import { GraphQLError } from 'graphql';
|
||||||
|
|
||||||
|
import type { VmDomain } from '@app/graphql/generated/api/types';
|
||||||
|
import { VmState } from '@app/graphql/generated/api/types';
|
||||||
|
|
||||||
const states = {
|
const states = {
|
||||||
0: 'NOSTATE',
|
0: 'NOSTATE',
|
||||||
1: 'RUNNING',
|
1: 'RUNNING',
|
||||||
@@ -17,10 +17,11 @@ const states = {
|
|||||||
/**
|
/**
|
||||||
* Get vm domains.
|
* Get vm domains.
|
||||||
*/
|
*/
|
||||||
export const getDomains =async () => {
|
export const getDomains = async () => {
|
||||||
|
const { ConnectListAllDomainsFlags } = await import('@vmngr/libvirt');
|
||||||
|
const { UnraidHypervisor } = await import('@app/core/utils/vms/get-hypervisor');
|
||||||
try {
|
try {
|
||||||
const hypervisor = await getHypervisor();
|
const hypervisor = await UnraidHypervisor.getInstance().getHypervisor();
|
||||||
if (!hypervisor) {
|
if (!hypervisor) {
|
||||||
throw new GraphQLError('VMs Disabled');
|
throw new GraphQLError('VMs Disabled');
|
||||||
}
|
}
|
||||||
@@ -30,9 +31,7 @@ export const getDomains =async () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const autoStartDomainNames = await Promise.all(
|
const autoStartDomainNames = await Promise.all(
|
||||||
autoStartDomains.map(async (domain) =>
|
autoStartDomains.map(async (domain) => hypervisor.domainGetName(domain))
|
||||||
hypervisor.domainGetName(domain)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get all domains
|
// Get all domains
|
||||||
@@ -53,9 +52,11 @@ export const getDomains =async () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
return resolvedDomains;
|
return resolvedDomains;
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
// If we hit an error expect libvirt to be offline
|
// If we hit an error expect libvirt to be offline
|
||||||
throw new GraphQLError(`Failed to fetch domains with error: ${error instanceof Error ? error.message : 'Unknown Error'}`);
|
throw new GraphQLError(
|
||||||
|
`Failed to fetch domains with error: ${error instanceof Error ? error.message : 'Unknown Error'}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { access } from 'fs/promises';
|
|
||||||
import { constants } from 'fs';
|
import { constants } from 'fs';
|
||||||
|
import { access } from 'fs/promises';
|
||||||
|
|
||||||
|
import type { Hypervisor as HypervisorType } from '@vmngr/libvirt';
|
||||||
|
|
||||||
import { Hypervisor } from '@vmngr/libvirt';
|
|
||||||
import { libvirtLogger } from '@app/core/log';
|
import { libvirtLogger } from '@app/core/log';
|
||||||
|
|
||||||
const uri = process.env.LIBVIRT_URI ?? 'qemu:///system';
|
const uri = process.env.LIBVIRT_URI ?? 'qemu:///system';
|
||||||
|
|
||||||
let hypervisor: Hypervisor | null;
|
|
||||||
|
|
||||||
const libvirtPid = '/var/run/libvirt/libvirtd.pid';
|
const libvirtPid = '/var/run/libvirt/libvirtd.pid';
|
||||||
|
|
||||||
const isLibvirtRunning = async (): Promise<boolean> => {
|
const isLibvirtRunning = async (): Promise<boolean> => {
|
||||||
@@ -19,29 +18,40 @@ const isLibvirtRunning = async (): Promise<boolean> => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getHypervisor = async (): Promise<Hypervisor> => {
|
export class UnraidHypervisor {
|
||||||
// Return hypervisor if it's already connected
|
private static instance: UnraidHypervisor | null = null;
|
||||||
const running = await isLibvirtRunning();
|
private hypervisor: HypervisorType | null = null;
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
if (hypervisor && running) {
|
public static getInstance(): UnraidHypervisor {
|
||||||
return hypervisor;
|
if (this.instance === null) {
|
||||||
|
this.instance = new UnraidHypervisor();
|
||||||
|
}
|
||||||
|
return this.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!running) {
|
public async getHypervisor(): Promise<HypervisorType | null> {
|
||||||
hypervisor = null;
|
// Return hypervisor if it's already connected
|
||||||
throw new Error('Libvirt is not running');
|
const running = await isLibvirtRunning();
|
||||||
}
|
|
||||||
|
|
||||||
hypervisor = new Hypervisor({ uri });
|
if (this.hypervisor && running) {
|
||||||
await hypervisor.connectOpen().catch((error: unknown) => {
|
return this.hypervisor;
|
||||||
libvirtLogger.error(
|
}
|
||||||
`Failed starting VM hypervisor connection with "${
|
|
||||||
(error as Error).message
|
|
||||||
}"`
|
|
||||||
);
|
|
||||||
|
|
||||||
throw error;
|
if (!running) {
|
||||||
});
|
this.hypervisor = null;
|
||||||
|
throw new Error('Libvirt is not running');
|
||||||
|
}
|
||||||
|
const { Hypervisor } = await import('@vmngr/libvirt');
|
||||||
|
this.hypervisor = new Hypervisor({ uri });
|
||||||
|
await this.hypervisor.connectOpen().catch((error: unknown) => {
|
||||||
|
libvirtLogger.error(
|
||||||
|
`Failed starting VM hypervisor connection with "${(error as Error).message}"`
|
||||||
|
);
|
||||||
|
|
||||||
return hypervisor;
|
throw error;
|
||||||
};
|
});
|
||||||
|
|
||||||
|
return this.hypervisor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { type Domain } from '@app/core/types';
|
import { type Domain } from '@app/core/types';
|
||||||
import { getHypervisor } from '@app/core/utils/vms/get-hypervisor';
|
|
||||||
|
|
||||||
export type DomainLookupType = 'id' | 'uuid' | 'name';
|
export type DomainLookupType = 'id' | 'uuid' | 'name';
|
||||||
|
|
||||||
@@ -21,7 +20,8 @@ export const parseDomain = async (type: DomainLookupType, id: string): Promise<D
|
|||||||
throw new Error(`Type must be one of [${Object.keys(types).join(', ')}], ${type} given.`);
|
throw new Error(`Type must be one of [${Object.keys(types).join(', ')}], ${type} given.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = await getHypervisor();
|
const { UnraidHypervisor } = await import('@app/core/utils/vms/get-hypervisor');
|
||||||
|
const client = await UnraidHypervisor.getInstance().getHypervisor();
|
||||||
const method = types[type];
|
const method = types[type];
|
||||||
const domain = await client[method](id);
|
const domain = await client[method](id);
|
||||||
const info = await domain.getInfoAsync();
|
const info = await domain.getInfoAsync();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Command, CommandRunner, Option } from 'nest-commander';
|
|||||||
|
|
||||||
import { ECOSYSTEM_PATH, PM2_PATH } from '@app/consts';
|
import { ECOSYSTEM_PATH, PM2_PATH } from '@app/consts';
|
||||||
import { levels, type LogLevel } from '@app/core/log';
|
import { levels, type LogLevel } from '@app/core/log';
|
||||||
import { LogService } from '@app/unraid-api/cli/log.service';
|
import type { LogService } from '@app/unraid-api/cli/log.service';
|
||||||
|
|
||||||
interface StartCommandOptions {
|
interface StartCommandOptions {
|
||||||
'log-level'?: string;
|
'log-level'?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user