aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorspmfox <spmfox@foxwd.com>2023-05-28 10:02:07 -0400
committerspmfox <spmfox@foxwd.com>2023-05-28 10:02:07 -0400
commitff35c64d337a286ac2864c8403c1840e28bb23b1 (patch)
tree36062b6fbe326b61c5abaa11721f4a909364af5a
parentefebf42259944acef6389937736995a0af8c17d5 (diff)
Initial commit
-rw-r--r--collections/requirements.yml3
-rw-r--r--group_vars/all13
-rw-r--r--roles/libvirt/tasks/vm-check-duplicate.yml10
-rw-r--r--roles/libvirt/tasks/vm-check-exists.yml10
-rw-r--r--roles/libvirt/tasks/vm-confirm-info.yml28
-rw-r--r--roles/libvirt/tasks/vm-install.yml12
-rw-r--r--roles/libvirt/tasks/vm-undefine.yml9
-rw-r--r--roles/libvirt/templates/kickstart/el8.ks51
-rw-r--r--roles/libvirt/templates/kickstart/el9.ks51
-rw-r--r--roles/libvirt/vars/main.yml15
-rw-r--r--roles/zfs/tasks/dataset-check-duplicate.yml11
-rw-r--r--roles/zfs/tasks/dataset-check-exists.yml3
-rw-r--r--roles/zfs/tasks/dataset-confirm-info.yml10
-rw-r--r--roles/zfs/tasks/dataset-create.yml4
-rw-r--r--roles/zfs/tasks/dataset-destroy.yml4
-rw-r--r--roles/zfs/vars/main.yml1
-rw-r--r--vm-create.yml19
-rw-r--r--vm-delete.yml27
18 files changed, 281 insertions, 0 deletions
diff --git a/collections/requirements.yml b/collections/requirements.yml
new file mode 100644
index 0000000..7588f8b
--- /dev/null
+++ b/collections/requirements.yml
@@ -0,0 +1,3 @@
+collections:
+ - name: community.general
+ - name: community.libvirt
diff --git a/group_vars/all b/group_vars/all
new file mode 100644
index 0000000..26a6a7c
--- /dev/null
+++ b/group_vars/all
@@ -0,0 +1,13 @@
+vm_name: ""
+parent_dataset: ""
+memory_mb: 2048
+cpus: 1
+disk_gb: 20
+disk_format: "raw"
+os: "rhel-unknown"
+kickstart: ""
+iso_path: ""
+network: ""
+timezone: "America/New_York"
+root_password: "{{ lookup('password', '/dev/null length=32 chars=ascii_letters,digits') }}"
+root_ssh_key: ""
diff --git a/roles/libvirt/tasks/vm-check-duplicate.yml b/roles/libvirt/tasks/vm-check-duplicate.yml
new file mode 100644
index 0000000..5070cfd
--- /dev/null
+++ b/roles/libvirt/tasks/vm-check-duplicate.yml
@@ -0,0 +1,10 @@
+- name: Fetch list of all VMs
+ community.libvirt.virt:
+ command: list_vms
+ register: vms_list
+
+- name: Fail if VM exists
+ ansible.builtin.fail:
+ msg: "VM {{ libvirt_vm_name }} is already in-use."
+ when: libvirt_vm_name | string in vms_list.list_vms
+
diff --git a/roles/libvirt/tasks/vm-check-exists.yml b/roles/libvirt/tasks/vm-check-exists.yml
new file mode 100644
index 0000000..c6adf31
--- /dev/null
+++ b/roles/libvirt/tasks/vm-check-exists.yml
@@ -0,0 +1,10 @@
+- name: Fetch list of all VMs
+ community.libvirt.virt:
+ command: list_vms
+ register: vms_list
+
+- name: Fail if VM does not exist
+ ansible.builtin.fail:
+ msg: "VM {{ libvirt_vm_name }} does not exist."
+ when: libvirt_vm_name | string not in vms_list.list_vms
+
diff --git a/roles/libvirt/tasks/vm-confirm-info.yml b/roles/libvirt/tasks/vm-confirm-info.yml
new file mode 100644
index 0000000..3121354
--- /dev/null
+++ b/roles/libvirt/tasks/vm-confirm-info.yml
@@ -0,0 +1,28 @@
+- name: Fetch VM information
+ community.libvirt.virt:
+ command: get_xml
+ name: "{{ libvirt_vm_name }}"
+ register: vm_info
+
+- name: Parse VM information
+ community.general.xml:
+ xmlstring: "{{ vm_info.get_xml }}"
+ xpath: "/domain/devices/disk/source"
+ content: attribute
+ register: vm_xml_output
+
+- name: Fail if expected disk is not found in VM XML
+ ansible.builtin.fail:
+ msg: "{{ libvirt_vm_destination }} was not found in VM disk definition {{ vm_xml_output.matches[0].source.file }}"
+ when: libvirt_vm_destination not in vm_xml_output.matches[0].source.file
+
+- name: Check filesystem for expected VM disk
+ ansible.builtin.stat:
+ path: "/{{ libvirt_vm_destination }}/{{ libvirt_vm_name }}.img"
+ get_checksum: false
+ register: vm_check_image_exists
+
+- name: Fail if expected VM disk is not found on destination filesystem
+ ansible.builtin.fail:
+ msg: "Expected VM disk /{{ libvirt_vm_destination }}/{{ libvirt_vm_name }}.img not found on filesystem."
+ when: not vm_check_image_exists.stat.exists
diff --git a/roles/libvirt/tasks/vm-install.yml b/roles/libvirt/tasks/vm-install.yml
new file mode 100644
index 0000000..a194ae7
--- /dev/null
+++ b/roles/libvirt/tasks/vm-install.yml
@@ -0,0 +1,12 @@
+- name: Copy kickstart file to destination filesystem
+ ansible.builtin.template:
+ src: "kickstart/{{ libvirt_vm_kickstart_file }}"
+ dest: "/{{ libvirt_vm_destination }}/{{ libvirt_vm_kickstart_file }}"
+
+- name: Create VM in destination filesystem
+ ansible.builtin.command: 'virt-install --name {{ libvirt_vm_name }} --memory {{ libvirt_vm_memory }} --vcpus {{ libvirt_vm_vcpus }} --network {{ libvirt_vm_network }} --disk size={{ libvirt_vm_disk_size }},path=/{{ libvirt_vm_destination }}/{{ libvirt_vm_name }}.img,format={{ libvirt_vm_disk_format }} --location {{ libvirt_vm_iso_path }} --os-variant {{ libvirt_vm_os }} --initrd-inject=/{{ libvirt_vm_destination }}/{{ libvirt_vm_kickstart_file }} --extra-args="inst.ks=file:/{{ libvirt_vm_kickstart_file }}"'
+
+- name: Remove kickstart file from destination filesystem
+ ansible.builtin.file:
+ path: "/{{ libvirt_vm_destination }}/{{ libvirt_vm_kickstart_file }}"
+ state: absent
diff --git a/roles/libvirt/tasks/vm-undefine.yml b/roles/libvirt/tasks/vm-undefine.yml
new file mode 100644
index 0000000..a43b12b
--- /dev/null
+++ b/roles/libvirt/tasks/vm-undefine.yml
@@ -0,0 +1,9 @@
+- name: Destroy VM
+ community.libvirt.virt:
+ name: "{{ libvirt_vm_name }}"
+ state: destroyed
+
+- name: Undefine VM
+ community.libvirt.virt:
+ name: "{{ libvirt_vm_name }}"
+ command: undefine
diff --git a/roles/libvirt/templates/kickstart/el8.ks b/roles/libvirt/templates/kickstart/el8.ks
new file mode 100644
index 0000000..d574837
--- /dev/null
+++ b/roles/libvirt/templates/kickstart/el8.ks
@@ -0,0 +1,51 @@
+#version=RHEL8
+text
+reboot
+
+repo --name="AppStream" --baseurl=file:///run/install/sources/mount-0000-cdrom/AppStream
+
+%packages
+@^server-product-environment
+kexec-tools
+
+%end
+
+# Keyboard layouts
+keyboard --xlayouts='us'
+# System language
+lang en_US.UTF-8
+
+# Network information
+network --bootproto=dhcp --device=enp1s0 --noipv6 --activate
+network --hostname={{ libvirt_kickstart_hostname }}
+
+# Use CDROM installation media
+cdrom
+
+# Run the Setup Agent on first boot
+firstboot --enable
+
+ignoredisk --only-use=vda
+autopart
+# Partition clearing information
+clearpart --none --initlabel
+
+# System timezone
+timezone {{ libvirt_kickstart_timezone }} --isUtc
+
+rootpw --iscrypted {{ libvirt_kickstart_root_password | password_hash("sha512") }}
+
+%post
+mkdir -m0700 /root/.ssh/
+
+cat <<EOF >/root/.ssh/authorized_keys
+{{ libvirt_kickstart_root_ssh_key }}
+EOF
+
+### set permissions
+chmod 0600 /root/.ssh/authorized_keys
+
+### fix up selinux context
+restorecon -R /root/.ssh/
+
+%end
diff --git a/roles/libvirt/templates/kickstart/el9.ks b/roles/libvirt/templates/kickstart/el9.ks
new file mode 100644
index 0000000..4f57464
--- /dev/null
+++ b/roles/libvirt/templates/kickstart/el9.ks
@@ -0,0 +1,51 @@
+#version=RHEL9
+text
+reboot
+
+repo --name="AppStream" --baseurl=file:///run/install/sources/mount-0000-cdrom/AppStream
+
+%packages
+@^server-product-environment
+kexec-tools
+
+%end
+
+# Keyboard layouts
+keyboard --xlayouts='us'
+# System language
+lang en_US.UTF-8
+
+# Network information
+network --bootproto=dhcp --device=enp1s0 --noipv6 --activate
+network --hostname={{ libvirt_kickstart_hostname }}
+
+# Use CDROM installation media
+cdrom
+
+# Run the Setup Agent on first boot
+firstboot --enable
+
+ignoredisk --only-use=vda
+autopart
+# Partition clearing information
+clearpart --none --initlabel
+
+# System timezone
+timezone {{ libvirt_kickstart_timezone }} --isUtc
+
+rootpw --iscrypted {{ libvirt_kickstart_root_password | password_hash("sha512") }}
+
+%post
+mkdir -m0700 /root/.ssh/
+
+cat <<EOF >/root/.ssh/authorized_keys
+{{ libvirt_kickstart_root_ssh_key }}
+EOF
+
+### set permissions
+chmod 0600 /root/.ssh/authorized_keys
+
+### fix up selinux context
+restorecon -R /root/.ssh/
+
+%end
diff --git a/roles/libvirt/vars/main.yml b/roles/libvirt/vars/main.yml
new file mode 100644
index 0000000..e809f48
--- /dev/null
+++ b/roles/libvirt/vars/main.yml
@@ -0,0 +1,15 @@
+libvirt_vm_name: "{{ vm_name }}"
+libvirt_vm_memory: "{{ memory_mb }}"
+libvirt_vm_vcpus: "{{ cpus }}"
+libvirt_vm_disk_size: "{{ disk_gb }}"
+libvirt_vm_disk_format: "{{ disk_format }}"
+libvirt_vm_os: "{{ os }}"
+libvirt_vm_kickstart_file: "{{ kickstart }}"
+libvirt_vm_iso_path: "{{ iso_path }}"
+libvirt_vm_destination: "{{ parent_dataset }}/{{ vm_name }}"
+libvirt_vm_network: "{{ network }}"
+libvirt_kickstart_hostname: "{{ vm_name }}"
+libvirt_kickstart_timezone: "{{ timezone }}"
+libvirt_kickstart_root_ssh_key: "{{ root_ssh_key }}"
+libvirt_kickstart_root_password: "{{ root_password }}"
+
diff --git a/roles/zfs/tasks/dataset-check-duplicate.yml b/roles/zfs/tasks/dataset-check-duplicate.yml
new file mode 100644
index 0000000..1272765
--- /dev/null
+++ b/roles/zfs/tasks/dataset-check-duplicate.yml
@@ -0,0 +1,11 @@
+- name: Check if ZFS dataset exists
+ community.general.zfs_facts:
+ dataset: "{{ zfs_dataset }}"
+ register: zfs_dataset_exists_check
+ ignore_errors: true
+
+- name: Fail if ZFS dataset already exists
+ ansible.builtin.fail:
+ msg: "Dataset {{ zfs_dataset }} is already in-use."
+ when: not zfs_dataset_exists_check.failed
+
diff --git a/roles/zfs/tasks/dataset-check-exists.yml b/roles/zfs/tasks/dataset-check-exists.yml
new file mode 100644
index 0000000..43de7d4
--- /dev/null
+++ b/roles/zfs/tasks/dataset-check-exists.yml
@@ -0,0 +1,3 @@
+- name: Check if ZFS dataset exists
+ community.general.zfs_facts:
+ dataset: "{{ zfs_dataset }}"
diff --git a/roles/zfs/tasks/dataset-confirm-info.yml b/roles/zfs/tasks/dataset-confirm-info.yml
new file mode 100644
index 0000000..65885fb
--- /dev/null
+++ b/roles/zfs/tasks/dataset-confirm-info.yml
@@ -0,0 +1,10 @@
+- name: Fetch ZFS dataset information
+ community.general.zfs_facts:
+ dataset: "{{ zfs_dataset }}"
+ register: dataset_facts
+
+- name: Fail if ZFS dataset contains child datasets
+ ansible.builtin.fail:
+ msg: "Dataset {{ zfs_dataset }} contains child datasets."
+ when: dataset_facts.ansible_facts.ansible_zfs_datasets[0].usedbychildren != "0B"
+
diff --git a/roles/zfs/tasks/dataset-create.yml b/roles/zfs/tasks/dataset-create.yml
new file mode 100644
index 0000000..37c6e6b
--- /dev/null
+++ b/roles/zfs/tasks/dataset-create.yml
@@ -0,0 +1,4 @@
+- name: Create ZFS dataset
+ community.general.zfs:
+ name: "{{ zfs_dataset }}"
+ state: present
diff --git a/roles/zfs/tasks/dataset-destroy.yml b/roles/zfs/tasks/dataset-destroy.yml
new file mode 100644
index 0000000..9159f20
--- /dev/null
+++ b/roles/zfs/tasks/dataset-destroy.yml
@@ -0,0 +1,4 @@
+- name: Destroy ZFS dataset and all snapshots
+ community.general.zfs:
+ name: "{{ zfs_dataset }}"
+ state: absent
diff --git a/roles/zfs/vars/main.yml b/roles/zfs/vars/main.yml
new file mode 100644
index 0000000..a53eb98
--- /dev/null
+++ b/roles/zfs/vars/main.yml
@@ -0,0 +1 @@
+zfs_dataset: "{{ parent_dataset }}/{{ vm_name }}"
diff --git a/vm-create.yml b/vm-create.yml
new file mode 100644
index 0000000..323e11e
--- /dev/null
+++ b/vm-create.yml
@@ -0,0 +1,19 @@
+- hosts: all
+ become: true
+
+ tasks:
+ - ansible.builtin.include_role:
+ name: zfs
+ tasks_from: dataset-check-duplicate.yml
+
+ - ansible.builtin.include_role:
+ name: libvirt
+ tasks_from: vm-check-duplicate.yml
+
+ - ansible.builtin.include_role:
+ name: zfs
+ tasks_from: dataset-create.yml
+
+ - ansible.builtin.include_role:
+ name: libvirt
+ tasks_from: vm-install.yml
diff --git a/vm-delete.yml b/vm-delete.yml
new file mode 100644
index 0000000..fb720e4
--- /dev/null
+++ b/vm-delete.yml
@@ -0,0 +1,27 @@
+- hosts: all
+ become: true
+
+ tasks:
+ - ansible.builtin.include_role:
+ name: zfs
+ tasks_from: dataset-check-exists.yml
+
+ - ansible.builtin.include_role:
+ name: libvirt
+ tasks_from: vm-check-exists.yml
+
+ - ansible.builtin.include_role:
+ name: libvirt
+ tasks_from: vm-confirm-info.yml
+
+ - ansible.builtin.include_role:
+ name: zfs
+ tasks_from: dataset-confirm-info.yml
+
+ - ansible.builtin.include_role:
+ name: libvirt
+ tasks_from: vm-undefine.yml
+
+ - ansible.builtin.include_role:
+ name: zfs
+ tasks_from: dataset-destroy.yml