ansible: Manage libvirt on CI 'hosts'
authorKienan Stewart <kstewart@efficios.com>
Wed, 7 Jun 2023 14:51:14 +0000 (10:51 -0400)
committerKienan Stewart <kstewart@efficios.com>
Wed, 7 Jun 2023 14:54:11 +0000 (10:54 -0400)
Change-Id: I970fe82d65de551ff51568f1ab72c5331cad3e57

.gitignore
automation/ansible/README.md
automation/ansible/hosts.yml
automation/ansible/roles/libvirt/files/rootnode-autoinstall.yml [new file with mode: 0644]
automation/ansible/roles/libvirt/files/user-data [new symlink]
automation/ansible/roles/libvirt/tasks/main.yml [new file with mode: 0644]
automation/ansible/roles/libvirt/templates/vm_template.xml.j2 [new file with mode: 0644]
automation/ansible/roles/libvirt/vars/main.yml [new file with mode: 0644]

index a4670d6e4731c7f2864f08f26ee60e59c69d898a..e64f1a9e975051617da7de9ad1c63ea0a192f1b9 100644 (file)
@@ -5,3 +5,5 @@
 *.retry
 lava/pdudaemon/venv/
 lava/pdudaemon/pdudaemon.db
+automation/ansible/roles/libvirt/files/meta-data
+automation/ansible/roles/libvirt/files/vendor-data
\ No newline at end of file
index 1a6c651371d6274685ed0ea37413662ba0d6a50a..0965062f9115cb1a3d5fed145f202fa0ccd7d7c3 100644 (file)
@@ -38,3 +38,32 @@ ansible-playbook -i hosts [-l SUBSET] site.yaml
 1. Configure either SSH or WinRM connection: see https://docs.ansible.com/ansible/latest/os_guide/windows_setup.html
 2. For arm64 hosts:
   * Install the necessary optional features (eg. OpenSSH, Hyper-V) since Windows RSAT isn't available on Arm64 yet
+
+## CI 'rootnode'
+
+1. Add an entry to the `vms` variable in the host vars for a libvirt host
+  * See the defaults and details in `roles/libvirt/vars/main.yml` and `roles/libvirt/tasks/main.yml`
+  * Make sure to set the `cdrom` key to the path of ISO for the installer
+2. Run the playbook, eg. `ansible-playbook -i hosts -l cloud07.internal.efficios.com site.yml`
+  * The VM should be created and started
+3. Once the VM is installed take a snapshot so that Jenkins may revert to the original state
+
+### Ubuntu auto-installer
+
+1. Note your IP address
+2. Switch to the directory with the user-data files: `cd roles/libvirt/files`
+3. Write out the instance-specific metadata, eg.
+
+```
+cat > meta-data <<EOF
+instance-id: iid-XXX
+hostname: XXX.internal.efficios.com
+EOF
+```
+  * The instance-id is used to determine if re-installation is necessary.
+4. Start a python web server: `python3 -m http.server 3003`
+5. Connect to the VM using a remote viewer on the address given by `virsh --connect qemu+ssh://root@host/system domdisplay`
+6. Edit the grub boot options for the installer and append the following as arguments for the kernel: `autoinstall 'ds=nocloud-net;s=http://IPADDRESS:3003/'` and boot the installer
+  * Note that the trailing `/` and quoting are important
+  * The will load the `user-data`, `meta-data`, and `vendor-data` files in the directory served by the python web server
+7. After the installation is complete, the system will reboot and run cloud-init for the final portion of the initial setup. Once completed, ansible can be run against it using the ubuntu user and becoming root, eg. `ansible-playbook -i hosts -u ubuntu -b ...`
index c02b6df0e1c2827ac7fc9ccae86be2258e07b870..db00eefae46b75f18d0ecc3ca54a848256566ca4 100644 (file)
@@ -1,3 +1,4 @@
 - hosts: hosts
   roles:
     - common
