Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# Community Code of Conduct

Please see the official
[Ansible Community Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html).
Please see the official [Ansible Community Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html).
675 changes: 675 additions & 0 deletions COPYING

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ This collection includes the following roles for operating OpenShift Virtualizat
<!--ROLES_LIST_START-->
* [vm_backup_restore](roles/vm_backup_restore/README.md) - Virtual Machine backup and restore capabilities.
* [vm_collect](roles/vm_collect/README.md) - Collection of Migration Toolkit for Virtualization inventory information.
* [vm_healthcheck](roles/vm_healthcheck/README.md) - Health validation and status reporting for Virtual Machines.
* [vm_hot_plug](roles/vm_hot_plug/README.md) - Hot Plug Virtual Machine resources.
* [vm_lifecycle](roles/vm_lifecycle/README.md) - Management of the lifecycle activities of Virtual Machines.
* [vm_networking](roles/vm_networking/README.md) - Management of Virtual Machine networking.
Expand Down
15 changes: 15 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Security Policy

## Supported Versions

Ansible applies security fixes according to the 3-versions-back support
policy. Please find more information in [our docs](
https://docs.ansible.com/ansible/devel/reference_appendices/release_and_maintenance.html#release-status
).

## Reporting a Vulnerability

We encourage responsible disclosure practices for security
vulnerabilities. Please read our [policies for reporting bugs](
https://docs.ansible.com/ansible/devel/community/reporting_bugs_and_features.html#reporting-a-bug
) if you want to report a security issue that might affect Ansible.
15 changes: 15 additions & 0 deletions playbooks/vm_healthcheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
- name: Run VM Healthchecks
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Invoke VM Healthcheck Role
ansible.builtin.include_role:
name: infra.openshift_virtualization_ops.vm_healthcheck
tasks_from: _healthcheck.yml
vars:
openshift_host: "{{ lookup('ansible.builtin.env', 'K8S_AUTH_HOST', default=Undefined) | default('', True) }}"
openshift_api_key: "{{ lookup('ansible.builtin.env', 'K8S_AUTH_API_KEY', default=Undefined) | default('', True) }}" # noqa: yaml[line-length]
openshift_verify_ssl: "{{ lookup('ansible.builtin.env', 'K8S_AUTH_VERIFY_SSL', default='') | default(false) | bool }}" # noqa: yaml[line-length]
...
398 changes: 398 additions & 0 deletions roles/vm_healthcheck/README.md

Large diffs are not rendered by default.

62 changes: 62 additions & 0 deletions roles/vm_healthcheck/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
# defaults file for vm_healthcheck
# title: Target namespace
# required: True
# description: Namespace containing VMs to healthcheck
vm_healthcheck_namespace: ""

# title: List of VM names
# required: False
# description: List of VM names to check. If empty, all VMs in namespace are checked
vm_healthcheck_vm_names: []

# title: Check networking
# required: False
# description: Whether to run networking health checks
vm_healthcheck_check_networking: true

# title: Check storage
# required: False
# description: Whether to run storage health checks
vm_healthcheck_check_storage: true

# title: Check resources
# required: False
# description: Whether to run resource utilization checks
vm_healthcheck_check_resources: true

# title: Generate report
# required: False
# description: Whether to generate an HTML healthcheck report
vm_healthcheck_generate_report: true

# title: Report output path
# required: False
# description: File path for the generated HTML healthcheck report
vm_healthcheck_report_path: "/tmp/vm_healthcheck_report.html"

# title: KubeVirt API Version
# required: True
# description: KubeVirt API Version
vm_healthcheck_kubevirt_api_version: kubevirt.io/v1

# title: CDI API Version
# required: True
# description: CDI API Version for DataVolume lookups
vm_healthcheck_cdi_api_version: cdi.kubevirt.io/v1beta1

# title: OpenShift host
# required: True
# description: OpenShift host
vm_healthcheck_openshift_host: "{{ openshift_host }}"

# title: OpenShift API Key
# required: True
# description: OpenShift API Key
vm_healthcheck_openshift_api_key: "{{ openshift_api_key }}"

