generate_vm_report
Generate detailed As-Built reports for one or more VMs, covering configuration, compute, disks, NICs, categories, and host placement, with an optional Excalidraw diagram.
Instructions
Generate a detailed As-Built report for one or more VMs. Covers VM configuration, compute resources, disks, NICs, categories, and host placement. Returns Markdown with an Excalidraw VM layout diagram.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| vm_uuids | Yes | List of VM UUIDs to report on (required). | |
| include_diagram | No | Include Excalidraw VM diagram JSON (default: true) |
Implementation Reference
- Main handler function for generate_vm_report. Accepts vm_uuids (required) and include_diagram (optional). Fetches VM details via v4_get for each UUID, builds Markdown report via _build_vm_markdown, and optionally generates Excalidraw diagram via _generate_vm_diagram.
async def handle_generate_vm_report( client: NutanixClient, arguments: dict[str, Any] ) -> dict[str, Any]: """Generate detailed VM As-Built report.""" vm_uuids = arguments["vm_uuids"] include_diagram = arguments.get("include_diagram", True) # Fetch detailed info for each VM vms = [] for vm_uuid in vm_uuids: try: vm_result = await client.v4_get( namespace="vmm", path=f"ahv/config/vms/{vm_uuid}" ) vms.append(vm_result.get("data", {})) except Exception as e: vms.append({"name": f"Error: {vm_uuid}", "extId": vm_uuid, "error": str(e)}) # Build markdown markdown = _build_vm_markdown(vms) result: dict[str, Any] = {"report_markdown": markdown} # Build diagram if include_diagram: diagram_elements = _generate_vm_diagram(vms) result["excalidraw_diagram"] = { "type": "excalidraw", "version": 2, "elements": diagram_elements, "appState": {"viewBackgroundColor": "#ffffff"}, } return result - Tool definition and input schema for generate_vm_report. Defines the 'vm_uuids' (required array of strings) and 'include_diagram' (optional boolean, default true) input parameters.
{ "name": "generate_vm_report", "description": ( "Generate a detailed As-Built report for one or more VMs. " "Covers VM configuration, compute resources, disks, NICs, categories, " "and host placement. Returns Markdown with an Excalidraw VM layout diagram." ), "inputSchema": { "type": "object", "properties": { "vm_uuids": { "type": "array", "items": {"type": "string"}, "description": "List of VM UUIDs to report on (required).", }, "include_diagram": { "type": "boolean", "description": "Include Excalidraw VM diagram JSON (default: true)", "default": True, }, }, "required": ["vm_uuids"], }, }, - src/nutanix_mcp/tools/report.py:1172-1178 (registration)Handler dispatch dict REPORT_HANDLERS that maps 'generate_vm_report' to handle_generate_vm_report function.
# ─── Handler Dispatch ───────────────────────────────────────────────────────── REPORT_HANDLERS: dict[str, Any] = { "generate_environment_report": handle_generate_environment_report, "generate_cluster_report": handle_generate_cluster_report, "generate_vm_report": handle_generate_vm_report, } - src/nutanix_mcp/tools/__init__.py:1-12 (registration)Tool registry that imports REPORT_TOOLS from report.py and exposes them via get_all_tools() aggregation function.
"""Tool registry and definitions.""" from nutanix_mcp.tools.vm import VM_TOOLS from nutanix_mcp.tools.cluster import CLUSTER_TOOLS from nutanix_mcp.tools.prism_element import PE_TOOLS from nutanix_mcp.tools.report import REPORT_TOOLS from nutanix_mcp.tools.networking import NETWORKING_TOOLS def get_all_tools() -> list[dict]: """Return all registered tool definitions.""" return VM_TOOLS + CLUSTER_TOOLS + PE_TOOLS + REPORT_TOOLS + NETWORKING_TOOLS - _build_vm_markdown helper that builds the detailed Markdown report for VMs, covering configuration, compute, disks, NICs, GPUs, boot config, and categories.
def _build_vm_markdown(vms: list[dict]) -> str: """Build detailed VM Markdown report.""" lines = [] lines.append("# Nutanix As-Built Report — Virtual Machines") lines.append(f"\n**Generated:** {_now_iso()}") lines.append(f"**VMs in Report:** {len(vms)}") lines.append("") for vm in vms: vm_name = vm.get("name", "N/A") ext_id = vm.get("extId", "N/A") power_state = vm.get("powerState", "N/A") lines.append(f"## {vm_name}") lines.append("") # Basic info lines.append("### Configuration") lines.append("") lines.append("| Property | Value |") lines.append("|----------|-------|") lines.append(f"| Name | {vm_name} |") lines.append(f"| UUID | `{ext_id}` |") lines.append(f"| Power State | {power_state} |") lines.append(f"| Description | {vm.get('description', 'N/A')} |") lines.append(f"| Guest OS | {vm.get('guestCustomization', {}).get('config', {}).get('sysprep', {}).get('installType', 'N/A')} |") lines.append(f"| Machine Type | {vm.get('machineType', 'N/A')} |") lines.append(f"| Hardware Clock Timezone | {vm.get('hardwareClockTimezone', 'N/A')} |") # Compute num_sockets = vm.get("numSockets", 1) cores_per_socket = vm.get("numCoresPerSocket", 1) threads_per_core = vm.get("numThreadsPerCore", 1) total_vcpus = num_sockets * cores_per_socket mem_bytes = vm.get("memorySizeBytes", 0) lines.append(f"| CPU Sockets | {num_sockets} |") lines.append(f"| Cores per Socket | {cores_per_socket} |") lines.append(f"| Threads per Core | {threads_per_core} |") lines.append(f"| Total vCPUs | {total_vcpus} |") lines.append(f"| Memory | {_format_bytes(mem_bytes)} |") # Cluster/Host cluster_info = vm.get("cluster", {}) host_info = vm.get("host", {}) lines.append(f"| Cluster | {cluster_info.get('name', cluster_info.get('extId', 'N/A'))} |") lines.append(f"| Host | {host_info.get('name', host_info.get('extId', 'N/A'))} |") # Categories categories = vm.get("categories", []) if categories: cat_str = ", ".join(f"{c.get('key', '')}:{c.get('value', '')}" for c in categories) lines.append(f"| Categories | {cat_str} |") lines.append("") # Disks disks = vm.get("disks", []) if disks: lines.append("### Disks") lines.append("") lines.append("| # | Type | Size | Bus | Controller | Storage Container |") lines.append("|---|------|------|-----|------------|-------------------|") for idx, disk in enumerate(disks): d_type = disk.get("diskType", "N/A") d_size = _format_bytes(disk.get("diskSizeBytes")) bus = disk.get("busType", "N/A") addr = disk.get("deviceAddress", {}) controller = f"{addr.get('busType', '')}{addr.get('index', '')}" if addr else "N/A" sc_ref = _safe_get(disk, "storageConfig", "storageContainerReference", "extId") lines.append(f"| {idx+1} | {d_type} | {d_size} | {bus} | {controller} | `{sc_ref}` |") lines.append("") # NICs nics = vm.get("nics", []) if nics: lines.append("### Network Interfaces") lines.append("") lines.append("| # | Type | Subnet | MAC | IP Address | VLAN Mode |") lines.append("|---|------|--------|-----|------------|-----------|") for idx, nic in enumerate(nics): n_type = nic.get("nicType", "N/A") subnet = _safe_get(nic, "subnet", "name") mac = nic.get("macAddress", "N/A") ip = _safe_get(nic, "ipAddress", "ip") vlan_mode = nic.get("vlanMode", "N/A") lines.append(f"| {idx+1} | {n_type} | {subnet} | {mac} | {ip} | {vlan_mode} |") lines.append("") # GPU gpus = vm.get("gpus", []) if gpus: lines.append("### GPUs") lines.append("") lines.append("| Mode | Vendor | Device |") lines.append("|------|--------|--------|") for gpu in gpus: lines.append(f"| {gpu.get('mode', 'N/A')} | {gpu.get('vendor', 'N/A')} | {gpu.get('deviceName', 'N/A')} |") lines.append("") # Boot config boot_config = vm.get("bootConfig", {}) if boot_config: lines.append("### Boot Configuration") lines.append("") lines.append(f"- Boot Type: {boot_config.get('bootType', 'N/A')}") boot_device = boot_config.get("bootDeviceOrder", []) if boot_device: lines.append(f"- Boot Device Order: {', '.join(boot_device)}") lines.append("") lines.append("---") lines.append("") return "\n".join(lines)