Author: arkurth
Date: Tue Dec  9 21:11:37 2014
New Revision: 1644185

URL: http://svn.apache.org/r1644185
Log:
VCL-174
Added backend support for NAT.

Added entries to $SUBROUTINE_MAPPINGS hash in DataStructure.pm for new 
NAT-related database tables.

Added subroutines:
-Module.pm::create_nathost_os_object
-Module.pm::nathost_os
-Module.pm::set_nathost_os subroutines.
-utils.pm::get_computer_nathost_info
-utils.pm::get_nathost_assigned_public_ports
-utils.pm::populate_reservation_natport
-utils.pm::insert_natport
-utils.pm::get_reservation_request_id
-utils.pm::get_reservation_request_info
-Linux.pm::firewall
-Linux.pm::enable_ip_forwarding

Updated utils.pm::get_connect_method_info to retrieve natport information.

Added code to State.pm::initialize to create a NAT host OS object if necessary.

Updated Module.pm::create_mn_os_object to pass the reservation ID to the 
DataStructure.pm constructor if it is available.

Updated utils.pm::get_request_info to populate the natport table.

Updated utils.pm::get_computer_info to also retrieve NAT host info.

Added modularized Linux firewall framework. This will eventually dynamically 
figure out which Linux firewall commands to use. For now, only iptables is 
supported. Added Linux/firewall directory and iptables.pm.

Added code to process_connect_methods to check if NAT is used. If so, a NAT 
port forwarding is added for each connect method port.

Added block to reclaim.pm::process to delete the NAT rules created for a 
reservation if necessary.

Updated all places that use results from connect_method_info to handle new 
format of hash returned.


Other
Fixed sloppy indentation in reclaim.pm. Please stop this nonsense.

Updated Module.pm::new to attempt to use an existing ManagementNode.pm object 
if one exists.

Added init.pm file, which is not the parent class of the Linux init modules. 
Updated Linux.pm::get_init_modules to pass a "base_package" argument to the 
init module constructor. Changed base package in init modules from 
VCL::Module::OS::Linux to VCL::Module::OS::Linux::init.

Added:
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init.pm
Modified:
    vcl/trunk/managementnode/lib/VCL/DataStructure.pm
    vcl/trunk/managementnode/lib/VCL/Module.pm
    vcl/trunk/managementnode/lib/VCL/Module/OS.pm
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/SysV.pm
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/Upstart.pm
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/systemd.pm
    vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm
    vcl/trunk/managementnode/lib/VCL/Module/State.pm
    vcl/trunk/managementnode/lib/VCL/new.pm
    vcl/trunk/managementnode/lib/VCL/reclaim.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=1644185&r1=1644184&r2=1644185&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/DataStructure.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/DataStructure.pm Tue Dec  9 21:11:37 2014
@@ -246,6 +246,17 @@ $SUBROUTINE_MAPPINGS{computer_predictive
 $SUBROUTINE_MAPPINGS{computer_predictive_module_description} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{predictive}{module}{description}';
 $SUBROUTINE_MAPPINGS{computer_predictive_module_perl_package} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{predictive}{module}{perlpackage}';
 
+$SUBROUTINE_MAPPINGS{nathost_info} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{nathost}';
+$SUBROUTINE_MAPPINGS{nathost_hostname} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{nathost}{HOSTNAME}';
+$SUBROUTINE_MAPPINGS{nathost_date_deleted} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{nathost}{datedeleted}';
+$SUBROUTINE_MAPPINGS{nathost_deleted} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{nathost}{deleted}';
+$SUBROUTINE_MAPPINGS{nathost_id} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{nathost}{id}';
+$SUBROUTINE_MAPPINGS{nathost_nat_ip} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{nathost}{natIP}';
+$SUBROUTINE_MAPPINGS{nathost_resource_id} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{nathost}{resource}{id}';
+$SUBROUTINE_MAPPINGS{nathost_resource_subid} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{nathost}{resource}{subid}';
+$SUBROUTINE_MAPPINGS{nathost_resourcetype_id} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{nathost}{resource}{resourcetype}{id}';
+$SUBROUTINE_MAPPINGS{nathost_resource_type} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{nathost}{resource}{resourcetype}{name}';
+
 $SUBROUTINE_MAPPINGS{vmhost_computer_id} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{vmhost}{computerid}';
 $SUBROUTINE_MAPPINGS{vmhost_hostname} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{vmhost}{computer}{hostname}';
 $SUBROUTINE_MAPPINGS{vmhost_short_name} = 
'$self->request_data->{reservation}{RESERVATION_ID}{computer}{vmhost}{computer}{SHORTNAME}';
@@ -682,7 +693,7 @@ sub _initialize : Init {
                }
                
$self->request_data->{reservation}{$self->reservation_id}{computer}{vmhost} = 
$vmhost_info;
        }
-       
+
        # If either the computer, image, or imagerevision identifier arguments 
