Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm?rev=1798487&r1=1798486&r2=1798487&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm 
(original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm Mon 
Jun 12 16:33:35 2017
@@ -1,1207 +1,1207 @@
-#!/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::firewalld.pm
-
-=head1 DESCRIPTION
-
- This module provides VCL support for firewalld-based firewalls.
-
-=cut
-
-###############################################################################
-package VCL::Module::OS::Linux::firewall::firewalld;
-
-# Specify the lib path using FindBin
-use FindBin;
-use lib "$FindBin::Bin/../../../../..";
-
-# Configure inheritance
-use base qw(VCL::Module::OS::Linux::firewall::iptables);
-
-# Specify the version of this module
-our $VERSION = '2.4';
-
-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 || {};
-       
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       notify($ERRORS{'DEBUG'}, 0, "initializing " . ref($self) . " object to 
control $computer_name");
-       
-       if (!$self->os->service_exists('firewalld')) {
-               notify($ERRORS{'DEBUG'}, 0, ref($self) . " object not 
initialized to control $computer_name, firewalld service does not exist");
-               return 0;
-       }
-       
-       if (!$self->os->is_service_enabled('firewalld')) {
-               notify($ERRORS{'DEBUG'}, 0, ref($self) . " object not 
initialized to control $computer_name, firewalld service is not enabled");
-               return 0;
-       }
-       
-       if (!$self->os->command_exists('firewall-cmd')) {
-               notify($ERRORS{'DEBUG'}, 0, ref($self) . " object not 
initialized to control $computer_name, firewall-cmd command does not exist");
-               return 0;
-       }
-       
-       notify($ERRORS{'DEBUG'}, 0, ref($self) . " object initialized to 
control $computer_name");
-       return 1;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 process_post_load
-
- Parameters  : none
- Returns     : boolean
- Description : Performs the initial iptables firewall configuration after an
-               image is loaded:
-               * Performs all of the tasks done by
-                 iptables.pm::process_post_load except the pre-VCL 2.5 legacy
-                 cleanup tasks
-               * Removes the ssh protocol from the public zone
-
-=cut
-
-sub process_post_load {
-       my $self = shift;
-       if (ref($self) !~ /VCL::Module::OS::Linux::firewall/i) {
-               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
-               return 0;
-       }
-       
-       my $computer_name = $self->data->get_computer_short_name();
-       
-       notify($ERRORS{'DEBUG'}, 0, "beginning firewalld post-load 
configuration on $computer_name");
-       
-       # Call subroutine in iptables.pm
-       return unless $self->SUPER::process_post_load();
-       
-       # Remove ssh from public zone
-       return unless $self->remove_service('public', 'ssh');
-       
-       $self->save_configuration();
-       
-       notify($ERRORS{'DEBUG'}, 0, "completed firewalld post-load 
configuration on $computer_name");
-       return 1;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 get_all_direct_rules
-
- Parameters  : none
- Returns     : array
- Description : Calls 'firewall-cmd --permanent --direct --get-all-rules' and
-               returns an array of strings.
-
-=cut
-
-sub get_all_direct_rules {
-       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 $computer_name = $self->data->get_computer_hostname();
-       
-       my $command = "firewall-cmd --permanent --direct --get-all-rules";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
retrieve all firewalld direct rules on $computer_name: $command");
-               return;
-       }
-       elsif ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to retrieve all firewalld 
direct rules on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
-               return;
-       }
-       
-       # Rules should be in the format:
-       # ipv4 filter vcl-pre_capture 0 --jump ACCEPT --protocol tcp --match 
comment --comment 'VCL: Allow traffic to SSH port 22 from any IP address 
(2017-04-07 17:19:21)' --match tcp --destination-port 22
-       # ipv4 filter INPUT 0 --jump vcl-pre_capture --match comment --comment 
'VCL: jump to rules added during the pre-capture stage (2017-04-07 17:19:21)'
-       my @rules = grep(/^(ipv4|ipv6|eb)/, @$output);
-       
-       notify($ERRORS{'DEBUG'}, 0, "retrieved all firewalld direct rules 
defined on $computer_name:\n" . join("\n", @rules));
-       return @rules;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 get_direct_chain_rules
-
- Parameters  : $table_name, $chain_name
- Returns     : array
- Description : Calls 'firewall-cmd --permanent --direct --get-rules' and 
returns
-               an array of strings.
-
-=cut
-
-sub get_direct_chain_rules {
-       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 (!$table_name) {
-               notify($ERRORS{'WARNING'}, 0, "table name argument was not 
specified");
-               return;
-       }
-       elsif (!$chain_name) {
-               notify($ERRORS{'WARNING'}, 0, "chain name argument was not 
specified");
-               return;
-       }
-       
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       my $command = "firewall-cmd --permanent --direct --get-rules ipv4 
$table_name $chain_name";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
retrieve firewalld direct rules defined for '$chain_name' chain in 
'$table_name' table on $computer_name: $command");
-               return;
-       }
-       elsif ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to retrieve firewalld 
direct rules defined for '$chain_name' chain in '$table_name' table on 
$computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . 
join("\n", @$output));
-               return;
-       }
-       
-       # All rule lines should begin with an integer:
-       #    0 --jump ACCEPT --source 10.25.7.2 --match comment --comment 'VCL: 
Allow traffic from management node (2017-04-07 15:36:24)'
-       #    1 --jump ACCEPT --source 10.25.7.2
-       my @rules = grep(/^\d+/, @$output);
-       
-       notify($ERRORS{'DEBUG'}, 0, "retrieved firewalld direct rules defined 
for '$chain_name' chain in '$table_name' table on $computer_name:\n" . 
join("\n", @rules));
-       return @rules;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 save_configuration
-
- Parameters  : none
- Returns     : boolean
- Description : Calls 'firewall-cmd --reload'.
-
-=cut
-
-sub save_configuration {
-       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 $computer_name = $self->data->get_computer_hostname();
-       
-       my $command = "firewall-cmd --reload";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
reload firewalld configuration on $computer_name: $command");
-               return;
-       }
-       elsif ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to reload firewalld 
configuration on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
-               return 0;
-       }
-       else {
-               notify($ERRORS{'OK'}, 0, "reloaded firewalld configuration on 
$computer_name");
-               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 = "firewall-cmd --permanent --direct --add-chain ipv4 
$table_name $chain_name";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command 
$computer_name: $command");
-               return;
-       }
-       elsif (grep(/ALREADY_ENABLED/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;
-       }
-       elsif (!grep(/success/, @$output)) {
-               notify($ERRORS{'WARNING'}, 0, "potentially failed to create 
'$chain_name' chain in '$table_name' table on $computer_name, output does not 
contain 'success', 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");
-               #$self->save_configuration();
-               return 1;
-       }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 remove_direct_chain_rules
-
- Parameters  : $table_name, $chain_name
- Returns     : boolean
- Description : Flushes (deletes) rules from the specified chain.
-
-=cut
-
-sub remove_direct_chain_rules {
-       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();
-       
-       # !!! WARNING !!!
-       # DON'T USE --remove-rules
-       # With firewall-cmd version 0.4.3.2, this option removes rules from ALL 
direct chains, not just the one specified
-       #my $command = "firewall-cmd --permanent --direct --remove-rules ipv4 
$table_name $chain_name";
-       
-       my @rules = $self->get_direct_chain_rules($table_name, $chain_name);
-       for my $rule (@rules) {
-               # [--permanent] --direct --remove-rule { ipv4 | ipv6 | eb } 
table chain priority args
-               my $command = "firewall-cmd --permanent --direct --remove-rule 
ipv4 $table_name $chain_name $rule";
-               my ($exit_status, $output) = $self->os->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 remove rule 
from '$chain_name' chain in '$table_name' table on $computer_name: '$rule', 
exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", 
@$output));
-                       return 0;
-               }
-               else {
-                       notify($ERRORS{'OK'}, 0, "removed direct rule from 
'$chain_name' chain in '$table_name' table on $computer_name: '$rule'");
-               }
-       }
-       
-       notify($ERRORS{'OK'}, 0, "removed all direct rules from '$chain_name' 
chain in '$table_name' table on $computer_name");
-       return 1;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 delete_chain
-
- Parameters  : $table_name, $chain_name
- Returns     : boolean
- Description : Deletes an existing chain. Returns true if the chain was
-               successfully deleted or doesn't exist.
-
-=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_argument) = @_;
-       if (!defined($table_name)) {
-               notify($ERRORS{'WARNING'}, 0, "table name argument was not 
specified");
-               return;
-       }
-       elsif (!defined($chain_name_argument)) {
-               notify($ERRORS{'WARNING'}, 0, "chain name argument was not 
specified");
-               return;
-       }
-       
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       my @chains_deleted;
-       my @chain_names = $self->get_table_chain_names($table_name);
-       for my $chain_name (@chain_names) {
-               if ($chain_name !~ /^$chain_name_argument$/) {
-                       next;
-               }
-               
-               # Delete all rules which reference the chain being deleted or 
else the chain can't be deleted
-               # Do this BEFORE checking if the chain exists to clean up 
leftover references in direct.xml
-               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;
-               }
-       
-               $self->remove_direct_chain_rules($table_name, $chain_name) || 
return;
-               
-               my $command = "firewall-cmd --permanent --direct --remove-chain 
ipv4 $table_name $chain_name";
-               my ($exit_status, $output) = $self->os->execute($command, 0);
-               if (!defined($output)) {
-                       notify($ERRORS{'WARNING'}, 0, "failed to execute 
command $computer_name: $command");
-                       return;
-               }
-               elsif (grep(/NOT_ENABLED/i, @$output)) {
-                       notify($ERRORS{'OK'}, 0, "'$chain_name' chain in 
'$table_name' does not exist on $computer_name");
-               }
-               elsif ($exit_status ne '0') {
-                       notify($ERRORS{'WARNING'}, 0, "failed to delete 
'$chain_name' chain in '$table_name' table on $computer_name, exit status: 
$exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
-                       return 0;
-               }
-               elsif (!grep(/success/, @$output)) {
-                       notify($ERRORS{'WARNING'}, 0, "potentially failed to 
delete '$chain_name' chain in '$table_name' table on $computer_name, output 
does not contain 'success', exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
-               }
-               else {
-                       notify($ERRORS{'OK'}, 0, "deleted '$chain_name' chain 
in '$table_name' table on $computer_name");
-                       #$self->save_configuration();
-               }
-               
-               if (!$self->clean_direct_xml($table_name . '.*jump\s+' . 
$chain_name)) {
-                       return;
-               }
-               
-               notify($ERRORS{'OK'}, 0, "deleted '$chain_name' chain from 
'$table_name' table on $computer_name");
-               push @chains_deleted, $chain_name;
-       }
-       
-       if (!@chains_deleted) {
-               notify($ERRORS{'DEBUG'}, 0, "no chains exist in '$table_name' 
table on $computer_name matching argument: '$chain_name_argument'");
-       }
-       return 1;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 clean_direct_xml
-
- Parameters  : $regex_pattern
- Returns     : boolean
- Description : 
-
-=cut
-
-sub clean_direct_xml {
-       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 $regex_pattern = shift;
-       if (!defined($regex_pattern)) {
-               notify($ERRORS{'WARNING'}, 0, "regex pattern argument was not 
supplied");
-               return;
-       }
-       
-       $self->os->firewall->save_configuration();
-       
-       my @keep_lines;
-       my @prune_lines;
-       my $file_path = '/etc/firewalld/direct.xml';
-       my @lines = $self->os->get_file_contents($file_path);
-       for my $line (@lines) {
-               if ($line =~ /$regex_pattern/i) {
-                       push @prune_lines, $line;
-               }
-               else {
-                       push @keep_lines, $line;
-               }
-       }
-       
-       if (@prune_lines) {
-               my $updated_contents = join("\n", @keep_lines);
-               notify($ERRORS{'DEBUG'}, 0, "pruning the following lines from 
$file_path matching pattern: '$regex_pattern'\n" . join("\n", @prune_lines) . 
"\nnew file contents:\n$updated_contents");
-               return $self->os->create_text_file($file_path, 
$updated_contents);
-       }
-       else {
-               notify($ERRORS{'DEBUG'}, 0, "no lines were pruned from 
$file_path matching pattern: '$regex_pattern'");
-               return 1;
-       }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 _insert_rule
-
- Parameters  : $table_name, $chain_name, $argument_string
- Returns     : boolean
- Description : Executes the command to insert a firewalld direct rule. This is 
a
-               helper subroutine and should only be called by
-               iptable.pm::insert_rule.
-
-=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 ($table_name, $chain_name, $argument_string) = @_;
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       my $command = "firewall-cmd --permanent --direct --add-rule ipv4 
$table_name $chain_name 0 $argument_string";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to add 
direct firewalld rule to $chain_name chain in $table_name table on 
$computer_name: $command");
-               return;
-       }
-       elsif ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to add direct firewalld 
rule to $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, "added direct firewalld rule to 
$chain_name chain in $table_name table on $computer_name, command: $command, 
output:\n" . join("\n", @$output));
-               #$self->save_configuration();
-               return 1;
-       }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 _delete_rule
-
- Parameters  : $table_name, $chain_name, $rule_specification_string
- Returns     : boolean
- Description : Deletes a firewalld direct rule. This should only used as a
-               helper subroutine.
-
-=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 ($table_name, $chain_name, $rule_specification_string) = @_;
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       my $command = "firewall-cmd --permanent --direct --remove-rule ipv4 
$table_name $chain_name 0 $rule_specification_string";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
delete firewalld direct rule on $computer_name: $command");
-               return;
-       }
-       elsif ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to delete firewalld 
direct rule on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
-               return;
-       }
-       else {
-               notify($ERRORS{'OK'}, 0, "deleted firewalld direct rule on 
$computer_name, command: '$command', output:\n" . join("\n", @$output));
-               #$self->save_configuration();
-               return 1;
-       }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 remove_service
-
- Parameters  : $zone_name, $service
- Returns     : boolean
- Description : 
-
-=cut
-
-sub remove_service {
-       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 ($zone_name, $service) = @_;
-       if (!defined($zone_name)) {
-               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
-               return;
-       }
-       elsif (!defined($service)) {
-               notify($ERRORS{'WARNING'}, 0, "interface name argument was not 
supplied");
-               return;
-       }
-       $service = 'tcp' unless $service;
-       
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       # [--permanent] [--zone=zone] 
--remove-service=serviceid[-serviceid]/service
-       #            Remove the service from zone. If zone is omitted, default 
zone will be used. This option can be specified
-       #            multiple times.
-       
-       my $command = "firewall-cmd --permanent --zone=$zone_name 
--remove-service=$service";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
remove $service service from '$zone_name' zone on $computer_name: $command");
-               return;
-       }
-       
-       # Remove color controls
-       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
-
-       if ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to remove $service 
service from '$zone_name' zone on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
-               return;
-       }
-       elsif (grep(/NOT_ENABLED/, @$output)) {
-               notify($ERRORS{'OK'}, 0, "$service service has not been added 
to '$zone_name' zone on $computer_name");
-       }
-       else {
-               notify($ERRORS{'OK'}, 0, "removed $service service from 
'$zone_name' zone on $computer_name");
-       }
-       
-       return 1;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 create_zone
-
- Parameters  : $zone_name
- Returns     : boolean
- Description : Creates a new firewalld zone on the computer.
-
-=cut
-
-sub create_zone {
-       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 ($zone_name) = @_;
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       my $command = "firewall-cmd --permanent --new-zone=$zone_name";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
create '$zone_name' zone on $computer_name: $command");
-               return;
-       }
-       
-       # Remove color controls
-       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
-       
-       if (grep(/NAME_CONFLICT/, @$output)) {
-               # Error: NAME_CONFLICT: new_zone(): 'vcl-test'
-               notify($ERRORS{'OK'}, 0, "'$zone_name' zone already exists on 
$computer_name");
-               return 1;
-       }
-       elsif ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to create '$zone_name' 
zone on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
-               return;
-       }
-       else {
-               notify($ERRORS{'OK'}, 0, "created '$zone_name' zone on 
$computer_name");
-               return 1;
-       }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 delete_zone
-
- Parameters  : $zone_name
- Returns     : boolean
- Description : Deletes a firewalld zone from the computer.
-
-=cut
-
-sub delete_zone {
-       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 ($zone_name) = @_;
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       my $command = "firewall-cmd --permanent --delete-zone=$zone_name";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
delete '$zone_name' zone on $computer_name: $command");
-               return;
-       }
-       
-       # Remove color controls
-       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
-       
-       if (grep(/INVALID_ZONE/, @$output)) {
-               # Error: INVALID_ZONE: vcl-test
-               notify($ERRORS{'OK'}, 0, "'$zone_name' zone does not exist on 
$computer_name");
-               return 1;
-       }
-       elsif ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to delete '$zone_name' 
zone on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
-               return;
-       }
-       else {
-               notify($ERRORS{'OK'}, 0, "deleted '$zone_name' zone on 
$computer_name");
-               return 1;
-       }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 get_zone_info
-
- Parameters  : $zone_name
- Returns     : hash reference
- Description : Retrieves information about a firewalld zone from the computer
-               and constructs a hash reference:
-                  {
-                    "forward-ports" => "",
-                    "icmp-block-inversion" => "no",
-                    "icmp-blocks" => "",
-                    "interfaces" => "",
-                    "masquerade" => "no",
-                    "ports" => "",
-                    "protocols" => "",
-                    "rich rules" => "",
-                    "services" => "",
-                    "sourceports" => "",
-                    "sources" => "",
-                    "target" => "default"
-                  }
-
-=cut
-
-sub get_zone_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 ($zone_name) = @_;
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       my $command = "firewall-cmd --info-zone $zone_name";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
delete '$zone_name' zone on $computer_name: $command");
-               return;
-       }
-       
-       # Remove color controls
-       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
-       
-       if ($exit_status ne '0' || grep(/Error:/, @$output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to retrieve info for 
'$zone_name' zone from $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
-               return;
-       }
-       
-       # vcl-test
-       #   target: default
-       #   icmp-block-inversion: no
-       #   interfaces:
-       #   sources:
-       #   services:
-       #   ports:
-       #   protocols:
-       #   masquerade: no
-       #   forward-ports:
-       #   sourceports:
-       #   icmp-blocks:
-       #   rich rules:
-
-       my $zone_info = {};
-       for my $line (@$output) {
-               my ($property, $value) = $line =~ /\s*(\S[^:]+)\s*:\s*(.*)/g;
-               if (!defined($property)) {
-                       notify($ERRORS{'DEBUG'}, 0, "ignoring line: '$line'") 
if ($line !~ /^$zone_name/);
-                       next;
-               }
-               $zone_info->{$property} = $value;
-       }
-       
-       notify($ERRORS{'OK'}, 0, "retrieved info for '$zone_name' zone on 
$computer_name:\n" . format_data($zone_info));
-       return $zone_info;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 set_zone_target
-
- Parameters  : $zone_name, $target
- Returns     : boolean
- Description : Sets the target for a firewalld zone.
-
-=cut
-
-sub set_zone_target {
-       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 ($zone_name, $target) = @_;
-       if (!defined($zone_name)) {
-               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
-               return;
-       }
-       elsif (!defined($target)) {
-               notify($ERRORS{'WARNING'}, 0, "target argument was not 
supplied");
-               return;
-       }
-       elsif ($target !~ /^(ACCEPT|DROP|REJECT)$/i) {
-               notify($ERRORS{'WARNING'}, 0, "target argument is not valid: 
$target, it must be 'ACCEPT', 'DROP', or 'REJECT'");
-               return;
-       }
-       $target = uc($target);
-       
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       # --permanent [--zone=zone] --set-target=target
-       #           Set the target of a permanent zone.  target is one of: 
default, ACCEPT, DROP, REJECT
-       my $command = "firewall-cmd --permanent --zone=$zone_name 
--set-target=$target";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to set 
target of '$zone_name' zone to '$target' on $computer_name: $command");
-               return;
-       }
-       
-       # Remove color controls
-       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
-       
-       if ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to set target of 
'$zone_name' zone to '$target' on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
-               return;
-       }
-       else {
-               notify($ERRORS{'OK'}, 0, "set target of '$zone_name' zone to 
'$target' on $computer_name");
-               return 1;
-       }
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 add_source
-
- Parameters  : $zone_name, $source
- Returns     : boolean
- Description : 
-
-=cut
-
-sub add_source {
-       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 ($zone_name, $source) = @_;
-       if (!defined($zone_name)) {
-               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
-               return;
-       }
-       elsif (!defined($source)) {
-               notify($ERRORS{'WARNING'}, 0, "source argument was not 
supplied");
-               return;
-       }
-       
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       # [--permanent] [--zone=zone] --add-source=source[/mask]|MAC|ipset:ipset
-       
-       my $command = "firewall-cmd --permanent --zone=$zone_name 
--add-source=$source";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to add 
'$source' source to '$zone_name' zone on $computer_name: $command");
-               return;
-       }
-       
-       # Remove color controls
-       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
-       
-       if ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to add source to 
'$zone_name' zone on $computer_name: $source, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
-               return;
-       }
-       elsif (grep(/ALREADY_ENABLED/, @$output)) {
-               # Warning: ALREADY_ENABLED: 10.1.2.3
-               notify($ERRORS{'OK'}, 0, "source was previously added to 
'$zone_name' zone on $computer_name: $source");
-       }
-       else {
-               notify($ERRORS{'OK'}, 0, "added source to '$zone_name' zone on 
$computer_name: $source");
-       }
-       
-       return 1;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 remove_source
-
- Parameters  : $zone_name, $source
- Returns     : boolean
- Description : 
-
-=cut
-
-sub remove_source {
-       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 ($zone_name, $source) = @_;
-       if (!defined($zone_name)) {
-               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
-               return;
-       }
-       elsif (!defined($source)) {
-               notify($ERRORS{'WARNING'}, 0, "source argument was not 
supplied");
-               return;
-       }
-       
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       # [--permanent] --remove-source=source[/mask]|MAC|ipset:ipset
-       
-       my $command = "firewall-cmd --permanent --zone=$zone_name 
--remove-source=$source";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
remove '$source' source from '$zone_name' zone on $computer_name: $command");
-               return;
-       }
-       
-       # Remove color controls
-       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
-       
-       if ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to remove source from 
'$zone_name' zone on $computer_name: $source, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
-               return;
-       }
-       elsif (grep(/NOT_ENABLED/, @$output)) {
-               notify($ERRORS{'OK'}, 0, "source is not specified in 
'$zone_name' zone on $computer_name: $source");
-       }
-       else {
-               notify($ERRORS{'OK'}, 0, "removed source from '$zone_name' zone 
on $computer_name: $source");
-       }
-       
-       return 1;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 add_interface
-
- Parameters  : $zone_name, $interface_name
- Returns     : boolean
- Description : 
-
-=cut
-
-sub add_interface {
-       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 ($zone_name, $interface_name) = @_;
-       if (!defined($zone_name)) {
-               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
-               return;
-       }
-       elsif (!defined($interface_name)) {
-               notify($ERRORS{'WARNING'}, 0, "interface name argument was not 
supplied");
-               return;
-       }
-       
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       # [--permanent] [--zone=zone] --add-interface=interface
-       
-       my $command = "firewall-cmd --permanent --zone=$zone_name 
--add-interface=$interface_name";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to add 
'$interface_name' interface to '$zone_name' zone on $computer_name: $command");
-               return;
-       }
-       
-       # Remove color controls
-       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
-       if ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to add interface to 
'$zone_name' zone on $computer_name: $interface_name, exit status: 
$exit_status, command:\n$command\noutput:\n$output_string");
-               return;
-       }
-       elsif (grep(/already bound/, @$output)) {
-               # The interface is under control of NetworkManager and already 
bound to 'public'
-               notify($ERRORS{'OK'}, 0, "interface is already bound to 
'$zone_name' zone on $computer_name: $interface_name");
-       }
-       else {
-               notify($ERRORS{'OK'}, 0, "bound interface to '$zone_name' zone 
on $computer_name: $interface_name");
-       }
-       
-       return 1;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 add_port
-
- Parameters  : $zone_name, $port, $protocol (optional)
- Returns     : boolean
- Description : 
-
-=cut
-
-sub add_port {
-       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 ($zone_name, $port, $protocol) = @_;
-       if (!defined($zone_name)) {
-               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
-               return;
-       }
-       elsif (!defined($port)) {
-               notify($ERRORS{'WARNING'}, 0, "interface name argument was not 
supplied");
-               return;
-       }
-       $protocol = 'tcp' unless $protocol;
-       
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       # [--permanent] [--zone=zone] --add-port=portid[-portid]/protocol 
[--timeout=timeval]
-       
-       my $command = "firewall-cmd --permanent --zone=$zone_name 
--add-port=$port/$protocol";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to add 
port $port/$protocol to '$zone_name' zone on $computer_name: $command");
-               return;
-       }
-       
-       # Remove color controls
-       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
-       
-       if ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to add port 
$port/$protocol to '$zone_name' zone on $computer_name, exit status: 
$exit_status, command:\n$command\noutput:\n$output_string");
-               return;
-       }
-       elsif (grep(/ALREADY_ENABLED/, @$output)) {
-               notify($ERRORS{'OK'}, 0, "port $port/$protocol was previously 
added to '$zone_name' zone on $computer_name");
-       }
-       else {
-               notify($ERRORS{'OK'}, 0, "added port $port/$protocol to 
'$zone_name' zone on $computer_name");
-       }
-       
-       return 1;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 remove_port
-
- Parameters  : $zone_name, $port, $protocol (optional)
- Returns     : boolean
- Description : 
-
-=cut
-
-sub remove_port {
-       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 ($zone_name, $port, $protocol) = @_;
-       if (!defined($zone_name)) {
-               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
-               return;
-       }
-       elsif (!defined($port)) {
-               notify($ERRORS{'WARNING'}, 0, "interface name argument was not 
supplied");
-               return;
-       }
-       $protocol = 'tcp' unless $protocol;
-       
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       # [--permanent] [--zone=zone] --remove-port=portid[-portid]/protocol
-       my $command = "firewall-cmd --permanent --zone=$zone_name 
--remove-port=$port/$protocol";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
remove port $port/$protocol from '$zone_name' zone on $computer_name: 
$command");
-               return;
-       }
-       
-       # Remove color controls
-       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
-
-       if ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to remove port 
$port/$protocol from '$zone_name' zone on $computer_name, exit status: 
$exit_status, command:\n$command\noutput:\n$output_string");
-               return;
-       }
-       elsif (grep(/NOT_ENABLED/, @$output)) {
-               notify($ERRORS{'OK'}, 0, "port $port/$protocol has not been 
added from '$zone_name' zone on $computer_name");
-       }
-       else {
-               notify($ERRORS{'OK'}, 0, "removed port $port/$protocol from 
'$zone_name' zone on $computer_name");
-       }
-       
-       return 1;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-=head2 add_service
-
- Parameters  : $zone_name, $service
- Returns     : boolean
- Description : 
-
-=cut
-
-sub add_service {
-       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 ($zone_name, $service) = @_;
-       if (!defined($zone_name)) {
-               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
-               return;
-       }
-       elsif (!defined($service)) {
-               notify($ERRORS{'WARNING'}, 0, "interface name argument was not 
supplied");
-               return;
-       }
-       $service = 'tcp' unless $service;
-       
-       my $computer_name = $self->data->get_computer_hostname();
-       
-       # [--permanent] [--zone=zone] --add-service=service [--timeout=timeval]
-       my $command = "firewall-cmd --permanent --zone=$zone_name 
--add-service=$service";
-       my ($exit_status, $output) = $self->os->execute($command, 0);
-       if (!defined($output)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to execute command to add 
$service service to '$zone_name' zone on $computer_name: $command");
-               return;
-       }
-       
-       # Remove color controls
-       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
-       
-       if ($exit_status ne '0') {
-               notify($ERRORS{'WARNING'}, 0, "failed to add $service service 
to '$zone_name' zone on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
-               return;
-       }
-       elsif (grep(/ALREADY_ENABLED/, @$output)) {
-               notify($ERRORS{'OK'}, 0, "$service service was previously added 
to '$zone_name' zone on $computer_name");
-       }
-       else {
-               notify($ERRORS{'OK'}, 0, "added $service service to 
'$zone_name' zone on $computer_name");
-       }
-       
-       return 1;
-}
-
-#//////////////////////////////////////////////////////////////////////////////
-
-1;
-__END__
-
-=head1 SEE ALSO
-
-L<http://cwiki.apache.org/VCL/>
-
-=cut
+#!/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::firewalld.pm
+
+=head1 DESCRIPTION
+
+ This module provides VCL support for firewalld-based firewalls.
+
+=cut
+
+###############################################################################
+package VCL::Module::OS::Linux::firewall::firewalld;
+
+# Specify the lib path using FindBin
+use FindBin;
+use lib "$FindBin::Bin/../../../../..";
+
+# Configure inheritance
+use base qw(VCL::Module::OS::Linux::firewall::iptables);
+
+# Specify the version of this module
+our $VERSION = '2.4';
+
+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 || {};
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       notify($ERRORS{'DEBUG'}, 0, "initializing " . ref($self) . " object to 
control $computer_name");
+       
+       if (!$self->os->service_exists('firewalld')) {
+               notify($ERRORS{'DEBUG'}, 0, ref($self) . " object not 
initialized to control $computer_name, firewalld service does not exist");
+               return 0;
+       }
+       
+       if (!$self->os->is_service_enabled('firewalld')) {
+               notify($ERRORS{'DEBUG'}, 0, ref($self) . " object not 
initialized to control $computer_name, firewalld service is not enabled");
+               return 0;
+       }
+       
+       if (!$self->os->command_exists('firewall-cmd')) {
+               notify($ERRORS{'DEBUG'}, 0, ref($self) . " object not 
initialized to control $computer_name, firewall-cmd command does not exist");
+               return 0;
+       }
+       
+       notify($ERRORS{'DEBUG'}, 0, ref($self) . " object initialized to 
control $computer_name");
+       return 1;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 process_post_load
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Performs the initial iptables firewall configuration after an
+               image is loaded:
+               * Performs all of the tasks done by
+                 iptables.pm::process_post_load except the pre-VCL 2.5 legacy
+                 cleanup tasks
+               * Removes the ssh protocol from the public zone
+
+=cut
+
+sub process_post_load {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module::OS::Linux::firewall/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return 0;
+       }
+       
+       my $computer_name = $self->data->get_computer_short_name();
+       
+       notify($ERRORS{'DEBUG'}, 0, "beginning firewalld post-load 
configuration on $computer_name");
+       
+       # Call subroutine in iptables.pm
+       return unless $self->SUPER::process_post_load();
+       
+       # Remove ssh from public zone
+       return unless $self->remove_service('public', 'ssh');
+       
+       $self->save_configuration();
+       
+       notify($ERRORS{'DEBUG'}, 0, "completed firewalld post-load 
configuration on $computer_name");
+       return 1;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 get_all_direct_rules
+
+ Parameters  : none
+ Returns     : array
+ Description : Calls 'firewall-cmd --permanent --direct --get-all-rules' and
+               returns an array of strings.
+
+=cut
+
+sub get_all_direct_rules {
+       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 $computer_name = $self->data->get_computer_hostname();
+       
+       my $command = "firewall-cmd --permanent --direct --get-all-rules";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
retrieve all firewalld direct rules on $computer_name: $command");
+               return;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to retrieve all firewalld 
direct rules on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
+               return;
+       }
+       
+       # Rules should be in the format:
+       # ipv4 filter vcl-pre_capture 0 --jump ACCEPT --protocol tcp --match 
comment --comment 'VCL: Allow traffic to SSH port 22 from any IP address 
(2017-04-07 17:19:21)' --match tcp --destination-port 22
+       # ipv4 filter INPUT 0 --jump vcl-pre_capture --match comment --comment 
'VCL: jump to rules added during the pre-capture stage (2017-04-07 17:19:21)'
+       my @rules = grep(/^(ipv4|ipv6|eb)/, @$output);
+       
+       notify($ERRORS{'DEBUG'}, 0, "retrieved all firewalld direct rules 
defined on $computer_name:\n" . join("\n", @rules));
+       return @rules;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 get_direct_chain_rules
+
+ Parameters  : $table_name, $chain_name
+ Returns     : array
+ Description : Calls 'firewall-cmd --permanent --direct --get-rules' and 
returns
+               an array of strings.
+
+=cut
+
+sub get_direct_chain_rules {
+       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 (!$table_name) {
+               notify($ERRORS{'WARNING'}, 0, "table name argument was not 
specified");
+               return;
+       }
+       elsif (!$chain_name) {
+               notify($ERRORS{'WARNING'}, 0, "chain name argument was not 
specified");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my $command = "firewall-cmd --permanent --direct --get-rules ipv4 
$table_name $chain_name";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
retrieve firewalld direct rules defined for '$chain_name' chain in 
'$table_name' table on $computer_name: $command");
+               return;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to retrieve firewalld 
direct rules defined for '$chain_name' chain in '$table_name' table on 
$computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . 
join("\n", @$output));
+               return;
+       }
+       
+       # All rule lines should begin with an integer:
+       #    0 --jump ACCEPT --source 10.25.7.2 --match comment --comment 'VCL: 
Allow traffic from management node (2017-04-07 15:36:24)'
+       #    1 --jump ACCEPT --source 10.25.7.2
+       my @rules = grep(/^\d+/, @$output);
+       
+       notify($ERRORS{'DEBUG'}, 0, "retrieved firewalld direct rules defined 
for '$chain_name' chain in '$table_name' table on $computer_name:\n" . 
join("\n", @rules));
+       return @rules;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 save_configuration
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Calls 'firewall-cmd --reload'.
+
+=cut
+
+sub save_configuration {
+       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 $computer_name = $self->data->get_computer_hostname();
+       
+       my $command = "firewall-cmd --reload";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
reload firewalld configuration on $computer_name: $command");
+               return;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to reload firewalld 
configuration on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
+               return 0;
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "reloaded firewalld configuration on 
$computer_name");
+               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 = "firewall-cmd --permanent --direct --add-chain ipv4 
$table_name $chain_name";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command 
$computer_name: $command");
+               return;
+       }
+       elsif (grep(/ALREADY_ENABLED/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;
+       }
+       elsif (!grep(/success/, @$output)) {
+               notify($ERRORS{'WARNING'}, 0, "potentially failed to create 
'$chain_name' chain in '$table_name' table on $computer_name, output does not 
contain 'success', 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");
+               #$self->save_configuration();
+               return 1;
+       }
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 remove_direct_chain_rules
+
+ Parameters  : $table_name, $chain_name
+ Returns     : boolean
+ Description : Flushes (deletes) rules from the specified chain.
+
+=cut
+
+sub remove_direct_chain_rules {
+       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();
+       
+       # !!! WARNING !!!
+       # DON'T USE --remove-rules
+       # With firewall-cmd version 0.4.3.2, this option removes rules from ALL 
direct chains, not just the one specified
+       #my $command = "firewall-cmd --permanent --direct --remove-rules ipv4 
$table_name $chain_name";
+       
+       my @rules = $self->get_direct_chain_rules($table_name, $chain_name);
+       for my $rule (@rules) {
+               # [--permanent] --direct --remove-rule { ipv4 | ipv6 | eb } 
table chain priority args
+               my $command = "firewall-cmd --permanent --direct --remove-rule 
ipv4 $table_name $chain_name $rule";
+               my ($exit_status, $output) = $self->os->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 remove rule 
from '$chain_name' chain in '$table_name' table on $computer_name: '$rule', 
exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", 
@$output));
+                       return 0;
+               }
+               else {
+                       notify($ERRORS{'OK'}, 0, "removed direct rule from 
'$chain_name' chain in '$table_name' table on $computer_name: '$rule'");
+               }
+       }
+       
+       notify($ERRORS{'OK'}, 0, "removed all direct rules from '$chain_name' 
chain in '$table_name' table on $computer_name");
+       return 1;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_chain
+
+ Parameters  : $table_name, $chain_name
+ Returns     : boolean
+ Description : Deletes an existing chain. Returns true if the chain was
+               successfully deleted or doesn't exist.
+
+=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_argument) = @_;
+       if (!defined($table_name)) {
+               notify($ERRORS{'WARNING'}, 0, "table name argument was not 
specified");
+               return;
+       }
+       elsif (!defined($chain_name_argument)) {
+               notify($ERRORS{'WARNING'}, 0, "chain name argument was not 
specified");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my @chains_deleted;
+       my @chain_names = $self->get_table_chain_names($table_name);
+       for my $chain_name (@chain_names) {
+               if ($chain_name !~ /^$chain_name_argument$/) {
+                       next;
+               }
+               
+               # Delete all rules which reference the chain being deleted or 
else the chain can't be deleted
+               # Do this BEFORE checking if the chain exists to clean up 
leftover references in direct.xml
+               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;
+               }
+       
+               $self->remove_direct_chain_rules($table_name, $chain_name) || 
return;
+               
+               my $command = "firewall-cmd --permanent --direct --remove-chain 
ipv4 $table_name $chain_name";
+               my ($exit_status, $output) = $self->os->execute($command, 0);
+               if (!defined($output)) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to execute 
command $computer_name: $command");
+                       return;
+               }
+               elsif (grep(/NOT_ENABLED/i, @$output)) {
+                       notify($ERRORS{'OK'}, 0, "'$chain_name' chain in 
'$table_name' does not exist on $computer_name");
+               }
+               elsif ($exit_status ne '0') {
+                       notify($ERRORS{'WARNING'}, 0, "failed to delete 
'$chain_name' chain in '$table_name' table on $computer_name, exit status: 
$exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
+                       return 0;
+               }
+               elsif (!grep(/success/, @$output)) {
+                       notify($ERRORS{'WARNING'}, 0, "potentially failed to 
delete '$chain_name' chain in '$table_name' table on $computer_name, output 
does not contain 'success', exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
+               }
+               else {
+                       notify($ERRORS{'OK'}, 0, "deleted '$chain_name' chain 
in '$table_name' table on $computer_name");
+                       #$self->save_configuration();
+               }
+               
+               if (!$self->clean_direct_xml($table_name . '.*jump\s+' . 
$chain_name)) {
+                       return;
+               }
+               
+               notify($ERRORS{'OK'}, 0, "deleted '$chain_name' chain from 
'$table_name' table on $computer_name");
+               push @chains_deleted, $chain_name;
+       }
+       
+       if (!@chains_deleted) {
+               notify($ERRORS{'DEBUG'}, 0, "no chains exist in '$table_name' 
table on $computer_name matching argument: '$chain_name_argument'");
+       }
+       return 1;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 clean_direct_xml
+
+ Parameters  : $regex_pattern
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub clean_direct_xml {
+       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 $regex_pattern = shift;
+       if (!defined($regex_pattern)) {
+               notify($ERRORS{'WARNING'}, 0, "regex pattern argument was not 
supplied");
+               return;
+       }
+       
+       $self->os->firewall->save_configuration();
+       
+       my @keep_lines;
+       my @prune_lines;
+       my $file_path = '/etc/firewalld/direct.xml';
+       my @lines = $self->os->get_file_contents($file_path);
+       for my $line (@lines) {
+               if ($line =~ /$regex_pattern/i) {
+                       push @prune_lines, $line;
+               }
+               else {
+                       push @keep_lines, $line;
+               }
+       }
+       
+       if (@prune_lines) {
+               my $updated_contents = join("\n", @keep_lines);
+               notify($ERRORS{'DEBUG'}, 0, "pruning the following lines from 
$file_path matching pattern: '$regex_pattern'\n" . join("\n", @prune_lines) . 
"\nnew file contents:\n$updated_contents");
+               return $self->os->create_text_file($file_path, 
$updated_contents);
+       }
+       else {
+               notify($ERRORS{'DEBUG'}, 0, "no lines were pruned from 
$file_path matching pattern: '$regex_pattern'");
+               return 1;
+       }
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 _insert_rule
+
+ Parameters  : $table_name, $chain_name, $argument_string
+ Returns     : boolean
+ Description : Executes the command to insert a firewalld direct rule. This is 
a
+               helper subroutine and should only be called by
+               iptable.pm::insert_rule.
+
+=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 ($table_name, $chain_name, $argument_string) = @_;
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my $command = "firewall-cmd --permanent --direct --add-rule ipv4 
$table_name $chain_name 0 $argument_string";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to add 
direct firewalld rule to $chain_name chain in $table_name table on 
$computer_name: $command");
+               return;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to add direct firewalld 
rule to $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, "added direct firewalld rule to 
$chain_name chain in $table_name table on $computer_name, command: $command, 
output:\n" . join("\n", @$output));
+               #$self->save_configuration();
+               return 1;
+       }
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 _delete_rule
+
+ Parameters  : $table_name, $chain_name, $rule_specification_string
+ Returns     : boolean
+ Description : Deletes a firewalld direct rule. This should only used as a
+               helper subroutine.
+
+=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 ($table_name, $chain_name, $rule_specification_string) = @_;
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my $command = "firewall-cmd --permanent --direct --remove-rule ipv4 
$table_name $chain_name 0 $rule_specification_string";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
delete firewalld direct rule on $computer_name: $command");
+               return;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to delete firewalld 
direct rule on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
+               return;
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "deleted firewalld direct rule on 
$computer_name, command: '$command', output:\n" . join("\n", @$output));
+               #$self->save_configuration();
+               return 1;
+       }
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 remove_service
+
+ Parameters  : $zone_name, $service
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub remove_service {
+       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 ($zone_name, $service) = @_;
+       if (!defined($zone_name)) {
+               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
+               return;
+       }
+       elsif (!defined($service)) {
+               notify($ERRORS{'WARNING'}, 0, "interface name argument was not 
supplied");
+               return;
+       }
+       $service = 'tcp' unless $service;
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       # [--permanent] [--zone=zone] 
--remove-service=serviceid[-serviceid]/service
+       #            Remove the service from zone. If zone is omitted, default 
zone will be used. This option can be specified
+       #            multiple times.
+       
+       my $command = "firewall-cmd --permanent --zone=$zone_name 
--remove-service=$service";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
remove $service service from '$zone_name' zone on $computer_name: $command");
+               return;
+       }
+       
+       # Remove color controls
+       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
+
+       if ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to remove $service 
service from '$zone_name' zone on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
+               return;
+       }
+       elsif (grep(/NOT_ENABLED/, @$output)) {
+               notify($ERRORS{'OK'}, 0, "$service service has not been added 
to '$zone_name' zone on $computer_name");
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "removed $service service from 
'$zone_name' zone on $computer_name");
+       }
+       
+       return 1;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 create_zone
+
+ Parameters  : $zone_name
+ Returns     : boolean
+ Description : Creates a new firewalld zone on the computer.
+
+=cut
+
+sub create_zone {
+       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 ($zone_name) = @_;
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my $command = "firewall-cmd --permanent --new-zone=$zone_name";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
create '$zone_name' zone on $computer_name: $command");
+               return;
+       }
+       
+       # Remove color controls
+       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
+       
+       if (grep(/NAME_CONFLICT/, @$output)) {
+               # Error: NAME_CONFLICT: new_zone(): 'vcl-test'
+               notify($ERRORS{'OK'}, 0, "'$zone_name' zone already exists on 
$computer_name");
+               return 1;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to create '$zone_name' 
zone on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
+               return;
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "created '$zone_name' zone on 
$computer_name");
+               return 1;
+       }
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_zone
+
+ Parameters  : $zone_name
+ Returns     : boolean
+ Description : Deletes a firewalld zone from the computer.
+
+=cut
+
+sub delete_zone {
+       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 ($zone_name) = @_;
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my $command = "firewall-cmd --permanent --delete-zone=$zone_name";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
delete '$zone_name' zone on $computer_name: $command");
+               return;
+       }
+       
+       # Remove color controls
+       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
+       
+       if (grep(/INVALID_ZONE/, @$output)) {
+               # Error: INVALID_ZONE: vcl-test
+               notify($ERRORS{'OK'}, 0, "'$zone_name' zone does not exist on 
$computer_name");
+               return 1;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to delete '$zone_name' 
zone on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
+               return;
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "deleted '$zone_name' zone on 
$computer_name");
+               return 1;
+       }
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 get_zone_info
+
+ Parameters  : $zone_name
+ Returns     : hash reference
+ Description : Retrieves information about a firewalld zone from the computer
+               and constructs a hash reference:
+                  {
+                    "forward-ports" => "",
+                    "icmp-block-inversion" => "no",
+                    "icmp-blocks" => "",
+                    "interfaces" => "",
+                    "masquerade" => "no",
+                    "ports" => "",
+                    "protocols" => "",
+                    "rich rules" => "",
+                    "services" => "",
+                    "sourceports" => "",
+                    "sources" => "",
+                    "target" => "default"
+                  }
+
+=cut
+
+sub get_zone_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 ($zone_name) = @_;
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       my $command = "firewall-cmd --info-zone $zone_name";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
delete '$zone_name' zone on $computer_name: $command");
+               return;
+       }
+       
+       # Remove color controls
+       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
+       
+       if ($exit_status ne '0' || grep(/Error:/, @$output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to retrieve info for 
'$zone_name' zone from $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
+               return;
+       }
+       
+       # vcl-test
+       #   target: default
+       #   icmp-block-inversion: no
+       #   interfaces:
+       #   sources:
+       #   services:
+       #   ports:
+       #   protocols:
+       #   masquerade: no
+       #   forward-ports:
+       #   sourceports:
+       #   icmp-blocks:
+       #   rich rules:
+
+       my $zone_info = {};
+       for my $line (@$output) {
+               my ($property, $value) = $line =~ /\s*(\S[^:]+)\s*:\s*(.*)/g;
+               if (!defined($property)) {
+                       notify($ERRORS{'DEBUG'}, 0, "ignoring line: '$line'") 
if ($line !~ /^$zone_name/);
+                       next;
+               }
+               $zone_info->{$property} = $value;
+       }
+       
+       notify($ERRORS{'OK'}, 0, "retrieved info for '$zone_name' zone on 
$computer_name:\n" . format_data($zone_info));
+       return $zone_info;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 set_zone_target
+
+ Parameters  : $zone_name, $target
+ Returns     : boolean
+ Description : Sets the target for a firewalld zone.
+
+=cut
+
+sub set_zone_target {
+       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 ($zone_name, $target) = @_;
+       if (!defined($zone_name)) {
+               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
+               return;
+       }
+       elsif (!defined($target)) {
+               notify($ERRORS{'WARNING'}, 0, "target argument was not 
supplied");
+               return;
+       }
+       elsif ($target !~ /^(ACCEPT|DROP|REJECT)$/i) {
+               notify($ERRORS{'WARNING'}, 0, "target argument is not valid: 
$target, it must be 'ACCEPT', 'DROP', or 'REJECT'");
+               return;
+       }
+       $target = uc($target);
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       # --permanent [--zone=zone] --set-target=target
+       #           Set the target of a permanent zone.  target is one of: 
default, ACCEPT, DROP, REJECT
+       my $command = "firewall-cmd --permanent --zone=$zone_name 
--set-target=$target";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to set 
target of '$zone_name' zone to '$target' on $computer_name: $command");
+               return;
+       }
+       
+       # Remove color controls
+       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
+       
+       if ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to set target of 
'$zone_name' zone to '$target' on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
+               return;
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "set target of '$zone_name' zone to 
'$target' on $computer_name");
+               return 1;
+       }
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 add_source
+
+ Parameters  : $zone_name, $source
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub add_source {
+       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 ($zone_name, $source) = @_;
+       if (!defined($zone_name)) {
+               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
+               return;
+       }
+       elsif (!defined($source)) {
+               notify($ERRORS{'WARNING'}, 0, "source argument was not 
supplied");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       # [--permanent] [--zone=zone] --add-source=source[/mask]|MAC|ipset:ipset
+       
+       my $command = "firewall-cmd --permanent --zone=$zone_name 
--add-source=$source";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to add 
'$source' source to '$zone_name' zone on $computer_name: $command");
+               return;
+       }
+       
+       # Remove color controls
+       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
+       
+       if ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to add source to 
'$zone_name' zone on $computer_name: $source, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
+               return;
+       }
+       elsif (grep(/ALREADY_ENABLED/, @$output)) {
+               # Warning: ALREADY_ENABLED: 10.1.2.3
+               notify($ERRORS{'OK'}, 0, "source was previously added to 
'$zone_name' zone on $computer_name: $source");
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "added source to '$zone_name' zone on 
$computer_name: $source");
+       }
+       
+       return 1;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 remove_source
+
+ Parameters  : $zone_name, $source
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub remove_source {
+       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 ($zone_name, $source) = @_;
+       if (!defined($zone_name)) {
+               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
+               return;
+       }
+       elsif (!defined($source)) {
+               notify($ERRORS{'WARNING'}, 0, "source argument was not 
supplied");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       # [--permanent] --remove-source=source[/mask]|MAC|ipset:ipset
+       
+       my $command = "firewall-cmd --permanent --zone=$zone_name 
--remove-source=$source";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
remove '$source' source from '$zone_name' zone on $computer_name: $command");
+               return;
+       }
+       
+       # Remove color controls
+       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
+       
+       if ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to remove source from 
'$zone_name' zone on $computer_name: $source, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
+               return;
+       }
+       elsif (grep(/NOT_ENABLED/, @$output)) {
+               notify($ERRORS{'OK'}, 0, "source is not specified in 
'$zone_name' zone on $computer_name: $source");
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "removed source from '$zone_name' zone 
on $computer_name: $source");
+       }
+       
+       return 1;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 add_interface
+
+ Parameters  : $zone_name, $interface_name
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub add_interface {
+       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 ($zone_name, $interface_name) = @_;
+       if (!defined($zone_name)) {
+               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
+               return;
+       }
+       elsif (!defined($interface_name)) {
+               notify($ERRORS{'WARNING'}, 0, "interface name argument was not 
supplied");
+               return;
+       }
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       # [--permanent] [--zone=zone] --add-interface=interface
+       
+       my $command = "firewall-cmd --permanent --zone=$zone_name 
--add-interface=$interface_name";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to add 
'$interface_name' interface to '$zone_name' zone on $computer_name: $command");
+               return;
+       }
+       
+       # Remove color controls
+       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
+       if ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to add interface to 
'$zone_name' zone on $computer_name: $interface_name, exit status: 
$exit_status, command:\n$command\noutput:\n$output_string");
+               return;
+       }
+       elsif (grep(/already bound/, @$output)) {
+               # The interface is under control of NetworkManager and already 
bound to 'public'
+               notify($ERRORS{'OK'}, 0, "interface is already bound to 
'$zone_name' zone on $computer_name: $interface_name");
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "bound interface to '$zone_name' zone 
on $computer_name: $interface_name");
+       }
+       
+       return 1;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 add_port
+
+ Parameters  : $zone_name, $port, $protocol (optional)
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub add_port {
+       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 ($zone_name, $port, $protocol) = @_;
+       if (!defined($zone_name)) {
+               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
+               return;
+       }
+       elsif (!defined($port)) {
+               notify($ERRORS{'WARNING'}, 0, "interface name argument was not 
supplied");
+               return;
+       }
+       $protocol = 'tcp' unless $protocol;
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       # [--permanent] [--zone=zone] --add-port=portid[-portid]/protocol 
[--timeout=timeval]
+       
+       my $command = "firewall-cmd --permanent --zone=$zone_name 
--add-port=$port/$protocol";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to add 
port $port/$protocol to '$zone_name' zone on $computer_name: $command");
+               return;
+       }
+       
+       # Remove color controls
+       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
+       
+       if ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to add port 
$port/$protocol to '$zone_name' zone on $computer_name, exit status: 
$exit_status, command:\n$command\noutput:\n$output_string");
+               return;
+       }
+       elsif (grep(/ALREADY_ENABLED/, @$output)) {
+               notify($ERRORS{'OK'}, 0, "port $port/$protocol was previously 
added to '$zone_name' zone on $computer_name");
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "added port $port/$protocol to 
'$zone_name' zone on $computer_name");
+       }
+       
+       return 1;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 remove_port
+
+ Parameters  : $zone_name, $port, $protocol (optional)
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub remove_port {
+       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 ($zone_name, $port, $protocol) = @_;
+       if (!defined($zone_name)) {
+               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
+               return;
+       }
+       elsif (!defined($port)) {
+               notify($ERRORS{'WARNING'}, 0, "interface name argument was not 
supplied");
+               return;
+       }
+       $protocol = 'tcp' unless $protocol;
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       # [--permanent] [--zone=zone] --remove-port=portid[-portid]/protocol
+       my $command = "firewall-cmd --permanent --zone=$zone_name 
--remove-port=$port/$protocol";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to 
remove port $port/$protocol from '$zone_name' zone on $computer_name: 
$command");
+               return;
+       }
+       
+       # Remove color controls
+       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
+
+       if ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to remove port 
$port/$protocol from '$zone_name' zone on $computer_name, exit status: 
$exit_status, command:\n$command\noutput:\n$output_string");
+               return;
+       }
+       elsif (grep(/NOT_ENABLED/, @$output)) {
+               notify($ERRORS{'OK'}, 0, "port $port/$protocol has not been 
added from '$zone_name' zone on $computer_name");
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "removed port $port/$protocol from 
'$zone_name' zone on $computer_name");
+       }
+       
+       return 1;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 add_service
+
+ Parameters  : $zone_name, $service
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub add_service {
+       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 ($zone_name, $service) = @_;
+       if (!defined($zone_name)) {
+               notify($ERRORS{'WARNING'}, 0, "zone name argument was not 
supplied");
+               return;
+       }
+       elsif (!defined($service)) {
+               notify($ERRORS{'WARNING'}, 0, "interface name argument was not 
supplied");
+               return;
+       }
+       $service = 'tcp' unless $service;
+       
+       my $computer_name = $self->data->get_computer_hostname();
+       
+       # [--permanent] [--zone=zone] --add-service=service [--timeout=timeval]
+       my $command = "firewall-cmd --permanent --zone=$zone_name 
--add-service=$service";
+       my ($exit_status, $output) = $self->os->execute($command, 0);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command to add 
$service service to '$zone_name' zone on $computer_name: $command");
+               return;
+       }
+       
+       # Remove color controls
+       (my $output_string = join("\n", @$output)) =~ s/\e\[\d+m//g;
+       
+       if ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to add $service service 
to '$zone_name' zone on $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n$output_string");
+               return;
+       }
+       elsif (grep(/ALREADY_ENABLED/, @$output)) {
+               notify($ERRORS{'OK'}, 0, "$service service was previously added 
to '$zone_name' zone on $computer_name");
+       }
+       else {
+               notify($ERRORS{'OK'}, 0, "added $service service to 
'$zone_name' zone on $computer_name");
+       }
+       
+       return 1;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+1;
+__END__
+
+=head1 SEE ALSO
+
+L<http://cwiki.apache.org/VCL/>
+
+=cut

Propchange: 
vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/OSX.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/OSX.pm?rev=1798487&r1=1798486&r2=1798487&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/OSX.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/OSX.pm Mon Jun 12 16:33:35 2017
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -w
 ###############################################################################
-# $Id: $
+# $Id$
 ###############################################################################
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with

Propchange: vcl/trunk/managementnode/lib/VCL/Module/OS/OSX.pm
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: vcl/trunk/managementnode/lib/VCL/Module/OS/OSX.pm
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Propchange: vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/
            ('svn:mergeinfo' removed)

Propchange: vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_5/2003.pm
            ('svn:mergeinfo' removed)

Propchange: vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_5/XP.pm
            ('svn:mergeinfo' removed)


Reply via email to