dmacvicar / terraform-provider-libvirt

Terraform provider to provision infrastructure with Linux's KVM using libvirt
Apache License 2.0
1.54k stars 457 forks source link

libvirt iscsi network-disk not working #894

Open tbals opened 2 years ago

tbals commented 2 years ago

System Information

Linux distribution

Ubuntu 20.04.3 LTS

Terraform and Provider version

Terraform v1.0.7
on linux_amd64
+ provider registry.terraform.io/dmacvicar/libvirt v0.6.11
+ provider registry.terraform.io/hashicorp/template v2.2.0

Description of Issue/Question

Setup

main.tf:

variable "qemu_server" { default = "172.20.2.3" }
variable "vm_id" { default = "vm0123" }
variable "hostname" { default = "vm-hostname" }
variable "domain" { default = "domain.tld" }
variable "network" { default = [ "br1" ] }
variable "ip4" { default = "172.30.1.2/24" }
variable "gw4" { default = "172.30.1.1" }
variable "ram" { default = 4096 }
variable "cpu" { default = 1 }
variable "dist" { default = "focal" }
variable "vol_size" { default = 20 * 1024 * 1024 * 1024 }
variable "swap_size" { default = "2G" }
variable "autostart" { default = false }

#-----------------------------------------------------------------------------

terraform {
  required_version = ">= 1.0.1"
  required_providers {
    libvirt = {
      source = "dmacvicar/libvirt"
      version = "0.6.11"
    }
  }
}

provider "libvirt" {
  uri = "qemu+ssh://terraform@${var.qemu_server}/system"
}

resource "libvirt_domain" "ubuntu-vm" {
  name = "${var.vm_id}__${var.hostname}"
  memory = var.ram
  vcpu   = var.cpu
  autostart = var.autostart

  disk {
    volume_id = libvirt_volume.ubuntu-vol.id
  }

  xml {
    xslt = "${data.template_file.iscsi.rendered}"
  }

  boot_device {
    dev = [ "hd", "network"]
  }

  dynamic "network_interface" {
    for_each = var.network
    content {
      bridge = network_interface.value
    }
  }

  cloudinit = libvirt_cloudinit_disk.cloud-init.id

  console {
    target_type = "serial"
    type        = "pty"
    target_port = "0"
  }
  console {
    target_type = "virtio"
    type        = "pty"
    target_port = "1"
  }
}

resource "libvirt_volume" "ubuntu-base" {
  name = "ubuntu-${var.dist}-base.qcow2"
  pool = "default"
  source = "/opt/${var.dist}-server-cloudimg-amd64.img"
  format = "qcow2"
}

resource "libvirt_volume" "ubuntu-vol" {
  name = "${var.vm_id}__${var.hostname}.qcow2"
  pool = "images"
  base_volume_id = libvirt_volume.ubuntu-base.id
  size = var.vol_size
}

data "template_file" "iscsi" {
  template = file("${path.module}/iscsi.xsl")
  vars = {
    target = "10.100.1.1"
    name = "iqn.2021-10.de.foobar:vm0123/1"
    dev = "vdc"
  }
}

data "template_file" "user_data" {
  template = file("${path.module}/../user-data.yml")
  vars = {
    hostname = var.hostname
    fqdn = "${var.hostname}.${var.domain}"
    chroot = var.vm_id
    swap = var.swap_size
  }
}

data "template_file" "network_config" {
  template = file("${path.module}/../network_config.yml")
  vars = {
    ip4 = var.ip4
    gw4 = var.gw4
  }
}

resource "libvirt_cloudinit_disk" "cloud-init" {
  name = "cloud-init-${var.hostname}.iso"
  pool = "cloud-init"
  user_data = data.template_file.user_data.rendered
  network_config = data.template_file.network_config.rendered
}