# title: Enable SSL Verification
# required: True
# description: Variable to enable SSL verification
vm_healthcheck_openshift_verify_ssl: "{{ openshift_verify_ssl }}"
...
10 changes: 10 additions & 0 deletions roles/vm_healthcheck/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
galaxy_info:
author: ""
description: Health validation and status reporting for Virtual Machines.
company: ""
license: GPL-3.0-only
min_ansible_version: 2.15.0
galaxy_tags: []
dependencies: []
...
118 changes: 118 additions & 0 deletions roles/vm_healthcheck/tasks/_healthcheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
---

- name: _healthcheck | Verify Required Parameters
ansible.builtin.assert:
that:
- vm_healthcheck_namespace | default("", true) | length > 0
- vm_healthcheck_openshift_host | default("", true) | length > 0
- vm_healthcheck_openshift_api_key | default("", true) | length > 0
quiet: true
fail_msg: "Required parameters not provided: namespace, openshift_host, and openshift_api_key are required"

- name: _healthcheck | Validate Report Destination
when: vm_healthcheck_generate_report | bool
block:
- name: _healthcheck | Check Report Directory Exists
ansible.builtin.stat:
path: "{{ vm_healthcheck_report_path | dirname }}"
register: _vm_healthcheck_report_dir

- name: _healthcheck | Assert Report Directory is Writable
ansible.builtin.assert:
that:
- _vm_healthcheck_report_dir.stat.exists
- _vm_healthcheck_report_dir.stat.writeable
quiet: true
fail_msg: >-
Report destination directory '{{ vm_healthcheck_report_path | dirname }}'
does not exist or is not writable. Set vm_healthcheck_report_path to a valid location.

- name: _healthcheck | Initialize Healthcheck Results
ansible.builtin.set_fact:
vm_healthcheck_results: []

- name: _healthcheck | Discover VMs in Namespace
kubernetes.core.k8s_info:
api_key: "{{ vm_healthcheck_openshift_api_key }}"
host: "{{ vm_healthcheck_openshift_host }}"
api_version: "{{ vm_healthcheck_kubevirt_api_version }}"
kind: VirtualMachine
namespace: "{{ vm_healthcheck_namespace }}"
validate_certs: "{{ vm_healthcheck_openshift_verify_ssl }}"
register: _vm_healthcheck_all_vms
when: vm_healthcheck_vm_names | length == 0

- name: _healthcheck | Discover Named VMs
kubernetes.core.k8s_info:
api_key: "{{ vm_healthcheck_openshift_api_key }}"
host: "{{ vm_healthcheck_openshift_host }}"
api_version: "{{ vm_healthcheck_kubevirt_api_version }}"
kind: VirtualMachine
namespace: "{{ vm_healthcheck_namespace }}"
name: "{{ __vm_healthcheck_vm_name }}"
validate_certs: "{{ vm_healthcheck_openshift_verify_ssl }}"
register: _vm_healthcheck_named_vms
loop: "{{ vm_healthcheck_vm_names }}"
loop_control:
loop_var: __vm_healthcheck_vm_name

- name: _healthcheck | Build VM List
ansible.builtin.set_fact:
_vm_healthcheck_vms: >-
{{
_vm_healthcheck_all_vms.resources | default([])
if vm_healthcheck_vm_names | length == 0
else _vm_healthcheck_named_vms.results | default([]) | map(attribute='resources') | flatten
}}

- name: _healthcheck | Verify VMs Found
ansible.builtin.assert:
that:
- _vm_healthcheck_vms | length > 0
quiet: true
fail_msg: "No VMs found in namespace {{ vm_healthcheck_namespace }}"

- name: _healthcheck | Run VM Status Checks
ansible.builtin.include_tasks:
file: vm_status.yml
loop: "{{ _vm_healthcheck_vms }}"
loop_control:
loop_var: __vm_healthcheck_vm
label: "{{ __vm_healthcheck_vm.metadata.name }}"

- name: _healthcheck | Run Networking Checks
ansible.builtin.include_tasks:
file: vm_networking.yml
loop: "{{ _vm_healthcheck_vms }}"
loop_control:
loop_var: __vm_healthcheck_vm
label: "{{ __vm_healthcheck_vm.metadata.name }}"
when: vm_healthcheck_check_networking | bool

- name: _healthcheck | Run Storage Checks
ansible.builtin.include_tasks:
file: vm_storage.yml
loop: "{{ _vm_healthcheck_vms }}"
loop_control:
loop_var: __vm_healthcheck_vm
label: "{{ __vm_healthcheck_vm.metadata.name }}"
when: vm_healthcheck_check_storage | bool

