Phase 2: Enhanced TUI/CLI with search, export/import, and custom tools for Proxmox and Ollama
This commit is contained in:
1 parent
f5026965f7
commit
9f3298fa13
7 files changed
+1353
-16
No files matched your search
@@ -0,0 +1,14 @@
|
||||
"""
|
||||
MaxClaw Agent Tools Package
|
||||
Provides custom tools for Proxmox and Ollama management
|
||||
"""
|
||||
|
||||
from .proxmox_tools import get_proxmox_tools, ProxmoxTools
|
||||
from .ollama_tools import get_ollama_tools, OllamaTools
|
||||
|
||||
__all__ = [
|
||||
'get_proxmox_tools',
|
||||
'ProxmoxTools',
|
||||
'get_ollama_tools',
|
||||
'OllamaTools'
|
||||
]
|
||||
@@ -0,0 +1,360 @@
|
||||
"""
|
||||
Ollama management tools for MaxClaw Agent
|
||||
Provides tools for managing Ollama models and instances
|
||||
"""
|
||||
import requests
|
||||
import json
|
||||
from typing import Dict, List, Optional, Any
|
||||
from langchain.tools import Tool
|
||||
|
||||
|
||||
class OllamaTools:
|
||||
"""Tools for managing Ollama instances and models"""
|
||||
|
||||
def __init__(self, base_url: str = "http://localhost:11434"):
|
||||
"""
|
||||
Initialize Ollama tools
|
||||
|
||||
Args:
|
||||
base_url: Base URL of the Ollama instance
|
||||
"""
|
||||
self.base_url = base_url.rstrip('/')
|
||||
self.api_url = f"{self.base_url}/api"
|
||||
|
||||
def _make_request(self, endpoint: str, method: str = "GET", data: Optional[Dict] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
Make a request to the Ollama API
|
||||
|
||||
Args:
|
||||
endpoint: API endpoint
|
||||
method: HTTP method (GET, POST, etc.)
|
||||
data: Optional data for POST requests
|
||||
|
||||
Returns:
|
||||
Dictionary with response data
|
||||
"""
|
||||
try:
|
||||
url = f"{self.api_url}/{endpoint}"
|
||||
|
||||
if method.upper() == "GET":
|
||||
response = requests.get(url, timeout=10)
|
||||
elif method.upper() == "POST":
|
||||
response = requests.post(url, json=data, timeout=30)
|
||||
else:
|
||||
return {"success": False, "error": f"Unsupported method: {method}"}
|
||||
|
||||
if response.status_code == 200:
|
||||
try:
|
||||
return {"success": True, "data": response.json()}
|
||||
except json.JSONDecodeError:
|
||||
return {"success": True, "data": response.text}
|
||||
else:
|
||||
return {"success": False, "error": f"HTTP {response.status_code}: {response.text}"}
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
return {"success": False, "error": "Request timed out"}
|
||||
except requests.exceptions.ConnectionError:
|
||||
return {"success": False, "error": f"Connection failed to {self.base_url}"}
|
||||
except Exception as e:
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
def list_models(self) -> str:
|
||||
"""
|
||||
List all available models on the Ollama instance
|
||||
|
||||
Returns:
|
||||
Formatted string with model information
|
||||
"""
|
||||
result = self._make_request("tags")
|
||||
|
||||
if not result["success"]:
|
||||
return f"Error listing models: {result['error']}"
|
||||
|
||||
models = result["data"].get("models", [])
|
||||
if not models:
|
||||
return "No models found on the Ollama instance."
|
||||
|
||||
output = "Available Ollama Models:\n"
|
||||
output += "=" * 50 + "\n"
|
||||
|
||||
for model in models:
|
||||
name = model.get("name", "Unknown")
|
||||
size = model.get("size", 0)
|
||||
modified = model.get("modified_at", "")
|
||||
|
||||
# Convert size to human readable format
|
||||
if size > 1024 * 1024 * 1024:
|
||||
size_str = f"{size / (1024 * 1024 * 1024):.1f} GB"
|
||||
elif size > 1024 * 1024:
|
||||
size_str = f"{size / (1024 * 1024):.1f} MB"
|
||||
else:
|
||||
size_str = f"{size / 1024:.1f} KB"
|
||||
|
||||
# Format modification date
|
||||
if modified:
|
||||
from datetime import datetime
|
||||
try:
|
||||
dt = datetime.fromisoformat(modified.replace('Z', '+00:00'))
|
||||
modified_str = dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
except:
|
||||
modified_str = modified
|
||||
else:
|
||||
modified_str = "N/A"
|
||||
|
||||
output += f"Name: {name}\n"
|
||||
output += f"Size: {size_str}\n"
|
||||
output += f"Modified: {modified_str}\n"
|
||||
output += "-" * 30 + "\n"
|
||||
|
||||
return output
|
||||
|
||||
def get_model_info(self, model_name: str) -> str:
|
||||
"""
|
||||
Get detailed information about a specific model
|
||||
|
||||
Args:
|
||||
model_name: Name of the model
|
||||
|
||||
Returns:
|
||||
Formatted string with model details
|
||||
"""
|
||||
result = self._make_request("show", method="POST", data={"name": model_name})
|
||||
|
||||
if not result["success"]:
|
||||
return f"Error getting model info: {result['error']}"
|
||||
|
||||
model_info = result["data"]
|
||||
|
||||
output = f"Model Information: {model_name}\n"
|
||||
output += "=" * 50 + "\n"
|
||||
|
||||
# Basic information
|
||||
if "license" in model_info:
|
||||
output += f"License: {model_info['license']}\n"
|
||||
|
||||
if "modelfile" in model_info:
|
||||
output += f"Modelfile: {model_info['modelfile'][:100]}...\n"
|
||||
|
||||
if "parameters" in model_info:
|
||||
output += f"Parameters: {model_info['parameters']}\n"
|
||||
|
||||
if "template" in model_info:
|
||||
output += f"Template: {model_info['template'][:100]}...\n"
|
||||
|
||||
# System information
|
||||
if "system" in model_info:
|
||||
output += f"System: {model_info['system'][:100]}...\n"
|
||||
|
||||
# Details
|
||||
if "details" in model_info:
|
||||
details = model_info["details"]
|
||||
output += "\nModel Details:\n"
|
||||
output += "-" * 30 + "\n"
|
||||
|
||||
if "parent_model" in details:
|
||||
output += f"Parent Model: {details['parent_model']}\n"
|
||||
|
||||
if "format" in details:
|
||||
output += f"Format: {details['format']}\n"
|
||||
|
||||
if "family" in details:
|
||||
output += f"Family: {details['family']}\n"
|
||||
|
||||
if "families" in details:
|
||||
output += f"Families: {', '.join(details['families'])}\n"
|
||||
|
||||
if "parameter_size" in details:
|
||||
output += f"Parameter Size: {details['parameter_size']}\n"
|
||||
|
||||
if "quantization_level" in details:
|
||||
output += f"Quantization Level: {details['quantization_level']}\n"
|
||||
|
||||
return output
|
||||
|
||||
def pull_model(self, model_name: str) -> str:
|
||||
"""
|
||||
Pull a model from the Ollama library
|
||||
|
||||
Args:
|
||||
model_name: Name of the model to pull
|
||||
|
||||
Returns:
|
||||
Result message
|
||||
"""
|
||||
result = self._make_request("pull", method="POST", data={"name": model_name})
|
||||
|
||||
if result["success"]:
|
||||
return f"✅ Model '{model_name}' pulled successfully."
|
||||
else:
|
||||
return f"❌ Failed to pull model '{model_name}': {result['error']}"
|
||||
|
||||
def delete_model(self, model_name: str) -> str:
|
||||
"""
|
||||
Delete a model from the Ollama instance
|
||||
|
||||
Args:
|
||||
model_name: Name of the model to delete
|
||||
|
||||
Returns:
|
||||
Result message
|
||||
"""
|
||||
result = self._make_request("delete", method="POST", data={"name": model_name})
|
||||
|
||||
if result["success"]:
|
||||
return f"✅ Model '{model_name}' deleted successfully."
|
||||
else:
|
||||
return f"❌ Failed to delete model '{model_name}': {result['error']}"
|
||||
|
||||
def generate_completion(self, model: str, prompt: str, system: Optional[str] = None) -> str:
|
||||
"""
|
||||
Generate a completion using a model
|
||||
|
||||
Args:
|
||||
model: Model name
|
||||
prompt: Prompt text
|
||||
system: Optional system prompt
|
||||
|
||||
Returns:
|
||||
Generated text
|
||||
"""
|
||||
data = {
|
||||
"model": model,
|
||||
"prompt": prompt,
|
||||
"stream": False
|
||||
}
|
||||
|
||||
if system:
|
||||
data["system"] = system
|
||||
|
||||
result = self._make_request("generate", method="POST", data=data)
|
||||
|
||||
if not result["success"]:
|
||||
return f"Error generating completion: {result['error']}"
|
||||
|
||||
response = result["data"]
|
||||
return response.get("response", "No response generated")
|
||||
|
||||
def check_ollama_status(self) -> str:
|
||||
"""
|
||||
Check if the Ollama instance is running and responsive
|
||||
|
||||
Returns:
|
||||
Status message
|
||||
"""
|
||||
try:
|
||||
response = requests.get(f"{self.base_url}/", timeout=5)
|
||||
if response.status_code == 200:
|
||||
return f"✅ Ollama instance at {self.base_url} is running and responsive."
|
||||
else:
|
||||
return f"⚠️ Ollama instance at {self.base_url} returned status {response.status_code}."
|
||||
except requests.exceptions.ConnectionError:
|
||||
return f"❌ Cannot connect to Ollama instance at {self.base_url}."
|
||||
except requests.exceptions.Timeout:
|
||||
return f"❌ Connection to Ollama instance at {self.base_url} timed out."
|
||||
except Exception as e:
|
||||
return f"❌ Error checking Ollama status: {str(e)}"
|
||||
|
||||
def get_running_models(self) -> str:
|
||||
"""
|
||||
Get information about currently running models
|
||||
|
||||
Returns:
|
||||
Formatted string with running model information
|
||||
"""
|
||||
result = self._make_request("ps")
|
||||
|
||||
if not result["success"]:
|
||||
return f"Error getting running models: {result['error']}"
|
||||
|
||||
running = result["data"].get("models", [])
|
||||
if not running:
|
||||
return "No models currently running."
|
||||
|
||||
output = "Currently Running Models:\n"
|
||||
output += "=" * 50 + "\n"
|
||||
|
||||
for model in running:
|
||||
name = model.get("name", "Unknown")
|
||||
size = model.get("size", 0)
|
||||
digest = model.get("digest", "")
|
||||
expires_at = model.get("expires_at", "")
|
||||
|
||||
# Convert size to human readable format
|
||||
if size > 1024 * 1024 * 1024:
|
||||
size_str = f"{size / (1024 * 1024 * 1024):.1f} GB"
|
||||
elif size > 1024 * 1024:
|
||||
size_str = f"{size / (1024 * 1024):.1f} MB"
|
||||
else:
|
||||
size_str = f"{size / 1024:.1f} KB"
|
||||
|
||||
# Format expiration time
|
||||
if expires_at:
|
||||
from datetime import datetime
|
||||
try:
|
||||
dt = datetime.fromisoformat(expires_at.replace('Z', '+00:00'))
|
||||
expires_str = dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
except:
|
||||
expires_str = expires_at
|
||||
else:
|
||||
expires_str = "N/A"
|
||||
|
||||
output += f"Name: {name}\n"
|
||||
output += f"Size: {size_str}\n"
|
||||
output += f"Digest: {digest[:20]}...\n"
|
||||
output += f"Expires: {expires_str}\n"
|
||||
output += "-" * 30 + "\n"
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def get_ollama_tools(base_url: str = "http://localhost:11434") -> List[Tool]:
|
||||
"""
|
||||
Get a list of Ollama tools for the MaxClaw agent
|
||||
|
||||
Args:
|
||||
base_url: Base URL of the Ollama instance
|
||||
|
||||
Returns:
|
||||
List of LangChain tools for Ollama management
|
||||
"""
|
||||
ollama = OllamaTools(base_url)
|
||||
|
||||
tools = [
|
||||
Tool(
|
||||
name="ollama_list_models",
|
||||
func=lambda: ollama.list_models(),
|
||||
description="List all available models on the Ollama instance"
|
||||
),
|
||||
Tool(
|
||||
name="ollama_model_info",
|
||||
func=lambda model_name: ollama.get_model_info(model_name),
|
||||
description="Get detailed information about a specific model"
|
||||
),
|
||||
Tool(
|
||||
name="ollama_pull_model",
|
||||
func=lambda model_name: ollama.pull_model(model_name),
|
||||
description="Pull a model from the Ollama library"
|
||||
),
|
||||
Tool(
|
||||
name="ollama_delete_model",
|
||||
func=lambda model_name: ollama.delete_model(model_name),
|
||||
description="Delete a model from the Ollama instance"
|
||||
),
|
||||
Tool(
|
||||
name="ollama_generate",
|
||||
func=lambda model, prompt, system="": ollama.generate_completion(model, prompt, system if system else None),
|
||||
description="Generate a completion using a model with optional system prompt"
|
||||
),
|
||||
Tool(
|
||||
name="ollama_status",
|
||||
func=lambda: ollama.check_ollama_status(),
|
||||
description="Check if the Ollama instance is running and responsive"
|
||||
),
|
||||
Tool(
|
||||
name="ollama_running_models",
|
||||
func=lambda: ollama.get_running_models(),
|
||||
description="Get information about currently running models"
|
||||
)
|
||||
]
|
||||
|
||||
return tools
|
||||
@@ -0,0 +1,320 @@
|
||||
"""
|
||||
Proxmox management tools for MaxClaw Agent
|
||||
Provides tools for managing Proxmox virtual machines and containers
|
||||
"""
|
||||
import subprocess
|
||||
import json
|
||||
from typing import Dict, List, Optional, Any
|
||||
from langchain.tools import Tool
|
||||
|
||||
|
||||
class ProxmoxTools:
|
||||
"""Tools for managing Proxmox virtual environment"""
|
||||
|
||||
def __init__(self, host: str = "192.168.0.100", user: str = "root",
|
||||
password: Optional[str] = None, api_token: Optional[str] = None):
|
||||
"""
|
||||
Initialize Proxmox tools
|
||||
|
||||
Args:
|
||||
host: Proxmox host address
|
||||
user: Username for authentication
|
||||
password: Password for authentication (optional if using API token)
|
||||
api_token: API token for authentication (optional if using password)
|
||||
"""
|
||||
self.host = host
|
||||
self.user = user
|
||||
self.password = password
|
||||
self.api_token = api_token
|
||||
self.base_url = f"https://{host}:8006/api2/json"
|
||||
|
||||
def _run_pvesh_command(self, command: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Run a pvesh command and return the result
|
||||
|
||||
Args:
|
||||
command: The pvesh command to run
|
||||
|
||||
Returns:
|
||||
Dictionary with command result
|
||||
"""
|
||||
try:
|
||||
# Build the full command
|
||||
full_command = f"pvesh {command}"
|
||||
|
||||
# Add authentication if needed
|
||||
if self.api_token:
|
||||
full_command = f"pvesh {command} --apitoken {self.api_token}"
|
||||
elif self.password:
|
||||
# Note: In production, use proper authentication methods
|
||||
full_command = f"pvesh {command} --username {self.user} --password {self.password}"
|
||||
|
||||
# Run the command
|
||||
result = subprocess.run(
|
||||
full_command,
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
try:
|
||||
return {"success": True, "data": json.loads(result.stdout)}
|
||||
except json.JSONDecodeError:
|
||||
return {"success": True, "data": result.stdout}
|
||||
else:
|
||||
return {"success": False, "error": result.stderr}
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
return {"success": False, "error": "Command timed out"}
|
||||
except Exception as e:
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
def list_vms(self) -> str:
|
||||
"""
|
||||
List all virtual machines in the Proxmox cluster
|
||||
|
||||
Returns:
|
||||
Formatted string with VM information
|
||||
"""
|
||||
result = self._run_pvesh_command("get /cluster/resources --type vm")
|
||||
|
||||
if not result["success"]:
|
||||
return f"Error listing VMs: {result['error']}"
|
||||
|
||||
vms = result["data"]
|
||||
if not vms:
|
||||
return "No virtual machines found."
|
||||
|
||||
output = "Virtual Machines:\n"
|
||||
output += "=" * 50 + "\n"
|
||||
|
||||
for vm in vms:
|
||||
vmid = vm.get("vmid", "N/A")
|
||||
name = vm.get("name", "Unknown")
|
||||
status = vm.get("status", "Unknown")
|
||||
node = vm.get("node", "Unknown")
|
||||
cpu = vm.get("cpu", 0) * 100
|
||||
mem = vm.get("mem", 0) / (1024 * 1024) # Convert to MB
|
||||
maxmem = vm.get("maxmem", 0) / (1024 * 1024 * 1024) # Convert to GB
|
||||
|
||||
output += f"VMID: {vmid}\n"
|
||||
output += f"Name: {name}\n"
|
||||
output += f"Status: {status}\n"
|
||||
output += f"Node: {node}\n"
|
||||
output += f"CPU Usage: {cpu:.1f}%\n"
|
||||
output += f"Memory: {mem:.1f} MB / {maxmem:.1f} GB\n"
|
||||
output += "-" * 30 + "\n"
|
||||
|
||||
return output
|
||||
|
||||
def get_vm_status(self, vmid: int) -> str:
|
||||
"""
|
||||
Get the status of a specific virtual machine
|
||||
|
||||
Args:
|
||||
vmid: Virtual machine ID
|
||||
|
||||
Returns:
|
||||
Formatted string with VM status
|
||||
"""
|
||||
result = self._run_pvesh_command(f"get /nodes/proxmox/qemu/{vmid}/status/current")
|
||||
|
||||
if not result["success"]:
|
||||
return f"Error getting VM status: {result['error']}"
|
||||
|
||||
vm = result["data"]
|
||||
status = vm.get("status", "Unknown")
|
||||
cpu = vm.get("cpu", 0) * 100
|
||||
mem = vm.get("mem", 0) / (1024 * 1024) # Convert to MB
|
||||
maxmem = vm.get("maxmem", 0) / (1024 * 1024 * 1024) # Convert to GB
|
||||
uptime = vm.get("uptime", 0)
|
||||
|
||||
# Convert uptime to human readable format
|
||||
days = uptime // (24 * 3600)
|
||||
hours = (uptime % (24 * 3600)) // 3600
|
||||
minutes = (uptime % 3600) // 60
|
||||
|
||||
output = f"VM {vmid} Status:\n"
|
||||
output += "=" * 30 + "\n"
|
||||
output += f"Status: {status}\n"
|
||||
output += f"CPU Usage: {cpu:.1f}%\n"
|
||||
output += f"Memory: {mem:.1f} MB / {maxmem:.1f} GB\n"
|
||||
output += f"Uptime: {days} days, {hours} hours, {minutes} minutes\n"
|
||||
|
||||
return output
|
||||
|
||||
def start_vm(self, vmid: int) -> str:
|
||||
"""
|
||||
Start a virtual machine
|
||||
|
||||
Args:
|
||||
vmid: Virtual machine ID
|
||||
|
||||
Returns:
|
||||
Result message
|
||||
"""
|
||||
result = self._run_pvesh_command(f"create /nodes/proxmox/qemu/{vmid}/status/start")
|
||||
|
||||
if result["success"]:
|
||||
return f"✅ VM {vmid} started successfully."
|
||||
else:
|
||||
return f"❌ Failed to start VM {vmid}: {result['error']}"
|
||||
|
||||
def stop_vm(self, vmid: int) -> str:
|
||||
"""
|
||||
Stop a virtual machine
|
||||
|
||||
Args:
|
||||
vmid: Virtual machine ID
|
||||
|
||||
Returns:
|
||||
Result message
|
||||
"""
|
||||
result = self._run_pvesh_command(f"create /nodes/proxmox/qemu/{vmid}/status/stop")
|
||||
|
||||
if result["success"]:
|
||||
return f"✅ VM {vmid} stopped successfully."
|
||||
else:
|
||||
return f"❌ Failed to stop VM {vmid}: {result['error']}"
|
||||
|
||||
def restart_vm(self, vmid: int) -> str:
|
||||
"""
|
||||
Restart a virtual machine
|
||||
|
||||
Args:
|
||||
vmid: Virtual machine ID
|
||||
|
||||
Returns:
|
||||
Result message
|
||||
"""
|
||||
result = self._run_pvesh_command(f"create /nodes/proxmox/qemu/{vmid}/status/reboot")
|
||||
|
||||
if result["success"]:
|
||||
return f"✅ VM {vmid} restarted successfully."
|
||||
else:
|
||||
return f"❌ Failed to restart VM {vmid}: {result['error']}"
|
||||
|
||||
def create_snapshot(self, vmid: int, snapname: str, description: str = "") -> str:
|
||||
"""
|
||||
Create a snapshot of a virtual machine
|
||||
|
||||
Args:
|
||||
vmid: Virtual machine ID
|
||||
snapname: Snapshot name
|
||||
description: Optional description
|
||||
|
||||
Returns:
|
||||
Result message
|
||||
"""
|
||||
command = f"create /nodes/proxmox/qemu/{vmid}/snapshot"
|
||||
command += f" --snapname {snapname}"
|
||||
if description:
|
||||
command += f" --description \"{description}\""
|
||||
|
||||
result = self._run_pvesh_command(command)
|
||||
|
||||
if result["success"]:
|
||||
return f"✅ Snapshot '{snapname}' created for VM {vmid}."
|
||||
else:
|
||||
return f"❌ Failed to create snapshot for VM {vmid}: {result['error']}"
|
||||
|
||||
def list_snapshots(self, vmid: int) -> str:
|
||||
"""
|
||||
List all snapshots for a virtual machine
|
||||
|
||||
Args:
|
||||
vmid: Virtual machine ID
|
||||
|
||||
Returns:
|
||||
Formatted string with snapshot information
|
||||
"""
|
||||
result = self._run_pvesh_command(f"get /nodes/proxmox/qemu/{vmid}/snapshot")
|
||||
|
||||
if not result["success"]:
|
||||
return f"Error listing snapshots: {result['error']}"
|
||||
|
||||
snapshots = result["data"]
|
||||
if not snapshots:
|
||||
return f"No snapshots found for VM {vmid}."
|
||||
|
||||
output = f"Snapshots for VM {vmid}:\n"
|
||||
output += "=" * 40 + "\n"
|
||||
|
||||
for snap in snapshots:
|
||||
name = snap.get("name", "Unknown")
|
||||
description = snap.get("description", "")
|
||||
snaptime = snap.get("snaptime", 0)
|
||||
|
||||
# Convert timestamp to readable format
|
||||
if snaptime:
|
||||
from datetime import datetime
|
||||
dt = datetime.fromtimestamp(snaptime)
|
||||
time_str = dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
else:
|
||||
time_str = "N/A"
|
||||
|
||||
output += f"Name: {name}\n"
|
||||
output += f"Description: {description}\n"
|
||||
output += f"Created: {time_str}\n"
|
||||
output += "-" * 30 + "\n"
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def get_proxmox_tools(host: str = "192.168.0.100", user: str = "root",
|
||||
password: Optional[str] = None, api_token: Optional[str] = None) -> List[Tool]:
|
||||
"""
|
||||
Get a list of Proxmox tools for the MaxClaw agent
|
||||
|
||||
Args:
|
||||
host: Proxmox host address
|
||||
user: Username for authentication
|
||||
password: Password for authentication
|
||||
api_token: API token for authentication
|
||||
|
||||
Returns:
|
||||
List of LangChain tools for Proxmox management
|
||||
"""
|
||||
proxmox = ProxmoxTools(host, user, password, api_token)
|
||||
|
||||
tools = [
|
||||
Tool(
|
||||
name="proxmox_list_vms",
|
||||
func=lambda: proxmox.list_vms(),
|
||||
description="List all virtual machines in the Proxmox cluster with their status and resource usage"
|
||||
),
|
||||
Tool(
|
||||
name="proxmox_vm_status",
|
||||
func=lambda vmid: proxmox.get_vm_status(int(vmid)),
|
||||
description="Get detailed status of a specific virtual machine by VMID"
|
||||
),
|
||||
Tool(
|
||||
name="proxmox_start_vm",
|
||||
func=lambda vmid: proxmox.start_vm(int(vmid)),
|
||||
description="Start a virtual machine by VMID"
|
||||
),
|
||||
Tool(
|
||||
name="proxmox_stop_vm",
|
||||
func=lambda vmid: proxmox.stop_vm(int(vmid)),
|
||||
description="Stop a virtual machine by VMID"
|
||||
),
|
||||
Tool(
|
||||
name="proxmox_restart_vm",
|
||||
func=lambda vmid: proxmox.restart_vm(int(vmid)),
|
||||
description="Restart a virtual machine by VMID"
|
||||
),
|
||||
Tool(
|
||||
name="proxmox_create_snapshot",
|
||||
func=lambda vmid, snapname, description="": proxmox.create_snapshot(int(vmid), snapname, description),
|
||||
description="Create a snapshot of a virtual machine with optional description"
|
||||
),
|
||||
Tool(
|
||||
name="proxmox_list_snapshots",
|
||||
func=lambda vmid: proxmox.list_snapshots(int(vmid)),
|
||||
description="List all snapshots for a specific virtual machine"
|
||||
)
|
||||
]
|
||||
|
||||
return tools
|
||||
Reference in new issue
Block a user