Skip to content

Commit

Permalink
Merge pull request #152 from redhatci/kvirt_cluster
Browse files Browse the repository at this point in the history
Add kvirt VM role
  • Loading branch information
betoredhat authored Feb 9, 2024
2 parents 73c7d7f + b3ad0b0 commit ddd0893
Show file tree
Hide file tree
Showing 7 changed files with 353 additions and 0 deletions.
115 changes: 115 additions & 0 deletions roles/kvirt_vm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# kvirt_vm role

This role allows the deployment of Kubevirt virtual machines. In order to execute the role a running OpenShift cluster and its credentials are required. i.e. through the KUBECONFIG environment variable.

The role is aimed for deployment of virtual nodes for OCP deployments, so only the root disk is created.

```shell
export KUBECONFIG=<path_kubeconfig>
```

Main tasks:
- Validate the storage class
- Check the required operators (CNV)
- Create the VMs and set it to the desired state

This role has been tested only in x86_64 architectures.

# Role variables
| Variable | Required | Type | Default | Description
| ----------------------------- | --------- | ------- | --------- | -----------
| kvirt_vm_config_file | Yes | String | Undefined | The configuration file with the VM's settings

## VM configs
| Variable | Default | Required | Description
| ----------------------------- | ----------------------------- | ----------- | ---------------------------------
| vms_settings | | yes | A list with the settings for each VM
| name | | yes | VM name
| force | false | no | Destroy the VM if already
| namespace | default | no | VM namespace
| storage_class | <default> | no | Root disk storage class
| memory | 8Gi | no | VM memory
| disk_mode | ReadWriteOnce | no | VM disk volume mode
| disk_size | 60Gi | no | Root disk size
| os | rhcos | no | VM Operating system annotation
| cpu_cores | 8 | no | VM CPU cores
| cpu_sockets | 1 | no | VM CPU sockets
| cpu_threads | 1 | no | VM CPU threads
| network_interface_multiqueue | true | no | Enable NIC multiqueue
| running | false | no | Set the initial VM power state
| node_selector | | no | Configure nodes selector
| interfaces | virtio/masquerade | no | Network interface definitions
| networks | Pod network | no | VM network definitions

## Usage examples

See below for some examples of how to use the kvirt_vm role to create a VM.

Deploy a VM with custom configs

```yaml
- name: "Create a kvirt VM"
vars:
vms_config_file: /path/to/vms-config-file.yaml
ansible.builtin.include_role:
name: redhatci.ocp.kvirt_vm
```
Three VMs with default settings
```yaml
---
vm_configs:
- name: master-0
- name: master-1
- name: master-2
```
Generic VM definition
```yaml
---
vm_configs:
- name: test
force: true
namespace: myns
memory: 8Gi
disk_mode: ReadWriteOnce
disk_size: 60Gi
os: rhcos
cpu_cores: 8
cpu_sockets: 1
cpu_threads: 1
network_interface_multiqueue: true
running: false
node_selector:
kubernetes.io/hostname: master-1
interfaces:
- masquerade: {}
model: virtio
name: default
networks:
- name: default
pod: {}
ansible.builtin.include_role:
name: redhatci.ocp.kvirt_vm
```
VM config file with SRIOV settings
```yaml
---
vm_configs:
- name: master-1
interfaces:
- macAddress: "54:54:00:00:21:20"
name: sriov_resource_name_0
sriov: {}
- macAddress: "54:54:00:00:21:21"
name: sriov_resource_name_1
sriov: {}
networks:
- multus:
networkName: sriov-network-name-0
name: sriov_resource_name_0
- multus:
networkName: sriov-network-name-1
name: sriov_resource_name_1
```
21 changes: 21 additions & 0 deletions roles/kvirt_vm/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
kvirt_vm_api_version: kubevirt.io/v1
kvirt_vm_namespace: default
kvirt_vm_memory: 8Gi
kvirt_vm_disk_mode: ReadWriteOnce
kvirt_vm_disk_size: 60Gi
kvirt_vm_os: rhcos
kvirt_vm_cpu_cores: 8
kvirt_vm_cpu_sockets: 1
kvirt_vm_cpu_threads: 1
kvirt_vm_network_interface_multiqueue: true
kvirt_vm_running: false
kvirt_vm_force: false
kvirt_vm_interfaces:
- masquerade: {}
model: virtio
name: default
kvirt_vm_networks:
- name: default
pod: {}
...
35 changes: 35 additions & 0 deletions roles/kvirt_vm/tasks/create-vm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
- name: Create the VM Namespace for {{ vm.name }}
community.kubernetes.k8s:
definition:
apiVersion: v1
kind: Namespace
metadata:
name: "{{ vm.namespace | default(kvirt_vm_namespace) }}"
labels:
security.openshift.io/scc.podSecurityLabelSync: "false"
pod-security.kubernetes.io/enforce: "baseline"
pod-security.kubernetes.io/enforce-version: "latest"

