n8n-nodes-proxmox
Version:
n8n community node for Proxmox Virtual Environment (VE) API integration with VM, container, storage, and cluster management capabilities
205 lines (204 loc) • 8.99 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeBackupOperation = executeBackupOperation;
const apiRequest_1 = require("../helpers/apiRequest");
const errorHandler_1 = require("../helpers/errorHandler");
const logger_1 = require("../helpers/logger");
async function executeBackupOperation(operation, i) {
try {
logger_1.logger.operation('backup', operation, 'start', { operation });
let response;
switch (operation) {
case 'create':
response = await createBackup.call(this, i);
break;
case 'restore':
response = await restoreBackup.call(this, i);
break;
case 'getAll':
response = await getAllBackups.call(this, i);
break;
case 'delete':
response = await deleteBackup.call(this, i);
break;
case 'getJobs':
response = await getBackupJobs.call(this, i);
break;
default:
throw new Error(`Unknown backup operation: ${operation}`);
}
logger_1.logger.operation('backup', operation, 'success', { operation });
return response;
}
catch (error) {
logger_1.logger.operation('backup', operation, 'error', { operation, error: error.message });
return errorHandler_1.handleProxMoxError.call(this, error, operation, 'backup');
}
}
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 5 seconds before checking again (backups can take a while)
await new Promise((resolve) => setTimeout(resolve, 5000));
}
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 createBackup(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const vmId = this.getNodeParameter('vmId', i);
const storage = this.getNodeParameter('storage', i);
const waitForTask = this.getNodeParameter('waitForTask', i, true);
const taskTimeout = this.getNodeParameter('taskTimeout', i, 300);
const backupOptions = this.getNodeParameter('backupOptions', i, {});
errorHandler_1.validateParameters.call(this, { nodeName, vmId, storage }, ['nodeName', 'vmId', 'storage'], 'create backup');
const body = {
vmid: vmId,
storage,
...backupOptions,
};
// Set default backup options
if (!body.compress)
body.compress = 'zstd';
if (!body.mode)
body.mode = 'snapshot';
if (body.includeRam)
body.vmstate = 1;
if (body.remove === undefined)
body.remove = 1;
logger_1.logger.debug('backup', 'Creating backup', { nodeName, vmId, storage, options: backupOptions });
const response = await apiRequest_1.apiRequest.call(this, 'POST', `/nodes/${nodeName}/vzdump`, body);
if (waitForTask && response.data) {
const taskResult = await waitForTask.call(this, nodeName, response.data, taskTimeout);
return { ...response, taskResult };
}
return response;
}
async function restoreBackup(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const archive = this.getNodeParameter('archive', i);
const newVmId = this.getNodeParameter('newVmId', i);
const waitForTask = this.getNodeParameter('waitForTask', i, true);
const taskTimeout = this.getNodeParameter('taskTimeout', i, 300);
const restoreOptions = this.getNodeParameter('restoreOptions', i, {});
errorHandler_1.validateParameters.call(this, { nodeName, archive, newVmId }, ['nodeName', 'archive', 'newVmId'], 'restore backup');
const body = {
archive,
vmid: newVmId,
...restoreOptions,
};
// Set default restore options
if (!body.storage)
body.storage = 'local-lvm';
if (body.start)
body.start = 1;
if (body.unique)
body.unique = 1;
if (body.force)
body.force = 1;
logger_1.logger.debug('backup', 'Restoring backup', {
nodeName,
archive,
newVmId,
options: restoreOptions,
});
// Determine if it's a VM or Container backup based on the archive name
const isContainer = archive.includes('vzdump-lxc-') || archive.includes('-ct-');
const endpoint = isContainer ? `/nodes/${nodeName}/lxc` : `/nodes/${nodeName}/qemu`;
const response = await apiRequest_1.apiRequest.call(this, 'POST', endpoint, body);
if (waitForTask && response.data) {
const taskResult = await waitForTask.call(this, nodeName, response.data, taskTimeout);
return { ...response, taskResult };
}
return response;
}
async function getAllBackups(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const returnAll = this.getNodeParameter('returnAll', i);
const filterOptions = this.getNodeParameter('filterOptions', i, {});
errorHandler_1.validateParameters.call(this, { nodeName }, ['nodeName'], 'get all backups');
let qs = {
content: filterOptions.content || 'backup',
};
// Add additional filters
if (filterOptions.vmid) {
qs.vmid = filterOptions.vmid;
}
logger_1.logger.debug('backup', 'Getting all backups', { nodeName, filterOptions });
// Get backups from all storages on the node
const storagesResponse = await apiRequest_1.apiRequest.call(this, 'GET', `/nodes/${nodeName}/storage`);
// Handle different response formats
let storages;
if (Array.isArray(storagesResponse)) {
storages = storagesResponse;
}
else if (storagesResponse && typeof storagesResponse === 'object' && storagesResponse.data) {
storages = Array.isArray(storagesResponse.data)
? storagesResponse.data
: [storagesResponse.data];
}
else {
logger_1.logger.error('backup', 'Unexpected storages response format', { storagesResponse });
throw new Error('Failed to get storage list - unexpected response format');
}
const allBackups = [];
for (const storage of storages) {
if (storage.content && storage.content.includes('backup')) {
try {
const backups = await apiRequest_1.apiRequest.call(this, 'GET', `/nodes/${nodeName}/storage/${storage.storage}/content`, {}, qs);
if (Array.isArray(backups)) {
allBackups.push(...backups.map((backup) => ({ ...backup, storage: storage.storage })));
}
}
catch (error) {
logger_1.logger.warn('backup', `Failed to get backups from storage ${storage.storage}: ${error.message}`);
}
}
}
if (returnAll) {
return allBackups;
}
else {
const limit = this.getNodeParameter('limit', i);
return allBackups.slice(0, limit);
}
}
async function deleteBackup(i) {
const nodeName = this.getNodeParameter('nodeName', i);
const storage = this.getNodeParameter('storage', i);
const backupFile = this.getNodeParameter('backupFile', i);
errorHandler_1.validateParameters.call(this, { nodeName, storage, backupFile }, ['nodeName', 'storage', 'backupFile'], 'delete backup');
// The volume ID format is typically storage:path
const volumeId = `${storage}:${backupFile}`;
logger_1.logger.debug('backup', 'Deleting backup', { nodeName, storage, backupFile, volumeId });
return await apiRequest_1.apiRequest.call(this, 'DELETE', `/nodes/${nodeName}/storage/${storage}/content/${encodeURIComponent(volumeId)}`);
}
async function getBackupJobs(i) {
const returnAll = this.getNodeParameter('returnAll', i);
logger_1.logger.debug('backup', 'Getting backup jobs');
if (returnAll) {
return await apiRequest_1.apiRequestAllItems.call(this, 'GET', '/cluster/backup', {});
}
else {
const limit = this.getNodeParameter('limit', i);
const response = await apiRequest_1.apiRequest.call(this, 'GET', '/cluster/backup', {}, { limit });
return Array.isArray(response) ? response.slice(0, limit) : [response];
}
}