Add first attempt at libvirt project

main
John Kenyon 2025-09-22 08:26:25 -07:00
parent c0f20a18fc
commit 4fbbcb99b2
8 changed files with 291 additions and 0 deletions

71
.gitignore vendored Normal file
View File

@ -0,0 +1,71 @@
# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.log
crash.*.log
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*.tfvars.json
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Include override files you do wish to add to version control using negated pattern
# !example_override.tf
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*
# Ignore CLI configuration files
.terraformrc
terraform.rc
# OpenTofu specific
.tofu/
*.tofu.lock.hcl
# IDE and Editor files
.vscode/
.idea/
*.swp
*.swo
*~
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# SSH keys (if any are generated locally)
*.pem
*.key
id_rsa*
*.pub
# Log files
*.log
# Backup files
*.backup
*.bak
# Temporary files
*.tmp
*.temp

View File

@ -0,0 +1,24 @@
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.opentofu.org/dmacvicar/libvirt" {
version = "0.7.6"
constraints = "~> 0.7.0"
hashes = [
"h1:mmbm4vTyC/DCGO4Ed/vbp5AKvy1gmVn/94fzB9VmR08=",
"zh:0bde54f6f658b20b620b875daf106b5b25b1bae4d15408d6c5f06d58360e254d",
"zh:0c97c6930015918b8a34b6d7a2b0c3d17a649c226fcd1874fcba5bbbc0f35972",
"zh:1bdd7aa0011c5f024a09a124836ee9bc8e71b05a6ece810c61824275fd3f695f",
"zh:2b0cc7c794e4caf395d84ffff0b380d17e4b3219a4696264271bfe5059450efe",
"zh:2f8633f7fe07f76c188836ed6f93321ec5fbf5c004bc7699e1741d9b21ed5f37",
"zh:5bf47eed286ce55ed10a5cf657de49a34ab21cc8677c56fef3aab69cdde41a27",
"zh:7dca790fc5fd1d42bc4bc7170be003a7093602026d0f95c8aab84ad551fdf2a4",
"zh:80476b68bc84e3d661d1390025f83879b88f9cdc836de9751af09bd5716089cb",
"zh:82f3e2f3f50176cd6041c8ba36e295cbda1b289ef52ab75b5eceb0f921f64f7b",
"zh:a179b165f3b9bb9a67ebbbf9d73157ded33f02d476b2f58906389dca03b653c9",
"zh:acae54a5d0616f22b3180ddd8e8aad39af664e604394fdacf1f7b337bca2d5b4",
"zh:da4406a2428a9a7e98272c032cb93431c3919253af2fe9934b532d26c0deab09",
"zh:f63dbd8e579ab5268d01ffab4503b8a8e736b70d1a04e4f271559ba8dd133dcd",
"zh:f85c1d9e51a94ecde137435c9d6b0fb7be590437ea8a725334d1577eebbc550c",
]
}

View File

@ -0,0 +1,50 @@
# VM Infrastructure with OpenTofu + LibVirt
This project provisions virtual machines using OpenTofu and LibVirt.
## Quick Start
```bash
# Initialize OpenTofu
tofu init
# Plan deployment
tofu plan
# Deploy VMs
tofu apply
# Get VM IPs
tofu output vm_ips
# Destroy infrastructure when done
tofu destroy
```
## Configuration
- **VMs**: 3 Ubuntu 22.04 VMs with 4GB RAM, 2 vCPUs, 30GB disk each
- **Authentication**: SSH key-based (no password auth)
- **User**: `ubuntu` with sudo access
- **Network**: Default libvirt network with DHCP
## Files
- `main.tf` - Main infrastructure configuration
- `variables.tf` - Input variables
- `outputs.tf` - Output values (VM IPs and names)
- `terraform.tfvars` - Variable values
- `cloud-init/` - VM initialization configuration
## Prerequisites
1. Install LibVirt and OpenTofu (see main PLAN.md)
2. Ensure your SSH public key is in `cloud-init/user-data.yaml`
3. User must be in `libvirt` group
## Accessing VMs
```bash
# SSH to VM (replace with actual IP)
ssh ubuntu@192.168.122.100
```

