refactor: bundle MCP servers inside plugins for cache compatibility

Claude Code only caches the plugin directory when installed from a
marketplace, not parent directories. This broke the shared mcp-servers/
architecture because relative paths like ../../mcp-servers/ resolved
to non-existent locations in the cache.

Changes:
- Move gitea and wikijs MCP servers into plugins/projman/mcp-servers/
- Move netbox MCP server into plugins/cmdb-assistant/mcp-servers/
- Update .mcp.json files to use ${CLAUDE_PLUGIN_ROOT}/mcp-servers/
- Update setup.sh to handle new bundled structure
- Add netbox.env config template to setup.sh
- Update CLAUDE.md and CANONICAL-PATHS.md documentation

This ensures plugins work correctly when installed and cached.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-15 17:23:02 -05:00
parent c73e4c4794
commit d84425cbb0
50 changed files with 158 additions and 112 deletions

View File

@@ -0,0 +1,935 @@
"""
DCIM (Data Center Infrastructure Management) tools for NetBox MCP Server.
Covers: Sites, Locations, Racks, Devices, Cables, Interfaces, and related models.
"""
import logging
from typing import List, Dict, Optional, Any
from ..netbox_client import NetBoxClient
logger = logging.getLogger(__name__)
class DCIMTools:
"""Tools for DCIM operations in NetBox"""
def __init__(self, client: NetBoxClient):
self.client = client
self.base_endpoint = 'dcim'
# ==================== Regions ====================
async def list_regions(
self,
name: Optional[str] = None,
slug: Optional[str] = None,
parent_id: Optional[int] = None,
**kwargs
) -> List[Dict]:
"""List all regions with optional filtering."""
params = {k: v for k, v in {
'name': name, 'slug': slug, 'parent_id': parent_id, **kwargs
}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/regions', params=params)
async def get_region(self, id: int) -> Dict:
"""Get a specific region by ID."""
return self.client.get(f'{self.base_endpoint}/regions', id)
async def create_region(self, name: str, slug: str, parent: Optional[int] = None, **kwargs) -> Dict:
"""Create a new region."""
data = {'name': name, 'slug': slug, **kwargs}
if parent:
data['parent'] = parent
return self.client.create(f'{self.base_endpoint}/regions', data)
async def update_region(self, id: int, **kwargs) -> Dict:
"""Update a region."""
return self.client.patch(f'{self.base_endpoint}/regions', id, kwargs)
async def delete_region(self, id: int) -> None:
"""Delete a region."""
self.client.delete(f'{self.base_endpoint}/regions', id)
# ==================== Site Groups ====================
async def list_site_groups(
self,
name: Optional[str] = None,
slug: Optional[str] = None,
parent_id: Optional[int] = None,
**kwargs
) -> List[Dict]:
"""List all site groups with optional filtering."""
params = {k: v for k, v in {
'name': name, 'slug': slug, 'parent_id': parent_id, **kwargs
}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/site-groups', params=params)
async def get_site_group(self, id: int) -> Dict:
"""Get a specific site group by ID."""
return self.client.get(f'{self.base_endpoint}/site-groups', id)
async def create_site_group(self, name: str, slug: str, parent: Optional[int] = None, **kwargs) -> Dict:
"""Create a new site group."""
data = {'name': name, 'slug': slug, **kwargs}
if parent:
data['parent'] = parent
return self.client.create(f'{self.base_endpoint}/site-groups', data)
async def update_site_group(self, id: int, **kwargs) -> Dict:
"""Update a site group."""
return self.client.patch(f'{self.base_endpoint}/site-groups', id, kwargs)
async def delete_site_group(self, id: int) -> None:
"""Delete a site group."""
self.client.delete(f'{self.base_endpoint}/site-groups', id)
# ==================== Sites ====================
async def list_sites(
self,
name: Optional[str] = None,
slug: Optional[str] = None,
status: Optional[str] = None,
region_id: Optional[int] = None,
group_id: Optional[int] = None,
tenant_id: Optional[int] = None,
**kwargs
) -> List[Dict]:
"""List all sites with optional filtering."""
params = {k: v for k, v in {
'name': name, 'slug': slug, 'status': status,
'region_id': region_id, 'group_id': group_id, 'tenant_id': tenant_id, **kwargs
}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/sites', params=params)
async def get_site(self, id: int) -> Dict:
"""Get a specific site by ID."""
return self.client.get(f'{self.base_endpoint}/sites', id)
async def create_site(
self,
name: str,
slug: str,
status: str = 'active',
region: Optional[int] = None,
group: Optional[int] = None,
tenant: Optional[int] = None,
facility: Optional[str] = None,
time_zone: Optional[str] = None,
description: Optional[str] = None,
physical_address: Optional[str] = None,
shipping_address: Optional[str] = None,
latitude: Optional[float] = None,
longitude: Optional[float] = None,
**kwargs
) -> Dict:
"""Create a new site."""
data = {'name': name, 'slug': slug, 'status': status, **kwargs}
for key, val in [
('region', region), ('group', group), ('tenant', tenant),
('facility', facility), ('time_zone', time_zone),
('description', description), ('physical_address', physical_address),
('shipping_address', shipping_address), ('latitude', latitude),
('longitude', longitude)
]:
if val is not None:
data[key] = val
return self.client.create(f'{self.base_endpoint}/sites', data)
async def update_site(self, id: int, **kwargs) -> Dict:
"""Update a site."""
return self.client.patch(f'{self.base_endpoint}/sites', id, kwargs)
async def delete_site(self, id: int) -> None:
"""Delete a site."""
self.client.delete(f'{self.base_endpoint}/sites', id)
# ==================== Locations ====================
async def list_locations(
self,
name: Optional[str] = None,
slug: Optional[str] = None,
site_id: Optional[int] = None,
parent_id: Optional[int] = None,
**kwargs
) -> List[Dict]:
"""List all locations with optional filtering."""
params = {k: v for k, v in {
'name': name, 'slug': slug, 'site_id': site_id, 'parent_id': parent_id, **kwargs
}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/locations', params=params)
async def get_location(self, id: int) -> Dict:
"""Get a specific location by ID."""
return self.client.get(f'{self.base_endpoint}/locations', id)
async def create_location(
self,
name: str,
slug: str,
site: int,
parent: Optional[int] = None,
**kwargs
) -> Dict:
"""Create a new location."""
data = {'name': name, 'slug': slug, 'site': site, **kwargs}
if parent:
data['parent'] = parent
return self.client.create(f'{self.base_endpoint}/locations', data)
async def update_location(self, id: int, **kwargs) -> Dict:
"""Update a location."""
return self.client.patch(f'{self.base_endpoint}/locations', id, kwargs)
async def delete_location(self, id: int) -> None:
"""Delete a location."""
self.client.delete(f'{self.base_endpoint}/locations', id)
# ==================== Rack Roles ====================
async def list_rack_roles(self, name: Optional[str] = None, **kwargs) -> List[Dict]:
"""List all rack roles."""
params = {k: v for k, v in {'name': name, **kwargs}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/rack-roles', params=params)
async def get_rack_role(self, id: int) -> Dict:
"""Get a specific rack role by ID."""
return self.client.get(f'{self.base_endpoint}/rack-roles', id)
async def create_rack_role(self, name: str, slug: str, color: str = '9e9e9e', **kwargs) -> Dict:
"""Create a new rack role."""
data = {'name': name, 'slug': slug, 'color': color, **kwargs}
return self.client.create(f'{self.base_endpoint}/rack-roles', data)
async def update_rack_role(self, id: int, **kwargs) -> Dict:
"""Update a rack role."""
return self.client.patch(f'{self.base_endpoint}/rack-roles', id, kwargs)
async def delete_rack_role(self, id: int) -> None:
"""Delete a rack role."""
self.client.delete(f'{self.base_endpoint}/rack-roles', id)
# ==================== Rack Types ====================
async def list_rack_types(self, manufacturer_id: Optional[int] = None, **kwargs) -> List[Dict]:
"""List all rack types."""
params = {k: v for k, v in {'manufacturer_id': manufacturer_id, **kwargs}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/rack-types', params=params)
async def get_rack_type(self, id: int) -> Dict:
"""Get a specific rack type by ID."""
return self.client.get(f'{self.base_endpoint}/rack-types', id)
async def create_rack_type(
self,
manufacturer: int,
model: str,
slug: str,
form_factor: str = '4-post-frame',
width: int = 19,
u_height: int = 42,
**kwargs
) -> Dict:
"""Create a new rack type."""
data = {
'manufacturer': manufacturer, 'model': model, 'slug': slug,
'form_factor': form_factor, 'width': width, 'u_height': u_height, **kwargs
}
return self.client.create(f'{self.base_endpoint}/rack-types', data)
async def update_rack_type(self, id: int, **kwargs) -> Dict:
"""Update a rack type."""
return self.client.patch(f'{self.base_endpoint}/rack-types', id, kwargs)
async def delete_rack_type(self, id: int) -> None:
"""Delete a rack type."""
self.client.delete(f'{self.base_endpoint}/rack-types', id)
# ==================== Racks ====================
async def list_racks(
self,
name: Optional[str] = None,
site_id: Optional[int] = None,
location_id: Optional[int] = None,
status: Optional[str] = None,
role_id: Optional[int] = None,
tenant_id: Optional[int] = None,
**kwargs
) -> List[Dict]:
"""List all racks with optional filtering."""
params = {k: v for k, v in {
'name': name, 'site_id': site_id, 'location_id': location_id,
'status': status, 'role_id': role_id, 'tenant_id': tenant_id, **kwargs
}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/racks', params=params)
async def get_rack(self, id: int) -> Dict:
"""Get a specific rack by ID."""
return self.client.get(f'{self.base_endpoint}/racks', id)
async def create_rack(
self,
name: str,
site: int,
status: str = 'active',
location: Optional[int] = None,
role: Optional[int] = None,
tenant: Optional[int] = None,
rack_type: Optional[int] = None,
width: int = 19,
u_height: int = 42,
**kwargs
) -> Dict:
"""Create a new rack."""
data = {'name': name, 'site': site, 'status': status, 'width': width, 'u_height': u_height, **kwargs}
for key, val in [('location', location), ('role', role), ('tenant', tenant), ('rack_type', rack_type)]:
if val is not None:
data[key] = val
return self.client.create(f'{self.base_endpoint}/racks', data)
async def update_rack(self, id: int, **kwargs) -> Dict:
"""Update a rack."""
return self.client.patch(f'{self.base_endpoint}/racks', id, kwargs)
async def delete_rack(self, id: int) -> None:
"""Delete a rack."""
self.client.delete(f'{self.base_endpoint}/racks', id)
# ==================== Rack Reservations ====================
async def list_rack_reservations(
self,
rack_id: Optional[int] = None,
site_id: Optional[int] = None,
tenant_id: Optional[int] = None,
**kwargs
) -> List[Dict]:
"""List all rack reservations."""
params = {k: v for k, v in {
'rack_id': rack_id, 'site_id': site_id, 'tenant_id': tenant_id, **kwargs
}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/rack-reservations', params=params)
async def get_rack_reservation(self, id: int) -> Dict:
"""Get a specific rack reservation by ID."""
return self.client.get(f'{self.base_endpoint}/rack-reservations', id)
async def create_rack_reservation(
self,
rack: int,
units: List[int],
user: int,
description: str,
tenant: Optional[int] = None,
**kwargs
) -> Dict:
"""Create a new rack reservation."""
data = {'rack': rack, 'units': units, 'user': user, 'description': description, **kwargs}
if tenant:
data['tenant'] = tenant
return self.client.create(f'{self.base_endpoint}/rack-reservations', data)
async def update_rack_reservation(self, id: int, **kwargs) -> Dict:
"""Update a rack reservation."""
return self.client.patch(f'{self.base_endpoint}/rack-reservations', id, kwargs)
async def delete_rack_reservation(self, id: int) -> None:
"""Delete a rack reservation."""
self.client.delete(f'{self.base_endpoint}/rack-reservations', id)
# ==================== Manufacturers ====================
async def list_manufacturers(self, name: Optional[str] = None, **kwargs) -> List[Dict]:
"""List all manufacturers."""
params = {k: v for k, v in {'name': name, **kwargs}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/manufacturers', params=params)
async def get_manufacturer(self, id: int) -> Dict:
"""Get a specific manufacturer by ID."""
return self.client.get(f'{self.base_endpoint}/manufacturers', id)
async def create_manufacturer(self, name: str, slug: str, **kwargs) -> Dict:
"""Create a new manufacturer."""
data = {'name': name, 'slug': slug, **kwargs}
return self.client.create(f'{self.base_endpoint}/manufacturers', data)
async def update_manufacturer(self, id: int, **kwargs) -> Dict:
"""Update a manufacturer."""
return self.client.patch(f'{self.base_endpoint}/manufacturers', id, kwargs)
async def delete_manufacturer(self, id: int) -> None:
"""Delete a manufacturer."""
self.client.delete(f'{self.base_endpoint}/manufacturers', id)
# ==================== Device Types ====================
async def list_device_types(
self,
manufacturer_id: Optional[int] = None,
model: Optional[str] = None,
slug: Optional[str] = None,
**kwargs
) -> List[Dict]:
"""List all device types."""
params = {k: v for k, v in {
'manufacturer_id': manufacturer_id, 'model': model, 'slug': slug, **kwargs
}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/device-types', params=params)
async def get_device_type(self, id: int) -> Dict:
"""Get a specific device type by ID."""
return self.client.get(f'{self.base_endpoint}/device-types', id)
async def create_device_type(
self,
manufacturer: int,
model: str,
slug: str,
u_height: float = 1.0,
is_full_depth: bool = True,
**kwargs
) -> Dict:
"""Create a new device type."""
data = {
'manufacturer': manufacturer, 'model': model, 'slug': slug,
'u_height': u_height, 'is_full_depth': is_full_depth, **kwargs
}
return self.client.create(f'{self.base_endpoint}/device-types', data)
async def update_device_type(self, id: int, **kwargs) -> Dict:
"""Update a device type."""
return self.client.patch(f'{self.base_endpoint}/device-types', id, kwargs)
async def delete_device_type(self, id: int) -> None:
"""Delete a device type."""
self.client.delete(f'{self.base_endpoint}/device-types', id)
# ==================== Module Types ====================
async def list_module_types(self, manufacturer_id: Optional[int] = None, **kwargs) -> List[Dict]:
"""List all module types."""
params = {k: v for k, v in {'manufacturer_id': manufacturer_id, **kwargs}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/module-types', params=params)
async def get_module_type(self, id: int) -> Dict:
"""Get a specific module type by ID."""
return self.client.get(f'{self.base_endpoint}/module-types', id)
async def create_module_type(self, manufacturer: int, model: str, **kwargs) -> Dict:
"""Create a new module type."""
data = {'manufacturer': manufacturer, 'model': model, **kwargs}
return self.client.create(f'{self.base_endpoint}/module-types', data)
async def update_module_type(self, id: int, **kwargs) -> Dict:
"""Update a module type."""
return self.client.patch(f'{self.base_endpoint}/module-types', id, kwargs)
async def delete_module_type(self, id: int) -> None:
"""Delete a module type."""
self.client.delete(f'{self.base_endpoint}/module-types', id)
# ==================== Device Roles ====================
async def list_device_roles(self, name: Optional[str] = None, **kwargs) -> List[Dict]:
"""List all device roles."""
params = {k: v for k, v in {'name': name, **kwargs}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/device-roles', params=params)
async def get_device_role(self, id: int) -> Dict:
"""Get a specific device role by ID."""
return self.client.get(f'{self.base_endpoint}/device-roles', id)
async def create_device_role(
self,
name: str,
slug: str,
color: str = '9e9e9e',
vm_role: bool = False,
**kwargs
) -> Dict:
"""Create a new device role."""
data = {'name': name, 'slug': slug, 'color': color, 'vm_role': vm_role, **kwargs}
return self.client.create(f'{self.base_endpoint}/device-roles', data)
async def update_device_role(self, id: int, **kwargs) -> Dict:
"""Update a device role."""
return self.client.patch(f'{self.base_endpoint}/device-roles', id, kwargs)
async def delete_device_role(self, id: int) -> None:
"""Delete a device role."""
self.client.delete(f'{self.base_endpoint}/device-roles', id)
# ==================== Platforms ====================
async def list_platforms(self, name: Optional[str] = None, manufacturer_id: Optional[int] = None, **kwargs) -> List[Dict]:
"""List all platforms."""
params = {k: v for k, v in {'name': name, 'manufacturer_id': manufacturer_id, **kwargs}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/platforms', params=params)
async def get_platform(self, id: int) -> Dict:
"""Get a specific platform by ID."""
return self.client.get(f'{self.base_endpoint}/platforms', id)
async def create_platform(
self,
name: str,
slug: str,
manufacturer: Optional[int] = None,
**kwargs
) -> Dict:
"""Create a new platform."""
data = {'name': name, 'slug': slug, **kwargs}
if manufacturer:
data['manufacturer'] = manufacturer
return self.client.create(f'{self.base_endpoint}/platforms', data)
async def update_platform(self, id: int, **kwargs) -> Dict:
"""Update a platform."""
return self.client.patch(f'{self.base_endpoint}/platforms', id, kwargs)
async def delete_platform(self, id: int) -> None:
"""Delete a platform."""
self.client.delete(f'{self.base_endpoint}/platforms', id)
# ==================== Devices ====================
async def list_devices(
self,
name: Optional[str] = None,
site_id: Optional[int] = None,
location_id: Optional[int] = None,
rack_id: Optional[int] = None,
status: Optional[str] = None,
role_id: Optional[int] = None,
device_type_id: Optional[int] = None,
manufacturer_id: Optional[int] = None,
platform_id: Optional[int] = None,
tenant_id: Optional[int] = None,
serial: Optional[str] = None,
asset_tag: Optional[str] = None,
**kwargs
) -> List[Dict]:
"""List all devices with optional filtering."""
params = {k: v for k, v in {
'name': name, 'site_id': site_id, 'location_id': location_id,
'rack_id': rack_id, 'status': status, 'role_id': role_id,
'device_type_id': device_type_id, 'manufacturer_id': manufacturer_id,
'platform_id': platform_id, 'tenant_id': tenant_id,
'serial': serial, 'asset_tag': asset_tag, **kwargs
}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/devices', params=params)
async def get_device(self, id: int) -> Dict:
"""Get a specific device by ID."""
return self.client.get(f'{self.base_endpoint}/devices', id)
async def create_device(
self,
name: str,
device_type: int,
role: int,
site: int,
status: str = 'active',
location: Optional[int] = None,
rack: Optional[int] = None,
position: Optional[float] = None,
face: Optional[str] = None,
platform: Optional[int] = None,
tenant: Optional[int] = None,
serial: Optional[str] = None,
asset_tag: Optional[str] = None,
primary_ip4: Optional[int] = None,
primary_ip6: Optional[int] = None,
**kwargs
) -> Dict:
"""Create a new device."""
data = {
'name': name, 'device_type': device_type, 'role': role,
'site': site, 'status': status, **kwargs
}
for key, val in [
('location', location), ('rack', rack), ('position', position),
('face', face), ('platform', platform), ('tenant', tenant),
('serial', serial), ('asset_tag', asset_tag),
('primary_ip4', primary_ip4), ('primary_ip6', primary_ip6)
]:
if val is not None:
data[key] = val
return self.client.create(f'{self.base_endpoint}/devices', data)
async def update_device(self, id: int, **kwargs) -> Dict:
"""Update a device."""
return self.client.patch(f'{self.base_endpoint}/devices', id, kwargs)
async def delete_device(self, id: int) -> None:
"""Delete a device."""
self.client.delete(f'{self.base_endpoint}/devices', id)
# ==================== Modules ====================
async def list_modules(self, device_id: Optional[int] = None, **kwargs) -> List[Dict]:
"""List all modules."""
params = {k: v for k, v in {'device_id': device_id, **kwargs}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/modules', params=params)
async def get_module(self, id: int) -> Dict:
"""Get a specific module by ID."""
return self.client.get(f'{self.base_endpoint}/modules', id)
async def create_module(self, device: int, module_bay: int, module_type: int, **kwargs) -> Dict:
"""Create a new module."""
data = {'device': device, 'module_bay': module_bay, 'module_type': module_type, **kwargs}
return self.client.create(f'{self.base_endpoint}/modules', data)
async def update_module(self, id: int, **kwargs) -> Dict:
"""Update a module."""
return self.client.patch(f'{self.base_endpoint}/modules', id, kwargs)
async def delete_module(self, id: int) -> None:
"""Delete a module."""
self.client.delete(f'{self.base_endpoint}/modules', id)
# ==================== Interfaces ====================
async def list_interfaces(
self,
device_id: Optional[int] = None,
name: Optional[str] = None,
type: Optional[str] = None,
enabled: Optional[bool] = None,
**kwargs
) -> List[Dict]:
"""List all interfaces."""
params = {k: v for k, v in {
'device_id': device_id, 'name': name, 'type': type, 'enabled': enabled, **kwargs
}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/interfaces', params=params)
async def get_interface(self, id: int) -> Dict:
"""Get a specific interface by ID."""
return self.client.get(f'{self.base_endpoint}/interfaces', id)
async def create_interface(
self,
device: int,
name: str,
type: str,
enabled: bool = True,
mtu: Optional[int] = None,
mac_address: Optional[str] = None,
description: Optional[str] = None,
mode: Optional[str] = None,
untagged_vlan: Optional[int] = None,
tagged_vlans: Optional[List[int]] = None,
**kwargs
) -> Dict:
"""Create a new interface."""
data = {'device': device, 'name': name, 'type': type, 'enabled': enabled, **kwargs}
for key, val in [
('mtu', mtu), ('mac_address', mac_address), ('description', description),
('mode', mode), ('untagged_vlan', untagged_vlan), ('tagged_vlans', tagged_vlans)
]:
if val is not None:
data[key] = val
return self.client.create(f'{self.base_endpoint}/interfaces', data)
async def update_interface(self, id: int, **kwargs) -> Dict:
"""Update an interface."""
return self.client.patch(f'{self.base_endpoint}/interfaces', id, kwargs)
async def delete_interface(self, id: int) -> None:
"""Delete an interface."""
self.client.delete(f'{self.base_endpoint}/interfaces', id)
# ==================== Console Ports ====================
async def list_console_ports(self, device_id: Optional[int] = None, **kwargs) -> List[Dict]:
"""List all console ports."""
params = {k: v for k, v in {'device_id': device_id, **kwargs}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/console-ports', params=params)
async def get_console_port(self, id: int) -> Dict:
"""Get a specific console port by ID."""
return self.client.get(f'{self.base_endpoint}/console-ports', id)
async def create_console_port(self, device: int, name: str, **kwargs) -> Dict:
"""Create a new console port."""
data = {'device': device, 'name': name, **kwargs}
return self.client.create(f'{self.base_endpoint}/console-ports', data)
async def update_console_port(self, id: int, **kwargs) -> Dict:
"""Update a console port."""
return self.client.patch(f'{self.base_endpoint}/console-ports', id, kwargs)
async def delete_console_port(self, id: int) -> None:
"""Delete a console port."""
self.client.delete(f'{self.base_endpoint}/console-ports', id)
# ==================== Console Server Ports ====================
async def list_console_server_ports(self, device_id: Optional[int] = None, **kwargs) -> List[Dict]:
"""List all console server ports."""
params = {k: v for k, v in {'device_id': device_id, **kwargs}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/console-server-ports', params=params)
async def get_console_server_port(self, id: int) -> Dict:
"""Get a specific console server port by ID."""
return self.client.get(f'{self.base_endpoint}/console-server-ports', id)
async def create_console_server_port(self, device: int, name: str, **kwargs) -> Dict:
"""Create a new console server port."""
data = {'device': device, 'name': name, **kwargs}
return self.client.create(f'{self.base_endpoint}/console-server-ports', data)
async def update_console_server_port(self, id: int, **kwargs) -> Dict:
"""Update a console server port."""
return self.client.patch(f'{self.base_endpoint}/console-server-ports', id, kwargs)
async def delete_console_server_port(self, id: int) -> None:
"""Delete a console server port."""
self.client.delete(f'{self.base_endpoint}/console-server-ports', id)
# ==================== Power Ports ====================
async def list_power_ports(self, device_id: Optional[int] = None, **kwargs) -> List[Dict]:
"""List all power ports."""
params = {k: v for k, v in {'device_id': device_id, **kwargs}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/power-ports', params=params)
async def get_power_port(self, id: int) -> Dict:
"""Get a specific power port by ID."""
return self.client.get(f'{self.base_endpoint}/power-ports', id)
async def create_power_port(self, device: int, name: str, **kwargs) -> Dict:
"""Create a new power port."""
data = {'device': device, 'name': name, **kwargs}
return self.client.create(f'{self.base_endpoint}/power-ports', data)
async def update_power_port(self, id: int, **kwargs) -> Dict:
"""Update a power port."""
return self.client.patch(f'{self.base_endpoint}/power-ports', id, kwargs)
async def delete_power_port(self, id: int) -> None:
"""Delete a power port."""
self.client.delete(f'{self.base_endpoint}/power-ports', id)
# ==================== Power Outlets ====================
async def list_power_outlets(self, device_id: Optional[int] = None, **kwargs) -> List[Dict]:
"""List all power outlets."""
params = {k: v for k, v in {'device_id': device_id, **kwargs}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/power-outlets', params=params)
async def get_power_outlet(self, id: int) -> Dict:
"""Get a specific power outlet by ID."""
return self.client.get(f'{self.base_endpoint}/power-outlets', id)
async def create_power_outlet(self, device: int, name: str, **kwargs) -> Dict:
"""Create a new power outlet."""
data = {'device': device, 'name': name, **kwargs}
return self.client.create(f'{self.base_endpoint}/power-outlets', data)
async def update_power_outlet(self, id: int, **kwargs) -> Dict:
"""Update a power outlet."""
return self.client.patch(f'{self.base_endpoint}/power-outlets', id, kwargs)
async def delete_power_outlet(self, id: int) -> None:
"""Delete a power outlet."""
self.client.delete(f'{self.base_endpoint}/power-outlets', id)
# ==================== Power Panels ====================
async def list_power_panels(self, site_id: Optional[int] = None, **kwargs) -> List[Dict]:
"""List all power panels."""
params = {k: v for k, v in {'site_id': site_id, **kwargs}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/power-panels', params=params)
async def get_power_panel(self, id: int) -> Dict:
"""Get a specific power panel by ID."""
return self.client.get(f'{self.base_endpoint}/power-panels', id)
async def create_power_panel(self, site: int, name: str, location: Optional[int] = None, **kwargs) -> Dict:
"""Create a new power panel."""
data = {'site': site, 'name': name, **kwargs}
if location:
data['location'] = location
return self.client.create(f'{self.base_endpoint}/power-panels', data)
async def update_power_panel(self, id: int, **kwargs) -> Dict:
"""Update a power panel."""
return self.client.patch(f'{self.base_endpoint}/power-panels', id, kwargs)
async def delete_power_panel(self, id: int) -> None:
"""Delete a power panel."""
self.client.delete(f'{self.base_endpoint}/power-panels', id)
# ==================== Power Feeds ====================
async def list_power_feeds(self, power_panel_id: Optional[int] = None, **kwargs) -> List[Dict]:
"""List all power feeds."""
params = {k: v for k, v in {'power_panel_id': power_panel_id, **kwargs}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/power-feeds', params=params)
async def get_power_feed(self, id: int) -> Dict:
"""Get a specific power feed by ID."""
return self.client.get(f'{self.base_endpoint}/power-feeds', id)
async def create_power_feed(
self,
power_panel: int,
name: str,
status: str = 'active',
type: str = 'primary',
supply: str = 'ac',
phase: str = 'single-phase',
voltage: int = 120,
amperage: int = 20,
**kwargs
) -> Dict:
"""Create a new power feed."""
data = {
'power_panel': power_panel, 'name': name, 'status': status,
'type': type, 'supply': supply, 'phase': phase,
'voltage': voltage, 'amperage': amperage, **kwargs
}
return self.client.create(f'{self.base_endpoint}/power-feeds', data)
async def update_power_feed(self, id: int, **kwargs) -> Dict:
"""Update a power feed."""
return self.client.patch(f'{self.base_endpoint}/power-feeds', id, kwargs)
async def delete_power_feed(self, id: int) -> None:
"""Delete a power feed."""
self.client.delete(f'{self.base_endpoint}/power-feeds', id)
# ==================== Cables ====================
async def list_cables(
self,
site_id: Optional[int] = None,
device_id: Optional[int] = None,
rack_id: Optional[int] = None,
type: Optional[str] = None,
status: Optional[str] = None,
**kwargs
) -> List[Dict]:
"""List all cables."""
params = {k: v for k, v in {
'site_id': site_id, 'device_id': device_id, 'rack_id': rack_id,
'type': type, 'status': status, **kwargs
}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/cables', params=params)
async def get_cable(self, id: int) -> Dict:
"""Get a specific cable by ID."""
return self.client.get(f'{self.base_endpoint}/cables', id)
async def create_cable(
self,
a_terminations: List[Dict],
b_terminations: List[Dict],
type: Optional[str] = None,
status: str = 'connected',
label: Optional[str] = None,
color: Optional[str] = None,
length: Optional[float] = None,
length_unit: Optional[str] = None,
**kwargs
) -> Dict:
"""
Create a new cable.
a_terminations and b_terminations are lists of dicts with:
- object_type: e.g., 'dcim.interface'
- object_id: ID of the object
"""
data = {
'a_terminations': a_terminations,
'b_terminations': b_terminations,
'status': status,
**kwargs
}
for key, val in [
('type', type), ('label', label), ('color', color),
('length', length), ('length_unit', length_unit)
]:
if val is not None:
data[key] = val
return self.client.create(f'{self.base_endpoint}/cables', data)
async def update_cable(self, id: int, **kwargs) -> Dict:
"""Update a cable."""
return self.client.patch(f'{self.base_endpoint}/cables', id, kwargs)
async def delete_cable(self, id: int) -> None:
"""Delete a cable."""
self.client.delete(f'{self.base_endpoint}/cables', id)
# ==================== Virtual Chassis ====================
async def list_virtual_chassis(self, **kwargs) -> List[Dict]:
"""List all virtual chassis."""
return self.client.list(f'{self.base_endpoint}/virtual-chassis', params=kwargs)
async def get_virtual_chassis(self, id: int) -> Dict:
"""Get a specific virtual chassis by ID."""
return self.client.get(f'{self.base_endpoint}/virtual-chassis', id)
async def create_virtual_chassis(self, name: str, domain: Optional[str] = None, **kwargs) -> Dict:
"""Create a new virtual chassis."""
data = {'name': name, **kwargs}
if domain:
data['domain'] = domain
return self.client.create(f'{self.base_endpoint}/virtual-chassis', data)
async def update_virtual_chassis(self, id: int, **kwargs) -> Dict:
"""Update a virtual chassis."""
return self.client.patch(f'{self.base_endpoint}/virtual-chassis', id, kwargs)
async def delete_virtual_chassis(self, id: int) -> None:
"""Delete a virtual chassis."""
self.client.delete(f'{self.base_endpoint}/virtual-chassis', id)
# ==================== Inventory Items ====================
async def list_inventory_items(self, device_id: Optional[int] = None, **kwargs) -> List[Dict]:
"""List all inventory items."""
params = {k: v for k, v in {'device_id': device_id, **kwargs}.items() if v is not None}
return self.client.list(f'{self.base_endpoint}/inventory-items', params=params)
async def get_inventory_item(self, id: int) -> Dict:
"""Get a specific inventory item by ID."""
return self.client.get(f'{self.base_endpoint}/inventory-items', id)
async def create_inventory_item(
self,
device: int,
name: str,
parent: Optional[int] = None,
manufacturer: Optional[int] = None,
part_id: Optional[str] = None,
serial: Optional[str] = None,
asset_tag: Optional[str] = None,
**kwargs
) -> Dict:
"""Create a new inventory item."""
data = {'device': device, 'name': name, **kwargs}
for key, val in [
('parent', parent), ('manufacturer', manufacturer),
('part_id', part_id), ('serial', serial), ('asset_tag', asset_tag)
]:
if val is not None:
data[key] = val
return self.client.create(f'{self.base_endpoint}/inventory-items', data)
async def update_inventory_item(self, id: int, **kwargs) -> Dict:
"""Update an inventory item."""
return self.client.patch(f'{self.base_endpoint}/inventory-items', id, kwargs)
async def delete_inventory_item(self, id: int) -> None:
"""Delete an inventory item."""
self.client.delete(f'{self.base_endpoint}/inventory-items', id)