You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@vcl.apache.org by ar...@apache.org on 2016/02/09 18:35:02 UTC

svn commit: r1729421 - /vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm

Author: arkurth
Date: Tue Feb  9 17:35:02 2016
New Revision: 1729421

URL: http://svn.apache.org/viewvc?rev=1729421&view=rev
Log:
VCL-844
Added subroutines to VIM_SSH.pm:
firewall_ruleset_allow_ip
firewall_ruleset_enable
get_firewall_ruleset_info
get_matching_firewall_ruleset_info
is_firewall_port_allowed

These are used to determine configure the firewalls between 2 ESXi hosts to allow SSH traffic for a migration.

Fixed problem where a VMware snapshot would fail if taken on a powered off or suspended VM after a previous snapshot was successfully taken.

Added 6 minute timeout to vim-cmd power on command. Powering on a VM may take some time if it is suspended.

Modified:
    vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm

Modified: vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm?rev=1729421&r1=1729420&r2=1729421&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm Tue Feb  9 17:35:02 2016
@@ -264,13 +264,23 @@ sub _run_vim_cmd {
 			}
 			return;
 		}
-		elsif ($exit_status != 0 || grep(/^(vim-cmd:|Killed|terminate called|Aborted|what\()/i, @$output)) {
+		elsif (grep(/^(vim-cmd:|Killed|terminate called|Aborted|what\()/i, @$output)) {
 			# terminate called after throwing an instance of 'std::bad_alloc'
 			# what():  std::bad_alloc
 			# Aborted
 			notify($ERRORS{'WARNING'}, 0, "attempt $attempt/$attempt_limit: failed to execute command on VM host $vmhost_computer_name: $command, exit status: $exit_status, output:\n" . join("\n", @$output));
 			next ATTEMPT;
 		}
+		elsif ($exit_status != 0) {
+			if (grep(/(Create snapshot failed)/i, @$output)) {
+				notify($ERRORS{'WARNING'}, 0, "attempt $attempt/$attempt_limit: command failed on VM host $vmhost_computer_name, not making another attempt, task error checking will be done by calling subroutine, command: $command, exit status: $exit_status, output:\n" . join("\n", @$output));
+				return ($exit_status, $output);
+			}
+			else {
+				notify($ERRORS{'WARNING'}, 0, "attempt $attempt/$attempt_limit: command failed on VM host $vmhost_computer_name: $command, exit status: $exit_status, output:\n" . join("\n", @$output));
+				next ATTEMPT;
+			}
+		}
 		else {
 			# VIM command command was executed
 			if ($attempt > 1) {
@@ -1038,6 +1048,12 @@ sub _wait_for_task {
 				my $task_info_all = $self->_get_task_info();
 				notify($ERRORS{'WARNING'}, 0, "task $task_id did not complete successfully, state: $task_state, error message: $error_message, task info:\n" . format_data($task_info_all));
 			}
+			elsif ($error_message =~ /state of the virtual machine has not changed since the last snapshot/i) {
+				# Snapshot may fail if VM is suspended and snapshot was already taken after suspension, message will be:
+				# message = "An error occurred while taking a snapshot: The state of the virtual machine has not changed since the last snapshot operation."
+				notify($ERRORS{'DEBUG'}, 0, "snapshot task is not necessary: $task_id, message: $error_message");
+				return 1;
+			}
 			else {
 				notify($ERRORS{'WARNING'}, 0, "task $task_id did not complete successfully, state: $task_state, error message: $error_message");
 			}
@@ -1192,7 +1208,7 @@ sub vm_power_on {
 	}
 	
 	my $vim_cmd_arguments = "vmsvc/power.on $vm_id";
-	my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
+	my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments, 360);
 	return if !$output;
 	
 	# Expected output if the VM was not previously powered on:
@@ -2053,10 +2069,12 @@ sub create_snapshot {
 	
 	notify($ERRORS{'DEBUG'}, 0, "create snapshot output:\n" . join("\n", @$output));
 	
-	if (grep(/failed|invalid/i, @$output)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to create snapshot of VM $vmx_file_path, VIM command arguments: '$vim_cmd_arguments', output:\n" . join("\n", @$output));
-		return;
-	}
+	# IMPORTANT: Don't check for 'failed' in the output, it may contain failed but the snapshot is not necessary:
+	# Snapshot not taken since the state of the virtual machine has not changed since the last snapshot operation.
+	#if (grep(/failed|invalid/i, @$output)) {
+	#	notify($ERRORS{'WARNING'}, 0, "failed to create snapshot of VM $vmx_file_path, VIM command arguments: '$vim_cmd_arguments', output:\n" . join("\n", @$output));
+	#	return;
+	#}
 	
 	# Get the task ID
 	my @task_ids = $self->_get_task_ids($vmx_file_path, 'createSnapshot');
@@ -2632,6 +2650,120 @@ sub get_config_option_guest_os_info {
  Returns     : true
  Description : Used for development/testing only. Prints list of possible
                guestOS values.
+               asianux3Guest                            vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               asianux3_64Guest                         vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               asianux4Guest                                   vmx-08 vmx-09 vmx-10 vmx-11
+               asianux4_64Guest                                vmx-08 vmx-09 vmx-10 vmx-11
+               asianux5_64Guest                                                     vmx-11
+               centos64Guest                            vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               centosGuest                              vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               coreos64Guest                                                        vmx-11
+               darwin10Guest                            vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               darwin10_64Guest                         vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               darwin11Guest                                   vmx-08 vmx-09 vmx-10 vmx-11
+               darwin11_64Guest                                vmx-08 vmx-09 vmx-10 vmx-11
+               darwin12_64Guest                                       vmx-09 vmx-10 vmx-11
+               darwin13_64Guest                                              vmx-10 vmx-11
+               darwin14_64Guest                                                     vmx-11
+               darwin64Guest                            vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               darwinGuest                              vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               debian4Guest                             vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               debian4_64Guest                          vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               debian5Guest                             vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               debian5_64Guest                          vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               debian6Guest                                    vmx-08 vmx-09 vmx-10 vmx-11
+               debian6_64Guest                                 vmx-08 vmx-09 vmx-10 vmx-11
+               debian7Guest                                                  vmx-10 vmx-11
+               debian7_64Guest                                               vmx-10 vmx-11
+               debian8Guest                                                         vmx-11
+               debian8_64Guest                                                      vmx-11
+               dosGuest                                 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               eComStation2Guest                               vmx-08 vmx-09 vmx-10 vmx-11
+               eComStationGuest                         vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               fedora64Guest                                                        vmx-11
+               fedoraGuest                                                          vmx-11
+               freebsd64Guest                           vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               freebsdGuest                             vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               netware5Guest              vmx-03 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               netware6Guest              vmx-03 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               oesGuest                          vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               openServer5Guest                         vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               openServer6Guest                                vmx-08 vmx-09 vmx-10 vmx-11
+               opensuse64Guest                                                      vmx-11
+               opensuseGuest                                                        vmx-11
+               oracleLinux64Guest                       vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               oracleLinuxGuest                         vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               os2Guest                                 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               other24xLinux64Guest                     vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               other24xLinuxGuest                       vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               other26xLinux64Guest                     vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               other26xLinuxGuest                       vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               other3xLinux64Guest                                           vmx-10 vmx-11
+               other3xLinuxGuest                                             vmx-10 vmx-11
+               otherGuest                 vmx-03 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               otherGuest64                      vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               otherLinux64Guest                 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               otherLinuxGuest            vmx-03 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               rhel2Guest                        vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               rhel3Guest                        vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               rhel3_64Guest                     vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               rhel4Guest                        vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               rhel4_64Guest                     vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               rhel5Guest                        vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               rhel5_64Guest                     vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               rhel6Guest                               vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               rhel6_64Guest                            vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               rhel7Guest                                             vmx-09 vmx-10
+               rhel7_64Guest                                          vmx-09 vmx-10 vmx-11
+               sles10Guest                       vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               sles10_64Guest                    vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               sles11Guest                       vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               sles11_64Guest                    vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               sles12Guest                                            vmx-09 vmx-10
+               sles12_64Guest                                         vmx-09 vmx-10 vmx-11
+               sles64Guest                       vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               slesGuest                         vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               solaris10Guest                    vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               solaris10_64Guest                 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               solaris11_64Guest                               vmx-08 vmx-09 vmx-10 vmx-11
+               solaris8Guest                            vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               solaris9Guest                            vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               ubuntu64Guest                     vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               ubuntuGuest                       vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               unixWare7Guest                           vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               vmkernel5Guest                                  vmx-08 vmx-09 vmx-10 vmx-11
+               vmkernel6Guest                                                       vmx-11
+               vmkernelGuest                            vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               win2000AdvServGuest        vmx-03 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               win2000ProGuest                   vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               win2000ServGuest           vmx-03 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               win31Guest                               vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               win95Guest                               vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               win98Guest                               vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winLonghorn64Guest                vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winLonghornGuest                  vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winNTGuest                 vmx-03 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winNetBusinessGuest        vmx-03 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winNetDatacenter64Guest           vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winNetDatacenterGuest      vmx-03 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winNetEnterprise64Guest           vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winNetEnterpriseGuest      vmx-03 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winNetStandard64Guest             vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winNetStandardGuest        vmx-03 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winNetWebGuest             vmx-03 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winVista64Guest                   vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winVistaGuest                     vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winXPPro64Guest                   vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               winXPProGuest              vmx-03 vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               windows7Guest                     vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               windows7Server64Guest             vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               windows7_64Guest                  vmx-04 vmx-07 vmx-08 vmx-09 vmx-10 vmx-11
+               windows8Guest                                   vmx-08 vmx-09 vmx-10 vmx-11
+               windows8Server64Guest                           vmx-08 vmx-09 vmx-10 vmx-11
+               windows8_64Guest                                vmx-08 vmx-09 vmx-10 vmx-11
+               windows9Guest                                                 vmx-10 vmx-11
+               windows9Server64Guest                                         vmx-10 vmx-11
+               windows9_64Guest                                              vmx-10 vmx-11
 
 =cut
 
@@ -3039,6 +3171,471 @@ sub get_vm_cpu_usage {
 }
 
 #/////////////////////////////////////////////////////////////////////////////
+
+=head2 firewall_ruleset_allow_ip
+
+ Parameters  : $ruleset_name, $ip_address
+ Returns     : boolean
+ Description : Adds an IP address to a firewall ruleset to allow traffic.
+
+=cut
+
+sub firewall_ruleset_allow_ip {
+	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;
+	}
+	
+	my ($ruleset_name, $ip_address) = @_;
+	if (!defined($ruleset_name)) {
+		notify($ERRORS{'WARNING'}, 0, "ruleset name argument was not supplied");
+		return;
+	}
+	elsif (!defined($ip_address)) {
+		notify($ERRORS{'WARNING'}, 0, "IP address argument was not supplied");
+		return;
+	}
+	
+	my $vmhost_computer_name = $self->data->get_vmhost_hostname();
+	
+	my $command;
+	if ($ip_address =~ /all/i) {
+		$command = "esxcli network firewall ruleset set --ruleset-id=$ruleset_name --allowed-all true";
+	}
+	else {
+		$command = "esxcli network firewall ruleset allowedip add --ruleset-id=$ruleset_name --ip-address=$ip_address";
+	}
+	
+	my ($exit_status, $output) = $self->vmhost_os->execute($command);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command on VM host $vmhost_computer_name: $command");
+		return;
+	}
+	elsif (grep(/already (exist|allowed)/i, @$output)) {
+		notify($ERRORS{'OK'}, 0, "$ip_address is already allowed for $ruleset_name ruleset on VM host $vmhost_computer_name");
+		return 1;
+	}
+	elsif (grep(/allowed-all/i, @$output)) {
+		# Couldn't update allowed ip list when allowed-all flag is true.
+		notify($ERRORS{'OK'}, 0, "all IP addresses are already allowed for $ruleset_name ruleset on VM host $vmhost_computer_name");
+		return 1;
+	}
+	elsif ($exit_status ne 0) {
+		notify($ERRORS{'WARNING'}, 0, "failed to add $ip_address to $ruleset_name ruleset on VM host $vmhost_computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
+		return 0;
+	}
+	else {
+		notify($ERRORS{'OK'}, 0, "added $ip_address to $ruleset_name ruleset on VM host $vmhost_computer_name");
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 firewall_ruleset_enable
+
+ Parameters  : $ruleset_name
+ Returns     : boolean
+ Description : Enables a firewall ruleset.
+
+=cut
+
+sub firewall_ruleset_enable {
+	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;
+	}
+	
+	my ($ruleset_name) = @_;
+	if (!defined($ruleset_name)) {
+		notify($ERRORS{'WARNING'}, 0, "ruleset name argument was not supplied");
+		return;
+	}
+	
+	my $vmhost_computer_name = $self->data->get_vmhost_hostname();
+	
+	my $command = "esxcli network firewall ruleset set --ruleset-id=$ruleset_name --enabled true";
+	my ($exit_status, $output) = $self->vmhost_os->execute($command);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command on VM host $vmhost_computer_name: $command");
+		return;
+	}
+	elsif ($exit_status ne 0) {
+		notify($ERRORS{'WARNING'}, 0, "failed to enable $ruleset_name ruleset on VM host $vmhost_computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
+		return 0;
+	}
+	else {
+		notify($ERRORS{'OK'}, 0, "enabled $ruleset_name ruleset on VM host $vmhost_computer_name");
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_firewall_ruleset_info
+
+ Parameters  : none
+ Returns     : array
+ Description : Retrieves information about all of the firewall rulesets from the
+               VM host. A hash reference is returned. Hash keys are the ruleset
+               names:
+                  "ipfam" => {
+                    "enabled" => 1,
+                    "rules" => [
+                      {
+                        "Direction" => "Inbound",
+                        "PortBegin" => 6999,
+                        "PortEnd" => 6999,
+                        "PortType" => "Dst",
+                        "Protocol" => "UDP"
+                      },
+                      {
+                        "Direction" => "Outbound",
+                        "PortBegin" => 6999,
+                        "PortEnd" => 6999,
+                        "PortType" => "Dst",
+                        "Protocol" => "UDP"
+                      }
+                    ]
+                  },
+                  "nfs41Client" => {
+                    "enabled" => 0,
+                    "rules" => [
+                      {
+                        "Direction" => "Outbound",
+                        "PortBegin" => 2049,
+                        "PortEnd" => 2049,
+                        "PortType" => "Dst",
+                        "Protocol" => "TCP"
+                      }
+                    ]
+                  },
+
+=cut
+
+sub get_firewall_ruleset_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;
+	}
+	
+	return $ENV{firewall_ruleset_info} if defined($ENV{firewall_ruleset_info});
+	
+	my $vmhost_computer_name = $self->data->get_vmhost_hostname();
+	
+	my $ruleset_info = {};
+	
+	# Get the enabled/disabled status of each ruleset
+	my $ruleset_list_command = "esxcli --formatter=csv network firewall ruleset list";
+	my ($ruleset_list_exit_status, $ruleset_list_output) = $self->vmhost_os->execute($ruleset_list_command);
+	if (!defined($ruleset_list_output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command on VM host $vmhost_computer_name: $ruleset_list_command");
+		return;
+	}
+	elsif ($ruleset_list_exit_status ne 0) {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve firewall ruleset info from VM host $vmhost_computer_name, exit status: $ruleset_list_exit_status, command:\n$ruleset_list_command\noutput:\n" . join("\n", @$ruleset_list_output));
+		return 0;
+	}
+	
+	# Enabled,Name,
+	# true,sshServer,
+	# true,sshClient,
+	# true,nfsClient,
+	# false,nfs41Client,
+	# ...
+	for my $line (@$ruleset_list_output) {
+		if ($line !~ /(true|false)/) {
+			next;
+		}
+		my ($enabled, $ruleset_name) = split(/,/, $line);
+		if ($enabled =~ /true/i) {
+			$ruleset_info->{$ruleset_name}{enabled} = 1;
+		}
+		else {
+			$ruleset_info->{$ruleset_name}{enabled} = 0;
+		}
+	}
+	
+	
+	
+	
+	# Get the allowed IPs of each ruleset
+	my $ruleset_allowed_ip_command = "esxcli --formatter=csv network firewall ruleset allowedip list";
+	my ($ruleset_allowed_ip_exit_status, $ruleset_allowed_ip_output) = $self->vmhost_os->execute($ruleset_allowed_ip_command);
+	if (!defined($ruleset_allowed_ip_output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command on VM host $vmhost_computer_name: $ruleset_allowed_ip_command");
+		return;
+	}
+	elsif ($ruleset_allowed_ip_exit_status ne 0) {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve firewall ruleset allowed IP info from VM host $vmhost_computer_name, exit status: $ruleset_allowed_ip_exit_status, command:\n$ruleset_allowed_ip_command\noutput:\n" . join("\n", @$ruleset_allowed_ip_output));
+		return 0;
+	}
+	
+	# AllowedIPAddresses,Ruleset,
+	# "152.1.4.152,10.25.7.2,10.25.11.104,10.25.0.241,10.25.0.242,10.25.0.243,10.25.0.244,10.25.0.245,10.25.0.246,10.25.1.178,",sshServer,
+	# "All,",sshClient,
+	# ...
+	for my $line (@$ruleset_allowed_ip_output) {
+		if ($line =~ /Ruleset/) {
+			next;
+		}
+		
+		my ($ip_address_string, $ruleset_name) = $line =~ /^"?(.+),"?,([^,]+),/g;
+		if (!defined($ruleset_name)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to retrieve firewall ruleset allowed IP info from VM host $vmhost_computer_name, failed to parse line:\n$line");
+			return;
+		}
+		
+		my @ip_addresses = split(/,/, $ip_address_string);
+		$ruleset_info->{$ruleset_name}{allowedip} = \@ip_addresses;
+	}
+	
+	# Get the rule port information
+	my $rule_list_command = "esxcli --formatter=csv network firewall ruleset rule list";
+	my ($rule_list_exit_status, $rule_list_output) = $self->vmhost_os->execute($rule_list_command);
+	if (!defined($rule_list_output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command on VM host $vmhost_computer_name: $rule_list_command");
+		return;
+	}
+	elsif ($rule_list_exit_status ne 0) {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve firewall rule info from VM host $vmhost_computer_name, exit status: $rule_list_exit_status, command:\n$rule_list_command\noutput:\n" . join("\n", @$rule_list_output));
+		return 0;
+	}
+	
+	# Parse the header line
+	# Direction,PortBegin,PortEnd,PortType,Protocol,Ruleset,
+	my $rule_header_line = shift @$rule_list_output;
+	my @rule_fields = split(/,/, $rule_header_line);
+	my $rule_field_count = scalar(@rule_fields);
+	
+	# Inbound,22,22,Dst,TCP,sshServer,
+	# Outbound,22,22,Dst,TCP,sshClient,
+	# Outbound,0,65535,Dst,TCP,nfsClient,
+	# Outbound,2049,2049,Dst,TCP,nfs41Client,
+	for my $line (@$rule_list_output) {
+		if ($line !~ /bound/) {
+			next;
+		}
+		my @values = split(/,/, $line);
+		my $rule = {};
+		for (my $i = 0; $i < $rule_field_count; $i++) {
+			my $field = $rule_fields[$i];
+			my $value = $values[$i];
+			$rule->{$field} = $value;
+		}
+		my $ruleset_name = $rule->{Ruleset};
+		delete $rule->{Ruleset};
+		
+		push @{$ruleset_info->{$ruleset_name}{rules}}, $rule;
+	}
+	
+	notify($ERRORS{'OK'}, 0, "retrieved firewall ruleset info from VM host $vmhost_computer_name:\n" . format_data($ruleset_info));
+	$ENV{firewall_ruleset_info} = $ruleset_info;
+	return $ruleset_info;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_matching_firewall_ruleset_info
+
+ Parameters  : $port (optional), $direction (optional), $include_disabled (optional)
+ Returns     : hash reference
+ Description : 
+
+=cut
+
+sub get_matching_firewall_ruleset_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;
+	}
+	
+	my ($direction_argument, $port_argument, $exclude_disabled) = @_;
+	if ($port_argument) {
+		if ($port_argument =~ /^(\*|any)$/) {
+			$port_argument = 'any';
+		}
+		elsif ($port_argument !~ /^\d+$/) {
+			notify($ERRORS{'WARNING'}, 0, "port argument was specified but the value is not an integer: $port_argument");
+			return;
+		}
+	}
+	else {
+		$port_argument = 'any';
+	}
+	
+	if ($direction_argument) {
+		if ($direction_argument =~ /^(\*|any)$/) {
+			$direction_argument = 'any';
+		}
+		elsif ($direction_argument =~ /in/i) {
+			$direction_argument = 'inbound';
+		}
+		elsif ($direction_argument =~ /out/i) {
+			$direction_argument = 'outbound';
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "direction argument is not valid: $direction_argument");
+			return;
+		}
+	}
+	else {
+		$direction_argument = 'any';
+	}
+	
+	my $vmhost_computer_name = $self->data->get_vmhost_short_name();
+	
+	my $ruleset_info = $self->get_firewall_ruleset_info() || return;
+	
+	my $matching_ruleset_info = {};
+	
+	RULESET: for my $ruleset_name (sort {lc($a) cmp lc($b)} keys %$ruleset_info) {
+		my $ruleset = $ruleset_info->{$ruleset_name};
+		
+		# Ignore disabled rulesets if argument was supplied
+		my $enabled = $ruleset->{enabled};
+		if (!$enabled && $exclude_disabled) {
+			next RULESET;
+		}
+		
+		RULE: for my $rule (@{$ruleset->{rules}}) {
+			if ($direction_argument ne 'any') {
+				my $direction = $rule->{Direction};
+				if ($direction !~ /$direction_argument/i) {
+					#notify($ERRORS{'DEBUG'}, 0, "$ruleset_name direction does not match: argument: $direction_argument, rule: $direction");
+					next RULE;
+				}
+			}
+			
+			if ($port_argument ne 'any') {
+				my $port_begin = $rule->{PortBegin};
+				my $port_end = $rule->{PortEnd};
+				if ($port_argument < $port_begin || $port_argument > $port_end) {
+					#notify($ERRORS{'DEBUG'}, 0, "$ruleset_name port does not match: argument: $port_argument, port begin: $port_begin, port end: $port_end");
+					next RULE;
+				}
+			}
+			
+			$matching_ruleset_info->{$ruleset_name} = $ruleset;
+		}
+	}
+	
+	my $ruleset_count = scalar(keys %$matching_ruleset_info);
+	notify($ERRORS{'DEBUG'}, 0, "retrieved $ruleset_count matching firewall ruleset from VM host $vmhost_computer_name matching port: $port_argument, direction: $direction_argument, exclude disabled: " . ($exclude_disabled ? 'yes' : 'no') . "\n" . join("\n", sort keys %$matching_ruleset_info));
+	return $matching_ruleset_info;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 is_firewall_port_allowed
+
+ Parameters  : $direction, $port, $remote_ip_address
+ Returns     : boolean
+ Description : Checks if an enabled firewall ruleset exists which matches the
+               arguments.
+ 
+               *** WARNING ***
+               There seems to be no reliable way to determine if a port is truly
+               open if a custom rule exists with identical port and definitions
+               as a standard service. The IBMIMM is an example. It defines
+               outbound port 22 as does sshClient. If both of these services are
+               enabled with different allowed IP address lists, the allowed IP
+               address list of the service which started last prevails.
+               Example:
+               * sshClient allows only 10.1.1.1
+               * IBMIMM allows only 10.2.2.2
+               * Restart sshClient : 10.1.1.1 allowed, 10.2.2.2 blocked
+               * Restart IBMIMM    : 10.2.2.2 allowed, 10.1.1.1 blocked
+
+=cut
+
+sub is_firewall_port_allowed {
+	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;
+	}
+	
+	my ($direction_argument, $port_argument, $remote_ip_address) = @_;
+	
+	if (!defined($direction_argument)) {
+		notify($ERRORS{'WARNING'}, 0, "direction argument was not specified");
+		return;
+	}
+	elsif ($direction_argument =~ /in/i) {
+		$direction_argument = 'inbound';
+	}
+	elsif ($direction_argument =~ /out/i) {
+		$direction_argument = 'outbound';
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "direction argument is not valid: $direction_argument");
+		return;
+	}
+	
+	if (!defined($remote_ip_address)) {
+		notify($ERRORS{'WARNING'}, 0, "remote IP address argument was not specified");
+		return;
+	}
+	elsif (!is_valid_ip_address($remote_ip_address)) {
+		notify($ERRORS{'WARNING'}, 0, "remote IP address argument is not valid: $remote_ip_address");
+		return;
+	}
+	
+	my $vmhost_computer_name = $self->data->get_vmhost_short_name();
+	
+	my $ruleset_info = $self->get_firewall_ruleset_info() || return;
+	
+	my $matching_ruleset_info = {};
+	
+	RULESET: for my $ruleset_name (keys %$ruleset_info) {
+		my $ruleset = $ruleset_info->{$ruleset_name};
+		
+		# Ignore disabled rulesets
+		if (!$ruleset->{enabled}) {
+			#notify($ERRORS{'DEBUG'}, 0, "$ruleset_name ruleset ignored because it is not enabled");
+			next RULESET;
+		}
+		
+		my $direction_port_match = 0;
+		RULE: for my $rule (@{$ruleset->{rules}}) {
+			if ($rule->{Direction} !~ /$direction_argument/i) {
+				next RULE;
+			}
+			
+			if ($port_argument >= $rule->{PortBegin} && $port_argument <= $rule->{PortEnd}) {
+				$direction_port_match = 1;
+				#notify($ERRORS{'DEBUG'}, 0, "$ruleset_name ruleset rule matches direction: $direction_argument, port: $port_argument\n" . format_data($rule));
+				last RULE;
+			}
+		}
+		if (!$direction_port_match) {
+			next RULESET;
+		}
+		
+		my @allowed_ip_addresses = @{$ruleset->{allowedip}};
+		if ($allowed_ip_addresses[0] =~ /all/i) {
+			notify($ERRORS{'DEBUG'}, 0, "$ruleset_name ruleset on VM host $vmhost_computer_name allows $direction_argument port $port_argument " . ($direction_argument =~ /in/i ? 'from' : 'to') . " all:\n" . format_data($ruleset));
+			next RULESET;
+			#return 1;
+		}
+		elsif (grep { $_ eq $remote_ip_address } @allowed_ip_addresses) {
+			notify($ERRORS{'DEBUG'}, 0, "$ruleset_name ruleset on VM host $vmhost_computer_name allows $direction_argument port $port_argument " . ($direction_argument =~ /in/i ? 'from' : 'to') . " $remote_ip_address:\n" . format_data($ruleset));
+			next RULESET;
+			#return 1;
+		}
+		notify($ERRORS{'DEBUG'}, 0, "$ruleset_name ruleset on VM host $vmhost_computer_name does NOT allow $direction_argument port $port_argument " . ($direction_argument =~ /in/i ? 'from' : 'to') . " $remote_ip_address:\n" . format_data($ruleset));
+	}
+	
+	notify($ERRORS{'DEBUG'}, 0, "$direction_argument firewall port $port_argument is NOT allowed for $remote_ip_address on VM host $vmhost_computer_name");
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
 
 =head2 DESTROY