UNPKG

n8n-nodes-netbox

Version:

n8n community node for NetBox API integration with comprehensive DCIM, IPAM, Virtualization, Circuits, Wireless, and data center management operations

450 lines (449 loc) 20.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.listPrefixes = listPrefixes; exports.getPrefix = getPrefix; exports.createPrefix = createPrefix; exports.updatePrefix = updatePrefix; exports.deletePrefix = deletePrefix; exports.getAvailableIPs = getAvailableIPs; exports.addTags = addTags; const apiRequest_1 = require("../../../helpers/apiRequest"); const responseFormatter_1 = require("../../../helpers/responseFormatter"); const resourceLookup_1 = require("../../../helpers/resourceLookup"); async function listPrefixes() { try { // Get pagination parameters const returnAll = this.getNodeParameter('returnAll', 0); // Get filters const filters = this.getNodeParameter('filters', 0, {}); // Prepare query parameters const queryParams = { ...filters }; // Handle array parameters (comma-separated values) [ 'site', 'site_id', 'vrf', 'vrf_id', 'tenant', 'tenant_id', 'role', 'role_id', 'tag', 'parent', 'id', ].forEach((param) => { if (queryParams[param] && typeof queryParams[param] === 'string') { // Convert comma-separated strings to arrays for API if (queryParams[param].includes(',')) { queryParams[param] = queryParams[param].split(',').map((item) => item.trim()); } } }); console.log('List Prefixes Query Params:', queryParams); let responseData; if (returnAll === true) { responseData = await apiRequest_1.apiRequestAllItems.call(this, 'GET', '/api/ipam/prefixes/', {}, queryParams); return responseFormatter_1.formatResponse.call(this, responseData); } else { const limit = this.getNodeParameter('limit', 0); queryParams.limit = limit; responseData = await apiRequest_1.apiRequest.call(this, 'GET', '/api/ipam/prefixes/', {}, queryParams); return responseFormatter_1.formatResponse.call(this, responseData); } } catch (error) { console.log('ERROR in listPrefixes:', error); throw error; } } async function getPrefix() { try { const prefixIdOrCidr = this.getNodeParameter('prefixId', 0); // Check if prefixId is numeric or a CIDR notation let endpoint; if (!isNaN(Number(prefixIdOrCidr))) { // If it's a numeric ID, use it directly endpoint = `/api/ipam/prefixes/${prefixIdOrCidr}/`; } else { // If it's not numeric, try to find by CIDR notation console.log(`Attempting to find prefix by CIDR: ${prefixIdOrCidr}`); const filters = { prefix: prefixIdOrCidr, }; const prefixes = await apiRequest_1.apiRequest.call(this, 'GET', '/api/ipam/prefixes/', {}, filters); if (prefixes.results && prefixes.results.length > 0) { // Found a matching prefix, use its ID const prefix = prefixes.results[0]; console.log(`Found prefix with ID: ${prefix.id}`); endpoint = `/api/ipam/prefixes/${prefix.id}/`; } else { throw new Error(`Could not find prefix with CIDR notation: ${prefixIdOrCidr}`); } } console.log(`Getting prefix with endpoint: ${endpoint}`); const response = await apiRequest_1.apiRequest.call(this, 'GET', endpoint, {}, {}); return responseFormatter_1.formatResponse.call(this, response); } catch (error) { console.log('ERROR in getPrefix:', error); throw error; } } async function createPrefix() { try { const useRawJson = this.getNodeParameter('useRawJson', 0, false); let prefixData; if (useRawJson) { // Use raw JSON input const prefixDataRaw = this.getNodeParameter('prefixData', 0); if (typeof prefixDataRaw === 'string') { try { prefixData = JSON.parse(prefixDataRaw); } catch (parseError) { throw new Error(`Invalid JSON in prefix data: ${parseError.message}`); } } else { prefixData = prefixDataRaw; } } else { // Build from individual fields const prefix = this.getNodeParameter('prefix', 0); const status = this.getNodeParameter('status', 0); const additionalFields = this.getNodeParameter('additionalFields', 0, {}); prefixData = { prefix, status, }; // Add additional fields only if they have values Object.keys(additionalFields).forEach((key) => { const value = additionalFields[key]; if (value !== undefined && value !== null && value !== '') { prefixData[key] = value; } }); // Handle nested object lookups using the resourceLookup helper const nestedFields = ['site', 'vrf', 'tenant', 'role']; for (const field of nestedFields) { if (prefixData[field] && typeof prefixData[field] === 'string') { try { const resourceType = field === 'vrf' ? 'vrfs' : `${field}s`; const domain = field === 'site' ? 'dcim' : field === 'tenant' ? 'tenancy' : 'ipam'; console.log(`Looking up ${field}: ${prefixData[field]} in domain ${domain}`); const resourceId = await resourceLookup_1.lookupResourceByName.call(this, resourceType, prefixData[field], domain); console.log(`Found ${field} ID: ${resourceId}`); prefixData[field] = resourceId; } catch (lookupError) { console.log(`Warning: Could not lookup ${field}: ${lookupError.message}`); // Continue without the field rather than failing delete prefixData[field]; } } } // Handle VLAN - convert to proper format if (prefixData.vlan_id && prefixData.vlan_id !== 0) { prefixData.vlan = prefixData.vlan_id; delete prefixData.vlan_id; } // Handle tags - convert comma-separated string to array of objects if (prefixData.tags && typeof prefixData.tags === 'string') { const tagNames = prefixData.tags.split(',').map((tag) => tag.trim()); prefixData.tags = tagNames.map((name) => ({ name })); } // Clean up any empty or zero values that might cause validation issues const fieldsToCheck = ['site', 'vrf', 'tenant', 'role', 'vlan', 'description', 'comments']; fieldsToCheck.forEach((field) => { if (prefixData[field] === '' || prefixData[field] === 0 || prefixData[field] === null) { delete prefixData[field]; } }); } // Validate required fields if (!prefixData.prefix) { throw new Error('Prefix field is required'); } console.log('Final prefix data being sent:', JSON.stringify(prefixData, null, 2)); const endpoint = '/api/ipam/prefixes/'; const response = await apiRequest_1.apiRequest.call(this, 'POST', endpoint, prefixData, {}); return responseFormatter_1.formatResponse.call(this, response); } catch (error) { console.log('ERROR in createPrefix:', error); throw error; } } async function updatePrefix() { try { const prefixIdOrCidr = this.getNodeParameter('prefixId', 0); const useRawJson = this.getNodeParameter('useRawJson', 0, false); let prefixData; if (useRawJson) { // Use raw JSON input const prefixDataRaw = this.getNodeParameter('prefixData', 0); if (typeof prefixDataRaw === 'string') { try { prefixData = JSON.parse(prefixDataRaw); } catch (parseError) { throw new Error(`Invalid JSON in prefix data: ${parseError.message}`); } } else { prefixData = prefixDataRaw; } } else { // Build from individual fields const updateFields = this.getNodeParameter('updateFields', 0, {}); prefixData = { ...updateFields }; // Handle nested object lookups using the resourceLookup helper const nestedFields = ['site', 'vrf', 'tenant', 'role']; for (const field of nestedFields) { if (updateFields[field] && typeof updateFields[field] === 'string') { try { const resourceType = field === 'vrf' ? 'vrfs' : `${field}s`; const domain = field === 'site' ? 'dcim' : field === 'tenant' ? 'tenancy' : 'ipam'; const resourceId = await resourceLookup_1.lookupResourceByName.call(this, resourceType, updateFields[field], domain); prefixData[field] = resourceId; } catch (lookupError) { console.log(`Warning: Could not lookup ${field}: ${lookupError.message}`); // Continue without the field rather than failing delete prefixData[field]; } } } // Handle VLAN - convert to proper format if (updateFields.vlan_id && updateFields.vlan_id !== 0) { prefixData.vlan = updateFields.vlan_id; delete prefixData.vlan_id; } // Handle tags - convert comma-separated string to array of objects if (updateFields.tags && typeof updateFields.tags === 'string') { const tagNames = updateFields.tags.split(',').map((tag) => tag.trim()); prefixData.tags = tagNames.map((name) => ({ name })); } } // Determine the actual prefix ID let prefixId = prefixIdOrCidr; // If not numeric, try to find by CIDR notation if (!isNaN(Number(prefixIdOrCidr))) { prefixId = prefixIdOrCidr; // It's already a numeric ID } else { // Try to find the prefix by CIDR notation console.log(`Attempting to find prefix by CIDR: ${prefixIdOrCidr}`); const filters = { prefix: prefixIdOrCidr, }; const prefixes = await apiRequest_1.apiRequest.call(this, 'GET', '/api/ipam/prefixes/', {}, filters); if (prefixes.results && prefixes.results.length > 0) { // Found a matching prefix, use its ID prefixId = prefixes.results[0].id; console.log(`Found prefix with ID: ${prefixId}`); } else { throw new Error(`Could not find prefix with CIDR notation: ${prefixIdOrCidr}`); } } const endpoint = `/api/ipam/prefixes/${prefixId}/`; const response = await apiRequest_1.apiRequest.call(this, 'PATCH', endpoint, prefixData, {}); return responseFormatter_1.formatResponse.call(this, response); } catch (error) { console.log('ERROR in updatePrefix:', error); throw error; } } async function deletePrefix() { try { const prefixIdOrCidr = this.getNodeParameter('prefixId', 0); // Determine the actual prefix ID let prefixId = prefixIdOrCidr; // If not numeric, try to find by CIDR notation if (!isNaN(Number(prefixIdOrCidr))) { prefixId = prefixIdOrCidr; // It's already a numeric ID } else { // Try to find the prefix by CIDR notation console.log(`Attempting to find prefix by CIDR: ${prefixIdOrCidr}`); const filters = { prefix: prefixIdOrCidr, }; const prefixes = await apiRequest_1.apiRequest.call(this, 'GET', '/api/ipam/prefixes/', {}, filters); if (prefixes.results && prefixes.results.length > 0) { // Found a matching prefix, use its ID prefixId = prefixes.results[0].id; console.log(`Found prefix with ID: ${prefixId}`); } else { throw new Error(`Could not find prefix with CIDR notation: ${prefixIdOrCidr}`); } } const endpoint = `/api/ipam/prefixes/${prefixId}/`; await apiRequest_1.apiRequest.call(this, 'DELETE', endpoint, {}, {}); return [ { json: { success: true, message: `Prefix with ID ${prefixId} successfully deleted` } }, ]; } catch (error) { console.log('ERROR in deletePrefix:', error); throw error; } } async function getAvailableIPs() { try { const prefixIdOrCidr = this.getNodeParameter('prefixId', 0); // Determine the actual prefix ID let prefixId = prefixIdOrCidr; // If not numeric, try to find by CIDR notation if (!isNaN(Number(prefixIdOrCidr))) { prefixId = prefixIdOrCidr; // It's already a numeric ID } else { // Try to find the prefix by CIDR notation console.log(`Attempting to find prefix by CIDR: ${prefixIdOrCidr}`); const filters = { prefix: prefixIdOrCidr, }; const prefixes = await apiRequest_1.apiRequest.call(this, 'GET', '/api/ipam/prefixes/', {}, filters); if (prefixes.results && prefixes.results.length > 0) { // Found a matching prefix, use its ID prefixId = prefixes.results[0].id; console.log(`Found prefix with ID: ${prefixId}`); } else { throw new Error(`Could not find prefix with CIDR notation: ${prefixIdOrCidr}`); } } const endpoint = `/api/ipam/prefixes/${prefixId}/available-ips/`; const response = await apiRequest_1.apiRequest.call(this, 'GET', endpoint, {}, {}); return responseFormatter_1.formatResponse.call(this, response); } catch (error) { console.log('ERROR in getAvailableIPs:', error); throw error; } } async function addTags() { try { const prefixIdOrCidr = this.getNodeParameter('prefixId', 0); const tagsToAddInput = this.getNodeParameter('tagsToAdd', 0); const options = this.getNodeParameter('addTagsOptions', 0, {}); const createTags = options.createTags !== false; // Default to true const replaceTags = options.replaceTags === true; // Default to false // Parse the tags input const tagNames = tagsToAddInput .split(',') .map((tag) => tag.trim()) .filter((tag) => tag.length > 0); if (tagNames.length === 0) { throw new Error('At least one tag must be specified'); } // Determine the actual prefix ID let prefixId = prefixIdOrCidr; // If not numeric, try to find by CIDR notation if (!isNaN(Number(prefixIdOrCidr))) { prefixId = prefixIdOrCidr; // It's already a numeric ID } else { // Try to find the prefix by CIDR notation console.log(`Attempting to find prefix by CIDR: ${prefixIdOrCidr}`); const filters = { prefix: prefixIdOrCidr, }; const prefixes = await apiRequest_1.apiRequest.call(this, 'GET', '/api/ipam/prefixes/', {}, filters); if (prefixes.results && prefixes.results.length > 0) { // Found a matching prefix, use its ID prefixId = prefixes.results[0].id; console.log(`Found prefix with ID: ${prefixId}`); } else { throw new Error(`Could not find prefix with CIDR notation: ${prefixIdOrCidr}`); } } // Get the current prefix to see existing tags const currentPrefix = await apiRequest_1.apiRequest.call(this, 'GET', `/api/ipam/prefixes/${prefixId}/`, {}, {}); // Process tags - either create them if they don't exist or get existing ones const tagIds = []; for (const tagName of tagNames) { try { // First, try to find existing tag const existingTags = await apiRequest_1.apiRequest.call(this, 'GET', '/api/extras/tags/', {}, { name: tagName }); if (existingTags.results && existingTags.results.length > 0) { // Tag exists, use its ID tagIds.push(existingTags.results[0].id); console.log(`Found existing tag: ${tagName} with ID: ${existingTags.results[0].id}`); } else if (createTags) { // Tag doesn't exist, create it console.log(`Creating new tag: ${tagName}`); const newTag = await apiRequest_1.apiRequest.call(this, 'POST', '/api/extras/tags/', { name: tagName, slug: tagName .toLowerCase() .replace(/[^a-z0-9-]/g, '-') .replace(/-+/g, '-'), }, {}); tagIds.push(newTag.id); console.log(`Created new tag: ${tagName} with ID: ${newTag.id}`); } else { // Tag doesn't exist and we're not creating new ones console.log(`Warning: Tag '${tagName}' does not exist and createTags is disabled`); continue; } } catch (tagError) { console.log(`Error processing tag '${tagName}':`, tagError); // Continue with other tags instead of failing completely continue; } } if (tagIds.length === 0) { throw new Error('No valid tags could be processed'); } // Prepare the final tags array - using only IDs for consistency let finalTagIds; if (replaceTags) { // Replace all existing tags with new ones finalTagIds = tagIds; console.log('Replacing all existing tags'); } else { // Add to existing tags (avoid duplicates) const existingTagIds = (currentPrefix.tags || []).map((tag) => tag.id); // Combine existing and new tag IDs, removing duplicates finalTagIds = [...new Set([...existingTagIds, ...tagIds])]; console.log(`Adding ${tagIds.length} new tags to ${existingTagIds.length} existing tags`); } // Update the prefix with the new tags - send only tag IDs const updateData = { tags: finalTagIds, }; console.log(`Updating prefix ${prefixId} with tag IDs:`, finalTagIds); const endpoint = `/api/ipam/prefixes/${prefixId}/`; const response = await apiRequest_1.apiRequest.call(this, 'PATCH', endpoint, updateData, {}); // Format the response to show what was accomplished const resultData = { ...response, _tagOperation: { prefixId: prefixId, operation: replaceTags ? 'replaced' : 'added', tagsProcessed: tagNames, totalTags: response.tags ? response.tags.length : 0, }, }; return responseFormatter_1.formatResponse.call(this, resultData); } catch (error) { console.log('ERROR in addTags:', error); throw error; } }