From c36f80ee09fde811ae874a52d3517ce24539770a Mon Sep 17 00:00:00 2001 From: yaacov Date: Tue, 10 Sep 2024 20:30:12 +0300 Subject: [PATCH] Add netplan config parding sh utility for ubuntu static ip Signed-off-by: yaacov --- virt-v2v/BUILD.bazel | 1 + .../scripts/rhel/run/netplan_config_util.sh | 95 +++++++++++++++++++ .../scripts/rhel/run/network_config_util.sh | 43 ++++++++- .../scripts/rhel/run/network_config_util_test | 58 ++++++++++- 4 files changed, 191 insertions(+), 6 deletions(-) create mode 100644 virt-v2v/scripts/rhel/run/netplan_config_util.sh diff --git a/virt-v2v/BUILD.bazel b/virt-v2v/BUILD.bazel index 4eb4a4af5..d51ca4ae5 100644 --- a/virt-v2v/BUILD.bazel +++ b/virt-v2v/BUILD.bazel @@ -106,6 +106,7 @@ go_library( "scripts/rhel/firstboot/README.md", "scripts/rhel/run/README.md", "scripts/rhel/run/network_config_util.sh", + "scripts/rhel/run/netplan_config_util.sh", "scripts/rhel/run/network_config_util_test", ], importpath = "github.com/konveyor/forklift-controller/virt-v2v", diff --git a/virt-v2v/scripts/rhel/run/netplan_config_util.sh b/virt-v2v/scripts/rhel/run/netplan_config_util.sh new file mode 100644 index 000000000..c35ce694d --- /dev/null +++ b/virt-v2v/scripts/rhel/run/netplan_config_util.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# Function to extract the interface name and all valid IPv4 addresses from a block of text +parse_interface_block() { + block="$1" + + # Regular expression for matching IPv4 and IPv6 addresses + ip_regex="([0-9]{1,3}\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?" + + # Extract the interface name (first word before ":") and take only the first match + interface_name=$(echo "$block" | grep -Eo '^[^:]+' | head -n 1) + + # Find and capture all IP addresses matching the regex in the block + ip_addresses=$(echo "$block" | grep -Eo "$ip_regex" | tr '\n' ' ') + + # Return the interface name and concatenated IP addresses + echo "${interface_name}: ${ip_addresses}" +} + +# Function to parse the netplan file +parse_netplan_file() { + netplan_file="$1" + + # Initialize variables + found_ethernets=0 + interface_indent="" + current_block="" + interfaces_list="" + + # Read the file line by line + while IFS= read -r line; do + # Check if the current line contains "ethernets:" + if [ "$found_ethernets" -eq 0 ] && echo "$line" | grep -q '^[[:space:]]*ethernets:'; then + found_ethernets=1 + continue + fi + + # Once "ethernets:" is found, determine the indentation of the interface block + if [ "$found_ethernets" -eq 1 ] && [ -n "$line" ]; then + interface_indent=$(echo "$line" | sed -e 's/^\([[:space:]]*\).*/\1/' | wc -c) + found_ethernets=2 + fi + + # If we are inside the "ethernets" section, capture each interface block + if [ "$found_ethernets" -eq 2 ]; then + current_indent=$(echo "$line" | sed -e 's/^\([[:space:]]*\).*/\1/' | wc -c) + + # If a new interface is found (same indentation as the interface level) + if [ "$current_indent" -eq "$interface_indent" ]; then + # Parse the previous block and add it to the list + if [ -n "$current_block" ]; then + interfaces_list="$interfaces_list$(parse_interface_block "$current_block") \n" + fi + + # Start a new interface block + current_block="$line" + else + current_block="$current_block\n$line" + fi + + # Stop capturing when encountering a line with less indentation than "interface_indent" + if [ "$current_indent" -lt "$interface_indent" ] && [ -n "$line" ]; then + found_ethernets=0 + fi + fi + done < "$netplan_file" + + # Parse and add the last interface block + if [ -n "$current_block" ]; then + interfaces_list="$interfaces_list$(parse_interface_block "$current_block") \n" + fi + + # Output the parsed interfaces with their IP addresses + printf "$interfaces_list" +} + +# Function to parse all YAML files in a directory using parse_netplan_file +parse_netplan_dir() { + dir="$1" + result="" + + # Iterate over all YAML files in the directory + for yaml_file in "$dir"/*.yaml; do + if [ -f "$yaml_file" ]; then + # Concatenate the output of parse_netplan_file for each YAML file + result=$(printf "%s\n%s" "$result" "$(parse_netplan_file "$yaml_file")") + fi + done + + # Return the concatenated result + echo "$result" +} + +# Call the function to parse the netplan files +parse_netplan_dir "$NETPLAN_DIR" diff --git a/virt-v2v/scripts/rhel/run/network_config_util.sh b/virt-v2v/scripts/rhel/run/network_config_util.sh index 191c49cae..723b0e09b 100644 --- a/virt-v2v/scripts/rhel/run/network_config_util.sh +++ b/virt-v2v/scripts/rhel/run/network_config_util.sh @@ -1,10 +1,16 @@ #!/bin/bash +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + # Global variables with default values V2V_MAP_FILE="${V2V_MAP_FILE:-/tmp/macToIP}" NETWORK_SCRIPTS_DIR="${NETWORK_SCRIPTS_DIR:-/etc/sysconfig/network-scripts}" NETWORK_CONNECTIONS_DIR="${NETWORK_CONNECTIONS_DIR:-/etc/NetworkManager/system-connections}" UDEV_RULES_FILE="${UDEV_RULES_FILE:-/etc/udev/rules.d/70-persistent-net.rules}" +NETPLAN_DIR="${NETPLAN_DIR:-/etc/netplan}" + +# Source the netplan helper functions +. ${SCRIPT_DIR}/netplan_config_util.sh # Dump debug strings into a new file descriptor and redirect it to stdout. exec 3>&1 @@ -26,7 +32,7 @@ fi # Clean strigs in case they have quates remove_quotes() { - echo "$1" | tr -d '"' + echo "$1" | tr -d '"' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' } # Validate MAC address and IPv4 address and extract them @@ -107,6 +113,40 @@ udev_from_nm() { done < "$V2V_MAP_FILE" } +# Create udev rules based on the macToIP mapping + output from parse_netplan_file +udev_from_netplan() { + # Check if netplan config directory exists + if [ ! -d "$NETPLAN_DIR" ]; then + log "Warning: Directory $NETPLAN_DIR does not exist." + return 0 + fi + + # Parse the netplan configuration and store interface to IP mappings + netplan_output=$(parse_netplan_dir "$NETPLAN_DIR") + + # Read the mapping file line by line + while IFS= read -r line; do + # Extract S_HW and S_IP from the current line in the mapping file + extract_mac_ip "$line" + + # If S_HW and S_IP were not extracted, skip the line + if [ -z "$S_HW" ] || [ -z "$S_IP" ]; then + continue + fi + + # Search the parsed netplan output for a matching IP address + interface_name=$(echo "$netplan_output" | grep -w "$S_IP" | cut -d ':' -f 1) + + # If no interface is found, skip this entry + if [ -z "$interface_name" ]; then + continue + fi + + # Create the udev rule based on the extracted MAC address and interface name + echo "SUBSYSTEM==\"net\",ACTION==\"add\",ATTR{address}==\"$(remove_quotes "$S_HW")\",NAME=\"$(remove_quotes "$interface_name")\"" + done < "$V2V_MAP_FILE" +} + # Checks for duplicate hardware addresses check_dupe_hws() { input=$(cat) @@ -128,6 +168,7 @@ main() { { udev_from_ifcfg udev_from_nm + udev_from_netplan } | check_dupe_hws > "$UDEV_RULES_FILE" 2>/dev/null } diff --git a/virt-v2v/scripts/rhel/run/network_config_util_test b/virt-v2v/scripts/rhel/run/network_config_util_test index 7d8f6bfc1..8ed6c21a6 100644 --- a/virt-v2v/scripts/rhel/run/network_config_util_test +++ b/virt-v2v/scripts/rhel/run/network_config_util_test @@ -10,13 +10,14 @@ export V2V_MAP_FILE="$TEST_DIR/macToIP" export NETWORK_SCRIPTS_DIR="$TEST_DIR/network-scripts" export NETWORK_CONNECTIONS_DIR="$TEST_DIR/system-connections" export UDEV_RULES_FILE="$TEST_DIR/70-persistent-net.rules" +export NETPLAN_DIR="$TEST_DIR/netplan" # Test systems using network-scripts # ---------------------------------- # Clean up from previous runs rm -f "$UDEV_RULES_FILE" -rm -rf "$NETWORK_SCRIPTS_DIR" "$NETWORK_CONNECTIONS_DIR" +rm -rf "$NETWORK_SCRIPTS_DIR" "$NETWORK_CONNECTIONS_DIR" "$NETPLAN_DIR" mkdir -p "$NETWORK_SCRIPTS_DIR" # Create mock data @@ -52,16 +53,13 @@ fi # Clean up from previous runs rm -f "$UDEV_RULES_FILE" -rm -rf "$NETWORK_SCRIPTS_DIR" "$NETWORK_CONNECTIONS_DIR" +rm -rf "$NETWORK_SCRIPTS_DIR" "$NETWORK_CONNECTIONS_DIR" "$NETPLAN_DIR" mkdir -p "$NETWORK_CONNECTIONS_DIR" # Create mock data printf "aa:bb:cc:dd:ee:ff:ip:192.168.1.10,things,more\naa:bb:cc:dd:ee:fe:ip:192.168.1.11,hello,world\naa:bb:cc:dd:ee:fd:ip:2001:0db8:85a3:0000:0000:8a2e:0370:7334\n" > "$V2V_MAP_FILE" printf "[connection]\ninterface-name=eth3\naddress1=192.168.1.11/24\n" > "$NETWORK_CONNECTIONS_DIR/eth1 but with spaces.nmconnection" -# Source the script under test -. ${SCRIPT_DIR}/network_config_util.sh - # Run the script main @@ -83,6 +81,56 @@ if ! echo "$EXPECTED_RULE" | cmp -s - "$UDEV_RULES_FILE"; then exit 1 fi +# Test systems using netplan YAML +# ------------------------------- + +NETPLAN_FILE="$NETPLAN_DIR/50-netplan.yaml" + +# Clean up from previous runs +rm -f "$UDEV_RULES_FILE" +rm -rf "$NETWORK_SCRIPTS_DIR" "$NETWORK_CONNECTIONS_DIR" "$NETPLAN_DIR" +mkdir -p "$NETPLAN_DIR" + +# Create mock data for netplan YAML test +cat < "$NETPLAN_FILE" +network: + version: 2 + ethernets: + eth0: + dhcp4: true + eth1: + dhcp4: false + addresses: + - 192.168.1.11/24 + enp3s0: + addresses: + - 192.168.1.12/24 +EOL + +# Create mock mapping file +printf "aa:bb:cc:dd:ee:ff:ip:192.168.1.10,things,more\naa:bb:cc:dd:ee:fe:ip:192.168.1.11,hello,world\naa:bb:cc:dd:ee:fd:ip:192.168.1.12,ipv6,support\n" > "$V2V_MAP_FILE" + +# Run the script +main + +printf "\nTest netplan output:\n" +echo "FILE_START" +cat $UDEV_RULES_FILE +echo "FILE_END" + +# Test 1: Verify the udev rules file was created +if [ ! -f "$UDEV_RULES_FILE" ]; then + echo "Test 1 Failed: UDEV_RULES_FILE not created." + exit 1 +fi + +# Test 3: Verify the content of the udev rules file +EXPECTED_RULE="SUBSYSTEM==\"net\",ACTION==\"add\",ATTR{address}==\"aa:bb:cc:dd:ee:fe\",NAME=\"eth1\"\nSUBSYSTEM==\"net\",ACTION==\"add\",ATTR{address}==\"aa:bb:cc:dd:ee:fd\",NAME=\"enp3s0\"" +if ! echo "$EXPECTED_RULE" | cmp -s - "$UDEV_RULES_FILE"; then + echo "Test 2 Failed: The content of $UDEV_RULES_FILE does not match the expected rule." + exit 1 +fi + # ----------------------- printf "\nAll tests passed successfully.\n"