Skip to content

Commit

Permalink
Add support for configuring amazon linux 2023 network via systemd
Browse files Browse the repository at this point in the history
  • Loading branch information
peckpeck committed Feb 28, 2024
1 parent d8fdc50 commit 3b991e6
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 0 deletions.
165 changes: 165 additions & 0 deletions plugins/guests/amazon/cap/configure_networks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1

require "tempfile"

require_relative "../../../../lib/vagrant/util/template_renderer"

module VagrantPlugins
module GuestAmazon
module Cap
class ConfigureNetworks
include Vagrant::Util
extend Vagrant::Util::GuestInspection::Linux

NETWORKD_DIRECTORY = "/etc/systemd/network".freeze

def self.configure_networks(machine, networks)
comm = machine.communicate
interfaces = machine.guest.capability(:network_interfaces)

if systemd?(comm)
if systemd_networkd?(comm)
configure_networkd(machine, interfaces, comm, networks)
else
configure_network_scripts(machine, interfaces, comm, networks)
end
else
configure_network_scripts(machine, interfaces, comm, networks)
end
end


# Simple helper to upload content to guest temporary file
#
# @param [Vagrant::Plugin::Communicator] comm
# @param [String] content
# @return [String] remote path
def self.upload_tmp_file(comm, content, remote_path=nil)
if remote_path.nil?
remote_path = "/tmp/vagrant-network-entry-#{Time.now.to_i}"
end
Tempfile.open("vagrant-debian-configure-networks") do |f|
f.binmode
f.write(content)
f.fsync
f.close
comm.upload(f.path, remote_path)
end
remote_path
end

# Configure guest networking using networkd
def self.configure_networkd(machine, interfaces, comm, networks)
root_device = interfaces.first
networks.each do |network|
dev_name = interfaces[network[:interface]]
net_conf = []
net_conf << "[Match]"
net_conf << "Name=#{dev_name}"
net_conf << "[Network]"
if network[:type].to_s == "dhcp"
net_conf << "DHCP=yes"
else
mask = network[:netmask]
if mask && IPAddr.new(network[:ip]).ipv4?
begin
mask = IPAddr.new(mask).to_i.to_s(2).count("1")
rescue IPAddr::Error
# ignore and use given value
end
end
address = [network[:ip], mask].compact.join("/")
net_conf << "DHCP=no"
net_conf << "Address=#{address}"
net_conf << "Gateway=#{network[:gateway]}" if network[:gateway]
end

remote_path = upload_tmp_file(comm, net_conf.join("\n"))
dest_path = "/etc/systemd/network/50-vagrant-#{dev_name}.network"
comm.sudo(["mkdir -p /etc/systemd/network",
"mv -f '#{remote_path}' '#{dest_path}'",
"chown root:root '#{dest_path}'",
"chmod 0644 '#{dest_path}'"].join("\n"))
end

comm.sudo(["systemctl restart systemd-networkd.service"].join("\n"))
end

def self.configure_network_scripts(machine, interfaces, comm, networks)
network_scripts_dir = machine.guest.capability(:network_scripts_dir)

commands = {:start => [], :middle => [], :end => []}
interfaces = machine.guest.capability(:network_interfaces)

# Check if NetworkManager is installed on the system
nmcli_installed = nmcli?(comm)
net_configs = machine.config.vm.networks.map do |type, opts|
opts if type.to_s.end_with?("_network")
end.compact
networks.each.with_index do |network, i|
network[:device] = interfaces[network[:interface]]
extra_opts = net_configs[i] ? net_configs[i].dup : {}

if nmcli_installed
# Now check if the device is actively being managed by NetworkManager
nm_controlled = nm_controlled?(comm, network[:device])
end

if !extra_opts.key?(:nm_controlled)
extra_opts[:nm_controlled] = !!nm_controlled
end

extra_opts[:nm_controlled] = case extra_opts[:nm_controlled]
when true
"yes"
when false, nil
"no"
else
extra_opts[:nm_controlled].to_s
end

if extra_opts[:nm_controlled] == "yes" && !nmcli_installed
raise Vagrant::Errors::NetworkManagerNotInstalled, device: network[:device]
end

# Render a new configuration
entry = TemplateRenderer.render("guests/redhat/network_#{network[:type]}",
options: extra_opts.merge(network),
)

# Upload the new configuration
remote_path = "/tmp/vagrant-network-entry-#{network[:device]}-#{Time.now.to_i}-#{i}"
Tempfile.open("vagrant-redhat-configure-networks") do |f|
f.binmode
f.write(entry)
f.fsync
f.close
machine.communicate.upload(f.path, remote_path)
end

# Add the new interface and bring it back up
final_path = "#{network_scripts_dir}/ifcfg-#{network[:device]}"

if nm_controlled
commands[:start] << "nmcli d disconnect iface '#{network[:device]}'"
else
commands[:start] << "/sbin/ifdown '#{network[:device]}'"
end
commands[:middle] << "mv -f '#{remote_path}' '#{final_path}'"
if extra_opts[:nm_controlled] == "no"
commands[:end] << "/sbin/ifup '#{network[:device]}'"
end
end
if nmcli_installed
commands[:middle] << "(test -f /etc/init.d/NetworkManager && /etc/init.d/NetworkManager restart) || " \
"((systemctl | grep NetworkManager.service) && systemctl restart NetworkManager)"
end
commands = commands[:start] + commands[:middle] + commands[:end]
comm.sudo(commands.join("\n"))
comm.wait_for_ready(5)
end
end
end
end
end
5 changes: 5 additions & 0 deletions plugins/guests/amazon/plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ class Plugin < Vagrant.plugin("2")
Guest
end

guest_capability(:amazon, :configure_networks) do
require_relative "cap/configure_networks"
Cap::ConfigureNetworks
end

guest_capability(:amazon, :flavor) do
require_relative "cap/flavor"
Cap::Flavor
Expand Down

0 comments on commit 3b991e6

Please sign in to comment.