# # Copyright (c) 2001-2013 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary Snapmirror ComponentState Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here =head1 NAME NACL::CS::Snapmirror =head1 DESCRIPTION C is a derived class of L. It represents the state of ONTAP Snapmirror. A related class is L, which represents access to ONTAP snapmirror. =head1 ATTRIBUTES The individual pieces of data that are part of the state of the Snapmirror element are the attributes of the Snapmirror ComponentState. =over =item C<< source_path >> =item C<< source_cluster >> =item C<< source_vserver >> =item C<< source_volume >> =item C<< destination_path >> =item C<< destination_cluster >> =item C<< destination_vserver >> =item C<< destination_volume >> =item C<< type >> =item C<< vserver >> =item C<< schedule >> =item C<< tries >> =item C<< throttle >> =item C<< state >> =item C<< status >> =item C<< transfer_snapshot >> =item C<< snapshot_progress >> =item C<< snapshot_checkpoint >> =item C<< newest_snapshot >> =item C<< newest_snapshot_timestamp >> =item C<< exported_snapshot >> =item C<< exported_snapshot_timestamp >> =item C<< healthy >> =item C<< last_transfer_type >> =item C<< current_transfer_error >> =item C<< current_transfer_type >> =item C<< contents >> =item C<< lag >> =item C<< last_transfer_from >> =item C<< last_transfer_duration >> =item C<< last_transfer_size >> =item C<< lag_time >> Filled in for 7Mode CLI. =item C<< source_qtree >> Filled in for 7Mode CLI. =item C<< destination_qtree >> Filled in for CMode CLI. =item C<< old_show >> Filled in for CMode CLI. =item C<< total_progress >> Filled in for CMode CLI. =item C<< dest_volume_msid >> Filled in for CMode CLI. =item C<< hidden_status >> Filled in for CMode CLI. =item C<< dest_vserver_id >> Filled in for CMode CLI. =item C<< policy >> Filled in for CMode CLI. =item C<< dest_cluster_id >> Filled in for CMode CLI. =item C<< last_transfer_error >> Filled in for CMode CLI. =item C<< physical_replica >> Filled in for CMode CLI. =item C<< snapshot_checkpoint >> Filled in for CMode CLI. =item C<< hidden_type >> Filled in for CMode CLI. =item C<< hidden_state >> Filled in for CMode CLI. =item C<< relationship_type >> Filled in for CMode CLI. =item C<< in_vserver_relationship >> Filled in for CMode CLI. =item C<< QOS_policy >> Filled in for CMode CLI. =item C<< dest_volume_dsid >> Filled in for CMode CLI. =item C<< percent_complete >> Filled in for CMode CLI. =item C<< relationship_capability >> Filled in for CMode CLI. Maps to: CM ZAPI : 'relationship-control-plane' $value =item C<< is_constituent >> Filled in for CMode CLI and CMode ZAPI. =item C<< identity_preserving >> Filled in for CMode CLI. =item C<< progress_last_updated >> Filled in for CMode CLI. =item C<< time_to_complete >> Filled in for CMode CLI. =item C<< dest_vserver_uuid >> Filled in for CMode CLI. =item C<< relationship_id >> Filled in for CMode CLI. =item C<< transfer_priority >> Filled in for CMode CLI. =item C<< data_transferred >> Filled in for 7Mode CLI. This field is displayed in 7Mode when a transfer is in progress. =item C<< foreground >> Filled in for CMode CLI. =item C<< qos_policy >> Filled in for CMode CLI. =item C<< is_smtape_op >> SMTape Operation, possible value(s) are true,false Filled in for CMode CLI. =item C<< control_plane >> Filled in for CMode CLI. =item C<< current_throttle >> Current Transfer Throttle (KB/sec), possible value(s) are ,unlimited Filled in for CMode CLI. =item C<< current_transfer_priority >> Current Transfer Priority, possible value(s) are low,normal Filled in for CMode CLI. =item C<< last_transfer_end_timestamp >> Last Transfer End Timestamp Filled in for CMode CLI/ZAPI. =item C<< last_transfer_error_codes >> Last Transfer Error Codes (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $last_transfer_error_codes = $obj->last_transfer_error_codes(); # $last_transfer_error_codes contains a reference to the array of values my @last_transfer_error_codes = $obj->last_transfer_error_codes(); # @last_transfer_error_codes contains the array of values If this field needs to be passed to the filter hash, the value for this field should be passed in as an arrayref # filter => { last_transfer_error_codes = [ value1, value2...] } Filled in for CMode CLI/ZAPI. =item C<< destination_volume_node >> Destination Volume Node Name Filled in for CMode CLI/ZAPI. =item C<< unhealthy_reason >> Unhealthy Reason Filled in for CMode CLI/ZAPI. =item C<< last_transfer_network_compression_ratio >> Last transfer network compression ratio Filled in for CMode CLI/ZAPI. =item C<< network_compression_ratio >> Network compression ratio Filled in for CMode CLI/ZAPI. =item C<< identity_preserve >> Identity Preserve Vserver DR possible value(s) are, true,false Filled in for CMode CLI. =item C<< op_uuid >> Operation UUID Filled in for CMode CLI. =item C<< group_type >> SnapMirror Group Type possible value(s) are, FlexvolDR,VserverDR,InfiniteVolDR Filled in for CMode CLI. =item C<< op_cookie >> Operation Cookie Filled in for CMode CLI. =item C<< asynchronous >> Synchronous/Asynchronous Filled in for CMode CLI. =item C<< break_succ_cnt >> Number of Successful Breaks Filled in for CMode CLI. =item C<< resync_succ_cnt >> Number of Successful Resyncs Filled in for CMode CLI. =item C<< update_succ_cnt >> Number of Successful Updates Filled in for CMode CLI. =item C<< resync_fail_cnt >> Number of Failed Resyncs Filled in for CMode CLI. =item C<< update_fail_cnt >> Number of Failed Updates Filled in for CMode CLI. =item C<< break_fail_cnt >> Number of Failed Breaks Filled in for CMode CLI. =item C<< file_restore_file_count >> File Restore File Count Filled in for CMode CLI/ZAPI. =item C<< policy_type >> SnapMirror Policy Type possible value(s) are, vault,async-mirror,mirror-vault,sync-mirror Filled in for CMode CLI/ZAPI. =item C<< file_restore_file_list >> File Restore File List (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $file_restore_file_list = $obj->file_restore_file_list(); # $file_restore_file_list contains a reference to the array of values my @file_restore_file_list = $obj->file_restore_file_list(); # @file_restore_file_list contains the array of values If this field needs to be passed to the filter hash, the value for this field should be passed in as an arrayref # filter => { file_restore_file_list = [ value1, value2...] } Filled in for CMode CLI/ZAPI. =item C<< destination_vserver_uuid >> Destination Vserver UUID Filled in for CMode CLI/ZAPI. =item C<< tot_trans_bytes >> Total Transfer Bytes Filled in for CMode CLI. =item C<< tot_trans_time_secs >> Total Transfer Time in Seconds Filled in for CMode CLI. =item C<< newDstMsId >> Dest MSID after LS Delete Filled in for CMode CLI. =item C<< source_vserver_uuid >> Source Vserver UUID Filled in for CMode CLI/ZAPI. =item C<< hidden_dest_vserver_uuid >> Destination Vserver UUID Filled in for CMode CLI. =item C<< operation_id >> operation id Filled in for CMode CLI. =item C<< current_operation_id >> new key for operation id Filled in for CMode CLI. =item C<< relationship_group_type >> new key for group_type Filled in for CMode CLI/ZAPI. =item C<< break_successful_count >> new key for break_succ_cnt Filled in for CMode CLI/ZAPI. =item C<< resync_successful_count >> new key for resync_succ_cnt Filled in for CMode CLI/ZAPI. =item C<< update_successful_count >> new key for update_succ_cnt Filled in for CMode CLI/ZAPI. =item C<< resync_failed_count >> new key for resync_fail_cnt Filled in for CMode CLI/ZAPI. =item C<< update_failed_count >> new key for update_fail_cnt Filled in for CMode CLI/ZAPI. =item C<< break_failed_count >> new key for break_fail_cnt Filled in for CMode CLI/ZAPI. =item C<< total_transfer_bytes >> new key for tot_trans_bytes Filled in for CMode CLI/ZAPI. =item C<< total_transfer_time_secs >> new key for tot_trans_time_secs Filled in for CMode CLI/ZAPI. =back =cut package NACL::CS::Snapmirror; use strict; use warnings; use feature 'state'; use Params::Validate qw(:all); use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use NACL::APISet::Exceptions::InvalidParamValueException qw(:try); use NACL::C::_Mixins::Snapmirror qw(:all); use NACL::CS::_Mixins::Snapmirror qw(:all); use base 'NACL::CS::ComponentState::ONTAP'; use Class::MethodMaker [ scalar => 'source_path', scalar => 'source_cluster', scalar => 'source_vserver', scalar => 'source_volume', scalar => 'destination_path', scalar => 'source_qtree', scalar => 'destination_qtree', scalar => 'destination_cluster', scalar => 'destination_vserver', scalar => 'destination_volume', scalar => 'type', scalar => 'vserver', scalar => 'schedule', scalar => 'tries', scalar => 'throttle', scalar => 'state', scalar => 'status', scalar => 'transfer_snapshot', scalar => 'snapshot_progress', scalar => 'snapshot_checkpoint', scalar => 'newest_snapshot', scalar => 'newest_snapshot_timestamp', scalar => 'exported_snapshot', scalar => 'exported_snapshot_timestamp', scalar => 'healthy', scalar => 'operation_id', scalar => 'current_operation_id', # specific to 7Mode scalar => 'last_transfer_type', scalar => 'current_transfer_error', scalar => 'current_transfer_type', scalar => 'contents', scalar => 'lag', scalar => 'last_transfer_from', scalar => 'last_transfer_duration', scalar => 'last_transfer_size', scalar => 'lag_time', scalar => 'old_show', scalar => 'total_progress', scalar => 'dest_volume_msid', scalar => 'hidden_status', scalar => 'dest_vserver_id', scalar => 'policy', scalar => 'dest_cluster_id', scalar => 'last_transfer_error', scalar => 'physical_replica', scalar => 'hidden_type', scalar => 'hidden_state', scalar => 'relationship_type', scalar => 'in_vserver_relationship', scalar => 'QOS_policy', scalar => 'dest_volume_dsid', scalar => 'percent_complete', scalar => 'relationship_capability', scalar => 'is_constituent', scalar => 'identity_preserving', scalar => 'progress_last_updated', scalar => 'time_to_complete', scalar => 'dest_vserver_uuid', scalar => 'relationship_id', scalar => 'transfer_priority', scalar => 'data_transferred', scalar => 'foreground', scalar => 'qos_policy', scalar => 'is_smtape_op', scalar => 'control_plane', scalar => 'current_throttle', scalar => 'current_transfer_priority', scalar => 'last_transfer_end_timestamp', array => 'last_transfer_error_codes', scalar => 'destination_volume_node', scalar => 'unhealthy_reason', scalar => 'identity_preserve', scalar => 'op_uuid', scalar => 'group_type', scalar => 'op_cookie', scalar => 'asynchronous', scalar => 'break_succ_cnt', scalar => 'resync_succ_cnt', scalar => 'update_succ_cnt', scalar => 'resync_fail_cnt', scalar => 'update_fail_cnt', scalar => 'break_fail_cnt', scalar => 'file_restore_file_count', scalar => 'network_compression_ratio', scalar => 'last_transfer_network_compression_ratio', scalar => 'policy_type', array => 'file_restore_file_list', scalar => 'destination_vserver_uuid', scalar => 'tot_trans_bytes', scalar => 'tot_trans_time_secs', scalar => 'newDstMsId', scalar => 'source_vserver_uuid', scalar => 'hidden_dest_vserver_uuid', # New keys scalar => 'relationship_group_type', scalar => 'break_successful_count', scalar => 'resync_successful_count', scalar => 'update_successful_count', scalar => 'resync_failed_count', scalar => 'update_failed_count', scalar => 'break_failed_count', scalar => 'total_transfer_bytes', scalar => 'total_transfer_time_secs', ]; =head1 METHODS =head2 fetch my $Snapmirror_state = NACL::CS::Snapmirror->fetch(command_interface => $ci, ...); my @Snapmirror_states = NACL::CS::Snapmirror->fetch(command_interface => $ci, ...); (Class method) Discovers which elements are present and returns their state in ComponentState objects. Called in scalar context it returns only one state object, in list context it returns all state objects. See L for a more detailed description along with a complete explanation of the options it accepts. Supports CMode-CLI, CMode-ZAPI (both iter and non-iter), 7Mode-CLI. Invokes 'snapmirror show' for CMode-CLI. Invokes 'snapmirror-get-iter' for iter CMode-ZAPI; invokes 'snapmirror-get' for non-iter CMode-ZAPI. Invokes 'snapmirror status' for 7Mode-CLI. =over =item Exceptions =over =item C When there are no elements matching the query specified or elements of that type doesn't exist, then this exception will be thrown. =back =back =cut sub fetch { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my %opts = validate @args, $pkg->_fetch_validate_spec(); my $ci = $opts{'command_interface'}; if ($ci->is_7mode() && !$opts{command_interface}->isa('NACL::C::Vfiler')) { require NACL::C::Snapmirror; my $path_hash = NACL::C::Snapmirror->_construct_full_path_7m( 'source-path' => $opts{filter}->{'source-path'}, 'destination-path' => $opts{filter}->{'destination-path'}, command_interface => $opts{command_interface}, ); my %filter = %{$opts{filter}}; $filter{'source-path'} = $path_hash->{'source-path'}; $filter{'destination-path'} = $path_hash->{'destination-path'}; $opts{filter} = \%filter; } my @state_objs = $pkg->SUPER::fetch( %opts, choices => [ { method => '_fetch_cmode_cli', interface => 'CLI', set => 'CMode', zapi_type => 'none', }, { method => '_fetch_cmode_zapi', interface => 'ZAPI', set => 'CMode', zapi_type => 'iter', }, { method => '_fetch_cmode_zapi_non_iter', interface => 'ZAPI', set => 'CMode', zapi_type => 'non-iter', check => '_non_iter_check_cs', }, { method => '_fetch_7mode_cli', interface => 'CLI', set => '7Mode', }, ], exception_text => 'No matching snapmirror(s) found', show_cmd => 'snapmirror show', ); $Log->exit() if $may_exit; return wantarray ? @state_objs : $state_objs[0]; } ## end sub fetch sub _fetch_cmode_cli { $Log->enter() if $may_enter; my $pkg = shift; my @state_objs = $pkg->SUPER::_fetch_cmode_cli(@_, api => 'snapmirror_show'); $Log->exit() if $may_exit; return @state_objs; } ## end sub _fetch_cmode_cli sub _fetch_7mode_cli { $Log->enter() if $may_enter; my $pkg = shift; my %opts = validate @_, $pkg->_fetch_backend_validate_spec(); # Do the conversion of path inputs require NACL::C::Snapmirror; my $path_hash = NACL::C::Snapmirror->_construct_full_path_7m( 'source-path' => $opts{filter}->{'source-path'}, 'destination-path' => $opts{filter}->{'destination-path'}, command_interface => $opts{command_interface}, ); # Strip out fields with relational and regex-like characters my $requested_fields = [@{$opts{requested_fields}}]; my $filter = {%{$opts{filter}}}; $pkg->_remove_relational_regex_filters( requested_fields => $requested_fields, filter => $filter ); $filter->{'source-path'} = $path_hash->{'source-path'} if (defined $path_hash->{'source-path'}); $filter->{'destination-path'} = $path_hash->{'destination-path'} if (defined $path_hash->{'destination-path'}); my %api_args; # Note that we can't limit location based on volume/qtree since we don't # know which node it is on if (defined $filter->{'destination-path'}) { $api_args{location} = $filter->{'destination-path'}; } elsif (defined $filter->{'source-path'}) { $api_args{location} = $filter->{'source-path'}; } if (!defined $api_args{location}) { # If cluster + volume only is provided then we can't construct # the path because it might be the volume on which qtrees are # present, and not necessarily volumes which are themselves # involved in a snapmirror relationship (VSM) my $sub = sub { my @opts = validate_pos(@_, {type => SCALAR}); my $src_or_dest = $opts[0]; if ( defined $filter->{"$src_or_dest-cluster"} && defined $filter->{"$src_or_dest-volume"} && defined $filter->{"$src_or_dest-qtree"}) { $api_args{location} = $filter->{"$src_or_dest-cluster"} . ':' . /vol/ . $filter->{"$src_or_dest-volume"} . '/' . $filter->{"$src_or_dest-qtree"}; } ## end if ( defined $filter->... }; $sub->('destination'); $sub->('source') if (!defined $api_args{location}); } ## end if ( !defined $api_args... if ($pkg->_want_any_field_of( requested_fields => $requested_fields, filter => $filter, fields_filled_by_api => [ qw(snapshot-progress newest-snapshot-timestamp newest-snapshot current-transfer-type current-transfer-error contents last-transfer-type last-transfer-size last-transfer-duration last-transfer-from) ] ) ) { $api_args{long} = 1; } ## end if ( $pkg->_want_any_field_of... $api_args{'connectrec-timeout'} = $opts{'method-timeout'} || 1200; my $apiset = $opts{apiset}; my ($response, $caught_exception); try { $response = $apiset->snapmirror_status(%api_args); } catch NACL::APISet::Exceptions::InvalidParamValueException with { # A caught exception indicates that the qtree being looked for # does not exist. We catch the exception and return immediately. The # 'fetch' frontend decides whether to throw a NoElementsFound # exception based on the value of 'allow_empty' $caught_exception = 1; }; if ($caught_exception) { $Log->exit() if $may_exit; return; } my $output = $response->get_parsed_output(); my $snapmirror_entries = delete $output->[0]->{snapmirror_entries}; my @state_objs; foreach my $row (@$snapmirror_entries) { my $final_attributes = $pkg->_hash_copy( source => $row, copy => [ qw( last_transfer_type current_transfer_error current_transfer_type status contents lag last_transfer_from last_transfer_duration state last_transfer_size data_transferred) ], map => { "source" => "source_path", "progress" => "snapshot_progress", "destination" => "destination_path", "mirror_timestamp" => "newest_snapshot_timestamp", "base_snapshot" => "newest_snapshot", }, ); # We need to extract the source-volume,destination-volume,source-cluster # and destination cluster from the source and destination paths. $final_attributes->{destination_path} =~ /(.+):(.+)/; $final_attributes->{destination_cluster} = $1; $final_attributes->{destination_volume} = $2; $final_attributes->{source_path} =~ /(.+):(.+)/; $final_attributes->{source_cluster} = $1; $final_attributes->{source_volume} = $2; # If it is QSM, then populate source_qtree and destination_qtree # This information is already returned in source_volume, # destination_volume which doesn't make sense but we will retain it # for backward compatibility. if ($final_attributes->{destination_path} =~ /(.+):(\/vol\/.+)/) { $final_attributes->{destination_qtree} = $2; } if ($final_attributes->{source_path} =~ /(.+):(\/vol\/.+)/) { $final_attributes->{source_qtree} = $2; } my $obj = $pkg->new(command_interface => $opts{command_interface}); $obj->_set_fields(row => $final_attributes); push @state_objs, $obj; } ## end foreach my $row (@$snapmirror_entries) $Log->exit() if $may_exit; return @state_objs; } ## end sub _fetch_7mode_cli sub _fetch_cmode_zapi { $Log->enter() if $may_enter; my ($pkg, %opts) = @_; $pkg->_update_filter_cmode_zapi(all_opts => \%opts); my @common_copy = $pkg->_common_zapi_copy(); my %common_map = $pkg->_common_zapi_map(); my @state_objs = $pkg->SUPER::_fetch_cmode_zapi( %opts, api => 'snapmirror-get-iter', copy => \@common_copy, map => \%common_map, ); $Log->exit() if $may_exit; return @state_objs; } sub _fetch_cmode_zapi_non_iter { $Log->enter() if $may_enter; my ($pkg, %opts) = @_; my $common_map = $pkg->_common_zapi_map(); my $common_copy = $pkg->_common_zapi_copy(); # The keys that are accepted are # (source|destination)-(path|vserver|volume|cluster) my @primary_keys; foreach my $src_or_dst (qw(source destination)) { foreach my $elem (qw(cluster volume vserver path)) { push @primary_keys, "$src_or_dst-$elem"; } } my @state_objs = $pkg->SUPER::_fetch_cmode_zapi_non_iter( %opts, api => 'snapmirror-get', copy => $common_copy, map => $common_map, primary_keys => \@primary_keys, ); $Log->exit() if $may_exit; return @state_objs; } # Perform the various mappings/translations needed to make the values in # the filter look like the CLI equivalent sub _update_filter_cmode_zapi { $Log->enter() if $may_enter; my ($pkg, @args) = @_; state $spec = {all_opts => {type => HASHREF}}; my %opts = validate(@args, $spec); my $all_opts = $opts{all_opts}; if ($all_opts->{filter}) { my %filter = %{$all_opts->{filter}}; $pkg->_update_iter_input(opts_ptr => \%filter); $all_opts->{filter} = \%filter; } $Log->exit() if $may_exit; } # Perform the various mappings/translations needed to make the values in # the CS object look like the CLI equivalent sub _update_state_objs_cmode_zapi { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my %relationship_mapping = $pkg->_relationship_capability_mapping(); my %reverse_relationship_mapping = reverse %relationship_mapping; $pkg->SUPER::_update_state_objs_cmode_zapi( @args, additional_translations => { throttle_unlimited => { method => '_zapi_output_translate_throttle', fields_to_translate => ['throttle'] }, }, zapi_field_translations => { hyphenate_value => [$pkg->_zapi_fields_underscored_value()], ucfirst => [$pkg->_ucfirst_state_fields_zapi()], }, hash_map_translations => { relationship_capability => { fields_to_translate => [qw(relationship-capability)], hash_map => {%reverse_relationship_mapping}, }, $pkg->_hash_map_translations_status(), $pkg->_hash_map_translations_type(), $pkg->_hash_map_translations_group_type(), }, ); $Log->exit() if $may_exit; } sub _zapi_output_translate_throttle { $Log->enter() if $may_enter; my ($pkg, $val) = @_; # CLI value of 'unlimited' is returned as 0 by ZAPI. $val = 'unlimited' if (!$val); $Log->exit() if $may_exit; return $val; } sub _super_apply_filter { $Log->enter() if $may_enter; my ($pkg, %opts) = @_; $pkg->SUPER::_apply_filter(%opts); $Log->exit() if $may_exit; } sub _extra_filter_fields { return ['expand'] } 1;