# Copyright (c) 2014 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. ## @summary Statistics MTask Module ## @author roopeshv@netapp.com ## @status shared package NACL::MTask::Statistics; use strict; use warnings; use Readonly; use base 'NACL::MTask::MTask'; use List::MoreUtils qw(all zip); use Params::Validate ':all'; use NATE::Log 'log_global'; use NACL::CS::Statistics; my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); Readonly::Array my @ALLOWED_OBJECTS_IN_FILTER => ('volume', 'aggregate'); use Class::MethodMaker [ new => [qw/-init new/], scalar => ['command_interface'], hash => [qw/cached_statistics labelled_filters/], ]; sub all_values_are_hashrefs { my $opts = shift; all { ref eq 'HASH' } values %{$opts}; } =head1 NAME NACL::MTask::Statistics =head1 DESCRIPTION C helps you get, cache and verify wafl statistics. =head1 SYNOPSIS The statistics are the values you can fetch through C<< NACL::CS::Statistics::fetch >>. This class object is created with a hash of filters that you can pass to C<< NACL::CS::Statistics::fetch >>. my $statistics = NACL::MTask::Statistics->new( command_interface => $ci, labelled_filters => { spacetax => { object => 'volume', instance => $vol_name, counter => 'wvsblk_space_tax', raw => 'true' # if not provided, defaults to 'true' }, ... } ); This statistics objects will store all provided filters for later use for retrieving current values, caching them for later use and verification. All the statistics can be cached at once by giving no arguments to the method. $statistics->cache_statistics(); The cached statistics are stored in a hash, with label used in initialization as keys and cached values as value. And can be retrieved by: my %cached_values = $statistics->cached_statistics(); $cached_values{spacetax}; To update a set of statistics, invoke cache_statistics on the object. $statistics->cache_statitics(labels => ['spacetax', ...]); To get current values of statistics of any given label, you can: my $current_values = $statistics->fetch_statistics(labels => ['spacetax', ...]); my $spacetax_current = $current_values->{spacetax}; To verify the current value of counters to cached counters, you can use: $statistics->verify_statistics( labelled_filters => ['spacetax', 'label1'], callback => sub { my $counters = shift; my $spacetax_cached = $counters->{spacetax}->{cached}; my $spacetax_current = $counters->{spacetax}->{current}; .... } ) If a cache_counters for a label is not issued, the cached value will be undef. =head1 METHODS =head2 new Creates a new statistics object to retrieve, cache and verify statistics using labels. my $statistics = NACL::MTask::Statistics->new( command_interface => $ci, labelled_filters => { spacetax => { object => 'volume', instance => $vol_name, counter => 'wvsblk_space_tax', raw => 'true' # if not provided, defaults to 'true' }, ... } ); =over =item Options =over =item C<< command_interface => $ci >> The command interface. Uses the command interface for apiset if provided. Uses the command interface of the mandatory aggregate provided. =item C<< labelled_filters => {label => $hashref, ...} >> (Mandatory) Hashref of labels and filter hashrefs. The filter hashrefs should be at least valid input for C<< NACL::CS::Statistics::fetch >> filter parameter. Additionally, the filter can default C<< raw >> value to 'true' if not provided. Also, {object => 'aggregate', instance => 'aggr_name'} or {object => 'volume', instance => 'vol_name'}, can be auto deduced if {aggregate => $Aggr_Obj } or {volume => $Vol_Obj} are provided. =back =back =cut sub init { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { labelled_filters => { type => HASHREF, callbacks => {'values are hashrefs' => \&all_values_are_hashrefs,} } }, ); $pkg->command_interface(delete $opts{command_interface}); my $filters_for_label = delete $opts{labelled_filters}; _process_all_filters($filters_for_label); $pkg->labelled_filters(%$filters_for_label); $Log->exit() if $may_exit; } ## end sub init =head2 cache_statistics On a statistics object, it will cache the latest statistics for provided labels. If no labels are provided, all labels are used. my $statistics = NACL::MTask::Statistics->new( command_interface => $ci, labelled_filters => { spacetax => { object => 'volume', instance => $vol_name, counter => 'wvsblk_space_tax', raw => 'true' # if not provided, defaults to 'true' }, ... } ); $statistics->cache_statistics(labels => ['spacetax']); %cached = $statistics->cached_statistics(); # will only contain spacetax value $spacetax = $cached{spacetax}; $spacetax = $statistics->cached_statistics_index('spacetax'); # alternative for above 2 lines =over =item Options =over =item C<< labels => [qw(labels ....)] >> Takes an arrayref of labels that will be updated. Defaults to caching all statistics if no labels are provided. =back =back =cut sub cache_statistics { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => {labels => {type => ARRAYREF, optional => 1}} ); # *_set method on method maker methods merges passed in hash with # existing hash $pkg->cached_statistics_set(%{$pkg->fetch_statistics(%opts)}); $Log->exit() if $may_exit; return; } ## end sub cache_statistics =head2 fetch_statistics On a statistics object, it will fetch the latest statistics for provided labels. If no labels are provided, all labels are used. The method behaves very similar to cache_statistics, but doesn't cache the values for later use. The current values are returned as a hashref, with labels as keys and latest statistics value as values for the keys. my $statistics = NACL::MTask::Statistics->new( command_interface => $ci, labelled_filters => { spacetax => { object => 'volume', instance => $vol_name, counter => 'wvsblk_space_tax', raw => 'true' # if not provided, defaults to 'true' }, ... } ); $current_values = $statistics->fetch_statistics(labels => ['spacetax']); $spacetax = $current_values->{spacetax}; =over =item Options =over =item C<< labels => [qw(labels ....)] >> Takes an arrayref of labels that will be used to fetch current values. Defaults to caching all statistics if no labels are provided. =back =back =cut sub fetch_statistics { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => {labels => {type => ARRAYREF, optional => 1},} ); my $labelled_filters = $pkg->_get_labels_filters_hash_for(%opts); my $current_value_for = {}; while (my ($label, $filter) = each %{$labelled_filters}) { $current_value_for->{$label} = $pkg->_fetch_statistics_for(filter => $filter); } $Log->exit() if $may_exit; return $current_value_for; } ## end sub fetch_statistics =head2 verify_statistics On a statistics object, it will fetch the latest statistics for provided labels, and pass in the cached statistics values to the callback method. The callback can be used to verify the current and cached values. my $statistics = NACL::MTask::Statistics->new( command_interface => $ci, labelled_filters => { spacetax => { object => 'volume', instance => $vol_name, counter => 'wvsblk_space_tax', raw => 'true' # if not provided, defaults to 'true' }, ... } ); $statistics->cache_counters(); # will cache all statistics $statistics->verify_statistics( labelled_filters => ['spacetax', 'label1'], # only current and cached values of these two labels are passed callback => sub { my $counters = shift; my $spacetax_cached = $counters->{spacetax}->{cached}; my $spacetax_current = $counters->{spacetax}->{current}; .... } ); The first parameter to the callback is a hashref with labels as keys and values are a hashref like this, C<< { cached => $cached_value, current => $current_value} >>. =over =item Options =over =item C<< labelled_filters => [qw(labels ....)] >> Takes an arrayref of labels, of whose current and cached values are passed to callback subroutine. =item C<< callback_ => sub {...} or \&subroutine_name >> The first parameter to the callback is a hashref with labels as keys and values are a hashref like this, C<< { cached => $cached_value, current => $current_value} >>. =back =back =cut sub verify_statistics { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => { labelled_filters => {type => ARRAYREF,}, callback => {type => CODEREF}, } ); my $input_for_callback = {}; my $labels = $opts{labelled_filters}; foreach my $filter_label (@$labels) { my $cached_value = $pkg->cached_statistics_index($filter_label); my $latest_value = $pkg->fetch_statistics(labels => [$filter_label]) ->{$filter_label}; $input_for_callback->{$filter_label}->{cached} = $cached_value; $input_for_callback->{$filter_label}->{current} = $latest_value; } my $result = $opts{callback}->($input_for_callback); $Log->exit() if $may_exit; return $result; } ## end sub verify_statistics ## the subroutine takes arrayref of labels and gets the hashref containing ## the labels as keys and the filters for getting the statistics as the values. ## If no labels are given, it will default to all labels as argument. sub _get_labels_filters_hash_for { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => {labels => {type => ARRAYREF, optional => 1}} ); my @labels = @{delete $opts{labels} || $pkg->labelled_filters_keys()}; my @filters = $pkg->labelled_filters_index(@labels); my %data_filters = zip @labels, @filters; $Log->exit() if $may_exit; return \%data_filters; } ## end sub _get_labels_filters_hash_for ## fetches statistics value for given filter. ## This is nothing more than a pass through for NACL::CS::Statistics::fetch sub _fetch_statistics_for { $Log->enter() if $may_enter; my $pkg = shift; my %opts = $pkg->_common_validate_with( params => \@_, additional_spec => {filter => {type => HASHREF},} ); my $result = NACL::CS::Statistics->fetch(%opts)->value(); $Log->exit() if $may_exit; return $result; } ## end sub _fetch_statistics_for ## For the parameters given to C<< new() >>. This goes through the filters ## provided (which are values of the hashref passed in), and one by one ## processes them. sub _process_all_filters { my $labelled_filters = shift; map { _process_filter($_) } values %$labelled_filters; } ## For a given hashref of filters for C<< new() >>, this function will ## a) make C<< raw >> value default to 'true' if not provided, and ## b) converts {aggregate => $Aggr_Obj} or {volume => $Vol_Obj} into ## {object => 'aggregate', instance => $Aggr_Obj->aggregate} ## {object => 'volume', instance => $Vol_Obj->volume} sub _process_filter { $Log->enter() if $may_enter; my $filter = shift; $filter->{raw} ||= 'true'; if (!defined($filter->{object}) || !defined($filter->{instance})) { foreach my $object (@ALLOWED_OBJECTS_IN_FILTER) { my $object_value = delete $filter->{$object}; if (defined $object_value) { my $instance = $object_value->$object(); $filter->{object} = $object; $filter->{instance} = $instance; } } } $Log->exit() if $may_exit; } ## end sub _process_filter