diff --git a/.gitignore b/.gitignore index 0208525..0c8926f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ templates/cloud-config.yml +templates/network-config.yml templates/launch-vm.ini templates/*lab* templates/*local* diff --git a/README.md b/README.md index 8ed7440..86e41f9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # **virt-install-cloud** Bash scripts and templates to **create, deploy, and remove cloud-init-enabled virtual machines** -using `virt-install`. It has been tested on **Ubuntu 26.04 LTS** and works +using `virt-install`. It has been tested on **Ubuntu 24.04 LTS** and **Gentoo** and works on other recent Ubuntu/Debian systems with a proper libvirt setup. --- @@ -15,15 +15,16 @@ sudo apt update sudo apt install libvirt-daemon-system libvirt-clients qemu-kvm qemu-utils virtinst wget ``` -For non-root usage, add your user to the `libvirt` and `kvm` groups: +For non-root usage, add your user to the `libvirt`, `qemu`, and `kvm` groups: ```bash -sudo usermod -aG libvirt,kvm $USER +sudo usermod -aG libvirt,kvm,qemu $USER ``` ### **Required Tools (Local or Remote)** -These commands must be available on the host where you run the scripts (local system or the remote hypervisor when using `LIBVIRT_DEFAULT_URI`): +These commands must be available on the host where you run the scripts +(local system or the remote hypervisor when using `LIBVIRT_DEFAULT_URI`): - `virsh` - `virt-install` @@ -38,12 +39,14 @@ These commands must be available on the host where you run the scripts (local sy Example templates are provided in the `templates` directory: - `cloud-config.yml-example` +- `network-config.yml-example` - `launch-vm.ini-example` To set up your configuration: ```bash cp templates/cloud-config.yml-example templates/cloud-config.yml +cp templates/network-config.yml-example templates/network-config.yml cp templates/launch-vm.ini-example templates/launch-vm.ini ``` @@ -51,6 +54,7 @@ Edit: - **`launch-vm.ini`** to configure network, CPUs, RAM, storage pool, etc. - **`cloud-config.yml`** to define the default user, password, and SSH key. +- **`network-config.yml`** to use anything besides default DHCP. ### **Generate a password hash** @@ -78,6 +82,12 @@ VMPOOL=vm-pool CONSOLE="pty,target_type=virtio" ``` +> [!IMPORTANT] +> The OS distro names are only as current as the version of `osinfo-db` +> installed in your environment. If the name is not found, eg, `rocky10`, +> then you need to edit the template file and change the OSVARIANT to +> `unknown` in order to install the desired OS version. + --- ## **Storage Pool Setup** @@ -192,14 +202,16 @@ export LAUNCH_VM_INI="/path/to/custom-launch-vm.ini" > Uses `/path/to/custom-launch-vm.ini` instead of `templates/launch-vm.ini`. -### **Use a custom cloud-init file** +### **Use a custom cloud-init or network config file** ```bash export CLOUD_CONFIG="/custom/path/cloud-config.yml" +export NET_CONFIG="/custom/path/network-config.yml" ./launch-vm.sh -d ubuntu22.04 -n test-vm ``` -> Uses `/custom/path/cloud-config.yml` instead of `templates/cloud-config.yml`. +> Uses `/custom/path/cloud-config.yml` instead of `templates/cloud-config.yml` +> and/or `/custom/path/network-config.yml` instead of `templates/network-config.yml`. ### **Use a specific Python interpreter for `virt-install`** diff --git a/bin/launch-vm.sh b/bin/launch-vm.sh index 2af5bce..485d79d 100755 --- a/bin/launch-vm.sh +++ b/bin/launch-vm.sh @@ -252,7 +252,12 @@ create-extra-volume() { } vm-setup() { + NET_ARG="" CLOUD_CONFIG_FILE="${CLOUD_CONFIG:-${LVTEMPLATES}/cloud-config.yml}" + NET_CONFIG_FILE="${NET_CONFIG:-${LVTEMPLATES}/network-config.yml}" + if [[ -f "${NET_CONFIG_FILE}" ]]; then + NET_ARG=",network-config=${NET_CONFIG_FILE}" + fi if [[ ! -f "${CLOUD_CONFIG_FILE}" ]]; then echo "Error: Cloud-init config file '${CLOUD_CONFIG_FILE}' not found." @@ -301,13 +306,51 @@ EOF --virt-type kvm \ --import \ --cloud-init "user-data=${CLOUD_CONFIG_FILE},meta-data=${META_DATA_FILE}" \ - --wait \ --noautoconsole \ "${console_arg[@]}" \ - --video none \ + --graphics=spice,port=-1,listen=localhost \ --qemu-commandline="-smbios type=1,serial=ds=nocloud;h=${VMNAME}.${DOMAIN}" } +get-vminfo() { + IP=${IP:-} + if [ -f "${NET_CONFIG_FILE}" ] && [ -z "${IP}" ] ; then + echo "Detected NET_CONFIG_FILE in use, aborting VM IP loop." + exit 0 + fi + timeout=60 # seconds + if [[ ! -n "$IP" ]]; then + echo "Waiting for $VMNAME IP address..." + for ((i = 0; i < timeout; i++)); do + DOM=$(virsh -q domifaddr "$VMNAME") + read -ra arr <<<"$DOM" + if [[ -n "${arr[@]}" ]]; then + IP="${arr[3]%/*}" + fi + + if [[ -n "$IP" ]]; then + break + fi + sleep 1 + done + fi + + if [[ -n "$IP" ]]; then + echo "" + echo "SSH to ${VMNAME}:" + echo " ssh ${IP}" + echo " ssh ubuntu@${IP}" + echo "" + echo "Checking for ${IP} in known_hosts file" + grep -q ${IP} ${HOME}/.ssh/known_hosts && + echo "Found entry for ${IP}. Removing" && + (sed --in-place "/^${IP}/d" ~/.ssh/known_hosts) || + echo "No entries found for ${IP}" + else + echo "Timed out waiting for DHCP lease" + fi +} + # ------------------------------------------------------------------------- # Main Execution Flow # ------------------------------------------------------------------------- @@ -320,3 +363,4 @@ clone-base resize-clone create-extra-volume vm-setup +get-vminfo diff --git a/templates/network-config.yml-example b/templates/network-config.yml-example new file mode 100644 index 0000000..3813dde --- /dev/null +++ b/templates/network-config.yml-example @@ -0,0 +1,12 @@ +network: + version: 2 + ethernets: + ens3: + dhcp4: yes + dhcp6: no + #addresses: [192.168.122.21/24] + #nameservers: + # addresses: [192.168.122.1] + #routes: + #- to: 0.0.0.0/0 + # via: 192.168.122.1 diff --git a/templates/rockylinux10.ini b/templates/rockylinux10.ini index bac9734..ad14fac 100644 --- a/templates/rockylinux10.ini +++ b/templates/rockylinux10.ini @@ -1,7 +1,5 @@ URL=https://dl.rockylinux.org/pub/rocky/10/images/x86_64/Rocky-10-GenericCloud-Base.latest.x86_64.qcow2 -SOURCE=source-rockylinux10.img -# libosinfo (osinfo-db 0.20250606) has no rocky10 entry yet; the nearest known -# variant is used only to pick sensible default device models. -# Bump this to rocky10 once `osinfo-query os` lists it. -OSVARIANT=rocky9 +SOURCE=source-rocky10.img +#OSVARIANT=rocky10 +OSVARIANT=rocky-unknown CONSOLE="pty,target_type=virtio" diff --git a/templates/rockylinux9.ini b/templates/rockylinux9.ini index cb13d69..d98d008 100644 --- a/templates/rockylinux9.ini +++ b/templates/rockylinux9.ini @@ -1,4 +1,4 @@ URL=https://dl.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud-Base.latest.x86_64.qcow2 -SOURCE=source-rockylinux9.img +SOURCE=source-rocky9.img OSVARIANT=rocky9 CONSOLE="pty,target_type=virtio" diff --git a/templates/ubuntu26.04.ini b/templates/ubuntu26.04.ini index 329cc75..5982ed3 100644 --- a/templates/ubuntu26.04.ini +++ b/templates/ubuntu26.04.ini @@ -1,7 +1,5 @@ URL=https://cloud-images.ubuntu.com/resolute/current/resolute-server-cloudimg-amd64.img SOURCE=source-ubuntu26.04.img -# libosinfo (osinfo-db 0.20250606) has no ubuntu26.04 entry yet; the nearest -# known variant is used only to pick sensible default device models. -# Bump this to ubuntu26.04 once `osinfo-query os` lists it. -OSVARIANT=ubuntu25.10 +#OSVARIANT=ubuntu26.04 +OSVARIANT=unknown CONSOLE="pty,target_type=virtio"