# # Copyright (c) 2011-2016 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. ## @summary Vserver Task Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here package NACL::STask::Vserver; use strict; use warnings; use feature 'state'; use base qw(NACL::C::Vserver 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 NACL::ComponentUtils qw(_optional_scalars Dumper); use NACL::GeneralUtils qw(arrange_by_create_time); use Params::Validate qw(validate :types); use NACL::Exceptions::NoElementsFound; use NACL::Exceptions::VerifyFailure (); use NACL::Exceptions::UnexpectedState (); use NATE::Exceptions::Argument (); use NACL::STask::Exceptions::NoAggregatesWithSufficientSpaceForVolume (); use NACL::C::Exceptions::Volume::DeleteSnapMirrorRelationship (); use NACL::C::Exceptions::Snapmirror::VolumeIsOffline (); use NACL::STask::Exceptions::VserverConfigDiffsPresent (); use NACL::STask::Exceptions::MCCQueuesNotDrained (); use NACL::C::Exceptions::Vserver::AlreadyExists (); use NACL::C::Exceptions::Vserver::AlreadyStopped (); use NACL::C::Exceptions::Vserver::VolumesExist; use NACL::C::Exceptions::Vserver::InfiniteVolumesExist; use NACL::C::Exceptions::Vserver::IgroupsExist; use NACL::C::Exceptions::Vserver::CifsShareExists; use NACL::C::Exceptions::Vserver::TpgroupsExist; use NACL::C::Exceptions::Vserver::IscsiIsnsExist; use NACL::C::Exceptions::Vserver::IscsiSecurityExist; use NACL::C::Exceptions::Vserver::PortsetsExist; use NACL::C::Exceptions::Vserver::DoesNotExist; use NACL::C::Exceptions::Vserver::PeerRelationshipExist; use NACL::C::Exceptions::Volume::CannotDeleteRootVolume; use NACL::C::Exceptions::Volume::HasClones; use NACL::C::Exceptions::Vserver::MirroredVolumesExist; use NACL::C::Exceptions::Vserver::CannotDeleteDPVserver; use NATE::BaseException qw(:try); use Scalar::Util qw(blessed); use NATE::Time qw(timeout2time); use NACL::STask::Volume; use NACL::STask::Aggregate; use NACL::STask::Snapmirror; use NACL::STask::VserverPeer; use NACL::STask::Node; use NACL::STask::VserverAudit; use NACL::C::VserverCifs; use NACL::C::VserverIscsi; use NACL::STask::LunIgroup; use NACL::CS::VserverNfs; use NACL::CS::Snapmirror; use NACL::CS::LunIgroup; use NACL::CS::Vserver; use NACL::CS::VserverCifs; use NACL::CS::VserverIscsi; use NACL::C::VserverIscsiTpgroup; use NACL::C::VserverIscsiIsns; use NACL::C::VserverIscsiSecurity; use NACL::C::VolumeClone; use NACL::CS::ClusterPeerHealth; use NACL::CS::VserverPeer; use NACL::CS::NetworkInterface; use Class::MethodMaker [ 'scalar' => [ { -type => 'NACL::C::Job' }, 'job_component', ] ]; use constant VSERVER_TIMEOUT => 600; use constant POLL_DELTA => 20; =head1 NAME NACL::STask::Vserver =head1 DESCRIPTION C provides a number of well-defined but potentially complex or multi-step methods related to Vserver 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 vserver-related commands. See C for details. This also means that a C object may generally be used in place of a component object. =head1 ATTRIBUTES =head2 command_interface (Required) A component object that represents the host to which to send commands. =head2 vserver (Required) The name of the Vserver to be created. =head2 job_component (Optional) A job component (NACL::C::Job) representing the last job that operated on this vserver. At the moment this will only get filled in for the job used to create this vserver (if this task object was used to create this vserver) =head1 METHODS =head2 create my $vserver_obj = NACL::STask::Vserver->create( command_interface => $command_interface, vserver => $vserver_name, nacltask_if_exists => $action, # default 'die' nacltask_wait => $boolean, # default '1' nacltask_to_cleanup => 1, #default '0' %other_options ); (Class Method) Create a vserver. This method provides additional services beyond what's inherent to the product's vserver creation commands, as controlled and described by the "nacltask_if_exists" and "nacltask_wait" options. Note that the arguments C, C, C, C and C are optional for this method. Refer to the section "Arguments defaulted/auto-determined" below for a description of the values auto-assigned to these arguments if they are not supplied by the caller. With these defaults, it is now possible to create a vserver by just doing: my $vs_obj = NACL::STask::Vserver->create(command_interface => $node); =over =item Options =over =item C<< nacltask_if_exists=>$action >> (Optional) What to do if the vserver to be created already exists. If $action is "die" (the default), then fail with an exception (in the same way that the vserver component would have: by trying the vserver create and letting the product complain about the vserver already existing). If action is "reuse", then do nothing (return a task object referring to the existing vserver). If action is "purge", then delete the vserver (see the C method, below) before creating a new one. =item C<< nacltask_wait => 0|1 >> (Optional) If 1 (the default), wait for this vserver to be created (see "wait_for_creation" method, below). If 0, do not wait any longer than necessary (any longer than the vserver creation command waits). =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 vservser was found (value will be 0; this scenario is possible when nacltask_if_exists => "reuse") or whether the vserver was created (value will be 1). This is necessary to determine whether the vserver needs to be cleaned up later. my $was_created; my $vserver_obj = NACL::STask::Vserver->create( nacltask_if_exists => 'reuse', _was_created => \$was_created, %other_opts ); # Operate on $vserver_obj here # ... # Now determine whether to clean up the vserver, since we're not sure # whether we reused an existing vserver or created a new one if ($was_created) { # New vserver was created. Clean it up. $vserver_obj->purge(); } =item C<< "method-timeout" => 600 >> (Optional) As component method-timeout, controls how long the command will wait before completing. However, the default timeout has been raised to 600 seconds. =item C<< polling_interval => $interval >> (Optional) If nacltask_wait is set to 1, this is the interval at which to show the progress of the job (if we're watching the progress of the job) or the interval at which to poll the filer for the value of the "admin-state" field. =item C<< nacltask_cifs_username=>$username >> (Optional) Username for cifs server (Required if cifs server is configured and create() is called with nacltask_if_exists equal to 'purge' ) =item C<< nacltask_cifs_password=>$password >> (Optional) Password for cifs server (Required if cifs server is configured and create() is called with nacltask_if_exists equal to 'purge') =item Other options All of the other various options described in L<< NACL::C::Vserver->create|lib-NACL-C-Vserver-pm/create >> are supported by NACL::STask::Vserver->create as well. For ui-change ns-switch-ui ns-switch and nm-switch options should not be for creation of the vserver.For ns-switch and nm-switch creation use NACL::C::VserverServicesNameServiceNsSwitch->create(). =back =back =over =item Arguments defaulted/auto-determined =over =item C Note that if this is not specified, a random name is generated using L. (See C for how to customize arguments to random_name_generator()) =item C The rootvolume of the vserver. If this is not provided then it the method generates the name as _root. (So, if the vserver name is "abc", then the rootvolume will be "abc_root") Note that if C is specified as "dp-destination", then this is not computed (because dp-destination vservers cannot be created with a root volume) =item C If not provided, an aggregate is searched for which can hold the rootvolume of this vserver. The aggregate must be online, not be a striped aggregate, must not be the root aggregate and must have at least 20MB available space. If no such aggregates can be found, then a L is thrown. Note that if C is specified as "dp-destination", then this is not computed (because aggregate is inapplicable for dp-destination vservers) =item C The Name Service Switch. If not provided, this is defaulted to [ 'file' ] (a single-element array-reference with 'file' as the only value) Above is applicable for the builds not containing the ns-switch ui change. Note that if C is specified as "dp-destination", then this is not computed (because aggregate is inapplicable for dp-destination vservers) =item C The rootvolume security style. This is defaulted to "mixed" if not provided. Note that if C is specified as "dp-destination", then this is not computed (because dp-destination vservers cannot be created with a root volume) =item C The options accepted for MCC configuration replication verification is documented at L. =item C<< nacltask_to_cleanup => 0|1 >> (Optional, defaults to 0(no to cleanup)) Flag indicating if this operation is to be cleaned up or not. =item C<< nacltask_cleanup_manager >> Cleanup manager to be used for registering. Default : Will use the default cleanup manager. =back =back =over =item Exceptions =over =item L If C is not provided, and no aggregates are found with sufficient space to host the volume are present, then this exception is thrown. =item Other exceptions See L<< NACL::C::Vserver->create|lib-NACL-C-Vserver-pm/create >> for all other exceptions that this method could throw. =back =back =cut sub create { $Log->enter() if $may_enter; my ( $pkg, @args ) = @_; my %opts = $pkg->_common_validate_with( params => \@args, additional_spec => { nacltask_if_exists => $pkg->_if_exists_validate_spec(), nacltask_wait => { type => SCALAR, default => 1 }, _was_created => { type => SCALARREF, optional => 1 }, polling_interval => { type => SCALAR, default => POLL_DELTA }, nacltask_rng_args => $pkg->_rng_args_validate_spec(), # "vserver" is auto-generated if not provided, so make it # optional here _optional_scalars( qw(nacltask_cifs_username nacltask_cifs_password vserver)), $pkg->_cleanup_validate_spec(), $pkg->_mcc_validate_spec(), }, allow_extra => 1, ); # Transform %opts from the options we received into the options to # pass to the base class method. my $command_interface = $opts{command_interface}; my $was_created = delete $opts{_was_created}; my $rng_args = delete $opts{nacltask_rng_args}; my (%task_opts,%mcc_opts); $pkg->_move_common_mcc_opts( source => \%opts, target => \%mcc_opts, ); my ( %opts_for_cleanup, %opts_for_register, $nacltask_to_cleanup ); $pkg->_move_common_cleanup_opts( source => \%opts, target => \%opts_for_cleanup, ); $pkg->_move_nacltask_options( source => \%opts, target => \%task_opts, ); my $nacltask_if_exists = delete $task_opts{nacltask_if_exists}; my $wait = delete $task_opts{nacltask_wait}; $opts{'method-timeout'} ||= VSERVER_TIMEOUT; $opts{polling_interval} ||= POLL_DELTA; my $is_dp; if (defined $opts{subtype} && $opts{subtype} =~ /dp[_\-]destination/) { $is_dp = 1; } my %common_opts; $pkg->_copy_common_component_params( source => \%opts, target => \%common_opts ); my $random_vserver_name_generated; if (!defined $opts{vserver}) { $random_vserver_name_generated = 1; $opts{vserver} = $pkg->random_name_generator(%$rng_args); } my $rootvol_generated; # A dp-destination vserver cannot be created with the rootvolume # specified. Hence, "rootvolume", "rootvolume-security-style" and # "aggregate" are all inapplicable. Also, "ns-switch" is not # applicable for a dp-destination vserver. if (!$is_dp) { if (!defined $opts{rootvolume}) { $rootvol_generated = 1; $opts{rootvolume} = $opts{vserver} . '_root'; $opts{rootvolume} =~ s/\-/_/g; } if (!defined $opts{'rootvolume-security-style'} ) { $opts{'rootvolume-security-style'} = 'mixed'; } if (!defined $opts{aggregate}) { $opts{aggregate} = NACL::STask::Volume->find_suitable_aggregates_for_volume( %common_opts, command_interface => $command_interface, vsroot => 'true', ); } ## end if ( !defined $opts{aggregate...}) } # cifs username and password are applicable only for purge my %purge_opts; $pkg->_hash_move( source => \%task_opts, target => \%purge_opts, move => [qw(nacltask_cifs_username nacltask_cifs_password)] ); $$was_created = 0; my %wait_for_creation_opts = %common_opts; $pkg->_hash_move( source => \%opts, target => \%wait_for_creation_opts, move => [qw(polling_interval)] ); # We want to know the job ID of the job performed by # SUPER::create, whether or not our caller does. my $dummy; my $self; my $purge_create; $opts{job_component} ||= \$dummy; CREATE: { use warnings qw(exiting); try { $self = $pkg->SUPER::create(%opts); $$was_created = 1; $self->job_component( ${ $opts{job_component} } ); $opts{subtype} = "default" if (!$opts{subtype}); $self->wait_for_creation(%wait_for_creation_opts, subtype => $opts{subtype}) if $wait; } ## end try catch NACL::C::Exceptions::Vserver::AlreadyExists with { my $exception = shift; my $text = $exception->text(); if ($random_vserver_name_generated) { $Log->trace( 'Random name collision, regenerate a ' . 'new name and retry' ); $opts{vserver} = $pkg->random_name_generator(%$rng_args); if ($rootvol_generated) { $opts{rootvolume} = $opts{vserver} . '_root'; $opts{rootvolume} =~ s/\-/_/g; } no warnings qw(exiting); redo CREATE; } else { $Log->trace('The vserver already exists'); $self = $pkg->_element_exists_handler( create_opts => \%opts, nacltask_if_exists => $nacltask_if_exists, exception => $exception, purge_opts => \%purge_opts ); if ( !$self ) { no warnings qw(exiting); redo CREATE; } } ## end else [ if ($random_vserver_name_generated)] }; } ## end CREATE: # Register 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' ); $pkg->_register_for_cleanup(%opts_for_register) if ( $nacltask_to_cleanup && $$was_created ); $self->_check_in_metrocluster_node( command_interface => $opts{command_interface}, method_used => "create", %mcc_opts, ); $Log->exit() if $may_exit; return $self; } ## end sub create =head2 purge $vserver->purge(%other_options); NACL::STask::Vserver->purge( command_interface => $command_interface, vserver => $vserver, %other_options ); (Class or instance method) This method deletes the vserver, as well as detaching or deleting other features that may be preventing this vserver from being deleted. The vserver should not be of type 'admin' or 'cluster'. =over =item Options =over =item C<< command_interface=>$command_interface >> (Required for class method, Not Applicable for instance method) See L =item C<< apiset_must=>$ruleset >> (Optional)See L =item C<< apiset_should=>$ruleset >> (Optional)See L =item C<< vserver=>$vserver >> (Required for class method, Not Applicable for instance method) Name of vserver. =item C<< nacltask_cifs_username=>$username >> (Optional) Username for cifs server (Required if cifs server is configured) =item C<< nacltask_cifs_password=>$password >> (Optional) Password for cifs server (Required if cifs server is configured) =item C<< nacltask_verify => $nacltask_verify_boolean >> (Optional) If '0' (default), verification will not be performed. If '1', verification will be performed to ensure that the deletion did happen successfully. =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 vserver did not exist (value will be 0; this scenario is possible when nacltask_if_purged => "pass") or whether the vserver was deleted (value will be 1). =item C<< nacltask_if_purged => die | pass >> (Optional, defaults to "die") Specifies what to do if the vserver we're trying to purge no longer exists. A value of C (the default) will result in an exception being thrown. A value of C will mean that the exception that would normally occur when we're trying to delete a vserver that does not exist is suppressed. =item C The options accepted for MCC configuration replication verification is documented at L. If passed it will be of NACL::C::Node type of object. =back =back =over =item Exceptions =over =item C This type of exception is thrown when an attempt is made to delete a vserver that does not exists. =item C This type of exception is thrown when verification fails for the deleted vserver. =item C This exception is raised when an attempt is made to delete a vserver which has one or more DP volumes configured on it. Probably the vserver will be part of a destination in a Vserver DR configuration. =item C This exception is raised when the vserver that is about to be deleted is a destination vserver in a Vserver DR configuration. =back =back =cut sub purge { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; my $pkg_name = $pkg_or_obj->get_package_name(); state $additional_spec = { nacltask_if_purged => $pkg_or_obj->_if_action_completed_already_validate_spec(), nacltask_verify => { type => BOOLEAN, default => 0 }, $pkg_or_obj->_mcc_validate_spec(), _optional_scalars( qw(nacltask_cifs_username nacltask_cifs_password)), _was_deleted => { type => SCALARREF, optional => 1 }, }; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => $additional_spec, ); my $nacltask_if_purged = delete $opts{nacltask_if_purged}; my $nacltask_verify = delete $opts{nacltask_verify}; my $nacltask_cifs_username = delete $opts{nacltask_cifs_username}; my $nacltask_cifs_password = delete $opts{nacltask_cifs_password}; my $need_to_return; my %common_opts; $pkg_or_obj->_copy_common_component_params( source => \%opts, target => \%common_opts ); my %mcc_opts; $pkg_or_obj->_move_common_mcc_opts( source => \%opts, target => \%mcc_opts, ); my $was_deleted = delete $opts{_was_deleted}; $$was_deleted = 0; PURGE: { use warnings; my $known_error = 0; my $job; try { $pkg_name->delete( "method-timeout" => VSERVER_TIMEOUT, job_component => \$job, %opts, ); $$was_deleted = 1; if ($job) { $Log->trace("Wait on the job ID"); $job->wait_on_job(%common_opts); } } ## end try catch NACL::C::Exceptions::Vserver::VolumesExist with { $known_error = $pkg_or_obj->_delete_vserver_volume( common_opts => \%common_opts, %opts, ); } ## end with catch NACL::C::Exceptions::Vserver::InfiniteVolumesExist with { $Log->trace('Delete the infinite volumes from the vserver'); # Delete the infinite volume $pkg_name->_delete_volumes( %opts, 'is-constituent' => "false" ); $known_error = 1; } ## end with catch NACL::C::Exceptions::Vserver::IgroupsExist with { $Log->trace('Delete the igroups from the vserver'); $pkg_name->_delete_igroup(%opts); $known_error = 1; } catch NACL::C::Exceptions::Vserver::CifsShareExists with { # Delete the vserver cifs in order to remove the vserver purge # dependency on cifs $Log->trace( 'Delete the vserver cifs in order to remove the vserver'); $pkg_name->_delete_cifs_share( %opts, nacltask_cifs_username => $nacltask_cifs_username, nacltask_cifs_password => $nacltask_cifs_password ); $known_error = 1; } ## end with catch NACL::C::Exceptions::Vserver::PortsetsExist with { $Log->trace('Delete the portsets from the vserver'); $pkg_name->_delete_portsets(%opts); $known_error = 1; } catch NACL::C::Exceptions::Vserver::PeerRelationshipExist with { $Log->trace('Delete the vserver peer relationship'); $pkg_name->_delete_vserver_peer(%opts); $known_error = 1; } catch NACL::C::Exceptions::Vserver::TpgroupsExist with { $Log->trace('Delete the Tpgroups'); $pkg_name->_delete_tpgroups(%opts); $known_error = 1; } catch NACL::C::Exceptions::Vserver::IscsiIsnsExist with { $Log->trace('Delete the iSCSI iSNS'); $pkg_name->_delete_iscsi_isns(%opts); $known_error = 1; } catch NACL::C::Exceptions::Vserver::IscsiSecurityExist with { $Log->trace('Delete the iSCSI security settings'); $pkg_name->_delete_iscsi_security(%opts); $known_error = 1; } catch NACL::C::Exceptions::Vserver::DoesNotExist with { my $exception = shift; if ( $nacltask_if_purged eq 'die' ) { $Log->trace( 'Check if the command failed because the vserver does not exists' ); $Log->exit() if $may_exit; $exception->throw(); } else { $need_to_return = 1; } } catch NACL::C::Exceptions::Vserver::MirroredVolumesExist with { $known_error = $pkg_or_obj->_delete_vserver_volume( common_opts => \%common_opts, %opts, ); } catch NACL::C::Exceptions::Vserver::CannotDeleteDPVserver with { $Log->trace('Source vserver of the Vserver DR configuration ". "needs to be deleted first'); my $cluster = NACL::CS::ClusterPeerHealth->fetch( command_interface => $opts{command_interface}, requested_fields => [ qw( destination-node ) ], %common_opts, ); my $dest_vserver = NACL::CS::VserverPeer->fetch( command_interface => $opts{command_interface}, filter => { vserver => $opts{vserver}, }, requested_fields => [ qw( peer-vserver ) ], %common_opts, ); my $dest_node = NACL::C::Node->new( node => $cluster->destination_node() ); my $pkg = ref($pkg_or_obj) || $pkg_or_obj; $pkg->purge( command_interface => $dest_node, vserver => $dest_vserver->peer_vserver(), nacltask_verify => 1, %common_opts, ); $known_error = 1; } catch NACL::C::Exceptions::Vserver::LifEnabled with { $Log->trace('Disable all the associated LIF\'s'); $pkg_name->_disable_associated_lifs(%opts); $known_error = 1; } catch NACL::APISet::Exceptions::ResponseException with { my $exception = shift; my $resp = $exception->get_response_object; if( $resp->get_raw_output() =~ /Button.*DeleteButton.*is not enabled/ ) { # GUI deos not throw appropriate error unless the SVM is stopped $known_error = 1; # Stopping the vserver before re-trying delete $pkg_name->stop( "method-timeout" => VSERVER_TIMEOUT, %opts, ); } else { $exception->throw(); } }; ## end if ( $nacltask_if_purged...) # Used for when nacltask_if_purged => 'pass' and the vserver isn't found # In these cases we should return immediately, rather than try the # commands after it. if ($need_to_return) { $Log->exit() if $may_exit; return; } $Log->debug("known_error is $known_error"); if ($known_error) { no warnings; redo PURGE; } else { $Log->trace("Not able to delete the vserver"); } } ## end PURGE: $pkg_or_obj->_check_in_metrocluster_node( command_interface => $opts{command_interface}, method_used => "purge", vserver => $opts{vserver}, %mcc_opts, ); if ($nacltask_verify) { # Verify that the vserver was purged. This is done by checking that the # vserver does not exist $pkg_name->_generic_purge_verify(%opts); } ## end if ($nacltask_verify) $Log->exit() if $may_exit; } ## end sub purge =head2 wait_for_creation $vserver_obj->wait_for_creation( 'method-timeout' => $time_to_wait, polling_interval => $interval ); or NACL::STask::Vserver->wait_for_creation( command_interface => $ci, vserver => $vs, 'method-timeout' => $time_to_wait, polling_interval => $interval ); (Class or instance method)This method is used to wait for creation of the vserver. =over =item Options =over =item C<< vserver => $vs >> (Required for Class Method, Not Applicable for Instance Method) The Vserver name that has been created =item C<< polling_interval => $interval >> (Optional) This is the interval at which to show the progress of the job the interval at which to poll the filer for the value of the "admin-state" field. =item C<< subtype => $subtype >> (Optional) This indicate the type of vserver which can be default, cached, dp-destination, sync-source and sync-destination. =item C<< command_interface => $ci >> (Required for Class Method, Not Applicable for Instance Method) This is a component/task object that represents the host to which to send commands. See L. =item C<< "method-timeout"=>$timeout >> (Optional) How long in seconds to wait for admin-state to become running. Defaults to 600 secs. =item C<< apiset_must=>$ruleset >> (Optional)See L =item C<< apiset_should=>$ruleset >> (Optional)See L =back =back =cut sub wait_for_creation { $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 => POLL_DELTA }, 'method-timeout' => { type => SCALAR, default => VSERVER_TIMEOUT }, job_component => { type => OBJECT | UNDEF, optional => 1 }, subtype => { type => SCALAR, optional =>1 }, }, ); $opts{job_component} ||= $pkg_or_obj->job_component(); my ($subtype, $till_value, %common_params); if($opts{subtype}) { $subtype = delete $opts{subtype}; } else { $pkg_or_obj->_copy_common_component_params_with_ci( source => \%opts, target => \%common_params ); my $vs_obj = $pkg_or_obj->get_CS_package_name()->fetch( %common_params, filter => { 'vserver' => $opts{vserver} }, requested_fields => [qw(subtype)], ); $subtype = $vs_obj->subtype(); } if($subtype =~ /sync-destination/) { $till_value="stopped"; } else { $till_value="running"; } $pkg_or_obj->wait_for_completion( %opts, attribute_to_check => 'admin-state', till_value => $till_value, ); $Log->exit() if $may_exit; } ## end sub wait_for_creation ########## HELPER ROUTINES ##################################### #============================================================= # This helper method will purge the volume using Volume task #============================================================= sub _delete_volumes { $Log->enter() if $may_enter; my $pkg = shift; my %common_params; my %opts = $pkg->_common_validate_with( params => \@_, allow_extra => 1 ); my %actual_opts = %opts; $pkg->_copy_common_component_params( source => \%opts, target => \%common_params, ); my $filter = {}; $pkg->_hash_move( source => \%opts, target => $filter, move => [qw(vserver is-constituent)] ); my @vol_objs = NACL::CS::Volume->fetch( %opts, filter => $filter, requested_fields => [qw(vserver volume vsroot type)], ); my @undeleted_volumes; my $rootvol_instance; if (@vol_objs) { foreach my $vol_obj (@vol_objs) { if ($vol_obj->is_vsroot()) { # Store the root volume object $rootvol_instance = $vol_obj->get_task_instance(); } else { PURGE: { try { $vol_obj->get_task_instance()->purge(%common_params); } catch NACL::C::Exceptions::Volume::DeleteSnapMirrorRelationship with { no warnings; try { $pkg->_delete_snapmirror( %actual_opts, volume_obj => $vol_obj ); redo PURGE; } catch NACL::Exceptions::NoElementsFound with { # since volume purge offlines the volume # bring it back to online $vol_obj->get_task_instance()->online(); # undeleted volumes need to be deleted later. push @undeleted_volumes, $vol_obj->get_task_instance(); }; use warnings; }; } ## end PURGE: } } ## end foreach my $vol_obj (@vol_objs) } ## end if (@vol_objs) # delete undeleted volumes. foreach my $vol_obj (@undeleted_volumes) { $vol_obj->purge(%common_params); } # Delete the root volume in the end $rootvol_instance->purge(%common_params); $Log->exit() if $may_exit; } ## end sub _delete_volumes #============================================================= # This helper method will delete the volume clones #============================================================= sub _purge_volumes_and_clones { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, ignore_primary_keys => 1, additional_spec => { volume_obj => { isa => 'NACL::STask::Volume', type => OBJECT }, vserver => { type => SCALAR } } ); my $vol_obj = delete $opts{volume_obj}; my %opts_without_ci = %opts; delete $opts_without_ci{command_interface}; # By this point %opts has only the common options (with ci) PURGE: { try { # Need to send nacltask_if_purged as pass because the order in # which the volumes get deleted can get re-ordered dynamically # based on which volumes are clones and root-volumes $vol_obj->purge( %opts_without_ci, nacltask_if_purged => 'pass' ); } ## end try catch NACL::C::Exceptions::Volume::DeleteSnapMirrorRelationship with { no warnings; try { $pkg->_delete_snapmirror( %opts, volume_obj => $vol_obj ); redo PURGE; } catch NACL::Exceptions::NoElementsFound with { $vol_obj->online(); }; use warnings; } ## end with catch NACL::C::Exceptions::Volume::HasClones with { my $exception = shift; # Need to purge clones of a volume before the volume can be # purged my @vol_clones; my @clone_objs = NACL::C::VolumeClone->find( command_interface => $opts{command_interface}, filter => { 'parent-volume' => $vol_obj->volume(), vserver => $vol_obj->vserver() } ); foreach my $each_clone (@clone_objs) { my $clone_obj = NACL::STask::Volume->new( command_interface => $each_clone->command_interface(), volume => $each_clone->flexclone(), vserver => $each_clone->vserver() ); push @vol_clones, $clone_obj; } ## end foreach my $each_clone (@clone_objs) foreach my $vol_clone (@vol_clones) { $pkg->_purge_volumes_and_clones( %opts, volume_obj => $vol_clone, ); } # Now that all the clones have been purged, re-try deleting the # volume $vol_obj->delete(%opts_without_ci); } ## end with catch NACL::C::Exceptions::Volume::CannotDeleteRootVolume with { # Need to delete all other volumes of a vserver to be able to delete # its root volume. $pkg->get_package_name()->_delete_volumes(%opts); }; $Log->exit() if $may_exit; } ## end PURGE: } ## end sub _purge_volumes_and_clones #============================================================= # This helper method will delete the igroups #============================================================= sub _delete_igroup { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, allow_extra => 1, ); my $ci = $opts{command_interface}; my $vserver = $opts{vserver}; my (%common_opts); $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts, ); my @igroups = NACL::CS::LunIgroup->fetch( command_interface => $ci, filter => { vserver => $vserver, }, requested_fields => [qw(igroup)], %common_opts, ); foreach my $igrp (@igroups) { $igrp->get_task_instance()->purge(); } $Log->exit() if $may_exit; } ## end sub _delete_igroup #============================================================= # This helper method will delete the snapmirror relationships #============================================================= sub _delete_snapmirror { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, allow_extra => 1 ); my $vol_obj = delete $opts{volume_obj}; my $ci = delete $opts{command_interface}; my $vserver = delete $opts{vserver}; my $snap_state = NACL::CS::Snapmirror->fetch( command_interface => $ci, filter => { vserver => $vserver, 'destination-volume' => $vol_obj->volume() } ); PURGE: { no warnings; $vol_obj->modify(state => 'online') ; try { NACL::STask::Snapmirror->purge( command_interface => $ci, 'source-path' => $snap_state->source_path(), 'destination-path' => $snap_state->destination_path() ); } ## end try catch NACL::C::Exceptions::Snapmirror::VolumeIsOffline with { if($vol_obj->isa('NACL::STask::Volume')) { $vol_obj->online(); }else{ $vol_obj->get_task_instance()->online(); } redo PURGE; }; use warnings; } ## end PURGE: $Log->exit() if $may_exit; } ## end sub _delete_snapmirror #============================================================= # This helper method will delete the vserver peer relationship #============================================================= sub _delete_vserver_peer { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, ); my @vs_peers = NACL::STask::VserverPeer->find( command_interface => $opts{command_interface}, filter => {vserver => $opts{vserver}}, allow_empty => 1, ); map {$_->purge(nacltask_if_purged => 'pass')} @vs_peers; $Log->exit() if $may_exit; } ## end sub _delete_vserver_peer #============================================================= # This helper method will delete the portsets #============================================================= sub _delete_portsets { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, ); my %common_opts; $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); my @portsets = NACL::C::LunPortset->find( %common_opts, filter => { vserver => $opts{vserver}, }, allow_empty => 1 ); map {$_->delete()} @portsets ; $Log->exit() if $may_exit; } ## end sub _delete_portsets #============================================================= # This helper method will delete the tpgroups #============================================================= sub _delete_tpgroups { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, ); my %common_opts; $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); my @tpgroups = NACL::C::VserverIscsiTpgroup->find( %common_opts, filter => { vserver => $opts{vserver}, }, allow_empty => 1 ); map {$_->delete()} @tpgroups ; $Log->exit() if $may_exit; } ## end sub _delete_tpgroups #============================================================= # This helper method will delete the iSCSI iSNS #============================================================= sub _delete_iscsi_security { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, ); my $ci = $opts{command_interface}; my $vserver = $opts{vserver}; my (%common_opts); $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); my @securities = NACL::CS::VserverIscsiSecurity->fetch( command_interface => $ci, filter => { vserver => $vserver, }, requested_fields => [qw(initiator-name)], %common_opts, ); foreach my $security (@securities) { if ( $security->initiator_name() !~ /default/ ) { $security->get_component_instance->delete(); } } $Log->exit() if $may_exit; } ## end sub _delete_tpgroups #============================================================= # This helper method will disable all associated LIF's by # by modifying "status-admin" setting to "down" #============================================================= sub _disable_associated_lifs { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, ); my $ci = $opts{command_interface}; my $vserver = $opts{vserver}; my (%common_opts); $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); # unlock verver management configuration if # it is locked try { NACL::C::Vserver->unlock( command_interface => $ci, vserver => $vserver, force => "true", ); } otherwise { #Supress if already unlocked }; my @lifs = NACL::CS::NetworkInterface->fetch( command_interface => $ci, filter => { vserver => $vserver, }, requested_fields => [qw(status-admin)], %common_opts, ); foreach my $lif (@lifs) { if ( $lif->status_admin() !~ /down/i ) { $lif->get_component_instance->modify( 'status-admin' => 'down' ); } } $Log->exit() if $may_exit; } ## end sub _disable_associated_lifs #============================================================= # This helper method will delete the iSCSI iSNS #============================================================= sub _delete_iscsi_isns { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, ); NACL::C::VserverIscsiIsns->delete(%opts); $Log->exit() if $may_exit; } ## end sub _delete_tpgroups #============================================================= # This helper method will delete the cifs share #============================================================= sub _delete_cifs_share { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { nacltask_cifs_username => { type => SCALAR }, nacltask_cifs_password => { type => SCALAR }, }, allow_extra => 1, ); my $command_interface = $opts{command_interface}; my $vserver = $opts{vserver}; my $cifs_user = $opts{nacltask_cifs_username}; my $cifs_pswd = $opts{nacltask_cifs_password}; my %username_password; $pkg->_hash_move( source => \%opts, target => \%username_password, map => { nacltask_cifs_username => 'username', nacltask_cifs_password => 'password' } ); NACL::C::VserverCifs->delete( %opts, %username_password ); $Log->exit() if $may_exit; } ## end sub _delete_cifs_share =head2 stop #method call description NACL::STask::Vserver->stop( 'command_interface' => $Node, vserver => $Vserver, forground => $true, "method-timeout" => $timeout, nacltask_wait => $flag, ); (Class call) or $vserver->stop(); (Instance call) (Class or Instance method) This method is used to stop the particular vserver. After the operation the Vserver LIF's status & the associated allowed-protocols is checked . If the status is down/disallowed the Vserver stop operation is successfully done , else CommandFailed exception is thrown . =over =item Options =over =item C<< command_interface => $Node >> (Required) Specifies the interface of the host on which the TASK is performed . =item C<< vserver => $vserver >> (Required) Specifies the vserver name on which the vserver stop operation is performed . =item C<< foreground => $true >> (Optional) Specifies the foreground value of the vserver stop operation . Default :: true . =item C<< "method-timeout" => $timeout >> (Optional) Specifies the time in seconds to wait for vserver to stop . Default :: 30secs . =item C<< nacltask_if_started=>$action >> (Optional) What to do if the vserver to be stopped is already stopped. If $action is "die" (the default), then fail with an exception (in the same way that the vserver component would have: by trying the vserver stop and letting the product complain about the vserver already stopped). If action is "pass", then do nothing. =item C<< nacltask_wait => $flag >> (Optional) This options is used as a flag . If set to 1 , the method will wait until the method-timeout for the stop command to be completed , else (set to 0) it would skip the wait process . =item C<< polling_interval => $interval >> (Optional) If nacltask_wait is set to 1, this is the interval at which to poll the filer for the value of the "admin-state" field. =item C<< nacltask_verify_lif_status => $flag >> (Optional) This option is used to check whether the LIF's associated to the particular vserver are down .(1|0) Default :: 1 (Need to verify the LIF status). =item C<< nacltask_verify_allowed_protocols_status => $flag >> (Optional) This option is used to check whether the allowed protocols associated to the particular vserver are disabled .(1|0) Default :: 1 (Need to verify the allowed-protocols status). =item apiset_must, apiset_should, method-timeout, all the other options supported by , stop method in Vserver.pm See L<< NACL::C::Vserver->stop|lib-NACL-C-Vserver-pm/stop >> =back =back =over =item Exceptions =over =item C This type of exception is thrown when status of the vserver is not down or state of the allowed protocols are not down/false after stoping a particular vserver. =back =back =cut sub stop { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { foreground => { type => SCALAR, default => "true" }, "method-timeout" => { type => SCALAR, default => 600 }, polling_interval => { type => SCALAR, default => 20 }, nacltask_if_stopped => $pkg_or_obj->_if_action_completed_already_validate_spec(), nacltask_wait => { type => BOOLEAN, default => 1 }, nacltask_verify_lif_status => { type => BOOLEAN, default => 0 }, nacltask_verify_allowed_protocols_status => { type => BOOLEAN, default => 0 }, }, allow_extra => 1, ); my ( $nfs_state, $cifs_state, $iscsi_state ); #Extracting all the nacltask related options my $command_interface = $opts{command_interface}; my $nacltask_if_stopped = delete $opts{nacltask_if_stopped}; my $nacltask_wait = delete $opts{nacltask_wait}; my $nacltask_verify_lif_status = delete $opts{nacltask_verify_lif_status}; my $nacltask_verify_allowed_protocols_status = delete $opts{nacltask_verify_allowed_protocols_status}; my ( %wait_for_stop_opts, %common_opts ); $pkg_or_obj->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); %wait_for_stop_opts = %common_opts; $pkg_or_obj->_hash_move( source => \%opts, target => \%wait_for_stop_opts, move => [qw(polling_interval)] ); try { #Performing Vserver stop operation $pkg_or_obj->SUPER::stop(%opts); if ($nacltask_wait) { $pkg_or_obj->wait_for_stopped( vserver => $opts{vserver}, %wait_for_stop_opts, ); } ## end if ($nacltask_wait) } catch NACL::C::Exceptions::Vserver::AlreadyStopped with { my $exception = shift; if ( $nacltask_if_stopped eq 'die' ) { $Log->exit() if $may_exit; $exception->throw(); } }; #Verifying the status of the LIF's associated with the vserver if ($nacltask_verify_lif_status) { $pkg_or_obj->taskverify_if_lif_down( command_interface => $opts{command_interface}, vserver => $opts{vserver}, %common_opts ); } ## end if ($nacltask_verify_lif_status) #Verifying the status of the allowed-protocols of the vserver if ($nacltask_verify_allowed_protocols_status) { $pkg_or_obj->verify_all_protocols_down( command_interface => $command_interface, vserver => $opts{vserver}, %common_opts, ); } ## end if ($nacltask_verify_allowed_protocols_status) $Log->exit() if $may_exit; } ## end sub stop =head2 start #method call description NACL::STask::Vserver->start( 'command_interface' => $Node, vserver => $Vserver, forground => $true, "method-timeout" => $timeout, nacltask_wait => $flag, nacltask_to_cleanup => 1, ); (Class call) or $vserver->start(); (Instance call) (Class or Instance method) This method is used to start a particular vserver . After the operation the Vserver LIF's status & the associated allowed-protocols is checked . If the status is up/allowed the Vserver start operation is successfully done , else CommandFailed exception is thrown . =over =item Options =over =item C<< command_interface => $Node >> (Required) Specifies the interface of the host on which the TASK is performed . =item C<< vserver => $vserver >> (Required) Specifies the vserver name on which the vserver start operation is performed . =item C<< foreground => $true >> (Optional) Specifies the foreground value of the vserver start operation . Default :: true . =item C<< "method-timeout" => $timeout >> (Optional) Specifies the time in seconds to wait for the vserver to be in running state. Default :: 30secs . =item C<< nacltask_wait => $flag >> (Optional) This options is used as a flag . If set to 1 , the method will wait until the method-timeout for the start command to be completed , else (set to 0) it would skip the wait process . =item C<< polling_interval => $interval >> (Optional) If nacltask_wait is set to 1, this is the interval at which to poll the filer for the value of the "admin-state" field. =item C<< nacltask_if_started=>$action >> (Optional) What to do if the vserver to be started is already started. If $action is "die" (the default), then fail with an exception (in the same way that the vserver component would have: by trying the vserver start and letting the product complain about the vserver already started). If action is "pass", then do nothing. =item C<< nacltask_verify_lif_status => $flag >> (Optional) This option is used to check whether the LIF's associated to the particular vserver are up .(1|0) Default :: 1 (Need to verify the LIF status). =item C<< nacltask_verify_allowed_protocols_status => $flag >> (Optional) This option is used to check whether the allowed protocols associated to the particular vserver are enabled .(1|0) Default :: 1 (Need to verify the allowed-protocols status). =item C<< nacltask_to_cleanup => 0|1 >> (Optional, defaults to 0(no to cleanup)) Flag indicating if this operation is to be cleaned up or not. =item C<< nacltask_cleanup_manager >> Cleanup manager to be used for registering. Default : Will use the default cleanup manager. =item apiset_must, apiset_should, method-timeout, all the other options supported by , start method in Vserver.pm See L<< NACL::C::Vserver->start|lib-NACL-C-Vserver-pm/start >> =back =back =over =item Exceptions =over =item C This type of exception is thrown when status of the vserver is not up or state of the allowed protocols are not up/true after starting a particular vserver. =back =back =cut sub start { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { foreground => { type => SCALAR, default => "true" }, "method-timeout" => { type => SCALAR, default => 600 }, polling_interval => { type => SCALAR, default => 20 }, nacltask_if_started => $pkg_or_obj->_if_action_completed_already_validate_spec(), nacltask_wait => { type => BOOLEAN, default => 1 }, nacltask_verify_lif_status => { type => BOOLEAN, default => 0 }, nacltask_verify_allowed_protocols_status => { type => BOOLEAN, default => 0 }, $pkg_or_obj->_cleanup_validate_spec(), }, allow_extra => 1, ); my ( $nfs_state, $cifs_state, $iscsi_state ); #Extracting all the nacltask related options my $command_interface = $opts{command_interface}; my $nacltask_if_started = delete $opts{nacltask_if_started}; my $nacltask_wait = delete $opts{nacltask_wait}; my $nacltask_verify_lif_status = delete $opts{nacltask_verify_lif_status}; my $nacltask_verify_allowed_protocols_status = delete $opts{nacltask_verify_allowed_protocols_status}; my ( %opts_for_cleanup, %opts_for_register, $nacltask_to_cleanup ); $pkg_or_obj->_move_common_cleanup_opts( source => \%opts, target => \%opts_for_cleanup, ); my ( %wait_for_start_opts, %common_opts ); $pkg_or_obj->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); %wait_for_start_opts = %common_opts; $pkg_or_obj->_hash_move( source => \%opts, target => \%wait_for_start_opts, move => [qw(polling_interval)] ); try { #Performing Vserver start operation $pkg_or_obj->SUPER::start(%opts); if ($nacltask_wait) { $pkg_or_obj->wait_for_started( vserver => $opts{vserver}, %wait_for_start_opts, ); } ## end if ($nacltask_wait) } catch NACL::C::Exceptions::Vserver::AlreadyStarted with { my $exception = shift; if ( $nacltask_if_started eq 'die' ) { $Log->exit() if $may_exit; $exception->throw(); } }; #Verifying the status of the LIF's associated with the vserver if ($nacltask_verify_lif_status) { $pkg_or_obj->taskverify_if_lif_up( command_interface => $opts{command_interface}, vserver => $opts{vserver}, %common_opts ); } ## end if ($nacltask_verify_lif_status) #Verifying the status of the allowed-protocols of the vserver if ($nacltask_verify_allowed_protocols_status) { $pkg_or_obj->verify_all_protocols_up( command_interface => $command_interface, vserver => $opts{vserver}, %common_opts, ); } ## end if ($nacltask_verify_allowed_protocols_status) # Register this resource with the Cleanup Manager for cleanup $pkg_or_obj->_copy_common_opts_for_cleanup( 'source' => { %opts, %opts_for_cleanup }, 'target' => \%opts_for_register, 'nacltask_to_cleanup' => \$nacltask_to_cleanup, 'to_cleanup' => 'stop' ); $pkg_or_obj->_register_for_cleanup(%opts_for_register) if ($nacltask_to_cleanup); $Log->exit() if $may_exit; } ## end sub start =head2 modify NACL::STask::Vserver->modify( command_interface => $command_interface, vserver => $vserver, nacltask_to_cleanup => 1, %other_options ); or $vserver_obj->modify(%opts); (Class or Instance method) Modify vserver options. This method will modify the options present for the specified vserver. =over =item Options =over =item C<< nacltask_verify => 0|1 >> (Optional, defaults to 0) Verifies whether all of the attributes got set to the specified value. 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_to_cleanup => 0|1 >> (Optional, defaults to 0(no to cleanup)) Flag indicating if this operation is 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 other options See Lmodify|lib-NACL-C-Vserver-pm/modify> for all the other options accepted by this method. =back =back =cut sub modify { $Log->enter() if $may_enter; my ( $pkg_or_obj, @args ) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { $pkg_or_obj->_cleanup_validate_spec(), }, allow_extra => 1, ); my ( $opts_for_register, $register_for_cleanup, %opts_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 ); } $pkg_or_obj->_call_method_then_verify(%opts); # 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 rename NACL::STask::Vserver->rename( command_interface => $command_interface, vserver => $vserver, nacltask_to_cleanup => 1, %other_options ); or $vserver_obj->rename(%opts); (Class or Instance method) Rename vserver name. =over =item Options =over =item C<< nacltask_verify => 0|1 >> (Optional, defaults to 0) Verifies whether all of the attributes got set to the specified value. 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_to_cleanup => 0|1 >> (Optional, defaults to 0(no to cleanup)) Flag indicating if this operation is 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<< "method-timeout" => $timeout >> (Optional) Specifies the time in seconds to wait for the vserver to be in running state. Default :: 300secs . =item other options See Lrename|lib-NACL-C-Vserver-pm/rename> for all the other options accepted by this method. =back =back =over =item Exceptions =over =item C This type of exception is thrown when verification fails for the renamed vserver. =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 => { $pkg_or_obj->_cleanup_validate_spec(), newname => { type => SCALAR }, nacltask_verify => { type => SCALAR, default => 0 }, 'method-timeout' => { type => SCALAR, default => 300 }, }, allow_extra => 1 ); my $command_interface = $opts{command_interface}; my $newname = $opts{newname}; my $nacltask_verify = delete $opts{nacltask_verify}; my %common_opts; $pkg_or_obj->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); my ( %nacltask_options, $nacltask_to_cleanup, %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' ); $pkg_or_obj->SUPER::rename(%opts); #verifying the new-name if ($nacltask_verify) { my $vs_obj = NACL::STask::Vserver->find( command_interface => $command_interface, filter => { vserver => $newname }, %common_opts, allow_empty => 1, ); if ($vs_obj) { $Log->debug( "The vserver is renamed to " . $vs_obj->get_one_state_attribute('vserver') ); } else { NACL::Exceptions::VerifyFailure->throw( "The vserver is " . "not rename properly" ); } } if ($nacltask_to_cleanup) { $opts_for_cleanup{'new_opts'} = { 'newname' => $opts{'newname'} }; $opts_for_cleanup{'orig_opts'} = { 'newname' => $opts{'vserver'} }; $opts_for_cleanup{'vserver'} = $opts{'newname'}; $pkg_or_obj->_register_for_cleanup(%opts_for_cleanup); } $Log->exit() if $may_exit; } ## end sub rename #=============================================================================== # METHODS TO CHECK THE (NFS,CIFS,iSCSI) PROTOCOLS STATE during vserver start/stop #=============================================================================== sub verify_all_protocols_down { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my $expected_value_hash = { 'nfs' => 'false', 'cifs' => 'down', 'iscsi' => 'down', }; $pkg_or_obj->_verify_protocol_helper( @_, expected_hash => $expected_value_hash, ); $Log->exit() if $may_exit; } ## end sub verify_all_protocols_down sub verify_all_protocols_up { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my $expected_value_hash = { 'nfs' => 'true', 'cifs' => 'up', 'iscsi' => 'up', }; $pkg_or_obj->_verify_protocol_helper( @_, expected_hash => $expected_value_hash, ); $Log->exit() if $may_exit; } ## end sub verify_all_protocols_up #======EOF PROTOCOL CHECK METHODS=============================================== #=============================================================================== # METHOD : wait_for_stopped # Usage : To wait until the vserver has stopped #=============================================================================== sub wait_for_stopped { $Log->enter() if $may_enter; my $pkg_or_obj = shift; $pkg_or_obj->_wait_helper( @_, till_value => ['stopped'], ); $Log->exit() if $may_exit; } ## end sub wait_for_stopped #=============================================================================== # METHOD : wait_for_started # Usage : To wait until the vserver has started #=============================================================================== sub wait_for_started { $Log->enter() if $may_enter; my $pkg_or_obj = shift; $pkg_or_obj->_wait_helper( @_, till_value => ['running'], ); $Log->exit() if $may_exit; } ## end sub wait_for_started #=============================================================================== # METHOD : taskverify_if_lif_down # Usage : To verify whether the LIF is down after vserver stop #=============================================================================== sub taskverify_if_lif_down { $Log->enter() if $may_enter; my $pkg_or_obj = shift; $pkg_or_obj->_verify_helper( @_, expected_value => "down", ); $Log->exit() if $may_exit; } ## end sub taskverify_if_lif_down #=============================================================================== # METHOD : taskverify_if_lif_up # Usage : To verify whether the LIF is up after vserver start #=============================================================================== sub taskverify_if_lif_up { $Log->enter() if $may_enter; my $pkg_or_obj = shift; $pkg_or_obj->_verify_helper( @_, expected_value => "up", ); $Log->exit() if $may_exit; } ## end sub taskverify_if_lif_up ### SUPPORTIVE HELPER METHODS FOR wait & verification ########################## #=============================================================================== # _wait_helper #=============================================================================== sub _wait_helper { $Log->enter() if $may_enter; my $pkg_or_obj = shift; $pkg_or_obj->wait_on_attribute( @_, attribute_to_check => 'admin-state' ); $Log->exit() if $may_exit; } ## end sub _wait_helper #=============================================================================== # _verify_helper #=============================================================================== sub _verify_helper { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { expected_value => { type => SCALAR }, }, ); my $expected_value = delete $opts{expected_value}; my $vserver = delete $opts{vserver}; my @state_objs = NACL::CS::NetworkInterface->fetch( %opts, filter => { vserver => $vserver, 'status-oper' => "!$expected_value", }, requested_fields => [qw(status-oper lif)], allow_empty => 1, ); if (@state_objs) { my @lifs; foreach my $state_obj (@state_objs) { push @lifs, $state_obj->lif(); } my $lif_str = join ', ', @lifs; my $status_oper = $state_objs[0]->status_oper(); $Log->exit() if $may_exit; NACL::Exceptions::UnexpectedState->throw( "The operational status of the LIF is not $expected_value" . "for the lifs $lif_str", ); } ## end if (@state_objs) $Log->debug( "The operational status of the LIF's associated " . " with $vserver vserver is $expected_value as expected" ); $Log->exit() if $may_exit; } ## end sub _verify_helper #=============================================================================== # _verify_protocol_helper #=============================================================================== sub _verify_protocol_helper { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { expected_hash => { type => HASHREF }, }, ); my $vserver = delete $opts{vserver}; my $hash = delete $opts{expected_hash}; my %hash = %$hash; if ( exists $hash{'nfs'} ) { my $nfs_state = NACL::CS::VserverNfs->fetch( %opts, filter => { vserver => $vserver }, requested_fields => [qw (access)], allow_empty => 1, ); if ( defined($nfs_state) ) { if ( $nfs_state->access() =~ /$hash{nfs}/ ) { $Log->debug("NFS access state is $hash{nfs} as expected"); } else { $Log->exit() if $may_exit; NACL::Exceptions::UnexpectedState->throw( "NFS protocol access state is not $hash{nfs}", monitoring_fields => ['access'], 'access' => $nfs_state->access() ); } ## end else [ if ( $nfs_state->access...)] } ## end if ( defined($nfs_state...)) } ## end if ( exists $hash{'nfs'...}) if ( exists $hash{'cifs'} ) { my $cifs_state = NACL::CS::VserverCifs->fetch( %opts, filter => { vserver => $vserver }, requested_fields => [qw (status-admin)], allow_empty => 1, ); if ( defined($cifs_state) ) { if ( $cifs_state->status_admin() =~ /$hash{cifs}/ ) { $Log->debug("CIFS status admin is $hash{cifs} as expected"); } else { $Log->exit() if $may_exit; NACL::Exceptions::UnexpectedState->throw( "CIFS protocol access state is not $hash{cifs}", monitoring_fields => ['status-admin'], 'status-admin' => $cifs_state->status_admin() ); } ## end else [ if ( $cifs_state->status_admin...)] } ## end if ( defined($cifs_state...)) } ## end if ( exists $hash{'cifs'...}) if ( exists $hash{'iscsi'} ) { my $iscsi_state = NACL::CS::VserverIscsi->fetch( %opts, filter => { vserver => $vserver }, requested_fields => [qw (status-admin)], allow_empty => 1, ); if ( defined($iscsi_state) ) { if ( $iscsi_state->status_admin() =~ /$hash{iscsi}/ ) { $Log->debug("iSCSI status admin is $hash{iscsi} as expected"); } else { $Log->exit() if $may_exit; NACL::Exceptions::UnexpectedState->throw( "iSCSI protocol access state is not $hash{iscsi}", monitoring_fields => ['status-admin'], 'status-admin' => $iscsi_state->status_admin() ); } ## end else [ if ( $iscsi_state->status_admin...)] } ## end if ( defined($iscsi_state...)) } ## end if ( exists $hash{'iscsi'...}) $Log->exit() if $may_exit; } ## end sub _verify_protocol_helper ##### Adding specified aggr-list to the vserver ################################### =head2 add_to_aggr_list $pkg_or_obj->add_to_aggr_list( command_interface => $ci, 'aggr-list' => \@aggr-list, ); (Instance method)This method is used to add aggr-list to the vserver. =over =item Options =over =item C<< aggr-list => \@aggr-list >> (Required) This is an array reference which needs to be passed to add to the aggr-list of the vserver. =item C<< command_interface => $ci >> (Required) This is a component/task object that represents the host to which to send commands. See L. =item command_interface, apiset_must, apiset_should etc. =back =back =cut sub add_to_aggr_list { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; my %opts = $pkg_or_obj->_add_remove_aggr_list_validation(@args); my $aggregates = delete $opts{'aggr-list'}; my ( %common_opts, %hash ); $pkg_or_obj->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); my $vserver_obj = NACL::CS::Vserver->fetch( %common_opts, filter => { vserver => $opts{vserver} }, requested_fields => [qw(aggr-list)], ); my @aggr_list = $vserver_obj->aggr_list(); my @sorted_aggrs_to_add = sort @$aggregates; @aggr_list = grep(!/^\-$/, @aggr_list); my @sorted_vserver_aggrs = sort @aggr_list; my $sorted_aggrs_to_add_str = join 'X;X', @sorted_aggrs_to_add; my $sorted_vserver_aggrs_str = join 'X;X', @sorted_vserver_aggrs; if ( $sorted_aggrs_to_add_str ne $sorted_vserver_aggrs_str ) { $sorted_vserver_aggrs_str = $sorted_aggrs_to_add_str . "X;X" . $sorted_vserver_aggrs_str ; my @aggrs_list = split( /X;X/, $sorted_vserver_aggrs_str ); map $hash{$_} = 1, @aggrs_list; @aggrs_list = keys(%hash); $pkg_or_obj->modify( %opts, 'aggr-list' => \@aggrs_list, ); } ## end if ( $sorted_aggrs_to_add_str...) $Log->exit() if $may_exit; return; } ## end sub add_to_aggr_list ##### Removing specified aggr-list from the vserver ################################### =head2 remove_from_aggr_list $pkg_or_obj->remove_from_aggr_list( command_interface => $ci, 'aggr-list' => \@aggr-list, ); (Instance method)This method is used to remove aggr-list from the vserver. =over =item Options =over =item C<< aggr-list => \@aggr-list >> (Required) This is an array reference which needs to be passed to remove from the aggr-list of the vserver. =item C<< command_interface => $ci >> (Required) This is a component/task object that represents the host to which to send commands. See L. =item command_interface, apiset_must, apiset_should etc. =back =back =over =item Exceptions =over =item C This type of exception is thrown when no aggregates are passed to modify aggr-list. =back =back =cut sub remove_from_aggr_list { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; my %opts = $pkg_or_obj->_add_remove_aggr_list_validation(@args); my $aggregates = delete $opts{'aggr-list'}; my %common_opts; $pkg_or_obj->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); my $vserver_cs = NACL::CS::Vserver->fetch( %common_opts, filter => { vserver => $opts{vserver} }, requested_fields => [qw(aggr-list)] ); my @aggr_list = $vserver_cs->aggr_list(); my %aggr_list_hash; if ( $aggr_list[0] eq '-' ) { # '-' means no aggregates are added to aggrlist in Vserver $Log->exit() if $may_exit; return; } else { foreach my $aggr (@aggr_list) { $aggr_list_hash{$aggr} = 1; } } my $need_to_remove; foreach my $aggr_to_remove (@$aggregates) { if ( exists $aggr_list_hash{$aggr_to_remove} ) { delete $aggr_list_hash{$aggr_to_remove}; $need_to_remove = 1; } } ## end foreach my $aggr_to_remove ... my @new_aggr_list = keys %aggr_list_hash; if (@new_aggr_list) { if ($need_to_remove) { $pkg_or_obj->modify( %opts, 'aggr-list' => \@new_aggr_list, ); } } else { $pkg_or_obj->modify( %opts, 'aggr-list' =>['-'],); } $Log->exit() if $may_exit; return; } ## end sub remove_from_aggr_list # If 'aggr-list' contains aggregate objects, then extract their names sub _add_remove_aggr_list_validation { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => {'aggr-list' => {type => ARRAYREF }}, ); my $orig_list = delete $opts{'aggr-list'}; my @final_aggr_list; foreach my $aggr (@$orig_list) { if (blessed ($aggr) && $aggr->isa('NACL::C::StorageAggregate')) { push @final_aggr_list, $aggr->aggregate(); } else { push @final_aggr_list, $aggr; } } $opts{'aggr-list'} = \@final_aggr_list; $Log->exit() if $may_exit; return %opts; } =head2 add_to_array_attribute $pkg_or_obj->add_to_array_attribute( command_interface => $ci, 'attribute' => 'allowed-protocols', 'list' => \@attr-list, ); (Instance method)This method is used to update any array attribute of vserver It would append the passed in list to the value of the array attribute , rather than overriding =over =item Options =over =item C<< attribute => $attribute_name >> (Required) Name of the array attribute of a vserver =item C<< list => \@attr-list >> (Required) This is an array reference which needs to be added to the array attribute of the vserver. =item C<< command_interface => $ci >> (Required) This is a component/task object that represents the host to which to send commands. See L. =item command_interface, apiset_must, apiset_should etc. =back =back =over =item Examples =over =item Updating allowed-protocols of a vserver my @arr = $pkg_or_obj->get_one_state_attribute('allowed-protocols'); #Lets assume @arr contains 'cifs' $pkg_or_obj->add_to_array_attribute(command_interface => $ci, 'attribute' => 'allowed-protocols', 'list' => [ nfs ], ); my @arr = $pkg_or_obj->get_one_state_attribute('allowed-protocols); # @arr will contain nfs,cifs =back =back =cut sub add_to_array_attribute { $Log->enter() if $may_enter; my ( $pkg_or_obj, @args ) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { 'list' => { type => ARRAYREF }, 'attribute' => { type => SCALAR }, 'vserver' => { type => SCALAR } }, ); my $list = $opts{'list'}; my $attribute = $opts{'attribute'}; my $vserver = $opts{vserver}; my ( %hash, @list ); my $vserver_obj = NACL::CS::Vserver->fetch( command_interface => $opts{command_interface}, filter => { vserver => $vserver }, requested_fields => [$attribute], ); my @existing_list = $vserver_obj->$attribute(); if ( $existing_list[0] eq '-' ) { $pkg_or_obj->modify( command_interface => $opts{command_interface}, vserver => $vserver, $attribute => $list ); } else { # Compare the two arrays my @sorted_list = sort @$list; my @sorted_existing_list = sort @existing_list; my $sorted_list_str = join 'X;X', @sorted_list; my $sorted_existing_list_str = join 'X;X', @sorted_existing_list; if ( $sorted_list_str ne $sorted_existing_list_str ) { $sorted_list_str = $sorted_list_str . "X;X" . $sorted_existing_list_str; @list = split( /X;X/, $sorted_list_str ); map $hash{$_} = 1, @list; @list = keys(%hash); $pkg_or_obj->modify( command_interface => $opts{command_interface}, vserver => $vserver, $attribute => \@list, ); } ## end if ( $sorted_list_str ...) } ## end else [ if ( $existing_list[0]...)] $Log->exit() if $may_exit; } ## end sub add_to_array_attribute sub _default_rng_suffix { return 'vs'; } sub _delete_vserver_volume { $Log->enter() if $may_enter; my ( $pkg_or_obj, @args ) = @_; state $additional_spec = { 'command_interface' => { type => OBJECT }, 'common_opts' => { type => HASHREF }, 'vserver' => { type => SCALAR }, }; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => $additional_spec, allow_extra => 1, ); my $known_error; my $pkg_name = $pkg_or_obj->get_package_name(); my $common_opts = delete $opts{'common_opts'}; my $command_interface = $opts{command_interface}; $Log->trace('Delete the volumes from the vserver'); # CMode returns a "create-time" field for volumes. If we # arrange the volumes in reverse order of creation (i.e. # descending order for the create-time field), then we should # be able to delete them without the delete command failing # because of there being clones or it being a root-volume my @volumes; my @cs_objects = NACL::CS::Volume->fetch( command_interface => $opts{command_interface}, filter => { vserver => $opts{vserver}, type => '!DEL', # Need to specify this to skip constituents for FlexGroups 'is-constituent' => 'false', }, requested_fields => [qw(create-time vsroot state type)] ); # Store all the non-root volumes in @non_root_cs. The root-volume has # to be purged last, so we can later add it in at the end of the list. # (If multiple volumes are offline, including the vsroot, then it's # possible that arrange_by_create_time will not be able to arrange the # vsroot at the end) my ($rootvol, @non_root_cs); foreach my $cs (@cs_objects) { # Possible to have LS-mirrors of a vserver root-volume. These # LS-mirrors also have their "vsroot" field set to "true" but these # are not the root-volume. if ($cs->is_vsroot()) { $rootvol = $cs->get_task_instance(); if ($cs->state() ne 'online') { # Vserver root-volume needs to be online to be able to purge # its contained volumes $cs->get_task_instance()->modify(state => 'online'); } } else { push @non_root_cs, $cs; } } ## end foreach my $cs_object (@cs_objects) my @arranged_cs_objects = arrange_by_create_time(volume_cs_objects => \@non_root_cs); foreach my $cs (@arranged_cs_objects) { push @volumes, $cs->get_task_instance(); } push (@volumes, $rootvol) if $rootvol; foreach my $vol (@volumes) { ##Fix for burt - 907236 ## ##Catch an exception for audit enabled vserver, disable audit on vserver and purge volume.## my $retry_audit = 0; PURGE_VSERVER: try{ $pkg_name->_purge_volumes_and_clones( %opts, volume_obj => $vol ); }catch NACL::APISet::Exceptions::CommandFailedException with{ my $exception = shift; if(($exception->text() =~ /This volume is configured to hold audit logs for Vserver/i) & (!$retry_audit)) { $Log->comment("Disabling audit on vserver $opts{vserver}"); NACL::STask::VserverAudit->disable( command_interface => $opts{command_interface}, vserver => $opts{vserver}, nacltask_verify => 1, nacltask_if_disabled => "pass",); $retry_audit = 1; goto PURGE_VSERVER; } else{ $exception->throw(); } }; $known_error = 1; } $Log->exit() if $may_exit; return $known_error; } =head2 establish_ls_mirrors NACL::STask::Vserver->establish_ls_mirrors( command_interface => $ci, vserver => $vserver, volume_create_options => \%opts, nacltask_to_cleanup => 1, nacltask_cleanup_manager => $obj, ); (or) $vserver_obj->establish_ls_mirrors(); (Class or Instance method) This method does the following steps when its called the first time: 1) Finds the root-volume of the the vserver passed to this method ( or in the object in case of instance call ). 2) Finds the node on which the root-volume is residing. 3) Creates the volumes of type "DP" on the remaining nodes (not the node on which the vserver's root-volume is residing). The size of the volumes which will be created is of the same size of the root-volume of the vserver. The volumes will be created on the root-aggregate of each nodes. 4) The next step is to create LS Snapmirror relationships with the DP volumes created on the other nodes. 5) The LS snapmirror relationships are then initialized. When the method is called again the following steps are done : 1) The method checks whether the vserver's root-volume has already been snapmirrored with the volumes of the nodes present in the cluster. ( Here the nodes will be the one's excluding the node on which the root-volume of the vserver is residing ). 2) If any of the node's volume is not snapmirrored, the method will create a DP volume on that node and provide LS snapmirror relationship with the root-volume of the vserver. This LS snapmirror relationship is then intialized. =over =item Options =over =item C<< command_interface=>$command_interface >> (Required for class method, Not Applicable for instance method) See L =item C<< vserver=>$vserver >> (Required for class method, Not Applicable for instance method) Name of vserver. =item C<< volume_create_options => \%opts >> (Optional) This parameter accepts a hash-reference which contains the parameters required to create volumes on the other nodes. The parameters can be viewed in L<< NACL::STask::Volume->create|lib-NACL-STask-Volume-pm/create >> method. eg : volume_create_options => { 'unix-permissions' => "777", 'junction-path' => $path, .. ... } Autogenerated options : 1) vserver - Vserver name is selected from the vserver on which this method is operated on. 2) volume - Volume name is auto generated in the C. 3) size - The size of the volume is autodetermined using the same size of the root-volume of the vserver. =item C<< nacltask_to_cleanup => 0|1 >> (Optional, defaults to 0(no to cleanup)) Flag indicating if this operation is to be cleaned up or not. This variable is passed on to the STasks which is used in this method. The STasks used are C & C. =item C<< nacltask_cleanup_manager >> Cleanup manager to be used for registering. Default : Will use the default cleanup manager. This variable is passed on to the STasks which is used in this method. The STasks used are C & C. =back =back =over =item Arguments defaulted/auto-determined =over =item C This path is auto-generated using the vserver and "DP" type of volume which is created on rest of the nodes. Eg : : This option is required for creating the snapmirror-relationship. The destination volumes will be of DP-type which is created on rest of the nodes. (Except the node on which the root-volume of the vserver is residing). =item C This path is auto-generated using the vserver and root-volume of the vserver. Eg : : This option is required for creating the snapmirror-relationship. =item C This value is defaulted to "LS" as we are determined to create LS type of Snapmirror relationship. =item C This value is auto generated in C. This is the volume names assigned for the new "DP" volumes which is to be created on each nodes. =item C This value is auto determined by using the same size of the vserver's root-volume. This option is required for creating the volumes. =back =back =cut sub establish_ls_mirrors { $Log->enter() if $may_enter; my ( $pkg_or_obj, @args ) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { volume_create_options => { type => HASHREF, default => {} }, $pkg_or_obj->_cleanup_validate_spec(), }, ); my ( %common_opts, @nodes, $vs_name, %opts_for_cleanup, @root_aggrs, @created_snapmirrors ); $pkg_or_obj->_move_common_cleanup_opts( source => \%opts, target => \%opts_for_cleanup, ); $pkg_or_obj->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); my $vserver = $opts{vserver}; my $volume_create_options = delete $opts{volume_create_options}; #find the root-volume of the vserver my $vol_obj = NACL::CS::Volume->fetch( filter => {vserver => $vserver}, canned_filters => [qw(vsroot_vols)], requested_fields => [ qw( node size ) ], %common_opts, ); my $root_vol = $vol_obj->volume(); my $node_of_vserver = $vol_obj->node(); my $root_vol_size = $vol_obj->size(); # Get root-aggrs for all nodes other than the one hosting the # vsroot volume. @root_aggrs = NACL::CS::Aggregate->fetch( %common_opts, filter => { root => 'true',}, requested_fields => [ qw( node )], ); my %root_aggrs_hash = map { $_->aggregate() => 1 } @root_aggrs; foreach my $aggr_obj (@root_aggrs) { my @nodes = $aggr_obj->node(); if($nodes[0] eq $node_of_vserver) { delete $root_aggrs_hash{$aggr_obj->aggregate()}; last; } } # Get all LS-relationships with this source volume. my @ls_mirrors = NACL::STask::Snapmirror->find( %common_opts, filter => { vserver => $vserver, type => 'LS', 'source-volume' => $root_vol, }, allow_empty => 1, ); if (@ls_mirrors) { my @dest_vols = map { $_->destination_volume() } @ls_mirrors; my $dest_vols_str = join '|', @dest_vols; # Get all the aggregates on which the ls_mirror volumes are residing on my @dest_aggrs = NACL::CS::Volume->fetch( %common_opts, filter => { vserver => $vserver, volume => $dest_vols_str, }, requested_fields => ['aggregate'] ); foreach my $aggr_obj (@dest_aggrs) { delete $root_aggrs_hash{$aggr_obj->aggregate()}; } } #Creating destination-volume of type DP & SM relationship creation foreach my $aggr_name (keys %root_aggrs_hash) { my $dest_vol = NACL::STask::Volume->create( aggregate => $aggr_name, size => $root_vol_size, vserver => $vserver, type => "DP", %$volume_create_options, %opts_for_cleanup, %common_opts, )->volume(); my $snapmirror_obj = NACL::STask::Snapmirror->create( "source-path" => "$vserver:$root_vol", "destination-path" => "$vserver:$dest_vol", type => "LS", do_initialize => 1, nacltask_wait => 0, %opts_for_cleanup, %common_opts, ); # Added this condition due to BURT:820770 # Snapmirror Create of "type = LS and do_initialize = 1" is returning empty object after initializing if( $snapmirror_obj){ push @created_snapmirrors, $snapmirror_obj ; } } if(@created_snapmirrors){ map { $_->wait_for_initialization(%common_opts) } @created_snapmirrors; } $Log->exit() if $may_exit; } =head2 wait_for_mcc_queues_to_drain # Package call, wait for queues to drain for a single vserver NACL::STask::Vserver->wait_for_mcc_queues_to_drain( command_interface => $ci, vserver => $vs_name ); # Instance call, wait for queues to drain for a single vserver $vs_obj->wait_for_mcc_queues_to_drain(); # To wait for queues to drain for the entire cluster, provide the vserver # value as '*' NACL::STask::Vserver->wait_for_mcc_queues_to_drain( command_interface => $ci, vserver => '*' ); This method waits for the various MCC queues (the CRS Pending Queue, the CRS Cluster-Queue and the CRS Queue) for a particular vserver to drain. To wait for the queues for the entire cluster to drain, the value of the C option should be specified as '*'. This throws a C if the queues do not drain within the specified timeout. =over =item Options =over =item C<< 'method-timeout' => $time >> (Defaults to 240) The time to wait for (in seconds). If not provided, this defaults to 240 seconds. =item C<< polling_interval => $polling_time >> (Defaults to 10) The interval at which to poll. If not provided, this defaults to 10 seconds. =back =back =cut sub wait_for_mcc_queues_to_drain { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { 'method-timeout' => {type => SCALAR, default => 240}, polling_interval => {type => SCALAR, default => 10}, } ); my %common_opts_ci; $pkg_or_obj->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts_ci, ); my ($stream_uuid, $vs_err_msg); if ($opts{vserver} eq '*') { $stream_uuid = '*'; $vs_err_msg = 'for the entire cluster'; } else { # The stream-uuid is the uuid of the vserver. $stream_uuid = NACL::CS::Vserver->fetch( %common_opts_ci, filter => {vserver => $opts{vserver}}, requested_fields => [qw(uuid)] )->uuid(); $vs_err_msg = "for the vserver '$opts{vserver}'"; } my $scaled_timeout = $opts{command_interface}->scale_timeout( 'method-timeout' => $opts{'method-timeout'} // 60); my $end_time = timeout2time($scaled_timeout); my $next = sub { my @queues = @_; if (time() <= $end_time) { Tharn::snooze($opts{polling_interval}); } else { $Log->exit() if $may_exit; NACL::STask::Exceptions::MCCQueuesNotDrained->throw( "After waiting for $scaled_timeout seconds all MCC queues " . "had not drained $vs_err_msg", timeout => $scaled_timeout, state_objs => \@queues ); } }; require NACL::CS::DebugCrsQueuePending; require NACL::CS::DebugCrsClusterQueue; require NACL::CS::DebugCrsQueue; while (1) { my @pending = NACL::CS::DebugCrsQueuePending->fetch( %common_opts_ci, filter => {streams => [$stream_uuid]}, allow_empty => 1, ); $next->(@pending) if (@pending); my @cluster = NACL::CS::DebugCrsClusterQueue->fetch( %common_opts_ci, filter => {streams => [$stream_uuid]}, allow_empty => 1, ); $next->(@cluster) if (@cluster); my @queue = NACL::CS::DebugCrsQueue->fetch( %common_opts_ci, filter => {'stream-uuid' => $stream_uuid}, allow_empty => 1, ); if (@queue) { $next->(@queue); } else { last; } } $Log->exit() if $may_exit; } =head2 config_diff # Package call, check for config diff for a single vserver NACL::STask::Vserver->config_diff( command_interface => $ci, vserver => $vs_name ); # Instance call, check for config diff for a single vserver $vs_obj->config_diff(); # To check for config diff for the entire cluster, provide the vserver # value as '*' NACL::STask::Vserver->config_diff( command_interface => $ci, vserver => '*' ); This method checks if there are any MCC config diffs, by internally using C. It first waits for queues to drain before checking for config diffs. The same options accepted by L are accepted by this method. Additionally, it accepts the following options: =over =item C<< is_dest => 0|1 >> (Optional, defaults to 0) If set to 0, then the vserver provided is the source-vserver, else the vserver provided is the destination-vserver. =back =cut sub config_diff { $Log->enter() if $may_enter; my ($pkg_or_obj, @args) = @_; my %opts = $pkg_or_obj->_common_validate_with( params => \@args, additional_spec => { # Private since it's mainly for unit test purposes. _wait_for_queues_to_drain => {type => SCALAR, default => 1}, is_dest => {type => SCALAR, default => 0}, }, allow_extra => 1 ); my $queue_wait = delete $opts{_wait_for_queues_to_drain}; my $is_dest = delete $opts{is_dest}; $pkg_or_obj->wait_for_mcc_queues_to_drain(%opts) if ($queue_wait); my %common_opts_ci; $pkg_or_obj->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts_ci, ); my $vs_field = $is_dest ? 'destination-vserver-name' : 'source-vserver-name'; require NACL::CS::VserverConfigDiff; my @cs = NACL::CS::VserverConfigDiff->fetch( %common_opts_ci, filter => {$vs_field => $opts{vserver}}, allow_empty => 1 ); if (@cs) { map {$_->command_interface_reset()} @cs; require Data::Dumper; NACL::STask::Exceptions::VserverConfigDiffsPresent->throw( "Vserver Config Diffs found!\n" . Data::Dumper::Dumper(\@cs), state_objs => \@cs ); } $Log->exit() if $may_exit; } =head2 parents @parents = $vserver_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::Aggregate); } 1;