11 use Storable
qw(dclone);
12 use JSON
; # allows simple debug-dumping of variables `print to_json($foo, {pretty => 1}) ."\n"`
16 # Current config, r() parses a network interface string into this variable
22 # perl conveniently lets you open a string as filehandle so we allow tests
23 # to temporarily save interface files to virtual files:
26 # Load a temp-file and return it as a string, if it didn't exist, try loading
31 if (my $local = $saved_files{$from}) {
35 open my $fh, '<', $from or die "failed to open $from: $!";
42 # Save a temporary file.
44 my ($file, $data) = @_;
45 $saved_files{$file} = $data;
48 # Delete a temporary file
51 die "no such file: $file" if !delete $saved_files{$file};
54 # Delete all temporary files.
56 foreach (keys %saved_files) {
57 delete $saved_files{$_} if $_ !~ m
,^shared
/,;
65 # Read an interfaces file with optional /proc/net/dev file content string and
66 # the list of active interfaces, which otherwise default
68 my ($ifaces, $proc_net_dev, $active) = @_;
69 $proc_net_dev //= load
('proc_net_dev');
70 $active //= [split(/\s+/, load
('active_interfaces'))];
71 open my $fh1, '<', \
$ifaces;
72 open my $fh2, '<', \
$proc_net_dev;
73 $config = PVE
::INotify
::__read_etc_network_interfaces
($fh1, $fh2, $active);
77 # Turn the current network config into a string.
79 # write shouldn't be able to change a previously parsed config
80 my $config_clone = dclone
($config);
81 return PVE
::INotify
::__write_etc_network_interfaces
($config_clone, 1);
85 ## Interface modification helpers
89 sub update_iface
($$%) {
90 my ($name, $families, %extra) = @_;
92 my $ifaces = $config->{ifaces
};
93 my $if = $ifaces->{$name};
95 die "no such interface: $name\n" if !$if;
99 # merge extra flags (like bridge_ports, ovs_*) directly
100 $if->{$_} = $extra{$_} foreach keys %extra;
102 return if !$families;
104 my $if_families = $if->{families
} ||= [];
105 foreach my $family (@$families) {
106 my $type = delete $family->{family
};
107 @$if_families = ((grep { $_ ne $type } @$if_families), $type);
109 (my $suffix = $type) =~ s/^inet//;
110 $if->{"method$suffix"} = $family->{address
} ?
'static' : 'manual';
111 foreach(qw(address netmask gateway options)) {
112 if (my $value = delete $family->{$_}) {
113 $if->{"$_${suffix}"} = $value;
119 # Create an interface and error if it already exists.
120 sub new_iface
($$$%) {
121 my ($name, $type, $families, %extra) = @_;
122 my $ifaces = $config->{ifaces
};
123 croak
"interface already exists: $name" if $ifaces->{$name};
124 $ifaces->{$name} = { type
=> $type };
125 update_iface
($name, $families, %extra);
128 # Delete an interface and error if it did not exist.
129 sub delete_iface
($;$) {
130 my ($name, $family) = @_;
131 my $ifaces = $config->{ifaces
};
132 my $if = $ifaces->{$name} ||= {};
133 croak
"interface doesn't exist: $name" if !$if;
136 delete $ifaces->{$name};
140 my $families = $if->{families
};
141 @$families = grep {$_ ne $family} @$families;
142 (my $suffix = $family) =~ s/^inet//;
143 delete $if->{"$_$suffix"} foreach qw(address netmask gateway options);
150 # Compare two strings line by line and show a diff/error if they differ.
155 my ($ra, $wa) = POSIX
::pipe();
156 my ($rb, $wb) = POSIX
::pipe();
157 my $ha = IO
::Handle-
>new_from_fd($wa, 'w');
158 my $hb = IO
::Handle-
>new_from_fd($wb, 'w');
160 open my $diffproc, '-|', 'diff', '-up', "/dev/fd/$ra", "/dev/fd/$rb"
161 or die "failed to run program 'diff': $!";
165 open my $f1, '<', \
$a;
166 open my $f2, '<', \
$b;
169 $ha->print($line1) if defined($line1 = <$f1>);
170 $hb->print($line2) if defined($line2 = <$f2>);
171 } while (defined($line1 // $line2));
178 my $diff = <$diffproc>;
180 die "files differ:\n$diff";
183 # Write the current interface config and compare the result to a string.
187 diff
($expected, $got);
191 ## Main test execution:
193 # (sorted, it's not used right now but tests could pass on temporary files by
194 # prefixing the name with shared/ and thus you might want to split a larger
195 # test into t.01.first-part.pl, t.02.second-part.pl, etc.
198 for our $Test (sort <t
.*.pl
>) {
205 print "FAIL: $Test\n$@\n\n";
208 print "PASS: $Test\n";
212 die "$failed out of $total tests failed\n" if $failed;