Enhance AutoInstaller GUI with detailed copy progress and network config improvements

- Add detailed copy system step with real-time rsync output display
- Show current file being copied, transfer statistics, and progress details
- Add small text area for copy progress with monospace font and auto-scroll
- Move network configuration file creation to occur after disk mounting
- This ensures /target/etc/network/interfaces is created on the mounted filesystem
- Update installation progress percentages to accommodate new network config step
- Add copy_progress signal and update_copy_progress method for GUI updates
- Enhanced rsync command with --progress and --stats for detailed output
- Improved error handling for rsync process with proper cleanup

This provides users with detailed feedback during the system copy process
and ensures network configuration files are properly created on target system.
parent cbc92a3b
...@@ -304,6 +304,7 @@ class InstallerWorker(QThread): ...@@ -304,6 +304,7 @@ class InstallerWorker(QThread):
step_completed = pyqtSignal(str) step_completed = pyqtSignal(str)
error_occurred = pyqtSignal(str) error_occurred = pyqtSignal(str)
finished = pyqtSignal(bool) finished = pyqtSignal(bool)
copy_progress = pyqtSignal(str)
def __init__(self, config): def __init__(self, config):
super().__init__() super().__init__()
...@@ -333,12 +334,12 @@ class InstallerWorker(QThread): ...@@ -333,12 +334,12 @@ class InstallerWorker(QThread):
self.progress_updated.emit(30) self.progress_updated.emit(30)
# Step 3: Network Configuration # Step 3: Network Configuration (moved before mounting)
if self.config.get('configure_network', False): if self.config.get('configure_network', False):
self.status_updated.emit("Configuring network...") self.status_updated.emit("Preparing network configuration...")
network_config = self.apply_network_config() # Just store the config for now, will apply after mounting
self.config['network_config'] = network_config self.log("Network configuration will be applied after mounting")
self.step_completed.emit("Network configured") self.step_completed.emit("Network configuration prepared")
else: else:
self.log("Skipping network configuration") self.log("Skipping network configuration")
...@@ -367,28 +368,39 @@ class InstallerWorker(QThread): ...@@ -367,28 +368,39 @@ class InstallerWorker(QThread):
self.mount_target(target_disk, target_mount) self.mount_target(target_disk, target_mount)
self.step_completed.emit("Target mounted") self.step_completed.emit("Target mounted")
self.progress_updated.emit(70) self.progress_updated.emit(65)
# Step 8: Apply Network Configuration (now that target is mounted)
if self.config.get('configure_network', False):
self.status_updated.emit("Applying network configuration to target...")
network_config = self.apply_network_config()
self.config['network_config'] = network_config
self.step_completed.emit("Network configuration applied")
else:
self.log("Skipping network configuration")
self.progress_updated.emit(75)
# Step 8: Copy Live System # Step 10: Copy Live System
self.status_updated.emit("Copying live system to disk...") self.status_updated.emit("Copying live system to disk...")
self.copy_live_system(target_mount) self.copy_live_system(target_mount)
self.step_completed.emit("System copied") self.step_completed.emit("System copied")
self.progress_updated.emit(80) self.progress_updated.emit(85)
# Step 9: Configure Target System # Step 11: Configure Target System
self.status_updated.emit("Configuring target system...") self.status_updated.emit("Configuring target system...")
self.configure_target_system(target_mount) self.configure_target_system(target_mount)
self.step_completed.emit("System configured") self.step_completed.emit("System configured")
self.progress_updated.emit(90) self.progress_updated.emit(95)
# Step 10: Install Bootloader # Step 12: Install Bootloader
self.status_updated.emit("Installing bootloader...") self.status_updated.emit("Installing bootloader...")
self.install_bootloader(target_mount, target_disk) self.install_bootloader(target_mount, target_disk)
self.step_completed.emit("Bootloader installed") self.step_completed.emit("Bootloader installed")
self.progress_updated.emit(95) self.progress_updated.emit(98)
# Post-Install Setup # Post-Install Setup
self.status_updated.emit("Running post-installation setup...") self.status_updated.emit("Running post-installation setup...")
...@@ -618,10 +630,50 @@ class InstallerWorker(QThread): ...@@ -618,10 +630,50 @@ class InstallerWorker(QThread):
self.log("Target mounted") self.log("Target mounted")
def copy_live_system(self, target_mount): def copy_live_system(self, target_mount):
# Use rsync via subprocess """Copy live system with detailed progress output"""
cmd = ['rsync', '-av', '--exclude=/proc', '--exclude=/sys', '/', target_mount + '/'] self.log("Starting system copy with detailed progress...")
subprocess.run(cmd, check=True)
self.log("System copied") # Use rsync with progress and verbose output
cmd = [
'rsync', '-av', '--progress', '--stats',
'--exclude=/proc', '--exclude=/sys', '--exclude=/dev',
'--exclude=/tmp', '--exclude=/run', '--exclude=/mnt',
'--exclude=/media', '--exclude=/lost+found',
'--exclude=/lib/live', '--exclude=/cdrom',
'--exclude=/var/cache/apt/archives/*.deb',
'--exclude=/var/log/*', '--exclude=/var/log/auto-installer.log',
'/', target_mount + '/'
]
try:
# Run rsync and capture output
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
universal_newlines=True
)
# Read output line by line and send to GUI
while True:
output = process.stdout.readline()
if output == '' and process.poll() is not None:
break
if output:
# Send progress updates to GUI
self.copy_progress.emit(output.strip())
# Check return code
if process.returncode != 0:
raise subprocess.CalledProcessError(process.returncode, cmd)
self.log("System copy completed successfully")
except subprocess.CalledProcessError as e:
self.log(f"System copy failed with return code {e.returncode}")
raise
def configure_target_system(self, target_mount): def configure_target_system(self, target_mount):
# Bind mounts and chroot commands # Bind mounts and chroot commands
...@@ -727,9 +779,19 @@ class AutoInstallerGUI(QMainWindow): ...@@ -727,9 +779,19 @@ class AutoInstallerGUI(QMainWindow):
# Log Output # Log Output
self.log_text = QTextEdit() self.log_text = QTextEdit()
self.log_text.setReadOnly(True) self.log_text.setReadOnly(True)
self.log_text.setMaximumHeight(200) self.log_text.setMaximumHeight(150)
layout.addWidget(self.log_text) layout.addWidget(self.log_text)
# Copy Progress Output (small text area for rsync details)
copy_group = QGroupBox("Copy Progress Details")
copy_layout = QVBoxLayout(copy_group)
self.copy_progress_text = QTextEdit()
self.copy_progress_text.setReadOnly(True)
self.copy_progress_text.setMaximumHeight(100)
self.copy_progress_text.setFont(QFont("Monospace", 8))
copy_layout.addWidget(self.copy_progress_text)
layout.addWidget(copy_group)
# Exit Button (for fullscreen) # Exit Button (for fullscreen)
self.exit_button = QPushButton("Exit (Esc)") self.exit_button = QPushButton("Exit (Esc)")
self.exit_button.clicked.connect(self.close) self.exit_button.clicked.connect(self.close)
...@@ -795,6 +857,7 @@ class AutoInstallerGUI(QMainWindow): ...@@ -795,6 +857,7 @@ class AutoInstallerGUI(QMainWindow):
self.worker.step_completed.connect(self.log_step_completed) self.worker.step_completed.connect(self.log_step_completed)
self.worker.error_occurred.connect(self.handle_error) self.worker.error_occurred.connect(self.handle_error)
self.worker.finished.connect(self.installation_finished) self.worker.finished.connect(self.installation_finished)
self.worker.copy_progress.connect(self.update_copy_progress)
self.worker.start() self.worker.start()
def update_status(self, message): def update_status(self, message):
...@@ -803,6 +866,26 @@ class AutoInstallerGUI(QMainWindow): ...@@ -803,6 +866,26 @@ class AutoInstallerGUI(QMainWindow):
def log_step_completed(self, step): def log_step_completed(self, step):
self.log_text.append(f"[COMPLETED] {step}") self.log_text.append(f"[COMPLETED] {step}")
def update_copy_progress(self, message):
"""Update the copy progress text area with rsync output"""
# Keep only the last 50 lines to avoid memory issues
current_text = self.copy_progress_text.toPlainText()
lines = current_text.split('\n')
if len(lines) > 45: # Keep some buffer
lines = lines[-45:]
current_text = '\n'.join(lines)
# Append new message
if current_text:
self.copy_progress_text.setPlainText(current_text + '\n' + message)
else:
self.copy_progress_text.setPlainText(message)
# Auto-scroll to bottom
cursor = self.copy_progress_text.textCursor()
cursor.movePosition(cursor.MoveOperation.End)
self.copy_progress_text.setTextCursor(cursor)
def handle_error(self, error): def handle_error(self, error):
QMessageBox.critical(self, "Installation Error", error) QMessageBox.critical(self, "Installation Error", error)
self.confirm_button.setEnabled(True) self.confirm_button.setEnabled(True)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment