BREAKING CHANGE: Removed circuits, tenancy, VPN, wireless modules entirely. Stripped DCIM, IPAM, virtualization, extras to only essential tools. Deleted NETBOX_ENABLED_MODULES filtering — no longer needed. - Delete circuits.py, tenancy.py, vpn.py, wireless.py - Strip dcim.py to sites, devices, interfaces only (11 tools) - Strip ipam.py to IPs, prefixes, services only (10 tools) - Strip virtualization.py to clusters, VMs, VM interfaces only (10 tools) - Strip extras.py to tags, journal entries only (6 tools) - Remove all module filtering code from config.py and server.py - Rewrite README.md with accurate 37-tool documentation - Update CHANGELOG.md with breaking change entry - Token reduction: ~19,810 → ~3,700 (~81%) Remaining work: Update cmdb-assistant plugin skills (10 files) in follow-up PR Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
178 lines
6.6 KiB
Python
178 lines
6.6 KiB
Python
"""
|
|
IPAM (IP Address Management) tools for NetBox MCP Server.
|
|
|
|
Covers: IP Addresses, Prefixes, and Services only.
|
|
"""
|
|
import logging
|
|
from typing import List, Dict, Optional, Any
|
|
from ..netbox_client import NetBoxClient
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class IPAMTools:
|
|
"""Tools for IPAM operations in NetBox"""
|
|
|
|
def __init__(self, client: NetBoxClient):
|
|
self.client = client
|
|
self.base_endpoint = 'ipam'
|
|
|
|
# ==================== Prefixes ====================
|
|
|
|
async def list_prefixes(
|
|
self,
|
|
prefix: Optional[str] = None,
|
|
site_id: Optional[int] = None,
|
|
vrf_id: Optional[int] = None,
|
|
vlan_id: Optional[int] = None,
|
|
role_id: Optional[int] = None,
|
|
tenant_id: Optional[int] = None,
|
|
status: Optional[str] = None,
|
|
family: Optional[int] = None,
|
|
is_pool: Optional[bool] = None,
|
|
within: Optional[str] = None,
|
|
within_include: Optional[str] = None,
|
|
contains: Optional[str] = None,
|
|
**kwargs
|
|
) -> List[Dict]:
|
|
"""List all prefixes with optional filtering."""
|
|
params = {k: v for k, v in {
|
|
'prefix': prefix, 'site_id': site_id, 'vrf_id': vrf_id,
|
|
'vlan_id': vlan_id, 'role_id': role_id, 'tenant_id': tenant_id,
|
|
'status': status, 'family': family, 'is_pool': is_pool,
|
|
'within': within, 'within_include': within_include, 'contains': contains, **kwargs
|
|
}.items() if v is not None}
|
|
return self.client.list(f'{self.base_endpoint}/prefixes', params=params)
|
|
|
|
async def get_prefix(self, id: int) -> Dict:
|
|
"""Get a specific prefix by ID."""
|
|
return self.client.get(f'{self.base_endpoint}/prefixes', id)
|
|
|
|
async def create_prefix(
|
|
self,
|
|
prefix: str,
|
|
status: str = 'active',
|
|
site: Optional[int] = None,
|
|
vrf: Optional[int] = None,
|
|
vlan: Optional[int] = None,
|
|
role: Optional[int] = None,
|
|
tenant: Optional[int] = None,
|
|
is_pool: bool = False,
|
|
mark_utilized: bool = False,
|
|
description: Optional[str] = None,
|
|
**kwargs
|
|
) -> Dict:
|
|
"""Create a new prefix."""
|
|
data = {'prefix': prefix, 'status': status, 'is_pool': is_pool, 'mark_utilized': mark_utilized, **kwargs}
|
|
for key, val in [
|
|
('site', site), ('vrf', vrf), ('vlan', vlan),
|
|
('role', role), ('tenant', tenant), ('description', description)
|
|
]:
|
|
if val is not None:
|
|
data[key] = val
|
|
return self.client.create(f'{self.base_endpoint}/prefixes', data)
|
|
|
|
# ==================== IP Addresses ====================
|
|
|
|
async def list_ip_addresses(
|
|
self,
|
|
address: Optional[str] = None,
|
|
vrf_id: Optional[int] = None,
|
|
tenant_id: Optional[int] = None,
|
|
status: Optional[str] = None,
|
|
role: Optional[str] = None,
|
|
interface_id: Optional[int] = None,
|
|
device_id: Optional[int] = None,
|
|
virtual_machine_id: Optional[int] = None,
|
|
family: Optional[int] = None,
|
|
parent: Optional[str] = None,
|
|
dns_name: Optional[str] = None,
|
|
**kwargs
|
|
) -> List[Dict]:
|
|
"""List all IP addresses with optional filtering."""
|
|
params = {k: v for k, v in {
|
|
'address': address, 'vrf_id': vrf_id, 'tenant_id': tenant_id,
|
|
'status': status, 'role': role, 'interface_id': interface_id,
|
|
'device_id': device_id, 'virtual_machine_id': virtual_machine_id,
|
|
'family': family, 'parent': parent, 'dns_name': dns_name, **kwargs
|
|
}.items() if v is not None}
|
|
return self.client.list(f'{self.base_endpoint}/ip-addresses', params=params)
|
|
|
|
async def get_ip_address(self, id: int) -> Dict:
|
|
"""Get a specific IP address by ID."""
|
|
return self.client.get(f'{self.base_endpoint}/ip-addresses', id)
|
|
|
|
async def create_ip_address(
|
|
self,
|
|
address: str,
|
|
status: str = 'active',
|
|
vrf: Optional[int] = None,
|
|
tenant: Optional[int] = None,
|
|
role: Optional[str] = None,
|
|
assigned_object_type: Optional[str] = None,
|
|
assigned_object_id: Optional[int] = None,
|
|
nat_inside: Optional[int] = None,
|
|
dns_name: Optional[str] = None,
|
|
description: Optional[str] = None,
|
|
**kwargs
|
|
) -> Dict:
|
|
"""Create a new IP address."""
|
|
data = {'address': address, 'status': status, **kwargs}
|
|
for key, val in [
|
|
('vrf', vrf), ('tenant', tenant), ('role', role),
|
|
('assigned_object_type', assigned_object_type),
|
|
('assigned_object_id', assigned_object_id),
|
|
('nat_inside', nat_inside), ('dns_name', dns_name),
|
|
('description', description)
|
|
]:
|
|
if val is not None:
|
|
data[key] = val
|
|
return self.client.create(f'{self.base_endpoint}/ip-addresses', data)
|
|
|
|
async def update_ip_address(self, id: int, **kwargs) -> Dict:
|
|
"""Update an IP address."""
|
|
return self.client.patch(f'{self.base_endpoint}/ip-addresses', id, kwargs)
|
|
|
|
# ==================== Services ====================
|
|
|
|
async def list_services(
|
|
self,
|
|
device_id: Optional[int] = None,
|
|
virtual_machine_id: Optional[int] = None,
|
|
name: Optional[str] = None,
|
|
protocol: Optional[str] = None,
|
|
port: Optional[int] = None,
|
|
**kwargs
|
|
) -> List[Dict]:
|
|
"""List all services."""
|
|
params = {k: v for k, v in {
|
|
'device_id': device_id, 'virtual_machine_id': virtual_machine_id,
|
|
'name': name, 'protocol': protocol, 'port': port, **kwargs
|
|
}.items() if v is not None}
|
|
return self.client.list(f'{self.base_endpoint}/services', params=params)
|
|
|
|
async def get_service(self, id: int) -> Dict:
|
|
"""Get a specific service by ID."""
|
|
return self.client.get(f'{self.base_endpoint}/services', id)
|
|
|
|
async def create_service(
|
|
self,
|
|
name: str,
|
|
ports: List[int],
|
|
protocol: str,
|
|
device: Optional[int] = None,
|
|
virtual_machine: Optional[int] = None,
|
|
ipaddresses: Optional[List[int]] = None,
|
|
description: Optional[str] = None,
|
|
**kwargs
|
|
) -> Dict:
|
|
"""Create a new service."""
|
|
data = {'name': name, 'ports': ports, 'protocol': protocol, **kwargs}
|
|
for key, val in [
|
|
('device', device), ('virtual_machine', virtual_machine),
|
|
('ipaddresses', ipaddresses), ('description', description)
|
|
]:
|
|
if val is not None:
|
|
data[key] = val
|
|
return self.client.create(f'{self.base_endpoint}/services', data)
|