[! 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 $bridge_avail; for (my $i = 0; $i < 1000; $i++) { if (!defined ($ifaces->{"vmbr$i"})) { push @$bridge_avail, "vmbr$i"; } } 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}); 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); if ($iface =~ m/^vmbr[0-9]{1,3}$/) { #$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"; } } } $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') { die "no more free bridges\n" if !$bridge_avail; $grid->add_row (__("Bridge name") . ':', $form->create_element ('iface', 'dropdown', undef, $bridge_avail)); } 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 .= "

" . $ddown->out_symbol("menu0", "iarrdown") . " " . __("Interface configuration") . "


"; foreach my $iface (sort keys %$ifaces) { next if $iface eq 'lo'; next if $ifaces->{$iface}->{type} !~ m/^(static|manual)$/; if ($iface =~ m/^eth(\d)$/) { next if device_is_used ($ifaces, $iface); } elsif ($iface =~ m/^vmbr[0-9]{1,3}$/) { } elsif ($iface =~ m/^bond[0-9]$/) { } else { next; } my $auto = $ifaces->{$iface}->{autostart} ? 'checked' : ''; my $active = $ifaces->{$iface}->{active} ? 'yes' : 'no'; my $menu = $ddown->out_symbol ("menu1","","&aa=$iface"); $table->set_row_link ("?edit=1&aa=$iface"); my $ports = ''; if ($ifaces->{$iface}->{'bridge_ports'}) { $ports = join (' ', sort keys %{$ifaces->{$iface}->{'bridge_ports'}}); } elsif ($ifaces->{$iface}->{'slaves'}) { $ports = join (' ', sort keys %{$ifaces->{$iface}->{'slaves'}}); } $table->add_row ('', $menu, $iface, $active, $ports, "", $ifaces->{$iface}->{address} , $ifaces->{$iface}->{netmask}, $ifaces->{$iface}->{gateway} || ' '); } $out .= $ddown->out_dropdown_menu("menu0"); $out .= $ddown->out_dropdown_menu("menu1"); $out .= $table->out_table(); if (defined ($fdat{viewchanges})) { $udat{viewchanges} = $fdat{viewchanges}; } if ($changes) { my $html = __("Network configuration changed.") . ' '; $html .= sprintf (__("Please reboot to activate changes."), "reboot.htm?state=confirm&m3=0"); if (!$udat{viewchanges}) { $html .= " (" . __("view changes") . ")" } else { $html .= " (" . __("hide changes") . ")" } print OUT PVE::HTMLUtils::create_noteframe (__("Attention"), $html); print OUT "
\n" } if ($udat{viewchanges} && $changes) { $out .= "
"; my $hdiff .= "
" . encode_entities ($changes) . "
"; $out .= PVE::HTMLUtils::create_statusframe (undef, "Changes", undef, $hdiff); } print OUT $out; -]