# # Copyright (c) 2001-2012 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary VolumeListFilesInDirectory ComponentState Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here =head1 NAME NACL::CS::VolumeListFilesInDirectory =head1 DESCRIPTION C is a derived class of L. Each object returned represents a file/directory present in the provided directory. =head1 ATTRIBUTES Since this subclasses L all of the attributes of that package are attributes of the objects returned by the fetch methods of this package. =cut package NACL::CS::VolumeListFilesInDirectory; use strict; use warnings; use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use Params::Validate qw(validate); use Data::Dumper; use NACL::Exceptions::NoElementsFound qw(:try); use NATE::Exceptions::Argument; use base 'NACL::CS::VolumeFile'; no strict 'refs'; # The base class (NACL::CS::VolumeFile) adds "_paths" for both the fetch # front-end and back-end but that is not applicable here, so delete it. foreach my $method (qw(_fetch_validate_spec _fetch_backend_validate_spec)) { *{$method} = sub { $Log->enter() if $may_enter; my ($pkg, @opts) = @_; my $super_method = "SUPER::$method"; my %spec = %{$pkg->$super_method(@opts)}; delete $spec{_paths}; $Log->exit() if $may_exit; return {%spec}; }; } use strict 'refs'; =head1 METHODS =head2 fetch # Scalar context, 7Mode my $state = NACL::CS::VolumeListFilesInDirectory->fetch( command_interface => $ci, filter => { 'directory-path' => $dir_path } ); # List context, 7Mode my @states = NACL::CS::VolumeListFilesInDirectory->fetch( command_interface => $ci, filter => { 'directory-path' => $dir_path } ); # Scalar context, CMode my $state = NACL::CS::VolumeListFilesInDirectory->fetch( command_interface => $ci, filter => { 'directory-path' => $dir_path, vserver => $vserver } ); # List context, CMode my @states = NACL::CS::VolumeListFilesInDirectory->fetch( command_interface => $ci, filter => { 'directory-path' => $dir_path, vserver => $vserver } ); (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. For this method, C is a mandatory input in the filter. Also, for CMode, C is mandatory in the filter. Uses a CMode-ZAPI or 7Mode-ZAPI APISet. =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(); unless (exists $opts{filter}->{'directory-path'}) { $Log->exit() if $may_exit; NATE::Exceptions::Argument->throw("'directory-path' is mandatory " . 'in the filter but was not provided'); } # Base class of this is NACL::CS::VolumeFile. We want to go straight to # ONTAP's fetch, so use the "base_class" method. my $base_class = $pkg->base_class(); my $base_fetch = "${base_class}::fetch"; my @state_objs = $pkg->$base_fetch( %opts, choices => [ { method => '_fetch_cmode_zapi', interface => 'ZAPI', set => 'CMode', }, { method => '_fetch_7mode_zapi', interface => 'ZAPI', set => '7Mode', }, ], exception_text => 'No matching volume file(s) found', frontend => 'NACL::CS::VolumeListFilesInDirectory::fetch', ); $Log->exit() if $may_exit; return wantarray ? @state_objs : $state_objs[0]; } ## end sub fetch sub _fetch_cmode_zapi { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my %opts = validate @args, $pkg->_fetch_backend_validate_spec(); # If a Node command_interface is used, "vserver-override-name" needs # to be sent to the ZAPI for it to work (it is a Vserver-context ZAPI). # Use _handle_zapi_vserver_context for this. my %pass_through_opts; my $vserver = $pkg->_handle_zapi_vserver_context( api_opts => \%pass_through_opts, vserver => $opts{filter}->{vserver}, command_interface => $opts{command_interface}, frontend_method => 'fetch', ); # The argument "path" is accepted directly as input to the ZAPI, it # is not accepted through "query". Hence, send it through the # pass-through options. my $directory_path = $opts{filter}->{'directory-path'}; $pass_through_opts{path} = $directory_path; # Get the list of "real" field names (i.e. hyphenated names) of # NACL::CS::VolumeFile. Delete all of the fields which we populate by # hand. All the remaining fields are those filled in by the ZAPI # and are used as the "copy" argument for SUPER::_fetch_cmode_zapi. my @real_fields = NACL::CS::VolumeFile->get_all_real_field_names(); my %attributes_hash; map { $attributes_hash{$_} = 1 } @real_fields; my @extra_fields = $pkg->_extra_populated_fields(); foreach my $field (@extra_fields) { delete $attributes_hash{$field}; } my @attributes = keys %attributes_hash; # At a minimum we need the name and file-type. If requested_fields # is set to something else (say [ 'perm' ]) then it will show only # that field, not "name" and "file-type". # This piece of code is to ensure "name" and "file-type" are # requested for. # If requested_fields = [], then don't modify it # If it contains a set of values, add "name" and "file-type" to it. my $orig_requested_fields = $opts{requested_fields}; my %requested_fields_hash; map { $requested_fields_hash{$_} = 1 } @$orig_requested_fields; if (keys %requested_fields_hash) { foreach my $field (qw(name file-type)) { $requested_fields_hash{$field} = 1; } } my @requested_fields = keys %requested_fields_hash; my $base_class = $pkg->base_class(); my $base_fetch_cmode_zapi = "${base_class}::_fetch_cmode_zapi"; my @state_objs = $pkg->$base_fetch_cmode_zapi( %opts, requested_fields => \@requested_fields, api => 'file-list-directory-iter', copy => \@attributes, pass_through_opts => \%pass_through_opts, fix_api_opts => sub { my %fix_opts = @_; my $api_opts = $fix_opts{api_opts}; # The description of "query" for this ZAPI from zapidoc: # "not used - here to satisfy smfgen" # Since it's not used, let's just delete it. delete $api_opts->{query}; # Unlike other ZAPIs, this requires desired-attributes as a # typedefarray (others require it as a typedef). To satisfy # what it requires, put the constructed hashref with an arrayref. if (exists $api_opts->{'desired-attributes'}) { $api_opts->{'desired-attributes'} = [$api_opts->{'desired-attributes'}]; } }, ); # Filtering is not done by the ZAPI, we need to do it in Perl code ${$opts{_apply_filter}} = 1; foreach my $state_obj (@state_objs) { $state_obj->vserver($vserver); $state_obj->directory_path($directory_path); $state_obj->path($directory_path . '/' . $state_obj->name()); } $Log->exit() if $may_exit; return @state_objs; } sub _fetch_7mode_zapi { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my %opts = validate @args, $pkg->_fetch_backend_validate_spec(); my $apiset = $opts{apiset}; my $path = $opts{filter}->{'directory-path'}; my ($does_not_exist, $response_start); try { $response_start = $apiset->file_list_directory_iter_start(path => $path); } catch NACL::APISet::Exceptions::ResponseException with { my $exception = $_[0]; if ($exception->error_number() == 2) { $does_not_exist = 1; } else { $Log->exit() if $may_exit; $exception->throw(); } }; if ($does_not_exist) { $Log->exit() if $may_exit; return; } my $row = $response_start->get_parsed_output()->[0]; my $next_response = $apiset->file_list_directory_iter_next( tag => $row->{tag}, maximum => $row->{records} ); my $next_output = $next_response->get_parsed_output(); my @state_objs; foreach my $row (@{$next_output->[0]{files}[0]{'file-info'}}) { my $obj = $pkg->new(command_interface => $opts{command_interface}); $obj->_set_fields(row => $row); $obj->directory_path($path); $obj->path($path . '/' . $obj->name()); push @state_objs, $obj; } $apiset->file_list_directory_iter_end(tag => $row->{tag}); $Log->exit() if $may_exit; return @state_objs; } =head1 CANNED FILTERS See http://wikid.netapp.com/w/QA/projects/Libraries_Initiative/Project/Common_Infrastructure/Users_Guide/Component_Layer_Users_Guide#Canned_filters for a description of canned filters. =head2 file Returns objects for files only. =cut sub _canned_filter_file { return {'file-type' => 'file'}; } =head2 directory Returns objects for directories only. =cut sub _canned_filter_directory { return {'file-type' => 'directory'}; } 1;