|
55 | 55 | enabled: true |
56 | 56 | state: started |
57 | 57 |
|
| 58 | +# Detect whether the kernel exposes the cpufreq 'boost' knob. The stock |
| 59 | +# virtual-guest profile inherits [cpu] boost=1 from throughput-performance. |
| 60 | +# Inside QEMU/KVM guests on AuthenticAMD hosts neither cpufreq/boost nor |
| 61 | +# intel_pstate/no_turbo is exposed, so TuneD cannot set the knob (harmless |
| 62 | +# apply-time warning) and `tuned-adm verify` always fails with |
| 63 | +# "device cpuN: 'boost' = 'None', expected '1'". If we detect this, we |
| 64 | +# install a sibling profile 'almalinux-virtual-guest' that includes stock |
| 65 | +# virtual-guest verbatim but replaces [cpu] with an empty section, and |
| 66 | +# switch the active profile to it. |
| 67 | +- name: Probe cpufreq boost sysfs knobs |
| 68 | + ansible.builtin.stat: |
| 69 | + path: "{{ item }}" |
| 70 | + loop: |
| 71 | + - /sys/devices/system/cpu/cpufreq/boost |
| 72 | + - /sys/devices/system/cpu/intel_pstate/no_turbo |
| 73 | + register: tuned_boost_knobs |
| 74 | + |
| 75 | +- name: Decide whether TuneD 'boost' knob is settable on this host |
| 76 | + ansible.builtin.set_fact: |
| 77 | + tuned_boost_available: "{{ tuned_boost_knobs.results | selectattr('stat.exists') | list | length > 0 }}" |
| 78 | + |
| 79 | +- name: Pick expected TuneD profile for this host |
| 80 | + ansible.builtin.set_fact: |
| 81 | + tuned_expected_profile: "{{ 'virtual-guest' if tuned_boost_available else 'almalinux-virtual-guest' }}" |
| 82 | + |
| 83 | +# TuneD's profile layout changed in 2.24: stock profiles moved from |
| 84 | +# /usr/lib/tuned/<name>/ to /usr/lib/tuned/profiles/<name>/, and the user |
| 85 | +# override path moved from /etc/tuned/<name>/ to /etc/tuned/profiles/<name>/. |
| 86 | +# AL8/AL9 ship the old layout, AL10/Kitten 10 (tuned >= 2.24) ship the new. |
| 87 | +# Detect which layout is in use so our custom profile lands where TuneD |
| 88 | +# actually looks; using the wrong dir would be a silent no-op. |
| 89 | +- name: Probe TuneD profile layout (new /profiles/ subdir vs old flat) |
| 90 | + ansible.builtin.stat: |
| 91 | + path: /usr/lib/tuned/profiles/virtual-guest/tuned.conf |
| 92 | + register: tuned_new_layout_stat |
| 93 | + when: not tuned_boost_available |
| 94 | + |
| 95 | +- name: Compute {{ tuned_expected_profile | default('almalinux-virtual-guest') }} profile directory |
| 96 | + ansible.builtin.set_fact: |
| 97 | + tuned_profile_dir: >- |
| 98 | + {{ ('/etc/tuned/profiles/' if tuned_new_layout_stat.stat.exists else '/etc/tuned/') |
| 99 | + ~ tuned_expected_profile }} |
| 100 | + when: not tuned_boost_available |
| 101 | + |
| 102 | +- name: Ensure {{ tuned_profile_dir | default('/etc/tuned/<profiles>/almalinux-virtual-guest') }} directory exists |
| 103 | + ansible.builtin.file: |
| 104 | + path: "{{ tuned_profile_dir }}" |
| 105 | + state: directory |
| 106 | + owner: root |
| 107 | + group: root |
| 108 | + mode: '0755' |
| 109 | + when: not tuned_boost_available |
| 110 | + |
| 111 | +- name: Install {{ tuned_expected_profile | default('almalinux-virtual-guest') }} TuneD profile |
| 112 | + ansible.builtin.copy: |
| 113 | + dest: "{{ tuned_profile_dir }}/tuned.conf" |
| 114 | + owner: root |
| 115 | + group: root |
| 116 | + mode: '0644' |
| 117 | + content: | |
| 118 | + # Managed by the cloud-images opennebula_guest Ansible role. |
| 119 | + # |
| 120 | + # Sibling of stock 'virtual-guest' that disables the [cpu] plugin. |
| 121 | + # On QEMU/KVM guests without cpufreq/intel_pstate exposure (e.g. |
| 122 | + # AuthenticAMD hosts) stock virtual-guest fails `tuned-adm verify` |
| 123 | + # because its inherited [cpu] boost=1 expectation cannot be |
| 124 | + # satisfied on a missing sysfs knob. This profile inherits every |
| 125 | + # stock setting verbatim via `include=virtual-guest`, so future |
| 126 | + # upstream changes to virtual-guest.tuned.conf flow through |
| 127 | + # automatically -- only [cpu] replace=1 is added to drop the |
| 128 | + # unverifiable plugin. |
| 129 | + # |
| 130 | + # `tuned-adm active` will report 'almalinux-virtual-guest' rather |
| 131 | + # than 'virtual-guest' on these hosts. |
| 132 | + [main] |
| 133 | + summary=AlmaLinux virtual-guest for virtualized CPUs without cpufreq/boost exposure |
| 134 | + include=virtual-guest |
| 135 | +
|
| 136 | + [cpu] |
| 137 | + replace=1 |
| 138 | + when: not tuned_boost_available |
| 139 | + |
| 140 | +# Unconditional: on Intel hosts this is a force-reapply of virtual-guest |
| 141 | +# (helps with the first-start race where plugins load late); on AMD |
| 142 | +# virtualized hosts it switches the active profile to our sibling one |
| 143 | +# before we assert on it. |
| 144 | +- name: Activate {{ tuned_expected_profile }} TuneD profile |
| 145 | + ansible.builtin.command: |
| 146 | + cmd: "tuned-adm profile {{ tuned_expected_profile }}" |
| 147 | + changed_when: false |
| 148 | + |
58 | 149 | - name: Get current TuneD profile |
59 | 150 | ansible.builtin.command: |
60 | 151 | cmd: tuned-adm active |
|
63 | 154 |
|
64 | 155 | - name: Test if TuneD profile is active |
65 | 156 | ansible.builtin.assert: |
66 | | - that: "tuned_adm_active.stdout == 'Current active profile: virtual-guest'" |
67 | | - fail_msg: Configured active profile is not same as current one |
| 157 | + that: "tuned_adm_active.stdout == ('Current active profile: ' ~ tuned_expected_profile)" |
| 158 | + fail_msg: "Configured active profile is not same as current one (expected: {{ tuned_expected_profile }})" |
68 | 159 | success_msg: The configured TuneD profile is current |
69 | 160 |
|
| 161 | +# Retry around the first-start race where `tuned-adm verify` can run before |
| 162 | +# the daemon finishes replaying every knob. `failed_when: false` keeps the |
| 163 | +# loop alive across the intermediate non-zero exits; the strict assert below |
| 164 | +# still fails the build if verification never succeeds. |
70 | 165 | - name: Get status of TuneD settings |
71 | 166 | ansible.builtin.command: |
72 | 167 | cmd: tuned-adm verify |
73 | 168 | register: tuned_adm_verify |
74 | 169 | changed_when: false |
| 170 | + failed_when: false |
| 171 | + retries: 5 |
| 172 | + delay: 3 |
| 173 | + until: "'Verification succeeded' in tuned_adm_verify.stdout" |
| 174 | + |
| 175 | +# `tuned-adm verify`'s stdout is just a generic "Verification failed ..." |
| 176 | +# banner; the specific plugin/knob that differs is only written to |
| 177 | +# /var/log/tuned/tuned.log. Capture the tail unconditionally so the strict |
| 178 | +# assert below can surface it in its fail_msg when verification fails. |
| 179 | +- name: Capture TuneD log tail for diagnostics |
| 180 | + ansible.builtin.command: |
| 181 | + cmd: tail -c 16384 /var/log/tuned/tuned.log |
| 182 | + register: tuned_log_tail |
| 183 | + changed_when: false |
| 184 | + failed_when: false |
75 | 185 |
|
76 | 186 | - name: Verify if settings on the TuneD profile is applied |
77 | 187 | ansible.builtin.assert: |
78 | 188 | that: "'Verification succeeded, current system settings match the preset profile.' in tuned_adm_verify.stdout" |
79 | | - fail_msg: Current system settings does not match current active profile |
| 189 | + fail_msg: |- |
| 190 | + Current system settings does not match current active profile. |
| 191 | + --- tuned-adm verify stdout --- |
| 192 | + {{ tuned_adm_verify.stdout | default('<no stdout>') }} |
| 193 | + --- tuned-adm verify stderr --- |
| 194 | + {{ tuned_adm_verify.stderr | default('<no stderr>') }} |
| 195 | + --- /var/log/tuned/tuned.log (last 16 KiB) --- |
| 196 | + {{ tuned_log_tail.stdout | default('<log unavailable>') }} |
80 | 197 | success_msg: Current system settings matches current active profile |
81 | 198 |
|
82 | 199 | - name: Regenerate all initramfs images |
|
0 commit comments