git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@3793 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
		
			
				
	
	
		
			539 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			539 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| # IBM(c) 2008 EPL license http://www.eclipse.org/legal/epl-v10.html
 | |
| # Ver. 2.1 (4) - sf@mauricebrinkmann.de
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head1
 | |
|   xCAT plugin package to handle VirtualBox machines
 | |
| 
 | |
|    Supported command:
 | |
|          rpower
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| package xCAT_plugin::vbox;
 | |
| require Sys::Hostname;
 | |
| require xCAT::Table;
 | |
| 
 | |
| require xCAT::Utils;
 | |
| 
 | |
| require xCAT::MsgUtils;
 | |
| use Getopt::Long;
 | |
| 1;
 | |
| 
 | |
| use strict;
 | |
| use warnings;
 | |
| 
 | |
| my $cmd = 'clienttest';
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  handled_commands
 | |
| 
 | |
| Return list of commands handled by this plugin
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| sub handled_commands
 | |
| {
 | |
|     return {rpower => 'nodehm:power,mgt'};
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  preprocess_request
 | |
| 
 | |
|   Check and setup for hierarchy 
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub preprocess_request
 | |
| {
 | |
|     my $req = shift;
 | |
|     my $cb  = shift;
 | |
|     if ($req->{_xcatpreprocessed}->[0] == 1) { return [$req]; }
 | |
|     #exit if preprocessed
 | |
|     my $nodes    = $req->{node};
 | |
|     my $service  = "xcat";
 | |
| 
 | |
|     # find service nodes for requested nodes
 | |
|     # build an individual request for each service node
 | |
|     my $sn = xCAT::Utils->get_ServiceNode($nodes, $service, "MN");
 | |
| 
 | |
| 	my @requests = ();
 | |
| 	
 | |
|     # build each request for each service node
 | |
| 
 | |
|     foreach my $snkey (keys %$sn)
 | |
|     {
 | |
| 	my $n=$sn->{$snkey};
 | |
| 	print "snkey=$snkey, nodes=@$n\n";
 | |
|             my $reqcopy = {%$req};
 | |
|             $reqcopy->{node} = $sn->{$snkey};
 | |
|             $reqcopy->{'_xcatdest'} = $snkey;
 | |
|             $reqcopy->{_xcatpreprocessed}->[0] = 1;
 | |
|             push @requests, $reqcopy;
 | |
| 
 | |
|     }
 | |
|     return \@requests;
 | |
| }
 | |
| 
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  process_request
 | |
| 
 | |
|   Process the command
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub process_request
 | |
| {
 | |
| 
 | |
|     my $request  = shift;
 | |
|     our $callback = shift;
 | |
|     my $nodes    = $request->{node};
 | |
|     my $command  = $request->{command}->[0];
 | |
|     my $args     = $request->{arg};
 | |
|     my $envs     = $request->{env};
 | |
| 	our $rsp;
 | |
| 	my $node;
 | |
| 
 | |
| 	my $soapsupport = eval { require SOAP::Lite; };
 | |
| 	unless ($soapsupport) { #Still no SOAP::Lite module
 | |
|       $callback->({error=>"SOAP::Lite perl module missing, unable to fulfill Virtual Box plugin requirements",errorcode=>[42]});
 | |
|       return [];
 | |
| 	}
 | |
| require xCAT::vboxService;
 | |
|    
 | |
|     my @nodes=@$nodes;
 | |
| 
 | |
|     PROCESSNODES: foreach $node (@nodes) ######################################
 | |
|     {
 | |
|         # Load node information from xCAT tables
 | |
|         my ($error, $url, $username, $password, $vmname, $vncport) = getInfo($node);
 | |
|         next PROCESSNODES           if ($error == 1);
 | |
|         last PROCESSNODES           if ($error == 2);
 | |
|         
 | |
|         # Create connection to VirtualBox web service and check for errors
 | |
|         my $vbox = getWebService($url, $username, $password, $node);
 | |
|         next PROCESSNODES           if (!$vbox);
 | |
|         
 | |
|         # Get the machine
 | |
|         my $machine = getMachine($url, $vbox, $vmname, $node);
 | |
|         if ($machine)
 | |
|         {
 | |
|                         
 | |
|             # Command differentiation #------------------------------------
 | |
|             
 | |
|             if ($command eq "rpower" and @$args > 0)
 | |
|             {
 | |
|                 powerCtrl($url, $vbox, $machine, $vmname, $node, $vncport, $args);
 | |
|             }
 | |
|             
 | |
|             #else # unrecognized command
 | |
|             #{
 | |
|             #    addError("$node: $command unsupported on vm");
 | |
|             #}
 | |
|             
 | |
|             # End Command differentiation #--------------------------------
 | |
| 
 | |
|         } # /existing $machine reference
 | |
|         
 | |
|         xCAT::vboxService->IWebsessionManager_logoff($url, $vbox);
 | |
|         
 | |
|     } # /for each $node in @nodes
 | |
|     continue
 | |
|     {
 | |
|         # print out status before waiting for others and others and ..
 | |
|         printMessages();
 | |
|         
 | |
|         # yield???
 | |
|     }
 | |
|     
 | |
|     # last chance to print remaining messages
 | |
|     printMessages();
 | |
|     
 | |
|     return;
 | |
|     
 | |
|     
 | |
|     sub addMessage ############################################################
 | |
|     {
 | |
|         push @{$rsp->{data}}, shift;
 | |
|         
 | |
|     } # /addMessage
 | |
| 	
 | |
| 	sub addError ##############################################################
 | |
|     {
 | |
|         push @{$rsp->{error}}, shift;
 | |
|         
 | |
|     } # /addError
 | |
|     
 | |
|     
 | |
|     sub printMessages #########################################################
 | |
|     {
 | |
|         if (defined($rsp->{data}) or defined($rsp->{error}))
 | |
|         {
 | |
| 			if (defined($rsp->{error}))
 | |
| 			{
 | |
| 				push @{$rsp->{errorcode}}, 1;
 | |
| 				xCAT::MsgUtils->message("E", $rsp, $callback, 0);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				xCAT::MsgUtils->message("I", $rsp, $callback, 0);
 | |
| 			}
 | |
|         }
 | |
|         
 | |
|     } # /printMessages
 | |
|     
 | |
|     
 | |
|     sub powerCtrl #############################################################
 | |
|     {
 | |
|         my  ($url, $vbox, $machine, $vmname, $node, $vncport, $args) =
 | |
|             (shift, shift, shift, shift, shift, shift, shift);
 | |
|         
 | |
|         # Get state of the machine first
 | |
|         my $vmstate = xCAT::vboxService->IMachine_getState($url, $machine);
 | |
|         
 | |
|         # Check if there is anything to do
 | |
|         if (!$vmstate)
 | |
|         {
 | |
|             addError("$node: Can not determine vm's state!");
 | |
|             return;
 | |
|         }
 | |
|         elsif ($vmstate =~ m/^PoweredOff|^Aborted|^Saved/)
 | |
|         { 
 | |
|             if ($args->[0] eq "off")
 | |
|             {
 | |
|                 addMessage("$node: off");
 | |
|                 return;
 | |
|             }
 | |
|             elsif ($args->[0] =~ m/^reset|^boot/)
 | |
|             {
 | |
|                 # if machine is off: turn on when reset was choosen
 | |
|                 $args->[0] = "on reset";
 | |
|             }
 | |
|             elsif ($args->[0] =~ m/^stat/)
 | |
|             {
 | |
|                 # addMessage("$node: off ($vmstate)");
 | |
| 				addMessage("$node: off");
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             if ($args->[0] eq "on")
 | |
|             {
 | |
|                 addMessage("$node: on");
 | |
|                 return;
 | |
|             }
 | |
|             elsif ($args->[0] =~ m/^stat/)
 | |
|             {
 | |
|                 # addMessage("$node: on ($vmstate)");
 | |
| 				addMessage("$node: on");
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         # Determine machine's UUID - Should work since we got the state
 | |
|         my $uuid = xCAT::vboxService->IMachine_getId($url, $machine);
 | |
| 
 | |
|         # Open Session
 | |
|         my $session = getSession($url, $vbox, $node, $vmname);
 | |
|         if ($session)
 | |
|         {
 | |
|             # Decide what to do
 | |
|             if ($args->[0] =~ m/^on/)
 | |
|             {
 | |
|                 powerOn($url, $vbox, $session, $uuid, $node, $vncport, $args);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 powerOff($url, $vbox, $session, $machine, $uuid, $node, $args);
 | |
|             }
 | |
|             
 | |
|             xCAT::vboxService->ISession_close($url, $session);
 | |
|         } # /existing $session
 | |
|         
 | |
|         return;
 | |
|         
 | |
|     
 | |
|         sub powerOn #**********************************************************
 | |
|         {
 | |
|             my  ($url, $vbox, $session, $uuid, $node, $vncport, $args) =
 | |
|                 (shift, shift, shift, shift, shift, shift, shift);
 | |
| 				
 | |
| 			##########################
 | |
| 			xCAT::vboxService->IVirtualBox_openSession(
 | |
|                     $url, $vbox, $session, $uuid);
 | |
| 					
 | |
| 			my $directmachine = xCAT::vboxService->ISession_getMachine($url, $session);
 | |
| 			if (!$directmachine)
 | |
| 			{
 | |
| 				addError("$node: Could not access mutable machine");
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				my $vrdp = xCAT::vboxService->IMachine_getVRDPServer(
 | |
| 							$url, $directmachine);
 | |
| 							
 | |
| 				my $portno = xCAT::vboxService->IVRDPServer_getPort($url, $vrdp);
 | |
| 				if ($vncport and ($vncport != $portno))
 | |
| 				{
 | |
| 					# If port is specified but different: Update machine settings
 | |
| 					xCAT::vboxService->IVRDPServer_setPort($url, $vrdp, $vncport);
 | |
| 					xCAT::vboxService->IMachine_saveSettings($url, $directmachine);
 | |
| 				} 
 | |
| 				elsif (!$vncport)
 | |
| 				{
 | |
| 					# If not specified yet: Save port to xCAT db
 | |
| 					$vncport = $portno;
 | |
| 					my $vmTab = openTable('vm');
 | |
| 					$vmTab->setNodeAttribs($node,{vncport=>$vncport});
 | |
| 					$vmTab->close();
 | |
| 				}
 | |
| 				xCAT::vboxService->IManagedObjectRef_release($url, $vrdp);
 | |
| 				xCAT::vboxService->IManagedObjectRef_release($url, $directmachine);
 | |
| 			}
 | |
| 			
 | |
| 			xCAT::vboxService->ISession_close($url, $session);
 | |
| 			##############################
 | |
|             
 | |
|             my $progress =  xCAT::vboxService->IVirtualBox_openRemoteSession(
 | |
|                             $url, $vbox, $session, $uuid, "vrdp", "");
 | |
|                             
 | |
|             if (!$progress)
 | |
|             {
 | |
|                 addError("$node: Can not open remote session");
 | |
|             }
 | |
|             else # existing $progress
 | |
|             {
 | |
|                 xCAT::vboxService->IProgress_waitForCompletion(
 | |
|                     $url, $progress, -1);
 | |
|             
 | |
|                 if (xCAT::vboxService->IProgress_getCompleted(
 | |
|                     $url, $progress))
 | |
|                 {
 | |
|                     addMessage("$node: $args->[0]");
 | |
|                 }
 | |
|                 else # not successfully completed
 | |
|                 {
 | |
|                     addError("$node: Power on failed");
 | |
|                 }
 | |
|                 
 | |
|             } # /existing $progress
 | |
|             
 | |
|             return;
 | |
|             
 | |
|         } # /powerOn
 | |
|         
 | |
|         
 | |
|         sub powerOff #*********************************************************
 | |
|         {
 | |
|             my  ($url, $vbox, $session, $machine, $uuid, $node, $args) =
 | |
|                 (shift, shift, shift, shift, shift, shift, shift);
 | |
|             
 | |
|             # Lock session with machine
 | |
|             my $vmsessionstate =    xCAT::vboxService->IMachine_getSessionState(
 | |
|                                     $url, $machine);
 | |
|                                     
 | |
|             if ($vmsessionstate eq "Open")
 | |
|             {
 | |
|                 xCAT::vboxService->IVirtualBox_openExistingSession(
 | |
|                     $url, $vbox, $session, $uuid);
 | |
|             }
 | |
|             elsif ($vmsessionstate eq "Closed")
 | |
|             {
 | |
|                 xCAT::vboxService->IVirtualBox_openSession(
 | |
|                     $url, $vbox, $session, $uuid);
 | |
|             } 
 | |
|             else # !$vmsessionstate
 | |
|             {
 | |
|                 addError("$node: No direct session to machine");
 | |
|             }
 | |
|             
 | |
| 			# The session is now locked with the machine, now its direct session or console can be optained and manipulated
 | |
|             # Now get the console for the mutable machine
 | |
|             my $console = xCAT::vboxService->ISession_getConsole($url, $session);
 | |
|             if (!$console)
 | |
|             {
 | |
|                 addError("$node: No console for mutable machine");
 | |
|             }
 | |
|             else # existing $console
 | |
|             {
 | |
|                 
 | |
|                 if ($args->[0] =~ m/^reset|^boot/)
 | |
|                 {
 | |
|                     xCAT::vboxService->IConsole_reset($url, $console);
 | |
|                     xCAT::vboxService->IManagedObjectRef_release($url, $console);
 | |
|                     addMessage("$node: on reset");
 | |
|                     
 | |
|                     xCAT::vboxService->IManagedObjectRef_release($url, $console);
 | |
|                     return;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     xCAT::vboxService->IConsole_powerButton($url, $console);
 | |
|                     unless (xCAT::vboxService->IConsole_getPowerButtonHandled(
 | |
|                                         $url, $console) eq "true")
 | |
|                     {
 | |
| 						# In case of no reaction: force to power down machine
 | |
| 						xCAT::vboxService->IConsole_powerDown($url, $console);
 | |
|                     }
 | |
| 					addMessage("$node: off");
 | |
|                 }
 | |
|                 
 | |
|                 xCAT::vboxService->IManagedObjectRef_release($url, $console);
 | |
|                 
 | |
|             } # /existing $console
 | |
|             
 | |
|             return;
 | |
|             
 | |
|         } # /powerOff
 | |
|         
 | |
|         
 | |
|     } # /powerCtrl
 | |
|     
 | |
|     
 | |
|     sub getSession ############################################################
 | |
|     {
 | |
|         my $sess;
 | |
|         my ($url, $vbox, $node, $vmname) = (shift, shift, shift, shift);
 | |
|         
 | |
|         eval {
 | |
|         $sess = xCAT::vboxService->IWebsessionManager_getSessionObject($url, $vbox);
 | |
|         };
 | |
|         
 | |
|         return $sess if ($sess and not $@);
 | |
|         
 | |
|         addError("$node: No session for $vmname on web service @ $url.");
 | |
|         return undef;
 | |
|     } # /getSession
 | |
|     
 | |
|     
 | |
|     sub getMachine ############################################################
 | |
|     {
 | |
|         my $machine;
 | |
|         my ($url, $vbox, $vmname, $node) = (shift, shift, shift, shift);
 | |
|         
 | |
|         eval {
 | |
|         $machine = xCAT::vboxService->IVirtualBox_findMachine($url, $vbox, $vmname);
 | |
|         };
 | |
|         
 | |
|         return $machine if ($machine and not $@);
 | |
|         
 | |
|         addError("$node: VM $vmname not known by the web service @ $url.");
 | |
|         return undef;
 | |
|     } # /getMachine
 | |
|     
 | |
|     
 | |
|     sub getWebService #########################################################
 | |
|     {
 | |
|         my $ws;
 | |
|         my ($url, $user, $passwd, $node) = (shift, shift, shift, shift);
 | |
|         
 | |
|         eval {
 | |
|         $ws = xCAT::vboxService->IWebsessionManager_logon($url, $user, $passwd);
 | |
|         };
 | |
|         
 | |
|         return $ws if ($ws and not $@);
 | |
|         
 | |
|         addError("$node: No connection to the web service @ $url.");
 | |
|         return undef;
 | |
|     } # /getWebService
 | |
|     
 | |
|     
 | |
|     sub getInfo ###############################################################
 | |
|     {
 | |
|         my $node = shift;
 | |
|         
 | |
|         # Open tables first, return in case of a critical error = 2
 | |
|         my $hostsTab = openTable('hosts');
 | |
|         return (2, undef, undef, undef, undef)   if (!$hostsTab);
 | |
|         
 | |
|         my $vmTab = openTable('vm');
 | |
|         return (2, undef, undef, undef, undef)   if (!$vmTab);
 | |
|         
 | |
|         my $websrvTab = openTable('websrv');
 | |
|         return (2, undef, undef, undef, undef)   if (!$websrvTab);
 | |
|         
 | |
|         # Load attributes, return in case of missing information error = 1
 | |
|         my $values;
 | |
| 		
 | |
| 		my @attributes  = ('host','vncport','comments');
 | |
|         $values = loadValues($vmTab, $node, \@attributes);
 | |
|         return (1, undef, undef, undef, undef)   if (!defined($values));
 | |
|             
 | |
|         my $hostname	= $values->{host};
 | |
|         my $vncport		= $values->{vncport};
 | |
|         my $comments	= $values->{comments};
 | |
| 		my $vmname		= undef;
 | |
| 		if ($comments 	=~ m/vmname:(.+)!/) {
 | |
| 			$vmname 	=  $1;
 | |
| 		} else {
 | |
| 			$vmname 	=  $node;
 | |
| 		}
 | |
|         
 | |
|         @attributes  = ('port','username', 'password');
 | |
|         $values = loadValues($websrvTab, $hostname, \@attributes);
 | |
|         return (1, undef, undef, undef, undef)   if (!defined($values));
 | |
|             
 | |
|         my $port     = $values->{port};
 | |
|         my $username = $values->{username};
 | |
|         my $password = $values->{password};
 | |
|         
 | |
|         @attributes  = ('ip');
 | |
|         $values = loadValues($hostsTab, $hostname, \@attributes);
 | |
|         return (1, undef, undef, undef, undef)   if (!defined($values));
 | |
|             
 | |
|         my $ipaddr   = $values->{ip};
 | |
|         my $url      = "http://$ipaddr:$port/";
 | |
|         
 | |
|         return (0, $url, $username, $password, $vmname, $vncport);
 | |
|         
 | |
|         
 | |
|         sub loadValues #*******************************************************
 | |
|         {
 | |
|             my ($table, $node, $attributes) = (shift, shift, shift);
 | |
|             my ($values) = $table->getAttribs({'node'=>$node}, @$attributes);
 | |
|             $table->close;
 | |
|             
 | |
|             addError("$node: Missing information: @$attributes")
 | |
|                 if (!defined($values));
 | |
|                 
 | |
|             return $values;
 | |
|             
 | |
|         } # /loadValues
 | |
|         
 | |
|         
 | |
|         sub openTable #********************************************************
 | |
|         {
 | |
|             my $tabname = shift;
 | |
|             my $table = xCAT::Table->new($tabname);
 | |
|             
 | |
|             # try to create vbox tables if they don't exist yet
 | |
|             if (!$table and $tabname =~ m/websrv/)
 | |
|             {
 | |
|                 $table = xCAT::Table->new( $tabname, -create=>1, -autocommit=>1 );
 | |
|                 $table->close;
 | |
|                 addMessage("[xCAT] Info: The table $tabname has been created.");
 | |
|                 $table = xCAT::Table->new($tabname);
 | |
|             }
 | |
|             
 | |
|             addError("[xCAT] Can not open table: $tabname. Command aborted.")
 | |
|                 if (!$table);
 | |
|             
 | |
|             return $table;
 | |
|             
 | |
|         } # /loadValues
 | |
|         
 | |
|     } # /getInfo
 | |
| }
 | |
| 
 | |
| 1;
 |