mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-21 02:31:08 -05:00
Refactor test utilities and enhance ASGI settings
- Cleaned up and standardized assertions in ApiTestMixin for API response validation. - Updated ASGI settings to use os.environ for setting the DJANGO_SETTINGS_MODULE. - Removed unused imports and improved formatting in settings.py. - Refactored URL patterns in urls.py for better readability and organization. - Enhanced view functions in views.py for consistency and clarity. - Added .flake8 configuration for linting and style enforcement. - Introduced type stubs for django-environ to improve type checking with Pylance.
This commit is contained in:
@@ -11,44 +11,46 @@ import os
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
import tempfile
|
||||
import subprocess
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
# Import our modular components
|
||||
from iso_builder import UbuntuISOBuilder
|
||||
from vm_manager import UnraidVMManager
|
||||
# Note: UnraidVMManager is defined locally in this file
|
||||
|
||||
# Configuration
|
||||
UNRAID_HOST = os***REMOVED***iron.get("UNRAID_HOST", "localhost")
|
||||
UNRAID_USER = os***REMOVED***iron.get("UNRAID_USER", "root")
|
||||
VM_NAME = os***REMOVED***iron.get("VM_NAME", "thrillwiki-vm")
|
||||
VM_MEMORY = int(os***REMOVED***iron.get("VM_MEMORY", 4096)) # MB
|
||||
VM_VCPUS = int(os***REMOVED***iron.get("VM_VCPUS", 2))
|
||||
VM_DISK_SIZE = int(os***REMOVED***iron.get("VM_DISK_SIZE", 50)) # GB
|
||||
SSH_PUBLIC_KEY = os***REMOVED***iron.get("SSH_PUBLIC_KEY", "")
|
||||
UNRAID_HOST = os.environ.get("UNRAID_HOST", "localhost")
|
||||
UNRAID_USER = os.environ.get("UNRAID_USER", "root")
|
||||
VM_NAME = os.environ.get("VM_NAME", "thrillwiki-vm")
|
||||
VM_MEMORY = int(os.environ.get("VM_MEMORY", 4096)) # MB
|
||||
VM_VCPUS = int(os.environ.get("VM_VCPUS", 2))
|
||||
VM_DISK_SIZE = int(os.environ.get("VM_DISK_SIZE", 50)) # GB
|
||||
SSH_PUBLIC_KEY = os.environ.get("SSH_PUBLIC_KEY", "")
|
||||
|
||||
# Network Configuration
|
||||
VM_IP = os***REMOVED***iron.get("VM_IP", "dhcp")
|
||||
VM_GATEWAY = os***REMOVED***iron.get("VM_GATEWAY", "192.168.20.1")
|
||||
VM_NETMASK = os***REMOVED***iron.get("VM_NETMASK", "255.255.255.0")
|
||||
VM_NETWORK = os***REMOVED***iron.get("VM_NETWORK", "192.168.20.0/24")
|
||||
VM_IP = os.environ.get("VM_IP", "dhcp")
|
||||
VM_GATEWAY = os.environ.get("VM_GATEWAY", "192.168.20.1")
|
||||
VM_NETMASK = os.environ.get("VM_NETMASK", "255.255.255.0")
|
||||
VM_NETWORK = os.environ.get("VM_NETWORK", "192.168.20.0/24")
|
||||
|
||||
# GitHub Configuration
|
||||
REPO_URL = os***REMOVED***iron.get("REPO_URL", "")
|
||||
GITHUB_USERNAME = os***REMOVED***iron.get("GITHUB_USERNAME", "")
|
||||
GITHUB_TOKEN = os***REMOVED***iron.get("GITHUB_TOKEN", "")
|
||||
REPO_URL = os.environ.get("REPO_URL", "")
|
||||
GITHUB_USERNAME = os.environ.get("GITHUB_USERNAME", "")
|
||||
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", "")
|
||||
|
||||
# Ubuntu version preference
|
||||
UBUNTU_VERSION = os***REMOVED***iron.get("UBUNTU_VERSION", "24.04")
|
||||
UBUNTU_VERSION = os.environ.get("UBUNTU_VERSION", "24.04")
|
||||
|
||||
# Setup logging
|
||||
os.makedirs("logs", exist_ok=True)
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(levelname)s - %(message)s",
|
||||
handlers=[logging.FileHandler("logs/unraid-vm.log"), logging.StreamHandler()],
|
||||
handlers=[
|
||||
logging.FileHandler("logs/unraid-vm.log"),
|
||||
logging.StreamHandler(),
|
||||
],
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -67,9 +69,9 @@ class UnraidVMManager:
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=15
|
||||
timeout=15,
|
||||
)
|
||||
|
||||
|
||||
if result.returncode == 0 and "Connected" in result.stdout:
|
||||
logger.info("Successfully connected to Unraid via SSH")
|
||||
return True
|
||||
@@ -114,24 +116,25 @@ class UnraidVMManager:
|
||||
import uuid
|
||||
|
||||
vm_uuid = existing_uuid if existing_uuid else str(uuid.uuid4())
|
||||
|
||||
|
||||
# Detect Ubuntu ISO dynamically
|
||||
ubuntu_iso_path = self._detect_ubuntu_iso()
|
||||
if not ubuntu_iso_path:
|
||||
raise FileNotFoundError("No Ubuntu ISO found for VM template")
|
||||
|
||||
|
||||
# Read XML template from file
|
||||
template_path = Path(__file__).parent / "thrillwiki-vm-template.xml"
|
||||
if not template_path.exists():
|
||||
raise FileNotFoundError(f"VM XML template not found at {template_path}")
|
||||
|
||||
with open(template_path, 'r', encoding='utf-8') as f:
|
||||
|
||||
with open(template_path, "r", encoding="utf-8") as f:
|
||||
xml_template = f.read()
|
||||
|
||||
|
||||
# Calculate CPU topology
|
||||
cpu_cores = VM_VCPUS // 2 if VM_VCPUS > 1 else 1
|
||||
cpu_threads = 2 if VM_VCPUS > 1 else 1
|
||||
|
||||
mac_suffix = self._generate_mac_suffix()
|
||||
|
||||
# Replace placeholders with actual values
|
||||
xml_content = xml_template.format(
|
||||
VM_NAME=VM_NAME,
|
||||
@@ -140,10 +143,10 @@ class UnraidVMManager:
|
||||
VM_VCPUS=VM_VCPUS,
|
||||
CPU_CORES=cpu_cores,
|
||||
CPU_THREADS=cpu_threads,
|
||||
MAC_SUFFIX=self._generate_mac_suffix(),
|
||||
UBUNTU_ISO_PATH=ubuntu_iso_path
|
||||
MAC_SUFFIX=mac_suffix,
|
||||
UBUNTU_ISO_PATH=ubuntu_iso_path,
|
||||
)
|
||||
|
||||
|
||||
return xml_content.strip()
|
||||
|
||||
def _detect_ubuntu_iso(self) -> Optional[str]:
|
||||
@@ -156,48 +159,52 @@ class UnraidVMManager:
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
|
||||
if find_all_result.returncode != 0 or not find_all_result.stdout.strip():
|
||||
return None
|
||||
|
||||
available_isos = find_all_result.stdout.strip().split('\n')
|
||||
|
||||
|
||||
available_isos = find_all_result.stdout.strip().split("\n")
|
||||
|
||||
# Prioritize ISOs by version and type
|
||||
# Sort by preference: 24.04 LTS > 22.04 LTS > 23.x > 20.04 > others
|
||||
# Within each version, prefer the latest point release
|
||||
priority_versions = [
|
||||
'24.04', # Ubuntu 24.04 LTS (highest priority)
|
||||
'22.04', # Ubuntu 22.04 LTS
|
||||
'23.10', # Ubuntu 23.10
|
||||
'23.04', # Ubuntu 23.04
|
||||
'20.04', # Ubuntu 20.04 LTS
|
||||
"24.04", # Ubuntu 24.04 LTS (highest priority)
|
||||
"22.04", # Ubuntu 22.04 LTS
|
||||
"23.10", # Ubuntu 23.10
|
||||
"23.04", # Ubuntu 23.04
|
||||
"20.04", # Ubuntu 20.04 LTS
|
||||
]
|
||||
|
||||
# Find the best ISO based on priority, preferring latest point releases
|
||||
|
||||
# Find the best ISO based on priority, preferring latest point
|
||||
# releases
|
||||
for version in priority_versions:
|
||||
# Find all ISOs for this version
|
||||
version_isos = []
|
||||
for iso in available_isos:
|
||||
if version in iso and ('server' in iso.lower() or 'live' in iso.lower()):
|
||||
if version in iso and (
|
||||
"server" in iso.lower() or "live" in iso.lower()
|
||||
):
|
||||
version_isos.append(iso)
|
||||
|
||||
|
||||
if version_isos:
|
||||
# Sort by version number (reverse to get latest first)
|
||||
# This will put 24.04.3 before 24.04.2 before 24.04.1 before 24.04
|
||||
# This will put 24.04.3 before 24.04.2 before 24.04.1
|
||||
# before 24.04
|
||||
version_isos.sort(reverse=True)
|
||||
return version_isos[0]
|
||||
|
||||
|
||||
# If no priority match, use the first server/live ISO found
|
||||
for iso in available_isos:
|
||||
if 'server' in iso.lower() or 'live' in iso.lower():
|
||||
if "server" in iso.lower() or "live" in iso.lower():
|
||||
return iso
|
||||
|
||||
|
||||
# If still no match, use the first Ubuntu ISO found (any type)
|
||||
if available_isos:
|
||||
return available_isos[0]
|
||||
|
||||
|
||||
return None
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error detecting Ubuntu ISO: {e}")
|
||||
return None
|
||||
@@ -212,7 +219,7 @@ class UnraidVMManager:
|
||||
# Always try to stop VM before updating (force stop)
|
||||
current_status = self.vm_status()
|
||||
logger.info(f"Current VM status: {current_status}")
|
||||
|
||||
|
||||
if current_status not in ["shut off", "unknown"]:
|
||||
logger.info(f"Stopping VM {VM_NAME} for configuration update...")
|
||||
self.stop_vm()
|
||||
@@ -230,7 +237,8 @@ class UnraidVMManager:
|
||||
check=True,
|
||||
)
|
||||
|
||||
# Create virtual disk if it doesn't exist (for both new and updated VMs)
|
||||
# Create virtual disk if it doesn't exist (for both new and updated
|
||||
# VMs)
|
||||
disk_check = subprocess.run(
|
||||
f"ssh {UNRAID_USER}@{UNRAID_HOST} 'test -f {self.vm_config_path}/vdisk1.qcow2'",
|
||||
shell=True,
|
||||
@@ -247,7 +255,8 @@ class UnraidVMManager:
|
||||
logger.info(f"Virtual disk already exists for VM {VM_NAME}")
|
||||
|
||||
# Always create/recreate cloud-init ISO for automated installation and ThrillWiki deployment
|
||||
# This ensures the latest configuration is used whether creating or updating the VM
|
||||
# This ensures the latest configuration is used whether creating or
|
||||
# updating the VM
|
||||
logger.info(
|
||||
"Creating cloud-init ISO for automated Ubuntu and ThrillWiki setup..."
|
||||
)
|
||||
@@ -257,9 +266,7 @@ class UnraidVMManager:
|
||||
|
||||
# For Ubuntu 24.04, use UEFI boot instead of kernel extraction
|
||||
# Ubuntu 24.04 has issues with direct kernel boot autoinstall
|
||||
logger.info(
|
||||
"Using UEFI boot for Ubuntu 24.04 compatibility..."
|
||||
)
|
||||
logger.info("Using UEFI boot for Ubuntu 24.04 compatibility...")
|
||||
if not self.fallback_to_uefi_boot():
|
||||
logger.error("UEFI boot setup failed")
|
||||
return False
|
||||
@@ -286,9 +293,9 @@ class UnraidVMManager:
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
|
||||
is_persistent = VM_NAME in persistent_check.stdout
|
||||
|
||||
|
||||
if is_persistent:
|
||||
# Undefine persistent VM with NVRAM flag
|
||||
logger.info(
|
||||
@@ -299,7 +306,9 @@ class UnraidVMManager:
|
||||
shell=True,
|
||||
check=True,
|
||||
)
|
||||
logger.info(f"Persistent VM {VM_NAME} undefined for reconfiguration")
|
||||
logger.info(
|
||||
f"Persistent VM {VM_NAME} undefined for reconfiguration"
|
||||
)
|
||||
else:
|
||||
# Handle transient VM - just destroy it
|
||||
logger.info(
|
||||
@@ -318,7 +327,7 @@ class UnraidVMManager:
|
||||
vm_xml = self.create_vm_xml(existing_uuid)
|
||||
xml_file = f"/tmp/{VM_NAME}.xml"
|
||||
|
||||
with open(xml_file, "w", encoding='utf-8') as f:
|
||||
with open(xml_file, "w", encoding="utf-8") as f:
|
||||
f.write(vm_xml)
|
||||
|
||||
# Copy XML to Unraid and define/redefine VM
|
||||
@@ -359,7 +368,7 @@ class UnraidVMManager:
|
||||
try:
|
||||
# Check available Ubuntu ISOs and select the correct one
|
||||
iso_mount_point = "/tmp/ubuntu-iso"
|
||||
|
||||
|
||||
logger.info("Checking for available Ubuntu ISOs...")
|
||||
# List available Ubuntu ISOs with detailed information
|
||||
result = subprocess.run(
|
||||
@@ -368,9 +377,9 @@ class UnraidVMManager:
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
|
||||
logger.info(f"Available ISOs: {result.stdout}")
|
||||
|
||||
|
||||
# First, try to find ANY existing Ubuntu ISOs dynamically
|
||||
# This will find all Ubuntu ISOs regardless of naming convention
|
||||
find_all_result = subprocess.run(
|
||||
@@ -379,82 +388,107 @@ class UnraidVMManager:
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
|
||||
ubuntu_iso_path = None
|
||||
available_isos = []
|
||||
|
||||
|
||||
if find_all_result.returncode == 0 and find_all_result.stdout.strip():
|
||||
available_isos = find_all_result.stdout.strip().split('\n')
|
||||
logger.info(f"Found {len(available_isos)} Ubuntu ISOs: {available_isos}")
|
||||
|
||||
available_isos = find_all_result.stdout.strip().split("\n")
|
||||
logger.info(
|
||||
f"Found {
|
||||
len(available_isos)} Ubuntu ISOs: {available_isos}"
|
||||
)
|
||||
|
||||
# Prioritize ISOs by version and type (prefer LTS, prefer newer versions)
|
||||
# Sort by preference: 24.04 LTS > 22.04 LTS > 23.x > 20.04 > others
|
||||
# Within each version, prefer the latest point release
|
||||
priority_versions = [
|
||||
'24.04', # Ubuntu 24.04 LTS (highest priority)
|
||||
'22.04', # Ubuntu 22.04 LTS
|
||||
'23.10', # Ubuntu 23.10
|
||||
'23.04', # Ubuntu 23.04
|
||||
'20.04', # Ubuntu 20.04 LTS
|
||||
"24.04", # Ubuntu 24.04 LTS (highest priority)
|
||||
"22.04", # Ubuntu 22.04 LTS
|
||||
"23.10", # Ubuntu 23.10
|
||||
"23.04", # Ubuntu 23.04
|
||||
"20.04", # Ubuntu 20.04 LTS
|
||||
]
|
||||
|
||||
# Find the best ISO based on priority, preferring latest point releases
|
||||
|
||||
# Find the best ISO based on priority, preferring latest point
|
||||
# releases
|
||||
for version in priority_versions:
|
||||
# Find all ISOs for this version
|
||||
version_isos = []
|
||||
for iso in available_isos:
|
||||
if version in iso and ('server' in iso.lower() or 'live' in iso.lower()):
|
||||
if version in iso and (
|
||||
"server" in iso.lower() or "live" in iso.lower()
|
||||
):
|
||||
version_isos.append(iso)
|
||||
|
||||
|
||||
if version_isos:
|
||||
# Sort by version number (reverse to get latest first)
|
||||
# This will put 24.04.3 before 24.04.2 before 24.04.1 before 24.04
|
||||
# This will put 24.04.3 before 24.04.2 before 24.04.1
|
||||
# before 24.04
|
||||
version_isos.sort(reverse=True)
|
||||
ubuntu_iso_path = version_isos[0]
|
||||
logger.info(f"Selected latest Ubuntu {version} ISO: {ubuntu_iso_path}")
|
||||
logger.info(
|
||||
f"Selected latest Ubuntu {version} ISO: {ubuntu_iso_path}"
|
||||
)
|
||||
break
|
||||
|
||||
|
||||
# If no priority match, use the first server/live ISO found
|
||||
if not ubuntu_iso_path:
|
||||
for iso in available_isos:
|
||||
if 'server' in iso.lower() or 'live' in iso.lower():
|
||||
if "server" in iso.lower() or "live" in iso.lower():
|
||||
ubuntu_iso_path = iso
|
||||
logger.info(f"Selected Ubuntu server/live ISO: {ubuntu_iso_path}")
|
||||
logger.info(
|
||||
f"Selected Ubuntu server/live ISO: {ubuntu_iso_path}"
|
||||
)
|
||||
break
|
||||
|
||||
|
||||
# If still no match, use the first Ubuntu ISO found (any type)
|
||||
if not ubuntu_iso_path and available_isos:
|
||||
ubuntu_iso_path = available_isos[0]
|
||||
logger.info(f"Selected first available Ubuntu ISO: {ubuntu_iso_path}")
|
||||
logger.warning(f"Using non-server Ubuntu ISO - this may not support autoinstall")
|
||||
|
||||
logger.info(
|
||||
f"Selected first available Ubuntu ISO: {ubuntu_iso_path}"
|
||||
)
|
||||
logger.warning(
|
||||
f"Using non-server Ubuntu ISO - this may not support autoinstall"
|
||||
)
|
||||
|
||||
if not ubuntu_iso_path:
|
||||
logger.error("No Ubuntu server ISO found in /mnt/user/isos/")
|
||||
logger.error("")
|
||||
logger.error("🔥 MISSING UBUNTU ISO - ACTION REQUIRED 🔥")
|
||||
logger.error("")
|
||||
logger.error("Please download Ubuntu LTS Server ISO to your Unraid server:")
|
||||
logger.error(
|
||||
"Please download Ubuntu LTS Server ISO to your Unraid server:"
|
||||
)
|
||||
logger.error("")
|
||||
logger.error("📦 RECOMMENDED: Ubuntu 24.04 LTS (Noble Numbat) - Latest LTS:")
|
||||
logger.error(
|
||||
"📦 RECOMMENDED: Ubuntu 24.04 LTS (Noble Numbat) - Latest LTS:"
|
||||
)
|
||||
logger.error(" 1. Go to: https://releases.ubuntu.com/24.04/")
|
||||
logger.error(" 2. Download: ubuntu-24.04-live-server-amd64.iso")
|
||||
logger.error(" 3. Upload to: /mnt/user/isos/ on your Unraid server")
|
||||
logger.error("")
|
||||
logger.error("📦 ALTERNATIVE: Ubuntu 22.04 LTS (Jammy Jellyfish) - Stable:")
|
||||
logger.error(
|
||||
"📦 ALTERNATIVE: Ubuntu 22.04 LTS (Jammy Jellyfish) - Stable:"
|
||||
)
|
||||
logger.error(" 1. Go to: https://releases.ubuntu.com/22.04/")
|
||||
logger.error(" 2. Download: ubuntu-22.04-live-server-amd64.iso")
|
||||
logger.error(" 3. Upload to: /mnt/user/isos/ on your Unraid server")
|
||||
logger.error("")
|
||||
logger.error("💡 Quick download via wget on Unraid server:")
|
||||
logger.error(" # For Ubuntu 24.04 LTS (recommended):")
|
||||
logger.error(" wget -P /mnt/user/isos/ https://releases.ubuntu.com/24.04/ubuntu-24.04-live-server-amd64.iso")
|
||||
logger.error(
|
||||
" wget -P /mnt/user/isos/ https://releases.ubuntu.com/24.04/ubuntu-24.04-live-server-amd64.iso"
|
||||
)
|
||||
logger.error(" # For Ubuntu 22.04 LTS (stable):")
|
||||
logger.error(" wget -P /mnt/user/isos/ https://releases.ubuntu.com/22.04/ubuntu-22.04-live-server-amd64.iso")
|
||||
logger.error(
|
||||
" wget -P /mnt/user/isos/ https://releases.ubuntu.com/22.04/ubuntu-22.04-live-server-amd64.iso"
|
||||
)
|
||||
logger.error("")
|
||||
logger.error("Then re-run this script.")
|
||||
logger.error("")
|
||||
return False
|
||||
|
||||
|
||||
# Verify ISO file integrity
|
||||
logger.info(f"Verifying ISO file: {ubuntu_iso_path}")
|
||||
stat_result = subprocess.run(
|
||||
@@ -466,23 +500,23 @@ class UnraidVMManager:
|
||||
if stat_result.returncode != 0:
|
||||
logger.error(f"Cannot access ISO file: {ubuntu_iso_path}")
|
||||
return False
|
||||
|
||||
|
||||
logger.info(f"ISO file stats: {stat_result.stdout.strip()}")
|
||||
|
||||
|
||||
# Clean up any previous mount points
|
||||
subprocess.run(
|
||||
f"ssh {UNRAID_USER}@{UNRAID_HOST} 'umount {iso_mount_point} 2>/dev/null || true'",
|
||||
shell=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
|
||||
# Remove mount point if it exists
|
||||
subprocess.run(
|
||||
f"ssh {UNRAID_USER}@{UNRAID_HOST} 'rmdir {iso_mount_point} 2>/dev/null || true'",
|
||||
shell=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
|
||||
# Create mount point
|
||||
logger.info(f"Creating mount point: {iso_mount_point}")
|
||||
subprocess.run(
|
||||
@@ -490,7 +524,7 @@ class UnraidVMManager:
|
||||
shell=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
# Check if loop module is loaded
|
||||
logger.info("Checking loop module availability...")
|
||||
loop_check = subprocess.run(
|
||||
@@ -500,7 +534,7 @@ class UnraidVMManager:
|
||||
text=True,
|
||||
)
|
||||
logger.info(f"Loop module check: {loop_check.stdout}")
|
||||
|
||||
|
||||
# Mount ISO with more verbose output
|
||||
logger.info(f"Mounting ISO: {ubuntu_iso_path} to {iso_mount_point}")
|
||||
mount_result = subprocess.run(
|
||||
@@ -509,15 +543,18 @@ class UnraidVMManager:
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
|
||||
if mount_result.returncode != 0:
|
||||
logger.error(f"Failed to mount ISO. Return code: {mount_result.returncode}")
|
||||
logger.error(
|
||||
f"Failed to mount ISO. Return code: {
|
||||
mount_result.returncode}"
|
||||
)
|
||||
logger.error(f"STDOUT: {mount_result.stdout}")
|
||||
logger.error(f"STDERR: {mount_result.stderr}")
|
||||
return False
|
||||
|
||||
|
||||
logger.info("ISO mounted successfully")
|
||||
|
||||
|
||||
# Create directory for extracted kernel files
|
||||
kernel_dir = f"/mnt/user/domains/{VM_NAME}/kernel"
|
||||
subprocess.run(
|
||||
@@ -525,37 +562,37 @@ class UnraidVMManager:
|
||||
shell=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
# Extract kernel and initrd
|
||||
subprocess.run(
|
||||
f"ssh {UNRAID_USER}@{UNRAID_HOST} 'cp {iso_mount_point}/casper/vmlinuz {kernel_dir}/'",
|
||||
shell=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
subprocess.run(
|
||||
f"ssh {UNRAID_USER}@{UNRAID_HOST} 'cp {iso_mount_point}/casper/initrd {kernel_dir}/'",
|
||||
shell=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
# Unmount ISO
|
||||
subprocess.run(
|
||||
f"ssh {UNRAID_USER}@{UNRAID_HOST} 'umount {iso_mount_point}'",
|
||||
shell=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
# Remove mount point
|
||||
subprocess.run(
|
||||
f"ssh {UNRAID_USER}@{UNRAID_HOST} 'rmdir {iso_mount_point}'",
|
||||
shell=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
logger.info("Ubuntu kernel and initrd extracted successfully")
|
||||
return True
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to extract Ubuntu kernel: {e}")
|
||||
# Clean up on failure
|
||||
@@ -570,19 +607,23 @@ class UnraidVMManager:
|
||||
"""Fallback to UEFI boot when kernel extraction fails."""
|
||||
try:
|
||||
logger.info("Setting up fallback UEFI boot configuration...")
|
||||
|
||||
|
||||
# First, detect available Ubuntu ISO for the fallback template
|
||||
ubuntu_iso_path = self._detect_ubuntu_iso()
|
||||
if not ubuntu_iso_path:
|
||||
logger.error("Cannot create UEFI fallback without Ubuntu ISO")
|
||||
return False
|
||||
|
||||
|
||||
# Create a fallback VM XML template path
|
||||
fallback_template_path = Path(__file__).parent / "thrillwiki-vm-uefi-fallback-template.xml"
|
||||
|
||||
fallback_template_path = (
|
||||
Path(__file__).parent / "thrillwiki-vm-uefi-fallback-template.xml"
|
||||
)
|
||||
|
||||
# Create fallback UEFI template with detected Ubuntu ISO
|
||||
logger.info(f"Creating fallback UEFI template with detected ISO: {ubuntu_iso_path}")
|
||||
uefi_template = f'''<?xml version='1.0' encoding='UTF-8'?>
|
||||
logger.info(
|
||||
f"Creating fallback UEFI template with detected ISO: {ubuntu_iso_path}"
|
||||
)
|
||||
uefi_template = f"""<?xml version='1.0' encoding='UTF-8'?>
|
||||
<domain type='kvm'>
|
||||
<name>{{VM_NAME}}</name>
|
||||
<uuid>{{VM_UUID}}</uuid>
|
||||
@@ -605,7 +646,7 @@ class UnraidVMManager:
|
||||
<vmport state='off'/>
|
||||
</features>
|
||||
<cpu mode='host-passthrough' check='none' migratable='on'>
|
||||
<topology sockets='1' dies='1' clusters='1' cores='{CPU_CORES}' threads='{CPU_THREADS}'/>
|
||||
<topology sockets='1' dies='1' clusters='1' cores='{{CPU_CORES}}' threads='{{CPU_THREADS}}'/>
|
||||
<cache mode='passthrough'/>
|
||||
<feature policy='require' name='topoext'/>
|
||||
</cpu>
|
||||
@@ -682,7 +723,7 @@ class UnraidVMManager:
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
|
||||
</controller>
|
||||
<interface type='bridge'>
|
||||
<mac address='52:54:00:{MAC_SUFFIX}'/>
|
||||
<mac address='52:54:00:{{MAC_SUFFIX}}'/>
|
||||
<source bridge='br0.20'/>
|
||||
<model type='virtio'/>
|
||||
<address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
|
||||
@@ -717,28 +758,32 @@ class UnraidVMManager:
|
||||
<address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
|
||||
</memballoon>
|
||||
</devices>
|
||||
</domain>'''
|
||||
|
||||
with open(fallback_template_path, 'w', encoding='utf-8') as f:
|
||||
</domain>"""
|
||||
|
||||
with open(fallback_template_path, "w", encoding="utf-8") as f:
|
||||
f.write(uefi_template)
|
||||
|
||||
|
||||
logger.info(f"Created fallback UEFI template: {fallback_template_path}")
|
||||
|
||||
|
||||
# Update the template path to use the fallback
|
||||
original_template = Path(__file__).parent / "thrillwiki-vm-template.xml"
|
||||
fallback_template = Path(__file__).parent / "thrillwiki-vm-uefi-fallback-template.xml"
|
||||
|
||||
fallback_template = (
|
||||
Path(__file__).parent / "thrillwiki-vm-uefi-fallback-template.xml"
|
||||
)
|
||||
|
||||
# Backup original template and replace with fallback
|
||||
if original_template.exists():
|
||||
backup_path = Path(__file__).parent / "thrillwiki-vm-template.xml.backup"
|
||||
backup_path = (
|
||||
Path(__file__).parent / "thrillwiki-vm-template.xml.backup"
|
||||
)
|
||||
original_template.rename(backup_path)
|
||||
logger.info(f"Backed up original template to {backup_path}")
|
||||
|
||||
|
||||
fallback_template.rename(original_template)
|
||||
logger.info("Switched to UEFI fallback template")
|
||||
|
||||
|
||||
return True
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to set up UEFI fallback: {e}")
|
||||
return False
|
||||
@@ -841,7 +886,7 @@ class UnraidVMManager:
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10 # 10 second timeout for the command itself
|
||||
timeout=10, # 10 second timeout for the command itself
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
@@ -853,25 +898,33 @@ class UnraidVMManager:
|
||||
logger.info(f"VM {VM_NAME} stopped gracefully")
|
||||
return True
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
# If still running after 30 seconds, force destroy
|
||||
logger.warning(f"VM {VM_NAME} didn't shutdown gracefully, forcing destroy...")
|
||||
logger.warning(
|
||||
f"VM {VM_NAME} didn't shutdown gracefully, forcing destroy..."
|
||||
)
|
||||
destroy_result = subprocess.run(
|
||||
f"ssh {UNRAID_USER}@{UNRAID_HOST} 'virsh destroy {VM_NAME}'",
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
timeout=10,
|
||||
)
|
||||
|
||||
|
||||
if destroy_result.returncode == 0:
|
||||
logger.info(f"VM {VM_NAME} forcefully destroyed")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"Failed to destroy VM: {destroy_result.stderr}")
|
||||
logger.error(
|
||||
f"Failed to destroy VM: {
|
||||
destroy_result.stderr}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
logger.error(f"Failed to initiate VM shutdown: {result.stderr}")
|
||||
logger.error(
|
||||
f"Failed to initiate VM shutdown: {
|
||||
result.stderr}"
|
||||
)
|
||||
return False
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
@@ -898,7 +951,9 @@ class UnraidVMManager:
|
||||
lines = result.stdout.strip().split("\n")
|
||||
for line in lines:
|
||||
if "ipv4" in line:
|
||||
# Extract IP from line like: vnet0 52:54:00:xx:xx:xx ipv4 192.168.1.100/24
|
||||
# Extract IP from line like: vnet0
|
||||
# 52:54:00:xx:xx:xx ipv4
|
||||
# 192.168.1.100/24
|
||||
parts = line.split()
|
||||
if len(parts) >= 4:
|
||||
ip_with_mask = parts[3]
|
||||
@@ -907,7 +962,8 @@ class UnraidVMManager:
|
||||
return ip
|
||||
|
||||
logger.info(
|
||||
f"Waiting for VM IP... (attempt {attempt + 1}/{max_attempts}) - Ubuntu autoinstall in progress"
|
||||
f"Waiting for VM IP... (attempt {
|
||||
attempt + 1}/{max_attempts}) - Ubuntu autoinstall in progress"
|
||||
)
|
||||
time.sleep(10)
|
||||
|
||||
@@ -928,27 +984,31 @@ class UnraidVMManager:
|
||||
ssh_public_key = os.getenv("SSH_PUBLIC_KEY", "")
|
||||
|
||||
# Read autoinstall user-data template
|
||||
autoinstall_template_path = Path(__file__).parent / "autoinstall-user-data.yaml"
|
||||
autoinstall_template_path = (
|
||||
Path(__file__).parent / "autoinstall-user-data.yaml"
|
||||
)
|
||||
if not autoinstall_template_path.exists():
|
||||
logger.error(f"Autoinstall template not found at {autoinstall_template_path}")
|
||||
logger.error(
|
||||
f"Autoinstall template not found at {autoinstall_template_path}"
|
||||
)
|
||||
return False
|
||||
|
||||
with open(autoinstall_template_path, 'r', encoding='utf-8') as f:
|
||||
|
||||
with open(autoinstall_template_path, "r", encoding="utf-8") as f:
|
||||
autoinstall_template = f.read()
|
||||
|
||||
# Replace placeholders in autoinstall template
|
||||
user_data = autoinstall_template.format(
|
||||
SSH_PUBLIC_KEY=ssh_public_key if ssh_public_key else "# No SSH key provided",
|
||||
GITHUB_REPO=repo_url if repo_url else ""
|
||||
SSH_PUBLIC_KEY=(
|
||||
ssh_public_key if ssh_public_key else "# No SSH key provided"
|
||||
),
|
||||
GITHUB_REPO=repo_url if repo_url else "",
|
||||
)
|
||||
|
||||
# Update network configuration in autoinstall based on VM_IP setting
|
||||
# Update network configuration in autoinstall based on VM_IP
|
||||
# setting
|
||||
if vm_ip.lower() == "dhcp":
|
||||
# Replace the static network config with DHCP
|
||||
user_data = user_data.replace(
|
||||
"dhcp4: true",
|
||||
"dhcp4: true"
|
||||
)
|
||||
user_data = user_data.replace("dhcp4: true", "dhcp4: true")
|
||||
else:
|
||||
# Update with static IP configuration
|
||||
gateway = os.getenv("VM_GATEWAY", "192.168.20.1")
|
||||
@@ -960,10 +1020,7 @@ class UnraidVMManager:
|
||||
addresses:
|
||||
- 8.8.8.8
|
||||
- 8.8.4.4"""
|
||||
user_data = user_data.replace(
|
||||
"dhcp4: true",
|
||||
network_config
|
||||
)
|
||||
user_data = user_data.replace("dhcp4: true", network_config)
|
||||
|
||||
# Force clean temp directory for cloud-init files
|
||||
cloud_init_dir = "/tmp/cloud-init"
|
||||
@@ -975,19 +1032,21 @@ class UnraidVMManager:
|
||||
server_dir = f"{cloud_init_dir}/server"
|
||||
os.makedirs(server_dir, exist_ok=True)
|
||||
|
||||
# Create user-data file in server/ directory with autoinstall configuration
|
||||
with open(f"{server_dir}/user-data", "w", encoding='utf-8') as f:
|
||||
# Create user-data file in server/ directory with autoinstall
|
||||
# configuration
|
||||
with open(f"{server_dir}/user-data", "w", encoding="utf-8") as f:
|
||||
f.write(user_data)
|
||||
|
||||
# Create empty meta-data file in server/ directory as per Ubuntu guide
|
||||
with open(f"{server_dir}/meta-data", "w", encoding='utf-8') as f:
|
||||
# Create empty meta-data file in server/ directory as per Ubuntu
|
||||
# guide
|
||||
with open(f"{server_dir}/meta-data", "w", encoding="utf-8") as f:
|
||||
f.write("")
|
||||
|
||||
# Create root level meta-data for cloud-init
|
||||
meta_data = f"""instance-id: thrillwiki-vm-{int(time.time())}
|
||||
local-hostname: thrillwiki-vm
|
||||
"""
|
||||
with open(f"{cloud_init_dir}/meta-data", "w", encoding='utf-8') as f:
|
||||
with open(f"{cloud_init_dir}/meta-data", "w", encoding="utf-8") as f:
|
||||
f.write(meta_data)
|
||||
|
||||
# Create user-data at root level (minimal cloud-config)
|
||||
@@ -995,7 +1054,7 @@ local-hostname: thrillwiki-vm
|
||||
# Root level cloud-config for compatibility
|
||||
# Main autoinstall config is in /server/user-data
|
||||
"""
|
||||
with open(f"{cloud_init_dir}/user-data", "w", encoding='utf-8') as f:
|
||||
with open(f"{cloud_init_dir}/user-data", "w", encoding="utf-8") as f:
|
||||
f.write(root_user_data)
|
||||
|
||||
# Force remove old ISO first
|
||||
@@ -1078,15 +1137,19 @@ local-hostname: thrillwiki-vm
|
||||
shell=True,
|
||||
check=False, # Don't fail if file doesn't exist
|
||||
)
|
||||
logger.info(f"Removed old cloud-init ISO from Unraid: /mnt/user/isos/{VM_NAME}-cloud-init.iso")
|
||||
|
||||
logger.info(
|
||||
f"Removed old cloud-init ISO from Unraid: /mnt/user/isos/{VM_NAME}-cloud-init.iso"
|
||||
)
|
||||
|
||||
# Copy new ISO to Unraid
|
||||
subprocess.run(
|
||||
f"scp {iso_path} {UNRAID_USER}@{UNRAID_HOST}:/mnt/user/isos/",
|
||||
shell=True,
|
||||
check=True,
|
||||
)
|
||||
logger.info(f"Copied new cloud-init ISO to Unraid: /mnt/user/isos/{VM_NAME}-cloud-init.iso")
|
||||
logger.info(
|
||||
f"Copied new cloud-init ISO to Unraid: /mnt/user/isos/{VM_NAME}-cloud-init.iso"
|
||||
)
|
||||
|
||||
logger.info("Cloud-init ISO created successfully")
|
||||
return True
|
||||
@@ -1154,7 +1217,7 @@ local-hostname: thrillwiki-vm
|
||||
shell=True,
|
||||
check=False, # Don't fail if file doesn't exist
|
||||
)
|
||||
|
||||
|
||||
# Remove extracted kernel files
|
||||
subprocess.run(
|
||||
f"ssh {UNRAID_USER}@{UNRAID_HOST} 'rm -rf /mnt/user/domains/{VM_NAME}/kernel'",
|
||||
|
||||
Reference in New Issue
Block a user