mirror of
https://git.proxmox.com/git/pve-common
synced 2025-07-27 15:19:17 +00:00
add support for openvswitch network configuration
This commit is contained in:
parent
180b97c2a6
commit
9b7b13a1cc
@ -674,10 +674,76 @@ my $bond_modes = { 'balance-rr' => 0,
|
|||||||
'balance-alb' => 6,
|
'balance-alb' => 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
my $ovs_bond_modes = {
|
||||||
|
'active-backup' => 1,
|
||||||
|
'balance-tcp' => 1,
|
||||||
|
'balance-slb' => 1,
|
||||||
|
};
|
||||||
|
|
||||||
#sub get_bond_modes {
|
#sub get_bond_modes {
|
||||||
# return $bond_modes;
|
# return $bond_modes;
|
||||||
#}
|
#}
|
||||||
|
|
||||||
|
my $parse_ovs_option = sub {
|
||||||
|
my ($data) = @_;
|
||||||
|
|
||||||
|
my $opts = {};
|
||||||
|
foreach my $kv (split (/\s+/, $data || '')) {
|
||||||
|
my ($k, $v) = split('=', $kv, 2);
|
||||||
|
$opts->{$k} = $v if $k && $v;
|
||||||
|
}
|
||||||
|
return $opts;
|
||||||
|
};
|
||||||
|
|
||||||
|
my $set_ovs_option = sub {
|
||||||
|
my ($d, %params) = @_;
|
||||||
|
|
||||||
|
my $opts = &$parse_ovs_option($d->{ovs_options});
|
||||||
|
|
||||||
|
foreach my $k (keys %params) {
|
||||||
|
my $v = $params{$k};
|
||||||
|
if ($v) {
|
||||||
|
$opts->{$k} = $v;
|
||||||
|
} else {
|
||||||
|
delete $opts->{$k};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $res = [];
|
||||||
|
foreach my $k (keys %$opts) {
|
||||||
|
push @$res, "$k=$opts->{$k}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (my $new = join(' ', @$res)) {
|
||||||
|
$d->{ovs_options} = $new;
|
||||||
|
return $d->{ovs_options};
|
||||||
|
} else {
|
||||||
|
delete $d->{ovs_options};
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
my $extract_ovs_option = sub {
|
||||||
|
my ($d, $name) = @_;
|
||||||
|
|
||||||
|
my $opts = &$parse_ovs_option($d->{ovs_options});
|
||||||
|
|
||||||
|
my $v = delete $opts->{$name};
|
||||||
|
|
||||||
|
my $res = [];
|
||||||
|
foreach my $k (keys %$opts) {
|
||||||
|
push @$res, "$k=$opts->{$k}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (my $new = join(' ', @$res)) {
|
||||||
|
$d->{ovs_options} = $new;
|
||||||
|
} else {
|
||||||
|
delete $d->{ovs_options};
|
||||||
|
}
|
||||||
|
|
||||||
|
return $v;
|
||||||
|
};
|
||||||
|
|
||||||
sub read_etc_network_interfaces {
|
sub read_etc_network_interfaces {
|
||||||
my ($filename, $fh) = @_;
|
my ($filename, $fh) = @_;
|
||||||
|
|
||||||
@ -700,15 +766,6 @@ sub read_etc_network_interfaces {
|
|||||||
# always add the vmbr0 bridge device
|
# always add the vmbr0 bridge device
|
||||||
$ifaces->{vmbr0}->{exists} = 1;
|
$ifaces->{vmbr0}->{exists} = 1;
|
||||||
|
|
||||||
if (my $fd2 = IO::File->new("/proc/net/if_inet6", "r")) {
|
|
||||||
while (defined ($line = <$fd2>)) {
|
|
||||||
if ($line =~ m/^[a-f0-9]{32}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+(lo|eth\d+|vmbr\d+|bond\d+)$/) {
|
|
||||||
$ifaces->{$1}->{active} = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close ($fd2);
|
|
||||||
}
|
|
||||||
|
|
||||||
my $gateway = 0;
|
my $gateway = 0;
|
||||||
|
|
||||||
while (defined ($line = <$fh>)) {
|
while (defined ($line = <$fh>)) {
|
||||||
@ -742,6 +799,9 @@ sub read_etc_network_interfaces {
|
|||||||
} elsif ($id eq 'gateway') {
|
} elsif ($id eq 'gateway') {
|
||||||
$d->{$id} = $value;
|
$d->{$id} = $value;
|
||||||
$gateway = 1;
|
$gateway = 1;
|
||||||
|
} elsif ($id eq 'ovs_type' || $id eq 'ovs_options'|| $id eq 'ovs_bridge' ||
|
||||||
|
$id eq 'ovs_bonds' || $id eq 'ovs_ports') {
|
||||||
|
$d->{$id} = $value;
|
||||||
} elsif ($id eq 'slaves' || $id eq 'bridge_ports') {
|
} elsif ($id eq 'slaves' || $id eq 'bridge_ports') {
|
||||||
my $devs = {};
|
my $devs = {};
|
||||||
foreach my $p (split (/\s+/, $value)) {
|
foreach my $p (split (/\s+/, $value)) {
|
||||||
@ -795,15 +855,30 @@ sub read_etc_network_interfaces {
|
|||||||
foreach my $iface (keys %$ifaces) {
|
foreach my $iface (keys %$ifaces) {
|
||||||
my $d = $ifaces->{$iface};
|
my $d = $ifaces->{$iface};
|
||||||
if ($iface =~ m/^bond\d+$/) {
|
if ($iface =~ m/^bond\d+$/) {
|
||||||
|
if (!$d->{ovs_type}) {
|
||||||
$d->{type} = 'bond';
|
$d->{type} = 'bond';
|
||||||
|
} elsif ($d->{ovs_type} eq 'OVSBond') {
|
||||||
|
$d->{type} = $d->{ovs_type};
|
||||||
|
# translate: ovs_options => bond_mode
|
||||||
|
$d->{'bond_mode'} = &$extract_ovs_option($d, 'bond_mode');
|
||||||
|
} else {
|
||||||
|
$d->{type} = 'unknown';
|
||||||
|
}
|
||||||
} elsif ($iface =~ m/^vmbr\d+$/) {
|
} elsif ($iface =~ m/^vmbr\d+$/) {
|
||||||
|
if (!$d->{ovs_type}) {
|
||||||
$d->{type} = 'bridge';
|
$d->{type} = 'bridge';
|
||||||
|
|
||||||
if (!defined ($d->{bridge_fd})) {
|
if (!defined ($d->{bridge_fd})) {
|
||||||
$d->{bridge_fd} = 0;
|
$d->{bridge_fd} = 0;
|
||||||
}
|
}
|
||||||
if (!defined ($d->{bridge_stp})) {
|
if (!defined ($d->{bridge_stp})) {
|
||||||
$d->{bridge_stp} = 'off';
|
$d->{bridge_stp} = 'off';
|
||||||
}
|
}
|
||||||
|
} elsif ($d->{ovs_type} eq 'OVSBridge') {
|
||||||
|
$d->{type} = $d->{ovs_type};
|
||||||
|
} else {
|
||||||
|
$d->{type} = 'unknown';
|
||||||
|
}
|
||||||
} elsif ($iface =~ m/^(\S+):\d+$/) {
|
} elsif ($iface =~ m/^(\S+):\d+$/) {
|
||||||
$d->{type} = 'alias';
|
$d->{type} = 'alias';
|
||||||
if (defined ($ifaces->{$1})) {
|
if (defined ($ifaces->{$1})) {
|
||||||
@ -813,16 +888,35 @@ sub read_etc_network_interfaces {
|
|||||||
$d->{exists} = 0;
|
$d->{exists} = 0;
|
||||||
}
|
}
|
||||||
} elsif ($iface =~ m/^eth\d+$/) {
|
} elsif ($iface =~ m/^eth\d+$/) {
|
||||||
|
if (!$d->{ovs_type}) {
|
||||||
$d->{type} = 'eth';
|
$d->{type} = 'eth';
|
||||||
} elsif ($iface =~ m/^lo$/) {
|
} elsif ($d->{ovs_type} eq 'OVSPort') {
|
||||||
$d->{type} = 'loopback';
|
$d->{type} = $d->{ovs_type};
|
||||||
} else {
|
} else {
|
||||||
$d->{type} = 'unknown';
|
$d->{type} = 'unknown';
|
||||||
}
|
}
|
||||||
|
} elsif ($iface =~ m/^lo$/) {
|
||||||
|
$d->{type} = 'loopback';
|
||||||
|
} else {
|
||||||
|
if (!$d->{ovs_type}) {
|
||||||
|
$d->{type} = 'unknown';
|
||||||
|
} elsif ($d->{ovs_type} eq 'OVSIntPort') {
|
||||||
|
$d->{type} = $d->{ovs_type};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$d->{method} = 'manual' if !$d->{method};
|
$d->{method} = 'manual' if !$d->{method};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (my $fd2 = IO::File->new("/proc/net/if_inet6", "r")) {
|
||||||
|
while (defined ($line = <$fd2>)) {
|
||||||
|
if ($line =~ m/^[a-f0-9]{32}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+(\S+)$/) {
|
||||||
|
$ifaces->{$1}->{active} = 1 if defined($ifaces->{$1});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close ($fd2);
|
||||||
|
}
|
||||||
|
|
||||||
return $ifaces;
|
return $ifaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -833,47 +927,90 @@ sub __interface_to_string {
|
|||||||
|
|
||||||
my $raw = '';
|
my $raw = '';
|
||||||
|
|
||||||
if ($d->{autostart}) {
|
|
||||||
$raw .= "auto $iface\n";
|
|
||||||
}
|
|
||||||
$raw .= "iface $iface inet $d->{method}\n";
|
$raw .= "iface $iface inet $d->{method}\n";
|
||||||
$raw .= "\taddress $d->{address}\n" if $d->{address};
|
$raw .= "\taddress $d->{address}\n" if $d->{address};
|
||||||
$raw .= "\tnetmask $d->{netmask}\n" if $d->{netmask};
|
$raw .= "\tnetmask $d->{netmask}\n" if $d->{netmask};
|
||||||
$raw .= "\tgateway $d->{gateway}\n" if $d->{gateway};
|
$raw .= "\tgateway $d->{gateway}\n" if $d->{gateway};
|
||||||
$raw .= "\tbroadcast $d->{broadcast}\n" if $d->{broadcast};
|
$raw .= "\tbroadcast $d->{broadcast}\n" if $d->{broadcast};
|
||||||
|
|
||||||
if ($d->{bridge_ports} || ($iface =~ m/^vmbr\d+$/)) {
|
my $done = { type => 1, priority => 1, method => 1, active => 1, exists => 1,
|
||||||
|
comments => 1, autostart => 1, options => 1,
|
||||||
|
address => 1, netmask => 1, gateway => 1, broadcast => 1 };
|
||||||
|
|
||||||
|
if ($d->{type} eq 'bridge') {
|
||||||
|
|
||||||
my $ports = $d->{bridge_ports} || 'none';
|
my $ports = $d->{bridge_ports} || 'none';
|
||||||
$raw .= "\tbridge_ports $ports\n";
|
$raw .= "\tbridge_ports $ports\n";
|
||||||
}
|
$done->{bridge_ports} = 1;
|
||||||
|
|
||||||
if ($d->{bridge_stp} || ($iface =~ m/^vmbr\d+$/)) {
|
my $v = defined($d->{bridge_stp}) ? $d->{bridge_stp} : 'off';
|
||||||
my $v = $d->{bridge_stp};
|
|
||||||
$v = defined ($v) ? $v : 'off';
|
|
||||||
$raw .= "\tbridge_stp $v\n";
|
$raw .= "\tbridge_stp $v\n";
|
||||||
}
|
$done->{bridge_stp} = 1;
|
||||||
|
|
||||||
if (defined ($d->{bridge_fd}) || ($iface =~ m/^vmbr\d+$/)) {
|
$v = defined($d->{bridge_fd}) ? $d->{bridge_fd} : 0;
|
||||||
my $v = $d->{bridge_fd};
|
|
||||||
$v = defined ($v) ? $v : 0;
|
|
||||||
$raw .= "\tbridge_fd $v\n";
|
$raw .= "\tbridge_fd $v\n";
|
||||||
}
|
$done->{bridge_fd} = 1;
|
||||||
|
|
||||||
|
} elsif ($d->{type} eq 'bond') {
|
||||||
|
|
||||||
if ($d->{slaves} || ($iface =~ m/^bond\d+$/)) {
|
|
||||||
my $slaves = $d->{slaves} || 'none';
|
my $slaves = $d->{slaves} || 'none';
|
||||||
$raw .= "\tslaves $slaves\n";
|
$raw .= "\tslaves $slaves\n";
|
||||||
}
|
$done->{slaves} = 1;
|
||||||
|
|
||||||
if (defined ($d->{'bond_miimon'}) || ($iface =~ m/^bond\d+$/)) {
|
my $v = defined ($d->{'bond_miimon'}) ? $d->{'bond_miimon'} : 100;
|
||||||
my $v = $d->{'bond_miimon'};
|
|
||||||
$v = defined ($v) ? $v : 100;
|
|
||||||
$raw .= "\tbond_miimon $v\n";
|
$raw .= "\tbond_miimon $v\n";
|
||||||
|
$done->{'bond_miimon'} = 1;
|
||||||
|
|
||||||
|
$v = defined ($d->{'bond_mode'}) ? $d->{'bond_mode'} : 'balance-rr';
|
||||||
|
$raw .= "\tbond_mode $v\n";
|
||||||
|
$done->{'bond_mode'} = 1;
|
||||||
|
|
||||||
|
} elsif ($d->{type} eq 'OVSBridge') {
|
||||||
|
|
||||||
|
$raw .= "\tovs_type $d->{type}\n";
|
||||||
|
$done->{ovs_type} = 1;
|
||||||
|
|
||||||
|
$raw .= "\tovs_ports $d->{ovs_ports}\n" if $d->{ovs_ports};
|
||||||
|
$done->{ovs_ports} = 1;
|
||||||
|
|
||||||
|
} elsif ($d->{type} eq 'OVSPort' || $d->{type} eq 'OVSIntPort' ||
|
||||||
|
$d->{type} eq 'OVSBond') {
|
||||||
|
|
||||||
|
$d->{autostart} = 0; # started by the bridge
|
||||||
|
|
||||||
|
if ($d->{type} eq 'OVSBond') {
|
||||||
|
|
||||||
|
$d->{bond_mode} = 'active-backup' if !$d->{bond_mode};
|
||||||
|
|
||||||
|
$ovs_bond_modes->{$d->{bond_mode}} ||
|
||||||
|
die "OVS does not support bond mode '$d->{bond_mode}\n";
|
||||||
|
|
||||||
|
&$set_ovs_option($d, bond_mode => $d->{bond_mode});
|
||||||
|
$done->{bond_mode} = 1;
|
||||||
|
|
||||||
|
$raw .= "\tovs_bonds $d->{ovs_bonds}\n" if $d->{ovs_bonds};
|
||||||
|
$done->{ovs_bonds} = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defined ($d->{'bond_mode'}) || ($iface =~ m/^bond\d+$/)) {
|
if ($d->{ovs_bridge}) {
|
||||||
my $v = $d->{'bond_mode'};
|
$raw = "allow-$d->{ovs_bridge} $iface\n$raw";
|
||||||
$v = defined ($v) ? $v : 'balance-rr';
|
}
|
||||||
$raw .= "\tbond_mode $v\n";
|
|
||||||
|
$raw .= "\tovs_type $d->{type}\n";
|
||||||
|
$done->{ovs_type} = 1;
|
||||||
|
|
||||||
|
if ($d->{ovs_bridge}) {
|
||||||
|
$raw .= "\tovs_bridge $d->{ovs_bridge}\n";
|
||||||
|
$done->{ovs_bridge} = 1;
|
||||||
|
}
|
||||||
|
# fixme: use Data::Dumper; print Dumper($d);
|
||||||
|
}
|
||||||
|
|
||||||
|
# print other settings
|
||||||
|
foreach my $k (keys %$d) {
|
||||||
|
next if $done->{$k};
|
||||||
|
next if !$d->{$k};
|
||||||
|
$raw .= "\t$k $d->{$k}\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach my $option (@{$d->{options}}) {
|
foreach my $option (@{$d->{options}}) {
|
||||||
@ -886,6 +1023,10 @@ sub __interface_to_string {
|
|||||||
$raw .= "#$cl\n";
|
$raw .= "#$cl\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($d->{autostart}) {
|
||||||
|
$raw = "auto $iface\n$raw";
|
||||||
|
}
|
||||||
|
|
||||||
$raw .= "\n";
|
$raw .= "\n";
|
||||||
|
|
||||||
return $raw;
|
return $raw;
|
||||||
@ -894,11 +1035,82 @@ sub __interface_to_string {
|
|||||||
sub write_etc_network_interfaces {
|
sub write_etc_network_interfaces {
|
||||||
my ($filename, $fh, $ifaces) = @_;
|
my ($filename, $fh, $ifaces) = @_;
|
||||||
|
|
||||||
|
my $used_ports = {};
|
||||||
|
|
||||||
|
foreach my $iface (keys %$ifaces) {
|
||||||
|
my $d = $ifaces->{$iface};
|
||||||
|
|
||||||
|
my $ports = '';
|
||||||
|
foreach my $k (qw(bridge_ports ovs_ports slaves ovs_bonds)) {
|
||||||
|
$ports .= " $d->{$k}" if $d->{$k};
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach my $p (PVE::Tools::split_list($ports)) {
|
||||||
|
die "port '$p' is already used on interface '$used_ports->{$p}'\n"
|
||||||
|
if $used_ports->{$p} && $used_ports->{$p} ne $iface;
|
||||||
|
$used_ports->{$p} = $iface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# delete unused OVS ports
|
||||||
|
foreach my $iface (keys %$ifaces) {
|
||||||
|
my $d = $ifaces->{$iface};
|
||||||
|
if ($d->{type} eq 'OVSPort' || $d->{type} eq 'OVSIntPort' ||
|
||||||
|
$d->{type} eq 'OVSBond') {
|
||||||
|
my $brname = $used_ports->{$iface};
|
||||||
|
if (!$brname || !$ifaces->{$brname}) {
|
||||||
|
delete $ifaces->{$iface};
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
my $bd = $ifaces->{$brname};
|
||||||
|
if ($bd->{type} ne 'OVSBridge') {
|
||||||
|
delete $ifaces->{$iface};
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# create OVS bridge ports
|
||||||
|
foreach my $iface (keys %$ifaces) {
|
||||||
|
my $d = $ifaces->{$iface};
|
||||||
|
if ($d->{type} eq 'OVSBridge' && $d->{ovs_ports}) {
|
||||||
|
foreach my $p (split (/\s+/, $d->{ovs_ports})) {
|
||||||
|
my $n = $ifaces->{$p};
|
||||||
|
die "OVS bridge '$iface' - unable to find port '$p'\n"
|
||||||
|
if !$n;
|
||||||
|
if ($n->{type} eq 'eth') {
|
||||||
|
$n->{type} = 'OVSPort';
|
||||||
|
$n->{ovs_bridge} = $iface;
|
||||||
|
} elsif ($n->{type} eq 'OVSBond' || $n->{type} eq 'OVSPort' ||
|
||||||
|
$n->{type} eq 'OVSIntPort') {
|
||||||
|
$n->{ovs_bridge} = $iface;
|
||||||
|
} else {
|
||||||
|
die "interface '$p' is not defined as OVS port/bond\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# check OVS bond ports
|
||||||
|
foreach my $iface (keys %$ifaces) {
|
||||||
|
my $d = $ifaces->{$iface};
|
||||||
|
if ($d->{type} eq 'OVSBond' && $d->{ovs_bonds}) {
|
||||||
|
foreach my $p (split (/\s+/, $d->{ovs_bonds})) {
|
||||||
|
my $n = $ifaces->{$p};
|
||||||
|
die "OVS bond '$iface' - unable to find slave '$p'\n"
|
||||||
|
if !$n;
|
||||||
|
die "OVS bond '$iface' - wrong interface type on slave '$p' " .
|
||||||
|
"('$n->{type}' != 'eth')\n" if $n->{type} ne 'eth';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
my $raw = "# network interface settings\n";
|
my $raw = "# network interface settings\n";
|
||||||
|
|
||||||
my $printed = {};
|
my $printed = {};
|
||||||
|
|
||||||
my $if_type_hash = {
|
my $if_type_hash = {
|
||||||
|
unknown => 0,
|
||||||
loopback => 10,
|
loopback => 10,
|
||||||
eth => 20,
|
eth => 20,
|
||||||
bond => 30,
|
bond => 30,
|
||||||
|
Loading…
Reference in New Issue
Block a user