From 2418918c54887e61ad9a8dcb5a06b881f47f0fee Mon Sep 17 00:00:00 2001 From: Ryan Ronnander Date: Sat, 4 Jul 2026 01:49:59 -0400 Subject: [PATCH] Fix SUSE guest networking Recent SUSE guests (SLE/openSUSE Leap 16, MicroOS) use NetworkManager and no longer include wicked or its ifcfg configuration files. Detect the correct location of the network configuration files, and configure the guest based on the detected location. Fixes #12777 --- plugins/guests/suse/cap/configure_networks.rb | 16 ++++++++ .../guests/suse/cap/network_scripts_dir.rb | 6 ++- .../suse/cap/configure_networks_test.rb | 40 +++++++++++++------ .../suse/cap/network_scripts_dir_test.rb | 25 ++++++++++-- 4 files changed, 70 insertions(+), 17 deletions(-) diff --git a/plugins/guests/suse/cap/configure_networks.rb b/plugins/guests/suse/cap/configure_networks.rb index 2b899707204..6d8daf2f02e 100644 --- a/plugins/guests/suse/cap/configure_networks.rb +++ b/plugins/guests/suse/cap/configure_networks.rb @@ -2,6 +2,7 @@ # SPDX-License-Identifier: BUSL-1.1 require "tempfile" +require "securerandom" require_relative "../../../../lib/vagrant/util/template_renderer" @@ -10,9 +11,24 @@ module GuestSUSE module Cap class ConfigureNetworks extend Vagrant::Util::Retryable + extend Vagrant::Util::GuestNetworks::Linux include Vagrant::Util def self.configure_networks(machine, networks) + network_scripts_dir = machine.guest.capability(:network_scripts_dir) + + # The legacy configuration will handle guests using wicked + # (SLES/Leap 15.x and earlier, Tumbleweed). Guests without + # wicked (SLES/Leap 16 and later, MicroOS) are configured + # via NetworkManager. + if network_scripts_dir.end_with?("sysconfig/network") + configure_networks_legacy(machine, networks) + else + configure_network_manager(machine, networks) + end + end + + def self.configure_networks_legacy(machine, networks) comm = machine.communicate network_scripts_dir = machine.guest.capability(:network_scripts_dir) diff --git a/plugins/guests/suse/cap/network_scripts_dir.rb b/plugins/guests/suse/cap/network_scripts_dir.rb index e7e4ab61252..2dd9a019c23 100644 --- a/plugins/guests/suse/cap/network_scripts_dir.rb +++ b/plugins/guests/suse/cap/network_scripts_dir.rb @@ -6,7 +6,11 @@ module GuestSUSE module Cap class NetworkScriptsDir def self.network_scripts_dir(machine) - "/etc/sysconfig/network" + if machine.communicate.test("test -d /etc/sysconfig/network") + "/etc/sysconfig/network" + else + "/etc/NetworkManager/system-connections" + end end end end diff --git a/test/unit/plugins/guests/suse/cap/configure_networks_test.rb b/test/unit/plugins/guests/suse/cap/configure_networks_test.rb index 2ad180afc8b..5f345d695a8 100644 --- a/test/unit/plugins/guests/suse/cap/configure_networks_test.rb +++ b/test/unit/plugins/guests/suse/cap/configure_networks_test.rb @@ -25,13 +25,6 @@ describe ".configure_networks" do let(:cap) { caps.get(:configure_networks) } - before do - allow(guest).to receive(:capability).with(:network_scripts_dir) - .and_return("/scripts") - allow(guest).to receive(:capability).with(:network_interfaces) - .and_return(["eth1", "eth2"]) - end - let(:network_1) do { interface: 0, @@ -49,12 +42,33 @@ } end - it "creates and starts the networks" do - cap.configure_networks(machine, [network_1, network_2]) - expect(comm.received_commands[0]).to match(/\/sbin\/ifdown 'eth1'/) - expect(comm.received_commands[0]).to match(/\/sbin\/ifup 'eth1'/) - expect(comm.received_commands[0]).to match(/\/sbin\/ifdown 'eth2'/) - expect(comm.received_commands[0]).to match(/\/sbin\/ifup 'eth2'/) + context "with wicked network configuration path" do + before do + allow(guest).to receive(:capability).with(:network_scripts_dir) + .and_return("/etc/sysconfig/network") + allow(guest).to receive(:capability).with(:network_interfaces) + .and_return(["eth1", "eth2"]) + end + + it "creates and starts the networks" do + cap.configure_networks(machine, [network_1, network_2]) + expect(comm.received_commands[0]).to match(/\/sbin\/ifdown 'eth1'/) + expect(comm.received_commands[0]).to match(/\/sbin\/ifup 'eth1'/) + expect(comm.received_commands[0]).to match(/\/sbin\/ifdown 'eth2'/) + expect(comm.received_commands[0]).to match(/\/sbin\/ifup 'eth2'/) + end + end + + context "with system-connections network configuration path" do + before do + allow(guest).to receive(:capability).with(:network_scripts_dir) + .and_return("/etc/NetworkManager/system-connections") + end + + it "should configure with network manager" do + expect(cap).to receive(:configure_network_manager).with(machine, [network_1, network_2]) + cap.configure_networks(machine, [network_1, network_2]) + end end end end diff --git a/test/unit/plugins/guests/suse/cap/network_scripts_dir_test.rb b/test/unit/plugins/guests/suse/cap/network_scripts_dir_test.rb index 2511e93125a..f2de3191ead 100644 --- a/test/unit/plugins/guests/suse/cap/network_scripts_dir_test.rb +++ b/test/unit/plugins/guests/suse/cap/network_scripts_dir_test.rb @@ -10,13 +10,32 @@ .guest_capabilities[:suse] end - let(:machine) { double("machine") } + let(:comm) { double("comm") } + let(:machine) { double("machine", communicate: comm) } describe ".network_scripts_dir" do let(:cap) { caps.get(:network_scripts_dir) } - it "runs /etc/sysconfig/network" do - expect(cap.network_scripts_dir(machine)).to eq("/etc/sysconfig/network") + context "when /etc/sysconfig/network exists" do + before do + allow(comm).to receive(:test).with("test -d /etc/sysconfig/network") + .and_return(true) + end + + it "returns /etc/sysconfig/network" do + expect(cap.network_scripts_dir(machine)).to eq("/etc/sysconfig/network") + end + end + + context "when /etc/sysconfig/network does not exist" do + before do + allow(comm).to receive(:test).with("test -d /etc/sysconfig/network") + .and_return(false) + end + + it "returns /etc/NetworkManager/system-connections" do + expect(cap.network_scripts_dir(machine)).to eq("/etc/NetworkManager/system-connections") + end end end end