Merge pull request #34 from Torxed/lab

Lab
This commit is contained in:
Anton Hvornum 2019-11-29 13:56:33 +00:00 committed by GitHub
commit 8dd30088cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 106 additions and 73 deletions

View File

@ -53,6 +53,9 @@ More options for the built ISO:
--drive=</dev/sdX>
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=<name>

View File

@ -365,6 +365,9 @@ class sys_command():#Thread):
else:
self.exit_code = 0
if 'ignore_errors' 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)
@ -550,7 +553,11 @@ def disk_info(drive, *positionals, **kwargs):
def cleanup_args():
for key in args:
if args[key] == '<STDIN>': args[key] = input(f'Enter a value for {key}: ')
if args[key] == '<STDIN>':
if not args['unattended']:
args[key] = input(f'Enter a value for {key}: ')
else:
args[key] = random_string(32)
elif args[key] == '<RND_STR>': args[key] = random_string(32)
elif args[key] == '<YUBIKEY>':
args[key] = gen_yubikey_password()
@ -693,29 +700,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) not 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
@ -770,7 +785,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'
@ -779,7 +794,8 @@ def setup_args_defaults(args, interactive=True):
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 <STDIN> 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
@ -788,33 +804,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:
@ -1050,6 +1039,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
@ -1102,7 +1092,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 +1114,16 @@ def run_post_install_steps(*positionals, **kwargs):
## "<hostname> 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@<hostname> ~]#
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)
@ -1147,33 +1137,62 @@ 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 and not args['minimal']:
instructions = load_automatic_instructions()
if 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']))
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['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:
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)
cleanup_args()
## If no drive was found in args, select one.
if not 'drive' in args:
if len(harddrives):
drives = sorted(list(harddrives.keys()))
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): ')
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 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)

View File

@ -2,5 +2,14 @@
"args" : {
"password" : "<STDIN>",
"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}
}
}
}