# $Id$ # # Copyright (c) 2001-2014 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary ComponentState Module for the method NACL::C::VolumeFileFingerprint->dump() (auto-generated by CGT) ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here =head1 NAME NACL::CS::VolumeFileFingerprintDump =head1 DESCRIPTION C is a derived class of L. Object(s) of this type are returned when NACL::C::VolumeFileFingerprint->dump() is invoked. ((This module does not represent the state of any element, but is an object repesentation of the output obtained when NACL::C::VolumeFileFingerprint->dump() is invoked.) =head1 ATTRIBUTES The fields of the output are fields of the ComponentState object. =over =item C<< file_type >> File Type Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< file >> File Path Filled in for CMode CLI/ZAPI, non-iter. =item C<< modified_time >> Modification Time Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< is_wraparound >> Is Retention Time wraparound possible value(s) are, true,false Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< hostname >> Hostname Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: filer-name =item C<< volume_dsid >> Volume DSID Filled in for CMode CLI/ZAPI, non-iter. =item C<< fingerprint_end_time >> Fingerprint End Time Filled in for CMode CLI/ZAPI, non-iter. =item C<< fingerprint_start_time >> Fingerprint Start Time Filled in for CMode CLI/ZAPI, non-iter. =item C<< formatted_fingerprint_end_time >> Formatted Fingerprint End Time Filled in for CMode CLI/ZAPI, non-iter. =item C<< scope >> Fingerprint Scope possible value(s) are, Data_and_Metadata,Data_only,Metadata_only Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: fingerprint-scope =item C<< algorithm >> Fingerprint Algorithm possible value(s) are, MD5,SHA256 Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: fingerprint-algorithm =item C<< access_time >> Access Time Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< file_size >> File Size Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< fileid >> File ID Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< fingerprint_version >> Fingerprint Version Filled in for CMode CLI/ZAPI, non-iter. =item C<< volume_compliance_clock >> Volume ComplianceClock Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: snaplock-volume-compliance-clock =item C<< owner_id >> Owner ID Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< data_fingerprint >> Data Fingerprint Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< formatted_fingerprint_start_time >> Formatted Fingerprint Start Time Filled in for CMode CLI/ZAPI, non-iter. =item C<< formatted_volume_compliance_clock >> Formatted Volume ComplianceClock Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: formatted-snaplock-volume-compliance-clock =item C<< formatted_modified_time >> Formatted Modification Time Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< session_id >> Session ID of fingerprint operation Filled in for CMode CLI/ZAPI, non-iter. =item C<< group_id >> Group ID Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< formatted_creation_time >> Formatted Creation Time Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< is_changed_time_wraparound >> Is Changed Time Wraparound possible value(s) are, true,false Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< snaplock_license >> Snaplock License Filled in for CMode CLI/ZAPI, non-iter. =item C<< vserver_uuid >> Vserver UUID Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: vserver-id =item C<< formatted_volume_expiry_date >> Formatted Volume Expiry Date Filled in for CMode CLI/ZAPI, non-iter. =item C<< retention_time >> Retention Time Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< fsid >> Fsid Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< formatted_retention_time >> Formatted Retention Time Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< creation_time >> Creation Time Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< volume >> Volume Name Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: volume-name =item C<< filer_id >> Filer ID Filled in for CMode CLI/ZAPI, non-iter. =item C<< vserver >> Vserver Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: vserver-name =item C<< volume_containing_aggregate >> Volume Containing Aggregate Filled in for CMode CLI/ZAPI, non-iter. =item C<< formatted_access_time >> Formatted Access Time Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< metadata_fingerprint >> Metadata Fingerprint Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< formatted_system_compliance_clock >> Formatted Snaplock System ComplianceClock Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: formatted-snaplock-system-compliance-clock =item C<< volume_snaplock_type >> Volume Snaplock Type possible value(s) are, non-snaplock,compliance,enterprise Filled in for CMode CLI/ZAPI, non-iter. =item C<< system_compliance_clock >> Snaplock System ComplianceClock Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: snaplock-system-compliance-clock =item C<< owner_sid >> Owner SID Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< volume_expiry_date >> Volume Expiry Date Filled in for CMode CLI/ZAPI, non-iter. =item C<< aggregate_uuid >> Aggregate ID Filled in for CMode CLI/ZAPI, non-iter. =item C<< formatted_changed_time >> Formatted Changed Time Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< changed_time >> Changed Time Filled in for CMode CLI/ZAPI, non-iter. Mapping for CMode ZAPI non-iter: =item C<< is_volume_expiry_date_wraparound >> Is Volume Expiry Date Wraparound possible value(s) are, true,false Filled in for CMode CLI/ZAPI, non-iter. =item C<< volume_msid >> Volume MSID Filled in for CMode CLI/ZAPI, non-iter. =back =cut package NACL::CS::VolumeFileFingerprintDump; use strict; use warnings; use Params::Validate qw(validate validate_with ARRAYREF OBJECT); use NATE::Log qw(log_global); use NACL::Exceptions::NoElementsFound qw(:try); use NACL::CS::ComponentState::ZapiSkip qw(make_zapi_skip); use NACL::CS::ComponentState::ZapiArray qw(make_zapi_array); use NACL::ComponentUtils qw(_dump_one); use base 'NACL::CS::ComponentState::ONTAP'; use Class::MethodMaker [ scalar => 'file_type', scalar => 'file', scalar => 'modified_time', scalar => 'is_wraparound', scalar => 'hostname', scalar => 'volume_dsid', scalar => 'fingerprint_end_time', scalar => 'fingerprint_start_time', scalar => 'formatted_fingerprint_end_time', scalar => 'scope', scalar => 'algorithm', scalar => 'access_time', scalar => 'file_size', scalar => 'fileid', scalar => 'fingerprint_version', scalar => 'volume_compliance_clock', scalar => 'owner_id', scalar => 'data_fingerprint', scalar => 'formatted_fingerprint_start_time', scalar => 'formatted_volume_compliance_clock', scalar => 'formatted_modified_time', scalar => 'session_id', scalar => 'group_id', scalar => 'formatted_creation_time', scalar => 'is_changed_time_wraparound', scalar => 'snaplock_license', scalar => 'vserver_uuid', scalar => 'formatted_volume_expiry_date', scalar => 'retention_time', scalar => 'fsid', scalar => 'formatted_retention_time', scalar => 'creation_time', scalar => 'volume', scalar => 'filer_id', scalar => 'vserver', scalar => 'volume_containing_aggregate', scalar => 'formatted_access_time', scalar => 'metadata_fingerprint', scalar => 'formatted_system_compliance_clock', scalar => 'volume_snaplock_type', scalar => 'system_compliance_clock', scalar => 'owner_sid', scalar => 'volume_expiry_date', scalar => 'aggregate_uuid', scalar => 'formatted_changed_time', scalar => 'changed_time', scalar => 'is_volume_expiry_date_wraparound', scalar => 'volume_msid', # These two fields are 7Mode-only scalar => 'volume_type', scalar => 'volume_uuid', ]; my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); sub _check_non_iter { $Log->enter() if $may_enter; my ($pkg, @opts) = @_; $pkg->_base_check_non_iter( @opts, _primary_keys => [qw(session-id)], _vs_context => 1 ); $Log->exit() if $may_exit; } =head1 METHODS =head2 fetch my $VolumeFileFingerprintDump_state = NACL::CS::VolumeFileFingerprintDump->fetch(command_interface => $ci, ...); my @VolumeFileFingerprintDump_states = NACL::CS::VolumeFileFingerprintDump->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. Uses CMode CLI/ZAPI. Invokes file-fingerprint-dump API for CMode ZAPI non-iter. =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 @state_objs = $pkg->SUPER::fetch( @args, show_cmd => 'volume file fingerprint dump', choices => [ { method => '_fetch_cmode_cli', interface => 'CLI', set => 'CMode', zapi_type => 'none', }, { method => '_fetch_cmode_zapi_non_iter', interface => 'ZAPI', set => 'CMode', zapi_type => 'non-iter', }, { method => '_fetch_7mode_cli', interface => 'CLI', set => '7Mode', zapi_type => 'none', }, ], exception_text => 'No matching VolumeFileFingerprintDump(s) found', frontend => 'NACL::CS::VolumeFileFingerprintDump::fetch', ); $Log->exit() if $may_exit; return wantarray ? @state_objs : $state_objs[0]; } sub _update_state_objs_cmode_zapi { $Log->enter() if $may_enter; my ($pkg, @args) = @_; $pkg->SUPER::_update_state_objs_cmode_zapi( @args, zapi_field_translations => { uc => [qw(algorithm)], hyphenate_value => [qw(volume-snaplock-type)], }, hash_map_translations => { scope => { fields_to_translate => [qw(scope)], hash_map => { 'data_and_metadata' => 'Data_and_Metadata', 'data_only' => 'Data_only', 'metadata_only' => 'Metadata_only', }, } }, ); $Log->exit() if $may_exit; } sub _fetch_cmode_cli { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my @state_objs = $pkg->SUPER::_fetch_cmode_cli(@args, api => 'volume_file_fingerprint_dump',); $Log->exit() if $may_exit; return @state_objs; } sub _common_zapi_copy { return [ qw(fingerprint-end-time volume-dsid fingerprint-start-time formatted-fingerprint-end-time fingerprint-version formatted-fingerprint-start-time session-id snaplock-license formatted-volume-expiry-date filer-id volume-containing-aggregate volume-snaplock-type aggregate-uuid volume-expiry-date is-volume-expiry-date-wraparound volume-msid ) ]; } sub _common_zapi_map { return { 'file' => 'fingerprint-input-path', 'file-type' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'file-type', ], 'is-wraparound' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'is-wraparound', ], 'modified-time' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'modified-time', ], 'hostname' => 'filer-name', 'scope' => 'fingerprint-scope', 'algorithm' => 'fingerprint-algorithm', 'access-time' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'access-time', ], 'file-size' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'file-size', ], 'fileid' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'fileid', ], 'volume-compliance-clock' => 'snaplock-volume-compliance-clock', 'data-fingerprint' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'data-fingerprint', ], 'owner-id' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'owner-id', ], 'formatted-volume-compliance-clock' => 'formatted-snaplock-volume-compliance-clock', 'formatted-modified-time' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'formatted-modified-time', ], 'group-id' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'group-id', ], 'formatted-creation-time' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'formatted-creation-time', ], 'is-changed-time-wraparound' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'is-changed-time-wraparound', ], 'vserver-uuid' => 'vserver-id', 'retention-time' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'retention-time', ], 'fsid' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'fsid', ], 'formatted-retention-time' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'formatted-retention-time', ], 'creation-time' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'creation-time', ], 'volume' => 'volume-name', 'vserver' => 'vserver-name', 'metadata-fingerprint' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'metadata-fingerprint', ], 'formatted-access-time' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'formatted-access-time', ], 'formatted-system-compliance-clock' => 'formatted-snaplock-system-compliance-clock', 'system-compliance-clock' => 'snaplock-system-compliance-clock', 'owner-sid' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'owner-sid', ], 'formatted-changed-time' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'formatted-changed-time', ], 'changed-time' => [ 'file-metadata', make_zapi_skip('file-fingerprint-info'), 'changed-time', ], }; } sub _fetch_cmode_zapi_non_iter { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my @state_objs = $pkg->SUPER::_fetch_cmode_zapi_non_iter( @args, copy => $pkg->_common_zapi_copy(), map => $pkg->_common_zapi_map(), api => 'file-fingerprint-dump', primary_keys => [qw( session-id )], ); $Log->exit() if $may_exit; return @state_objs; } sub _fetch_7mode_cli { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my %opts = validate @args, $pkg->_fetch_backend_validate_spec(); my $apiset = $opts{apiset}; my $ci = $opts{command_interface}; my $filter = $opts{filter}; # Force the XML form of the output since that shows all details and also # allows us to reuse most of the CMode code my %api_opts = ('xml-output' => 1); $pkg->_hash_copy( source => $filter, target => \%api_opts, map => { algorithm => 'digest-algorithm', file => 'path', }, ); # CMode accepts values as scope => Data_and_Metadata | Data_only | Metadata_only # For 7Mode-CLI, -d sets data_scope, -m sets metadata_scope, and providing # neither results in it working for both data + metadata. if (defined $filter->{scope}) { # CMode works for both upper and lower-cased, so lc to make # the comparisons below simpler my $scope = lc $filter->{scope}; if ($scope eq 'data_only') { $api_opts{'data-scope'} = 1; } elsif ($scope eq 'metadata_only') { $api_opts{'metadata-scope'} = 1; } } my $response = $apiset->file_fingerprint(%api_opts); my $cs = $pkg->new(command_interface => $ci); my @state_objs = ($cs); # Output is in XML format, which is extremely similar to the CMode ZAPI format, # so let's reuse the XML parsing of the CMode-ZAPI APISet and the CMode-ZAPI # Component mapping. my $xml_output = $response->get_raw_output(); # Output contains the "results" tag my $output = ' ' . $xml_output . ' '; # Complete output has the "netapp" tag as well my $complete_raw_output = ' ' . $output . ' '; require NACL::APISet::Response::ZAPI::CMode; my $zapi_response = NACL::APISet::Response::ZAPI::CMode->new( output => $output, complete_raw_output => $complete_raw_output, command => '', execution_time => 0, error_number => 0, error_reason => '', execution_status => 'passed', ); my $zapi_parsed_output = $zapi_response->get_parsed_output(); my $only_hashref = $zapi_parsed_output->[0]{fingerprint}[0]{'fingerprint-info'}[0]; # The typedef which is called "metadata-files" in 7Mode is called # "file-metadata" in CMode. Rename this field in the parsed output # so that we can completely reuse the ZAPI mapping $only_hashref->{'file-metadata'} = delete $only_hashref->{'metadata-files'}; # Reuse CMode-ZAPI mapping my $copy = $pkg->_common_zapi_copy(); # These two fields are 7M-only push @$copy, (qw(volume-type volume-uuid)); my $cli_to_zapi = $pkg->_common_zapi_map(); my @zapi_to_cli = reverse %$cli_to_zapi; my %obj_fields = (file => $filter->{file}); $pkg->_zapi_hash_copy( source => $only_hashref, target => \%obj_fields, copy => $copy, map => \@zapi_to_cli, source_has_extra_arrays => 1, ); $cs->_set_fields(row => \%obj_fields, need_to_quote => 1, is_zapi => 1); # CMode-ZAPI value translations can also be reused $pkg->_update_state_objs_cmode_zapi( state_objs => \@state_objs, command_interface => $opts{command_interface}, ); $Log->exit() if $may_exit; return @state_objs; } =head2 compare_fingerprint_dump # Get original file fingerprint dump my $orig_dump_cs = $file_obj->show_fingerprint(...); # ... Perform operations using the file ... # Get final file fingerprint dump my $final_dump_cs = $file_obj->show_fingerprint(...); # Compare fingerprint dumps $orig_dump_cs->compare_fingerprint_dump(final_fingerprint_cs => $final_dump_cs); This method is to be used to compare the file fingerprint dump. The typical usage of this would be to obtain the fingerprint dump soon after the file is created, perform some actions, obtain the fingerprint dump and then compare the fingerprint to see that they match. This would typically be used for WORM (Write-Once Read-Many) files, though this method can be used for any type of file. # Compares the default list (listed below in the "Options" section) $orig_dump_cs->compare_fingerprint_dump(final_fingerprint_cs => $final_dump_cs); # In addition to the default list, also compare the field "scope" $orig_dump_cs->compare_fingerprint_dump( final_fingerprint_cs => $final_dump_cs, additional_fields_to_compare => [qw(scope)] ); # Compares the default list except the "snaplock-license" field $orig_dump_cs->compare_fingerprint_dump( final_fingerprint_cs => $final_dump_cs, fields_to_ignore => [qw(snaplock-license)] ); On mismatch, this throws a C exception. For example, if the file-size changes from "123" to "234", then the error text will be: File fingerprint comparison failed for file 'somefile'. The list of mismatches are: Field Original Value Final Value file-size '123' '234' The list of mismatches can also be programmatically extracted from the exception object, like this: try { $orig_dump_cs->compare_fingerprint_dump(other_fingerprint_cs => $final_dump_cs); } catch NACL::C::Exceptions::VolumeFileFingerprintDump::FingerprintMismatch with { my $exception = $_[0]; my %mismatches = $exception->mismatch_values(); # The contents of %mismatches will be: # # { # 'file-size' => { # 'final_value' => '234', # 'original_value' => '123' # } # } # }; =over =item Options =over =item C<< final_fingerprint_cs => $fingerprint_dump_cs >> (Mandatory) The C object for the final fingerprint dump. =item C<< fields_to_compare => \@fields_to_compare >> (Optional) An array-reference of the fields to be compared. If not provided, the following fields are compared: 'file','filer-id','hostname','snaplock-license','volume','volume-snaplock-type', 'volume-containing-aggregate','aggregate-uuid','fsid','fileid','file-type', 'file-size','creation-time','formatted-creation-time','modified-time', 'formatted-modified-time','changed-time','formatted-changed-time','access-time', 'formatted-access-time','owner-id','group-id' Also, if the file is B a C file, then C is also compared. =item C<< additional_fields_to_compare => \@additional_fields_to_compare >> (Optional) This argument is to be used if there are fields in addition to the default list that need to be compared. =item C<< fields_to_ignore => \@fields_to_ignore >> (Optional) An array-reference of fields to be ignored, i.e. the values of these fields are not compared. =back =back =cut sub compare_fingerprint_dump { $Log->enter() if $may_enter; my ($self, @args) = @_; my %opts = validate_with( params => \@args, spec => { fields_to_compare => {type => ARRAYREF, optional => 1}, additional_fields_to_compare => {type => ARRAYREF, default => []}, fields_to_ignore => {type => ARRAYREF, default => []}, final_fingerprint_cs => {type => OBJECT, isa => __PACKAGE__}, }, ); if (!exists $opts{fields_to_compare}) { my @list = (qw(file filer-id hostname snaplock-license volume volume-snaplock-type volume-containing-aggregate aggregate-uuid fsid fileid file-type file-size creation-time formatted-creation-time modified-time formatted-modified-time changed-time formatted-changed-time access-time formatted-access-time owner-id group-id)); if (!$self->file_type_isset() || $self->file_type() !~ /worm_appendable/) { push @list, 'data-fingerprint'; } $opts{fields_to_compare} = \@list; } my @fields_to_compare = (@{$opts{fields_to_compare}}, @{$opts{additional_fields_to_compare}}); my @fields_to_ignore = @{$opts{fields_to_ignore}}; if (@fields_to_ignore) { require List::Compare; my $lc = List::Compare->new( { lists => [\@fields_to_compare, \@fields_to_ignore], unsorted => 1, accelerated => 1 } ); @fields_to_compare = $lc->get_Lonly(); } my $final_cs = $opts{final_fingerprint_cs}; my %self_fields_vals = map {$_ => scalar $self->$_()} @fields_to_compare; try { $final_cs->verify_fields(%self_fields_vals); } catch NACL::Exceptions::VerifyFailure with { my $verify_exception = $_[0]; my $unexpected_values = $verify_exception->unexpected_values(); my $common_formatting = $self->_common_formatting_for_verification(); my $formatted_headers = sprintf $common_formatting, 'Field', 'Original Value', 'Final Value'; my $err_msg = "File fingerprint comparison failed for file '" . $self->file() . "'. The list of mismatches are:\n$formatted_headers\n"; my %mismatch_hash; while (my ($field, $final_val) = each %$unexpected_values) { my $orig_val = $self_fields_vals{$field}; $mismatch_hash{$field} = { original_value => $orig_val, final_value => $final_val, }; my $formatted_val = sprintf $common_formatting, $field, _dump_one($orig_val), _dump_one($final_val); $err_msg .= "$formatted_val\n"; } require NACL::C::Exceptions::VolumeFileFingerprintDump::FingerprintMismatch; $Log->exit() if $may_exit; NACL::C::Exceptions::VolumeFileFingerprintDump::FingerprintMismatch->throw( $err_msg, mismatch_values => \%mismatch_hash, ); }; $Log->exit() if $may_exit; } 1;