Cleaning up guided.py a bit to be less complex and convoluted, while still performing the same task.

This commit is contained in:
Anton Hvornum 2021-02-17 13:30:14 +01:00
parent 5cb3b0d176
commit 572d59e560
3 changed files with 130 additions and 114 deletions

View File

@ -13,4 +13,6 @@ class ProfileNotFound(BaseException):
class HardwareIncompatibilityError(BaseException): class HardwareIncompatibilityError(BaseException):
pass pass
class PermissionError(BaseException): class PermissionError(BaseException):
pass
class UserError(BaseException):
pass pass

View File

@ -14,6 +14,77 @@ def get_password(prompt="Enter a password: "):
log(' * Passwords did not match * ', bg='black', fg='red') log(' * Passwords did not match * ', bg='black', fg='red')
continue continue
return passwd return passwd
return None
def ask_for_superuser_account(prompt='Create a required super-user with sudo privileges: ', forced=False):
while 1:
new_user = input(prompt).strip(' ')
if not new_user and forced:
# TODO: make this text more generic?
# It's only used to create the first sudo user when root is disabled in guided.py
log(' * Since root is disabled, you need to create a least one (super) user!', bg='black', fg='red')
continue
elif not new_user and not forced:
raise UserError("No superuser was created.")
password = get_password(prompt=f'Password for user {new_user}: ')
return {new_user: password}
def ask_for_additional_users(prompt='Any additional users to install (leave blank for no users): '):
users = {}
super_users = {}
while 1:
new_user = input(prompt).strip(' ')
if not new_user:
break
password = get_password(prompt=f'Password for user {new_user}: ')
if input("Should this user be a sudo (super) user (y/N): ").strip(' ').lower() in ('y', 'yes'):
super_users[new_user] = password
else:
users[new_user] = password
return users, super_users
def ask_to_configure_network():
# Optionally configure one network interface.
#while 1:
# {MAC: Ifname}
interfaces = {'ISO-CONFIG' : 'Copy ISO network configuration to installation', **archinstall.list_interfaces()}
archinstall.storage['_guided']['network'] = None
nic = archinstall.generic_select(interfaces.values(), "Select one network interface to configure (leave blank to skip): ")
if nic and nic != 'Copy ISO network configuration to installation':
mode = archinstall.generic_select(['DHCP (auto detect)', 'IP (static)'], f"Select which mode to configure for {nic}: ")
if mode == 'IP (static)':
while 1:
ip = input(f"Enter the IP and subnet for {nic} (example: 192.168.0.5/24): ").strip()
if ip:
break
else:
ArchInstall.log(
"You need to enter a valid IP in IP-config mode.",
level=archinstall.LOG_LEVELS.Warning,
bg='black',
fg='red'
)
if not len(gateway := input('Enter your gateway (router) IP address or leave blank for none: ').strip()):
gateway = None
dns = None
if len(dns_input := input('Enter your DNS servers (space separated, blank for none): ').strip()):
dns = dns_input.split(' ')
return {'nic': nic, 'dhcp': False, 'ip': ip, 'gateway' : gateway, 'dns' : dns}
else:
return {'nic': nic}
elif nic:
return nic
return None
def generic_select(options, input_text="Select one of the above by index or absolute value: ", sort=True): def generic_select(options, input_text="Select one of the above by index or absolute value: ", sort=True):
""" """

View File