- name: Create the VM {{ vm.name }}
community.kubernetes.k8s:
definition: "{{ lookup('template', 'templates/vm-template.yml.j2') }}"

- name: Wait for VM to be in desired state {{ vm.name }}
vars:
state: |-
{%- if vm.running | default(kvirt_vm_running) %}
Running{%- else %}
Stopped{%- endif %}
community.kubernetes.k8s_info:
api: "{{ vm.api_version | default(kvirt_vm_api_version) }}"
kind: VirtualMachine
name: "{{ vm.name }}"
namespace: "{{ vm.namespace | default(kvirt_vm_namespace) }}"
register: info
until:
- info.resources is defined
- info.resources[0].status.printableStatus == state
retries: 60
delay: 5
...
22 changes: 22 additions & 0 deletions roles/kvirt_vm/tasks/delete-vm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
- name: "Delete VM {{ vm.name }}"
community.kubernetes.k8s:
kind: VirtualMachine
api_version: "{{ vm.api_version | default(kvirt_vm_api_version) }}"
name: "{{ vm.name }}"
namespace: "{{ vm.namespace | default(kvirt_vm_namespace) }}"
state: absent

- name: Wait for VirtualMachine to be deleted
community.kubernetes.k8s_info:
api: "{{ vm.api_version | default(kvirt_vm_api_version) }}"
kind: VirtualMachine
name: "{{ vm.name }}"
namespace: "{{ vm.namespace | default(kvirt_vm_namespace) }}"
register: info
until:
- info.resources is defined
- info.resources | length == 0
retries: 60
delay: 5
...
19 changes: 19 additions & 0 deletions roles/kvirt_vm/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
- name: Run validations
ansible.builtin.include_tasks: validations.yml

- name: Delete VM
ansible.builtin.include_tasks: delete-vm.yml
when: vm.force | default(kvirt_vm_force) | bool
loop: "{{ vm_configs }}"
loop_control:
loop_var: vm
label: "{{ vm.name }}"

- name: Create the VM
ansible.builtin.include_tasks: create-vm.yml
loop: "{{ vm_configs }}"
loop_control:
loop_var: vm
label: "{{ vm.name }}"
...
77 changes: 77 additions & 0 deletions roles/kvirt_vm/tasks/validations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
- name: Check VM config file
ansible.builtin.stat:
path: "{{ kvirt_vm_config_file }}"
register: vms_conf_file_stat

- name: Fail if VM config file is not found
ansible.builtin.fail:
msg: "VM config file {{ kvirt_vm_config_file }} not found"
when: not vms_conf_file_stat.stat.exists

- name: Fail VM config file is empty
ansible.builtin.fail:
msg: "VM config file {{ kvirt_vm_config_file }} is empty"
when: vms_conf_file_stat.stat.size == 0

- name: Load VM config file
ansible.builtin.include_vars:
file: "{{ kvirt_vm_config_file }}"
when: vms_conf_file_stat.stat.exists

- name: Fail if VM config file does not contain vm_configs
ansible.builtin.fail:
msg: "VM config file {{ kvirt_vm_config_file }} does not contain vm_configs"
when: vm_configs is not defined