are specified, retrieve appropriate image and imagerevision data
        if (defined($imagerevision_identifier) || defined($image_identifier) || 
defined($computer_identifier)) {
                my $imagerevision_info;
@@ -1126,46 +1137,45 @@ sub get_reservation_data {
 
 =head2 set_reservation_remote_ip
 
- Parameters  : None
- Returns     : string
- Description : 
+ Parameters  : $remote_ip
+ Returns     : boolean
+ Description : Updates the reservation.remoteIP value in the database.
 
 =cut
 
 sub set_reservation_remote_ip {
-   my $self = shift;
-   my $reservation_id  = $self->get_reservation_id();
+       my $self = shift;
+       my $reservation_id = $self->get_reservation_id();
        
-       my $new_remote_ip = shift;
+       my $remote_ip = shift;
        
        # Check to make sure reservation ID was passed
-   if (!$new_remote_ip) {
-        notify($ERRORS{'WARNING'}, 0, "new_remote_ip was not specified, 
returning self");
-        return 0;;
-   }
-
-       
-       my $update_statement = "
-                 UPDATE
-                 reservation
-                 SET
-                 remoteIP = \'$new_remote_ip\'
-                 WHERE
-                 id = \'$reservation_id\'
-                         ";
-
-        # Call the database execute subroutine
-        if (database_execute($update_statement)) {
-                # Update successful
-                notify($ERRORS{'OK'}, 0, "new remoteIP $new_remote_ip for 
reservation id $reservation_id updated");
-                return 1;
-        }
-        else {
-                notify($ERRORS{'CRITICAL'}, 0, "unable to update new remote ip 
for reservation id $reservation_id");
-                return 0;
-        }
-
-
+       if (!$remote_ip) {
+               notify($ERRORS{'WARNING'}, 0, "remote IP address argument was 
not specified");
+               return 0;
+       }
+       
+       # Set the current value in the request data hash
+       $self->request_data->{reservation}{$reservation_id}{remoteIP} = 
$remote_ip;
+       
+       my $update_statement = <<EOF;
+UPDATE
+reservation
+SET
+remoteIP = '$remote_ip'
+WHERE
+id = '$reservation_id'
+EOF
+       
+       # Call the database execute subroutine
+       if (database_execute($update_statement)) {
+               notify($ERRORS{'OK'}, 0, "remote IP updated to $remote_ip for 
reservation $reservation_id");
+               return 1;
+       }
+       else {
+               notify($ERRORS{'CRITICAL'}, 0, "failed to update remote IP to 
$remote_ip for reservation $reservation_id");
+               return 0;
+       }
 } ## end sub set_reservation_remote_ip
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -1180,7 +1190,7 @@ sub set_reservation_remote_ip {
 
 sub get_reservation_remote_ip {
        my $self = shift;
-       my $reservation_id  = $self->get_reservation_id();
+       my $reservation_id = $self->get_reservation_id();
 
        # Create the select statement
        my $select_statement = "

Modified: vcl/trunk/managementnode/lib/VCL/Module.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module.pm?rev=1644185&r1=1644184&r2=1644185&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module.pm Tue Dec  9 21:11:37 2014
@@ -183,7 +183,7 @@ sub new {
                my $request_state_name = $self->data->get_request_state_name(0) 
|| '<not set>';
                notify($ERRORS{'DEBUG'}, 0, ref($self) . " object created for 
state $request_state_name, address: $address");
        }
-       elsif ($self->isa('VCL::Module::OS')) {
+       elsif ($self->isa('VCL::Module::OS') && 
!$self->isa('VCL::Module::OS::Linux::ManagementNode')) {
                my $image_name = $self->data->get_image_name(0) || '<not set>';
                notify($ERRORS{'DEBUG'}, 0, ref($self) . " object created for 
image $image_name, address: $address");
        }
@@ -203,8 +203,11 @@ sub new {
                if ($args->{mn_os}) {
                        $mn_os = $args->{mn_os};
                }
+               elsif ($self->mn_os(0)) {
+                       $mn_os = $self->mn_os();
+               }
                else {
-                       $mn_os = $self->create_mn_os_object()
+                       $mn_os = $self->create_mn_os_object();
                }
                
                if ($mn_os) {
@@ -316,10 +319,17 @@ sub create_object {
        my $perl_package = $argument;
        
        my $data;
-       if (my $data_structure_arguments = shift || !$self) {
+       my $data_structure_arguments = shift;
+       if ($data_structure_arguments) {
+               notify($ERRORS{'DEBUG'}, 0, "new DataStructure object will be 
created for the $perl_package object, data structure arguments passed:\n" . 
format_data($data_structure_arguments));
                $data = create_datastructure_object($data_structure_arguments);
        }
+       elsif (!$self) {
+               notify($ERRORS{'DEBUG'}, 0, "new DataStructure object will be 
created for the $perl_package object, data structure arguments not passed and 
not called as an object reference");
+               $data = create_datastructure_object();
+       }
        elsif ($self) {
+               notify($ERRORS{'DEBUG'}, 0, "existing DataStructure object will 
be passed to the new $perl_package object");
                $data = $self->data;
        }
 
@@ -441,10 +451,24 @@ sub create_os_object {
 =cut
 
 sub create_mn_os_object {
+       my $self = shift;
+       
+       my $datastructure_arguments = {
+               'image_identifier' => 'noimage'
+       };
+       
+       # Check if called as an object reference
+       if ($self && ref($self) =~ /VCL/) {
+               # Add the reservation ID to the DataStructure arguments
+               # Otherwise, get_reservation_id won't be available
+               my $reservation_id = $self->data->get_reservation_id();
+               $datastructure_arguments->{reservation_id} = $reservation_id;
+       }
+       
        # Create a DataStructure object containing computer data for the 
management node
        my $mn_data;
        eval {
-               $mn_data = new VCL::DataStructure('image_identifier' => 
'noimage');
+               $mn_data = new VCL::DataStructure($datastructure_arguments);
        };
        
        # Attempt to load the OS module
@@ -572,6 +596,83 @@ sub create_vmhost_os_object {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 create_nathost_os_object
+
+ Parameters  : none
+ Returns     : VCL::Module::OS object reference
+ Description : Creates an OS module object to control the reservation 
computer's
+               NAT host.
+
+=cut
+
+sub create_nathost_os_object {
+my $self = shift;
+       unless (ref($self) && $self->isa('VCL::Module')) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       # Check if an OS object has already been stored in the calling object
+       if (my $nathost_os = $self->nathost_os(0)) {
+               return $nathost_os;
+       }
+       
+       notify($ERRORS{'DEBUG'}, 0, "attempting to create NAT host OS object");
+       
+       # Make sure calling object isn't an OS module to avoid an infinite loop
+       if ($self->isa('VCL::Module::OS')) {
+               notify($ERRORS{'WARNING'}, 0, "this subroutine cannot be called 
from an existing OS module: " . ref($self));
+               return;
+       }
+       
+       # Make sure computer is mapped to a NAT host
+       my $nathost_id = $self->data->get_nathost_id();
+       if (!$nathost_id) {
+               notify($ERRORS{'WARNING'}, 0, "NAT host OS object not created, 
computer is not mapped to a NAT host");
+               return;
+       }
+       
+       my $request_data = $self->data->get_request_data();
+       my $reservation_id = $self->data->get_reservation_id();
+       
+       my $nathost_hostname = $self->data->get_nathost_hostname();
+       my $nathost_resource_subid = $self->data->get_nathost_resource_subid();
+       my $nathost_resource_type = $self->data->get_nathost_resource_type();
+       if ($nathost_resource_type eq 'managementnode') {
+               notify($ERRORS{'DEBUG'}, 0, "NAT host resource type is 
$nathost_resource_type, returning management node OS object to control 
$nathost_hostname");
+               return $self->mn_os();
+       }
+       elsif ($nathost_resource_type eq 'computer') {
+               # Get the computer info in order to determine the OS module to 
use
+               my $computer_info = get_computer_info($nathost_resource_subid);
+               if (!$computer_info) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to create NAT 
host OS object, failed to retrieve info for computer ID: 
$nathost_resource_subid, NAT host info:\n" . 
format_data($self->data->get_nathost_info()));
+               }
+               my $computer_os_package = 
$computer_info->{currentimagerevision}{image}{OS}{module}{perlpackage};
+               
+               notify($ERRORS{'DEBUG'}, 0, "NAT host resource type is 
$nathost_resource_type, creating $computer_os_package OS object to control 
$nathost_hostname");
+               
+               my $nathost_os = $self->create_object($computer_os_package, {
+                       #request_data => $request_data,
+                       reservation_id => $reservation_id,
+                       computer_identifier => $nathost_resource_subid
+               });
+               if ($nathost_os) {
+                       return $nathost_os;
+               }
+               else {
+                       notify($ERRORS{'WARNING'}, 0, "failed to create NAT 
host OS object to control $nathost_hostname");
+                       return;
+               }
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "unable to create NAT host OS 
object to control $nathost_hostname, NAT host resource type is not supported: 
$nathost_resource_type, NAT host info:\n" . 
format_data($self->data->get_nathost_info()));
+               return;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 create_provisioning_object
 
  Parameters  : none
@@ -807,6 +908,39 @@ sub vmhost_os {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 nathost_os
+
+ Parameters  : $display_warning (optional)
+ Returns     : NAT hosts's OS object
+ Description : Allows modules to access the NAT host's OS object.
+
+=cut
+
+sub nathost_os {
+       my $self = shift;
+       if (!ref($self) || !$self->isa('VCL::Module')) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was not called as a 
VCL::Module or VCL::DataStructure class method");
+               return;
+       }
+       
+       my $display_warning = shift;
+       if (!defined($display_warning)) {
+               $display_warning = 1;
+       }
+       
+       if (!$self->{nathost_os}) {
+               if ($display_warning) {
+                       notify($ERRORS{'WARNING'}, 0, "unable to return NAT 
host OS object, \$self->{nathost_os} is not set");
+               }
+               return;
+       }
+       else {
+               return $self->{nathost_os};
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 set_data
 
  Parameters  : $data
@@ -935,6 +1069,41 @@ sub set_vmhost_os {
        return 1;
 }
 
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 set_nathost_os
+
+ Parameters  : $nathost_os
+ Returns     : boolean
+ Description : Sets the NAT host OS object for the module to access.
+
+=cut
+
+sub set_nathost_os {
+       my $self = shift;
+       if (!ref($self) || !$self->isa('VCL::Module')) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was not called as a 
VCL::Module class method");
+               return;
+       }
+       
+       my $nathost_os = shift;
+       if (!defined($nathost_os)) {
+               notify($ERRORS{'WARNING'}, 0, "OS object reference argument not 
supplied");
+               return;
+       }
+       elsif (!ref($nathost_os) || !$nathost_os->isa('VCL::Module')) {
+               notify($ERRORS{'WARNING'}, 0, "supplied argument is not a 
VCL::Module object reference:\n" . format_data($nathost_os));
+               return;
+       }
+       
+       my $address = sprintf('%x', $self);
+       my $type = ref($self);
+       my $nathost_os_address = sprintf('%x', $nathost_os);
+       notify($ERRORS{'DEBUG'}, 0, "storing reference to NAT host OS object 
(address: $nathost_os_address) in this $type object (address: $address)");
+       $self->{nathost_os} = $nathost_os;
+       return 1;
+}
+
 #/////////////////////////////////////////////////////////////////////////////
 
 =head2 set_provisioner

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS.pm?rev=1644185&r1=1644184&r2=1644185&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS.pm Tue Dec  9 21:11:37 2014
@@ -2111,7 +2111,7 @@ sub remove_lines_from_file {
 =cut
 
 sub execute {
-#return execute_new(@_);
+return execute_new(@_);
        my ($argument) = @_;
        my ($computer_name, $command, $display_output, $timeout_seconds, 
$max_attempts, $port, $user, $password, $identity_key, $ignore_error);
        
@@ -2653,6 +2653,8 @@ sub process_connect_methods {
                return;
        }
        
+       my $reservation_id = $self->data->get_reservation_id();
+       my $request_state = $self->data->get_request_state_name();
        my $computer_node_name = $self->data->get_computer_node_name();
        
        # Retrieve the connect method info hash
@@ -2661,7 +2663,26 @@ sub process_connect_methods {
                notify($ERRORS{'WARNING'}, 0, "failed to retrieve connect 
method info");
                return;
        }
-
+       
+       # Check if NAT is used
+       my $nathost_hostname;
+       my $computer_private_ip_address;
+       if ($self->nathost_os(0)) {
+               $nathost_hostname = $self->data->get_nathost_hostname();
+               # Call configure_nat - this adds a chain for the reservation if 
one does not already exist
+               if (!$self->nathost_os->firewall->configure_nat()) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to configure NAT 
on $nathost_hostname");
+                       return;
+               }
+               
+               # Retrieve the computer's private IP address
+               $computer_private_ip_address = $self->get_private_ip_address();
+               if (!$computer_private_ip_address) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to retrieve 
private IP address of computer $computer_node_name, unable to configure NAT 
port forwarding");
+                       return;
+               }
+       }
+       
        my $remote_ip = shift;
        if (!$remote_ip) {
                notify($ERRORS{'OK'}, 0, "reservation remote IP address is not 
defined, connect methods will be available from any IP address");
@@ -2681,11 +2702,8 @@ sub process_connect_methods {
                $overwrite = 0;
        }
        
-       my $request_state = $self->data->get_request_state_name();
-       
        CONNECT_METHOD: for my $connect_method_id (sort keys 
%{$connect_method_info} ) {
                my $connect_method = $connect_method_info->{$connect_method_id};
-               #notify($ERRORS{'DEBUG'}, 0, "processing connect method:\n" . 
format_data($connect_method_info->{$connect_method_id}));
                
                my $name            = $connect_method->{name};
                my $description     = $connect_method->{description};
@@ -2693,7 +2711,6 @@ sub process_connect_methods {
                my $startup_script  = $connect_method->{startupscript};
                my $install_script  = $connect_method->{installscript};
                my $disabled        = 
$connect_method->{connectmethodmap}{disabled};
-
                
                if ($disabled || $request_state =~ /deleted|timeout/) {
                        if ($self->service_exists($service_name)) {
@@ -2704,11 +2721,11 @@ sub process_connect_methods {
                        
                        # Close the firewall ports
                        if ($self->can('disable_firewall_port')) {
-                               for my $protocol (keys 
%{$connect_method->{connectmethodport}}) {
-                                       for my $port (keys 
%{$connect_method->{connectmethodport}{$protocol}}) {
-                                               if 
(!$self->disable_firewall_port($protocol, $port, $remote_ip, 1)) {
-                                                       
notify($ERRORS{'WARNING'}, 0, "failed to close firewall port $port on 
$computer_node_name for $remote_ip $name connect method");
-                                               }
+                               for my $connect_method_port_id (keys 
%{$connect_method->{connectmethodport}}) {
+                                       my $protocol = 
$connect_method->{connectmethodport}{$connect_method_port_id}{protocol};
+                                       my $port = 
$connect_method->{connectmethodport}{$connect_method_port_id}{port};
+                                       if 
(!$self->disable_firewall_port($protocol, $port, $remote_ip, 1)) {
+                                               notify($ERRORS{'WARNING'}, 0, 
"failed to close firewall port $protocol/$port on $computer_node_name for 
$remote_ip $name connect method");
                                        }
                                }
                        }
@@ -2752,15 +2769,35 @@ sub process_connect_methods {
                                }
                        }
                        
-                       # Open the firewall ports
-                       if ($self->can('enable_firewall_port')) {
-                               for my $protocol (keys 
%{$connect_method->{connectmethodport}}) {
-                                       for my $port (keys 
%{$connect_method->{connectmethodport}{$protocol}}) {
-                                               if 
(!$self->enable_firewall_port($protocol, $port, $remote_ip, 1)) {
-                                                       
notify($ERRORS{'WARNING'}, 0, "failed to open firewall port $port on 
$computer_node_name for $remote_ip $name connect method");
-                                               }
+                       for my $connect_method_port_id (keys 
%{$connect_method->{connectmethodport}}) {
+                               my $protocol = 
$connect_method->{connectmethodport}{$connect_method_port_id}{protocol};
+                               my $port = 
$connect_method->{connectmethodport}{$connect_method_port_id}{port};
+                               
+                               # Open the firewall port
+                               if ($self->can('enable_firewall_port')) {
+                                       if 
(!$self->enable_firewall_port($protocol, $port, $remote_ip, 1)) {
+                                               notify($ERRORS{'WARNING'}, 0, 
"failed to open firewall port $protocol/$port on $computer_node_name for 
$remote_ip $name connect method");
+                                       }
+                               }
+                               
+                               my $nat_public_port = 
$connect_method->{connectmethodport}{$connect_method_port_id}{natport}{publicport};
+                               if ($nat_public_port) {
+                                       if (!$self->nathost_os(0)) {
+                                               notify($ERRORS{'WARNING'}, 0, 
"connect method info contains NAT port information but NAT OS object is not 
available to control $nathost_hostname");
+                                               return;
+                                       }
+                                       if 
($self->nathost_os->firewall->add_nat_port_forward($protocol, $nat_public_port, 
$computer_private_ip_address, $port, $reservation_id)) {
+                                               notify($ERRORS{'OK'}, 0, 
"configured forwarded NAT port on $nathost_hostname: $protocol/$nat_public_port 
--> $computer_private_ip_address:$port");
+                                       }
+                                       else {
+                                               notify($ERRORS{'WARNING'}, 0, 
"failed to process '$name' connect method, unable to configure forwarded NAT 
port on $nathost_hostname: $protocol/$nat_public_port --> 
$computer_private_ip_address:$port");
+                                               return;
                                        }
                                }
+                               elsif ($self->nathost_os(0)) {
+                                       notify($ERRORS{'WARNING'}, 0, "NAT OS 
object is not available but connect method info does not contain NAT port 
information:\n" . format_data($connect_method_info));
+                                       return;
+                               }
                        }
                }
        }
@@ -2800,15 +2837,16 @@ sub is_user_connected {
        foreach my $connect_method_id (keys %$connect_methods) {
                my $connect_method = $connect_methods->{$connect_method_id};
                my $name = $connect_method->{name};
-               for my $protocol (keys %{$connect_method->{connectmethodport}}) 
{
-                       for my $port (keys 
%{$connect_method->{connectmethodport}{$protocol}}) {
-                               notify($ERRORS{'DEBUG'}, 0, "checking '$name' 
connect method, protocol: $protocol, port: $port");
-                               
-                               my $result = 
$self->check_connection_on_port($port);
-                               if ($result && $result !~ /no/i) {
-                                       notify($ERRORS{'OK'}, 0, 
"$user_login_id is connected to $computer_node_name using $name connect method, 
result: $result");
-                                       return 1;
-                               }
+               
+               for my $connect_method_port_id (keys 
%{$connect_method->{connectmethodport}}) {
+                       my $protocol = 
$connect_method->{connectmethodport}{$connect_method_port_id}{protocol};
+                       my $port = 
$connect_method->{connectmethodport}{$connect_method_port_id}{port};
+                       
+                       notify($ERRORS{'DEBUG'}, 0, "checking '$name' connect 
method, protocol: $protocol, port: $port");
+                       my $result = $self->check_connection_on_port($port);
+                       if ($result && $result !~ /no/i) {
+                               notify($ERRORS{'OK'}, 0, "$user_login_id is 
connected to $computer_node_name using $name connect method, result: $result");
+                               return 1;
                        }
                }
        }
@@ -3544,54 +3582,54 @@ sub get_connect_method_remote_ip_address
        my @remote_ip_addresses = ();
        
        my $connect_method_info = $self->data->get_connect_methods();
-       foreach my $connect_method_id (keys %$connect_method_info) {
-               my $connect_method = $connect_method_info->{$connect_method_id};
-               my $connect_method_name = $connect_method->{name};
+       for my $connect_method_id (keys %$connect_method_info) {
+               my $connect_method_name = 
$connect_method_info->{$connect_method_id}{name};
                
-               for my $connect_method_protocol (keys 
%{$connect_method->{connectmethodport}}) {
-                       for my $connect_method_port (keys 
%{$connect_method->{connectmethodport}{$connect_method_protocol}}) {
-                               notify($ERRORS{'DEBUG'}, 0, "checking connect 
method: '$connect_method_name', protocol: $connect_method_protocol, port: 
$connect_method_port");
-                               
-                               CONNECTION_PROTOCOL: for my 
$connection_protocol (keys %$connection_info) {
-                                       # Check if the protocol defined for the 
connect method matches the established connection
-                                       if (!$connect_method_protocol || 
$connect_method_protocol =~ /(\*|any|all)/i) {
-                                               #notify($ERRORS{'DEBUG'}, 0, 
"skipping validation of connect method protocol: $connect_method_protocol");
+               for my $connect_method_port_id (sort keys 
%{$connect_method_info->{$connect_method_id}{connectmethodport}}) {
+                       my $connect_method_protocol = 
$connect_method_info->{$connect_method_id}{connectmethodport}{$connect_method_port_id}{protocol};
+                       my $connect_method_port = 
$connect_method_info->{$connect_method_id}{connectmethodport}{$connect_method_port_id}{port};
+                       
+                       notify($ERRORS{'DEBUG'}, 0, "checking connect method: 
'$connect_method_name', protocol: $connect_method_protocol, port: 
$connect_method_port");
+                       
+                       CONNECTION_PROTOCOL: for my $connection_protocol (keys 
%$connection_info) {
+                               # Check if the protocol defined for the connect 
method matches the established connection
+                               if (!$connect_method_protocol || 
$connect_method_protocol =~ /(\*|any|all)/i) {
+                                       #notify($ERRORS{'DEBUG'}, 0, "skipping 
validation of connect method protocol: $connect_method_protocol");
+                               }
+                               else {
+                                       if ($connect_method_protocol =~ 
/$connection_protocol/i || $connection_protocol =~ /$connect_method_protocol/i) 
{
+                                               notify($ERRORS{'DEBUG'}, 0, 
"connect method protocol matches established connection protocol: 
$connection_protocol");
                                        }
                                        else {
-                                               if ($connect_method_protocol =~ 
/$connection_protocol/i || $connection_protocol =~ /$connect_method_protocol/i) 
{
-                                                       
notify($ERRORS{'DEBUG'}, 0, "connect method protocol matches established 
connection protocol: $connection_protocol");
-                                               }
-                                               else {
-                                                       
notify($ERRORS{'DEBUG'}, 0, "connect method protocol $connect_method_protocol 
does NOT match established connection protocol $connection_protocol");
-                                                       next 
CONNECTION_PROTOCOL;
-                                               }
+                                               notify($ERRORS{'DEBUG'}, 0, 
"connect method protocol $connect_method_protocol does NOT match established 
connection protocol $connection_protocol");
+                                               next CONNECTION_PROTOCOL;
                                        }
-                                       
-                                       CONNECTION_PORT: for my 
$connection_port (keys %{$connection_info->{$connection_protocol}}) {
-                                               # Check if the port defined for 
the connect method matches the established connection
-                                               if ($connect_method_port eq 
$connection_port) {
-                                                       
notify($ERRORS{'DEBUG'}, 0, "connect method port matches established connection 
port: $connection_port");
-                                                       
-                                                       for my $connection 
(@{$connection_info->{$connection_protocol}{$connection_port}}) {
-                                                               my 
$remote_ip_address = $connection->{remote_ip};
-                                                               if 
(!$remote_ip_address) {
-                                                                       
notify($ERRORS{'WARNING'}, 0, "connection does NOT contain remote IP address 
(remote_ip) key:\n" . format_data($connection));
-                                                               }
-                                                               elsif 
($remote_ip_address eq $mn_private_ip_address || $remote_ip_address eq 
$mn_public_ip_address) {
-                                                                       
notify($ERRORS{'DEBUG'}, 0, "ignoring connection to port $connection_port from 
management node: $remote_ip_address");
-                                                               }
-                                                               elsif (my 
($ignored_remote_ip_address) = grep { $remote_ip_address =~ /($_)/ } 
@ignored_remote_ip_addresses) {
-                                                                       
notify($ERRORS{'DEBUG'}, 0, "ignoring connection to port $connection_port from 
ignored remote IP address ($ignored_remote_ip_address): $remote_ip_address");
-                                                               }
-                                                               else {
-                                                                       push 
@remote_ip_addresses, $remote_ip_address;
-                                                               }
+                               }
+                               
+                               CONNECTION_PORT: for my $connection_port (keys 
%{$connection_info->{$connection_protocol}}) {
+                                       # Check if the port defined for the 
connect method matches the established connection
+                                       if ($connect_method_port eq 
$connection_port) {
+                                               notify($ERRORS{'DEBUG'}, 0, 
"connect method port matches established connection port: $connection_port");
+                                               
+                                               for my $connection 
(@{$connection_info->{$connection_protocol}{$connection_port}}) {
+                                                       my $remote_ip_address = 
$connection->{remote_ip};
+                                                       if 
(!$remote_ip_address) {
+                                                               
notify($ERRORS{'WARNING'}, 0, "connection does NOT contain remote IP address 
(remote_ip) key:\n" . format_data($connection));
+                                                       }
+                                                       elsif 
($remote_ip_address eq $mn_private_ip_address || $remote_ip_address eq 
$mn_public_ip_address) {
+                                                               
notify($ERRORS{'DEBUG'}, 0, "ignoring connection to port $connection_port from 
management node: $remote_ip_address");
+                                                       }
+                                                       elsif (my 
($ignored_remote_ip_address) = grep { $remote_ip_address =~ /($_)/ } 
@ignored_remote_ip_addresses) {
+                                                               
notify($ERRORS{'DEBUG'}, 0, "ignoring connection to port $connection_port from 
ignored remote IP address ($ignored_remote_ip_address): $remote_ip_address");
+                                                       }
+                                                       else {
+                                                               push 
@remote_ip_addresses, $remote_ip_address;
                                                        }
                                                }
-                                               else {
-                                                       
notify($ERRORS{'DEBUG'}, 0, "connect method port $connect_method_port does NOT 
match established connection port $connection_port");
-                                                       next CONNECTION_PORT;
-                                               }
+                                       }
+                                       else {
+                                               notify($ERRORS{'DEBUG'}, 0, 
"connect method port $connect_method_port does NOT match established connection 
port $connection_port");
+                                               next CONNECTION_PORT;
                                        }
                                }
                        }
@@ -3615,8 +3653,8 @@ sub get_connect_method_remote_ip_address
 
  Parameters  : none
  Returns     : boolean
- Description : Opens the firewall port for the remote IP address for each
-               connect method.
+ Description : Updates the firewall to allow traffic to the address stored in
+               reservation remoteIP for each connection method.
 
 =cut
 
@@ -3627,11 +3665,13 @@ sub firewall_compare_update {
       return;
    }
        
-       # Make sure the OS module implements an enable_firewall_port subroutine
+       # Make sure the OS module implements get_firewall_configuration and 
enable_firewall_port subroutine
        return 1 unless $self->can('enable_firewall_port');
+       return 1 unless $self->can('get_firewall_configuration');
        
    my $computer_node_name = $self->data->get_computer_node_name();
-   my $remote_ip = $self->data->get_reservation_remote_ip();
+       
+       my $remote_ip = $self->data->get_reservation_remote_ip();
        if (!$remote_ip) {
                notify($ERRORS{'WARNING'}, 0, "unable to update firewall on 
$computer_node_name, remote IP could not be retrieved for reservation");
       return;
@@ -3640,27 +3680,32 @@ sub firewall_compare_update {
    # Retrieve the connect method info
    my $connect_method_info = $self->data->get_connect_methods();
    if (!$connect_method_info) {
-      notify($ERRORS{'WARNING'}, 0, "failed to retrieve connect method 
information");
+      notify($ERRORS{'WARNING'}, 0, "failed to retrieve connect method info");
       return;
    }
        
-   # Loop through the connect methods, check to make sure firewall is open for 
remote IP
+   # Retrieve the firewall configuration from the computer
+   my $firewall_configuration = $self->get_firewall_configuration() || return;
+       
+       # Loop through the connect methods, check to make sure firewall is open 
for remote IP
        my $error_encountered = 0;
-   for my $connect_method_id (sort keys %{$connect_method_info}) {
-      my $connect_method_name = 
$connect_method_info->{$connect_method_id}{name};
-               for my $protocol (keys 
%{$connect_method_info->{$connect_method_id}{connectmethodport}}) {
-                       for my $port (keys 
%{$connect_method_info->{$connect_method_id}{connectmethodport}{$protocol}}) {
-                               if ($self->enable_firewall_port($protocol, 
$port, $remote_ip, 0)) {
-                                       notify($ERRORS{'DEBUG'}, 0, 
"$connect_method_name: processed firewall port $protocol $port on 
$computer_node_name for remote IP address: $remote_ip");
-                               }
-                               else {
-                                       $error_encountered = 1;
-                                       notify($ERRORS{'WARNING'}, 0, 
"$connect_method_name: failed to process firewall port $protocol $port on 
$computer_node_name for remote IP address: $remote_ip");
-                               }
+       for my $connect_method_id (sort keys %$connect_method_info) {
+               my $connect_method_name = 
$connect_method_info->{$connect_method_id}{name};
+               
+               for my $connect_method_port_id (sort keys 
%{$connect_method_info->{$connect_method_id}{connectmethodport}}) {
+                       my $connect_method_port = 
$connect_method_info->{$connect_method_id}{connectmethodport}{$connect_method_port_id};
+                       my $protocol = 
$connect_method_info->{$connect_method_id}{connectmethodport}{$connect_method_port_id}{protocol};
+                       my $port = 
$connect_method_info->{$connect_method_id}{connectmethodport}{$connect_method_port_id}{port};
+                       
+                       if ($self->enable_firewall_port($protocol, $port, 
$remote_ip, 0)) {
+                               notify($ERRORS{'DEBUG'}, 0, 
"$connect_method_name: processed firewall port $protocol $port on 
$computer_node_name for remote IP address: $remote_ip");
+                       }
+                       else {
+                               $error_encountered = 1;
+                               notify($ERRORS{'WARNING'}, 0, 
"$connect_method_name: failed to process firewall port $protocol $port on 
$computer_node_name for remote IP address: $remote_ip");
                        }
                }
        }
-       
        return !$error_encountered;
 }
 

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm?rev=1644185&r1=1644184&r2=1644185&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm Tue Dec  9 21:11:37 2014
@@ -164,7 +164,7 @@ sub get_init_modules {
                # initialize will check the computer to determine if it 
contains the corresponding Linux init daemon installed
                # If not installed, the constructor will return false
                my $init;
-               eval { $init = ($init_perl_package)->new({data_structure => 
$self->data, os => $self, mn_os => $self->mn_os}) };
+               eval { $init = ($init_perl_package)->new({data_structure => 
$self->data, os => $self, mn_os => $self->mn_os, base_package => ref($self)}) };
                if ($init) {
                        my @required_commands = eval "@" . $init_perl_package . 
"::REQUIRED_COMMANDS";
                        if ($EVAL_ERROR) {
@@ -223,6 +223,75 @@ sub get_init_modules {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 firewall
+
+ Parameters  : none
+ Returns     : Linux firewall module reference
+ Description : Determines the Linux firewall module to use and creates an
+               object.
+
+=cut
+
+sub firewall {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+
+       return $self->{firewall} if $self->{firewall};
+       
+       notify($ERRORS{'DEBUG'}, 0, "beginning Linux firewall daemon module 
initialization");
+       
+       my $computer_node_name = $self->data->get_computer_node_name();
+       
+       # Get the absolute path of the init module directory
+       my $firewall_directory_path = 
"$FindBin::Bin/../lib/VCL/Module/OS/Linux/firewall";
+       notify($ERRORS{'DEBUG'}, 0, "Linux firewall module directory path: 
$firewall_directory_path");
+       
+       # Get a list of all *.pm files in the firewall module directory
+       my @firewall_module_paths = 
$self->mn_os->find_files($firewall_directory_path, '*.pm');
+       
+       # Attempt to create an initialize an object for each firewall module
+       my %firewall_module_hash;
+       FIREWALL_MODULE: for my $firewall_module_path (@firewall_module_paths) {
+               my $firewall_name = fileparse($firewall_module_path, 
qr/\.pm$/i);
+               my $firewall_perl_package = 
"VCL::Module::OS::Linux::firewall::$firewall_name";
+               
+               # Attempt to load the module
+               eval "use $firewall_perl_package";
+               if ($EVAL_ERROR) {
+                       notify($ERRORS{'WARNING'}, 0, "$firewall_perl_package 
module could not be loaded, error:\n" . $EVAL_ERROR);
+                       return;
+               }
+               notify($ERRORS{'DEBUG'}, 0, "$firewall_perl_package module 
loaded");
+               
+               # Attempt to create the object
+               my $firewall_object;
+               eval {
+                       $firewall_object = 
($firewall_perl_package)->new({data_structure => $self->data, base_package => 
ref($self)})
+               };
+               
+               if ($EVAL_ERROR) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to create 
$firewall_perl_package object, error: $EVAL_ERROR");
+               }
+               elsif (!$firewall_object) {
+                       notify($ERRORS{'DEBUG'}, 0, "$firewall_perl_package 
object could not be initialized");
+               }
+               else {
+                       my $address = sprintf('%x', $firewall_object);
+                       notify($ERRORS{'DEBUG'}, 0, "$firewall_perl_package 
object created, address: $address");
+                       $self->{firewall} = $firewall_object;
+                       return $firewall_object;
+               }
+       }
+       
+       notify($ERRORS{'WARNING'}, 0, "failed to create firewall object to 
control $computer_node_name");
+       return;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 pre_capture
 
  Parameters  : none
@@ -3023,14 +3092,19 @@ sub service_exists {
                }
                
                if (grep(/^$service_name$/, @service_names)) {
-                       notify($ERRORS{'DEBUG'}, 0, "'$service_name' service 
exists on $computer_node_name, controlled by $init_module_name init module 
($init_module_index)");
-                       
                        $self->{service_init_module}{$service_name} = {
                                init_module_index => $init_module_index,
                                init_module_name => $init_module_name,
                        };
                        
-                       return (wantarray) ? ($init_module_index) : 1;
+                       if (wantarray) {
+                               notify($ERRORS{'DEBUG'}, 0, "'$service_name' 
service exists on $computer_node_name, controlled by $init_module_name init 
module ($init_module_index), returning array: ($init_module_index)");
+                               return $init_module_index;
+                       }
+                       else {
+                               notify($ERRORS{'DEBUG'}, 0, "'$service_name' 
service exists on $computer_node_name, controlled by $init_module_name init 
module ($init_module_index), returning scalar: 1");
+                               return 1;
+                       }
                }
                else {
                        notify($ERRORS{'DEBUG'}, 0, "'$service_name' service is 
not controlled by $init_module_name init module ($init_module_index)");
@@ -3208,12 +3282,10 @@ sub check_connection_on_port {
                return;
        }
        
-       my $management_node_keys        = 
$self->data->get_management_node_keys();
        my $computer_node_name          = $self->data->get_computer_node_name();
        my $remote_ip                   = 
$self->data->get_reservation_remote_ip();
-       my $computer_public_ip_address  = 
$self->data->get_computer_public_ip_address();
+       my $computer_public_ip_address  = $self->get_public_ip_address();
        my $request_state_name          = $self->data->get_request_state_name();
-       my $username                    = $self->data->get_user_login_id();
        
        my $port = shift;
        if (!$port) {
@@ -3221,47 +3293,38 @@ sub check_connection_on_port {
                return "failed";
        }
        
-       my $ret_val = "no";
-       my $command = "netstat -an";
-       my ($status, $output) = $self->execute($command, 0);
-       notify($ERRORS{'DEBUG'}, 0, "checking connections on node 
$computer_node_name on port $port");
-       foreach my $line (@{$output}) {
-               if ($line =~ /Connection refused|Permission denied/) {
-                       chomp($line);
-                       notify($ERRORS{'WARNING'}, 0, "$line");
-                       if ($request_state_name =~ /reserved/) {
-                               $ret_val = "failed";
-                       }
-                       else {
-                               $ret_val = "timeout";
+       my $port_connection_info = $self->get_port_connection_info();
+       for my $protocol (keys %$port_connection_info) {
+               if (!defined($port_connection_info->{$protocol}{$port})) {
+                       next;
+               }
+               
+               for my $connection 
(@{$port_connection_info->{$protocol}{$port}}) {
+                       my $connection_local_ip = $connection->{local_ip};
+                       my $connection_remote_ip = $connection->{remote_ip};
+                       
+                       if ($connection_local_ip ne 
$computer_public_ip_address) {
+                               notify($ERRORS{'DEBUG'}, 0, "ignoring 
connection, not connected to public IP address ($computer_public_ip_address): 
$connection_remote_ip --> $connection_local_ip:$port ($protocol)");
+                               next;
                        }
-                       return $ret_val;
-               } ## end if ($line =~ /Connection refused|Permission denied/)
-               if ($line =~ 
/tcp\s+([0-9]*)\s+([0-9]*)\s($computer_public_ip_address:$port)\s+([.0-9]*):([0-9]*)(.*)(ESTABLISHED)/)
 {
-                       if ($4 eq $remote_ip) {
-                               $ret_val = "connected";
-                               return $ret_val;
+                       
+                       if ($connection_remote_ip eq $remote_ip) {
+                               notify($ERRORS{'DEBUG'}, 0, "connection 
detected from reservation remote IP: $connection_remote_ip --> 
$connection_local_ip:$port ($protocol)");
+                               return 1;
                        }
-                       else {
-                               my $new_remote_ip = $4;
-                               # this isn't the defined remoteIP
-                               # Confirm the user is logged in
-                               # Is user logged in
-                               if (!$self->user_logged_in()) {
-                                       notify($ERRORS{'OK'}, 0, "Detected 
$new_remote_ip is connected. $username is not logged in yet. Returning no 
connection");
-                                       $ret_val = "no";
-                                       return $ret_val;
-                               }
-                               else {
-                                       
$self->data->set_reservation_remote_ip($new_remote_ip);
-                                       notify($ERRORS{'OK'}, 0, "Updating 
reservation remote_ip with $new_remote_ip");
-                                       $ret_val = "conn_wrong_ip";
-                                       return $ret_val;
-                               }
+                       
+                       # Connection is not from reservation remote IP address, 
check if user is logged in
+                       if ($self->user_logged_in()) {
+                               notify($ERRORS{'DEBUG'}, 0, "connection 
detected from different remote IP address than current reservation remote IP 
($remote_ip): $connection_remote_ip --> $connection_local_ip:$port ($protocol), 
updating reservation remote IP to $connection_remote_ip");
+                               
$self->data->set_reservation_remote_ip($connection_remote_ip);
+                               return 1;
                        }
-               }    # tcp check
+                       
+                       notify($ERRORS{'DEBUG'}, 0, "ignoring connection, user 
is not logged in and remote IP address does not match current reservation 
remote IP ($remote_ip): $connection_remote_ip --> $connection_local_ip:$port 
($protocol)");
+               }
        }
-       return $ret_val;
+       
+       return 0;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -5135,6 +5198,42 @@ sub get_port_connection_info {
        return $connection_info;
 }
 
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 enable_ip_forwarding
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Enables IP forwarding by executing:
+               echo 1 > /proc/sys/net/ipv4/ip_forward
+
+=cut
+
+sub enable_ip_forwarding {
+       my $self = shift;
+       if (ref($self) !~ /linux/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my $computer_node_name = $self->data->get_computer_node_name();
+       
+       my $command = "echo 1 > /proc/sys/net/ipv4/ip_forward";
+       my ($exit_status, $output) = $self->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
enable IP forwarding on $computer_node_name: $command");
+               return;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to enable IP forwarding 
on $computer_node_name, command: '$command', exit status: $exit_status, 
output:\n" . join("\n", @$output));
+               return 0;
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "verified IP forwarding is enabled on 
$computer_node_name");
+               return 1;
+       }
+}
+
 ##/////////////////////////////////////////////////////////////////////////////
 1;
 __END__

Added: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm?rev=1644185&view=auto
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm 
(added)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm Tue 
Dec  9 21:11:37 2014
@@ -0,0 +1,882 @@
+#!/usr/bin/perl -w
+###############################################################################
+# $Id:  $
+###############################################################################
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###############################################################################
+
+=head1 NAME
+
+VCL::Module::OS::Linux::firewall::iptables.pm
+
+=head1 DESCRIPTION
+
+ This module provides VCL support for iptables-based firewalls.
+
+=cut
+
+##############################################################################
+package VCL::Module::OS::Linux::firewall::iptables;
+
+# Specify the lib path using FindBin
+use FindBin;
+use lib "$FindBin::Bin/../../../../..";
+
+# Configure inheritance
+use base qw(VCL::Module::OS::Linux);
+
+# Specify the version of this module
+our $VERSION = '2.3';
+
+our @ISA;
+
+# Specify the version of Perl to use
+use 5.008000;
+
+use strict;
+use warnings;
+use diagnostics;
+
+use VCL::utils;
+
+##############################################################################
+
+=head1 OBJECT METHODS
+
+=cut
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 initialize
+
+ Parameters  : none
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub initialize {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return 0;
+       }
+       
+       my $arguments = shift || {};
+       
+       # Check if base_package argument was specified
+       # This is necessary for ManagementNode OS objects to work
+       # Otherwise the base Linux.pm subroutines would be used instead of 
ManagementNode.pm
+       if (defined($arguments->{base_package})) {
+               notify($ERRORS{'DEBUG'}, 0, "overriding object package: " . 
$ISA[0] . " --> $arguments->{base_package}");
+               @ISA = ($arguments->{base_package});
+       }
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       notify($ERRORS{'DEBUG'}, 0, "initializing " . ref($self) . " object to 
control $computer_name");
+       
+       if (!$self->service_exists('iptables')) {
+               notify($ERRORS{'DEBUG'}, 0, ref($self) . " object not 
initialized to control $computer_name, iptables service does not exist");
+               return 0;
+       }
+       
+       notify($ERRORS{'DEBUG'}, 0, ref($self) . " object initialized to 
control $computer_name");
+       return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 insert_rule
+
+ Parameters  : none
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub insert_rule {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return 0;
+       }
+       
+       my $arguments = shift;
+       if (!$arguments) {
+               notify($ERRORS{'WARNING'}, 0, "argument was not supplied");
+               return;
+       }
+       elsif (!ref($arguments) || ref($arguments) ne 'HASH') {
+               notify($ERRORS{'WARNING'}, 0, "argument is not a hash 
reference");
+               return;
+       }
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my $command = '/sbin/iptables';
+       
+       # Add the table argument if specified
+       if ($arguments->{table}) {
+               $command .= " -t $arguments->{table}";
+       }
+       
+       # Get the chain argument
+       my $chain = $arguments->{chain};
+       if (!defined($chain)) {
+               notify($ERRORS{'WARNING'}, 0, "chain argument was not 
specified:\n" . format_data($arguments));
+               return;
+       }
+       $command .= " -I $chain";
+       
+       # Add the parameters to the command
+       for my $parameter (sort keys %{$arguments->{parameters}}) {
+               my $value = $arguments->{parameters}{$parameter};
+               $command .= " --$parameter $value";
+       }
+       
+       # Add the match extension to the command
+       for my $match_extension (sort keys %{$arguments->{match_extensions}}) {
+               $command .= " --match $match_extension";
+               for my $option (sort keys 
%{$arguments->{match_extensions}{$match_extension}}) {
+                       my $value = 
$arguments->{match_extensions}{$match_extension}{$option};
+                       
+                       if ($option =~ /(comment)/) {
+                               $value = "\"$value\"";
+                       }
+                       
+                       $command .= " --$option $value";
+               }
+       }
+       
+       # Add the target extensions to the command
+       for my $target_extension (sort keys %{$arguments->{target_extensions}}) 
{
+               $command .= " --jump $target_extension";
+               for my $option (sort keys 
%{$arguments->{target_extensions}{$target_extension}}) {
+                       my $value = 
$arguments->{target_extensions}{$target_extension}{$option};
+                       $command .= " --$option $value";
+               }
+       }
+       
+       my ($exit_status, $output) = $self->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command 
$computer_name: $command");
+               return;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to add iptables rule on 
$computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . 
join("\n", @$output));
+               return 0;
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "added iptables rule on 
$computer_name, command: $command");
+               return 1;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_rule
+
+ Parameters  : hash reference
+               -or-
+                                       $table_name, $chain_name, 
$rule_specification
+ Returns     : boolean
+ Description : Deletes a rule.
+
+=cut
+
+sub delete_rule {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return 0;
+       }
+       
+       my $argument = shift;
+       if (!$argument) {
+               notify($ERRORS{'WARNING'}, 0, "argument was not supplied");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my $command = '/sbin/iptables';
+       
+       
+       if (ref($argument) && ref($argument) eq 'HASH') {
+               # Add the table argument if specified
+               if ($argument->{table}) {
+                       $command .= " -t $argument->{table}";
+               }
+               
+               # Get the chain argument
+               my $chain = $argument->{chain};
+               if (!defined($chain)) {
+                       notify($ERRORS{'WARNING'}, 0, "chain argument was not 
specified:\n" . format_data($argument));
+                       return;
+               }
+               $command .= " -D $chain";
+               
+               # Add the parameters to the command
+               for my $parameter (sort keys %{$argument->{parameters}}) {
+                       my $value = $argument->{parameters}{$parameter};
+                       $command .= " --$parameter $value";
+               }
+               
+               # Add the match extension to the command
+               for my $match_extension (sort keys 
%{$argument->{match_extensions}}) {
+                       $command .= " --match $match_extension";
+                       for my $option (sort keys 
%{$argument->{match_extensions}{$match_extension}}) {
+                               my $value = 
$argument->{match_extensions}{$match_extension}{$option};
+                               
+                               if ($option =~ /(comment)/) {
+                                       $value = "\"$value\"";
+                               }
+                               
+                               $command .= " --$option $value";
+                       }
+               }
+               
+               # Add the target extensions to the command
+               for my $target_extension (sort keys 
%{$argument->{target_extensions}}) {
+                       $command .= " --jump $target_extension";
+                       for my $option (sort keys 
%{$argument->{target_extensions}{$target_extension}}) {
+                               my $value = 
$argument->{target_extensions}{$target_extension}{$option};
+                               $command .= " --$option $value";
+                       }
+               }
+       }
+       elsif (my $type = ref($argument)) {
+               notify($ERRORS{'WARNING'}, 0, "argument $type reference not 
supported, argument must only be a HASH reference or scalar");
+               return;
+       }
+       else {
+               my $table_name = $argument;
+               my ($chain_name, $specification) = @_;
+               if (!defined($chain_name) || !defined($specification)) {
+                       notify($ERRORS{'WARNING'}, 0, "1st argument is a 
scalar, 2nd chain name and 3rd rule specification arguments not provided");
+                       return;
+               }
+               $command .= " -D $chain_name -t $table_name $specification";
+       }
+       
+       my ($exit_status, $output) = $self->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command 
$computer_name: $command");
+               return;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to delete iptables rule 
on $computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . 
join("\n", @$output));
+               return 0;
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "deleted iptables rule on 
$computer_name, command: $command");
+               return 1;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 create_chain
+
+ Parameters  : $table_name, $chain_name
+ Returns     : boolean
+ Description : Creates a new chain. Returns true if the chain was successfully
+               created or already exists.
+
+=cut
+
+sub create_chain {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return 0;
+       }
+       
+       my ($table_name, $chain_name) = @_;
+       if (!defined($table_name)) {
+               notify($ERRORS{'WARNING'}, 0, "table name argument was not 
specified");
+               return;
+       }
+       elsif (!defined($chain_name)) {
+               notify($ERRORS{'WARNING'}, 0, "chain name argument was not 
specified");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my $command = "/sbin/iptables --new-chain $chain_name --table 
$table_name";
+       my ($exit_status, $output) = $self->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command 
$computer_name: $command");
+               return;
+       }
+       elsif (grep(/already exists/i, @$output)) {
+               notify($ERRORS{'OK'}, 0, "'$chain_name' chain in '$table_name' 
table already exists on $computer_name");
+               return 1;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to create '$chain_name' 
chain in '$table_name' table on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
+               return 0;
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "created '$chain_name' chain in 
'$table_name' table on $computer_name");
+               return 1;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_chain
+
+ Parameters  : $table_name, $chain_name
+ Returns     : boolean
+ Description : Deletes the specified chain from the specified table. All rules
+               which exist in the chain or reference the chain are deleted 
prior
+               to deletion of the chain.
+
+=cut
+
+sub delete_chain {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return 0;
+       }
+       
+       my ($table_name, $chain_name) = @_;
+       if (!defined($table_name)) {
+               notify($ERRORS{'WARNING'}, 0, "table name argument was not 
specified");
+               return;
+       }
+       elsif (!defined($chain_name)) {
+               notify($ERRORS{'WARNING'}, 0, "chain name argument was not 
specified");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my $table_info = $self->get_table_info($table_name);
+       if (!defined($table_info->{$chain_name})) {
+               notify($ERRORS{'DEBUG'}, 0, "'$chain_name' chain in 
'$table_name' table does not exist on $computer_name");
+               return 1;
+       }
+       
+       # Flush the chain first - delete will fail if the chain still contains 
rules
+       if (!$self->flush_chain($table_name, $chain_name)) {
+               notify($ERRORS{'WARNING'}, 0, "unable to delete '$chain_name' 
chain from '$table_name' table on $computer_name, failed to flush chain prior 
to deletion");
+               return;
+       }
+       
+       # Delete all rules which reference the chain being deleted or else the 
chain can't be deleted
+       if (!$self->delete_chain_references($table_name, $chain_name)) {
+               notify($ERRORS{'WARNING'}, 0, "unable to delete '$chain_name' 
chain from '$table_name' table on $computer_name, failed to delete all rules 
which reference the chain prior to deletion");
+               return;
+       }
+       
+       my $command = "/sbin/iptables --delete-chain $chain_name --table 
$table_name";
+       my ($exit_status, $output) = $self->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command 
$computer_name: $command");
+               return;
+       }
+       elsif (grep(/Too many links/i, @$output)) {
+               notify($ERRORS{'WARNING'}, 0, "unable to delete '$chain_name' 
chain from '$table_name' table on $computer_name, the chain is referenced by 
another rule");
+               return 0;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to delete '$chain_name' 
chain from '$table_name' table on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
+               return 0;
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "deleted '$chain_name' chain from 
'$table_name' table on $computer_name");
+               return 1;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_chain_references
+
+ Parameters  : $table_name, $referenced_chain_name
+ Returns     : boolean
+ Description : Checks all chains in the specified table for references to the
+               $referenced_chain_name argument. If found, the referencing rules
+               are deleted.
+
+=cut
+
+sub delete_chain_references {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return 0;
+       }
+       
+       my ($table_name, $referenced_chain_name) = @_;
+       if (!defined($table_name)) {
+               notify($ERRORS{'WARNING'}, 0, "table name argument was not 
specified");
+               return;
+       }
+       elsif (!defined($referenced_chain_name)) {
+               notify($ERRORS{'WARNING'}, 0, "referenced chain name argument 
was not specified");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my $table_info = $self->get_table_info($table_name);
+       for my $referencing_chain_name (keys %$table_info) {
+               for my $rule_specification 
(@{$table_info->{$referencing_chain_name}{rules}}) {
+                       if ($rule_specification =~ /-j 
$referenced_chain_name(\s|$)/) {
+                               notify($ERRORS{'DEBUG'}, 0, "rule in 
'$table_name' table references '$referenced_chain_name' chain, referencing 
chain: $referencing_chain_name, rule specification: $rule_specification");
+                               if (!$self->delete_rule($table_name, 
$referencing_chain_name, $rule_specification)) {
+                                       return;
+                               }
+                       }
+               }
+       }
+       
+       notify($ERRORS{'DEBUG'}, 0, "deleted all rules in '$table_name' table 
referencing '$referenced_chain_name' chain on $computer_name");
+       return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 flush_chain
+
+ Parameters  : $table_name, $chain_name
+ Returns     : boolean
+ Description : Flushes (deletes) rules from the specified chain.
+
+=cut
+
+sub flush_chain {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return 0;
+       }
+       
+       my ($table_name, $chain_name) = @_;
+       if (!defined($table_name)) {
+               notify($ERRORS{'WARNING'}, 0, "table name argument was not 
specified");
+               return;
+       }
+       elsif (!defined($chain_name)) {
+               notify($ERRORS{'WARNING'}, 0, "chain name argument was not 
specified");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my $command = "/sbin/iptables --flush";
+       my $chain_text = 'all chains';
+       if ($chain_name ne '*') {
+               $chain_text = "'$chain_name' chain";
+               $command .= " $chain_name";
+       }
+       $command .= " --table $table_name";
+       
+       my ($exit_status, $output) = $self->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command 
$computer_name: $command");
+               return;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to flush $chain_text in 
'$table_name' table on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
+               return 0;
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "flushed $chain_text in '$table_name' 
table on $computer_name");
+               return 1;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_table_info
+
+ Parameters  : $table_name, $chain_name (optional)
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub get_table_info {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return 0;
+       }
+       
+       my ($table_name, $chain_name) = @_;
+       if (!defined($table_name)) {
+               notify($ERRORS{'WARNING'}, 0, "table name argument was not 
specified");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my $command = "/sbin/iptables --list-rules";
+       my $chain_text = '';
+       if (defined($chain_name)) {
+               $command .= " $chain_name";
+               $chain_text = "of '$chain_name' chain ";
+       }
+       $command .= " --table $table_name";
+       
+       my ($exit_status, $output) = $self->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command 
$computer_name: $command");
+               return;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to list rules " . 
$chain_text . "from '$table_name' table on $computer_name, exit status: 
$exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
+               return 0;
+       }
+       
+       my $table_info = {};
+       for my $line (@$output) {
+               my ($iptables_command, $chain_name, $specification) = $line =~ 
/^(-\w) ([^ ]+)\s*(.*)$/;
+               if (!defined($iptables_command) || !defined($chain_name)) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to parse line: 
'$line'\ncommand: $command");
+                       next;
+               }
+               $specification = '' unless defined($specification);
+               
+               if ($iptables_command eq '-P') {
+                       # -P, --policy chain target (Set  the policy for the 
chain to the given target)
+                       $table_info->{$chain_name}{policy} = $specification;
+               }
+               elsif ($iptables_command eq '-N') {
+                       # -N, --new-chain chain
+                       $table_info->{$chain_name} = {} unless 
defined($table_info->{$chain_name});
+               }
+               elsif ($iptables_command eq '-A') {
+                       # -A, --append chain rule-specification
+                       push @{$table_info->{$chain_name}{rules}}, 
$specification;
+               }
+               else {
+                       notify($ERRORS{'WARNING'}, 0, "'$iptables_command' 
command is not supported: $line");
+               }
+       }
+       
+       notify($ERRORS{'DEBUG'}, 0, "retrieved rules " . $chain_text . "from 
'$table_name' table from $computer_name:\n" . format_data($table_info));
+       return $table_info;
+}
+
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 configure_nat
+
+ Parameters  : none
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub configure_nat {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return 0;
+       }
+       
+       my $reservation_id = $self->data->get_reservation_id();
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my $table_info = $self->get_table_info('nat');
+       if (!$table_info) {
+               notify($ERRORS{'WARNING'}, 0, "failed to configure NAT on 
$computer_name, nat table info could not be retrieved");
+               return;
+       }
+       elsif (!defined($table_info->{PREROUTING})) {
+               notify($ERRORS{'WARNING'}, 0, "unable to configure NAT on 
$computer_name, nat table does not contain a PREROUTING chain:\n" . 
format_data($table_info));
+               return;
+       }
+       elsif (!defined($table_info->{POSTROUTING})) {
+               notify($ERRORS{'WARNING'}, 0, "unable to configure NAT on 
$computer_name, nat table does not contain a POSTROUTING chain:\n" . 
format_data($table_info));
+               return;
+       }
+       
+       # Check if NAT has previously been configured
+       my $nat_previously_configured = 0;
+       for my $rule_specification (@{$table_info->{POSTROUTING}{rules}}) {
+               if ($rule_specification =~ /MASQUERADE/) {
+                       $nat_previously_configured = 1;
+                       notify($ERRORS{'DEBUG'}, 0, "POSTROUTING chain in nat 
table contains a MASQUERADE rule, assuming NAT has already been configured: 
$rule_specification");
+                       last;
+               }
+       }
+       if (!$nat_previously_configured) {
+               my $private_interface_name = 
$self->get_private_interface_name();
+               my $private_ip_address = $self->get_private_ip_address();
+               my $public_interface_name = $self->get_public_interface_name();
+               my $public_ip_address = $self->get_public_ip_address();
+               
+               my $natport_ranges_variable = get_variable('natport_ranges') || 
'49152-65535';
+               my $destination_ports = '';
+               for my $natport_range (split(/[,;]+/, 
$natport_ranges_variable)) {
+                       my ($start_port, $end_port) = $natport_range =~ 
/(\d+)-(\d+)/g;
+                       if (!defined($start_port)) {
+                               notify($ERRORS{'WARNING'}, 0, "unable to parse 
NAT port range: '$natport_range'");
+                               next;
+                       }
+                       $destination_ports .= "," if ($destination_ports);
+                       $destination_ports .= "$start_port:$end_port";
+               }
+               
+               
+               if (!$self->insert_rule({
+                       'table' => 'nat',
+                       'chain' => 'POSTROUTING',
+                       'parameters' => {
+                               'out-interface' => $private_interface_name,
+                               'jump' => 'MASQUERADE',
+                       },
+                       'match_extensions' => {
+                               'comment' => {
+                                       'comment' => "change IP of outbound 
private $private_interface_name packets to NAT host private IP address 
$private_interface_name",
+                               },
+                       },
+               })) {
+                       return;
+               }
+               
+               if (!$self->insert_rule({
+                       'chain' => 'INPUT',
+                       'parameters' => {
+                               'in-interface' => $public_interface_name,
+                               'destination' => $public_ip_address,
+                               'jump' => 'ACCEPT',
+                               'protocol' => 'tcp',
+                       },
+                       'match_extensions' => {
+                               'state' => {
+                                       'state' => 'RELATED,ESTABLISHED',
+                               },
+                               'multiport' => {
+                                       'destination-ports' => 
$destination_ports,
+                               },
+                       },
+               })) {
+                       return;
+               }
+               
+               if (!$self->insert_rule({
+                       'chain' => 'INPUT',
+                       'parameters' => {
+                               'in-interface' => $public_interface_name,
+                               'destination' => $public_ip_address,
+                               'jump' => 'ACCEPT',
+                               'protocol' => 'udp',
+                       },
+                       'match_extensions' => {
+                               'state' => {
+                                       'state' => 'RELATED,ESTABLISHED',
+                               },
+                               'multiport' => {
+                                       'destination-ports' => 
$destination_ports,
+                               },
+                       },
+               })) {
+                       return;
+               }
+               
+               if (!$self->insert_rule({
+                       'chain' => 'FORWARD',
+                       'parameters' => {
+                               'in-interface' => $public_interface_name,
+                               'out-interface' => $private_interface_name,
+                               'jump' => 'ACCEPT',
+                       },
+                       'match_extensions' => {
+                               'state' => {
+                                       'state' => 'NEW,RELATED,ESTABLISHED',
+                               },
+                               'comment' => {
+                                       'comment' => "forward inbound packets 
from public $public_interface_name to private $private_interface_name",
+                               },
+                       },      
+               })) {
+                       return;
+               }
+               
+               if (!$self->insert_rule({
+                       'chain' => 'FORWARD',
+                       'parameters' => {
+                               'in-interface' => $private_interface_name,
+                               'out-interface' => $public_interface_name,
+                               'jump' => 'ACCEPT',
+                       },
+                       'match_extensions' => {
+                               'comment' => {
+                                       'comment' => "forward outbound packets 
from private $private_interface_name to public $public_interface_name",
+                               },
+                       },
+               })) {
+                       return;
+               }
+               
+               #if (!$self->insert_rule({
+               #       'chain' => 'INPUT',
+               #       'parameters' => {
+               #               'in-interface' => $public_interface_name,
+               #       },
+               #       'target_extensions' => {
+               #               'REJECT' => {
+               #                       'reject-with' => "icmp-host-prohibited",
+               #               },
+               #       },
+               #})) {
+               #       return;
+               #}
+               #
+               #if (!$self->insert_rule({
+               #       'chain' => 'FORWARD',
+               #       'target_extensions' => {
+               #               'REJECT' => {
+               #                       'reject-with' => "icmp-host-prohibited",
+               #               },
+               #       },
+               #})) {
+               #       return;
+               #}
+       }
+       
+       # Check if chain for reservation has already been created
+       if (defined($table_info->{$reservation_id})) {
+               notify($ERRORS{'DEBUG'}, 0, "'$reservation_id' chain already 
exists in nat table on $computer_name for this reservation");
+       }
+       else {
+               if (!$self->create_chain('nat', $reservation_id)) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to configure NAT 
on $computer_name, '$reservation_id' chain could not be created in nat table 
for this reservation");
+                       return;
+               }
+       }
+       
+       # Check if rule to jump to reservation's chain already exists in the 
PREROUTING table
+       my $jump_previously_configured = 0;
+       for my $rule_specification (@{$table_info->{PREROUTING}{rules}}) {
+               if ($rule_specification =~ /-j $reservation_id(\s|$)/) {
+                       $jump_previously_configured = 1;
+                       notify($ERRORS{'DEBUG'}, 0, "PREROUTING chain in nat 
table on $computer_name already contains a rule to jump to '$reservation_id' 
chain: $rule_specification");
+                       last;
+               }
+       }
+       if (!$jump_previously_configured) {
+               if (!$self->insert_rule({
+                       'table' => 'nat',
+                       'chain' => 'PREROUTING',
+                       'parameters' => {
+                               'jump' => $reservation_id,
+                       },
+               })) {
+                       notify($ERRORS{'WARNING'}, 0, "unable to configure NAT 
on $computer_name, failed to create rule in PREROUTING chain in nat table to 
jump to '$reservation_id' chain");
+                       return;
+               }
+       }
+       
+       notify($ERRORS{'DEBUG'}, 0, "successfully configured NAT on 
$computer_name for reservation");
+       return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 add_nat_port_forward
+
+ Parameters  : $protocol, $source_port, $destination_ip_address, 
$destination_port, $chain_name (optional)
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub add_nat_port_forward {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return 0;
+       }
+
+       my $reservation_id = $self->data->get_reservation_id();
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my ($protocol, $source_port, $destination_ip_address, 
$destination_port, $chain_name) = @_;
+       if (!defined($protocol)) {
+               notify($ERRORS{'WARNING'}, 0, "protocol argument was not 
provided");
+               return;
+       }
+       elsif (!defined($source_port)) {
+               notify($ERRORS{'WARNING'}, 0, "source port argument was not 
provided");
+               return;
+       }
+       elsif (!defined($destination_ip_address)) {
+               notify($ERRORS{'WARNING'}, 0, "destination IP address argument 
was not provided");
+               return;
+       }
+       elsif (!defined($destination_port)) {
+               notify($ERRORS{'WARNING'}, 0, "destination port argument was 
not provided");
+               return;
+       }
+       $chain_name = 'PREROUTING' unless defined $chain_name;
+       
+       $protocol = lc($protocol);
+       
+       my $public_interface_name = $self->get_public_interface_name();
+       my $public_ip_address = $self->get_public_ip_address();
+       
+       if ($self->insert_rule({
+               'table' => 'nat',
+               'chain' => $chain_name,
+               'parameters' => {
+                       'protocol' => $protocol,
+                       'in-interface' => $public_interface_name,
+                       'destination' => $public_ip_address,
+               },
+               'match_extensions' => {
+                       'comment' => {
+                               'comment' => "change destination address: 
$public_ip_address:$source_port --> $destination_ip_address:$destination_port 
($protocol)",
+                       },
+                       $protocol => {
+                               'destination-port' => $source_port,
+                       },
+               },
+               'target_extensions' => {
+                       'DNAT' => {
+                               'to-destination' => 
"$destination_ip_address:$destination_port",
+                       },
+               },
+       })) {
+               notify($ERRORS{'OK'}, 0, "added NAT port forward on 
$computer_name: $public_interface_name:$source_port --> 
$destination_ip_address:$destination_port");
+               return 1;
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to add NAT port forward 
on $computer_name: $public_interface_name:$source_port --> 
$destination_ip_address:$destination_port");
+               return;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+1;
+__END__
+
+=head1 SEE ALSO
+
+L<http://cwiki.apache.org/VCL/>
+
+=cut

Added: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init.pm?rev=1644185&view=auto
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init.pm (added)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init.pm Tue Dec  9 
21:11:37 2014
@@ -0,0 +1,100 @@
+#!/usr/bin/perl -w
+###############################################################################
+# $Id: $
+###############################################################################
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###############################################################################
+
+=head1 NAME
+
+VCL::Module::OS::Linux::init
+
+=head1 DESCRIPTION
+
+ This module is the parent class for the Linux init modules.
+
+=cut
+
+##############################################################################
+package VCL::Module::OS::Linux::init;
+
+# Specify the lib path using FindBin
+use FindBin;
+use lib "$FindBin::Bin/../../../..";
+
+# Configure inheritance
+use base qw(VCL::Module::OS::Linux);
+
+# Specify the version of this module
+our $VERSION = '2.3';
+
+our @ISA;
+
+# Specify the version of Perl to use
+use 5.008000;
+
+use strict;
+use warnings;
+use diagnostics;
+
+use VCL::utils;
+
+##############################################################################
+
+=head1 OBJECT METHODS
+
+=cut
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 initialize
+
+ Parameters  : 
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub initialize {
+       my $self = shift;
+       if (ref($self) !~ /linux/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       my $arguments = shift || {};
+       
+       # Check if base_package argument was specified
+       # This is necessary for ManagementNode OS objects to work
+       # Otherwise the base Linux.pm subroutines would be used instead of 
ManagementNode.pm
+       if (defined($arguments->{base_package})) {
+               notify($ERRORS{'DEBUG'}, 0, "overriding object package: " . 
$ISA[0] . " --> $arguments->{base_package}");
+               @ISA = ($arguments->{base_package});
+       }
+       
+       return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+1;
+__END__
+
+=head1 SEE ALSO
+
+L<http://cwiki.apache.org/VCL/>
+
+=cut

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/SysV.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/SysV.pm?rev=1644185&r1=1644184&r2=1644185&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/SysV.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/SysV.pm Tue Dec  9 
21:11:37 2014
@@ -39,7 +39,7 @@ use FindBin;
 use lib "$FindBin::Bin/../../../../..";
 
 # Configure inheritance
-use base qw(VCL::Module::OS::Linux);
+use base qw(VCL::Module::OS::Linux::init);
 
 # Specify the version of this module
 our $VERSION = '2.3';

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/Upstart.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/Upstart.pm?rev=1644185&r1=1644184&r2=1644185&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/Upstart.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/Upstart.pm Tue Dec  9 
21:11:37 2014
@@ -38,7 +38,7 @@ use FindBin;
 use lib "$FindBin::Bin/../../../../..";
 
 # Configure inheritance
-use base qw(VCL::Module::OS::Linux);
+use base qw(VCL::Module::OS::Linux::init);
 
 # Specify the version of this module
 our $VERSION = '2.3';

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/systemd.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/systemd.pm?rev=1644185&r1=1644184&r2=1644185&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/systemd.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init/systemd.pm Tue Dec  9 
21:11:37 2014
@@ -39,7 +39,7 @@ use FindBin;
 use lib "$FindBin::Bin/../../../../..";
 
 # Configure inheritance
-use base qw(VCL::Module::OS::Linux);
+use base qw(VCL::Module::OS::Linux::init);
 
 # Specify the version of this module
 our $VERSION = '2.3';

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm?rev=1644185&r1=1644184&r2=1644185&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm Tue Dec  9 21:11:37 
2014
@@ -10818,8 +10818,11 @@ sub get_environment_variable_value {
 =head2 check_connection_on_port
 
  Parameters  : $port
- Returns     : (connected|conn_wrong_ip|timeout|failed)
- Description : uses netstat to see if any thing is connected to the provided 
port
+ Returns     : boolean
+ Description : Checks if a connection is established to the port specified from
+                                       the reservation remote IP address. If a 
connection is detected
+                                       from another address and the user is 
logged in,
+                                       reservation.remoteIP is updated.
 
 =cut
 
@@ -10832,7 +10835,7 @@ sub check_connection_on_port {
        
        my $computer_node_name          = $self->data->get_computer_node_name();
        my $remote_ip                   = 
$self->data->get_reservation_remote_ip();
-       my $computer_public_ip_address  = 
$self->data->get_computer_public_ip_address();
+       my $computer_public_ip_address  = $self->get_public_ip_address();
        my $request_state_name          = $self->data->get_request_state_name();
        
        my $port = shift;
@@ -10841,104 +10844,38 @@ sub check_connection_on_port {
                return "failed";
        }
        
-       my $ret_val = "no";
-       my $command = "netstat -an";
-       my ($status, $output) = $self->execute($command, 0, 30, 0);
-       
-       notify($ERRORS{'DEBUG'}, 0, "checking connections on node 
$computer_node_name on port $port");
-       
-       foreach my $line (@{$output}) {
-               if ($line =~ /Connection refused|Permission denied/) {
-                       chomp($line);
-                       notify($ERRORS{'WARNING'}, 0, "$line");
-                       if ($request_state_name =~ /reserved/) {
-                               $ret_val = "failed";
-                       }
-                       else {
-                               $ret_val = "timeout";
-                       }
-                       return $ret_val;
-               } ## end if ($line =~ /Connection refused|Permission denied/)
+       my $port_connection_info = $self->get_port_connection_info();
+       for my $protocol (keys %$port_connection_info) {
+               if (!defined($port_connection_info->{$protocol}{$port})) {
+                       next;
+               }
                
-               if ($line =~ 
/\s+($computer_public_ip_address:$port)\s+([.0-9]*):([0-9]*)\s+(ESTABLISHED)/) {
-                       if ($2 eq $remote_ip) {
-                               $ret_val = "connected";
-                               return $ret_val;
+               for my $connection 
(@{$port_connection_info->{$protocol}{$port}}) {
+                       my $connection_local_ip = $connection->{local_ip};
+                       my $connection_remote_ip = $connection->{remote_ip};
+                       
+                       if ($connection_local_ip ne 
$computer_public_ip_address) {
+                               notify($ERRORS{'DEBUG'}, 0, "ignoring 
connection, not connected to public IP address ($computer_public_ip_address): 
$connection_remote_ip --> $connection_local_ip:$port ($protocol)");
+                               next;
                        }
-                       else {
-                               # this isn't the remoteIP
-                               # Is user logged in
-                               if (!$self->user_logged_in()) {
-                                       notify($ERRORS{'OK'}, 0, "Detected $4 
is connected. user is not logged in yet. Returning no connection");
-                                       $ret_val = "no";
-                                       return $ret_val;
-                               }
-                               else {
-                                       my $new_remote_ip = $2;
-                                       
$self->data->set_reservation_remote_ip($new_remote_ip);  
-                                       notify($ERRORS{'OK'}, 0, "Updating 
reservation remote_ip with $new_remote_ip");
-                                       $ret_val = "conn_wrong_ip";
-                                       return $ret_val;
-                               }
+                       
+                       if ($connection_remote_ip eq $remote_ip) {
+                               notify($ERRORS{'DEBUG'}, 0, "connection 
detected from reservation remote IP: $connection_remote_ip --> 
$connection_local_ip:$port ($protocol)");
+                               return 1;
                        }
+                       
+                       # Connection is not from reservation remote IP address, 
check if user is logged in
+                       if ($self->user_logged_in()) {
+                               notify($ERRORS{'DEBUG'}, 0, "connection 
detected from different remote IP address than current reservation remote IP 
($remote_ip): $connection_remote_ip --> $connection_local_ip:$port ($protocol), 
updating reservation remote IP to $connection_remote_ip");
+                               
$self->data->set_reservation_remote_ip($connection_remote_ip);
+                               return 1;
+                       }
+                       
+                       notify($ERRORS{'DEBUG'}, 0, "ignoring connection, user 
is not logged in and remote IP address does not match current reservation 
remote IP ($remote_ip): $connection_remote_ip --> $connection_local_ip:$port 
($protocol)");
                }
        }
        
-       
-       return $ret_val;
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
-=head2 firewall_compare_update
-
- Parameters  : $node,$reote_IP, $identity, $type
- Returns     : 0 or 1 (nochange or updated)
- Description : compares and updates the firewall for rdp port, specfically for 
windows
-               Currently only handles windows and allows two seperate scopes
-
-=cut
-
-sub firewall_compare_update {
-   my $self = shift;
-   if (ref($self) !~ /windows/i) {
-      notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it 
must be called as a class method");
-      return;
-   }
-       
-   my $computer_node_name = $self->data->get_computer_node_name();
-   my $imagerevision_id   = $self->data->get_imagerevision_id();
-   my $remote_ip          = $self->data->get_reservation_remote_ip();
-       
-       if (!$remote_ip) {
-               notify($ERRORS{'WARNING'}, 0, "unable to update firewall on 
$computer_node_name, remote IP could not be retrieved for reservation");
-      return;
-       }
-       
-   # Retrieve the connect method info
-   my $connect_method_info = get_connect_method_info($imagerevision_id);
-   if (!$connect_method_info) {
-      notify($ERRORS{'WARNING'}, 0, "failed to retrieve connect method info 
for image revision $imagerevision_id");
-      return;
-   }
-       
-   # Retrieve the firewall configuration from the computer
-   my $firewall_configuration = $self->get_firewall_configuration() || return;
-       
-       # Loop through the connect methods, check to make sure firewall is open 
for remote IP
-   for my $connect_method_id (sort keys %{$connect_method_info} ) {
-      my $connect_method_name = 
$connect_method_info->{$connect_method_id}{name};
-      my $protocol            = 
$connect_method_info->{$connect_method_id}{protocol} || 'TCP';
-      my $port                = 
$connect_method_info->{$connect_method_id}{port};
-               
-               next if (!$port);
-               
-               if ($self->enable_firewall_port($protocol, $port, $remote_ip, 
0)) {
-                       notify($ERRORS{'DEBUG'}, 0, "opened/verified firewall 
port $port on $computer_node_name for $remote_ip $connect_method_name connect 
method");
-               }
-       }
-       return 1;
-
+       return 0;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -11745,16 +11682,25 @@ sub check_rdp_port_configuration {
                return;
        }
        
-       # Make sure only 1 port is defined
-       my @protocols = keys %{$connect_method_port_info};
-       my $protocol = $connect_method_port_info->{$protocols[0]};
-       my @ports = keys %$protocol;
-       my $connect_method_rdp_port = $ports[0];
-       if (scalar(@protocols) > 1 || scalar(@ports) > 1) {
+       # Extract the port numbers - multiple ports may be defined, for example 
TCP/3389 and UDP/3389
+       my %connect_method_port_hash;
+       for my $connect_method_port_id (keys %$connect_method_port_info) {
+               my $port = 
$connect_method_port_info->{$connect_method_port_id}{port};
+               $connect_method_port_hash{$port} = 1;
+       }
+       
+       # Make sure a single port number is defined for the RDP connect method
+       my @connect_method_ports = keys(%connect_method_port_hash);
+       if (!@connect_method_ports) {
+               notify($ERRORS{'WARNING'}, 0, "port is not defined for connect 
method:\n" . format_data($connect_method_port_info));
+               return;
+       }
+       elsif (scalar(@connect_method_ports) > 1) {
                notify($ERRORS{'WARNING'}, 0, "unable to determine which port 
is supposed to be used for RDP, multiple ports are defined for connect 
method:\n" . format_data($connect_method_port_info));
                return;
        }
        
+       my $connect_method_rdp_port = $connect_method_ports[0];
        my $rdp_port_key = 
'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal 
Server\WinStations\RDP-Tcp';
        my $rdp_port_value = 'PortNumber';
        

Modified: vcl/trunk/managementnode/lib/VCL/Module/State.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/State.pm?rev=1644185&r1=1644184&r2=1644185&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/State.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/State.pm Tue Dec  9 21:11:37 2014
@@ -93,6 +93,7 @@ sub initialize {
        my $is_vm = $self->data->get_computer_vmhost_id(0);
        my $is_parent_reservation = $self->data->is_parent_reservation();
        my $reservation_count = $self->data->get_reservation_count();
+       my $nathost_id = $self->data->get_nathost_id(0);
        
        # Initialize the database handle count
        $ENV{dbh_count} = 0;
@@ -138,6 +139,20 @@ sub initialize {
                $self->set_vmhost_os($vmhost_os);
        }
        
+       # Create a NAT host OS object if computer is mapped to a NAT host
+       my $nathost_os;
+       if ($nathost_id) {
+               $nathost_os = $self->create_nathost_os_object();
+               if (!$nathost_os) {
+                       $self->reservation_failed("failed to create NAT host OS 
object");
+               }
+               $self->set_nathost_os($nathost_os);
+               
+               # Allow the OS object to access the nathost_os object
+               # This is necessary to allow the OS code to call the 
subroutines to forward ports
+               $self->os->set_nathost_os($self->nathost_os());
+       }
+       
        # Create a provisioning object
        if (my $provisioner = $self->create_provisioning_object()) {
                $self->set_provisioner($provisioner);


Reply via email to