You've already forked Projects
Pulumi libvirt project commit
Initial commit for pulumi kvm project
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
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:
|
||||
"""Converts gigabytes to bytes for resource capacity."""
|
||||
return int(gb) * 1024 * 1024 * 1024
|
||||
|
||||
|
||||
config = pulumi.Config("kvm")
|
||||
|
||||
ssh_user = config.get("ssh_user") or "root"
|
||||
base_image_path = config.require("base_image_path")
|
||||
nodes = config.require_object("nodes")
|
||||
secret_hash = config.require_secret("password_hash")
|
||||
|
||||
# Load user data once outside the loop
|
||||
try:
|
||||
with open(config.require("user_data_path"), "r") as f:
|
||||
user_data = pulumi.Output.format(f.read(), password_hash=secret_hash)
|
||||
except Exception as e:
|
||||
# Handle case where file path is wrong or secret fails to resolve
|
||||
print(f"Warning: Could not process user_data. Skipping node deployment. Error: {e}")
|
||||
|
||||
# Dictionary to cache providers per target host
|
||||
providers = {}
|
||||
|
||||
for node in nodes:
|
||||
name = node["name"]
|
||||
target_host = node["host"]
|
||||
bridge_interface = node["bridge"]
|
||||
disk_gb = node["disk_gb"]
|
||||
memory_mb = node["memory_mb"]
|
||||
vcpu = node["vcpu"]
|
||||
|
||||
# Get or cache providers in providers dictionary
|
||||
if target_host not in providers:
|
||||
providers[target_host] = libvirt.Provider(
|
||||
f"prov-{target_host}",
|
||||
uri=f"qemu+sshcmd://{ssh_user}@{target_host}/system"
|
||||
)
|
||||
host_provider = providers[target_host]
|
||||
|
||||
meta_data_yaml = yaml.dump({"instance-id": f"i-{name}", "local-hostname": name})
|
||||
|
||||
# Initialize network payload in V1 format
|
||||
network_config_payload = {
|
||||
"version": 1,
|
||||
"config": [] # Start with an empty list of configurations (The list container)
|
||||
}
|
||||
|
||||
|
||||
# physical interface
|
||||
physical_interface = {
|
||||
"type": "physical",
|
||||
"name": "eth0",
|
||||
"subnets": [
|
||||
{
|
||||
"type": "static",
|
||||
"address": node["static_ip"],
|
||||
"gateway": node["gateway"],
|
||||
}
|
||||
]
|
||||
}
|
||||
network_config_payload["config"].append(physical_interface)
|
||||
|
||||
|
||||
# Append the nameservers to network_config_payload (solves missing nameserver issues)
|
||||
if "dns_servers" in node:
|
||||
nameserver_block = {
|
||||
"type": "nameserver",
|
||||
"address": [str(ip) for ip in node["dns_servers"]] # Create clean python list of strings
|
||||
}
|
||||
network_config_payload["config"].append(nameserver_block)
|
||||
|
||||
|
||||
if "dns_search" in node and node["dns_search"]:
|
||||
network_config_payload["config"][0]["subnets"][0]["dns_search"] = [str(s) for s in node["dns_search"]] # Create clean python list of strings
|
||||
|
||||
# complete network config yaml
|
||||
network_config_yaml = yaml.dump(network_config_payload)
|
||||
|
||||
|
||||
# cloud image backing store
|
||||
base_volume = libvirt.Volume(
|
||||
f"base-vol-{name}",
|
||||
name=f"{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(
|
||||
f"{name}",
|
||||
name=f"{name}",
|
||||
pool=pool_target,
|
||||
capacity=gb_to_bytes(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(
|
||||
f"ci-disk-{name}",
|
||||
meta_data=meta_data_yaml,
|
||||
user_data=user_data,
|
||||
network_config=network_config_yaml, # Passes the fully constructed network payload
|
||||
opts=pulumi.ResourceOptions(provider=host_provider)
|
||||
)
|
||||
|
||||
cloud_init_volume = libvirt.Volume(
|
||||
f"ci-vol-{name}",
|
||||
pool=pool_target,
|
||||
target={
|
||||
"format": {"type": "raw"}
|
||||
},
|
||||
create={
|
||||
"content": {
|
||||
"url": cloud_init_disk.path
|
||||
}
|
||||
},
|
||||
opts=pulumi.ResourceOptions(provider=host_provider)
|
||||
)
|
||||
|
||||
guest = libvirt.Domain(
|
||||
f"dom-{name}",
|
||||
name=name,
|
||||
type="kvm",
|
||||
autostart=True,
|
||||
cpu={
|
||||
"mode": "host-passthrough"
|
||||
},
|
||||
memory=memory_mb,
|
||||
memory_unit="MiB",
|
||||
vcpu=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)
|
||||
@@ -0,0 +1,217 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user