# Copyright (c) 2001-2017 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary NetworkInterface STask Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here package NACL::STask::NetworkInterface; use strict; use warnings; use base qw(NACL::C::NetworkInterface NACL::STask::STask); use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use Net::IP qw(:PROC); use Params::Validate qw(validate_with SCALAR SCALARREF ARRAYREF HASHREF OBJECT); use NACL::Exceptions::VerifyFailure qw(:try); use NACL::APISet::Exceptions::ResponseException qw(:try); use NACL::CS::Ifconfig; use NACL::C::NetworkOptionsIpv6; use NACL::ComponentUtils qw(Dumper); use NACL::STask::Vserver; use Array::Utils qw(array_minus); use Class::MethodMaker [ hash => [ 'deleted_lif', ],scalar => '_was_lif_created' ]; use NACL::C::Exceptions::NetworkInterface::PortHealthStatusDegraded; use NACL::C::Exceptions::NetworkInterface::NoAddressInSubnet; =head1 NAME NACL::STask::NetworkInterface =head1 DESCRIPTION C provide methods to configure, modify, migrate, destroy, set_iscsi_lif and delete_iscsi_lif network interfaces in ONTAP. It builds on top of, and is a derived class of C, and so it also provides methods that are more in the scope of individual interface-related commands. See C for details. This also means that a C object may generally be used in place of a component object. =head1 CLEANUP METHODS Cleanup can be registered for the following methods, 'create', 'modify', 'rename'. Cleanup methods are, NetworkInterface Method Cleanup Method ----------------------------------------------- create purge modify modify rename rename =head1 ATTRIBUTES =head2 command_interface (Required) As C. A component object that represents the host to which to send commands. =head2 lif (Required) The name of the logical interface, used to identify it uniquely =head2 vserver (Required) The name of the vserver that contains the lif =head2 deleted_lif (Optional) While creating iscsi lif, if there are no free data ips available and if the no_convert_existing_lif is set to 0, an existing iscsi lif is deleted in order to fetch its ip-address. So the details of the deleted lif is present in this attribute. This parameter will be required in the purge in order to re-create. =head1 METHODS =head2 create my $interface_obj = NACL::STask::NetworkInterface->create( command_interface => $command_interface, lif => $lif, vserver => $vserver_name, role => $role, 'home-node' => $home_node, 'home-port' => $home_port, address => $address, netmask => $netmask, nacltask_if_exists => $action, # default 'die' nacltask_to_cleanup => 1, # default 0, nacltask_cleanup_manager => $CleanupObj, nacltask_verify_only_sk => 1, # default 0, nacltask_verify_only_bsd => 1, # default 0, %other_options ); (Class method) create a network interface. This method provides additional services as controlled and described by the new C. And also provides an mechanism of cleanup for the objects that is created by this method. Supports only CMode CLI and ZAPI. =over =item Options =over =item C<< command_interface >> (Required) As NACL::C::Node. A component object that represents the host to which to send commands. =item C<< lif >> (Required) The name of the logical interface, used to identify it uniquely. The name of the logical interface is unique within a particular vserver. =item C<< vserver >> (Required) The name of the vserver that contains the lif. =item C<< role >> (Required) Role of the logical interface that is created. =item C<< home-node >> (Required) Name of the node in which the lif needs to be created. =item C<< home-port >> (Required) Name of the port in which the lif needs to be created. =item C<< address >> (Optional) Ip-address that will be assigned to this lif. Unused Ip-address would be picked from natehost file if address is not specified. This feature is expected to work in a system with ONTAP created IPSpaces when the node comes up i.e Default and Cluster. =item C<< netmask >> (Required) Netmask for the ip-address. =item C<< nacltask_if_exists => $action >> (Optional) Specifies an action to be taken if the interface already exists. - if set to "die" (default), an exception is raised. - if set to "reuse", no action is performed. - if set to "purge", deletes the existing interface before creating it. =item C<< nacltask_verify >> (Optional) - when set to 1, interface creation will be verified. - when set to 0, the verification step is skipped. - by default its set to 0. Note that it is also possible to perform the verification later by calling L. This can be used for cases where ONTAP chooses to not set all attributes to the exact values specified. In the call we can specify those attributes we want verified. =item C<< nacltask_verify_only_sk=>$nacltask_verify_only_sk >> (Optional) If '0' (default), verification will not be performed. If '1', verification will be performed to ensure that the addition did happen successfully in sk. =item C<< nacltask_verify_only_bsd=>$nacltask_verify_only_bsd >> (Optional) If '0' (default), verification will not be performed. If '1', verification will be performed to ensure that the addition did happen successfully in bsd. =item C<< _was_created => \$scalar >> (Optional) When this option is provided a reference to a scalar variable, the variable gets filled in with a boolean value describing whether the interface was found (value will be 0; this scenario is possible when if_exists => "reuse") or whether the interface was created (value will be 1). This is necessary to determine whether the intreface needs to be cleaned up later. my $was_created; my $interface_obj = NACL::STask::NetworkInterface->create( if_exists => 'reuse', _was_created => \$was_created, %other_opts ); # Operate on $interface_obj here # ... # Now determine whether to clean up the interface, since we're not sure # whether we reused an existing interface or created a new one if ($was_created) { # New interface was created. Clean it up. $interface_obj->purge(); } =item C<< nacltask_to_cleanup => 0|1 >> (Optional, default to 0(no to cleanup) Flag indicating if this operation needs to be registered for clean up or not. =item C<< nacltask_cleanup_manager >> Cleanup manager to be used for registering. Default : Will use the default cleanup manager. =item C The options accepted for MCC configuration replication verification is documented at L. =item Other options All of the other various options are described in L<< NACL::C::NetworkInterface->create|lib-NACL-C-NetworkInterface-pm/create >> =back =item Exceptions =over =item C This type of exception is thrown when an attempt is made to create an interface that already exists. =item C This type of exception is thrown when there is a failure in verifying network interface creation in ngsh, sk and bsd. =item C This type of exception is thrown when an attempt is made to create a lif on degraded port. =back =back =cut sub create { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { nacltask_if_exists => $pkg->_if_exists_validate_spec(), nacltask_verify => { type => SCALAR, default => 0 }, nacltask_verify_only_sk => { type => SCALAR, default => 0 }, nacltask_verify_only_bsd => { type => SCALAR, default => 0 }, _was_created => { type => SCALARREF, optional => 1 }, $pkg->_cleanup_validate_spec(), $pkg->_mcc_validate_spec(), }, allow_extra => 1 ); my (%opts_for_cleanup, %opts_for_register, $nacltask_to_cleanup); $pkg->_move_common_cleanup_opts( source => \%opts, target => \%opts_for_cleanup, ); my $if_exists = delete $opts{nacltask_if_exists}; my $was_created = delete $opts{_was_created}; my $command_interface = $opts{command_interface}; my $verify = delete $opts{nacltask_verify}; my $self; my $verify_bsd = delete $opts{nacltask_verify_only_bsd}; my $verify_sk = delete $opts{nacltask_verify_only_sk}; my %mcc_opts; my %hostrec_opts; my @data_protocol; if (defined $opts{'data-protocol'}) { if (ref($opts{'data-protocol'}) eq "ARRAY") { @data_protocol = @{$opts{'data-protocol'}}; } else { @data_protocol = $opts{'data-protocol'}; } } # Address parameter is not needed if data-protocol is fcp, subnet-name is provided or auto=true (ONTAP assignment) # skipping automatic address picking feature if ( !defined $opts{address} && !defined $opts{'subnet-name'} && (!grep {$_ eq "fcp"} @data_protocol) && (!grep {$_ eq "fc-nvme"} @data_protocol) && $opts{auto} !~ /^true$/i) { $opts{address} = $pkg->_get_unused_hostrec_ip_address(%opts); } $pkg->_move_common_mcc_opts( source => \%opts, target => \%mcc_opts, ); $$was_created = 0; # If Both netmask and netmask-lenth are defined then remove one based on the ip version. # If ipv6 then remove netmask. Otherwise remove netmask-length. # If only one is provided then do nothing as both are allowed by ontap for ipv4 and ipv6 lifs. if(defined($opts{netmask}) and defined($opts{'netmask-length'})) { if ( Net::IP::ip_is_ipv6( $opts{'address'} ) ) { delete $opts{netmask}; } else { delete $opts{'netmask-length'}; } } CREATE: { if ( Net::IP::ip_is_ipv6( $opts{address} ) ) { $opts{address} = Net::IP::ip_compress_address( $opts{address}, 'IPv6' ); } use warnings qw(exiting); try { if ( Net::IP::ip_is_ipv6( $opts{'address'} ) ) { NACL::C::NetworkOptionsIpv6->modify( command_interface => $command_interface, enabled => "true", ); } $self = $pkg->SUPER::create(%opts); $$was_created = 1; $self->_was_lif_created(1); if ($verify) { $pkg->taskverify_create(%opts); } } catch NACL::C::Exceptions::NetworkInterface::AlreadyExists with { my $exception = $_[0]; $Log->trace('The network interface already exists'); $self = $pkg->_element_exists_handler( create_opts => \%opts, nacltask_if_exists => $if_exists, exception => $exception ); if ( !$self ) { no warnings qw(exiting); redo CREATE; } } catch NACL::C::Exceptions::NetworkInterface::PortHealthStatusDegraded with { my $exception = $_[0]; my %port_args = {}; $port_args{'command_interface'} = $opts{'command_interface'}; $port_args{'home_port'} = $opts{'home-port'}; $exception->port_args(%port_args); $exception->throw(); } catch NACL::C::Exceptions::NetworkInterface::IpAlreadyBound with { my $exception = $_[0]; $Log->comment("A lif already exists for this address"); if ( $if_exists eq 'reuse' ) { $self = $pkg->find( command_interface => $opts{command_interface}, filter => { address => $opts{'address'}} ); } elsif( $if_exists eq 'purge') { $self = $pkg->find( command_interface => $opts{command_interface}, filter => { address => $opts{'address'}} ); $self->purge(); redo CREATE; } else { $exception->throw(); } } catch NACL::C::Exceptions::NetworkInterface::NoAddressInSubnet with { my $exception = $_[0]; my $lif_obj = $pkg->find( command_interface => $opts{command_interface}, filter => { lif => $opts{lif}, vserver => $opts{vserver}}, allow_empty => 1, ); if ( $lif_obj ) { $Log->trace('The network interface already exists'); $self = $pkg->_element_exists_handler( create_opts => \%opts, nacltask_if_exists => $if_exists, exception => $exception ); if ( !$self ) { no warnings qw(exiting); redo CREATE; } } else { $exception->throw(); } }; } $self->_check_in_metrocluster_node( command_interface => $opts{command_interface}, method_used => "create", %mcc_opts, ); # Checking the lif creation on the BSD if ($verify_bsd) { $pkg->taskverify_create( %opts, 'shell_type' => "systemshell" ); } # Checking the lif creation on the SK if ($verify_sk) { $pkg->taskverify_create( %opts, 'shell_type' => "nodescope" ); } # Regigster this resource with the cleanup manager for cleanup. $pkg->_copy_common_opts_for_cleanup( 'source' => {%opts, %opts_for_cleanup}, 'target' => \%opts_for_register, 'nacltask_to_cleanup'=> \$nacltask_to_cleanup, 'to_cleanup' => 'purge', ); $opts_for_register{lif} = $self->lif(); $pkg->_register_for_cleanup(%opts_for_register) if ($nacltask_to_cleanup && $$was_created); $Log->exit() if $may_exit; return $self; } ## end sub create =head2 modify NACL::STask::NetworkInterface->modify( command_interface => $command_interface, lif => $lif, vserver => $vserver_name, nacltask_to_cleanup => 1, # default 0, nacltask_cleanup_manager => $CleanupObj, nacltask_verify => 1, # default 0, nacltask_verify_only_sk => 1, # default 0, nacltask_verify_only_bsd => 1, # default 0, %other_options ); or $interface_obj->modify(%other_options); (Class method and instance method) Modify a interface. This method will modify the specified interface attributes and also verify that the attribute is modified as expected. And also provides an mechanism of cleanup for the objects that is modified by this method. Supports only CMode CLI and ZAPI. =over =item Options =over =item C<< nacltask_verify => 1 >> (Optional) - would verify that the interface is modified at the end, if set to 1 - by default it is set to 0 Note that it is also possible to perform the verification later by calling L. This can be used for cases where ONTAP chooses to not set all attributes to the exact values specified. In the call we can specify those attributes we want verified. =item C<< nacltask_verify_only_sk=>$nacltask_verify_only_sk >> (Optional) If '0' (default), verification will not be performed. If '1', verification will be performed to ensure that the modification happen successfully in sk. =item C<< nacltask_verify_only_bsd=>$nacltask_verify_only_bsd >> (Optional) If '0' (default), verification will not be performed. If '1', verification will be performed to ensure that the modification happen successfully in bsd. =item C<< nacltask_to_cleanup => 0|1 >> (Optional, defaults to 0(not to cleanup)) Flag indicating if this operation needs to be registered for clean up or not. =item C<< nacltask_cleanup_manager >> Cleanup manager to use for registering Default : will use the default cleanup manager. =item All of the other various options are described in L<< NACL::C::NetworkInterface->modify|lib-NACL-C-NetworkInterface-pm/modify >> =back =item Exceptions =over =item C This type of exception is thrown when there is a failure in verifying modified interface attributes in ngsh, sk and bsd. =back =back =cut sub modify { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { nacltask_verify => { type => SCALAR, default => 0 }, nacltask_verify_only_sk => { type => SCALAR, default => 0 }, nacltask_verify_only_bsd => { type => SCALAR, default => 0 }, $pkg_or_obj->_cleanup_validate_spec(), }, allow_extra => 1 ); my (%opts_for_cleanup, $opts_for_register, $register_for_cleanup); $pkg_or_obj->_move_common_cleanup_opts( source => \%opts, target => \%opts_for_cleanup, nacltask_to_cleanup => \$register_for_cleanup, ); if ($register_for_cleanup) { $opts_for_register = $pkg_or_obj->_common_modify_for_cleanup(%opts_for_cleanup, %opts); } my $verify_sk = delete $opts{nacltask_verify_only_sk}; my $verify_bsd = delete $opts{nacltask_verify_only_bsd}; $pkg_or_obj->_call_method_then_verify(%opts); # Checking the lif creation on the BSD if ($verify_bsd) { $pkg_or_obj->taskverify_modify( %opts, 'shell_type' => 'systemshell' ); } # Checking the lif creation on the SK if ($verify_sk) { $pkg_or_obj->taskverify_modify( %opts, 'shell_type' => 'nodescope' ); } #Register for cleanup if $nacltask_to_cleanup is set $pkg_or_obj->_register_for_cleanup(%{$opts_for_register}) if ($register_for_cleanup); $Log->exit() if $may_exit; } ## end sub modify =head2 migrate NACL::STask::NetworkInterface->migrate( command_interface => $command_interface, lif => $lif, vserver => $vserver, 'dest-node' => $node, 'dest-port' => $dest_port, %other_options ); or $intreface_obj->migrate(); (Class or Instance Method) This method migrates an interface element to a different port. If interface migration is unsuccessful, it will revert the interface to it's home node and port. Supports only CMode CLI and ZAPI. =over =item Options =over =item C<< nacltask_verify => 1 >> (Optional) - would verify that the interface is successfully migrated at the end, if set to 1 - by default it is set to 0 =item All of the other various options are described in L<< NACL::C::NetworkInterface->migrate|lib-NACL-C-NetworkInterface-pm/migrate >> =back =item Exceptions =over =item C This type of exception is thrown when there is a failure in verifying network interface migration. =back =back =cut sub migrate { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { nacltask_verify => { type => SCALAR, default => 0 } }, allow_extra => 1 ); my $pkg = ref($pkg_or_obj) || $pkg_or_obj; my $verify = delete $opts{nacltask_verify}; my %verify_opts; $pkg->_hash_copy( source => \%opts, target => \%verify_opts, copy => [ keys %{ $pkg->_common_validate_spec() }, $pkg->get_primary_keys() ] ); if ($verify) { my $state_obj = NACL::CS::NetworkInterface->fetch( command_interface => $opts{command_interface}, filter => { lif => $opts{lif} , vserver => $opts{vserver} }, requested_fields => [ 'home-node', 'home-port' ] ); $verify_opts{'home-port'} = $state_obj->home_port(); $verify_opts{'home-node'} = $state_obj->home_node(); } ## end if ($verify) # Migrate the interface $pkg->SUPER::migrate(%opts); Tharn::snooze 5; # Check current node and current port after interface migration. if ($verify) { $verify_opts{'curr-node'} = delete $opts{'dest-node'}; my $curr_port = delete $opts{'dest-port'}; $verify_opts{'curr-port'} = $curr_port if ( defined $curr_port ); $pkg->taskverify_migrate(%verify_opts); } ## end if ($verify) $Log->exit() if $may_exit; } ## end sub migrate sub taskverify_migrate { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, allow_extra => 1 ); my $pkg = ref($pkg_or_obj) || $pkg_or_obj; my %common_params; $pkg->_hash_copy( source => \%opts, target => \%common_params, copy => [ keys %{ $pkg->_common_validate_spec() }, $pkg->get_primary_keys(), ] ); # Verify interface attributes after migration try { $pkg->verify_state(%opts); } catch NACL::Exceptions::VerifyFailure with { my $e = shift; $Log->debug("Migration failed. Reverting back"); $pkg->revert(%common_params); $Log->exit() if $may_exit; $e->throw(); }; $Log->exit() if $may_exit; } ## end sub taskverify_migrate =head2 purge NACL::STask::NetworkInterface->purge( command_interface => $command_interface, lif => $lif, vserver => $vserver_name, nacltask_verify => 1, # default is 0 nacltask_if_purged => $action, # default is 'die' %other_options ); or $interface_obj->purge(); (Class or instance method) This method deletes network interface on the given vserver. Supports only CMode CLI and ZAPI. =over =item Options =over =item C<< _was_deleted => \$scalar >> (Optional) When this option is provided a reference to a scalar variable, the variable gets filled in with a boolean value describing whether the lif did not exist (value will be 0; this scenario is possible when nacltask_if_purged => "pass") or whether the lif was deleted (value will be 1). =item C<< nacltask_if_purged => die|pass >> (Optional) - If 'die', exception is thrown when interface to be deleted does not exist. Default id 'die'. - If 'pass', exits quietly when interface to be deleted does not exist =item C<< nacltask_verify => 1 >> (Optional) - would verify that the interface is purged at the end, if set to 1 - by default it is set to 0 =item C<< nacltask_verify_only_sk=>$nacltask_verify_only_sk >> (Optional) If '0' (default), verification will not be performed. If '1', verification will be performed to ensure that the interface/ip removed successfully in sk. =item C<< nacltask_verify_only_bsd=>$nacltask_verify_only_bsd >> (Optional) If '0' (default), verification will not be performed. If '1', verification will be performed to ensure that the interface/ip removed successfully in sk. =item C The options accepted for MCC configuration replication verification is documented at L. =back =item Exceptions =over =item C This type of exception is thrown when an attempt is made to delete an interface that does not exist. =item C This type of exception is thrown when there is any failure in verifying network interface deletion in ngsh, sk and bsd. =back =back =cut sub purge { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my $pkg = ref($pkg_or_obj) || $pkg_or_obj; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { nacltask_verify => { type => SCALAR, default => 0 }, nacltask_verify_only_sk => { type => SCALAR, default => 0 }, nacltask_verify_only_bsd => { type => SCALAR, default => 0 }, _was_deleted => { type => SCALARREF, optional => 1 }, nacltask_if_purged => $pkg_or_obj->_if_action_completed_already_validate_spec(), $pkg_or_obj->_mcc_validate_spec(), }, allow_extra => 1 ); my $command_interface = $opts{command_interface}; my $if_purged = delete $opts{nacltask_if_purged}; my $verify = delete $opts{nacltask_verify}; my $verify_bsd = delete $opts{nacltask_verify_only_bsd}; my $verify_sk = delete $opts{nacltask_verify_only_sk}; my $was_deleted = delete $opts{_was_deleted}; $$was_deleted = 0; my %mcc_opts; $pkg_or_obj->_move_common_mcc_opts( source => \%opts, target => \%mcc_opts, ); # Call the component delete. If interface doesn't exist # then it would throw an exeception which is handled below. my %lif_details; if ( ($verify_bsd) || ($verify_sk) ) { %lif_details = $pkg->_fetch_lif_details(%opts); } my $retry_count = 5; AGAIN: { try { $pkg->delete(%opts); $$was_deleted = 1; } catch NACL::C::Exceptions::NetworkInterface::LifEnabled with { my $e = $_[0]; $pkg->modify( 'command_interface' => $command_interface, 'vserver' => $opts{'vserver'} || $opts{'server'}, 'lif' => $opts{'lif'}, 'status-admin' => 'down' ); $retry_count--; if ( $retry_count > 0 ) { goto AGAIN; } else { $Log->warn("Unable to delete lif. Probably hit burt1064539"); $e->throw(); } } catch NACL::C::Exceptions::NetworkInterface::DoesNotExist with { my $e = $_[0]; if ( $if_purged =~ m[^die$]i ) { $Log->exit() if $may_exit; $e->throw(); } }; } my %primary_keys; $pkg_or_obj->_copy_primary_keys(source => \%opts, target => \%primary_keys); $pkg_or_obj->_check_in_metrocluster_node( command_interface => $opts{'command_interface'}, method_used => "purge", %mcc_opts, %primary_keys, ); if ($verify) { $pkg->taskverify_purge(%opts); } # Checking the lif creation on the BSD if ($verify_bsd) { $pkg->taskverify_purge( %opts, %lif_details, 'shell_type' => "systemshell" ); } # Checking the lif creation on the SK if ($verify_sk) { $pkg->taskverify_purge( %opts, %lif_details, 'shell_type' => "nodescope" ); } $Log->exit() if $may_exit; } ## end sub purge =head2 taskverify_purge $task_obj->taskverify_purge(); or NACL::STask::NetworkInterface->taskverify_purge( command_interface => $node, lif => $lif, shell_type => $shell_type ); (Class or Instance method) This function takes lif-name as input. It will fetch vserver name , routing group , curr-port, curr-node and lif-address for this particular lif. As the lif is expected to have been deleted using purge, there should not be a matching one using the given parameters,in the systemshell/nodescope. Based on that VerifyFailure exception will be thrown along with the appropriate error message. =over =item Options =over =item C<< command_interface >> (Required) As NACL::C::CommandInterface::ONTAP. A component object that represents the host to which to send commands. =item C<< lif >> (Required) Name of the lif. =item C<< shell_type >> (Optional, Default value is ngsh) Type of the shell. The possible values are 'systemshell' , 'nodescope' and 'ngsh' =item apiset_must, apiset_should, method-timeout, other options supported by fetch method in Ifconfig.pm. See L<< NACL::CS::Ifconfig->fetch|lib-NACL-CS-Ifconfig-pm/fetch >> =back =back =cut sub taskverify_purge { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my $pkg = ref($pkg_or_obj) || $pkg_or_obj; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { shell_type => { regex => qr/^(systemshell|nodescope|ngsh)$/, default => 'ngsh' } }, allow_extra => 1 ); $pkg->_verify_purge(%opts); } ## end sub taskverify_purge =head2 taskverify_create $task_obj->taskverify_create(); or NACL::STask::NetworkInterface->taskverify_create( command_interface => $node, lif => $lif ); (Class or Instance method) This function takes lif-name as input. It will fetch curr-node ,curr-port , vserver and routing-group and lif-address .It checks the exact match for these parameters value from the ifconfig fetch method (using ifconfig ) on the systemshell/nodescope. Based on that, VerifyFailure exception will be thrown along with the appropriate error message. In systemshell, interface will be like _ . =over =item Options =over =item C<< command_interface >> (Required) As NACL::C::CommandInterface::ONTAP. A component object that represents the host to which to send commands. =item C<< lif >> (Required) Name of the lif. =item C<< shell_type >> (Optional, Default value is nodescope) Type of the shell. The possible values are 'systemshell' and 'nodescope' =item apiset_must, apiset_should, method-timeout, all the other options supported by ,fetch method in Ifconfig.pm See L<< NACL::CS::Ifconfig->fetch|lib-NACL-CS-Ifconfig-pm/fetch >> =back =back =cut sub taskverify_create { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my $pkg = ref($pkg_or_obj) || $pkg_or_obj; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { shell_type => { regex => qr/^(systemshell|nodescope|ngsh)$/, default => 'ngsh' } }, allow_extra => 1 ); if ( $opts{'shell_type'} !~ /ngsh/ ) { $pkg_or_obj->_verify_create_or_modify(%opts); } else { delete $opts{'shell_type'}; $pkg->_generic_create_modify_verify(%opts); } } ## end sub taskverify_create =head2 taskverify_modify $task_obj->taskverify_modify(); or NACL::STask::NetworkInterface->taskverify_modify( command_interface => $node, lif => $lif, 'status-admin' => $status, address => $address ); (Class or Instance method) This function takes lif-name as input. It will fetch curr-node ,curr-port , vserver and routing-group and lif-address .It checks the exact match for these parameters value from ifconfig fetch method (using ifconfig ) on the systemshell/nodescope. Based on that, VerifyFailure exception will be thrown along with the appropriate error message . In systemshell, interface will be like _ . =over =item Options =over =item C<< command_interface >> (Required) As NACL::C::CommandInterface::ONTAP. A component object that represents the host to which to send commands. =item C<< lif >> (Required) Name of the lif. =item C<< address >> (Optional) Address of the lif. =item C<< status-admin >> (Optional) Status of the lif . It can be either up or down. =item C<< shell_type >> (Optional, Default value is nodescope) Type of the shell. The possible values are 'systemshell' and 'nodescope' =item apiset_must, apiset_should, method-timeout, all the other options supported by , fetch method in Ifconfig.pm See L<< NACL::CS::Ifconfig->fetch|lib-NACL-CS-Ifconfig-pm/fetch >> =back =back =cut sub taskverify_modify { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my $pkg = ref($pkg_or_obj) || $pkg_or_obj; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { shell_type => { regex => qr/^(systemshell|nodescope|ngsh)$/, default => 'ngsh' } }, allow_extra => 1 ); if ( $opts{'shell_type'} !~ /ngsh/ ) { $pkg_or_obj->_verify_create_or_modify(%opts); } else { delete $opts{'shell_type'}; $pkg->_generic_create_modify_verify(%opts); } } ## end sub taskverify_modify =head2 set_iscsi_lif #Method call NACL::STask::NetworkInterface->set_iscsi_lif( command_interface => $ci, vserver => $vs, lif => $lif, role => $role, no_convert_existing_lif => 0, nacltask_verify => 1, %lif_create_options, ); (Class method) Creates new iSCSI lif for the vserver. If user provides the required arguments, creates with user provided values. If not, searches for a free resources such as data-ip,data-port,ipaddress,netmask,etc and create with the available free resources. If not available, converts an existing non-iscsi lif. Returns hash reference for the created lif on success else returns NATE::BaseException if error occured during creation. =over =item Options =over =item C<< command_interface=>$command_interface >> (Required, isa NACL::C::CommandInterface) The command interface of the host on which lif needs to be created. =item C<< vserver=>$vserver >> (Required) The vserver on which lif should be created. =item C<< lif=>$lif_name >> (Required) Name of logical interface to be created. =item C<< role=>$role >> (Optional) Role of the logical interface that is created.(Default:data). =item C<< "home-node"=>$homenode >> (Optional) Name of the node in which the lif needs to be created. If not passed, the library searches for the node from the information present in the command interface. =item C<< "home-port"=>$homeport >> (Optional) Name of the port in which the lif needs to be created. If not passed, the library searches for free ports in the particular host. If there are no free ports available, and if no_convert_existing_lif is set 0, an existing lif's home-port is used to create it. If no_convert_existing_lif is set 1, then the method is exited with an error mentioning that lif creation has failed since no free ipaddresses available for creating. =item C<< address=>$address >> (Optional) Ipaddress that will be assigned to this lif. If not passed, the library searches for free ipaddresses from the nate host file of the particular host(eg:DATA_IP1,DATA_IP2). =item C<< netmask=>$netmask >> (Optional) Netmask for the ipaddress. If not passed, the library searches for netmask from the nate host file of the particular host(eg:DATA_MASK1,DATA_MASK2). =item C<< "data-protocol"=>[$data_protocol] >> (Optional) data protocol that will be supported by this interface.(Default:iscsi). =item C<< nacltask_no_convert_existing_lif=>1|0 >> (Optional) If the user does not provide any arguments & iscsi lif needs to be created by converting the existing lif, value "0" needs to be passed. If the user does not want to convert the existing non-iscsi lif, but want to create the iscsi lif out of free data ip, value "1" needs to be provided. (Default:0). =item C<< nacltask_verify=>1|0 >> (Optional) This option is used to verify if the Lif which is created, is present and operational in the host.(Default:1). =item C<< nacltask_if_lif_exists=>$action >> (Optional) specifies an action to be taken if a lif exists with the same name as passed in the 'lif' option. $action - if set to "die"(default), a NATE::BaseException type exception is raised. - if set to "reuse", no action is performed, the existing lif is used. - if set to "purge", deleting the existing lif is deleted, and the lif is recreated with the options passed. =item apiset_must, apiset_should, method-timeout, and all other options supported by , create method in NetworkInterface.pm, see also L<< NACL::C::NetworkInterface->create|lib-NACL-C-NetworkInterface-pm/create >> =back =item Exceptions =over =item C This type of exception is thrown when the address field is undefined and there are no free ip addresses to fetch from the nate host file of the particular host, OR when the address field is undefined and there are no lif to convert then an exception of this type will be thrown. =back =back =cut sub set_iscsi_lif { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { command_interface => { isa => 'NACL::C::CommandInterface::ONTAP' }, vserver => { type => SCALAR }, lif => { type => SCALAR, optional => 1 }, role => { type => SCALAR, optional => 1 }, "home-node" => { type => SCALAR, optional => 1 }, "home-port" => { type => SCALAR, optional => 1 }, address => { type => SCALAR, optional => 1 }, netmask => { type => SCALAR, optional => 1 }, "netmask-length" => { type => SCALAR, optional => 1 }, "data-protocol" => { type => ARRAYREF, optional => 1 }, nacltask_no_convert_existing_lif => { type => SCALAR, default => 0 }, nacltask_verify => { type => SCALAR, default => 1 }, }, allow_extra => 1 ); my ( %args, %return, %deleted_lif_opts, %common_opts, %delete_lif ); my ( $deleted_lif, $nacltask_verify, $lif_found, $no_convert_existing_lif, $delete_lif ); my $command_interface = delete $opts{command_interface}; $pkg->_move_common_component_params( source => \%opts, target => \%common_opts ); $no_convert_existing_lif = delete $opts{nacltask_no_convert_existing_lif}; $nacltask_verify = delete $opts{nacltask_verify}; $args{command_interface} = $command_interface; $args{vserver} = $opts{vserver}; if ( defined $opts{"netmask-length"} ) { $args{"netmask-length"} = $opts{"netmask-length"}; } my @protocol = ("iscsi"); if ( defined $opts{"data-protocol"} ) { $args{"data-protocol"} = \@{ $opts{"data-protocol"} }; } else { $args{"data-protocol"} = \@protocol; } my @reqd_params = ( 'lif', 'role', 'address', 'netmask', 'home-port', 'home-node', 'data-protocol', 'vserver', 'netmask-length' ); #Find the lif's present in the host. my @lif_states = NACL::CS::NetworkInterface->fetch( command_interface => $command_interface, ); #Check whether user has provided ip address and port #If provided, create the iscsi lif with user provided values if ( ( defined $opts{address} ) && ( defined $opts{'home-port'} ) ) { $args{netmask} = defined $opts{netmask} ? delete $opts{netmask} : $command_interface->hostrec->{DATA_MASK1}; $args{"home-node"} = defined $opts{"home-node"} ? delete $opts{"home-node"} : $command_interface->{node}; $args{address} = $opts{address}; $args{'home-port'} = $opts{'home-port'}; } else { #user did not provide both the ip and home-port #Search all the free data_ips which are not used for any other lif #DATA_IP values would be provided in nate_host file of the cluster nodes. my $no_of_entries = 0; FREE_DATAIP_SEARCH_THIS_NODE: while (1) { $no_of_entries++; my $data_ip_tmp = $command_interface->hostrec->{ 'DATA_IP' . $no_of_entries }; if ( ( !defined $data_ip_tmp ) or ( $data_ip_tmp eq q{} ) ) { last FREE_DATAIP_SEARCH_THIS_NODE; } my $data_ip_tmp_inuse = 0; foreach my $lif_temp (@lif_states) { if ( $lif_temp->address() eq $data_ip_tmp ) { $data_ip_tmp_inuse = 1; } } if ( !$data_ip_tmp_inuse ) { $args{'address'} = $opts{'address'} ? $opts{'address'} : $command_interface->hostrec->{ 'DATA_IP' . $no_of_entries }; $args{'netmask'} = $opts{'netmask'} ? $opts{'netmask'} : $command_interface->hostrec->{ 'DATA_MASK' . $no_of_entries }; $args{'home-port'} = $opts{'home-port'} ? $opts{'home-port'} : $command_interface->hostrec->{ 'DATA_PORT' . $no_of_entries }; $args{'home-node'} = $opts{'home-node'} ? $opts{'home-node'} : $command_interface->{node}; last FREE_DATAIP_SEARCH_THIS_NODE; } } } if ( !$args{address} ) { if ($no_convert_existing_lif) { NATE::BaseException->throw("No free data-ips are available"); } else { foreach my $lif (@lif_states) { if ( $lif->role() =~ /data/ && $lif->data_protocol() !~ /iscsi/ ) { AGAIN: { try { $pkg->delete( command_interface => $command_interface, lif => $lif->lif(), vserver => $lif->vserver(), ); $deleted_lif = $lif; } catch NACL::C::Exceptions::NetworkInterface::LifEnabled with { $pkg->modify( 'command_interface' => $command_interface, 'vserver' => $lif->vserver(), 'lif' => $lif->lif(), 'status-admin' => 'down' ); goto AGAIN; }; } foreach my $param (@reqd_params) { if ( !defined $args{$param} ) { my $field = $param; $field =~ s/-/_/; $args{$param} = $lif->$field(); } } last; } } } } if ( !$args{'address'} ) { NATE::BaseException->throw( "There are no free data ips, nor lifs to convert"); } #set the remaining attribute values required to create the iscsi lif #create an unique lif name $args{'lif'} = defined $opts{'lif'} ? $opts{'lif'} : $command_interface->node() . time . '_data'; $args{'role'} = $args{'role'} ? $args{'role'} : 'data'; $args{'data-protocol'} = ['iscsi']; $args{nacltask_if_exists} = $opts{nacltask_if_lif_exists} if defined $opts{nacltask_if_lif_exists}; $Log->debug( "The arguments passed to lif creation" . Dumper(%args) ); #Using the stask for creating interfaces. $pkg->create( %args, %common_opts ); if ($nacltask_verify) { try { $lif_found = $pkg->find( command_interface => $command_interface, filter => { server => $args{vserver}, lif => $args{lif}, } ); } catch NACL::Exceptions::NoElementsFound with { NATE::BaseException->throw( "Lif creation failed as entry does not exists"); }; } if ($lif_found) { $Log->debug("Lif creation is verified"); } my $obj = $pkg->new( command_interface => $command_interface, vserver => $args{vserver}, lif => $args{lif}, ); if ($deleted_lif) { $pkg->_hash_copy( source => \%$deleted_lif, target => \%delete_lif, copy => [qw (lif vserver address netmask role)], map => { home_node => "home-node", home_port => "home-port", data_protocol => "data-protocol", netmask_length => "netmask-length", }, ); $obj->deleted_lif(%delete_lif); } $Log->exit() if $may_exit; return $obj; } ## end sub set_iscsi_lif =head2 delete_iscsi_lif #Method call NACL::STask::Vserver->delete_iscsi_lif( command_interface => $ci, vserver => $vserver, lif => $lif ); (or) $Vserverobj->delete_iscsi_lif(); (Class or instance method) This method is used to delete the lif which created in the set_iscsi_lif method or to delete the lif which passed during the class call. This method will delete a lif of protocol type as iscsi. It is also concerned with the recreation of the existing lif which was used to delete in set_iscsi_lif method(if nacltask_no_convert_existing_lif=>0 & a lif was deleted in set_iscsi_lif()). =over =item Options =over =item C<< vserver=>$vserver >> (Optional) The vserver on which lif should be deleted. If the vserver =item C<< lif=>$lif_name >> (Optional) Name of logical interface to be deleted. =item C<< nacltask_verify=>1|0 >> (Optional) This option is used to verify if the Lif which is deleted, is absent.(Default:1). =item apiset_must, apiset_should, method-timeout, and all other options supported by, delete method in NetworkInterface.pm, see also L<< NACL::C::NetworkInterface->delete|lib-NACL-C-NetworkInterface-pm/delete >> =back =item Exceptions =over =item C This type of exception is thrown when there is any failure in verifying network interface deletion. =back =back =cut sub delete_iscsi_lif { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { vserver => { type => SCALAR }, lif => { type => SCALAR }, nacltask_verify => { type => SCALAR, default => 1, }, }, allow_extra => 1, ); my $lif_found; my $command_interface = $opts{command_interface}; my $nacltask_verify = delete $opts{nacltask_verify}; #Deleting the iscsi lif $pkg_or_obj->purge( command_interface => $command_interface, lif => $opts{lif}, vserver => $opts{vserver}, ); if ($nacltask_verify) { try { $lif_found = NACL::STask::NetworkInterface->find( command_interface => $command_interface, filter => { server => $opts{vserver}, lif => $opts{lif}, } ); } catch NACL::Exceptions::NoElementsFound with { $Log->debug("Lif deletion is done successfully"); }; } if ($lif_found) { NATE::BaseException->throw("Lif deletion failed"); } #Recreating the original lif if it was deleted in set_iscsi_lif my %recreate_lif = $pkg_or_obj->deleted_lif(); if (%recreate_lif) { NACL::STask::NetworkInterface->create( command_interface => $command_interface, %recreate_lif, ); } $Log->exit() if $may_exit; } ## end sub delete_iscsi_lif =head2 rename NACL::STask::NetworkInterface->rename( command_interface => $ci, lif => $lif_name, vserver => $vserver_name, newname => $new_name, nacltask_verify => 1, #default '0' nacltask_to_cleanup => 1, #default '0' nacltask_cleanup_manager => $cleanupobj polling_interval => $polling_interval, #default 20, ); or $interface_object->rename(...); (Class or instance method) This method renames an interface element. And also provides an mechanism of cleanup for the objects that is renamed by this method. =over =item Options =over =item C<< nacltask_verify => 0|1 >> (Optional, defaults to 0) Verify that the created network interface has all of the attributes set to the values specified in the call. Note that it is also possible to perform the verification later by calling L. This can used for cases where ONTAP chooses to not set all attributes to the exact values specified. In the call we can specify those attributes we want to verify. =item C<< nacltask_to_cleanup => 0|1 >> (Optional, default to 0(not to cleanup)) Flag indicating if this operation to be cleaned up or not. =item C<< nacltask_cleanup_manager >> Cleanup manager to use for registering Default : will use the default cleanup manager. =item C<< polling_interval=>$polling_interval >> (Optional) This is the interval (in seconds) after which the value of the attributes will be polled. If not specified it defaults to 20 seconds. =item C<< method-timeout=>$timeout >> (Optional) The timeout value for the wait_on_attribute method which waits until the new value of the lif is changed . Default :: 60. See L for all the other options accepted by this method. =back =back =cut sub rename { $Log->enter() if $may_enter; my ( $pkg_or_obj, @args ) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { polling_interval => { type => SCALAR, default => 20 }, "method-timeout" => { type => SCALAR, default => 60 }, $pkg_or_obj->_cleanup_validate_spec(), newname => { type => SCALAR }, nacltask_verify => { type => SCALAR, default => 0 }, }, allow_extra => 1, ); my $command_interface = $opts{command_interface}; my $newname = $opts{newname}; my $nacltask_verify = delete $opts{nacltask_verify}; my $timeout = delete $opts{"method-timeout"}; my $polling_interval = delete $opts{polling_interval}; my ( %nacltask_options, $nacltask_to_cleanup, $updated_newname, %opts_for_cleanup ); $pkg_or_obj->_copy_common_opts_for_cleanup( 'source' => \%opts, 'target' => \%opts_for_cleanup, 'nacltask_to_cleanup' => \$nacltask_to_cleanup, 'to_cleanup' => 'rename' ); if ($nacltask_to_cleanup) { $opts{_updated_newname} = \$updated_newname; } $pkg_or_obj->SUPER::rename(%opts); #verifying the new-name my $end_time = time() + $timeout; FIND: { use warnings; try { my $ni_obj = $pkg_or_obj->find( command_interface => $command_interface, filter => { vserver => $opts{vserver}, lif => $newname, }, ); } catch NACL::Exceptions::NoElementsFound with { if ( time() > $end_time ) { $Log->exit() if $may_exit; NACL::Exceptions::Timeout->throw( "The rename has not happened within the TIMEOUT value = $end_time" ); } else { no warnings; Tharn::snooze($polling_interval); goto FIND; } } } if ($nacltask_to_cleanup) { # See burt 802513 for cluster lif naming behaviour # These extra steps are to overcome the naming issue $opts_for_cleanup{'new_opts'} = {newname => ${$opts{_updated_newname}}}; $opts_for_cleanup{'orig_opts'} = { 'newname' => $opts{'lif'} }; $opts_for_cleanup{'lif'} = ${$opts{_updated_newname}}; $pkg_or_obj->_register_for_cleanup(%opts_for_cleanup); } $Log->exit() if $may_exit; } #=============================================================================== # Name : _verify_create_or_modify # Description : Invoked from taskverify_create ,taskverify_modify. # This method use _fetch_lif_details() to get lif attributes like # curr-node , curr-port, vserver, lif-address, routing-group etc . # It will look for an exact match for these parameters with the # values from the apiset ifconfig on the nodescope/systemshell # It will throw VerifyFailue Exception if it dint find any match . #=============================================================================== sub _verify_create_or_modify { $Log->enter() if $may_enter; my ( $pkg, %opts ) = @_; my ( $err_msg, %apiset_option, $interface, @ip_address, $address_family, $status, @flag, %lif_details ); my $found = 0; my $shell_type = delete $opts{shell_type}; if ( $shell_type eq 'nodescope' ) { %lif_details = $pkg->_fetch_lif_details(%opts); $interface = $lif_details{'curr-port'}; $err_msg = "Verification failed in Nodescope!! lif IP " . "$lif_details{address} is not mirrored in SK"; %apiset_option = ( apiset_must => { set => 'Nodescope' } ); } elsif ( $shell_type eq 'systemshell' ) { my $apiset_obj = $opts{command_interface}->apiset( category => "Node", interface => "CLI", set => "CMode", ); my $response = $apiset_obj->debug_smdb_table_show( table => "vserver_id_to_name", name => $opts{vserver}); my $output = $response->get_parsed_output(); $interface = @$output[0]->{id}; $err_msg = "Verification failed in SystemShell! lif IP " . "$lif_details{address} is not mirrored in BSD"; %apiset_option = ( apiset_must => { set => 'Systemshell' } ); } try { my $ifconfig_cs; if ( $shell_type eq 'systemshell' ) { my $ifconfig_cmd = "vcontext -v $interface /sbin/ifconfig " . "-a"; my $apiset = $opts{command_interface}->get_systemshell_apiset(); my $raw_output = $apiset->execute_command(command => $ifconfig_cmd); my $response = NACL::APISet::Response::CLI::Freebsd->new( output => $raw_output, command => $ifconfig_cmd, parser_file => 'NACL/APISet/Host/CLI/Freebsd/default/ifconfig.pm', cdef_path=> 'NACL/APISet/Host/CLI/Freebsd/default/ifconfig.cdef', method_name => 'ifconfig', execution_time => 1 , actual_command => $ifconfig_cmd, execution_status => 0, ); $ifconfig_cs = $response->get_parsed_output(); my $cs; #Remove the below check for FuseN mode once FuseN is enabled by default in Harpoon. my $cmd = "is_fusen_mode"; my $result = $apiset->execute_raw_command(command => $cmd); my $fusen_mode = 0; $fusen_mode = 1 if ($result =~ /enabled/i); $Log->comment("Fusen_mode: " . $fusen_mode); if ($fusen_mode) { %lif_details = $pkg->_fetch_lif_details(%opts); $interface = $lif_details{'curr-port'}; } for ( @$ifconfig_cs ) { my $cs_obj = $_; $cs = $cs_obj if ( $cs_obj->{'name'} eq $interface ); last if ( defined $cs); } $Log->error("Unable to find interface $interface in ifconfig output") if (!defined $cs); $ifconfig_cs = $cs if (defined $cs); bless $ifconfig_cs , 'NACL::CS::Ifconfig'; $ifconfig_cs->{'flags'} = [ $ifconfig_cs->{'flags'} ]; }else { $ifconfig_cs = NACL::CS::Ifconfig->fetch( command_interface => $opts{command_interface}, filter => { interface => $interface }, %apiset_option ); } if ( Net::IP::ip_is_ipv6( $lif_details{address} ) ) { @ip_address = $ifconfig_cs->ipv6(); } else { @ip_address = $ifconfig_cs->ipv4(); } @flag = $ifconfig_cs->flags(); if ( $shell_type eq 'systemshell' ) { $status = 1 if ( $flag[0] =~ /UP/ ); if ($status) { foreach (@ip_address) { $found = 1 if ( $_->{address} eq $opts{address} ); } } else { $err_msg = "Verification failed in SystemShell!" . "Port is not UP in BSD"; } } else { $status = 1 if ( $flag[0]->{UP} == 1 ); if ($status) { foreach (@ip_address) { $found = 1 if ( $_ eq $lif_details{address} ); } } else { $err_msg = "Verification failed in Nodescope! " . "Port is not UP in SK"; } } if ( defined $opts{'status-admin'} && $opts{'status-admin'} =~ /down/i ) { $found = 1; } } catch NATE::BaseException with { my $e = shift; my $error_text1 = "does not exist"; my $error_text2 = "no such interface"; if (($e->isa("NACL::APISet::Exceptions::CommandFailedException") && ( $e->text() =~ m[$error_text1]i || $e->text() !~ m[$error_text2]i ) ) || ($e->isa("NACL::APISet::Exceptions::InvalidParamValueException") && ( $e->text() =~ m[$error_text1]i || $e->text() !~ m[$error_text2]i ) ) ) { # If status admin is down , then interface will not be present . # This is what expected . So setting the flag to 1 so that error # should not be thrown if ( defined $opts{'status-admin'} && $opts{'status-admin'} =~ /down/i ) { $found = 1; } else { $Log->exit() if $may_exit; $e->throw(); } } else { $Log->exit() if $may_exit; $e->throw(); } }; $Log->exit() if $may_exit; require NACL::Exceptions::VerifyFailure; NACL::Exceptions::VerifyFailure->throw($err_msg) if ( !$found ); } ## end sub _verify_create_or_modify #=============================================================================== # Name : _verify_purge # Description : Invoked from taskverify_purge_systemshell , # taskverify_purge_nodescope .This method use the details obtained # from _fetch_lif_details() . It will look for an exact match for these # parameters with the values from the apiset ifconfig on the # nodescope/systemshell. # It will throw VerifyFailue Exception if it find any match . #=============================================================================== sub _verify_purge { $Log->enter() if $may_enter; my ( $pkg, %lif_details ) = @_; my ($address_family, $err_msg, $found, $interface, %apiset_option, @ip_address ); my $deleted = 0; my $shell_type = delete $lif_details{shell_type}; if ( $shell_type eq 'nodescope' ) { $interface = $lif_details{'curr-port'}; $err_msg = "Verification failed !! Lif $lif_details{address}" . " exists in nodescope"; %apiset_option = ( apiset_must => { set => 'Nodescope' } ); } elsif ( $shell_type eq 'systemshell' ) { $interface = $lif_details{vserver} . "_" . $lif_details{'routing-group'}; %apiset_option = ( apiset_must => { set => 'Systemshell' } ); $err_msg = "Verification failed !! Lif $lif_details{address}" . " exists in systemshell"; } try { my $ifconfig_cs = NACL::CS::Ifconfig->fetch( command_interface => $lif_details{command_interface}, filter => { interface => $interface }, %apiset_option ); if ( Net::IP::ip_is_ipv6( $lif_details{address} ) ) { @ip_address = $ifconfig_cs->ipv6(); } else { @ip_address = $ifconfig_cs->ipv4(); } if ( $shell_type eq 'nodescope' ) { foreach (@ip_address) { if ( $_ eq $lif_details{address} ) { $found = 1; last; } } $deleted = 1 if ( !$found ); } elsif ( $shell_type eq 'systemshell' ) { foreach (@ip_address) { if ( $_->{address} ne $lif_details{address} ) { $deleted = 1; last; } } $deleted = 1 if ( !$found ); } else { $pkg->_generic_purge_verify(%lif_details); $deleted = 1; } } catch NATE::BaseException with { my $e = shift; my $error_text1 = "does not exist"; my $error_text2 = "no such initerface"; if ($e->isa("NACL::APISet::Exceptions::CommandFailedException") && ( $e->text() =~ m[$error_text1]i || $e->text() !~ m[$error_text2]i ) ) { $deleted = 1; } else { $Log->exit() if $may_exit; $e->throw(); } }; $Log->exit() if $may_exit; require NACL::Exceptions::VerifyFailure; NACL::Exceptions::VerifyFailure->throw($err_msg) if ( !$deleted ); } ## end sub _verify_purge #=============================================================================== # Name : _fetch_lif_details # Description : Invoked from task_verify_nodescope , task_verify_systemshell and # purge function. This method will fetch lif attributes like # curr-node , curr-port, vserver, lif-ip, routing-group etc. # It will return reference to hash which contain all these values #=============================================================================== sub _fetch_lif_details { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my $pkg = ref($pkg_or_obj) || $pkg_or_obj; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, allow_extra => 1 ); my $CS_pkg = $pkg->get_CS_package_name(); my $lif_obj = $CS_pkg->fetch( command_interface => $opts{command_interface}, filter => { lif => $opts{lif} }, requested_fields => [qw(curr-node curr-port vserver address routing-group)] ); my $curr_node = $lif_obj->curr_node(); my $curr_port = $lif_obj->curr_port(); my $address = $lif_obj->address(); my $routing_group = $lif_obj->routing_group(); my $vserver = $lif_obj->vserver(); my $command_interface = NACL::C::Node->new( node => $curr_node ); my %details = ( command_interface => $command_interface, 'curr-port' => $curr_port, 'curr-node' => $curr_node, address => $address, vserver => $vserver, 'routing-group' => $routing_group, ); return %details; } ## end sub _fetch_lif_details sub _remove_invalid_verify_state_opts { $Log->enter() if $may_enter; my ($pkg,%opts) = @_; my @invalid_fields = qw(force-subnet-association); foreach my $invalid_field (@invalid_fields) { delete $opts{$invalid_field}; } $Log->exit() if $may_exit; return %opts; } =head2 parents @parents = $net_int_obj->parents(); ( Class or Instance method ) This method returns the list containing the names of the parent objects of this resource. Used for finding dependencies between resources. =cut sub parents { $Log->enter() if $may_enter; $Log->exit() if $may_exit; return qw(NACL::STask::Vserver); } sub _get_unused_hostrec_ip_address { $Log->enter() if $may_enter; my ($pkg,%opts) = @_; my ( @hostrec_address, @used_v4_lif_ips, @used_v6_lif_ips, @available_ips ); my @ipspaces = NACL::CS::NetworkIpspace->fetch( command_interface => $opts{command_interface}, ); if ($#ipspaces >= 2) { NATE::BaseException->throw("Could not determine the ip address " . "automatically as system has multiple ipspaces configured." . "This feature is expected to work in a system with ONTAP created " . "IPSpaces when the node comes up i.e Default and Cluster."); } my @lifs = NACL::CS::NetworkInterface->fetch( command_interface => $opts{command_interface}, filter => { role => $opts{role},}, requested_fields => ['address'], allow_empty => 1, ); foreach my $lif (@lifs) { if ( Net::IP::ip_is_ipv4($lif->address() )) { push ( @used_v4_lif_ips, $lif->address() ); } else { push ( @used_v6_lif_ips, $lif->address() ); } } my %hostrec = %{ $opts{command_interface}->hostrec() }; my $role = $opts{role}; $role =~ s/-/_/; if ( (defined $opts{netmask} && Net::IP::ip_is_ipv4($opts{netmask})) || $opts{'netmask-length'} != 64 ) { my $regex = $role . "_IP" ; push (@hostrec_address, $hostrec{$_}) for grep /$regex/i, keys %hostrec; @available_ips = array_minus(@hostrec_address, @used_v4_lif_ips); } else { my @ipv6_address = @ { $hostrec{net} }; foreach my $ipv6 (@ipv6_address) { if ($ipv6->{nettype} eq $opts{role}) { push (@hostrec_address, $ipv6->{address}); } } @available_ips = array_minus(@hostrec_address, @used_v6_lif_ips); } # get the un-used ip by cross checking the lif with hostrec ips if ($available_ips[0]) { $Log->exit() if $may_exit; return $available_ips[0]; } else { $Log->exit() if $may_exit; NATE::BaseException->throw("No free ips are available"); } } 1;