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
JavaScript
;
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;
}
}