Преглед изворни кода

Interfaz gráfico para gestión

Celestino Rey пре 7 месеци
родитељ
комит
7bac142382
4 измењених фајлова са 163 додато и 3 уклоњено
  1. 2 0
      README.md
  2. 14 3
      app/main.py
  3. 88 0
      static/app.js
  4. 59 0
      static/index.html

+ 2 - 0
README.md

@@ -32,6 +32,8 @@ uvicorn app.main:app --reload --host 0.0.0.0
 
 The API will be available at http://0.0.0.0:8000
 
+Access the web interface at http://0.0.0.0:8000
+
 Access the interactive documentation at http://0.0.0.0:8000/docs
 
 ## Endpoints

+ 14 - 3
app/main.py

@@ -1,16 +1,21 @@
 from fastapi import FastAPI
+from fastapi.staticfiles import StaticFiles
+from fastapi.responses import FileResponse
 import os
 import libvirt
 import subprocess
+import random
 
 app = FastAPI(title="VirtManager API", description="API for managing virtual machines")
 
+app.mount("/static", StaticFiles(directory="static"), name="static")
+
 # Connect to libvirt
 conn = libvirt.open('qemu:///system')
 
 @app.get("/")
 async def root():
-    return {"message": "VirtManager API"}
+    return FileResponse("static/index.html")
 
 @app.get("/vms")
 async def list_vms():
@@ -48,6 +53,9 @@ async def stop_vm(vm_id: int):
 async def create_vm(name: str, memory: int, disk_size: int, iso: str):
     images_path = os.environ.get('LIBVIRT_IMAGES_PATH', '/var/lib/libvirt/images')
     disk_path = f"{images_path}/{name}.qcow2"
+    iso_path = f"{images_path}/{iso}"
+    
+    print(f"Using ISO path: {iso_path}")
     
     # Create disk
     try:
@@ -57,6 +65,9 @@ async def create_vm(name: str, memory: int, disk_size: int, iso: str):
     except Exception as e:
         return {"error": f"Error creating disk: {str(e)}"}
     
+    # Generate unique MAC
+    mac = f"52:54:00:{random.randint(0, 255):02x}:{random.randint(0, 255):02x}:{random.randint(0, 255):02x}"
+    
     # Basic XML template
     xml_template = f"""<domain type='kvm'>
   <name>{name}</name>
@@ -93,12 +104,12 @@ async def create_vm(name: str, memory: int, disk_size: int, iso: str):
     </disk>
     <disk type='file' device='cdrom'>
       <driver name='qemu' type='raw'/>
-      <source file='{images_path}/{iso}'/>
+      <source file='{iso_path}'/>
       <target dev='hda' bus='ide'/>
       <readonly/>
     </disk>
     <interface type='network'>
-      <mac address='52:54:00:00:00:01'/>
+      <mac address='{mac}'/>
       <source network='default'/>
       <model type='virtio'/>
     </interface>

+ 88 - 0
static/app.js

@@ -0,0 +1,88 @@
+const API_BASE = window.location.origin;
+
+async function loadVMs() {
+    try {
+        const response = await fetch(`${API_BASE}/vms`);
+        const data = await response.json();
+        const tbody = document.getElementById('vmTableBody');
+        tbody.innerHTML = '';
+        
+        data.vms.forEach(vm => {
+            const row = document.createElement('tr');
+            row.innerHTML = `
+                <td>${vm.id}</td>
+                <td>${vm.name}</td>
+                <td>${vm.state}</td>
+                <td>
+                    <button class="btn btn-sm btn-success" onclick="startVM(${vm.id})">Start</button>
+                    <button class="btn btn-sm btn-warning" onclick="stopVM(${vm.id})">Stop</button>
+                    <button class="btn btn-sm btn-danger" onclick="deleteVM('${vm.name}')">Delete</button>
+                </td>
+            `;
+            tbody.appendChild(row);
+        });
+    } catch (error) {
+        console.error('Error loading VMs:', error);
+        alert('Error loading VMs');
+    }
+}
+
+async function startVM(id) {
+    try {
+        await fetch(`${API_BASE}/vms/${id}/start`);
+        loadVMs();
+    } catch (error) {
+        console.error('Error starting VM:', error);
+        alert('Error starting VM');
+    }
+}
+
+async function stopVM(id) {
+    try {
+        await fetch(`${API_BASE}/vms/${id}/stop`);
+        loadVMs();
+    } catch (error) {
+        console.error('Error stopping VM:', error);
+        alert('Error stopping VM');
+    }
+}
+
+async function deleteVM(name) {
+    if (confirm(`Delete VM ${name}?`)) {
+        try {
+            await fetch(`${API_BASE}/vms/${name}`, { method: 'DELETE' });
+            loadVMs();
+        } catch (error) {
+            console.error('Error deleting VM:', error);
+            alert('Error deleting VM');
+        }
+    }
+}
+
+document.getElementById('createVMForm').addEventListener('submit', async (e) => {
+    e.preventDefault();
+    const name = document.getElementById('name').value;
+    const memory = document.getElementById('memory').value;
+    const disk_size = document.getElementById('disk_size').value;
+    const iso = document.getElementById('iso').value;
+    
+    try {
+        const response = await fetch(`${API_BASE}/vms?name=${name}&memory=${memory}&disk_size=${disk_size}&iso=${iso}`, {
+            method: 'POST'
+        });
+        const result = await response.json();
+        if (response.ok) {
+            alert(result.message);
+            loadVMs();
+            document.getElementById('createVMForm').reset();
+        } else {
+            alert(result.error);
+        }
+    } catch (error) {
+        console.error('Error creating VM:', error);
+        alert('Error creating VM');
+    }
+});
+
+// Load VMs on page load
+loadVMs();

+ 59 - 0
static/index.html

@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>VirtManager</title>
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
+</head>
+<body>
+    <div class="container mt-5">
+        <h1 class="mb-4">VirtManager</h1>
+        
+        <div class="row">
+            <div class="col-md-8">
+                <h2>Virtual Machines</h2>
+                <button class="btn btn-primary mb-3" onclick="loadVMs()">Refresh</button>
+                <table class="table table-striped" id="vmTable">
+                    <thead>
+                        <tr>
+                            <th>ID</th>
+                            <th>Name</th>
+                            <th>State</th>
+                            <th>Actions</th>
+                        </tr>
+                    </thead>
+                    <tbody id="vmTableBody">
+                    </tbody>
+                </table>
+            </div>
+            
+            <div class="col-md-4">
+                <h2>Create VM</h2>
+                <form id="createVMForm">
+                    <div class="mb-3">
+                        <label for="name" class="form-label">Name</label>
+                        <input type="text" class="form-control" id="name" required>
+                    </div>
+                    <div class="mb-3">
+                        <label for="memory" class="form-label">Memory (MiB)</label>
+                        <input type="number" class="form-control" id="memory" required>
+                    </div>
+                    <div class="mb-3">
+                        <label for="disk_size" class="form-label">Disk Size (GB)</label>
+                        <input type="number" class="form-control" id="disk_size" required>
+                    </div>
+                    <div class="mb-3">
+                        <label for="iso" class="form-label">ISO File</label>
+                        <input type="text" class="form-control" id="iso" required>
+                    </div>
+                    <button type="submit" class="btn btn-success">Create VM</button>
+                </form>
+            </div>
+        </div>
+    </div>
+
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
+    <script src="/static/app.js"></script>
+</body>
+</html>