-
Notifications
You must be signed in to change notification settings - Fork 246
/
55-migrate-network-devices.sh
294 lines (277 loc) · 14.3 KB
/
55-migrate-network-devices.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
#
# Propose new network interface if original interface MAC is not found on the current system.
#
# Migrate network device configuration found in /etc/udev/rules.d/*persistent*{net|names}*.rules
# to match different hardware from the source system.
# We assume that udev or static module loading was used to load the
# appropriate drivers and do not do anything about driver loading.
#
# If network interface name is not managed by udev ( > rhel7 and > ubuntu 16.04 ),
# update 60-network-devices.sh and 62-routing.sh system-setup script (inet renamed, migration)
# adjusts the udev rule and triggers udev.
#
# Get the rule files (though it should be only one):
# RULE_FILES array is now global defined in default.conf and renamed as UDEV_NET_MAC_RULE_FILES
# RULE_FILES=( /etc/udev/rules.d/*persistent*{names,net,cd}.rules /etc/udev/rules.d/*eno-fix.rules )
ORIG_MACS_FILE=/etc/mac-addresses
MAC_MAPPING_FILE=/etc/rear/mappings/mac
MANUAL_MAC_MAPPING=
network_setup_scripts=( "/etc/scripts/system-setup.d/60-network-devices.sh" "/etc/scripts/system-setup.d/62-routing.sh" )
# First check the existence of the original network devices:
# The MIGRATE_MACS array collects the MAC addresses that we need to migrate:
MIGRATE_MACS=()
# The ORIGINAL_MACS array collects the original MAC addresses:
ORIGINAL_MACS=()
# The ORIGINAL_DEVICES collects the original device names:
ORIGINAL_DEVICES=()
# The ORIG_MACS_FILE contains lines of the form: network_interface mac_address
# Temporary rear_mappings_mac used when interfaces have been renamed
tmp_mac_mapping_file=$(mktemp)
# TODO: What should happen if there is no ORIG_MACS_FILE or when it is empty?
while read orig_dev orig_mac junk ; do
ORIGINAL_DEVICES+=( "$orig_dev")
ORIGINAL_MACS+=( "$orig_mac" )
# Continue with the next original MAC address if it is found on the current
# system, otherwise we consider it needs migration:
new_dev=$( get_device_by_hwaddr "$orig_mac" )
if [ -n "$new_dev" ] ; then
[ "$new_dev" = "$orig_dev" ] && continue
# The device was found but has been renamed (it was customized in
# source system).
# Create a temporary mac mapping, we don't want finalize() to update
# the ifcfg-* files!
echo "$orig_mac $orig_mac $orig_dev" >> $tmp_mac_mapping_file
else
MIGRATE_MACS+=( "$orig_mac" )
fi
done < $ORIG_MACS_FILE
if [ ${#MIGRATE_MACS[@]} -ne 0 ] ; then
# If some MACs were not found (MIGRATE_MACS not empty) then, we need a migration
:
elif [ -s $tmp_mac_mapping_file ] ; then
# Else, if some devices were renamed, we also need a migration, but it will
# be automatic thanks to the $tmp_mac_mapping_file mapping file
# We do not need the $MAC_MAPPING_FILE file from the user, just overwrite it
# Later, we will remove that file to not have finalize() modify the ifcfg-*
# files.
mkdir -p $(dirname $MAC_MAPPING_FILE)
cp $tmp_mac_mapping_file $MAC_MAPPING_FILE
else
# Skip this process if all MACs and network interfaces (devices) are accounted for
unset tmp_mac_mapping_file
return 0
fi
# Find the MAC addresses that are now available.
# This is an array with values of the form "$dev $mac $driver"
# which is similar to /etc/mac-addresses but with the driver information added:
NEW_DEVICES=()
for dev_dir in /sys/class/net/* ; do
# basename $dev_dir
dev="${dev_dir##*/}"
case $dev in
# Skip all kind of internal devices:
(lo|pan*|sit*|tun*|tap*|vboxnet*|vmnet*) continue ;;
esac
# Skip unless we have a MAC address:
test -s $dev_dir/address || continue
# Read first word from address file:
read mac junk <$dev_dir/address
# Skip devices without MAC address:
test "$mac" = "00:00:00:00:00:00" && continue
# Get the drivers.
# E.g. 'udevadm info -a -p /sys/class/net/eth0'
# prints a list of DRIVER[S]=="module" lines like
# DRIVER==""
# DRIVERS=="8139cp"
# DRIVERS==""
# where only non-empty values get stored in the drivers array:
drivers=( $( my_udevinfo -a -p /sys/class/net/$dev | sed -ne '/DRIVER.*=".\+"/s/.*"\(.*\)".*/\1/p' ) )
# The drivers array contains a list of drivers, but I care only about the first one:
NEW_DEVICES+=( "$dev $mac $drivers" )
done
# Check the existence of a valid mapping file.
# The file is valid, if at least one old MAC is mapped to an existing new one.
# The read_and_strip_file() function outputs non-empty and non-comment lines in MAC_MAPPING_FILE
# so that it is shown to the user what MAC address mappings will be done:
if read_and_strip_file $MAC_MAPPING_FILE ; then
while read orig_dev orig_mac junk ; do
read_and_strip_file $MAC_MAPPING_FILE | grep -qw "^$orig_mac" && MANUAL_MAC_MAPPING=true
done < $ORIG_MACS_FILE
fi
if unattended_recovery ; then
# we gonna cheat a bit and say we have map made (but we did not and just hope that the interfaces
# will be in the same order on the recover vm as on the client vm)
# For some background info see https://github.com/gdha/rear-automated-testing/issues/36
test $MANUAL_MAC_MAPPING || MANUAL_MAC_MAPPING=unattended
fi
# Let the user choose replacement network interfaces (unless manual mapping is specified).
# When there is only one original MAC and only one network interface on the current system
# automatically map the original MAC to the new MAC of the current network interface:
if test $MANUAL_MAC_MAPPING ; then
if test "unattended" = $MANUAL_MAC_MAPPING ; then
# When MANUAL_MAC_MAPPING is 'unattended' there is no valid mapping file
# and then we can only proceed in the hope that things will be right:
echo "Trying unattended MAC address mapping (no $MAC_MAPPING_FILE file)"
else
# Tell the user that his predefined network MAC address mappings will be done:
echo "Mapping MAC addresses as specified in the above $MAC_MAPPING_FILE lines (old_MAC new_MAC)"
fi
else
# Abandon this process if no manual mapping should be done and the ORIGINAL_MACS array is empty
# because when the ORIGINAL_MACS array is empty it does not make sense to let the user choose something:
if test ${#ORIGINAL_MACS[@]} -lt 1 ; then
echo "Skipping network interface migration because no MAC address of the original system is known"
return 0
fi
# Abandon this process if no manual mapping should be done and the NEW_DEVICES array is empty
# because when the NEW_DEVICES array is empty it is impossible to let the user choose something:
if test ${#NEW_DEVICES[@]} -lt 1 ; then
echo "Cannot migrate network interface setup because there is no usable MAC address on this system"
return 0
fi
# Ensure the directory of the MAC_MAPPING_FILE is there:
mkdir -p $( dirname $MAC_MAPPING_FILE )
# If there is only one original MAC and only one NEW_DEVICES array element
# automatically map the original MAC to the new one:
if test ${#ORIGINAL_MACS[@]} -eq 1 -a ${#NEW_DEVICES[@]} -eq 1 ; then
index=0
old_dev=${ORIGINAL_DEVICES[$index]}
old_mac=${ORIGINAL_MACS[$index]}
choice="${NEW_DEVICES[$index]}"
# Split choice="dev mac driver" into words:
dev_mac_driver=( $choice )
new_dev=${dev_mac_driver[0]}
new_mac=${dev_mac_driver[1]}
# Output the old_mac->new_mac mapping for later use below:
echo "$old_mac $new_mac $old_dev" >>$MAC_MAPPING_FILE
# Get new device name from current MAC address:
new_dev=$( get_device_by_hwaddr "$new_mac" )
# Tell the user about the automated mapping (and how he could avoid it):
echo "The only original network interface $old_dev $old_mac is not available"
echo "and no mapping is specified in $MAC_MAPPING_FILE"
echo "Mapping it to the only available $new_dev $new_mac"
else
# When there is more than one original MAC or more than one NEW_DEVICES array elements
# loop over all the original MACs and ask the user to specify a replacement
# even though maybe some MACs stayed we want to offer the user the option to
# reassign all MAC addresses. That is why we loop over all the original MACs and
# not only over the MACs that require reassignment:
echo "No network interface mapping is specified in $MAC_MAPPING_FILE"
for (( index=0 ; index < ${#ORIGINAL_MACS[@]} ; index++ )) ; do
old_dev=${ORIGINAL_DEVICES[$index]}
old_mac=${ORIGINAL_MACS[$index]}
echo "The original network interface $old_dev $old_mac is not available"
PS3="Choose replacement for $old_dev $old_mac "
skip_choice="Skip replacing $old_dev $old_mac"
select choice in "${NEW_DEVICES[@]}" "$skip_choice" ; do
# Invalid input causes choice to be set to null:
test "$choice" || continue
# User selected to skip replacing the network interface:
if test "$skip_choice" = "$choice" ; then
echo "Skipping $old_dev $old_mac (you may have to manually fix your network setup)"
# Continue with next MAC address in the outer 'for' loop:
continue 2
fi
# User selected one of the NEW_DEVICES array elements:
break
done
# Split choice="dev mac driver" into words:
dev_mac_driver=( $choice )
new_dev=${dev_mac_driver[0]}
new_mac=${dev_mac_driver[1]}
# Output the old_mac->new_mac mapping for later use below:
echo "$old_mac $new_mac $old_dev" >>$MAC_MAPPING_FILE
# Get new device name from current MAC address:
new_dev=$( get_device_by_hwaddr "$new_mac" )
# Tell the user about the mapping:
echo "Mapping $old_dev $old_mac to $new_dev $new_mac"
# Prepare the 'select' choices for the next MAC address:
# When one of the NEW_DEVICES array elements was selected
# replace the selected NEW_DEVICES array element with the empty string
# (i.e. do not remove that element from the NEW_DEVICES array)
# so that for the next MAC address in the 'for' loop an already
# selected NEW_DEVICE is no longer actually shown in the select list but
# there is still an empty choice (with number) shown in the select list
# so that the choice numbers for the remaining NEW_DEVICES
# stay the same for all MAC addresses during the 'for' loop:
NEW_DEVICES=( "${NEW_DEVICES[@]/$choice/}" )
done
fi
fi
# TODO: The current code in this script calls
# new_dev=$( get_device_by_hwaddr "$new_mac" )
# three times (at two places above and again below).
# If the current MAC_MAPPING_FILE entries syntax
# $old_mac $new_mac $old_dev
# could be enhanced to
# $old_mac $new_mac $old_dev $new_dev
# where $new_dev is optional, it would be backward compatible
# so that there are no regressions for users who manually specify MAC_MAPPING_FILE
# but now users could also manually specify a $new_dev in MAC_MAPPING_FILE
# (whatever that could be good for - perhaps for some kind of verification?)
# and the code above could write $new_dev into MAC_MAPPING_FILE
# so that the code below could use that (if exists).
# Initialize reload_udev variable to false because
# below we reload udev only if we actually have MAC mappings:
reload_udev=false
# Actually do the MAC mappings (if any).
# There could be no non-empty MAC mapping file when above
# the user had skipped replacing any network interface:
if test -s $MAC_MAPPING_FILE ; then
# MAC_MAPPING_FILE contains "$old_mac $new_mac $old_dev" lines:
while read old_mac new_mac old_dev junk ; do
# Get new device name from current MAC address:
new_dev=$( get_device_by_hwaddr "$new_mac" )
# Migrate udev persistent-net rules files (if any):
if test $UDEV_NET_MAC_RULE_FILES ; then
if grep -q "$old_mac" "${UDEV_NET_MAC_RULE_FILES[@]}" ; then
# Remove the "wrong" line with the new mac address and
# replace the old mac address with the new mac address:
sed -i -e "/$new_mac/d" -e "s#$old_mac#$new_mac#gI" "${UDEV_NET_MAC_RULE_FILES[@]}"
reload_udev=true
else
if grep -q "$old_dev" "${UDEV_NET_MAC_RULE_FILES[@]}" ; then
# Remove the "wrong" line with the new mac address and
# rename the new device name with the old one:
test "$new_dev" && sed -i -e "/$old_dev/d" -e "s#$new_dev#$old_dev#g" "${UDEV_NET_MAC_RULE_FILES[@]}"
reload_udev=true
else
# Device is not managed by udev rules.
# We have to update the network_setup_scripts with the new interface name:
test "$new_dev" && sed -i -e "s#$old_dev#$new_dev#g" "${network_setup_scripts[@]}"
fi
fi
else
# Device is not managed by udev rules.
# We have to update the network_setup_scripts with the new interface name:
test "$new_dev" && sed -i -e "s#$old_dev#$new_dev#g" "${network_setup_scripts[@]}"
fi
done < <( read_and_strip_file "$MAC_MAPPING_FILE" )
fi
# Reload udev if we have MAC mappings:
if is_true $reload_udev ; then
echo -n "Reloading udev ... "
# Force udev to reload rules (as they were just changed)
# Failback to "udevadm control --reload" in case of problem (as specify in udevadm manpage in SLES12)
# If nothing work, then wait 1 second delay. It should let the time for udev to detect changes in the rules files.
udevadm control --reload-rules || udevadm control --reload || sleep 1
my_udevtrigger
sleep 1
my_udevsettle
if test "$( ps --no-headers -C systemd )" ; then
# This might be not mandatory.
# It will release orphaned (old) device names in systemd
# Maybe it can be done by some less invazive command, but I didn't found it yet:
systemctl daemon-reload
fi
echo "done."
fi
# A later script in finalize/* will also go over the MAC mappings file and
# apply them to the files in the recovered system, unless we did the mapping
# automatically, which means some device has been renamed and will probably
# gets its name back upon reboot.
if [ -s $tmp_mac_mapping_file ] ; then
rm $MAC_MAPPING_FILE $tmp_mac_mapping_file
fi
unset tmp_mac_mapping_file
# vim: set et ts=4 sw=4: