import os from pyinfra import host, inventory from pyinfra.operations import server, apk, files, openrc from pyinfra.facts.server import Mounts from pyinfra_util import get_pass files.replace( name="Enable TCP forwarding via SSH server", path="/etc/ssh/sshd_config", text="AllowTcpForwarding no", replace="AllowTcpForwarding yes", ) openrc.service( name="Restart sshd", service="sshd", restarted=True, ) files.replace( name="Enable community repository", path="/etc/apk/repositories", text="#http://dl-cdn.alpinelinux.org/alpine/v3.20/community", replace="http://dl-cdn.alpinelinux.org/alpine/v3.20/community", ) apk.update() apk.packages( packages=["cryptsetup", "vim"] ) mounts = host.get_fact(Mounts) if "/var/lib/libvirt" not in mounts: decryption_password = get_pass('0x90/ararat/sdb-crypt').strip() if decryption_password: server.shell( name="Decrypt and mount /data", commands=[ f" echo -n '{decryption_password}' | cryptsetup luksOpen --key-file - /dev/sdb1 sdb_crypt || true", "mount /dev/mapper/sdb_crypt /var/lib/libvirt", ] ) apk.packages( packages=["libvirt-daemon", "qemu-img", "qemu-system-x86_64", "virt-install"] ) openrc.service( name="Start libvirtd", service="libvirtd", running=True, enabled=False, ) # add networking: https://wiki.alpinelinux.org/wiki/KVM#Networking server.modprobe( name="activate tun kernel module", module="tun", ) # echo "tun" >> /etc/modules-load.d/tun.conf files.line( name="autostart tun", path="/etc/modules-load.d/tun.conf", line="tun", ) # cat /etc/modules | grep tun || echo tun >> /etc/modules #files.line(path="/etc/modules",line="tun") # add VMs to public network: virsh_network_guests = [] for vm in inventory.groups.get("debian_vms"): #sudo ip addr add 65.109.242.20 dev eth0 ipv4 = vm.data.get("ipv4") mac_address = '52:54:00:6c:3c:%02x'%vm.data.get("id") files.template( name=f"Add {ipv4} for {vm} to ararat", src="ararat/files/floating-ip.cfg.j2", dest=f"/etc/network/interfaces.d/60-{vm}-floating-up.cfg", # doesn't work, interfaces.d isn't included vm=vm, ipv4=ipv4, ) #server.shell(name=f"Add {ipv4} for {vm} to ararat", commands=[f"ip addr add {ipv4} dev eth{vm}"],) virsh_network_guests.append(f"") openrc.service( service="networking", restarted=True, ) # create public kvm network files.template( name="Generate libvirt public network XML", src="ararat/files/public.network.j2", dest="/tmp/public.network", guests='\n '.join(virsh_network_guests), host_ipv4=host.name, ) server.shell( name="Update libvirt public network", commands=[ "virsh net-destroy public ; virsh net-undefine public || true", "virsh net-define /tmp/public.network", "virsh net-start public", ] ) # disable ipv6 in a bridge if necessary debian_image_path = "/var/lib/libvirt/images/debian-12-generic-amd64.qcow2" files.download( name="Download Debian 12 base image", src="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2", dest=debian_image_path, ) for vm in inventory.groups.get("debian_vms"): if os.path.isfile(f"{vm}/files/cloud-init.yml"): files.put( name=f"Upload {vm}-cloud-init.yml", src=f"{vm}/files/cloud-init.yml", dest=f"/root/{vm}-cloud-init.yml", ) #virt-install else: if vm.data.get("authorized_keys"): authorized_keys = "ssh_authorized_keys:\n - " + " - ".join( [get_pass(f"0x90/ssh_keys/{admin}.pub") for admin in vm.data.get("authorized_keys")] ) else: authorized_keys = "" files.template( name=f"Upload {vm}-cloud-init.yml", src="ararat/files/cloud-init.yml.j2", dest=f"/root/{vm}-cloud-init.yml", ssh_authorized_keys=authorized_keys, ) mac_address = '52:54:00:6c:3c:%02x' % vm.data.get("id") memory = 1024 vcpus = 1 disk_size = 4 server.shell( name=f"virt-install {vm}", commands=[ f"virsh list -all | grep {vm} || " # only run virt-install if VM doesn't exist yet f"virt-install --name {vm} --disk=size={disk_size},backing_store={debian_image_path} " f"--memory {memory} --vcpus {vcpus} --cloud-init user-data=/root/{vm}-cloud-init.yml,disable=on " f"--network 'bridge=virbr0,network=public,mac_address={mac_address}' --osinfo=debian12 || true", ] )