Skip to content
This repository was archived by the owner on Feb 28, 2023. It is now read-only.

Commit a95b10e

Browse files
committed
Fix kernel installation on Ubuntu with some specific variants (like aws) + refactor code
1 parent 468eab3 commit a95b10e

File tree

7 files changed

+94
-121
lines changed

7 files changed

+94
-121
lines changed

.ansible-lint

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
skip_list:
33
# Required to list with extra arguments
44
- '303'
5+
- role-name

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Role Variables
2121
Default to any version.
2222
* **reboot_on_kernel_update**: If True, reboot the system if the kernel was updated.
2323
Default to `true`.
24+
* **kernel_variant**: If specified on a Debian based distributions, use the required kernel variant (like "", "common", "generic", "aws", "azure", ...) else use the current kernel variant.
2425

2526
Example Playbook
2627
----------------

azure-pipelines.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,13 @@ jobs:
6767
displayName: Test Ansible role
6868
condition: always()
6969
env:
70-
ANSIBLE_STDOUT_CALLBACK: debug
70+
ANSIBLE_DISPLAY_SKIPPED_HOSTS: "False"
7171
ANSIBLE_FORCE_COLOR: "True"
72+
ANSIBLE_FORKS: "30"
7273
ANSIBLE_NOCOLOR": "False"
74+
ANSIBLE_PIPELINING: "True"
75+
ANSIBLE_SSH_ARGS: "-o ControlMaster=auto -o ControlPersist=60s"
76+
ANSIBLE_STDOUT_CALLBACK: debug
7377

7478
- job:
7579
displayName: Ansible Galaxy publish

filter_plugins/main.py

