[!
use strict;
use PVE::pvecfg;
use PVE::I18N;
use PVE::ConfigServer;
use PVE::HTMLTable;
use PVE::HTMLControls;
use PVE::Config;
use PVE::HTMLForm;
use PVE::HTMLUtils;
use HTML::Entities;
use PVE::HTMLGrid;
!]
[-
use strict;
sub merge_ifdata {
my ($oldif, $newif) = @_;
my $res;
if ($oldif->{type} eq $newif->{type}) {
foreach my $k (keys %$oldif) {
$res->{$k} = $oldif->{$k};
}
foreach my $k (keys %$newif) {
$res->{$k} = $newif->{$k};
}
} else {
foreach my $k (keys %$newif) {
$res->{$k} = $newif->{$k};
}
}
return $res;
}
sub device_is_used {
my ($ifl, $port) = @_;
foreach my $if (keys %$ifl) {
if ($ifl->{$if}->{'bridge_ports'} && $ifl->{$if}->{'bridge_ports'}->{$port}) {
return $if;
}
if ($ifl->{$if}->{'slaves'} && $ifl->{$if}->{'slaves'}->{$port}) {
return $if;
}
}
return undef;
}
my $cinfo = $fdat{__cinfo};
my $out = '';
my $form = PVE::HTMLForm->new (\%fdat);
my $conn = PVE::ConfigClient::connect ($udat{auth_cookie});
my $cdata = $conn->get_config_data ('interfaces', 1)->result;
my $ifaces = $cdata->{data};
my $changes = $cdata->{changes};
my $bond_avail;
for (my $i = 0; $i < 10; $i++) {
if (!defined ($ifaces->{"bond$i"})) {
push @$bond_avail, "bond$i";
}
}
my $bond_modes = PVE::Config::get_bond_modes();
my $bmodes = [ sort { $bond_modes->{$a} <=> $bond_modes->{$b} } keys %$bond_modes ];
if ($udat{action}) {
my $iface = $fdat{aa};
if ($udat{action} eq 'delete') {
eval {
check_write_mode ($udat{AM});
if (my $use = device_is_used ($ifaces, $iface)) {
die "device is in use ($use) - unable to delete '$iface'\n";
}
delete $ifaces->{$iface};
$cdata = $conn->set_config_data ('interfaces', $ifaces, 1)->result;
$ifaces = $cdata->{data};
$changes = $cdata->{changes};
};
if ($@) {
$udat{popup_error} = $@;
}
} elsif ($udat{action} eq 'discard') {
eval {
check_write_mode ($udat{AM});
$cdata = $conn->discard_config_changes ('interfaces', 1)->result;
$ifaces = $cdata->{data};
$changes = $cdata->{changes};
};
if ($@) {
$udat{popup_error} = $@;
}
}
print OUT $out;
return;
}
if ($form->action eq 'save') {
my $iface = $fdat{iface};
eval {
check_write_mode ($udat{AM});
unless ($fdat{edit} == 1) {
# normalize interface name
$iface =~ s/^(bond|vmbr)0*([0-9]+)$/$1$2/;
die "Device $iface already exists\n" if $ifaces->{$iface};
}
my $newif;
if (!($fdat{address} && $fdat{address} ne '127.0.0.1' &&
$fdat{address} ne '0.0.0.0' && $fdat{netmask})) {
$newif = { type => 'manual' };
} else {
$newif = {
type => 'static',
address => $fdat{address},
netmask => $fdat{netmask},
gateway => $fdat{gateway},
};
}
if (device_is_used ($ifaces, $iface) && ($iface =~ m/^bond[0-9]$/)) {
$newif->{autostart} = 1;
} else {
$newif->{autostart} = $fdat{autostart};
}
$ifaces->{$iface} = merge_ifdata ($ifaces->{$iface}, $newif);
my $br_num;
if ((($br_num) = $iface =~ m/^vmbr([0-9]{1,4})$/) && $br_num < 4095) {
#$ifaces->{$iface}->{autostart} = 1;
$ifaces->{$iface}->{'bridge_ports'} = {};
$fdat{ports} =~ s/,;/ /g;
foreach my $port (split (/\s+/, $fdat{ports})) {
if (my $use = device_is_used ($ifaces, $port)) {
die "unable to bridge device '$port' - device is already in use ($use)\n";
}
if ($port =~ m/^eth\d+$/ && defined ($ifaces->{$port})) {
$ifaces->{$iface}->{'bridge_ports'}->{$port} = 1;
my $nif = { type => 'manual', autostart => 0 };
$ifaces->{$port} = merge_ifdata ($ifaces->{$port}, $nif);
} elsif ($port =~ m/^(eth\d+)\.\d+$/ && defined ($ifaces->{$1})) {
$ifaces->{$iface}->{'bridge_ports'}->{$port} = 1;
} elsif ($port =~ m/^bond\d+$/ && defined ($ifaces->{$port})) {
$ifaces->{$iface}->{'bridge_ports'}->{$port} = 1;
my $nif = { type => 'manual', autostart => 1 };
$ifaces->{$port} = merge_ifdata ($ifaces->{$port}, $nif);
} else {
die "unable to bridge device '$port'\n";
}
}
} elsif ($iface =~ m/^bond[0-9]$/) {
#$ifaces->{$iface}->{autostart} = 1;
$ifaces->{$iface}->{'bond_mode'} = $fdat{bond_mode};
$ifaces->{$iface}->{'slaves'} = {};
$fdat{slaves} =~ s/,;/ /g;
foreach my $slave (split (/\s+/, $fdat{slaves})) {
if (my $use = device_is_used ($ifaces, $slave)) {
die "unable to add slave device '$slave' - device is already in use ($use)\n";
}
if ($slave =~ m/^eth\d+$/ && defined ($ifaces->{$slave})) {
$ifaces->{$iface}->{'slaves'}->{$slave} = 1;
my $nif = { type => 'manual', autostart => 0 };
$ifaces->{$slave} = merge_ifdata ($ifaces->{$slave}, $nif);
} else {
die "unable to add slave device '$slave'\n";
}
}
} elsif ($iface =~ m/^eth\d+$/) {
# nothing to do here
} else {
die "invalid interface name for bridge or bonding device\n";
}
$cdata = $conn->set_config_data ('interfaces', $ifaces, 1)->result;
$ifaces = $cdata->{data};
$changes = $cdata->{changes};
};
if ($@) {
$udat{popup_error} = $@;
$udat{redirect} = $fdat{__uri};
return;
}
$fdat{edit} = undef;
}
if ($fdat{edit}) {
$out .= $form->create_header($fdat{__uri});
my $grid = PVE::HTMLGrid->new ('fw1', 'fw2', 'fw3:right', 'fw4');
my $d;
if ($fdat{edit} == 1) {
$d = $ifaces->{$fdat{aa}};
} else {
$d = { autostart => 1 };
}
my $title;
if ($fdat{edit} == 1) {
if ($fdat{aa} =~ m/^vmbr\d+$/) {
$title = sprintf (__("Configure bridge '%s'"), $fdat{aa});
$fdat{itype} = 'bridge';
} elsif ($fdat{aa} =~ m/^bond\d+$/) {
$title = sprintf (__("Configure bond '%s'"), $fdat{aa});
$fdat{itype} = 'bond';
} elsif ($fdat{aa} =~ m/^eth/) {
$title = sprintf (__("Configure network interface '%s'"), $fdat{aa});
$fdat{itype} = 'eth';
} else {
die "unknown device naming scheme '$fdat{aa}'";
}
} else {
if ($fdat{itype} eq 'bridge') {
$title = __("Create bridge device");
} elsif ($fdat{itype} eq 'bond') {
$title = __("Create bonding device");
$d->{bond_mode} = 'active-backup';
} else {
die "unknown type '$fdat{itype}'";
}
}
if ($fdat{edit} == 2) {
if ($fdat{itype} eq 'bridge') {
$grid->add_row (__("Bridge name") . ':',
$form->create_element ('iface', 'text', 'vmbr'));
$grid->add_row (undef,
__("Allowable bridge names: vmbrn, where 0 ≤ n ≤ 4094"));
} elsif ($fdat{itype} eq 'bond') {
die "no more free bonding devices\n" if !$bond_avail;
$grid->add_row (__("Bond name") . ':',
$form->create_element ('iface', 'dropdown', undef, $bond_avail));
} else {
die "unknown type '$fdat{itype}'";
}
}
my @td;
push @td, __("IP Address") . ':',
$form->create_element ('address', 'ip', $d->{address} || '0.0.0.0');
push @td, __("Autostart") . ':',
$form->create_element ('autostart', 'bool', $d->{autostart});
$grid->add_row (@td);
@td = ();
push @td, __("Subnet Mask") . ':',
$form->create_element ('netmask', 'ip', $d->{netmask} || '255.255.255.0');
if ($fdat{itype} eq 'bridge') {
my $ports = '';
if ($d->{'bridge_ports'}) {
$ports = join (' ', sort keys %{$d->{'bridge_ports'}});
}
push @td, __("Bridge Ports") . ':',
$form->create_element ('ports', 'text', $ports);
} elsif ($fdat{itype} eq 'bond') {
my $slaves = '';
if ($d->{'slaves'}) {
$slaves = join (' ', sort keys %{$d->{'slaves'}});
}
push @td, __("Slaves") . ':',
$form->create_element ('slaves', 'text', $slaves);
}
$grid->add_row (@td);
@td = ();
if (defined ($d->{gateway})) {
push @td, __("Gateway") . ':',
$form->create_element ('gateway', 'ip', $d->{gateway} || '0.0.0.0');
} else {
push @td, '', '';
}
if ($fdat{itype} eq 'bond') {
push @td, __("Mode") . ':',
$form->create_element ('bond_mode', 'dropdown', $d->{bond_mode} || 'balance-rr', $bmodes);
}
$grid->add_row (@td);
my $html = $grid->html();
$html .= "
";
$html .= $form->create_element("iface", "hidden", $fdat{aa});
$html .= $form->create_element("edit", "hidden", $fdat{edit});
$html .= $form->create_cmdbutton ('save');
$out .= PVE::HTMLUtils::create_statusframe (undef, $title, undef, $html);
$out .= $form->create_footer();
print OUT $out;
return;
}
my @header = (
'1', '20px', ' ',
'1', '75px', __('Interface'),
'1', '75px', __('Active'),
'1', '100px', __('Ports/Slaves'),
'1', '75px', __('Autostart'),
'1', '100px', __('IP Address'),
'1', '100px', __('Subnet Mask'),
'1', '100px', __('Gateway'),
);
my $table = PVE::HTMLTable->new ([]);
$table->add_headline (\@header);
my $ddown = PVE::HTMLDropDown->new ();
$ddown->add_item("menu0","?edit=2&itype=bridge", __("Create bridge device") );
$ddown->add_item("menu0","?edit=2&itype=bond", __("Create bonding device") );
$ddown->add_item("menu0","?action=discard", __("Discard all changes") ) if $changes;
$ddown->add_item("menu1","?edit=1", __("Edit"));
$ddown->add_item("menu1","?action=delete", __("Delete"));
$out .= "
" . encode_entities ($changes) . ""; $out .= PVE::HTMLUtils::create_statusframe (undef, "Changes", undef, $hdiff); } print OUT $out; -]