Author: arkurth Date: Thu May 28 18:01:18 2009 New Revision: 779702 URL: http://svn.apache.org/viewvc?rev=779702&view=rev Log: VCL-140 Added get_variable() and set_variable() subroutines which access the variable table.
Other Added get_computer_private_ip_address() subroutine. It searches the /etc/hosts file on the managment node for the computer specified Modified: incubator/vcl/trunk/managementnode/lib/VCL/DataStructure.pm Modified: incubator/vcl/trunk/managementnode/lib/VCL/DataStructure.pm URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/DataStructure.pm?rev=779702&r1=779701&r2=779702&view=diff ============================================================================== --- incubator/vcl/trunk/managementnode/lib/VCL/DataStructure.pm (original) +++ incubator/vcl/trunk/managementnode/lib/VCL/DataStructure.pm Thu May 28 18:01:18 2009 @@ -78,6 +78,8 @@ use Object::InsideOut; use List::Util qw(min max); +use YAML; + use VCL::utils; ############################################################################## @@ -1408,6 +1410,303 @@ #///////////////////////////////////////////////////////////////////////////// +=head2 get_computer_private_ip_address + + Parameters : computer name (optional) + Returns : If successful: string containing IP address + If failed: false + Description : Retrieves the IP address for a computer from the + management node's local /etc/hosts file. + + An optional argument can + be supplied containing the name of the computer for which the IP + address will be retrieved. + + This subroutine may or may not be called as a DataStructure + object method. If this subroutine is called as an object method, + the computer name argument is optional. If it is not called as an + object method, the computer name argument must be supplied. + + The first time this subroutine is called as an object method + without an argument, the reservation computer's IP address is + retrieved from the /etc/hosts file and saved in the DataStructure + object's data. Subsequent calls as an object method without an + argument will return the IP address retrieved the first time for + efficiency. + +=cut + +sub get_computer_private_ip_address { + my $self; + my $argument = shift; + my $computer_name; + + # Check if subroutine was called as an object method + if (ref($argument) && $argument->isa('VCL::DataStructure')) { + # Subroutine was called as an object method, check if an argument was specified + $self = $argument; + $argument = shift; + if ($argument) { + # Argument was specified, use this as the computer name + $computer_name = $argument; + } + else { + # Argument was not specified, check if private IP address for this reservation's computer was already retrieved from /etc/hosts + if (my $existing_private_ip_address = $self->request_data->{reservation}{$self->reservation_id}{computer}{privateIPaddress}) { + # This subroutine has already been run for the reservation computer, return IP address retrieved earlier + notify($ERRORS{'DEBUG'}, 0, "returning private IP address previously retrieved from /etc/hosts: $existing_private_ip_address"); + return $existing_private_ip_address; + } + + # Argument was not specified and private IP address has not yet been saved in the request data + # Get the computer short name for this reservation + $computer_name = $self->get_computer_short_name(); + } + } + elsif (ref($argument)) { + notify($ERRORS{'WARNING'}, 0, "subroutine was called with an illegal argument type: " . ref($argument)); + return; + } + else { + # Subroutine was not called as an object method + $computer_name = $argument; + } + + # Make sure the computer name was determined either from an argument or the request data + if (!$computer_name) { + notify($ERRORS{'WARNING'}, 0, "unable to determine computer name from argument or request data"); + return; + } + + notify($ERRORS{'DEBUG'}, 0, "attempting to retrieve private IP address for computer: $computer_name"); + + # Retrieve the contents of /etc/hosts using cat + my ($exit_status, $output) = run_command('cat /etc/hosts', 1); + if (defined $exit_status && $exit_status == 0) { + notify($ERRORS{'DEBUG'}, 0, "retrieved contents of /etc/hosts on this management node, contains " . scalar @$output . " lines"); + } + elsif (defined $exit_status) { + notify($ERRORS{'WARNING'}, 0, "failed to cat /etc/hosts on this management node, exit status: $exit_status, output:\n" . join("\n", @$output)); + return; + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to run command to cat /etc/hosts on this management node"); + return; + } + + # Find lines containing the computer name + my @matching_computer_hosts_lines = grep(/\s$computer_name/i, @$output); + + # Extract matching lines which aren't commented out + my @uncommented_computer_hosts_lines = grep(/^\s*[^#]/, @matching_computer_hosts_lines); + + # Make sure 1 uncommented line was found + if (@matching_computer_hosts_lines == 0) { + notify($ERRORS{'WARNING'}, 0, "did not find any lines in /etc/hosts containing '$computer_name'"); + return; + } + elsif (@uncommented_computer_hosts_lines == 0) { + notify($ERRORS{'WARNING'}, 0, "did not find any uncommented lines in /etc/hosts containing '$computer_name':\n" . join("\n", @matching_computer_hosts_lines)); + return; + } + elsif (@uncommented_computer_hosts_lines > 1) { + notify($ERRORS{'WARNING'}, 0, "found multiple uncommented lines in /etc/hosts containing '$computer_name':\n" . join("\n", @matching_computer_hosts_lines)); + return; + } + + my $matching_computer_hosts_line = $matching_computer_hosts_lines[0]; + notify($ERRORS{'DEBUG'}, 0, "found line for '$computer_name' in /etc/hosts:\n$matching_computer_hosts_line"); + + # Extract the IP address from the matching line + my ($ip_address) = $matching_computer_hosts_line =~ /\s*((?:[0-9]{1,3}\.?){4})\s+$computer_name/i; + + # Check if IP address was found + if (!$ip_address) { + notify($ERRORS{'WARNING'}, 0, "unable to determine IP address from line:\n$matching_computer_hosts_line"); + return; + } + + notify($ERRORS{'DEBUG'}, 0, "found IP address: $ip_address"); + + # Update the request data if subroutine was called as an object method without an argument + if ($self && !$argument) { + $self->set_computer_private_ip_address($ip_address); + } + + return $ip_address; +} + +#///////////////////////////////////////////////////////////////////////////// + +=head2 get_variable + + Parameters : $variable_name + Returns : If successful: reference to the data stored in the variable table for + the variable name specified + If failed: false + Description : Queries the variable table for the variable with the name specified by the subroutine argument. + Returns a reference to the data. + +=cut + +sub get_variable { + my $self = shift; + my $name = shift; + + # Check if subroutine was called as an object method + unless (ref($self) && $self->isa('VCL::DataStructure')) { + notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be called as a VCL::DataStructure module object method"); + return; + } + + # Check the arguments + if (!defined($name)) { + notify($ERRORS{'WARNING'}, 0, "variable name argument was not supplied"); + return; + } + + # Construct the select statement +my $select_statement .= <<"EOF"; +SELECT +variable.value +FROM +variable +WHERE +variable.name = '$name' +EOF + + # Call the database select subroutine + my @selected_rows = database_select($select_statement); + + # Check to make 1 sure row was returned + if (!...@selected_rows) { + notify($ERRORS{'WARNING'}, 0, "unable to execute select statement to retrieve variable '$name' from the database"); + return; + } + elsif (@selected_rows == 0){ + notify($ERRORS{'WARNING'}, 0, "variable '$name' does not exist in the database"); + return; + } + elsif (@selected_rows > 1){ + notify($ERRORS{'WARNING'}, 0, "multiple rows exist in the database for variable '$name':\n" . format_data(\...@selected_rows)); + return; + } + + # Get the serialized value from the variable row + my $serialized_value = $selected_rows[0]{value}; + + # Attempt to deserialize the value + # Use eval because Load() will call die() if it encounters an error + my $deserialized_value; + eval '$deserialized_value = YAML::Load($serialized_value)'; + if ($EVAL_ERROR) { + notify($ERRORS{'WARNING'}, 0, "unable to deserialize the value using YAML::Load(): $serialized_value"); + return; + } + + # Display the data type of the value retrieved from the variable table + if (my $deserialized_data_type = ref($deserialized_value)) { + notify($ERRORS{'DEBUG'}, 0, "returning $deserialized_data_type reference"); + } + else { + notify($ERRORS{'DEBUG'}, 0, "returning scalar ($deserialized_value)"); + } + + return $deserialized_value; +} + +#///////////////////////////////////////////////////////////////////////////// + +=head2 set_variable + + Parameters : $variable_name, $variable_value + Returns : If successful: true + If failed: false + Description : Inserts or updates a row in the variable table. This subroutine + will also update the variable.timestamp column. The + variable.setby column is automatically set to the filename and + line number which called this subroutine. + +=cut + +sub set_variable { + my $self = shift; + my $name_argument = shift; + my $value_argument = shift; + + # Check if subroutine was called as an object method + unless (ref($self) && $self->isa('VCL::DataStructure')) { + notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be called as a VCL::DataStructure module object method"); + return; + } + + # Check the arguments + if (!defined($name_argument)) { + notify($ERRORS{'WARNING'}, 0, "variable name argument was not supplied"); + return; + } + elsif (!defined($value_argument)) { + notify($ERRORS{'WARNING'}, 0, "variable value argument was not supplied"); + return; + } + + # Construct a string indicating where the variable was set from + my @caller = caller(0); + (my $calling_file) = $caller[1] =~ /([^\/]*)$/; + my $calling_line = $caller[2]; + my $caller_string = "$calling_file:$calling_line"; + + # Attempt to serialize the value using YAML::Dump() + # Use eval because Dump() will call die() if it encounters an error + my $serialized_value; + eval '$serialized_value = YAML::Dump($value_argument)'; + if ($EVAL_ERROR) { + notify($ERRORS{'WARNING'}, 0, "unable to serialize the value using YAML::Dump(): $value_argument"); + return; + } + + # Escape all single quote characters with a backslash + # or else the SQL statement will fail becuase it is wrapped in single quotes + $serialized_value =~ s/'/\\'/g;; + + # Assemble an insert statement, if the variable already exists, update the existing row + my $insert_statement .= <<"EOF"; +INSERT INTO variable +( +name, +value, +setby, +timestamp +) +VALUES +( +'$name_argument', +'$serialized_value', +'$caller_string', +NOW() +) +ON DUPLICATE KEY UPDATE +name=VALUES(name), +value=VALUES(value), +setby=VALUES(setby), +timestamp=VALUES(timestamp) +EOF + + # Execute the insert statement, the return value should be the id of the row + my $inserted_id = database_execute($insert_statement); + if ($inserted_id) { + notify($ERRORS{'OK'}, 0, "set variable '$name_argument', id: $inserted_id"); + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to set variable '$name_argument'"); + return; + } + + return 1; +} + +#///////////////////////////////////////////////////////////////////////////// + 1; __END__