diff --git a/plugins/guests/suse/cap/configure_networks.rb b/plugins/guests/suse/cap/configure_networks.rb index 54fbdda87..cd1b5d697 100644 --- a/plugins/guests/suse/cap/configure_networks.rb +++ b/plugins/guests/suse/cap/configure_networks.rb @@ -1,7 +1,5 @@ -require "set" require "tempfile" -require_relative "../../../../lib/vagrant/util/retryable" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins @@ -12,48 +10,43 @@ module VagrantPlugins include Vagrant::Util def self.configure_networks(machine, networks) - network_scripts_dir = machine.guest.capability("network_scripts_dir") + comm = machine.communicate - # Accumulate the configurations to add to the interfaces file as - # well as what interfaces we're actually configuring since we use that - # later. - interfaces = Set.new - networks.each do |network| - interfaces.add(network[:interface]) + network_scripts_dir = machine.guest.capability(:network_scripts_dir) - # Remove any previous vagrant configuration in this network interface's - # configuration files. - machine.communicate.sudo("touch #{network_scripts_dir}/ifcfg-eth#{network[:interface]}") - machine.communicate.sudo("sed -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' #{network_scripts_dir}/ifcfg-eth#{network[:interface]} > /tmp/vagrant-ifcfg-eth#{network[:interface]}") - machine.communicate.sudo("cat /tmp/vagrant-ifcfg-eth#{network[:interface]} > #{network_scripts_dir}/ifcfg-eth#{network[:interface]}") - machine.communicate.sudo("rm -f /tmp/vagrant-ifcfg-eth#{network[:interface]}") + commands = [] + interfaces = [] + + comm.sudo("ip -o -0 addr | grep -v LOOPBACK | awk '{print $2}' | sed 's/://'") do |_, stdout| + interfaces = stdout.split("\n") + end + + networks.each.with_index do |network, i| + network[:device] = interfaces[network[:interface]] - # Render and upload the network entry file to a deterministic - # temporary location. entry = TemplateRenderer.render("guests/suse/network_#{network[:type]}", - options: network) + options: network, + ) + + remote_path = "/tmp/vagrant-network-#{network[:device]}-#{Time.now.to_i}-#{i}" Tempfile.open("vagrant-suse-configure-networks") do |f| f.binmode f.write(entry) f.fsync f.close - machine.communicate.upload(f.path, "/tmp/vagrant-network-entry_#{network[:interface]}") - end - end - - # Bring down all the interfaces we're reconfiguring. By bringing down - # each specifically, we avoid reconfiguring eth0 (the NAT interface) so - # SSH never dies. - interfaces.each do |interface| - retryable(on: Vagrant::Errors::VagrantError, tries: 3, sleep: 2) do - machine.communicate.sudo("/sbin/ifdown eth#{interface} 2> /dev/null", error_check: false) - machine.communicate.sudo("cat /tmp/vagrant-network-entry_#{interface} >> #{network_scripts_dir}/ifcfg-eth#{interface}") - machine.communicate.sudo("/sbin/ifup eth#{interface} 2> /dev/null") + comm.upload(f.path, remote_path) end - machine.communicate.sudo("rm -f /tmp/vagrant-network-entry_#{interface}") + local_path = "#{network_scripts_dir}/ifcfg-#{network[:device]}" + commands << <<-EOH.gsub(/^ {14}/, '') + /sbin/ifdown '#{network[:device]}' || true + mv '#{remote_path}' '#{local_path}' + /sbin/ifup '#{network[:device]}' + EOH end + + comm.sudo(commands.join("\n")) end end end diff --git a/templates/guests/suse/network_dhcp.erb b/templates/guests/suse/network_dhcp.erb index 3504b265e..0ec6dcac4 100644 --- a/templates/guests/suse/network_dhcp.erb +++ b/templates/guests/suse/network_dhcp.erb @@ -2,5 +2,5 @@ # The contents below are automatically generated by Vagrant. Do not modify. BOOTPROTO='dhcp' STARTMODE='auto' -DEVICE='eth<%= options[:interface] %>' +DEVICE='<%= options[:device] %>' #VAGRANT-END diff --git a/templates/guests/suse/network_static.erb b/templates/guests/suse/network_static.erb index c9270d7cd..35bf34d64 100644 --- a/templates/guests/suse/network_static.erb +++ b/templates/guests/suse/network_static.erb @@ -3,10 +3,10 @@ BOOTPROTO='static' IPADDR='<%= options[:ip] %>' NETMASK='<%= options[:netmask] %>' -DEVICE='eth<%= options[:interface] %>' -<% if options[:gateway] %> +DEVICE='<%= options[:device] %>' +<% if options[:gateway] -%> GATEWAY='<%= options[:gateway] %>' -<% end %> +<% end -%> PEERDNS='no' STARTMODE='auto' USERCONTROL='no' diff --git a/test/unit/plugins/guests/suse/cap/configure_networks_test.rb b/test/unit/plugins/guests/suse/cap/configure_networks_test.rb new file mode 100644 index 000000000..b31f4e3e7 --- /dev/null +++ b/test/unit/plugins/guests/suse/cap/configure_networks_test.rb @@ -0,0 +1,60 @@ +require_relative "../../../../base" + +describe "VagrantPlugins::GuestSUSE::Cap::ConfigureNetworks" do + let(:caps) do + VagrantPlugins::GuestSUSE::Plugin + .components + .guest_capabilities[:suse] + end + + let(:machine) { double("machine") } + let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + + before do + allow(machine).to receive(:communicate).and_return(comm) + comm.stub_command("ip -o -0 addr | grep -v LOOPBACK | awk '{print $2}' | sed 's/://'", + stdout: "eth1\neth2") + end + + after do + comm.verify_expectations! + end + + describe ".configure_networks" do + let(:cap) { caps.get(:configure_networks) } + + let(:network_1) do + { + interface: 0, + type: "dhcp", + } + end + + let(:network_2) do + { + interface: 1, + type: "static", + ip: "33.33.33.10", + netmask: "255.255.0.0", + gateway: "33.33.0.1", + } + end + + let(:guest) { double("guest") } + + before do + allow(machine).to receive(:guest).and_return(guest) + allow(guest).to receive(:capability) + .with(:network_scripts_dir) + .and_return("/scripts") + end + + it "creates and starts the networks" do + cap.configure_networks(machine, [network_1, network_2]) + expect(comm.received_commands[1]).to match(/\/sbin\/ifdown 'eth1'/) + expect(comm.received_commands[1]).to match(/\/sbin\/ifup 'eth1'/) + expect(comm.received_commands[1]).to match(/\/sbin\/ifdown 'eth2'/) + expect(comm.received_commands[1]).to match(/\/sbin\/ifup 'eth2'/) + end + end +end