Skip to content

Commit

Permalink
MGMT-13997: Issues when using multiple bonds with CIM Assisted Instal…
Browse files Browse the repository at this point in the history
…ler (#5233)

When using NMSTATE if a user defines several NMSTATE files for a single
host, the data from only single NMSTATE file will be appied on the host.
The reason for that is that the script that places the configuration
files in the Network Manager configuration directory
(system-connections), stops searching when a configuration files from single
NMSTATE file is found.
This commit changes that.  It gathers all the confuguration files
generated from NMSTATE files for the host that runs the script.
Matching of host directory is done by MAC address matching.  This means
that if a host running the script has a nic with MAC address that
appears in mac_mapping.ini script in the host directory, this directory
is assumed to be matched.  All of the files from this directory will be
processed.
  • Loading branch information
ori-amizur committed May 30, 2023
1 parent b253ac8 commit 1d92a60
Show file tree
Hide file tree
Showing 18 changed files with 432 additions and 112 deletions.
246 changes: 134 additions & 112 deletions internal/constants/scripts.go
Expand Up @@ -17,11 +17,11 @@ package constants
// 1. Map logical interface name to MAC Address of the host. The logical interface name is a
// name provided by the user for the interface. It will be replaced by the script with the
// actual network interface name.
// 2. Identify the host directory which belongs to the current host by matching a MAC Address
// 2. Identify if a host directory belongs to the current host by matching a MAC Address
// from the mapping file with host network interfaces.
//
// Applying the network configuration of each host will be done by:
// 1. Associate the current host with its matching hostX directory. The association will be done by
// 1. Associate the current host with its matching hostX directories. The association will be done by
// matching host's mac addresses with those in mac_interface.ini.
// 2. Replace logical interface name in nmconnection files with the interface name as set on the host
// 3. Rename nmconnection files to start with the interface name (instead of the logical interface name)
Expand All @@ -30,8 +30,10 @@ package constants
// '/etc/coreos-firstboot-network' when the script is part of the minimal ISO.
const PreNetworkConfigScript = `#!/bin/bash
PATH_PREFIX=${PATH_PREFIX:=''}
# The directory that contains nmconnection files of all nodes
NMCONNECTIONS_DIR=/etc/assisted/network
NMCONNECTIONS_DIR=${PATH_PREFIX}/etc/assisted/network
MAC_NIC_MAPPING_FILE=mac_interface.ini
if [[ ! -d "$NMCONNECTIONS_DIR" ]]
Expand All @@ -43,163 +45,183 @@ fi
# A map of host mac addresses to interface names
declare -A host_macs_to_hw_iface
# The directory that contains nmconnection files for the current host
host_dir=""
# The mapping file of the current host
mapping_file=""
# A mac-to-nic map created from the mapping file associated with the host
declare -A logical_mac_nic_map
# A nic-to-mac map created from the mapping file associated with the host
declare -A logical_nic_mac_map
# Working directory used for storage of processed files before installation in the target directory
work_dir=$(mktemp -d)
# Find destination directory based on ISO mode
if [[ -f /etc/initrd-release ]]; then
ETC_NETWORK_MANAGER="/etc/coreos-firstboot-network"
if [[ -f ${PATH_PREFIX}/etc/initrd-release ]]; then
ETC_NETWORK_MANAGER="${PATH_PREFIX}/etc/coreos-firstboot-network"
else
ETC_NETWORK_MANAGER="/etc/NetworkManager/system-connections"
ETC_NETWORK_MANAGER="${PATH_PREFIX}/etc/NetworkManager/system-connections"
fi
echo "Info: ETC_NETWORK_MANAGER was set to $ETC_NETWORK_MANAGER"
# Create a map of host mac addresses to their network interfaces
function map_host_macs_to_interfaces() {
SYS_CLASS_NET_DIR='/sys/class/net'
SYS_CLASS_NET_DIR="${PATH_PREFIX}/sys/class/net"
for nic in $( ls $SYS_CLASS_NET_DIR )
do
mac=$(cat $SYS_CLASS_NET_DIR/$nic/address | tr '[:lower:]' '[:upper:]')
host_macs_to_hw_iface[$mac]=$nic
[[ -n "$mac" ]] && host_macs_to_hw_iface[$mac]=$nic
done
}
function find_host_directory_by_mac_address() {
for d in $(ls -d ${NMCONNECTIONS_DIR}/host*)
do
# Copy all nmstate files representing a host nic. All these files have the host nic as filename prefix.
function copy_physical_nics_files() {
host_src_dir=$1
mapping_file=$2
host_work_dir=$3
for mac_address in $(cat $mapping_file | cut -d= -f1 | tr '[:lower:]' '[:upper:]'); do
host_nic="${host_macs_to_hw_iface[${mac_address}]:-}"
if [[ -n "$host_nic" ]]; then
logical_nic="${logical_mac_nic_map[${mac_address}]:-}"
if [[ -n "$(ls -A $host_src_dir/*.nmconnection )" ]] ; then
for nmfile in $host_src_dir/*.nmconnection ; do
filename=$(basename $nmfile)
prefix="${filename%%.*}"
extension="${filename#*.}"
if [[ -n "$prefix" ]] && [[ "$prefix" == "$logical_nic" ]] ; then
target_file=$host_work_dir/${host_nic}.${extension}
mv -f $nmfile $target_file
echo "Info: Copied $nmfile to $target_file"
fi
done
fi
fi
done
}
# Copy all files that were not copied already by the function copy_physical_nics_files. These files represent nics
# that do not have prefix of physical nic (example: eth0). In this case the file is either copied as is
# having the same name as the source file, or if such a name already exists an error is generated
function copy_other_files() {
host_src_dir=$1
host_work_dir=$2
if [[ -n "$(ls -A ${host_src_dir}/*.nmconnection)" ]] ; then
for nmfile in ${host_src_dir}/*.nmconnection ; do
filename=$(basename $nmfile)
target_file=$host_work_dir/$filename
# Copy the file only if the file name was not used already
if ! [[ -f $work_dir/$filename ]] ; then
mv -f $nmfile $target_file
echo "Info: Copied $nmfile to $target_file"
else
echo "Error: connection file name ${filename} is already in use. Please rename"
fi
done
fi
}
# Update references to host nics in the nmstate files
function update_physical_nics_references() {
mapping_file=$1
host_work_dir=$2
if [ "$(ls -A $host_work_dir )" ] ; then
for mac_address in $(cat $mapping_file | cut -d= -f1 | tr '[:lower:]' '[:upper:]'); do
host_nic="${host_macs_to_hw_iface[${mac_address}]:-}"
if [[ -n "$host_nic" ]]; then
logical_nic="${logical_mac_nic_map[${mac_address}]:-}"
for nmfile in $host_work_dir/*.nmconnection ; do
sed -i -e "s/=$logical_nic$/=$host_nic/g" -e "s/=$logical_nic\./=$host_nic\./g" $nmfile
echo "Info: Using logical interface name '$logical_nic' for interface with Mac address '$mac_address', updated $nmfile"
done
fi
done
fi
}
mapping_file="${d}/${MAC_NIC_MAPPING_FILE}"
# Iterate over the host directories and process any of them that having matching interfaces with any of the local nics.
# A host directory matches a host if the MAC address of one of the nics of the local host appears in the file
# mac_mapping.ini in the directory.
function process_host_directories_by_mac_address() {
pattern="$(echo -n ${!host_macs_to_hw_iface[@]} | sed 's/ */|/g')"
if [[ -z "$pattern" ]] ; then
return
fi
src_dir=$(mktemp -d)
cp -r ${NMCONNECTIONS_DIR}/host* $src_dir || /bin/true
for host_src_dir in $(ls -1 -d $src_dir/host* || echo)
do
mapping_file="${host_src_dir}/${MAC_NIC_MAPPING_FILE}"
if [[ ! -f "$mapping_file" ]]
then
echo "Warning: Mapping file $mapping_file is missing. Skipping on directory $d"
echo "Warning: Mapping file $mapping_file is missing. Skipping on directory $host_src_dir"
continue
fi
if [[ -z "$(ls -A $d/*.nmconnection)" ]]
if [[ -z "$(ls -A $host_src_dir/*.nmconnection)" ]]
then
echo "Warning: Host directory does not contain any nmconnection files, skipping"
continue
fi
# check if mapping file contains mac-address that exists on the current host
for mac_address in $(cat $mapping_file | cut -d= -f1 | tr '[:lower:]' '[:upper:]')
do
if [[ ! -z "${host_macs_to_hw_iface[${mac_address}]:-}" ]]
then
host_dir=$(mktemp -d)
cp ${d}/* $host_dir
echo "Info: Found host directory: $d, configuration copied to $host_dir"
return
fi
echo "Info: No match found in host macs for '$mac_address'"
done
done
if grep -q -i -E "$pattern" $mapping_file; then
echo "Info: Found host directory: $(basename $host_src_dir) , copying configuration"
host_work_dir=$(mktemp -d)
if [ -z "$host_dir" ]
then
echo "Error: None of the host directories contained a mac-address to host mapping for the current host"
// We should not exit the script here in any fashion as the Dracut initqueue handler will not run any subsequent scripts if there is an exit.
fi
}
# Copy all physical nics files that match logical nics in the mapping file
copy_physical_nics_files $host_src_dir $mapping_file $host_work_dir
function set_logical_nic_mac_mapping() {
echo "Info: Checking '$mapping_file' for logical nic to mac mappings"
# initialize logical_nic_mac_map with mapping file entries
readarray -t lines < "${mapping_file}"
for line in "${lines[@]}"
do
mac=${line%%=*}
nic=${line#*=}
logical_nic_mac_map[$nic]=${mac^^}
# Copy the rest of the files from the host directory
copy_other_files $host_src_dir $host_work_dir
# Update references in the nmstate files to host nics
update_physical_nics_references $mapping_file $host_work_dir
cp $host_work_dir/* $work_dir || /bin/true
rm -rf $host_work_dir
fi
done
rm -rf $src_dir
}
# Replace logical interface name in nmconnection files with the interface name from the mapping file
# of host's directory. Replacement is done based on mac-address matching
function update_interface_names_by_mapping_file() {
echo "Info: Updaing logical interface names in nmconnection files with interface name from mapping files"
# iterate over host's nmconnection files and replace logical interface name with host's nic name
for nmconn_file in $(ls -1 ${host_dir}/*.nmconnection)
do
echo "Info: Updaing logical interface name in nmconnection files with interface name from mapping file $host_dir/$nmconn_file"
# iterate over mapping to find nmconnection files with logical interface name
for nic in "${!logical_nic_mac_map[@]}"
function map_mac_logical_nic_mapping() {
echo "Info: Checking all '${MAC_NIC_MAPPING_FILE}' for mac to logical nic mappings"
# initialize logical_mac_nic_map with mapping file entries
for f in $(echo ${NMCONNECTIONS_DIR}/host*/${MAC_NIC_MAPPING_FILE} || echo) ; do
readarray -t lines < "${f}"
for line in "${lines[@]}"
do
mac=${logical_nic_mac_map[$nic]}
# the pattern should match '=eth0' (interface name) or '=eth0.' (for vlan devices)
if grep -q -e "=$nic$" -e "=$nic\." "$nmconn_file"
then
# get host interface name
host_iface=${host_macs_to_hw_iface[$mac]}
if [ -z "$host_iface" ]
then
echo "Warning: Mapping file contains MAC Address '$mac' (for logical interface name '$nic') that doesn't exist on the host"
continue
fi
# replace logical interface name with host interface name
sed -i -e "s/=$nic$/=$host_iface/g" -e "s/=$nic\./=$host_iface\./g" $nmconn_file
echo "Info: Using logical interface name '$nic' for interface with Mac address '$mac', updated $nmconn_file)"
fi
mac=${line%%=*}
nic=${line#*=}
logical_mac_nic_map[${mac^^}]=$nic
done
done
}
function copy_nmconnection_files_to_nm_config_dir() {
echo "Info: Copying nmconnection files to $ETC_NETWORK_MANAGER"
for nmconn_file in $(ls -1 ${host_dir}/*.nmconnection)
do
# rename nmconnection files based on the actual interface name
filename=$(basename $nmconn_file)
prefix="${filename%%.*}"
extension="${filename#*.}"
if [ ! -z "${logical_nic_mac_map[$prefix]}" ]
then
dir_path=$(dirname $nmconn_file)
mac_address=${logical_nic_mac_map[$prefix]}
host_iface=${host_macs_to_hw_iface[$mac_address]}
if [ ! -z "$host_iface" ]
then
mv $nmconn_file "${dir_path}/${host_iface}.${extension}"
echo "Info: Copied $nmconn_file to $dir_path/$host_iface/$extension"
continue
fi
echo "Warning: Mapping for '$mac_address' was not present while attempting to copy $nmconn_file to $dir_path/$host_iface/$extension"
fi
done
mkdir -p "${ETC_NETWORK_MANAGER}"
cp ${host_dir}/*.nmconnection ${ETC_NETWORK_MANAGER}/
echo "Info: Copied all nmconnection files from $host_dir to $ETC_NETWORK_MANAGER"
}
echo "PreNetworkConfig Start"
# Get the mac to host nic mapping from local machine
map_host_macs_to_interfaces
find_host_directory_by_mac_address
// Make sure we do not run any of the following functions if there was no matching config.
if [ "$host_dir" ]
then
# Get mac to local nic mapping from all mac_mapping.ini files in host directories
map_mac_logical_nic_mapping
# Process all relevant files and put them in work_dir
process_host_directories_by_mac_address
# Make sure we do not run any of the following functions if there was no matching config.
if [ "$(ls -A $work_dir )" ]; then
# Remove default connection file create by NM(nm-initrd-generator). This is a WA until
# NM is back to supporting priority between nmconnections.
#
# Note that when this is intended mostly for the full ISO scenario. In the minimal ISO scenario
# this script runs before the 'coreos-coreos-copy-firstboot-network' service, and that script
# already removes the connection files, but it doesn't hurt to do it again.
mkdir -p ${ETC_NETWORK_MANAGER}
echo "Info: Removing default connection files in '$ETC_NETWORK_MANAGER'"
rm -f ${ETC_NETWORK_MANAGER}/*
echo "Removing default connection files in '$ETC_NETWORK_MANAGER'"
set_logical_nic_mac_mapping
update_interface_names_by_mapping_file
copy_nmconnection_files_to_nm_config_dir
echo "Info: Copying files from working directory to '$ETC_NETWORK_MANAGER'"
cp -v $work_dir/* ${ETC_NETWORK_MANAGER}
else
echo "Info: No matching host directories found"
fi
rm -rf $work_dir
echo "PreNetworkConfig End"
`

Expand Down

0 comments on commit 1d92a60

Please sign in to comment.