Author: arkurth
Date: Thu May 11 23:10:34 2017
New Revision: 1794901
URL: http://svn.apache.org/viewvc?rev=1794901&view=rev
Log:
VCL-919
Extended DataStructure.pm::substitute_string_variables to allow the use of the
subroutines explicitly defined in DataStructure.pm such as
[is_parent_reservation].
Added subroutines:
* DataStructure.pm::get_invalid_substitution_identifiers
* utils.pm::get_message_variable_info
* utils.pm::message_needs_validating
Modified:
vcl/trunk/managementnode/lib/VCL/DataStructure.pm
vcl/trunk/managementnode/lib/VCL/utils.pm
Modified: vcl/trunk/managementnode/lib/VCL/DataStructure.pm
URL:
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/DataStructure.pm?rev=1794901&r1=1794900&r2=1794901&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/DataStructure.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/DataStructure.pm Thu May 11 23:10:34 2017
@@ -2642,26 +2642,33 @@ sub substitute_string_variables {
my ($subroutine_mapping_key) = $input_substitute_section =~
/^[^a-z]*([a-z][a-z_]+[a-z])[^a-z]*$/;
if (!$subroutine_mapping_key) {
notify($ERRORS{'CRITICAL'}, 0, "failed to extract
subroutine mapping key from section of input string matching substitution
identifier pattern '$variable_identifier_regex': '$input_substitute_section'");
- return;
+ next;
}
#notify($ERRORS{'DEBUG'}, 0, "extracted subroutine mapping key
from section of input string: '$input_substitute_section' -->
'$subroutine_mapping_key'");
# Attempt to retrieve the substitution value from the
DataStructure data
# Check if DataStructure.pm implements a matching 'get_'
function
- my $get_function_name = "get_$subroutine_mapping_key";
- if (!$self->can($get_function_name)) {
- notify($ERRORS{'CRITICAL'}, 0, "failed to determine
replacement value for substitution section: '$input_substitute_section',
DataStructure does not implement a '$get_function_name' function");
- return;
+ my $function_name = "get_$subroutine_mapping_key";
+ if (!$self->can($function_name)) {
+ # Check if implemented without 'get_'
+ # This allows explicit subroutines such as
is_server_request to be substituted
+ if ($self->can($subroutine_mapping_key)) {
+ $function_name = $subroutine_mapping_key;
+ }
+ else {
+ notify($ERRORS{'CRITICAL'}, 0, "failed to
determine replacement value for substitution section:
'$input_substitute_section', DataStructure does not implement a
'$function_name' function");
+ next;
+ }
}
# Assemble a code string to retrieve the value from the
DataStructure:
- my $eval_code = "\$self->$get_function_name(0)";
+ my $eval_code = "\$self->$function_name(0)";
# Evaluate the code string:
my $substitution_value = eval $eval_code;
if (!defined($substitution_value)) {
notify($ERRORS{'CRITICAL'}, 0, "failed to determine
replacement value for substitution section: '$input_substitute_section',
$eval_code returned undefined");
- return;
+ next;
}
notify($ERRORS{'DEBUG'}, 0, "determined replacement value for
substitution section: '$input_substitute_section', $eval_code =
'$substitution_value'");
@@ -2685,6 +2692,83 @@ sub substitute_string_variables {
}
#//////////////////////////////////////////////////////////////////////////////
+
+=head2 get_invalid_substitution_identifiers
+
+ Parameters : $input_string, $variable_identifier_regex (optional)
+ Returns : array
+ Description : Checks the input string for invalid substitution identifiers. A
+ substitution identifier is valid if it
corresponds to one of the
+ keys defined in %SUBROUTINE_MAPPINGS or
matches one of the
+ subroutines explicitly defined in
DataStructure.pm. Examples:
+ * [user_id]
+ * [computer_short_name]
+ * [is_parent_reservation]
+
+ An identifier is invalid if it contains a typo or doesn't match
+ any keys or subroutine names:
+ * [usr_id]
+ * [foo_bar]
+
+ If any invalid subroutines are found, an array is returned
+ containing the strings found in the input string which appear to
+ be intended as substitution identifiers but don't correlate:
+ ('[usr_id]', '[foo_bar]')
+
+ Note: This subroutine does not need to
be called as an object
+ method via $self->data. It's intended
to be called from utils.pm.
+=cut
+
+sub get_invalid_substitution_identifiers {
+ my $input_string = shift;
+ if (ref($input_string)) {
+ $input_string = shift;
+ }
+ if (!defined($input_string)) {
+ notify($ERRORS{'WARNING'}, 0, "input string argument was not
supplied");
+ return;
+ }
+ elsif (ref($input_string)) {
+ notify($ERRORS{'WARNING'}, 0, "input string argument is a
reference:\n" . format_data($input_string));
+ return;
+ }
+
+ my $variable_identifier_regex = shift;
+ if (!$variable_identifier_regex) {
+ $variable_identifier_regex = '\[[a-z][a-z_]+[a-z]\]';
+ }
+
+ my @invalid_string_variable_identifiers;
+
+ my @input_string_variable_identifiers = $input_string =~
/($variable_identifier_regex)/g;
+ for my $input_string_variable_identifier
(remove_array_duplicates(@input_string_variable_identifiers)) {
+ my ($subroutine_mapping_key) =
$input_string_variable_identifier =~ /^[^a-z]*([a-z][a-z_]+[a-z])[^a-z]*$/;
+ if (defined($SUBROUTINE_MAPPINGS{$subroutine_mapping_key})) {
+ next;
+ }
+ elsif (exists(&$subroutine_mapping_key)) {
+ notify($ERRORS{'DEBUG'}, 0, "explicit subroutine exists
in DataStructure.pm: $subroutine_mapping_key");
+ next;
+ }
+
+ my $get_subroutine_mapping_key = 'get_' .
$subroutine_mapping_key;
+ if (exists(&$get_subroutine_mapping_key)) {
+ notify($ERRORS{'DEBUG'}, 0, "explicit subroutine exists
in DataStructure.pm: $get_subroutine_mapping_key");
+ next;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "neither mapping key not
explicit subroutine exists in DataStructure.pm: $subroutine_mapping_key");
+ push @invalid_string_variable_identifiers,
$input_string_variable_identifier;
+ }
+ }
+
+ if (@invalid_string_variable_identifiers) {
+ notify($ERRORS{'WARNING'}, 0, "input string contains invalid
substitution identifiers: " . join(', ', @invalid_string_variable_identifiers)
. "\ninput string:\n$input_string");
+ }
+ return @invalid_string_variable_identifiers;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
1;
__END__
Modified: vcl/trunk/managementnode/lib/VCL/utils.pm
URL:
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/utils.pm?rev=1794901&r1=1794900&r2=1794901&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/utils.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/utils.pm Thu May 11 23:10:34 2017
@@ -167,6 +167,7 @@ our @EXPORT = qw(
get_management_node_requests
get_management_node_vmhost_ids
get_management_node_vmhost_info
+ get_message_variable_info
get_module_info
get_nathost_assigned_public_ports
get_natport_ranges
@@ -224,6 +225,7 @@ our @EXPORT = qw(
known_hosts
mail
makedatestring
+ message_needs_validating
nmap_port
normalize_file_path
notify
@@ -13274,10 +13276,11 @@ sub set_variable {
}
# Construct a string indicating where the variable was set from
+ my $management_node_name = get_management_node_info()->{SHORTNAME} ||
hostname;
my @caller = caller(0);
(my $calling_file) = $caller[1] =~ /([^\/]*)$/;
my $calling_line = $caller[2];
- my $caller_string = "$calling_file:$calling_line";
+ my $caller_string = "$management_node_name:$calling_file:$calling_line";
# Attempt to serialize the value if necessary
my $database_value;
@@ -15036,6 +15039,208 @@ sub get_collapsed_hash_reference {
}
#//////////////////////////////////////////////////////////////////////////////
+
+=head2 get_message_variable_info
+
+ Parameters : none
+ Returns : hash reference
+ Description : Retrieves info for all adminmessage and usermessage entries from
+ the variable table. A hash is constructed. The keys are the
+ variable.name values:
+ {
+ "usermessage|endtime_reached|Global" => {
+ "id" => 957,
+ "serialization" => "yaml",
+ "setby" => undef,
+ "timestamp" => "0000-00-00 00:00:00",
+ "value" => "---\nmessage: |\n Your reservation of
[image_prettyname]\nshort_message: ~\nsubject: 'VCL -- End of reservation'\n"
+ },
+ "usermessage|reserved|Global" => {
+ "id" => 973,
+ "serialization" => "yaml",
+ "setby" => undef,
+ "timestamp" => "0000-00-00 00:00:00",
+ "value" => "---\nmessage: |\n The
rer_affiliation_sitewwwaddress]..."
+ },
+ ...
+ }
+
+=cut
+
+sub get_message_variable_info {
+ my $select_statement = <<EOF;
+SELECT
+*
+FROM
+variable
+WHERE
+variable.name REGEXP '^(admin|user)message'
+EOF
+
+ my @rows = database_select($select_statement);
+
+ my $info = {};
+ for my $row (@rows) {
+ my $variable_name = $row->{name};
+ $info->{$variable_name} = $row;
+ delete $info->{$variable_name}{name};
+ }
+
+ notify($ERRORS{'DEBUG'}, 0, "retrieved info for all admin and user
messages from the variable table:\n" . format_data($info));
+ return $info;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 message_needs_validating
+
+ Parameters : none
+ Returns : boolean
+ Description : If the variable table has an entry named
+ 'usermessage_needs_validating' with a value is set to 1 the
+ management node's name, this subroutine proceeds to:
+ * Set value of 'usermessage_needs_validating' to its hostname so
+ that other management nodes don't attempt to validate the
+ messages at the same time
+ * Retrieves all of the adminmessage and usermessage rows from
the
+ variable table
+ * Checks the subject, message, and short_message strings
+ contained in variable.value for invalid substitution
+ identifiers (strings such as [request_foo] which don't
+ correspond to a function that DataStructure.pm implements)
+ * If any invalid identifiers are found, a
+ 'usermessage_invalid_fields' variable is added with a value
+ containing a hash reference:
+ {
+ "adminmessage|image_creation_started" => {
+ "message" => [
+ "[this_be_invalid_yo]"
+ ]
+ },
+ "usermessage|endtime_reached|Local" => {
+ "message" => [
+ "[is_blah]"
+ ],
+ "subject" => [
+ "[usr_login_id]"
+ ]
+ }
+ }
+
+ If no invalid fields are found, the 'usermessage_invalid_fields'
+ variable will be deleted if it was previously created.
+
+ After validating is complete, the 'usermessage_needs_validating'
+ variable is deleted.
+
+=cut
+
+sub message_needs_validating {
+ my $management_node_name = get_management_node_info()->{SHORTNAME} ||
hostname;
+
+ my $validate_variable_name = 'usermessage_needs_validating';
+ my $invalid_variable_name = 'usermessage_invalid_fields';
+
+ # Just get the id to minimize this query since it runs with every vcld
daemon loop
+ my $select_id_statement = <<EOF;
+SELECT
+id
+FROM
+variable
+WHERE
+variable.name = '$validate_variable_name'
+EOF
+ my @id_rows = database_select($select_id_statement);
+ if (!@id_rows) {
+ notify($ERRORS{'DEBUG'}, 0, "admin and user message variables
to NOT need to be validated, $validate_variable_name variable does NOT exist in
the database");
+ return 0;
+ }
+ my $validate_variable_id = $id_rows[0]->{id};
+ notify($ERRORS{'DEBUG'}, 0, "$validate_variable_name variable exists in
the database, ID: $validate_variable_id, attempting to update its value to
'$management_node_name'");
+
+
+ # Set the $validate_variable_name entry's variable.value to the
management node name
+ my $update_statement = <<EOF;
+UPDATE
+variable
+SET
+value = '$management_node_name',
+timestamp = NOW()
+WHERE
+variable.id = $validate_variable_id
+AND (
+ variable.value = '1'
+ OR variable.value = '$management_node_name'
+)
+EOF
+ my $update_result = database_execute($update_statement);
+ if ($update_result) {
+ notify($ERRORS{'OK'}, 0, "attempted to update value of
$validate_variable_name variable (ID: $validate_variable_id) to
'$management_node_name', verifying the update was successful");
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to update value of
$validate_variable_name variable (ID: $validate_variable_id) to
'$management_node_name', another management node may have processed the
verification and deleted the entry");
+ return;
+ }
+
+ # Make sure the value was updated, database_execute returns true if the
syntax is valid but 0 rows were updated
+ my $verify_statement = <<EOF;
+SELECT
+*
+FROM
+variable
+WHERE
+variable.id = $validate_variable_id
+EOF
+ my @verify_rows = database_select($verify_statement);
+ if (!@verify_rows) {
+ notify($ERRORS{'WARNING'}, 0, "failed to verify the value of
$validate_variable_name variable (ID: $validate_variable_id) was updated to
'$management_node_name', another management node may have processed the
verification and deleted the entry");
+ return;
+ }
+ if ($verify_rows[0]->{value} && $verify_rows[0]->{value} eq
$management_node_name) {
+ notify($ERRORS{'DEBUG'}, 0, "admin and user message variables
need to be validated, updated value of $validate_variable_name variable to
'$verify_rows[0]->{value}'");
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to update the value of
$validate_variable_name variable (ID: $validate_variable_id) to
'$management_node_name', its current value is '$verify_rows[0]->{value}',
another management node may have started processing the verification");
+ return 0;
+ }
+
+ # Check all of the admin and user messages
+ my $variable_info = get_message_variable_info();
+ my $invalid_info;
+ for my $variable_name (sort keys %$variable_info) {
+ my $yaml_string_value = $variable_info->{$variable_name}{value};
+ my $hash_reference_value = yaml_deserialize($yaml_string_value);
+ if (!defined($hash_reference_value)) {
+ notify($ERRORS{'WARNING'}, 0, "unable to validate
'$variable_name' variable, YAML string could not be
deserialized:\n$yaml_string_value");
+ next;
+ }
+ elsif (!ref($hash_reference_value) ||
ref($hash_reference_value) ne 'HASH') {
+ notify($ERRORS{'WARNING'}, 0, "unable to validate
'$variable_name' variable, deserialized value is not a hash reference:\n" .
format_data($hash_reference_value));
+ next;
+ }
+
+ for my $key ('subject', 'message', 'short_message') {
+ my $input_string = $hash_reference_value->{$key} ||
next;
+ notify($ERRORS{'DEBUG'}, 0, "validating substitution
identifiers contained in '$variable_name' $key");
+ my @invalid_substitution_identifiers =
VCL::DataStructure::get_invalid_substitution_identifiers($input_string);
+ if (@invalid_substitution_identifiers) {
+ $invalid_info->{$variable_name}{$key} =
\@invalid_substitution_identifiers;
+ }
+ }
+ }
+
+ if ($invalid_info) {
+ notify($ERRORS{'WARNING'}, 0, "message variables with invalid
substitution identifiers found, setting '$invalid_variable_name' variable with
value:\n" . format_data($invalid_info));
+ set_variable($invalid_variable_name, $invalid_info);
+ }
+ else {
+ delete_variable($invalid_variable_name);
+ }
+ delete_variable($validate_variable_name);
+ return 1;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
1;