iscsi.xsl:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:template match="node()|@*">
      <xsl:copy>
         <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
   </xsl:template>

  <xsl:template match="/domain/devices">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>

            <xsl:element name="disk">
                <xsl:attribute name="type">network</xsl:attribute>
                <xsl:attribute name="device">disk</xsl:attribute>
                <xsl:element name="driver">
                    <xsl:attribute name="name">qemu</xsl:attribute>
                    <xsl:attribute name="type">raw</xsl:attribute>
                </xsl:element>
                <xsl:element name="source">
                    <xsl:attribute name="protocol">iscsi</xsl:attribute>
                    <xsl:attribute name="name">${name}</xsl:attribute>
                    <xsl:element name="host">
                        <xsl:attribute name="name">${target}</xsl:attribute>
                        <xsl:attribute name="port">3260</xsl:attribute>
                    </xsl:element>
                </xsl:element>
                <xsl:element name="target">
                    <xsl:attribute name="dev">vdc</xsl:attribute>
                    <xsl:attribute name="bus">virtio</xsl:attribute>
                </xsl:element>
            </xsl:element>

    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Steps to Reproduce Issue


terraform apply 

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # libvirt_cloudinit_disk.cloud-init will be created
  + resource "libvirt_cloudinit_disk" "cloud-init" {
      + id             = (known after apply)
      + name           = "cloud-init-vm-hostname.iso"
      + network_config = <<-EOT
            version: 2
            ethernets:
              ens3:
                dhcp4: no
                addresses: ["172.30.1.2/24"]
                gateway4: "172.30.1.1"
                nameservers:
                  search: [ domain.tld, foo.domain.tld ]
                  addresses: [ 141.1.1.1 ]
        EOT
      + pool           = "cloud-init"
      + user_data      = <<-EOT
            #cloud-config

            #disable_root: true
            disable_root: false
            chpasswd:
              list: |
                   root:ping
              expire: False

            users:
              - name: user
                gecos: user@vm-hostname
                groups: adm
                shell: /bin/bash
                sudo: ALL=(ALL) NOPASSWD:ALL
                ssh-authorized-keys:
                 - ssh-rsa AAAAB3N.....

            timezone: Etc/UTC

            hostname: "vm-hostname"
            fqdn: "vm-hostname.domain.tld"
            manage_etc_hosts: true
            manage_resolv_conf: true
            resolv_conf:
              nameservers: ['1.1.1.1', '8.8.8.8']
              searchdomains:
                - domain.tld
                - foo.domain.tld
              domain: domain.tld
              options:
                rotate: true
                timeout: 1

            swap:
              filename: /swap.img
              size: "auto"
              maxsize: 2G

            apt:
              preserve_sources_list: true

            package_update: true
            package_upgrade: true
            package_reboot_if_required: true
            packages:
              - htop
              - git

            runcmd:
              - echo 'SELECTED_EDITOR="/usr/bin/vim.basic"' > /root/.selected_editor
              - echo "vm0123" > /etc/debian_chroot
              - touch /etc/cloud/cloud-init.disabled
              - apt -y purge lxc lxd snapd
              - apt -y --purge autoremove

            power_state:
              mode: reboot
              message: Bye Bye
              timeout: 30
              condition: True
        EOT
    }

  # libvirt_domain.ubuntu-vm will be created
  + resource "libvirt_domain" "ubuntu-vm" {
      + arch        = (known after apply)
      + autostart   = false
      + cloudinit   = (known after apply)
      + disk        = [
          + {
              + block_device = null
              + file         = null
              + scsi         = null
              + url          = null
              + volume_id    = (known after apply)
              + wwn          = null
            },
        ]
      + emulator    = (known after apply)
      + fw_cfg_name = "opt/com.coreos/config"
      + id          = (known after apply)
      + machine     = (known after apply)
      + memory      = 4096
      + name        = "vm0123__vm-hostname"
      + qemu_agent  = false
      + running     = true
      + vcpu        = 1

      + boot_device {
          + dev = [
              + "hd",
              + "network",
            ]
        }

      + console {
          + source_host    = "127.0.0.1"
          + source_service = "0"
          + target_port    = "0"
          + target_type    = "serial"
          + type           = "pty"
        }
      + console {
          + source_host    = "127.0.0.1"
          + source_service = "0"
          + target_port    = "1"
          + target_type    = "virtio"
          + type           = "pty"
        }

      + network_interface {
          + addresses    = (known after apply)
          + bridge       = "br1"
          + hostname     = (known after apply)
          + mac          = (known after apply)
          + network_id   = (known after apply)
          + network_name = (known after apply)
        }

      + xml {
          + xslt = <<-EOT
                <?xml version="1.0"?>
                <xsl:stylesheet version="1.0"
                                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
                  <xsl:output omit-xml-declaration="yes" indent="yes"/>
                  <xsl:template match="node()|@*">
                      <xsl:copy>
                         <xsl:apply-templates select="node()|@*"/>
                      </xsl:copy>
                   </xsl:template>

                  <xsl:template match="/domain/devices">
                    <xsl:copy>
                        <xsl:apply-templates select="node()|@*"/>

                            <xsl:element name="disk">
                                <xsl:attribute name="type">network</xsl:attribute>
                                <xsl:attribute name="device">disk</xsl:attribute>
                                <xsl:element name="driver">
                                    <xsl:attribute name="name">qemu</xsl:attribute>
                                    <xsl:attribute name="type">raw</xsl:attribute>
                                </xsl:element>
                                <xsl:element name="source">
                                    <xsl:attribute name="protocol">iscsi</xsl:attribute>
                                    <xsl:attribute name="name">iqn.2021-10.de.foobar:vm0123/1</xsl:attribute>
                                    <xsl:element name="host">
                                        <xsl:attribute name="name">10.100.1.1</xsl:attribute>
                                        <xsl:attribute name="port">3260</xsl:attribute>
                                    </xsl:element>
                                </xsl:element>
                                <xsl:element name="target">
                                    <xsl:attribute name="dev">vdc</xsl:attribute>
                                    <xsl:attribute name="bus">virtio</xsl:attribute>
                                </xsl:element>
                            </xsl:element>

                    </xsl:copy>
                  </xsl:template>

                </xsl:stylesheet>
            EOT
        }
    }

  # libvirt_volume.ubuntu-base will be created
  + resource "libvirt_volume" "ubuntu-base" {
      + format = "qcow2"
      + id     = (known after apply)
      + name   = "ubuntu-focal-base.qcow2"
      + pool   = "default"
      + size   = (known after apply)
      + source = "/opt/focal-server-cloudimg-amd64.img"
    }

  # libvirt_volume.ubuntu-vol will be created
  + resource "libvirt_volume" "ubuntu-vol" {
      + base_volume_id = (known after apply)
      + format         = (known after apply)
      + id             = (known after apply)
      + name           = "vm0123__vm-hostname.qcow2"
      + pool           = "images"
      + size           = 21474836480
    }