+    - libvirt
diff --git a/automation/ansible/roles/libvirt/files/rootnode-autoinstall.yml b/automation/ansible/roles/libvirt/files/rootnode-autoinstall.yml
new file mode 100644 (file)
index 0000000..de90398
--- /dev/null
@@ -0,0 +1,85 @@
+## template: jinja
+#cloud-config
+# @see https://github.com/canonical/subiquity
+autoinstall:
+  apt:
+    disable_components: []
+    geoip: true
+    preserve_sources_list: false
+    primary:
+    - arches:
+      - amd64
+      - i386
+      uri: http://ca.archive.ubuntu.com/ubuntu
+    - arches:
+      - default
+      uri: http://ports.ubuntu.com/ubuntu-ports
+  drivers:
+    install: false
+  kernel:
+    package: linux-generic
+  keyboard:
+    layout: us
+    toggle: null
+    variant: ''
+  locale: en_US.UTF-8
+  network:
+    ethernets:
+      enp1s0:
+        dhcp4: true
+    version: 2
+  source:
+    id: ubuntu-server
+    search_drivers: false
+  ssh:
+    allow-pw: false
+    install-server: true
+    authorized-keys:
+      # yamllint disable-line rule:line-length
+      - 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBnCyGcahJXys7md2yb3jP8L6hLN3D72aZCzsqUrJDsC kstewart@laptop-kstewart'
+      # yamllint disable-line rule:line-length
+      - 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHVFn/ymsG8LqPvgVzyMaSVzYCVn/440ME8O6AzbZG39 mjeanson@laptop-mjeanson'
+  storage:
+    config:
+    - ptable: gpt
+      path: /dev/sda
+      wipe: superblock-recursive
+      preserve: false
+      name: ''
+      grub_device: true
+      type: disk
+      id: disk-sda
+    - device: disk-sda
+      size: 1048576
+      flag: bios_grub
+      number: 1
+      preserve: false
+      grub_device: false
+      offset: 1048576
+      type: partition
+      id: partition-0
+    - device: disk-sda
+      size: 85896200192
+      wipe: superblock
+      number: 2
+      preserve: false
+      grub_device: false
+      offset: 2097152
+      type: partition
+      id: partition-1
+    - fstype: ext4
+      volume: partition-1
+      preserve: false
+      type: format
+      id: format-0
+    - path: /
+      device: format-0
+      type: mount
+      id: mount-0
+  updates: security
+  user-data:
+    fqdn: "{{ds.meta_data.hostname}}"
+    prefer_fqdn_over_hostname: true
+    users:
+      default: {}
+  version: 1
diff --git a/automation/ansible/roles/libvirt/files/user-data b/automation/ansible/roles/libvirt/files/user-data
new file mode 120000 (symlink)
index 0000000..c0ec265
--- /dev/null
@@ -0,0 +1 @@
+rootnode-autoinstall.yml
\ No newline at end of file
diff --git a/automation/ansible/roles/libvirt/tasks/main.yml b/automation/ansible/roles/libvirt/tasks/main.yml
new file mode 100644 (file)
index 0000000..a23ce3b
--- /dev/null
@@ -0,0 +1,33 @@
+---
+- name: Install virtualization packages
+  apt:
+    name: ['qemu-kvm', 'libvirt-daemon-system']
+- name: Run libvirtd
+  systemd:
+    name: libvirtd
+    enabled: true
+    state: started
+- name: Download ISOs
+  loop: "{{ isos }}"
+  get_url:
+    dest: "{{item.dest}}"
+    url: "{{item.url}}"
+    checksum: "{{item.checksum}}"
+- name: Create VM disks
+  loop: "{{ lookup('vars', 'vms', default=[]) }}"
+  vars:
+    vm: "{{ vm_defaults | combine(item.vars) }}"
+  when: vm.disk != ""
+  shell:
+    cmd: "qemu-img create -f qcow2 {{vm.disk}} {{vm.disk_capacity}}"
+    creates: "{{vm.disk}}"
+- name: Define VMs
+  # Note: is vm.uuid is not set and the template is changed, those changes will not be applied
+  # Note: many changes will require the VM to be destroyed then started again
+  community.libvirt.virt:
+    command: define
+    xml: "{{ lookup('template', item.template|default('vm_template.xml.j2')) }}"
+    autostart: true
+  loop: "{{ lookup('vars', 'vms', default=[]) }}"
+  vars:
+    vm: "{{ vm_defaults | combine(item.vars) }}"
diff --git a/automation/ansible/roles/libvirt/templates/vm_template.xml.j2 b/automation/ansible/roles/libvirt/templates/vm_template.xml.j2
new file mode 100644 (file)
index 0000000..3313f30
--- /dev/null
@@ -0,0 +1,155 @@
+<domain type='kvm'>
+  <name>{{ vm.name }}</name>
+  {% if 'uuid' in vm %}
+  <uuid>{{ vm.uuid }}</uuid>
+  {% endif %}
+  <memory unit='KiB'>{{ vm.memory }}</memory>
+  <vcpu placement='static'>{{ vm.vcpu }}</vcpu>
+  <os>
+    <type arch='x86_64' machine='pc-q35-4.2'>hvm</type>
+    <bootmenu enable='yes'/>
+  </os>
+  <features>
+    <acpi/>
+    <apic/>
+    <vmport state='off'/>
+  </features>
+  <cpu mode='host-model' check='partial'/>
+  <clock offset='utc'>
+    <timer name='rtc' tickpolicy='catchup'/>
+    <timer name='pit' tickpolicy='delay'/>
+    <timer name='hpet' present='no'/>
+  </clock>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <pm>
+    <suspend-to-mem enabled='no'/>
+    <suspend-to-disk enabled='no'/>
+  </pm>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <disk type='file' device='cdrom'>
+      <driver name='qemu' type='raw'/>
+      <target dev='sda' bus='sata'/>
+      {% if 'cdrom' in vm %}
+      <source file='{{vm.cdrom}}'/>
+      {% endif %}
+      <readonly/>
+      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+      <boot order='2'/>
+    </disk>
+    {% if 'disk' in vm %}
+    <disk type='file' device='disk'>
+      <driver name='qemu' type='qcow2' discard='unmap'/>
+      <source file='{{vm.disk}}'/>
+      <target dev='sdb' bus='sata'/>
+      <address type='drive' controller='0' bus='0' target='0' unit='1'/>
+      <boot order='1'/>
+    </disk>
+    {% endif %}
+    <controller type='usb' index='0' model='ich9-ehci1'>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x7'/>
+    </controller>
+    <controller type='usb' index='0' model='ich9-uhci1'>
+      <master startport='0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x0' multifunction='on'/>
+    </controller>
+    <controller type='usb' index='0' model='ich9-uhci2'>
+      <master startport='2'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x1'/>
+    </controller>
+    <controller type='usb' index='0' model='ich9-uhci3'>
+      <master startport='4'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1d' function='0x2'/>
+    </controller>
+    <controller type='scsi' index='0' model='virtio-scsi'>
+      <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+    </controller>
+    <controller type='sata' index='0'>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
+    </controller>
+    <controller type='pci' index='0' model='pcie-root'/>
+    <controller type='pci' index='1' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='1' port='0x10'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
+    </controller>
+    <controller type='pci' index='2' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='2' port='0x11'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
+    </controller>
+    <controller type='pci' index='3' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='3' port='0x12'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
+    </controller>
+    <controller type='pci' index='4' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='4' port='0x13'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
+    </controller>
+    <controller type='pci' index='5' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='5' port='0x14'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
+    </controller>
+    <controller type='pci' index='6' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='6' port='0x15'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
+    </controller>
+    <controller type='virtio-serial' index='0'>
+      <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+    </controller>
+    <interface type='bridge'>
+      <mac/>
+      <source bridge='{{vm.net_bridge}}'/>
+      <model type='virtio'/>
+      <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+    </interface>
+    <serial type='pty'>
+      <target type='isa-serial' port='0'>
+        <model name='isa-serial'/>
+      </target>
+    </serial>
+    <console type='pty'>
+      <target type='serial' port='0'/>
+    </console>
+    <channel type='unix'>
+      <target type='virtio' name='org.qemu.guest_agent.0'/>
+      <address type='virtio-serial' controller='0' bus='0' port='1'/>
+    </channel>
+    <channel type='spicevmc'>
+      <target type='virtio' name='com.redhat.spice.0'/>
+      <address type='virtio-serial' controller='0' bus='0' port='2'/>
+    </channel>
+    <input type='tablet' bus='usb'>
+      <address type='usb' bus='0' port='1'/>
+    </input>
+    <input type='mouse' bus='ps2'/>
+    <input type='keyboard' bus='ps2'/>
+    <graphics type='spice' autoport='yes'>
+      <listen type='address' address='0.0.0.0'/>
+    </graphics>
+    <audio id='1' type='spice'/>
+    <video>
+      <model type='virtio' heads='1' primary='yes'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
+    </video>
+    <redirdev bus='usb' type='spicevmc'>
+      <address type='usb' bus='0' port='2'/>
+    </redirdev>
+    <redirdev bus='usb' type='spicevmc'>
+      <address type='usb' bus='0' port='3'/>
+    </redirdev>
+    <memballoon model='virtio'>
+      <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+    </memballoon>
+    <rng model='virtio'>
+      <backend model='random'>/dev/urandom</backend>
+      <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+    </rng>
+  </devices>
+</domain>
\ No newline at end of file
diff --git a/automation/ansible/roles/libvirt/vars/main.yml b/automation/ansible/roles/libvirt/vars/main.yml
new file mode 100644 (file)
index 0000000..4fb0be7
--- /dev/null
@@ -0,0 +1,15 @@
+---
+vm_defaults:
+  memory: 4194304
+  vcpu: 2
+  pool: default
+  disk_capacity: '80G'
+  net_bridge: 'br102'
+  # Non-default keys
+  # cdrom: /path/to/file.iso
+  # uuid: xxxx-yyyy
+  # name: xyz
+isos:
+  - url: https://releases.ubuntu.com/jammy/ubuntu-22.04.2-live-server-amd64.iso
+    checksum: "sha256:5e38b55d57d94ff029719342357325ed3bda38fa80054f9330dc789cd2d43931"
+    dest: /var/lib/libvirt/images/ubuntu-22.04.2-live-server-amd64.iso
This page took 0.027852 seconds and 4 git commands to generate.