Commit 417e3b6c authored by Nigel Kukard's avatar Nigel Kukard
Browse files

Merge branch 'nkupdates' into 'master'

Nkupdates

See merge request idms-linux-rolling/projects/idms-linux-installer!13
parents 13e1be3b 50dd0523
......@@ -106,6 +106,9 @@ class Ili:
if not self.state.networknaming_strategy:
self._get_networknaming_strategy()
if not self.state.kernel:
self._get_kernels()
# Process network interface mapping before we get network config so the devices have the right names
self._plugins.process_network_interface_mapping(self.state)
......@@ -115,6 +118,10 @@ class Ili:
for device_name in self.state.network_interface_mappings:
self._get_network_config(device_name)
# Get users choice to install microcode or not
if self.state.install_microcode is None:
self._get_install_microcode()
# Set hostname
if not self.state.hostname:
self._get_hostname()
......@@ -231,6 +238,10 @@ class Ili:
if self._preseed.has_option('system', 'install_microcode'):
self._status_callback('preseed system -> install_microcode')
self.state.hostname = self._preseed.getboolean('system', 'install_microcode')
# Process kernel
if self._preseed.has_option('system', 'kernel'):
self._status_callback('preseed system -> kernel')
self.state.kernel = self._preseed.get('system', 'kernel')
# Process repository section
if self._preseed.has_section('repository'):
......@@ -391,7 +402,7 @@ class Ili:
# Display a radiolist with the network naming strategies
code, chosen_strategy = self._dialog.radiolist(
'Select network naming strategy',
height=20, width=60, list_height=10,
height=20, width=50, list_height=10,
title='Network Naming Strategy',
choices=[
(x['strategy'], x['description'], False) for x in self.state.supported_networknaming_strategies
......@@ -402,13 +413,44 @@ class Ili:
if code == self._dialog.DIALOG_CANCEL:
exit(0)
# If no strategy were selected, we need to display an error, and do it again
# If no strategy was selected, we need to display an error, and do it again
if not chosen_strategy:
self._error('ERROR', 'No network naming strategy was selected!')
# Set the networknaming strategy
self.state.networknaming_strategy = chosen_strategy
def _get_kernels(self):
"""Get kernel to use from user."""
# Get available kernels
self._plugins.get_kernels(self.state)
chosen_kernel = ''
# Loop while no network naming strategy was selected
while not chosen_kernel:
# Display a radiolist with the kernels
code, chosen_kernel = self._dialog.radiolist(
'Select kernel to use...',
height=20, width=40, list_height=10,
title='Kernel',
choices=[
(x['kernel'], x['description'], False) for x in self.state.supported_kernels
],
)
# If the user selected cancel, we need to abort
if code == self._dialog.DIALOG_CANCEL:
exit(0)
# If no kernel was selected, we need to display an error, and do it again
if not chosen_kernel:
self._error('ERROR', 'No network naming strategy was selected!')
# Set the kernel to use
self.state.kernel = chosen_kernel
def _get_network_config(self, interface: str):
"""Get network device config from user."""
......@@ -419,7 +461,7 @@ class Ili:
# Display a radiolist with the network addressing types
code, chosen_addressing = self._dialog.radiolist(
f'Select addressing type for {interface}',
height=20, width=60, list_height=10,
height=20, width=50, list_height=10,
title='Network Addressing Type',
choices=[
('NONE', 'No configuration', False),
......@@ -597,6 +639,38 @@ class Ili:
except ValueError:
self._error('ERROR', f'The DNS2 address "{dns2address_raw}" is not valid!')
def _get_install_microcode(self):
"""Get the users choice to install microcode."""
install_microcode = None
# Loop while no disk layout strategy was selected
while install_microcode is None:
# Display a radiolist with the network addressing types
code, install_microcode = self._dialog.radiolist(
f'Install CPU microcode updates?',
height=20, width=50, list_height=10,
title='Install Microcode',
choices=[
('YES', 'Install CPU microcode updates', False),
('NO', 'Do not install CPU microcode', False),
],
)
# If the user selected cancel, we need to abort
if code == self._dialog.DIALOG_CANCEL:
exit(0)
# If no option was chosen, display a warning
if not install_microcode:
self._error('ERROR', 'No option was selected regarding microcode install!')
# Set the install_microcode attribute
if install_microcode == 'YES':
self.state.install_microcode = True
else:
self.state.install_microcode = False
def _get_hostname(self):
"""Get hostname from user."""
......
......@@ -38,6 +38,7 @@ class IliState:
_diskusage_strategy: Optional[str]
_disklayout_strategy: Optional[str]
_networknaming_strategy: Optional[str]
_kernel: Optional[str]
# root size in Gbytes
_root_size: int
# Hostname
......@@ -49,7 +50,7 @@ class IliState:
# Mirrorlist
_mirrorlist: Optional[str]
# Install microcode
_install_microcode: bool
_install_microcode: Optional[bool]
# Root password
_root_password: Optional[str]
# Extra user username
......@@ -72,6 +73,7 @@ class IliState:
_supported_diskusage_strategies: List[Dict[str, str]]
_supported_disklayout_strategies: List[Dict[str, str]]
_supported_networknaming_strategies: List[Dict[str, str]]
_supported_kernels: List[Dict[str, str]]
# Block devices we'll be creating fileystems on, indexed by 'efi', 'boot', 'root'
_blockdevices: Dict[str, str]
......@@ -102,6 +104,9 @@ class IliState:
# Packages to install along with the system base
_base_packages: List[str]
# Preset to use for mkinitcpio
_mkinitcpio_preset: Optional[str]
# This property indicates that the base operating system files were installed
_base_installed: bool
......@@ -116,13 +121,14 @@ class IliState:
self._diskusage_strategy = None
self._disklayout_strategy = None
self._networknaming_strategy = None
self._kernel = None
self._root_size = 10
self._hostname = None
self._locale = 'en_US.UTF-8'
self._timezone = 'UTC'
self._mirrorlist = None
self._packages = []
self._install_microcode = True
self._install_microcode = None
self._root_password = None
self._user_username = None
self._user_password = None
......@@ -135,6 +141,7 @@ class IliState:
self._supported_diskusage_strategies = []
self._supported_disklayout_strategies = []
self._supported_networknaming_strategies = []
self._supported_kernels = []
self._blockdevices = {}
......@@ -153,6 +160,8 @@ class IliState:
self._base_packages = []
self._mkinitcpio_preset = None
self._base_installed = False
# Check if we have an output_callback, if not set to the dummy one we have
......@@ -172,7 +181,7 @@ class IliState:
@property
def install_disks(self):
"""Return the install_disks."""
"""Return the install_disks property."""
return self._install_disks
# Disk usage
......@@ -183,17 +192,17 @@ class IliState:
@property
def supported_diskusage_strategies(self):
"""Return the diskusage_strategy."""
"""Return the diskusage_strategy property."""
return self._supported_diskusage_strategies
@property
def diskusage_strategy(self):
"""Return the diskusage_strategy."""
"""Return the diskusage_strategy property."""
return self._diskusage_strategy
@diskusage_strategy.setter
def diskusage_strategy(self, value: str):
"""Set the diskusage_strategy."""
"""Set the diskusage_strategy property."""
self._diskusage_strategy = value
# Disk layout
......@@ -204,17 +213,17 @@ class IliState:
@property
def supported_disklayout_strategies(self):
"""Return the disklayout_strategies."""
"""Return the disklayout_strategies property."""
return self._supported_disklayout_strategies
@property
def disklayout_strategy(self):
"""Return the disklayout_strategy."""
"""Return the disklayout_strategy property."""
return self._disklayout_strategy
@disklayout_strategy.setter
def disklayout_strategy(self, value: str):
"""Set the disklayout_strategy."""
"""Set the disklayout_strategy property."""
self._disklayout_strategy = value
# Network naming
......@@ -225,72 +234,103 @@ class IliState:
@property
def supported_networknaming_strategies(self):
"""Return the networknaming_strategies."""
"""Return the networknaming_strategies property."""
return self._supported_networknaming_strategies
@property
def networknaming_strategy(self):
"""Return the networknaming_strategy."""
"""Return the networknaming_strategy property."""
return self._networknaming_strategy
@networknaming_strategy.setter
def networknaming_strategy(self, value: str):
"""Set the networknaming_strategy."""
"""Set the networknaming_strategy property."""
self._networknaming_strategy = value
# Kernels
def add_kernel(self, kernel: str, description: str):
"""Add a kernel to the list of kernels supported."""
self._supported_kernels.append({'kernel': kernel, 'description': description})
@property
def supported_kernels(self):
"""Return the supported kernels property."""
return self._supported_kernels
@property
def kernel(self):
"""Return the kernel property."""
return self._kernel
@kernel.setter
def kernel(self, value: str):
"""Set the kernel property."""
self._kernel = value
@property
def mkinitcpio_preset(self):
"""Return the mkinitcpio_preset property."""
return self._kernel
@mkinitcpio_preset.setter
def mkinitcpio_preset(self, value: str):
"""Set the mkinitcpio_preset property."""
self._kernel = value
# Root size
@property
def root_size(self):
"""Return the root_size."""
"""Return the root_size property."""
return self._root_size
@root_size.setter
def root_size(self, value: int):
"""Set the root_size."""
"""Set the root_size property."""
self._root_size = value
# Hostname
@property
def hostname(self):
"""Return the hostname."""
"""Return the hostname property."""
return self._hostname
@hostname.setter
def hostname(self, value: str):
"""Set the hostname."""
"""Set the hostname property."""
self._hostname = value
# Locale
@property
def locale(self):
"""Return the locale."""
"""Return the locale property."""
return self._locale
@locale.setter
def locale(self, value: str):
"""Set the locale."""
"""Set the locale property."""
self._locale = value
# Timezone
@property
def timezone(self):
"""Return the timezone."""
"""Return the timezone property."""
return self._timezone
@timezone.setter
def timezone(self, value: str):
"""Set the timezone."""
"""Set the timezone property."""
self._timezone = value
# Mirrorlist
@property
def mirrorlist(self):
"""Return the mirrorlist."""
"""Return the mirrorlist property."""
return self._mirrorlist
@mirrorlist.setter
def mirrorlist(self, value: str):
"""Set the mirrorlist."""
"""Set the mirrorlist property."""
self._mirrorlist = value
# Packages
......@@ -306,17 +346,17 @@ class IliState:
@property
def packages(self):
"""Return the packages."""
"""Return the packages property."""
return self._packages
@packages.setter
def packages(self, value: List[str]):
"""Set the packages."""
"""Set the packages property."""
self._packages = value
@property
def base_packages(self):
"""Return the base packages."""
"""Return the base packages property."""
return self._base_packages
# Install microcode
......@@ -325,35 +365,40 @@ class IliState:
"""Return if we should install microcode or not."""
return self._install_microcode
@install_microcode.setter
def install_microcode(self, value: bool):
"""Set the install_microcode property."""
self._install_microcode = value
# Users
@property
def root_password(self):
"""Return the root_password."""
"""Return the root_password property."""
return self._root_password
@root_password.setter
def root_password(self, value: str):
"""Set the root_password."""
"""Set the root_password property."""
self._root_password = value
@property
def user_username(self):
"""Return the user_username."""
"""Return the user_username property."""
return self._user_username
@user_username.setter
def user_username(self, value: str):
"""Set the user_username."""
"""Set the user_username property."""
self._user_username = value
@property
def user_password(self):
"""Return the user_password."""
"""Return the user_password property."""
return self._user_password
@user_password.setter
def user_password(self, value: str):
"""Set the user_password."""
"""Set the user_password property."""
self._user_password = value
def add_user_sshkeys(self, sshkey: str):
......@@ -363,12 +408,12 @@ class IliState:
@property
def user_sshkeys(self):
"""Return the user_sshkeys."""
"""Return the user_sshkeys property."""
return self._user_sshkeys
@user_sshkeys.setter
def user_sshkeys(self, value: List[str]):
"""Set the user_sshkeys."""
"""Set the user_sshkeys property."""
self._user_sshkeys = value
# Enable services
......@@ -379,7 +424,7 @@ class IliState:
@property
def enable_services(self):
"""Return the enable_services."""
"""Return the enable_services property."""
return self._enable_services
# Network
......@@ -404,7 +449,7 @@ class IliState:
@network_interface_mappings.setter
def network_interface_mappings(self, value: Dict[str, str]):
"""Set the network_interface_mappings."""
"""Set the network_interface_mappings property."""
self._network_interface_mappings = value
@property
......@@ -523,7 +568,7 @@ class IliState:
@target_root.setter
def target_root(self, value: str):
"""Set the target root."""
"""Set the target_root property."""
self._target_root = value
@property
......@@ -545,12 +590,12 @@ class IliState:
# Output callback
@property
def output_callback(self):
"""Return the output_callback."""
"""Return the output_callback property."""
return self._output_callback
@output_callback.setter
def output_callback(self, value: OutputCallback):
"""Set the output_callback."""
"""Set the output_callback property."""
self._output_callback = value
# Cleanup function
......
......@@ -33,15 +33,19 @@ class Plugin:
"""Plugin __init__ method."""
def get_diskusage_strategies(self, ili_state: IliState):
"""Return a list of disk usage strategies we can provide based on the list of block devices."""
"""Populate state with a list of disk usage strategies we can provide based on the list of block devices."""
raise NotImplementedError
def get_disklayout_strategies(self, ili_state: IliState):
"""Return a list of disk layout strategies we can provide based on the disk usage."""
"""Populate state with a list of disk layout strategies we can provide based on the disk usage."""
raise NotImplementedError
def get_networknaming_strategies(self, ili_state: IliState):
"""Return a list of network naming strategies we can provide based on the disk usage."""
"""Populate state with a list of network naming strategies we can provide based on the disk usage."""
raise NotImplementedError
def get_kernels(self, ili_state: IliState):
"""Populate state with a list of kernels we can provide."""
raise NotImplementedError
def commit_diskusage_strategy(self, ili_state: IliState):
......@@ -117,6 +121,11 @@ class PluginCollection:
self._call_plugins(ili_state, 'get_networknaming_strategies')
def get_kernels(self, ili_state: IliState):
"""Run kernel plugins so they can add their kernels to the install state."""
self._call_plugins(ili_state, 'get_kernels')
def process_network_interface_mapping(self, ili_state: IliState):
"""Process network interface mapping."""
......
......@@ -51,5 +51,8 @@ class ConfigSystemMkinitcpio(Plugin):
sysmkinitcpio.configure(ili_state.target_root, **kwargs)
if ili_state.kernel_preset is None:
raise RuntimeError('The kernel_preset should of been set')
ili_state.output_callback('Creating mkinitcpio')
sysmkinitcpio.create(ili_state.target_root, ili_state.output_callback)
sysmkinitcpio.create(ili_state.target_root, ili_state.kernel_preset, ili_state.output_callback)
......@@ -15,6 +15,7 @@
"""Configure systemd networkd."""
import ipaddress
from typing import Callable, Dict, List, Optional
from idmslinux_installer.ilistate import IliState
......@@ -68,6 +69,9 @@ class ConfigSystemdNetworkd(Plugin):
ili_state.output_callback('Configuring systemd-networkd')
# Make sure systemd-networkd starts on boot
ili_state.add_enable_service('systemd-networkd')
# If the strategy is not NONE, then we need to name the interfaces
if ili_state.networknaming_strategy != 'NONE':
self._write_link_files(ili_state)
......@@ -105,18 +109,21 @@ class ConfigSystemdNetworkd(Plugin):
# Check if this interface uses DHCP
if 'dhcp' in config:
network_file.write(f'DHCP=yes\n')
else:
network_file.write(f'ConfigureWithoutCarrier=yes\n')
network_file.write(f'IgnoreCarrierLoss=yes\n')
# Check if we have IP addresses
if 'ipv4address' in config:
network_file.write(f'address=%s\n' % config['ipv4address'])
network_file.write(f'Address=%s\n' % config['ipv4address'])
if 'ipv6address' in config:
network_file.write(f'address=%s\n' % config['ipv6address'])
network_file.write(f'Address=%s\n' % config['ipv6address'])
# Check if we have gateways
if 'ipv4gateway' in config:
network_file.write(f'gateway=%s\n' % config['ipv4gateway'])
network_file.write(f'Gateway=%s\n' % config['ipv4gateway'])
if 'ipv6gateway' in config:
network_file.write(f'gateway=%s\n' % config['ipv6gateway'])
network_file.write(f'Gateway=%s\n' % config['ipv6gateway'])
# Check if we have DNS servers
if 'dns1address' in config:
......@@ -124,6 +131,40 @@ class ConfigSystemdNetworkd(Plugin):
if 'dns2address' in config:
network_file.write(f'DNS=%s\n' % config['dns2address'])
# If ipv4gateway is not within ipv4address's range, then add a route section
if self._outside_network4(config, 'ipv4address', 'ipv4gateway'):
network_file.write('\n[Route]\n')
network_file.write('Destination=%s\n' % config['ipv4gateway'])
# If ipv6gateway is not within ipv6address's range, then add a route section
if self._outside_network6(config, 'ipv6address', 'ipv6gateway'):
network_file.write('\n[Route]\n')
network_file.write('Destination=%s\n' % config['ipv6gateway'])
network_file.write(f'\n')
# Finally close the file
network_file.close()
def _outside_network4(self, config: Dict[str, str], network_name: str, address_name: str):
"""Check if a network does not overlaps an address."""
if (network_name not in config) or (address_name not in config):
return False
# Reduce both to networks
network = ipaddress.IPv4Network(config[network_name], strict=False)
address = ipaddress.IPv4Network(config[address_name])
return not network.overlaps(address)
def _outside_network6(self, config: Dict[str, str], network_name: str, address_name: str):
"""Check if a network does not overlaps an address."""
if (network_name not in config) or (address_name not in config):
return False
# Reduce both to networks
network = ipaddress.IPv6Network(config[network_name], strict=False)
address = ipaddress.IPv6Network(config[address_name])