#!/usr/bin/env python3 import os import sys import json import argparse import requests from urllib.parse import urljoin class NetBoxUpdater: def __init__(self, url, token): self.base_url = url.rstrip('/') self.api_url = urljoin(self.base_url + '/', 'api/') self.headers = { 'Authorization': f'Token {token}', 'Content-Type': 'application/json', 'Accept': 'application/json' } self.session = requests.Session() self.session.headers.update(self.headers) def post(self, endpoint, data): """Make POST request to NetBox API""" url = urljoin(self.api_url, endpoint.lstrip('/')) response = self.session.post(url, json=data) if response.status_code not in [200, 201]: print(f"Error creating {endpoint}: {response.status_code}") print(f"Response: {response.text}") response.raise_for_status() return response.json() def patch(self, endpoint, data): """Make PATCH request to NetBox API""" url = urljoin(self.api_url, endpoint.lstrip('/')) response = self.session.patch(url, json=data) if response.status_code not in [200, 201]: print(f"Error updating {endpoint}: {response.status_code}") print(f"Response: {response.text}") response.raise_for_status() return response.json() def get(self, endpoint, params=None): """Make GET request to NetBox API""" url = urljoin(self.api_url, endpoint.lstrip('/')) response = self.session.get(url, params=params) response.raise_for_status() return response.json() def get_or_create_role(self, name, slug=None): """Get or create an IPAM role""" if not slug: slug = name.lower().replace(' ', '-') response = self.get('ipam/roles/', params={'slug': slug}) if response['count'] > 0: return response['results'][0]['id'] # Create role role_data = { 'name': name, 'slug': slug } created = self.post('ipam/roles/', role_data) return created['id'] def determine_prefix_role(interface, description=''): """Determine the role of a prefix based on interface name and description""" interface_lower = str(interface).lower() # Infrastructure links if any(term in interface_lower for term in ['airfiber', '11ghz', '24ghz', 'backhaul', 'ether3', 'ether4', 'ether5', 'ether6']): return 'infrastructure' # Loopback if 'loopback' in interface_lower or interface == 'lo': return 'loopback' # Management if any(term in interface_lower for term in ['mgmt', 'management', 'ether1']): return 'management' # Customer bridges if 'bridge' in interface_lower or 'pppoe' in interface_lower: return 'customer' # VLANs are often customer-facing if 'vlan' in interface_lower: return 'customer' return 'infrastructure' # Default def process_router_data(json_file, site_name, dry_run=False): """Process router data JSON and update NetBox""" # Load router data with open(json_file, 'r') as f: router_data = json.load(f) # Get API token api_token = os.environ.get('NETBOX_KEY', 'e50298f7fd20f7fd6f1931f635511b34f6e8cfde') nb = NetBoxUpdater('https://netbox.vntx.net/', api_token) print(f"=== Processing Router Data for {site_name} ===\n") # Get site ID site_response = nb.get('dcim/sites/', params={'name': site_name}) if site_response['count'] == 0: print(f"Error: Site '{site_name}' not found in NetBox") print(f"Please create the site first using: python3 create_site_and_router.py {site_name} {router_data['host']}") return False site_id = site_response['results'][0]['id'] print(f"Found site: {site_name} (ID: {site_id})") # Get device device_response = nb.get('dcim/devices/', params={'site_id': site_id}) if device_response['count'] == 0: print(f"Error: No device found for site '{site_name}'") return False device = device_response['results'][0] device_id = device['id'] device_name = device['name'] print(f"Found device: {device_name} (ID: {device_id})\n") # Get or create roles role_mapping = { 'infrastructure': 'Infrastructure', 'loopback': 'Loopback', 'customer': 'Customer', 'management': 'Management' } if not dry_run: print("Setting up IPAM roles...") role_ids = {} for key, name in role_mapping.items(): role_ids[key] = nb.get_or_create_role(name, key) print(f" ✓ {name}") print() # Process subnets (non-dynamic only) static_subnets = [s for s in router_data['subnets'] if not s.get('dynamic', False)] print(f"=== Processing {len(static_subnets)} Static Subnets ===") prefixes_to_create = [] ips_to_create = [] for subnet in static_subnets: address = subnet['address'] network = subnet['network'] interface = subnet['interface'] comment = subnet.get('comment', '') # Skip PPPoE dynamic IPs if str(interface).startswith('