Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLord Anton Hvornum <anton.feeds@gmail.com>2018-05-30 20:14:37 +0200
committerLord Anton Hvornum <anton.feeds@gmail.com>2018-05-30 20:14:37 +0200
commit96b8669b0af43b2bf9393e9c2792fd60e36b87ad (patch)
treea03977945a22169247c26e18379cf0d88d46d7c3
parent3f827eb56fbc36b0b11f583486b52468df7a8651 (diff)
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
-rw-r--r--README.md47
-rw-r--r--archinstall.py55
-rw-r--r--deployments/workstation_unattended.json6
3 files changed, 95 insertions, 13 deletions
diff --git a/README.md b/README.md
index 3f7d24ad..2c9cf019 100644
--- a/README.md
+++ b/README.md
@@ -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"
+ }
+}