mirror of
https://github.com/BlackLight/vmctl.git
synced 2024-05-23 15:12:32 +02:00
Major refactor
- Project renamed to `vmctl` - Using a single `vmctl` executable script (it makes the installation process trivial) - Better documentation for `vmctl install`
This commit is contained in:
parent
650356f104
commit
9daa3907a9
5 changed files with 208 additions and 173 deletions
73
README.md
73
README.md
|
@ -1,7 +1,7 @@
|
||||||
# qemu-arch-linux-automation
|
# vmctl
|
||||||
|
|
||||||
Scripts and playbooks to automate the installation, configuration and
|
`vmctl` is a script that automates the installation, provisioning and
|
||||||
management of Arch Linux VMs in qemu.
|
management of Arch Linux virtual machines.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
|
@ -11,33 +11,59 @@ management of Arch Linux VMs in qemu.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
- Add the path to the `src` directory to your `PATH` environment variable
|
Just copy the `vmctl` script anywhere in your `PATH`, or clone the repository
|
||||||
- Create a symbolic link to `src/` directory to your `PATH` environment variable
|
and create a symbolic link to `vmctl` in your `PATH`.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Install an Arch Linux virtual machine
|
### Install an Arch Linux virtual machine
|
||||||
|
|
||||||
|
The `vmctl install` command can be used to create an Arch Linux virtual machine
|
||||||
|
on the fly, wherever you are. It does the following:
|
||||||
|
|
||||||
|
- It downloads the latest Arch Linux installer ISO image if none was downloaded
|
||||||
|
or a new one is available.
|
||||||
|
- It creates a `qemu` disk file.
|
||||||
|
- It boots a new KVM that uses the ISO file as as CDROM and automates the
|
||||||
|
installation of Arch Linux in the virtual machine (no user prompt required).
|
||||||
|
- It can install extra packages and run custom post-installation provisioning scripts.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Usage: qemu-arch install [-o <output-disk-image>] [-a <architecture>] [-s <disk-size>]
|
Usage: vmctl install [-o <output-disk-image>] [-a <architecture>] [-s <disk-size>]
|
||||||
[-m <memory>] [-h <hostname>] [-P <root-password>] [-u <non-root-username>]
|
[-m <memory>] [-h <hostname>] [-P <root-password>] [-u <non-root-username>]
|
||||||
[-p <non-root-user-password>] [-z <timezone>] [-l <locale>] [-M <arch-mirror-url>]
|
[-p <non-root-user-password>] [-z <timezone>] [-l <locale>] [-M <arch-mirror-url>]
|
||||||
|
|
||||||
-o <output-disk-image> Path of the output disk image (default: ./arch.img)
|
-o <output-disk-image> Path of the output disk image (default: ./arch.img)
|
||||||
-a <architecture> Target architecture (default: x86_64)
|
-a <architecture> Target architecture (default: x86_64)
|
||||||
-s <disk-size> Disk size (default: 8G)
|
-s <disk-size> Disk size (default: 8G)
|
||||||
-m <memory> RAM size in KB (default: 2048)
|
-m <memory> RAM size in KB (default: 2048)
|
||||||
-h <hostname> VM hostname (default: qemu)
|
-h <hostname> VM hostname (default: qemu)
|
||||||
-P <root-password> Root password. If not specified it will be prompted
|
-P <root-password> Root password. If not specified it will be prompted
|
||||||
-u <non-root-username> Username for the main non-root user
|
-u <non-root-username> Username for the main non-root user
|
||||||
-p <non-root-user-password> Password for the non-root user. If not specified
|
-p <non-root-user-password> Password for the non-root user. If not specified it will be prompted
|
||||||
it will be prompted
|
-z <timezone> System timezone (default: UTC)
|
||||||
-z <timezone> System timezone (default: UTC)
|
-l <locale> System locale (default: en_US.UTF-8)
|
||||||
-l <locale> System locale (default: en_US.UTF-8)
|
-M <arch-mirror-url> Arch Linux download mirror URL (default: http://mirror.cj2.nl/archlinux/iso/latest/)
|
||||||
-M <arch-mirror-url> Arch Linux download mirror URL
|
Consult https://archlinux.org/download/ for a full list of the available download mirrors.
|
||||||
(default: http://mirror.cj2.nl/archlinux/iso/latest/)
|
```
|
||||||
Consult https://archlinux.org/download/ for a
|
|
||||||
full list of the available download mirrors.
|
If a required option is not specified on the command line then it will be
|
||||||
|
interactively prompted to the user (defaults are available for most of the
|
||||||
|
options).
|
||||||
|
|
||||||
|
Non-interactive example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vmctl install \
|
||||||
|
-h my_vm \
|
||||||
|
-a x86_64 \
|
||||||
|
-s 16G \
|
||||||
|
-m 2048 \
|
||||||
|
-P root \
|
||||||
|
-u myuser \
|
||||||
|
-p password \
|
||||||
|
-z Europe/Amsterdam \
|
||||||
|
-o arch-base.img
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to install an extra list of packages besides the default ones, then
|
If you want to install an extra list of packages besides the default ones, then
|
||||||
|
@ -52,11 +78,6 @@ as the disk image file.
|
||||||
The keyring population process may currently (as of March 2022) take a long time.
|
The keyring population process may currently (as of March 2022) take a long time.
|
||||||
This is a [known issue](https://www.reddit.com/r/archlinux/comments/rbjbcr/pacman_keyring_update_taking_too_long/).
|
This is a [known issue](https://www.reddit.com/r/archlinux/comments/rbjbcr/pacman_keyring_update_taking_too_long/).
|
||||||
|
|
||||||
As a workaround, if you want to speed up the OS installation process, you can
|
|
||||||
temporarily disable pacman keyring checks upon package installation by
|
|
||||||
uncommenting the relevant lines in `src/helpers/install.sh` (function:
|
|
||||||
`install_os`).
|
|
||||||
|
|
||||||
### Resize an existing image
|
### Resize an existing image
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
@ -1,109 +0,0 @@
|
||||||
export default_imgfile=arch.img
|
|
||||||
export default_architecture=x86_64
|
|
||||||
export default_disk_size=8G
|
|
||||||
export default_memory=2048
|
|
||||||
export default_hostname=qemu
|
|
||||||
export default_root_password=root
|
|
||||||
export default_username=user
|
|
||||||
export default_user_password=password
|
|
||||||
export default_timezone=UTC
|
|
||||||
export default_locale=en_US.UTF-8
|
|
||||||
export default_img_download_page='http://mirror.cj2.nl/archlinux/iso/latest/'
|
|
||||||
export isofile=archlinux-latest.iso
|
|
||||||
|
|
||||||
imgfile=
|
|
||||||
architecture=
|
|
||||||
disk_size=
|
|
||||||
memory=
|
|
||||||
hostname=
|
|
||||||
root_password=
|
|
||||||
username=
|
|
||||||
user_password=
|
|
||||||
timezone=
|
|
||||||
locale="$default_locale"
|
|
||||||
img_download_page="$default_img_download_page"
|
|
||||||
|
|
||||||
function usage() {
|
|
||||||
echo "Install an Arch Linux system on a QEMU disk image"
|
|
||||||
echo
|
|
||||||
echo "Usage: $(basename "$0") install [-o <output-disk-image>] [-a <architecture>] [-s <disk-size>]"
|
|
||||||
echo -e "\t[-m <memory>] [-h <hostname>] [-P <root-password>] [-u <non-root-username>]"
|
|
||||||
echo -e "\t[-p <non-root-user-password>] [-z <timezone>] [-l <locale>] [-M <arch-mirror-url>]"
|
|
||||||
echo
|
|
||||||
echo -e "-o <output-disk-image>\t\tPath of the output disk image (default: ./arch.img)"
|
|
||||||
echo -e "-a <architecture>\t\tTarget architecture (default: x86_64)"
|
|
||||||
echo -e "-s <disk-size>\t\t\tDisk size (default: 8G)"
|
|
||||||
echo -e "-m <memory>\t\t\tRAM size in KB (default: 2048)"
|
|
||||||
echo -e "-h <hostname>\t\t\tVM hostname (default: qemu)"
|
|
||||||
echo -e "-P <root-password>\t\tRoot password. If not specified it will be prompted"
|
|
||||||
echo -e "-u <non-root-username>\t\tUsername for the main non-root user"
|
|
||||||
echo -e "-p <non-root-user-password>\tPassword for the non-root user. If not specified it will be prompted"
|
|
||||||
echo -e "-z <timezone>\t\t\tSystem timezone (default: UTC)"
|
|
||||||
echo -e "-l <locale>\t\t\tSystem locale (default: en_US.UTF-8)"
|
|
||||||
echo -e "-M <arch-mirror-url>\t\tArch Linux download mirror URL (default: http://mirror.cj2.nl/archlinux/iso/latest/)"
|
|
||||||
echo -e "\t\t\t\tConsult https://archlinux.org/download/ for a full list of the available download mirrors."
|
|
||||||
echo
|
|
||||||
echo "If you want to install an extra list of packages besides the default ones, then"
|
|
||||||
echo "specify them in a file named PKGLIST in the same directory as the disk image file."
|
|
||||||
echo
|
|
||||||
echo "If you want to run a custom post-installation script after the core system has been"
|
|
||||||
echo "installed, then create a custom script named post-install.sh in the same directory as"
|
|
||||||
echo "the disk image file".
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
optstring=':o:a:s:m:h:P:u:p:z:l:M:'
|
|
||||||
[[ "$1" == '--help' ]] && usage
|
|
||||||
|
|
||||||
while getopts ${optstring} arg; do
|
|
||||||
case ${arg} in
|
|
||||||
o) imgfile="${OPTARG}";;
|
|
||||||
a) architecture="${OPTARG}";;
|
|
||||||
s) disk_size="${OPTARG}";;
|
|
||||||
m) memory="${OPTARG}";;
|
|
||||||
h) hostname="${OPTARG}";;
|
|
||||||
P) root_password="${OPTARG}";;
|
|
||||||
u) username="${OPTARG}";;
|
|
||||||
p) user_password="${OPTARG}";;
|
|
||||||
z) timezone="${OPTARG}";;
|
|
||||||
l) locale="${OPTARG}";;
|
|
||||||
M) img_download_page="${OPTARG}";;
|
|
||||||
?)
|
|
||||||
echo "Invalid option: -${OPTARG}" >&2
|
|
||||||
usage;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
[ -z "$imgfile" ] && read -p "Output disk image file [$default_imgfile]: " imgfile
|
|
||||||
[ -z "$architecture" ] && read -p "Architecture [$default_architecture]: " architecture
|
|
||||||
[ -z "$disk_size" ] && read -p "Disk size [$default_disk_size]: " disk_size
|
|
||||||
[ -z "$memory" ] && read -p "Memory in KB [$default_memory]: " memory
|
|
||||||
[ -z "$hostname" ] && read -p "Hostname [$default_hostname]: " hostname
|
|
||||||
[ -z "$root_password" ] && read -sp "Root password [$default_root_password]: " root_password && echo
|
|
||||||
[ -z "$username" ] && read -p "Non-admin username [$default_username]: " username
|
|
||||||
[ -z "$user_password" ] && read -sp "Non-admin user password [$default_user_password]: " user_password && echo
|
|
||||||
[ -z "$timezone" ] && read -p "Timezone [$default_timezone]: " timezone
|
|
||||||
[ -z "$locale" ] && read -p "Locale [$default_locale]: " locale
|
|
||||||
|
|
||||||
for var in imgfile \
|
|
||||||
architecture \
|
|
||||||
disk_size \
|
|
||||||
memory \
|
|
||||||
hostname \
|
|
||||||
root_password \
|
|
||||||
username \
|
|
||||||
user_password \
|
|
||||||
timezone \
|
|
||||||
locale
|
|
||||||
do
|
|
||||||
default_var=default_$var
|
|
||||||
[ -z "${!var}" ] && declare ${var}=${!default_var}
|
|
||||||
export $var
|
|
||||||
done
|
|
||||||
|
|
||||||
source "$srcdir/helpers/install.sh"
|
|
||||||
download_latest_arch_iso
|
|
||||||
create_disk_image
|
|
||||||
install_os
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
export GREEN='\033[0;32m'
|
|
||||||
export RED='\033[0;31m'
|
|
||||||
export WHITE='\033[0;37m'
|
|
||||||
export NORMAL='\033[0m'
|
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
function usage() {
|
|
||||||
actions=$(find "$srcdir/actions" -maxdepth 1 -name '*.sh' | xargs basename | cut -d. -f1)
|
|
||||||
cat <<EOF
|
|
||||||
Usage: $(basename "$0") <action>
|
|
||||||
|
|
||||||
Run $(basename "$0") <action> --help for more details.
|
|
||||||
Available actions:
|
|
||||||
|
|
||||||
$actions
|
|
||||||
EOF
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ -L "$0" ]; then
|
|
||||||
export srcdir="$(dirname "$(readlink -f "$0")")"
|
|
||||||
else
|
|
||||||
export srcdir="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
source "$srcdir/helpers/common.sh"
|
|
||||||
action=$1
|
|
||||||
shift
|
|
||||||
|
|
||||||
case "$action" in
|
|
||||||
install) source "$srcdir/actions/install.sh";;
|
|
||||||
*) usage;;
|
|
||||||
esac
|
|
||||||
|
|
161
src/helpers/install.sh → vmctl
Normal file → Executable file
161
src/helpers/install.sh → vmctl
Normal file → Executable file
|
@ -1,3 +1,77 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#########################
|
||||||
|
# Shared constants
|
||||||
|
export GREEN='\033[0;32m'
|
||||||
|
export RED='\033[0;31m'
|
||||||
|
export WHITE='\033[0;37m'
|
||||||
|
export NORMAL='\033[0m'
|
||||||
|
#########################
|
||||||
|
|
||||||
|
|
||||||
|
############################################
|
||||||
|
# Default values for configuration variables
|
||||||
|
|
||||||
|
export default_imgfile=arch.img
|
||||||
|
export default_architecture=x86_64
|
||||||
|
export default_disk_size=8G
|
||||||
|
export default_memory=2048
|
||||||
|
export default_hostname=qemu
|
||||||
|
export default_root_password=root
|
||||||
|
export default_username=user
|
||||||
|
export default_user_password=password
|
||||||
|
export default_timezone=UTC
|
||||||
|
export default_locale=en_US.UTF-8
|
||||||
|
export default_img_download_page='http://mirror.cj2.nl/archlinux/iso/latest/'
|
||||||
|
export isofile=archlinux-latest.iso
|
||||||
|
############################################
|
||||||
|
|
||||||
|
|
||||||
|
################
|
||||||
|
# install script
|
||||||
|
|
||||||
|
# Shared variables
|
||||||
|
imgfile=
|
||||||
|
architecture=
|
||||||
|
disk_size=
|
||||||
|
memory=
|
||||||
|
hostname=
|
||||||
|
root_password=
|
||||||
|
username=
|
||||||
|
user_password=
|
||||||
|
timezone=
|
||||||
|
locale="$default_locale"
|
||||||
|
img_download_page="$default_img_download_page"
|
||||||
|
|
||||||
|
function install_usage() {
|
||||||
|
echo "Install an Arch Linux system on a QEMU disk image"
|
||||||
|
echo
|
||||||
|
echo "Usage: $(basename "$0") install [-o <output-disk-image>] [-a <architecture>] [-s <disk-size>]"
|
||||||
|
echo -e "\t[-m <memory>] [-h <hostname>] [-P <root-password>] [-u <non-root-username>]"
|
||||||
|
echo -e "\t[-p <non-root-user-password>] [-z <timezone>] [-l <locale>] [-M <arch-mirror-url>]"
|
||||||
|
echo
|
||||||
|
echo -e "-o\t<output-disk-image>\t\tPath of the output disk image (default: ./arch.img)"
|
||||||
|
echo -e "-a\t<architecture>\t\t\tTarget architecture (default: x86_64)"
|
||||||
|
echo -e "-s\t<disk-size>\t\t\tDisk size (default: 8G)"
|
||||||
|
echo -e "-m\t<memory>\t\t\tRAM size in KB (default: 2048)"
|
||||||
|
echo -e "-h\t<hostname>\t\t\tVM hostname (default: qemu)"
|
||||||
|
echo -e "-P\t<root-password>\t\t\tRoot password. If not specified it will be prompted"
|
||||||
|
echo -e "-u\t<non-root-username>\t\tUsername for the main non-root user"
|
||||||
|
echo -e "-p\t<non-root-user-password>\tPassword for the non-root user. If not specified it will be prompted"
|
||||||
|
echo -e "-z\t<timezone>\t\t\tSystem timezone (default: UTC)"
|
||||||
|
echo -e "-l\t<locale>\t\t\tSystem locale (default: en_US.UTF-8)"
|
||||||
|
echo -e "-M\t<arch-mirror-url>\t\tArch Linux download mirror URL (default: http://mirror.cj2.nl/archlinux/iso/latest/)"
|
||||||
|
echo -e "\t\t\t\t\tConsult https://archlinux.org/download/ for a full list of the available download mirrors."
|
||||||
|
echo
|
||||||
|
echo "If you want to install an extra list of packages besides the default ones, then"
|
||||||
|
echo "specify them in a file named PKGLIST in the same directory as the disk image file."
|
||||||
|
echo
|
||||||
|
echo "If you want to run a custom post-installation script after the core system has been"
|
||||||
|
echo "installed, then create a custom script named post-install.sh in the same directory as"
|
||||||
|
echo "the disk image file".
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
function download_latest_arch_iso() {
|
function download_latest_arch_iso() {
|
||||||
latest_iso=$(
|
latest_iso=$(
|
||||||
curl -s "$img_download_page" |
|
curl -s "$img_download_page" |
|
||||||
|
@ -283,3 +357,90 @@ EOF
|
||||||
echo "--- Log closed at $(date)" >> "$logfile"
|
echo "--- Log closed at $(date)" >> "$logfile"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function install() {
|
||||||
|
optstring=':o:a:s:m:h:P:u:p:z:l:M:'
|
||||||
|
[[ "$1" == '--help' ]] && install_usage
|
||||||
|
|
||||||
|
while getopts ${optstring} arg; do
|
||||||
|
case ${arg} in
|
||||||
|
o) imgfile="${OPTARG}";;
|
||||||
|
a) architecture="${OPTARG}";;
|
||||||
|
s) disk_size="${OPTARG}";;
|
||||||
|
m) memory="${OPTARG}";;
|
||||||
|
h) hostname="${OPTARG}";;
|
||||||
|
P) root_password="${OPTARG}";;
|
||||||
|
u) username="${OPTARG}";;
|
||||||
|
p) user_password="${OPTARG}";;
|
||||||
|
z) timezone="${OPTARG}";;
|
||||||
|
l) locale="${OPTARG}";;
|
||||||
|
M) img_download_page="${OPTARG}";;
|
||||||
|
?)
|
||||||
|
echo "Invalid option: -${OPTARG}" >&2
|
||||||
|
install_usage;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[ -z "$imgfile" ] && read -p "Output disk image file [$default_imgfile]: " imgfile
|
||||||
|
[ -z "$architecture" ] && read -p "Architecture [$default_architecture]: " architecture
|
||||||
|
[ -z "$disk_size" ] && read -p "Disk size [$default_disk_size]: " disk_size
|
||||||
|
[ -z "$memory" ] && read -p "Memory in KB [$default_memory]: " memory
|
||||||
|
[ -z "$hostname" ] && read -p "Hostname [$default_hostname]: " hostname
|
||||||
|
[ -z "$root_password" ] && read -sp "Root password [$default_root_password]: " root_password && echo
|
||||||
|
[ -z "$username" ] && read -p "Non-admin username [$default_username]: " username
|
||||||
|
[ -z "$user_password" ] && read -sp "Non-admin user password [$default_user_password]: " user_password && echo
|
||||||
|
[ -z "$timezone" ] && read -p "Timezone [$default_timezone]: " timezone
|
||||||
|
[ -z "$locale" ] && read -p "Locale [$default_locale]: " locale
|
||||||
|
|
||||||
|
for var in imgfile \
|
||||||
|
architecture \
|
||||||
|
disk_size \
|
||||||
|
memory \
|
||||||
|
hostname \
|
||||||
|
root_password \
|
||||||
|
username \
|
||||||
|
user_password \
|
||||||
|
timezone \
|
||||||
|
locale
|
||||||
|
do
|
||||||
|
default_var=default_$var
|
||||||
|
[ -z "${!var}" ] && declare ${var}=${!default_var}
|
||||||
|
export $var
|
||||||
|
done
|
||||||
|
|
||||||
|
download_latest_arch_iso
|
||||||
|
create_disk_image
|
||||||
|
install_os
|
||||||
|
}
|
||||||
|
################
|
||||||
|
|
||||||
|
|
||||||
|
##################
|
||||||
|
# Main script
|
||||||
|
|
||||||
|
function usage() {
|
||||||
|
actions=(install)
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $(basename "$0") <action>
|
||||||
|
|
||||||
|
Run $(basename "$0") <action> --help for more details.
|
||||||
|
Available actions:
|
||||||
|
|
||||||
|
$actions
|
||||||
|
EOF
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
action=$1
|
||||||
|
shift
|
||||||
|
|
||||||
|
case "$action" in
|
||||||
|
install) install $*;;
|
||||||
|
*) usage;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
main $*
|
||||||
|
|
||||||
|
##################
|
Loading…
Reference in a new issue