# # Copyright (c) 2001-2010 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary VolumeCloneSplit ComponentState Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here =head1 NAME NACL::CS::VolumeCloneSplit =head1 DESCRIPTION C is a derived class of L. It represents the state of an ONTAP VolumeCloneSplit. A related class is L, which represents access to an ONTAP VolumeCloneSplit. =head1 ATTRIBUTES The individual pieces of data that are part of the state of the VolumeCloneSplit element are the attributes of the VolumeCloneSplit ComponentState. =over =item C<< "inodes_processed" >> Filled in for CMode CLI, CMode ZAPI. CMode ZAPI: Accessible through the field "inodes-processed" in the output of ZAPI "volume-clone-split-status" =item C<< "volume" >> =item C<< "flexclone" >> Filled in for CMode CLI, CMode ZAPI. CMode ZAPI: Accessible through the field "name" in the output of ZAPI "volume-clone-split-status" =item C<< "inodes_total" >> Filled in for CMode CLI, CMode ZAPI. CMode ZAPI: Accessible through the field "inodes-total" in the output of ZAPI "volume-clone-split-status" =item C<< "blocks_scanned" >> Filled in for CMode CLI, CMode ZAPI. CMode ZAPI: Accessible through the field "blocks-scanned" in the output of ZAPI "volume-clone-split-status" =item C<< "vserver" >> =item C<< "blocks_updated" >> Filled in for CMode CLI, CMode ZAPI. CMode ZAPI: Accessible through the field "blocks-updated" in the output of ZAPI "volume-clone-split-status" =item C<< "inode_percentage_complete" >> Filled in for CMode CLI, CMode ZAPI. CMode ZAPI: Accessible through the field "inode-percentage-complete" in the output of ZAPI "volume-clone-split-status" =back =cut package NACL::CS::VolumeCloneSplit; use strict; use warnings; use Params::Validate qw(validate); use NACL::ComponentUtils qw(_dump_one Dumper); use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use NACL::Exceptions::NoElementsFound qw(:try); use NACL::APISet::Exceptions::NoMatchingEntriesException (); use NACL::C::Exceptions::VolumeCloneSplit::CloneAttachedToParent; use NACL::C::Exceptions::VolumeCloneSplit::NonExistentVolume; use NACL::C::Exceptions::VolumeCloneSplit::NotAClone; use base 'NACL::CS::ComponentState::ONTAP'; use Class::MethodMaker [ scalar => 'inodes_processed', scalar => 'volume', scalar => 'flexclone', scalar => 'inodes_total', scalar => 'blocks_scanned', scalar => 'vserver', scalar => 'blocks_updated', scalar => 'inode_percentage_complete', ]; =head1 METHODS =head2 fetch my $VolumeCloneSplit_state = NACL::CS::VolumeCloneSplit->fetch(command_interface => $ci, ...); my @VolumeCloneSplit_states = NACL::CS::VolumeCloneSplit->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. =over =item Exceptions =over =item C This exception is thrown when you try to find any ongoing split actions on a cloned volume which hasn't yet separated from its parent. =item C This exception is thrown when you try to find any ongoing split actions on a volume which doesn't exist. =item C This exception is thrown when you try to find any ongoing split actions on a normal volume (it was either never cloned before or it was cloned but it had been split from the parent). =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 = shift; my %opts = @_; my @state_objs; try { @state_objs = $pkg->SUPER::fetch( %opts, choices => [ { method => '_fetch_cmode_cli', interface => 'CLI', set => 'CMode', }, { method => '_fetch_cmode_zapi', interface => 'ZAPI', set => 'CMode', }, { method => '_fetch_7mode_cli', interface => 'CLI', set => '7Mode', }, ], exception_text => 'No matching volume clone split(s) found' ); } catch NACL::C::Exceptions::VolumeCloneSplit::NonExistentVolume with { my $ex = shift; # Existing 7Mode backend behavior is to throw NoElementsFound for this case. # Let's retain the same. unless ($opts{command_interface}->is_7mode && $opts{allow_empty}) { $Log->exit() if $may_exit; $ex->throw; } } catch NACL::C::Exceptions::VolumeCloneSplit::NotAClone with { my $ex = shift; # Existing 7Mode backend behavior is to throw NoElementsFound for this case. # Let's retain the same. unless ($opts{command_interface}->is_7mode && $opts{allow_empty}) { $Log->exit() if $may_exit; $ex->throw; } } catch NACL::APISet::Exceptions::ResponseException with { my $ex = shift; if ($ex->text =~ /not being split from its parent/i) { # Bless this object of new component exception type and rethrow # This will be hit for CMode CLI, ZAPI and 7Mode CLI $Log->exit() if $may_exit; NACL::C::Exceptions::VolumeCloneSplit::CloneAttachedToParent ->convert_and_throw(exception => $ex); } elsif ($ex->text =~ /volume .+ was not found/i) { # Bless this object of new component exception type and rethrow # This will be hit for CMode ZAPI. For 7Mode CLI, the backend itself # will classify and throw the exception $Log->exit() if $may_exit; NACL::C::Exceptions::VolumeCloneSplit::NonExistentVolume ->convert_and_throw(exception => $ex); } elsif ($ex->text =~ /not a clone/i) { # Bless this object of new component exception type and rethrow # This will be hit for CMode CLI, ZAPI. For 7Mode CLI, the backend # itself will classify and throw the exception $Log->exit() if $may_exit; NACL::C::Exceptions::VolumeCloneSplit::NotAClone ->convert_and_throw(exception => $ex); } else { # For any other failures, rethrow it back $Log->exit() if $may_exit; $ex->throw; } }; $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, @args) = @_; my $ex; my @state_objs = $pkg->SUPER::_fetch_cmode_cli( @args, api => 'volume_clone_split_show', response_exception_ref => \$ex ); if (defined($ex) && $ex->text =~ /volume .+ was not found/i) { $Log->exit() if $may_exit; NACL::C::Exceptions::VolumeCloneSplit::NonExistentVolume ->convert_and_throw(exception => $ex); } $Log->exit() if $may_exit; return @state_objs; } ## end sub _fetch_cmode_cli sub _fetch_cmode_zapi { $Log->enter() if $may_enter; my $pkg = shift; my %opts = validate @_, $pkg->_fetch_backend_validate_spec(); my $apiset = $opts{apiset}; my $filter = $opts{filter}; my $caught_exception; my $status_response; my %cmd_args; # ZAPI runs only in vserver context my $vserver = $pkg->_handle_zapi_vserver_context( api_opts => \%cmd_args, vserver => $opts{filter}->{vserver}, command_interface => $opts{command_interface} ); $pkg->_hash_copy( source => $opts{filter}, target => \%cmd_args, map => {'flexclone' => 'volume'} ); try { $status_response = $apiset->volume_clone_split_status(%cmd_args); } catch NACL::APISet::Exceptions::NoMatchingEntriesException with { $Log->trace("Entry doesn't exist"); $caught_exception = 1; }; if ($caught_exception) { $Log->exit() if $may_exit; return; } my $status_output = $status_response->get_parsed_output(); $Log->debug(sub { Dumper($status_output) }); my @state_objs; my $state_fields = $pkg->_zapi_hash_copy( source => $status_output->[0]{'clone-split-details'}[0] {'clone-split-detail-info'}[0], copy => [ qw(blocks-scanned blocks-updated inode-percentage-complete inodes-processed inodes-total ) ], map => ["name" => "flexclone",], ); my $obj = $pkg->new(command_interface => $opts{command_interface}); $obj->_set_fields(row => $state_fields); $obj->vserver($vserver); push @state_objs, $obj; $Log->exit() if $may_exit; return @state_objs; } ## end sub _fetch_cmode_zapi sub _fetch_7mode_cli { $Log->enter() if $may_enter; my $pkg = shift; my %opts = validate @_, $pkg->_fetch_backend_validate_spec(); my $apiset = $opts{apiset}; my ($response, $caught_exception, $output, %row, @state_objs); my $filter = $opts{filter}; my $obj = $pkg->new(command_interface => $opts{command_interface}); if (exists($filter->{flexclone})) { try { $response = $apiset->vol_clone_split_status( 'volume' => $filter->{flexclone}); } catch NACL::APISet::Exceptions::InvalidParamValueException with { my $ex = shift; if ($ex->text =~ /volume .+ was not found/i) { $Log->exit() if $may_exit; NACL::C::Exceptions::VolumeCloneSplit::NonExistentVolume ->convert_and_throw(exception => $ex); } elsif ($ex->text =~ /not a clone/i) { $Log->exit() if $may_exit; NACL::C::Exceptions::VolumeCloneSplit::NotAClone ->convert_and_throw(exception => $ex); } else { # I'm not sure about the possible other cases for which this # exception is obtained. But the existing code catches all # InvalidParamValueException and turns them as NoElementsFound. # Let's retain the same behavior. $caught_exception = 1; } }; } else { try { $response = $apiset->vol_clone_split_status(); } catch NACL::APISet::Exceptions::CommandFailedException with { my $ex = shift; unless ($ex->text =~ /No clone status/i) { $Log->exit() if $may_exit; $ex->throw(); } # Turn it into NoElementsFoundException $caught_exception = 1; }; } if ($caught_exception) { $Log->exit() if $may_exit; return; } $output = $response->get_parsed_output(); foreach my $vol (@$output) { my $final_attributes = $pkg->_hash_copy( source => $vol, copy => [ qw( blocks_scanned blocks_updated inodes_processed inodes_total inode_percentage_complete ) ], map => {"clone_volume" => "flexclone",} ); $obj->_set_fields(row => $final_attributes); push(@state_objs, $obj); } ## end foreach my $vol (@$output) $Log->exit() if $may_exit; return @state_objs; } ## end sub _fetch_7mode_cli 1;