View File

@ -0,0 +1,2 @@
instance-id: vm-instance
local-hostname: vm-host

View File

@ -0,0 +1,29 @@
#cloud-config
users:
- name: ubuntu
groups: sudo
shell: /bin/bash
sudo: ['ALL=(ALL) NOPASSWD:ALL']
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDE0jo1NZHqhXNPmWZ8x0wdFDfDiloSLfZL0NK/DbukhF3WN5hSn/6Vv6Qa6USpq1S3XiaBL7pA+zrTZGr/ZH6iZKDelYufU1a22dWx6OYKVX84d/wIc+3PHAFm1A5g3+787pT+6HHH4g7GKmf1/zYJlfqPr5zyGVBdQkzglDadC7wdfwGE7Q3gjHflhAIyGvcBfAQ/hYQSgWAMdsYhthh1DCMvPQIqA0E2tslgD1AtpDhjE72a+pLAsGxDjiBcuKW4SEAjcxt+Jcu10brrXAbt5bHg9SNk/NNwwMZVy0siAJRRcg6Dv41VqMa2MvIGoDUiDju4kRwjJMcxorwxAa8n jlkenyon@helios.local
packages:
- curl
- wget
- git
- htop
package_update: true
package_upgrade: true
# Set timezone
timezone: UTC
# Enable SSH
ssh_pwauth: false
# Run commands on first boot
runcmd:
- systemctl enable ssh
- systemctl start ssh
- echo "VM provisioned successfully" > /tmp/provision-complete

69
vm-infrastructure/main.tf Normal file
View File

@ -0,0 +1,69 @@
terraform {
required_providers {
libvirt = {
source = "dmacvicar/libvirt"
version = "~> 0.7.0"
}
}
}
# Configure the LibVirt Provider
provider "libvirt" {
uri = "qemu:///system"
}
# Download Ubuntu cloud image
resource "libvirt_volume" "base_image" {
name = "ubuntu-22.04-base.qcow2"
pool = var.pool_name
source = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
format = "qcow2"
}
# Create cloud-init disk
resource "libvirt_cloudinit_disk" "cloudinit" {
name = "cloudinit.iso"
pool = var.pool_name
user_data = file("${path.module}/cloud-init/user-data.yaml")
meta_data = file("${path.module}/cloud-init/meta-data.yaml")
}
# Create VM volumes from base image
resource "libvirt_volume" "vm_disk" {
count = var.vm_count
name = "vm-${count.index + 1}-disk.qcow2"
pool = var.pool_name
base_volume_id = libvirt_volume.base_image.id
size = var.disk_size
}
# Create VMs
resource "libvirt_domain" "vm" {
count = var.vm_count
name = "vm-${count.index + 1}"
memory = var.memory
vcpu = var.vcpu
cloudinit = libvirt_cloudinit_disk.cloudinit.id
network_interface {
network_name = "default"
wait_for_lease = true
}
disk {
volume_id = libvirt_volume.vm_disk[count.index].id
}
console {
type = "pty"
target_port = "0"
target_type = "serial"
}
graphics {
type = "spice"
listen_type = "address"
autoport = true
}
}

View File

@ -0,0 +1,11 @@
output "vm_ips" {
description = "IP addresses of the created VMs"
value = {
for i, vm in libvirt_domain.vm : vm.name => vm.network_interface[0].addresses[0]
}
}
output "vm_names" {
description = "Names of the created VMs"
value = libvirt_domain.vm[*].name
}

View File

@ -0,0 +1,35 @@
variable "pool_name" {
description = "Name of the storage pool"
type = string
default = "vm-pool"
}
variable "pool_path" {
description = "Path to the storage pool"
type = string
default = "/var/lib/libvirt/images/vm-pool"
}
variable "vm_count" {
description = "Number of VMs to create"
type = number
default = 3
}
variable "memory" {
description = "Memory allocation for each VM (MB)"
type = number
default = 2048
}
variable "vcpu" {
description = "Number of vCPUs for each VM"
type = number
default = 2
}
variable "disk_size" {
description = "Disk size for each VM (bytes)"
type = number
default = 21474836480 # 20GB
}