- name: _healthcheck | Run Resource Checks
ansible.builtin.include_tasks:
file: vm_resources.yml
loop: "{{ _vm_healthcheck_vms }}"
loop_control:
loop_var: __vm_healthcheck_vm
label: "{{ __vm_healthcheck_vm.metadata.name }}"
when: vm_healthcheck_check_resources | bool

- name: _healthcheck | Generate Healthcheck Report
ansible.builtin.include_tasks:
file: report.yml
when: vm_healthcheck_generate_report | bool

- name: _healthcheck | Display Healthcheck Summary
ansible.builtin.debug:
msg: "Healthcheck complete: {{ vm_healthcheck_results | selectattr('status', 'equalto', 'pass') | list | length }} passed, {{ vm_healthcheck_results | selectattr('status', 'equalto', 'fail') | list | length }} failed" # noqa: yaml[line-length]
...
3 changes: 3 additions & 0 deletions roles/vm_healthcheck/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
# tasks file for vm_healthcheck
...
28 changes: 28 additions & 0 deletions roles/vm_healthcheck/tasks/report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---

- name: report | Set Report Timestamp
ansible.builtin.set_fact:
_vm_healthcheck_report_timestamp: "{{ now(utc=true).strftime('%Y-%m-%dT%H:%M:%SZ') }}"

- name: report | Build Summary Statistics
ansible.builtin.set_fact:
_vm_healthcheck_summary:
total_checks: "{{ vm_healthcheck_results | length }}"
passed: "{{ vm_healthcheck_results | selectattr('status', 'equalto', 'pass') | list | length }}"
failed: "{{ vm_healthcheck_results | selectattr('status', 'equalto', 'fail') | list | length }}"
vms_checked: "{{ vm_healthcheck_results | map(attribute='vm') | unique | list }}"
status_results: "{{ vm_healthcheck_results | selectattr('category', 'equalto', 'status') | list }}"
networking_results: "{{ vm_healthcheck_results | selectattr('category', 'equalto', 'networking') | list }}"
storage_results: "{{ vm_healthcheck_results | selectattr('category', 'equalto', 'storage') | list }}"
resources_results: "{{ vm_healthcheck_results | selectattr('category', 'equalto', 'resources') | list }}"

- name: report | Render HTML Report
ansible.builtin.template:
src: healthcheck_report.html.j2
dest: "{{ vm_healthcheck_report_path }}"
mode: "0644"

- name: report | Report Location
ansible.builtin.debug:
msg: "Healthcheck report written to {{ vm_healthcheck_report_path }}"
...
101 changes: 101 additions & 0 deletions roles/vm_healthcheck/tasks/vm_networking.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
---

- name: vm_networking | Skip Networking Checks for Stopped VMs
ansible.builtin.set_fact:
vm_healthcheck_results: >-
{{ vm_healthcheck_results + [{'vm': __vm_healthcheck_vm.metadata.name,
'category': 'networking', 'check': 'networking_skipped',
'source': 'vm',
'status': 'fail',
'detail': 'VM stopped — networking checks require a running VMI'}] }}
when: not (_vm_healthcheck_vmi_exists | bool)

- name: vm_networking | Get VMI for Network Info
kubernetes.core.k8s_info:
api_key: "{{ vm_healthcheck_openshift_api_key }}"
host: "{{ vm_healthcheck_openshift_host }}"
api_version: "{{ vm_healthcheck_kubevirt_api_version }}"
kind: VirtualMachineInstance
namespace: "{{ __vm_healthcheck_vm.metadata.namespace }}"
name: "{{ __vm_healthcheck_vm.metadata.name }}"
validate_certs: "{{ vm_healthcheck_openshift_verify_ssl }}"
register: _vm_healthcheck_net_vmi
when: _vm_healthcheck_vmi_exists | bool

- name: vm_networking | Set VMI Network Data
ansible.builtin.set_fact:
_vm_healthcheck_net_vmi_resource: "{{ _vm_healthcheck_net_vmi.resources[0] | default({}) }}"
_vm_healthcheck_spec_networks: "{{ __vm_healthcheck_vm.spec.template.spec.networks | default([]) }}"
when: _vm_healthcheck_vmi_exists | bool