- name: Fail if not all VMs have a name
ansible.builtin.fail:
msg: "At least a VM in config file {{ kvirt_vm_config_file }} does not contain a name"
when: vm_configs | selectattr('name', 'undefined') | list | length > 0

- name: "Get Storage Classes"
community.kubernetes.k8s_info:
api_version: v1
kind: StorageClass
register: sc
no_log: true

- name: "Fail when there is no storage class available"
ansible.builtin.fail:
msg: "A storage class does not exists"
when:
- sc.resources | length == 0

- name: "Fail when defined storage class does not exist"
vars:
query_sc_name: 'resources[*].metadata.name'
query_results: "{{ sc | json_query(query_sc_name) }}"
ansible.builtin.fail:
msg: "The defined storage class does not exist"
when:
- kvirt_cluster_storage_class is defined
- kvirt_cluster_storage_class not in query_results

- name: "Fail when no default storage class"
vars:
query_default_sc: 'resources[*].metadata.annotations."storageclass.kubernetes.io/is-default-class"'
query_results: "{{ sc | json_query(query_default_sc) }}"
ansible.builtin.fail:
msg: "No default storage class was found"
when:
- sc is defined
- "not('true' in query_results)"
- kvirt_cluster_storage_class is undefined

- name: "Check if the CNV CRD is present"
community.kubernetes.k8s_info:
kind: CustomResourceDefinition
name: kubevirts.kubevirt.io
register: kvirt_crd
no_log: true

- name: "Fail if CNV CRD is not present"
fail:
msg: "CRDs are not present"
when: kvirt_crd.resources | list | count == 0
...
64 changes: 64 additions & 0 deletions roles/kvirt_vm/templates/vm-template.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
apiVersion: "{{ vm.api_version | default(kvirt_vm_api_version) }}"
kind: "VirtualMachine"
metadata:
name: "{{ vm.name }}"
namespace: "{{ vm.namespace | default(kvirt_vm_namespace) }}"
spec:
dataVolumeTemplates:
- apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
annotations:
cdi.kubevirt.io/storage.bind.immediate.requested: "true"
name: os-disk-{{ vm.namespace | default(kvirt_vm_namespace) }}-{{ vm.name }}
spec:
accessModes: [ "{{ vm.disk_mode | default(kvirt_vm_disk_mode) }}" ]
{% if storage_class is defined %}
storageClassName: "{{ vm.storage_class | default(kvirt_vm_storage_class) }}"
{% endif %}
source:
blank: {}
storage:
resources:
requests:
storage: "{{ vm.disk_size | default(kvirt_vm_disk_size) }}"
running: {{ vm.running | default(kvirt_vm_running) }}
template:
metadata:
annotations:
vm.kubevirt.io/os: "{{ vm.os | default(kvirt_vm_os) }}"
labels:
kubevirt.io/domain: "{{ vm.namespace | default(kvirt_vm_namespace) }}"
spec:
{% if node_selector is defined %}
nodeSelector: {{ vm.node_selector }}
{% endif %}
architecture: amd64
domain:
cpu:
cores: {{ vm.cpu_cores | default(kvirt_vm_cpu_cores) }}
sockets: {{ vm.cpu_sockets | default(kvirt_vm_cpu_sockets) }}
threads: {{ vm.cpu_threads | default(kvirt_vm_cpu_threads) }}
devices:
disks:
- disk:
bus: virtio
name: rootdisk
interfaces: {{ vm.interfaces | default(kvirt_vm_interfaces) }}
networkInterfaceMultiqueue: {{ vm.network_interface_multiqueue | default(kvirt_vm_network_interface_multiqueue) }}
rng: {}
features:
acpi: {}
smm:
enabled: true
firmware:
bootloader:
efi: {}
memory:
guest: "{{ vm.memory | default(kvirt_vm_memory)}}"
resources: {}
networks: {{ vm.networks | default(kvirt_vm_networks) }}
volumes:
- dataVolume:
name: os-disk-{{ vm.namespace | default(kvirt_vm_namespace) }}-{{ vm.name }}
name: rootdisk

0 comments on commit ddd0893

Please sign in to comment.