Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--archinstall/lib/disk.py203
-rw-r--r--archinstall/lib/general.py3
-rw-r--r--archinstall/lib/user_interaction.py287
-rw-r--r--docs/flowcharts/BlockDeviceSelection.svg3
-rw-r--r--docs/flowcharts/DiskSelectionProcess.drawio1
-rw-r--r--examples/guided.py194
6 files changed, 493 insertions, 198 deletions
diff --git a/archinstall/lib/disk.py b/archinstall/lib/disk.py
index de39bafd..e0d9b423 100644
--- a/archinstall/lib/disk.py
+++ b/archinstall/lib/disk.py
@@ -2,7 +2,6 @@ import glob
import pathlib
import re
import time
-from collections import OrderedDict
from typing import Optional
from .general import *
@@ -30,11 +29,11 @@ class BlockDevice:
self.path = path
self.info = info
self.keep_partitions = True
- self.part_cache = OrderedDict()
+ self.part_cache = {}
+
# TODO: Currently disk encryption is a BIT misleading.
# It's actually partition-encryption, but for future-proofing this
# I'm placing the encryption password on a BlockDevice level.
- self.encryption_password = None
def __repr__(self, *args, **kwargs):
return f"BlockDevice({self.device})"
@@ -48,6 +47,12 @@ class BlockDevice:
raise KeyError(f'{self} does not contain information: "{key}"')
return self.info[key]
+ def __len__(self):
+ return len(self.partitions)
+
+ def __lt__(self, left_comparitor):
+ return self.path < left_comparitor.path
+
def json(self):
"""
json() has precedence over __dump__, so this is a way
@@ -61,12 +66,21 @@ class BlockDevice:
def __dump__(self):
return {
- 'path': self.path,
- 'info': self.info,
- 'partition_cache': self.part_cache
+ self.path : {
+ 'partuuid' : self.uuid,
+ 'wipe' : self.info.get('wipe', None),
+ 'partitions' : [part.__dump__() for part in self.partitions.values()]
+ }
}
@property
+ def partition_type(self):
+ output = json.loads(SysCommand(f"lsblk --json -o+PTTYPE {self.path}").decode('UTF-8'))
+
+ for device in output['blockdevices']:
+ return device['pttype']
+
+ @property
def device(self):
"""
Returns the actual device-endpoint of the BlockDevice.
@@ -78,7 +92,7 @@ class BlockDevice:
raise DiskError(f'Could not locate backplane info for "{self.path}"')
if self.info['type'] == 'loop':
- for drive in json.loads(b''.join(SysCommand(['losetup', '--json'])).decode('UTF_8'))['loopdevices']:
+ for drive in json.loads(SysCommand(['losetup', '--json']).decode('UTF_8'))['loopdevices']:
if not drive['name'] == self.path:
continue
@@ -100,18 +114,17 @@ class BlockDevice:
@property
def partitions(self):
- o = b''.join(SysCommand(['partprobe', self.path]))
+ SysCommand(['partprobe', self.path])
- # o = b''.join(sys_command('/usr/bin/lsblk -o name -J -b {dev}'.format(dev=dev)))
- o = b''.join(SysCommand(['/usr/bin/lsblk', '-J', self.path]))
+ result = SysCommand(['/usr/bin/lsblk', '-J', self.path])
- if b'not a block device' in o:
+ if b'not a block device' in result:
raise DiskError(f'Can not read partitions off something that isn\'t a block device: {self.path}')
- if not o[:1] == b'{':
+ if not result[:1] == b'{':
raise DiskError('Error getting JSON output from:', f'/usr/bin/lsblk -J {self.path}')
- r = json.loads(o.decode('UTF-8'))
+ r = json.loads(result.decode('UTF-8'))
if len(r['blockdevices']) and 'children' in r['blockdevices'][0]:
root_path = f"/dev/{r['blockdevices'][0]['name']}"
for part in r['blockdevices'][0]['children']:
@@ -140,10 +153,18 @@ class BlockDevice:
This is more reliable than relying on /dev/disk/by-partuuid as
it doesn't seam to be able to detect md raid partitions.
"""
- lsblk = b''.join(SysCommand(f'lsblk -J -o+UUID {self.path}'))
- for partition in json.loads(lsblk.decode('UTF-8'))['blockdevices']:
+ for partition in json.loads(SysCommand(f'lsblk -J -o+UUID {self.path}').decode('UTF-8'))['blockdevices']:
return partition.get('uuid', None)
+ @property
+ def size(self):
+ output = json.loads(SysCommand(f"lsblk --json -o+SIZE {self.path}").decode('UTF-8'))
+
+ for device in output['blockdevices']:
+ assert device['size'][-1] == 'G' # Make sure we're counting in Gigabytes, otherwise the next logic fails.
+
+ return float(device['size'][:-1])
+
def has_partitions(self):
return len(self.partitions)
@@ -154,7 +175,7 @@ class BlockDevice:
return False
def flush_cache(self):
- self.part_cache = OrderedDict()
+ self.part_cache = {}
class Partition:
@@ -210,6 +231,77 @@ class Partition:
else:
return f'Partition(path={self.path}, size={self.size}, fs={self.filesystem}{mount_repr})'
+ def __dump__(self):
+ return {
+ 'type' : 'primary',
+ 'PARTUUID' : self.uuid,
+ 'wipe' : self.allow_formatting,
+ 'boot' : self.boot,
+ 'ESP' : self.boot,
+ 'mountpoint' : self.target_mountpoint,
+ 'encrypted' : self._encrypted,
+ 'start' : self.start,
+ 'size' : self.end,
+ 'filesystem' : {
+ 'format' : get_filesystem_type(self.path)
+ }
+ }
+
+ @property
+ def sector_size(self):
+ output = json.loads(SysCommand(f"lsblk --json -o+LOG-SEC {self.path}").decode('UTF-8'))
+
+ for device in output['blockdevices']:
+ return device.get('log-sec', None)
+
+ @property
+ def start(self):
+ output = json.loads(SysCommand(f"sfdisk --json {self.block_device.path}").decode('UTF-8'))
+
+ for partition in output.get('partitiontable', {}).get('partitions', []):
+ if partition['node'] == self.path:
+ return partition['start']# * self.sector_size
+
+ @property
+ def end(self):
+ # TODO: Verify that the logic holds up, that 'size' is the size without 'start' added to it.
+ output = json.loads(SysCommand(f"sfdisk --json {self.block_device.path}").decode('UTF-8'))
+
+ for partition in output.get('partitiontable', {}).get('partitions', []):
+ if partition['node'] == self.path:
+ return partition['size']# * self.sector_size
+
+ @property
+ def boot(self):
+ output = json.loads(SysCommand(f"sfdisk --json {self.block_device.path}").decode('UTF-8'))
+
+ # Get the bootable flag from the sfdisk output:
+ # {
+ # "partitiontable": {
+ # "label":"dos",
+ # "id":"0xd202c10a",
+ # "device":"/dev/loop0",
+ # "unit":"sectors",
+ # "sectorsize":512,
+ # "partitions": [
+ # {"node":"/dev/loop0p1", "start":2048, "size":10483712, "type":"83", "bootable":true}
+ # ]
+ # }
+ # }
+
+ for partition in output.get('partitiontable', {}).get('partitions', []):
+ if partition['node'] == self.path:
+ return partition.get('bootable', False)
+
+ return False
+
+ @property
+ def partition_type(self):
+ lsblk = json.loads(SysCommand(f"lsblk --json -o+PTTYPE {self.path}").decode('UTF-8'))
+
+ for device in lsblk['blockdevices']:
+ return device['pttype']
+
@property
def uuid(self) -> Optional[str]:
"""
@@ -217,8 +309,8 @@ class Partition:
This is more reliable than relying on /dev/disk/by-partuuid as
it doesn't seam to be able to detect md raid partitions.
"""
- lsblk = b''.join(SysCommand(f'lsblk -J -o+PARTUUID {self.path}'))
- for partition in json.loads(lsblk.decode('UTF-8'))['blockdevices']:
+ lsblk = json.loads(SysCommand(f'lsblk -J -o+PARTUUID {self.path}').decode('UTF-8'))
+ for partition in lsblk['blockdevices']:
return partition.get('partuuid', None)
return None
@@ -237,7 +329,7 @@ class Partition:
@property
def real_device(self):
- for blockdevice in json.loads(b''.join(SysCommand('lsblk -J')).decode('UTF-8'))['blockdevices']:
+ for blockdevice in json.loads(SysCommand('lsblk -J').decode('UTF-8'))['blockdevices']:
if parent := self.find_parent_of(blockdevice, os.path.basename(self.path)):
return f"/dev/{parent}"
# raise DiskError(f'Could not find appropriate parent for encrypted partition {self}')
@@ -331,30 +423,29 @@ class Partition:
log(f'Formatting {path} -> {filesystem}', level=logging.INFO)
if filesystem == 'btrfs':
- o = b''.join(SysCommand(f'/usr/bin/mkfs.btrfs -f {path}'))
- if b'UUID' not in o:
- raise DiskError(f'Could not format {path} with {filesystem} because: {o}')
+ if b'UUID' not in (mkfs := SysCommand(f'/usr/bin/mkfs.btrfs -f {path}')):
+ raise DiskError(f'Could not format {path} with {filesystem} because: {mkfs}')
self.filesystem = 'btrfs'
elif filesystem == 'vfat':
- o = b''.join(SysCommand(f'/usr/bin/mkfs.vfat -F32 {path}'))
- if (b'mkfs.fat' not in o and b'mkfs.vfat' not in o) or b'command not found' in o:
- raise DiskError(f'Could not format {path} with {filesystem} because: {o}')
+ mkfs = SysCommand(f'/usr/bin/mkfs.vfat -F32 {path}')
+ if (b'mkfs.fat' not in mkfs and b'mkfs.vfat' not in mkfs) or b'command not found' in mkfs:
+ raise DiskError(f"Could not format {path} with {filesystem} because: {mkfs}")
self.filesystem = 'vfat'
elif filesystem == 'ext4':
if (handle := SysCommand(f'/usr/bin/mkfs.ext4 -F {path}')).exit_code != 0:
- raise DiskError(f'Could not format {path} with {filesystem} because: {b"".join(handle)}')
+ raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}")
self.filesystem = 'ext4'
elif filesystem == 'xfs':
if (handle := SysCommand(f'/usr/bin/mkfs.xfs -f {path}')).exit_code != 0:
- raise DiskError(f'Could not format {path} with {filesystem} because: {b"".join(handle)}')
+ raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}")
self.filesystem = 'xfs'
elif filesystem == 'f2fs':
if (handle := SysCommand(f'/usr/bin/mkfs.f2fs -f {path}')).exit_code != 0:
- raise DiskError(f'Could not format {path} with {filesystem} because: {b"".join(handle)}')
+ raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}")
self.filesystem = 'f2fs'
elif filesystem == 'crypto_LUKS':
@@ -393,9 +484,9 @@ class Partition:
try:
if options:
- SysCommand(f'/usr/bin/mount -o {options} {self.path} {target}')
+ SysCommand(f"/usr/bin/mount -o {options} {self.path} {target}")
else:
- SysCommand(f'/usr/bin/mount {self.path} {target}')
+ SysCommand(f"/usr/bin/mount {self.path} {target}")
except SysCallError as err:
raise err
@@ -404,7 +495,7 @@ class Partition:
def unmount(self):
try:
- exit_code = SysCommand(f'/usr/bin/umount {self.path}').exit_code
+ SysCommand(f"/usr/bin/umount {self.path}")
except SysCallError as err:
exit_code = err.exit_code
@@ -476,17 +567,29 @@ class Filesystem:
# TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager
if len(args) >= 2 and args[1]:
raise args[1]
- b''.join(SysCommand('sync'))
+ SysCommand('sync')
return True
+ def load_layout(self, layout :dict):
+ for partition in layout:
+ # We don't want to re-add an existing partition (those containing a UUID already)
+ if 'UUID' not in partition:
+ self.add_partition(partition.get('type', 'primary'),
+ start=partition.get('start', '1MiB'), # TODO: Revisit sane block starts (4MB for memorycards for instance)
+ end=partition.get('size', '100%'),
+ partition_format=partition.get('filesystem', {}).get('format', 'btrfs'))
+
+
+
+ exit(0)
+
def find_partition(self, mountpoint):
for partition in self.blockdevice:
if partition.target_mountpoint == mountpoint or partition.mountpoint == mountpoint:
return partition
def raw_parted(self, string: str):
- x = SysCommand(f'/usr/bin/parted -s {string}')
- return x
+ return SysCommand(f'/usr/bin/parted -s {string}')
def parted(self, string: str):
"""
@@ -582,9 +685,10 @@ def device_state(name, *args, **kwargs):
# lsblk --json -l -n -o path
def all_disks(*args, **kwargs):
kwargs.setdefault("partitions", False)
- drives = OrderedDict()
- # for drive in json.loads(sys_command(f'losetup --json', *args, **lkwargs, hide_from_log=True)).decode('UTF_8')['loopdevices']:
- for drive in json.loads(b''.join(SysCommand('lsblk --json -l -n -o path,size,type,mountpoint,label,pkname,model')).decode('UTF_8'))['blockdevices']:
+ drives = {}
+
+ lsblk = json.loads(SysCommand('lsblk --json -l -n -o path,size,type,mountpoint,label,pkname,model').decode('UTF_8'))
+ for drive in lsblk['blockdevices']:
if not kwargs['partitions'] and drive['type'] == 'part':
continue
@@ -617,12 +721,10 @@ def harddrive(size=None, model=None, fuzzy=False):
def get_mount_info(path) -> dict:
try:
- output = SysCommand(f'/usr/bin/findmnt --json {path}')
+ output = SysCommand(f'/usr/bin/findmnt --json {path}').decode('UTF-8')
except SysCallError:
return {}
- output = output.decode('UTF-8')
-
if not output:
return {}
@@ -636,14 +738,12 @@ def get_mount_info(path) -> dict:
def get_partitions_in_use(mountpoint) -> list:
try:
- output = SysCommand(f'/usr/bin/findmnt --json -R {mountpoint}')
+ output = SysCommand(f"/usr/bin/findmnt --json -R {mountpoint}").decode('UTF-8')
except SysCallError:
return []
mounts = []
- output = output.decode('UTF-8')
-
if not output:
return []
@@ -659,16 +759,25 @@ def get_partitions_in_use(mountpoint) -> list:
def get_filesystem_type(path):
try:
- handle = SysCommand(f"blkid -o value -s TYPE {path}")
- return b''.join(handle).strip().decode('UTF-8')
+ return SysCommand(f"blkid -o value -s TYPE {path}").decode('UTF-8').strip()
except SysCallError:
return None
def disk_layouts():
try:
- handle = SysCommand("lsblk -f -o+TYPE,SIZE -J")
- return json.loads(b''.join(handle).decode('UTF-8'))
+ return json.loads(SysCommand("lsblk -f -o+TYPE,SIZE -J").decode('UTF-8'))
except SysCallError as err:
log(f"Could not return disk layouts: {err}")
return None
+
+
+def encrypted_partitions(blockdevices :dict) -> bool:
+ for partition in blockdevices.values():
+ if partition.get('encrypted', False):
+ yield partition
+
+def find_partition_by_mountpoint(partitions, relative_mountpoint :str):
+ for partition in partitions:
+ if partition.get('mountpoint', None) == relative_mountpoint:
+ return partition \ No newline at end of file
diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py
index b9dc66ab..4d3257ba 100644
--- a/archinstall/lib/general.py
+++ b/archinstall/lib/general.py
@@ -71,6 +71,8 @@ def locate_binary(name):
raise RequirementError(f"Binary {name} does not exist.")
+def json_dumps(*args, **kwargs):
+ return json.dumps(*args, **{**kwargs, 'cls': JSON})
class JsonEncoder:
def _encode(obj):
@@ -113,7 +115,6 @@ class JSON(json.JSONEncoder, json.JSONDecoder):
def encode(self, obj):
return super(JSON, self).encode(self._encode(obj))
-
class SysCommandWorker:
def __init__(self, cmd, callbacks=None, peak_output=False, environment_vars=None, logfile=None, working_directory='./'):
if not callbacks:
diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py
index 07b675a4..4f99820d 100644
--- a/archinstall/lib/user_interaction.py
+++ b/archinstall/lib/user_interaction.py
@@ -9,6 +9,7 @@ import signal
import sys
import time
+from .disk import BlockDevice
from .exceptions import *
from .general import SysCommand
from .hardware import AVAILABLE_GFX_DRIVERS, has_uefi
@@ -17,7 +18,6 @@ from .networking import list_interfaces
from .output import log
from .profiles import Profile, list_profiles
-
# TODO: Some inconsistencies between the selection processes.
# Some return the keys from the options, some the values?
@@ -186,8 +186,24 @@ def generic_multi_select(options, text="Select one or more of the options above
except RequirementError as e:
log(f" * {e} * ", fg='red')
+ sys.stdout.write('\n')
+ sys.stdout.flush()
return selected_options
+def select_encrypted_partitions(blockdevices :dict) -> dict:
+ print(blockdevices[0])
+
+ if len(blockdevices) == 1:
+ if len(blockdevices[0]['partitions']) == 2:
+ root = find_partition_by_mountpoint(blockdevices[0]['partitions'], '/')
+ blockdevices[0]['partitions'][root]['encrypted'] = True
+ return True
+
+ options = []
+ for partition in blockdevices.values():
+ options.append({key: val for key, val in partition.items() if val})
+
+ print(generic_multi_select(options, f"Choose which partitions to encrypt (leave blank when done): "))
class MiniCurses:
def __init__(self, width, height):
@@ -535,6 +551,275 @@ def generic_select(options, input_text="Select one of the above by index or abso
return selected_option
+def select_partition_layout(block_device):
+ return {
+ BlockDevice("/dev/sda"): { # Block Device level
+ "wipe": False, # Safety flags
+ "partitions" : [ # Affected / New partitions
+ {
+ "PARTUUID" : "654bb317-1b73-4339-9a00-7222792f4ba9", # If existing partition
+ "wipe" : False, # Safety flags
+ "boot" : True, # Safety flags / new flags
+ "ESP" : True, # Safety flags / new flags
+ "mountpoint" : "/mnt/boot"
+ }
+ ]
+ },
+ BlockDevice("/dev/sdb") : {
+ "wipe" : True,
+ "partitions" : [
+ {
+ # No PARTUUID required here since it's a new partition
+ "type" : "primary", # parted options
+ "size" : "100%",
+ "filesystem" : {
+ "encrypted" : True, # TODO: Not sure about this here
+ "format": "btrfs", # mkfs options
+ },
+ "mountpoint" : "/mnt"
+ }
+ ]
+ }
+ }
+
+def valid_fs_type(fstype :str) -> bool:
+ # https://www.gnu.org/software/parted/manual/html_node/mkpart.html
+
+ return fstype in [
+ "ext2",
+ "fat16", "fat32",
+ "hfs", "hfs+", "hfsx",
+ "linux-swap",
+ "NTFS",
+ "reiserfs",
+ "ufs",
+ "btrfs",
+ ]
+
+def valid_parted_position(pos :str):
+ if not len(pos):
+ return False
+
+ if pos.isdigit():
+ return True
+
+ if pos[-1] == '%' and pos[:-1].isdigit():
+ return True
+
+ if pos[-3:].lower() in ['mib', 'kib', 'b', 'tib'] and pos[:-3].isdigit():
+ return True
+
+ return False
+
+def partition_overlap(partitions :list, start :str, end :str) -> bool:
+ # TODO: Implement sanity check
+ return False
+
+def get_default_partition_layout(block_devices):
+ if len(block_devices) == 1:
+ return {
+ block_devices[0] : [
+ { # Boot
+ "type" : "primary",
+ "start" : "0MiB",
+ "size" : "513MiB",
+ "boot" : True,
+ "mountpoint" : "/boot",
+ "filesystem" : {
+ "format" : "fat32"
+ }
+ },
+ { # Root
+ "type" : "primary",
+ "start" : "513MiB",
+ "encrypted" : True,
+ "size" : f"{max(block_devices[0].size*0.2, 20)*1024}MiB", # Warning: Won't work on small where max size is 16GB for instance.
+ "mountpoint" : "/",
+ "filesystem" : {
+ "format" : "btrfs"
+ }
+ },
+ { # Home
+ "type" : "primary",
+ "encrypted" : True,
+ "start" : f"{513 + (max(block_devices[0].size*0.2, 20)*1024)}MiB",
+ "size" : "100%",
+ "mountpoint" : "/home",
+ "filesystem" : {
+ "format" : "btrfs"
+ }
+ }
+ ]
+ }
+ # TODO: Implement sane generic layout for 2+ drives
+
+def wipe_and_create_partitions(block_device :BlockDevice) -> dict:
+ if hasUEFI():
+ partition_type = 'gpt'
+ else:
+ partition_type = 'msdos'
+
+ partitions_result = [] # Test code: [part.__dump__() for part in block_device.partitions.values()]
+ suggested_layout = [
+ { # Boot
+ "type" : "primary",
+ "start" : "0MiB",
+ "size" : "513MiB",
+ "boot" : True,
+ "mountpoint" : "/boot",
+ "filesystem" : {
+ "format" : "fat32"
+ }
+ },
+ { # Root
+ "type" : "primary",
+ "start" : "513MiB",
+ "encrypted" : True,
+ "size" : f"{max(block_device.size*0.2, 20)}GiB",
+ "mountpoint" : "",
+ "filesystem" : {
+ "format" : "btrfs"
+ }
+ },
+ { # Home
+ "type" : "primary",
+ "encrypted" : True,
+ "start" : f"{max(block_device.size*0.2, 20)}GiB",
+ "size" : "100%",
+ "mountpoint" : "/home",
+ "filesystem" : {
+ "format" : "btrfs"
+ }
+ }
+ ]
+ # TODO: Squeeze in BTRFS subvolumes here
+
+ while True:
+ modes = [
+ "Create new partition",
+ "Suggest partition layout",
+ "Delete partition" if len(partitions_result) else "",
+ "Assign mount-point for partition" if len(partitions_result) else "",
+ "Mark/Unmark a partition as encrypted" if len(partitions_result) else "",
+ "Mark/Unmark a partition as bootable (automatic for /boot)" if len(partitions_result) else ""
+ ]
+
+ # Print current partition layout:
+ if len(partitions_result):
+ print('Current partition layout:')
+ for partition in partitions_result:
+ print({key: val for key, val in partition.items() if val})
+ print()
+
+ task = generic_select(modes,
+ input_text=f"Select what to do with {block_device} (leave blank when done): ")
+
+ if task == 'Create new partition':
+ if partition_type == 'gpt':
+ # https://www.gnu.org/software/parted/manual/html_node/mkpart.html
+ # https://www.gnu.org/software/parted/manual/html_node/mklabel.html
+ name = input("Enter a desired name for the partition: ").strip()
+ fstype = input("Enter a desired filesystem type for the partition: ").strip()
+ start = input("Enter the start sector of the partition (percentage or block number, ex: 0%): ").strip()
+ end = input("Enter the end sector of the partition (percentage or block number, ex: 100%): ").strip()
+
+ if valid_parted_position(start) and valid_parted_position(end) and valid_fs_type(fstype):
+ if partition_overlap(partitions_result, start, end):
+ log(f"This partition overlaps with other partitions on the drive! Ignoring this partition creation.", fg="red")
+ continue
+
+ partitions_result.append({
+ "type" : "primary", # Strictly only allowed under MSDOS, but GPT accepts it so it's "safe" to inject
+ "start" : start,
+ "size" : end,
+ "mountpoint" : None,
+ "filesystem" : {
+ "format" : fstype
+ }
+ })
+ else:
+ log(f"Invalid start, end or fstype for this partition. Ignoring this partition creation.", fg="red")
+ continue
+ elif task == "Suggest partition layout":
+ if len(partitions_result):
+ if input(f"{block_device} contains queued partitions, this will remove those, are you sure? y/N: ").strip().lower() in ('', 'n'):
+ continue
+
+ partitions_result = [*suggested_layout]
+ elif task is None:
+ return {
+ block_device : partitions_result
+ }
+ else:
+ for index, partition in enumerate(partitions_result):
+ print(f"{index}: Start: {partition['start']}, End: {partition['size']} ({partition['filesystem']['format']}{', mounting at: '+partition['mountpoint'] if partition['mountpoint'] else ''})")
+
+ if task == "Delete partition":
+ if (partition := generic_select(partitions_result, 'Select which partition to delete: ', options_output=False)):
+ del(partitions_result[partitions_result.index(partition)])
+ elif task == "Assign mount-point for partition":
+ if (partition := generic_select(partitions_result, 'Select which partition to mount where: ', options_output=False)):
+ print(' * Partition mount-points are relative to inside the installation, the boot would be /boot as an example.')
+ mountpoint = input('Select where to mount partition (leave blank to remove mountpoint): ').strip()
+
+ if len(mountpoint):
+ partitions_result[partitions_result.index(partition)]['mountpoint'] = mountpoint
+ if mountpoint == '/boot':
+ log(f"Marked partition as bootable because mountpoint was set to /boot.", fg="yellow")
+ partitions_result[partitions_result.index(partition)]['boot'] = True
+ else:
+ del(partitions_result[partitions_result.index(partition)]['mountpoint'])
+
+ elif task == "Mark/Unmark a partition as encrypted":
+ if (partition := generic_select(partitions_result, 'Select which partition to mark as encrypted: ', options_output=False)):
+ # Negate the current encryption marking
+ partitions_result[partitions_result.index(partition)]['encrypted'] = not partitions_result[partitions_result.index(partition)].get('encrypted', False)
+
+ elif task == "Mark/Unmark a partition as bootable (automatic for /boot)":
+ if (partition := generic_select(partitions_result, 'Select which partition to mark as bootable: ', options_output=False)):
+ partitions_result[partitions_result.index(partition)]['boot'] = not partitions_result[partitions_result.index(partition)].get('boot', False)
+
+ return {
+ block_device : partitions_result
+ }
+
+def select_individual_blockdevice_usage(block_devices :list):
+ result = {}
+
+ for device in block_devices:
+ log(f'Select what to do with {device}', fg="yellow")
+ modes = [
+ "Wipe and create new partitions",
+ "Re-use partitions"
+ ]
+
+ device_mode = generic_select(modes)
+
+ if device_mode == "Re-use partitions":
+ layout = select_partition_layout(device)
+ elif device_mode == "Wipe and create new partitions":
+ layout = wipe_and_create_partitions(device)
+ else:
+ continue
+
+ result[device] = layout
+
+ return result
+
+
+def select_disk_layout(block_devices :list):
+ modes = [
+ "Wipe all selected drives and use a best-effort default partition layout",
+ "Select what to do with each individual drive (followed by partition usage)"
+ ]
+
+ mode = generic_select(modes, input_text=f"Select what you wish to do with the selected block devices: ")
+
+ if mode == 'Wipe all selected drives and use a best-effort default partition layout':
+ return get_default_partition_layout(block_devices)
+ else:
+ return select_individual_blockdevice_usage(block_devices)
+
def select_disk(dict_o_disks):
"""
diff --git a/docs/flowcharts/BlockDeviceSelection.svg b/docs/flowcharts/BlockDeviceSelection.svg
new file mode 100644
index 00000000..2d63f674
--- /dev/null
+++ b/docs/flowcharts/BlockDeviceSelection.svg
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="521px" height="546px" viewBox="-0.5 -0.5 521 546" content="&lt;mxfile host=&quot;Electron&quot; modified=&quot;2021-05-02T19:58:13.539Z&quot; agent=&quot;5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.5.1 Chrome/89.0.4389.82 Electron/12.0.1 Safari/537.36&quot; etag=&quot;Pkv6xZ_P1Z1qw9YxKuYv&quot; version=&quot;14.5.1&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;C5RBs43oDa-KdzZeNtuy&quot; name=&quot;Page-1&quot;&gt;7VvZdqM4EP2anHlKDpsxPMZ20sl0kl7S05meNwVkYCIjt5C3+fqRjFgFNnbwksQv3a5CCKG6dWsROdP7o/knAsb+PXYhOtMUd36mD840TVUsi/3HNYtYY5tC4ZHAFYMyxWPwH0zuFNpJ4MKoMJBijGgwLiodHIbQoQUdIATPisOGGBWfOgYelBSPDkCy9ilwqR9rLa2b6W9g4PnJk1XTjq+MQDJYvEnkAxfPcir96kzvE4xp/Gs070PENy/Zl6fbxRO6ezE//fkt+g3+6n3+8fDzPJ7sepNb0lcgMKRbT/3y2bLtaXf4oFrf7ga/rYfB/dW5IV6NLpL9gi7bPiFiQn3s4RCgq0zbI3gSupDPqjIpG3OH8Vgo/4WULgQWwIRipvLpCImr7C3I4m8mKBedRPzFxUQYzAvSQkhDHNI+RpgsV6obpmF3BkwfUYJfYO5K17IuL3V+R4BQTt/T+vZlbzmve80upetxLznSmOggEEWBs5wUECoGKYmcDAtxyHcieoHU8cWAeCf59pWAt8ZqYlyEJ8SBK8bpwnkA8eCq+cwUmsynIR5BtoXsPgIRoMG0uDggnMtLx2UAYj8EhjaAqljkFKCJeNIjRNytNaWHsPMygNPAYXRQRt3MDyh8HIPlDswYExURwy0v4KSyl+t53EypYbj1E9/mo1NHVSS01gFiBYRqQLeZdaeQUDjP7b1soOSqKehG8K0lxFlGXmrCSH6OuAzl9SatpAhNMukDPrFGBUHskwzMhmSgKtVga50NKqEjs8GvCt8/IHbUA2HnbUScCpBp3mx4N50TtftTuRl3H7/9Y4fnmrGS0c6Vi65qCCw0Bp6Y7isOQpobgofDiC2mjMz0qduHLlMC69VoTPl7xCEswKEEXeLj0fOELa23JoAVQhJH1TUYBYjD7AaiKaSBAyrCHECBF3KMMHtCUh3r2COD0GOSmUk/lm7Bkru3H/7UbsP4Z7UQ/6rRXcNiCp9WU7YKhs6ETJdGkTOUgodvQXWbU1OdieE8oDnOZFJKmex3xphcSAhz2yBd4LpqSizxpiDEjFeVjVGZJ8Bq02sNw2y3koprQa5cqIZmv44QxVRGp+QvZskP4rWLu/Ll4f6YtSsza+iQxZg//Tsvn0/VQB1SOnrJvPahywHlSMqBd5nWJ02ztSlXZ13G9XqCaZUXKqGkSlA6VHmwx0j3YXBbPbCG7vbTnVJl8rqf8FY0g5Gm9AZ/yOg7Zfi7zvC1piFtdxm+Kpn9naTwbbt5coCzLjxZDfPhPbm9HGjSrjSb8HmZgvbTig6ELvsXj3nFD/jkI2ZdeupZN3dpQ99jlloJQLul1KJd723dS+tzw2N20srVqHKVuFVh8arucCGRy/K62qbFG7G0ah6VqTuSpftsv0AQ8q7aV5bGBpx6T7nY7olb7xxbLqbKzL1/GtjskOjgNGC1TQO5gxvdFj3Qo2gjVKfvbQX7nab0FWd5pYq98lCwXOQfvjJ4FQS140o6LAk5X5CbrwoUH/Cg5LBdhKHcqP64EQkQR6xU0/cYoAz14AHKOkWjlVSgNv02pemh2WaRh1EoWOQGjHkcinYRd1T5i4G0rRAfaynXtwhGi4jCkQSaU/eg7oxLb+rjO+seaHKFcmzpRFt5gAsiP11Wy0yQ7FrDD4iOJitI1p0/JuAtwNXV6RqHLgGgnA9sdiQkM0Cd3fO8sS8H3ucnqyvxlDPgbcjS7eWd/DMunuCNKvK57W0o2crSnnXTbGIrtwMt1yjZquCXzA70C0/tKN9hY5e21I0js6Xe1inxqbrbnsiblncJERwLkcv13VMwhrH/96q+PPq4Bd3OCKVUwXWMTjNG2VkFpx9dMve+2aN9Uqj+FNJUS3WEYRenqPkUsq16MHnP/LkGgoDwnIN3lUzEdrP3zGTT47/iwtA9F50mZqWKuHYipNazVaMBIRntEBITs78PjXGW/ZWtfvU/&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><path d="M 60 40 L 60 83.63" fill="none" stroke="#788aa3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 60 88.88 L 56.5 81.88 L 60 83.63 L 63.5 81.88 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><rect x="0" y="0" width="120" height="40" rx="6" ry="6" fill="#b2c9ab" stroke="#788aa3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 20px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Select BlockDevices</div></div></div></foreignObject><text x="60" y="24" fill="#46495D" font-family="Helvetica" font-size="12px" text-anchor="middle">Select BlockDevices</text></switch></g><path d="M 60 170 L 60 203.63" fill="none" stroke="#788aa3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 60 208.88 L 56.5 201.88 L 60 203.63 L 63.5 201.88 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 190px; margin-left: 60px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">No</div></div></div></foreignObject><text x="60" y="193" fill="#46495D" font-family="Helvetica" font-size="11px" text-anchor="middle">No</text></switch></g><path d="M 120 130 L 230 130 Q 240 130 240 120 L 240 46.37" fill="none" stroke="#788aa3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 240 41.12 L 243.5 48.12 L 240 46.37 L 236.5 48.12 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 130px; margin-left: 150px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Yes</div></div></div></foreignObject><text x="150" y="133" fill="#46495D" font-family="Helvetica" font-size="11px" text-anchor="middle">Yes</text></switch></g><path d="M 30 110 L 51.68 95.55 Q 60 90 68.32 95.55 L 111.68 124.45 Q 120 130 111.68 135.55 L 68.32 164.45 Q 60 170 51.68 164.45 L 8.32 135.55 Q 0 130 8.32 124.45 Z" fill="#b2c9ab" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 110px; height: 1px; padding-top: 128px; margin-left: 5px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Empty Selection</div></div></div></foreignObject><text x="60" y="132" fill="#46495D" font-family="Helvetica" font-size="12px" text-anchor="middle">Empty Selection</text></switch></g><path d="M 430 46.37 L 430 110" fill="none" stroke="#788aa3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 430 41.12 L 433.5 48.12 L 430 46.37 L 426.5 48.12 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 80px; margin-left: 430px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Yes / No</div></div></div></foreignObject><text x="430" y="83" fill="#46495D" font-family="Helvetica" font-size="11px" text-anchor="middle">Yes / No</text></switch></g><rect x="370" y="110" width="120" height="40" rx="6" ry="6" fill="#b2c9ab" stroke="#788aa3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 130px; margin-left: 371px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Encrypt Root</div></div></div></foreignObject><text x="430" y="134" fill="#46495D" font-family="Helvetica" font-size="12px" text-anchor="middle">Encrypt Root</text></switch></g><path d="M 120 250 L 183.63 250" fill="none" stroke="#788aa3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 188.88 250 L 181.88 253.5 L 183.63 250 L 181.88 246.5 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 250px; margin-left: 150px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">No</div></div></div></foreignObject><text x="150" y="253" fill="#46495D" font-family="Helvetica" font-size="11px" text-anchor="middle">No</text></switch></g><path d="M 60 290 L 60 343.63" fill="none" stroke="#788aa3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 60 348.88 L 56.5 341.88 L 60 343.63 L 63.5 341.88 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 320px; margin-left: 60px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Yes</div></div></div></foreignObject><text x="60" y="323" fill="#46495D" font-family="Helvetica" font-size="11px" text-anchor="middle">Yes</text></switch></g><path d="M 30 230 L 51.68 215.55 Q 60 210 68.32 215.55 L 111.68 244.45 Q 120 250 111.68 255.55 L 68.32 284.45 Q 60 290 51.68 284.45 L 8.32 255.55 Q 0 250 8.32 244.45 Z" fill="#b2c9ab" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 110px; height: 1px; padding-top: 248px; margin-left: 5px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Multiple BD's</div></div></div></foreignObject><text x="60" y="252" fill="#46495D" font-family="Helvetica" font-size="12px" text-anchor="middle">Multiple BD's</text></switch></g><path d="M 120 370 L 183.63 370" fill="none" stroke="#788aa3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 188.88 370 L 181.88 373.5 L 183.63 370 L 181.88 366.5 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><rect x="0" y="350" width="120" height="40" rx="6" ry="6" fill="#b2c9ab" stroke="#788aa3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 370px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Select /boot, / and optional mounts</div></div></div></foreignObject><text x="60" y="374" fill="#46495D" font-family="Helvetica" font-size="12px" text-anchor="middle">Select /boot, / and...</text></switch></g><path d="M 250 290 L 250 323.63" fill="none" stroke="#788aa3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 250 328.88 L 246.5 321.88 L 250 323.63 L 253.5 321.88 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 310px; margin-left: 250px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Yes</div></div></div></foreignObject><text x="250" y="313" fill="#46495D" font-family="Helvetica" font-size="11px" text-anchor="middle">Yes</text></switch></g><path d="M 310 250 L 363.63 250" fill="none" stroke="#788aa3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 368.88 250 L 361.88 253.5 L 363.63 250 L 361.88 246.5 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 250px; margin-left: 340px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">No</div></div></div></foreignObject><text x="340" y="253" fill="#46495D" font-family="Helvetica" font-size="11px" text-anchor="middle">No</text></switch></g><path d="M 220 230 L 241.68 215.55 Q 250 210 258.32 215.55 L 301.68 244.45 Q 310 250 301.68 255.55 L 258.32 284.45 Q 250 290 241.68 284.45 L 198.32 255.55 Q 190 250 198.32 244.45 Z" fill="#b2c9ab" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 110px; height: 1px; padding-top: 248px; margin-left: 195px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Contains Partitions</div></div></div></foreignObject><text x="250" y="252" fill="#46495D" font-family="Helvetica" font-size="12px" text-anchor="middle">Contains Partitions</text></switch></g><path d="M 310 370 L 420 370 Q 430 370 430 360 L 430 276.37" fill="none" stroke="#788aa3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 430 271.12 L 433.5 278.12 L 430 276.37 L 426.5 278.12 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 370px; margin-left: 339px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">No</div></div></div></foreignObject><text x="339" y="373" fill="#46495D" font-family="Helvetica" font-size="11px" text-anchor="middle">No</text></switch></g><path d="M 250 410 L 250 458.63" fill="none" stroke="#788aa3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 250 463.88 L 246.5 456.88 L 250 458.63 L 253.5 456.88 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 438px; margin-left: 250px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Yes</div></div></div></foreignObject><text x="250" y="441" fill="#46495D" font-family="Helvetica" font-size="11px" text-anchor="middle">Yes</text></switch></g><path d="M 220 350 L 240.43 336.38 Q 250 330 259.57 336.38 L 300.43 363.62 Q 310 370 300.43 376.38 L 259.57 403.62 Q 250 410 240.43 403.62 L 199.57 376.38 Q 190 370 199.57 363.62 Z" fill="#b2c9ab" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 110px; height: 1px; padding-top: 368px; margin-left: 195px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Old /boot has content</div></div></div></foreignObject><text x="250" y="372" fill="#46495D" font-family="Helvetica" font-size="12px" text-anchor="middle">Old /boot has cont...</text></switch></g><path d="M 430 230 L 430 156.37" fill="none" stroke="#788aa3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 430 151.12 L 433.5 158.12 L 430 156.37 L 426.5 158.12 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><rect x="370" y="230" width="120" height="40" rx="6" ry="6" fill="#b2c9ab" stroke="#788aa3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 250px; margin-left: 371px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Select Root FIlesystem</div></div></div></foreignObject><text x="430" y="254" fill="#46495D" font-family="Helvetica" font-size="12px" text-anchor="middle">Select Root FIlesyst...</text></switch></g><path d="M 370 20 L 306.37 20" fill="none" stroke="#788aa3" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><path d="M 301.12 20 L 308.12 16.5 L 306.37 20 L 308.12 23.5 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><rect x="370" y="0" width="120" height="40" rx="6" ry="6" fill="#b2c9ab" stroke="#788aa3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 20px; margin-left: 371px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Mount Partitions</div></div></div></foreignObject><text x="430" y="24" fill="#46495D" font-family="Helvetica" font-size="12px" text-anchor="middle">Mount Partitions</text></switch></g><rect x="180" y="0" width="120" height="40" rx="6" ry="6" fill="#d5e8d4" stroke="#82b366" stroke-dasharray="3 3" pointer-events="all"/><g transform="translate(-0.5 -0.5)" opacity="0.4"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 20px; margin-left: 181px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Install on /mnt</div></div></div></foreignObject><text x="240" y="24" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Install on /mnt</text></switch></g><path d="M 310 505 L 373.63 505" fill="none" stroke="#788aa3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 378.88 505 L 371.88 508.5 L 373.63 505 L 371.88 501.5 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 505px; margin-left: 345px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Yes</div></div></div></foreignObject><text x="345" y="508" fill="#46495D" font-family="Helvetica" font-size="11px" text-anchor="middle">Yes</text></switch></g><path d="M 220 485 L 241.68 470.55 Q 250 465 258.32 470.55 L 301.68 499.45 Q 310 505 301.68 510.55 L 258.32 539.45 Q 250 545 241.68 539.45 L 198.32 510.55 Q 190 505 198.32 499.45 Z" fill="#b2c9ab" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 110px; height: 1px; padding-top: 503px; margin-left: 195px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Wipe /Boot</div></div></div></foreignObject><text x="250" y="507" fill="#46495D" font-family="Helvetica" font-size="12px" text-anchor="middle">Wipe /Boot</text></switch></g><path d="M 450 465 L 450 275.37" fill="none" stroke="#788aa3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 450 270.12 L 453.5 277.12 L 450 275.37 L 446.5 277.12 Z" fill="#788aa3" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><path d="M 415 485 L 441.32 469.96 Q 450 465 458.68 469.96 L 511.32 500.04 Q 520 505 511.32 509.96 L 458.68 540.04 Q 450 545 441.32 540.04 L 388.68 509.96 Q 380 505 388.68 500.04 Z" fill="#b2c9ab" stroke="#788aa3" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 130px; height: 1px; padding-top: 503px; margin-left: 385px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #46495D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Clear old<br />systemd-boot files</div></div></div></foreignObject><text x="450" y="507" fill="#46495D" font-family="Helvetica" font-size="12px" text-anchor="middle">Clear old...</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg> \ No newline at end of file
diff --git a/docs/flowcharts/DiskSelectionProcess.drawio b/docs/flowcharts/DiskSelectionProcess.drawio
new file mode 100644
index 00000000..7c8a1fcb
--- /dev/null
+++ b/docs/flowcharts/DiskSelectionProcess.drawio
@@ -0,0 +1 @@
+<mxfile host="Electron" modified="2021-05-02T19:57:46.193Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.5.1 Chrome/89.0.4389.82 Electron/12.0.1 Safari/537.36" etag="WWkzNgJUxTiFme1f07FW" version="14.5.1" type="device"><diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">7VvZdqM4EP2anHlKDpsxPMZ20sl0kl7S05meNwVkYCIjt5C3+fqRjFgFNnbwksQv3a5CCKG6dWsROdP7o/knAsb+PXYhOtMUd36mD840TVUsi/3HNYtYY5tC4ZHAFYMyxWPwH0zuFNpJ4MKoMJBijGgwLiodHIbQoQUdIATPisOGGBWfOgYelBSPDkCy9ilwqR9rLa2b6W9g4PnJk1XTjq+MQDJYvEnkAxfPcir96kzvE4xp/Gs070PENy/Zl6fbxRO6ezE//fkt+g3+6n3+8fDzPJ7sepNb0lcgMKRbT/3y2bLtaXf4oFrf7ga/rYfB/dW5IV6NLpL9gi7bPiFiQn3s4RCgq0zbI3gSupDPqjIpG3OH8Vgo/4WULgQWwIRipvLpCImr7C3I4m8mKBedRPzFxUQYzAvSQkhDHNI+RpgsV6obpmF3BkwfUYJfYO5K17IuL3V+R4BQTt/T+vZlbzmve80upetxLznSmOggEEWBs5wUECoGKYmcDAtxyHcieoHU8cWAeCf59pWAt8ZqYlyEJ8SBK8bpwnkA8eCq+cwUmsynIR5BtoXsPgIRoMG0uDggnMtLx2UAYj8EhjaAqljkFKCJeNIjRNytNaWHsPMygNPAYXRQRt3MDyh8HIPlDswYExURwy0v4KSyl+t53EypYbj1E9/mo1NHVSS01gFiBYRqQLeZdaeQUDjP7b1soOSqKehG8K0lxFlGXmrCSH6OuAzl9SatpAhNMukDPrFGBUHskwzMhmSgKtVga50NKqEjs8GvCt8/IHbUA2HnbUScCpBp3mx4N50TtftTuRl3H7/9Y4fnmrGS0c6Vi65qCCw0Bp6Y7isOQpobgofDiC2mjMz0qduHLlMC69VoTPl7xCEswKEEXeLj0fOELa23JoAVQhJH1TUYBYjD7AaiKaSBAyrCHECBF3KMMHtCUh3r2COD0GOSmUk/lm7Bkru3H/7UbsP4Z7UQ/6rRXcNiCp9WU7YKhs6ETJdGkTOUgodvQXWbU1OdieE8oDnOZFJKmex3xphcSAhz2yBd4LpqSizxpiDEjFeVjVGZJ8Bq02sNw2y3koprQa5cqIZmv44QxVRGp+QvZskP4rWLu/Ll4f6YtSsza+iQxZg//Tsvn0/VQB1SOnrJvPahywHlSMqBd5nWJ02ztSlXZ13G9XqCaZUXKqGkSlA6VHmwx0j3YXBbPbCG7vbTnVJl8rqf8FY0g5Gm9AZ/yOg7Zfi7zvC1piFtdxm+Kpn9naTwbbt5coCzLjxZDfPhPbm9HGjSrjSb8HmZgvbTig6ELvsXj3nFD/jkI2ZdeupZN3dpQ99jlloJQLul1KJd723dS+tzw2N20srVqHKVuFVh8arucCGRy/K62qbFG7G0ah6VqTuSpftsv0AQ8q7aV5bGBpx6T7nY7olb7xxbLqbKzL1/GtjskOjgNGC1TQO5gxvdFj3Qo2gjVKfvbQX7nab0FWd5pYq98lCwXOQfvjJ4FQS140o6LAk5X5CbrwoUH/Cg5LBdhKHcqP64EQkQR6xU0/cYoAz14AHKOkWjlVSgNv02pemh2WaRh1EoWOQGjHkcinYRd1T5i4G0rRAfaynXtwhGi4jCkQSaU/eg7oxLb+rjO+seaHKFcmzpRFt5gAsiP11Wy0yQ7FrDD4iOJitI1p0/JuAtwNXV6RqHLgGgnA9sdiQkM0Cd3fO8sS8H3ucnqyvxlDPgbcjS7eWd/DMunuCNKvK57W0o2crSnnXTbGIrtwMt1yjZquCXzA70C0/tKN9hY5e21I0js6Xe1inxqbrbnsiblncJERwLkcv13VMwhrH/96q+PPq4Bd3OCKVUwXWMTjNG2VkFpx9dMve+2aN9Uqj+FNJUS3WEYRenqPkUsq16MHnP/LkGgoDwnIN3lUzEdrP3zGTT47/iwtA9F50mZqWKuHYipNazVaMBIRntEBITs78PjXGW/ZWtfvU/</diagram></mxfile> \ No newline at end of file
diff --git a/examples/guided.py b/examples/guided.py
index 266efbd4..2eb5fede 100644
--- a/examples/guided.py
+++ b/examples/guided.py
@@ -33,18 +33,20 @@ def ask_user_questions():
Not until we're satisfied with what we want to install
will we continue with the actual installation steps.
"""
- if not archinstall.arguments.get('keyboard-language', None):
+ if not archinstall.arguments.get('keyboard-layout', None):
while True:
try:
- archinstall.arguments['keyboard-language'] = archinstall.select_language(archinstall.list_keyboard_languages()).strip()
+ archinstall.arguments['keyboard-layout'] = archinstall.select_language(archinstall.list_keyboard_languages()).strip()
break
except archinstall.RequirementError as err:
archinstall.log(err, fg="red")
+
# Before continuing, set the preferred keyboard layout/language in the current terminal.
# This will just help the user with the next following questions.
- if len(archinstall.arguments['keyboard-language']):
- archinstall.set_keyboard_language(archinstall.arguments['keyboard-language'])
+ if len(archinstall.arguments['keyboard-layout']):
+ archinstall.set_keyboard_language(archinstall.arguments['keyboard-layout'])
+
# Set which region to download packages from during the installation
if not archinstall.arguments.get('mirror-region', None):
@@ -68,125 +70,43 @@ def ask_user_questions():
if not archinstall.arguments.get('sys-encoding', None):
archinstall.arguments['sys-encoding'] = 'utf-8'
- # Ask which harddrive/block-device we will install to
- if archinstall.arguments.get('harddrive', None):
- archinstall.arguments['harddrive'] = archinstall.BlockDevice(archinstall.arguments['harddrive'])
+ # Ask which harddrives/block-devices we will install to
+ # and convert them into archinstall.BlockDevice() objects.
+ if archinstall.arguments.get('harddrives', None):
+ archinstall.arguments['harddrives'] = [archinstall.BlockDevice(BlockDev) for BlockDev in archinstall.arguments['harddrives'].split(',')]
else:
- archinstall.arguments['harddrive'] = archinstall.select_disk(archinstall.all_disks())
- if archinstall.arguments['harddrive'] is None:
- archinstall.arguments['target-mount'] = archinstall.storage.get('MOUNT_POINT', '/mnt')
-
- # Perform a quick sanity check on the selected harddrive.
- # 1. Check if it has partitions
- # 3. Check that we support the current partitions
- # 2. If so, ask if we should keep them or wipe everything
- if archinstall.arguments['harddrive'] and archinstall.arguments['harddrive'].has_partitions():
- archinstall.log(f"{archinstall.arguments['harddrive']} contains the following partitions:", fg='yellow')
-
- # We curate a list pf supported partitions
- # and print those that we don't support.
- partition_mountpoints = {}
- for partition in archinstall.arguments['harddrive']:
- try:
- if partition.filesystem_supported():
- archinstall.log(f" {partition}")
- partition_mountpoints[partition] = None
- except archinstall.UnknownFilesystemFormat as err:
- archinstall.log(f" {partition} (Filesystem not supported)", fg='red')
-
- # We then ask what to do with the partitions.
- if (option := archinstall.ask_for_disk_layout()) == 'abort':
- archinstall.log("Safely aborting the installation. No changes to the disk or system has been made.")
- exit(1)
- elif option == 'keep-existing':
- archinstall.arguments['harddrive'].keep_partitions = True
-
- archinstall.log(" ** You will now select which partitions to use by selecting mount points (inside the installation). **")
- archinstall.log(" ** The root would be a simple / and the boot partition /boot (as all paths are relative inside the installation). **")
- mountpoints_set = []
- while True:
- # Select a partition
- # If we provide keys as options, it's better to convert them to list and sort before passing
- mountpoints_list = sorted(list(partition_mountpoints.keys()))
- partition = archinstall.generic_select(mountpoints_list, "Select a partition by number that you want to set a mount-point for (leave blank when done): ")
- if not partition:
- if set(mountpoints_set) & {'/', '/boot'} == {'/', '/boot'}:
- break
-
- continue
-
- # Select a mount-point
- mountpoint = input(f"Enter a mount-point for {partition}: ").strip(' ')
- if len(mountpoint):
-
- # Get a valid & supported filesystem for the partition:
- while 1:
- new_filesystem = input(f"Enter a valid filesystem for {partition} (leave blank for {partition.filesystem}): ").strip(' ')
- if len(new_filesystem) <= 0:
- if partition.encrypted and partition.filesystem == 'crypto_LUKS':
- old_password = archinstall.arguments.get('!encryption-password', None)
- if not old_password:
- old_password = input(f'Enter the old encryption password for {partition}: ')
-
- if autodetected_filesystem := partition.detect_inner_filesystem(old_password):
- new_filesystem = autodetected_filesystem
- else:
- archinstall.log("Could not auto-detect the filesystem inside the encrypted volume.", fg='red')
- archinstall.log("A filesystem must be defined for the unlocked encrypted partition.")
- continue
- break
-
- # Since the potentially new filesystem is new
- # we have to check if we support it. We can do this by formatting /dev/null with the partitions filesystem.
- # There's a nice wrapper for this on the partition object itself that supports a path-override during .format()
- try:
- partition.format(new_filesystem, path='/dev/null', log_formatting=False, allow_formatting=True)
- except archinstall.UnknownFilesystemFormat:
- archinstall.log(f"Selected filesystem is not supported yet. If you want archinstall to support '{new_filesystem}',")
- archinstall.log("please create a issue-ticket suggesting it on github at https://github.com/archlinux/archinstall/issues.")
- archinstall.log("Until then, please enter another supported filesystem.")
- continue
- except archinstall.SysCallError:
- pass # Expected exception since mkfs.<format> can not format /dev/null. But that means our .format() function supported it.
- break
-
- # When we've selected all three criteria,
- # We can safely mark the partition for formatting and where to mount it.
- # TODO: allow_formatting might be redundant since target_mountpoint should only be
- # set if we actually want to format it anyway.
- mountpoints_set.append(mountpoint)
- partition.allow_formatting = True
- partition.target_mountpoint = mountpoint
- # Only overwrite the filesystem definition if we selected one:
- if len(new_filesystem):
- partition.filesystem = new_filesystem
-
- archinstall.log('Using existing partition table reported above.')
- elif option == 'format-all':
- if not archinstall.arguments.get('filesystem', None):
- archinstall.arguments['filesystem'] = archinstall.ask_for_main_filesystem_format()
- archinstall.arguments['harddrive'].keep_partitions = False
- elif archinstall.arguments['harddrive']:
- # If the drive doesn't have any partitions, safely mark the disk with keep_partitions = False
- # and ask the user for a root filesystem.
- if not archinstall.arguments.get('filesystem', None):
- archinstall.arguments['filesystem'] = archinstall.ask_for_main_filesystem_format()
- archinstall.arguments['harddrive'].keep_partitions = False
+ archinstall.arguments['harddrives'] = archinstall.generic_multi_select(archinstall.all_disks(),
+ text="Select one or more harddrives to use and configure (leave blank to skip this step): ",
+ allow_empty=True)
+
+ if archinstall.arguments.get('harddrives', None):
+ archinstall.storage['disk_layouts'] = archinstall.select_disk_layout(archinstall.arguments['harddrives'])
# Get disk encryption password (or skip if blank)
- if archinstall.arguments['harddrive'] and archinstall.arguments.get('!encryption-password', None) is None:
- if passwd := archinstall.get_password(prompt='Enter disk encryption password (leave blank for no encryption): '):
+ if archinstall.arguments['harddrives'] and archinstall.arguments.get('!encryption-password', None) is None:
+ if (passwd := archinstall.get_password(prompt='Enter disk encryption password (leave blank for no encryption): ')):
archinstall.arguments['!encryption-password'] = passwd
- archinstall.arguments['harddrive'].encryption_password = archinstall.arguments['!encryption-password']
+
+ # If no partitions was marked as encrypted (rare), but a password was supplied -
+ # then we need to identify which partitions to encrypt. This will default to / (root) if only
+ # root and boot are detected.
+ if len(list(archinstall.encrypted_partitions(archinstall.storage['disk_layouts']))) == 0:
+ archinstall.storage['disk_layouts'] = archinstall.select_encrypted_partitions(archinstall.storage['disk_layouts'])
+
+ # Ask which boot-loader to use (will only ask if we're in BIOS (non-efi) mode)
archinstall.arguments["bootloader"] = archinstall.ask_for_bootloader()
+
+
# Get the hostname for the machine
if not archinstall.arguments.get('hostname', None):
archinstall.arguments['hostname'] = input('Desired hostname for the installation: ').strip(' ')
+
# Ask for a root password (optional, but triggers requirement for super-user if skipped)
if not archinstall.arguments.get('!root-password', None):
archinstall.arguments['!root-password'] = archinstall.get_password(prompt='Enter root password (Recommendation: leave blank to leave root disabled): ')
+
# Ask for additional users (super-user if root pw was not set)
archinstall.arguments['users'] = {}
archinstall.arguments['superusers'] = {}
@@ -197,12 +117,14 @@ def ask_user_questions():
archinstall.arguments['users'] = users
archinstall.arguments['superusers'] = {**archinstall.arguments['superusers'], **superusers}
+
# Ask for archinstall-specific profiles (such as desktop environments etc)
if not archinstall.arguments.get('profile', None):
archinstall.arguments['profile'] = archinstall.select_profile()
else:
archinstall.arguments['profile'] = Profile(installer=None, path=archinstall.arguments['profile'])
+
# Check the potentially selected profiles preparations to get early checks if some additional questions are needed.
if archinstall.arguments['profile'] and archinstall.arguments['profile'].has_prep_function():
with archinstall.arguments['profile'].load_instructions(namespace=f"{archinstall.arguments['profile'].namespace}.py") as imported:
@@ -210,6 +132,7 @@ def ask_user_questions():
archinstall.log(' * Profile\'s preparation requirements was not fulfilled.', fg='red')
exit(1)
+
# Ask about audio server selection if one is not already set
if not archinstall.arguments.get('audio', None):
# only ask for audio server selection on a desktop profile
@@ -220,11 +143,13 @@ def ask_user_questions():
# we will not try to remove packages post-installation to not have audio, as that may cause multiple issues
archinstall.arguments['audio'] = None
+
# Ask for preferred kernel:
if not archinstall.arguments.get("kernels", None):
kernels = ["linux", "linux-lts", "linux-zen", "linux-hardened"]
archinstall.arguments['kernels'] = archinstall.select_kernel(kernels)
+
# Additional packages (with some light weight error handling for invalid package names)
print("Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.")
print("If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt.")
@@ -261,7 +186,7 @@ def ask_user_questions():
archinstall.log("Hardware time and other post-configuration steps might be required in order for NTP to work. For more information, please check the Arch wiki.", fg="yellow")
-def perform_installation_steps():
+def perform_filesystem_operations():
print()
print('This is your chosen configuration:')
archinstall.log("-- Guided template chosen (with below config) --", level=logging.DEBUG)
@@ -282,8 +207,8 @@ def perform_installation_steps():
We mention the drive one last time, and count from 5 to 0.
"""
- if archinstall.arguments.get('harddrive', None):
- print(f" ! Formatting {archinstall.arguments['harddrive']} in ", end='')
+ if archinstall.arguments.get('harddrives', None):
+ print(f" ! Formatting {archinstall.arguments['harddrives']} in ", end='')
archinstall.do_countdown()
"""
@@ -293,42 +218,10 @@ def perform_installation_steps():
mode = archinstall.GPT
if has_uefi() is False:
mode = archinstall.MBR
- with archinstall.Filesystem(archinstall.arguments['harddrive'], mode) as fs:
- # Wipe the entire drive if the disk flag `keep_partitions`is False.
- if archinstall.arguments['harddrive'].keep_partitions is False:
- fs.use_entire_disk(root_filesystem_type=archinstall.arguments.get('filesystem', 'btrfs'))
-
- # Check if encryption is desired and mark the root partition as encrypted.
- if archinstall.arguments.get('!encryption-password', None):
- root_partition = fs.find_partition('/')
- root_partition.encrypted = True
-
- # After the disk is ready, iterate the partitions and check
- # which ones are safe to format, and format those.
- for partition in archinstall.arguments['harddrive']:
- if partition.safe_to_format():
- # Partition might be marked as encrypted due to the filesystem type crypt_LUKS
- # But we might have omitted the encryption password question to skip encryption.
- # In which case partition.encrypted will be true, but passwd will be false.
- if partition.encrypted and (passwd := archinstall.arguments.get('!encryption-password', None)):
- partition.encrypt(password=passwd)
- else:
- partition.format()
- else:
- archinstall.log(f"Did not format {partition} because .safe_to_format() returned False or .allow_formatting was False.", level=logging.DEBUG)
-
- if archinstall.arguments.get('!encryption-password', None):
- # First encrypt and unlock, then format the desired partition inside the encrypted part.
- # archinstall.luks2() encrypts the partition when entering the with context manager, and
- # unlocks the drive so that it can be used as a normal block-device within archinstall.
- with archinstall.luks2(fs.find_partition('/'), 'luksloop', archinstall.arguments.get('!encryption-password', None)) as unlocked_device:
- unlocked_device.format(fs.find_partition('/').filesystem)
- unlocked_device.mount(archinstall.storage.get('MOUNT_POINT', '/mnt'))
- else:
- fs.find_partition('/').mount(archinstall.storage.get('MOUNT_POINT', '/mnt'))
- if has_uefi():
- fs.find_partition('/boot').mount(archinstall.storage.get('MOUNT_POINT', '/mnt') + '/boot')
+ for drive in archinstall.arguments['harddrives']:
+ with archinstall.Filesystem(drive, mode) as fs:
+ fs.load_layout(archinstall.storage['disk_layouts'][drive])
perform_installation(archinstall.storage.get('MOUNT_POINT', '/mnt'))
@@ -460,6 +353,7 @@ else:
archinstall.arguments['profile'] = archinstall.Profile(None, archinstall.arguments.get('profile', None))
else:
archinstall.arguments['profile'] = None
+
if archinstall.arguments.get('mirror-region', None) is not None:
if type(archinstall.arguments.get('mirror-region', None)) is dict:
archinstall.arguments['mirror-region'] = archinstall.arguments.get('mirror-region', None)
@@ -468,7 +362,9 @@ else:
archinstall.arguments['mirror-region'] = {selected_region: archinstall.list_mirrors()[selected_region]}
archinstall.arguments['sys-language'] = archinstall.arguments.get('sys-language', 'en_US')
archinstall.arguments['sys-encoding'] = archinstall.arguments.get('sys-encoding', 'utf-8')
+
if archinstall.arguments.get('gfx_driver', None) is not None:
archinstall.storage['gfx_driver_packages'] = AVAILABLE_GFX_DRIVERS.get(archinstall.arguments.get('gfx_driver', None), None)
-perform_installation_steps()
+perform_filesystem_operations()
+perform_installation(archinstall.arguments.get('target-mountpoint', None))