# # Copyright (c) 2014 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary NFS Task Module ## @author anupamap@netapp.com, kathar.hidayath@netapp.com, dl-nacl-dev@netapp.com ## @status shared ## @pod here =head1 NAME NACL::MTask::NFS =head1 DESCRIPTION C provides a number of well-defined but potentially complex or multi-step methods related to NFS in ONTAP. The MTask will mainly be concerned with setting up of Nfs on a given volume with the given parameters on both CMode and 7Mode. =head1 ATTRIBUTES =head2 command_interface (Required) A component object that represents the host to which to send commands. =head2 export_policy Represents the NACL::C::ExportPolicy instance that will get created during the nfs setup. This is applicable only for CMode. =head2 export_policy_rule Represents the NACL::C::ExportPolicyRule instance that will get created during the nfs setup. This is applicable only for CMode. =head2 nfs Represents the NACL::STask::VserverNfs instance that will get created during the nfs setup. This is applicable only for CMode. =head2 volume Represents the NACL::STask::Volume instance that will find or modify the given volume during the nfs setup. This is applicable only for CMode. =head2 lif Represents the NACL::STask::NetworkInterface instance that will create logical interface during the nfs setup. This is applicable only for CMode. =head2 routing_group Represents the NACL::C::NetworkRoutingGroupsRoute instance that will create route for the particular routing group during the nfs setup. This is applicable only for CMode. =head2 route Represents the NACL::C::NetworkRoutingGroups instance that will fetch routing group name for the particular vserver during the nfs setup. This is applicable only for CMode. =head2 license Represents the NACL::STask::SystemLicense instance that will add the license if it is not added. This is applicable for both CMode and 7Mode. =head1 METHODS =head2 setup # Specifying minimal options, and the library taking the other defaults, my $nfs_obj = NACL::MTask::NFS->setup( 'command_interface' => $command_interface, 'vserver' => $vserver_name, 'volume' => $volume_name, 'address' => $address, 'home-port' => $home_port 'nacltask_if_exists'=> $action, # default 'die' 'nacltask_if_exportpolicy_exists' => $action, # default 'die' 'nacltask_if_exportpolicyrule_exists' => $action, # default 'die' 'nacltask_if_mount_exists' => $action, # default 'die' %other_options ); # Specifying all the various options, my $nfs_obj = NACL::MTask::NFS->setup( 'command_interface' => $command_interface, 'vserver' => $vserver_name, 'volume' => $volume_name, 'junction-path' => $junctoin_path, 'policyname' => $policyname, 'clientmatch' => $clientmatch, 'rorule' => $rorule, 'rwrule' => $rwrule, 'ruleindex' => $ruleindex, 'protocol' => $protocol, 'anon' => $anon, 'superuser' => $superuser, 'address' => $address, 'netmask' => $netmask, 'gateway' => $gateway, 'home-port' => $home_port, 'nacltask_if_exists' => $action, # default 'die' 'nacltask_if_exportpolicy_exists' => $action, # default 'die' 'nacltask_if_exportpolicyrule_exists' => $action, # default 'die' 'nacltask_if_mount_exists' => $action, # default 'die' 'nacltask_address_family' => $action, # default 'inet' for CMode %other_options ); (Class method)This method is called for both CMode and 7Mode NFS setup. Given the vserver name, volume name, root volume name, policyname , read write rule, client details, protocol , superuser etc. This task configures nfs server on the provided command interface. This method provides additional services beyond nfs creation commands, as controlled and described by the new "nacltask_if_exists" option. In addition to creating the nfs server, it also takes care of the following dependencies. 1) Adding Nfs license if not added previously. if its already present, then based on the 'nacltask_if_exists' value, it will purge or reuse or die. In case of CMode, it handles, 2) It enables the ipv6 option of NFS if nacltask_address_family is set to inet6. 3) Creating network interface with nfs protocol support. Set up will check first whether given vserver is having any lif which supports nfs or not. If no lif is present , then it will be created .If lif is already existing , then based on the 'nacltask_if_exists' value, it will purge or reuse or die. 4) Creating nfs on the vserver , ie Vserver nfs create . This step will check whether nfs is created on the particular vserver or not. if its already created , then based on the 'nacltask_if_exists' value, it will purge or reuse or die. 5) Creating the vserver export policy. 6) Creating the vserver export policy rule. 7) Modify the volume with newly created export policy. 8) Modify the root volume with the newly created exportpolicy If export policy is default , then step 4 to 7 will not perform. For all the other cases, export policy and rule will get created. If its already exists , then based on the 'nacltask_if_exists' value, it will continue. 9) Setting up the junction path for the data volume In case of 7Mode, it handles, 2) Sets nfs.v4.enable and nfs.v6.enable to on , if not set. if the options are already set , then it will go to next step. 3) Set nfs to on , if it is not running currently. If we set ipv6 option , then nfs should restart ie nfs off;nfs on . 4) export the volume and verify the same using exportfs. While exporting the volume , it will check for nacltask_if_exists value. if die , then setup will die by throwing message that its already exported. If purge, then setup will unexport the volume and then export it. if reuse , method will not export agiain. it will continue by telling that volume is exported already and goping to reuse it. =over =item Options =over =item C<< command_interface => $command_interface >> (Required) A component object that represents the host to which to send commands. =item C<< nacltask_if_exists=>$action >> (Optional) specifies an action to be taken if the nfs setup already exists. $action - if set to "die"(default), an exception is raised. - if set to "reuse", no action is performed, and return reference to the corresponding nfs object. - if set to "purge", deletes the existing Nfs setup before creating it. =item C<< nacltask_if_lif_exists=>$action >> (Optional) specifies an action to be taken if lif already exists. $action - if set to "die"(default), an exception is raised. - if set to "reuse", no action is performed, and return reference to the corresponding lif object. - if set to "purge", deletes the existing lif before creating it. =item C<< nacltask_if_exportpolicyrule_exists=>$action >> (Optional) specifies an action to be taken if export policy rule already exists. $action - if set to "die"(default), an exception is raised. - if set to "reuse", no action is performed, and return reference to the corresponding export policy rule object. - if set to "purge", deletes the existing export policy rule before creating it. =item C<< nacltask_if_exportpolicy_exists=>$action >> (Optional) specifies an action to be taken if export policy already exists. $action - if set to "die"(default), an exception is raised. - if set to "reuse", no action is performed, and return reference to the corresponding policy object. - if set to "purge", deletes the existing policy before creating it. =item C<< nacltask_if_mount_exists=>$action >> (Optional) specifies an action to be taken if junction path already exists. $action - if set to "die"(default), an exception is raised. - if set to "reuse", no action is performed. - if set to "purge", deletes the junction path before creating it. =item C<< nacltask_verify=> 0|1 >> (Optional) If '0' (default), verification will not be performed. If '1', verification will be performed to ensure that volume has exported successfully or nfs is created successfully on the vserver. =item C<< nacltask_address_family=> inet|inet6 >> (Optional) This option is used only for CMode. If 'inet' (default), nfs create with ipv4 address. If 'inet6', nfs create with ipv6 address. If nacltask_address_family is not passed it would set address family based on the address being passed =item C<< vserver => $vserver_name >> (Required) The name of the vserver. =item C<< lif => $lif >> (Optional) The name of logical interface. Default value is 'lif1' =item C<< address => $address >> (Optional) address of LIF. It fetches unused ip addresses if address is not passed in through network_interface_params. =item C<< subnet-name => $subnet_name >> (Optional) subnet name. =item C<< gateway => $gateway >> (Optional) gateway for LIF creation. =item C<< netmask => $netmask >> (Optional) netmask for LIF creation. =item C<< home-port => $home-port >> (Optional) home port for LIF creation. =item C<< volume => $volume_name >> (Required) The name of volume. =item C<< aggregate => $aggr_name >> (Optional) The name of aggregate. =item C<< diskcount => $disk_count >> (Optional) The number of disk in aggregate. =item C<< junction-path => $junction_path >> (Optional) Defines junction path for the data volume. Default value is /$volume_name =item C<< policyname => $policyname >> (Optional) The name of the NFS policy to be created. Default value is 'default' =item C<< clientmatch => $clientmatch >> (Optional) specify the ip of client to get matched. Default value is '0.0.0.0/0' for 7Mode and '::0/0' for CMode =item C<< rorule => $rorule >> (Optional) specify readonly rule. Default value is 'any' =item C<< rwrule => $rwrule >> (Optional) specify readwrite rule. Default value is 'any' =item C<< ruleindex => $rule index >> (Optional) specify rule index during setup. Default value is '1' =item C<< anon => $anon >> (Optional) specify anon value during setup. Default value is '1234' =item C<< superuser => $superuser >> (Optional) specify super user. Default value is 'any' =back =item command_interface, apiset_must, apiset_should, method-timeout, all the other options supported by create method in ExportPolicyRule.pm see L<< NACL::C::ExportPolicyRule->create|lib-NACL-C-ExportPolicyRule-pm/create >> fetch method in RoutingGroups.pm See L<< NACL::C::RoutingGroups->fetch|lib-NACL-C-RoutingGroups-pm/fetch >> create method in NetworkRoutingGroupsRoute.pm See L<< NACL::STask::NetworkRoutingGroupsRoute->create|lib-NACL-C-NetworkRoutingGroupsRoute-pm/create >> =back =cut package NACL::MTask::NFS; use strict; use warnings; use Tharn; use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use NACL::ComponentUtils qw(Dumper); use base qw(NACL::MTask::MTask NACL::STask::STask); use NACL::C::Component; use NACL::C::Options; use NACL::C::ExportPolicy; use NACL::C::ExportPolicyRule; use NACL::C::VserverNfs; use NACL::C::Volume; use NACL::STask::Aggregate; use NACL::C::Interface; use NACL::C::RoutingGroups; use NACL::C::NetworkRoutingGroupsRoute; use NACL::STask::SystemLicense; use NACL::STask::NetworkInterface; use NACL::STask::VserverNfs; use NACL::STask::Volume; use NATE::Exceptions::Argument qw(:try); use NACL::C::NetworkOptionsIpv6 qw(modify); use Net::IP qw(:PROC); use NACL::APISet::Exceptions::CommandFailedException; use Params::Validate qw/validate_with SCALAR ARRAYREF HASHREF OBJECT SCALARREF/; use Class::MethodMaker [ new => [ '-hash', 'new' ], scalar => [ { -type => 'NACL::C::CommandInterface::ONTAP' }, 'command_interface', ], scalar => [ { -type => 'NACL::STask::VserverNfs' }, 'nfs' ], scalar => [ { -type => 'NACL::C::ExportPolicy' }, 'export_policy' ], scalar => [ { -type => 'NACL::C::ExportPolicyRule' }, 'export_policy_rule' ], scalar => [ { -type => 'NACL::STask::Volume' }, 'volume' ], scalar => [ { -type => 'NACL::STask::Aggregate' }, 'aggregate' ], scalar => [ { -type => 'NACL::C::NetworkRoutingGroupsRoute' }, 'route' ], scalar => [ { -type => 'NACL::CS::NetworkInterfaceShowRoutingGroup' }, 'routing_group' ], scalar => [ { -type => 'NACL::STask::NetworkInterface' }, 'lif' ], scalar => [ { -type => 'NACL::STask::SystemLicense' }, 'license' ], scalar => '_export_status', scalar => '_nfs_status', scalar => '_export_policy_rule_status', scalar => '_export_policy_status', scalar => '_route_status', scalar => '_create_lif_status', scalar => '_modified_volumes', scalar => '_rootvol', scalar => '_mount_status', scalar => '_volume_status', scalar => '_aggregate_status', ]; #================================================================================================== # Name : setup # Description : Actual task function to be invoked by test scripts. POD above has complete details # Creates export policies,export policy rule, and then creates NFS. # #=================================================================================================== sub setup { $Log->enter() if $may_enter; my $pkg = shift; NATE::BaseException->throw( "Setup can only be invoked with a proper package call") if ref $pkg; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { nacltask_if_exists => $pkg->_if_exists_validate_spec(), vserver => { type => SCALAR, optional => 1 }, volume => { type => SCALAR }, aggregate => { type => SCALAR, optional => 1 }, diskcount => { type => SCALAR, optional => 1 }, raidtype => { type => SCALAR, optional => 1 }, address => { type => SCALAR, optional => 1 }, 'subnet-name' => { type => SCALAR, optional => 1 }, 'home-port' => { type => SCALAR, optional => 1, depends => ['lif'] }, 'home-node' => { type => SCALAR, optional => 1, depends => ['lif'] }, lif => { type => SCALAR, optional => 1 }, netmask => { type => SCALAR, optional => 1, depends => ['lif'] }, destination => { type => SCALAR, default => '0.0.0.0/0' }, clientmatch => { type => SCALAR, default => '0.0.0.0/0' }, role => { type => SCALAR, default => 'data' }, policyname => { type => SCALAR, default => 'default' }, protocol => { type => ARRAYREF, default => ['nfs'] }, rwrule => { type => ARRAYREF, default => ['any'] }, rorule => { type => ARRAYREF, default => ['any'] }, ruleindex => { type => SCALAR, default => '1' }, options => { type => SCALAR, default => 'rw,anon=0' }, superuser => { type => ARRAYREF, default => ['any'] }, _was_created => { type => SCALARREF, optional => 1 }, nacltask_verify => { type => SCALAR, default => '0' }, 'nacltask_if_lif_exists' => { type => SCALAR, default => 'die' }, 'nacltask_if_routinggrouproute_exists' => { type => SCALAR, default => 'die' }, 'nacltask_if_exportpolicy_exists' => { type => SCALAR, default => 'die' }, 'nacltask_if_exportpolicyrule_exists' => { type => SCALAR, default => 'die' }, 'nacltask_if_mount_exists' => { type => SCALAR, default => 'die' }, 'nacltask_if_volume_exists' => { type => SCALAR, default => 'die' }, 'nacltask_if_aggregate_exists' => { type => SCALAR, default => 'die' }, 'nacltask_address_family' => { type => SCALAR, optional => 1 }, }, allow_extra => 1, ); my $obj = $pkg->new( command_interface => $opts{command_interface}, ); my $command_interface = $opts{command_interface}; my $vserver = $opts{'vserver'}; my $volume = $opts{'volume'}; my $aggregate = $opts{'aggregate'}; my $diskcount = $opts{'diskcount'}; my $raidtype = $opts{'raidtype'}; my $nacltask_if_exists = $opts{nacltask_if_exists}; my $Was_Created = 0; my $change = 0; my $_was_created = delete $opts{_was_created}; my $nacltask_address_family; if ( ! defined $opts{nacltask_address_family} ) { my $ip = Net::IP::ip_is_ipv6( $opts{'address'} ); if ( $ip == 1 ) { $nacltask_address_family = "inet6"; } else { $nacltask_address_family = "inet"; } } else { $nacltask_address_family = delete $opts{nacltask_address_family}; } my ($disk_count, $raid_type); $$_was_created = 0; $opts{'home-node'} = $command_interface->node() if ( !( defined $opts{'home-node'} ) ); my ( $nfs, $vol_ref, $routing_group, $export_policy, $export_rule ); if ( defined( $opts{'junction-path'} ) && $opts{'junction-path'} !~ /^\/.*/ ) { $opts{'junction-path'} = "/" . $opts{'junction-path'}; } # Default mount point $opts{'junction-path'} = "/" . $volume if ( !( defined $opts{'junction-path'} ) ); if (! defined $opts{diskcount}) { $disk_count = 2; } else { $disk_count = delete $opts{diskcount}; } if (! defined $opts{raidtype}) { $raid_type = "raid4"; } else { $raid_type = delete $opts{raidtype}; } my @address_family_array = ("nfs.v4.enable"); if ( $command_interface->mode() eq "7Mode" ) { if ( $nacltask_address_family =~ /inet6/i ){ push(@address_family_array , "nfs.ipv6.enable") ; push(@address_family_array , "ip.v6.enable") ; push(@address_family_array , "ip.v6.ra_enable") ; } try { # If command interface mode is 7Mode # Set nfs enable for ipv4 and ipv6 options foreach my $val ( @address_family_array ) { NACL::C::Options->option( command_interface => $command_interface, 'option-name' => $val, 'option-value' => 'on' ); } ## end foreach my $val ( "nfs.v4.enable"...) } catch NACL::APISet::Exceptions::ResponseException with { my $exception = shift; if($exception->get_response_object()->get_raw_output() !~ /No such option nfs.ipv6.enable/i) { $Log->exit() if $may_exit; $exception->throw(); } }; LOOP1: try { # Check whether NFS is on or not # If license is missing , it will throw exception foreach my $method ( "off", "on" ) { NACL::C::VserverNfs->$method( command_interface => $command_interface, ); } } ## end try catch NACL::APISet::Exceptions::CommandFailedException with { my $err_msg = shift; #Adding the license if (( $err_msg->text() =~ /Use the "license" command to license it/ ) || ( $err_msg->text() =~ /NFS not licensed/ ) || ( $err_msg->text() =~ /NFS service is not licensed/ ) ) { my $license = NACL::STask::SystemLicense->add( command_interface => $command_interface, feature => 'nfs' ); $obj->license($license); goto LOOP1; } else { $Log->exit() if $may_exit; $err_msg->throw(); } }; my $Apiset = $command_interface->apiset( category => 'Node', interface => 'CLI', set => '7Mode' ); my $response = $Apiset->exportfs(); my $output = $response->get_parsed_output(); try { # Performing find for the volume $vol_ref = NACL::STask::Volume->find( command_interface => $command_interface, filter => { volume => $volume }, ); $obj->volume($vol_ref); } ## end try catch NACL::Exceptions::NoElementsFound with { # Create the aggregate if not exists my $aggregate = _handle_creation( %opts, component => 'NACL::STask::Aggregate', method => 'create', delete_method => 'delete', if_exists => $opts{'nacltask_if_aggregate_exists'}, raidtype => $raid_type, diskcount => $disk_count, _was_created => \$Was_Created ); $obj->aggregate($aggregate); #Set the flag if aggregate is created $obj->_aggregate_status(1); # Create the volume if not exists my $volume = _handle_creation( %opts, component => 'NACL::STask::Volume', method => 'create', delete_method => 'delete', if_exists => $opts{'nacltask_if_volume_exists'}, _was_created => \$Was_Created ); $obj->volume($volume); #Set the flag if aggregate is created $obj->_volume_status(1); }; # Check whether volume is already exported or not my $i = 0; my $Export_Flag = ""; foreach (@$output) { if ( $output->[$i]{'path'} =~ /vol\/$volume/ ) { if ($output->[$i]{'export_options'} =~ /[-]$opts{'options'}/ ) { $Export_Flag = "exists"; } } ## end if ( $output->[$i]{'path'...}) $i++; } ## end foreach (@$output) if ( $Export_Flag =~ /exists/ ) { # Check for nacltask_if_exists , if already exported if ( $nacltask_if_exists eq 'die' ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "Volume with specified permissions is exported already"); } elsif ( $nacltask_if_exists eq 'purge' ) { $Apiset->exportfs( "zap" => 1, path => "/vol/$volume" ); $obj->_export_status(1); } else { $Log->comment("Volume is present , Reusing the same one!!"); $Log->exit() if $may_exit; return $obj; } } ## end if ( $Export_Flag =~ /exists/) try { # If volume is not exported $Apiset->exportfs( ignore => 1, options => $opts{'options'}, path => "/vol/$volume" ); $obj->_export_status(1); } ## end try catch NACL::APISet::Exceptions::CommandFailedException with { my $err_msg = shift; $Log->exit() if $may_exit; $err_msg->throw(); }; } elsif ( $command_interface->mode() eq "CMode" ) { # If CMode try { #Enable IPv6 on the nfs if ( $nacltask_address_family =~ /inet6/ ) { NACL::C::NetworkOptionsIpv6->modify( command_interface => $command_interface, enabled => "true", ); $opts{clientmatch} = '::0/0' if ( $opts{clientmatch} =~ /0\.0\.0\.0\/0/ ); } } ## end try catch NACL::APISet::Exceptions::ResponseException with { my $err_msg = shift; $Log->exit() if $may_exit; $err_msg->throw(); }; try { #Check whether data volume is present or not my $vol_ref = NACL::STask::Volume->find( command_interface => $command_interface, filter => { vserver => $vserver, volume => $volume }, ); $obj->volume($vol_ref); my $junc_path = $vol_ref->get_one_state_attribute('junction-path'); if ( $junc_path ne "-" ) { # Reuse the mount point $opts{'junction-path'} = $junc_path; } else { # Mount the volume $vol_ref->mount( 'junction-path' => $opts{'junction-path'} ); } } ## end try catch NACL::Exceptions::NoElementsFound with { # Create the aggregate if not exists my $aggregate = _handle_creation( %opts, component => 'NACL::STask::Aggregate', method => 'create', delete_method => 'delete', if_exists => $opts{'nacltask_if_aggregate_exists'}, raidtype => $raid_type, diskcount => $disk_count, _was_created => \$Was_Created ); $obj->aggregate($aggregate); #Set the flag if aggregate is created $obj->_aggregate_status(1); # Create the volume if not exists my $volume = _handle_creation( %opts, component => 'NACL::STask::Volume', method => 'create', delete_method => 'delete', if_exists => $opts{'nacltask_if_volume_exists'}, _was_created => \$Was_Created ); $obj->volume($volume); #Set the flag if aggregate is created $obj->_volume_status(1); }; try { my $lif = _handle_creation( %opts, component => 'NACL::STask::NetworkInterface', method => 'create', delete_method => 'delete', if_exists => $opts{'nacltask_if_lif_exists'}, _was_created => \$Was_Created ); $obj->lif($lif); #Set the flag if lif is created $obj->_create_lif_status(1) if ($Was_Created); } ## end try catch NACL::APISet::Exceptions::CommandFailedException with { my $err_msg = shift; $Log->exit() if $may_exit; $err_msg->throw(); }; if ( ! $command_interface->version_manager->has_uichange( uichange => 'ipspaces-ms4', throw_exception => 0) ) { try { #Check for routing group and route $routing_group = $obj->lif()->show_routing_group(); $obj->routing_group($routing_group); $opts{'routing-group'} = $routing_group->routing_group(); $obj->{route} = _handle_creation( %opts, component => 'NACL::C::NetworkRoutingGroupsRoute', method => 'create', delete_method => 'delete', if_exists => $opts{'nacltask_if_routinggrouproute_exists'}, _was_created => \$Was_Created ); $obj->_route_status(1) if ($Was_Created); } ## end try catch NACL::Exceptions::NoElementsFound with { my $err_msg = shift; $Log->exit() if $may_exit; $err_msg->throw(); }; } # If policy name is 'default', then purge does not work. 'default' # export policy once created, cannot be deleted. # To avoid failures unnecessarily, we change # nacltask_if_exportpolicy_exists to 'reuse' # only in this case if ( $opts{nacltask_if_exportpolicy_exists} eq 'purge' && $opts{'policyname'} eq 'default' ) { $opts{nacltask_if_exportpolicy_exists} = 'reuse'; } if ( $opts{'policyname'} ne 'default' ) { # Create export-policy with given policy name if ( $opts{'nacltask_if_exportpolicy_exists'} eq 'purge' ) { try { my @vol_obj = NACL::STask::Volume->find( command_interface => $command_interface, filter => { vserver => $vserver, policy => $opts{policyname} }, ); #If export policy is present and volumes are associated with it #if nacltask_if_exists is purge , then volume policy need to be #modified as default my $volume_name = ""; foreach my $vol (@vol_obj) { $volume_name = $vol->volume() . "," . $volume_name; } chop($volume_name); $obj->_modified_volumes($volume_name); NACL::STask::Volume->modify( command_interface => $opts{command_interface}, volume => $volume_name, vserver => $vserver, policy => "default", ); } ## end try catch NACL::Exceptions::NoElementsFound with { $Log->comment( "No volume are associated with $opts{policyname} "); }; } ## end if ( $opts{'nacltask_if_exportpolicy_exists'...}) #Create exportpolicy $export_policy = _handle_creation( %opts, component => 'NACL::C::ExportPolicy', method => 'create', delete_method => 'delete', if_exists => $opts{'nacltask_if_exportpolicy_exists'}, _was_created => \$Was_Created ); $obj->export_policy($export_policy); #Set the flag if policy is created # $Created will get set only if the component is not existing #and we are creating it $obj->_export_policy_status(1) if ($Was_Created); } ## end if ( $opts{'policyname'...}) # Create policy rule $export_rule = _handle_creation( %opts, component => 'NACL::C::ExportPolicyRule', method => 'create', delete_method => 'delete', if_exists => $opts{'nacltask_if_exportpolicyrule_exists'}, _was_created => \$Was_Created ); $obj->export_policy_rule($export_rule); #Set the flag if policy rule is created # $Created will get set only if the rule is not existing #and we are creating it $obj->_export_policy_rule_status(1) if ($Was_Created); #Modify the root volume and data volume with the newly created policy $vol_ref = NACL::STask::Volume->modify( command_interface => $command_interface, volume => $volume, vserver => $vserver, policy => $opts{policyname}, ); my $vs = NACL::C::Vserver->find( command_interface => $command_interface, filter => { vserver => $vserver, }, ); #Find root volume for the given vserver my $vsr = $vs->state( requested_fields => [qw(rootvolume)] ); my $root_vol = $vsr->rootvolume(); $obj->_rootvol($root_vol); NACL::STask::Volume->modify( command_interface => $command_interface, volume => $vsr->rootvolume(), vserver => $vserver, policy => $opts{policyname} ); #Create vserver nfs LOOP: try { $nfs = _handle_creation( %opts, component => 'NACL::STask::VserverNfs', method => 'create', delete_method => 'delete', nacltask_verify => $opts{nacltask_verify}, if_exists => $opts{'nacltask_if_exists'}, _was_created => \$Was_Created ); $obj->nfs($nfs); $obj->_nfs_status(1) if ($Was_Created); } ## end try catch NACL::APISet::Exceptions::CommandFailedException with { my $err_msg = shift; if ( $err_msg->text() =~ /You do not have a valid NFS (.*)license/ ) { my $license = NACL::STask::SystemLicense->add( command_interface => $command_interface, feature => 'nfs' ); $obj->license($license); goto LOOP; } else { $Log->exit() if $may_exit; $err_msg->throw(); } }; try { my $mount = NACL::STask::Volume->find( command_interface => $command_interface, filter => { vserver => $opts{vserver}, 'junction-path' => $opts{'junction-path'}, }, ); if ( ( $mount->vserver() eq $opts{vserver} ) && ( $mount->volume() eq $opts{volume} ) ) { if ( $opts{'nacltask_if_mount_exists'} eq "die" ) { NACL::APISet::Exceptions::CommandFailedException->throw( text => "Already exists : Duplicate Entry" ); } elsif ( $opts{'nacltask_if_mount_exists'} eq "purge" ) { #Create junction path for the data volume _handle_creation( %opts, component => 'NACL::C::Volume', method => 'mount', delete_method => 'unmount', if_exists => $opts{'nacltask_if_exists'}, _was_created => \$Was_Created ); } ## end elsif ( $opts{'nacltask_if_mount_exists'...}) } else { NACL::APISet::Exceptions::CommandFailedException->throw( text => "Some other volume mounted on the same junction path" ); } ## end else [ if ( ( $mount->vserver...))] } ## end try catch NACL::Exceptions::NoElementsFound with { $obj->_mount_status(1); NACL::STask::Volume->mount( command_interface => $opts{command_interface}, volume => $volume, vserver => $vserver, 'junction-path' => $opts{'junction-path'}, ); }; } ## end elsif ( $command_interface...) $$_was_created = 1; $Log->exit() if $may_exit; return $obj; } ## end sub setup #=================================================================================================================== # Name : _handle_creation # Description : Invoked from setup. If there are duplicate entries already existing on the filer, based on # nacltask_if_exists option which might be to die,purge or reuse, appropriate action is taken. If e.g. # nacltask_if_exists => purge, It will call purge action which deletes the entries and recreates # the entries. If that configuration is not there, it simply creates the entry. Returns an # object of the requested component type only if this method creates one so that this can be # stored as an attribute for deletion during cleanup. #=================================================================================================================== sub _handle_creation { $Log->enter() if $may_enter; my %opts = @_; my $Was_Created = delete $opts{_was_created}; $$Was_Created = 0; # get the name of the component and the name of the method required for # create/add my $pkg = $opts{component}; my $method = $opts{method}; # extract other necessary details my $command_interface = $opts{command_interface}; my $nacltask_if_exists = $opts{if_exists}; my $existing = undef; # We will not handle the nacltask_if_exists => 'die' case here since the # create call will anyway throw an error if the element exists. if ( $nacltask_if_exists ne 'die' ) { $Log->trace('Attempt to find the element'); $existing = $pkg->find( command_interface => $command_interface, filter => $pkg->get_primary_keys_options(%opts), allow_empty => 1 ); } ## end if ( $nacltask_if_exists...) if ($existing) { $Log->trace('Found a matching element'); if ( $nacltask_if_exists eq 'purge' ) { _purge(%opts); } elsif ( $nacltask_if_exists eq 'reuse' ) { $Log->exit() if $may_exit; return $existing; } else { NACL::APISet::Exceptions::CommandFailedException->throw( text => "Already exists : Duplicate Entry" ); } } else { # Created flag will set only if it is not existing $$Was_Created = 1; } # Create the element here if not found above, and if nacltask_if_exists is set to 'die' my $self = $pkg->$method( %opts, command_interface => $command_interface, allow_extra => 1 ); $Log->exit() if $may_exit; return $self; } ## end sub _handle_creation =head2 purge $nfs->purge(); NACL::MTask::NFS->purge( command_interface => $command_interface, vserver => $vserver volume => $volume, lif => $lif, policyname => $policyname, ); (Class or instance method) This method deletes the NFS setup for both CMode and 7Mode. It will delete license , logical interface and export policy if it is created during the set up. =over =item Options =over =item C<< volume=>$volume >> (Required) Name of the volume to be exported. purge will unexport the volume if it is exported during set up. =item C<< vserver=>$vserver >> (Required) Name of the vserver =item C<< lif => $lif >> (Optional) The name of logical interface. Default value is 'lif1' purge will delete the lif if it is created during set up. =item C<< policyname => $policyname >> (Optional) Export policy name. Default value is 'default' purge will delete the export policy if it is created during set up. =back =back =cut #=================================================================================================================== # Name : _purge # Description : Invoked from _handle_creation. It takes the appropriate delete method to be used and the # component that has requested for purge, frames the method with appropriate parameters # to be called and invokes the same. #=================================================================================================================== sub _purge { $Log->enter() if $may_enter; my %opts_temp = @_; my $pkg = $opts_temp{component}; my $method = $opts_temp{delete_method}; my $command_interface = $opts_temp{command_interface}; # call either delete/remove on the package provided $pkg->$method( command_interface => $command_interface, %opts_temp, allow_extra => 1 ); $Log->exit() if $may_exit; } ## end sub _purge #=================================================================================================================== # Name : purge # Description : Invoked from test script . It takes the appropriate delete method to be used and the # component that has requested for doing clean up once setup has been done #=================================================================================================================== sub purge { $Log->enter() if $may_enter; my $obj = shift; my %opts = $obj->_common_validate_with( params => \@_, additional_spec => { command_interface => { type => OBJECT, isa => 'NACL::C::CommandInterface::ONTAP' }, }, ); if ( $obj->command_interface()->mode() eq "CMode" ) { if ( $obj->_route_status() ) { $obj->route()->delete(); } if ( $obj->_create_lif_status() ) { $obj->lif()->purge(); if ( ! $obj->command_interface()->version_manager->has_uichange( uichange => 'ipspaces-ms4', throw_exception => 0) ) { NACL::C::RoutingGroups->delete( command_interface => $obj->command_interface(), vserver => $obj->routing_group()->vserver(), 'routing-group' => $obj->routing_group()->routing_group() ); } } ## end if ( $obj->_create_lif_status...) if ( $obj->_nfs_status() ) { $obj->nfs()->purge(); } if ( $obj->_export_policy_rule_status() ) { foreach my $vol ( $obj->volume()->{volume}, $obj->_rootvol() ) { NACL::STask::Volume->modify( command_interface => $obj->command_interface(), volume => $vol, vserver => $obj->volume()->{vserver}, policy => "default", ); } ## end foreach my $vol ( $obj->volume...) $obj->export_policy_rule()->delete(); } ## end if ( $obj->_export_policy_rule_status...) if ( $obj->_export_policy_status() ) { $obj->export_policy()->delete(); } if ( $obj->_modified_volumes() ) { NACL::STask::Volume->modify( command_interface => $obj->command_interface(), volume => $obj->_modified_volumes(), vserver => $obj->volume()->{vserver}, policy => $obj->export_policy()->{'policyname'}, ); } ## end if ( $obj->_modified_volumes...) if ( $obj->_mount_status() ) { NACL::STask::Volume->unmount( command_interface => $obj->command_interface(), volume => $obj->volume()->{volume}, vserver => $obj->volume()->{vserver}, ); } ## end if ( $obj->_mount_status...) } else { if ( $obj->_export_status() ) { my $Apiset = $obj->command_interface()->apiset( category => 'Node', interface => 'CLI', set => '7Mode' ); $Apiset->exportfs( "zap" => 1, path => "/vol/" . $obj->volume()->{volume} ); } ## end if ( $obj->_export_status...) } ## end else [ if ( $obj->command_interface...)] if ( $obj->license() ) { $obj->license()->purge(); } # Delete the volume created by setup if ( $obj->_volume_status() ) { $obj->volume()->purge(); } # Delete the aggregate created by setup if ( $obj->_aggregate_status() ) { $obj->aggregate()->purge(); } $Log->exit() if $may_exit; } ## end sub purge sub _common_validate_with { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = validate_with( params => \@_, spec => { params => { type => ARRAYREF }, additional_spec => { type => HASHREF, default => {} }, allow_extra => { optional => 1 }, }, ); my %params = @{ $opts{params} }; my %spec = %{ $opts{additional_spec} }; my @keys = qw (command_interface); if ( ref $pkg_or_obj ) { foreach my $key (@keys) { my $key_isset = "${key}_isset"; if ( $params{$key} && $pkg_or_obj->$key_isset() ) { NATE::Exceptions::Argument->throw( "Option '$key' not allowed (it is already an " . 'attribute of this object' ); } if ( $pkg_or_obj->$key_isset() ) { my $value = $pkg_or_obj->$key(); $params{$key} = $value; } } ## end foreach my $key (@keys) } ## end if ( ref $pkg_or_obj ) # Any argument to a method call can either be of the type specified in # the call to _common_validate_with (i.e. scalar or arrayref) or can be # an object. This makes OBJECT be a valid type for all arguments while ( my ( $key, $value ) = each %spec ) { $value->{type} = $value->{type} | OBJECT if ( defined $value->{type} ); } $Log->exit() if $may_exit; return validate_with( params => \%params, spec => \%spec, allow_extra => 1, ); } ## end sub _common_validate_with 1;