# Functions for setting up a virtual IP interface # setup_virt(&domain) # Bring up an interface for a domain, if the IP isn't already enabled sub setup_virt { &foreign_require("net", "net-lib.pl"); local @boot = &net::active_interfaces(); if (!$config{'iface_manual'} && !$_[0]->{'virtalready'}) { # Actually bring up &$first_print(&text('setup_virt', $_[0]->{'ip'})); local ($iface) = grep { $_->{'fullname'} eq $config{'iface'} } @boot; if (!$iface) { # Interface doesn't really exist! &$second_print(&text('setup_virtmissing', $config{'iface'})); return 0; } local $b; local $vmax = $config{'iface_base'} || int($net::min_virtual_number); foreach $b (@boot) { $vmax = $b->{'virtual'} if ($b->{'name'} eq $iface->{'name'} && $b->{'virtual'} > $vmax); } local $virt = { 'address' => $_[0]->{'ip'}, 'netmask' => $net::virtual_netmask || $iface->{'netmask'}, 'broadcast' => $net::virtual_netmask eq "255.255.255.255" ? $_[0]->{'ip'} : $iface->{'broadcast'}, 'name' => $iface->{'name'}, 'virtual' => $vmax+1, 'up' => 1, 'desc' => "Virtualmin server $_[0]->{'dom'}", }; $virt->{'fullname'} = $virt->{'name'}.":".$virt->{'virtual'}; &net::save_interface($virt); &net::activate_interface($virt); $_[0]->{'iface'} = $virt->{'fullname'}; &$second_print(&text('setup_virtdone', $_[0]->{'iface'})); } else { # Just guess the interface &$first_print(&text('setup_virt2', $_[0]->{'ip'})); local ($virt) = grep { $_->{'address'} eq $_[0]->{'ip'} } @boot; $_[0]->{'iface'} = $virt ? $virt->{'fullname'} : undef; if ($_[0]->{'iface'}) { &$second_print(&text('setup_virtdone2', $_[0]->{'iface'})); } else { &$second_print(&text('setup_virtnotdone', $_[0]->{'ip'})); } } } # delete_virt(&domain) # Take down the network interface for a domain sub delete_virt { if (!$config{'iface_manual'} && !$_[0]->{'virtalready'}) { &$first_print($text{'delete_virt'}); &foreign_require("net", "net-lib.pl"); local ($biface) = grep { $_->{'address'} eq $_[0]->{'ip'} } &net::boot_interfaces(); local ($aiface) = grep { $_->{'address'} eq $_[0]->{'ip'} } &net::active_interfaces(); if (!$biface) { &$second_print(&text('delete_noiface', $_[0]->{'iface'})); } elsif ($biface->{'virtual'} ne '') { &net::delete_interface($biface); &net::deactivate_interface($aiface) if ($aiface && $aiface->{'virtual'} ne ''); &$second_print($text{'setup_done'}); } else { &$second_print(&text('delete_novirt', $biface->{'fullname'})); } } delete($_[0]->{'iface'}); } # modify_virt(&domain, &old) # Change the virtual IP address for a domain sub modify_virt { if ($_[0]->{'ip'} ne $_[1]->{'ip'} && $_[0]->{'virt'} && !$config{'iface_manual'} && !$_[0]->{'virtalready'}) { &$first_print($text{'save_virt'}); &foreign_require("net", "net-lib.pl"); local ($biface) = grep { $_->{'address'} eq $_[1]->{'ip'} } &net::boot_interfaces(); local ($aiface) = grep { $_->{'address'} eq $_[1]->{'ip'} } &net::active_interfaces(); if ($biface && $aiface) { if ($biface->{'virtual'} ne '') { $biface->{'address'} = $_[0]->{'ip'}; &net::save_interface($biface); } if ($aiface->{'virtual'} ne '') { $aiface->{'address'} = $_[0]->{'ip'}; &net::activate_interface($aiface); } &$second_print($text{'setup_done'}); } else { &$second_print(&text('delete_novirt', $_[1]->{'iface'})); } } } # validate_virt(&domain) # Check for boot-time and active network interfaces sub validate_virt { local ($d) = @_; &foreign_require("net", "net-lib.pl"); return undef if ($config{'iface_manual'}); # manually setup if (!$_[0]->{'virtalready'}) { # Only check boot-time interface if added by Virtualmin local ($biface) = grep { $_->{'address'} eq $d->{'ip'} } &net::boot_interfaces(); return &text('validate_evirtb', $d->{'ip'}) if (!$biface); } local ($aiface) = grep { $_->{'address'} eq $d->{'ip'} } &net::active_interfaces(); return &text('validate_evirta', $d->{'ip'}) if (!$aiface); return undef; } # check_virt_clash(ip) # Returns the interface if some IP is already in use sub check_virt_clash { return undef if ($config{'iface_manual'}); # no clash for manual mode # Check active and boot-time interfaces &foreign_require("net", "net-lib.pl"); local @boot = &net::boot_interfaces(); local ($boot) = grep { $_->{'address'} eq $_[0] } @boot; local @active = &net::active_interfaces(); local ($active) = grep { $_->{'address'} eq $_[0] } @active; return 1 if ($active || $boot); # Do a quick ping test local $pingcmd = $gconfig{'os_type'} =~ /-linux$/ ? "ping -c 1 -t 1" : "ping"; local ($out, $timed_out) = &backquote_with_timeout( $pingcmd." ".$_[0]." 2>&1", 2, 1); return 1 if (!$timed_out && !$?); return 0; } # virtual_ip_input(&templates, [reseller]) # Returns HTML for selecting a virtual IP mode for a new server, or not sub virtual_ip_input { local ($tmpls, $resel) = @_; local $defip = &get_default_ip($resel); if ($config{'all_namevirtual'}) { # Always name-based, but on varying IP return &ui_textbox("ip", $defip, 20); } else { # An IP can be selected, perhaps private, shared or default local ($t, $anyalloc, $anychoose, $anyzone); if (&running_in_zone() || (defined(&running_in_vserver) && &running_in_vserver())) { # When running in a Solaris zone or VServer, you MUST select an # existing active IP, as they are controlled from the host. $anyzone = 1; } elsif (&can_use_feature("virt")) { # Check if private IPs are allocated or manual, if we are # allowed to choose them. foreach $t (@$tmpls) { local $tmpl = &get_template($t->{'id'}); if ($tmpl->{'ranges'} ne "none") { $anyalloc++; } else { $anychoose++; } } } local @opts = ( [ 0, &text('form_shared', $defip)."
" ] ); local @shared = &list_shared_ips(); if (@shared && &can_edit_sharedips()) { # Can select from extra shared list push(@opts, [ 3, $text{'form_shared2'}." ". &ui_select("sharedip", undef, [ map { [ $_ ] } @shared ])."
" ]); } if ($anyalloc) { # Can allocate push(@opts, [ 2, &text('form_alloc')."
" ]); } if ($anychoose) { # Can enter arbitrary IP push(@opts, [ 1, $text{'form_vip'}." ". &ui_textbox("ip", undef, 20)." (". &ui_checkbox("virtalready", 1, $text{'form_virtalready'}).")
" ]); } if ($anyzone) { # Can select an existing active IP &foreign_require("net", "net-lib.pl"); local @act = grep { $_->{'virtual'} ne '' } &net::active_interfaces(); if (@act) { push(@opts, [ 4, $text{'form_activeip'}." ". &ui_select("zoneip", undef, [ map { [ $_->{'address'} ] } @act ]) ]); } else { push(@opts, [ 4, $text{'form_activeip'}." ". &ui_textbox("zoneip", undef, 20) ]); } } return &ui_radio("virt", 0, \@opts); } } # parse_virtual_ip(&template, reseller) # Parses the virtual IP input field, and returns the IP to use and virt flag. # May call &error if the input is invalid. sub parse_virtual_ip { local ($tmpl, $resel) = @_; if ($config{'all_namevirtual'}) { # Make sure the IP *is* assigned &check_ipaddress($in{'ip'}) || &error($text{'setup_eip'}); if (!&check_virt_clash($in{'ip'})) { &error(&text('setup_evirtclash2')); } return ($in{'ip'}, 0, 1); } elsif ($in{'virt'} == 2) { # Automatic IP allocation chosen .. select one from either the # reseller's range, or the template if ($resel) { # Creating by or under a reseller .. use his range, if any local %acl = &get_reseller_acl($resel); if ($acl{'ranges'}) { local $ip = &free_ip_address(\%acl); $ip || &error(&text('setup_evirtalloc')); return ($ip, 1); } } $tmpl->{'ranges'} ne "none" || &error(&text('setup_evirttmpl')); local $ip = &free_ip_address($tmpl); $ip || &error(&text('setup_evirtalloc')); return ($ip, 1, 0); } elsif ($in{'virt'} == 1) { # Manual IP allocation chosen $tmpl->{'ranges'} eq "none" ||&error(&text('setup_evirttmpl2')); &check_ipaddress($in{'ip'}) || &error($text{'setup_eip'}); local $clash = &check_virt_clash($in{'ip'}); if ($in{'virtalready'}) { # Fail if the IP isn't yet active, or if claimed by another # virtual server $clash || &error(&text('setup_evirtclash2')); local $already = &get_domain_by("ip", $in{'ip'}); $already && &error(&text('setup_evirtclash4', $already->{'dom'})); } else { # Fail if the IP *is* already active $clash && &error(&text('setup_evirtclash')); } return ($in{'ip'}, 1, $in{'virtalready'}); } elsif ($in{'virt'} == 3 && &can_edit_sharedips()) { # On a shared virtual IP &indexof($in{'sharedip'}, &list_shared_ips()) >= 0 || &error(&text('setup_evirtnoshared')); return ($in{'sharedip'}, 0, 0); } elsif ($in{'virt'} == 4 && (&running_in_zone() || defined(&running_in_vserver) && &running_in_vserver())) { # On an active IP on a virtual machine that cannot bring up its # own IP. &check_ipadress($in{'zoneip'}) || &error($text{'setup_eip'}); local $clash = &check_virt_clash($in{'zoneip'}); $clash || &error(&text('setup_evirtclash2')); local $already = &get_domain_by("ip", $in{'ip'}); $already && &error(&text('setup_evirtclash4', $already->{'dom'})); return ($in{'zoneip'}, 1, 1); } else { # Global shared IP local $defip = &get_default_ip($resel); return ($defip, 0, 0); } } # show_template_virt(&tmpl) # Outputs HTML for editing virtual IP related template options sub show_template_virt { local ($tmpl) = @_; # IP allocation range @ranges = &parse_ip_ranges($tmpl->{'ranges'}) if ($tmpl->{'ranges'} ne "none"); local @rfields = map { ("ranges_start_".$_, "ranges_end_".$_) } (0..scalar(@ranges)+1); $rtable = &none_def_input("ranges", $tmpl->{'ranges'}, $text{'tmpl_rangesbelow'}, 0, 0, undef, \@rfields); $rtable .= &ui_columns_start([ $text{'tmpl_ranges_start'}, $text{'tmpl_ranges_end'} ]); $i = 0; foreach $r (@ranges, [ ], [ ]) { $rtable .= &ui_columns_row([ &ui_textbox("ranges_start_$i", $r->[0], 20), &ui_textbox("ranges_end_$i", $r->[1], 20), ]); $i++; } $rtable .= &ui_columns_end(); print &ui_table_row(&hlink($text{'tmpl_ranges'},"template_ranges_mode"), $rtable); } # parse_template_virt(&tmpl) # Updates virtual IP related template options from %in sub parse_template_virt { local ($tmpl) = @_; # Save IP allocation ranges if ($in{'ranges_mode'} == 0) { $tmpl->{'ranges'} = "none"; } elsif ($in{'ranges_mode'} == 1) { $tmpl->{'ranges'} = undef; } else { for($i=0; defined($start = $in{"ranges_start_$i"}); $i++) { next if (!$start); $end = $in{"ranges_end_$i"}; &check_ipaddress($start) || &error(&text('tmpl_eranges_start', $start)); &check_ipaddress($end) || &error(&text('tmpl_eranges_end', $start)); @start = split(/\./, $start); @end = split(/\./, $end); $start[0] == $end[0] && $start[1] == $end[1] && $start[2] == $end[2] || &error(&text('tmpl_eranges_net', $start)); $start[3] <= $end[3] || &error(&text('tmpl_eranges_lower', $start)); push(@ranges, [ $start, $end ]); } @ranges || &error($text{'tmpl_eranges'}); $tmpl->{'ranges'} = &join_ip_ranges(\@ranges); } } $done_feature_script{'virt'} = 1; 1;