#!/usr/bin/env python3 import os import requests import json from urllib.parse import urljoin class NetBoxManager: 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 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 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 create_site(self, name, slug=None, status='active', comments=''): """Create a new site""" if not slug: slug = name.lower().replace(' ', '-') site_data = { 'name': name, 'slug': slug, 'status': status, 'comments': comments } return self.post('dcim/sites/', site_data) def create_device(self, name, device_type_id, role_id, site_id, status='active'): """Create a new device""" device_data = { 'name': name, 'device_type': device_type_id, 'role': role_id, 'site': site_id, 'status': status } return self.post('dcim/devices/', device_data) def get_or_create_manufacturer(self, name): """Get or create a manufacturer""" response = self.get('dcim/manufacturers/', params={'name': name}) if response['count'] > 0: return response['results'][0]['id'] # Create manufacturer mfg_data = { 'name': name, 'slug': name.lower() } created = self.post('dcim/manufacturers/', mfg_data) return created['id'] def get_or_create_device_type(self, model, manufacturer_id): """Get or create a device type""" response = self.get('dcim/device-types/', params={'model': model}) if response['count'] > 0: return response['results'][0]['id'] # Create device type dt_data = { 'manufacturer': manufacturer_id, 'model': model, 'slug': model.lower().replace(' ', '-').replace('/', '-') } created = self.post('dcim/device-types/', dt_data) return created['id'] def get_or_create_device_role(self, name, slug=None): """Get or create a device role""" if not slug: slug = name.lower().replace(' ', '-') response = self.get('dcim/device-roles/', params={'name': name}) if response['count'] > 0: return response['results'][0]['id'] # Create device role role_data = { 'name': name, 'slug': slug, 'color': '2196f3' # Blue color } created = self.post('dcim/device-roles/', role_data) return created['id'] def main(): # Get API token from environment variable or use the one from CLAUDE.md api_token = os.environ.get('NETBOX_KEY', 'e50298f7fd20f7fd6f1931f635511b34f6e8cfde') # Initialize NetBox client nb = NetBoxManager('https://netbox.vntx.net/', api_token) print("=== Creating Verona Site and Router in NetBox ===\n") # Check if Verona site exists site_response = nb.get('dcim/sites/', params={'name': 'Verona'}) if site_response['count'] == 0: # Create Verona site print("Creating Verona site...") try: site = nb.create_site( name='Verona', slug='verona', status='active', comments='Verona tower site with MikroTik router' ) site_id = site['id'] print(f"✓ Created Verona site (ID: {site_id})") except Exception as e: print(f"✗ Failed to create Verona site: {e}") return else: site_id = site_response['results'][0]['id'] print(f"✓ Verona site already exists (ID: {site_id})") # Check if router already exists device_response = nb.get('dcim/devices/', params={'name': 'verona-router', 'site_id': site_id}) if device_response['count'] == 0: print("\nCreating Verona router device...") # Get or create MikroTik manufacturer print(" - Setting up manufacturer...") manufacturer_id = nb.get_or_create_manufacturer('MikroTik') # Get or create device type (assuming RouterBOARD or generic) print(" - Setting up device type...") device_type_id = nb.get_or_create_device_type('RouterBOARD', manufacturer_id) # Get or create device role print(" - Setting up device role...") role_id = nb.get_or_create_device_role('Router') # Create the device try: device = nb.create_device( name='verona-router', device_type_id=device_type_id, role_id=role_id, site_id=site_id, status='active' ) device_id = device['id'] print(f"✓ Created Verona router device (ID: {device_id})") # Add primary IP print("\n - Adding primary IP address...") ip_data = { 'address': '10.254.254.101/32', 'status': 'active', 'description': 'Verona router loopback', 'role': 'loopback' } try: ip_response = nb.post('ipam/ip-addresses/', ip_data) ip_id = ip_response['id'] # Set as primary IP for the device device_update = { 'primary_ip4': ip_id } nb.session.patch(f"{nb.api_url}dcim/devices/{device_id}/", json=device_update) print("✓ Added primary IP address") except Exception as e: print(f"✗ Failed to add primary IP: {e}") except Exception as e: print(f"✗ Failed to create Verona router: {e}") return else: device_id = device_response['results'][0]['id'] print(f"✓ Verona router already exists (ID: {device_id})") print(f"\n=== Summary ===") print(f"Site: Verona (ID: {site_id})") print(f"Device: verona-router (ID: {device_id})") print(f"\nYou can now run update_netbox_verona.py to add all the subnet and interface data.") if __name__ == "__main__": main()