@ -1,14 +1,10 @@
import getpass, time, json, sys, signal, os import getpass, time, json, sys, signal, os
import archinstall import archinstall
# Create a storage structure for all our information.
# We'll print this right before the user gets informed about the formatting timer.
archinstall.storage['_guided'] = {}
archinstall.storage['_guided_hidden'] = {} # This will simply be hidden from printouts and things.
""" """
This signal-handler chain (and global variable) This signal-handler chain (and global variable)
is used to trigger the "Are you sure you want to abort?" question further down. is used to trigger the "Are you sure you want to abort?" question further down.
It might look a bit odd, but have a look at the line: "if SIG_TRIGGER:"
""" """
SIG_TRIGGER = False SIG_TRIGGER = False
def kill_handler(sig, frame): def kill_handler(sig, frame):
@ -23,13 +19,14 @@ def sig_handler(sig, frame):
original_sigint_handler = signal.getsignal(signal.SIGINT) original_sigint_handler = signal.getsignal(signal.SIGINT)
signal.signal(signal.SIGINT, sig_handler) signal.signal(signal.SIGINT, sig_handler)
def perform_installation(device, boot_partition, language, mirrors): def perform_installation(device, boot_partition, language, mirrors):
""" """
Performs the installation steps on a block device. Performs the installation steps on a block device.
Only requirement is that the block devices are Only requirement is that the block devices are
formatted and setup prior to entering this function. formatted and setup prior to entering this function.
""" """
with archinstall.Installer(device, boot_partition=boot_partition, hostname=archinstall.storage['_guided']['hostname']) as installation: with archinstall.Installer(device, boot_partition=boot_partition, hostname=archinstall.arguments.get('hostname', 'Archinstall')) as installation:
## if len(mirrors): ## if len(mirrors):
# Certain services might be running that affects the system during installation. # Certain services might be running that affects the system during installation.
# Currently, only one such service is "reflector.service" which updates /etc/pacman.d/mirrorlist # Currently, only one such service is "reflector.service" which updates /etc/pacman.d/mirrorlist
@ -152,137 +149,83 @@ if archinstall.arguments['harddrive'].has_partitions():
else: else:
archinstall.arguments['harddrive'].keep_partitions = False archinstall.arguments['harddrive'].keep_partitions = False
# Get disk encryption password (or skip if blank)
if not archinstall.arguments.get('encryption-password', None):
archinstall.arguments['encryption-password'] = archinstall.get_password(prompt='Enter disk encryption password (leave blank for no encryption): ')
archinstall.arguments['harddrive'].encryption_password = archinstall.arguments['encryption-password']
disk_password = archinstall.get_password(prompt='Enter disk encryption password (leave blank for no encryption): ') # Get the hostname for the machine
archinstall.arguments['harddrive'].encryption_passwoed = disk_password if not archinstall.arguments.get('hostname', None):
archinstall.arguments['hostname'] = input('Desired hostname for the installation: ').strip(' ')
exit(0)
# Ask for a hostname
hostname = input('Desired hostname for the installation: ')
if len(hostname) == 0:
hostname = 'ArchInstall'
archinstall.storage['_guided']['hostname'] = hostname
# Ask for a root password (optional, but triggers requirement for super-user if skipped) # Ask for a root password (optional, but triggers requirement for super-user if skipped)
while (root_pw := getpass.getpass(prompt='Enter root password (leave blank to leave root disabled): ')): if not archinstall.arguments.get('!root-password', None):
root_pw_verification = getpass.getpass(prompt='And one more time for verification: ') archinstall.arguments['!root-password'] = archinstall.get_password(prompt='Enter root password (Recommended: leave blank to leave root disabled): ')
if root_pw != root_pw_verification:
archinstall.log(' * Passwords did not match * ', bg='black', fg='red')
continue
# Storing things in _guided_hidden helps us avoid printing it # # Storing things in _guided_hidden helps us avoid printing it
# when echoing user configuration: archinstall.storage['_guided'] # # when echoing user configuration: archinstall.storage['_guided']
archinstall.storage['_guided_hidden']['root_pw'] = root_pw # archinstall.storage['_guided_hidden']['root_pw'] = root_pw
archinstall.storage['_guided']['root_unlocked'] = True # archinstall.storage['_guided']['root_unlocked'] = True
break # break
# Ask for additional users (super-user if root pw was not set) # Ask for additional users (super-user if root pw was not set)
users = {} archinstall.arguments['users'] = {}
new_user_text = 'Any additional users to install (leave blank for no users): ' archinstall.arguments['superusers'] = {}
if len(root_pw.strip()) == 0: if not archinstall.arguments.get('!root-password', None):
new_user_text = 'Create a super-user with sudo privileges: ' archinstall.arguments['superusers'] = archinstall.ask_for_superuser_account('Create a required super-user with sudo privileges: ', forced=True)
archinstall.storage['_guided']['users'] = None users, superusers = archinstall.ask_for_additional_users('Any additional users to install (leave blank for no users): ')
while 1: archinstall.arguments['users'] = users
new_user = input(new_user_text) archinstall.arguments['superusers'] = {**archinstall.arguments['superusers'], **superusers}
if len(new_user.strip()) == 0:
if len(root_pw.strip()) == 0:
archinstall.log(' * Since root is disabled, you need to create a least one (super) user!', bg='black', fg='red')
continue
break
if not archinstall.storage['_guided']['users']:
archinstall.storage['_guided']['users'] = []
archinstall.storage['_guided']['users'].append(new_user)
new_user_passwd = getpass.getpass(prompt=f'Password for user {new_user}: ')
new_user_passwd_verify = getpass.getpass(prompt=f'Enter password again for verification: ')
if new_user_passwd != new_user_passwd_verify:
archinstall.log(' * Passwords did not match * ', bg='black', fg='red')
continue
users[new_user] = new_user_passwd
break
# Ask for archinstall-specific profiles (such as desktop environments etc) # Ask for archinstall-specific profiles (such as desktop environments etc)
while 1: if not archinstall.arguments.get('profile', None):
profile = archinstall.select_profile(archinstall.list_profiles()) while 1:
if profile: profile = archinstall.select_profile(archinstall.list_profiles())
archinstall.storage['_guided']['profile'] = profile print(profile)
if profile:
archinstall.storage['_guided']['profile'] = profile
if type(profile) != str: # Got a imported profile if type(profile) != str: # Got a imported profile
archinstall.storage['_guided']['profile'] = profile[0] # The second return is a module, and not a handle/object. archinstall.storage['_guided']['profile'] = profile[0] # The second return is a module, and not a handle/object.
if not profile[1]._prep_function(): if not profile[1]._prep_function():
# TODO: See how we can incorporate this into # TODO: See how we can incorporate this into
# the general log flow. As this is pre-installation # the general log flow. As this is pre-installation
# session setup. Which creates the installation.log file. # session setup. Which creates the installation.log file.
archinstall.log( archinstall.log(
' * Profile\'s preparation requirements was not fulfilled.', ' * Profile\'s preparation requirements was not fulfilled.',
bg='black', bg='black',
fg='red' fg='red'
) )
continue continue
break
else:
break break
else:
break
# Additional packages (with some light weight error handling for invalid package names) # Additional packages (with some light weight error handling for invalid package names)
archinstall.storage['_guided']['packages'] = None if not archinstall.arguments.get('packages', None):
while 1: archinstall.arguments['packages'] = [package for package in input('Additional packages aside from base (space separated): ').split(' ') if len(package)]
packages = [package for package in input('Additional packages aside from base (space separated): ').split(' ') if len(package)]
if not packages: # Verify packages that were given
break try:
archinstall.validate_package_list(archinstall.arguments['packages'])
except archinstall.RequirementError as e:
archinstall.log(e, fg='red')
exit(1)
try: # Ask or Call the helper function that asks the user to optionally configure a network.
if archinstall.validate_package_list(packages): if not archinstall.arguments.get('nic', None):
archinstall.storage['_guided']['packages'] = packages archinstall.arguments['nic'] = archinstall.ask_to_configure_network()
break
except archinstall.RequirementError as e:
print(e)
# Optionally configure one network interface.
#while 1:
# {MAC: Ifname}
interfaces = {'ISO-CONFIG' : 'Copy ISO network configuration to installation', **archinstall.list_interfaces()}
archinstall.storage['_guided']['network'] = None
nic = archinstall.generic_select(interfaces.values(), "Select one network interface to configure (leave blank to skip): ")
if nic and nic != 'Copy ISO network configuration to installation':
mode = archinstall.generic_select(['DHCP (auto detect)', 'IP (static)'], f"Select which mode to configure for {nic}: ")
if mode == 'IP (static)':
while 1:
ip = input(f"Enter the IP and subnet for {nic} (example: 192.168.0.5/24): ").strip()
if ip:
break
else:
ArchInstall.log(
"You need to enter a valid IP in IP-config mode.",
level=archinstall.LOG_LEVELS.Warning,
bg='black',
fg='red'
)
if not len(gateway := input('Enter your gateway (router) IP address or leave blank for none: ').strip()):
gateway = None
dns = None
if len(dns_input := input('Enter your DNS servers (space separated, blank for none): ').strip()):
dns = dns_input.split(' ')
archinstall.storage['_guided']['network'] = {'nic': nic, 'dhcp': False, 'ip': ip, 'gateway' : gateway, 'dns' : dns}
else:
archinstall.storage['_guided']['network'] = {'nic': nic}
elif nic:
archinstall.storage['_guided']['network'] = nic
print() print()
print('This is your chosen configuration:') print('This is your chosen configuration:')
archinstall.log("-- Guided template chosen (with below config) --", level=archinstall.LOG_LEVELS.Debug) archinstall.log("-- Guided template chosen (with below config) --", level=archinstall.LOG_LEVELS.Debug)
archinstall.log(json.dumps(archinstall.storage['_guided'], indent=4, sort_keys=True, cls=archinstall.JSON), level=archinstall.LOG_LEVELS.Info) archinstall.log(json.dumps(archinstall.storage['_guided'], indent=4, sort_keys=True, cls=archinstall.JSON), level=archinstall.LOG_LEVELS.Info)
archinstall.log(json.dumps(archinstall.arguments, indent=4, sort_keys=True, cls=archinstall.JSON), level=archinstall.LOG_LEVELS.Info)
print() print()
input('Press Enter to continue.') input('Press Enter to continue.')
exit(0)
""" """
Issue a final warning before we continue with something un-revertable. Issue a final warning before we continue with something un-revertable.