#!/usr/bin/perl #------------------------------------------------------- =head1 xCAT plugin package to handle xdsh Supported command: nodenetconn ipforward (internal command) =cut #------------------------------------------------------- package xCAT_plugin::vlan; BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; } use lib "$::XCATROOT/lib/perl"; use strict; use xCAT::Table; use xCAT::Utils; use xCAT::NetworkUtils; use xCAT::MsgUtils; use Getopt::Long; use xCAT::NodeRange; use Data::Dumper; use Text::Balanced qw(extract_bracketed); use xCAT::SwitchHandler; use Safe; my $evalcpt = new Safe; $evalcpt->share('&mknum'); $evalcpt->permit('require'); my %Switches = (); 1; #------------------------------------------------------- =head3 handled_commands Return list of commands handled by this plugin =cut #------------------------------------------------------- sub handled_commands { return { mkvlan => "vlan", chvlan => "vlan", rmvlan => "vlan", lsvlan => "vlan", chvlanports => "vlan", }; } #------------------------------------------------------- =head3 preprocess_request Preprocess the command =cut #------------------------------------------------------- sub preprocess_request { my $request = shift; my $callback = shift; my $sub_req = shift; my $command = $request->{command}->[0]; $::CALLBACK = $callback; #if already preprocessed, go straight to request if ((defined($request->{_xcatpreprocessed})) && ($request->{_xcatpreprocessed}->[0] == 1)) { return [$request]; } if ($command eq "mkvlan") { return preprocess_mkvlan($request, $callback, $sub_req); } elsif ($command eq "chvlanports") { return preprocess_chvlanports($request, $callback, $sub_req); } elsif ($command eq "chvlan") { return preprocess_chvlan($request, $callback, $sub_req); } elsif ($command eq "rmvlan") { return preprocess_rmvlan($request, $callback, $sub_req); } elsif ($command eq "lsvlan") { return preprocess_lsvlan($request, $callback, $sub_req); } else { my $rsp = {}; $rsp->{error}->[0] = "$command: unsupported command."; $callback->($rsp); return 1; } } sub preprocess_mkvlan { my $request = shift; my $callback = shift; my $sub_req = shift; my $command = $request->{command}->[0]; my $args = $request->{arg}; # parse the options @ARGV = (); if ($args) { @ARGV = @{$args}; } Getopt::Long::Configure("bundling"); Getopt::Long::Configure("no_pass_through"); my $nr; my $net; my $netmask; my $prefix; my $nic; if (!GetOptions( 'h|help' => \$::HELP, 'v|version' => \$::VERSION, 'n|nodes=s' => \$nr, 't|net=s' => \$net, 'm|mask=s' => \$netmask, 'p|prefix=s' => \$prefix, 'i|interface=s' => \$nic, )) { &mkvlan_usage($callback); return 1; } # display the usage if -h or --help is specified if ($::HELP) { &mkvlan_usage($callback); return 0; } # display the version statement if -v or --verison is specified if ($::VERSION) { my $rsp = {}; $rsp->{data}->[0] = xCAT::Utils->Version(); $callback->($rsp); return 0; } my $vlan_id = 0; if (@ARGV > 0) { $vlan_id = $ARGV[0]; } my @nodes = (); if ($nr) { #get nodes @nodes = noderange($nr); if (nodesmissed) { my $rsp = {}; $rsp->{data}->[0] = "Invalid nodes in noderange:" . join(',', nodesmissed); $callback->($rsp); return 1; } } else { my $rsp = {}; $rsp->{data}->[0] = "Please specify a list of nodes to be added to the new vlan."; $callback->($rsp); return 1; } if ($net && (!$netmask)) { my $rsp = {}; $rsp->{data}->[0] = "Please specify a netmask for the vlan."; $callback->($rsp); return 1; } if ($netmask && (!$net)) { my $rsp = {}; $rsp->{data}->[0] = "Please specify a network address for the vlan."; $callback->($rsp); return 1; } #let process _request to handle it my $reqcopy = {%$request}; $reqcopy->{_xcatpreprocessed}->[0] = 1; $reqcopy->{vlanid}->[0] = $vlan_id; $reqcopy->{node} = \@nodes; if ($net) { $reqcopy->{net}->[0] = $net; } if ($netmask) { $reqcopy->{netmask}->[0] = $netmask; } if ($prefix) { $reqcopy->{prefix}->[0] = $prefix; } if ($nic) { $reqcopy->{nic}->[0] = $nic; } return [$reqcopy]; } # TODO: finish this sub preprocess_chvlanports { my $request = shift; my $callback = shift; my $sub_req = shift; my $command = $request->{command}->[0]; my $args = $request->{arg}; # parse the options @ARGV = (); if ($args) { @ARGV = @{$args}; } Getopt::Long::Configure("bundling"); Getopt::Long::Configure("no_pass_through"); my $nr; my $delete; my $nic; if (!GetOptions( 'h|help' => \$::HELP, 'v|version' => \$::VERSION, 'n|nodes=s' => \$nr, 'd|delete' => \$delete, 'i|interface=s' => \$nic, )) { &chvlanports_usage($callback); return 1; } # display the usage if -h or --help is specified if ($::HELP) { &chvlanports_usage($callback); return 0; } # display the version statement if -v or --verison is specified if ($::VERSION) { my $rsp = {}; $rsp->{data}->[0] = xCAT::Utils->Version(); $callback->($rsp); return 0; } if (@ARGV == 0) { my $rsp = {}; $rsp->{data}->[0] = "Please specify a vlan id."; $callback->($rsp); return 1; } my $vlan_id = $ARGV[0]; my @nodes = (); if (!$nr) { my $rsp = {}; $rsp->{data}->[0] = "Please specify a range of nodes to be added to vlan $vlan_id."; $callback->($rsp); return 1; } else { #get nodes @nodes = noderange($nr); if (nodesmissed) { my $rsp = {}; $rsp->{data}->[0] = "Invalid nodes in noderange:" . join(',', nodesmissed); $callback->($rsp); return 1; } } if (!$nic) { my $rsp = {}; $rsp->{data}->[0] = "Please specify a network interface for nodes"; $callback->($rsp); return 1; } #let process _request to handle it my $reqcopy = {%$request}; $reqcopy->{_xcatpreprocessed}->[0] = 1; $reqcopy->{vlanid}->[0] = $vlan_id; $reqcopy->{node} = \@nodes; $reqcopy->{delete}->[0] = $delete; $reqcopy->{nic}->[0] = $nic; return [$reqcopy]; } sub preprocess_chvlan { my $request = shift; my $callback = shift; my $sub_req = shift; my $command = $request->{command}->[0]; my $args = $request->{arg}; # parse the options @ARGV = (); if ($args) { @ARGV = @{$args}; } Getopt::Long::Configure("bundling"); Getopt::Long::Configure("no_pass_through"); my $nr; my $delete; my $nic; if (!GetOptions( 'h|help' => \$::HELP, 'v|version' => \$::VERSION, 'n|nodes=s' => \$nr, 'd|delete' => \$delete, 'i|interface=s' => \$nic, )) { &chvlan_usage($callback); return 1; } # display the usage if -h or --help is specified if ($::HELP) { &chvlan_usage($callback); return 0; } # display the version statement if -v or --verison is specified if ($::VERSION) { my $rsp = {}; $rsp->{data}->[0] = xCAT::Utils->Version(); $callback->($rsp); return 0; } if (@ARGV == 0) { my $rsp = {}; $rsp->{data}->[0] = "Please specify a vlan id."; $callback->($rsp); return 1; } my $vlan_id = $ARGV[0]; my @nodes = (); if (!$nr) { my $rsp = {}; $rsp->{data}->[0] = "Please specify a range of nodes to be added to vlan $vlan_id."; $callback->($rsp); return 1; } else { #get nodes @nodes = noderange($nr); if (nodesmissed) { my $rsp = {}; $rsp->{data}->[0] = "Invalid nodes in noderange:" . join(',', nodesmissed); $callback->($rsp); return 1; } } #let process _request to handle it my $reqcopy = {%$request}; $reqcopy->{_xcatpreprocessed}->[0] = 1; $reqcopy->{vlanid}->[0] = $vlan_id; $reqcopy->{node} = \@nodes; $reqcopy->{delete}->[0] = $delete; if ($nic) { $reqcopy->{nic}->[0] = $nic; } return [$reqcopy]; } sub preprocess_rmvlan { my $request = shift; my $callback = shift; my $sub_req = shift; my $command = $request->{command}->[0]; my $args = $request->{arg}; # parse the options @ARGV = (); if ($args) { @ARGV = @{$args}; } Getopt::Long::Configure("bundling"); Getopt::Long::Configure("no_pass_through"); my $nodes; if (!GetOptions( 'h|help' => \$::HELP, 'v|version' => \$::VERSION, )) { &rmvlan_usage($callback); return 1; } # display the usage if -h or --help is specified if ($::HELP) { &rmvlan_usage($callback); return 0; } # display the version statement if -v or --verison is specified if ($::VERSION) { my $rsp = {}; $rsp->{data}->[0] = xCAT::Utils->Version(); $callback->($rsp); return 0; } if (@ARGV == 0) { my $rsp = {}; $rsp->{data}->[0] = "Please specify a vlan id."; $callback->($rsp); return 1; } my $vlan_id = $ARGV[0]; #let process _request to handle it my $reqcopy = {%$request}; $reqcopy->{_xcatpreprocessed}->[0] = 1; $reqcopy->{vlanid}->[0] = $vlan_id; return [$reqcopy]; } sub preprocess_lsvlan { my $request = shift; my $callback = shift; my $sub_req = shift; my $command = $request->{command}->[0]; my $args = $request->{arg}; # parse the options @ARGV = (); if ($args) { @ARGV = @{$args}; } Getopt::Long::Configure("bundling"); Getopt::Long::Configure("no_pass_through"); my $nodes; if (!GetOptions( 'h|help' => \$::HELP, 'v|version' => \$::VERSION, )) { &lsvlan_usage($callback); return 1; } # display the usage if -h or --help is specified if ($::HELP) { &lsvlan_usage($callback); return 0; } # display the version statement if -v or --verison is specified if ($::VERSION) { my $rsp = {}; $rsp->{data}->[0] = xCAT::Utils->Version(); $callback->($rsp); return 0; } my $vlan_id = 0; if (@ARGV > 0) { $vlan_id = $ARGV[0]; } #let process _request to handle it my $reqcopy = {%$request}; $reqcopy->{_xcatpreprocessed}->[0] = 1; $reqcopy->{vlanid}->[0] = $vlan_id; return [$reqcopy]; } #------------------------------------------------------- =head3 process_request Process the command =cut #------------------------------------------------------- sub process_request { my $request = shift; my $callback = shift; my $sub_req = shift; my $command = $request->{command}->[0]; my $args = $request->{arg}; $::CALLBACK = $callback; if ($command eq "mkvlan") { return process_mkvlan($request, $callback, $sub_req); } elsif ($command eq "chvlanports") { return process_chvlanports($request, $callback, $sub_req); } elsif ($command eq "chvlan") { return process_chvlan($request, $callback, $sub_req); } elsif ($command eq "rmvlan") { return process_rmvlan($request, $callback, $sub_req); } elsif ($command eq "lsvlan") { return process_lsvlan($request, $callback, $sub_req); } else { my $rsp = {}; $rsp->{error}->[0] = "$command: unsupported command."; $callback->($rsp); return 1; } return 0; } sub process_mkvlan { my $request = shift; my $callback = shift; my $sub_req = shift; my $vlan_id = 0; if (exists($request->{vlanid})) { $vlan_id = $request->{vlanid}->[0]; } my @nodes = (); if (exists($request->{node})) { @nodes = @{ $request->{node} }; } my $net; if (exists($request->{net})) { $net = $request->{net}->[0]; } my $netmask; if (exists($request->{netmask})) { $netmask = $request->{netmask}->[0]; } my $prefix; if (exists($request->{prefix})) { $prefix = $request->{prefix}->[0]; } my $nic; if (exists($request->{nic})) { $nic = $request->{nic}->[0]; } #check if the vlan is already defined on the table my $nwtab = xCAT::Table->new("networks", -create => 1); if ($vlan_id > 0) { if ($nwtab) { my @tmp1 = $nwtab->getAllAttribs(('vlanid', 'net', 'mask')); if ((@tmp1) && (@tmp1 > 0)) { foreach (@tmp1) { if ($vlan_id eq $_->{vlanid}) { my $rsp = {}; $rsp->{error}->[0] = "The vlan $vlan_id is already used. Please choose another vlan id."; $callback->($rsp); return 1; } } } } } #make sure the vlan id is not currently used by another vlan #if no vlan id is supplied, automatically generate a new vlan id if ($vlan_id > 0) { if (!verify_vlanid($vlan_id, $callback)) { return 1; } } else { $vlan_id = get_next_vlanid($callback); if (!$vlan_id) { return 1; } } if (!$prefix) { $prefix = "v" . $vlan_id . "n"; #default } ####now get the switch ports for the nodes my %swinfo = (); # example: %swinfo=( switch1=>{ 23=>{ nodes: [node1] # interface: eth0 # }, # 24=>{ nodes: [node2,node3], # vmhost: kvm1 # interface: eth1 # } # }, # switch12=>{ 3=>{ nodes: [node4]}, # 7=>{ nodes: [node4] # interface: eth0 }, # 9=>{ nodes: [node6]} # } # ) my %vminfo = (); # example: %vminfo=( kvm1=>{clients:[node1,node2...]}, # kvm2=>{clients:[node2,node3...]}, # ) # my @vmnodes = (); # vm clients my @snodes = (); # stand alone nodes #get the vm hosts my $vmtab = xCAT::Table->new("vm", -create => 1); my $vmtmphash = $vmtab->getNodesAttribs(\@nodes, [ 'host', 'nics' ]); foreach my $node (@nodes) { my $host; my $ent = $vmtmphash->{$node}->[0]; if (ref($ent) and defined $ent->{host}) { $host = $ent->{host}; if (exists($vminfo{$host})) { my $pa = $vminfo{$host}->{clients}; push(@$pa, $node); } else { $vminfo{$host}->{clients} = [$node]; } push(@vmnodes, $node); } } if (@vmnodes > 0) { foreach my $node (@nodes) { if (!grep /^$node$/, @vmnodes) { push(@snodes, $node); } } } else { @snodes = @nodes; } #get the switch and port numbers for each node my @vmhosts = keys(%vminfo); my @anodes = (@snodes, @vmhosts); #nodes that connects to the switch my %swsetup = (); my $swtab = xCAT::Table->new("switch", -create => 1); my $swtmphash = $swtab->getNodesAttribs(\@anodes, [ 'switch', 'port', 'vlan', 'interface' ]); my @missed_nodes = (); foreach my $node (@anodes) { my $node_enties = $swtmphash->{$node}; if ($node_enties) { my $i = -1; my $use_this = 0; foreach my $ent (@$node_enties) { $i++; if (ref($ent) and defined $ent->{switch} and defined $ent->{port}) { my $switch; my $port; $switch = $ent->{switch}; $port = $ent->{port}; my $interface = "primary"; if (defined $ent->{interface}) { $interface = $ent->{interface}; } # for primary nic, the interface can be empty, "primary" or "primary:eth0" #print "***nic=$nic, interface=$interface\n"; if ($nic) { if ($interface =~ /primary/) { $interface =~ s/primary(:)?//g; } if ($interface && ($interface eq $nic)) { $use_this = 1; } } else { if ($interface =~ /primary/) { $use_this = 1; } } if (!$use_this) { next; } else { $swsetup{$node}->{port} = $port; $swsetup{$node}->{switch} = $switch; if (defined $ent->{vlan}) { $swsetup{$node}->{vlan} = $ent->{vlan}; } else { $swsetup{$node}->{vlan} = ""; } } if ($interface) { $swinfo{$switch}->{$port}->{interface} = $interface; } if (exists($vminfo{$node})) { $swinfo{$switch}->{$port}->{vmhost} = $node; $swinfo{$switch}->{$port}->{nodes} = $vminfo{$node}->{clients}; } else { $swinfo{$switch}->{$port}->{nodes} = [$node]; } last; } } if ($use_this != 1) { push(@missed_nodes, $node); } } } if (@missed_nodes > 0) { my $rsp = {}; $rsp->{error}->[0] = "Cannot proceed, please define switch and port info on the switch table for the following nodes:\n @missed_nodes\n"; $callback->($rsp); return 1; } #print "vminfo=" . Dumper(%vminfo) . "\n"; #print "swinfo=" . Dumper(%swinfo) . "\n"; #print "snodes=" . Dumper(@snodes) . "\n"; #print "anodes=" . Dumper(@anodes) . "\n"; #print "vmnodes=" . Dumper(@vmnodes) . "\n"; #print "swtmphash" . Dumper($swtmphash). "\n"; ### verify the ports are not used by other vlans #if (!verify_switch_ports($vlan_id, \%swinfo, $callback)) { return 1;} ### now pick a network address for the vlan if (!$net) { ($net, $netmask) = get_subnet($vlan_id, $callback); } ### save the vlan on the networks table my %key_col = (netname => "vlan$vlan_id"); my %tb_cols = (vlanid => $vlan_id, net => $net, mask => $netmask); $nwtab->setAttribs(\%key_col, \%tb_cols); ### configure vlan on the switch if (!create_vlan($vlan_id, \%swinfo, $callback)) { return 1; } if (!add_ports($vlan_id, \%swinfo, $callback)) { return 1; } my @sws = keys(%swinfo); if (!add_crossover_ports($vlan_id, \@sws, $callback)) { return 1; } ### add the vlanid for the standalone nodes on the switch table ### append the vlan id for the vmhosts on the switch table add_vlan_to_switch_table(\%swsetup, $vlan_id); ### get node ip and vlan hostname from the hosts table. #If it is not defined, put the default into the host table my @allnodes = (@anodes, @vmnodes); if (!add_vlan_ip_host($net, $netmask, $prefix, 1, \@allnodes, $callback)) { return 1; } ### for vm nodes, add an additional nic on the vm.nics if (@vmnodes > 0) { my %setupnics = (); my $new_nic = "vl$vlan_id"; foreach my $node (@vmnodes) { my $ent = $vmtmphash->{$node}->[0]; my $nics; if (ref($ent) and defined $ent->{nics}) { $nics = $ent->{nics}; my @a = split(",", $nics); if (!grep(/^$new_nic$/, @a)) { $nics = "$nics,$new_nic"; } } else { $nics = $new_nic; } $setupnics{$node} = { nics => "$nics" }; } $vmtab->setNodesAttribs(\%setupnics); } ### populate the /etc/hosts and make the DNS server on the mn aware this change $::CALLBACK = $callback; my $res = xCAT::Utils->runxcmd({ command => ['makehosts'], }, $sub_req, 0, 1); my $rsp = {}; $rsp->{data}->[0] = "Running makehosts..."; $callback->($rsp); if ($res && (@$res > 0)) { $rsp = {}; $rsp->{data} = $res; $callback->($rsp); } $::CALLBACK = $callback; my $res = xCAT::Utils->runxcmd({ command => ['makedns'], }, $sub_req, 0, 1); my $rsp = {}; $rsp->{data}->[0] = "Running makedns..."; $callback->($rsp); if ($res && (@$res > 0)) { $rsp->{data} = $res; $callback->($rsp); } my $cmd = "service named restart"; my $rc = system $cmd; ### now go to the nodes to configure the vlan interface $::CALLBACK = $callback; my $args = [ "-P", "configvlan $vlan_id --keephostname" ]; my $res = xCAT::Utils->runxcmd({ command => ['updatenode'], node => \@snodes, arg => $args }, $sub_req, 0, 1); my $rsp = {}; $rsp->{data}->[0] = "Running updatenode..."; $callback->($rsp); if ($res && (@$res > 0)) { $rsp->{data} = $res; $callback->($rsp); } ### add configvlan postscripts to the postscripts table for the node # so that next time the node bootup, the vlan can get configured my @pnodes = (@snodes, @vmnodes); add_postscript($callback, \@pnodes); my $rsp = {}; $rsp->{data}->[0] = "The vlan is successfully configured. "; $rsp->{data}->[1] = " vlan id: $vlan_id"; $rsp->{data}->[2] = " vlan subnet: $net"; $rsp->{data}->[3] = " vlan netmask: $netmask"; #$rsp->{data}->[4]= " vlan dhcp server:"; #$rsp->{data}->[5]= " vlan dns server:"; #$rsp->{data}->[6]= " vlan gateway:"; $callback->($rsp); return 0; } #------------------------------------------------------- =head3 add_vlan_to_switch_table It adds the vlan id to the switch.vlan for the given nodes. =cut #------------------------------------------------------- sub add_vlan_to_switch_table { my $swsetup = shift; my $vlan_id = shift; my $swtab1 = xCAT::Table->new('switch', -create => 1, -autocommit => 0); foreach my $node (keys(%$swsetup)) { my %keyhash = (); my %updates = (); $keyhash{'node'} = $node; $keyhash{'switch'} = $swsetup->{$node}->{switch}; $keyhash{'port'} = $swsetup->{$node}->{port}; $updates{'vlan'} = $vlan_id; my $vlan; if ($swsetup->{$node}->{vlan}) { $vlan = $swsetup->{$node}->{vlan}; my @a = split(",", $vlan); if (!grep(/^$vlan_id$/, @a)) { $vlan = "$vlan,$vlan_id"; } $updates{'vlan'} = $vlan; } $swtab1->setAttribs(\%keyhash, \%updates); } $swtab1->commit; } sub remove_vlan_from_switch_table { my $swsetup = shift; my $vlan_id = shift; #remove the vlan id from the switch table for standalone nodes my $swtab1 = xCAT::Table->new('switch', -create => 1, -autocommit => 0); foreach my $node (keys(%$swsetup)) { my %keyhash = (); my %updates = (); $keyhash{'node'} = $node; $keyhash{'switch'} = $swsetup->{$node}->{switch}; $keyhash{'port'} = $swsetup->{$node}->{port}; $updates{'vlan'} = ""; if ($swsetup->{$node}->{vlan}) { my @a = split(',', $swsetup->{$node}->{vlan}); my @b = grep(!/^$vlan_id$/, @a); if (@b > 0) { $updates{'vlan'} = join(',', @b); } } $swtab1->setAttribs(\%keyhash, \%updates); } $swtab1->commit; } #------------------------------------------------------- =head3 add_postscript It adds the 'configvlan' postscript to the postscript table for the given nodes. =cut #------------------------------------------------------- sub add_postscript { my $callback = shift; my $anodes = shift; my $posttab = xCAT::Table->new("postscripts", -create => 1); if ($posttab) { (my $ref1) = $posttab->getAttribs({ node => 'xcatdefaults' }, ('postscripts', 'postbootscripts')); #if configvlan is in xcadefaults, then do nothing if ($ref1) { if ($ref1->{postscripts}) { my @a = split(/,/, $ref1->{postscripts}); if (grep(/^configvlan$/, @a)) { next; } } if ($ref1->{postbootscripts}) { my @a = split(/,/, $ref1->{postbootscripts}); if (grep(/^configvlan$/, @a)) { next; } } } #now check for each node my %setup_hash; my $postcache = $posttab->getNodesAttribs($anodes, [qw(postscripts postbootscripts)]); foreach my $node (@$anodes) { my $ref = $postcache->{$node}->[0]; if ($ref) { if (exists($ref->{postscripts})) { my @a = split(/,/, $ref->{postscripts}); if (grep(/^configvlan$/, @a)) { next; } } if (exists($ref->{postbootscripts})) { my $post = $ref->{postbootscripts}; my @old_a = split(',', $post); if (grep(/^configvlan$/, @old_a)) { next; } else { $setup_hash{$node} = { postbootscripts => "$post,configvlan" }; } } else { $setup_hash{$node} = { postbootscripts => "configvlan" }; } } else { $setup_hash{$node} = { postbootscripts => "configvlan" }; } } if (keys(%setup_hash) > 0) { $posttab->setNodesAttribs(\%setup_hash); } } return 0; } #------------------------------------------------------- =head3 add_vlan_ip_host It goes to the hosts.otherinterfaces to see if the vlan ip and hostname is defined. If not, it will add the default in the table. The default is vn =cut #------------------------------------------------------- sub add_vlan_ip_host { my $subnet = shift; my $netmask = shift; my $prefix = shift; my $node_number = shift; my $nodes = shift; my $callback = shift; my $hoststab = xCAT::Table->new('hosts'); my $hostscache = $hoststab->getNodesAttribs($nodes, [qw(node otherinterfaces)]); my %setup_hash; foreach my $node (@$nodes) { my $ref = $hostscache->{$node}->[0]; my $found = 0; my $otherinterfaces; if ($ref && exists($ref->{otherinterfaces})) { $otherinterfaces = $ref->{otherinterfaces}; my @itf_pairs = split(/,/, $otherinterfaces); foreach (@itf_pairs) { my ($name, $ip) = split(/:/, $_); if ($name =~ /^-/) { $name = $node . $name; } if (xCAT::NetworkUtils->ishostinsubnet($ip, $netmask, $subnet)) { $found = 1; } } } if (!$found) { my $hostname = $prefix . "$node_number"; my $ip = ""; if ($subnet =~ /\d+\.\d+\.\d+\.\d+/) { # ipv4 address $subnet =~ /([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/; my $netnum = ($1 << 24) + ($2 << 16) + ($3 << 8) + $4; my $ipnum = $netnum + $node_number; my @a = (); for (my $i = 3 ; $i > -1 ; $i--) { $a[$i] = $ipnum % 256; $ipnum = int($ipnum / 256); } $ip = "$a[0].$a[1].$a[2].$a[3]"; #print "ip=$ip\n"; } else { my $rsp = {}; $rsp->{error}->[0] = "Does not support IPV6 address yet."; $callback->($rsp); return 0; } $node_number++; if ($otherinterfaces) { $setup_hash{$node} = { otherinterfaces => "$hostname:$ip,$otherinterfaces" }; } else { $setup_hash{$node} = { otherinterfaces => "$hostname:$ip" }; } } } #foreach node if (keys(%setup_hash) > 0) { $hoststab->setNodesAttribs(\%setup_hash); } return 1; } #------------------------------------------------------- =head3 get_prefix_and_nodenumber It gets the prefix and max node number from the current nodes in the given vlan. =cut #------------------------------------------------------- sub get_prefix_and_nodenumber { my $vlan_id = shift; my $subnet = shift; my $netmask = shift; #get all the nodes that are in the vlan my $swtab = xCAT::Table->new("switch", -create => 0); my @nodes = (); my @tmp1 = $swtab->getAllAttribs(('node', 'vlan')); if ((@tmp1) && (@tmp1 > 0)) { foreach my $ent (@tmp1) { my @nodes_tmp = noderange($ent->{node}); foreach my $node (@nodes_tmp) { if ($ent->{vlan}) { my @a = split(",", $ent->{vlan}); if (grep(/^$vlan_id$/, @a)) { push(@nodes, $node); } } } } } #get all the vm clients if the node is a vm host my $vmtab = xCAT::Table->new("vm", -create => 0); my @vmnodes = (); if ($vmtab) { my @tmp1 = $vmtab->getAllAttribs('node', 'host'); if ((@tmp1) && (@tmp1 > 0)) { foreach (@tmp1) { my $host = $_->{host}; if (grep(/^$host$/, @nodes)) { my @nodes_tmp = noderange($_->{node}); foreach my $node (@nodes_tmp) { push(@vmnodes, $node); } } } } } @nodes = (@nodes, @vmnodes); #print "nodes=@nodes\n"; #now go to hosts table to get the prefix and max node number my $hoststab = xCAT::Table->new('hosts'); my $hostscache = $hoststab->getNodesAttribs(\@nodes, [qw(node otherinterfaces)]); my $max = 0; my $prefix; foreach my $node (@nodes) { my $ref = $hostscache->{$node}->[0]; my $otherinterfaces; if ($ref && exists($ref->{otherinterfaces})) { $otherinterfaces = $ref->{otherinterfaces}; my @itf_pairs = split(/,/, $otherinterfaces); my @itf_pairs2 = (); foreach (@itf_pairs) { my ($name, $ip) = split(/:/, $_); if (xCAT::NetworkUtils->ishostinsubnet($ip, $netmask, $subnet)) { $name =~ /^(.*)([\d]+)$/; if ($2 > $max) { $max = $2; } if (!$prefix) { $prefix = $1; } #print "name=$name\n, 1=$1, 2=$2\n"; } } } } #foreach node return ($prefix, $max); } #------------------------------------------------------- =head3 remove_vlan_ip_host It goes to the hosts.otherinterfaces to see if the vlan ip and hostname is defined. If it is, it will remove it. It also remove the entried in the /etc/hosts file =cut #------------------------------------------------------- sub remove_vlan_ip_host { my $subnet = shift; my $netmask = shift; my $nodes = shift; my $callback = shift; my $hoststab = xCAT::Table->new('hosts'); my $hostscache = $hoststab->getNodesAttribs($nodes, [qw(node otherinterfaces)]); my %setup_hash; foreach my $node (@$nodes) { my $ref = $hostscache->{$node}->[0]; my $otherinterfaces; if ($ref && exists($ref->{otherinterfaces})) { $otherinterfaces = $ref->{otherinterfaces}; my @itf_pairs = split(/,/, $otherinterfaces); my @itf_pairs2 = (); my $index = 0; foreach (@itf_pairs) { my ($name, $ip) = split(/:/, $_); if (!xCAT::NetworkUtils->ishostinsubnet($ip, $netmask, $subnet)) { $itf_pairs2[$index] = $_; $index++; } else { my $cmd = "sed -i /$ip/d /etc/hosts"; my $rc = system $cmd; } } if (@itf_pairs2 > 0) { my $new_intf = join(",", @itf_pairs2); $setup_hash{$node} = { otherinterfaces => $new_intf } } else { $setup_hash{$node} = { otherinterfaces => "" }; } } } #foreach node if (keys(%setup_hash) > 0) { $hoststab->setNodesAttribs(\%setup_hash); } return 1; } #------------------------------------------------------- =head3 verify_vlanid It goes to all the switches to make sure that the vlan id is not used by other vlans. =cut #------------------------------------------------------- sub verify_vlanid { my $vlan_id = shift; my $callback = shift; my $switchestab = xCAT::Table->new('switches', -create => 0); my @tmp1 = $switchestab->getAllAttribs(('switch')); if ((@tmp1) && (@tmp1 > 0)) { foreach (@tmp1) { my @switches_tmp = noderange($_->{switch}); if (@switches_tmp == 0) { push @switches_tmp, $_->{switch}; } #sometimes the switch name is not on the node list table. foreach my $switch (@switches_tmp) { my $swh; if (exists($Switches{$switch})) { $swh = $Switches{$switch}; } else { $swh = new xCAT::SwitchHandler->new($switch); $Switches{$switch} = $swh; } my @ids = $swh->get_vlan_ids(); print "ids=@ids\n"; foreach my $id (@ids) { if ($id == $vlan_id) { my $rsp = {}; $rsp->{error}->[0] = "The vlan id $vlan_id already exists on switch $switch. Please choose another vlan id.\n"; $callback->($rsp); return 0; } } } } } return 1; } #------------------------------------------------------- =head3 get_next_vlanid It automatically generates the vlan ID. It goes to all the switches, get the smallest common integer that is not used by any existing vlans. =cut #------------------------------------------------------- sub get_next_vlanid { my $callback = shift; my $switchestab = xCAT::Table->new('switches', -create => 0); my @tmp1 = $switchestab->getAllAttribs(('switch')); my %vlanids = (); if ((@tmp1) && (@tmp1 > 0)) { foreach (@tmp1) { my @switches_tmp = noderange($_->{switch}); if (@switches_tmp == 0) { push @switches_tmp, $_->{switch}; } #sometimes the switch name is not on the node list table. foreach my $switch (@switches_tmp) { my $swh; if (exists($Switches{$switch})) { $swh = $Switches{$switch}; } else { $swh = new xCAT::SwitchHandler->new($switch); $Switches{$switch} = $swh; } my @ids = $swh->get_vlan_ids(); foreach my $id (@ids) { $vlanids{$id} = 1; } } } } for (my $index = 2 ; $index < 255 ; $index++) { if (!exists($vlanids{$index})) { return $index; } } my $rsp = {}; $rsp->{data}->[0] = "No valid vlan ID can be used any more, Please remove unused vlans.\n"; $callback->($rsp); return 0; } #------------------------------------------------------- =head3 verify_switch_ports It checks if the switch ports to be configured are used by other vlans. =cut #------------------------------------------------------- sub verify_switch_ports { my $vlan_id = shift; my $swinfo = shift; my $callback = shift; my $ret = 1; foreach my $switch (keys %$swinfo) { my $porthash = $swinfo->{$switch}; my $swh; if (exists($Switches{$switch})) { $swh = $Switches{$switch}; } else { $swh = new xCAT::SwitchHandler->new($switch); $Switches{$switch} = $swh; } my $port_vlan_hash = $swh->get_vlanids_for_ports(keys(%$porthash)); print "port_vlan_hash=" . Dumper($port_vlan_hash) . "\n"; my @error_ports = (); foreach my $port (keys(%$port_vlan_hash)) { my $val = $port_vlan_hash->{$port}; foreach my $tmp_vid (@$val) { if (($tmp_vid != $vlan_id) && ($tmp_vid != 1) && ($tmp_vid ne 'NA')) { if (exists($porthash->{$port}->{vmhost})) { next; } #skip the vmhost, vmhost can have more than one vlans push(@error_ports, $port); last; } } } if (@error_ports > 0) { $ret = 0; my $error_str; foreach (@error_ports) { my @tmp = @{ $porthash->{$_}->{nodes} }; my $ids_tmp = $port_vlan_hash->{$_}; $error_str .= "$_: vlan-ids=@$ids_tmp nodes=@tmp\n"; } my $rsp = {}; $rsp->{error}->[0] = "The following ports on switch $switch are used by other vlans.\n$error_str"; $callback->($rsp); } } return $ret; } #------------------------------------------------------- =head3 create_vlan It goes to the switches and create a new vlan. Returns: 1 -- suggessful 0 -- fail =cut #------------------------------------------------------- sub create_vlan { my $vlan_id = shift; my $swinfo = shift; my $callback = shift; my $ret = 1; foreach my $switch (keys %$swinfo) { my $swh; if (exists($Switches{$switch})) { $swh = $Switches{$switch}; } else { $swh = new xCAT::SwitchHandler->new($switch); $Switches{$switch} = $swh; } #check if the vlan already exists on the switch my @ids = $swh->get_vlan_ids(); my $vlan_exists = 0; foreach my $id (@ids) { if ($id == $vlan_id) { $vlan_exists = 1; last; } } if (!$vlan_exists) { #create the vlan print "create vlan $vlan_id on switch $switch\n"; my @ret = $swh->create_vlan($vlan_id); if ($ret[0] != 0) { my $rsp = {}; $rsp->{error}->[0] = "create_vlan: $ret[1]"; $callback->($rsp); } } } return 1; } #------------------------------------------------------- =head3 add_ports It adds the ports to the vlan. Returns: 1 -- suggessful 0 -- fail =cut #------------------------------------------------------- sub add_ports { my $vlan_id = shift; my $swinfo = shift; my $callback = shift; my $portmode = shift; my $ret = 1; foreach my $switch (keys %$swinfo) { my $porthash = $swinfo->{$switch}; my $swh; if (exists($Switches{$switch})) { $swh = $Switches{$switch}; } else { $swh = new xCAT::SwitchHandler->new($switch); $Switches{$switch} = $swh; } my @ret = $swh->add_ports_to_vlan($vlan_id, $portmode, keys(%$porthash)); if ($ret[0] != 0) { my $rsp = {}; $rsp->{error}->[0] = "add_ports_to_vlan: $ret[1]"; $callback->($rsp); } } return 1; } #------------------------------------------------------- =head3 add_crossover_ports It enables the vlan on the cross-over links. Returns: 1 -- suggessful 0 -- fail =cut #------------------------------------------------------- sub add_crossover_ports { my $vlan_id = shift; my $psws = shift; my $callback = shift; #now make sure the links between the switches allows this vlan to go through my @sws = @$psws; print "sws=@sws\n"; if (@sws > 1) { foreach my $switch (@sws) { my $swh; if (exists($Switches{$switch})) { $swh = $Switches{$switch}; } else { $swh = new xCAT::SwitchHandler->new($switch); $Switches{$switch} = $swh; } my @sws_b = grep(!/^$switch$/, @sws); my @ret = $swh->add_crossover_ports_to_vlan($vlan_id, @sws_b); if ($ret[0] != 0) { my $rsp = {}; $rsp->{error}->[0] = "add_crossover_ports: $ret[1]"; $callback->($rsp); } else { if ($ret[1]) { my $rsp = {}; $rsp->{data}->[0] = "add_crossover_ports: $ret[1]"; $callback->($rsp); } } } } return 1; } #------------------------------------------------------- =head3 remove_ports It goes to the switches and create a new vlan. Returns: 1 -- suggessful 0 -- fail =cut #------------------------------------------------------- sub remove_ports { my $vlan_id = shift; my $swinfo = shift; my $callback = shift; my $novmhost = shift; my $ret = 1; foreach my $switch (keys %$swinfo) { my $porthash = $swinfo->{$switch}; my $swh; if (exists($Switches{$switch})) { $swh = $Switches{$switch}; } else { $swh = new xCAT::SwitchHandler->new($switch); $Switches{$switch} = $swh; } my @ports = (); if ($novmhost) { #skip the vm hosts for chvlan foreach my $port (keys(%$porthash)) { if (!exists($porthash->{$port}->{vmhost})) { push(@ports, $port); } } } else { @ports = keys(%$porthash); } my @ret = $swh->remove_ports_from_vlan($vlan_id, @ports); if ($ret[0] != 0) { my $rsp = {}; $rsp->{error}->[0] = "remove_ports_from_vlan: $ret[1]"; $callback->($rsp); } } return 1; } #------------------------------------------------------- =head3 get_subnet It gets the subnet address and netmask for the given vlan ID. The pattern is defined by "vlannets" and "vlanmask" on the site table. The default is "10.<$vlanid>.0.0"/"255.255.0.0". =cut #------------------------------------------------------- sub get_subnet { my $vlan_id = shift; my $callback = shift; my $net; my $mask; #get vlannets and vlanidmask from the site table my $vlannets = "|(\\d+)|10.(\$1+0).0.0|"; my $vlanmask = "255.255.0.0"; my $sitetab = xCAT::Table->new('site'); my $sent = $sitetab->getAttribs({ key => 'vlannets' }, 'value'); if ($sent and ($sent->{value})) { $vlannets = $sent->{value}; } $sent = $sitetab->getAttribs({ key => 'vlanmask' }, 'value'); if ($sent and ($sent->{value})) { $vlanmask = $sent->{value}; } $mask = $vlanmask; if ($vlannets =~ /^\/[^\/]*\/[^\/]*\/$/) { my $exp = substr($vlannets, 1); chop $exp; my @parts = split('/', $exp, 2); $net = $vlan_id; $net =~ s/$parts[0]/$parts[1]/; } elsif ($vlannets =~ /^\|.*\|.*\|$/) { my $exp = substr($vlannets, 1); chop $exp; my @parts = split('\|', $exp, 2); my $curr; my $next; my $prev; my $retval = $parts[1]; ($curr, $next, $prev) = extract_bracketed($retval, '()', qr/[^()]*/); unless ($curr) { #If there were no paramaters to save, treat this one like a plain regex undef $@; #extract_bracketed would have set $@ if it didn't return, undef $@ $retval = $vlan_id; $retval =~ s/$parts[0]/$parts[1]/; } while ($curr) { my $value = $vlan_id; $value =~ s/$parts[0]/$curr/; $value = $evalcpt->reval('use integer;' . $value); $retval = $prev . $value . $next; ($curr, $next, $prev) = extract_bracketed($retval, '()', qr/[^()]*/); } undef $@; $net = $vlan_id; $net =~ s/$parts[0]/$retval/; } return ($net, $mask); } # process_chvlanports only support physical nodes # bond not supported and multi nics are not supported sub process_chvlanports { my $request = shift; my $callback = shift; my $sub_req = shift; my $vlan_id = 0; my $nic = ""; my @nodes = (); my $delete = 0; # validate vlan id value. $vlan_id = $request->{vlanid}->[0]; #debug message. xCAT::MsgUtils->message('S', "vlanid: $vlan_id"); if ($vlan_id <= 0) { my $rsp = {}; $rsp->{error}->[0] = "Invalid vlan id: $vlan_id"; $callback->($rsp); return; } #Check if the vlan is defined in networks table. my $net = ""; my $netmask = ""; my $nwtab = xCAT::Table->new("networks", -create => 1); if ($nwtab) { my @nwentires = $nwtab->getAllAttribs(('vlanid', 'net', 'mask')); foreach (@nwentires) { if ($vlan_id eq $_->{vlanid}) { $net = $_->{net}; $netmask = $_->{mask}; } } } if ((!$net) || (!$netmask)) { my $rsp = {}; $rsp->{data}->[0] = "Can not find valid network/netmask definition from table networks for vlan $vlan_id."; $callback->($rsp); return 1; } $nic = $request->{nic}->[0]; #debug message. xCAT::MsgUtils->message('S', "nic is: $nic"); @nodes = @{ $request->{node} }; #debug message. my $nodesstr = Dumper(@nodes); xCAT::MsgUtils->message('S', "nodes are: $nodesstr"); $delete = $request->{delete}->[0]; #debug message. xCAT::MsgUtils->message('S', "delete flag: $delete"); ####now get the switch ports for the nodes my %swinfo = (); # example: %swinfo=( switch1=>{ 23=>{ nodes: [node1]}, # 24=>{ nodes: [node2]}, # }, # switch12=>{ 3=>{ nodes: [node4], # } # ) my %nodeswinfo = (); # example: %nodeswinfo=( # node1=>{eth0=>{switch => swith1, # port => 1, # vlanids => [2,3]}, # eth1=>{switch => switch1, # port => 2, # vlanids => [5]}} # ) my %swsetup = (); #get the switch and port numbers for each node my $swtab = xCAT::Table->new("switch", -create => 1); my $swtmphash = $swtab->getNodesAttribs(\@nodes, [ 'switch', 'port', 'vlan', 'interface' ]); foreach my $node (keys(%$swtmphash)) { my $node_enties = $swtmphash->{$node}; foreach my $ent (@$node_enties) { if (ref($ent) and defined $ent->{switch} and defined $ent->{port}) { my $switch = $ent->{switch}; my $port = $ent->{port}; my $interface = ""; if (defined $ent->{interface}) { $interface = $ent->{interface} } my $vlan = ""; if (defined $ent->{vlan}) { $vlan = $ent->{vlan} } if ($interface) { $nodeswinfo{$node}->{$interface}->{switch} = $switch; $nodeswinfo{$node}->{$interface}->{port} = $port; $nodeswinfo{$node}->{$interface}->{vlan} = $vlan; } } } } #debug message. my $nodesinfostr = Dumper(%nodeswinfo); xCAT::MsgUtils->message('S', "nodeswinfo: $nodesinfostr"); # Validate node's switch info %nodeswinfo and build up %swinfo, %swsetup. my @missed_switch_nodes = (); my @missed_vlanid_nodes = (); foreach my $node (@nodes) { # Check whether the switch, port and interface info defined for all nodes. if (defined $nodeswinfo{$node} && defined $nodeswinfo{$node}->{$nic}) { } else { push(@missed_switch_nodes, $node); next; } if ($delete) { # For delete mode, must make sure all node's has such a vlan ID defined for the interface. my @vlanids = split(",", $nodeswinfo{$node}->{$nic}->{vlan}); if (@vlanids && (grep /^$vlan_id$/, @vlanids)) { my $switch = $nodeswinfo{$node}->{$nic}->{switch}; my $port = $nodeswinfo{$node}->{$nic}->{port}; #setup swinfo and swsetup . $swinfo{$switch}->{$port}->{hosts} = [$node]; $swsetup{$node}->{switch} = $switch; $swsetup{$node}->{port} = $port; $swsetup{$node}->{vlan} = $nodeswinfo{$node}->{$nic}->{vlan}; } else { push(@missed_vlanid_nodes, $node); next; } } else { # non-delete mode, just setup swinfo directly. my $switch = $nodeswinfo{$node}->{$nic}->{switch}; my $port = $nodeswinfo{$node}->{$nic}->{port}; #setup swinfo and swsetup for add_ports, add_crossover_ports and add_vlan_to_switch_table call later. $swinfo{$switch}->{$port}->{hosts} = [$node]; $swsetup{$node}->{switch} = $switch; $swsetup{$node}->{port} = $port; $swsetup{$node}->{vlan} = $nodeswinfo{$node}->{$nic}->{vlan}; } } if (@missed_switch_nodes > 0) { my $rsp = {}; $rsp->{error}->[0] = "Cannot proceed, please define switch, port and interface info on the switch table for the following nodes:\n @missed_switch_nodes\n"; $callback->($rsp); return 1; } if (@missed_vlanid_nodes > 0) { my $rsp = {}; $rsp->{error}->[0] = "Cannot proceed, no such vlan ID $vlan_id defined for following nodes:\n @missed_vlanid_nodes\n"; $callback->($rsp); return 1; } #debug message. my $swinfostr = Dumper(%swinfo); xCAT::MsgUtils->message('S', "swinfo: $swinfostr"); my $swsetupstr = Dumper(%swsetup); xCAT::MsgUtils->message('S', "swsetup: $swsetupstr"); # Do actual configurations on switches if (!$delete) { ### add ports to the vlan if (!add_ports($vlan_id, \%swinfo, $callback, 1)) { return 1; } xCAT::MsgUtils->message('S', "Adding ports to switch success!"); ### add the cross-over ports to the vlan my @sws = keys(%swinfo); #get all the switches that are in the vlan my $swtab = xCAT::Table->new("switch", -create => 0); if ($swtab) { my @tmp1 = $swtab->getAllAttribs('switch', 'vlan'); if ((@tmp1) && (@tmp1 > 0)) { foreach my $item (@tmp1) { my $vlan = $item->{vlan}; my $sw = $item->{switch}; if ($vlan) { my @a = split(",", $vlan); if (grep(/^$vlan_id$/, @a)) { if (!grep(/^$sw$/, @sws)) { push(@sws, $sw); } } } } } } if (!add_crossover_ports($vlan_id, \@sws, $callback)) { return 1; } xCAT::MsgUtils->message('S', "Configuring cross over ports success!"); #add the vlanid for the standalone nodes on the switch table #append the vlan id for the vmhosts on the switch table add_vlan_to_switch_table(\%swsetup, $vlan_id); xCAT::MsgUtils->message('S', "Adding vlan to switch table success!"); # done my $rsp = {}; $rsp->{data}->[0] = "The interface $nic of following nodes are added to the vlan $vlan_id:\n@nodes"; $callback->($rsp); } else { ### remove ports from the vlan my $novmhost = 1; if (!remove_ports($vlan_id, \%swinfo, $callback, $novmhost)) { return 1; } xCAT::MsgUtils->message('S', "Removing ports from vlan success!"); #remove the vlan id from the switch table. remove_vlan_from_switch_table(\%swsetup, $vlan_id); xCAT::MsgUtils->message('S', "Removing ports from switch table success!"); # done my $rsp = {}; $rsp->{data}->[0] = "The interface $nic of following nodes are removed from the vlan $vlan_id:\n@nodes"; $callback->($rsp); } return 0; } sub process_chvlan { my $request = shift; my $callback = shift; my $sub_req = shift; my $vlan_id = 0; if (exists($request->{vlanid})) { $vlan_id = $request->{vlanid}->[0]; } if ($vlan_id == 0) { my $rsp = {}; $rsp->{error}->[0] = "Invalid vlan id: $vlan_id"; $callback->($rsp); return; } my $nic; if (exists($request->{nic})) { $nic = $request->{nic}->[0]; } my @nodes = (); if (exists($request->{node})) { @nodes = @{ $request->{node} }; } my $delete = 0; if (exists($request->{delete})) { $delete = $request->{delete}->[0]; } my $net; my $netmask; #check if the vlan is already defined on the table my $found = 0; my $nwtab = xCAT::Table->new("networks", -create => 1); if ($vlan_id > 0) { if ($nwtab) { my @tmp1 = $nwtab->getAllAttribs(('vlanid', 'net', 'mask')); if ((@tmp1) && (@tmp1 > 0)) { foreach (@tmp1) { if ($vlan_id eq $_->{vlanid}) { $found = 1; $net = $_->{net}; $netmask = $_->{mask}; } } } } } if (!$found) { my $rsp = {}; $rsp->{data}->[0] = "The vlan $vlan_id does not exist."; $callback->($rsp); return 1; } if ((!$net) || (!$netmask)) { my $rsp = {}; $rsp->{data}->[0] = "Please make sure subnet and netmask are specified on the networks table for vlan $vlan_id."; $callback->($rsp); return 1; } ####now get the switch ports for the nodes my %swinfo = (); # example: %swinfo=( switch1=>{ 23=>{ nodes: [node1]}, # 24=>{ nodes: [node2,node3], # vmhost: kvm1 # } # }, # switch12=>{ 3=>{ nodes: [node4]}, # 7=>{ nodes: [node5]}, # 9=>{ nodes: [node6]} # } # ) my %vminfo = (); # example: %vminfo=( kvm1=>{clients:[node1,node2...]}, # kvm2=>{clients:[node2,node3...]}, # ) # my @vmnodes = (); # vm clients my @snodes = (); # stand alone nodes #get the vm hosts my $vmtab = xCAT::Table->new("vm", -create => 1); my $vmtmphash = $vmtab->getNodesAttribs(\@nodes, [ 'host', 'nics' ]); foreach (@nodes) { my $node = $_; my $host; my $ent = $vmtmphash->{$node}->[0]; if (ref($ent) and defined $ent->{host}) { $host = $ent->{host}; if (exists($vminfo{$host})) { my $pa = $vminfo{$host}->{clients}; push(@$pa, $node); } else { $vminfo{$host}->{clients} = [$node]; } push(@vmnodes, $node); } } if (@vmnodes > 0) { foreach my $node (@nodes) { if (!grep /^$node$/, @vmnodes) { push(@snodes, $node); } } } else { @snodes = @nodes; } #get the switch and port numbers for each node my @vmhosts = keys(%vminfo); my @anodes = (@snodes, @vmhosts); #nodes that connects to the switch my %swsetup = (); my $swtab = xCAT::Table->new("switch", -create => 1); my $swtmphash = $swtab->getNodesAttribs(\@anodes, [ 'switch', 'port', 'vlan', 'interface' ]); my @missed_nodes = (); foreach my $node (@anodes) { my $switch; my $port; my $node_enties = $swtmphash->{$node}; if ($node_enties) { my $i = -1; my $use_this = 0; foreach my $ent (@$node_enties) { $i++; if (ref($ent) and defined $ent->{switch} and defined $ent->{port}) { $switch = $ent->{switch}; $port = $ent->{port}; my $interface = "primary"; if (defined $ent->{interface}) { $interface = $ent->{interface}; } # for primary nic, the interface can be empty, "primary" or "primary:eth0" if ($delete) { if (defined($ent->{vlan})) { my @a = split(',', $ent->{vlan}); if (grep(/^$vlan_id$/, @a)) { $use_this = 1; } } } else { if ($nic) { if ($interface =~ /primary/) { $interface =~ s/primary(:)?//g; } if ($interface && ($interface eq $nic)) { $use_this = 1; } } else { if ($interface =~ /primary/) { $use_this = 1; } } } if (!$use_this) { next; } else { $swsetup{$node}->{port} = $port; $swsetup{$node}->{switch} = $switch; if (defined $ent->{vlan}) { $swsetup{$node}->{vlan} = $ent->{vlan}; } else { $swsetup{$node}->{vlan} = ""; } } if ($interface) { $swinfo{$switch}->{$port}->{interface} = $interface; } if (exists($vminfo{$node})) { $swinfo{$switch}->{$port}->{vmhost} = $node; $swinfo{$switch}->{$port}->{nodes} = $vminfo{$node}->{clients}; } else { $swinfo{$switch}->{$port}->{nodes} = [$node]; } last; } } if ($use_this != 1) { push(@missed_nodes, $node); } } } if (@missed_nodes > 0) { my $rsp = {}; $rsp->{error}->[0] = "Cannot proceed, please define switch and port info on the switch table for the following nodes:\n @missed_nodes\n"; $callback->($rsp); return 1; } #print "vminfo=" . Dumper(%vminfo) . "\n"; #print "swinfo=" . Dumper(%swinfo) . "\n"; #print "anodes=" . Dumper(@anodes) . "\n"; #print "vmnodes=" . Dumper(@vmnodes) . "\n"; if (!$delete) { ### verify the ports are not used by other vlans #if (!verify_switch_ports($vlan_id, \%swinfo, $callback)) { return 1;} ###create the vlan if it does not exist if (!create_vlan($vlan_id, \%swinfo, $callback)) { return 1; } ### add ports to the vlan if (!add_ports($vlan_id, \%swinfo, $callback)) { return 1; } ### add the cross-over ports to the vlan my @sws = keys(%swinfo); #get all the switches that are in the vlan my $swtab = xCAT::Table->new("switch", -create => 0); if ($swtab) { my @tmp1 = $swtab->getAllAttribs('switch', 'vlan'); if ((@tmp1) && (@tmp1 > 0)) { foreach my $item (@tmp1) { my $vlan = $item->{vlan}; my $sw = $item->{switch}; if ($vlan) { my @a = split(",", $vlan); if (grep(/^$vlan_id$/, @a)) { if (!grep(/^$sw$/, @sws)) { push(@sws, $sw); } } } } } } if (!add_crossover_ports($vlan_id, \@sws, $callback)) { return 1; } #add the vlanid for the standalone nodes on the switch table #append the vlan id for the vmhosts on the switch table add_vlan_to_switch_table(\%swsetup, $vlan_id); #we'll derive the prefix and the node numbers from the existing #nodes on the vlan my ($prefix, $start_number) = get_prefix_and_nodenumber($vlan_id, $net, $netmask); ### get node ip and vlan hostname from the hosts table. #If it is not defined, put the default into the host table my @allnodes = (@anodes, @vmnodes); if (!add_vlan_ip_host($net, $netmask, $prefix, $start_number + 1, \@allnodes, $callback)) { return 1; } ### for vm nodes, add an additional nic on the vm.nics if (@vmnodes > 0) { my %setupnics = (); my $new_nic = "vl$vlan_id"; foreach my $node (@vmnodes) { my $ent = $vmtmphash->{$node}->[0]; my $nics; if (ref($ent) and defined $ent->{nics}) { $nics = $ent->{nics}; my @a = split(",", $nics); if (!grep(/^$new_nic$/, @a)) { $nics = "$nics,$new_nic"; } } else { $nics = $new_nic; } $setupnics{$node} = { nics => "$nics" }; } $vmtab->setNodesAttribs(\%setupnics); } ### populate the /etc/hosts and make the DNS server on the mn aware this change $::CALLBACK = $callback; my $res = xCAT::Utils->runxcmd({ command => ['makehosts'], }, $sub_req, 0, 1); my $rsp = {}; $rsp->{data}->[0] = "Running makehosts..."; $callback->($rsp); if ($res && (@$res > 0)) { $rsp = {}; $rsp->{data} = $res; $callback->($rsp); } $callback->($rsp); $::CALLBACK = $callback; my $res = xCAT::Utils->runxcmd({ command => ['makedns'], }, $sub_req, 0, 1); my $rsp = {}; $rsp->{data}->[0] = "Running makedns..."; if ($res && (@$res > 0)) { $callback->($rsp); $rsp->{data} = $res; $callback->($rsp); } my $cmd = "service named restart"; my $rc = system $cmd; ### now go to the nodes to configure the vlan interface $::CALLBACK = $callback; my $args = [ "-P", "configvlan $vlan_id --keephostname" ]; my $res = xCAT::Utils->runxcmd({ command => ['updatenode'], node => \@snodes, arg => $args }, $sub_req, 0, 1); my $rsp = {}; $rsp->{data}->[0] = "Running updatenode..."; if ($res && (@$res > 0)) { $callback->($rsp); $rsp->{data} = $res; $callback->($rsp); } ### add configvlan postscripts to the postscripts table for the node my @pnodes = (@snodes, @vmnodes); add_postscript($callback, \@pnodes); # done my $rsp = {}; $rsp->{data}->[0] = "The following nodes are added to the vlan $vlan_id:\n@nodes"; $callback->($rsp); } else { ### go to the nodes to de-configure the vlan interface if (@snodes > 0) { my $args = [ "-P", "deconfigvlan $vlan_id" ]; my $res = xCAT::Utils->runxcmd({ command => ['updatenode'], node => \@snodes, arg => $args }, $sub_req, 0, 1); my $rsp = {}; $rsp->{data}->[0] = "Running updatenode..."; if ($res && (@$res > 0)) { $callback->($rsp); $rsp->{data} = $res; $callback->($rsp); } } ### remove ports from the vlan my $novmhost = 1; if (!remove_ports($vlan_id, \%swinfo, $callback, $novmhost)) { return 1; } #remove the vlan id from the switch table for standalone nodes #cannot call this function because %swsetup contains vmhosts #remove_vlan_from_switch_table(\%swsetup,$vlan_id); #print "swsetup=". Dumper(%swsetup); my $swtab1 = xCAT::Table->new('switch', -create => 1, -autocommit => 0); foreach my $node (@snodes) { if (exists($swsetup{$node})) { my %keyhash = (); my %updates = (); $keyhash{'node'} = $node; $keyhash{'switch'} = $swsetup{$node}->{switch}; $keyhash{'port'} = $swsetup{$node}->{port}; $updates{'vlan'} = ""; if ($swsetup{$node}->{vlan}) { my @a = split(',', $swsetup{$node}->{vlan}); my @b = grep(!/^$vlan_id$/, @a); if (@b > 0) { $updates{'vlan'} = join(',', @b); } } $swtab1->setAttribs(\%keyhash, \%updates); } } $swtab1->commit; #remove the vlan from the vm.nic for vm clients if (@vmnodes > 0) { my %setupnics = (); my $new_nic = "vl$vlan_id"; foreach my $node (@vmnodes) { my $ent = $vmtmphash->{$node}->[0]; my $nics = ''; if (ref($ent) and defined $ent->{nics}) { $nics = $ent->{nics}; my @a = split(",", $nics); my @b = grep(!/^$new_nic$/, @a); if (@b > 0) { $nics = join(',', @b); } } $setupnics{$node} = { nics => "$nics" }; } $vmtab->setNodesAttribs(\%setupnics); } #remove the node's vlan hostname and the ip from the host table and /etc/hosts my @pnodes = (@snodes, @vmnodes); if (!remove_vlan_ip_host($net, $netmask, \@pnodes, $callback)) { return 1; } #refresh the DNS server my $res = xCAT::Utils->runxcmd({ command => ['makedns'], }, $sub_req, 0, 1); my $rsp = {}; $rsp->{data}->[0] = "Running makedns..."; if ($res && (@$res > 0)) { $callback->($rsp); $rsp->{data} = $res; $callback->($rsp); } my $cmd = "service named restart"; my $rc = system $cmd; # remove configvlan postscripts from the postscripts table for the node remove_postscript($callback, \@pnodes); # done my $rsp = {}; $rsp->{data}->[0] = "The following nodes are removed from the vlan $vlan_id:\n@nodes"; $callback->($rsp); } return 0; } sub process_rmvlan { my $request = shift; my $callback = shift; my $sub_req = shift; my $vlan_id = 0; if (exists($request->{vlanid})) { $vlan_id = $request->{vlanid}->[0]; } my @anodes = (); my %swportinfo = (); my %swsetup = (); my $swtab = xCAT::Table->new("switch", -create => 0); if ($swtab) { my @tmp1 = $swtab->getAllAttribs(('node', 'switch', 'port', 'vlan')); if ((@tmp1) && (@tmp1 > 0)) { foreach my $ent (@tmp1) { my @nodes_tmp = noderange($ent->{node}); foreach my $node (@nodes_tmp) { my $switch = $ent->{switch}; my $port = $ent->{port}; if ($ent->{vlan}) { my @a = split(",", $ent->{vlan}); if (grep(/^$vlan_id$/, @a)) { push(@anodes, $node); if (exists($swportinfo{$switch})) { my $pa = $swportinfo{$switch}; push(@$pa, $port); } else { $swportinfo{$switch} = [$port]; } $swsetup{$node}->{port} = $port; $swsetup{$node}->{switch} = $switch; $swsetup{$node}->{vlan} = $ent->{vlan}; } } } } } } my $switchestab = xCAT::Table->new('switches', -create => 0); if ($switchestab) { my @tmp1 = $switchestab->getAllAttribs(('switch')); if ((@tmp1) && (@tmp1 > 0)) { foreach (@tmp1) { my @switches_tmp = noderange($_->{switch}); if (@switches_tmp == 0) { push @switches_tmp, $_->{switch}; } #sometimes the switch name is not on the node list table. foreach my $switch (@switches_tmp) { my $ports = []; if (exists($swportinfo{$switch})) { $ports = $swportinfo{$switch}; } my $swh; if (exists($Switches{$switch})) { $swh = $Switches{$switch}; } else { $swh = new xCAT::SwitchHandler->new($switch); $Switches{$switch} = $swh; } print "switch=$switch, ports=@$ports\n"; if (@$ports > 0) { my @ret = $swh->remove_ports_from_vlan($vlan_id, @$ports); if ($ret[0] != 0) { my $rsp = {}; $rsp->{error}->[0] = "remove_ports_from_vlan: $ret[1]"; $callback->($rsp); } my @ret = $swh->remove_vlan($vlan_id); if ($ret[0] != 0) { my $rsp = {}; $rsp->{error}->[0] = "remove_vlan: $ret[1]"; $callback->($rsp); } } else { #check if the vlan exists on the switch my @ids = $swh->get_vlan_ids(); foreach my $id (@ids) { if ($id == $vlan_id) { #remove it if exists my @ret = $swh->remove_vlan($vlan_id); if ($ret[0] != 0) { my $rsp = {}; $rsp->{error}->[0] = "remove_vlan: $ret[1]"; $callback->($rsp); } last; } } } } } } } ### now go to the nodes to de-configure the vlan interface my $args = [ "-P", "deconfigvlan $vlan_id" ]; my $res = xCAT::Utils->runxcmd({ command => ['updatenode'], node => \@anodes, arg => $args }, $sub_req, 0, 1); my $rsp = {}; $rsp->{data}->[0] = "Running updatenode..."; if ($res && (@$res > 0)) { $callback->($rsp); $rsp->{data} = $res; $callback->($rsp); } #remove the vlan from the networks table my $nwtab = xCAT::Table->new("networks", -create => 1); my $sent = $nwtab->getAttribs({ vlanid => "$vlan_id" }, 'net', 'mask'); my $net; my $netmask; if ($sent and ($sent->{net})) { $net = $sent->{net}; $netmask = $sent->{mask}; } my %key_col = (vlanid => $vlan_id); $nwtab->delEntries(\%key_col); #remove the vlan from the switch table for standalone nodes and vm hosts remove_vlan_from_switch_table(\%swsetup, $vlan_id); #remove the vlan nic from vm.nics for the vm clients my @vmnodes = (); my %vmsetup = (); my $vmtab = xCAT::Table->new("vm", -create => 0); if ($vmtab) { my @tmp1 = $vmtab->getAllAttribs(('node', 'host', 'nics')); if ((@tmp1) && (@tmp1 > 0)) { foreach (@tmp1) { my @nodes_tmp = noderange($_->{node}); my $nics = $_->{nics}; my $new_nic = "vl$vlan_id"; if ($nics) { foreach my $node (@nodes_tmp) { my @a = split(",", $nics); if (grep(/^$new_nic$/, @a)) { push(@vmnodes, $node); my @b = grep(!/^$new_nic$/, @a); if (@b > 0) { $vmsetup{$node} = { nics => join(',', @b) }; } else { $vmsetup{$node} = { nics => '' }; } } } } } } if (keys(%vmsetup) > 0) { $vmtab->setNodesAttribs(\%vmsetup); } } #remove the node's vlan hostname and the ip from the host table and /etc/hosts my @allnodes = (@anodes, @vmnodes); if (!remove_vlan_ip_host($net, $netmask, \@allnodes, $callback)) { return 1; } #refresh the DNS server my $res = xCAT::Utils->runxcmd({ command => ['makedns'], }, $sub_req, 0, 1); my $rsp = {}; $rsp->{data}->[0] = "Running makedns..."; if ($res && (@$res > 0)) { $callback->($rsp); $rsp->{data} = $res; $callback->($rsp); } my $cmd = "service named restart"; my $rc = system $cmd; ### remove configvlan postscripts from the postscripts table for the node # note: if configvlan is in xcatdefaults, it will not get removed becase # it may affect other vlans #remove_postscript($callback, \@allnodes); --- will not remove for multi-vlan support } #------------------------------------------------------- =head3 remove_postscript It removes configvlan postscripts from the postscripts table for the node Note: if configvlan is in xcatdefaults, it will not get removed becase it may affect other vlans =cut #------------------------------------------------------- sub remove_postscript { my $callback = shift; my $anodes = shift; my $posttab = xCAT::Table->new("postscripts", -create => 0); if ($posttab) { my %setup_hash; my $postcache = $posttab->getNodesAttribs($anodes, [qw(postscripts postbootscripts)]); foreach my $node (@$anodes) { my $ref = $postcache->{$node}->[0]; if ($ref) { if (exists($ref->{postbootscripts})) { my $post = $ref->{postbootscripts}; my @old_a = split(',', $post); my @new_a = grep (!/^configvlan$/, @old_a); if (scalar(@new_a) != scalar(@old_a)) { #print "newa =@new_a\n"; $setup_hash{$node} = { postbootscripts => join(',', @new_a) }; } } if (exists($ref->{postscripts})) { my $post = $ref->{postscripts}; my @old_a = split(',', $post); my @new_a = grep (!/^configvlan$/, @old_a); if (scalar(@new_a) != scalar(@old_a)) { $setup_hash{$node} = { postscripts => join(',', @new_a) }; } } } } if (keys(%setup_hash) > 0) { $posttab->setNodesAttribs(\%setup_hash); } } } sub process_lsvlan { my $request = shift; my $callback = shift; my $sub_req = shift; my $vlan_id = 0; if (exists($request->{vlanid})) { $vlan_id = $request->{vlanid}->[0]; } my %vlans = (); #get all the vm clients if the node is a vm host my $nwtab = xCAT::Table->new("networks", -create => 0); if ($nwtab) { my @tmp1 = $nwtab->getAllAttribs('net', 'mask', 'vlanid'); if ((@tmp1) && (@tmp1 > 0)) { foreach (@tmp1) { if (exists($_->{vlanid})) { $vlans{ $_->{vlanid} }->{net} = $_->{net}; $vlans{ $_->{vlanid} }->{mask} = $_->{mask}; } } } } if ($vlan_id != 0 && !exists($vlans{$vlan_id})) { my $rsp = {}; $rsp->{data}->[0] = "the vlan $vlan_id is not defined for the cluster nodes."; $rsp->{errorcode} = -1; $callback->($rsp); return; } if ($vlan_id == 0) { #just show the existing vlan ids my $rsp = {}; my $index = 0; if (keys(%vlans) > 0) { foreach my $id (sort keys(%vlans)) { $rsp->{data}->[$index] = "vlan $id:\n subnet " . $vlans{$id}->{net} . "\n netmask " . $vlans{$id}->{mask} . "\n"; $index++; } } else { $rsp->{data}->[0] = "No vlans defined for the cluster nodes." } $callback->($rsp); } else { #shows the details #get all the nodes that are in the vlan my $swtab = xCAT::Table->new("switch", -create => 0); my @nodes = (); if ($swtab) { my @tmp1 = $swtab->getAllAttribs('node', 'vlan', 'interface'); if ((@tmp1) && (@tmp1 > 0)) { foreach my $grp (@tmp1) { my $vlan = $grp->{vlan}; my $nic = "primary"; if ($grp->{interface}) { $nic = $grp->{interface}; } my @nodes_tmp = noderange($grp->{node}); if ($vlan) { my @a = split(",", $vlan); if (grep(/^$vlan_id$/, @a)) { foreach my $node (@nodes_tmp) { push(@nodes, $node); $vlans{$vlan_id}->{node}->{$node}->{name} = $node; $vlans{$vlan_id}->{node}->{$node}->{interface} = $nic; } } } } } } #get all the vm clients if the node is a vm host my $vmtab = xCAT::Table->new("vm", -create => 0); my @vmnodes = (); if ($vmtab) { my @tmp1 = $vmtab->getAllAttribs('node', 'host', 'nics'); if ((@tmp1) && (@tmp1 > 0)) { my $new_nic = "vl$vlan_id"; foreach (@tmp1) { my $host = $_->{host}; my $nics = $_->{nics}; if ($nics) { my @a = split(",", $nics); if (grep(/^$new_nic$/, @a)) { my @nodes_tmp = noderange($_->{node}); foreach my $node (@nodes_tmp) { push(@vmnodes, $node); $vlans{$vlan_id}->{node}->{$node}->{name} = $node; $vlans{$vlan_id}->{node}->{$node}->{vmhost} = $host; } } } } } } @nodes = (@nodes, @vmnodes); #now go to hosts table to get the host name and ip on the vlan my $hoststab = xCAT::Table->new('hosts'); my $hostscache = $hoststab->getNodesAttribs(\@nodes, [qw(node otherinterfaces)]); my $max = 0; my $prefix; foreach my $node (@nodes) { my $ref = $hostscache->{$node}->[0]; my $otherinterfaces; if ($ref && exists($ref->{otherinterfaces})) { $otherinterfaces = $ref->{otherinterfaces}; my @itf_pairs = split(/,/, $otherinterfaces); my @itf_pairs2 = (); foreach (@itf_pairs) { my ($name, $ip) = split(/:/, $_); if (xCAT::NetworkUtils->ishostinsubnet($ip, $vlans{$vlan_id}->{mask}, $vlans{$vlan_id}->{net})) { $vlans{$vlan_id}->{node}->{$node}->{ip} = $ip; $vlans{$vlan_id}->{node}->{$node}->{vname} = $name; } } } } #foreach node my $rsp = {}; $rsp->{data}->[0] = "vlan $vlan_id"; $rsp->{data}->[1] = " subnet " . $vlans{$vlan_id}->{net}; $rsp->{data}->[2] = " netmask " . $vlans{$vlan_id}->{mask} . "\n"; my $node_hash = $vlans{$vlan_id}->{node}; #print Dumper($node_hash); if ($node_hash && keys(%$node_hash) > 0) { $rsp->{data}->[3] = " hostname\tip address\tnode \tvm host \tinterface"; my $index = 4; foreach (sort keys(%$node_hash)) { my $vname = $node_hash->{$_}->{vname}; if (!$vname) { $vname = " "; } my $ip = $node_hash->{$_}->{ip}; if (!$ip) { $ip = " "; } my $name = $node_hash->{$_}->{name}; if (!$name) { $name = " "; } my $host = $node_hash->{$_}->{vmhost}; if (!$host) { $host = " "; } my $nic = $node_hash->{$_}->{interface}; $rsp->{data}->[$index] = " $vname\t$ip\t$name\t$host\t$nic"; $index++; } } $callback->($rsp); } } sub mkvlan_usage { my $cb = shift; my $rsp = {}; $rsp->{data}->[0] = "Usage: mkvlan -h"; $rsp->{data}->[1] = " mkvlan -v"; $rsp->{data}->[2] = " mkvlan [vlanid] -n noderange [-t net -m mask] [-p node_prefix] [-i nic]"; $cb->($rsp); } sub rmvlan_usage { my $cb = shift; my $rsp = {}; $rsp->{data}->[0] = "Usage: rmvlan -h"; $rsp->{data}->[1] = " rmvlan -v"; $rsp->{data}->[2] = " rmvlan vlanid"; $cb->($rsp); } sub chvlanports_usage { my $cb = shift; my $rsp = {}; $rsp->{data}->[0] = "Usage: chvlanports -h"; $rsp->{data}->[1] = " chvlanports -v"; $rsp->{data}->[2] = " chvlanports vlanid -n noderange -i nic"; $rsp->{data}->[3] = " chvlanports vlanid -n noderange -i nic -d"; $cb->($rsp); } sub chvlan_usage { my $cb = shift; my $rsp = {}; $rsp->{data}->[0] = "Usage: chvlan -h"; $rsp->{data}->[1] = " chvlan -v"; $rsp->{data}->[2] = " chvlan vlanid -n noderange [-i nic]"; $rsp->{data}->[3] = " chvlan vlanid -n noderange -d"; $cb->($rsp); } sub lsvlan_usage { my $cb = shift; my $rsp = {}; $rsp->{data}->[0] = "Usage: lsvlan -h"; $rsp->{data}->[1] = " lsvlan -v"; $rsp->{data}->[2] = " lsvlan"; $rsp->{data}->[3] = " lsvlan vlanid"; $cb->($rsp); } #------------------------------------------------------- =head3 getNodeVlanConfData This function is called by Postage.pm to collect all the environmental variables for setting up a vlan for a given node. =cut #------------------------------------------------------- sub getNodeVlanConfData { my $node = shift; if ($node =~ /xCAT_plugin::vlan/) { $node = shift; } my @scriptd = (); my $swtab = xCAT::Table->new("switch", -create => 0); if ($swtab) { my $tmp_switch = $swtab->getNodesAttribs([$node], [ 'vlan', 'interface' ], prefetchcache => 1); #print Dumper($tmp_switch); if (defined($tmp_switch) && (exists($tmp_switch->{$node})) && (defined($tmp_switch->{$node}->[0]))) { my $tmp_node_array = $tmp_switch->{$node}; my $index = 0; foreach my $tmp (@$tmp_node_array) { if (exists($tmp->{vlan})) { my $nic = "primary"; if (exists($tmp->{interface})) { $nic = $tmp->{interface}; } my @vlanid_array = split(',', $tmp->{vlan}); foreach my $vlan (@vlanid_array) { $index++; push @scriptd, "VLANID_$index='" . $vlan . "'\n"; push @scriptd, "export VLANID_$index\n"; push @scriptd, "VLANNIC_$index='" . $nic . "'\n"; push @scriptd, "export VLANNIC_$index\n"; my @temp_data = getNodeVlanOtherConfData($node, $vlan, $index); @scriptd = (@scriptd, @temp_data); } } } if ($index > 0) { push @scriptd, "VLANMAXINDEX='" . $index . "'\n"; push @scriptd, "export VLANMAXINDEX\n"; } } else { my $vmtab = xCAT::Table->new("vm", -create => 0); if ($vmtab) { my $tmp1 = $vmtab->getNodeAttribs($node, [ 'nics', 'host' ], prefetchcache => 1); my $vlan; my $index = 0; if (defined($tmp1) && ($tmp1) && $tmp1->{nics}) { push @scriptd, "VMNODE='YES'\n"; push @scriptd, "export VMNODE\n"; push @scriptd, "VMNICS='" . $tmp1->{nics} . "'\n"; push @scriptd, "export VMNICS\n"; my @nics = split(',', $tmp1->{nics}); #get the vlan id and interface from the host my $host = $tmp1->{host}; #my $host_vlan_info=get_vm_host_vlan_info($host); my $nic_position = 0; foreach my $nic (@nics) { $nic_position++; if ($nic =~ /^vl([\d]+)$/) { $vlan = $1; $index++; push @scriptd, "VLANID_$index='" . $vlan . "'\n"; push @scriptd, "export VLANID_$index\n"; push @scriptd, "VLAN_VMNICPOS_$index='" . $nic_position . "'\n"; push @scriptd, "export VLAN_VMNICPOS_$index\n"; #if ($host_vlan_info && (exists($host_vlan_info->{$vlan}))) { # push @scriptd, "HOST_VLANNIC_$index='" . $host_vlan_info->{$vlan} . "'\n"; # push @scriptd, "export HOST_VLANNIC_$index\n"; #} my @temp_data = getNodeVlanOtherConfData($node, $vlan, $index); @scriptd = (@scriptd, @temp_data); } } #end foreach } if ($index > 0) { push @scriptd, "VLANMAXINDEX='" . $index . "'\n"; push @scriptd, "export VLANMAXINDEX\n"; } } } } return @scriptd; } sub getNodeVlanOtherConfData { my $node = shift; if ($node =~ /xCAT_plugin::vlan/) { $node = shift; } my $vlan = shift; my $index = shift; my @scriptd = (); my $nwtab = xCAT::Table->new("networks", -create => 0); if ($nwtab) { my $sent = $nwtab->getAttribs({ vlanid => "$vlan" }, 'net', 'mask'); my $subnet; my $netmask; if ($sent and ($sent->{net})) { $subnet = $sent->{net}; $netmask = $sent->{mask}; } if (($subnet) && ($netmask)) { my $hoststab = xCAT::Table->new("hosts", -create => 0); if ($hoststab) { my $tmp = $hoststab->getNodeAttribs($node, ['otherinterfaces'], prefetchcache => 1); if (defined($tmp) && ($tmp) && $tmp->{otherinterfaces}) { my $otherinterfaces = $tmp->{otherinterfaces}; my @itf_pairs = split(/,/, $otherinterfaces); foreach (@itf_pairs) { my ($name, $ip) = split(/:/, $_); if (xCAT::NetworkUtils->ishostinsubnet($ip, $netmask, $subnet)) { if ($name =~ /^-/) { $name = $node . $name; } push @scriptd, "VLANHOSTNAME_$index='" . $name . "'\n"; push @scriptd, "export VLANHOSTNAME_$index\n"; push @scriptd, "VLANIP_$index='" . $ip . "'\n"; push @scriptd, "export VLANIP_$index\n"; push @scriptd, "VLANSUBNET_$index='" . $subnet . "'\n"; push @scriptd, "export VLANSUBNET_$index\n"; push @scriptd, "VLANNETMASK_$index='" . $netmask . "'\n"; push @scriptd, "export VLANNETMASK_$index\n"; last; } } } } } } return @scriptd; } #------------------------------------------------------- =head3 get_vm_host_vlan_info This function returns a hash pointer that has the vlan id and the interface info for the given KVM host. A host can support more than one vlans for a interface. For example: {1=>"eth0", 2=>"eth0", 3=>"eth1" ...} =cut #------------------------------------------------------- sub get_vm_host_vlan_info { my $host = shift; my $host_vlan_info = {}; my $swtab = xCAT::Table->new("switch", -create => 0); if ($swtab) { my $tmp_switch = $swtab->getNodesAttribs([$host], [ 'vlan', 'interface' ], prefetchcache => 1); if (defined($tmp_switch) && (exists($tmp_switch->{$host}))) { my $tmp_node_array = $tmp_switch->{$host}; foreach my $tmp (@$tmp_node_array) { if (exists($tmp->{vlan})) { my $vlans = $tmp->{vlan}; my $nic = "primary"; if (exists($tmp->{interface})) { $nic = $tmp->{interface}; } foreach my $vlan (split(',', $vlans)) { $host_vlan_info->{$vlan} = $nic; } } } } return $host_vlan_info; } }