diff --git a/src/api/proxmox/cluster.ts b/src/api/proxmox/cluster.ts index a44d855e5..0d933f21a 100644 --- a/src/api/proxmox/cluster.ts +++ b/src/api/proxmox/cluster.ts @@ -15,16 +15,26 @@ export async function isNodeInCluster(this: ProxmoxClient): Promise<{ isCluster: // Try to access the cluster status endpoint const response = await this.client.get('/cluster/status'); - if (response.data && response.data.data && Array.isArray(response.data.data) && response.data.data.length > 0) { - // If we get a valid response with data, the node is part of a cluster - // Find the cluster name from the response + // Log the full response for debugging + this.logger.debug(`Cluster status response: ${JSON.stringify(response.data)}`); + + if (response.data && response.data.data && Array.isArray(response.data.data)) { + // Only consider it a cluster if we find an item with type: "cluster" const clusterInfo = response.data.data.find((item: any) => item.type === 'cluster'); - const clusterName = clusterInfo?.name || 'proxmox-cluster'; - this.logger.info(`Node is part of cluster: ${clusterName}`); - return { isCluster: true, clusterName }; + // Log the cluster info for debugging + this.logger.debug(`Cluster info: ${JSON.stringify(clusterInfo)}`); + + if (clusterInfo && clusterInfo.type === 'cluster') { + const clusterName = clusterInfo.name || 'proxmox-cluster'; + this.logger.info(`Node is part of cluster: ${clusterName}`); + return { isCluster: true, clusterName }; + } else { + this.logger.info('Node has cluster API but no cluster type found - not part of a cluster'); + return { isCluster: false, clusterName: '' }; + } } else { - this.logger.info('Node is not part of a cluster'); + this.logger.info('Node is not part of a cluster (empty response data)'); return { isCluster: false, clusterName: '' }; } } catch (error: any) { diff --git a/src/api/proxmox/guests.ts b/src/api/proxmox/guests.ts index 391c8eb30..08559669d 100644 --- a/src/api/proxmox/guests.ts +++ b/src/api/proxmox/guests.ts @@ -1,5 +1,27 @@ import { ProxmoxClient } from './index'; import { ProxmoxVM, ProxmoxContainer } from '../../types'; +import config from '../../config'; + +/** + * Generate a unique ID for a VM or container + * @param type The type of guest ('qemu' or 'lxc') + * @param vmid The VM ID + * @param nodeId The node ID + * @returns A unique ID string + */ +function generateGuestId(type: 'qemu' | 'lxc', vmid: number, nodeId: string): string { + // In cluster mode, use the cluster name instead of the node ID + if (config.clusterMode) { + return type === 'qemu' + ? `${config.clusterName}-vm-${vmid}` + : `${config.clusterName}-ct-${vmid}`; + } else { + // In non-cluster mode, use the node ID + return type === 'qemu' + ? `${nodeId}-vm-${vmid}` + : `${nodeId}-ct-${vmid}`; + } +} /** * Get all virtual machines for the node @@ -30,7 +52,7 @@ export async function getVirtualMachines(this: ProxmoxClient): Promise { try { @@ -119,56 +144,53 @@ export async function getContainers(this: ProxmoxClient): Promise setTimeout(resolve, 500)); - } } return results; } catch (error) { - this.logger.error('Failed to get containers', { error }); + this.logger.error('Error getting containers', { error }); return []; } } diff --git a/src/api/proxmox/index.ts b/src/api/proxmox/index.ts index 3a5ebc5db..5954c09e2 100644 --- a/src/api/proxmox/index.ts +++ b/src/api/proxmox/index.ts @@ -37,6 +37,21 @@ export class ProxmoxClient extends EventEmitter implements ProxmoxClientMethods this.retryAttempts = parseInt(process.env.API_RETRY_ATTEMPTS || '3', 10); this.retryDelayMs = parseInt(process.env.API_RETRY_DELAY_MS || '5000', 10); + // Determine if SSL verification should be disabled + // Check multiple environment variables that could control SSL verification + const disableSSLVerification = + ignoreSSLErrors || + process.env.PROXMOX_REJECT_UNAUTHORIZED === 'false' || + process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0' || + process.env.HTTPS_REJECT_UNAUTHORIZED === 'false' || + process.env.PROXMOX_INSECURE === 'true' || + process.env.PROXMOX_VERIFY_SSL === 'false' || + process.env.IGNORE_SSL_ERRORS === 'true'; + + if (disableSSLVerification) { + this.logger.warn('SSL certificate verification is disabled. This is insecure and should only be used with trusted networks.'); + } + // Create axios instance with base configuration const axiosConfig = { baseURL: `${config.host}/api2/json`, @@ -45,7 +60,7 @@ export class ProxmoxClient extends EventEmitter implements ProxmoxClientMethods }, timeout: apiTimeoutMs, httpsAgent: new https.Agent({ - rejectUnauthorized: !ignoreSSLErrors + rejectUnauthorized: !disableSSLVerification }) }; @@ -100,6 +115,11 @@ export class ProxmoxClient extends EventEmitter implements ProxmoxClientMethods return Promise.reject(error); } ); + + // Initialize cluster detection if auto-detection is enabled + if (config.autoDetectCluster) { + this.initializeClusterDetection(); + } } } @@ -139,6 +159,39 @@ export class ProxmoxClient extends EventEmitter implements ProxmoxClientMethods return false; } } + + /** + * Initialize cluster detection + * This method checks if the node is part of a cluster and updates the configuration accordingly + */ + private async initializeClusterDetection(): Promise { + try { + // Only auto-detect if the setting is enabled + if (!config.autoDetectCluster) { + this.logger.info('Cluster auto-detection is disabled. Using manual cluster mode setting.'); + return; + } + + // Check if the node is part of a cluster + const { isCluster, clusterName } = await this.isNodeInCluster(); + + if (isCluster) { + // If the node is part of a cluster, update the global config + this.logger.info(`Node is part of cluster: ${clusterName}. Enabling cluster mode.`); + + // Update the global config to enable cluster mode + // This will affect how IDs are generated for VMs and containers + config.clusterMode = true; + config.clusterName = clusterName; + } else { + this.logger.info('Node is not part of a cluster. Cluster mode will not be enabled.'); + // Explicitly disable cluster mode when not in a cluster + config.clusterMode = false; + } + } catch (error) { + this.logger.error('Error initializing cluster detection', { error }); + } + } } // Import functionality after the class definition to avoid circular dependencies