Lines changed: 53 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,14 @@ def _rhel_kernel_info(packages, kernel_version, current_version):
1515
"""
1616
kernels = list()
1717

18-
# If current version match with required version, use this version
1918
if current_version.startswith(kernel_version):
2019
kernel_version = current_version.rsplit(".", 1)[0]
2120

22-
# List all available kernel version and associated repository
2321
for line in packages["stdout"].splitlines():
2422
if line.startswith("kernel.") and not line.startswith("kernel.src"):
2523
package = line.strip().split()
2624
kernels.append(dict(version=package[1], repo=package[2]))
2725

28-
# Return more recent kernel version that match version requirement
2926
for kernel in reversed(kernels):
3027
if kernel["version"].startswith(kernel_version):
3128
return kernel
@@ -66,139 +63,106 @@ def rhel_repo(packages, kernel_version, current_version):
6663
return _rhel_kernel_info(packages, kernel_version, current_version)["repo"]
6764

6865

69-
def deb_kernel(packages, kernel_version, current_version):
66+
def deb_kernel(packages, kernel_version, current_version, variant=None):
7067
"""
7168
Return best matching kernel version.
7269
7370
Args:
7471
packages (dict): apt-cache showpkg output.
7572
kernel_version (str): Kernel version to install.
7673
current_version (str): Current kernel version.
74+
variant (str): Kernel variant to use ("common", ...) If not specified use
75+
current variant.
7776
7877
Returns:
7978
str: kernel version.
8079
"""
81-
kernels = set()
82-
83-
# If current version match with required version, use this version
84-
if current_version.startswith(kernel_version):
85-
kernel_version = current_version
80+
if current_version.startswith(kernel_version) and not variant:
81+
return current_version
8682

87-
# List all available kernel version and associated repository
88-
for line in packages["stdout"].splitlines():
89-
line = line.strip()
90-
if line.startswith("Package: ") and (
91-
line.endswith("-common") or line.endswith("-generic") # Debian
92-
): # Ubuntu
93-
kernel = line.split()[1]
83+
import re
9484

95-
for string in ("linux-headers-", "common", "generic"):
96-
kernel = kernel.replace(string, "")
97-
kernel = kernel.strip("-")
85+
kernels = set()
86+
kernels_add = kernels.add
87+
current_version, current_variant = re.match(
88+
r"^([0-9-.]+)(-[a-z0-9]+)?$", current_version
89+
).groups()
90+
variant = "-" + (
91+
variant
92+
if not (variant is None or variant.startswith("__omit_place_holder__"))
93+
else (current_variant or "")
94+
).lstrip("-")
95+
match = re.compile(r"^Package: linux-headers-([a-z0-9-.]+%s)\s*$" % variant).match
9896

99-
if kernel:
100-
kernels.add(kernel)
97+
for line in packages["stdout"].splitlines():
98+
line_match = match(line)
99+
if line_match:
100+
kernels_add(line_match.group(1))
101101

102-
# Sort Kernel versions
103102
versions = {}
104103
for kernel in kernels:
105-
try:
106-
version, build = kernel.split("-", 1)
107-
except ValueError:
108-
version = kernel
109-
build = ""
104+
version_info = kernel.split("-")
105+
version = version_info[0]
106+
build = version_info[1]
110107
versions[kernel] = list(int(ver) for ver in version.split(".")) + [build]
111108
kernels = sorted(versions.keys(), key=versions.get, reverse=True)
112109

113-
# Return more recent kernel package that match version requirement
114110
for kernel in kernels:
115111
if kernel.startswith(kernel_version):
116112
return kernel
117113

118114
raise RuntimeError(
119-
'No kernel matching to "%s". Available kernel versions: %s'
120-
% (kernel_version, ", ".join(reversed(kernels)))
121-
)
122-
123-
124-
def _deb_kernel_package(kernel, dist, arch, name):
125-
"""
126-
Return kernel package name.
127-
128-
Args:
129-
kernel (str): Kernel version.
130-
dist (str): Distribution.
131-
arch (str): Architecture.
132-
name (str): Package name.
133-
134-
Returns:
135-
str: kernel package.
136-
"""
137-
# Define package suffix
138-
if dist == "Ubuntu":
139-
suffix = "generic"
140-
elif name == "linux-image":
141-
suffix = arch.replace("x86_64", "amd64")
142-
else:
143-
suffix = "common"
144-
145-
return "-".join((name, kernel, suffix))
146-
147-
148-
def deb_kernel_pkg(packages, kernel_version, current_version, dist, arch, name):
149-
"""
150-
Return kernel package to install.
151-
152-
Args:
153-
packages (dict): apt-cache showpkg output.
154-
kernel_version (str): Kernel version to install.
155-
current_version (str): Current kernel version.
156-
dist (str): Distribution.
157-
arch (str): Architecture.
158-
name (str): Package name.
159-
160-
Returns:
161-
str: kernel package to install.
162-
"""
163-
return _deb_kernel_package(
164-
deb_kernel(packages, kernel_version, current_version), dist, arch, name
115+
'No kernel matching to "%s". Current version: %s. Available kernel versions: %s'
116+
% (kernel_version, current_version, ", ".join(reversed(kernels)))
165117
)
166118

167119

168-
def deb_installed_kernel(installed, packages, kernel_version, current_version):
120+
def deb_installed_kernel(installed, kernel_version, arch):
169121
"""
170122
Return old kernel packages to remove.
171123
172124
Args:
173125
installed (dict): dpkg -l output.
174-
packages (dict): apt-cache showpkg output.
175126
kernel_version (str): Kernel version to install.
176-
current_version (str): Current kernel version.
127+
arch (str): Architecture.
177128
178129
Returns:
179130
list of str: Kernel packages to remove.
180131
"""
181-
# Filter installed package to keep
182-
to_keep = deb_kernel(packages, kernel_version, current_version)
132+
packages = ("linux-image-", "linux-headers-")
133+
to_keep = tuple(deb_kernel_package(name, kernel_version, arch) for name in packages)
183134

184-
# Return installed package to remove
185135
to_remove = []
186136
for line in installed["stdout"].splitlines():
187137
if " linux-" not in line:
188138
continue
189-
190-
package = line.split()[1]
191-
if (
192-
package.startswith("linux-image-") or package.startswith("linux-headers-")
193-
) and not (
194-
package.startswith("linux-image-" + to_keep)
195-
or package.startswith("linux-headers-" + to_keep)
139+
package = line.split()[1].strip()
140+
if any(package.startswith(name) for name in packages) and not any(
141+
package.startswith(name) for name in to_keep
196142
):
197143
to_remove.append(package)
198-
199144
return to_remove
200145

201146

147+
def deb_kernel_package(name, kernel_version, arch):
148+
"""
149+
Check if kernel version match.
150+
151+
Args:
152+
name (str): package name.
153+
kernel_version (str): Kernel version to install.
154+
arch (str): Architecture.
155+
156+
Returns:
157+
str: Package name.
158+
"""
159+
package = "%s-%s" % (name, kernel_version)
160+
if name == "linux-image":
161+
# Debian "image" packages does not end by the variant like headers
162+
package = package.replace("-common", "-" + arch.replace("x86_64", "amd64"))
163+
return package
164+
165+
202166
def kernel_match(kernel, kernel_spec):
203167
"""
204168
Check if kernel version match.
@@ -223,7 +187,7 @@ def filters():
223187
"rhel_kernel": rhel_kernel,
224188
"rhel_repo": rhel_repo,
225189
"deb_kernel": deb_kernel,
226-
"deb_kernel_pkg": deb_kernel_pkg,
227190
"deb_installed_kernel": deb_installed_kernel,
191+
"deb_kernel_package": deb_kernel_package,
228192
"kernel_match": kernel_match,
229193
}

molecule/default/playbook.yml renamed to molecule/default/converge.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
- name: Converge
33
hosts: all
4+
strategy: free
45
roles:
56
- role: ansible-role-linux-kernel
67
vars:

molecule/default/molecule.yml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,22 @@ provisioner:
2424
host_vars:
2525
# Warning: Always specify a version: bad version detected from container
2626
centos_7:
27-
kernel_version: 3.10.0-693
27+
kernel_version: "3.10.0-693"
2828
centos_8:
29-
kernel_version: 4.18.0
29+
kernel_version: "4.18.0"
3030
ubuntu_bionic:
31-
kernel_version: 4.15.0-55
31+
kernel_version: "4.15"
32+
kernel_variant: generic
3233
debian_buster:
3334
kernel_version: 4.19.0
35+
kernel_variant: common
3436

