# # Copyright (c) 2011-2017 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary Revert Task Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here package NACL::MTask::Revert; use strict; use warnings; use base qw(NACL::MTask::MTask); use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use Params::Validate qw(validate validate_with :types); use NATE::Exceptions::Argument qw(:try); use NACL::APISet::Exceptions::ResponseException; use NACL::Exceptions::HighAvailabilityError; use NATE::BaseException qw(:try); use NACL::ComponentUtils qw(_optional_scalars Dumper); use NACL::APISet; use NACL::STask::Node; use NACL::STask::SystemNodeImage; use NACL::CS::Cluster; use NACL::CS::DashboardHealthVserver; use NACL::STask::Volume; use NACL::CS::Volume; use NACL::STask::Aggregate; use NACL::CS::Aggregate; use NACL::STask::NetworkInterface; use NACL::CS::NetworkInterface; use NACL::C::VolumeSnapshotPolicy; use NACL::STask::Snapmirror; use NACL::C::Job; use NACL::C::JobPrivate; use NACL::STask::VolumeSnapshot; use NACL::C::SystemLicense; use NACL::STask::StorageFailover; use NACL::C::ClusterHa; use NACL::C::SecurityCertificate; use NACL::Transit; use NACL::C::AntivirusEngine; use NACL::C::Lun; use NACL::C::VserverServicesLdap; use NACL::C::VolumeFlexcache; use NACL::C::VserverCifsOptions; use NACL::C::VserverCifsShare; use NACL::C::SecuritySsl; use NACL::Exceptions::RevertFailure; use NACL::C::Cluster; use NACL::STask::StorageAggregate64bitUpgrade; use NACL::C::SystemServicesNdmpLegacy; use NACL::C::VserverCifs; use NACL::C::SystemNodeCoredump; use NACL::C::NetworkRoutingGroups; use NACL::C::NetworkRoutingGroupsRoute; use NACL::C::VserverNfs; use NACL::C::VserverIscsi; use NACL::C::VserverFcp; use NACL::C::VserverCifsDomainPreferredDc; use NACL::C::VserverServicesLdapClient; use NACL::C::VserverExportPolicyRule; use NACL::C::VserverServicesDnsHosts; use NACL::C::VserverServicesDns; use NACL::C::VserverServicesNisDomain; use NACL::STask::VolumeEfficiency; use NACL::CS::VolumeEfficiency; use NACL::CS::StorageAggregateWaflironBackup; use NACL::C::Snapvault; use NACL::C::SystemServicesNdmpNodeScopeMode; use NACL::C::Snaplock; use NACL::C::Options; use NACL::C::QosPolicyGroup; use NACL::C::QosWorkload; use NACL::C::SecuritySsh; use NACL::C::DebugVreport; use NACL::C::SnapmirrorPolicy; use NACL::C::Snapmirror; use NACL::C::SecurityConfig; use Net::IP; use NACL::InstantComponent; use NACL::CS::VolumeClone; use NACL::CS::SnapmirrorListDestinations; use NACL::C::SecurityLogin; use NACL::C::Interface; use NACL::C::Cserver; use NACL::C::VserverFpolicy; use NACL::Global; use NACL::STask::StorageAggregateObjectStore; use NACL::C::VolumeCloneSplit; use NACL::C::VserverCifsShareAccessControl; use NACL::C::VolumeQtree; use NACL::C::SystemCapability; use NACL::C::StorageErrors; use NACL::C::Disk; use Class::MethodMaker [ new => [ '-hash', '-init', 'new' ], scalar => 'command_interface', scalar => 'revert_to', scalar => 'node', scalar => 'revert_to_timeout', scalar => 'callback_on_revert', scalar => 'data_purge_allowed', scalar => 'software_update_params', scalar => 'revert_try_count', scalar => 'restore_data_after_revert', scalar => '_node_to_interface', array => '_items_to_restore', array => 'get_unrestorable_items', ]; =head1 NAME NACL::MTask::Revert =head1 SYNOPSIS # Complete Revert my $revert_obj = NACL::MTask::Revert->revert_to( command_interface => $Node, revert_to => '8.1' ); # Individual Steps my $revert_obj = NACL::MTask::Revert->new( command_interface => $Node, revert_to => '8.1', node=>$Node->node(), ); $revert_obj->perform_revert_check_cmode(); $revert_obj->perform_revert_cmode(); $revert_obj->perform_revert_check_7mode(); $revert_obj->perform_revert_7mode(); $revert_obj->boot_to_revert_image(); =head1 DESCRIPTION The NACL::MTask::Revert Task prepares the file system for reverting to a given previous release of Data ONTAP. It currently supports only revert of a cluster or cluster-mode nodes, but not 7-mode. We are not supporting 7-mode because most of Revert Events are capable to handle C-mode and 7-mode both and 'restore' can not handle 7-mode. Most of the functionality of this library involves preparing the systems for a successful revert: =over =item * Disabling features that cannot run in parallel with revert (for example, stopping serving NFS) =item * Disabling features that are not supported in the older release (for example, removing any snapshots with newer filesystem features) =item * Making any other modifications imposed in order for revert to succeed (for example, it is not allowed to leave volumes offline, as that would let them accidentally not be reverted) (is this a true example?) =back These actions are performed mainly as a reaction to the appropriate "revert" command complaining that the given feature or setting is still set. Each such message is a revert "event", and a list of such revert events is given below. The default action for each revert event will be to delete or disable or other action as needed in order for revert to be possible, but it is possible to override what action is taken for each event. =cut # @Revert_Events is used as part of implementing the # "callback_on_revert" option to "revert". It is filled in by # "push" statements seen below interspersed with the documentation for # revert events, below. It will contain a pair of array elements for # each revert event: # - the first element is a regular expression matching this event in # revert-to output. This regular expression should be in # parenthesis so that it captures the whole output in "$1" # - the second element is a subroutine which, when called, returns an # array reference. The subroutine takes no parameters, but should # look at $1, $2, etc. from the previous expression to determine # what to return. The subroutine returns an array reference, where: # - the first element is the name of the revert event # - the second element is the whole output ($1, mentioned above) # - the third and future elements are any additional key+value # options that should be passed to the callback for this revert # event. our @Revert_Events; =head2 Revert Events When one of the revert commands performed by the C routine raises an issue that needs to be dealt with (usually that some feature needs to be disabled or a structure needs to be destroyed), the C subroutine will usually just perform the necessary action to continue (disable the feature, destroy the structure). However, one can specify a different action by passing a callback to the C option to C. All of the callbacks are passed the following options: =over =item C<< revert_to_output => $string >> The output line which triggered this event =item C<< command_interface => $command_interface >> The command interface being used for revert-to/revert_to commands =back Here are the various callbacks, the message they are based on, and what arguments are passed to them: =over =item disable_vserver_http_feature =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: http.ontap.8.3.2: Disable HTTP feature introduced in Data ONTAP 8.3.2 using the command (privilege: diagnostic) "vserver http prepare-to-downgrade". =item Reason: HTTP feature introduced in Data ONTAP 8.3.2 need to be disabled before revert command is issued. =item Additional Arguments: none =back =cut push @Revert_Events, qr/(http.ontap.*: Disable HTTP feature introduced in Data ONTAP)/ => sub { [ 'disable_vserver_http_feature', $1 ] }; =item delete_all_unsupported_cluster_switches =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: unsupported.cluster.switches: Remove all unsupported cluster switches by using the command "system cluster-switch downgrade". =item Reason: All unsupported cluster switches needs to be removed before revert command is issued. =item Additional Arguments: none =back =cut push @Revert_Events, qr/(unsupported.cluster.switches: Remove all unsupported cluster switches by using the command \"system cluster-switch downgrade\")/ => sub { [ 'delete_all_unsupported_cluster_switches', $1 ] }; =over =item disable_network_interface =over =item Output: Turn Off Data LIFs (network interfaces) =item Reason: Some network interfaces are online. revert requires they be offline so that no data traffic can disrupt the revert. =back =cut push @Revert_Events, qr/(Turn Off Data LIFs|Bring the data LIFs down)/ => sub { [ 'disable_network_interface', $1, filter => { role => 'data' } ] }; =item delete_network_interface =over =item Output: One or more IPv6 LIFs must be deleted. =item Reason: IPv6 feature is not supported in previous build. So Need to deletes all IPV6 LIFs. =item Additional Arguments: filter => { 'address-family' => 'ipv6' } =back =cut push @Revert_Events, qr/(One or more IPv6 LIFs must be deleted)/ => sub { [ 'delete_network_interface', $1, filter => { 'address-family' => 'ipv6' } ]; }; =item delete_ipv6_dns =over =item Output: IPv6 addresses exist in DNS configuration. Use "vserver services dns modify" to modify IPv6 addresses to IPv4 addresses or "vserver services dns delete" to delete IPv6 addresses and try again. =item Reason: IPv6 feature is not supported in previous build. So Need to deletes IPV6 DNS. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(IPv6 addresses exist in DNS configuration)/ => sub { [ 'delete_ipv6_dns', $1, ]; }; =item delete_ipv6_dns_hosts =over =item Output: IPv6 addresses exist in DNS HOSTS configuration. Use "vserver services dns hosts modify" to modify IPv6 addresses to IPv4 addresses or "vserver services dns hosts delete" to delete IPv6 addresses and try again.IPv6 addresses exist in DNS configuration. =item Reason: IPv6 feature is not supported in previous build. So Need to deletes IPV6 DNS hosts. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(IPv6 addresses exist in DNS HOSTS configuration)/ => sub { [ 'delete_ipv6_dns_hosts', $1, ]; }; =item delete_ipv6_nis =over =item Output: IPv6 addresses exist in NIS domain configuration. Use "vserver services nis-domain modify" to modify IPv6 addresses to IPv4 addresses or "vserver services nis-domain delete" to delete IPv6 addresses and try again. =item Reason: IPv6 feature is not supported in previous build. So Need to deletes IPV6 NIS. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(IPv6 addresses exist in NIS domain configuration)/ => sub { [ 'delete_ipv6_nis', $1, ]; }; =item revert_sis_metafile_on_volume =over =item Output: Please revert SIS metafiles on Volume persist_csc to 8.1. =item Reason: Our default action is to run sis revert to volume. =item Additional Arguments: volume => $vol_name =back =cut push @Revert_Events, qr/(Please revert SIS metafiles on Volume (\S+) to (.*))/ => sub { [ 'revert_sis_metafile_on_volume', $1, volume => $2,version => $3 ] }; =item volume_efficiency_revert =over =item Output: Run the "volume efficiency revert-to" command on all volumes with efficiency. Command to list volumes with efficiency: volume show -is-sis-volume true -node local Command to revert volumes with efficiency: volume efficiency revert-to -version 8.3 -vserver * -volume * -check-snapshot false =item Reason: Run volume efficiency revert-to to volumes. =item Additional Arguments: version => scalar =back =cut push @Revert_Events, qr/(Command to revert volumes with efficiency: volume efficiency revert-to -version (\S+) )/ => sub { [ 'volume_efficiency_revert', $1, version => $2 ]; }; =item delete_fcp_iscsi_network_interface =over =item Output: Delete all [fcp|iscsi] lifs. =item Reason: FCP/ISCSI interfaces need to deleted before revert. =item Additional Arguments: filter => { 'data-protocol' => 'fcp|iscsi' } =back =cut push @Revert_Events, qr/(\QDelete all [fcp|iscsi] lifs\E)/ => sub { [ 'delete_network_interface', $1, filter => { 'data-protocol' => 'fcp|iscsi' } ]; }; =item disable_ipv6_network_route =over =item Output: Deletes the IPv6 Routing Groups (network interfaces) =item Reason: IPv6 feature is not supported in previous build. So Need to deletes all IPV6 Routing Groups and routes. =item Additional Arguments: filter => { 'address-family' => 'ipv6' } =back =cut push @Revert_Events, qr/(One or more IPv6 Routing Groups must be deleted)/ => sub { [ 'disable_ipv6_network_route', $1, filter => { 'address-family' => 'ipv6' } ]; }; =item disable_snapshot_policies =over =item Output: Disable snapshot policies =item Reason: All snapshot policies need to be disabled. =back =cut push @Revert_Events, qr/(Disable snapshot policies)/ => sub { [ 'disable_snapshot_policies', $1 ] }; =item delete_tmp_volumes =over =item Output: Delete temporary volumes =item Reason: All 'tmp' volumes should be deleted. =back =cut push @Revert_Events, qr/(Delete temporary volumes)/ => sub { [ 'delete_tmp_volumes', $1 ] }; =item delete_DP_volumes =over =item Output: Break off all data-protection (DP) volumes, and delete Uninitialized data-protection (DP) volumes. =item Reason: All 'DP' volumes should be deleted. =back =cut push @Revert_Events, qr/(Break off (?:the|all) (?:initialized )?(online )?data-protection \(DP\) volumes)/ => sub { [ 'delete_DP_volumes', $1 ] }; =item delete_LS_snapmirror_volumes =over =item Output: Delete all SnapMirror load-sharing relationships, and then delete their destination volumes. =item Reason: All SnapMirror load-sharing relationships and related volumes should be deleted. =back =cut push @Revert_Events, qr/(Delete all SnapMirror load-sharing relationships, and then delete their destination volumes)/ => sub { [ 'delete_LS_snapmirror_volumes', $1 ] }; =item delete_Vault_UDP_snapmirror_relations =over =item Output: Delete all SnapMirror relationship destination information for FlexGroup with policy type vault, mirror-vault =item Reason: All relations of type Vault and Mirror-Vault should be deleted and released from the source side =back =cut push @Revert_Events, qr/(Delete all SnapMirror relationship destination information for FlexGroup with policy type vault, mirror-vault)/ => sub { [ 'delete_Vault_UDP_snapmirror_relations', $1 ] }; =item delete_snapmirror_relationship_capability =over =item Output: Delete all SnapMirror relationships with relationship capability "8.2 and above". Command to list SnapMirror relationships with the relationship capability "8.2 and above": snapmirror show -relationship-capability "8.2 and above" Command to delete a SnapMirror relationship: snapmirror delet =item Reason: All SnapMirror relationships with the specified relationship capability should be deleted. =item Additional Arguments: relationship-capability => $string =back =cut push @Revert_Events, qr/(Delete all SnapMirror relationships with relationship capability ("[^"]+"))/s => sub { [ 'delete_snapmirror_relationship_capability', $1 , 'relationship-capability' => $2] }; =item modify_volume_autosize =over =item Output: Change volume autosize mode from "grow_shrink" to "grow" or "off" on all volumes. Command to list volumes with grow_shrink mode: volume show -autosize-mode grow_shrink Command to modify autosize mode of a volume: volume modify -vserver -volume -autosize-mode =item Reason: All volumes with autosize mode to "grow_shrink" should changed to 'off'. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(Change volume autosize mode from "grow_shrink" to "grow" or "off" on all volumes)/ => sub { [ 'modify_volume_autosize', $1 ] }; =item disable_failover =over =item Output: Disable SFO (2 steps) on all the nodes in the cluster. or Disable SFO (2 steps) on the current node =item Reason: High Availability(HA) and SFO should be disabled. =back =cut push @Revert_Events, qr/(Disable SFO \(2 steps\)|Controller Failover must be disabled before doing "revert_to")/ => sub { [ 'disable_failover', $1 ] }; =item transfer_cluster_epsilon =over =item Output: You are reverting the node that holds epsilon. To maintain the quorum, move the epsilon to the partner node. =item Reason: We are transfering the epsilon to the partner node. =back =cut push @Revert_Events, qr/(You are reverting the node that holds epsilon. To maintain the quorum, move the epsilon to the partner node)/ => sub { [ 'transfer_cluster_epsilon', $1 ] }; =item delete_file_guaranteed_vols =over =item Output: Change Volume Space Guarantee from File to Volume None. =item Reason: All 'file guarantee' type volumes must be deleted. =item Additional Arguments: none =back =cut push @Revert_Events, qr/(Change Volume Space Guarantee from File to Volume\/None)/ => sub { [ 'delete_file_guaranteed_vols', $1 ] }; =item delete_qos_workloads_provisioning =over =item Output: qos.15k_workloads_provisioning: Remove existing QoS workloads and QoS policy groups by using the following command sequence: "qos workload delete *" (privilege: diagnostic), "qos policy-group delete *". =item Reason: QoS workloads & QoS policy groups must be deleted. =item Additional Arguments: none =back =cut push @Revert_Events, qr/(qos.15k_workloads_provisioning: Remove existing QoS workloads and QoS policy groups)/ => sub { [ 'delete_qos_workloads_provisioning', $1 ] }; =item online_volumes =over =item Output: (Cmode) Some of the volumes in the cluster are not online or The cluster contains offline volumes. (7mode) All volumes must be online before doing "revert_to" =item Reason: Filer revert commands insist that all volumes be online before they will continue. =back =cut push @Revert_Events, qr/(Some of the volumes in the cluster are not online|All volumes must be online before doing|The cluster contains offline volumes)/ => sub { [ 'online_volumes', $1 ] }; =item online_aggregates =over =item Output: (Cmode) Some of the aggregates in the cluster are not online (7Mode) All aggregates must be online before doing "revert_to" =item Reason: Filer revert commands insist that all aggregates be online before they will continue. =item Additional Arguments: none =back =cut push @Revert_Events, qr/(Some of the aggregates in the cluster are not online|All aggregates must be online before doing "revert_to")/ => sub { [ 'online_aggregates', $1 ] }; =item delete_snapshots =over =item Output: Delete current version snapshots... Command to delete snapshots: snapshot delete { -fs-version VERSION } =item Reason: Delete all snapshots with the given filesystem version =item Additional Arguments: 'fs-version' = scalar or command => prepare-for-revert =back =cut push @Revert_Events, qr/(Command to list snapshots: "?snapshot show -fs-version (\S+)\" .*?\s*Command to delete snapshots)/ => sub { [ 'delete_snapshots', $1, "fs-version" => $2 ] }; push @Revert_Events, qr/(Command to delete snapshots: "?snapshot delete { -fs-version (\S+))/ => sub { [ 'delete_snapshots', $1, "fs-version" => $2 ] }; push @Revert_Events, qr/(Command to delete snapshots: "?.*snapshot (prepare-for-revert) )/ => sub { [ 'delete_snapshots', $1, command => $2 ] }; =item clear_snapmirror_labels_on_snapshot =over =item Output: Clear SnapMirror labels from all snapshot policies =item Reason: Apparently the release being reverted to does not support this snapmirror labels. Our default action is to clear all snapmirror labels. =back =cut push @Revert_Events, qr/(Clear SnapMirror labels from all snapshot policies)/ => sub { [ 'clear_snapmirror_labels_on_snapshot', $1 ] }; =item delete_ldap_config =over =item Output: One more more vserver owned LDAP client configuration\(s\) in active use. =item Reason: Filer revert commands insist that LDAP be disabled before they will continue. =back =cut push @Revert_Events, qr/(One more more vserver owned LDAP client configuration\(s\) in active use)/ => sub { [ 'delete_ldap_config', $1 ] }; =item disable_antivirus_engine =over =item Output: Disable antivirus engine. =item Reason: Filer revert commands insist that the antivirus engines be disabled before they will continue. =back =cut push @Revert_Events, qr/(Disable antivirus engine)/ => sub { [ 'disable_antivirus_engine', $1 ] }; =item delete_hybrid_aggregates =over =item Output: This node contains hybrid aggregates. Delete them to continue reverting =item Reason: Apparently the release being reverted to does not support hybrid aggregates. Our default action is to find and delete them. =back =cut push @Revert_Events, qr/(This node contains hybrid aggregates. Delete them to continue reverting)/ => sub { [ 'delete_hybrid_aggregates', $1 ] }; =item delete_advanced_zoned_aggregates =over =item Output: This node contains advanced zoned checksum aggregates =item Reason: Apparently the release being reverted to does not support zoned checksum aggregates. Our default action is to find and delete them. =back =cut push @Revert_Events, qr/(This node contains advanced zoned checksum aggregates|Destroy the advanced zoned checksum aggregate)/ => sub { [ 'delete_advanced_zoned_aggregates', $1 ] }; =item upgrade_64_bit_vols_on_32_bit_aggr =over =item Output: (Cmode) The following 32-bit aggregates contain 64-bit volumes (7mode) The 64-bit volume vol32 in a 32-bit aggregate aggr_32 cannot be reverted; either upgrade the aggregate to 64-bit or remove the volume. =item Reason: Apparently revert cannot cope with these being mismatched. Our default action will be to start to upgrade the aggregates to 64-bit and wait for that to complete. =item Additional Arguments: aggregates => \@name_of_aggrs =back =cut push @Revert_Events, qr/(The following 32-bit aggregates contain 64-bit volumes.*?Aggregate:\s*(.*?)\s*\n\s*\n|The 64-bit volume \w+ in a 32-bit aggregate (\w+) cannot be reverted)/s => sub { [ 'upgrade_64_bit_vols_on_32_bit_aggr', $1, aggregates => [ split( /\s+/, $2 || $3 ) ] ]; }; =item retry_7mode_revert_check_cmd =over =item Output: Error: "run" is not a recognized command =item Reason: Burt813994 =item Additional Arguments: None =back =cut push @Revert_Events, qr/(Error: "run" is not a recognized command)/s => sub { [ 'retry_7mode_revert_check_cmd', $1, ]; }; =item delete_big_aggr =over =item Output: The following aggregates cannot be reverted because the size =item Reason: Our default action is to delete the given aggregates =item Additional Arguments: aggregates => \@name_of_aggrs =back =cut push @Revert_Events, qr/(The following aggregates cannot be reverted because the size.*?Aggregate:\s*(.*?)\s*\n\s*\n)/s => sub { [ 'delete_big_aggr', $1, aggregates => [ split( /\s+/, $2 ) ] ] }; =item wait_for_aggregate_running_64bit_upgrade =over =item Output: The 64-bit upgrade is still in progress on the following... Aggregate: =item Reason: Revert cannot cope with this upgrade scanner running in parallel. Our default action will be to wait for it to complete. =item Additional Arguments: aggregates => \@name_of_aggrs =back =cut push @Revert_Events, qr/(The 64-bit upgrade is still in progress on the following.*?Aggregate:\s*(.*?)\s*\n\s*\n)/s => sub { [ 'wait_for_aggregate_running_64bit_upgrade', $1, aggregates => [ split( /\s+/, $2 ) ] ]; }; =item upgrade_32_bit_vols_on_64_bit_aggr =over =item Output: The following 64-bit aggregates contain 32-bit volumes =item Reason: Apparently revert cannot cope with these being mismatched. Our default action will be to start to upgrade the volumes to 64-bit and wait for that to complete. =item Additional Arguments: aggregates => \@name_of_aggrs =back =cut push @Revert_Events, qr/(The following 64-bit aggregates contain 32-bit volumes.*?Aggregate:\s*(.*?)\s*\n\s*\n)/s => sub { [ 'upgrade_32_bit_vols_on_64_bit_aggr', $1, aggregates => [ split( /\s+/, $2 ) ] ]; }; =item delete_security_certificate =over =item Output: security.certificate_management: Remove the certificates from all of =item Reason: Apparently the release being reverted to does not support SSL certificates (?). =item Additional Arguments: none =back =cut push @Revert_Events, qr/(security.certificate_management: Remove the certificates from all of)/ => sub { [ 'delete_security_certificate', $1 ] }; =item downgrade_security_config_fips =over =item Output: Restores security configuration to releases earlier than Data ONTAP 9.0.0 using the "security config prepare-to-downgrade" command =item Reason: Apparently the release being reverted to does not support FIPS security mode in the cluster. So we need to downgrade it. =item Additional Arguments: none =back =cut push @Revert_Events, qr/(security.config.fips: Disable FIPS security mode for releases earlier than Data ONTAP ${NACL::Global::ONTAP_Release_Longboard_0_short})/ => sub { [ 'downgrade_security_config_fips', $1 ] }; =item downgrade_vserver_cifs =over =item Output: cifs.restrict_anonymous: Set CIFS options to values compatible with the downgrade version of Data ONTAP using the "vserver cifs options prepare-to-downgrade" command. =item Reason: Apparently the release being reverted to does not support Current vserver cifs options configuration. So we need to downgrade it. =item Additional Arguments: none =back =cut push @Revert_Events, qr/(Set CIFS options to values compatible with the downgrade version of Data ONTAP using)/ => sub { [ 'downgrade_vserver_cifs',$1 ] }; =item downgrade_vserver_cifs_netbios_alias =over =item Output: netbios.alias: You must delete all the NetBIOS aliases on all the Vservers using the command: vserver cifs server netbios-alias prepare-to-downgrade version of Data ONTAP using the "vserver cifs options prepare-to-downgrade" command. =item Reason: Apparently the release being reverted to does not support Current NetBIOS aliases configuration on vservers. So we need to downgrade it. =item Additional Arguments: none =back =cut push @Revert_Events, qr/(You must delete all the NetBIOS aliases on all the Vservers)/ => sub { [ 'downgrade_vserver_cifs_netbios_alias',$1 ] }; =item disable_security_ssl =over =item Output: web.ssl_encryption: Disable SSL on all Vservers using the "security ssl" command =item Reason: Apparently the release being reverted to does not support SSL certificates (?). =back =cut push @Revert_Events, qr/(web.ssl_encryption: Disable SSL on all Vservers using the "security ssl" command)/ => sub { [ 'disable_security_ssl', $1 ] }; =item delete_lun =over =item Output: Delete all luns =item Reason: Apparently the release being reverted to does not support SAN. Our default action is to delete all LUNs. =back =cut push @Revert_Events, qr/(Delete all luns)/ => sub { [ 'delete_lun', $1 ] }; =item disable_nvfail =over =item Output: volume.nvfail: Disable NVFAIL feature on all cluster-mode volumes =item Reason: Apparently the release being reverted to does not support the nvfail volume option, but some volumes have it set. Our default action is to disable it on all cluster mode volumes where it is on. =item Additional Arguments: none =back =cut push @Revert_Events, qr/(volume.nvfail: Disable NVFAIL feature on all cluster-mode volumes)/ => sub { [ 'disable_nvfail', $1 ] }; =item disable_snapmirror_relationships =over =item Output: Delete all snapmirror relationships or SnapMirror must be turned off before doing "revert_to" =item Reason: Apparently the release being reverted to does not support this snapmirror engine. or The revert command cannot handle snapmirror operations happening as it's running. Our default action is to delete all snapmirror relationships. =back =cut push @Revert_Events, qr/(Delete all snapmirror relationships|SnapMirror must be turned off before doing "revert_to"|Turn SnapMirror off )/ => sub { [ 'disable_snapmirror_relationships', $1 ] }; =item disable_snapvault =over =item Output: SnapVault must be turned off =item Reason: Apparently the release being reverted to does not support this snapvault engine. Our default action is to disable snampvault. =back =cut push @Revert_Events, qr/(SnapVault must be turned off)/ => sub { [ 'disable_snapvault', $1 ] }; =item delete_flexcache_volumes =over =item Output: Delete all flexcache volumes in the cluster =item Reason: Apparently the release being reverted to does not support flexcache volumes. Our default action is to delete all such volumes. =back =cut push @Revert_Events, qr/(Delete all flexcache volumes in the cluster)/ => sub { [ 'delete_flexcache_volumes', $1 ] }; =item disable_smb2_on_vservers =over =item Output: You must disable the SMB 2.0/2.1 protocols on all Vservers =item Reason: Our default action is to disable the SMB 2.0/2.1 functionality on all the vservers. =item Additional Arguments: none =back =cut push @Revert_Events, qr/(You must disable the SMB 2.0\/2.1 protocols on all Vservers)/ => sub { [ 'disable_smb2_on_vservers', $1 ] }; =item disable_cifs_options_on_vservers =over =item Output: cifs.shadowcopy: Disable this capability on all vservers or cifs.lug: You must disable the CIFS local users and groups feature on all Vservers or cifs.smb3: You must disable the SMB 3 protocol on all Vservers. =item Reason: Our default action is to disable the CIFS options(i.e. shadowcopy,smb3, lug) functionality on all the vservers. =item Additional Arguments: cifs_option => $option #e.g. local-users-and-groups-enabled,smb3-enabled =back =cut #push @Revert_Events, # qr/(cifs\.\w+: (You must disable .* on all Vservers)|(Disable this capability on all vservers).*vserver cifs options modify .* \-(\S+) false)/ => push @Revert_Events, qr/(vserver cifs options modify -vserver \* -(\S+) false)/ => sub { [ 'disable_cifs_options_on_vservers', $1, cifs_option => "$2" ] }; =item disable_cifs_catching =over =item Output: cifs.share.offline.caching: You must disable Offline Caching on all the CIFS shares in the cluster before reverting. Use the command "cifs share modify { -offline-files !none } -offline-files none" to disable Offline Caching in the CIFS shares. =item Reason: Our default action is to disable the Offline Caching on all CIFS share. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(cifs.share.offline.caching: You must disable Offline Caching on all the CIFS shares in the cluster before reverting)/ => sub { [ 'disable_cifs_catching', $1] }; =item set_default_boot_image =over =item Output: To revert the Data ONTAP x.x boot image must be the default boot image on node NODENAME. =item Reason: The revert_to command insists that the machine be set up to boot a release matching the version being reverted to. Our default action will be to set the boot image that is not "current" to be the default boot image. =item Additional Arguments: node => $node_name =back =cut push @Revert_Events, qr/(To revert the Data ONTAP \S+ boot image must be the\s*default boot image on node (\S+))/ => sub { [ 'set_default_boot_image', $1, node => $2 ] }; =item exit_on_install_revert_to_image =over =item Output: The supported version of Data ONTAP x.x boot image is not installed on node NODENAME. =item Reason: The revert_to command insists that the machine be set up to boot a release matching the version being reverted to. Our default action will be to give up (raise an exception). =item Additional Arguments: node => $node_name =back =cut push @Revert_Events, qr/(The supported version of Data ONTAP \S+ boot image is\s*not installed on node (\S+))/ => sub { [ 'exit_on_install_revert_to_image', $1, node => $2 ] }; =item exit_on_unsupported_revert_version =over =item Output: "X.X" is an invalid value for field "-version ". =item Reason: The revert command did not like the value it was given for what release to revert to. Our default action will be to give up (raise an exception). =item Additional Arguments: version => $revert_version =back =cut push @Revert_Events, qr/("([^\"]*)" is an invalid value for field "-version ")/ => sub { [ 'exit_on_unsupported_revert_version', $1, version => $2 ] }; =item delete_snapshot_on_aggregate =over =item Output: Newer snapshots on aggregate AGGREGATE that must be deleted =item Reason: Our default action is to delete the snapshots on the given aggregate. =item Additional Arguments: aggregate => $aggregate_name =back =cut push @Revert_Events, qr/(Newer snapshots on aggregate (\S+) that must be deleted)/ => sub { [ 'delete_snapshot_on_aggregate', $1, aggregate => $2 ] }; =item delete_snapshot_on_volume =over =item Output: Newer snapshots on volume VOLUME that must be deleted =item Reason: Our default action is to delete the snapshots on the given volume. =item Additional Arguments: volume => $name_of_volume =back =cut push @Revert_Events, qr/(Newer snapshots on volume (\S+) that must be deleted)/ => sub { [ 'delete_snapshot_on_volume', $1, volume => $2 ] }; #=item delete_system_licenses # #=over # #=item Output: # #FEATURE must be terminated before doing "revert_to". # #=item Reason: # #Our default action is to disable the given feature (nfs, cifs, etc.) # #=item Additional Arguments: # #type => $feature_name # #=back # #=cut # #push @Revert_Events, # qr/((\w+) was turned off before doing "revert_to")/ => # sub { [ 'delete_system_licenses', $1, type => $2 ] }; # =item cifs_terminate =over =item Output: CIFS must be terminated before doing "revert_to". =item Reason: Our default action is to disable the given feature (nfs, cifs, etc.) =item Additional Arguments: type => $feature_name =back =cut #qr/(CIFS must be terminated before doing "revert_to")/ => push @Revert_Events, qr/(CIFS must be terminated)/ => sub { [ 'cifs_terminate', $1 ] }; =item wait_for_summery_file_cleanup =over =item Output: Summary file cleanup on aggregate/volume NAME still in progress and revert cannot occur until it is complete. Monitor status with 'wafl scan status'. =item Reason: Our default action is to wait for this activity to complete. =item Additional Arguments: either (aggregate => $name) or (volume => $name) =back =cut push @Revert_Events, qr/(Summary file cleanup on (aggregate|volume) (\S+) still in progress)/ => sub { [ 'wait_for_summery_file_cleanup', $1, $2 => $3 ] }; =item delete_savecore =over =item Output: There is a sparecore disk present which cannot be reverted. =item Reason: Our default action is to delete all the sparecore . =item Additional Arguments: None. =back =cut push @Revert_Events, qr/(There is a sparecore disk present which cannot be reverted)/ => sub { [ 'delete_savecore', $1 ] }; =item disable_snapshot_schedule =over =item Output: Snapshot schedule on volume "VOL" must be disabled prior to reverting or Snapshot schedule on aggregate "AGGR" must be disabled prior to reverting. =item Reason: Our default action is to disable the Snapshot schedule =item Additional Arguments: volume => $vol_name or aggregate => $aggr_name =back =cut push @Revert_Events, qr/(Snapshot schedule on (.*) "(\S+)" must be disabled prior to reverting)/ => sub { [ 'disable_snapshot_schedule', $1, $2 => $3 ] }; =item stop_sis_on_all_volumes =over =item Output: SIS is active on some volumes, stop sis on all volumes and retry. =item Reason: Our default action is to stop active sis operations. =item Additional Arguments: None. =back =cut push @Revert_Events, qr/(SIS is active on some volumes)/i => sub { [ 'stop_active_storage_efficiency_on_vols', $1, ] }; =item disable_ndmp_vserver_awareness =over =item Output: Disable the NDMP vserver awareness by turning on the legacy behavior. =item Reason: Our default action is to enable NDMP Legacy.. =item Additional Arguments: None. =back =cut push @Revert_Events, qr/(ndmp.vserver_aware: Disable NDMP vserver awareness by turning on the legacy behavior)/ => sub { [ 'disable_ndmp_vserver_awareness', $1 ] }; =item disable_ndmp_vserver_awareness_by_nodescope =over =item Output: Disable the NDMP vserver awareness by turning on the legacy behavior. =item Reason: Our default action is to enable NDMP Legacy.. =item Additional Arguments: None. =back =cut push @Revert_Events, qr/(ndmp.vserver_aware: Disable NDMP vserver awareness by turning on the node-scope behavior)/ => sub { [ 'disable_ndmp_vserver_awareness_by_nodescope', $1 ] }; =item disable_vserver_nfs_options =over =item Output: nfs.rquota.nfsv4x: Disable NFSv4.0, NFSv4.1 version and RQUOTA related options for all Vservers in the cluster =item Reason: Our default action is disable nfs v4.0, v4.1 and rquota options on all vservers. =item Additional Arguments: None. =back =cut push @Revert_Events, qr/(nfs.rquota.nfsv4x: Disable NFSv4.0, NFSv4.1 version and RQUOTA related options for all Vservers in the cluster)/ => sub { [ 'disable_vserver_nfs_options', $1 ] }; =item nfs_prepare_to_downgrade =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: Remove NFS configurations added in Data ONTAP 9.2.0 using the (privilege: advanced) "vserver nfs prepare-to-downgrade -disable-feature-set 9.2.0" command. =item Reason: Nfs configurations added in Data ONTAP 9.2.0 should be removed before revert. =item Additional Arguments: None. =back =cut push @Revert_Events, qr/(nfs\.ontap\.9\.2\.0: Remove NFS configurations added in Data ONTAP 9\.2\.0 using the \(privilege: advanced\) \"vserver nfs prepare-to-downgrade -disable-feature-set (9\.2\.0)\" command.)/s => sub { [ 'nfs_prepare_to_downgrade', $1, 'disable-feature-set' => $2 ]; }; =item disable_fcp_iscsi_services =over =item Output: Delete all [fcp|iscsi] services. =item Reason: Our default action is to disable fcp and iscsi services. =item Additional Arguments: None. =back =cut push @Revert_Events, qr/(\QDelete all [fcp|iscsi] services\E)/ => sub { [ 'disable_fcp_iscsi_services', $1 ] }; =item disable_iscsi_service =over =item Output: ISCSI must be turned off before doing "revert_to" =item Reason: Our default action is to disable iscsi services. =item Additional Arguments: None. =back =cut push @Revert_Events, qr/(ISCSI must be turned off before doing "revert_to")/i => sub { [ 'disable_iscsi_service', $1 ] }; =item disable_fcp_service =over =item Output: FCP must be turned off before doing "revert_to" =item Reason: Our default action is to disable FCP services. =item Additional Arguments: None. =back =cut push @Revert_Events, qr/(FCP must be turned off before doing "revert_to")/i => sub { [ 'disable_fcp_service', $1 ] }; =item set_bootarg_flag =over =item Output: Node is in non-HA mode. Set node in HA mode to continue reverting. To override this check, set the bootarg bootarg.bypass.HAmode.check to true. This override works only in debug builds =item Reason: Our default action is to set bootarg.bypass.HAmode.check flag to true. =item Additional Arguments: bootarg_flag => {bootarg.bypass.HAmode.check => 'true'} =back =cut push @Revert_Events, qr/( non-HA mode. Set nodes? in HA mode to continue reverting. To override this check, set the bootarg (\S+) to (\w+))/ => sub { [ 'set_bootarg_flag', $1, bootarg_flag => { $2 => $3 } ] }; =item delete_cifs_prederred_dc_ipv6_clients =over =item Output: IPv6 addresses exist in CIFS preferred DC client configuration. Use "vserver cifs domain prederred-dc remove" to delete IPv6 addresses and try again. =item Reason: Our default action is to delete ipv6 enabled cifs prederred-dc clients. =item Additional Arguments: None. =back =cut push @Revert_Events, qr/(IPv6 addresses exist in CIFS preferred DC client configuration)/ => sub { [ 'delete_cifs_prederred_dc_ipv6_clients', $1 ] }; =item delete_ldap_ipv6_clients =over =item Output: IPv6 addresses exist in LDAP client configuration. Use "vserver services ldap client modify" to modify IPv6 addresses to IPv4 addresses or "vserver services ldap client delete" to delete IPv6 addresses and try again. =item Reason: Our default action is to delete ipv6 enabled ldap clients. =item Additional Arguments: None. =back =cut push @Revert_Events, qr/(IPv6 addresses exist in LDAP client configuration)/ => sub { [ 'delete_ldap_ipv6_clients', $1 ] }; =item delete_ipv6_vserver_export_policy_rule =over =item Output: IPv6 addresses exist in export-policy rules configuration. Use "vserver export-policy rule modify" to modify IPv6 addresses to IPv4 addresses or "vserver export-policy rule delete" to delete IPv6 addresses. =item Reason: Our default action is to delete ipv6 enabled export-policy rule. =item Additional Arguments: None. =back =cut push @Revert_Events, qr/(IPv6 addresses exist in export-policy rules configuration)/ => sub { [ 'delete_ipv6_vserver_export_policy_rule', $1 ] }; =item delete_wafliron_backup =over =item Output: The following aggregates have a Wafliron backup image: sushant (expires 1/2/1970 19:30:02). Ask support personnel to remove all Wafliron backup images to continue revert. =item Reason: Our default action is to delete wafliron backup image on given aggregate. =item Additional Arguments: aggregate => aggr_name =back =cut push @Revert_Events, qr/(The following aggregates have a Wafliron backup image: (\S+)\s)/ => sub { [ 'delete_wafliron_backup', $1, aggregate => $2] }; =back =item vserver_prepare_for_revert =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. One or more Vservers have operations scheduled that must be disabled before revert. Command to disable: "vserver prepare-for-revert". =item Reason: pipd needs to cleanup event logs and stop the process before revert is allowed. =back =cut push @Revert_Events, qr/(One or more Vservers have operations scheduled that must be disabled before revert)/ => sub { [ 'vserver_prepare_for_revert', $1 ] }; =item VLD_is_inconsistent_with_WAFL =over =item Output: The Volume Location Database (VLDB) is inconsistent with WAFL information =item Reason: Need to verify that WAFL and VLDB volume/aggregate records are consistent. =back =cut push @Revert_Events, qr/(The Volume Location Database \(VLDB\) is inconsistent with WAFL information)/ => sub { [ 'VLD_is_inconsistent_with_WAFL',$1 ] }; =item disable_nfs_auth_sys_extended_groups =over =item Output: Found at least one Vserver with AUTH_SYS extended groups enabled. Use "vserver nfs modify -is-auth-sys-extended-groups-enabled false" to disable AUTH_SYS extended groups on those Vservers =item Reason: Need to disable AUTH_SYS extended groups =back =cut push @Revert_Events, qr/(Found at least one Vserver with AUTH_SYS extended groups enabled. Use "vserver nfs modify -(\S+) (\S+)")/ => sub { [ 'disable_nfs_auth_sys_extended_groups',$1, $2 => $3 ] }; =item retry_7mode_revert_updating_disk_registry =over =item Output: Please wait one moment while RAID updates the Failed Disk Registry. =item Reason: FDR periodic check is done once in every 5 mins to see any update is required for newly failed/unfailed disks. This operation checks in the beginning if revert is pending and if so the operation is skipped. If revert is issued after this operation has started, then revert fails with above message. operation runs just for few msecs. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(Please wait one moment while RAID updates the Failed Disk Registry)/s => sub { [ 'retry_7mode_revert_updating_disk_registry', $1, ]; }; =item set_vserver_audit_events_to_file_ops =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: multiproto.audit.cifslogonlogoff.support: Set the events audited to 'file-ops' for all the Vservers in the cluster. Use the command: vserver audit modify -vserver * -events file-ops. =item Reason: multiproto.audit.cifslogonlogoff.support capablities must be disabled before revert command is issued. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(multiproto\.audit\.cifslogonlogoff\.support: Set the events audited to 'file-ops' for all the Vservers in the cluster)/s => sub { [ 'set_vserver_audit_events_to_file_ops', $1, ]; }; =item set_vserver_audit_events_to_file_ops_logon_logoff =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Remove audit events not supported by the downgrade version of Data ONTAP. Run "vserver audit modify -vserver * -events file-ops, cifs-logon-logoff" =item Reason: New events supported in LB.0 must be disabled before revert command is issued. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(Remove audit events not supported by the downgrade version of Data ONTAP.)/s => sub { [ 'set_vserver_audit_events_to_file_ops_logon_logoff', $1, ]; }; =item disable_cifs_features =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: cifs.ontap.8.3.1: Disable CIFS features introduced in Data ONTAP 8.3.1 using the command (privilege: advanced) "vserver cifs prepare-to-downgrade -disable-feature-set 8.3.1". =item Reason: CIFS features introduced in Data ONTAP 8.3.1 need to be disabled before revert command is issued. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(cifs\.ontap\.8\.3\.1: Disable CIFS features introduced in Data ONTAP 8\.3\.1 using the command \(privilege: advanced\) \"vserver cifs prepare-to-downgrade -disable-feature-set (8\.3\.1)\".)/s => sub { [ 'disable_cifs_features', $1, 'disable-feature-set' => $2 ]; }; =item disable_cifs_features =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: cifs.8.3.2: Disable CIFS features introduced in Data ONTAP 8.3.2 using the command (privilege: advanced) "vserver cifs prepare-to-downgrade -disable-feature-set 8.3.2". =item Reason: CIFS features introduced in Data ONTAP 8.3.2 need to be disabled before revert command is issued. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(cifs\.8\.3\.2: Disable CIFS features introduced in Data ONTAP 8\.3\.2 using the command \(privilege: advanced\) \"vserver cifs prepare-to-downgrade -disable-feature-set (8\.3\.2)\".)/s => sub { [ 'disable_cifs_features', $1, 'disable-feature-set' => $2 ]; }; =item disable_cifs_features =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: cifs.8.3.3: Disable CIFS features introduced in Data ONTAP 8.3.3 using the command (privilege: advanced) "vserver cifs prepare-to-downgrade -disable-feature-set 8.3.3". =item Reason: CIFS features introduced in Data ONTAP 8.3.3 need to be disabled before revert command is issued. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(cifs\.9\.2\.0: Disable CIFS features introduced in Data ONTAP 9\.2\.0 using the command \(privilege: advanced\) \"vserver cifs prepare-to-downgrade -disable-feature-set (9\.2\.0)\".)/s => sub { [ 'disable_cifs_features', $1, 'disable-feature-set' => $2 ]; }; =item disable_cifs_features =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: cifs.9.0.0: Disable CIFS features introduced in Data ONTAP 9.0.0 using the command (privilege: advanced) "vserver cifs prepare-to-downgrade -disable-feature-set 9.0.0". =item Reason: CIFS features introduced in Data ONTAP 9.0.0 need to be disabled before revert command is issued. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(cifs\.${NACL::Global::ONTAP_Release_Longboard_0}: Disable CIFS features introduced in Data ONTAP ${NACL::Global::ONTAP_Release_Longboard_0} using the \(privilege: advanced\) \"vserver cifs prepare-to-downgrade -disable-feature-set (${NACL::Global::ONTAP_Release_Longboard_0})\" command.)/s => sub { [ 'disable_cifs_features', $1, 'disable-feature-set' => $2 ]; }; =item disable_cifs_features =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: cifs.9.0.1: Disable CIFS features introduced in Data ONTAP 9.0.1 using the command (privilege: advanced) "vserver cifs prepare-to-downgrade -disable-feature-set 9.0.1". =item Reason: CIFS features introduced in Data ONTAP 9.0.1 need to be disabled before revert command is issued. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(cifs\.${NACL::Global::ONTAP_Release_Longboard_1}: Disable CIFS features introduced in Data ONTAP ${NACL::Global::ONTAP_Release_Longboard_1} using the \(privilege: advanced\) \"vserver cifs prepare-to-downgrade -disable-feature-set (${NACL::Global::ONTAP_Release_Longboard_1})\" command.)/s => sub { [ 'disable_cifs_features', $1, 'disable-feature-set' => $2 ]; }; =item disable_fpolicy_features =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: fpolicy.ontap.9.0.0: Disable FPolicy features introduced in Data ONTAP 9.0.0 using the (privilege: advanced) "vserver fpolicy prepare-to-downgrade -disable-feature-set 9.0.0" command. =item Reason: FPolicy features introduced in Data ONTAP 9.0.0 need to be disabled before revert command is issued. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(fpolicy\.ontap\.${NACL::Global::ONTAP_Release_Longboard_0}: Disable FPolicy features introduced in Data ONTAP ${NACL::Global::ONTAP_Release_Longboard_0} using the \(privilege: advanced\) \"vserver fpolicy prepare-to-downgrade -disable-feature-set (${NACL::Global::ONTAP_Release_Longboard_0})\" command.)/s => sub { [ 'disable_fpolicy_features', $1, 'disable-feature-set' => $2 ]; }; =item disable_ddns =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: nameservices.ddns: You must disable the Dynamic DNS Update feature in the cluster before downgrading using the (privilege: advanced) "vserver services name-service dns dynamic-update prepare-to-downgrade" command. =item Reason: DDNS feature introduced in Data ONTAP 8.3.1 need to be disabled before revert command is issued. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(nameservices\.ddns: You must disable the Dynamic DNS Update feature in the cluster before downgrading)/ => sub { [ 'disable_ddns_feature', $1 ] }; =item unassigning_tdp_mirror_policy =over =item Output: Error: Change SnapMirror policy of all TDP relationships having any custom tdp-mirror policy to TDPDefault using command: snapmirror modify {-type TDP -policy !TDPDefault} -policy TDPDefault. =item Reason: tdp-mirror policies features introduced in Data ONTAP 8.3.1 need to be disabled before revert command is issued. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(Change\s*SnapMirror\s*policy\s*of\s*all\S*TDP\s*relationships\s*having\s*any\s*custom\s*tdp\-mirror\s*policy\s*to\s*TDPDefault\s*using \s*command:)/sm => sub { [ 'unassigning_tdp_mirror_policy', $1, ]; }; =item delete_custom_tdp_mirror_policy =over =item Output: Error: Delete all custom tdp-mirror policies except TDPDefault.Delete all SnapMirror policies with type tdp-mirror except TDPDefault. Command to list all SnapMirror policies to be deleted: snapmirror policy show -type tdp-mirror -policy !TDPDefault Command to delete SnapMirror policies: snapmirror policy delete {-type tdp-mirror -policy !TDPDefault} =item Reason: tdp-mirror policies features introduced in Data ONTAP 8.3.1 need to be disabled before revert command is issued. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(Delete\s*all\s*SnapMirror\s*policies\s*with\s*type\s*tdp\-mirror\s*except\s*\"TDPDefault\")/smi => sub { [ 'delete_custom_tdp_mirror_policy', $1, ]; }; =item security_ssh_prepare_to_downgrade =over =item security_login_role_prepare_to_downgrade =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: vserver.fs2_management: Update role configurations so that they are compatible with releases earlier than Data ONTAP 9.0.0 using the (privilege: advanced) "security login role prepare-to-downgrade" command\r\n =item Additional Arguments: None =back =cut push @Revert_Events, qr/(Update role configurations so that they are compatible with releases.*security login role prepare-to-downgrade)/i => sub { [ 'security_login_role_prepare_to_downgrade', $1, ]; }; =item security_login_password_prepare_to_downgrade =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed.Disable the following capabilities security.login.password.motd_support: Disable the MOTD feature that prints unsuccessful login attempts using the (privilege: advanced) "security login password-prepare-to-downgrade -disable-feature-set 8.3.1" command. security.login.password.sha2_support: Reset the system administrator password with encryption type supported by releases earlier than Data ONTAP 9.0.0 using the (privilege: advanced) "security login password-prepare-to-downgrade -disable-feature-set 9.0.0" command. =item Reason: As part of password infrastructure support for motd project, new revert event check has been introduced while reverting from dot dev to FS.0. As part of sha2 password hashing project, new revert event check has been introduced while reverting from dot dev to FS.X. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(Disable the MOTD feature that prints.*security login password-prepare-to-downgrade -disable-feature-set (8\.3\.1)\")/i => sub { [ 'security_login_password_prepare_to_downgrade', $1, 'disable-feature-set' => $2 ]; }; push @Revert_Events, qr/(security.login.password.sha2_support: Reset the system administrator password.*security login password-prepare-to-downgrade -disable-feature-set (${NACL::Global::ONTAP_Release_Longboard_0})\")/i => sub { [ 'security_login_password_prepare_to_downgrade', $1, 'disable-feature-set' => $2 ]; }; =item modify_all_cluster_peer_ipspaces_to_default =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: cluster_peer_ipspaces: All cluster peer relationships must use only the Default IPspace. To move all cluster peer relationships to the Default IPspace, use the "cluster peer modify * -ipspace Default" command. =item Reason: "cluster_peer_ipspaces" revert processing capability will be introduced in burt 865755, to FS.1. =item Additional Arguments: None =back =cut push @Revert_Events, qr/(move.*cluster peer relations.*Default IPspace.*use.*cluster peer modify \* -ipspace Default)/i => sub { [ 'modify_all_cluster_peer_ipspaces_to_default', $1, ]; }; =item volume_efficiency_prepare_to_downgrade =over =item Output: Disable the following capabilities: dense_adaptive_compression_cluster: Before downgrading, run the "volume efficiency prepare-to-downgrade" command. This command also needs to be executed on any peer clusters being used for MetroCluster or Vserver DR. dense_auto_policy_cluster: Run the "volume efficiency prepare-to-downgrade -disable-feature-set 9.3.0" command. This command also needs to be executed on any peer clusters being used for MetroCluster or Vserver DR. If there are active sis operations on one or more volumes then run the "volume efficiency stop -all true -vserver * -volume *" command first to stop storage efficiency operations on all volumes in the cluster, otherwise this command will fail. =item Additional Arguments: None =back =back =cut push @Revert_Events, qr/(dense_adaptive_compression_cluster: Before downgrading, run the \"volume efficiency prepare-to-downgrade -disable-feature-set (8\.3\.1)\")/i => sub { [ 'volume_efficiency_prepare_to_downgrade', $1, 'disable-feature-set' => $2 ]; }; push @Revert_Events, qr/(dense_inline_dedupe_cluster: Before downgrading, run the \"volume efficiency prepare-to-downgrade -disable-feature-set (8\.3\.2)\")/i => sub { ['volume_efficiency_prepare_to_downgrade', $1, 'disable-feature-set' => $2 ]; }; push @Revert_Events, qr/(dense_auto_policy_cluster:.*\"volume efficiency prepare-to-downgrade -disable-feature-set (9\.3\.0)\")/i => sub { ['volume_efficiency_prepare_to_downgrade', $1, 'disable-feature-set' => $2 ]; }; =item wait_for_sm_release_jobs_to_complete =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. SnapMirror release jobs in progress. Wait for the jobs to finish. Command to list in-progress SnapMirror release jobs: job show -jobtype ReleaseV2 -category SnapMirror =item Reason: SnapMirror release jobs need to be completed before revert is allowed. =back =cut push @Revert_Events, qr/(SnapMirror release jobs in progress. Wait for the jobs to finish.)/ => sub { [ 'wait_for_sm_release_jobs_to_complete', $1 ] }; =item security_key_manager_prepare_to_downgrade =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: onboard.keymanager.v2.capability: Disable Onboard keymanager features introduced in DATA ONTAP 9.0.1 using the (privilege: advanced) "security key-manager prepare-to-downgrade" command.\r\n =item Additional Arguments: None =back =cut push @Revert_Events, qr/(Disable Onboard keymanager features introduced in DATA ONTAP ${NACL::Global::ONTAP_Release_Longboard_1}.*security key-manager prepare-to-downgrade)/i => sub { [ 'security_key_manager_prepare_to_downgrade', $1, ]; }; =item delete_unsupported_cluster_switches =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: unsupported.cluster.switches: Remove all unsupported cluster switches by using the command "system cluster-switch prepare-to-downgrade". =item Reason: All unsupported cluster switches needs to be removed before revert command is issued. =item Additional Arguments: none =back =cut push @Revert_Events, qr/(unsupported.cluster.switches: Remove all unsupported cluster switches by using the command \"system cluster-switch prepare-to-downgrade\")/ => sub { [ 'delete_unsupported_cluster_switches', $1 ] }; =item downgrade_security_ssh =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Downgrade the SSH configuration so that it is compatible with versions of Data ONTAP earlier than 9.2.0 using the (privilege: advanced) "security ssh prepare-to-downgrade" command. =back =cut push @Revert_Events, qr/(Downgrade the SSH configuration so that it is compatible with versions of Data ONTAP earlier than.* using the \(privilege: advanced\) \"security ssh prepare-to-downgrade\" command.)/i => sub { ['downgrade_security_ssh', $1] }; =item snaplock.volume.append.mode: Disable SnapLock volume append mode feature using the (privilege: advanced) "volume snaplock prepare-to-downgrade" command =item Reason: Apparently the release being reverted to does not support SnapLock volume append mode feature. =back =cut push @Revert_Events, qr/(snaplock.volume.append.mode: Disable SnapLock volume append mode feature using the \(privilege: advanced\) "volume snaplock prepare-to-downgrade" command)/ => sub { [ 'disable_snaplock_volume_append_mode', $1 ] }; =over =item cross_vol_share_undo_start =item Reason: Apparently some aggregates have cross-volume shared data. Undo all cross-volume savings on all aggregates in the cluster. =back =cut push @Revert_Events, qr/Some aggregates have cross-volume shared data. Run the \"system node run -node \ -command aggr cross_vol_share undo-start \ \" command on all aggregates in the cluster to undo all cross-volume savings/ => sub { [ 'cross_vol_share_undo_start', $1 ] }; =over =item stop_active_storage_efficiency_on_vols =item Reason: Apparently some volumes have active storage efficiency operations. Stop such storage efficiency operations on all volumes in the cluster. =back =cut push @Revert_Events, qr/Storage efficiency operations are active on one or more volumes. Run the \"volume efficiency stop -all true -vserver \* -volume \*\" command to stop storage efficiency operations on all volumes in the cluster/ => sub { [ 'stop_active_storage_efficiency_on_vols', $1 ] }; =over =item autosize_enabled_on_flexgroups =item Reason: Apparently One or more FlexGroups have autosize enabled, which is not supported in the target release. =back =cut push @Revert_Events, qr/One or more FlexGroups have autosize enabled.*Run the \"volume modify {-vserver \* -is-flexgroup true} -autosize-mode off\" command/ => sub { [ 'autosize_enabled_on_flexgroups', $1 ] }; =over =item remove_flexGroups_from_qos_policy_groups =item Reason: Apparently cluster contains FlexGroups in a QoS policy group, which is not supported in the target release. =back =cut push @Revert_Events, qr/Cluster contains FlexGroups in a QoS policy group.*Use the \"volume modify { -volume-style-extended flexgroup } -qos-policy-group none\" command/ => sub { [ 'remove_flexGroups_from_qos_policy_groups', $1 ] }; =over =item delete_temporary_clone_parents =item Reason: Apparently some cluster contains temporary volumes that were left behind by the system when a volume with clones was moved.Delete the children or split the clone relationships. =back =cut push @Revert_Events, qr/The cluster contains temporary volumes that were left behind by the system when a volume with clones was moved.*These temporary volumes cannot be cleaned up until their clone relationships have been broken./ => sub { [ 'delete_temporary_clone_parents', $1 ] }; =over =item cancel_snapmirror_by_remov_temp_rst_relation =item Reason: Apparently some SnapMirror restore operations have RST relationships.Cancel all SnapMirror restore operations by removing all temporary RST relationships. =back =cut push @Revert_Events, qr/Cancel all SnapMirror restore operations by removing all temporary RST relationships./ => sub { [ 'cancel_snapmirror_by_remov_temp_rst_relation', $1 ] }; =over =item delete_cluster_contains_store_configurations =item Reason: Apparently cluster contains object store configurations.To continue reverting, the configurations need to be deleted. =back =cut push @Revert_Events, qr/Cluster contains object store configurations. To continue reverting, the configurations need to be deleted. Aggregates that have the object store attached to them must be deleted before the object store configuration itself can be deleted./ => sub { [ 'delete_cluster_contains_store_configurations', $1 ] }; =over =item remove_qos_min_throughput =item Reason: Apparently policy-groups with min-throughput set,list and remove the min-throughput setting from all policy-groups. =back =cut push @Revert_Events, qr/qos.min_throughput: Remove the min-throughput setting from all QoS policy-groups./ => sub { [ 'remove_qos_min_throughput', $1 ] }; =over =item disable_snaplock_v2 =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: (1) snaplock_v2: Disable SnapLock features introduced in DATA ONTAP 9.1.0 using the (privilege: advanced) "snaplock prepare-to-downgrade" command. =item Additional Arguments: none =back =cut push @Revert_Events, qr/snaplock_v2: Disable SnapLock features introduced in DATA ONTAP ${NACL::Global::ONTAP_Release_Longboard_1} using the \(privilege: advanced\) \"snaplock prepare-to-downgrade\" command./ => sub { [ 'disable_snaplock_v2', $1 ] }; =over =item delete_access_control_permission_custom =item Reason: Apparently cluster contains one or more CIFS shares has the ACL permission set to "Custom", which is not supported by the target release. =back =cut push @Revert_Events, qr/One or more CIFS shares has the ACL permission set to \"Custom\"/ => sub { [ 'delete_access_control_permission_custom', $1 ] }; =over =item prepare_flexgroups_to_revert =item Reason: Apparently FlexGroups must be prepared for revert by running the command "node run * flexgroup clean all" before proceeding. =back =cut push @Revert_Events, qr/One or more FlexGroups must be prepared for revert by running the command \"node run \* flexgroup clean all\" before proceeding./ => sub { [ 'prepare_flexgroups_to_revert', $1 ] }; =over =item disable_qtree_support =item Reason: Apparently one or more flexGroups have qtree support enabled, which is not supported in the target release. =back =cut push @Revert_Events, qr/One or more FlexGroups have qtree support enabled/ => sub { [ 'disable_qtree_support', $1 ] }; =over =item cross_vol_share_revert_to =item Reason: Apparently some aggregates have cross volume shared data,undo all cross-volume savings on all aggregates. =back =cut push @Revert_Events, qr/Some aggregates have cross volume shared data. Use the \"system node run -node \ -command aggr cross_vol_share status\" command to find cross volume shared aggregates. Run the \"system node run -node \ -command aggr cross_vol_share revert-to \\s*\" command on all found aggregates to (undo|revert) cross volume savings./ => sub { [ 'cross_vol_share_revert_to', $1 ] }; =over =item remove_adaptive_qos =item Reason: Apparently some adaptive qos configuration are present. =back =cut push @Revert_Events, qr/qos.adaptive: Remove existing adaptive QoS configuration/ => sub { [ 'remove_adaptive_qos', $1 ] }; =item handle_encryption_rekey_conversion =item Reason: Apparently one or more encrypted volumes in the cluster are undergoing or has undergone encryption rekey/conversion. =back =cut push @Revert_Events, qr/One or more encrypted volumes in the cluster are undergoing or has undergone encryption rekey\/conversion/ => sub { [ 'handle_encryption_rekey_conversion', $1 ] }; =over =item stop_active_storage_efficiency_on_aggr =item Reason: Apparently some aggregates have active storage efficiency operations. Stop such storage efficiency operations on all aggregates in the cluster. =back =cut push @Revert_Events, qr/Storage efficiency operations are active on one or more aggregates/ => sub { [ 'stop_active_storage_efficiency_on_aggr', $1 ] }; =over =item remove_snapmirror_policy_rules =item Reason: Apparently some snapmirror policies have rules associated with type vault and mirror-vault. Remove all rules with the -schedule field set in snapmirror policies. =back =cut push @Revert_Events, qr/Remove all rules with the -schedule field set in snapmirror policies/ => sub { [ 'remove_snapmirror_policy_rules', $1 ] }; =over =item disable_volume_snapmirror_v4 =item Reason: Disable the following capabilities: (1) volume.snapmirror_v4: Remove all SnapMirror consistency group relationships. =back =cut push @Revert_Events, qr/volume.snapmirror_v4: Remove all SnapMirror consistency group relationships/ => sub { [ 'disable_volume_snapmirror_v4', $1 ] }; =over =item stop_active_storage_efficiency_on_aggr_dedupe =item Reason: Apparently storage efficiency operations are active on one or more aggregates. Stop such storage efficiency operations on all aggregates in the cluster. =back =cut push @Revert_Events, qr/Storage efficiency operations are active on one or more aggregates. Run the \"aggr efficiency cross-volume-dedupe stop -aggregate \\" command on all the aggregates in the cluster to stop storage efficiency operations./ => sub { [ 'stop_active_storage_efficiency_on_aggr_dedupe', $1 ] }; =over =item _cross_vol_share_revert_to_dedupe =item Reason: Apparently some aggregates have cross volume shared data.Revert all cross volume savings on all found aggregates. =back =cut push @Revert_Events, qr/Some aggregates have cross volume shared data.*. Run the \"aggr efficiency cross-volume-dedupe revert-to -aggregate \\" command on all found aggregates to revert cross volume savings./ => sub { [ 'cross_vol_share_revert_to_dedupe', $1 ] }; =over =item volume_encryption_prepare_to_downgrade =item Reason: Apparently volume encryption feature is enable on some volumes. =back =cut push @Revert_Events, qr/volume.encryption: Disable the volume encryption feature using the \(privilege: advanced\) \"volume encryption prepare-to-downgrade\" command./ => sub { [ 'volume_encryption_prepare_to_downgrade', $1 ] }; =head1 METHODS =head2 new my $revert_obj=NACL::MTask::Revert->new( command_interface => $command_interface, revert_to => '8.1', software_update_params => { package => $testURL }, revert_to_timeout => 180, revert_try_count => 10, restore_data_after_revert => 0 ); Create a revert object that can be used to downgrade(revert) the filter OS from one ONTAP version to other version. =over =item Options: =over =item C<< command_interface => $command_interface >> (Required, isa NACL::C::CommandInterface) A component object that represents the host to which to send commands, such as a NACL::STask::Node or a NACL::C::Cluster. See "command_interface" in NACL::C::Component. =item C<< revert_to => $revert_to_version >> (Required) Specifies the version to revert, such as "8.0" or "8.1" or similar. This must be a software version that the filer's "revert_to" command can take. =item C<< node => $node_name|@node_names >> (Optional, defaults to all nodes in tthe cluster) Specifies which nodes within the cluster should be reverted. =item C<< software_update_params => \%update_param >> (Optional) If defined, then a new software image will be installed as part of the revert. This should be a reference to a hash containing options to pass to NACL::STask::SystemNodeImage->update() in order to perform the installation. If not defined, then no software image will be installed. Note that you may need to have already installed such an image if you want revert to succeed (it may complain if there is no installed image matching the revert_to version). =item C<< revert_to_timeout => $revert_to_timeout >> (Optional, defaults to '1200' seconds) Specifies the maximum amount of time to wait for a revert command to finish. If it takes longer than this then an exception is thrown (it is assumed that it is an error for it to take this long, including that it might never complete) =item C<< restore_data_after_revert => 0|1 >> (Optional, defaults to 1) If 1 (true), then after all nodes are reverted, perform restore operations (restore licenses, etc.). If 0 (false), skip this step. The step may be performed later by calling ->restore on the returned NACL::MTask::Revert object. =item C<< data_purge_allowed => 0|1 >> (Optional, defaults to 1) If 1 (true), then Task can delete the data according to respective Revert Event (delete licenses, etc.). If 0 (false), then Some Revert Events will fail, which require some data deletion. =item C<< revert_try_count => $revert_retry >> (Optional, defaults to 60) Maximum number times to fix any complaints from revert commands and then try again. If more than this many tries are required, it is assumed that revert will never succeed, and an exception is thrown. =begin comment And we're not saying what the exception message is, or that it's a NACL::BaseException, since if it's important to catch separately than we should have made a separate exception for it instead. =end comment =item C<< callback_on_revert => \%callback >> (Optional) This option may be used to override the default actions for particular revert events and/or for all revert events. This might be used to write tests which check that certain events must occur or must not occur (and stop if they are found) rather than perform the usual action to allow revert to continue. The value passed in must be a reference to a hash, where the values of that hash are callbacks (references to subroutines). For each revert event that appears in the output of a revert command (regardless of whether it's the C-mode cluster revert or 7-mode/nodescope revert_to), only one action will be performed: =over =item * If that event name is a key in the hash, then the corresponding callback is called. =item * If that event is not listed, but the special key "*" (asterisk) is in the hash, then its callback is called. =item * If none of the keys match, then the default action will be performed for that event. =back Example: # In this use case user wants to replace task way to handle this # 'upgrade_64_bit_vols_on_32_bit_aggr' method to its own subroutine my $revert_obj = NACL::MTask::Revert->revert( command_interface => $Node, revert_to => '8.0', node => $Node->hostrec()->hostname(), callback_on_revert => { '*' => sub { }, upgrade_64_bit_vols_on_32_bit_aggr => sub { my %opts = @_; my $ci = delete $opts{command_interface}; my $output = delete $opts{revert_to_output}; my $aggrs = delete $opts{aggregates}; $_64_vol_on_32_aggr_flag = 1; ...; }, }, ); =back =back =cut push @Revert_Events, qr/(Clones exist where the clone's Vserver is not the same as the FlexClone parent Vserver)/ => sub { [ 'del_clones_vserver_not_same_flxcln_parent_vserver', $1 ] }; sub init { $Log->enter() if $may_enter; my $self = shift; $Log->debug( "Opts to 'init':\n" . Dumper(@_) ) if ( $Log->may_debug() ); my %opts = validate_with( params => \@_, spec => { revert_to => { type => SCALAR }, node => { type => SCALAR | ARRAYREF, optional => 1 }, software_update_params => { type => HASHREF, optional => 1 }, revert_to_timeout => { type => SCALAR, default => 1200 }, callback_on_revert => { type => HASHREF, optional => 1 }, revert_try_count => { type => SCALAR, default => 60 }, restore_data_after_revert => { type => BOOLEAN, default => 1 }, data_purge_allowed => { type => BOOLEAN, default => 1 }, command_interface => { isa => 'NACL::C::CommandInterface::ONTAP' }, } ); while ( my ( $k, $v ) = each(%opts) ) { $self->$k($v); } # Getting the all nodes my @nodes; if ( $self->command_interface()->is_cmode() ) { @nodes = NACL::C::Cluster->node_objs( command_interface => $self->command_interface() ); } else { try { @nodes = ( $self->command_interface() ); my $partner = NACL::STask::Node->get_partner_obj( command_interface => $self->command_interface() ); push @nodes, $partner; } ## end try catch NACL::Exceptions::HighAvailabilityError with {}; } ## end else [ if ( $self->command_interface...)] my $int_hash = {}; foreach my $_node_obj (@nodes) { $int_hash->{ $_node_obj->node() } = $_node_obj; } $self->_node_to_interface($int_hash); $Log->exit() if $may_exit; } ## end sub init =head2 revert $revert_obj->revert(); (Instance Method) or my $revert_obj = NACL::MTask::Revert->revert( command_interface => $command_interface, revert_to => '8.1', software_update_params => { package => $testURL }, revert_to_timeout => 180, revert_try_count => 10, restore_data_after_revert => 0, ); (Class Method) This method will returns an object of type NACL::MTask::Revert on success. On failure, It will throw the NACL::Exceptions::RevertFailure Exception. =over =item Options: When called as an instance method, this method takes no options. When called as a class method, this method takes the same options as C, above. =back =cut sub revert { $Log->enter() if $may_enter; my $pkg_or_obj = shift; $Log->debug( "Opts to 'revert':\n" . Dumper(@_) ) if ( $Log->may_debug() ); # subroutine vars my $obj = undef; if ( ref($pkg_or_obj) ) { $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { revert_to => { type => SCALAR }, } ); $obj = $pkg_or_obj; } else { $obj = $pkg_or_obj->new(@_); } # Check stability of cluster before performing revert my @cluster_rings = NACL::CS::ClusterRing->fetch( command_interface => $obj->command_interface() ); foreach my $filer(@cluster_rings) { NATE::BaseException->throw("Cluster is not stable, cannot perform revert") if( $filer->online() !~ /secondary|master/); } my $command_interface = $obj->command_interface(); my $nodes = $obj->node(); my $revert_try_count = $obj->revert_try_count(); # Getting the all nodes my @node_names; if ( defined($nodes) ) { if ( ref($nodes) eq 'ARRAY' ) { @node_names = @{$nodes}; } else { push @node_names, $nodes; } } else { if ( $command_interface->is_cmode() ) { @node_names = NACL::C::Cluster->nodes( command_interface => $command_interface ); } else { @node_names = ( $command_interface->node() ); try { my $partner = NACL::STask::Node->get_partner( command_interface => $command_interface ); push @node_names, $partner; } catch NACL::Exceptions::HighAvailabilityError with {}; } ## end else [ if ( $command_interface...)] } ## end else [ if ( defined($nodes) )] foreach my $node (@node_names) { $Log->trace("Reverting to $node started"); $obj->revert_try_count($revert_try_count); $obj->_node_revert_to( node => $node ); $Log->trace("Reverting to $node completed"); } ## end foreach my $node (@node_names) $obj->restore() if ( $obj->restore_data_after_revert() ); $Log->exit() if $may_exit; return $obj; } ## end sub revert # (private helper method for ->revert, above, called to perform all # the steps that need to be performed on the given node) sub _node_revert_to { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; $Log->debug( "Opts to '_node_revert_to':\n" . Dumper(@_) ) if ( $Log->may_debug() ); my $node = delete $opts{node}; my $software_update_params = $self->software_update_params(); my %update_params = %{$software_update_params} if ( defined($software_update_params) ); my $command_interface = $self->{_node_to_interface}->{$node}; NACL::C::SystemNodeImage->update( command_interface => $command_interface, 'method-timeout' => 6000, %update_params, node => $node, 'replace-package' => 'true', setdefault => 'true', ) if ( defined($software_update_params) ); # Verify the status of cluster jobs and complete/delete # any running or queued entries $self->_delete_jobs( command_interface => $command_interface ); # Verify the status of private snapshot jobs and delete any queued entries $self->_delete_private_jobs( command_interface => $command_interface ); # Run revert-to -check-only true command for Cmode filer # Then Run revert-to command for Cmode filer # skips if filer is 7Mode if ( $command_interface->mode() =~ /CMode/i ) { $self->perform_revert_check_cmode( node => $node ); $self->perform_revert_cmode( node => $node ); } # Run revert_to command (check mode) in 7mode $self->perform_revert_check_7mode( node => $node, ); # Run revert_to command in 7mode $self->perform_revert_7mode( node => $node ); # $self->boot_to_revert_image( node => $node ); return; } ## end sub _node_revert_to # Verifying the Cluster Health #sub _verify_cluster_health { # $Log->enter() if $may_enter; # my $self = shift; # my $command_interface = $self->{command_interface}; # my $_obj; # # if ( $command_interface->mode() eq "CMode" ) { # my @cluster_objs = NACL::CS::Cluster->fetch( # command_interface => $command_interface, # filter => { node => $self->{node} } # ); # foreach $_obj (@cluster_objs) { # if ( $_obj->health() ne 'true' ) { # NATE::BaseException->throw("Cluster health is not 'true'"); # } # } # } ## end if ( $command_interface...) # # $Log->exit() if $may_exit; #} ## end sub _verify_cluster_health # # revert event handler: modify the volume status to online sub _online_volumes { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; my @vols = NACL::CS::Volume->fetch( command_interface => $command_interface, requested_fields => ['state'], allow_empty => 1 ); foreach my $_vol_obj (@vols) { if ( $_vol_obj->state() ne 'online' ) { $self->_update_restore_data( $_vol_obj, 'modify' ); $_vol_obj->get_component_instance()->modify( state => 'online' ); } } ## end foreach my $_vol_obj (@vols) $Log->exit() if $may_exit; } ## end sub _online_volumes # revert event handler: modify the aggregate status to online sub _online_aggregates { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; my @aggrs = NACL::CS::Aggregate->fetch( command_interface => $command_interface, requested_fields => ['state'], allow_empty => 1 ); foreach $_obj (@aggrs) { if ( $_obj->state() ne 'online' ) { $self->_update_restore_data( $_obj, 'modify' ); $_obj->get_component_instance()->modify( state => 'online' ); } } ## end foreach $_obj (@aggrs) $Log->exit() if $may_exit; } ## end sub _online_aggregates # revert event handler: delete the hybrid aggregate sub _delete_hybrid_aggregates { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; if ( !$self->data_purge_allowed() ) { $Log->exit() if $may_exit; my $event_name = ( caller(0) )[3]; $event_name =~ s/^.//; NATE::BaseException->throw( "Revert Event $event_name requires to purge data " . "and data_purge_allowed flag is set to 0." . "So We cannot proceed further." ); } ## end if ( !$self->data_purge_allowed...) my @aggrs = NACL::CS::Aggregate->fetch( command_interface => $command_interface, filter => { nodes => [ $command_interface->node() ], 'hybrid-enabled' => 'true' }, allow_empty => 1 ); foreach $_obj (@aggrs) { my @vols = NACL::CS::Volume->fetch( command_interface => $command_interface, filter => { aggregate => $_obj->aggregate() }, allow_empty => 1 ); foreach my $_vol_obj (@vols) { $self->_update_restore_data( $_vol_obj, 'create' ); my $C_obj = $_vol_obj->get_component_instance(); $C_obj->offline(); $C_obj->delete(); } ## end foreach my $_vol_obj (@vols) $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } ## end foreach $_obj (@aggrs) $Log->exit() if $may_exit; } ## end sub _delete_hybrid_aggregates # revert event handler: delete the advanced zoned aggregate sub _delete_advanced_zoned_aggregates { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; if ( !$self->data_purge_allowed() ) { $Log->exit() if $may_exit; my $event_name = ( caller(0) )[3]; $event_name =~ s/^.//; NATE::BaseException->throw( "Revert Event $event_name requires to purge data " . "and data_purge_allowed flag is set to 0." . "So We cannot proceed further." ); } ## end if ( !$self->data_purge_allowed...) my @aggrs = NACL::CS::Aggregate->fetch( command_interface => $command_interface, filter => { nodes => [ $command_interface->node() ], 'chksumstyle' => 'advanced_zoned' }, allow_empty => 1 ); foreach $_obj (@aggrs) { my @vols = NACL::CS::Volume->fetch( command_interface => $command_interface, filter => { aggregate => $_obj->aggregate() }, allow_empty => 1 ); foreach my $_vol_obj (@vols) { $self->_update_restore_data( $_vol_obj, 'create' ); my $C_obj = $_vol_obj->get_component_instance(); $C_obj->offline(); $C_obj->delete(); } ## end foreach my $_vol_obj (@vols) $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } ## end foreach $_obj (@aggrs) $Log->exit() if $may_exit; } ## end sub _delete_advanced_zoned_aggregates # revert event handler: disable the data network interface sub _disable_network_interface { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $filter = delete $opts{filter}; $filter->{'status-admin'} = 'up'; my $_obj; if ( $command_interface->mode() eq "CMode" ) { my @intfs = NACL::C::NetworkInterface->find( command_interface => $command_interface, filter => $filter, allow_empty => 1 ); foreach $_obj (@intfs) { try { $self->_items_to_restore_push( { 'modify' => { __command__ => 'network_interface_modify', __package__ => 'NACL::C::NetworkInterface', __nodename__ => $command_interface->node(), $_obj->get_primary_keys_hash(), 'status-admin' => 'up' } } ); $_obj->modify( 'status-admin' => 'down' ); } catch NATE::BaseException with { my $exception = shift; if ( $exception->text() =~ /This operation is not permitted on a Vserver that is configured as the destination of a MetroCluster/i) { $Log->warn ("This is an expected behaviour with the mcc config"); }else{ $exception->throw(); } }; } ## end foreach $_obj (@intfs) } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _disable_network_interface # revert event handler: disable the ipv6 dns config sub _delete_ipv6_dns { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $filter = delete $opts{filter}; my $_obj; my @dns_objs= NACL::CS::VserverServicesDns->fetch( command_interface => $command_interface, allow_empty => 1 ); foreach $_obj(@dns_objs) { my @ips=$_obj->name_servers(); my $ip_is_ip6 = 0; foreach my $_ip (@ips) { if ( Net::IP::ip_is_ipv6($_ip) ) { $ip_is_ip6 = 1; } } if ($ip_is_ip6) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } } $Log->exit() if $may_exit; } # revert event handler: disable the ipv6 dns host config sub _delete_ipv6_dns_hosts { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $filter = delete $opts{filter}; my $_obj; my @dns_objs= NACL::CS::VserverServicesDnsHosts->fetch( command_interface => $command_interface, allow_empty => 1 ); foreach $_obj(@dns_objs) { if ( Net::IP::ip_is_ipv6($_obj->address())) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } } $Log->exit() if $may_exit; } # revert event handler: disable the ipv6 Nis config sub _delete_ipv6_nis { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $filter = delete $opts{filter}; my $_obj; my @dns_objs= NACL::CS::VserverServicesNisDomain->fetch( command_interface => $command_interface, allow_empty => 1 ); foreach $_obj(@dns_objs) { my @ips=$_obj->servers(); my $ip_is_ip6 = 0; foreach my $_ip (@ips) { if ( Net::IP::ip_is_ipv6($_ip) ) { $ip_is_ip6 = 1; } } if ($ip_is_ip6) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } } $Log->exit() if $may_exit; } # revert event handler: Run sis revert cmd to volume sub _revert_sis_metafile_on_volume { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $volume = delete $opts{volume}; my $version = delete $opts{version}; my $Vol_Eff_Obj = NACL::C::VolumeEfficiency->find(command_interface => $command_interface, filter => {volume => $volume}); $Vol_Eff_Obj->revert_to(version => $version); $Log->exit() if $may_exit; } # revert event handler: disable the ipv6 dns host config sub _volume_efficiency_revert { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $version = delete $opts{version}; my $_obj; my @vol_objs= NACL::STask::Volume->find( command_interface => $command_interface, filter =>{ 'is-sis-volume' => 'true', node => 'local'}, ); foreach $_obj(@vol_objs) { # stop sis - ignore if already stopped or disabled $_obj->efficiency_stop(ignore_disabled=>1, nacltask_if_stopped=>'pass'); # revert $_obj->efficiency_revert_to(version=>$version, 'check-snapshot'=>'false'); } $Log->exit() if $may_exit; } # revert event handler: disable the data network interface sub _delete_network_interface { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $filter = delete $opts{filter}; my $_obj; if ( $command_interface->mode() eq "CMode" ) { my @intfs = NACL::CS::NetworkInterface->fetch( command_interface => $command_interface, filter => $filter, allow_empty => 1 ); foreach $_obj (@intfs) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _delete_network_interface # revert event handler: disable the data network interface sub _disable_ipv6_network_route { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $filter = delete $opts{filter}; my $_obj; if ( $command_interface->mode() eq "CMode" ) { my @routes = NACL::CS::NetworkRoutingGroupsRoute->fetch( command_interface => $command_interface, filter => $filter, allow_empty => 1 ); foreach $_obj (@routes) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } my @routing_groups = NACL::CS::NetworkRoutingGroups->fetch( command_interface => $command_interface, filter => $filter, allow_empty => 1 ); foreach $_obj (@routing_groups) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _disable_ipv6_network_route sub _modify_volume_autosize { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; my @vols = NACL::CS::Volume->fetch( command_interface => $command_interface, filter => {'autosize-mode' => 'grow_shrink'}, allow_empty => 1 ); foreach my $_vol_obj (@vols) { $self->_update_restore_data( $_vol_obj, 'modify' ); $_vol_obj->get_component_instance()->modify( 'autosize-mode' => 'off' ); } ## end foreach my $_vol_obj (@vols) $Log->exit() if $may_exit; } ## end sub _online_volumes # revert event handler: disable the snapshot relationship capability sub _delete_snapmirror_relationship_capability { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $relationship_capability = delete $opts{'relationship-capability'}; my $_obj; if ( $command_interface->mode() eq "CMode" ) { my @snap_policy = NACL::CS::Snapmirror->fetch( command_interface => $command_interface, filter => {'relationship-capability' => $relationship_capability}, allow_empty => 1 ); foreach $_obj (@snap_policy) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } ## end foreach $_obj (@snap_policy) } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _disable # revert event handler: disable the snapshot policy sub _disable_snapshot_policies { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; if ( $command_interface->mode() eq "CMode" ) { my @snap_policy = NACL::CS::VolumeSnapshotPolicy->fetch( command_interface => $command_interface, allow_empty => 1 ); foreach $_obj (@snap_policy) { if ( $_obj->enabled() ne 'false' ) { try { $self->_update_restore_data( $_obj, 'modify' ); $_obj->get_component_instance()->modify( enabled => 'false' ); } catch NATE::BaseException with { my $exception = shift; if ( $exception->text() =~ /This operation is not permitted on a Vserver that is configured as the destination of a MetroCluster/i) { $Log->warn ("This is an expected behaviour with the mcc config"); }else{ $exception->throw(); } }; } } ## end foreach $_obj (@snap_policy) } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _disable_snapshot_policies # revert event handler: disable the snapmirror schedule update sub _disable_snapmirror_schedule_update { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; my @snapmirrors = NACL::CS::Snapmirror->fetch( command_interface => $command_interface, allow_empty => 1 ); foreach $_obj (@snapmirrors) { $self->_update_restore_data( $_obj, 'create' ); if ( $_obj->schedule() ne '-' ) { $_obj->get_component_instance()->modify( schedule => "" ); } $_obj->get_component_instance()->delete( schedule => "" ); } ## end foreach $_obj (@snapmirrors) $Log->exit() if $may_exit; } ## end sub _disable_snapmirror_schedule_update # revert event handler: delete the currently running jobs # (Actually, this isn't handled as a revert event, though it should # be?) sub _delete_jobs { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $command_interface = delete $opts{command_interface}; my $_obj; if ( $command_interface->mode() =~ /CMode/i ) { my @jobs = NACL::C::Job->find( command_interface => $command_interface, allow_empty => 1 ); foreach my $job (@jobs) { try { $job->delete(); } catch NATE::BaseException with { my $e = shift; $Log->trace( sub { "Unable to delete job " . $e->text() } ); }; } ## end foreach my $job (@jobs) } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _delete_jobs # revert event hander: deleting the currently running private jobs # (Actually, this isn't handled as a revert event, though it should # be?) sub _delete_private_jobs { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $command_interface = delete $opts{command_interface}; my $_obj; if ( $command_interface->mode() =~ /CMode/i ) { my @jobs = NACL::C::JobPrivate->find( command_interface => $command_interface, filter => { name => 'snap*' }, allow_empty => 1 ); foreach my $job (@jobs) { try { $job->delete(); } catch NATE::BaseException with { my $e = shift; $Log->trace( sub { "Unable to delete private job " . $e->text() } ); }; } ## end foreach my $job (@jobs) } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _delete_private_jobs # revert event handler: delete the volume snapshot in D-blade sub _delete_snapshot_on_volume { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $volume = delete $opts{volume}; my $_obj; my $apiset_obj = $command_interface->get_7m_or_nodescope_apiset(); $apiset_obj->snap_delete( all => 1, force => 1, volume => 1, 'vol-name' => $volume ); $Log->exit() if $may_exit; } ## end sub _delete_snapshot_on_volume # revert event handler: delete the aggregates snapshot in D-blade sub _delete_snapshot_on_aggregate { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $aggregate = delete $opts{aggregate}; my $_obj; my $retry_count = 3; snap_delete: { try { my $apiset_obj = $command_interface->get_7m_or_nodescope_apiset(); $apiset_obj->snap_delete( all => 1, force => 1, aggregate => 1, 'aggregate-name' => $aggregate ); } catch NATE::BaseException with { my $exception = shift; if ( $exception->text() =~ /Remaining snapshots are currently in use by dump/i && $retry_count-- > 0) { Tharn::snooze(10); redo snap_delete; ## retrying 3 times } $exception->throw(); }; } ## end snap_delete $Log->exit() if $may_exit; } ## end sub _delete_snapshot_on_aggregate # revert event handler: delete the current version snapshots sub _delete_snapshots { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my %temp= %opts; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $version = delete $opts{'fs-version'}; my $command = delete $opts{command}; my $_obj; if (defined ($command) and $command eq 'prepare-for-revert') { try { NACL::STask::VolumeSnapshot->prepare_for_revert( command_interface => $command_interface, node => $command_interface->node() ); } catch NATE::BaseException with { my $exception = shift; if($exception->text() =~ /Cannot remove Snapshot copies that have an owner/i){ $self->_handle_snapshot_copy_exp(%temp); }else{ $exception->throw(); } }; }elsif(defined ($version)) { my @snapshots = NACL::STask::VolumeSnapshot->find( command_interface => $command_interface, filter => { 'fs-version' => $version }, allow_empty => 1 ); foreach $_obj (@snapshots) { try { $_obj->purge('ignore-owners'=> 'true', force => 'true'); } catch NATE::BaseException with { my $exception = shift; if($exception->text() =~ /Cannot remove Snapshot copies that have an owner/i){ $self->_handle_snapshot_copy_exp(%temp); } }; } } $Log->exit() if $may_exit; } ## end sub _delete_snapshots sub _handle_snapshot_copy_exp { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $command_interface = delete $opts{command_interface}; my $command = delete $opts{command}; my $snapshot_brk; my @owners = ("!-"); try { my @VolumeSnapshot_states = NACL::CS::VolumeSnapshot->fetch( command_interface => $command_interface, 'requested_fields' => ['owners'], 'filter' => { 'owners' => \@owners, node => $command_interface->node(), } ); foreach my $snapshot (@VolumeSnapshot_states) { try { $snapshot_brk = NACL::CS::Snapmirror->fetch( command_interface => $command_interface, 'filter' => { 'source-path' => $snapshot->{vserver} . ':' . $snapshot->{volume} }, 'requested_fields' => ['destination-path'] ); NACL::C::Snapmirror->break( command_interface => $command_interface, 'destination-path' => $snapshot_brk->{'destination_path'}, 'source-path' => $snapshot_brk->{'source_path'} ); } catch NATE::BaseException with { my $exception = shift; if ( $exception->text() =~ /must be a data-protection volume/i ) { try { NACL::C::Snapmirror->release( command_interface => $command_interface, 'destination-path' => $snapshot_brk->{'destination_path'}, 'source-path' => $snapshot_brk->{'source_path'} ); my $vol_obj = NACL::CS::VolumeClone->fetch( command_interface => $command_interface, 'filter' => { 'parent-volume' => $snapshot->{volume}, 'vserver' => $snapshot->{vserver} }, 'requested_fields' => ['flexclone'] ); NACL::C::VolumeClone->split( command_interface => $command_interface, 'flexclone' => $vol_obj->{'flexclone'}, 'vserver' => $snapshot->{vserver} ); sleep(60); NACL::C::VolumeSnapshot->prepare_for_revert( command_interface => $command_interface, node => $command_interface->node(), ); } catch NATE::BaseException with { my $exception = shift; if ( $exception->text() =~ /Cannot remove Snapshot copies that have an owner/i ) { try { NACL::CS::VolumeSnapshot->fetch( command_interface => $command_interface, 'requested_fields' => ['owners'], 'filter' => { 'owners' => \@owners, node => $command_interface->node() } ); } catch NATE::BaseException with { my $exception = shift; $Log->warn( " Objects Not Found : " . $exception->text() ); }; } }; } }; } } catch NATE::BaseException with { my $exception = shift; $Log->warn( " Objects Not Found : " . $exception->text() ); }; $Log->exit() if $may_exit; } ##end of function sub _handle_snapshot_copy_exp # revert event handler: disable SMB2 on vservers sub _disable_smb2_on_vservers { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $field_to_disable = delete $opts{field_to_disable}; my $_obj; my @cifs = NACL::CS::VserverCifsOptions->fetch( command_interface => $command_interface, allow_empty => 1 ); foreach $_obj (@cifs) { $self->_update_restore_data( $_obj, 'modify' ); $_obj->get_component_instance()->modify( 'smb2-enabled' => 'false' ); } $Log->exit() if $may_exit; } ## end sub _disable_smb2_on_vservers # revert event handler: disable cifs.shadowcopy on vservers sub _disable_cifs_options_on_vservers { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $option = delete $opts{cifs_option}; my $_obj; my @cifs = NACL::CS::VserverCifsOptions->fetch( command_interface => $command_interface, filter => { "$option" => 'true' }, allow_empty => 1 ); foreach $_obj (@cifs) { $self->_update_restore_data( $_obj, 'modify' ); $_obj->get_component_instance()->modify( "$option" => 'false' ); } $Log->exit() if $may_exit; } ## end sub _disable_cifs_options_on_vservers # revert event handler: disable cifs.share.offline.caching sub _disable_cifs_catching { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; my @cifs = NACL::CS::VserverCifsShare->fetch( command_interface => $command_interface, filter => { "offline-files" => '!none' }, allow_empty => 1 ); foreach $_obj (@cifs) { $self->_update_restore_data( $_obj, 'modify' ); my $c_obj=$_obj->get_component_instance(); $c_obj->modify( "offline-files" => 'none' ); } $Log->exit() if $may_exit; } ## end sub _disable_cifs_options_on_vservers # revert event handler: delete the flexcache volumes sub _delete_flexcache_volumes { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; if ( !$self->data_purge_allowed() ) { $Log->exit() if $may_exit; my $event_name = ( caller(0) )[3]; $event_name =~ s/^.//; NATE::BaseException->throw( "Revert Event $event_name requires to purge data " . "and data_purge_allowed flag is set to 0." . "So We cannot proceed further." ); } ## end if ( !$self->data_purge_allowed...) if ( $command_interface->mode() eq "CMode" ) { my @volsflash = NACL::CS::VolumeFlexcache->fetch( command_interface => $command_interface, allow_empty => 1 ); foreach $_obj (@volsflash) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _delete_flexcache_volumes # revert event handler: delete temporary volumes sub _delete_tmp_volumes { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; my @vols = NACL::CS::Volume->fetch( command_interface => $command_interface, filter => { type => 'TMP' }, allow_empty => 1 ); foreach $_obj (@vols) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } $Log->exit() if $may_exit; } ## end sub _delete_tmp_volumes # revert event handler: delete temporary volumes sub _delete_DP_volumes { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; my @snapmirror = NACL::CS::Snapmirror->fetch( command_interface => $command_interface, filter => { type => 'DP|XDP' }, allow_empty => 1 ); foreach $_obj (@snapmirror) { try{ $_obj->get_task_instance()->break( nacltask_if_broken => 'pass' ); } catch NATE::BaseException with { my $exception = shift; if ( $exception->text() =~ /Some or all constituents of destination FlexGroup/){ $Log->warn ("This is an expected behaviour with already broken-off flexgroup relations."); }else{ $exception->throw(); } }; } my @vol = NACL::STask::Volume->find( command_interface => $command_interface, filter => { type => 'DP', 'is-constituent' => 'false', }, allow_empty => 1 ); foreach $_obj (@vol) { try { $_obj->purge(); } catch NATE::BaseException with { my $exception = shift; if ( $exception->text() =~ /This operation is not permitted on a Vserver that is configured as the destination of a MetroCluster/i) { $Log->warn ("This is an expected behaviour with the mcc config"); }else{ $exception->throw(); } }; } $Log->exit() if $may_exit; } ## end sub _delete_DP_volumes # revert event handler: delete all snapmirror relations of policy-type mirror-vault and vault sub _delete_Vault_UDP_snapmirror_relations { $Log->enter() if $may_enter; my ($self,%opts) = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; my @snapmirror = NACL::CS::Snapmirror->fetch( command_interface => $command_interface, filter => {'policy-type' => '!async-mirror'}, allow_empty => 1 ); foreach $_obj (@snapmirror) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_task_instance()->delete(); } my @list_destinations = NACL::C::Snapmirror->list_destinations( command_interface => $command_interface, filter => { 'policy-type' => '!async-mirror' }, allow_empty => 1 ); foreach $_obj (@list_destinations) { $self->_update_restore_data( $_obj, 'create' ); #$_obj->get_task_instance()->release(); NACL::STask::Snapmirror->release( command_interface => $command_interface, 'destination-path' => $_obj->destination_path, 'relationship-info-only' => 'true', ); }; $Log->exit() if $may_exit; } ## end sub _delete_Vault_UDP_snapmirror_relations # revert event handler: delete LS snapmirror and related volume sub _delete_LS_snapmirror_volumes { $Log->enter() if $may_enter; my ($self,%opts) = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; my @snapmirror = NACL::CS::Snapmirror->fetch( command_interface => $command_interface, filter => { type => 'LS' }, allow_empty => 1 ); foreach $_obj (@snapmirror) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_task_instance()->delete(); my $vol_obj = NACL::CS::Volume->fetch( command_interface => $command_interface, filter => { volume => $_obj->get_component_instance()->get_destination_volume() } ); $self->_update_restore_data( $vol_obj, 'create' ); $vol_obj->get_task_instance()->purge(); } $Log->exit() if $may_exit; } ## end sub _delete_LS_snapmirror_volumes # revert event handler: delete the system licenses sub _delete_system_licenses { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $type = delete $opts{type}; my $command_interface = delete $opts{command_interface}; my $_obj; if ( !$self->data_purge_allowed() ) { $Log->exit() if $may_exit; my $event_name = ( caller(0) )[3]; $event_name =~ s/^.//; NATE::BaseException->throw( "Revert Event $event_name requires to purge data " . "and data_purge_allowed flag is set to 0." . "So We cannot proceed further." ); } ## end if ( !$self->data_purge_allowed...) my @lics = NACL::CS::SystemLicense->fetch( command_interface => $command_interface ); foreach my $lic (@lics) { if ( $lic->feature() =~ /$type/i ) { $self->_update_restore_data( $lic, 'add' ); $lic->get_component_instance()->delete(); } } ## end foreach my $lic (@lics) $Log->exit() if $may_exit; } ## end sub _delete_system_licenses # revert event handler: delete the system licenses sub _delete_savecore { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $command_interface = delete $opts{command_interface}; my $_obj; if ( !$self->data_purge_allowed() ) { $Log->exit() if $may_exit; my $event_name = ( caller(0) )[3]; $event_name =~ s/^.//; NATE::BaseException->throw( "Revert Event $event_name requires to purge data " . "and data_purge_allowed flag is set to 0." . "So We cannot proceed further." ); } ## end if ( !$self->data_purge_allowed...) NACL::C::SystemNodeCoredump->save_all( command_interface => $command_interface, invalidate => 1 ); $Log->exit() if $may_exit; } ## end sub _delete_savecore # revert event handler: delete the system licenses sub _cifs_terminate { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $command_interface = delete $opts{command_interface}; my $_obj; if ( !$self->data_purge_allowed() ) { $Log->exit() if $may_exit; my $event_name = ( caller(0) )[3]; $event_name =~ s/^.//; NATE::BaseException->throw( "Revert Event $event_name requires to purge data " . "and data_purge_allowed flag is set to 0." . "So We cannot proceed further." ); } ## end if ( !$self->data_purge_allowed...) NACL::C::VserverCifs->delete( command_interface => $command_interface ); $Log->exit() if $may_exit; } ## end sub _cifs_terminate # revert event handler: disable the HA config sub _disable_ha { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; if ( $command_interface->mode() eq "CMode" ) { my @objs = NACL::CS::ClusterHa->fetch( command_interface => $command_interface, allow_empty => 1 ); foreach $_obj (@objs) { if ( $_obj->configured() ne 'false' ) { $self->_update_restore_data( $_obj, 'modify' ); $_obj->get_component_instance() ->modify( configured => 'false' ); } } ## end foreach $_obj (@objs) } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _disable_ha # revert event handler: disable failover sub _disable_failover { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; $self->_disable_ha(%opts); my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; my @objs = NACL::CS::StorageFailover->fetch( command_interface => $command_interface, filter => { node => '*', enabled => 'true' }, allow_empty => 1 ); foreach $_obj (@objs) { $self->_update_restore_data( $_obj, 'modify' ); # $_obj->get_component_instance()->modify( enabled => 'false' ); NACL::STask::StorageFailover->modify( command_interface => $command_interface, node => $_obj->node(), enabled => 'false' ); } ## end foreach $_obj (@objs) $Log->exit() if $may_exit; } ## end sub _disable_failover # revert event handler: transfer cluster epsilon sub _transfer_cluster_epsilon { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; $self->_disable_ha(%opts); my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; my $partner = NACL::STask::Node->get_partner( command_interface => $command_interface ); my $time=time(); $time +=300; my $REDO=1; while($time > time() && $REDO) { $REDO++; try { try { NACL::STask::Cluster->modify( command_interface => $command_interface, epsilon => 'false', node => $command_interface->node() ); } catch NATE::BaseException with { my $e=shift; if($e->text() =~/Epsilon for \S+ is already set to/ && $REDO==2) { $Log->exit() if $may_exit; $e->throw(); } }; try { NACL::STask::Cluster->modify( command_interface => $command_interface, epsilon => 'true', node => $partner ); } catch NATE::BaseException with { my $e=shift; if($e->text() =~/Epsilon for \S+ is already set to/ && $REDO==2) { $Log->exit() if $may_exit; $e->throw(); } }; $REDO=0; } catch NATE::BaseException with { my $e=shift; if($e->text() !~ /Cluster applications are not online and stable/) { $Log->exit() if $may_exit; $e->throw(); } }; Tharn::snooze 30; } if($REDO) { $Log->exit() if $may_exit; NATE::BaseException->throw("Cluster health is not good after 300 seconds"); } $Log->exit() if $may_exit; } ## end sub _transfer_cluster_epsilon # revert event handler: downgrade the security config fips feature sub _downgrade_security_config_fips { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; NACL::C::SecurityConfig->prepare_to_downgrade( command_interface => $command_interface, %opts ); $Log->exit() if $may_exit; } ## end sub _downgrade_security_config_fips # revert event handler: disable the security ssl sub _disable_security_ssl { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; my @objs = NACL::CS::SecuritySsl->fetch( command_interface => $command_interface, allow_empty => 1 ); foreach $_obj (@objs) { $self->_update_restore_data( $_obj, 'modify' ); $_obj->get_component_instance()->modify( enabled => 'false' ); } $Log->exit() if $may_exit; } ## end sub _disable_security_ssl # revert event handler: downgrade the security ssh config sub _downgrade_security_ssh { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; NACL::C::SecuritySsh->prepare_to_downgrade( command_interface => $command_interface, %opts ); $Log->exit() if $may_exit; } ## end sub _downgrade_security_ssh # revert event handler: downgrade the vserver cifs config sub _downgrade_vserver_cifs { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; NACL::C::VserverCifsOptions->prepare_to_downgrade( command_interface => $command_interface, %opts ); $Log->exit() if $may_exit; } ## end sub _downgrade_vserver_cifs # revert event handler: downgrade the vserver netbios config sub _downgrade_vserver_cifs_netbios_alias { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; NACL::InstantComponent::instant_component( command_interface => $command_interface, component_name => 'NACL::C::VserverCifsNetbiosAlias' ); NACL::C::VserverCifsNetbiosAlias->prepare_to_downgrade( command_interface => $command_interface, %opts ); $Log->exit() if $may_exit; } ## end sub _downgrade_vserver_cifs # revert event handler: delete the security certificate sub _delete_security_certificate { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; if ( $command_interface->mode() =~ /CMode/i ) { my @objs = NACL::CS::SecurityCertificate->fetch( command_interface => $command_interface, allow_empty => 1 ); foreach $_obj (@objs) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _delete_security_certificate # revert event handler: set the other boot image to be the default sub _set_default_boot_image { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $node = delete $opts{node}; my $_obj; if ( $command_interface->mode() eq "CMode" ) { my @images = NACL::CS::SystemNodeImage->fetch( command_interface => $command_interface, filter => { node => $node } ); if ( $images[0]->iscurrent() eq 'true' ) { $images[1]->get_component_instance() ->modify( isdefault => 'true' ); } else { $images[1]->get_component_instance() ->modify( isdefault => 'false' ); } } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _set_default_boot_image # installing default boot image sub _exit_on_install_revert_to_image { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $node = delete $opts{node}; $Log->exit() if $may_exit; NATE::BaseException->throw( "Revert_to boot image is not installed to $node node.so exiting..." . Dumper($output) ); } ## end sub _exit_on_install_revert_to_image # revert event handler: the necessary boot image is not installed, so give up sub _exit_on_unsupported_revert_version { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $version = delete $opts{version}; $Log->exit() if $may_exit; NATE::BaseException->throw( "Revert-to is not supporting $version version for reverting on " . $command_interface->node() . " node. so exiting..." . Dumper($output) ); } ## end sub _exit_on_unsupported_revert_version # revert event handler: disable the antivirus engine sub _disable_antivirus_engine { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; if ( $command_interface->mode() eq "CMode" ) { NACL::C::AntivirusEngine->disable( command_interface => $command_interface ); $self->_items_to_restore_push( { 'enable' => { __command__ => 'antivirus_engine_enable', __package__ => 'NACL::C::AntivirusEngine', __nodename__ => $command_interface->node(), } } ); } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _disable_antivirus_engine # revert event handler: delete qos workload and qos policy groups sub _delete_qos_workloads_provisioning { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; if ($command_interface->mode() eq "CMode" ) { #deleing all QosWorkload my @_qos_wl= NACL::CS::QosWorkload->fetch( command_interface => $command_interface, allow_empty =>1 ); foreach $_obj(@_qos_wl) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } #deleing all PolicyGroup my @_qos_pg= NACL::CS::QosPolicyGroup->fetch( command_interface => $command_interface, allow_empty =>1 ); foreach $_obj(@_qos_pg) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _delete_qos_workloads_provisioning # revert event handler: delete the file guaranteed volumes sub _delete_file_guaranteed_vols { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; if ( !$self->data_purge_allowed() ) { $Log->exit() if $may_exit; my $event_name = ( caller(0) )[3]; $event_name =~ s/^.//; NATE::BaseException->throw( "Revert Event $event_name requires to purge data " . "and data_purge_allowed flag is set to 0." . "So We cannot proceed further." ); } ## end if ( !$self->data_purge_allowed...) my @vols = NACL::CS::Volume->fetch( command_interface => $command_interface, filter => { 'space-guarantee' => 'file' }, allow_empty => 1 ); foreach $_obj (@vols) { $self->_update_restore_data( $_obj, 'create' ); my $C_obj = $_obj->get_component_instance(); $C_obj->offline(); $C_obj->delete(); } ## end foreach $_obj (@vols) $Log->exit() if $may_exit; } ## end sub _delete_file_guaranteed_vols # revert event handler: delete the snapmirror relationships sub _disable_snapmirror_relationships { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; if( $command_interface->is_cmode()) { my @objs = NACL::CS::Snapmirror->fetch( command_interface => $command_interface, allow_empty => 1 ); foreach $_obj (@objs) { $self->_update_restore_data( $_obj, 'resync' ); $_obj->get_component_instance()->break(); } } else { NACL::C::Snapmirror->off(command_interface => $command_interface); $self->_items_to_restore_push( { 'on' => { __command__ => 'snapmirror_on', __package__ => 'NACL::C::Snapmirror', __nodename__ => $command_interface->node(), } } ); } ## end foreach $_obj (@objs) $Log->exit() if $may_exit; } ## end sub _disable_snapmirror_relationships # revert event handler: disable snapvault sub _disable_snapvault { $Log->enter() if $may_enter; my ($self, %opts) = @_; my $command_interface = $opts{command_interface}; if( $command_interface->is_7mode()) { NACL::C::Options->option( command_interface => $command_interface, 'option-name'=>'snapvault.enable','option-value'=>'off' ); } ## end $Log->exit() if $may_exit; } ## end sub _disable_snapvault # revert event handler: clear the snapmirror labels on snapshot policy sub _clear_snapmirror_labels_on_snapshot { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; if ( $command_interface->is_cmode() ) { my @snap_policy = NACL::CS::VolumeSnapshotPolicy->fetch( command_interface => $command_interface, requested_fields => ['snapmirror_labels'], allow_empty => 1 ); foreach $_obj (@snap_policy) { my @all_sm_label = $_obj->snapmirror_labels(); my @set_sm_label = grep { $_ ne '-' } @all_sm_label; if ( $#set_sm_label >= 0 ) { try { $self->_update_restore_data( $_obj, 'modify' ); $_obj->get_component_instance()->modify( 'snapmirror-labels' => ['""'] ); } catch NATE::BaseException with { my $exception = shift; if ( $exception->text() =~ /This operation is not permitted on a Vserver that is configured as the destination of a MetroCluster/i) { $Log->warn ("This is an expected behaviour with the mcc config"); }else{ $exception->throw(); } }; } } ## end foreach $_obj (@snap_policy) } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _clear_snapmirror_labels_on_snapshot # revert event handler: deleting the ldap config sub _delete_ldap_config { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; if ( $command_interface->is_cmode() ) { # Delets the ldap config only on data vservers my @data_vservers = NACL::CS::Vserver->fetch( command_interface => $command_interface, filter => { 'type' => 'data' } ); foreach my $data_vserver (@data_vservers) { my @objs = NACL::CS::VserverServicesLdapClient->fetch( command_interface => $command_interface, filter => { 'is-owner' => 'true', vserver => $data_vserver->vserver() }, allow_empty => 1 ); foreach $_obj (@objs) { my $ldap_obj = NACL::CS::VserverServicesLdap->fetch( command_interface => $command_interface, filter => { 'client-config' => $_obj->client_config() }, allow_empty => 1 ); if(defined($ldap_obj)) { $self->_update_restore_data( $ldap_obj, 'create' ); $ldap_obj->get_component_instance()->delete(); } } ## end foreach $_obj (@objs) } ## end foreach my $data_vserver (@data_vservers) } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _delete_ldap_config # revert event handler: disable nvfail sub _disable_nvfail { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; my @objs = NACL::CS::Volume->fetch( command_interface => $command_interface, filter => { 'nvfail' => 'on', 'is-cluster-volume' => 'true' }, allow_empty => 1 ); foreach $_obj (@objs) { $self->_update_restore_data( $_obj, 'modify' ); $_obj->get_component_instance()->modify( 'nvfail' => 'off' ); } $Log->exit() if $may_exit; } ## end sub _disable_nvfail # revert event handler: delete luns sub _delete_lun { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $_obj; if ( !$self->data_purge_allowed() ) { $Log->exit() if $may_exit; my $event_name = ( caller(0) )[3]; $event_name =~ s/^.//; NATE::BaseException->throw( "Revert Event $event_name requires to purge data " . "and data_purge_allowed flag is set to 0." . "So We cannot proceed further." ); } ## end if ( !$self->data_purge_allowed...) my @objs = NACL::CS::Lun->fetch( command_interface => $command_interface ); foreach $_obj (@objs) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } $Log->exit() if $may_exit; } ## end sub _delete_lun # revert event handler: upgrade the 32 bit volumes on the given 64 bit aggregates sub _upgrade_32_bit_vols_on_64_bit_aggr { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $aggrs = delete $opts{aggregates}; my $_obj; my @objs = NACL::CS::Volume->fetch( command_interface => $command_interface, filter => { aggregate => $aggrs, 'block-type' => '32-bit' }, allow_empty => 1 ); foreach $_obj (@objs) { my $vol_up_obj = NACL::STask::Volume64bitUpgrade->new( command_interface => $command_interface, volume => $_obj->volume(), vserver => $_obj->vserver() ); $vol_up_obj->start(); $vol_up_obj->wait_for_ipu(); if ( !$vol_up_obj->is_64bit_format() ) { NATE::BaseException->throw( "Unable to upgrade 32-bit volume " . $_obj->volume() . " to 64-bit" ); } } ## end foreach $_obj (@objs) $Log->exit() if $may_exit; } ## end sub _upgrade_32_bit_vols_on_64_bit_aggr # revert event handler: upgrade the given 32-bit aggregates that contain 64 bit volumes sub _upgrade_64_bit_vols_on_32_bit_aggr { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $aggrs = delete $opts{aggregates}; my $_obj; foreach my $aggr_name ( @{$aggrs} ) { my $_obj = NACL::STask::StorageAggregate64bitUpgrade->new( command_interface => $command_interface, aggregate => $aggr_name, ); $_obj->start( 'method-timeout' => 300 ); $_obj->wait_for_ipu(); if ( !$_obj->is_64bit_format() ) { NATE::BaseException->throw( "Unable to upgrade 32-bit aggr $aggr_name to 64-bit"); } } ## end foreach my $aggr_name ( @{$aggrs...}) $Log->exit() if $may_exit; } ## end sub _upgrade_64_bit_vols_on_32_bit_aggr # revert event handler: Disable NDMP vserver awareness by turning on the legacy behavior sub _disable_ndmp_vserver_awareness { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = $opts{revert_to_output}; my $command_interface = $opts{command_interface}; if ( $command_interface->mode() eq "CMode" ) { NACL::C::SystemServicesNdmpLegacy->on( command_interface => $command_interface ); $self->_items_to_restore_push( { 'off' => { __command__ => 'system_services_ndmp_legacy_off', __package__ => 'NACL::C::SystemServicesNdmpLegacy', __nodename__ => $command_interface->node(), } } ); } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _disable_ndmp_vserver_awareness # revert event handler: Disable NDMP vserver awareness by turning on the node-scope behavior sub _disable_ndmp_vserver_awareness_by_nodescope { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = $opts{revert_to_output}; my $command_interface = $opts{command_interface}; if ( $command_interface->mode() eq "CMode" ) { NACL::C::SystemServicesNdmpNodeScopeMode->on( command_interface => $command_interface ); $self->_items_to_restore_push( { 'off' => { __command__ => 'system_services_ndmp_node_scope_mode_off', __package__ => 'NACL::C::SystemServicesNdmpNodeScopeMode', __nodename__ => $command_interface->node(), } } ); } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _disable_ndmp_vserver_awareness # revert event handler: Disable vserver nfs sub _disable_vserver_nfs_options { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = $opts{revert_to_output}; my $command_interface = $opts{command_interface}; if ( $command_interface->mode() eq "CMode" ) { foreach my $nfs_opt ( "v4.0", "v4.1", "rquota" ) { my @objs = NACL::CS::VserverNfs->fetch( command_interface => $command_interface, filter => { $nfs_opt => 'enabled' }, allow_empty => 1 ); foreach my $_obj (@objs) { $self->_update_restore_data( $_obj, 'modify' ); $_obj->get_component_instance() ->modify( $nfs_opt => 'disabled' ); } } ## end foreach my $nfs_opt ( "v4.0"...) } ## end if ( $command_interface...) $Log->exit() if $may_exit; } ## end sub _disable_vserver_nfs_options # revert event handler: nfs_prepare_to_downgrade sub _nfs_prepare_to_downgrade { $Log->enter(); my ( $self, %opts ) = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; NACL::InstantComponent::instant_component( command_interface => $command_interface, component_name => 'NACL::C::VserverNfs' ); NACL::C::VserverNfs->prepare_to_downgrade( command_interface => $command_interface, %opts ); $Log->exit() if $may_exit; } # revert event handler: Disable vserver FCP for 7mode sub _disable_fcp_service { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = $opts{revert_to_output}; my $command_interface = $opts{command_interface}; NACL::C::VserverFcp->stop( command_interface => $command_interface ); $self->_items_to_restore_push( { 'start' => { __command__ => 'fcp_start', __package__ => 'NACL::C::VserverFcp', __nodename__ => $command_interface->node(), } } ); $Log->exit() if $may_exit; } ## end sub _disable_fcp_iscsi_services # revert event handler: Disable vserver ISCSI for 7mode sub _disable_iscsi_service { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = $opts{revert_to_output}; my $command_interface = $opts{command_interface}; NACL::C::VserverIscsi->stop( command_interface => $command_interface ); $self->_items_to_restore_push( { 'start' => { __command__ => 'iscsi_start', __package__ => 'NACL::C::VserverIscsi', __nodename__ => $command_interface->node(), } } ); $Log->exit() if $may_exit; } ## end sub _disable_fcp_iscsi_services # revert event handler: Disable snapshot schedule sub _disable_snapshot_schedule { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $apiset = $command_interface->get_7m_or_nodescope_apiset(); # Same Revert event occuer in 7mode and Cmode, So using apiset if ( exists $opts{volume} ) { $apiset->vol_options( volume => $opts{volume}, 'option-name' => 'nosnap', 'option-value'=> 'on'); } elsif ( exists $opts{aggregate} ) { $apiset->aggr_options( aggregate => $opts{aggregate}, 'option-name' => 'nosnap', 'option-value'=> 'on'); } $Log->exit() if $may_exit; } ## end sub _disable_snapshot_schedule # revert event handler: delete aggregates that are too big to represent in the reverted system sub _delete_big_aggr { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $aggrs = delete $opts{aggregates}; my $_obj; if ( !$self->data_purge_allowed() ) { $Log->exit() if $may_exit; my $event_name = ( caller(0) )[3]; $event_name =~ s/^.//; NATE::BaseException->throw( "Revert Event $event_name requires to purge data " . "and data_purge_allowed flag is set to 0." . "So We cannot proceed further." ); } ## end if ( !$self->data_purge_allowed...) my @objs = NACL::CS::Volume->fetch( command_interface => $command_interface, filter => { aggregate => $aggrs }, allow_empty => 1 ); foreach $_obj (@objs) { $self->_update_restore_data( $_obj, 'create' ); my $C_obj = $_obj->get_component_instance(); $C_obj->offline(); $C_obj->delete(); } ## end foreach $_obj (@objs) @objs = NACL::CS::Aggregate->fetch( command_interface => $command_interface, filter => { aggregate => $aggrs }, allow_empty => 1 ); foreach $_obj (@objs) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } $Log->exit() if $may_exit; } ## end sub _delete_big_aggr sub _retry_7mode_revert_check_cmd { $Log->enter() if $may_enter; Tharn::sleep(5); $Log->comment("run local command is not working so retrying... Burt813994"); $Log->exit() if $may_exit; } # revert event handler: wait for upgrade on aggregates still running 64bit upgrade sub _wait_for_aggregate_running_64bit_upgrade { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $aggrs = delete $opts{aggregates}; foreach my $aggr_name ( @{$aggrs} ) { my $_obj = NACL::STask::StorageAggregate64bitUpgrade->new( command_interface => $command_interface, aggregate => $aggr_name, ); $_obj->wait_for_ipu(); if ( !$_obj->is_64bit_format() ) { NATE::BaseException->throw( "Unable to complete upgrade process of 32-bit aggr $aggr_name to 64-bit " ); } } ## end foreach my $aggr_name ( @{$aggrs...}) $Log->exit() if $may_exit; $Log->exit() if $may_exit; } ## end sub _wait_for_aggregate_running_64bit_upgrade # revert event handler: wait for summary file cleanup sub _wait_for_summery_file_cleanup { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $aggrs = delete $opts{aggregate}; my $vols = delete $opts{volume}; my %options = (); $options{aggregate} = $aggrs if ( defined($aggrs) ); $options{volume} = $vols if ( defined($vols) ); my $_obj; my $apiset = $command_interface->get_7m_or_nodescope_apiset(); my $time=time()+300; my ($retry,$scanid); while($time > time()) { my $parsed_output = $apiset->wafl_scan_status(%options)->get_parsed_output(); $retry=0; foreach my $raw (@{$parsed_output->[0]->{info}}) { if($raw->{type_of_scan} =~ /blocks used summary update/i) { $retry = 1; $scanid = $raw->{scan_id}; last; } } last if (!$retry); Tharn::snooze 15; } if($retry) { $Log->debug("wafl scan is not able to complete within 300 seconds.". "So aborting this scan $scanid."); $apiset->wafl_scan_abort( scanid => $scanid ); } $Log->exit() if $may_exit; } ## end sub _wait_for_summery_file_cleanup # revert event handler: to set the bootargs flag sub _set_bootarg_flag { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $flag_to_set = delete $opts{bootarg_flag}; my ( $flag, $value ) = %$flag_to_set; my $dblade_apiset = $command_interface->get_7m_or_nodescope_apiset(); $dblade_apiset->bootargs_set( arg => $flag, value => $value ); $Log->exit() if $may_exit; } ## end sub _set_bootarg_flag # revert event handler: tp delete ipv6 cifs prederred-dc clients sub _delete_cifs_prederred_dc_ipv6_clients { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my @objs = NACL::CS::VserverCifsDomainPreferredDc->fetch( command_interface => $command_interface ); foreach my $_obj (@objs) { my @ips = $_obj->preferred_dc(); my $ip_is_ip6 = 0; foreach my $_ip (@ips) { if ( Net::IP::ip_is_ipv6($_ip) ) { $ip_is_ip6 = 1; } } if ($ip_is_ip6) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->remove(); } } ## end foreach my $_obj (@objs) $Log->exit() if $may_exit; } ## end sub _delete_cifs_prederred_dc_ipv6_clients # revert event handler: tp delete ipv6 ldap clients sub _delete_ldap_ipv6_clients { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my @objs = NACL::CS::VserverServicesLdapClient->fetch( command_interface => $command_interface, filter => { 'is-owner' => 'true' } ); foreach my $_obj (@objs) { my @ips = $_obj->servers(); my $ip_is_ip6 = 0; foreach my $_ip (@ips) { if ( Net::IP::ip_is_ipv6($_ip) ) { $ip_is_ip6 = 1; } } if ($ip_is_ip6) { my @ldap_objs=NACL::CS::VserverServicesLdap->fetch( command_interface => $command_interface, filter => {'client-config' => $_obj->client_config()}, allow_empty => 1 ); foreach my $_ldap_obj(@ldap_objs) { $self->_update_restore_data( $_ldap_obj, 'create' ); $_ldap_obj->get_component_instance()->delete(); } $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } } ## end foreach my $_obj (@objs) $Log->exit() if $may_exit; } ## end sub _delete_ldap_ipv6_clients # revert event handler: delete ipv6 vserver export-policy rules sub _delete_ipv6_vserver_export_policy_rule { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my @objs = NACL::CS::VserverExportPolicyRule->fetch( command_interface => $command_interface ); foreach my $_obj (@objs) { my @clientmatch = split('/', $_obj->clientmatch()); if ( Net::IP::ip_is_ipv6( $clientmatch[0] ) ) { $self->_update_restore_data( $_obj, 'create' ); $_obj->get_component_instance()->delete(); } } ## end foreach my $_obj (@objs) $Log->exit() if $may_exit; } ## end sub _delete_ipv6_vserver_export_policy_rule # revert event handler: delete wafliron backup image sub _delete_wafliron_backup { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $aggregate = delete $opts{aggregate}; my @objs = NACL::CS::StorageAggregateWaflironBackup->fetch( command_interface => $command_interface, filter => {aggregate => $aggregate}, allow_empty => 1); foreach my $_obj (@objs) { $_obj->get_component_instance()->delete(); } ## end foreach my $_obj (@objs) $Log->exit() if $may_exit; } # revert event handler: vserver_prepare_for_revert sub _vserver_prepare_for_revert { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my $apiset = $command_interface->apiset( interface => 'CLI', set => 'CMode' ); $apiset->vserver_prepare_for_revert(); $Log->exit() if $may_exit; } # revert event handler: VLD_is_inconsistent_with_WAFL sub _VLD_is_inconsistent_with_WAFL { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my @debug_info = NACL::C::DebugVreport->find( command_interface => $command_interface, allow_empty => 1 ); foreach my $comp (@debug_info) { $comp->fix(); } $Log->exit() if $may_exit; } # revert event handler: disable_nfs_auth_sys_extended_groups sub _disable_nfs_auth_sys_extended_groups { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; NACL::C::VserverNfs->modify( command_interface => $command_interface, vserver => '*', %opts ); $Log->exit() if $may_exit; } # helper for _update_restore_data, below. Copy the params to create/modify sub _copy_param_from_CS_object { $Log->enter() if $may_enter; my $self = shift; my $cs_obj = shift; my $command = shift; my $command_interface = shift; $command =~ s/ |-/_/g; my $apiset_mode = 'Node,CLI,' . $command_interface->mode(); my $options = $command_interface->_apisets()->{$apiset_mode}->{'schema'} ->{'commands'}->{$command}->{alias_params}; my @all_opts = keys(%$options); @all_opts = map { my $opt = $_; $opt =~ s/-/_/g; $opt; } @all_opts; my %cmd_opts; $self->_hash_copy( source => $cs_obj, target => \%cmd_opts, copy => \@all_opts ); my %return; while ( my ( $k, $v ) = each(%cmd_opts) ) { #delete ($cmd_opts{$k})if($v eq '-'); next if ( $v =~ /^-+$/ ); $k =~ s/_/-/g; $return{$k} = $v; } ## end while ( my ( $k, $v ) = each...) $Log->trace( sub { "copied params for $command are: " . Dumper( \%cmd_opts ) . Dumper( \%return ); } ); $Log->exit() if $may_exit; return %return; } ## end sub _copy_param_from_CS_object # Helper routine for ->restore phase. Copying the objects params # which are common on current build and revert_to build sub _get_revert_build_params { $Log->enter() if $may_enter; my $self = shift; my $method = shift; my $options_old = shift; my $command_interface = shift; my $command = delete( $options_old->{__command__} ); $command = $command . '_' . $method; #my $command_interface = $self->{command_interface}; my $apiset_mode = 'Node,CLI,' . $command_interface->mode(); my $options_new = $command_interface->_apisets()->{$apiset_mode}->{'schema'} ->{'commands'}->{$command}->{alias_params}; $Log->trace( sub { "In coming options valid for current build" . Dumper($options_new); } ); my @all_opts = keys(%$options_new); my %cmd_opts; $self->_hash_copy( source => $options_old, target => \%cmd_opts, copy => \@all_opts ); my %return; while ( my ( $k, $v ) = each(%cmd_opts) ) { next if ( $v =~ /^-+$/ ); $k =~ s/_/-/g; $return{$k} = $v; } $Log->trace( sub { "Outgoing options for Revert build ($command)" . Dumper( \%cmd_opts ) . Dumper( \%return ); } ); $Log->exit() if $may_exit; return %return; } ## end sub _get_revert_build_params =head2 restore $revert_obj->restore(); (Instance Method) Restore any restorable settings that were modified in order to complete any of the previous revert calls on this NACL::MTask::Revert object. This method takes no options. =cut sub restore { $Log->enter() if $may_enter; my $self = shift; $Log->debug( "Opts to 'restore':\n" . Dumper(@_) ) if ( $Log->may_debug() ); # my %opts = validate_with( # params => \@_, # spec => # { %{ NACL::C::Component->_common_validate_spec_without_ci() }, }, # ); my $command_interface = $self->{command_interface}; my @could_not_restore = (); my @items = $self->_items_to_restore(); $command_interface->refresh_command_interface(); # Refreshing the command interface my $node_2_int = $self->_node_to_interface(); while ( my ( $_node, $_ci ) = each( %{$node_2_int} ) ) { $_ci->refresh_command_interface() if ( !$_ci->apiset()->api_is_session_alive() ); } $Log->debug( "Items to Restore after Revert" . Dumper( \@items ) ); for ( my $i = $#items; $i >= 0; $i-- ) { my ( $key, $value ) = %{ $items[$i] }; my $package = delete( $value->{__package__} ); my $node_name = delete( $value->{__nodename__} ); $Log->trace( sub { "Restoring the $package->$key item on $node_name node" . Dumper($value); } ); try { delete $value->{__command__}; my %params = ( command_interface => $self->_node_to_interface()->{$node_name}, %{$value}, allow_extra => 1 ); $package->$key(%params); } ## end try catch NATE::BaseException with { my $e = shift; $Log->trace( sub { "Unable to restore $package->$key " . Dumper($e) } ); push @could_not_restore, $items[$i]; }; } ## end for ( my $i = $#items; ...) $self->get_unrestorable_items(@could_not_restore); $Log->trace( "Items to could not be restored after Revert" . Dumper( \@could_not_restore ) ); $Log->exit() if $may_exit; } ## end sub restore # A private method for revert event callbacks to use to register # cleanups for the ->restore phase. sub _update_restore_data { $Log->enter() if $may_enter; my ( $self, $_obj, $method ) = @_; my $package = $_obj->get_C_package_name(); my $command = $package; $command =~ s/NACL::[^:]+::(.*)/$1/; $command =~ s/([A-Z])/\L_$1/g; $command =~ s/^_//; $self->_items_to_restore_push( { $method => { __command__ => $command, __package__ => $package, __nodename__ => $_obj->command_interface()->node(), %{$_obj} } } ); $Log->exit() if $may_exit; } ## end sub _update_restore_data # Validate options that correspond to the attributes of this class # (options taken by ->new), in a similar style to how # $component->common_validate_with validates common component options. sub _object_attributes_validate_spec { return { revert_to => { type => SCALAR }, _node_to_interface => { type => HASHREF, optional => 1 }, software_update_params => { type => HASHREF, optional => 1 }, callback_on_revert => { type => HASHREF, optional => 1 }, _optional_scalars( qw(node revert_to_timeout revert_try_count restore_data_after_revert data_purge_allowed) ) }; } ## end sub _object_attributes_validate_spec # private back end method to provide common behavior for all four # perform_* methods (7mode or Cmode, check or non-check). sub _perform_any { $Log->enter() if $may_enter; my $self = shift; $Log->debug( "Opts to '_perform_any:\n" . Dumper(@_) ) if ( $Log->may_debug() ); my %opts = validate_with( params => \@_, spec => { revert_to_timeout => { type => SCALAR }, revert_to => { type => SCALAR }, node => { type => SCALAR }, callback_on_revert => { type => HASHREF, optional => 1 }, _node_to_interface => { type => HASHREF }, revert_try_count => { type => SCALAR }, additional_success_prompts => { type => ARRAYREF, optional => 1 }, additional_prompts => { type => ARRAYREF, optional => 1 }, restore_data_after_revert => { type => SCALAR }, software_update_params => { type => HASHREF, optional => 1 }, proceed => { type => BOOLEAN }, data_purge_allowed => { type => BOOLEAN }, description => { type => SCALAR }, invocation => { type => CODEREF }, apiset => { optional => 1 }, }, allow_extra => 1 ); my $node = $opts{node}; my $command_interface = $self->{_node_to_interface}->{ $opts{node} }; my $version = $opts{revert_to}; my $revert_tries = $opts{revert_try_count}; my $revert_to_timeout = $opts{revert_to_timeout}; my $callback = $opts{callback_on_revert}; my $data_purge_allowed = $opts{data_purge_allowed}; #my $success = 0; my $apiset_obj = delete $opts{apiset}; my $additional_success_messages = delete $opts{additional_success_prompts}; my $additional_prompts = delete $opts{additional_prompts}; my $proceed = delete $opts{proceed}; my $description = delete $opts{description}; my $revert_retry_copy = $self->revert_try_count(); my @events_seen; my @match_table; my $success; my @revert_events = @Revert_Events; $Log->trace("Running $description with max $revert_retry_copy try"); try { foreach my $message ( @{$additional_success_messages} ) { push @match_table, ( $message => sub { $success = 1; undef; } ); } ## end foreach my $message ( @{$additional_success_messages...}) if ( !$proceed ) { push @match_table, 'Are you sure you want to proceed' => sub { $success = 1; return 'no'; }; } push @match_table, ( "every controller in the cluster must be reverted.*Do you want to continue" => 'y' ); while (@revert_events) { my $event_output = shift @revert_events; my $callback = shift @revert_events; push @match_table, $event_output; push @match_table, sub { push @events_seen, $callback->(); undef; }; } ## end while (@revert_events) my $count = 1; while ( $self->revert_try_count() ) { $self->revert_try_count( $self->revert_try_count() - 1 ); $Log->debug( "Running $description (trying $count time out of $revert_retry_copy)" ); my $resp_obj; my $string = undef; try { $resp_obj = $opts{invocation} ->( \%opts, \@match_table, $additional_prompts ); } catch NATE::BaseException with { my $e = shift; $Log->debug( "revert-to command failed.(Expected)" . Dumper($e) ); }; $Log->trace( sub{"Revert Events seen: success = $success" . Dumper( \@events_seen )} ); if ( !@events_seen && !$success ) { $Log->debug( sub {"Revert Events seen: success = $success" . Dumper( \@events_seen )} ); $Log->exit() if $may_exit; NATE::BaseException->throw( "revert command failed, but no known Revert Events seen. " . "This could be a product issue or " . "Revert Task is unable to handle this issue." . 'If this is the case, please raise a burt against ' . 'nacl (type=nacl;subtype=nacl_core) or mail ' . 'dl-nacl-dev@netapp.com regarding the issue' ); } ## end if ( !@events_seen && ...) while ( my $event_seen = shift @events_seen ) { my ( $event_name, $event_output, %additional_args ) = @$event_seen; my %args = ( command_interface => $command_interface, revert_to_output => $event_output, %additional_args, ); my $coderef = $self->{callback_on_revert}->{$event_name} || $self->{callback_on_revert}->{'*'}; if ($coderef) { $Log->trace("Running callback $event_name subroutine"); $coderef->(%args); } else { my $method = '_' . $event_name; $Log->trace("Running STask $method subroutine"); $self->$method(%args); } } ## end while ( my $event_seen = ...) last if ($success); } ## end while ( $self->revert_try_count...) if ( $self->revert_try_count() <= 0 ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "revert_try_count=$revert_retry_copy exceeded (we have called the revert command this many times and performed what we know of its suggested steps each time, but this still hasn't converged on a clean revert)" ); } ## end if ( $self->revert_try_count...) } ## end try otherwise { my $e = shift; my $text=''; if($e->isa('NATE::BaseException')) { $text=$e->text(); } elsif($e->isa('NATE::Result')) { $text=$e->message_string(); } else { $Log->comment("Revert Failed with " .Dumper($e)); } $Log->exit() if $may_exit; NACL::Exceptions::RevertFailure->throw( "Unable to perform $description after " . ( $revert_retry_copy - $self->revert_try_count() ) . " retry for node $node. " . $text, revert_object => $self ); }; ## end otherwise $Log->exit() if $may_exit; return $success; } ## end sub _perform_any =head2 perform_revert_check_cmode $revert_obj->perform_revert_check_cmode(node=>'abc-vsim1'); (Instance method) Perform cluster shell level revert command ("system node revert_to") in "check" mode ("-check" option true, so no changes are committed). If any revert events are seen, then perform the actions to resolve those events and retry the command, until it succeeds or the maximum number of retries are reached. =over =item Options: =over =item C<< node => $node_name >> (Required) Specifies the node name to be reverted. =item C, C, C, C, C (Optional) C takes these options, which correspond to attributes on the $revert_to object. See C, above, for a description. If the option is not given, the setting on the object will be used. =back =back =cut sub perform_revert_check_cmode { my $self = shift; $Log->enter() if $may_enter; my %opts = $self->_common_validate_with( params => \@_, additional_spec => { node => { type => SCALAR }, revert_to => { type => SCALAR }, } ); try { $self->_perform_any( description => "CMode revert (check)", invocation => sub { my ( $args, $match_table ) = @_; my $version = $args->{revert_to}; my $apiset_obj = $self->{_node_to_interface}->{ $args->{node} }->apiset( category => 'Node', interface => 'CLI', set => "CMode" ); my $retry_count = 0; RETRY_REVERT: { try { return $apiset_obj->system_node_revert_to( node => $args->{node}, version => $version, check => 'true', 'connectrec-timeout' => $args->{revert_to_timeout}, 'connectrec-match_table' => $match_table, ); } catch NATE::BaseException with { my $ex = shift; if (($ex-> text() =~ /You must clean up configuration errors in order to perform revert./i) && ($retry_count++ == 0)) { my @storage_errors = NACL::CS::StorageErrors->fetch( command_interface => $self->{_node_to_interface}->{ $args->{node} }, requested_fields => [qw(disk error-type)], allow_empty => 1, ); my $disk_unfail_attempted = 0; foreach my $storage_data (@storage_errors) { if (grep { $_ =~ /diskfail/i } @{$storage_data->error_type()}) { NACL::C::Disk->unfail( command_interface => $self->{_node_to_interface}->{ $args->{node} }, disk => $storage_data->disk(), ); $disk_unfail_attempted = 1; } } redo RETRY_REVERT if ($disk_unfail_attempted); } $ex->throw(); }; } ##end Revert: }, additional_success_prompts => ["System node revert-to check successful"], proceed => 0, %opts, ); # as per burt#1073726, listing down all sis operation details # while performing revert check cmode itself for triaging purpose NACL::CS::VolumeEfficiency->fetch( command_interface => $self->{_node_to_interface}->{$opts{node}}, requested_fields => [qw(op-status)], allow_empty => 1, ); # as per burt#1069492, stop all SIS operations before proceeding # to the dblade revert. my @sis = NACL::STask::VolumeEfficiency->find( command_interface => $self->{_node_to_interface}->{$opts{node}}, filter => { 'op-status' => '!Idle'}, allow_empty => 1, ); foreach my $each_sis (@sis) { try { $each_sis->stop(ignore_disabled => 1, nacltask_if_stopped => 'pass'); } catch NATE::BaseException with { my $exception = shift; if ( ($exception->text() =~ /This operation is not permitted on a Vserver that is configured as the destination of a MetroCluster/i) || ($exception->text() =~ /This operation is not supported for the administrative Vserver/i) ) { # as per burt#1070348, there might be cases where stopping sis operation # is not permitted on a Vserver. $Log->trace ("this is an expected behaviour with the mcc config"); }else{ $exception->throw(); } }; } } finally { try { # as per burt#990746, list down configuration errors (if any) $self->{_node_to_interface}->{$opts{node}}->apiset()->storage_errors_show(); } otherwise { # Ignore the exception if any }; }; $Log->exit() if $may_exit; } ## end sub perform_revert_check_cmode =head2 perform_revert_check_7mode $revert_obj->perform_revert_check_7mode(node=>$node_name); As perform_revert_check_cmode, above, but drives the 7mode or node shell "revert_to" command (also in "check" mode, meaning it answers "no" to the question of whether to commit to reverting), instead of driving the cluster shell "system node revert_to" command. (Note that C-mode requires the execution of both cluster shell revert and this node shell revert) =over =item Options: As C =back =cut sub perform_revert_check_7mode { my $self = shift; $Log->enter() if $may_enter; my %opts = $self->_common_validate_with( params => \@_, additional_spec => { node => { type => SCALAR }, revert_to => { type => SCALAR }, } ); my ($ci, $count); try { $self->_perform_any( description => "7Mode/Nodescope revert (check)", invocation => sub { my ( $args, $match_table ) = @_; $count++; my $version=$self->_get_revert_to_version(revert_to => $args->{revert_to}); my $command = ( $self->command_interface()->is_cmode() ) ? ("system node run local revert_to $version") : ("revert_to $version"); $ci = $self->{_node_to_interface}->{ $args->{node} }; my $apiset = $ci->get_7m_or_nodescope_apiset(); #Burt775985 Need to resync the connection due to mgmt restart $apiset->get_connection()->resync(); return $apiset->execute_raw_command( command => $command, 'connectrec-timeout' => $args->{revert_to_timeout}, 'connectrec-match_table' => $match_table, ); }, additional_success_prompts => [], proceed => 0, %opts, ); } finally { # Code added to help with debugging issues such as burt 836136. See # this update: # http://burtweb-prd.eng.netapp.com/burt/burt-bin/start?id=836136&fz=713&btn=edit#Notes_srir_WedJul2310:43:51PDT2014 $Log->comment('Logging to help with debugging (see burt 836136)'); $Log->comment("Number of attempts of 'run local revert_to' made: $count"); try { $ci->get_systemshell_apiset()->sysctl(name => 'sysvar.boottimes'); } otherwise { # We don't want errors from diagnostic code to fail the call }; }; $Log->exit() if $may_exit; } ## end sub perform_revert_check_7mode sub _get_revert_to_version { $Log->enter() if $may_enter; my $self = shift; my %opts=@_; my $version; if ( $self->command_interface()->version_manager() ->is_version_lt( release => '8.3' ) ) { $version = ( $self->command_interface()->is_cmode() ) ? ( $opts{revert_to} . 'c' ) : ( $opts{revert_to} ); } else { $version = $opts{revert_to}; } $Log->exit() if $may_exit; return $version; } =head2 perform_revert_cmode As C, above, except the "system node revert-to" command is run in commit mode ("-check false", the default). =over =item Options: As C. =back =cut sub perform_revert_cmode { my $self = shift; $Log->enter() if $may_enter; my %opts = $self->_common_validate_with( params => \@_, additional_spec => { node => { type => SCALAR }, revert_to => { type => SCALAR }, } ); # Version details would have changed, so invalidate existing cache. NACL::C::CommandInterface::ONTAP->invalidate_version_manager_cache(); $self->_perform_any( description => "CMode revert (commit)", invocation => sub { my ( $args, $match_table, $additional_prompts ) = @_; my $version = $args->{revert_to}; # loging to console connection using transit object { my $transit_obj = NACL::Transit->new( name => $args->{node} ); $transit_obj->change_state( to => 'CLI' ); }; my $apiset_obj = $self->{_node_to_interface}->{ $args->{node} } ->apiset( connid => 'console' ); # as per burt#1082991, listing down all sis operation details # just before performing CMode revert for triaging purpose NACL::CS::VolumeEfficiency->fetch( command_interface => $self->{_node_to_interface}->{ $args->{node} }, requested_fields => [qw(op-status)], allow_empty => 1, ); my $retry = 0; # NATE::BaseException->throw("Need the test to stop here"); AGAIN: { try { my $obj = $apiset_obj->system_node_revert_to( node => $args->{node}, version => $version, 'connectrec-timeout' => $args->{revert_to_timeout}, 'connectrec-additional_prompts' => $additional_prompts, 'connectrec-match_table' => $match_table, ); return $obj; } catch NATE::BaseException with { my $ex = shift; if ( $ex->text() =~ /Storage efficiency operations are active on one or more volumes/i && $retry++ == 0 ) { eval { # temporary hack to supress errors from 'volume efficiency stop' call NACL::STask::VolumeEfficiency->stop( command_interface => $self->{_node_to_interface}->{ $args->{node} }, all => 'true', vserver => "*", volume => "*", nacltask_if_stopped => 'pass', ignore_disabled => 1, ); }; redo AGAIN; } $ex->throw(); }; } # label AGAIN: }, additional_success_prompts => [ qr/Log in again to complete the revert task/s, 'The cluster configuration has been successfully reverted' ], additional_prompts => [ 'running the "revert_to" command from the Nodeshell', qr/run local revert_to/, 'login:' ], proceed => 1, revert_try_count => 1, %opts, ); $Log->exit() if $may_exit; } ## end sub perform_revert_cmode =head2 perform_revert_7mode As C, above, except the "revert_to" command is run in commit mode (the revert_to command is run with "-i", so that it assumes it should commit). =over =item Options: As C. =back =cut sub perform_revert_7mode { my $self = shift; $Log->enter() if $may_enter; my %opts = $self->_common_validate_with( params => \@_, additional_spec => { node => { type => SCALAR }, revert_to => { type => SCALAR }, } ); # Version details would have changed, so invalidate existing cache. NACL::C::CommandInterface::ONTAP->invalidate_version_manager_cache(); $self->_perform_any( description => "7Mode/Nodescope revert (commit)", invocation => sub { my ( $args, $match_table, $additional_prompts ) = @_; my $version=$self->_get_revert_to_version(revert_to => $args->{revert_to}); my $command = ( $self->command_interface()->mode() eq "CMode" ) ? ("system node run local revert_to -i $version") : ("revert_to -i $version"); # loging to console connection using transit object { my $transit_obj = NACL::Transit->new( name => $args->{node} ); $transit_obj->change_state( to => 'CLI' ); }; my $apiset_obj = $self->{_node_to_interface}->{ $args->{node} } ->apiset( connid => 'console' ); # as per burt#1082991, listing down all sis operation details # just before performing 7Mode revert for triaging purpose my $sis_command = ( $self->command_interface()->mode() eq "CMode" ) ? ("run local sis status") : ("sis status"); $apiset_obj->execute_raw_command(command => $sis_command); my $obj = $apiset_obj->execute_raw_command( command => $command, 'connectrec-timeout' => $args->{revert_to_timeout}, 'connectrec-match_table' => $match_table, 'connectrec-additional_prompts' => $additional_prompts, ); return $obj; }, additional_success_prompts => [ 'D-Blade revert complete', 'WAFL revert complete', 'root: mroot unmounted' ], additional_prompts => [ 'The operating system has halted', 'Please press any key to reboot', 'You can now reboot the system' ], proceed => 1, revert_try_count => 1, %opts, ); $Log->exit() if $may_exit; } ## end sub perform_revert_7mode =head2 boot_to_revert_image $revert_obj->boot_to_revert_image(); (Instance method) Boot a filer which has just finished running nodescope revert_to (so it needs to be booted, and the related command_interface object needs to be refreshed). =over =item Options: =over =item C<< node => $node_name >> (Required) Specifies the node name to be reverted. =item I<< all other options >> This method also takes the same options as the C method, above. =back =back =cut sub boot_to_revert_image { $Log->enter() if $may_enter; my $self = shift; $Log->debug( "Opts to 'boot_to_revert_image':\n" . Dumper(@_) ) if ( $Log->may_debug() ); my %opts = $self->_common_validate_with( params => \@_, additional_spec => { node => { type => SCALAR }, _node_to_interface => { type => HASHREF, optional => 1 }, } ); my $node = delete $opts{node}; my $command_interface; $command_interface = $self->{_node_to_interface}->{$node}; my $transit_obj = NACL::Transit->new( name => $node ); $transit_obj->change_state( to => 'CLI', timeout => 1600 ); $command_interface->refresh_command_interface(); $self->{command_interface}->refresh_command_interface(); $command_interface->apiset(); my $admin_vserver = NACL::C::Cserver->find(command_interface => $command_interface)->cserver(); try { NACL::C::SecurityLogin->password( command_interface => $command_interface, 'vserver' => $admin_vserver, 'username' => 'diag', 'newpwd' => 'netapp1!', 'newpwd2' => 'netapp1!', ); } otherwise { # Ignore the exception if password is already set to 'netapp1!' # Case 1: This happens when one node is reverted at a time # Case 2: If the cluster is upgraded from FS to LB and then reverted back. Hash function will remain md5, hence password never expired }; NACL::C::SecurityLogin->unlock( 'command_interface' => $command_interface, 'vserver' => $admin_vserver, 'username' => "diag", ); $Log->exit() if $may_exit; } ## end sub boot_to_revert_image sub _retry_7mode_revert_updating_disk_registry { $Log->enter() if $may_enter; Tharn::sleep(5); $Log->comment("Please wait one moment while RAID updates the Failed Disk Registry... Burt840548"); $Log->exit() if $may_exit; } sub _set_vserver_audit_events_to_file_ops { $Log->enter() if $may_enter; my ($self,%opts) = @_; require NACL::STask::VserverAudit; NACL::STask::VserverAudit->modify( command_interface => $opts{command_interface}, vserver => '*', events => [qw(file-ops)], ); $Log->exit() if $may_exit; } sub _set_vserver_audit_events_to_file_ops_logon_logoff { $Log->enter() if $may_enter; my ($self,%opts) = @_; require NACL::STask::VserverAudit; NACL::STask::VserverAudit->modify( command_interface => $opts{command_interface}, vserver => '*', events => [qw(file-ops cifs-logon-logoff)], ); $Log->exit() if $may_exit; } # revert event handler: disable_cifs_features sub _disable_cifs_features { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; NACL::InstantComponent::instant_component( command_interface => $command_interface, component_name => 'NACL::C::VserverCifs' ); my $audit_retry = 0; my $properties_remove_retry = 0; RETRY: { try { NACL::C::VserverCifs->prepare_to_downgrade( command_interface => $command_interface, %opts ); } catch NATE::BaseException with { my $ex = shift; if ( $ex->text() =~ /Audit events not supported by the downgrade version of Data ONTAP need to be removed/i && $audit_retry++ == 0 ) { require NACL::STask::VserverAudit; NACL::STask::VserverAudit->prepare_to_downgrade( command_interface => $command_interface, %opts ); redo RETRY; } elsif ( $ex->text() =~ /One or more CIFS shares have share properties that are not supported on the target release/i && $properties_remove_retry++ == 0 ) { require NACL::STask::VserverCifsShareProperties; try { NACL::STask::VserverCifsShareProperties->remove( command_interface => $command_interface, 'share-name' => '*', 'share-properties' => 'show-previous-versions', ); } otherwise { # supress any error }; redo RETRY; } $Log->exit() if $may_exit; $ex->throw(); }; } # end RETRY: $Log->exit() if $may_exit; } # revert event handler: disable_fpolicy_features sub _disable_fpolicy_features { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; NACL::InstantComponent::instant_component( command_interface => $command_interface, component_name => 'NACL::C::VserverFpolicy' ); NACL::C::VserverFpolicy->prepare_to_downgrade( command_interface => $command_interface, %opts ); $Log->exit() if $may_exit; } # revert event handler: disable_ddns_feature sub _disable_ddns_feature { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; NACL::InstantComponent::instant_component( command_interface => $command_interface, component_name => 'NACL::C::VserverServicesNameServiceDnsDynamicUpdate' ); NACL::C::VserverServicesNameServiceDnsDynamicUpdate->prepare_to_downgrade( command_interface => $command_interface, ); $Log->exit() if $may_exit; } sub _unassigning_tdp_mirror_policy { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my @snap_objs = NACL::C::Snapmirror->find( command_interface => $command_interface, filter => { type => 'TDP', policy => '!TDPDefault' }, allow_empty => 1 ); foreach my $snap_mirror_obj (@snap_objs) { $snap_mirror_obj->modify( policy => 'TDPDefault' ); } ## end foreach my $_snap_policy_obj (@snap_policy) $Log->exit() if $may_exit; } ## end sub _unassigning_tdp_mirror_policy sub _delete_custom_tdp_mirror_policy { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; my @snap_policy = NACL::C::SnapmirrorPolicy->find( command_interface => $command_interface, filter => { type => 'tdp-mirror', policy => '!TDPDefault' }, allow_empty => 1 ); foreach my $snap_policy_obj (@snap_policy) { $snap_policy_obj->delete(); } ## end foreach my $_snap_policy_obj (@snap_policy) $Log->exit() if $may_exit; } ## end sub _delete_custom_tdp_mirror_policy sub _security_login_role_prepare_to_downgrade { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; require NACL::C::SecurityLoginRole; my %common_options; $self->_copy_common_component_params_with_ci( source => \%opts, target => \%common_options, ); NACL::C::SecurityLoginRole->prepare_to_downgrade( %common_options, ); $Log->exit() if $may_exit; } sub _security_login_password_prepare_to_downgrade { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; NACL::InstantComponent::instant_component( command_interface => $command_interface, component_name => 'NACL::C::SecurityLogin' ); NACL::C::SecurityLogin->password_prepare_to_downgrade( command_interface => $command_interface, password => 'netapp1!', %opts ); $Log->exit() if $may_exit; } sub _disable_vserver_http_feature { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $command_interface = delete $opts{command_interface}; NACL::InstantComponent::instant_component( command_interface => $command_interface, component_name => 'NACL::C::VserverHttp' ); NACL::C::VserverHttp->prepare_to_downgrade( command_interface => $command_interface, ); $Log->exit() if $may_exit; } ## end sub _disable_vserver_http_feature sub _delete_all_unsupported_cluster_switches { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $command_interface = delete $opts{command_interface}; NACL::InstantComponent::instant_component( command_interface => $command_interface, component_name => 'NACL::C::SystemClusterSwitch' ); NACL::C::SystemClusterSwitch->downgrade( command_interface => $command_interface, ); $Log->exit() if $may_exit; } ## end sub _delete_all_unsupported_cluster_switches sub _modify_all_cluster_peer_ipspaces_to_default { $Log->enter() if $may_enter; my ($self, %opts) = @_; require NACL::C::ClusterPeer; my %common_options; $self->_copy_common_component_params_with_ci( source => \%opts, target => \%common_options, ); NACL::C::ClusterPeer->modify( cluster => '*', ipspace => 'Default', %common_options, ); $Log->exit() if $may_exit; } ## end sub _modify_all_cluster_peer_ipspaces_to_default sub _volume_efficiency_prepare_to_downgrade { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; NACL::InstantComponent::instant_component( command_interface => $command_interface, component_name => 'NACL::C::VolumeEfficiency' ); $self->_stop_active_storage_efficiency_on_vols( command_interface => $command_interface ); NACL::STask::VolumeEfficiency->prepare_to_downgrade(command_interface => $command_interface); $Log->exit() if $may_exit; } sub _ensure_efficiency_operations_are_idle { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $command_interface = delete $opts{command_interface}; my $ignore_exception = delete $opts{ignore_exception}; my @vol_eff_not_idle = NACL::STask::VolumeEfficiency->find( command_interface => $command_interface, filter => { 'op-status' => '!Idle'}, allow_empty => 1 ); foreach (@vol_eff_not_idle) { try { $_->wait_for_status(); } catch NACL::Exceptions::NoElementsFound with { if ($ignore_exception) { # Do nothing when $ignore_exception is set # this is for the case where revert-to is active at one instant # (which means a sis operation is running), but the operation # finishes before we check whether it has finished, leading to # no entry being present in the # volume efficiency show output (this happens for volumes that # are not sis enabled in the first place) } else { my $e = shift; $e->throw(); } }; } $Log->exit() if $may_exit; } # revert event handler: _wait_for_sm_release_jobs_to_complete sub _wait_for_sm_release_jobs_to_complete { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $command_interface = delete $opts{command_interface}; my @jobs = NACL::C::Job->find( command_interface => $command_interface, filter => { jobtype => 'ReleaseV2', category => 'SnapMirror' }, allow_empty => 1 ); foreach my $job (@jobs) { $job->wait_on_job(); } $Log->exit() if $may_exit; } # revert event handler: _del_clones_vserver_not_same_flxcln_parent_vserver sub _del_clones_vserver_not_same_flxcln_parent_vserver { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; my $command_interface = delete $opts{command_interface}; my @clones = NACL::CS::VolumeClone->fetch( command_interface => $command_interface, requested_fields => [qw(vserver parent-vserver flexclone)] ); foreach my $clone_cs (@clones) { if ( $clone_cs->vserver() ne $clone_cs->parent_vserver() ) { #get the source information of the SnapMirror relationships my @list_dest = NACL::CS::SnapmirrorListDestinations->fetch( command_interface => $command_interface, filter => { 'source-vserver' => $clone_cs->vserver(), 'source-volume' => $clone_cs->flexclone(), }, allow_empty => 1, ); #release the source information of the SnapMirror relationships foreach my $dest_obj (@list_dest) { NACL::STask::Snapmirror->release( command_interface => $command_interface, 'destination-path' => $dest_obj->destination_path(), nacltask_wait => 1, ); } $clone_cs->get_task_instance()->purge(); } } $Log->exit() if $may_exit; } ## end sub _del_clones_vserver_not_same_flxcln_parent_vserver # revert event handler: disable onboard key manager features for 9.0.1 sub _security_key_manager_prepare_to_downgrade { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; my $output = delete $opts{revert_to_output}; my $command_interface = delete $opts{command_interface}; NACL::InstantComponent::instant_component( command_interface => $command_interface, component_name => 'NACL::C::SecurityKeyManager' ); NACL::C::SecurityKeyManager->prepare_to_downgrade( command_interface => $command_interface, %opts ); $Log->exit() if $may_exit; } sub _delete_unsupported_cluster_switches { $Log->enter() if $may_enter; my $self = shift; my %opts = @_; my $command_interface = delete $opts{command_interface}; NACL::InstantComponent::instant_component( command_interface => $command_interface, component_name => 'NACL::C::SystemClusterSwitch' ); NACL::C::SystemClusterSwitch->prepare_to_downgrade( command_interface => $command_interface, ); $Log->exit() if $may_exit; } ## end sub _delete_unsupported_cluster_switches # revert event handler: disable_snaplock_volume_append_mode sub _disable_snaplock_volume_append_mode { $Log->enter() if $may_enter; my ($self, %opts) = @_; NACL::InstantComponent::instant_component( command_interface => $opts{command_interface}, component_name => 'NACL::C::VolumeSnaplock' ); my %common_opts; $self->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); NACL::C::VolumeSnaplock->prepare_to_downgrade(%common_opts); $Log->exit() if $may_exit; } # revert event handler: cross_vol_share_undo_start sub _cross_vol_share_undo_start { $Log->enter() if $may_enter; my ($self, %opts) = @_; my @all_aggregates = NACL::CS::Aggregate->fetch( command_interface => $opts{command_interface}, requested_fields => [qw(node aggregate)], allow_empty => 1, ); my $response; foreach my $aggr (@all_aggregates){ try { $response = $opts{command_interface}->apiset()->system_node_run( node => $aggr->node(), command => "aggr cross_vol_share status ".$aggr->aggregate(), ); my $raw_output = $response->get_raw_output(); my @cli_output = split(/\n/, $raw_output); # Now skip 1st three lines which are for the header, then parse for the data my @values = split(/\s+/, $cli_output[3]); if ( ( $response->get_raw_output() =~ /Aggregate .* has cross volume savings/i ) || ( defined $values[2] && $values[2] !~ /^No$/i ) ) { $opts{command_interface}->apiset()->system_node_run( node => $aggr->node(), command => "aggr cross_vol_share undo-start ".$aggr->aggregate(), 'connectrec-match_table' => ['Do you want to continue?' => 'y'], ); } } catch NATE::BaseException with { my $ex = shift; if ( $ex->text() =~ /No aggregate named .* was found/i || $ex->text() =~ /Aggregate .* does not have any cross volume savings/i || $ex->text() =~ /Undo is not required as there are no cross volume savings on aggregate/i ) { # suppressing here } else { $ex->throw(); } }; } $Log->exit() if $may_exit; } # revert event handler: stop_active_storage_efficiency_on_vols sub _stop_active_storage_efficiency_on_vols { $Log->enter() if $may_enter; my ($self, %opts) = @_; # listing down all sis operation details my @all_vol_eff = NACL::CS::VolumeEfficiency->fetch( command_interface => $opts{command_interface}, requested_fields => [qw(op-status)], allow_empty => 1, ); if (@all_vol_eff) { NACL::STask::VolumeEfficiency->stop( command_interface => $opts{command_interface}, vserver => "*", volume => "*", all => "true", nacltask_if_stopped => 'pass', ignore_disabled => 1, ); } $Log->exit() if $may_exit; } # revert event handler: autosize_enabled_on_flexgroups sub _autosize_enabled_on_flexgroups { $Log->enter() if $may_enter; my ($self, %opts) = @_; # listing down all FlexGroups my @all_flexgroups = NACL::C::Volume->find( command_interface => $opts{command_interface}, filter => { vserver => '*', 'is-flexgroup' => 'true' }, allow_empty => 1, ); foreach my $flexgroup (@all_flexgroups) { $flexgroup->modify( 'autosize-mode' => 'off', ); } $Log->exit() if $may_exit; } # revert event handler: remove_flexGroups_from_qos_policy_groups sub _remove_flexGroups_from_qos_policy_groups { $Log->enter() if $may_enter; my ($self, %opts) = @_; # listing down all FlexGroups my @all_flexgroups = NACL::C::Volume->find( command_interface => $opts{command_interface}, filter => { 'qos-policy-group' => '!-', 'volume-style-extended' => 'flexgroup' }, allow_empty => 1, ); foreach my $flexgroup (@all_flexgroups) { $flexgroup->modify( 'qos-policy-group' => 'none', ); } $Log->exit() if $may_exit; } # revert event handler: delete_temporary_clone_parents sub _delete_temporary_clone_parents { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; my $jobid; # listing all of the relevant Flexclone volumes my @all_flexclone_vol = NACL::CS::VolumeClone->fetch( command_interface => $opts{command_interface}, filter => { 'parent-vol-type' => 'TEMPORARY_VOL' }, allow_empty => 1, ); foreach my $data (@all_flexclone_vol) { # to make the volume online try { NACL::STask::Volume->online( command_interface => $opts{command_interface}, vserver => $data->vserver(), volume => $data->parent_volume(), ); } catch NATE::BaseException with { my $ex = shift; if ( $ex->text() =~ /is already online/i ) { # suppressing here $Log->comment("Volume is already online."); } else { $ex->throw(); } }; # to split the clone relationship my $retry_count = 0; AGAIN: try { my $vol_clone_split = NACL::C::VolumeCloneSplit->start( command_interface => $opts{command_interface}, flexclone => $data->flexclone(), vserver => $data->vserver(), job_component => \$jobid, ); $vol_clone_split->wait_for_completion( job_component => $jobid, 'method-timeout' => 1800 ); } catch NATE::BaseException with { my $ex = shift; if (($ex->text() =~ /Volume has locked snapshots/i) && ($retry_count++ == 0)) { my @snapshots = NACL::STask::VolumeSnapshot->find( command_interface => $opts{command_interface}, filter => { vserver => $data->vserver(), volume => $data->flexclone() }, allow_empty => 1 ); foreach my $_obj (@snapshots) { $_obj->purge(force => 'true', 'ignore-owners' => 'true'); } goto AGAIN; } $ex->throw(); }; # to offline the volume NACL::STask::Volume->offline( command_interface => $opts{command_interface}, vserver => $data->vserver(), volume => $data->parent_volume(), ); # to delete the volume NACL::STask::Volume->delete( command_interface => $opts{command_interface}, vserver => $data->vserver(), volume => $data->parent_volume(), 'method-timeout' => 1800 ); } $Log->exit() if $may_exit; } ## end of sub _delete_temporary_clone_parents { # Cancel all SnapMirror restore operations by removing all temporary RST relationships. sub _cancel_snapmirror_by_remov_temp_rst_relation { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; # listing all SnapMirror restore relationships my @all_snap_rest_rel = NACL::CS::Snapmirror->fetch( command_interface => $opts{command_interface}, filter => { 'type' => 'RST' }, allow_empty => 1, ); foreach my $data (@all_snap_rest_rel) { # to clean up a SnapMirror restore relationship NACL::STask::Snapmirror->restore( command_interface => $opts{command_interface}, 'destination-path' => $data->destination_path(), 'clean-up-failure' => 'true', ); } $Log->exit() if $may_exit; } ## end of sub _cancel_snapmirror_by_remov_temp_rst_relation # Delete the Cluster contains object store configurations. sub _delete_cluster_contains_store_configurations { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; #Command to list configurations. my @all_configs = NACL::STask::StorageAggregateObjectStore->config_show( command_interface => $opts{command_interface}, ); #Command to list the aggregates within object stores. instant_component( component_name => 'NACL::C::StorageAggregateObjectStore', command_interface => $opts{command_interface}, ); my @all_aggrs = NACL::CS::StorageAggregateObjectStore->fetch( command_interface => $opts{command_interface}, ); #Command to delete an aggregate. foreach my $aggrs_data (@all_aggrs) { NACL::C::Aggregate->delete( command_interface => $opts{command_interface}, aggregate => $aggrs_data->aggregate() ); } #Command to delete a configuration. foreach my $config_data (@all_configs) { NACL::STask::StorageAggregateObjectStore->config_delete( command_interface => $opts{command_interface}, 'object-store-name' => $config_data->object_store_name() ); } $Log->exit() if $may_exit; } ## end of sub _delete_cluster_contains_store_configurations { # Delete the Cluster contains object store configurations. sub _remove_qos_min_throughput { $Log->enter() if $may_enter; my ( $self, %opts ) = @_; #Command to list the policy-groups with min-throughput set. my @policy_group_with_min_throughput_set = NACL::C::QosPolicyGroup->find( command_interface => $opts{command_interface}, filter => { 'min-throughput' => '>0' }, ); #Command to remove the min-throughput setting from all policy-groups. foreach my $data (@policy_group_with_min_throughput_set) { $data->modify( 'min-throughput' => 'none', ); } $Log->exit() if $may_exit; } ## end of sub _remove_qos_min_throughput # revert event handler: disable_snaplock_v2 sub _disable_snaplock_v2 { $Log->enter() if $may_enter; my ($self, %opts) = @_; NACL::C::Snaplock->prepare_to_downgrade($opts{command_interface}); $Log->exit() if $may_exit; } ##end of sub _disable_snaplock_v2 # revert event handler: delete_access_control_permission_custom sub _delete_access_control_permission_custom { $Log->enter() if $may_enter; my ($self, %opts) = @_; # get all cifs share where ACL permission is set to "Custom" my @all_cifs_share = NACL::C::VserverCifsShareAccessControl->find( command_interface => $opts{command_interface}, filter => {'permission' => 'Custom',}, allow_empty => 1, ); foreach my $cifs_share (@all_cifs_share) { $cifs_share->delete(); } $Log->exit() if $may_exit; } # revert event handler: prepare_flexgroups_to_revert sub _prepare_flexgroups_to_revert { $Log->enter() if $may_enter; my ($self, %opts) = @_; my @all_nodes = NACL::CS::Cluster->fetch(command_interface => $opts{command_interface}, requested_fields => [qw(node)]); foreach my $node (@all_nodes) { # preparing one or more FlexGroups for revert $opts{command_interface}->apiset()->execute_raw_command(command => "node run -node ".$node->node()." flexgroup clean all"); } $Log->exit() if $may_exit; } # revert event handler: disable_qtree_support sub _disable_qtree_support { $Log->enter() if $may_enter; my ($self, %opts) = @_; my $command_interface = delete $opts{command_interface}; # disabling qtree support against flexgroup my @flexgrp_qtree_enabled_volumes = NACL::CS::Volume->fetch( command_interface => $command_interface, filter => { 'is-flexgroup-qtree-enabled' => 'true', 'volume-style-extended' => 'flexgroup', }, allow_empty => 1, ); NACL::InstantComponent::instant_component( command_interface => $command_interface, component_name => 'NACL::C::VolumeFlexgroup' ); my $retry_count=0; foreach my $volume (@flexgrp_qtree_enabled_volumes) { AGAIN: try { NACL::C::VolumeFlexgroup->qtree_disable( command_interface => $command_interface, vserver => $volume->vserver(), 'volume' => $volume->volume(), ); } catch NATE::BaseException with { my $ex = shift; if (($ex-> text() =~/Qtrees exist on the FlexGroup.*delete the qtrees first and then run the command again./i) && ($retry_count++ == 0)) { my @list_vol_qtree = NACL::CS::VolumeQtree->fetch( command_interface => $command_interface, filter => { vserver => $volume->vserver(), 'volume' => $volume->volume(), }, allow_empty => 1, ); foreach my $qtree (@list_vol_qtree) { try { my $job_obj; NACL::C::VolumeQtree->delete( command_interface => $command_interface, vserver => $volume->vserver(), 'volume' => $volume->volume(), 'qtree' => $qtree->qtree(), 'job_component' => \$job_obj, force => 'true' ); $job_obj->wait_on_job('method-timeout' => 300); } catch NATE::BaseException with { my $exception =shift; if ($exception-> text() =~ /You cannot delete qtree/i) { ## Suppressing issue and continuing with execution. } else { $exception->throw(); } }; } goto AGAIN; } $ex->throw(); }; } $Log->exit() if $may_exit; } # revert event handler: cross_vol_share_revert_to sub _cross_vol_share_revert_to { $Log->enter() if $may_enter; my ($self, %opts) = @_; my @all_aggregates = NACL::CS::Aggregate->fetch( command_interface => $opts{command_interface}, requested_fields => [qw(node aggregate)], allow_empty => 1, ); my $response; foreach my $aggr (@all_aggregates){ try { $response = $opts{command_interface}->apiset()->system_node_run( node => $aggr->node(), command => "aggr cross_vol_share status ".$aggr->aggregate(), ); my $raw_output = $response->get_raw_output(); my @cli_output = split(/\n/, $raw_output); # Now skip 1st three lines which are for the header, then parse for the data my @values = split(/\s+/, $cli_output[3]); if ( ( $response->get_raw_output() =~ /Aggregate .* has cross volume savings/i ) || ( defined $values[2] && $values[2] !~ /^No$/i ) ) { $opts{command_interface}->apiset()->system_node_run( node => $aggr->node(), command => "aggr cross_vol_share revert-to ".$aggr->aggregate(), 'connectrec-match_table' => ['Do you want to continue?' => 'y'], ); NACL::InstantComponent::instant_component( command_interface => $opts{command_interface}, component_name => 'NACL::C::StorageAggregateEfficiencyCrossVolumeDedupe' ); my $aggr_efficiency_obj = NACL::C::StorageAggregateEfficiencyCrossVolumeDedupe->find( command_interface => $opts{command_interface}, filter => {aggregate => $aggr->aggregate()}, ); $aggr_efficiency_obj->wait_on_attribute( attribute_to_check => 'background-op-status', till_value => ['Idle'], 'method-timeout' => 1800, ); } } catch NATE::BaseException with { my $ex = shift; if ( $ex->text() =~ /No aggregate named .* was found/i || $ex->text() =~ /Aggregate .* does not have any cross volume savings/i || $ex->text() =~ /is not required as there are no cross volume savings on aggregate/i ) { # suppressing here } else { $ex->throw(); } }; } $Log->exit() if $may_exit; } # revert event handler: remove_adaptive_qos sub _remove_adaptive_qos { $Log->enter() if $may_enter; my ($self, %opts) = @_; try { try { NACL::STask::Volume->modify( command_interface => $opts{command_interface}, extended_query => {'type' => 'RW,DP', 'is-constituent' => 'false'}, 'qos-adaptive-policy-group' => 'none', ); } otherwise { # For few configurations "qos-adaptive-policy-group" can not be updated to 'none' i.e. "FlexGroup -Read-Write volume", 7-mode volume .. # hence suppressing }; NACL::STask::QosAdaptivePolicyGroup->delete( command_interface => $opts{command_interface}, 'policy-group' => '!extreme,!performance,!value', ); } catch NACL::APISet::Exceptions::NoMatchingEntriesException with { #suppressing here }; $Log->exit() if $may_exit; } # revert event handler: handle_encryption_rekey_conversion sub _handle_encryption_rekey_conversion { $Log->enter() if $may_enter; my ($self, %opts) = @_; NACL::InstantComponent::instant_component( command_interface => $opts{command_interface}, component_name => 'NACL::C::VolumeEncryption' ); my @volumes_keygen = NACL::CS::Volume->fetch( command_interface => $opts{command_interface}, 'key-generation' => '>0', allow_empty => 1, ); my @volumes_enc = NACL::CS::VolumeEncryption->fetch( command_interface => $opts{command_interface}, allow_empty => 1, ); foreach my $volume (@volumes_keygen, @volumes_enc) { my $aggr = NACL::CS::Volume->fetch( command_interface => $opts{command_interface}, filter => {'vserver' => $volume->vserver(), 'volume' => $volume->volume(),}, requested_fields => [qw(aggregate)] ); NACL::STask::VolumeMove->start( command_interface => $opts{command_interface}, 'encrypt-destination' => 'false', volume => $volume->volume(), 'destination-aggregate' => $aggr->aggregate(), nacltask_wait => 1, nacltask_verify => 1 ); } $Log->exit() if $may_exit; } # revert event handler: stop_active_storage_efficiency_on_aggr sub _stop_active_storage_efficiency_on_aggr { $Log->enter() if $may_enter; my ($self, %opts) = @_; NACL::InstantComponent::instant_component( command_interface => $opts{command_interface}, component_name => 'NACL::C::StorageAggregateEfficiency' ); my @all_aggregates = NACL::CS::StorageAggregateEfficiency->fetch( command_interface => $opts{command_interface}, allow_empty => 1, ); foreach my $aggr (@all_aggregates){ try { $opts{command_interface}->apiset()->system_node_run( node => $aggr->node(), command => "aggr cross_vol_share stop ".$aggr->aggregate(), ); } catch NATE::BaseException with { my $ex = shift; if ( $ex->text() =~ /Operation is currently idle/i ) { # suppressing here } else { $ex->throw(); } }; } $Log->exit() if $may_exit; } sub _remove_snapmirror_policy_rules { $Log->enter() if $may_enter; my ($self,%opts) = @_; my $command_interface = delete $opts{command_interface}; my @snapmirror_policies = NACL::CS::SnapmirrorPolicy->fetch( command_interface => $command_interface, filter => {'type' => 'vault,mirror-vault'}, allow_empty => 1 ); foreach my $_obj (@snapmirror_policies) { foreach my $snapmirror_label (@{$_obj->snapmirror_label()}) { try { $_obj->get_task_instance()->remove_rule('snapmirror-label' => $snapmirror_label); } otherwise { # Suppress error } } } $Log->exit() if $may_exit; } ## end sub _remove_snapmirror_policy_rules sub _disable_volume_snapmirror_v4 { $Log->enter() if $may_enter; my ($self,%opts) = @_; my $command_interface = delete $opts{command_interface}; NACL::C::SystemCapability->disable( command_interface => $command_interface, 'capabilities' => ['volume.snapmirror_v4'], ); } ## end sub _disable_volume_snapmirror_v4 sub _stop_active_storage_efficiency_on_aggr_dedupe { $Log->enter() if $may_enter; my ($self,%opts) = @_; NACL::InstantComponent::instant_component( command_interface => $opts{command_interface}, component_name => 'NACL::C::StorageAggregateEfficiencyCrossVolumeDedupe' ); my @all_aggregates = NACL::CS::StorageAggregateEfficiencyCrossVolumeDedupe->fetch( command_interface => $opts{command_interface}, allow_empty => 1, ); foreach my $aggr (@all_aggregates){ try { NACL::C::StorageAggregateEfficiencyCrossVolumeDedupe->stop( command_interface => $opts{command_interface}, 'aggregate' => $aggr->aggregate(), ); } catch NATE::BaseException with { my $ex =shift; if ( $ex->text() =~ /Operation is currently idle/i ) { # suppressing here } else { $ex->throw(); } }; } } ## end sub _stop_active_storage_efficiency_on_aggr_dedupe sub _cross_vol_share_revert_to_dedupe { $Log->enter() if $may_enter; my ($self,%opts) = @_; NACL::InstantComponent::instant_component( command_interface => $opts{command_interface}, component_name => 'NACL::C::StorageAggregateEfficiency' ); my @all_aggregates = NACL::CS::StorageAggregateEfficiency->fetch( command_interface => $opts{command_interface}, 'requested_fields' => ['cross-volume-dedupe-savings'], allow_empty => 1, ); NACL::InstantComponent::instant_component( command_interface => $opts{command_interface}, component_name => 'NACL::C::StorageAggregateEfficiencyCrossVolumeDedupe' ); foreach my $aggr (@all_aggregates){ NACL::C::StorageAggregateEfficiencyCrossVolumeDedupe->revert_to( command_interface => $opts{command_interface}, 'aggregate' => $aggr->aggregate(), ); my $aggr_efficiency_obj = NACL::C::StorageAggregateEfficiencyCrossVolumeDedupe->find( command_interface => $opts{command_interface}, filter => {aggregate => $aggr->aggregate()}, ); $aggr_efficiency_obj->wait_on_attribute( attribute_to_check => 'background-op-status', till_value => ['Idle'], 'method-timeout' => 1800, ); } } ## end sub _cross_vol_share_revert_to_dedupe sub _volume_encryption_prepare_to_downgrade { $Log->enter() if $may_enter; my ($self, %opts) = @_; NACL::InstantComponent::instant_component( command_interface => $opts{command_interface}, component_name => 'NACL::C::VolumeEncryption' ); NACL::C::VolumeEncryption->prepare_to_downgrade( command_interface => $opts{command_interface}, ); $Log->exit() if $may_exit; } ## end sub _volume_encryption_prepare_to_downgrade =head1 FOR DEVELOPERS When a new feature is added in a particular release of ONTAP, but not the previous release, then the revert will not be successful unless that particular feature is disabled. (See the L section for more on this.) Handling this new feature requires implementing a new Revert "event". listed below are the steps that will generally be needed to implement a new "event". To illustrate what needs to be done, let's consider an example where a new SSH feature is being added, which results in the "system node revert-to" command failing with this error message: security.openssh.version: Change the SSH configurations of all Vservers and the cluster to the default settings for releases earlier than Data ONTAP 9.0.0 using the "security ssh prepare-to-downgrade" (privilege advanced) command. (This error message is generally whatever is defined in cap.xml.) The new code that will need to be added to the Revert library is: =item security_ssh_prepare_to_downgrade =over =item Output: Error: command failed: The revert check phase failed. The following issues must be resolved before revert can be completed. Disable the following capabilities: security.openssh.version: Change the SSH configurations of all Vservers and the cluster to the default settings for releases earlier than Data ONTAP 9.0.0 using the "security ssh prepare-to-downgrade" (privilege advanced) command.\r\n =back =cut push @Revert_Events, qr/(Change the SSH configurations of all Vservers and the cluster.*security ssh prepare-to-downgrade)/i => sub { ['security_ssh_prepare_to_downgrade', $1] }; sub _security_ssh_prepare_to_downgrade { $Log->enter() if $may_enter; my ($self, %opts) = @_; instant_component( command_interface => $opts{command_interface}, component_name => 'NACL::C::SecuritySsh' ); my %common_opts; $self->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); NACL::C::SecuritySsh->prepare_to_downgrade(%common_opts); $Log->exit() if $may_exit; } Here is an explanation of each of the pieces of code above: =over =item Add to @Revert_Events push @Revert_Events, qr/(Change the SSH configurations of all Vservers and the cluster.*security ssh prepare-to-downgrade)/i => sub { ['security_ssh_prepare_to_downgrade', $1] }; The @Revert_Events array has the list of all events to be handled, along with how to handle them. Each entry contains a pair of elements: * The first element is a regular expression matching this event in revert-to output. This regular expression should be in parenthesis so that it captures the whole output in "$1". As can be seen above, the regular expression matches the revert-to output obtained. * The second element is a subroutine reference which, when called, returns an array reference, which contains: ** The first element is the name of the revert event handler. ** The second element is the output (which is present in $1) Note that POD also needs to be added for each revert event introduced. The code snippet above shows the format to be followed for this POD documentation. =item Define the event handler In the example above, the command says that the "security ssh prepare-to-downgrade" command needs to be run, so the event handler should invoke the NACL library that will run this command (this would be NACL::C::SecuritySsh->prepare_to_downgrade()). The name of the event handler subroutine should be the name of the revert event with a leading underscore. Here, the event name was specified as "security_ssh_prepare_to_downgrade", therefore the name of the event handler subroutine should be "_security_ssh_prepare_to_downgrade". It would be defined like: sub _security_ssh_prepare_to_downgrade { $Log->enter() if $may_enter; my ($self, %opts) = @_; require NACL::C::SecuritySsh; my %common_opts; $self->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts ); NACL::C::SecuritySsh->prepare_to_downgrade(%common_opts); $Log->exit() if $may_exit; } =item Add the CDEF If a new command is being added, then the CDEF file (i.e. the "Command Definition" file) needs to be added to the NACL directory. In this case, let's assume the "security ssh prepare-to-downgrade" is a new command, so its CDEF needs to be coped to the NACL directory. The CDEF files are generated as a part of the ONTAP build. They are present under /final/bedrock/export/common/cdefsprod/. grep'ing for "security ssh prepare-to-downgrade" will show that its CDEF file is security_ssh.cdefs. Here is the relevant entry for it: security_ssh_prepare_to_downgrade => { command => "security ssh prepare-to-downgrade", keys => undef, attributes => undef, options => undef, required_options => undef, optional_options => undef, alias_map => undef, additional_options => undef, }, The NACL copy of the CDEFs are available under test/lib/NACL/APISet/Node/CLI/CMode. This is then further divided into subdirectories for the various releases, so if the command is for LB, then add to the "main" directory, if the command is for FS, then add to the "Rfullsteam" directory. Since this new command is for LB, what needs to be done is to copy the CDEF entry for security_ssh_prepare_to_downgrade (listed above) from /final/bedrock/export/common/cdefsprod/security_ssh.cdefs to test/lib/NACL/APISet/Node/CLI/CMode/main/security_ssh.cdefs. B This step is required B if a new command is being added. A simple way to check this is to grep for the command within the NACL CDEF directory. =back =cut 1;