- vultr_api/: Python library wrapping Vultr API v2 - 17 resource modules (instances, dns, firewall, vpc, etc.) - Pagination support, error handling - server/: FastAPI REST server - All API endpoints exposed via HTTP - X-API-Key header authentication - Swagger docs at /docs - Podman quadlet config for systemd deployment Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
292 lines
8.4 KiB
Python
292 lines
8.4 KiB
Python
"""
|
|
Bare Metal Resource
|
|
|
|
Bare metal server management
|
|
"""
|
|
|
|
from typing import Dict, List
|
|
from .base import BaseResource
|
|
|
|
|
|
class BareMetalResource(BaseResource):
|
|
"""
|
|
Bare metal server management
|
|
|
|
Usage:
|
|
# List bare metal servers
|
|
servers = client.bare_metal.list()
|
|
|
|
# Create server
|
|
server = client.bare_metal.create(
|
|
region="ewr",
|
|
plan="vbm-4c-32gb",
|
|
os_id=215
|
|
)
|
|
"""
|
|
|
|
def list(
|
|
self,
|
|
per_page: int = 100,
|
|
cursor: str = None,
|
|
tag: str = None,
|
|
label: str = None,
|
|
main_ip: str = None,
|
|
region: str = None,
|
|
) -> Dict:
|
|
"""
|
|
List bare metal servers
|
|
|
|
Returns:
|
|
Dict with 'bare_metals' list and 'meta' pagination
|
|
"""
|
|
params = {"per_page": per_page}
|
|
if cursor:
|
|
params["cursor"] = cursor
|
|
if tag:
|
|
params["tag"] = tag
|
|
if label:
|
|
params["label"] = label
|
|
if main_ip:
|
|
params["main_ip"] = main_ip
|
|
if region:
|
|
params["region"] = region
|
|
|
|
return self.client.get("bare-metals", params=params)
|
|
|
|
def list_all(self, **kwargs) -> List[Dict]:
|
|
"""List all bare metal servers (auto-paginate)"""
|
|
return self.client.paginate("bare-metals", "bare_metals", params=kwargs)
|
|
|
|
def get(self, baremetal_id: str) -> Dict:
|
|
"""
|
|
Get bare metal server details
|
|
|
|
Args:
|
|
baremetal_id: Bare metal ID
|
|
|
|
Returns:
|
|
Server details
|
|
"""
|
|
response = self.client.get(f"bare-metals/{baremetal_id}")
|
|
return response.get("bare_metal", {})
|
|
|
|
def create(
|
|
self,
|
|
region: str,
|
|
plan: str,
|
|
os_id: int = None,
|
|
script_id: str = None,
|
|
snapshot_id: str = None,
|
|
enable_ipv6: bool = False,
|
|
label: str = None,
|
|
sshkey_id: List[str] = None,
|
|
app_id: int = None,
|
|
image_id: str = None,
|
|
user_data: str = None,
|
|
activation_email: bool = False,
|
|
hostname: str = None,
|
|
tag: str = None,
|
|
tags: List[str] = None,
|
|
reserved_ipv4: str = None,
|
|
persistent_pxe: bool = False,
|
|
attach_vpc2: List[str] = None,
|
|
) -> Dict:
|
|
"""
|
|
Create a bare metal server
|
|
|
|
Args:
|
|
region: Region ID
|
|
plan: Plan ID
|
|
os_id: Operating System ID
|
|
script_id: Startup script ID
|
|
snapshot_id: Snapshot ID
|
|
enable_ipv6: Enable IPv6
|
|
label: Server label
|
|
sshkey_id: SSH key IDs
|
|
app_id: Application ID
|
|
image_id: Custom image ID
|
|
user_data: Cloud-init user data
|
|
activation_email: Send activation email
|
|
hostname: Server hostname
|
|
tag: Deprecated, use tags
|
|
tags: Tags
|
|
reserved_ipv4: Reserved IPv4 to assign
|
|
persistent_pxe: Enable persistent PXE
|
|
attach_vpc2: VPC 2.0 IDs to attach
|
|
|
|
Returns:
|
|
Created server details
|
|
"""
|
|
data = {"region": region, "plan": plan}
|
|
|
|
if os_id is not None:
|
|
data["os_id"] = os_id
|
|
if script_id:
|
|
data["script_id"] = script_id
|
|
if snapshot_id:
|
|
data["snapshot_id"] = snapshot_id
|
|
if enable_ipv6:
|
|
data["enable_ipv6"] = enable_ipv6
|
|
if label:
|
|
data["label"] = label
|
|
if sshkey_id:
|
|
data["sshkey_id"] = sshkey_id
|
|
if app_id is not None:
|
|
data["app_id"] = app_id
|
|
if image_id:
|
|
data["image_id"] = image_id
|
|
if user_data:
|
|
data["user_data"] = user_data
|
|
if activation_email:
|
|
data["activation_email"] = activation_email
|
|
if hostname:
|
|
data["hostname"] = hostname
|
|
if tag:
|
|
data["tag"] = tag
|
|
if tags:
|
|
data["tags"] = tags
|
|
if reserved_ipv4:
|
|
data["reserved_ipv4"] = reserved_ipv4
|
|
if persistent_pxe:
|
|
data["persistent_pxe"] = persistent_pxe
|
|
if attach_vpc2:
|
|
data["attach_vpc2"] = attach_vpc2
|
|
|
|
response = self.client.post("bare-metals", data)
|
|
return response.get("bare_metal", {})
|
|
|
|
def update(
|
|
self,
|
|
baremetal_id: str,
|
|
user_data: str = None,
|
|
label: str = None,
|
|
tag: str = None,
|
|
tags: List[str] = None,
|
|
os_id: int = None,
|
|
app_id: int = None,
|
|
image_id: str = None,
|
|
enable_ipv6: bool = None,
|
|
) -> Dict:
|
|
"""
|
|
Update a bare metal server
|
|
|
|
Args:
|
|
baremetal_id: Bare metal ID
|
|
(other args same as create)
|
|
|
|
Returns:
|
|
Updated server details
|
|
"""
|
|
data = {}
|
|
|
|
if user_data is not None:
|
|
data["user_data"] = user_data
|
|
if label is not None:
|
|
data["label"] = label
|
|
if tag is not None:
|
|
data["tag"] = tag
|
|
if tags is not None:
|
|
data["tags"] = tags
|
|
if os_id is not None:
|
|
data["os_id"] = os_id
|
|
if app_id is not None:
|
|
data["app_id"] = app_id
|
|
if image_id is not None:
|
|
data["image_id"] = image_id
|
|
if enable_ipv6 is not None:
|
|
data["enable_ipv6"] = enable_ipv6
|
|
|
|
response = self.client.patch(f"bare-metals/{baremetal_id}", data)
|
|
return response.get("bare_metal", {})
|
|
|
|
def delete(self, baremetal_id: str) -> None:
|
|
"""
|
|
Delete a bare metal server
|
|
|
|
Args:
|
|
baremetal_id: Bare metal ID to delete
|
|
"""
|
|
self.client.delete(f"bare-metals/{baremetal_id}")
|
|
|
|
def start(self, baremetal_id: str) -> None:
|
|
"""Start a bare metal server"""
|
|
self.client.post(f"bare-metals/{baremetal_id}/start")
|
|
|
|
def halt(self, baremetal_id: str) -> None:
|
|
"""Halt a bare metal server"""
|
|
self.client.post(f"bare-metals/{baremetal_id}/halt")
|
|
|
|
def reboot(self, baremetal_id: str) -> None:
|
|
"""Reboot a bare metal server"""
|
|
self.client.post(f"bare-metals/{baremetal_id}/reboot")
|
|
|
|
def reinstall(self, baremetal_id: str, hostname: str = None) -> Dict:
|
|
"""
|
|
Reinstall a bare metal server
|
|
|
|
Args:
|
|
baremetal_id: Bare metal ID
|
|
hostname: New hostname (optional)
|
|
|
|
Returns:
|
|
Server details
|
|
"""
|
|
data = {}
|
|
if hostname:
|
|
data["hostname"] = hostname
|
|
|
|
response = self.client.post(f"bare-metals/{baremetal_id}/reinstall", data)
|
|
return response.get("bare_metal", {})
|
|
|
|
def get_bandwidth(self, baremetal_id: str) -> Dict:
|
|
"""Get bandwidth usage"""
|
|
return self.client.get(f"bare-metals/{baremetal_id}/bandwidth")
|
|
|
|
def get_user_data(self, baremetal_id: str) -> Dict:
|
|
"""Get user data"""
|
|
return self.client.get(f"bare-metals/{baremetal_id}/user-data")
|
|
|
|
def get_upgrades(self, baremetal_id: str, upgrade_type: str = None) -> Dict:
|
|
"""Get available upgrades"""
|
|
params = {}
|
|
if upgrade_type:
|
|
params["type"] = upgrade_type
|
|
return self.client.get(f"bare-metals/{baremetal_id}/upgrades", params=params)
|
|
|
|
def get_vnc(self, baremetal_id: str) -> Dict:
|
|
"""Get VNC URL"""
|
|
return self.client.get(f"bare-metals/{baremetal_id}/vnc")
|
|
|
|
# IPv4 management
|
|
def list_ipv4(self, baremetal_id: str) -> List[Dict]:
|
|
"""List IPv4 addresses"""
|
|
response = self.client.get(f"bare-metals/{baremetal_id}/ipv4")
|
|
return response.get("ipv4s", [])
|
|
|
|
def list_ipv6(self, baremetal_id: str) -> List[Dict]:
|
|
"""List IPv6 addresses"""
|
|
response = self.client.get(f"bare-metals/{baremetal_id}/ipv6")
|
|
return response.get("ipv6s", [])
|
|
|
|
# VPC 2.0
|
|
def list_vpc2(self, baremetal_id: str) -> List[Dict]:
|
|
"""List attached VPC 2.0 networks"""
|
|
response = self.client.get(f"bare-metals/{baremetal_id}/vpc2")
|
|
return response.get("vpcs", [])
|
|
|
|
def attach_vpc2(
|
|
self,
|
|
baremetal_id: str,
|
|
vpc_id: str,
|
|
ip_address: str = None
|
|
) -> None:
|
|
"""Attach VPC 2.0 to bare metal"""
|
|
data = {"vpc_id": vpc_id}
|
|
if ip_address:
|
|
data["ip_address"] = ip_address
|
|
self.client.post(f"bare-metals/{baremetal_id}/vpc2/attach", data)
|
|
|
|
def detach_vpc2(self, baremetal_id: str, vpc_id: str) -> None:
|
|
"""Detach VPC 2.0 from bare metal"""
|
|
self.client.post(f"bare-metals/{baremetal_id}/vpc2/detach", {"vpc_id": vpc_id})
|