From 14e144cfaac71b7973f4d7288f6fe25ac585a5be Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 22 Apr 2025 11:49:04 -0400 Subject: [PATCH 1/3] Add a guest/instance for building the test kernel Provision a separate guest/instance for the purpose of building the Linux kernel under test. It's not used yet. Signed-off-by: Chuck Lever --- playbooks/roles/gen_hosts/defaults/main.yml | 2 ++ playbooks/roles/gen_hosts/tasks/main.yml | 12 ++++++++++ .../roles/gen_hosts/templates/builder.j2 | 13 +++++++++++ playbooks/roles/gen_nodes/defaults/main.yml | 2 ++ playbooks/roles/gen_nodes/tasks/main.yml | 22 +++++++++++++++++++ workflows/linux/Kconfig | 10 +++++++++ workflows/linux/Makefile | 4 ++++ 7 files changed, 65 insertions(+) create mode 100644 playbooks/roles/gen_hosts/templates/builder.j2 diff --git a/playbooks/roles/gen_hosts/defaults/main.yml b/playbooks/roles/gen_hosts/defaults/main.yml index f6ab9bcce..85afb3504 100644 --- a/playbooks/roles/gen_hosts/defaults/main.yml +++ b/playbooks/roles/gen_hosts/defaults/main.yml @@ -29,6 +29,8 @@ kdevops_workflow_enable_ltp: False kdevops_workflow_enable_nfstest: false kdevops_workflow_enable_sysbench: false +bootlinux_builder: false + is_fstests: False fstests_fstyp: "bogus" fs_config_role_path: "/dev/null" diff --git a/playbooks/roles/gen_hosts/tasks/main.yml b/playbooks/roles/gen_hosts/tasks/main.yml index a50355f72..9182ac46f 100644 --- a/playbooks/roles/gen_hosts/tasks/main.yml +++ b/playbooks/roles/gen_hosts/tasks/main.yml @@ -56,6 +56,18 @@ when: - is_fstests +- name: Generate the Ansible hosts file for a Linux kernel build + tags: [ 'hosts' ] + template: + src: "{{ kdevops_hosts_template }}" + dest: "{{ topdir_path }}/{{ kdevops_hosts }}" + force: yes + trim_blocks: True + lstrip_blocks: True + when: + - bootlinux_builder + - ansible_hosts_template.stat.exists + - name: Generate the Ansible hosts file tags: [ 'hosts' ] template: diff --git a/playbooks/roles/gen_hosts/templates/builder.j2 b/playbooks/roles/gen_hosts/templates/builder.j2 new file mode 100644 index 000000000..0c9ba1e8e --- /dev/null +++ b/playbooks/roles/gen_hosts/templates/builder.j2 @@ -0,0 +1,13 @@ +[all] +{{ kdevops_hosts_prefix }}-builder +[all:vars] +ansible_python_interpreter = "{{ kdevops_python_interpreter }}" + +[baseline] +{{ kdevops_hosts_prefix }}-builder +[baseline:vars] +ansible_python_interpreter = "{{ kdevops_python_interpreter }}" + +[dev] +[dev:vars] +ansible_python_interpreter = "{{ kdevops_python_interpreter }}" diff --git a/playbooks/roles/gen_nodes/defaults/main.yml b/playbooks/roles/gen_nodes/defaults/main.yml index 8ff9b8799..d5fa444c9 100644 --- a/playbooks/roles/gen_nodes/defaults/main.yml +++ b/playbooks/roles/gen_nodes/defaults/main.yml @@ -93,6 +93,8 @@ fstests_fstyp: "bogus" fs_config_role_path: "/dev/null" fs_config_data: "[section_1]" +bootlinux_builder: false +builder_nodes: [] bootlinux_9p: False bootlinux_9p_host_path: "/dev/null" bootlinux_9p_msize: 0 diff --git a/playbooks/roles/gen_nodes/tasks/main.yml b/playbooks/roles/gen_nodes/tasks/main.yml index d541dcbf1..e3d0127d1 100644 --- a/playbooks/roles/gen_nodes/tasks/main.yml +++ b/playbooks/roles/gen_nodes/tasks/main.yml @@ -43,6 +43,14 @@ when: - kdevops_baseline_and_dev +- name: Set builder nodes array + tags: vars + set_fact: + builder_nodes: + - "{{ kdevops_host_prefix + '-builder' }}" + when: + - bootlinux_builder + - name: Set iscsi_nodes list ansible.builtin.set_fact: iscsi_nodes: "{{ [kdevops_host_prefix + '-iscsi'] }}" @@ -138,6 +146,20 @@ - not kdevops_workflows_dedicated_workflow - ansible_nodes_template.stat.exists +- name: Generate the builder kdevops nodes file using {{ kdevops_nodes_template }} as jinja2 source template + tags: [ 'nodes' ] + vars: + node_template: "{{ kdevops_nodes_template | basename }}" + all_generic_nodes: "{{ builder_nodes }}" + nodes: "{{ all_generic_nodes }}" + template: + src: "{{ node_template }}" + dest: "{{ topdir_path }}/{{ kdevops_nodes }}" + force: yes + when: + - bootlinux_builder + - ansible_nodes_template.stat.exists + - name: Generate the pynfs kdevops nodes file using {{ kdevops_nodes_template }} as jinja2 source template tags: [ 'nodes' ] vars: diff --git a/workflows/linux/Kconfig b/workflows/linux/Kconfig index 797469e60..8a26eb2e2 100644 --- a/workflows/linux/Kconfig +++ b/workflows/linux/Kconfig @@ -36,6 +36,16 @@ config BOOTLINUX_PURE_IOMAP endif # HAVE_SUPPORTS_PURE_IOMAP +config BOOTLINUX_BUILDER + bool "Build once, test many" + output yaml + default n + help + Enabling this option creates a separate guest/instance + where the Linux kernel is built. kdevops passes the + artifacts of this build to the test runner nodes to be + installed before each workflow runs. + config BOOTLINUX_9P bool "Use 9p to build Linux" depends on LIBVIRT && !GUESTFS_LACKS_9P diff --git a/workflows/linux/Makefile b/workflows/linux/Makefile index ecce273a4..65bbb8ae9 100644 --- a/workflows/linux/Makefile +++ b/workflows/linux/Makefile @@ -13,6 +13,10 @@ ifeq (y,$(CONFIG_BOOTLINUX_PURE_IOMAP)) TREE_CONFIG:=config-$(TREE_REF)-pure-iomap endif +ifeq (y,$(CONFIG_BOOTLINUX_BUILDER)) +KDEVOPS_HOSTS_TEMPLATE=builder.j2 +endif + # Describes the Linux clone BOOTLINUX_ARGS += target_linux_git=$(TREE_URL) # ifeq (y,$(CONFIG_BOOTLINUX_TREE_CUSTOM_NAME)) From 79803bb61ae81fe646bc92e2dc64eb162d57250c Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 22 Apr 2025 11:49:05 -0400 Subject: [PATCH 2/3] playbooks: Add a build_linux role Currently, for cloud configurations or when 9p is disabled, kdevops builds the test kernel on each test runner. This is inefficient, and gets worse as the test matrix for a single kernel version scales out. Instead we want to build the test kernel once and make the build artifacts available for test runners to install. This would be similar to what KOTD does now, except it does not require setting up a separate yum repo. For this experiment, I've created a stripped down version of the bootlinux role that has only the steps needed to build the kernel on a target node. It adds some steps to create kernel packages and then fetch them to the control host. Signed-off-by: Chuck Lever --- .gitignore | 2 + playbooks/build_linux.yml | 4 + playbooks/roles/build_linux/README.md | 74 +++++ playbooks/roles/build_linux/defaults/main.yml | 38 +++ .../tasks/install-deps/debian/main.yml | 46 +++ .../tasks/install-deps/redhat/main.yml | 102 ++++++ .../tasks/install-deps/suse/main.yml | 31 ++ playbooks/roles/build_linux/tasks/main.yml | 295 ++++++++++++++++++ workflows/linux/Makefile | 7 + 9 files changed, 599 insertions(+) create mode 100644 playbooks/build_linux.yml create mode 100644 playbooks/roles/build_linux/README.md create mode 100644 playbooks/roles/build_linux/defaults/main.yml create mode 100644 playbooks/roles/build_linux/tasks/install-deps/debian/main.yml create mode 100644 playbooks/roles/build_linux/tasks/install-deps/redhat/main.yml create mode 100644 playbooks/roles/build_linux/tasks/install-deps/suse/main.yml create mode 100644 playbooks/roles/build_linux/tasks/main.yml diff --git a/.gitignore b/.gitignore index 45113a669..f51213a59 100644 --- a/.gitignore +++ b/.gitignore @@ -86,6 +86,8 @@ playbooks/roles/linux-mirror/linux-mirror-systemd/mirrors.yaml # yet. workflows/selftests/results/ +workflows/linux/artifacts/ + workflows/linux/refs/default/Kconfig.linus workflows/linux/refs/default/Kconfig.next workflows/linux/refs/default/Kconfig.stable diff --git a/playbooks/build_linux.yml b/playbooks/build_linux.yml new file mode 100644 index 000000000..094441d1b --- /dev/null +++ b/playbooks/build_linux.yml @@ -0,0 +1,4 @@ +--- +- hosts: all + roles: + - role: build_linux diff --git a/playbooks/roles/build_linux/README.md b/playbooks/roles/build_linux/README.md new file mode 100644 index 000000000..cbf9755ce --- /dev/null +++ b/playbooks/roles/build_linux/README.md @@ -0,0 +1,74 @@ +build_linux +=========== + +The build_linux role downloads and builds the Linux kernel. It also +lets you apply custom patches, remove kernels, etc.; anything you +have to do with regards to generic kernel development. + +By default, it tracks one of the latest stable kernels that are +still supported using the linux stable git tree. + +Requirements +------------ + +A separate block device is required on which to create the file +system where the test kernel is built. + +Role Variables +-------------- + + * infer_uid_and_group: defaults to False, if set to True, then we will ignore + the passed on data_user and data_group and instead try to infer this by + inspecting the `whoami` and getent on the logged in target system we are + provisioning. So if user sam is running able on a host, targetting a system + called foofighter and logging into that system using username pincho, + then the data_user will be set overwritten and set to pincho. We will then + also lookup for pincho's default group id and use that for data_group. + This is useful if you are targetting a slew of systems and don't really + want to deal with the complexities of the username and group, and the + default target username you use to ssh into a system suffices to use as + a base. This is set to False to remain compatible with old users of + this role. + * data_path: where to place the git trees we clone under + * data_user: the user to assign permissions to + * data_group: the group to assign permissions to + + * data_device: the target device to use for the data partition + * data_fstype: the filesystem to store the data parition under + * data_label: the label to use + * data_fs_opts: the filesystem options to use, you want to ensure to add the + label + + * target_linux_admin_name: your developer name + * target_linux_admin_email: your email + * target_linux_git: the git tree to clone, by default this is the linux-stable + tree + * target_linux_tree: the name of the tree + * target_linux_dir_path: where to place the tree on the target system + + * target_linux_ref : the actual tag as used on linux, so v4.19.62 + * target_linux_extra_patch: if defined an extra patch to apply with git + am prior to compilation + * target_linux_config: the configuration file to use + +Dependencies +------------ + +None. + +Example Playbook +---------------- + +Below is an example playbook, say a build_linux.yml file: + +``` +--- +- hosts: all + roles: + - role: build_linux +``` + +License +------- + +copyleft-next-0.3.1 diff --git a/playbooks/roles/build_linux/defaults/main.yml b/playbooks/roles/build_linux/defaults/main.yml new file mode 100644 index 000000000..5ff5ece28 --- /dev/null +++ b/playbooks/roles/build_linux/defaults/main.yml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier copyleft-next-0.3.1 +--- +kdevops_bootlinux: false +infer_uid_and_group: false + +data_path: "/data" +data_user: "kdevops" +data_group: "kdevops" +data_device: "/dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_kdevops0" +data_fstype: "xfs" +data_label: "data" +data_fs_opts: "-L {{ disk_setup_label }}" + +target_linux_admin_name: "Hacker Amanda" +target_linux_admin_email: "devnull@kernel.org" +target_linux_git: "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git" +target_linux_shallow_depth: 0 +target_linux_tree: "linux-stable" +target_linux_dir_path: "{{ data_path }}/{{ target_linux_tree }}" +kdevops_baseline_and_dev: false + +target_linux_ref: "v4.19.133" +target_linux_delta_file: +target_linux_config: "config-{{ target_linux_ref }}" +make: "make" +target_linux_make_cmd: "{{ make }} -j{{ ansible_processor_nproc }}" +target_linux_make_install_cmd: "{{ target_linux_make_cmd }} modules_install install" + +build_artifacts_dir: "{{ topdir_path }}/workflows/linux/artifacts/" + +uninstall_kernel_enable: false + +bootlinux_b4_am_this_host: false + +kdevops_workflow_enable_cxl: false +bootlinux_cxl_test: false + +bootlinux_tree_set_by_cli: false diff --git a/playbooks/roles/build_linux/tasks/install-deps/debian/main.yml b/playbooks/roles/build_linux/tasks/install-deps/debian/main.yml new file mode 100644 index 000000000..ef57dca75 --- /dev/null +++ b/playbooks/roles/build_linux/tasks/install-deps/debian/main.yml @@ -0,0 +1,46 @@ +--- +# Install dependencies for building linux on Debian + +- name: Update apt cache + become: true + become_method: ansible.builtin.sudo + ansible.builtin.apt: + update_cache: true + tags: + - linux + +# apt-get build-dep does not capture all requirements +- name: Install Linux kernel build dependencies + become: true + become_method: ansible.builtin.sudo + ansible.builtin.apt: + name: + - bison + - flex + - git + - gcc + - make + - gawk + - bc + - dump + - indent + - sed + - libssl-dev + - libelf-dev + - liburcu-dev + - xfsprogs + - e2fsprogs + - btrfs-progs + - ntfs-3g + - mdadm + - rpcbind + - portmap + - hwinfo + - open-iscsi + - python3-pip + - zstd + - libncurses-dev + - b4 + state: present + tags: + - linux diff --git a/playbooks/roles/build_linux/tasks/install-deps/redhat/main.yml b/playbooks/roles/build_linux/tasks/install-deps/redhat/main.yml new file mode 100644 index 000000000..371e7f7f1 --- /dev/null +++ b/playbooks/roles/build_linux/tasks/install-deps/redhat/main.yml @@ -0,0 +1,102 @@ +--- +- name: Enable installation of packages from EPEL + ansible.builtin.include_role: + name: epel-release + when: + - ansible_distribution != "Fedora" + +- name: Install packages for building the Linux kernel + become: true + become_method: ansible.builtin.sudo + ansible.builtin.dnf: + name: "{{ packages }}" + state: present + update_cache: true + retries: 3 + delay: 5 + register: result + until: result is succeeded + vars: + packages: + - bison + - flex + - git-core + - e2fsprogs + - xfsprogs + - xfsdump + - lvm2 + - gcc + - make + - gawk + - bc + - dump + - libtool + - psmisc + - sed + - vim + - fio + - libaio-devel + - diffutils + - net-tools + - ncurses-devel + - xfsprogs + - e2fsprogs + - elfutils-libelf-devel + - ntfs-3g + - mdadm + - rpcbind + - portmap + - hwinfo + - iscsi-initiator-utils + - openssl + - openssl-devel + - dwarves + - userspace-rcu + - zstd + +- name: Install btrfs-progs + become: true + become_method: ansible.builtin.sudo + ansible.builtin.dnf: + name: + - btrfs-progs + state: present + update_cache: true + retries: 3 + delay: 5 + register: btrfs_result + until: btrfs_result is succeeded + when: + - ansible_distribution == "Fedora" + +- name: Install rpmbuild + become: true + become_method: ansible.builtin.sudo + ansible.builtin.dnf: + name: + - rpmbuild + state: present + update_cache: true + retries: 3 + delay: 5 + register: rpmbuild_result + until: rpmbuild_result is succeeded + when: + - ansible_distribution != "Fedora" + +- name: Install rpmbuild + become: true + become_method: ansible.builtin.sudo + ansible.builtin.dnf: + name: + - perl-core + - rpm-build + - rsync + state: present + update_cache: true + retries: 3 + delay: 5 + register: rpmbuild_result + until: rpmbuild_result is succeeded + when: + - ansible_distribution == "Fedora" diff --git a/playbooks/roles/build_linux/tasks/install-deps/suse/main.yml b/playbooks/roles/build_linux/tasks/install-deps/suse/main.yml new file mode 100644 index 000000000..0260da5a9 --- /dev/null +++ b/playbooks/roles/build_linux/tasks/install-deps/suse/main.yml @@ -0,0 +1,31 @@ +--- +- name: Install Linux kernel build dependencies for SUSE + become: true + become_method: ansible.builtin.sudo + community.general.zypper: + name: + - bison + - flex + - git-core + - gcc + - make + - gawk + - bc + - dump + - sed + - libopenssl-devel + - libelf-devel + - liburcu8 + - diffutils + - net-tools + - ncurses-devel + - xfsprogs + - e2fsprogs + - btrfsprogs + - ntfs-3g + - mdadm + - rpcbind + - portmap + - hwinfo + - open-iscsi + disable_recommends: false diff --git a/playbooks/roles/build_linux/tasks/main.yml b/playbooks/roles/build_linux/tasks/main.yml new file mode 100644 index 000000000..a95b5ee98 --- /dev/null +++ b/playbooks/roles/build_linux/tasks/main.yml @@ -0,0 +1,295 @@ +--- +- name: Include optional extra_vars + ansible.builtin.include_vars: + file: "{{ item }}" + with_first_found: + - files: + - "../extra_vars.yml" + - "../extra_vars.yaml" + - "../extra_vars.json" + skip: true + failed_when: false + +- name: Debian-specific set up + ansible.builtin.import_tasks: install-deps/debian/main.yml + when: + - ansible_os_family == "Debian" + +- name: Red Hat-specific set up + ansible.builtin.import_tasks: install-deps/redhat/main.yml + when: + - ansible_os_family == "RedHat" + +- name: Suse-specific set up + ansible.builtin.import_tasks: install-deps/suse/main.yml + when: + - ansible_os_family == "Suse" + +- name: Install b4 + become: true + become_method: ansible.builtin.sudo + ansible.builtin.pip: + name: + - b4 + when: + - target_linux_install_b4 is defined + - target_linux_install_b4 + - ansible_os_family == "Debian" + +- name: Create the /data partition + ansible.builtin.include_role: + name: create_data_partition + +- name: Wipe the build directory + ansible.builtin.file: + path: "{{ target_linux_dir_path }}" + state: absent + +- name: Clone {{ target_linux_tree }} + ansible.builtin.git: + repo: "{{ target_linux_git }}" + dest: "{{ target_linux_dir_path }}" + update: true + depth: "{{ target_linux_shallow_depth }}" + version: "{{ target_linux_ref }}" + retries: 3 + delay: 5 + register: git_result + until: not git_result.failed + +- name: Copy the kernel delta to the builder + ansible.builtin.template: + src: "{{ target_linux_extra_patch }}" + dest: "{{ target_linux_dir_path }}/{{ target_linux_extra_patch }}" + owner: "{{ data_user }}" + group: "{{ data_group }}" + mode: "u=rw,g=r,o=r" + when: + - target_linux_extra_patch is defined + +- name: Apply the kernel delta on the builder + ansible.builtin.command: + cmd: "git am {{ target_linux_extra_patch }}" # noqa: command-instead-of-module + chdir: "{{ target_linux_dir_path }}" + register: git_am + changed_when: not git_am.failed + when: + - target_linux_extra_patch is defined + +- name: Set git user name and email + ansible.builtin.shell: | + if ! $(git config --get user.email) ; then + git config --global user.email user@example.com + fi + if ! $(git config --get user.name) ; then + git config --global user.name user + fi + register: git_config + changed_when: not git_config.failed + when: + - target_linux_apply_patch_message_id is defined + - target_linux_apply_patch_message_id | length > 0 + - bootlinux_b4_am_this_host|bool + +- name: Apply a message patch set + ansible.builtin.shell: + cmd: | + set -o pipefail + b4 am -o - {{ target_linux_apply_patch_message_id }} | git am + chdir: "{{ target_linux_dir_path }}" + register: b4_am + changed_when: not b4_am.failed + when: + - target_linux_apply_patch_message_id is defined + - target_linux_apply_patch_message_id | length > 0 + - bootlinux_b4_am_this_host|bool + +- name: Set the pathname of the .config templates directory + ansible.builtin.set_fact: + template_path: "{{ topdir_path }}/playbooks/roles/bootlinux/templates" + +- name: Check whether config-kdevops exists + delegate_to: localhost + ansible.builtin.stat: + path: "{{ template_path }}/config-kdevops" + register: config_kdevops + +- name: Found config-kdevops, using it for template + ansible.builtin.set_fact: + linux_config: "config-kdevops" + when: config_kdevops.stat.exists + +- name: No config-kdevops, looking for {{ target_linux_config }} + ansible.builtin.set_fact: + linux_config: "{{ target_linux_config }}" + when: not config_kdevops.stat.exists + +- name: Check whether specific kernel config exists for {{ target_linux_ref }} + delegate_to: localhost + ansible.builtin.stat: + path: "{{ template_path }}/{{ target_linux_config }}" + register: kernel_config + +- name: Find all linux-next configs + delegate_to: localhost + ansible.builtin.find: + paths: "{{ template_path }}" + patterns: "config-next*" + file_type: file + recurse: false + register: found_configs + when: + - not config_kdevops.stat.exists + - not kernel_config.stat.exists + +- name: Extract the date from the filenames + ansible.builtin.set_fact: + configs_with_dates: "{{ configs_with_dates | default([]) + [{'file': item.path, 'date': (item.path | regex_search('config-next-(\\d{8})')).split('-')[-1]}] }}" + loop: "{{ found_configs.files }}" + no_log: true + when: + - not config_kdevops.stat.exists + - not kernel_config.stat.exists + - item.path is search('config-next-(\\d{8})') + +- name: Sort configs based on date extracted from filename + ansible.builtin.set_fact: + sorted_configs: "{{ configs_with_dates | selectattr('date', 'defined') | sort(attribute='date', reverse=True) | map(attribute='file') | list }}" + when: + - not config_kdevops.stat.exists + - not kernel_config.stat.exists + - configs_with_dates | length > 0 + +- name: Set latest linux-next config + ansible.builtin.set_fact: + latest_linux_next_config: "{{ sorted_configs[0] }}" + when: + - not config_kdevops.stat.exists and not kernel_config.stat.exists + - sorted_configs | length > 0 + +- name: Use the specific kernel config or fallback to the latest linux-next + ansible.builtin.set_fact: + linux_config: "{{ target_linux_config | default('') if kernel_config.stat.exists else (latest_linux_next_config | default('') | basename) }}" + when: + - not config_kdevops.stat.exists + - not kernel_config.stat.exists + - latest_linux_next_config is defined + +- name: Verify that the Linux configuration file exists + delegate_to: localhost + ansible.builtin.stat: + path: "{{ template_path }}/{{ linux_config }}" + register: config_stat + when: linux_config is defined + +- name: Fail if the configuration file does not exist + ansible.builtin.fail: + msg: "The configuration file {{ template_path }}/{{ linux_config }} does not exist." + when: not config_stat.stat.exists + +- name: Copy configuration for Linux {{ target_linux_tree }} + ansible.builtin.template: + src: "{{ template_path }}/{{ linux_config }}" + dest: "{{ target_linux_dir_path }}/.config" + owner: "{{ data_user }}" + group: "{{ data_group }}" + mode: "u=rw,g=r,o=r" + +- name: Set the kernel localversion + ansible.builtin.lineinfile: + path: "{{ target_linux_dir_path }}/localversion" + line: "{{ target_linux_localversion }}" + mode: "u=rw,g=r,o=r" + create: true + when: + - target_linux_localversion is defined and target_linux_localversion != "" + +- name: Configure Linux {{ target_linux_tree }} + community.general.make: + chdir: "{{ target_linux_dir_path }}" + target: "olddefconfig" + +- name: Build {{ target_linux_tree }} + community.general.make: + chdir: "{{ target_linux_dir_path }}" + target: "all" + jobs: "{{ ansible_processor_nproc }}" + +- name: Remove the artifacts directory + delegate_to: localhost + ansible.builtin.file: + path: "{{ build_artifacts_dir }}" + state: absent + +- name: Create an empty artifacts directory + delegate_to: localhost + ansible.builtin.file: + path: "{{ build_artifacts_dir }}" + state: directory + mode: "u=rwx,g=rx,o=rx" + +- name: Build kernel .deb packages + when: + - ansible_os_family == "Debian" + block: + - name: Make the bindeb-pkg target + community.general.make: + chdir: "{{ target_linux_dir_path }}" + target: "bindeb-pkg" + + - name: Find the build artifacts + ansible.builtin.find: + paths: "{{ target_linux_dir_path }}" + patterns: "*.deb" + file_type: file + recurse: true + register: found_debs + + - name: Fetch the build artifacts to the control host + ansible.builtin.fetch: + src: "{{ item.path }}" + dest: "{{ build_artifacts_dir }}" + flat: true + loop: "{{ found_debs.files }}" + loop_control: + label: "Fetching {{ item.path }}" + +- name: Build kernel .rpm packages + when: + - ansible_os_family == "RedHat" + block: + - name: Make the binrpm-pkg target + community.general.make: + chdir: "{{ target_linux_dir_path }}" + target: "binrpm-pkg" + + - name: Find the build artifacts + ansible.builtin.find: + paths: "{{ target_linux_dir_path }}/rpmbuild/RPMS" + patterns: "*.rpm" + file_type: file + recurse: true + register: found_rpms + + - name: Fetch the build artifacts to the control host + ansible.builtin.fetch: + src: "{{ item.path }}" + dest: "{{ build_artifacts_dir }}" + flat: true + loop: "{{ found_rpms.files }}" + loop_control: + label: "Fetching {{ item.path }}" + +- name: Extract the release information of the built kernel + community.general.make: + chdir: "{{ target_linux_dir_path }}" + target: "kernelrelease" + register: kernelrelease + +- name: Store the kernel release information with the build artifacts + delegate_to: localhost + ansible.builtin.lineinfile: + create: true + line: "{{ kernelrelease.stdout }}" + mode: "u=rw,g=r,o=r" + path: "{{ build_artifacts_dir }}/kernel.release" diff --git a/workflows/linux/Makefile b/workflows/linux/Makefile index 65bbb8ae9..bb7441e71 100644 --- a/workflows/linux/Makefile +++ b/workflows/linux/Makefile @@ -84,6 +84,7 @@ linux-help-menu: @echo "linux-clone - Only clones Linux" @echo "linux-grub-setup - Ensures the appropriate target kernel is set to boot" @echo "linux-reboot - Reboot guests" + @echo "linux-packages - Clones, builds, and packages a Linux kernel" @echo "uname - Prints current running kernel" PHONY += linux-help-end @@ -159,6 +160,12 @@ linux-reboot: $(KDEVOPS_HOSTFILE) $(KDEVOPS_PLAYBOOKS_DIR)/bootlinux.yml \ --extra-vars="$(BOOTLINUX_ARGS)" $(LIMIT_HOSTS) --tags vars,reboot +PHONY += linux-packages +linux-packages: + $(Q)ansible-playbook $(ANSIBLE_VERBOSE) -i $(KDEVOPS_HOSTFILE) \ + $(KDEVOPS_PLAYBOOKS_DIR)/build_linux.yml \ + --extra-vars="$(BOOTLINUX_ARGS)" $(LIMIT_HOSTS) + PHONY += uname uname: $(Q)ansible all -i hosts -b -m command -a "uname -r" -o \ From 8a304f84c42bdd5c508578c7002de09cee7a4073 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 22 Apr 2025 11:49:06 -0400 Subject: [PATCH 3/3] Experimental: Add a separate install_linux role Add a role that can grab the artifacts in workflows/linux/artifacts and install them on all guests/instances. Signed-off-by: Chuck Lever --- playbooks/install_linux.yml | 4 + playbooks/roles/install_linux/README.md | 136 ++++++++++++ .../roles/install_linux/defaults/main.yml | 43 ++++ .../tasks/install-deps/debian/main.yml | 44 ++++ .../tasks/install-deps/redhat/main.yml | 76 +++++++ .../tasks/install-deps/suse/main.yml | 31 +++ playbooks/roles/install_linux/tasks/main.yml | 142 +++++++++++++ .../tasks/update-grub/debian.yml | 8 + .../tasks/update-grub/install.yml | 196 ++++++++++++++++++ .../install_linux/tasks/update-grub/main.yml | 15 ++ .../tasks/update-grub/redhat.yml | 36 ++++ .../install_linux/tasks/update-grub/suse.yml | 11 + workflows/linux/Makefile | 8 + 13 files changed, 750 insertions(+) create mode 100644 playbooks/install_linux.yml create mode 100644 playbooks/roles/install_linux/README.md create mode 100644 playbooks/roles/install_linux/defaults/main.yml create mode 100644 playbooks/roles/install_linux/tasks/install-deps/debian/main.yml create mode 100644 playbooks/roles/install_linux/tasks/install-deps/redhat/main.yml create mode 100644 playbooks/roles/install_linux/tasks/install-deps/suse/main.yml create mode 100644 playbooks/roles/install_linux/tasks/main.yml create mode 100644 playbooks/roles/install_linux/tasks/update-grub/debian.yml create mode 100644 playbooks/roles/install_linux/tasks/update-grub/install.yml create mode 100644 playbooks/roles/install_linux/tasks/update-grub/main.yml create mode 100644 playbooks/roles/install_linux/tasks/update-grub/redhat.yml create mode 100644 playbooks/roles/install_linux/tasks/update-grub/suse.yml diff --git a/playbooks/install_linux.yml b/playbooks/install_linux.yml new file mode 100644 index 000000000..273ef4512 --- /dev/null +++ b/playbooks/install_linux.yml @@ -0,0 +1,4 @@ +--- +- hosts: all + roles: + - role: install_linux diff --git a/playbooks/roles/install_linux/README.md b/playbooks/roles/install_linux/README.md new file mode 100644 index 000000000..0b440d213 --- /dev/null +++ b/playbooks/roles/install_linux/README.md @@ -0,0 +1,136 @@ +bootlinux +========= + +The ansible bootlinux lets you get, build and install Linux. It also lets you +apply custom patches, remove kernels, etc. Anything you have to do with regards +to generic kernel development. The defaults it will track one of the latest +stable kernels that are still supported, using the linux stable git tree. + +Requirements +------------ + +You are expected to have an extra partition + +Role Variables +-------------- + + * infer_uid_and_group: defaults to False, if set to True, then we will ignore + the passed on data_user and data_group and instead try to infer this by + inspecting the `whoami` and getent on the logged in target system we are + provisioning. So if user sam is running able on a host, targetting a system + called foofighter and logging into that system using username pincho, + then the data_user will be set overwritten and set to pincho. We will then + also lookup for pincho's default group id and use that for data_group. + This is useful if you are targetting a slew of systems and don't really + want to deal with the complexities of the username and group, and the + default target username you use to ssh into a system suffices to use as + a base. This is set to False to remain compatible with old users of + this role. + * data_path: where to place the git trees we clone under + * data_user: the user to assign permissions to + * data_group: the group to assign permissions to + + * data_device: the target device to use for the data partition + * data_fstype: the filesystem to store the data parition under + * data_label: the label to use + * data_fs_opts: the filesystem options to use, you want to ensure to add the + label + + * target_linux_admin_name: your developer name + * target_linux_admin_email: your email + * target_linux_git: the git tree to clone, by default this is the linux-stable + tree + * target_linux_tree: the name of the tree + * target_linux_dir_path: where to place the tree on the target system + + * target_linux_ref : the actual tag as used on linux, so v4.19.62 + * target_linux_extra_patch: if defined an extra patch to apply with git + am prior to compilation + * target_linux_config: the configuration file to use + * make: the make command to use + * target_linux_make_cmd: the actual full make command and its arguments + * target_linux_make_install_cmd: the install command + +Dependencies +------------ + +None. + +Example Playbook +---------------- + +Below is an example playbook, say a bootlinux.yml file: + +``` +--- +- hosts: all + roles: + - role: bootlinux +``` + +Custom runs +=========== + +Say you want to boot compile a vanilla kernel and you have created a new +section under the hosts file called [dev], with a subset of the [all] section. +You can compile say a vanilla kernel v4.19.58 with an extra set of patches we'd +`git am` for you on top by using the following: + +``` +cd ansible +ansible-playbook -i hosts -l dev --extra-vars "target_linux_extra_patch=pend-v4.19.58-fixes-20190716-v2.patch" bootlinux.yml +``` + +You'd place the `pend-v4.19.58-fixes-20190716-v2.patch` file on the directory +`ansible/roles/bootlinux/templates/`. + +Now say you wantd to be explicit about a tag of Linux you'd want to use: + +``` +ansible-playbook -i hosts -l dev --extra-vars "target_linux_ref=v4.19.21 "target_linux_extra_patch=try-v4.19.20-fixes-20190716-v1.patch" bootlinux.yml +``` + +To uninstall a kernel: + +``` +ansible-playbook -i hosts -l dev --tags uninstall-linux --extra-vars "uninstall_kernel_ver=4.19.58+" bootlinux.yml +``` + +To ensure you can get the grub prompt: + +```bash +ansible-playbook -i hosts --tags console,vars,manual-update-grub playbooks/bootlinux.yml +``` + +The ansible bootlinux role relies on the create_partition role to create a data +partition where we can stuff code, and compile it. To test that aspect of +the bootlinux role you can run: + +``` +ansible-playbook -i hosts -l baseline --tags data_partition,partition bootlinux.yml +``` + +To reboot all hosts: + +```bash +ansible-playbook -i hosts bootlinux.yml --tags reboot +``` + +For further examples refer to one of this role's users, the +[https://github.com/mcgrof/kdevops](kdevops) project or the +[https://github.com/mcgrof/oscheck](oscheck) project from where +this code originally came from. + +# TODO + +## Avoiding carrying linux-next configs + +It seems a waste of space to be adding configurations for linux-next for all +tags. It seems easier to just look for the latest linux-next and try that. +We just symlink linux-next files when we really need to, and when something +really needs a new config, we then just add a new file. + +License +------- + +copyleft-next-0.3.1 diff --git a/playbooks/roles/install_linux/defaults/main.yml b/playbooks/roles/install_linux/defaults/main.yml new file mode 100644 index 000000000..43edb3caf --- /dev/null +++ b/playbooks/roles/install_linux/defaults/main.yml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier copyleft-next-0.3.1 +--- +kdevops_bootlinux: False +infer_uid_and_group: False + +data_path: "/data" +data_user: "vagrant" +data_group: "vagrant" + +data_device: "/dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_kdevops0" +data_fstype: "xfs" +data_label: "data" +data_fs_opts: "-L {{ disk_setup_label }}" + +# Linux target defaults +target_linux_admin_name: "Hacker Amanda" +target_linux_admin_email: "devnull@kernel.org" +target_linux_git: "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git" +target_linux_shallow_depth: 0 +target_linux_tree: "linux-stable" +target_linux_dir_path: "{{ data_path }}/{{ target_linux_tree }}" +kdevops_baseline_and_dev: False + +target_linux_ref: "v4.19.133" +target_linux_delta_file: +target_linux_config: "config-{{ target_linux_ref }}" +make: "make" +# Once ansible v2.10 becomes available we can move on to using +# ansible_processor_nproc but that was merged in 2020: +# The commit is 34db57a47f875d11c4068567b9ec7ace174ec4cf +# introduce fact "ansible_processor_nproc": number of usable vcpus #66569 +# https://github.com/ansible/ansible/pull/66569 +target_linux_make_cmd: "{{ make }} -j{{ ansible_processor_vcpus }}" +target_linux_make_install_cmd: "{{ target_linux_make_cmd }} modules_install install" + +uninstall_kernel_enable: False + +build_artifacts_dir: "{{ topdir_path }}/workflows/linux/artifacts/" + +kdevops_workflow_enable_cxl: False + +bootlinux_cxl_test: False +bootlinux_tree_set_by_cli: False diff --git a/playbooks/roles/install_linux/tasks/install-deps/debian/main.yml b/playbooks/roles/install_linux/tasks/install-deps/debian/main.yml new file mode 100644 index 000000000..51b216e47 --- /dev/null +++ b/playbooks/roles/install_linux/tasks/install-deps/debian/main.yml @@ -0,0 +1,44 @@ +--- +# Install dependencies for building linux on Debian + +- name: Update apt cache + become: yes + become_method: sudo + apt: + update_cache: yes + tags: linux + +# apt-get build-dep does not capture all requirements +- name: Install Linux kernel build dependencies + become: yes + become_method: sudo + apt: + name: + - bison + - flex + - git + - gcc + - make + - gawk + - bc + - dump + - indent + - sed + - libssl-dev + - libelf-dev + - liburcu-dev + - xfsprogs + - e2fsprogs + - btrfs-progs + - ntfs-3g + - mdadm + - rpcbind + - portmap + - hwinfo + - open-iscsi + - python3-pip + - zstd + - libncurses-dev + - b4 + state: present + tags: linux diff --git a/playbooks/roles/install_linux/tasks/install-deps/redhat/main.yml b/playbooks/roles/install_linux/tasks/install-deps/redhat/main.yml new file mode 100644 index 000000000..57a340979 --- /dev/null +++ b/playbooks/roles/install_linux/tasks/install-deps/redhat/main.yml @@ -0,0 +1,76 @@ +--- +- name: Enable installation of packages from EPEL + ansible.builtin.include_role: + name: epel-release + when: + - ansible_distribution != "Fedora" + +- name: Install packages we care about + become: true + become_method: ansible.builtin.sudo + ansible.builtin.dnf: + update_cache: true + name: "{{ packages }}" + retries: 3 + delay: 5 + register: result + until: result is succeeded + vars: + packages: + - bison + - flex + - git-core + - e2fsprogs + - xfsprogs + - xfsdump + - lvm2 + - gcc + - make + - gawk + - bc + - dump + - libtool + - psmisc + - sed + - vim + - fio + - libaio-devel + - diffutils + - net-tools + - ncurses-devel + - xfsprogs + - e2fsprogs + - elfutils-libelf-devel + - ntfs-3g + - mdadm + - rpcbind + - portmap + - hwinfo + - iscsi-initiator-utils + - openssl + - openssl-devel + - dwarves + - userspace-rcu + - zstd + +- name: Install btrfs-progs + become: true + become_method: ansible.builtin.sudo + ansible.builtin.dnf: + update_cache: true + name: "{{ packages }}" + retries: 3 + delay: 5 + register: result + until: result is succeeded + vars: + packages: + - btrfs-progs + when: ansible_distribution == 'Fedora' + +- name: Remove packages that mess with initramfs + become: true + become_method: ansible.builtin.sudo + ansible.builtin.dnf: + state: absent + name: dracut-config-generic diff --git a/playbooks/roles/install_linux/tasks/install-deps/suse/main.yml b/playbooks/roles/install_linux/tasks/install-deps/suse/main.yml new file mode 100644 index 000000000..204a181b8 --- /dev/null +++ b/playbooks/roles/install_linux/tasks/install-deps/suse/main.yml @@ -0,0 +1,31 @@ +--- +- name: Install Linux kernel build dependencies for SUSE sources + become: yes + become_method: sudo + zypper: + name: + - bison + - flex + - git-core + - gcc + - make + - gawk + - bc + - dump + - sed + - libopenssl-devel + - libelf-devel + - liburcu8 + - diffutils + - net-tools + - ncurses-devel + - xfsprogs + - e2fsprogs + - btrfsprogs + - ntfs-3g + - mdadm + - rpcbind + - portmap + - hwinfo + - open-iscsi + disable_recommends: no diff --git a/playbooks/roles/install_linux/tasks/main.yml b/playbooks/roles/install_linux/tasks/main.yml new file mode 100644 index 000000000..a0da4c110 --- /dev/null +++ b/playbooks/roles/install_linux/tasks/main.yml @@ -0,0 +1,142 @@ +--- +- name: Include optional extra_vars + ansible.builtin.include_vars: + file: "{{ item }}" + with_first_found: + - files: + - "../extra_vars.yml" + - "../extra_vars.yaml" + - "../extra_vars.json" + skip: true + failed_when: false + tags: vars + +- name: Debian-specific set up + ansible.builtin.import_tasks: install-deps/debian/main.yml + when: + - ansible_os_family == "Debian" + +- name: Suse-specific set up + ansible.builtin.import_tasks: install-deps/suse/main.yml + when: + - ansible_os_family == "Suse" + +- name: Red Hat-specific set up + ansible.builtin.import_tasks: install-deps/redhat/main.yml + when: + - ansible_os_family == "RedHat" + +# We use "console serial" so to enable real consoles to be +# preferred first, and fallback to the serial as secondary +# option. This let's us work with hardware serial consoles +# say on IPMIs and virtual guests ('virsh console'). +- name: Ensure we can get the GRUB prompt on reboot + become: true + become_flags: 'su - -c' + become_method: ansible.builtin.sudo + ansible.builtin.lineinfile: + path: /etc/default/grub + regexp: '^GRUB_TERMINAL=' + line: GRUB_TERMINAL="console serial" + tags: + - linux + - git + - config + - console + +- name: Update the boot GRUB file + ansible.builtin.import_tasks: update-grub/main.yml + tags: + - linux + - uninstall-linux + - manual-update-grub + - console + +- name: Ensure DEFAULTDEBUG is set + become: true + become_flags: 'su - -c' + become_method: ansible.builtin.sudo + register: grub_default_saved_cmd + ansible.builtin.lineinfile: + path: /etc/sysconfig/kernel + regexp: '^DEFAULTDEBUG=' + line: DEFAULTDEBUG=yes + when: + - ansible_os_family == "RedHat" + tags: + - linux + - git + - config + - saved + +- name: Install the built kernel RPMs on the target nodes + when: + - ansible_os_family == "RedHat" + tags: + - linux + - install-linux + block: + - name: Find the kernel build artifacts on the control host + delegate_to: localhost + ansible.builtin.find: + paths: "{{ build_artifacts_dir }}" + patterns: "*.rpm" + file_type: file + recurse: true + register: found_rpms + + - name: Upload the kernel build artifacts to the target nodes + ansible.builtin.copy: + src: "{{ item.path }}" + dest: "/tmp" + mode: "u=rw,g=r,o=r" + loop: "{{ found_rpms.files }}" + loop_control: + label: "Uploading {{ item.path }}" + + - name: Initialize list of packages to install + ansible.builtin.set_fact: + packages: [] + + - name: Build a list of packages to install + ansible.builtin.set_fact: + packages: "{{ packages + ['/tmp/' + item.path | basename ] }}" + loop: "{{ found_rpms.files }}" + loop_control: + label: "Adding {{ item.path }}" + + - name: Install the kernel build artifacts on the target nodes + become: true + become_method: ansible.builtin.sudo + ansible.builtin.dnf: + name: "{{ packages }}" + state: present + disable_gpg_check: true + +- name: Set the default kernel on the target nodes + ansible.builtin.import_tasks: update-grub/install.yml + tags: + - linux + - git + - config + - saved + +- name: Reboot the target nodes into Linux {{ target_linux_tree }} + become: true + become_method: ansible.builtin.sudo + ansible.builtin.reboot: + tags: + - linux + - reboot + +- name: Refresh facts + ansible.builtin.gather_facts: + +- name: Check the uname on the target nodes + ansible.builtin.debug: + msg: "Target kernel {{ target_linux_ref }}; Running kernel {{ ansible_kernel }}" + tags: + - linux + - git + - config + - uname diff --git a/playbooks/roles/install_linux/tasks/update-grub/debian.yml b/playbooks/roles/install_linux/tasks/update-grub/debian.yml new file mode 100644 index 000000000..3c7deea21 --- /dev/null +++ b/playbooks/roles/install_linux/tasks/update-grub/debian.yml @@ -0,0 +1,8 @@ +- name: Run update-grub + become: yes + become_flags: 'su - -c' + become_method: sudo + command: "update-grub" + register: grub_update + changed_when: "grub_update.rc == 0" + tags: [ 'linux', 'manual-update-grub', 'console' ] diff --git a/playbooks/roles/install_linux/tasks/update-grub/install.yml b/playbooks/roles/install_linux/tasks/update-grub/install.yml new file mode 100644 index 000000000..17966af58 --- /dev/null +++ b/playbooks/roles/install_linux/tasks/update-grub/install.yml @@ -0,0 +1,196 @@ +# There is slightly confusing user-experience and not complete documentation +# about the requirements for using grub-set-default in light of the fact that +# most Linux distributions use sub-menus. You need to use GRUB_DEFAULT=saved +# there is a few caveats with its use which are not well documented anywhere +# and I'm pretty sure tons of people are running into these issues. +# +# I'll document them here for posterity and so to justify the approach used +# in kdevops to ensure we do boot the correct kernel. +# +# Some users erroneously claim that you also need GRUB_SAVEDEFAULT=true when +# using GRUB_DEFAULT=saved but this is not true. The issue with using +# GRUB_DEFAULT=saved which causes confusion is that most distributions +# today use submenus folks do not take these into account when using +# grub-set-default and the documentation about grub-set-default is not +# clear about this requirement. +# +# Sadly, if you use a bogus kernel grub-set-default will not complain. For +# example since most distributions use submenus, if you install a new kernel you +# may end up in a situation as follows: +# +# menuentry 'Debian GNU/Linux' ... { +# ... +# } +# submenu 'Advanced options for Debian GNU/Linux' ... { +# menuentry 'Debian GNU/Linux, with Linux 5.16.0-4-amd64' ... { +# ... +# } +# menuentry 'Debian GNU/Linux, with Linux 5.16.0-4-amd64 (recovery mode)' ... { +# ... +# } +# menuentry 'Debian GNU/Linux, with Linux 5.10.105' ... { +# ... +# } +# ... etc ... +# } +# +# So under this scheme the 5.10.105 kernel is actually "1>2" and so if +# you used: +# +# grub-set-default 3 +# +# This would not return an error and you would expect it to work. This +# is a bug in grub-set-default, it should return an error. The correct +# way to set this with submenus would be: +# +# grub-set-default "1>2" +# +# However doing the reverse mapping is something which can get complicated +# and there is no upstream GRUB2 support to do this for you. We can simplify +# this problem instead by disabling the submenus, with GRUB_DISABLE_SUBMENU=y, +# making the menu flat and then just querying for the linear mapping using +# ansible using awk | grep and tail. +# +# So for instance, using GRUB_DISABLE_SUBMENU=y results in the following +# options: +# +# vagrant@kdevops-xfs-nocrc ~ $ awk -F\' '/menuentry / {print $2}' /boot/grub/grub.cfg | awk '{print NR-1" ... "$0}' +# 0 ... Debian GNU/Linux, with Linux 5.16.0-4-amd64 +# 1 ... Debian GNU/Linux, with Linux 5.16.0-4-amd64 (recovery mode) +# 2 ... Debian GNU/Linux, with Linux 5.10.105 +# 3 ... Debian GNU/Linux, with Linux 5.10.105 (recovery mode) +# 4 ... Debian GNU/Linux, with Linux 5.10.0-5-amd64 +# 5 ... Debian GNU/Linux, with Linux 5.10.0-5-amd64 (recovery mode) +# +# We have a higher degree of confidence with this structure when looking +# for "5.10.105" that its respective boot entry 2 is the correct one. So we'd +# now just use: +# +# grub-set-default 2 +- name: Ensure we have GRUB_DEFAULT=saved + become: true + become_flags: 'su - -c' + become_method: ansible.builtin.sudo + ansible.builtin.lineinfile: + path: /etc/default/grub + regexp: '^GRUB_DEFAULT=' + line: GRUB_DEFAULT=saved + register: grub_default_saved_cmd + tags: + - linux + - git + - config + - saved + +- name: Use GRUB_DISABLE_SUBMENU=y to enable grub-set-default use with one digit + become: true + become_flags: 'su - -c' + become_method: ansible.builtin.sudo + register: grub_disable_submenu_cmd + ansible.builtin.lineinfile: + path: /etc/default/grub + regexp: '^GRUB_DISABLE_SUBMENU=' + line: GRUB_DISABLE_SUBMENU=y + tags: + - linux + - git + - config + - saved + +- name: Update your boot GRUB file if necessary to ensure GRUB flat earth + ansible.builtin.import_tasks: update-grub/main.yml + tags: + - linux + - uninstall-linux + - manual-update-grub + - console + +- name: Read the artifacts release file + delegate_to: localhost + vars: + release_file: "{{ topdir_path }}/workflows/linux/artifacts/kernel.release" + ansible.builtin.set_fact: + kernelrelease: "{{ lookup('file', release_file) }}" + +- name: Show kernel release information + ansible.builtin.debug: + var: kernelrelease + +- name: Construct command line to determine default kernel ID + ansible.builtin.set_fact: + determine_default_kernel_id: >- + awk -F\' '/menuentry / {print $2}' + /boot/grub/grub.cfg | awk '{print NR-1" ... "$0}' | + grep {{ kernelrelease }} | head -1 | awk '{print $1}' + when: + - ansible_os_family != 'RedHat' or ansible_distribution_major_version | int < 8 + +- name: Construct command line to determine default kernel ID for RHEL >= 8 + ansible.builtin.set_fact: + determine_default_kernel_id: >- + for f in $(ls -1 /boot/loader/entries/*.conf); do + cat $f; + done | grep title | awk '{ gsub("title ", "", $0); print }' | grep '{{ kernelrelease }}'; + when: + - ansible_os_family == "RedHat" and ansible_distribution_major_version | int >= 8 + +# If this fails then grub-set-default won't be run, and the assumption here +# is either you do the work to enhance the heuristic or live happy with the +# assumption that grub2's default of picking the latest kernel is the best +# option. +- name: Try to find your target kernel's GRUB boot entry number now that the menu is flattened for {{ target_linux_ref }} using inferred KERNELRELEASE {{ kernelrelease}} + become: true + become_flags: 'su - -c' + become_method: ansible.builtin.sudo + vars: + target_kernel: "{{ target_linux_ref | replace('v', '') }}" + ansible.builtin.shell: " {{ determine_default_kernel_id }} " + register: grub_boot_number_cmd + tags: + - linux + - git + - config + - saved + +- name: Obtain command to set default kernel to boot + ansible.builtin.set_fact: + grub_set_default_boot_kernel: grub-set-default + when: + - ansible_os_family != "RedHat" or ansible_distribution_major_version | int < 8 + +- name: Obtain command to set default kernel to boot for RHEL >= 8 + ansible.builtin.set_fact: + grub_set_default_boot_kernel: grub2-set-default + when: + - ansible_os_family == "RedHat" and ansible_distribution_major_version | int >= 8 + +- name: Set the target kernel to be booted by default + become: true + become_flags: 'su - -c' + become_method: ansible.builtin.sudo + vars: + target_boot_entry: "{{ grub_boot_number_cmd.stdout_lines.0 }}" + ansible.builtin.command: "{{ grub_set_default_boot_kernel }} \"{{ target_boot_entry }}\"" + when: + - grub_boot_number_cmd.rc == 0 + - grub_boot_number_cmd.stdout != "" + tags: + - linux + - git + - config + - saved + +- name: Itemize kernel and GRUB entry we just selected + vars: + target_kernel: "{{ target_linux_ref | replace('v', '') }}" + target_boot_entry: "{{ grub_boot_number_cmd.stdout_lines.0 }}" + ansible.builtin.debug: + msg: "{{ target_kernel }} determined to be {{ target_boot_entry }} on the GRUB2 flat menu. Ran: grub-set-default {{ target_boot_entry }}" + when: + - grub_boot_number_cmd.rc == 0 + - grub_boot_number_cmd.stdout != "" + tags: + - linux + - git + - config + - saved diff --git a/playbooks/roles/install_linux/tasks/update-grub/main.yml b/playbooks/roles/install_linux/tasks/update-grub/main.yml new file mode 100644 index 000000000..a565e0ac2 --- /dev/null +++ b/playbooks/roles/install_linux/tasks/update-grub/main.yml @@ -0,0 +1,15 @@ +--- +- name: Debian-specific grub update + ansible.builtin.import_tasks: debian.yml + when: + - ansible_os_family == "Debian" + +- name: Red Hat-specific grub update + ansible.builtin.import_tasks: redhat.yml + when: + - ansible_os_family == "RedHat" + +- name: Suse-specific grub update + ansible.builtin.import_tasks: suse.yml + when: + - ansible_os_family == "Suse" diff --git a/playbooks/roles/install_linux/tasks/update-grub/redhat.yml b/playbooks/roles/install_linux/tasks/update-grub/redhat.yml new file mode 100644 index 000000000..11a92f34b --- /dev/null +++ b/playbooks/roles/install_linux/tasks/update-grub/redhat.yml @@ -0,0 +1,36 @@ +- name: Disable Grub menu auto-hide + become: true + become_flags: 'su - -c' + become_method: ansible.builtin.sudo + ansible.builtin.command: "grub2-editenv - unset menu_auto_hide" + register: grub_edit + changed_when: "grub_edit.rc == 0" + +- name: Determine if system was booted using UEFI + ansible.builtin.stat: + path: "/sys/firmware/efi/efivars" + register: efi_boot + +- name: Use /etc/grub2.cfg as the grub configuration file + ansible.builtin.set_fact: + grub_config_file: "/etc/grub2.cfg" + when: + - not efi_boot.stat.exists + +- name: Use /etc/grub2-efi.cfg as the configuration file + ansible.builtin.set_fact: + grub_config_file: "/etc/grub2-efi.cfg" + when: + - efi_boot.stat.exists + +- name: Run update-grub + become: true + become_flags: 'su - -c' + become_method: ansible.builtin.sudo + ansible.builtin.command: "grub2-mkconfig -o {{ grub_config_file }}" + register: grub_update + changed_when: "grub_update.rc == 0" + tags: + - linux + - manual-update-grub + - console diff --git a/playbooks/roles/install_linux/tasks/update-grub/suse.yml b/playbooks/roles/install_linux/tasks/update-grub/suse.yml new file mode 100644 index 000000000..1cb1fdc55 --- /dev/null +++ b/playbooks/roles/install_linux/tasks/update-grub/suse.yml @@ -0,0 +1,11 @@ +- name: Run update-grub + become: true + become_flags: 'su - -c' + become_method: ansible.builtin.sudo + ansible.builtin.command: "update-bootloader --refresh" + register: grub_update + changed_when: "grub_update.rc == 0" + tags: + - linux + - manual-update-grub + - console diff --git a/workflows/linux/Makefile b/workflows/linux/Makefile index bb7441e71..06722d590 100644 --- a/workflows/linux/Makefile +++ b/workflows/linux/Makefile @@ -85,6 +85,7 @@ linux-help-menu: @echo "linux-grub-setup - Ensures the appropriate target kernel is set to boot" @echo "linux-reboot - Reboot guests" @echo "linux-packages - Clones, builds, and packages a Linux kernel" + @echo "linux-artifacts - Installs artifacts generated by 'linux-packages'" @echo "uname - Prints current running kernel" PHONY += linux-help-end @@ -166,6 +167,13 @@ linux-packages: $(KDEVOPS_PLAYBOOKS_DIR)/build_linux.yml \ --extra-vars="$(BOOTLINUX_ARGS)" $(LIMIT_HOSTS) +PHONY += linux-artifacts +linux-artifacts: + $(Q)ansible-playbook $(ANSIBLE_VERBOSE) \ + -i $(KDEVOPS_HOSTFILE) \ + $(KDEVOPS_PLAYBOOKS_DIR)/install_linux.yml \ + --extra-vars="$(BOOTLINUX_ARGS)" $(LIMIT_HOSTS) + PHONY += uname uname: $(Q)ansible all -i hosts -b -m command -a "uname -r" -o \