Plan: 4 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

libvirt_cloudinit_disk.cloud-init: Creating...
libvirt_volume.ubuntu-base: Creating...
libvirt_cloudinit_disk.cloud-init: Creation complete after 0s [id=/opt/images/cloud-init/cloud-init-vm-hostname.iso;a0925e03-1d8e-4be2-a4c4-967b387d426f]
libvirt_volume.ubuntu-base: Creation complete after 6s [id=/var/lib/libvirt/images/ubuntu-focal-base.qcow2]
libvirt_volume.ubuntu-vol: Creating...
libvirt_volume.ubuntu-vol: Creation complete after 1s [id=/opt/images/vm0123__vm-hostname.qcow2]
libvirt_domain.ubuntu-vm: Creating...
╷
│ Error: parse "iscsi://10.100.1.1:3260iqn.2021-10.foo.domain:vm0123/1": invalid port ":vm0123" after host
│ 
│   with libvirt_domain.ubuntu-vm,
│   on main.tf line 31, in resource "libvirt_domain" "ubuntu-vm":
│   31: resource "libvirt_domain" "ubuntu-vm" {
│ 
╵

Additional information:

Because of missing support of icsci network disks in this version of libvirt-provider, I add this kind of disk with "xml injection" like described in the examples. The VM will be deployed and is working with the iscsi disk, but my state is broken because the libvirt parser ist not working correctly. from now "terraform plan" or "terraform destroy" are not working.