Fix disk detection and network configuration in AutoInstaller GUI

- Implement same disk detection logic as auto-installer.sh for detect_usb_device and detect_target_disk
- Use subprocess calls to lsblk, df, and mount point checks for accurate USB boot device detection
- Filter target disks by size (>4GB) and exclude USB boot device
- Add comprehensive network interface detection dialog
- Support both Ethernet and Wireless interfaces with full WiFi configuration
- Implement WiFi scanning, SSID selection, security type selection (WPA/WEP/Open)
- Add static IP configuration with IP address, gateway, and DNS settings
- Generate proper /etc/network/interfaces file for installed system
- Handle WiFi configuration with wireless-essid, wpa-passphrase, and wireless-key
- Apply network configuration to target system during installation

This resolves the issues where GUI failed to detect correct installation disk
and provides full network configuration capabilities including WiFi support.
parent a1e1ce4a
...@@ -117,6 +117,168 @@ class TimezoneDialog(QDialog): ...@@ -117,6 +117,168 @@ class TimezoneDialog(QDialog):
else: else:
QMessageBox.warning(self, "Selection Required", "Please select a timezone") QMessageBox.warning(self, "Selection Required", "Please select a timezone")
class NetworkInterfaceDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Select Network Interface")
self.setModal(True)
self.setMinimumSize(600, 400)
self.selected_interface = None
self.interface_type = None
self.wifi_config = None
layout = QVBoxLayout(self)
# Interface selection
interface_group = QGroupBox("Available Network Interfaces")
interface_layout = QVBoxLayout(interface_group)
self.interface_list = QListWidget()
self.interface_list.itemClicked.connect(self.interface_selected)
interface_layout.addWidget(self.interface_list)
layout.addWidget(interface_group)
# WiFi configuration (shown only for wireless)
self.wifi_group = QGroupBox("WiFi Configuration")
wifi_layout = QVBoxLayout(self.wifi_group)
self.ssid_list = QListWidget()
self.ssid_list.itemClicked.connect(self.ssid_selected)
wifi_layout.addWidget(QLabel("Available Networks:"))
wifi_layout.addWidget(self.ssid_list)
self.security_combo = QComboBox()
self.security_combo.addItems(["WPA/WPA2 (most common)", "WEP (legacy)", "Open (no password)"])
wifi_layout.addWidget(QLabel("Security Type:"))
wifi_layout.addWidget(self.security_combo)
self.password_edit = QLineEdit()
self.password_edit.setEchoMode(QLineEdit.EchoMode.Password)
self.password_edit.setPlaceholderText("Enter WiFi password")
wifi_layout.addWidget(QLabel("Password:"))
wifi_layout.addWidget(self.password_edit)
layout.addWidget(self.wifi_group)
self.wifi_group.setVisible(False)
# Buttons
button_layout = QHBoxLayout()
self.ok_button = QPushButton("OK")
self.ok_button.clicked.connect(self.accept_selection)
self.cancel_button = QPushButton("Cancel")
self.cancel_button.clicked.connect(self.reject)
button_layout.addWidget(self.ok_button)
button_layout.addWidget(self.cancel_button)
layout.addLayout(button_layout)
self.load_interfaces()
def load_interfaces(self):
"""Load available network interfaces"""
try:
# Get ethernet interfaces
result = subprocess.run(['ip', 'link', 'show'], capture_output=True, text=True, check=True)
ethernet_interfaces = []
for line in result.stdout.split('\n'):
if 'e(n|th)' in line and 'state' in line:
parts = line.split(':')
if len(parts) >= 2:
iface = parts[1].strip()
ethernet_interfaces.append(iface)
# Get wireless interfaces
wireless_interfaces = []
try:
result = subprocess.run(['iw', 'dev'], capture_output=True, text=True, check=True)
for line in result.stdout.split('\n'):
if 'Interface' in line:
parts = line.split()
if len(parts) >= 2:
wireless_interfaces.append(parts[1])
except subprocess.CalledProcessError:
pass # iw not available
# Add to list
for iface in ethernet_interfaces:
item = QListWidgetItem(f"{iface} (Ethernet)")
item.setData(Qt.ItemDataRole.UserRole, ('ethernet', iface))
self.interface_list.addItem(item)
for iface in wireless_interfaces:
item = QListWidgetItem(f"{iface} (Wireless)")
item.setData(Qt.ItemDataRole.UserRole, ('wireless', iface))
self.interface_list.addItem(item)
except subprocess.CalledProcessError:
QMessageBox.warning(self, "Error", "Could not detect network interfaces")
def interface_selected(self, item):
"""Handle interface selection"""
data = item.data(Qt.ItemDataRole.UserRole)
if data:
self.interface_type, self.selected_interface = data
if self.interface_type == 'wireless':
self.wifi_group.setVisible(True)
self.scan_wifi_networks()
else:
self.wifi_group.setVisible(False)
def scan_wifi_networks(self):
"""Scan for available WiFi networks"""
self.ssid_list.clear()
try:
# Enable interface for scanning
subprocess.run(['ip', 'link', 'set', self.selected_interface, 'up'], check=True)
# Wait a moment
import time
time.sleep(2)
# Scan
result = subprocess.run(['iw', 'dev', self.selected_interface, 'scan'],
capture_output=True, text=True, check=True)
networks = []
for line in result.stdout.split('\n'):
if 'SSID:' in line:
ssid = line.split('SSID:')[1].strip()
if ssid and ssid not in networks:
networks.append(ssid)
for ssid in networks:
item = QListWidgetItem(ssid)
self.ssid_list.addItem(item)
except subprocess.CalledProcessError:
QMessageBox.warning(self, "WiFi Scan Failed", "Could not scan for wireless networks")
def ssid_selected(self, item):
"""Handle SSID selection"""
self.selected_ssid = item.text()
def accept_selection(self):
"""Validate and accept selection"""
if not self.selected_interface:
QMessageBox.warning(self, "Selection Required", "Please select a network interface")
return
if self.interface_type == 'wireless':
if not hasattr(self, 'selected_ssid') or not self.selected_ssid:
QMessageBox.warning(self, "WiFi Required", "Please select a WiFi network")
return
# Get security type
security_map = {
"WPA/WPA2 (most common)": "WPA",
"WEP (legacy)": "WEP",
"Open (no password)": "NONE"
}
security = security_map.get(self.security_combo.currentText(), "WPA")
self.wifi_config = {
'ssid': self.selected_ssid,
'security': security,
'password': self.password_edit.text() if security != "NONE" else ""
}
self.accept()
class InstallerWorker(QThread): class InstallerWorker(QThread):
progress_updated = pyqtSignal(int) progress_updated = pyqtSignal(int)
log_message = pyqtSignal(str) log_message = pyqtSignal(str)
...@@ -231,18 +393,157 @@ class InstallerWorker(QThread): ...@@ -231,18 +393,157 @@ class InstallerWorker(QThread):
f.write(full_message + '\n') f.write(full_message + '\n')
def detect_usb_device(self): def detect_usb_device(self):
# Implement device detection logic from auto-installer.sh """Detect USB device we're booting from - same logic as auto-installer.sh"""
# For brevity, return a placeholder; in real implementation, use subprocess to run lsblk/df self.log("Detecting USB boot device...")
# Example: subprocess.run(['df', '/lib/live/mount/medium'], capture_output=True)
return "/dev/sda" # Placeholder # Check different possible mount points for live media
for mount_point in ['/lib/live/mount/medium', '/cdrom', '/run/live/medium']:
if os.path.isdir(mount_point):
try:
result = subprocess.run(['df', mount_point], capture_output=True, text=True, check=True)
# Get device from df output
lines = result.stdout.strip().split('\n')
if len(lines) >= 2:
device = lines[1].split()[0]
# Strip partition number (e.g., /dev/sda1 -> /dev/sda)
device = re.sub(r'\d+$', '', device)
if os.path.exists(device) and os.path.exists(f'/sys/block/{os.path.basename(device)}'):
self.log(f"Detected live media device: {device}")
return device
except subprocess.CalledProcessError:
continue
# Fallback: check for mounted filesystems with live or boot labels
try:
result = subprocess.run(['lsblk', '-rno', 'NAME,MOUNTPOINT,LABEL'],
capture_output=True, text=True, check=True)
for line in result.stdout.strip().split('\n'):
if line and ('live' in line.lower() or 'boot' in line.lower()):
parts = line.split()
if len(parts) >= 1:
device = f'/dev/{parts[0]}'
device = re.sub(r'\d+$', '', device) # Strip partition number
if os.path.exists(device) and os.path.exists(f'/sys/block/{os.path.basename(device)}'):
self.log(f"Detected USB device: {device}")
return device
except subprocess.CalledProcessError:
pass
# Final fallback
self.log("Could not detect USB device, assuming /dev/sdb")
return "/dev/sdb"
def detect_target_disk(self, usb_device): def detect_target_disk(self, usb_device):
# Implement disk detection """Find available target disk (not the USB device) - same logic as auto-installer.sh"""
return "/dev/sdb" # Placeholder self.log("Detecting target installation disk...")
try:
# Get all available disk devices
result = subprocess.run(['lsblk', '-rno', 'NAME,TYPE'], capture_output=True, text=True, check=True)
disks = []
for line in result.stdout.strip().split('\n'):
if line and 'disk' in line:
parts = line.split()
if len(parts) >= 1:
disks.append(f'/dev/{parts[0]}')
for disk in disks:
# Skip the USB device we're booting from
if disk == usb_device:
self.log(f"Skipping USB boot device: {disk}")
continue
# Skip if disk is too small (less than 4GB)
try:
size_result = subprocess.run(['lsblk', '-rbno', 'SIZE', disk],
capture_output=True, text=True, check=True)
size_bytes = int(size_result.stdout.strip().split('\n')[0])
size_gb = size_bytes // (1024 * 1024 * 1024)
if size_gb < 4:
self.log(f"Skipping small disk: {disk} ({size_gb} GB)")
continue
# Found suitable target disk
self.log(f"Selected target disk: {disk} ({size_gb} GB)")
return disk
except (subprocess.CalledProcessError, ValueError, IndexError):
continue
except subprocess.CalledProcessError:
pass
# If no suitable disk found, raise error
self.log("No suitable target disk found!")
raise Exception("No suitable target disk found! Available disks:\n" +
subprocess.run(['lsblk', '-rno', 'NAME,SIZE,TYPE'],
capture_output=True, text=True).stdout)
def apply_network_config(self): def apply_network_config(self):
# Apply network from config """Apply network configuration to installed system - same logic as auto-installer.sh"""
return {} # Placeholder network_config = self.config
if not network_config.get('selected_interface'):
self.log("No network interface selected")
return {}
selected_interface = network_config['selected_interface']
interface_type = network_config.get('interface_type', 'ethernet')
ip_method = network_config.get('ip_method', 'DHCP (automatic)')
self.log(f"Applying network configuration for {selected_interface} ({interface_type})")
# Create interfaces configuration
interfaces_content = "# Network configuration applied during installation\nauto lo\niface lo inet loopback\n\nauto {selected_interface}\n"
if ip_method == "DHCP (automatic)":
interfaces_content += f"iface {selected_interface} inet dhcp\n"
self.log(f"Configured {selected_interface} for DHCP")
else:
# Static IP
static_ip = network_config.get('static_ip', '')
static_gateway = network_config.get('static_gateway', '')
if static_ip:
interfaces_content += f"iface {selected_interface} inet static\n"
interfaces_content += f" address {static_ip}\n"
if static_gateway:
interfaces_content += f" gateway {static_gateway}\n"
self.log(f"Configured {selected_interface} with static IP: {static_ip}")
# WiFi configuration
if interface_type == "wireless":
wifi_config = network_config.get('wifi_config', {})
if wifi_config:
ssid = wifi_config.get('ssid', '')
security = wifi_config.get('security', 'WPA')
password = wifi_config.get('password', '')
interfaces_content += f" wireless-essid {ssid}\n"
if security == "WPA" and password:
interfaces_content += f" wpa-passphrase {password}\n"
elif security == "WEP" and password:
interfaces_content += f" wireless-key {password}\n"
self.log(f"Configured WiFi: {ssid} ({security})")
# Write interfaces file
interfaces_path = f"/target/etc/network/interfaces"
with open(interfaces_path, 'w') as f:
f.write(interfaces_content)
# Configure DNS if static
if ip_method != "DHCP (automatic)":
static_dns = network_config.get('static_dns', '')
if static_dns:
resolv_path = f"/target/etc/resolv.conf"
with open(resolv_path, 'w') as f:
f.write(f"nameserver {static_dns}\n")
self.log(f"Configured DNS: {static_dns}")
self.log("Network configuration applied to installed system")
return network_config
def find_preseed(self): def find_preseed(self):
# Find preseed file # Find preseed file
...@@ -320,12 +621,40 @@ class AutoInstallerGUI(QMainWindow): ...@@ -320,12 +621,40 @@ class AutoInstallerGUI(QMainWindow):
network_layout.addWidget(self.network_checkbox) network_layout.addWidget(self.network_checkbox)
self.network_widget = QWidget() self.network_widget = QWidget()
network_form = QFormLayout(self.network_widget) network_form = QVBoxLayout(self.network_widget)
self.interface_combo = QComboBox()
# Interface selection button
self.interface_button = QPushButton("Select Network Interface...")
self.interface_button.clicked.connect(self.select_network_interface)
self.interface_label = QLabel("No interface selected")
network_form.addWidget(self.interface_button)
network_form.addWidget(self.interface_label)
# IP Configuration
ip_group = QGroupBox("IP Configuration")
ip_layout = QVBoxLayout(ip_group)
self.ip_method_combo = QComboBox() self.ip_method_combo = QComboBox()
self.ip_method_combo.addItems(["DHCP (automatic)", "Static IP (manual)"]) self.ip_method_combo.addItems(["DHCP (automatic)", "Static IP (manual)"])
network_form.addRow("Interface:", self.interface_combo) self.ip_method_combo.currentTextChanged.connect(self.toggle_static_ip_fields)
network_form.addRow("IP Method:", self.ip_method_combo) ip_layout.addWidget(QLabel("IP Method:"))
ip_layout.addWidget(self.ip_method_combo)
# Static IP fields (initially hidden)
self.static_ip_widget = QWidget()
static_layout = QFormLayout(self.static_ip_widget)
self.ip_address_edit = QLineEdit()
self.ip_address_edit.setPlaceholderText("192.168.1.100")
self.gateway_edit = QLineEdit()
self.gateway_edit.setPlaceholderText("192.168.1.1")
self.dns_edit = QLineEdit()
self.dns_edit.setPlaceholderText("8.8.8.8")
static_layout.addRow("IP Address:", self.ip_address_edit)
static_layout.addRow("Gateway:", self.gateway_edit)
static_layout.addRow("DNS Server:", self.dns_edit)
ip_layout.addWidget(self.static_ip_widget)
self.static_ip_widget.setVisible(False)
network_form.addWidget(ip_group)
network_layout.addWidget(self.network_widget) network_layout.addWidget(self.network_widget)
layout.addWidget(network_group) layout.addWidget(network_group)
...@@ -368,12 +697,39 @@ class AutoInstallerGUI(QMainWindow): ...@@ -368,12 +697,39 @@ class AutoInstallerGUI(QMainWindow):
def toggle_network_config(self, state): def toggle_network_config(self, state):
self.network_widget.setVisible(state == Qt.CheckState.Checked.value) self.network_widget.setVisible(state == Qt.CheckState.Checked.value)
def toggle_static_ip_fields(self, method):
self.static_ip_widget.setVisible(method == "Static IP (manual)")
def select_network_interface(self):
"""Open dialog to select network interface - same logic as auto-installer.sh"""
dialog = NetworkInterfaceDialog(self)
if dialog.exec() == QDialog.DialogCode.Accepted:
self.selected_interface = dialog.selected_interface
self.interface_type = dialog.interface_type
self.wifi_config = dialog.wifi_config
self.interface_label.setText(f"Selected: {self.selected_interface} ({self.interface_type})")
if self.interface_type == "wireless" and self.wifi_config:
self.interface_label.setText(f"Selected: {self.selected_interface} (WiFi: {self.wifi_config.get('ssid', 'Unknown')})")
def start_installation(self): def start_installation(self):
self.config['timezone'] = getattr(self, 'selected_timezone', 'UTC') self.config['timezone'] = getattr(self, 'selected_timezone', 'UTC')
self.config['configure_network'] = self.network_checkbox.isChecked() self.config['configure_network'] = self.network_checkbox.isChecked()
if self.config['configure_network']: if self.config['configure_network']:
# Get network details (simplified) # Get network details
self.config['ip_method'] = self.ip_method_combo.currentText() if hasattr(self, 'selected_interface'):
self.config['selected_interface'] = self.selected_interface
self.config['interface_type'] = getattr(self, 'interface_type', 'ethernet')
self.config['wifi_config'] = getattr(self, 'wifi_config', None)
self.config['ip_method'] = self.ip_method_combo.currentText()
if self.config['ip_method'] == "Static IP (manual)":
self.config['static_ip'] = self.ip_address_edit.text()
self.config['static_gateway'] = self.gateway_edit.text()
self.config['static_dns'] = self.dns_edit.text()
else:
QMessageBox.warning(self, "Network Configuration", "Please select a network interface first")
return
self.confirm_button.setEnabled(False) self.confirm_button.setEnabled(False)
self.worker = InstallerWorker(self.config) self.worker = InstallerWorker(self.config)
......
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