import pulumi import pulumi_libvirt as libvirt import yaml # Function to convert GB to bytes for disk size stated in stack config def gb_to_bytes(gb: int) -> int: return int(gb) * 1024 * 1024 * 1024 config = pulumi.Config("kvm") ssh_user = config.get("ssh_user") or "root" target_host = config.require("target_host") guest_name = config.require("guest_name") bridge_interface = config.require("bridge_interface") secret_hash = config.require_secret("password_hash") # Load cloud-config with open(config.require("user_data_path"), "r") as f: user_data = pulumi.Output.format(f.read(), password_hash=secret_hash) # Set KVM host host_provider = libvirt.Provider( "ssh-provider", uri=f"qemu+sshcmd://{ssh_user}@{target_host}/system" ) meta_data_yaml = yaml.dump({"instance-id": f"i-{guest_name}", "local-hostname": guest_name}) # Initialize DNS nameservers and search domain dns_servers = config.require_object("dns_servers") dns_search = config.get_object("dns_search") or [] # Initialize network config network_config_payload = { "version": 1, "config": [] } # Append "physical" interface physical_interface = { "type": "physical", "name": "eth0", "subnets": [ { "type": "static", "address": config.require("static_ip"), "gateway": config.require("gateway") } ] } network_config_payload["config"].append(physical_interface) # Append nameservers if dns_servers: nameserver_block = { "type": "nameserver", "interface": "eth0", "address": dns_servers } network_config_payload["config"].append(nameserver_block) # Append search domain if dns_search: network_config_payload["config"][0]["subnets"][0]["dns_search"] = list(str(dns_search)) network_config_yaml = yaml.dump(network_config_payload) base_volume = libvirt.Volume( "base-vol", name=f"{guest_name}-base", pool=pool_target, capacity=gb_to_bytes(10), target={ "format": {"type": "qcow2"} }, create={ "content": { "url": base_image_path } }, opts=pulumi.ResourceOptions(provider=host_provider) ) guest_volume = libvirt.Volume( "guest-disk", name=f"{guest_name}", pool=pool_target, capacity=gb_to_bytes(config.require_int("disk_gb")), target={ "format": {"type": "qcow2"} }, backing_store={ "path": base_volume.id, "format": { "type": "qcow2" } }, opts=pulumi.ResourceOptions(provider=host_provider) ) cloud_init_disk = libvirt.CloudinitDisk( "cloudinit-iso", meta_data=meta_data_yaml, user_data=user_data, network_config=network_config_yaml, opts=pulumi.ResourceOptions(provider=host_provider) ) # Create the cloud-init volume with metadata cloud_init_volume = libvirt.Volume( "cloudinit-vol", pool=pool_target, target={ "format": {"type": "raw"} }, create={ "content": { "url": cloud_init_disk.path } }, opts=pulumi.ResourceOptions(provider=host_provider) ) guest = libvirt.Domain( "kvm-guest", name=guest_name, type="kvm", autostart=True, cpu={ "mode": "host-passthrough" }, memory=config.require_int("memory_mb"), memory_unit="MiB", vcpu=config.require_int("vcpu"), os={ "type": "hvm", "type_arch": "x86_64", "type_machine": "q35", "firmware": "efi", "boot_devices": [] }, features={ "acpi": True }, devices={ "disks": [ { "source": { "volume": { "pool": guest_volume.pool, "volume": guest_volume.name } }, "target": { "dev": "vda", "bus": "virtio" }, "driver": { "type": "qcow2" }, "boot": { "order": 1 } }, { "device": "cdrom", "source": { "volume": { "pool": cloud_init_volume.pool, "volume": cloud_init_volume.name } }, "target": { "dev": "sda", "bus": "sata" }, "driver": { "type": "raw" } } ], "interfaces": [ { "type": "bridge", "model": { "type": "virtio" }, "source": { "bridge": { "bridge": bridge_interface } }, "wait_for_ip": { "timeout": 300, "source": "lease" } } ], "graphics": [ { "type": "vnc", "vnc": { "autoport": True, "listen": "127.0.0.1" } } ] }, opts=pulumi.ResourceOptions(provider=host_provider) ) pulumi.export("deployed_node", guest.name)