- name: vm_networking | Check Interface IP Assignment
ansible.builtin.set_fact:
vm_healthcheck_results: >-
{{ vm_healthcheck_results + [{'vm': __vm_healthcheck_vm.metadata.name,
'category': 'networking', 'check': 'interface_ip_' + __vm_healthcheck_iface.name,
'source': 'vmi',
'status': 'pass' if (__vm_healthcheck_iface.ipAddress | default('') | length > 0) else 'fail',
'detail': 'Interface ' + __vm_healthcheck_iface.name
+ ': ' + (__vm_healthcheck_iface.ipAddress | default('no IP'))}] }}
loop: "{{ _vm_healthcheck_net_vmi_resource.status.interfaces | default([]) }}"
loop_control:
loop_var: __vm_healthcheck_iface
label: "{{ __vm_healthcheck_iface.name }}"
when: _vm_healthcheck_vmi_exists | bool

- name: vm_networking | Check Interface Names Match Spec
ansible.builtin.set_fact:
_vm_healthcheck_spec_iface_names: >-
{{ __vm_healthcheck_vm.spec.template.spec.domain.devices.interfaces
| default([]) | map(attribute='name') | list }}
_vm_healthcheck_status_iface_names: >-
{{ _vm_healthcheck_net_vmi_resource.status.interfaces | default([]) | map(attribute='name') | list }}
when: _vm_healthcheck_vmi_exists | bool

- name: vm_networking | Record Interface Name Match
ansible.builtin.set_fact:
vm_healthcheck_results: >-
{{ vm_healthcheck_results + [{'vm': __vm_healthcheck_vm.metadata.name,
'category': 'networking', 'check': 'interface_names_match',
'source': 'vmi',
'status': 'pass' if (_vm_healthcheck_spec_iface_names
| difference(_vm_healthcheck_status_iface_names) | length == 0) else 'fail',
'detail': 'Spec interfaces: ' + (_vm_healthcheck_spec_iface_names | join(', '))
+ ' | Status interfaces: ' + (_vm_healthcheck_status_iface_names | join(', '))}] }}
when: _vm_healthcheck_vmi_exists | bool

- name: vm_networking | Check Masquerade Pod Network
ansible.builtin.set_fact:
vm_healthcheck_results: >-
{{ vm_healthcheck_results + [{'vm': __vm_healthcheck_vm.metadata.name,
'category': 'networking', 'check': 'masquerade_pod_network',
'source': 'vmi',
'status': 'pass' if (_vm_healthcheck_net_vmi_resource.status.interfaces | default([])
| selectattr('name', 'equalto', __vm_healthcheck_masq_net.name) | list | length > 0) else 'fail',
'detail': 'Masquerade network: ' + __vm_healthcheck_masq_net.name}] }}
loop: >-
{{ __vm_healthcheck_vm.spec.template.spec.domain.devices.interfaces | default([])
| selectattr('masquerade', 'defined') | list }}
loop_control:
loop_var: __vm_healthcheck_masq_net
label: "{{ __vm_healthcheck_masq_net.name }}"
when: _vm_healthcheck_vmi_exists | bool

- name: vm_networking | Check Bridge Network Attachment
ansible.builtin.set_fact:
vm_healthcheck_results: >-
{{ vm_healthcheck_results + [{'vm': __vm_healthcheck_vm.metadata.name,
'category': 'networking', 'check': 'bridge_multus_' + __vm_healthcheck_bridge_net.name,
'source': 'vmi',
'status': 'pass' if (_vm_healthcheck_spec_networks
| selectattr('name', 'equalto', __vm_healthcheck_bridge_net.name)
| selectattr('multus', 'defined') | list | length > 0) else 'fail',
'detail': 'Bridge network: ' + __vm_healthcheck_bridge_net.name + ' multus attachment'}] }}
loop: >-
{{ __vm_healthcheck_vm.spec.template.spec.domain.devices.interfaces | default([])
| selectattr('bridge', 'defined') | list }}
loop_control:
loop_var: __vm_healthcheck_bridge_net
label: "{{ __vm_healthcheck_bridge_net.name }}"
when: _vm_healthcheck_vmi_exists | bool
...
Loading
Loading