Added two new features, --no-default parameter that halts the script instead of installing a default arch machine if no MAC-address profile was found for unattended installs. The second feature is the --profiles parameter that enables custom web-server paths when looking for profiles to deploy

This commit is contained in:
Lord Anton Hvornum 2018-05-30 20:14:37 +02:00
parent 3f827eb56f
commit 96b8669b0a
3 changed files with 95 additions and 13 deletions

View File

@ -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

View File

@ -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)

View File

@ -0,0 +1,6 @@
{
"args" : {
"password" : "0000",
"include" : "workstation"
}
}