# # Copyright (c) 2001-2011 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary Antivirus Task Module ## @author swati.bajaj@netapp.com, dl-nacl-dev@netapp.com ## @status shared ## @pod here package NACL::MTask::Antivirus; use strict; use warnings; use base qw(NACL::MTask::MTask); use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use Params::Validate qw(validate SCALAR HASHREF); use NACL::STask::AntivirusEngine; use NACL::STask::AntivirusOnDemand; use NACL::STask::AntivirusOnDemandCommand; use NACL::STask::AntivirusOnDemandCommandScanCluster; use NACL::STask::AntivirusOnDemandCommandScanVserver; use NACL::STask::AntivirusOnDemandCommandScanFile; use NACL::STask::AntivirusOnDemandCommandScanDir; use NACL::STask::AntivirusOnAccess; use NACL::STask::AntivirusOnAccessPolicy; use NACL::C::AntivirusRemedy; use NACL::C::Cluster; use NATE::Exceptions::Argument qw(:try); use NACL::Exceptions::VerifyFailure (); use NACL::ComponentUtils qw(Dumper); use Class::MethodMaker [ scalar => [ { -type => 'NACL::C::Job' }, 'job_component' ], ]; =head1 NAME NACL::MTask::Antivirus =head1 DESCRIPTION C provides a number of well-defined but potentially complex or multi-step methods related to Antivirus in ONTAP. This task is applicable only for CMode. =head1 ATTRIBUTES =head2 command_interface (Required) A component object that represents the host to which to send commands. =head2 job_component (Optional) A job component (NACL::C::Job) representing the job used to enable the antivirus engine. =head1 METHODS =head2 setup my $av_obj = NACL::MTask::Antivirus->setup( command_interface => $command_interface, vendor => $vendor_name, nacltask_av_engine_modify => \%av_engine_modify_opts, nacltask_verify => 0, #defaults to 0 nacltask_wait => 1, #defaults to 1 nacltask_enable_all_nodes => 1, #defaults to 1 %other_options ); (Class method) This method will mainly be concerned with setting up of antivirus engine (sophos, mcafee, trend). This Task is applicable only for CMode. The setup method takes care of handling the following based on the user provided options for each - 1) Modifies the antivirus engine with the vendor configuration specified. If there are two or more nodes in a cluster, on which this method is executed, then the engine will be enabled on all the nodes of the cluster based on 'nacltask_enable_all_nodes' option. 2) Verifies the configuration if nacltask_verify is set to 1. 3) Enables the engine with the configured vendor. 4) Waits for the antivirus engine to be enabled if nacltask_wait is set to 1. This also includes verification that the engine is enabled. =over =item Options =over =item C<< nacltask_av_engine_modify => \%av_engine_modify_opts >> (Optional, HashRef) Antivirus engine modify options. It takes all the options as described in L<< NACL::C::AntivirusEngine->modify|lib-NACL-C-AntivirusEngine-pm/modify >> If not provided, then the engine is not modified. =item C<< nacltask_wait => 0|1 >> (Optional, defaults to 1) If 1, wait for the engine to be enabled. If 0, do not wait any longer than necessary. =item C<< nacltask_verify => 0|1 >> (Optional, defaults to 0) When set to 1, it verifies that the engine has been configured with all of the attributes specified. Also it verifies whether the antivirus engine has been enabled. When set to 0, verification is not performed. =item C<< nacltask_enable_all_nodes => 0|1 >> (Optional, defaults to 1) If 1, all the nodes in the cluster are enabled. If 0, based on the number of licenses specified, antivirus is enabled. =back All of the other various options are described in L<< NACL::C::AntivirusEngine->enable|lib-NACL-C-AntivirusEngine-pm/enable >> =back =cut sub setup { $Log->enter() if $may_enter; my $pkg = shift; my %opts = NACL::C::Component->_common_validate_with( params => \@_, additional_spec => { nacltask_verify => { type => SCALAR, default => 0 }, nacltask_wait => { type => SCALAR, default => 1 }, nacltask_enable_all_nodes => { type => SCALAR, default => 1 }, nacltask_av_engine_modify => { type => HASHREF, optional => 1 }, }, allow_extra => 1, ); my $command_interface = $opts{command_interface}; # Throw an exception if invoked on a 7Mode node. if ( $command_interface->mode() =~ /7Mode/ ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "7-Mode support for Antivirus setup is not valid"); } my $verify = delete $opts{nacltask_verify}; my $wait = delete $opts{nacltask_wait}; my $all_nodes = delete $opts{nacltask_enable_all_nodes}; # The task defaults to enabling AV on all nodes in the cluster # For this we need to set num-license equal to the # number of nodes in the cluster if ($all_nodes) { my @nodes = NACL::C::Cluster->find( command_interface => $command_interface, ); # Override the number of license to enable # antivirus on all the nodes $opts{'nacltask_av_engine_modify'}->{'num-license'} = $#nodes + 1; } # Need to disable engine before modifying NACL::STask::AntivirusEngine->disable( nacltask_if_disabled => "pass", 'command_interface' => $command_interface ); my %av_options; # Modifying the engine with the vendor specific configuration if ( defined $opts{'nacltask_av_engine_modify'} ) { my $av_options = delete $opts{nacltask_av_engine_modify}; %av_options = %$av_options; NACL::STask::AntivirusEngine->modify( 'command_interface' => $command_interface, 'nacltask_verify' => $verify, %av_options ); } $opts{'vendor'} ||= $av_options{'vendor'}; # Now enable the engine NACL::STask::AntivirusEngine->enable( 'nacltask_wait' => $wait, %opts, ); $Log->exit() if $may_exit; return; } =head2 avod_schedule NACL::MTask::Antivirus->avod_schedule( command_interface => $command_interface, nacltask_on_demand_command => \%command_opts, nacltask_remedy => \%remedy_opts, nacltask_if_exists => $action, #defaults to 'die' nacltask_verify => 0, #defaults to 0 %other_options ); (Class method) This method will mainly be concerned with scheduling the on-demand scan. This Task method is applicable only for CMode. The avod_schedule method takes care of handling the following based on the user provided options for each - 1) Create the on-demand command if nacltask_on_demand_command is provided. If the command to be created already exists, then the action to be performed(die, reuse or purge) is decided by the 'nacltask_if_exists' option. 2) Schedule an avod command. 3) Verify the on-demand scan schedule if nacltask_verify is set to 1. 4) Modify the remedy configuration if nacltask_remedy is provided. =over =item Options =over =item C<< nacltask_if_exists => $action >> (Optional, defaults to 'die') When set to 'die' an exception is thrown if the on-demand command already exists. When set to 'reuse' the on-demand command which is already created is reused. When set to 'purge', the on-demand command is purged, and a new command is created. =item C<< nacltask_verify => 0|1 >> (Optional, defaults to 0) When set to 1, the on-demand schedule is verified. When set to 0, verification is not performed. =item C<< nacltask_remedy => \%remedy_opts >> (Optional, HashRef) Antivirus remedy options. It takes all the options as described in L<< NACL::C::AntivirusRemedy->modify|lib-NACL-C-AntivirusRemedy-pm/modify >> If not provided remedy action is not modified. =item C<< nacltask_on_demand_command => \%command_opts >> (Optional, HashRef) Antivirus on-demand command create options. It takes all the options as described in L<< NACL::C::AntivirusOnDemandCommand->create|lib-NACL-C-AntivirusOnDemandCommand-pm/create >> If not provided, then the command is not created. Note that from SN.0 the antivirus commands were vserverized. In this process, commands from the "antivirus on-demand command" directory were moved into separate sub-directories based on the type of scanning that needs to be performed. The "scan-type" field can be provided to specify which sub-directory's command to invoke. For example: nacltask_on_demand_command => { 'scan-type' => 'cluster', files => "^.*\.doc$" } will result in NACL::STask::AntivirusOnDemandCommandScanCluster->create(files => "^.*\.doc$") being invoked. In short, the "scan-type" will control which STask is invoked and all the other arguments will be sent to the create method. Note that if the C argument within this hash-ref is not provided, it is defaulted to the C provided for the schedule command. Also, if the C argument is not provided within this hash-ref, it is defaulted to the C argument provided for the schedule command. (Checking for whether the antivirus commands are vserverized in your build can be done like this: $command_interface->has_uichange(uichange => 'antivirus-vserverization') ) =back All of the other various options are described in L<< NACL::C::AntivirusOnDemand->schedule|lib-NACL-C-AntivirusOnDemand-pm/schedule >> =back =cut sub avod_schedule { $Log->enter() if $may_enter; my $pkg = shift; my %opts = NACL::C::Component->_common_validate_with( params => \@_, additional_spec => { nacltask_verify => { type => SCALAR, default => 0 }, nacltask_on_demand_command => { type => HASHREF, optional => 1 }, nacltask_remedy => { type => HASHREF, optional => 1 }, nacltask_if_exists => NACL::STask::STask->_if_exists_validate_spec(), }, allow_extra => 1, ); my $command_interface = $opts{command_interface}; # Throw an exception if invoked on a 7Mode node. if ( $command_interface->is_7mode() ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "7-Mode support for AV On-Demand schedule is not valid"); } my %orig_opts = %opts; my $verify = delete $opts{nacltask_verify}; my $if_exists = delete $opts{nacltask_if_exists}; my $od_command_options = delete $opts{nacltask_on_demand_command}; my $remedy_options = delete $opts{nacltask_remedy}; # Create the on-demand command if 'nacltask_on_demand_command' is provided $pkg->_create_od_command(%orig_opts) if (defined $orig_opts{'nacltask_on_demand_command'}); # Run the AV on-demand schedule to schedule the avod command NACL::STask::AntivirusOnDemand->schedule( nacltask_verify => $verify, nacltask_if_scheduled => 'pass', %opts ); # Modify the remedy action if 'nacltask_remedy' is provided if ( defined $orig_opts{'nacltask_remedy'} ) { my %remedy_options = %$remedy_options; NACL::C::AntivirusRemedy->modify( 'command_interface' => $opts{command_interface}, %remedy_options ); } $Log->exit() if $may_exit; } =head2 avod_run NACL::MTask::Antivirus->avod_run( command_interface => $command_interface, nacltask_on_demand_command => \%command_opts, nacltask_remedy => \%remedy_opts, nacltask_if_exists => $action, #defaults to die nacltask_verify => 0, #defaults to 0 %other_options ); (Class method) This method will mainly be concerned with running the on-demand scan. This Task method is applicable only for CMode. The avod_run method takes care of handling the following based on the user provided options for each - 1) Create the on-demand command if nacltask_on_demand_command is provided. If the command to be created already exists, then the action to be performed(die, reuse, or purge) is decided by the 'nacltask_if_exists' option. 2) Run an avod command. 3) Verify using 'antivirus on-demand report show' to check if the run has started, if nacltask_verify is set to 1. Before checking this output the old reports are deleted, so that the report show just gives report of only the latest run. 4) Modify the remedy configuration if nacltask_remedy is provided. =over =item Options =over =item C<< nacltask_if_exists => $action >> (Optional, defaults to 'die') When set to 'die' an exception is thrown if the on-demand command already exists. When set to 'reuse' the on-demand command which is already created is reused. When set to 'purge', the on-demand command is purged, and a new command is created. =item C<< nacltask_verify => 0|1 >> (Optional, defaults to 0) When set to 1, the on-demand run is verified. When set to 0, verification is not performed. =item C<< nacltask_remedy => \%remedy_opts >> (Optional, HashRef) Antivirus remedy options. It takes all the options as described in L<< NACL::C::AntivirusRemedy->modify|lib-NACL-C-AntivirusRemedy-pm/modify >> If not provided remedy action is not modified. =item C<< nacltask_on_demand_command => \%command_opts >> (Optional, HashRef) Antivirus on-demand command create options. It takes all the options as described in L<< NACL::C::AntivirusOnDemandCommand->create|lib-NACL-C-AntivirusOnDemandCommand-pm/create >> If not provided, then the command is not created. =back All of the other various options are described in L<< NACL::C::AntivirusOnDemand->run|lib-NACL-C-AntivirusOnDemand-pm/run >> =back =cut sub avod_run { $Log->enter() if $may_enter; my $pkg = shift; my %opts = NACL::C::Component->_common_validate_with( params => \@_, additional_spec => { nacltask_verify => { type => SCALAR, default => 0 }, nacltask_on_demand_command => { type => HASHREF, optional => 1 }, nacltask_remedy => { type => HASHREF, optional => 1 }, nacltask_if_exists => NACL::STask::STask->_if_exists_validate_spec(), }, allow_extra => 1, ); my $command_interface = $opts{command_interface}; # Throw an exception if invoked on a 7Mode node. if ( $command_interface->mode() =~ /7Mode/ ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "7-Mode support for AV On-Demand run is not valid"); } my %orig_opts = %opts; my $verify = delete $opts{nacltask_verify}; my $if_exists = delete $opts{nacltask_if_exists}; my $od_command_options = delete $opts{nacltask_on_demand_command}; my $remedy_options = delete $opts{nacltask_remedy}; # Create the on-demand command if 'nacltask_on_demand_command' is provided $pkg->_create_od_command(%orig_opts) if (defined $orig_opts{'nacltask_on_demand_command'}); # Run the on-demand command NACL::STask::AntivirusOnDemand->run( 'nacltask_verify' => $verify, %opts ); # Modify the remedy action if 'nacltask_remedy' is provided if ( defined $orig_opts{'nacltask_remedy'} ) { my %remedy_options = %$remedy_options; NACL::C::AntivirusRemedy->modify( command_interface => $opts{command_interface}, %remedy_options ); } $Log->exit() if $may_exit; } =head2 avoa_setup NACL::MTask::Antivirus->avoa_setup( command_interface => $command_interface, nacltask_on_access_policy => \%command_opts, nacltask_verify => 0, #defaults to 0 %other_options ); (Class method) This method will mainly be concerned with setting up of antivirus on-access policy. This Task method is applicable only for CMode. This method takes care of handling the following based on the user provided options for each - 1) Create an on-access policy. 2) Verify the created on-access policy if nacltask_verify is set to 1. 3) Modify the on-access policy to a vserver or to a volume. 4) Verify the on-access policy applied to an vserver or to a volume if nacltask_verify is set to 1. =over =item Options =over =item C<< nacltask_verify => 0|1 >> (Optional, defaults to 0) When set to 1, it verifies that the on-access policy has been set correctly. When set to 0, verification is not performed. =item C<< nacltask_on_access_policy => \%command_opts >> (Optional, HashRef) Antivirus on-access policy create options. It takes all the options as described in L<< NACL::C::AntivirusOnAccessPolicy->create|lib-NACL-C-AntivirusOnAccessPolicy-pm/create >> If not provided, then the policy is not created. =back All of the other various options are described in L<< NACL::C::AntivirusOnAccess->modify|lib-NACL-C-AntivirusOnAccess-pm/modify >> L<< NACL::C::AntivirusOnAccessPolicy->create|lib-NACL-C-AntivirusOnAccessPolicy-pm/create >> =back =cut sub avoa_setup { $Log->enter() if $may_enter; my $pkg = shift; my %opts = NACL::C::Component->_common_validate_with( params => \@_, additional_spec => { nacltask_verify => { type => SCALAR, default => 0 }, nacltask_on_access_policy => { type => HASHREF, optional => 1 }, nacltask_if_exists => NACL::STask::STask->_if_exists_validate_spec(), }, allow_extra => 1, ); my $command_interface = $opts{command_interface}; # Throw an exception if invoked on a 7Mode node. if ( $command_interface->mode() =~ /7Mode/ ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "7-Mode support for AV On-Access setup is not valid"); } my $oa_policy_options = delete $opts{nacltask_on_access_policy}; my $verify = delete $opts{nacltask_verify}; my $if_exists = delete $opts{nacltask_if_exists}; # Create the AV on-access policy if ( defined $oa_policy_options ) { my %component_call_opts = %$oa_policy_options; my $avoa_policy = NACL::STask::AntivirusOnAccessPolicy->create( %component_call_opts, command_interface => $command_interface, nacltask_verify => $verify, nacltask_if_exists => $if_exists, ); } # If vserver-policy and/or volume-policy are not provided, we default # it to be the policy which was created. $opts{'vserver-policy'} ||= $oa_policy_options->{name}; $opts{'volume-policy'} ||= $oa_policy_options->{name}; # Modify the on-access policy to a vserver or/and to a volume. NACL::STask::AntivirusOnAccess->modify( 'nacltask_verify' => $verify, %opts ); $Log->exit() if $may_exit; } # Helper routine to create on-demand command sub _create_od_command { my ($self, %opts) = @_; $Log->enter() if $may_enter; my $od_command_options = delete $opts{nacltask_on_demand_command}; my %od_command_options = %$od_command_options; my %other_opts = ( command_interface => $opts{command_interface}, nacltask_verify => $opts{nacltask_verify}, nacltask_if_exists => $opts{nacltask_if_exists} ); if ( exists $od_command_options{'scan-type'} ) { my $scan_type = delete $od_command_options{'scan-type'}; if ( $scan_type !~ /^cluster$|^vserver$|^file$|^dir$/ ) { $Log->exit() if $may_exit; NATE::Exceptions::Argument->throw( 'The value of the ' . "'scan-type' field in 'nacltask_on_demand_command' " . "can be any of 'cluster', 'vserver', 'file' or 'dir' " . "but was instead provided as '$scan_type'" ); } $od_command_options{name} ||= $opts{command}; unless ( $scan_type eq 'cluster' ) { $od_command_options{vserver} ||= $opts{vserver}; } my $avod_pkg = 'NACL::STask::AntivirusOnDemandCommandScan' . ucfirst $scan_type; $avod_pkg->create( %od_command_options, %other_opts ); } else { NACL::STask::AntivirusOnDemandCommand->create( %od_command_options, %other_opts ); } $Log->exit() if $may_exit; } 1;