Commit 8cfc4615 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!11
parents 1f79726c 1d053a84
......@@ -102,8 +102,8 @@ class Ili:
if not self.state.disklayout_strategy:
self._get_disklayout_strategy()
if not self.state.disklayout_strategy:
self._get_disklayout_strategy()
if not self.state.networknaming_strategy:
self._get_networknaming_strategy()
# Set hostname
if not self.state.hostname:
......@@ -256,6 +256,17 @@ class Ili:
self._status_callback('preseed services -> enable_services')
self.state.mirrorlist = json.loads(self._preseed.get('services', 'enable_services'))
# Process network section
if self._preseed.has_section('network'):
# Process networknaming strategy
if self._preseed.has_option('network', 'networknaming_strategy'):
self._status_callback('preseed network -> networknaming_strategy')
self.state.networknaming_strategy = self._preseed.get('network', 'networknaming_strategy').upper()
# Process interface mapping
if self._preseed.has_option('network', 'interface_mapping'):
self._status_callback('preseed network -> interface_mapping')
self.state.network_interface_mappings = self._preseed_read_json('network', 'interface_mapping')
def _get_install_disks(self):
"""Get the user to choose the disks to install on."""
......@@ -327,7 +338,7 @@ class Ili:
code, chosen_strategy = self._dialog.radiolist(
'Select disk layout strategy',
height=20, width=40, list_height=10,
title='Disk Selection',
title='Disk Layout Strategy',
choices=[
(x['strategy'], x['description'], False) for x in self.state.supported_disklayout_strategies
],
......@@ -337,13 +348,44 @@ class Ili:
if code == self._dialog.DIALOG_CANCEL:
exit(0)
# If no disks were selected, we need to display an error, and do it again
# If no disk layout strategy was selected, we need to display an error, and do it again
if not chosen_strategy:
self._error('ERROR', 'No disk layout strategy was selected!')
# Set the disklayout strategy
self.state.disklayout_strategy = chosen_strategy
def _get_networknaming_strategy(self):
"""Get network naming strategy from user."""
# Get available networknaming strategies
self._plugins.get_networknaming_strategies(self.state)
chosen_strategy = ""
# Loop while no network naming strategy was selected
while not chosen_strategy:
# 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,
title='Network Naming Strategy',
choices=[
(x['strategy'], x['description'], False) for x in self.state.supported_networknaming_strategies
],
)
# If the user selected cancel, we need to abort
if code == self._dialog.DIALOG_CANCEL:
exit(0)
# If no strategy were 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_hostname(self):
"""Get hostname from user."""
......
......@@ -37,28 +37,31 @@ class IliState:
# Chosen strategies
_diskusage_strategy: Optional[str]
_disklayout_strategy: Optional[str]
_networknaming_strategy: Optional[str]
# root size in Gbytes
_root_size: int
# Chosen hostname
# Hostname
_hostname: Optional[str]
# Chosen locale
# Locale
_locale: str
# Chosen timezone
# Timezone
_timezone: str
# Chosen mirrorlist
# Mirrorlist
_mirrorlist: Optional[str]
# Chosen install_microcode
# Install microcode
_install_microcode: bool
# Chosen root password
# Root password
_root_password: Optional[str]
# Chosen extra user username
# Extra user username
_user_username: Optional[str]
# Chosen extra user password
# Extra user password
_user_password: Optional[str]
# Chosen extra user ssh keys
# Extra user ssh keys
_user_sshkeys: List[str]
# Chosen services to enable
# Services to enable
_enable_services: List[str]
# Network
_network_interface_mappings: Dict[str, str]
#
# INTERNALS
......@@ -67,6 +70,7 @@ class IliState:
# Supported strategies
_supported_diskusage_strategies: List[Dict[str, str]]
_supported_disklayout_strategies: List[Dict[str, str]]
_supported_networknaming_strategies: List[Dict[str, str]]
# Block devices we'll be creating fileystems on, indexed by 'efi', 'boot', 'root'
_blockdevices: Dict[str, str]
......@@ -110,6 +114,7 @@ class IliState:
self._install_disks = []
self._diskusage_strategy = None
self._disklayout_strategy = None
self._networknaming_strategy = None
self._root_size = 10
self._hostname = None
self._locale = 'en_US.UTF-8'
......@@ -122,10 +127,12 @@ class IliState:
self._user_password = None
self._user_sshkeys = []
self._enable_services = []
self._network_interface_mappings = {}
# These properties are set during the install process
self._supported_diskusage_strategies = []
self._supported_disklayout_strategies = []
self._supported_networknaming_strategies = []
self._blockdevices = {}
......@@ -187,6 +194,48 @@ class IliState:
"""Set the diskusage_strategy."""
self._diskusage_strategy = value
# Disk layout
def add_disklayout_strategy(self, strategy: str, description: str):
"""Add a disk layout strategy to the list of strategies supported."""
self._supported_disklayout_strategies.append({'strategy': strategy, 'description': description})
@property
def supported_disklayout_strategies(self):
"""Return the disklayout_strategies."""
return self._supported_disklayout_strategies
@property
def disklayout_strategy(self):
"""Return the disklayout_strategy."""
return self._disklayout_strategy
@disklayout_strategy.setter
def disklayout_strategy(self, value: str):
"""Set the disklayout_strategy."""
self._disklayout_strategy = value
# Network naming
def add_networknaming_strategy(self, strategy: str, description: str):
"""Add a network naming strategy to the list of strategies supported."""
self._supported_networknaming_strategies.append({'strategy': strategy, 'description': description})
@property
def supported_networknaming_strategies(self):
"""Return the networknaming_strategies."""
return self._supported_networknaming_strategies
@property
def networknaming_strategy(self):
"""Return the networknaming_strategy."""
return self._networknaming_strategy
@networknaming_strategy.setter
def networknaming_strategy(self, value: str):
"""Set the networknaming_strategy."""
self._networknaming_strategy = value
# Root size
@property
def root_size(self):
......@@ -274,27 +323,6 @@ class IliState:
"""Return if we should install microcode or not."""
return self._install_microcode
# Disk layout
def add_disklayout_strategy(self, strategy: str, description: str):
"""Add a disk layout strategy to the list of strategies supported."""
self._supported_disklayout_strategies.append({'strategy': strategy, 'description': description})
@property
def supported_disklayout_strategies(self):
"""Return the disklayout_strategy."""
return self._supported_disklayout_strategies
@property
def disklayout_strategy(self):
"""Return the disklayout_strategy."""
return self._disklayout_strategy
@disklayout_strategy.setter
def disklayout_strategy(self, value: str):
"""Set the disklayout_strategy."""
self._disklayout_strategy = value
# Users
@property
def root_password(self):
......@@ -352,6 +380,22 @@ class IliState:
"""Return the enable_services."""
return self._enable_services
# Network
def add_network_interface_mapping(self, interface: str, mac: str):
"""Add a network interface mapping."""
self._network_interface_mappings[interface] = mac
@property
def network_interface_mappings(self):
"""Return network_interface_mappings."""
return self._network_interface_mappings
@network_interface_mappings.setter
def network_interface_mappings(self, value: Dict[str, str]):
"""Set the network_interface_mappings."""
self._network_interface_mappings = value
# Block devices
def add_blockdevice(self, usage: str, blockdevice: str):
"""Add a block device that we will be creating a filesystems on."""
......
......@@ -33,11 +33,15 @@ class Plugin:
"""Plugin __init__ method."""
def get_diskusage_strategies(self, ili_state: IliState):
"""Return a list of usage strategies we can provide based on the list of block devices."""
"""Return 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 layout strategies we can provide based on the disk usage."""
"""Return 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."""
raise NotImplementedError
def commit_diskusage_strategy(self, ili_state: IliState):
......@@ -104,6 +108,11 @@ class PluginCollection:
self._call_plugins(ili_state, 'get_disklayout_strategies')
def get_networknaming_strategies(self, ili_state: IliState):
"""Run network naming strategy plugins so they can add their strategies to the install state."""
self._call_plugins(ili_state, 'get_networknaming_strategies')
def commit_diskusage_strategy(self, ili_state: IliState):
"""Call plugins to commit the disk usage strategy."""
......
# Copyright (c) 2019, AllWorldIT
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Configure systemd networkd."""
from typing import Callable, Dict, List, Optional
from idmslinux_installer.ilistate import IliState
from idmslinux_installer.plugin import Plugin
from idmslinux_installer.util.networkdevices import NetworkDevices
# Ignore warning that we have not overridden all base class methods.
# pylama:ignore=W:select=W023
class ConfigSystemdNetworkd(Plugin):
"""Configure systemd networkd."""
def __init__(self):
"""Plugin init method."""
self.description = "System configuration systemd networkd"
Plugin.__init__(self)
def get_networknaming_strategies(self, ili_state: IliState):
"""Check which strategies we can provided."""
# Add the strategies we support
ili_state.add_networknaming_strategy('NONE', 'Do not rename interfaces')
ili_state.add_networknaming_strategy('AUTO-SEQ', 'Rename en* devices as e0pN')
def config_system(self, ili_state: IliState):
"""Configure system systemd networkd."""
# Just return now if the strategy is NONE
if ili_state.networknaming_strategy == 'NONE':
return
ili_state.output_callback('Configuring systemd-networkd')
# This denotes if we're going to write out link config
write_link_config = False
# Check if we have the automatic strategy
if ili_state.networknaming_strategy == 'AUTO-SEQ':
# Grab system network devices object
networkdevices = NetworkDevices()
# Grab the ethernet devices subset
ethernet_devices = networkdevices.ethernet
# Loop with each device and name sequentially
seq = 0
for device_name in sorted(ethernet_devices):
ili_state.add_network_interface_mapping(f'e0p{seq}', ethernet_devices[device_name]['mac'])
seq += 1
# Write out link config below
write_link_config = True
# If this is custom naming, we don't need to do anything apart from write out the link config below
if ili_state.networknaming_strategy == 'CUSTOM':
write_link_config = True
# If we're going to write configs out, do it now
if write_link_config:
# Loop with interfaces name mapping and output config files
for device_name, device_mac in ili_state.network_interface_mappings.items():
# Open the hardware address file
with open(f'{ili_state.target_root}/etc/systemd/network/10-{device_name}.link', 'w') as link_file:
link_file.write(f'[Match]\n')
link_file.write(f'Path=pci-*\n')
link_file.write(f'MACAddress={device_mac}\n')
link_file.write('\n')
link_file.write(f'[Link]\n')
link_file.write(f'Name={device_name}\n')
link_file.close()
......@@ -46,6 +46,10 @@ class PostInstallUsers(Plugin):
else:
# Add sudo user, not requiring password
ili_state.add_sudo_user(ili_state.user_username, require_password=False)
# If the user has sshkeys defined, we can safely enable sshd
if ili_state.user_sshkeys:
ili_state.add_base_package('openssh')
ili_state.add_enable_service('sshd')
def post_install(self, ili_state: IliState):
"""Post install task to setup users."""
......
......@@ -49,8 +49,8 @@ class EtcSudoers:
sudoers_file.write('# Added during install\n')
# Check if we require a password or not
if entry['require_password']:
sudoers_file.write(f'{username}=(ALL) ALL\n')
sudoers_file.write(f'{username} ALL=(ALL) ALL\n')
else:
sudoers_file.write(f'{username}=(ALL) NOPASSWD: ALL\n')
sudoers_file.write(f'{username} ALL=(ALL) NOPASSWD: ALL\n')
# Finally close file
sudoers_file.close()
# Copyright (c) 2019, AllWorldIT
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Support class for system network devices."""
import os
import re
from typing import Dict
class NetworkDevices:
"""The NetworkDevices class handles system network devices."""
# Network device list, this is global
_network_devices: Dict[str, Dict[str, str]] = {}
def __init__(self):
"""Initialize our class."""
# If we've not been initialized yet, initialize
if not self._network_devices:
self.refresh()
def refresh(self):
"""Refresh network devices from system."""
class_dir = '/sys/class/net'
self._network_devices = {}
# List the directory contents so we can get the device names
network_devices = os.listdir(class_dir)
for device_name in network_devices:
# Initialize device dict
self._network_devices[device_name] = {}
# Open the hardware address file
with open(f'{class_dir}/{device_name}/address', 'r') as address_file:
# Read in MAC and strip the newline
self._network_devices[device_name]['mac'] = address_file.read().rstrip()
# Finally close file
address_file.close()
@property
def ethernet(self):
"""Return a dict of only ethernet devices."""
# Filter out ethernet interfaces
ethernet_devices = {x: self._network_devices[x] for x in self._network_devices if re.match('^en[pso]', x)}
return ethernet_devices
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