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:
pacnpal
2025-08-20 19:51:59 -04:00
parent 69c07d1381
commit 66ed4347a9
230 changed files with 15094 additions and 11578 deletions

View File

@@ -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'",