diff --git a/app/core/modules/vms/get-domains.ts b/app/core/modules/vms/get-domains.ts index c1aac91dd..0062a2147 100644 --- a/app/core/modules/vms/get-domains.ts +++ b/app/core/modules/vms/get-domains.ts @@ -31,31 +31,42 @@ export const getDomains = async (context: CoreContext): Promise => { possession: 'any' }); - const hypervisor = await getHypervisor(); - const activeDomains = await hypervisor.connectListAllDomains(ConnectListAllDomainsFlags.ACTIVE); - const inactiveDomains = await hypervisor.connectListAllDomains(ConnectListAllDomainsFlags.INACTIVE); - const autoStartDomains = await hypervisor.connectListAllDomains(ConnectListAllDomainsFlags.AUTOSTART); - const activeDomainNames = await Promise.all(activeDomains.map(async domain => hypervisor.domainGetName(domain))); - const inactiveDomainNames = await Promise.all(inactiveDomains.map(async domain => hypervisor.domainGetName(domain))); - const autoStartDomainNames = await Promise.all(autoStartDomains.map(async domain => hypervisor.domainGetName(domain))); + try { + const hypervisor = await getHypervisor(); + const activeDomains = await hypervisor.connectListAllDomains(ConnectListAllDomainsFlags.ACTIVE); + const inactiveDomains = await hypervisor.connectListAllDomains(ConnectListAllDomainsFlags.INACTIVE); + const autoStartDomains = await hypervisor.connectListAllDomains(ConnectListAllDomainsFlags.AUTOSTART); + const activeDomainNames = await Promise.all(activeDomains.map(async domain => hypervisor.domainGetName(domain))); + const inactiveDomainNames = await Promise.all(inactiveDomains.map(async domain => hypervisor.domainGetName(domain))); + const autoStartDomainNames = await Promise.all(autoStartDomains.map(async domain => hypervisor.domainGetName(domain))); + + // Get all domains + const domains = await hypervisor.connectListAllDomains(); + const resolvedDomains = await Promise.all(domains.map(async domain => { + const info = await hypervisor.domainGetInfo(domain); + const name = await hypervisor.domainGetName(domain); + const features = {}; + return { + name, + uuid: await hypervisor.domainGetUUIDString(domain), + state: states[info.state], + autoStart: autoStartDomainNames.includes(name), + features + }; + })); - // Get all domains - const domains = await hypervisor.connectListAllDomains(); - const resolvedDomains = await Promise.all(domains.map(async domain => { - const info = await hypervisor.domainGetInfo(domain); - const name = await hypervisor.domainGetName(domain); - const features = {}; return { - name, - uuid: await hypervisor.domainGetUUIDString(domain), - state: states[info.state], - autoStart: autoStartDomainNames.includes(name), - features + text: `Defined domains: ${JSON.stringify(activeDomainNames, null, 2)}\nActive domains: ${JSON.stringify(inactiveDomainNames, null, 2)}`, + json: resolvedDomains }; - })); + } catch (error: unknown) { + if (error instanceof Error && error.message === 'Libvirt service is not running') { + return { + text: `Defined domains: ${JSON.stringify([], null, 2)}\nActive domains: ${JSON.stringify([], null, 2)}`, + json: null + }; + } - return { - text: `Defined domains: ${JSON.stringify(activeDomainNames, null, 2)}\nActive domains: ${JSON.stringify(inactiveDomainNames, null, 2)}`, - json: resolvedDomains - }; + throw error; + } }; diff --git a/app/core/utils/vms/get-hypervisor.ts b/app/core/utils/vms/get-hypervisor.ts index 6706e5d1b..95cbc612b 100644 --- a/app/core/utils/vms/get-hypervisor.ts +++ b/app/core/utils/vms/get-hypervisor.ts @@ -4,13 +4,18 @@ */ import fs from 'fs'; +import path from 'path'; import { AppError } from '../../errors'; import { Hypervisor } from '@vmngr/libvirt'; +import { watch } from 'chokidar'; +import { log } from '../../log'; const uri = process.env.LIBVIRT_URI ?? 'qemu:///system'; let hypervisor: Hypervisor; +const libvirtDir = '/var/run/libvirt/'; + export const getHypervisor = async () => { // Return hypervisor if it's already connected if (hypervisor) { @@ -18,13 +23,40 @@ export const getHypervisor = async () => { } // Check if libvirt service is running and then connect - const running = fs.existsSync('/var/run/libvirt/libvirtd.pid'); + const running = fs.existsSync(path.join(libvirtDir, 'libvirtd.pid')); if (!running) { throw new AppError('Libvirt service is not running'); } + // Watch extra origin path for changes + const libvirtDirWatcher = watch(libvirtDir, { + persistent: true, + ignoreInitial: true + }); + + // Restart hypervisor connection + libvirtDirWatcher.on('all', async (event, fileName) => { + // VM hypervisor stopped + if (event === 'unlink' && fileName === '/var/run/libvirt/libvirt-sock') { + // Kill connection + await hypervisor.connectClose().catch((error: unknown) => { + log.error(`Failed killing VM hypervisor connection with "${(error as Error).message}"`); + }); + } + + // VM hypervisor started + if (event === 'add' && fileName === 'add /var/run/libvirt/qemu/driver.pid') { + // Start connection + await hypervisor.connectOpen().catch((error: unknown) => { + log.error(`Failed restarting VM hypervisor connection with "${(error as Error).message}"`); + }); + } + }); + hypervisor = new Hypervisor({ uri }); - await hypervisor.connectOpen(); + await hypervisor.connectOpen().catch((error: unknown) => { + log.error(`Failed starting VM hypervisor connection with "${(error as Error).message}"`); + }); return hypervisor; };