# # Copyright (c) 2001-2010 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary Statistics ComponentState Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here =head1 NAME NACL::CS::Statistics =head1 DESCRIPTION C is a derived class of L. It represents the state of ONTAP Statistics. A related class is L, which represents access to ONTAP statistics. =head1 ATTRIBUTES The individual pieces of data that are part of the state of the Statistics element are the attributes of the Statistics ComponentState. =over =item C<< node >> =item C<< instance >> =item C<< category >> =item C<< object >> =item C<< counter >> =item C<< value >> =item C<< delta >> =item C<< description >> =item C<< prop >> =item C<< base_value >> Filled in for CMode CLI. =item C<< denominator >> Filled in for CMode CLI. =item C<< base_units >> Filled in for CMode CLI. =item C<< numerators >> Filled in for CMode CLI. (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $numerators = $obj->numerators(); # $numerators contains a reference to the array of values my @numerators = $obj->numerators(); # @numerators contains the array of values Filled in for CMode CLI. =item C<< countername >> Filled in for CMode CLI. =item C<< base_delta >> Filled in for CMode CLI. =item C<< base_rate >> Filled in for CMode CLI. =item C<< labels >> Filled in for CMode CLI,ZAPI. (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $labels = $obj->labels(); # $labels contains a reference to the array of values my @labels = $obj->labels(); # @labels contains the array of values Filled in for CMode CLI. =item C<< instancename >> Filled in for CMode CLI. =item C<< valueorlabel >> Filled in for CMode CLI. =item C<< numerator >> Filled in for CMode CLI. =item C<< values >> Filled in for CMode CLI. (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $values = $obj->values(); # $values contains a reference to the array of values my @values = $obj->values(); # @values contains the array of values Filled in for CMode CLI. =item C<< describe >> Filled in for CMode CLI. (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $describe = $obj->describe(); # $describe contains a reference to the array of values my @describe = $obj->describe(); # @describe contains the array of values Filled in for CMode CLI. =item C<< objectname >> Filled in for CMode CLI. =item C<< denominators >> Filled in for CMode CLI. (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $denominators = $obj->denominators(); # $denominators contains a reference to the array of values my @denominators = $obj->denominators(); # @denominators contains the array of values Filled in for CMode CLI. =item C<< aggr_display >> Filled in for CMode CLI. =item C<< scope_display_desc >> Filled in for CMode CLI. =item C<< scope_display >> Filled in for CMode CLI. =item C<< object_name >> Filled in for CMode CLI. =item C<< instance_name >> Filled in for CMode CLI. =item C<< vserver >> Filled in for CMode CLI. =item C<< category_name >> Filled in for CMode CLI. =item C<< counter_name >> Filled in for CMode CLI. =item C<< category_instance >> Filled in for CMode CLI. =item C<< instance_uuid >> Filled in for CMode CLI. =item C<< filter >> Filter Data Filled in for CMode CLI. =item C<< raw >> Raw Values Request Filled in for CMode CLI. =item C<< start_time >> Filled in for CMode CLI. =item C<< end_time >> Filled in for CMode CLI. =item C<< display_values >> Filled in for CMode CLI. =item C<< validity >> Filled in for CMode CLI. =item C<< display_value >> *Text Value Filled in for CMode CLI. =item C<< additional_info >> Additional Info List possible value(s) are, text (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $additional_info = $obj->additional_info(); # $additional_info contains a reference to the array of values my @additional_info = $obj->additional_info(); # @additional_info 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 => { additional_info = [ value1, value2...] } Filled in for CMode CLI. =item C<< elapsed_time >> Elapsed Time Filled in for CMode CLI. =item C<< tab_display >> Tabular Display Filled in for CMode CLI. =item C<< counter64_value >> Value Filled in for CMode CLI. =item C<< instance_sort_id >> Instance Sort ID Filled in for CMode CLI. =item C<< iteration_count >> Iteration Count Filled in for CMode CLI. =back =head1 ADDITIONAL FILTER FIELDS These fields are not attributes, but can still be used in the filter. =over =item C The sample-id associated with the statistics. =item C<< "sort_key" >> Filled in for CMode CLI. =item C<< "sort_order" >> Filled in for CMode CLI. =back =cut package NACL::CS::Statistics; use strict; use warnings; use Params::Validate qw(validate); use NATE::Log qw(log_global); use Devel::StackTrace; my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use NACL::APISet::Exceptions::ResponseException qw(:try); use NACL::APISet::Exceptions::ConnectionFailedException; use NACL::APISet::Exceptions::InvalidParamValueException; use NACL::Exceptions::InvalidChoice; use base 'NACL::CS::ComponentState::ONTAP'; use Class::MethodMaker [ scalar => "node", scalar => "instance", scalar => "category", scalar => "object", scalar => "counter", scalar => "value", scalar => "delta", scalar => "description", scalar => "prop", scalar => 'base_value', scalar => 'denominator', scalar => 'base_units', array => 'numerators', scalar => 'countername', scalar => 'base_delta', scalar => 'base_rate', array => 'labels', scalar => 'instancename', scalar => 'valueorlabel', scalar => 'numerator', array => 'values', array => 'describe', scalar => 'objectname', array => 'denominators', scalar => 'aggr_display', scalar => 'scope_display_desc', scalar => 'scope_display', scalar => 'object_name', scalar => 'instance_name', scalar => 'vserver', scalar => 'category_name', scalar => 'counter_name', scalar => 'category_instance', scalar => 'instance_uuid', scalar => 'filter', scalar => 'sample_id', scalar => 'raw', scalar => 'start_time', scalar => 'end_time', scalar => 'display_values', scalar => 'validity', scalar => 'display_value', array => 'additional_info', scalar => 'elapsed_time', scalar => 'tab_display', scalar => 'counter64_value', scalar => 'instance_sort_id', scalar => 'iteration_count', scalar => 'preset', scalar => 'sort_order', ]; =head1 METHODS =head2 fetch my $Statistics_state = NACL::CS::Statistics->fetch(command_interface => $ci, ...); my @Statistics_states = NACL::CS::Statistics->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 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 @state_objs = $pkg->SUPER::fetch( @_, choices => [ { method => '_fetch_cmode_cli', interface => 'CLI', set => 'CMode', }, { method => '_fetch_cmode_zapi', interface => 'ZAPI', set => 'CMode', check => '_cmode_zapi_check', }, { method => '_fetch_7mode_cli', interface => 'CLI', set => '7Mode', }, ], exception_text => 'No matching statistics(s) found', show_cmd => 'statistics 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, %opts) = @_; my @state_objs; # All other CMode CLI show commands result in a NoMatchingEntriesException # being thrown if an invalid value is passed in the filter. However, for # Statistics it results in an InvalidParamValueException. This needs to be # caught. This is the only CMode CS implementation for which this # exception is thrown, hence we haven't moved it to the _fetch_cmode_cli # implementation in ComponentState.pm try { @state_objs = $pkg->SUPER::_fetch_cmode_cli( %opts, api => 'statistics_show', ); } ## end try catch NACL::APISet::Exceptions::InvalidParamValueException with { my $exception = shift; $Log->debug("InvalidParamValueException caught. Error:\n" . $exception->text()); }; $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(); my $apiset = $opts{apiset}; my $requested_fields = $opts{requested_fields}; my $filter = {%{$opts{filter}}}; my %stat_args; # based on what the caller supplied for object,instance and counter,create # an argument my $object_def_arg; # Some counters are array elements (Any counter with a "." in it denotes # that it's an element of an array). The product does not allow us to # access each array element individually, but we can request for the # entire array of values # Refer to burt 458089 my $counter; if ($filter->{counter}) { $counter = $filter->{counter}; if ($counter =~ /^([^.]+)\./) { $counter = $1; } } ## end if ( $filter->{counter... if ($filter->{object}) { $object_def_arg = $filter->{object}; if ($filter->{instance}) { $object_def_arg .= ":" . $filter->{instance}; if ($counter) { $object_def_arg .= ":" . $counter; } } elsif ($counter) { $object_def_arg .= "::" . $counter; } } ## end if ( $filter->{object}) # add a object:instance:counter argument to the call to stats if ($object_def_arg) { #@stat_args = {'obj_inst_cnt' => $object_def_arg}; $stat_args{obj_inst_cnt} = $object_def_arg; } my ($response, $caught_exception); try { $response = $apiset->stats_show(%stat_args); } catch NACL::APISet::Exceptions::InvalidParamValueException with { # A caught exception indicates that the parser caught an # invalid parameter while running 'stats show' $caught_exception = 1; }; if ($caught_exception) { $Log->exit() if $may_exit; return; } my $output = $response->get_parsed_output(); my @state_objs; foreach my $row (@$output) { my $obj_int_cnt = $row->{name}; my %final_attributes = (); # split up the name into object,value,counter ( $final_attributes{object}, $final_attributes{instance}, $final_attributes{counter} ) = ($obj_int_cnt =~ /([^:]+):(.+):([^:]+)/); $final_attributes{value} = $row->{value}; my $obj = $pkg->new(command_interface => $opts{command_interface}); $obj->_set_fields(row => \%final_attributes); push @state_objs, $obj; } ## end foreach my $row (@$output) $Log->exit() if $may_exit; return @state_objs; } ## end sub _fetch_7mode_cli sub _cmode_zapi_check { $Log->enter() if $may_enter; my ($pkg, %opts) = @_; if (exists $opts{filter}->{'sample-id'}) { my $msg = "The field 'sample-id' was provided in the filter in the " . 'call to NACL::CS::Statistics->fetch but ZAPI provides no ' . 'way to filter on the sample-id, so the ZAPI implementation ' . 'cannot be used'; $Log->comment($msg); $Log->exit() if $may_exit; NACL::Exceptions::InvalidChoice->throw($msg); } $Log->exit() if $may_exit; } 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 $requested_fields = $opts{requested_fields}; my $filter = $opts{filter}; my @state_objs; my (@objects_found, @objects_to_iterate_over); my $trace = Devel::StackTrace->new(); my $string = $trace->as_string(); my $objects_response = $apiset->perf_object_list_info(); my $objects_output = $objects_response->get_parsed_output(); foreach my $object_hash (@{$objects_output->[0]{objects}[0]{'object-info'}}) { push @objects_found, $object_hash->{name}; } if (defined $filter->{object}) { my $found; foreach my $found_object (@objects_found) { if ($filter->{object} eq $found_object) { $found = 1; last; } } ## end foreach my $found_object (@objects_found) if ($found) { push @objects_to_iterate_over, $filter->{object}; } else { # Search for the object the user requested for in the list # of objects found. If we do not find it, then it is an invalid # object name. Return immediately. $Log->exit() if $may_exit; return; } ## end else [ if ($found) } else { @objects_to_iterate_over = @objects_found; } return if($string =~ /NACL::Exceptions::NoElementsFound::gather_diagnostics/i); # Now, drill down on each object. This includes finding the instances # and counters (along with their value, description and properties) my (%query_hash); if (defined $filter->{instance}) { $query_hash{query} = {name => $filter->{instance}}; } foreach my $object (@objects_to_iterate_over) { my @instances; try { my $instance_response = $apiset->perf_object_instance_list_info_iter( objectname => $object, 'desired-attributes' => {name => 1}, 'max-records' => 10000, %query_hash ); my $instance_output = $instance_response->get_parsed_output(); foreach my $instance_hash ( @{ $instance_output->[0]{'attributes-list'}[0] {'instance-info'} } ) { push @instances, $instance_hash->{name}; } ## end foreach my $instance_hash (... } ## end try catch NACL::APISet::Exceptions::ResponseException with { # Do nothing. We would enter here if the instance could not # be found for this object. However, this instance might be # valid for some other object, so do not return. }; my %counters_keyed_by_name; if ($pkg->_want_any_field_of( requested_fields => $requested_fields, filter => $filter, fields_filled_by_api => [qw(description prop)] ) ) { my $counter_response = $apiset->perf_object_counter_list_info( objectname => $object); my $counter_output = $counter_response->get_parsed_output(); foreach my $counter_hash ( @{$counter_output->[0]{counters}[0]{'counter-info'}}) { # Store the details in a hash which is keyed by the counter name $counters_keyed_by_name{$counter_hash->{name}} = {}; # Copy out the details we need and map them to their CS name $pkg->_hash_copy( source => $counter_hash, target => $counters_keyed_by_name{$counter_hash->{name}}, map => { 'desc' => 'description', 'properties' => 'prop' }, copy => [qw(labels)] ); } ## end foreach my $counter_hash ( ... } ## end if ( $pkg->_want_any_field_of... my %counter_hash; if (defined $filter->{counter}) { $counter_hash{counters} = [$filter->{counter}]; } my %extra_args = (defined $filter->{node}) ? ('filter-data' => "node_name=".$filter->{node}) : (); foreach my $instance (@instances) { try { my $value_response = $apiset->perf_object_get_instances( objectname => $object, instances => [$instance], %extra_args, %counter_hash ); my $value_output = $value_response->get_parsed_output(); foreach my $value_hash ( @{ $value_output->[0]{instances}[0]{'instance-data'}[0] {counters}[0]{'counter-data'} } ) { my $obj = $pkg->new( command_interface => $opts{command_interface}); my $counter = $value_hash->{name}; my $value = $value_hash->{value}; $obj->object($object); $obj->instance($instance); $obj->counter($counter); $obj->value($value); if (exists $counters_keyed_by_name{$counter} ) { my $counter_info_hashref = $counters_keyed_by_name{$counter}; $obj->_set_fields(row => $counter_info_hashref); } push @state_objs, $obj; } ## end foreach my $value_hash ( @{... } ## end try catch NACL::APISet::Exceptions::ResponseException with { # The counter does not exist in this object:instance hierarchy # Do nothing since it might exist in some other object:instance # hierarchy }; } ## end foreach my $instance (@instances) } ## end foreach my $object (@objects_to_iterate_over) $Log->exit() if $may_exit; return @state_objs; } ## end sub _fetch_cmode_zapi sub _apply_filter { my $pkg = shift; $pkg->SUPER::_apply_filter(@_, treat_special_characters_literally => 1); } sub _extra_filter_fields { $Log->enter() if $may_enter; $Log->exit() if $may_exit; return [ qw( sample-id sort-key sort-order) ]; } 1;