3537
verifier:
3638
name: testinfra
39+
40+
scenario:
41+
test_sequence:
42+
- create
43+
- converge
44+
- idempotence
45+
- verify

tasks/main.yml

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,10 @@
2828
rhel_kernel(kernel_version, ansible_kernel) }}"
2929
when: yum_kernel_list is not skipped
3030

31-
- name: Ensure kernel packages versions with YUM
31+
- name: "Ensure kernel {{ _kernel }} packages are installed with YUM"
3232
yum:
3333
name: "{{ item.name }}-{{ _kernel }}"
34-
enablerepo: "{{ yum_kernel_list |
35-
rhel_repo(kernel_version, ansible_kernel) }}"
34+
enablerepo: "{{ yum_kernel_list | rhel_repo(kernel_version, ansible_kernel) }}"
3635
allow_downgrade: true
3736
retries: 10
3837
delay: 1
@@ -68,11 +67,10 @@
6867
rhel_kernel(kernel_version, ansible_kernel) }}"
6968
when: dnf_kernel_list is not skipped
7069

71-
- name: Ensure kernel packages versions with DNF
70+
- name: "Ensure kernel {{ _kernel }} packages are installed with DNF"
7271
dnf:
7372
name: "{{ item.name }}-{{ _kernel }}"
74-
enablerepo: "{{ dnf_kernel_list |
75-
rhel_repo(kernel_version, ansible_kernel) }}"
73+
enablerepo: "{{ dnf_kernel_list | rhel_repo(kernel_version, ansible_kernel) }}"
7674
allow_downgrade: true
7775
retries: 10
7876
delay: 1
@@ -103,27 +101,24 @@
103101
- name: Get available kernel versions with APT
104102
command: apt-cache showpkg linux-headers-*
105103
changed_when: false
106-
when:
107-
- ansible_os_family == 'Debian'
108104
register: apt_kernel_list
105+
when: ansible_os_family == 'Debian'
109106

110107
- name: Get installed packages with APT
111108
command: dpkg -l
112109
changed_when: false
113-
when:
114-
- ansible_os_family == 'Debian'
115110
register: apt_packages_list
111+
when: ansible_os_family == 'Debian'
116112

117113
- name: Set target APT kernel version
118114
set_fact: _kernel="{{ apt_kernel_list |
119-
deb_kernel(kernel_version, ansible_kernel) }}"
115+
deb_kernel(kernel_version, ansible_kernel,
116+
kernel_variant | default(omit)) }}"
120117
when: apt_kernel_list is not skipped
121118

122-
- name: Ensure kernel packages versions with APT
119+
- name: "Ensure kernel {{ _kernel }} packages are installed with APT"
123120
apt:
124-
name: "{{ apt_kernel_list | deb_kernel_pkg(
125-
kernel_version, ansible_kernel, ansible_distribution,
126-
ansible_architecture, item.name) }}"
121+
name: "{{ item.name | deb_kernel_package(_kernel, ansible_architecture) }}"
127122
retries: 10
128123
delay: 1
129124
register: _apt_install
@@ -139,33 +134,31 @@
139134

140135
- name: Ensure any other kernel packages are removed with APT
141136
apt:
142-
name: "{{ apt_packages_list | deb_installed_kernel(
143-
apt_kernel_list, kernel_version, ansible_kernel) }}"
137+
name: "{{ apt_packages_list |
138+
deb_installed_kernel(_kernel, ansible_architecture) }}"
144139
state: absent
145140
purge: true
146141
when: ansible_os_family == 'Debian'
142+
tags: molecule-idempotence-notest
147143

148144
- name: Get /var/run/reboot-required stat
149145
# Note: Should exist on Debian based OS if reboot is required
150146
stat:
151147
path: /var/run/reboot-required
152148
register: reboot_flag
153149

154-
- name: Notify about Kernel update and reboot requierement
155-
debug:
156-
msg: 'REBOOT REQUIRED, kernel version changed
157-
from {{ ansible_kernel }} to {{ _kernel }}.'
158-
when:
159-
- (not (ansible_kernel | kernel_match(_kernel)) or reboot_flag.stat.exists)
160-
161-
- name: Ensure the new kernel is enabled by rebooting
150+
- name: "Reboot if kernel changed (from {{ ansible_kernel }} to {{ _kernel }})."
162151
reboot:
152+
register: linux_kernel_rebooted
163153
when:
164154
- reboot_on_kernel_update | bool
165155
- (not (ansible_kernel | kernel_match(_kernel)) or reboot_flag.stat.exists)
166156

167157
- name: Update facts
168158
setup:
169-
when:
170-
- reboot_on_kernel_update | bool
171-
- (not (ansible_kernel | kernel_match(_kernel)) or reboot_flag.stat.exists)
159+
when: linux_kernel_rebooted.changed # noqa no-handler
160+
161+
- name: Show kernel version after reboot
162+
debug:
163+
msg: 'The current kernel version is now: {{ ansible_kernel }}.'
164+
when: linux_kernel_rebooted.changed # noqa no-handler

0 commit comments

Comments
 (0)