Skip to content

Commit b37a045

Browse files
authored
Merge pull request #13640 from chrisroberts/arch-networking
Fix arch guest networking setup
2 parents e62d18b + f4d9f75 commit b37a045

File tree

9 files changed

+296
-204
lines changed

9 files changed

+296
-204
lines changed

Diff for: lib/vagrant/util.rb

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ module Util
2121
autoload :FileMutex, 'vagrant/util/file_mutex'
2222
autoload :GuestHosts, 'vagrant/util/guest_hosts'
2323
autoload :GuestInspection, 'vagrant/util/guest_inspection'
24+
autoload :GuestNetworks, 'vagrant/util/guest_networks'
2425
autoload :HashWithIndifferentAccess, 'vagrant/util/hash_with_indifferent_access'
2526
autoload :HCLogFormatter, 'vagrant/util/logging_formatter'
2627
autoload :HCLogOutputter, 'vagrant/util/logging_formatter'

Diff for: lib/vagrant/util/guest_inspection.rb

+8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ def systemd_networkd?(comm)
2626
comm.test("systemctl -q is-active systemd-networkd.service", sudo: true)
2727
end
2828

29+
# NetworkManager.service is in use
30+
#
31+
# @param [Vagrant::Plugin::V2::Communicator] comm Guest communicator
32+
# @return [Boolean]
33+
def systemd_network_manager?(comm)
34+
comm.test("systemctl -q is-active NetworkManager.service", sudo: true)
35+
end
36+
2937
# Check if a unit file with the given name is defined. Name can
3038
# be a pattern or explicit name.
3139
#

Diff for: lib/vagrant/util/guest_networks.rb

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Copyright (c) HashiCorp, Inc.
2+
# SPDX-License-Identifier: BUSL-1.1
3+
4+
module Vagrant
5+
module Util
6+
# Helper methods for configuring guest networks
7+
module GuestNetworks
8+
module Linux
9+
NETWORK_MANAGER_DEVICE_DIRECTORY = "/etc/NetworkManager/system-connections".freeze
10+
11+
def configure_network_manager(machine, networks, **opts)
12+
comm = machine.communicate
13+
nm_directory = opts.fetch(:nm_directory, NETWORK_MANAGER_DEVICE_DIRECTORY)
14+
15+
interfaces = machine.guest.capability(:network_interfaces)
16+
net_configs = machine.config.vm.networks.find_all { |type, _| type.to_s.end_with?("_network") }.map(&:last)
17+
18+
# Get IDs of currently configured devices
19+
current_devs = Hash.new.tap do |cd|
20+
comm.execute("nmcli -t c show") do |type, data|
21+
if type == :stdout
22+
_, id, _, dev = data.strip.split(":")
23+
cd[dev] = id
24+
end
25+
end
26+
end
27+
28+
networks.each.with_index do |network, i|
29+
net_opts = (net_configs[i] || {}).merge(network)
30+
net_opts[:type] = net_opts[:type].to_s
31+
net_opts[:device] = interfaces[network[:interface]]
32+
33+
if !net_opts[:mac_address]
34+
comm.execute("cat /sys/class/net/#{net_opts[:device]}/address") do |type, data|
35+
net_opts[:mac_address] = data if type == :stdout
36+
end
37+
end
38+
39+
tmpl_opts = {
40+
interface_name: net_opts[:device],
41+
type: net_opts[:type],
42+
mac_address: net_opts[:mac_address],
43+
uuid: SecureRandom.uuid
44+
}
45+
46+
if net_opts[:type] != "dhcp"
47+
begin
48+
addr = IPAddr.new("#{net_opts[:ip]}")
49+
if addr.ipv4?
50+
tmpl_opts[:ipv4] = addr.to_string
51+
masked = addr.mask(net_opts[:netmask])
52+
53+
tmpl_opts[:ipv4_mask] = masked.prefix
54+
tmpl_opts[:ipv4_gateway] = masked.succ.to_string
55+
else
56+
tmpl_opts[:ipv6] = addr.to_string
57+
masked = addr.mask(net_opts[:netmask])
58+
59+
tmpl_opts[:ipv6_mask] = masked.prefix
60+
tmpl_opts[:ipv6_gateway] = masked.succ.to_string
61+
end
62+
rescue IPAddr::Error => err
63+
raise NetworkAddressInvalid,
64+
address: net_opts[:ip],
65+
mask: net_opts[:netmask],
66+
error: err.to_s
67+
end
68+
end
69+
70+
entry = TemplateRenderer.render("networking/network_manager/network_manager_device", options: tmpl_opts)
71+
remote_path = "/tmp/vagrant-network-entry-#{net_opts[:device]}-#{Time.now.to_i}-#{i}"
72+
final_path = "#{nm_directory}/#{net_opts[:device]}.nmconnection"
73+
74+
Tempfile.open("vagrant-nm-configure-networks") do |f|
75+
f.binmode
76+
f.write(entry)
77+
f.fsync
78+
f.close
79+
comm.upload(f.path, remote_path)
80+
end
81+
82+
# Remove the device if it already exists
83+
if device_id = current_devs[net_opts[:device]]
84+
[
85+
"nmcli d disconnect '#{net_opts[:device]}'",
86+
"nmcli c delete '#{device_id}'",
87+
].each do |cmd|
88+
comm.sudo(cmd, error_check: false)
89+
end
90+
end
91+
92+
# Apply the config
93+
[
94+
"chown root:root '#{remote_path}'",
95+
"chmod 0600 '#{remote_path}'",
96+
"mv '#{remote_path}' '#{final_path}'",
97+
"nmcli c load '#{final_path}'",
98+
"nmcli d connect '#{net_opts[:device]}'"
99+
].each do |cmd|
100+
comm.sudo(cmd)
101+
end
102+
end
103+
end
104+
end
105+
end
106+
end
107+
end

