Merge pull request #55 from Torxed/43-overview

Added a configuration overview before formatting
This commit is contained in:
Anton Hvornum 2020-10-18 21:24:46 +02:00 committed by GitHub
commit f32f1e238a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 118 additions and 20 deletions

View File

@ -25,6 +25,16 @@ class BlockDevice():
raise KeyError(f'{self} does not contain information: "{key}"')
return self.info[key]
def json(self):
"""
json() has precedence over __dump__, so this is a way
to give less/partial information for user readability.
"""
return {
'path' : self.path,
'size' : self.info['size'] if 'size' in self.info else '<unknown>'
}
def __dump__(self):
return {
'path' : self.path,

View File

@ -1,5 +1,6 @@
import os, json, hashlib, shlex, sys
import time, pty
from datetime import datetime, date
from subprocess import Popen, STDOUT, PIPE, check_output
from select import epoll, EPOLLIN, EPOLLHUP
from .exceptions import *
@ -30,6 +31,43 @@ def locate_binary(name):
return os.path.join(root, file)
break # Don't recurse
class JSON_Encoder:
def _encode(obj):
if isinstance(obj, dict):
## We'll need to iterate not just the value that default() usually gets passed
## But also iterate manually over each key: value pair in order to trap the keys.
for key, val in list(obj.items()):
if isinstance(val, dict):
val = json.loads(json.dumps(val, cls=JSON)) # This, is a EXTREMELY ugly hack..
# But it's the only quick way I can think of to
# trigger a encoding of sub-dictionaries.
else:
val = JSON_Encoder._encode(val)
del(obj[key])
obj[JSON_Encoder._encode(key)] = val
return obj
elif hasattr(obj, 'json'):
return obj.json()
elif hasattr(obj, '__dump__'):
return obj.__dump__()
elif isinstance(obj, (datetime, date)):
return obj.isoformat()
elif isinstance(obj, (list, set, tuple)):
r = []
for item in obj:
r.append(json.loads(json.dumps(item, cls=JSON)))
return r
else:
return obj
class JSON(json.JSONEncoder, json.JSONDecoder):
def _encode(self, obj):
return JSON_Encoder._encode(obj)
def encode(self, obj):
return super(JSON, self).encode(self._encode(obj))
class sys_command():#Thread):
"""
Stolen from archinstall_gui

View File

@ -61,6 +61,9 @@ class Profile():
self._cache = None
self.args = args
def __dump__(self, *args, **kwargs):
return {'path' : self._path}
def __repr__(self, *args, **kwargs):
return f'Profile({self._path} <"{self.path}">)'

View File

@ -1,4 +1,20 @@
import archinstall, getpass, time
import archinstall
import getpass, time, json, sys, signal
"""
This signal-handler chain (and global variable)
is used to trigger the "Are you sure you want to abort?" question.
"""
SIG_TRIGGER = False
def kill_handler(sig, frame):
print()
exit(0)
def sig_handler(sig, frame):
global SIG_TRIGGER
SIG_TRIGGER = True
signal.signal(signal.SIGINT, kill_handler)
original_sigint_handler = signal.getsignal(signal.SIGINT)
signal.signal(signal.SIGINT, sig_handler)
def perform_installation(device, boot_partition, language, mirrors):
"""
@ -50,8 +66,13 @@ archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', surpress_error
keyboard_language = archinstall.select_language(archinstall.list_keyboard_languages())
archinstall.set_keyboard_language(keyboard_language)
# 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'] = {}
# Set which region to download packages from during the installation
mirror_regions = archinstall.select_mirror_regions(archinstall.list_mirrors())
archinstall.storage['_guided']['mirrors'] = mirror_regions
# Ask which harddrive/block-device we will install to
harddrive = archinstall.select_disk(archinstall.all_disks())
@ -61,10 +82,12 @@ while (disk_password := getpass.getpass(prompt='Enter disk encryption password (
archinstall.log(' * Passwords did not match * ', bg='black', fg='red')
continue
break
archinstall.storage['_guided']['harddrive'] = harddrive
# 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)
while (root_pw := getpass.getpass(prompt='Enter root password (leave blank to leave root disabled): ')):
@ -87,6 +110,10 @@ while 1:
archinstall.log(' * Since root is disabled, you need to create a least one (super) user!', bg='black', fg='red')
continue
break
if 'users' not in archinstall.storage['_guided']: 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:
@ -99,12 +126,17 @@ while 1:
# Ask for archinstall-specific profiles (such as desktop environments etc)
while 1:
profile = archinstall.select_profile(archinstall.list_profiles())
if profile and type(profile) != str: # Got a imported profile
if not profile[1]._prep_function():
archinstall.log(' * Profile\'s preperation requirements was not fulfilled.', bg='black', fg='red')
continue
profile = profile[0]._path # Once the prep is done, replace the selected profile with the profile name ("path") given from select_profile()
break
if profile:
archinstall.storage['_guided']['profile'] = 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.
if not profile[1]._prep_function():
archinstall.log(' * Profile\'s preperation requirements was not fulfilled.', bg='black', fg='red')
continue
profile = profile[0]._path # Once the prep is done, replace the selected profile with the profile name ("path") given from select_profile()
break
else:
break
@ -117,27 +149,42 @@ while 1:
try:
if archinstall.validate_package_list(packages):
archinstall.storage['_guided']['packages'] = packages
break
except archinstall.RequirementError as e:
print(e)
# TODO: Print a summary here of all the options chosen.
# Ideally, archinstall should keep track of this internally
# and there should be something like print(archinstall.config).
print()
print('This is your chosen configuration:')
print(json.dumps(archinstall.storage['_guided'], indent=4, sort_keys=True, cls=archinstall.JSON))
print()
"""
Issue a final warning before we continue with something un-revertable.
We mention the drive one last time, and count from 5 to 0.
"""
print(f' ! Formatting {harddrive} in 5...')
time.sleep(1)
print(f' ! Formatting {harddrive} in 4...')
time.sleep(1)
print(f' ! Formatting {harddrive} in 3...')
time.sleep(1)
print(f' ! Formatting {harddrive} in 2...')
time.sleep(1)
print(f' ! Formatting {harddrive} in 1...')
time.sleep(1)
print(f' ! Formatting {harddrive} in ', end='')
for i in range(5, 0, -1):
print(f"{i}", end='')
for x in range(4):
sys.stdout.flush()
time.sleep(0.25)
print(".", end='')
if SIG_TRIGGER:
abort = input('\nDo you really want to abort (y/n)? ')
if abort.strip() != 'n':
exit(0)
if SIG_TRIGGER is False:
sys.stdin.read()
SIG_TRIGGER = False
signal.signal(signal.SIGINT, sig_handler)
print()
signal.signal(signal.SIGINT, original_sigint_handler)
"""
Setup the blockdevice, filesystem (and optionally encryption).