# $Id: //depot/prod/test/main/lib/NACL/MTask/Cluster.pm#1 $ # Copyright (c) 2011 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary Cluster Task Module ## @author sneham, dl-nacl-dev@netapp.com ## @status shared ## @pod here package NACL::MTask::Cluster; use strict; use warnings; use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use Params::Validate qw(validate validate_with BOOLEAN SCALAR HASHREF OBJECT SCALARREF ARRAYREF); use NATE::Exceptions::Argument qw(:try); use NACL::APISet::Exceptions::TimeoutException (); use NACL::Exceptions::InvalidChoice; use NATE::BaseException; use NACL::C::Network; use NACL::C::ClusterHa; use NACL::C::Vserver; use NACL::C::Component; use NACL::CS::NetworkInterfaceFailoverGroups; use NACL::CS::Aggregate; use NACL::CS::Volume; use NACL::STask::VolumeMove; use NACL::Transit; use NACL::STask::Cluster; use NACL::STask::StorageFailover; use NACL::CS::SystemNode; use NACL::STask::NetworkInterface; use NACL::STask::Node; use NACL::C::LunPortset; use NACL::CS::LunPortset; use NACL::ComponentUtils qw(Dumper); use base qw (NACL::MTask::MTask); =head1 NAME NACL::MTask::Cluster =head1 DESCRIPTION "NACL::MTask::Cluster" provides a well-defined but potentially complex or multi-step method related to cluster. * unjoin() would either cleanup the specified vserver(s) and its related objects or it would default to cleaning up vserver 'vs0' and its related objects followed by unjoining the node. If there are more than one vserver involved in unjoining the node, you can either (a) specify: vservers => ['vs1','vs2', ...] or (b) use the cleanup_vserver_objects_for_unjoin() method to cleanup each of those vservers and their related objects after which you can invoke unjoin() method to actually unjoin the node. * cleanup_vserver_objects_for_unjoin method will cleanup the given vserver's objects : its root volume and lifs. It wont unjoin the node. As the previous method does cleanup of vserver's objects, this method will provide a way to cleanup a single vserver for the unjoin procedure to continue =head1 METHODS =cut =head2 unjoin (Class method only) This method will unjoin node from a cluster. This method will consider only deux_init created objects for cleanup. This package takes care of * Disable HA * Move Epsilon if on unjoining node * Disable SFO * Move/Delete vserver lifs if on unjoining node * Modify home node of lifs if their home node is unjoining node * Move root volume of vserver if it is on unjoining node * Migrate Cluster mgmt lif it is on unjoining node * Modify the home node of Cluster mgmt lif if their home node is unjoining node * Unjoin operation * RDB Check More info on unjoin operation present at http://wikid.netapp.com/w/Cluster_Unjoin unjoin with only mandatory paramenters : NACL::MTask::Cluster->unjoin(command_interface => $node_ci_B, unjoin_node => $node_ci_A); Which is same as unjoin with default options given here : NACL::MTask::Cluster->unjoin(command_interface => $node_ci_B, unjoin_node => $node_ci_A, ha_disable => 1, epsilon_modify => 1, assign_epsilon_to_node => nodeB, sfo_disable => 1, vserver_data_lifs => "migrate", vserver_data_lifs_to_node => nodeB, home_node_data_lifs => "modify", home_node_data_lifs_to_node => nodeB, vserver_root_volume => "move", vserver_root_volume_to_aggregate => "aggr1", cluster_mgmt_lif => "migrate", cluster_mgmt_lif_to_node => nodeB, home_node_cluster_mgmt_lif => "modify", home_node_cluster_mgmt_lif_to_node => nodeB, unjoin => 1, rdb_check => 0); Unjoin operation opting deletion of Vserver lifs, migration of cluster mgmt lif, do_nothing for other operations, unjoin and RDB check. NACL::MTask::Cluster->unjoin(command_interface => $node_ci_B, unjoin_node => $node_ci_A, ha_disable => 0, epsilon_modify => 0, sfo_disable => 0, vserver_data_lifs => "delete", home_node_data_lifs => "do_nothing", vserver_root_volume => "do_nothing", cluster_mgmt_lif => "migrate", cluster_mgmt_lif_to_node => nodeB, home_node_cluster_mgmt_lif => "do_nothing", unjoin => 1, rdb_check => 1); The following style can be used if you wish to have the unjoin perform lif removals/migrations on multiple vservers at a time. NACL::MTask::Cluster->unjoin(command_interface => $node_ci_B, unjoin_node => $node_ci_A, vservers => ['vserver1', 'vserver2']); =over =item Options =over =item C<< command_interface => $ci_B >> (Required, isa NACL::C::CommandInterface::ONTAP) The command interface on which unjoin command is executed. This node is other than the node which is to be unjoined. See L =item C<< unjoin_node => $node_name >> (mandatory) $node_name Node will be unjoined. =item C<< vserver => $vserver >> (Optional, defaults to "vs0") vserver Operations on root volume, lifs will consider this vserver =item C<< vservers => $vservers >> (Optional, defaults to []) An array reference of vserver names to consider operating against when unjoining the unjoin_node. This enhances the 'vserver' option by allowing one to specify multiple vservers in one unjoin() API call. If 'vservers' is specified, the value of 'vserver' is ignored. =item C<< ha_disable => 1 or 0 >> (Optional, defaults to 1) When set to 1, HA Disables =item C<< epsilon_modify => 1 or 0 >> (Optional, Defaults to 1) If 1, Epsilon will be moved to a node if unjoining node has Epsilon. It is set to "assign_epsilon_to_node" if provided, else randomly chosen node. If 0, EPSILON check and modify operation will not be performed. =item C<< assign_epsilon_to_node => $node_name >> (Optional)If not provided, a node is chosen randomly. =item C<< sfo_disable => 1 or 0 >> (Optional, Defaults to 1) If 0, SFO Disble operation will not be performed. =item C<< vserver_data_lifs => migrate or delete or do_nothing >> (Optional, Defaults to migrate) Default value is migrate. If migrate, then vserver lifs which are on unjoining node will be migrated to other nodes in cluster. If "vserver_data_lifs_to_node" is provided, it is used to migrate to that node, else randomly it will be chosen. If delete, then vserver lifs on unjoining node are deleted. If do_nothing, then no operation related to vserver lifs is performed. We will consider (provided option) vserver's root volume. =item C<< vserver_san_data_lifs => delete or do_nothing >> (Optional, Defaults to delete) It is applicable only to $vserver_name vserver's SAN data lifs. If delete, then vserver lifs on unjoining node are deleted. If do_nothing, then no operation related to vserver lifs is performed. =item C<< vserver_san_data_lifs_from_portsets => remove or do_nothing >> (Optional, Defaults to remove) It is applicable only to $vserver_name vserver's SAN data lifs. If set to 'remove' will ensure that all data lifs for the specified vserver(s) are removed from portsets. If set to 'do_nothing', then portsets will not be modified. This will most likely cause the unjoin operation to fail though, if portsets exist. =item C<< migrate_vserver_data_lifs_to_node => $node_name >> (Optional)If not provided, a node is chosen randomly. If "vserver_data_lifs" is migrate and "vserver_data_lifs_to_node" is provided, it is used to migrate to that node, else randomly it will be chosen. We will consider (provided option) vserver's lifs. =item C<< home_node_data_lifs => modify or do_nothing >> (Optional, defaults to modify) Default value is modify. If modify and data lifs' home node is unjoining node, then home node is modified to other nodes in cluster. They are modified to "home_node_data_lifs_to_node" if provided else node is chosen randomly. If do_nothing, then no operation performed related to home node modification. We will consider (provided option) vserver's lifs. =item C<< home_node_data_lifs_to_node => $node_name >> (Optional)If not provided, a node is chosen randomly. We will consider (provided option) vserver's lifs. =item C<< vserver_root_volume => move or do_nothing >> (Optional, Default value is move) If move, then vserver root volumes on unjoining node is moved to other aggregate. If "vserver_root_volume_to_aggregate" is set, vserver root volumes are moved to that aggregate else randomly chosen aggrs We will consider (provided option) vserver's root volume. =item C<< vserver_root_volume_to_aggregate => $aggr_name >> (Optional) If "vserver_root_volume" is move, then vserver root volumes on unjoining node is moved to "vserver_root_volume_to_aggregate" aggregate or randomly chosen aggrs We will consider (provided option) vserver's root volume. =item C<< cluster_mgmt_lif => migrate or do_nothing >> (Optional) Default value is migrate. If migrate, then cluster mgmt lif if on unjoining node will be migrated to other nodes in cluster. If "cluster_mgmt_lif_to_node" is provided, it is used to migrate to that node, else randomly it will be chosen. If delete, then cluster mgmt lif on unjoining node are deleted. If do_nothing, then no operation related to cluster mgmt lif is performed. =item C<< home_node_cluster_mgmt_lif => modify or do_nothing >> (Optional) Default value is modify. If modify and cluster mgmt lif's home node is unjoining node, then home node is modified to other nodes in cluster. They are modified to home_node_cluster_mgmt_lif_to_node if provided else node is chosen randomly. If do_nothing, then no operation performed related to home node modification. =item C<< nacltask_check_for_prompt => 1 or 0 >> (Optional) Default value is 1. If the value is 0, After unjoin the method will not check for any prompt =item C<< unjoin => 1 or 0 >> (Optional, Default value is 1) If 0, unjoin operation will not be run. =item C<< timeout_to_reach_firmware => $timeout_to_reach_firmware >> (Optional, Default value is 120) Number of seconds wait to reach firmware after issuing unjoin . =item C<< rdb_check => 1 or 0 >> (Optional, Default value is 0) After unjoin, if "rdb_check" is 1, RDB check is peformed =item C<< apiset_must=>$ruleset >> (Optional)See L =item C<< apiset_should=>$ruleset >> (Optional)See L =item C<< method-timeout=>$method_timeout_in)seconds >> (Optional) As accepted by Components. =back =back =cut sub unjoin { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { command_interface => { isa => 'NACL::C::Node' }, unjoin_node => { isa => 'NACL::C::Node' }, vserver => { type => SCALAR, default => "vs0" }, vservers => { type => ARRAYREF, default => [] }, ha_disable => { type => SCALAR, default => 1 }, epsilon_modify => { type => SCALAR, default => 1 }, assign_epsilon_to_node => { type => SCALAR, optional => 1 }, sfo_disable => { type => SCALAR, default => 1 }, vserver_data_lifs => { type => SCALAR, default => "migrate" }, vserver_san_data_lifs => { type => SCALAR, default => "delete" }, vserver_san_data_lifs_from_portsets => { type => SCALAR, default => "remove" }, home_node_data_lifs => { type => SCALAR, default => "modify" }, vserver_root_volume => { type => SCALAR, default => "move" }, vserver_root_volume_to_aggregate => { type => SCALAR, optional => 1 }, cluster_mgmt_lif => { type => SCALAR, default => "migrate" }, home_node_cluster_mgmt_lif => { type => SCALAR, default => "modify" }, unjoin => { type => SCALAR, default => 1 }, rdb_check => { type => SCALAR, default => 1 }, timeout_to_reach_firmware => { type => SCALAR, default => 300 }, nacltask_check_for_prompt => { type => SCALAR, default => 1 }, }, ); my %common_options; $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_options, ); $Log->debug( "opts to NACL::MTask::Cluster::Unjoin " . Dumper( \%opts ) ); my $unjoin_node_interface = delete $opts{unjoin_node}; my $unjoin_node = $unjoin_node_interface->name(); my $ha_disable = delete $opts{ha_disable}; my $epsilon_modify = delete $opts{epsilon_modify}; my $assign_epsilon_to_node = delete $opts{assign_epsilon_to_node}; my $sfo_disable = delete $opts{sfo_disable}; my $vserver_data_lifs = delete $opts{vserver_data_lifs}; my $home_node_data_lifs = delete $opts{home_node_data_lifs}; my $vserver_root_volume = delete $opts{vserver_root_volume}; my $cluster_mgmt_lif = delete $opts{cluster_mgmt_lif}; my $home_node_cluster_mgmt_lif = delete $opts{home_node_cluster_mgmt_lif}; my $unjoin = delete $opts{unjoin}; my $rdb_check = delete $opts{rdb_check}; my $timeout_to_reach_firmware = delete $opts{timeout_to_reach_firmware}; my $vserver = delete $opts{vserver}; my $vservers = delete $opts{vservers}; my $vserver_san_data_lifs = delete $opts{vserver_san_data_lifs}; my $vserver_san_data_lifs_from_portsets = delete $opts{vserver_san_data_lifs_from_portsets}; my $nacltask_check_for_prompt = delete $opts{nacltask_check_for_prompt}; my ( @node_ports, @net_obj ); my $has_uichange = $opts{command_interface}->has_uichange( uichange => 'ipspaces-ms4', throw_exception => 0 ); $Log->debug("Check if vserver exists"); # backwards compatibility for $opts{vserver} if ( not scalar( @{$vservers} ) ) { @{$vservers} = $vserver; } # find the vservers specified and store in the vs objects array $Log->debug("Find Vservers to Operate against"); my @vs_objects = (); foreach my $vs ( @{$vservers} ) { push( @vs_objects, NACL::C::Vserver->find( command_interface => $opts{command_interface}, filter => { vserver => $vs }, allow_empty => 1, %common_options ) ); } $Log->debug("Find Healthy Nodes"); my @healthy_nodes_in_cluster = NACL::CS::SystemNode->fetch( command_interface => $opts{command_interface}, filter => { node => "!$unjoin_node", health => "true", eligibility => "true", }, requested_fields => ['node'], %common_options ); $Log->debug( "" . Dumper( \@healthy_nodes_in_cluster ) ); $Log->debug("Find Failover Groups"); if ($has_uichange) { my $healthy_node = $healthy_nodes_in_cluster[0]->node(); @net_obj = NACL::CS::NetworkInterfaceFailoverGroups->fetch( command_interface => $opts{command_interface}, requested_fields => ['targets'], filter => { 'failover-group' => 'Default' }, %common_options, ); foreach my $obj (@net_obj) { my @targets = grep { /$healthy_node/ } $obj->targets(); foreach my $target (@targets) { $target =~ /^$healthy_node:(\S+)/; push @node_ports, $1; } } } else { @net_obj = NACL::CS::NetworkInterfaceFailoverGroups->fetch( command_interface => $opts{command_interface}, filter => { node => $healthy_nodes_in_cluster[0]->node(), }, requested_fields => ['port'], %common_options, ); foreach my $obj (@net_obj) { push @node_ports, $obj->port(); } } $Log->debug("Vserver lif ports : @node_ports "); if ($ha_disable) { $Log->debug("Disable HA"); NACL::C::ClusterHa->modify( command_interface => $opts{command_interface}, configured => "false", %common_options ); } $Log->debug("Find if epsilon node is unjoining node"); my $if_epsilon = NACL::C::Cluster->find( command_interface => $opts{command_interface}, allow_empty => 1, filter => { node => $unjoin_node, epsilon => "true" }, %common_options ); if ( defined $if_epsilon ) { if ($epsilon_modify) { $Log->debug("Moving Epsilon out of $unjoin_node"); NACL::STask::Cluster->modify( command_interface => $opts{command_interface}, node => $unjoin_node, epsilon => "false", %common_options ); $assign_epsilon_to_node ||= $healthy_nodes_in_cluster[0]->node(); $Log->debug("Assigning Epsilon to $assign_epsilon_to_node node"); NACL::STask::Cluster->modify( command_interface => $opts{command_interface}, node => $assign_epsilon_to_node, epsilon => "true", %common_options ); } } if ($sfo_disable) { $Log->debug("Disabling storage failover on node $unjoin_node"); try { NACL::STask::StorageFailover->disable( command_interface => $opts{command_interface}, node => $unjoin_node_interface, %common_options ); } catch NACL::C::Exceptions::StorageFailover::NonHAMode with { # Failed to disable storage failover service. Reason: Non-HA mode $Log->debug( sub { "Catching exception and continuing to accomodate " . "cluster unjoin in Non-HA mode."; } ); } catch NACL::Exceptions::InvalidChoice with { my $e = shift; if ( $e->text() =~ /Odd number of nodes/i ) { # ignore this error because it is perfectly valid for someone to run unjoin with # a cluster that contains an odd number of nodes $Log->debug("Surpressing Odd number of nodes exception"); } else { # rethrow otherwise $e->throw(); } }; } # cleanup multiple vservers if ( scalar(@vs_objects) ) { foreach my $vserver (@vs_objects) { $pkg->cleanup_vserver_objects_for_unjoin( command_interface => $opts{command_interface}, unjoin_node => $unjoin_node, vserver => $vserver, vserver_data_lifs => $vserver_data_lifs, vserver_san_data_lifs => $vserver_san_data_lifs, vserver_san_data_lifs_from_portsets => $vserver_san_data_lifs_from_portsets, home_node_data_lifs => $home_node_data_lifs, vserver_root_volume => $vserver_root_volume, vserver_root_volume_to_aggregate => $opts{vserver_root_volume_to_aggregate} || "", rdb_check => 0, ); } } $Log->debug("Finding Cluster Mgmt LIF"); # burt1005189 more than one Cluster Mgmt LIFs can be present on cluster my @cluster_mgmt_lif_all_obj = NACL::STask::NetworkInterface->find( allow_empty => 1, command_interface => $opts{command_interface}, filter => { role => "cluster-mgmt", }, %common_options ); foreach my $cluster_mgmt_lif_obj (@cluster_mgmt_lif_all_obj) { my $cluster_mgmt_lif_state = $cluster_mgmt_lif_obj->state(); if ( $cluster_mgmt_lif_state->home_node() eq $unjoin_node ) { $Log->debug( "Modifying home node of Cluster Mgmt lif " . $cluster_mgmt_lif_state->lif() . " to " . $healthy_nodes_in_cluster[0]->node() . ":" . $node_ports[0] ); if ( $home_node_cluster_mgmt_lif eq "modify" ) { $cluster_mgmt_lif_obj->modify( 'home-node' => $healthy_nodes_in_cluster[0]->node(), 'home-port' => $node_ports[0], ); } } if ( $cluster_mgmt_lif_state->curr_node() eq $unjoin_node ) { if ( $cluster_mgmt_lif eq "migrate" ) { $Log->debug( "Migrating Cluster Mgmt LIF to " . $healthy_nodes_in_cluster[0]->node() . ":" . $node_ports[0] ); $cluster_mgmt_lif_obj->migrate( 'dest-node' => $healthy_nodes_in_cluster[0]->node(), 'dest-port' => $node_ports[0], ); } } } if ($unjoin) { $Log->debug("Unjoin Node $unjoin_node"); NACL::C::Cluster->unjoin( command_interface => $opts{command_interface}, node => $unjoin_node, 'method-timeout' => 360, %common_options ); if ($nacltask_check_for_prompt) { my $initial_wait_time = ( $timeout_to_reach_firmware > 120 ) ? 120 : $timeout_to_reach_firmware; $timeout_to_reach_firmware = $timeout_to_reach_firmware - 120; Tharn::sleep $initial_wait_time; my $transit_obj = NACL::Transit->new( name => $unjoin_node ); my $current_state = $transit_obj->get_state(); if ( $current_state ne "FIRMWARE" && $timeout_to_reach_firmware > 0 ) { my $end_time = time() + $timeout_to_reach_firmware; # By waiting for the end time, we even account the time taken # by transit to find the current state while ( time() < $end_time && $current_state ne "FIRMWARE" ) { # Keep checking the current state and wait for the filer # to reach firmware state; $Log->debug("STATE OF UNJOINING NODE : $current_state "); Tharn::sleep 10; $current_state = $transit_obj->get_state(); } } if ( $current_state ne "FIRMWARE" ) { $Log->exit() if $may_exit; NACL::APISet::Exceptions::TimeoutException->throw( "Unjoin method timed out." . "Waited for $timeout_to_reach_firmware." . " Still did not get FIRMWARE Prompt" ); } } } if ($rdb_check) { $Log->debug("RDB Check"); NACL::STask::Cluster->check_rings( command_interface => $opts{command_interface}, %common_options ); } $Log->exit() if $may_exit; } ## end sub unjoin =head2 cleanup_vserver_objects_for_unjoin (Class method only) This method cleans up specified vserver objects from unjoining node in cluster. This method takes care of: * Remove SAN Data lifs from portsets * Move/Delete specified vserver lifs if on unjoining node * Modify home node of specified vserver lifs if their home node is unjoining node * Move root volume of specified vserver if it is on unjoining node This method with only mandatory paramenters : NACL::MTask::Cluster->cleanup_vserver_objects_for_unjoin( command_interface => $ci_B, unjoin_node => nodeA, vserver => $vserver_name, ); which is same as, NACL::MTask::Cluster->cleanup_vserver_objects_for_unjoin( command_interface => $ci_B, unjoin_node => nodeA, vserver => $vserver_name, vserver_data_lifs => "migrate", vserver_san_data_lifs => "delete", vserver_san_data_lifs_from_portsets => "remove", home_node_data_lifs => "modify", vserver_root_volume => "move", vserver_root_volume_to_aggregate => "aggr1", ); Cleanup operation opting deletion of Vserver lifs and do_nothing for other operations . NACL::MTask::Cluster->cleanup_vserver_objects_for_unjoin( command_interface => $ci_B, unjoin_node => nodeA, vserver => $vserver_name, vserver_data_lifs => "delete", vserver_root_volume => "do_nothing", ); Cleanup Operation for vservers ( other than vserver which is taken care by unjoin ) and finally unjoin with RDB Check: NACL::MTask::Cluster->cleanup_vserver_objects_for_unjoin( command_interface => $ci_B, unjoin_node => nodeA, vserver => "vs1", ); NACL::MTask::Cluster->unjoin( command_interface => $ci_B, unjoin_node => nodeA, rdb_check => 1, ); =over =item Options =over =item C<< command_interface => $ci_B >> (Required, isa NACL::C::CommandInterface::ONTAP) The command interface on which unjoin command is executed. This node is other than the node which is to be unjoined. See L =item C<< unjoin_node => $node_name >> (mandatory) $node_name Node will be unjoining node. =item C<< vserver => $vserver_name >> (mandatory) Vserver related Operations for unjoin is applicable only to $vserver_name vserver. =item C<< vserver_data_lifs => migrate or delete or do_nothing >> (Optional, Defaults to migrate) It is applicable only to $vserver_name vserver's data lifs. If migrate, then "$vserver_name" vserver lifs which are on unjoining node will be migrated to other nodes in cluster. If "vserver_data_lifs_to_node" is provided, it is used to migrate to that node, else randomly it will be chosen. If delete, then vserver lifs on unjoining node are deleted. If do_nothing, then no operation related to vserver lifs is performed. =item C<< vserver_san_data_lifs => delete or do_nothing >> (Optional, Defaults to delete) It is applicable only to $vserver_name vserver's SAN data lifs. If delete, then vserver lifs on unjoining node are deleted. If do_nothing, then no operation related to vserver lifs is performed. =item C<< vserver_san_data_lifs_from_portsets => remove or do_nothing >> (Optional, Defaults to remove) It is applicable only to $vserver_name vserver's SAN data lifs. If set to 'remove' will ensure that all data lifs for the specified vserver are removed from portsets. If set to 'do_nothing', then portsets will not be modified. This will most likely cause the unjoin operation to fail though, if portsets exist. =item C<< home_node_data_lifs => modify or do_nothing >> (Optional, defaults to modify) It is applicable only to $vserver_name vserver's data lifs. Default value is modify. If modify and data lifs' home node is unjoining node, then home node is modified to other nodes in cluster. They are modified to "home_node_data_lifs_to_node" if provided else node is chosen randomly. If do_nothing, then no operation performed related to home node modification. =item C<< vserver_root_volume => move or do_nothing >> (Optional, Default value is move) It is applicable only to $vserver_name vserver's root volume. If move, then vserver root volumes on unjoining node is moved to other aggregate. If "vserver_root_volume_to_aggregate" is set, vserver root volume are moved to that aggregate else randomly chosen aggr. If do_nothing, then no operation performed related to vserver root volume move. =item C<< vserver_root_volume_to_aggregate => $aggr_name >> (Optional) It is applicable only to $vserver_name vserver's root volume. If "vserver_root_volume" is move, then vserver root volumes on unjoining node is moved to "vserver_root_volume_to_aggregate" aggregate or randomly chosen aggr. =item C<< apiset_must=>$ruleset >> (Optional)See L =item C<< apiset_should=>$ruleset >> (Optional)See L =item C<< method-timeout=>$method_timeout_in)seconds >> (Optional) As accepted by Components. =back =back =cut sub cleanup_vserver_objects_for_unjoin { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { command_interface => { isa => 'NACL::C::Node' }, unjoin_node => { type => SCALAR }, vserver => { type => SCALAR }, vserver_data_lifs => { type => SCALAR, default => "migrate" }, vserver_data_lifs_to_node => { type => SCALAR, optional => 1 }, vserver_san_data_lifs => { type => SCALAR, default => "delete" }, vserver_san_data_lifs_from_portsets => { type => SCALAR, default => "remove" }, home_node_data_lifs => { type => SCALAR, default => "modify" }, vserver_root_volume => { type => SCALAR, default => "move" }, vserver_root_volume_to_aggregate => { type => SCALAR, optional => 1 }, rdb_check => { type => SCALAR, default => 1 }, }, ); my %common_options; $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_options, ); $Log->debug( "opts to NACL::MTask::Cluster::cleanup_vserver_objects_for_unjoin " . Dumper( \%opts ) ); my $unjoin_node = delete $opts{unjoin_node}; my $vserver = delete $opts{vserver}; my $vserver_data_lifs = delete $opts{vserver_data_lifs}; my $vserver_data_lifs_to_node = delete $opts{vserver_data_lifs_to_node}; my $vserver_san_data_lifs = delete $opts{vserver_san_data_lifs}; my $vserver_san_data_lifs_from_portsets = delete $opts{vserver_san_data_lifs_from_portsets}; my $home_node_data_lifs = delete $opts{home_node_data_lifs}; my $home_node_data_lifs_to_node = delete $opts{home_node_data_lifs_to_node}; my $vserver_root_volume = delete $opts{vserver_root_volume}; my $vserver_root_volume_to_aggregate = delete $opts{vserver_root_volume_to_aggregate}; my $rdb_check = delete $opts{rdb_check}; my ( @vserver_lifs, @node_ports, @portsets, @net_obj ); my $has_uichange = $opts{command_interface}->has_uichange( uichange => 'ipspaces-ms4', throw_exception => 0 ); $Log->debug("Check if vserver exists"); my $vs_object = NACL::CS::Vserver->fetch( command_interface => $opts{command_interface}, filter => { vserver => $vserver }, %common_options ); $Log->debug("Find Healthy Nodes"); my @healthy_nodes_in_cluster = NACL::CS::SystemNode->fetch( command_interface => $opts{command_interface}, filter => { node => "!$unjoin_node", health => "true", eligibility => "true", }, requested_fields => ['node'], %common_options ); $Log->debug("Find Vserver LIFS on home-node $unjoin_node"); @vserver_lifs = NACL::STask::NetworkInterface->find( command_interface => $opts{command_interface}, filter => { vserver => $vserver, 'home-node' => $unjoin_node }, allow_empty => 1, %common_options ); $Log->debug("Find Failover Groups"); if ($has_uichange) { my $healthy_node = $healthy_nodes_in_cluster[0]->node(); @net_obj = NACL::CS::NetworkInterfaceFailoverGroups->fetch( command_interface => $opts{command_interface}, requested_fields => ['targets'], filter => { 'failover-group' => 'Default' }, %common_options, ); foreach my $obj (@net_obj) { my @targets = grep { /$healthy_node/ } $obj->targets(); foreach my $target (@targets) { $target =~ /^$healthy_node:(\S+)/; push @node_ports, $1; } } } else { @net_obj = NACL::CS::NetworkInterfaceFailoverGroups->fetch( command_interface => $opts{command_interface}, filter => { node => $healthy_nodes_in_cluster[0]->node(), }, requested_fields => ['port'], %common_options, ); foreach my $obj (@net_obj) { push @node_ports, $obj->port(); } } $Log->debug("node_ports are - @node_ports"); if ( $vserver_san_data_lifs_from_portsets eq "remove" ) { $Log->debug("Find Portsets"); @portsets = NACL::CS::LunPortset->fetch( command_interface => $opts{command_interface}, filter => { vserver => $vserver }, allow_empty => 1 ); } if ( $#vserver_lifs >= 0 ) { foreach my $lif (@vserver_lifs) { my $lif_state = $lif->state(); # remove lifs from portsets they belong to before attempting to remove the lifs if ( $vserver_san_data_lifs_from_portsets eq "remove" ) { $Log->debug("Requested to remove data lifs from portsets"); foreach my $portset (@portsets) { # scan through all the portset members in this portset to determine if $lif is a member of it $Log->debug( "Vserver $vserver: Searching for " . $lif_state->lif() . " in portset " . $portset->portset() ); foreach my $portset_member ( @{ $portset->port_name() } ) { # if the current lif is in this portset, then we need to remove it from the portset $Log->debug( "Vserver $vserver: Comparing $portset_member with " . $lif_state->lif() ); if ( $portset_member eq $lif_state->lif() ) { # remove this lif from the portset $Log->debug( "Vserver $vserver: removing " . $lif_state->lif() . " from portset " . $portset->portset() ); NACL::C::LunPortset->remove( command_interface => $opts{command_interface}, vserver => $vserver, portset => $portset->portset(), 'port-name' => [ $lif_state->lif() ] ); last; } } } } # SAN Lifs if ( grep( /fcp|iscsi/i, $lif_state->data_protocol() ) ) { # delete SAN lifs if requested if ( $vserver_san_data_lifs eq "delete" ) { $Log->debug( "Delete SAN Data lif $vserver : " . $lif_state->lif() ); $lif->purge(); } } else { # non-SAN Lifs if ( $vserver_data_lifs ne "delete" && $lif_state->home_node() eq $unjoin_node ) { if ( $home_node_data_lifs eq "modify" ) { $Log->debug( "Modify Home node of lif $vserver:" . $lif_state->lif() . " to " . $healthy_nodes_in_cluster[0]->node() . ":" . $node_ports[0] ); @node_ports = grep { $_ !~ /e0M/ } @node_ports if( $node_ports[0] =~ /e0M/ ); $lif->modify( 'home-node' => $healthy_nodes_in_cluster[0]->node(), 'home-port' => $node_ports[0], ); } } if ( $lif_state->curr_node() eq $unjoin_node ) { if ( $vserver_data_lifs eq "migrate" ) { $Log->debug( "Migrate Data lif $vserver : " . $lif_state->lif() . " to " . $healthy_nodes_in_cluster[0]->node() . ":" . $node_ports[0] ); $lif->migrate( 'dest-node' => $healthy_nodes_in_cluster[0]->node(), 'dest-port' => $node_ports[0], ); } elsif ( $vserver_data_lifs eq "delete" ) { $Log->debug( "Delete Data lif $vserver : " . $lif_state->lif() ); $lif->purge(); } } } } } if ( $vserver_root_volume eq "move" ) { my $vsroot_aggregate = NACL::CS::Aggregate->fetch( command_interface => $opts{command_interface}, filter => { aggregate => $vs_object->aggregate() }, requested_fields => ['nodes'], %common_options ); $Log->debug( Dumper( $vsroot_aggregate->nodes() ) ); if ( ( $vsroot_aggregate->nodes() )[0] eq $unjoin_node ) { $Log->debug( "Move $vserver_root_volume to $vserver_root_volume_to_aggregate" ); if ($vserver_root_volume_to_aggregate) { NACL::STask::VolumeMove->start( command_interface => $opts{command_interface}, 'volume' => $vs_object->rootvolume(), 'destination-aggregate' => $vserver_root_volume_to_aggregate, vserver => $vserver, %common_options ); } else { my $vsroot_vol = NACL::CS::Volume->fetch( command_interface => $opts{command_interface}, filter => { volume => $vs_object->rootvolume(), vserver => $vserver }, requested_fields => ['size'], %common_options ); my @all_aggrs = NACL::CS::Aggregate->fetch( command_interface => $opts{command_interface}, filter => { root => "false", availsize => ">" . $vsroot_vol->size() }, %common_options ); my @move_to_aggregates; foreach my $obj (@all_aggrs) { push @move_to_aggregates, $obj if ( $obj->owner_name() ne $unjoin_node ); } $Log->debug( " Move to Aggregates : " . Dumper( \@move_to_aggregates ) ); if ( $#move_to_aggregates >= 0 ) { $Log->debug( "Move $vserver_root_volume to " . $move_to_aggregates[0]->aggregate() ); NACL::STask::VolumeMove->start( command_interface => $opts{command_interface}, 'volume' => $vs_object->rootvolume(), 'destination-aggregate' => $move_to_aggregates[0]->aggregate(), vserver => $vserver, ); } } } } if ($rdb_check) { $Log->debug("RDB check .."); NACL::STask::Cluster->check_rings( command_interface => $opts{command_interface}, %common_options ); } $Log->exit() if $may_exit; } ## end sub unjoin 1;