Diff for: plugins/guests/arch/cap/configure_networks.rb

+4
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@ module Cap
1313
class ConfigureNetworks
1414
include Vagrant::Util
1515
extend Vagrant::Util::GuestInspection::Linux
16+
extend Vagrant::Util::GuestNetworks::Linux
1617

1718
def self.configure_networks(machine, networks)
1819
comm = machine.communicate
20+
21+
return configure_network_manager(machine, networks) if systemd_network_manager?(comm)
22+
1923
commands = []
2024
uses_systemd_networkd = systemd_networkd?(comm)
2125

Diff for: plugins/guests/redhat/cap/configure_networks.rb

+6-92
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module Cap
1212
class ConfigureNetworks
1313
include Vagrant::Util
1414
extend Vagrant::Util::GuestInspection::Linux
15+
extend Vagrant::Util::GuestNetworks::Linux
1516

1617
def self.configure_networks(machine, networks)
1718
@logger = Log4r::Logger.new("vagrant::guest::redhat::configurenetworks")
@@ -23,98 +24,11 @@ def self.configure_networks(machine, networks)
2324
# The legacy configuration will handle rhel/centos pre-10
2425
# versions. The newer versions have a different path for
2526
# network configuration files.
26-
27-
return configure_networks_legacy(machine, networks) if network_scripts_dir.end_with?("network-scripts")
28-
29-
comm = machine.communicate
30-
31-
interfaces = machine.guest.capability(:network_interfaces)
32-
net_configs = machine.config.vm.networks.find_all { |type, _| type.to_s.end_with?("_network") }.map(&:last)
33-
34-
# Get IDs of currently configured devices
35-
current_devs = Hash.new.tap do |cd|
36-
comm.execute("nmcli -t c show") do |type, data|
37-
if type == :stdout
38-
_, id, _, dev = data.strip.split(":")
39-
cd[dev] = id
40-
end
41-
end
42-
end
43-
44-
networks.each.with_index do |network, i|
45-
net_opts = (net_configs[i] || {}).merge(network)
46-
net_opts[:type] = net_opts[:type].to_s
47-
net_opts[:device] = interfaces[network[:interface]]
48-
49-
if !net_opts[:mac_address]
50-
comm.execute("cat /sys/class/net/#{net_opts[:device]}/address") do |type, data|
51-
net_opts[:mac_address] = data if type == :stdout
52-
end
53-
end
54-
55-
tmpl_opts = {
56-
interface_name: net_opts[:device],
57-
type: net_opts[:type],
58-
mac_address: net_opts[:mac_address],
59-
uuid: SecureRandom.uuid
60-
}
61-
62-
if net_opts[:type] != "dhcp"
63-
begin
64-
addr = IPAddr.new("#{net_opts[:ip]}")
65-
if addr.ipv4?
66-
tmpl_opts[:ipv4] = addr.to_string
67-
masked = addr.mask(net_opts[:netmask])
68-
69-
tmpl_opts[:ipv4_mask] = masked.prefix
70-
tmpl_opts[:ipv4_gateway] = masked.succ.to_string
71-
else
72-
tmpl_opts[:ipv6] = addr.to_string
73-
masked = addr.mask(net_opts[:netmask])
74-
75-
tmpl_opts[:ipv6_mask] = masked.prefix
76-
tmpl_opts[:ipv6_gateway] = masked.succ.to_string
77-
end
78-
rescue IPAddr::Error => err
79-
raise NetworkAddressInvalid,
80-
address: net_opts[:ip],
81-
mask: net_opts[:netmask],
82-
error: err.to_s
83-
end
84-
end
85-
86-
entry = TemplateRenderer.render("guests/redhat/network_manager_device", options: tmpl_opts)
87-
remote_path = "/tmp/vagrant-network-entry-#{net_opts[:device]}-#{Time.now.to_i}-#{i}"
88-
final_path = "#{network_scripts_dir}/#{net_opts[:device]}.nmconnection"
89-
90-
Tempfile.open("vagrant-redhat-configure-networks") do |f|
91-
f.binmode
92-
f.write(entry)
93-
f.fsync
94-
f.close
95-
comm.upload(f.path, remote_path)
96-
end
97-
98-
# Remove the device if it already exists
99-
if device_id = current_devs[net_opts[:device]]
100-
[
101-
"nmcli d disconnect '#{net_opts[:device]}'",
102-
"nmcli c delete '#{device_id}'",
103-
].each do |cmd|
104-
comm.sudo(cmd, error_check: false)
105-
end
106-
end
107-
108-
# Apply the config
109-
[
110-
"chown root:root '#{remote_path}'",
111-
"chmod 0600 '#{remote_path}'",
112-
"mv '#{remote_path}' '#{final_path}'",
113-
"nmcli c load '#{final_path}'",
114-
"nmcli d connect '#{net_opts[:device]}'"
115-
].each do |cmd|
116-
comm.sudo(cmd)
117-
end
27+
if network_scripts_dir.end_with?("network-scripts")
28+
configure_networks_legacy(machine, networks)
29+
else
30+
# Recent versions use Network Manager
31+
configure_network_manager(machine, networks)
11832
end
11933
end
12034

Diff for: test/unit/plugins/guests/arch/cap/configure_networks_test.rb

+13
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
allow(guest).to receive(:capability).with(:network_interfaces)
3030
.and_return(["eth1", "eth2"])
3131
allow(cap).to receive(:systemd_networkd?).and_return(true)
32+
allow(cap).to receive(:systemd_network_manager?).and_return(false)
3233
end
3334

3435
let(:network_1) do
@@ -81,5 +82,17 @@
8182
expect(comm.received_commands[0]).to match(/netctl enable 'eth2'/)
8283
end
8384
end
85+
86+
context "network is controlled by NetworkManager via systemd" do
87+
before do
88+
expect(cap).to receive(:systemd_network_manager?).and_return(true)
89+
end
90+
91+
it "should configure for network manager" do
92+
expect(cap).to receive(:configure_network_manager).with(machine, [network_1])
93+
94+
cap.configure_networks(machine, [network_1])
95+
end
96+
end
8497
end
8598
end

0 commit comments

Comments
 (0)