From 8f54f125fa2d933c5647ce8ee0f3b66aa087602c Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Fri, 10 Mar 2023 14:15:36 +0000 Subject: [PATCH] Copy udev rules before calling bootloader finish client --- service/lib/dinstaller/storage/finisher.rb | 47 ++++++ .../test/dinstaller/storage/finisher_test.rb | 140 ++++++++++++++++++ .../test/dinstaller/storage/manager_test.rb | 7 +- .../etc/udev/rules.d/41-cio-ignore.rules | 2 + .../udev/rules.d/41-dasd-eckd-0.0.0160.rules | 10 ++ .../etc/udev/rules.d/41-qeth-0.0.0800.rules | 22 +++ .../etc/udev/rules.d/70-persistent-net.rules | 8 + 7 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 service/test/dinstaller/storage/finisher_test.rb create mode 100644 service/test/fixtures/root_dir/etc/udev/rules.d/41-cio-ignore.rules create mode 100644 service/test/fixtures/root_dir/etc/udev/rules.d/41-dasd-eckd-0.0.0160.rules create mode 100644 service/test/fixtures/root_dir/etc/udev/rules.d/41-qeth-0.0.0800.rules create mode 100644 service/test/fixtures/root_dir/etc/udev/rules.d/70-persistent-net.rules diff --git a/service/lib/dinstaller/storage/finisher.rb b/service/lib/dinstaller/storage/finisher.rb index 5846c5158e..7cc9cc756f 100644 --- a/service/lib/dinstaller/storage/finisher.rb +++ b/service/lib/dinstaller/storage/finisher.rb @@ -28,6 +28,8 @@ require "dinstaller/helpers" require "abstract_method" +Yast.import "Arch" + module DInstaller module Storage # Auxiliary class to handle the last storage-related steps of the installation @@ -36,6 +38,9 @@ class Finisher include Helpers # Constructor + # @param logger [Logger] + # @param config [Config] + # @param security [Security] def initialize(logger, config, security) @logger = logger @config = config @@ -69,6 +74,7 @@ def run def possible_steps [ SecurityStep.new(logger, security), + CopyFilesStep.new(logger), BootloaderStep.new(logger), TpmStep.new(logger, config), IguanaStep.new(logger), @@ -121,6 +127,41 @@ def staging_graph end end + # Step to copy files from the inst-sys to the target system + class CopyFilesStep < Step + UDEV_RULES_DIR = "/etc/udev/rules.d" + ROOT_PATH = "/" + FILES = [ + { dir: "/etc/udev/rules.d", file: "40-*" }, + { dir: "/etc/udev/rules.d", file: "41-*" }, + { dir: "/etc/udev/rules.d", file: "70-persistent-net.rules" } + ].freeze + + def label + "Copying important installation files to the target system" + end + + def run? + glob_files.any? + end + + def run + target = File.join(Yast::Installation.destdir, UDEV_RULES_DIR) + FileUtils.mkdir_p(target) + FileUtils.cp(glob_files, target) + end + + private + + def root_dir + ROOT_PATH + end + + def glob_files + Dir.glob(FILES.map { |f| File.join(root_dir, f[:dir], f[:file]) }) + end + end + # Step to write the security settings class SecurityStep < Step # Constructor @@ -145,8 +186,14 @@ def label end def run + cio_ignore_finish if Yast::Arch.s390 ::Bootloader::FinishClient.new.write end + + def cio_ignore_finish + require "installation/cio_ignore" + wfm_write("cio_ignore_finish") + end end # Step to configure the file-system snapshots diff --git a/service/test/dinstaller/storage/finisher_test.rb b/service/test/dinstaller/storage/finisher_test.rb new file mode 100644 index 0000000000..b811b704d7 --- /dev/null +++ b/service/test/dinstaller/storage/finisher_test.rb @@ -0,0 +1,140 @@ +# frozen_string_literal: true + +# Copyright (c) [2023] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require_relative "storage_helpers" +require "dinstaller/helpers" +require "dinstaller/config" +require "dinstaller/security" +require "dinstaller/storage/finisher" + +describe DInstaller::Storage::Finisher do + include DInstaller::RSpec::StorageHelpers + + subject(:storage) { described_class.new(logger, config, security) } + + let(:logger) { Logger.new($stdout, level: :warn) } + let(:config_path) do + File.join(FIXTURES_PATH, "root_dir", "etc", "d-installer.yaml") + end + + let(:destdir) { File.join(FIXTURES_PATH, "target_dir") } + let(:config) { DInstaller::Config.from_file(config_path) } + let(:security) { instance_double(DInstaller::Security, probe: nil, write: nil) } + let(:copy_files) { DInstaller::Storage::Finisher::CopyFilesStep.new(logger) } + let(:progress) { instance_double(DInstaller::Progress, step: nil) } + + describe "#run" do + before do + allow_any_instance_of(described_class::Step).to receive(:run?).and_return(false) + allow(described_class::Step).to receive(:run?).and_return(false) + allow(copy_files.class).to receive(:new).and_return(copy_files) + allow(copy_files).to receive(:run?).and_return(true) + allow(subject).to receive(:progress).and_return(progress) + end + + it "runs the possible steps that must be run" do + expect(subject).to receive(:start_progress).with(1) + expect(subject.progress).to receive(:step) do |label, &block| + expect(label).to eql(copy_files.label) + expect(copy_files).to receive(:run) + block.call + end + + subject.run + end + end + + describe described_class::Step do + subject { described_class.new(logger) } + + describe "#run?" do + it "returns whether the step must be executed or not (default: true)" do + expect(subject.run?).to eql(true) + end + end + end + + describe described_class::CopyFilesStep do + subject { copy_files } + before do + allow(Yast::Installation).to receive(:destdir).and_return(destdir) + allow(subject).to receive(:root_dir).and_return(File.join(FIXTURES_PATH, "root_dir")) + end + + around do |block| + FileUtils.mkdir_p(destdir) + block.call + FileUtils.rm_rf(destdir) + end + + describe "#run" do + let(:rules) do + [ + "41-cio-ignore.rules", "41-dasd-eckd-0.0.0160.rules", + "41-qeth-0.0.0800.rules", "70-persistent-net.rules" + ] + end + + it "copies some specific udev rules to the target system when exist" do + subject.run + rules.each do |rule| + expect(File.exist?(File.join(destdir, "/etc/udev/rules.d/#{rule}"))).to eql(true) + end + end + end + end + + describe described_class::BootloaderStep do + subject { described_class.new(logger) } + let(:on_s390) { false } + + before do + allow(Yast::Arch).to receive(:s390).and_return(on_s390) + allow_any_instance_of(::Bootloader::FinishClient).to receive(:write) + end + + context "when running on s390x" do + let(:on_s390) { true } + + describe "#run" do + it "runs the cio_ignore_finish client" do + expect(subject).to receive(:wfm_write).with("cio_ignore_finish") + subject.run + end + end + end + + context "when not running on s390x" do + describe "#run" do + it "does not run the cio_ignore_finish client" do + expect(subject).to_not receive(:wfm_write).with("cio_ignore_finish") + subject.run + end + end + end + + it "runs the Bootloader Finish Client" do + expect_any_instance_of(::Bootloader::FinishClient).to receive(:write) + subject.run + end + end +end diff --git a/service/test/dinstaller/storage/manager_test.rb b/service/test/dinstaller/storage/manager_test.rb index f752ebea79..7c2e4efaf7 100644 --- a/service/test/dinstaller/storage/manager_test.rb +++ b/service/test/dinstaller/storage/manager_test.rb @@ -131,13 +131,18 @@ before do mock_storage(devicegraph: devicegraph) allow(File).to receive(:directory?).with("/iguana").and_return iguana + allow(copy_files_class).to receive(:new).and_return(copy_files) end + let(:copy_files_class) { DInstaller::Storage::Finisher::CopyFilesStep } + let(:copy_files) { instance_double(copy_files_class, run?: true, run: true, label: "Copy") } let(:iguana) { false } let(:devicegraph) { "staging-plain-partitions.yaml" } - it "installs the bootloader, sets up the snapshots, copy logs, and umounts the file systems" do + it "copy needed files, installs the bootloader, sets up the snapshots, " \ + "copy logs, and umounts the file systems" do expect(security).to receive(:write) + expect(copy_files).to receive(:run) expect(bootloader_finish).to receive(:write) expect(Yast::WFM).to receive(:CallFunction).with("snapshots_finish", ["Write"]) expect(Yast::WFM).to receive(:CallFunction).with("copy_logs_finish", ["Write"]) diff --git a/service/test/fixtures/root_dir/etc/udev/rules.d/41-cio-ignore.rules b/service/test/fixtures/root_dir/etc/udev/rules.d/41-cio-ignore.rules new file mode 100644 index 0000000000..6efab2339c --- /dev/null +++ b/service/test/fixtures/root_dir/etc/udev/rules.d/41-cio-ignore.rules @@ -0,0 +1,2 @@ +# Generated by chzdev +ACTION=="add", SUBSYSTEM=="subsystem", KERNEL=="ccw", RUN{program}+="/bin/sh -c 'echo free 0160,0800-0802 > /proc/cio_ignore'" diff --git a/service/test/fixtures/root_dir/etc/udev/rules.d/41-dasd-eckd-0.0.0160.rules b/service/test/fixtures/root_dir/etc/udev/rules.d/41-dasd-eckd-0.0.0160.rules new file mode 100644 index 0000000000..baa30ff69e --- /dev/null +++ b/service/test/fixtures/root_dir/etc/udev/rules.d/41-dasd-eckd-0.0.0160.rules @@ -0,0 +1,10 @@ +# Generated by chzdev +ACTION=="add", SUBSYSTEM=="ccw", KERNEL=="0.0.0160", DRIVER=="dasd-eckd", GOTO="cfg_dasd_eckd_0.0.0160" +ACTION=="add", SUBSYSTEM=="drivers", KERNEL=="dasd-eckd", TEST=="[ccw/0.0.0160]", GOTO="cfg_dasd_eckd_0.0.0160" +GOTO="end_dasd_eckd_0.0.0160" + +LABEL="cfg_dasd_eckd_0.0.0160" +ATTR{[ccw/0.0.0160]use_diag}="0" +ATTR{[ccw/0.0.0160]online}="1" + +LABEL="end_dasd_eckd_0.0.0160" diff --git a/service/test/fixtures/root_dir/etc/udev/rules.d/41-qeth-0.0.0800.rules b/service/test/fixtures/root_dir/etc/udev/rules.d/41-qeth-0.0.0800.rules new file mode 100644 index 0000000000..6d7099d094 --- /dev/null +++ b/service/test/fixtures/root_dir/etc/udev/rules.d/41-qeth-0.0.0800.rules @@ -0,0 +1,22 @@ +# Generated by chzdev +ACTION=="add", SUBSYSTEM=="drivers", KERNEL=="qeth", GOTO="group_qeth_0.0.0800" +ACTION=="add", SUBSYSTEM=="ccw", KERNEL=="0.0.0800", DRIVER=="qeth", GOTO="group_qeth_0.0.0800" +ACTION=="add", SUBSYSTEM=="ccw", KERNEL=="0.0.0801", DRIVER=="qeth", GOTO="group_qeth_0.0.0800" +ACTION=="add", SUBSYSTEM=="ccw", KERNEL=="0.0.0802", DRIVER=="qeth", GOTO="group_qeth_0.0.0800" +ACTION=="add", SUBSYSTEM=="ccwgroup", KERNEL=="0.0.0800", DRIVER=="qeth", GOTO="cfg_qeth_0.0.0800" +GOTO="end_qeth_0.0.0800" + +LABEL="group_qeth_0.0.0800" +TEST=="[ccwgroup/0.0.0800]", GOTO="end_qeth_0.0.0800" +TEST!="[ccw/0.0.0800]", GOTO="end_qeth_0.0.0800" +TEST!="[ccw/0.0.0801]", GOTO="end_qeth_0.0.0800" +TEST!="[ccw/0.0.0802]", GOTO="end_qeth_0.0.0800" +ATTR{[drivers/ccwgroup:qeth]group}="0.0.0800,0.0.0801,0.0.0802" +GOTO="end_qeth_0.0.0800" + +LABEL="cfg_qeth_0.0.0800" +ATTR{[ccwgroup/0.0.0800]portno}="0" +ATTR{[ccwgroup/0.0.0800]layer2}="1" +ATTR{[ccwgroup/0.0.0800]online}="1" + +LABEL="end_qeth_0.0.0800" diff --git a/service/test/fixtures/root_dir/etc/udev/rules.d/70-persistent-net.rules b/service/test/fixtures/root_dir/etc/udev/rules.d/70-persistent-net.rules new file mode 100644 index 0000000000..389ad21173 --- /dev/null +++ b/service/test/fixtures/root_dir/etc/udev/rules.d/70-persistent-net.rules @@ -0,0 +1,8 @@ +# This file was automatically generated by the /usr/lib/udev/write_net_rules +# program, run by the persistent-net-generator.rules rules file. +# +# You can modify it, as long as you keep each rule on a single +# line, and change only the value of the NAME= key. + +# S/390 qeth device at 0.0.0800 +SUBSYSTEM=="net", ACTION=="add", DRIVERS=="qeth", ATTR{dev_id}=="0x0", KERNELS=="0.0.0800", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0