n8n-nodes-proxmox
Version:
n8n community node for Proxmox Virtual Environment (VE) API integration with VM, container, storage, and cluster management capabilities
398 lines (397 loc) • 19.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeContainerOperation = executeContainerOperation;
const apiRequest_1 = require("../helpers/apiRequest");
const errorHandler_1 = require("../helpers/errorHandler");
const logger_1 = require("../helpers/logger");
async function executeContainerOperation(operation, i) {
try {
logger_1.logger.operation('container', operation, 'start', { operation });
let response;
switch (operation) {
case 'create':
response = await createContainer.call(this, i);
break;
case 'delete':
response = await deleteContainer.call(this, i);
break;
case 'get':
response = await getContainer.call(this, i);
break;
case 'getAll':
response = await getAllContainers.call(this, i);
break;
case 'start':
response = await startContainer.call(this, i);
break;
case 'stop':
response = await stopContainer.call(this, i);
break;
case 'shutdown':
response = await shutdownContainer.call(this, i);
break;
case 'reboot':
response = await rebootContainer.call(this, i);
break;
case 'suspend':
response = await suspendContainer.call(this, i);
break;
case 'resume':
response = await resumeContainer.call(this, i);
break;
case 'clone':
response = await cloneContainer.call(this, i);
break;
case 'migrate':
response = await migrateContainer.call(this, i);
break;
case 'snapshot':
response = await snapshotContainer.call(this, i);
break;
case 'getStatus':
response = await getContainerStatus.call(this, i);
break;
default:
throw new Error(`Unknown container operation: ${operation}`);
}
logger_1.logger.operation('container', operation, 'success', { operation });
return response;
}
catch (error) {
logger_1.logger.operation('container', operation, 'error', { operation, error: error.message });
return errorHandler_1.handleProxMoxError.call(this, error, operation, 'container');
}
}
async function waitForTask(nodeName, taskId, timeout = 300) {
const startTime = Date.now();
const timeoutMs = timeout * 1000;
logger_1.logger.debug('task', `Waiting for task ${taskId} on node ${nodeName}`, { taskId, timeout });
while (Date.now() - startTime < timeoutMs) {
try {
const taskStatus = await apiRequest_1.apiRequest.call(this, 'GET', `/nodes/${nodeName}/tasks/${taskId}/status`);
if (taskStatus.status === 'stopped') {
if (taskStatus.exitstatus === 'OK') {
logger_1.logger.debug('task', `Task ${taskId} completed successfully`);
return taskStatus;
}
else {
throw new Error(`Task failed: ${taskStatus.exitstatus}`);
}
}
// Wait 2 seconds before checking again
await new Promise((resolve) => setTimeout(resolve, 2000));
}
catch (error) {
logger_1.logger.error('task', `Error checking task status: ${error.message}`);
throw error;
}
}
throw new Error(`Task ${taskId} timed out after ${timeout} seconds`);
}
async function createContainer(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const newVmId = this.getNodeParameter('newVmId', i);
const ostemplate = this.getNodeParameter('ostemplate', i);
const waitForTask = this.getNodeParameter('waitForTask', i, true);
const taskTimeout = this.getNodeParameter('taskTimeout', i, 300);
const additionalOptions = this.getNodeParameter('additionalOptions', i, {});
errorHandler_1.validateParameters.call(this, { nodeName, newVmId, ostemplate }, ['nodeName', 'newVmId', 'ostemplate'], 'create container');
const body = {
vmid: newVmId,
ostemplate,
...additionalOptions,
};
// Set defaults for container creation
if (!body.memory)
body.memory = 512;
if (!body.swap)
body.swap = 512;
if (!body.cores)
body.cores = 1;
if (!body.storage)
body.storage = 'local-lvm';
if (!body.rootfs)
body.rootfs = `${body.storage}:8`;
if (!body.net0)
body.net0 = 'name=eth0,bridge=vmbr0,firewall=1,ip=dhcp';
if (body.unprivileged === undefined)
body.unprivileged = 1;
logger_1.logger.debug('container', 'Creating container', { nodeName, vmId: newVmId, ostemplate });
const response = await apiRequest_1.apiRequest.call(this, 'POST', `/nodes/${nodeName}/lxc`, body);
if (waitForTask && response.data) {
const taskResult = await waitForTask.call(this, nodeName, response.data, taskTimeout);
// Start container if requested
if (additionalOptions.startAfterCreate) {
logger_1.logger.debug('container', 'Starting container after creation');
await apiRequest_1.apiRequest.call(this, 'POST', `/nodes/${nodeName}/lxc/${newVmId}/status/start`);
}
return { ...response, taskResult };
}
return response;
}
async function deleteContainer(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const vmId = this.getNodeParameter('vmId', i);
const waitForTask = this.getNodeParameter('waitForTask', i, true);
const taskTimeout = this.getNodeParameter('taskTimeout', i, 300);
errorHandler_1.validateParameters.call(this, { nodeName, vmId }, ['nodeName', 'vmId'], 'delete container');
logger_1.logger.debug('container', 'Deleting container', { nodeName, vmId });
const response = await apiRequest_1.apiRequest.call(this, 'DELETE', `/nodes/${nodeName}/lxc/${vmId}`);
if (waitForTask && response.data) {
const taskResult = await waitForTask.call(this, nodeName, response.data, taskTimeout);
return { ...response, taskResult };
}
return response;
}
async function getContainer(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const vmId = this.getNodeParameter('vmId', i);
errorHandler_1.validateParameters.call(this, { nodeName, vmId }, ['nodeName', 'vmId'], 'get container');
logger_1.logger.debug('container', 'Getting container details', { nodeName, vmId });
return await apiRequest_1.apiRequest.call(this, 'GET', `/nodes/${nodeName}/lxc/${vmId}/config`);
}
async function getAllContainers(i) {
const returnAll = this.getNodeParameter('returnAll', i);
logger_1.logger.debug('container', 'Getting all containers');
console.log('=== getAllContainers Debug ===');
console.log('returnAll parameter:', returnAll);
if (returnAll) {
console.log('Making API request for all VMs (includes containers)...');
// Get all VMs and filter for LXC containers (type: 'lxc' in response data)
const response = await apiRequest_1.apiRequestAllItems.call(this, 'GET', '/cluster/resources', {}, { type: 'vm' });
console.log('API response received, response type:', typeof response, 'isArray:', Array.isArray(response));
console.log('Sample raw response data:', JSON.stringify(Array.isArray(response) ? response.slice(0, 3) : response, null, 2));
// The ProxMox API should return an array directly for apiRequestAllItems
let allResources;
if (Array.isArray(response)) {
allResources = response;
console.log('Response is direct array with', allResources.length, 'resources');
}
else if (response && response.data && Array.isArray(response.data)) {
allResources = response.data;
console.log('Found data array with', allResources.length, 'resources');
}
else {
allResources = [response];
console.log('Response is single object, converting to array');
}
// Filter for LXC containers
const containers = allResources.filter((resource) => {
console.log('Checking resource:', JSON.stringify({ id: resource.id, type: resource.type, vmid: resource.vmid }, null, 2));
return resource.type === 'lxc';
});
console.log('Filtered containers count:', containers.length);
console.log('Filtered containers:', JSON.stringify(containers.slice(0, 2), null, 2));
return containers;
}
else {
const limit = this.getNodeParameter('limit', i);
console.log('Making limited API request for VMs (includes containers), limit:', limit);
// Get all VMs and filter for LXC containers
const response = await apiRequest_1.apiRequest.call(this, 'GET', '/cluster/resources', {}, { type: 'vm' });
console.log('Limited API response type:', typeof response, 'isArray:', Array.isArray(response));
console.log('Limited API response received:', JSON.stringify(Array.isArray(response) ? response.slice(0, 3) : response, null, 2));
// The ProxMox API wraps the data in a 'data' property
let allResources;
if (response && response.data && Array.isArray(response.data)) {
allResources = response.data;
console.log('Found data array with', allResources.length, 'resources');
}
else if (Array.isArray(response)) {
allResources = response;
console.log('Response is direct array with', allResources.length, 'resources');
}
else {
allResources = [response];
console.log('Response is single object, converting to array');
}
// Filter for LXC containers and then apply limit
const containers = allResources.filter((resource) => {
console.log('Checking resource:', JSON.stringify({ id: resource.id, type: resource.type, vmid: resource.vmid }, null, 2));
return resource.type === 'lxc';
});
console.log('Filtered containers count before limit:', containers.length);
return containers.slice(0, limit);
}
}
async function startContainer(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const vmId = this.getNodeParameter('vmId', i);
const waitForTask = this.getNodeParameter('waitForTask', i, true);
const taskTimeout = this.getNodeParameter('taskTimeout', i, 300);
errorHandler_1.validateParameters.call(this, { nodeName, vmId }, ['nodeName', 'vmId'], 'start container');
logger_1.logger.debug('container', 'Starting container', { nodeName, vmId });
const response = await apiRequest_1.apiRequest.call(this, 'POST', `/nodes/${nodeName}/lxc/${vmId}/status/start`);
if (waitForTask && response.data) {
const taskResult = await waitForTask.call(this, nodeName, response.data, taskTimeout);
return { ...response, taskResult };
}
return response;
}
async function stopContainer(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const vmId = this.getNodeParameter('vmId', i);
const waitForTask = this.getNodeParameter('waitForTask', i, true);
const taskTimeout = this.getNodeParameter('taskTimeout', i, 300);
const additionalOptions = this.getNodeParameter('additionalOptions', i, {});
errorHandler_1.validateParameters.call(this, { nodeName, vmId }, ['nodeName', 'vmId'], 'stop container');
const body = {};
if (additionalOptions.force)
body.force = 1;
if (additionalOptions.skipLock)
body.skiplock = 1;
logger_1.logger.debug('container', 'Stopping container', { nodeName, vmId, options: additionalOptions });
const response = await apiRequest_1.apiRequest.call(this, 'POST', `/nodes/${nodeName}/lxc/${vmId}/status/stop`, body);
if (waitForTask && response.data) {
const taskResult = await waitForTask.call(this, nodeName, response.data, taskTimeout);
return { ...response, taskResult };
}
return response;
}
async function shutdownContainer(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const vmId = this.getNodeParameter('vmId', i);
const waitForTask = this.getNodeParameter('waitForTask', i, true);
const taskTimeout = this.getNodeParameter('taskTimeout', i, 300);
const additionalOptions = this.getNodeParameter('additionalOptions', i, {});
errorHandler_1.validateParameters.call(this, { nodeName, vmId }, ['nodeName', 'vmId'], 'shutdown container');
const body = {};
if (additionalOptions.force)
body.forceStop = 1;
if (additionalOptions.skipLock)
body.skiplock = 1;
logger_1.logger.debug('container', 'Shutting down container', {
nodeName,
vmId,
options: additionalOptions,
});
const response = await apiRequest_1.apiRequest.call(this, 'POST', `/nodes/${nodeName}/lxc/${vmId}/status/shutdown`, body);
if (waitForTask && response.data) {
const taskResult = await waitForTask.call(this, nodeName, response.data, taskTimeout);
return { ...response, taskResult };
}
return response;
}
async function rebootContainer(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const vmId = this.getNodeParameter('vmId', i);
const waitForTask = this.getNodeParameter('waitForTask', i, true);
const taskTimeout = this.getNodeParameter('taskTimeout', i, 300);
errorHandler_1.validateParameters.call(this, { nodeName, vmId }, ['nodeName', 'vmId'], 'reboot container');
logger_1.logger.debug('container', 'Rebooting container', { nodeName, vmId });
const response = await apiRequest_1.apiRequest.call(this, 'POST', `/nodes/${nodeName}/lxc/${vmId}/status/reboot`);
if (waitForTask && response.data) {
const taskResult = await waitForTask.call(this, nodeName, response.data, taskTimeout);
return { ...response, taskResult };
}
return response;
}
async function suspendContainer(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const vmId = this.getNodeParameter('vmId', i);
const waitForTask = this.getNodeParameter('waitForTask', i, true);
const taskTimeout = this.getNodeParameter('taskTimeout', i, 300);
errorHandler_1.validateParameters.call(this, { nodeName, vmId }, ['nodeName', 'vmId'], 'suspend container');
logger_1.logger.debug('container', 'Suspending container', { nodeName, vmId });
const response = await apiRequest_1.apiRequest.call(this, 'POST', `/nodes/${nodeName}/lxc/${vmId}/status/suspend`);
if (waitForTask && response.data) {
const taskResult = await waitForTask.call(this, nodeName, response.data, taskTimeout);
return { ...response, taskResult };
}
return response;
}
async function resumeContainer(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const vmId = this.getNodeParameter('vmId', i);
const waitForTask = this.getNodeParameter('waitForTask', i, true);
const taskTimeout = this.getNodeParameter('taskTimeout', i, 300);
errorHandler_1.validateParameters.call(this, { nodeName, vmId }, ['nodeName', 'vmId'], 'resume container');
logger_1.logger.debug('container', 'Resuming container', { nodeName, vmId });
const response = await apiRequest_1.apiRequest.call(this, 'POST', `/nodes/${nodeName}/lxc/${vmId}/status/resume`);
if (waitForTask && response.data) {
const taskResult = await waitForTask.call(this, nodeName, response.data, taskTimeout);
return { ...response, taskResult };
}
return response;
}
async function cloneContainer(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const vmId = this.getNodeParameter('vmId', i);
const newVmId = this.getNodeParameter('newVmId', i);
const cloneName = this.getNodeParameter('cloneName', i, '');
const waitForTask = this.getNodeParameter('waitForTask', i, true);
const taskTimeout = this.getNodeParameter('taskTimeout', i, 300);
const additionalOptions = this.getNodeParameter('additionalOptions', i, {});
errorHandler_1.validateParameters.call(this, { nodeName, vmId, newVmId }, ['nodeName', 'vmId', 'newVmId'], 'clone container');
const body = {
newid: newVmId,
...additionalOptions,
};
if (cloneName) {
body.hostname = cloneName;
}
logger_1.logger.debug('container', 'Cloning container', { nodeName, vmId, newVmId, cloneName });
const response = await apiRequest_1.apiRequest.call(this, 'POST', `/nodes/${nodeName}/lxc/${vmId}/clone`, body);
if (waitForTask && response.data) {
const taskResult = await waitForTask.call(this, nodeName, response.data, taskTimeout);
return { ...response, taskResult };
}
return response;
}
async function migrateContainer(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const vmId = this.getNodeParameter('vmId', i);
const targetNode = this.getNodeParameter('targetNode', i);
const waitForTask = this.getNodeParameter('waitForTask', i, true);
const taskTimeout = this.getNodeParameter('taskTimeout', i, 300);
const additionalOptions = this.getNodeParameter('additionalOptions', i, {});
errorHandler_1.validateParameters.call(this, { nodeName, vmId, targetNode }, ['nodeName', 'vmId', 'targetNode'], 'migrate container');
const body = {
target: targetNode,
...additionalOptions,
};
// Set default migration options
if (additionalOptions.online !== false) {
body.online = 1;
}
if (additionalOptions.restart) {
body.restart = 1;
}
logger_1.logger.debug('container', 'Migrating container', {
nodeName,
vmId,
targetNode,
options: additionalOptions,
});
const response = await apiRequest_1.apiRequest.call(this, 'POST', `/nodes/${nodeName}/lxc/${vmId}/migrate`, body);
if (waitForTask && response.data) {
const taskResult = await waitForTask.call(this, nodeName, response.data, taskTimeout);
return { ...response, taskResult };
}
return response;
}
async function snapshotContainer(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const vmId = this.getNodeParameter('vmId', i);
const snapshotName = this.getNodeParameter('snapshotName', i);
const waitForTask = this.getNodeParameter('waitForTask', i, true);
const taskTimeout = this.getNodeParameter('taskTimeout', i, 300);
errorHandler_1.validateParameters.call(this, { nodeName, vmId, snapshotName }, ['nodeName', 'vmId', 'snapshotName'], 'snapshot container');
const body = {
snapname: snapshotName,
};
logger_1.logger.debug('container', 'Creating container snapshot', { nodeName, vmId, snapshotName });
const response = await apiRequest_1.apiRequest.call(this, 'POST', `/nodes/${nodeName}/lxc/${vmId}/snapshot`, body);
if (waitForTask && response.data) {
const taskResult = await waitForTask.call(this, nodeName, response.data, taskTimeout);
return { ...response, taskResult };
}
return response;
}
async function getContainerStatus(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const vmId = this.getNodeParameter('vmId', i);
errorHandler_1.validateParameters.call(this, { nodeName, vmId }, ['nodeName', 'vmId'], 'get container status');
logger_1.logger.debug('container', 'Getting container status', { nodeName, vmId });
return await apiRequest_1.apiRequest.call(this, 'GET', `/nodes/${nodeName}/lxc/${vmId}/status/current`);
}