diff --git a/mxnetctl/mxnetctl b/mxnetctl/mxnetctl new file mode 100755 index 0000000..c571746 --- /dev/null +++ b/mxnetctl/mxnetctl @@ -0,0 +1,254 @@ +#! /usr/bin/perl +use strict; +use warnings; + +use Getopt::Long; + +# options + +our ($opt_quiet,$opt_noop,$opt_ignore_hw); +use constant OPTIONS => ( + 'quiet' => \$opt_quiet, + 'noop' => \$opt_noop, +); + +sub USAGE { + return <<"__EOF__"; +usage: $0 + start [options] + stop [options] # (ignored) + upgrade # switch from eth to net + +options: + --quiet : do not log actions + --noop : dry run + +__EOF__ +} + +sub scandir { + my ($dirname)=@_; + opendir my $dir,$dirname or die "$dirname: $!\n"; + return sort grep !/^\./,readdir $dir; +} + +sub slurpfile { + my ($path)=@_; + open my $fh,'<',$path or die "$path: $!\n"; + return join ('',<$fh>); +} + +sub slurpfile_chomp { + my $data=slurpfile($_[0]); + chomp($data); + return $data; +} + +sub network_devices { # -> ( 'bond0','dummy0','eth0','eth1','eth2','eth2.43','eth3','eth4','eth5','lo','sit0' ) + return grep -d "/sys/class/net/$_",scandir('/sys/class/net'); +} + +sub network_hardware_devices { # -> ( eth0','eth1','eth2','eth3','eth4','eth5' ) + return grep -e "/sys/class/net/$_/device",network_devices(); +} + + +sub get_hw_address { # 'eth1' -> '00:1b:21:79:76:67' + my ($dev)=@_; + my $path="/sys/class/net/$dev/address"; + return slurpfile_chomp($path); +} + + +our %HW_TO_DEV; # ( '00:1b:21:79:76:67' => 'net02' , ... ) +our %DEV_TO_HW; # ('net02'=>'00:1b:21:79:76:67', ... ) +our $CHANGES; + + +sub register_stable { + my ($hw,$dev)=@_; + + # force one-to-one mapping, even if file is erroneus + + exists $DEV_TO_HW{$dev} and delete $HW_TO_DEV{$DEV_TO_HW{$dev}}; + exists $HW_TO_DEV{$dev} and delete $DEV_TO_HW{$HW_TO_DEV{$hw}}; + + + $HW_TO_DEV{$hw}=$dev; + $DEV_TO_HW{$dev}=$hw; + $CHANGES++; +} + + +sub read_mxnet { + %HW_TO_DEV=(); + %DEV_TO_HW=(); + -r '/etc/local/mxnet' or return; + open my $file,'<','/etc/local/mxnet' or die "/etc/local/mxnet: $!\n"; + while (<$file>) { + s/#.*//; + /\S/ or next; + my ($hw,$dev)=split; + register_stable($hw,$dev); + } + $CHANGES=0; +} + +sub write_mxnet { + my $mxnet; + unless ($opt_noop) { + open my $mxnet,'>',"/etc/local/mxnet" or die "/etc/local/mxnet: $!\n"; + print $mxnet "# generated ".scalar(localtime)." by $0\n\n"; + for my $dev (sort keys %DEV_TO_HW) { + print $mxnet $DEV_TO_HW{$dev},' ',$dev."\n"; + } + print $mxnet "\n"; + +# open my $out,'>',"/etc/local/mxnet-udev.rules" or die "/etc/local/mxnet-udev.rules: $!\n"; +# print $out "# generated ".scalar(localtime)." by $0\n\n"; +# for my $dev (sort keys %DEV_TO_HW) { +# print $out qq'SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="$DEV_TO_HW{$dev}", NAME="$dev"\n'; +# } + } +} + +sub get_unused_name { + my $next_name=0; + while(1) { + my $name=sprintf 'net%02d',$next_name++; + exists $DEV_TO_HW{$name} or return $name; + } +} + + +sub rename_if { + my ($src,$dst)=@_; + warn "rename $src to $dst\n" unless $opt_quiet; + unless ($opt_noop) { + if (defined $src =~ /^eth/) { + system 'ip','link','set',$src,'name',$dst,'alias',$src; # continue on error + $? and warn "failed to rename $src to $dst\n"; + } else { + system 'ip','link','set',$src,'name',$dst; # continue on error + $? and warn "failed to rename $src to $dst\n"; + } + } +} + + +sub preferred_device_name { + my ($dev,$hw)=@_; + + exists $HW_TO_DEV{$hw} and return $HW_TO_DEV{$hw}; # configured + $dev =~ /(\d+)/; + + my $new_dev=sprintf 'net%02d',$1; # stable number if free + unless (exists $DEV_TO_HW{$new_dev}) { + register_stable($hw,$new_dev); + return $new_dev; + } + + $new_dev=get_unused_name(); # any free new number + register_stable($hw,$new_dev); + return $new_dev; +} + + + +our $TMP_NUM=0; +sub out_of_the_way { + my ($dev)=@_; # only 'netXX' or 'ethX' - never 'tmpXX' + + -e "/sys/class/net/$dev/device" or return; + + my $i = $dev=~/(\d+)/; + rename_if($dev,sprintf('tmpnet%02d',$TMP_NUM++)); +} + +sub start { + read_mxnet(); + + # 1: rename configured ethX and netXX devices to configured netXX - move away conflicting devices + + for my $dev (grep /^(eth|net)(\d+)/,network_hardware_devices()) { + my $hw=get_hw_address($dev); + my $new_dev=preferred_device_name($dev,$hw); + if ($dev ne $new_dev) { + out_of_the_way($new_dev); + rename_if($dev,$new_dev); + } + } + + # 2 : rename moved away devices to netXX + + for my $dev (grep /^(tmpnet)(\d+)/,network_hardware_devices()) { + my $hw=get_hw_address($dev); + my $new_dev=preferred_device_name($dev,$hw); + rename_if($dev,$new_dev); + } + + #if ($CHANGES || ! -e "/etc/local/mxnet-udev.rules") { + if ($CHANGES) { + write_mxnet(); + } +} + + + +sub upgrade { + umask 022; + + -d '/etc/local' or mkdir '/etc/local' or die "/etc/local: $!\n"; + + read_mxnet(); + for my $dev (grep /^(eth|net)(\d+)/,network_hardware_devices()) { + my $hw=get_hw_address($dev); + my $new_dev=preferred_device_name($dev,$hw); + } + write_mxnet(); + + unlink "/etc/udev/rules.d/70-persistent-net.rules"; + + my $data=slurpfile('/etc/systemd/system/network.service'); + unless ($data=~/mxnetctl/) { + $data=~s#^ExecStart#ExecStart=/usr/sbin/mxnetctl start\nExecStart#m; + $data=~s/eth(\d+)/sprintf 'net%02d',$1/eg; + open my $out,'>','/etc/systemd/system/network.service' or die "/etc/systemd/system/network.service: $!\n"; + print $out $data; + close $out; + } + + warn "updated /etc/local/mxnet , /etc/systemd/system/network.service , /etc/udev/rules.d/70-persistent-net.rules - reboot to activate\n"; +} + + + + + + + +GetOptions(OPTIONS) or die USAGE; + +@ARGV>=1 or die USAGE; + +my ($cmd)=@ARGV; + + +if ($cmd eq 'start') { + start(); +} elsif ($cmd eq 'stop') { + ; +} elsif ($cmd eq 'upgrade') { + upgrade(); + ; +} else { + die USAGE; +} + + + + + + + +