index : archinstall32 | |
Archlinux32 installer | gitolite user |
summaryrefslogtreecommitdiff |
-rw-r--r-- | README.md | 47 | ||||
-rw-r--r-- | archinstall.py | 55 | ||||
-rw-r--r-- | deployments/workstation_unattended.json | 6 |
@@ -1,22 +1,44 @@ # archinstall Just a bare bone automated [Arch](https://wiki.archlinux.org/index.php/Arch_Linux) install with network deployment instructions based on MAC-address. -# Autorun on Arch Live CD +# Autorun on Arch Live CD (Unattended install) # cd ~/archlive # echo -e "git\npython-psutil" >> packages.both - # echo "cd /root" >> ./airootfs/root/customize_airootfs.sh - # echo "git clone https://github.com/Torxed/archinstall.git" >> ./airootfs/root/customize_airootfs.sh - # echo "chmod +x ~/archinstall/archinstall.py" >> ./airootfs/root/customize_airootfs.sh + # cat <<EOF >> ./airootfs/root/customize_airootfs.sh + cd /root + git clone https://github.com/Torxed/archinstall.git + chmod +x ~/archinstall/archinstall.py + EOF # mkdir ./airootfs/etc/skel - # echo '[[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] && sh -c ~/archinstall/archinstall.py' >> ./airootfs/etc/skel/.zprofile + # echo '[[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] && sh -c "~/archinstall/archinstall.py"' >> ./airootfs/etc/skel/.zprofile # rm -v work*; ./build.sh -v > Note: `~/archlive` might be different on your system, see [ArchISO#Setup](https://wiki.archlinux.org/index.php/archiso#Setup) for more info. -Whenever this live-cd boots, from here on now - it'll run `archinstall.py` with the `net-deploy` branch. +Whenever this live-cd boots, from here on now - it'll run `archinstall.py` and attempt to unattendely install a default Arch Linux base OS with `base base-devel` as packages. +Or - if successfull - a profile was found at [/deployments](https://github.com/Torxed/archinstall/tree/master/deployments) for the machine being installed (MAC-address based lookup). -> CAUTION: If no parameters are given, it will devour the first disk in your system (/dev/sda, /dev/nvme0n1 etc). +> CAUTION: If no parameters are given, **it will devour the first disk in your system** (/dev/sda, /dev/nvme0n1 etc). + +## Autorun on Arch Live CD (User specified profile) + +Everything in the steps above are the same, except for one line that needs to change: + + # echo '[[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] && sh -c "~/archinstall/archinstall.py --no-default"' >> ./airootfs/etc/skel/.zprofile + +This will cause the script to halt and ask for a profile to install before proceeding. +When asked, enter `workstation` for instance - to install based on the workstation template. + +> CAUTION: Even if `--no-default` is given, if a MAC-address matches under `/deployments`, that profile will forcefully be installed. + +## Autorun on Arch Live CD (With custom webserver for deployment profiles) + +Again, one line differs from the unattended install, change the following line: + + # echo '[[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] && sh -c "~/archinstall/archinstall.py --profiles-path=http://example.com/profiles"' >> ./airootfs/etc/skel/.zprofile + +This will cause the script to look at `http://example.com/profiles/<mac>.json` for instructions. # Manually run it on a booted Live CD @@ -27,7 +49,7 @@ Whenever this live-cd boots, from here on now - it'll run `archinstall.py` with # umount -R /mnt; cryptsetup close /dev/mapper/luksdev # python3 ./archinstall/archinstall.py -> Note: This assumes `--post=stay` is set to avoid instant reboot at the end. +> Note: This assumes `--post=stay` is set to avoid instant reboot at the end or if during any time a user pressed `Ctrl-C` and aborted the installation. # Some parameters you can give it @@ -63,7 +85,14 @@ Whenever this live-cd boots, from here on now - it'll run `archinstall.py` with --post=reboot (Default) After a successful install, reboots into the system. Use --post=stay to not reboot. -net-deployment structs support all these and more, among those, custom arguments with string formatting. + --no-default + This parameter causes the installation script to halt if no MAC-based profile was found under /deployments + + --profiles-path=https://example.com/profiles + Changes the default path the script looks for deployment profiles. + The default path is 'https://raw.githubusercontent.com/Torxed/archinstall/master/deployments' + +Deployment profile structs support all the above parameters and more, for instance, custom arguments with string formatting. See [deployments/workstation.json](https://github.com/Torxed/archinstall/blob/net-deploy/deployments/workstation.json) for examples. ## End note diff --git a/archinstall.py b/archinstall.py index 6ab216ae..42d3ff65 100644 --- a/archinstall.py +++ b/archinstall.py @@ -14,7 +14,9 @@ from time import sleep rootdir_pattern = re.compile('^.*?/devices') harddrives = oDict() -deploy_target = 'https://raw.githubusercontent.com/Torxed/archinstall/net-deploy/deployments' +## == Profiles Path can be set via --profiles-path=/path +## This just sets the default path if the parameter is omitted. +profiles_path = 'https://raw.githubusercontent.com/Torxed/archinstall/master/deployments' args = {} positionals = [] @@ -148,7 +150,7 @@ def grab_url_data(path): def get_instructions(target): instructions = {} try: - instructions = grab_url_data('{}/{}.json'.format(deploy_target, target)) + instructions = grab_url_data('{}/{}.json'.format(args['profiles-path'], target)) except urllib.error.HTTPError: print('[N] No instructions found called: {}'.format(target)) return instructions @@ -197,6 +199,8 @@ if __name__ == '__main__': 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 'no-default' in args: args['no-default'] = False + if not 'profiles-path' in args: args['profiles-path'] = profiles_path ## == If we got networking, # Try fetching instructions for this box and execute them. @@ -227,6 +231,37 @@ if __name__ == '__main__': else: print('[N] No gateway - No net deploy') + first = True + while args['no-default'] 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 --no-default was given and a default installation is prohibited)') + first = False + + if 'args' in instructions: + ## == Recursively fetch instructions if "include" is found under {args: ...} + while 'include' in instructions['args']: + includes = instructions['args']['include'] + print('[!] Importing net-deploy target: {}'.format(includes)) + del(instructions['args']['include']) + if type(includes) in (dict, list): + for include in includes: + instructions = merge_dicts(instructions, get_instructions(include), before=True) + else: + instructions = merge_dicts(instructions, get_instructions(includes), before=True) + + ## Update arguments if we found any + for key, val in instructions['args'].items(): + args[key] = val + + ## TODO: Reuseable code, there's to many get_instructions, merge_dictgs and args updating going on. + ## Update arguments if we found any + for key, val in instructions['args'].items(): + args[key] = val + if args['password'] == '<STDIN>': args['password'] = input('Enter a disk (and root) password: ') print(args) @@ -313,6 +348,9 @@ if __name__ == '__main__': if len(opts): if 'pass-args' in opts or 'format' in opts: command = command.format(**args) + ## FIXME: Instead of deleting the two options + ## in order to mute command output further down, + ## check for a 'debug' flag per command and delete these two if 'pass-args' in opts: del(opts['pass-args']) elif 'format' in opts: @@ -365,7 +403,6 @@ if __name__ == '__main__': mkinit.write('HOOKS=(base udev autodetect modconf block encrypt filesystems keyboard fsck)\n') o = run('arch-chroot /mnt mkinitcpio -p linux') o = run('arch-chroot /mnt bootctl --path=/boot install') - print('bootctl:', o) with open('/mnt/boot/loader/loader.conf', 'w') as loader: loader.write('default arch\n') @@ -393,7 +430,17 @@ if __name__ == '__main__': raw_command = command opts = conf[title][command] if type(conf[title][command]) in (dict, oDict) else {} if len(opts): - print('[-] Options: {}'.format(opts)) + if 'pass-args' in opts or 'format' in opts: + command = command.format(**args) + ## FIXME: Instead of deleting the two options + ## in order to mute command output further down, + ## check for a 'debug' flag per command and delete these two + if 'pass-args' in opts: + del(opts['pass-args']) + elif 'format' in opts: + del(opts['format']) + else: + print('[-] Options: {}'.format(opts)) if 'pass-args' in opts and opts['pass-args']: command = command.format(**args) diff --git a/deployments/workstation_unattended.json b/deployments/workstation_unattended.json new file mode 100644 index 00000000..089f7f40 --- /dev/null +++ b/deployments/workstation_unattended.json @@ -0,0 +1,6 @@ +{
+ "args" : {
+ "password" : "0000",
+ "include" : "workstation"
+ }
+}
|