From 4652a3eee483fa4e565a48c2b6ed531b8c46ffc4 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 29 Nov 2019 12:47:17 +0100 Subject: Fixing #24 and #12 --- archinstall.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/archinstall.py b/archinstall.py index a7d46211..5dd57667 100644 --- a/archinstall.py +++ b/archinstall.py @@ -365,6 +365,9 @@ class sys_command():#Thread): else: self.exit_code = 0 + if 'ignore_error' in self.kwargs: + self.exit_code = 0 + if self.exit_code != 0: log(f"{self.cmd[0]} did not exit gracefully, exit code {self.exit_code}.", origin='spawn', level=3) log(self.trace_log.decode('UTF-8'), origin='spawn', level=3) @@ -1102,7 +1105,7 @@ def run_post_install_steps(*positionals, **kwargs): o = simple_command("cd /mnt; mount -t proc /proc proc") o = simple_command("cd /mnt; mount --make-rslave --rbind /sys sys") o = simple_command("cd /mnt; mount --make-rslave --rbind /dev dev") - o = simple_command('chroot /mnt /bin/bash -c "{c}"'.format(c=command), opts=opts) + o = simple_command('chroot /mnt /bin/bash -c "{c}"'.format(c=command), events=opts) o = simple_command("cd /mnt; umount -R dev") o = simple_command("cd /mnt; umount -R sys") o = simple_command("cd /mnt; umount -R proc") @@ -1124,16 +1127,16 @@ def run_post_install_steps(*positionals, **kwargs): ## " login" followed by "Passwodd" in case it's been set in a previous step.. usually this shouldn't be nessecary ## since we set the password as the last step. And then the command itself which will be executed by looking for: ## [root@ ~]# - o = b''.join(sys_command('/usr/bin/systemd-nspawn -D /mnt -b --machine temporary', opts={'triggers' : { - bytes(f'login:', 'UTF-8') : b'root\n', - #b'Password:' : bytes(args['password']+'\n', 'UTF-8'), - bytes(f'[root@{args["hostname"]} ~]#', 'UTF-8') : bytes(command+'\n', 'UTF-8'), - }, **opts})) + o = b''.join(sys_command('/usr/bin/systemd-nspawn -D /mnt -b --machine temporary', events={ + bytes(f'login:', 'UTF-8') : b'root\n', + #b'Password:' : bytes(args['password']+'\n', 'UTF-8'), + bytes(f'[root@{args["hostname"]} ~]#', 'UTF-8') : bytes(command+'\n', 'UTF-8'), + }, **opts)) ## Not needed anymore: And cleanup after out selves.. Don't want to leave any residue.. # os.remove('/mnt/etc/systemd/system/console-getty.service.d/override.conf') else: - o = b''.join(sys_command('/usr/bin/systemd-nspawn -D /mnt --machine temporary {c}'.format(c=command), opts=opts)) + o = b''.join(sys_command('/usr/bin/systemd-nspawn -D /mnt --machine temporary {c}'.format(c=command), **opts)) if type(conf[title][raw_command]) == bytes and len(conf[title][raw_command]) and not conf[title][raw_command] in o: print('[W] Post install command failed: {}'.format(o.decode('UTF-8'))) #print(o) -- cgit v1.2.3-54-g00ecf From c8f31bd344187d2d1b6c4249c6ce6b9ef28c9b63 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 29 Nov 2019 12:55:38 +0100 Subject: Fixing #18 and #12 --- archinstall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall.py b/archinstall.py index 5dd57667..268787ec 100644 --- a/archinstall.py +++ b/archinstall.py @@ -365,7 +365,7 @@ class sys_command():#Thread): else: self.exit_code = 0 - if 'ignore_error' in self.kwargs: + if 'ignore_errors' in self.kwargs: self.exit_code = 0 if self.exit_code != 0: -- cgit v1.2.3-54-g00ecf From d89bacdf9f187c1737439ccdef53254fc84749c5 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 29 Nov 2019 12:58:58 +0100 Subject: Adding tests to default, will change this to a test template later --- deployments/default.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/deployments/default.json b/deployments/default.json index 5deded3e..95870c6c 100644 --- a/deployments/default.json +++ b/deployments/default.json @@ -2,5 +2,14 @@ "args" : { "password" : "", "post" : "reboot" + }, + "post" : { + "test exit codes" : { + "exit 1" : {"ignore_errors" : true}, + "echo 'test1'; read moo; echo 'test2'; read mooo" : {"events" : { + "test1" : "something", + "test2" : "something" + }, "boot" : true} + } } } -- cgit v1.2.3-54-g00ecf From a83b21ec016e4302fb1e767e338527865beefba3 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 29 Nov 2019 13:21:39 +0100 Subject: Fixing offline deployment a bit --- archinstall.py | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/archinstall.py b/archinstall.py index 268787ec..2a51a35e 100644 --- a/archinstall.py +++ b/archinstall.py @@ -696,29 +696,37 @@ def get_application_instructions(target): return instructions +def get_local_instructions(target): + instructions = oDict() + local_path = './deployments' if os.path.isfile('./archinstall.py') else './archinstall/deployments' # Dangerous assumption + if os.path.isfile(f'{local_path}/{target}.json'): + with open(f'{local_path}/{target}.json', 'r') as fh: + instructions = fh.read() + + print('[N] Found local instructions called: {}'.format(target)) + else: + print('[N] No instructions found called: {}'.format(target)) + return instructions + def get_instructions(target, *positionals, **kwargs): instructions = oDict() - try: - instructions = grab_url_data('{}/{}.json'.format(args['profiles-path'], target)).decode('UTF-8') - print('[N] Found net-deploy instructions called: {}'.format(target)) - except urllib.error.HTTPError: - print('[N] Could not find remote instructions. Trying local instructions under ./deployments') - local_path = './deployments' if os.path.isfile('./archinstall.py') else './archinstall/deployments' # Dangerous assumption - if os.path.isfile(f'{local_path}/{target}.json'): - with open(f'{local_path}/{target}.json', 'r') as fh: - instructions = fh.read() + if get_default_gateway_linux(): + try: + instructions = grab_url_data('{}/{}.json'.format(args['profiles-path'], target)).decode('UTF-8') + print('[N] Found net-deploy instructions called: {}'.format(target)) + except urllib.error.HTTPError: + print('[N] Could not find remote instructions. Trying local instructions under ./deployments') + isntructions = get_local_instructions(target, *positionals) + else: + isntructions = get_local_instructions(target, *positionals) - print('[N] Found local instructions called: {}'.format(target)) - else: - print('[N] No instructions found called: {}'.format(target)) - return instructions - - try: - instructions = json.loads(instructions, object_pairs_hook=oDict) - except: - print('[E] JSON syntax error in {}'.format('{}/{}.json'.format(args['profiles-path'], target))) - traceback.print_exc() - exit(1) + if type(instructions) in (dict, oDict): + try: + instructions = json.loads(instructions, object_pairs_hook=oDict) + except: + print('[E] JSON syntax error in {}'.format('{}/{}.json'.format(args['profiles-path'], target))) + traceback.print_exc() + exit(1) return instructions -- cgit v1.2.3-54-g00ecf From 62ecb7ccfa43150d756961c1e5ac45a9dd805cfe Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 29 Nov 2019 13:26:35 +0100 Subject: Fixed instruction loading --- archinstall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall.py b/archinstall.py index 2a51a35e..9d96d439 100644 --- a/archinstall.py +++ b/archinstall.py @@ -720,7 +720,7 @@ def get_instructions(target, *positionals, **kwargs): else: isntructions = get_local_instructions(target, *positionals) - if type(instructions) in (dict, oDict): + if type(instructions) not in (dict, oDict,): try: instructions = json.loads(instructions, object_pairs_hook=oDict) except: -- cgit v1.2.3-54-g00ecf From d84f4e2bf9bbba01cbab0e9c569af4e054b2d73a Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 29 Nov 2019 13:32:48 +0100 Subject: Fixed an issue where --profile didn't override the network deploy. --- archinstall.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/archinstall.py b/archinstall.py index 9d96d439..ce76ccba 100644 --- a/archinstall.py +++ b/archinstall.py @@ -1158,26 +1158,27 @@ if __name__ == '__main__': args = setup_args_defaults(args) ## == If we got networking, - # Try fetching instructions for this box and execute them. - instructions = load_automatic_instructions() + # Try fetching instructions for this box unless a specific profile was given, and execute them. + if args['profile'] is None: + instructions = load_automatic_instructions() - if args['profile'] and not args['default']: + elif args['profile'] and not args['default']: instructions = get_instructions(args['profile']) if len(instructions) <= 0: print('[E] No instructions by the name of {} was found.'.format(args['profile'])) print(' Installation won\'t continue until a valid profile is given.') print(' (this is because --profile was given and a --default is not given)') exit(1) - else: - first = True - while not args['default'] and not args['profile'] and len(instructions) <= 0: - profile = input('What template do you want to install: ') - instructions = get_instructions(profile) - if first and len(instructions) <= 0: - print('[E] No instructions by the name of {} was found.'.format(profile)) - print(' Installation won\'t continue until a valid profile is given.') - print(' (this is because --default is not instructed and no --profile given)') - first = False + + first = True + while not args['default'] and not args['profile'] and len(instructions) <= 0: + profile = input('What template do you want to install: ') + instructions = get_instructions(profile) + if first and len(instructions) <= 0: + print('[E] No instructions by the name of {} was found.'.format(profile)) + print(' Installation won\'t continue until a valid profile is given.') + print(' (this is because --default is not instructed and no --profile given)') + first = False # TODO: Might not need to return anything here, passed by reference? instructions = merge_in_includes(instructions) -- cgit v1.2.3-54-g00ecf From 670afabf9f3d2a3fce43ceb4d134af89b42442ba Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 29 Nov 2019 13:39:29 +0100 Subject: Moved disk selection to after profile fetching. Should not need to select a disk if it's specified in the template --- archinstall.py | 56 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/archinstall.py b/archinstall.py index ce76ccba..06c6aa63 100644 --- a/archinstall.py +++ b/archinstall.py @@ -799,33 +799,6 @@ def setup_args_defaults(args, interactive=True): if not 'aur-support' in args: args['aur-support'] = True # Support adds yay (https://github.com/Jguer/yay) in installation steps. if not 'ignore-rerun' in args: args['ignore-rerun'] = False if not 'phone-home' in args: args['phone-home'] = False - if not 'drive' in args: - if interactive and len(harddrives): - drives = sorted(list(harddrives.keys())) - if len(drives) > 1 and 'force' not in args and ('default' in args and 'first-drive' not in args): - for index, drive in enumerate(drives): - print(f"{index}: {drive} ({harddrives[drive]['size'], harddrives[drive]['fstype'], harddrives[drive]['label']})") - drive = input('Select one of the above disks (by number): ') - if not drive.isdigit(): - raise KeyError("Multiple disks found, --drive=/dev/X not specified (or --force/--first-drive)") - drives = [drives[int(drive)]] # Make sure only the selected drive is in the list of options - args['drive'] = drives[0] # First drive found - else: - args['drive'] = None - rerun = args['ignore-rerun'] - - if args['drive'] and args['drive'][0] != '/': - ## Remap the selected UUID to the device to be formatted. - drive = get_drive_from_uuid(args['drive']) - if not drive: - print(f'[N] Could not map UUID "{args["drive"]}" to a device. Trying to match via PARTUUID instead!') - - drive = get_drive_from_part_uuid(args['drive']) - if not drive: - print(f'[E] Could not map UUID "{args["drive"]}" to a device. Aborting!') - exit(1) - - args['drive'] = drive # Setup locales if we didn't get one. if not 'country' in args: @@ -1061,6 +1034,7 @@ def run_post_install_steps(*positionals, **kwargs): update_git(conf['git-branch']) del(conf['git-branch']) + rerun = args['ignore-rerun'] for title in conf: if args['rerun'] and args['rerun'] != title and not rerun: continue @@ -1184,6 +1158,34 @@ if __name__ == '__main__': instructions = merge_in_includes(instructions) cleanup_args() + ## If no drive was found in args, select one. + if not 'drive' in args: + if interactive and len(harddrives): + drives = sorted(list(harddrives.keys())) + if len(drives) > 1 and 'force' not in args and ('default' in args and 'first-drive' not in args): + for index, drive in enumerate(drives): + print(f"{index}: {drive} ({harddrives[drive]['size'], harddrives[drive]['fstype'], harddrives[drive]['label']})") + drive = input('Select one of the above disks (by number): ') + if not drive.isdigit(): + raise KeyError("Multiple disks found, --drive=/dev/X not specified (or --force/--first-drive)") + drives = [drives[int(drive)]] # Make sure only the selected drive is in the list of options + args['drive'] = drives[0] # First drive found + else: + args['drive'] = None + + if args['drive'] and args['drive'][0] != '/': + ## Remap the selected UUID to the device to be formatted. + drive = get_drive_from_uuid(args['drive']) + if not drive: + print(f'[N] Could not map UUID "{args["drive"]}" to a device. Trying to match via PARTUUID instead!') + + drive = get_drive_from_part_uuid(args['drive']) + if not drive: + print(f'[E] Could not map UUID "{args["drive"]}" to a device. Aborting!') + exit(1) + + args['drive'] = drive + print(json.dumps(args, indent=4)) if args['default'] and not 'force' in args: if(input('Are these settings OK? (No return beyond this point) N/y: ').lower() != 'y'): -- cgit v1.2.3-54-g00ecf From 53669d63c9e01c2ed6e47d718b6983442e57fb6d Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 29 Nov 2019 13:40:47 +0100 Subject: Removing interactive since it's redundant --- archinstall.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archinstall.py b/archinstall.py index 06c6aa63..c0018858 100644 --- a/archinstall.py +++ b/archinstall.py @@ -781,7 +781,7 @@ def guess_country(ip, *positionals, **kwargs): log(f'Missing GeoIP database: {GEOIP_DB}', origin='guess_country', level=LOG_LEVELS.ERROR) return result -def setup_args_defaults(args, interactive=True): +def setup_args_defaults(args, *positionals, **kwargs): if not 'size' in args: args['size'] = '100%' if not 'mirrors' in args: args['mirrors'] = True if not 'start' in args: args['start'] = '513MiB' @@ -1160,7 +1160,7 @@ if __name__ == '__main__': ## If no drive was found in args, select one. if not 'drive' in args: - if interactive and len(harddrives): + if len(harddrives): drives = sorted(list(harddrives.keys())) if len(drives) > 1 and 'force' not in args and ('default' in args and 'first-drive' not in args): for index, drive in enumerate(drives): -- cgit v1.2.3-54-g00ecf From ef117744cbebc98ba6aed31e4b47de0e73ae1349 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 29 Nov 2019 13:48:51 +0100 Subject: Fixing #29. --default is now renamed to --minimal. --unattended has been added to skip steps (equal to --force). Will add a --default shortcut which will point towards the profile 'default.json' --- archinstall.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/archinstall.py b/archinstall.py index c0018858..d514b283 100644 --- a/archinstall.py +++ b/archinstall.py @@ -553,7 +553,11 @@ def disk_info(drive, *positionals, **kwargs): def cleanup_args(): for key in args: - if args[key] == '': args[key] = input(f'Enter a value for {key}: ') + if args[key] == '': + if not args['unattended']: + args[key] = input(f'Enter a value for {key}: ') + else: + args[key] = random_string(32) elif args[key] == '': args[key] = random_string(32) elif args[key] == '': args[key] = gen_yubikey_password() @@ -790,7 +794,8 @@ def setup_args_defaults(args, *positionals, **kwargs): if not 'packages' in args: args['packages'] = '' # extra packages other than default if not 'post' in args: args['post'] = 'reboot' if not 'password' in args: args['password'] = '0000' # Default disk passord, can be or a fixed string - if not 'default' in args: args['default'] = False + if not 'minimal' in args: args['minimal'] = False + if not 'unattended' in args: args['unattended'] = False if not 'profile' in args: args['profile'] = None if not 'skip-encrypt' in args: args['skip-encrypt'] = False if not 'profiles-path' in args: args['profiles-path'] = profiles_path @@ -1136,7 +1141,7 @@ if __name__ == '__main__': if args['profile'] is None: instructions = load_automatic_instructions() - elif args['profile'] and not args['default']: + elif args['profile'] and not args['minimal']: instructions = get_instructions(args['profile']) if len(instructions) <= 0: print('[E] No instructions by the name of {} was found.'.format(args['profile'])) @@ -1145,7 +1150,7 @@ if __name__ == '__main__': exit(1) first = True - while not args['default'] and not args['profile'] and len(instructions) <= 0: + while not args['minimal'] and not args['profile'] and len(instructions) <= 0: profile = input('What template do you want to install: ') instructions = get_instructions(profile) if first and len(instructions) <= 0: @@ -1162,7 +1167,7 @@ if __name__ == '__main__': if not 'drive' in args: if len(harddrives): drives = sorted(list(harddrives.keys())) - if len(drives) > 1 and 'force' not in args and ('default' in args and 'first-drive' not in args): + if len(drives) > 1 and 'force' not in args and not 'unattended' in args and ('minimal' in args and 'first-drive' not in args): for index, drive in enumerate(drives): print(f"{index}: {drive} ({harddrives[drive]['size'], harddrives[drive]['fstype'], harddrives[drive]['label']})") drive = input('Select one of the above disks (by number): ') @@ -1187,7 +1192,7 @@ if __name__ == '__main__': args['drive'] = drive print(json.dumps(args, indent=4)) - if args['default'] and not 'force' in args: + if args['minimal'] and not 'force' in args and not 'unattended' in args: if(input('Are these settings OK? (No return beyond this point) N/y: ').lower() != 'y'): exit(1) -- cgit v1.2.3-54-g00ecf From 30e45d57a1829196fdbf1ba8eaaafa16f8e9996a Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 29 Nov 2019 13:54:17 +0100 Subject: Added --minimal to skip profile lookups completely. #29 --- archinstall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archinstall.py b/archinstall.py index d514b283..8437575c 100644 --- a/archinstall.py +++ b/archinstall.py @@ -1138,7 +1138,7 @@ if __name__ == '__main__': ## == If we got networking, # Try fetching instructions for this box unless a specific profile was given, and execute them. - if args['profile'] is None: + if args['profile'] is None and not args['minimal']: instructions = load_automatic_instructions() elif args['profile'] and not args['minimal']: -- cgit v1.2.3-54-g00ecf From a66c2436ee67532e7625d88e5e7f293364e39fe0 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 29 Nov 2019 13:56:42 +0100 Subject: Added debug --- archinstall.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/archinstall.py b/archinstall.py index 8437575c..ec16e7ce 100644 --- a/archinstall.py +++ b/archinstall.py @@ -811,6 +811,8 @@ def setup_args_defaults(args, *positionals, **kwargs): if get_default_gateway_linux(): ip = get_external_ip() country = guess_country(ip) + print(ip) + print(country) args['country'] = 'all' if not country else country if not 'localtime' in args: args['localtime'] = 'Europe/Stockholm' if args['country'] == 'SE' else 'GMT+0' # TODO: Arbitrary for now -- cgit v1.2.3-54-g00ecf From 405117565054db73dde6ffd519873060a8564eaa Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 29 Nov 2019 14:55:13 +0100 Subject: Updated README to reflect parameter changes. --- README.md | 9 +++++++-- archinstall.py | 2 -- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2682979e..39a40bd8 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,9 @@ More options for the built ISO: --drive= Which drive to install arch on, if absent, the first disk under /dev/ is used + + --minimal + Starts a minimal installation, and skips looking for profiles. --size=100% (Default) Sets the size of the root filesystem (btrfs) @@ -75,8 +78,10 @@ More options for the built ISO: --hostname=Arcinstall (Default) Sets the hostname of the box - --country=SE (Default) + --country=all (Default) Default mirror allocation for fetching packages. + If network is found, archinstall will try to attempt and guess which country the + install originates from, basing it off GeoIP off your public IP (uses https://hvornu.se/ip/ for lookups) --packages='' (Default) Which additional packages to install, defaults to none. @@ -88,7 +93,7 @@ More options for the built ISO: --post=reboot (Default) After a successful install, reboots into the system. Use --post=stay to not reboot. - --default + --unattended This parameter causes the installation script to install arch unattended on the first disk --profile= diff --git a/archinstall.py b/archinstall.py index ec16e7ce..8437575c 100644 --- a/archinstall.py +++ b/archinstall.py @@ -811,8 +811,6 @@ def setup_args_defaults(args, *positionals, **kwargs): if get_default_gateway_linux(): ip = get_external_ip() country = guess_country(ip) - print(ip) - print(country) args['country'] = 'all' if not country else country if not 'localtime' in args: args['localtime'] = 'Europe/Stockholm' if args['country'] == 'SE' else 'GMT+0' # TODO: Arbitrary for now -- cgit v1.2.3-54-g00ecf