Changeset 810

Show
Ignore:
Timestamp:
08/31/07 16:34:50 (1 year ago)
Author:
kindlund
Message:

Completed initial VM::Clone support.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • honeyclient/branches/exp/kindlund-dynamic_updates/etc/honeyclient_log.conf

    r601 r810  
    6464#log4perl.logger.HoneyClient.Agent.Integrity.Registry=DEBUG, Screen 
    6565#log4perl.logger.HoneyClient.DB=DEBUG, Screen 
     66#log4perl.logger.HoneyClient.Manager.VM.Clone=DEBUG, Screen 
    6667# Suppress Parser Debugging Messages 
    6768#log4perl.logger.HoneyClient.Agent.Integrity.Registry.Parser=INFO, Screen 
  • honeyclient/branches/exp/kindlund-dynamic_updates/lib/HoneyClient/Manager/VM.pm

    r808 r810  
    719719    my ($class, %args) = @_; 
    720720 
     721    # Log resolved arguments. 
     722    $LOG->debug(sub { 
     723        # Make Dumper format more terse. 
     724        $Data::Dumper::Terse = 1; 
     725        $Data::Dumper::Indent = 0; 
     726        Dumper(\%args); 
     727    }); 
     728 
    721729    # Sanity check.  Make sure the daemon isn't already running. 
    722730    if (defined($DAEMON_PID)) { 
     
    806814 
    807815sub destroy { 
     816    # Log resolved arguments. 
     817    $LOG->debug(sub { 
     818        # Make Dumper format more terse. 
     819        $Data::Dumper::Terse = 1; 
     820        $Data::Dumper::Indent = 0; 
     821        Dumper(); 
     822    }); 
     823 
    808824    my $ret = undef; 
    809825    # Make sure the PID is defined and not 
  • honeyclient/branches/exp/kindlund-dynamic_updates/lib/HoneyClient/Manager/VM/Clone.pm

    r808 r810  
    167167 
    168168=begin testing 
     169 
     170# Make sure ExtUtils::MakeMaker loads. 
     171BEGIN { use_ok('ExtUtils::MakeMaker', qw(prompt)) or diag("Can't load ExtUtils::MakeMaker package.  Check to make sure the package library is correctly listed within the path."); } 
     172require_ok('ExtUtils::MakeMaker'); 
     173can_ok('ExtUtils::MakeMaker', 'prompt'); 
     174use ExtUtils::MakeMaker qw(prompt); 
    169175 
    170176# Make sure Log::Log4perl loads 
     
    293299# The global variable, used to count the number of 
    294300# Clone objects that have been created. 
    295 our $OBJECT_COUNT : shared = 0
     301our $OBJECT_COUNT : shared = -1
    296302 
    297303=pod 
     
    352358 
    353359=cut 
    354  
    355 my %PARAMS = ( 
    356     # The full absolute path to the master VM's configuration file, whose 
    357     # contents will be the basis for each subsequently cloned VM. 
    358     master_vm_config => getVar(name => "master_vm_config"), 
    359  
    360     # A variable containing the absolute path to the cloned VM's 
    361     # configuration file. 
    362     config => undef, 
    363  
    364     # A variable containing the MAC address of the cloned VM's primary 
    365     # interface. 
    366     mac_address => undef, 
    367      
    368     # A variable containing the IP address of the cloned VM's primary 
    369     # interface. 
    370     ip_address => undef, 
    371      
    372     # A variable containing the name the cloned VM. 
    373     name => undef, 
    374      
    375     # A SOAP handle to the VM manager daemon.  (This internal variable 
    376     # should never be modified externally.) 
    377     _vm_handle => undef, 
    378  
    379     # A variable indicated how long the object should wait for 
    380     # between subsequent retries to the HoneyClient::Manager::VM 
    381     # daemon (in seconds).  (This internal variable should never 
    382     # be modified externally.) 
    383     _retry_period => 2, 
    384 ); 
    385360 
    386361####################################################################### 
     
    449424    my $self = shift; 
    450425 
    451     if (defined($self->{'config'})) { 
    452         my $som = $self->{'_vm_handle'}->getMACaddrVM(config => $self->{'config'}); 
     426    if (($OBJECT_COUNT >= 0) && defined($self->{'config'})) { 
     427        $LOG->info("Suspending clone VM (" . $self->{'config'} . ")."); 
     428        my $som = $self->{'_vm_handle'}->suspendVM(config => $self->{'config'}); 
    453429        if (!$som->result()) { 
    454430            $LOG->error("Unable to suspend VM (" . $self->{'config'} . ")."); 
     
    458434    # Decrement our global object count. 
    459435    $OBJECT_COUNT--; 
    460  
    461     # Upon last use, destroy the global instance of the VM manager. 
    462     if ($OBJECT_COUNT <= 0) { 
     436
     437 
     438END { 
     439    # Upon termination, destroy the global instance of the VM manager. 
     440    if ($OBJECT_COUNT == 0) { 
    463441        HoneyClient::Manager::VM->destroy(); 
    464442    } 
    465443} 
    466444 
    467 ####################################################################### 
    468 # Public Methods Implemented                                          # 
    469 ####################################################################### 
    470  
    471 =pod 
    472  
    473 =head1 METHODS IMPLEMENTED  
    474  
    475 The following functions have been implemented by any Clone object. 
    476  
    477 =head2 HoneyClient::Manager::VM::Clone->new($param => $value, ...) 
    478  
    479 =over 4 
    480  
    481 Creates a new Clone object, which contains a hashtable 
    482 containing any of the supplied "param => value" arguments. 
    483  
    484 I<Inputs>: 
    485  B<$param> is an optional parameter variable. 
    486  B<$value> is $param's corresponding value. 
    487   
    488 Note: If any $param(s) are supplied, then an equal number of 
    489 corresponding $value(s) B<must> also be specified. 
    490  
    491 I<Output>: The instantiated Clone B<$object>, fully initialized. 
    492  
    493 =back 
    494  
    495 =begin testing 
    496  
    497 # Shared test variables. 
    498 my ($stub, $som, $URL); 
    499 my $testVM = $ENV{PWD} . "/" . getVar(name      => "test_vm_config", 
    500                                       namespace => "HoneyClient::Manager::VM::Test"); 
    501  
    502 # Catch all errors, in order to make sure child processes are 
    503 # properly killed. 
    504 eval { 
    505  
    506     $URL = HoneyClient::Manager::VM->init(); 
    507  
    508     # Connect to daemon as a client. 
    509     $stub = getClientHandle(namespace => "HoneyClient::Manager::VM"); 
    510  
    511     # In order to test setMasterVM(), we're going to fully clone 
    512     # the testVM, then set the newly created clone as a master VM. 
    513  
    514     # Get the test VM's parent directory, 
    515     # in order to create a temporary master VM. 
    516     my $testVMDir = dirname($testVM); 
    517     my $masterVMDir = dirname($testVMDir) . "/test_vm_master"; 
    518     my $masterVM = $masterVMDir . "/" . basename($testVM); 
    519  
    520     # Create the master VM. 
    521     $som = $stub->fullCloneVM(src_config => $testVM, dest_dir => $masterVMDir); 
    522  
    523     # Wait a small amount of time for the asynchronous clone 
    524     # to complete. 
    525     sleep (60); 
    526  
    527     # The master VM should be on. 
    528     $som = $stub->getStateVM(config => $masterVM); 
    529  
    530     # Since the master VM doesn't have an OS installed on it, 
    531     # the VM may be considered stuck.  Go ahead and answer 
    532     # this question, if need be. 
    533     if ($som->result == VM_EXECUTION_STATE_STUCK) { 
    534         $som = $stub->answerVM(config => $masterVM); 
    535     } 
    536  
    537     HoneyClient::Manager::VM->destroy(); 
    538     sleep (1); 
    539  
    540     # Create a generic clone, with test state data. 
    541     my $clone = HoneyClient::Manager::VM::Clone->new(test => 1, master_vm_config => $masterVM); 
    542     is($clone->{test}, 1, "new(test => 1, master_vm_config => '$masterVM')") or diag("The new() call failed."); 
    543     isa_ok($clone, 'HoneyClient::Manager::VM::Clone', "new(test => 1, master_vm_config => '$masterVM')") or diag("The new() call failed."); 
    544  
    545     # Destroy the master VM. 
    546     $som = $stub->destroyVM(config => $masterVM); 
    547 }; 
    548  
    549 # Kill the child daemon, if it still exists. 
    550 HoneyClient::Manager::VM->destroy(); 
    551 sleep (1); 
    552  
    553 # Report any failure found. 
    554 if ($@) { 
    555     fail($@); 
    556 
    557  
    558 =end testing 
    559  
    560 =cut 
    561  
    562 sub new { 
    563     # - This function takes in an optional hashtable, 
    564     #   that contains various key => 'value' configuration 
    565     #   parameters. 
    566     # 
    567     # - For each parameter given, it overwrites any corresponding 
    568     #   parameters specified within the default hashtable, %PARAMS,  
    569     #   with custom entries that were given as parameters. 
    570     # 
    571     # - Finally, it returns a blessed instance of the 
    572     #   merged hashtable, as an 'object'. 
    573  
    574     # Get the class name. 
    575     my $self = shift; 
    576  
    577     # Get the rest of the arguments, as a hashtable. 
    578     # Hash-based arguments are used, since HoneyClient::Util::SOAP is unable to handle 
    579     # hash references directly.  Thus, flat hashtables are used throughout the code 
    580     # for consistency. 
    581     my %args = @_; 
    582  
    583     # Check to see if the class name is inherited or defined. 
    584     my $class = ref($self) || $self; 
    585  
    586     # Initialize default parameters. 
    587     $self = { }; 
    588     my %params = %{dclone(\%PARAMS)}; 
    589     @{$self}{keys %params} = values %params; 
    590  
    591     # Now, overwrite any default parameters that were redefined 
    592     # in the supplied arguments. 
    593     @{$self}{keys %args} = values %args; 
    594  
    595     # Now, assign our object the appropriate namespace. 
    596     bless $self, $class; 
    597  
    598     # Upon first use, start up a global instance of the VM manager. 
    599     if ($OBJECT_COUNT <= 0) { 
    600         HoneyClient::Manager::VM->init(); 
    601     } 
    602  
    603     # Set a valid handle for the VM daemon. 
    604     $self->{'_vm_handle'} = getClientHandle(namespace => "HoneyClient::Manager::VM"); 
    605  
    606     # Set the master VM. 
    607     $LOG->info("Setting VM (" . $self->{'master_vm_config'} . ") as master."); 
    608     my $som = $self->{'_vm_handle'}->setMasterVM(config => $self->{'master_vm_config'}); 
    609     if (!$som->result()) { 
    610         $LOG->fatal("Unable to set VM (" . $self->{'master_vm_config'} . ") as a master VM."); 
    611         Carp::croak "Unable to set VM (" . $self->{'master_vm_config'} . ") as a master VM."; 
    612     } 
    613  
    614     # Update our global object count. 
    615     $OBJECT_COUNT++; 
    616  
    617     # Finally, return the blessed object. 
    618     return $self; 
    619 
    620  
    621 =pod 
    622  
    623 =head2 $object->start() 
    624  
    625 =over 4 
    626  
    627 If not previously called, this method creates a new clone VM 
    628 from the supplied master VM.  Furthermore, this method will power 
    629 on the clone, and wait until the clone VM has fully booted and 
    630 has an operational Agent daemon running on it. 
    631  
    632 During this power on process, the name, MAC address, and  
    633 IP address of the running clone are recorded in the object. 
    634  
    635 I<Output>: The updated Clone B<$object>, containing state information 
    636 from starting the clone VM.  Will croak if this operation fails. 
    637  
    638 =back 
    639  
    640 # XXX: FINISH THIS 
    641 #=begin testing 
    642 
    643 # Create a generic driver, with test state data. 
    644 #my $driver = HoneyClient::Agent::Driver->new(test => 1); 
    645 #dies_ok {$driver->drive()} 'drive()' or diag("The drive() call failed.  Expected drive() to throw an exception."); 
    646 
    647 #=end testing 
    648  
    649 =cut 
    650  
    651 sub start { 
     445# Initialized cloned VMs. 
     446
     447# If no existing configuration is supplied, then this method creates 
     448# a new clone VM from the supplied master VM.  Furthermore, this method 
     449# will power on the clone, and wait until the clone VM has fully booted and 
     450# has an operational HoneyClient::Agent daemon running on it. 
     451#  
     452# During this power on process, the name, MAC address, and  
     453# IP address of the running clone are recorded in the object. 
     454
     455# Output: The updated Clone $object, containing state information 
     456# from starting the clone VM.  Will croak if this operation fails. 
     457
     458# TODO: Need to configure a timeout failure operation -- in case 
     459# there's a problem and the VM operations hang. 
     460sub _init { 
    652461 
    653462    # Extract arguments. 
     
    668477    my $ret = undef; 
    669478 
    670     # Perform the quick clone operation. 
    671     $LOG->info("Quick cloning master VM (" . $self->{'master_vm_config'} . ")."); 
    672     $som = $self->{'_vm_handle'}->quickCloneVM(src_config => $self->{'master_vm_config'}); 
    673     $ret = $som->result(); 
    674     if (!$ret) { 
    675         $LOG->fatal("Unable to quick clone master VM (" . $self->{'master_vm_config'} . ")."); 
    676         Carp::croak "Unable to quick clone master VM (" . $self->{'master_vm_config'} . ")."; 
    677     } 
    678     # Set the cloned VM configuration. 
    679     $self->{'config'} = $ret; 
     479    # If the clone's configuration wasn't supplied initially, then 
     480    # perform the quick clone operation. 
     481    if (!defined($self->{'config'})) { 
     482        $LOG->info("Quick cloning master VM (" . $self->{'master_vm_config'} . ")."); 
     483        $som = $self->{'_vm_handle'}->quickCloneVM(src_config => $self->{'master_vm_config'}); 
     484        $ret = $som->result(); 
     485        if (!$ret) { 
     486            $LOG->fatal("Unable to quick clone master VM (" . $self->{'master_vm_config'} . ")."); 
     487            Carp::croak "Unable to quick clone master VM (" . $self->{'master_vm_config'} . ")."; 
     488        } 
     489        # Set the cloned VM configuration. 
     490        $self->{'config'} = $ret; 
     491    } else { 
     492        $LOG->debug("Starting clone VM (" . $self->{'config'} . ")."); 
     493        $som = $self->{'_vm_handle'}->startVM(config => $self->{'config'}); 
     494        $ret = $som->result(); 
     495        if (!$ret) { 
     496            $LOG->fatal("Unable to start clone VM (" . $self->{'config'} . ")."); 
     497            Carp::croak "Unable to start clone VM (" . $self->{'config'} . ")."; 
     498        } 
     499    } 
    680500 
    681501    # Wait until the VM gets registered, before proceeding. 
    682     $LOG->info("Checking if clone VM (" . $self->{'config'} . ") is registered."); 
     502    $LOG->debug("Checking if clone VM (" . $self->{'config'} . ") is registered."); 
    683503    $ret = undef; 
    684504    while (!defined($ret) or !$ret) { 
     
    693513 
    694514    # Once registered, check if the VM is ON yet. 
     515    $LOG->debug("Checking if clone VM (" . $self->{'config'} . ") is powered on."); 
    695516    $ret = undef; 
    696517    while (!defined($ret) or ($ret != VM_EXECUTION_STATE_ON)) { 
     
    705526 
    706527    # Now, get the VM's MAC address. 
     528    $LOG->debug("Retrieving MAC address of clone VM (" . $self->{'config'} . ")."); 
    707529    $som = $self->{'_vm_handle'}->getMACaddrVM(config => $self->{'config'}); 
    708530    $self->{'mac_address'} = $som->result(); 
    709531 
    710532    # Now, get the VM's name. 
     533    $LOG->debug("Retrieving name of clone VM (" . $self->{'config'} . ")."); 
    711534    $som = $self->{'_vm_handle'}->getNameVM(config => $self->{'config'}); 
    712535    $self->{'name'} = $som->result(); 
    713536 
    714537    # Now, get the VM's IP address. 
     538    $LOG->debug("Retrieving IP address of clone VM (" . $self->{'config'} . ")."); 
    715539    $ret = undef; 
    716540    my $stubAgent = undef; 
     
    725549            next; # skip further processing 
    726550        } elsif (!$logMsgPrinted) { 
    727             $LOG->info("Created clone VM (" . $self->{'name'} . ") using IP (" . 
    728                        $self->{'ip_address'} . ") and MAC (" . $self->{'mac_address'} . "."); 
     551            $LOG->info("Initialized clone VM (" . $self->{'name'} . ") using IP (" . 
     552                       $self->{'ip_address'} . ") and MAC (" . $self->{'mac_address'} . ")."); 
    729553            $logMsgPrinted = 1; 
    730554        } 
     
    753577} 
    754578 
     579####################################################################### 
     580# Public Methods Implemented                                          # 
     581####################################################################### 
     582 
    755583=pod 
    756584 
    757 =head2 $object->isFinished() 
     585=head1 METHODS IMPLEMENTED  
     586 
     587The following functions have been implemented by any Clone object. 
     588 
     589=head2 HoneyClient::Manager::VM::Clone->new($param => $value, ...) 
    758590 
    759591=over 4 
    760592 
    761 Indicates if the Driver B<$object> has driven the back-end application 
    762 through the Driver's entire state and is unable to drive the application 
    763 further without additional input. 
    764  
    765 I<Output>: True if the Driver B<$object> is finished, false otherwise. 
     593Creates a new Clone object, which contains a hashtable 
     594containing any of the supplied "param => value" arguments. 
     595 
     596I<Inputs>: 
     597 B<$param> is an optional parameter variable. 
     598 B<$value> is $param's corresponding value. 
     599  
     600Note: If any $param(s) are supplied, then an equal number of 
     601corresponding $value(s) B<must> also be specified. 
     602 
     603I<Output>: The instantiated Clone B<$object>, fully initialized 
     604with a ready-to-use cloned honeyclient VM. 
    766605 
    767606=back 
    768607 
    769 #=begin testing 
    770 
    771 # Create a generic driver, with test state data. 
    772 #my $driver = HoneyClient::Agent::Driver->new(test => 1); 
    773 #dies_ok {$driver->isFinished()} 'isFinished()' or diag("The isFinished() call failed.  Expected isFinished() to throw an exception."); 
    774 
    775 #=end testing 
     608=begin testing 
     609 
     610# Shared test variables. 
     611my ($stub, $som, $URL); 
     612my $testVM = $ENV{PWD} . "/" . getVar(name      => "test_vm_config", 
     613                                      namespace => "HoneyClient::Manager::VM::Test"); 
     614 
     615# Include notice, to clarify our assumptions. 
     616diag("About to run basic unit tests; these may take some time."); 
     617diag("Note: These tests *expect* VMware Server or VMware GSX to be installed and running on this system beforehand."); 
     618 
     619# Catch all errors, in order to make sure child processes are 
     620# properly killed. 
     621eval { 
     622 
     623    $URL = HoneyClient::Manager::VM->init(); 
     624 
     625    # Connect to daemon as a client. 
     626    $stub = getClientHandle(namespace => "HoneyClient::Manager::VM"); 
     627 
     628    # In order to test setMasterVM(), we're going to fully clone 
     629    # the testVM, then set the newly created clone as a master VM. 
     630 
     631    # Get the test VM's parent directory, 
     632    # in order to create a temporary master VM. 
     633    my $testVMDir = dirname($testVM); 
     634    my $masterVMDir = dirname($testVMDir) . "/test_vm_master"; 
     635    my $masterVM = $masterVMDir . "/" . basename($testVM); 
     636 
     637    # Create the master VM. 
     638    $som = $stub->fullCloneVM(src_config => $testVM, dest_dir => $masterVMDir); 
     639 
     640    # Wait a small amount of time for the asynchronous clone 
     641    # to complete. 
     642    sleep (10); 
     643 
     644    # The master VM should be on. 
     645    $som = $stub->getStateVM(config => $masterVM); 
     646    
     647    # Since the master VM doesn't have an OS installed on it, 
     648    # the VM may be considered stuck.  Go ahead and answer 
     649    # this question, if need be. 
     650    if ($som->result == VM_EXECUTION_STATE_STUCK) { 
     651        $som = $stub->answerVM(config => $masterVM); 
     652    } 
     653 
     654    # Turn off the master VM. 
     655    $som = $stub->stopVM(config => $masterVM); 
     656 
     657    # Now, kill the VM daemon. 
     658    HoneyClient::Manager::VM->destroy(); 
     659    sleep (1); 
     660 
     661    # Create a generic empty clone, with test state data. 
     662    my $clone = HoneyClient::Manager::VM::Clone->new(test => 1, master_vm_config => $masterVM, _dont_init => 1); 
     663    is($clone->{test}, 1, "new(test => 1, master_vm_config => '$masterVM', _dont_init => 1)") or diag("The new() call failed."); 
     664    isa_ok($clone, 'HoneyClient::Manager::VM::Clone', "new(test => 1, master_vm_config => '$masterVM', _dont_init => 1)") or diag("The new() call failed."); 
     665    $clone = undef; 
     666 
     667    # Destroy the master VM. 
     668    $som = $stub->destroyVM(config => $masterVM); 
     669 
     670    my $question; 
     671    $question = prompt("#\n" . 
     672                       "# Note: Testing real clone operations will *ONLY* work\n" . 
     673                       "# with a fully functional master VM that has the HoneyClient code\n" . 
     674                       "# loaded upon boot-up.\n" . 
     675                       "#\n" . 
     676                       "# Your master VM is: " . getVar(name => "master_vm_config", namespace => "HoneyClient::Manager::VM") . "\n" . 
     677                       "#\n" . 
     678                       "# Do you want to test cloning this master VM?", "no"); 
     679    if ($question =~ /^y.*/i) { 
     680        $clone = HoneyClient::Manager::VM::Clone->new(test => 1); 
     681        is($clone->{test}, 1, "new(test => 1)") or diag("The new() call failed."); 
     682        isa_ok($clone, 'HoneyClient::Manager::VM::Clone', "new(test => 1)") or diag("The new() call failed."); 
     683        my $cloneConfig = $clone->{config}; 
     684        $clone = undef; 
     685     
     686        # Destroy the clone VM. 
     687        $som = $stub->destroyVM(config => $cloneConfig); 
     688    } 
     689}; 
     690 
     691# Kill the child daemon, if it still exists. 
     692HoneyClient::Manager::VM->destroy(); 
     693sleep (1); 
     694 
     695# Report any failure found. 
     696if ($@) { 
     697    fail($@); 
     698
     699 
     700=end testing 
    776701 
    777702=cut 
    778703 
    779 sub isFinished { 
     704sub new { 
     705    # - This function takes in an optional hashtable, 
     706    #   that contains various key => 'value' configuration 
     707    #   parameters. 
     708    # 
     709    # - For each parameter given, it overwrites any corresponding 
     710    #   parameters specified within the default hashtable, %params,  
     711    #   with custom entries that were given as parameters. 
     712    # 
     713    # - Finally, it returns a blessed instance of the 
     714    #   merged hashtable, as an 'object'. 
     715 
    780716    # Get the class name. 
    781717    my $self = shift; 
    782      
     718 
     719    # Get the rest of the arguments, as a hashtable. 
     720    # Hash-based arguments are used, since HoneyClient::Util::SOAP is unable to handle 
     721    # hash references directly.  Thus, flat hashtables are used throughout the code 
     722    # for consistency. 
     723    my %args = @_; 
     724 
    783725    # Check to see if the class name is inherited or defined. 
    784726    my $class = ref($self) || $self; 
     727 
     728    # Initialize default parameters. 
     729    $self = { }; 
     730    my %params = ( 
     731        # The full absolute path to the master VM's configuration file, whose 
     732        # contents will be the basis for each subsequently cloned VM. 
     733        master_vm_config => getVar(name => "master_vm_config"), 
     734 
     735        # A variable containing the absolute path to the cloned VM's 
     736        # configuration file. 
     737        config => undef, 
     738 
     739        # A variable containing the MAC address of the cloned VM's primary 
     740        # interface. 
     741        mac_address => undef, 
    785742     
    786     # Sanity check: Make sure we've been fed an object. 
    787     unless (ref($self)) { 
    788         $LOG->error("Error: Function must be called in reference to a " . 
    789                     __PACKAGE__ . "->new() object!"); 
    790         Carp::croak "Error: Function must be called in reference to a " . 
    791                     __PACKAGE__ . "->new() object!"; 
    792     } 
    793  
    794     # Emit generic "not implemented" error message. 
    795     $LOG->error($class . "->isFinished() is not implemented!"); 
    796     Carp::croak "Error: " . $class . "->isFinished() is not implemented!\n"; 
     743        # A variable containing the IP address of the cloned VM's primary 
     744        # interface. 
     745        ip_address => undef, 
     746     
     747        # A variable containing the name the cloned VM. 
     748        name => undef, 
     749     
     750        # A SOAP handle to the VM manager daemon.  (This internal variable 
     751        # should never be modified externally.) 
     752        _vm_handle => undef, 
     753 
     754        # A variable indicated how long the object should wait for 
     755        # between subsequent retries to the HoneyClient::Manager::VM 
     756        # daemon (in seconds).  (This internal variable should never 
     757        # be modified externally.) 
     758        _retry_period => 2, 
     759    ); 
     760 
     761    @{$self}{keys %params} = values %params; 
     762 
     763    # Now, overwrite any default parameters that were redefined 
     764    # in the supplied arguments. 
     765    @{$self}{keys %args} = values %args; 
     766 
     767    # Now, assign our object the appropriate namespace. 
     768    bless $self, $class; 
     769 
     770    # Upon first use, start up a global instance of the VM manager. 
     771    if ($OBJECT_COUNT < 0) { 
     772        HoneyClient::Manager::VM->init(); 
     773        $OBJECT_COUNT = 0; 
     774    } 
     775 
     776    # Set a valid handle for the VM daemon. 
     777    $self->{'_vm_handle'} = getClientHandle(namespace => "HoneyClient::Manager::VM"); 
     778 
     779    # If the clone's configuration wasn't supplied initially, then 
     780    # set the master VM to prepare for cloning. 
     781    unless (defined($self->{'config'})) { 
     782        $LOG->info("Setting VM (" . $self->{'master_vm_config'} . ") as master."); 
     783        my $som = $self->{'_vm_handle'}->setMasterVM(config => $self->{'master_vm_config'}); 
     784        if (!$som->result()) { 
     785            $LOG->fatal("Unable to set VM (" . $self->{'master_vm_config'} . ") as a master VM."); 
     786            Carp::croak "Unable to set VM (" . $self->{'master_vm_config'} . ") as a master VM."; 
     787        } 
     788    } 
     789 
     790    # Update our global object count. 
     791    $OBJECT_COUNT++; 
     792 
     793    # Finally, return the blessed object, with a fully initialized 
     794    # cloned VM unless otherwise specified. 
     795    if ($self->{'_dont_init'}) { 
     796        return $self; 
     797    } else { 
     798        return $self->_init(); 
     799    } 
    797800} 
    798  
    799 =pod 
    800  
    801 =head2 $object->next() 
    802  
    803 =over 4 
    804  
    805 Returns the next set of server hostnames and/or IP addresses that the 
    806 back-end application will contact, upon the next subsequent call to 
    807 the B<$object>'s drive() method. 
    808  
    809 Specifically, the returned data is a reference to a hashtable, containing 
    810 detailed information about which resources, hostnames, IPs, protocols, and  
    811 ports that the application will contact upon the next iteration. 
    812  
    813 Here is an example of such returned data: 
    814  
    815   $hashref = { 
    816    
    817       # The set of servers that the driver will contact upon 
    818       # the next drive() operation. 
    819       targets => { 
    820           # The application will contact 'site.com' using 
    821           # TCP ports 80 and 81. 
    822           'site.com' => { 
    823               'tcp' => [ 80, 81 ], 
    824           }, 
    825  
    826           # The application will contact '192.168.1.1' using 
    827           # UDP ports 53 and 123. 
    828           '192.168.1.1' => { 
    829               'udp' => [ 53, 123 ], 
    830           }, 
    831   
    832           # Or, more generically: 
    833           'hostname_or_IP' => { 
    834               'protocol_type' => [ portnumbers_as_list ], 
    835           }, 
    836       }, 
    837  
    838       # The set of resources that the driver will operate upon 
    839       # the next drive() operation. 
    840       resources => { 
    841           'http://www.mitre.org/' => 1, 
    842       }, 
    843   }; 
    844  
    845 B<Note>: For each hostname or IP address specified, if B<no> 
    846 corresponding protocol/port sub-hastables are given, then it 
    847 must be B<assumed> that the back-end application may contact 
    848 the hostname or IP address using B<ANY> protocol/port. 
    849  
    850 I<Output>: The aforementioned B<$hashref> containing the next set of 
    851 resources that the back-end application will attempt to contact upon 
    852 the next drive() iteration. 
    853  
    854 # XXX: Resolve this. 
    855  
    856 B<Note>: Eventually this B<$hashref> will become a structured object, 
    857 created via a HoneyClient::Util::* package.  However, the underlying 
    858 structure of this hashtable is not expected to change.  
    859  
    860 =back 
    861  
    862 #=begin testing 
    863 # 
    864 # Create a generic driver, with test state data. 
    865 #my $driver = HoneyClient::Agent::Driver->new(test => 1); 
    866 #dies_ok {$driver->next()} 'next()' or diag("The next() call failed.  Expected next() to throw an exception."); 
    867 # 
    868 #=end testing 
    869  
    870 =cut 
    871  
    872 sub next { 
    873     # Get the class name. 
    874     my $self = shift; 
    875      
    876     # Check to see if the class name is inherited or defined. 
    877     my $class = ref($self) || $self; 
    878      
    879     # Sanity check: Make sure we've been fed an object. 
    880     unless (ref($self)) { 
    881         $LOG->error("Error: Function must be called in reference to a " . 
    882                     __PACKAGE__ . "->new() object!"); 
    883         Carp::croak "Error: Function must be called in reference to a " . 
    884                     __PACKAGE__ . "->new() object!"; 
    885     } 
    886  
    887     # Emit generic "not implemented" error message. 
    888     $LOG->error($class . "->next() is not implemented!"); 
    889     Carp::croak "Error: " . $class . "->next() is not implemented!\n"; 
    890 } 
    891  
    892 =pod 
    893  
    894 =head2 $object->status() 
    895  
    896 =over 4 
    897  
    898 Returns the current status of the Driver B<$object>, as it's state 
    899 exists, between subsequent calls to $object->driver(). 
    900  
    901 Specifically, the data returned is a reference to a hashtable, 
    902 containing specific statistical information about the status 
    903 of the Driver's progress during back-end application automation. 
    904  
    905 As such, the exact structure of this returned hashtable is not strictly 
    906 defined.  Instead, it is left up to each specific Driver implementation 
    907 to return useful, statistical information back to the Agent that 
    908 makes sense for the driven application. 
    909  
    910 For example, if an Internet Explorer specific Driver were implemented, 
    911 then the corresponding status hashtable reference returned may look 
    912 something like: 
    913  
    914   $hashref = { 
    915       'links_remaining'  =>       56, # Number of URLs left to process. 
    916       'links_processed'  =>       44, # Number of URLs processed. 
    917       'links_total'      =>      100, # Total number of URLs given. 
    918       'percent_complete' => '44.00%', # Percent complete. 
    919   }; 
    920  
    921 For another example, if an Outlook specific Driver were implemented, 
    922 then the corresponding status hashtable reference returned may look 
    923 something like: 
    924  
    925   $hashref = { 
    926       'mail_remaining'   =>       56, # Number of messages left to process. 
    927       'mail_processed'   =>       44, # Number of messages processed. 
    928       'mail_total'       =>      100, # Total number of messages given. 
    929       'percent_complete' => '44.00%', # Percent complete. 
    930   }; 
    931  
    932 I<Output>: A corresponding B<$hashref>, containing statistical information 
    933 about the Driver's progress, as previously mentioned. 
    934  
    935 # XXX: Resolve this. 
    936  
    937 B<Note>: The exact structure of this status hashtable may become more 
    938 concrete, as we define a generic concept of a "unit of work" per every 
    939 iteration of the $object->drive() method.  For example, it may be 
    940 likely that each Driver will attempt to contact a series of resources 
    941 per every "unit of work" iteration.  As such, we may generically 
    942 record how many "work units" are remaining, processed, and total -- 
    943 rather than specifically state "links" or "mail" within the hashtable 
    944 key names, accordingly. 
    945  
    946 At the least, it can be assumed that even if a generic structure were 
    947 defined, we would leave room available in the status hashtable to 
    948 capture additional, implementation-specific statistics that are not 
    949 generic among every Driver implementation. 
    950  
    951 =back 
    952  
    953 #=begin testing 
    954 # 
    955 # Create a generic driver, with test state data. 
    956 #my $driver = HoneyClient::Agent::Driver->new(test => 1); 
    957 #dies_ok {$driver->status()} 'status()' or diag("The status() call failed.  Expected status() to throw an exception."); 
    958 # 
    959 #=end testing 
    960  
    961 =cut 
    962  
    963 sub status { 
    964     # Get the class name. 
    965     my $self = shift; 
    966      
    967     # Check to see if the class name is inherited or defined. 
    968     my $class = ref($self) || $self; 
    969      
    970     # Sanity check: Make sure we've been fed an object. 
    971     unless (ref($self)) { 
    972         $LOG->error("Error: Function must be called in reference to a " . 
    973                     __PACKAGE__ . "->new() object!"); 
    974         Carp::croak "Error: Function must be called in reference to a " . 
    975                     __PACKAGE__ . "->new() object!"; 
    976     } 
    977  
    978     # Emit generic "not implemented" error message. 
    979     $LOG->error($class . "->next() is not implemented!"); 
    980     Carp::croak "Error: " . $class . "->next() is not implemented!\n"; 
    981 } 
    982  
    983801 
    984802####################################################################### 
  • honeyclient/branches/exp/kindlund-dynamic_updates/t/honeyclient_manager_vm_clone.t

    r590 r810  
    99# =begin testing 
    1010{ 
     11# Make sure ExtUtils::MakeMaker loads. 
     12BEGIN { use_ok('ExtUtils::MakeMaker', qw(prompt)) or diag("Can't load ExtUtils::MakeMaker package.  Check to make sure the package library is correctly listed within the path."); } 
     13require_ok('ExtUtils::MakeMaker'); 
     14can_ok('ExtUtils::MakeMaker', 'prompt'); 
     15use ExtUtils::MakeMaker qw(prompt); 
     16 
    1117# Make sure Log::Log4perl loads 
    1218BEGIN { use_ok('Log::Log4perl', qw(:nowarn)) 
     
    107113                                      namespace => "HoneyClient::Manager::VM::Test"); 
    108114 
     115# Include notice, to clarify our assumptions. 
     116diag("About to run basic unit tests; these may take some time."); 
     117diag("Note: These tests *expect* VMware Server or VMware GSX to be installed and running on this system beforehand."); 
     118 
    109119# Catch all errors, in order to make sure child processes are 
    110120# properly killed. 
     
    130140    # Wait a small amount of time for the asynchronous clone 
    131141    # to complete. 
    132     sleep (60); 
     142    sleep (10); 
    133143 
    134144    # The master VM should be on. 
    135145    $som = $stub->getStateVM(config => $masterVM); 
    136  
     146    
    137147    # Since the master VM doesn't have an OS installed on it, 
    138148    # the VM may be considered stuck.  Go ahead and answer 
     
    142152    } 
    143153 
     154    # Turn off the master VM. 
     155    $som = $stub->stopVM(config => $masterVM); 
     156 
     157    # Now, kill the VM daemon. 
    144158    HoneyClient::Manager::VM->destroy(); 
    145159    sleep (1); 
    146160 
    147     # Create a generic clone, with test state data. 
    148     my $clone = HoneyClient::Manager::VM::Clone->new(test => 1, master_vm_config => $masterVM); 
    149     is($clone->{test}, 1, "new(test => 1, master_vm_config => '$masterVM')") or diag("The new() call failed."); 
    150     isa_ok($clone, 'HoneyClient::Manager::VM::Clone', "new(test => 1, master_vm_config => '$masterVM')") or diag("The new() call failed."); 
     161    # Create a generic empty clone, with test state data. 
     162    my $clone = HoneyClient::Manager::VM::Clone->new(test => 1, master_vm_config => $masterVM, _dont_init => 1); 
     163    is($clone->{test}, 1, "new(test => 1, master_vm_config => '$masterVM', _dont_init => 1)") or diag("The new() call failed."); 
     164    isa_ok($clone, 'HoneyClient::Manager::VM::Clone', "new(test => 1, master_vm_config => '$masterVM', _dont_init => 1)") or diag("The new() call failed."); 
     165    $clone = undef; 
    151166 
    152167    # Destroy the master VM. 
    153168    $som = $stub->destroyVM(config => $masterVM); 
     169 
     170    my $question; 
     171    $question = prompt("#\n" . 
     172                       "# Note: Testing real clone operations will *ONLY* work\n" . 
     173                       "# with a fully functional master VM that has the HoneyClient code\n" . 
     174                       "# loaded upon boot-up.\n" . 
     175                       "#\n" . 
     176                       "# Your master VM is: " . getVar(name => "master_vm_config", namespace => "HoneyClient::Manager::VM") . "\n" . 
     177                       "#\n" . 
     178                       "# Do you want to test cloning this master VM?", "no"); 
     179    if ($question =~ /^y.*/i) { 
     180        $clone = HoneyClient::Manager::VM::Clone->new(test => 1); 
     181        is($clone->{test}, 1, "new(test => 1)") or diag("The new() call failed."); 
     182        isa_ok($clone, 'HoneyClient::Manager::VM::Clone', "new(test => 1)") or diag("The new() call failed."); 
     183        my $cloneConfig = $clone->{config}; 
     184        $clone = undef; 
     185     
     186        # Destroy the clone VM. 
     187        $som = $stub->destroyVM(config => $cloneConfig); 
     188    } 
    154189}; 
    155190