# # Copyright (c) 2001-2013 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary EventLog ComponentState Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here =head1 NAME NACL::CS::EventLog =head1 DESCRIPTION C is a derived class of L. It represents the state of an ONTAP EventLog. A related class is L, which represents access to an ONTAP EventLog. =head1 ATTRIBUTES The individual pieces of data that are part of the state of the EventLog element are the attributes of the EventLog ComponentState. =over =item C<< "source" >> Filled in for CMode CLI/ZAPI. Maps to: CM ZAPI: For "requested_fields", "filter" and Output mapping: $value 7M CLI: Input: For "requested_fields": Not applicable, but the field will be populated in the CS object. For "filter": Applicable, Filtering will be done by Components. =item C<< "seqnum" >> Filled in for CMode CLI/ZAPI. Maps to: CM ZAPI: For "requested_fields", "filter" and Output mapping: $value 7M CLI: Input: For "requested_fields": Not applicable, but the field will be populated in the CS object. For "filter": Applicable, Filtering will be done by Components. =item C<< "time" >> Filled in for CMode CLI/ZAPI. Maps to: CM ZAPI: For "requested_fields", "filter" and Output mapping: 7M CLI: Input: For "requested_fields": Not applicable, but the field will be populated in the CS object. For "filter": Applicable, Filtering will be done by Components. =item C<< "message-name" >> Filled in for CMode CLI/ZAPI. Maps to: CM ZAPI: For "requested_fields", "filter" and Output mapping: the "messagename" field is changed to "message-name" field $value 7M CLI: Input: For "requested_fields": Not applicable, but the field will be populated in the CS object. For "filter": Applicable, Filtering will be done by Components. =item C<< "node" >> Filled in for CMode CLI/ZAPI. Maps to: CM ZAPI: For "requested_fields", "filter" and Output mapping: $value 7M CLI: Input: For "requested_fields": Not applicable, but the field will be populated in the CS object. For "filter": Applicable, Filtering will be done by Components. =item C<< "event" >> The full text of the event that occurred. The 7Mode API "ems log dump" shows this as a list of values. In 7Mode, a CMode-like string is constructed by concatenating the list of values together into a string of this form: : param1="val1", param2="val2" ... For example, output such as this in 7Mode: results in value of this field being returned as: raid_rg_media_scrub_done_1: owner="", rg="/aggr0/plex0/rg0", duration="7:19.11" Note that the string constructed is not guaranteed to be exactly the same as the CMode event string. This is particularly true of cases where the event string in CMode is more sentence-like, such as this: "wafl.snap.create: asynchronous creation of snapshot named hourly.2011-08-10_1105 in volume component_root_volume@vserver:c028ff86-bf5d-11e0-893f-123478563412 successful. current CP took 672 milli seconds to finish " The suggested way of using this field is to use wildcard characters to help filter to get the message we want. For example, a wafl.vol.offline event will contain the name of the volume taken offline. Here's an example of how to use this field to find the desired message: # Assuming we're looking for a "wafl.vol.offline" message for vol "vol_abc" NACL::CS::EventLog->fetch( command_interface => $node, filter => { message-name => 'wafl.vol.offline', event => '*vol_abc* } ); Filled in for CMode CLI/ZAPI. Maps to: CM ZAPI: For "requested_fields", "filter" and Output mapping: $value =item C<< "event_xml_len" >> Filled in for CMode CLI/ZAPI. Maps to: CM ZAPI: For "requested_fields", "filter" and Output mapping: $value =item C<< "num_suppressed_since_last" >> Filled in for CMode CLI/ZAPI. Maps to: CM ZAPI: For "requested_fields", "filter" and Output mapping: $value =item C<< "ems_severity" >> Filled in for CMode CLI/ZAPI. Maps to: CM ZAPI: For "requested_fields", "filter" and Output mapping: $value =item C<< "kernelseqnum" >> Filled in for CMode CLI/ZAPI. Maps to: CM ZAPI: For "requested_fields", "filter" and Output mapping: $value =item C<< "kernelgen" >> Filled in for CMode CLI/ZAPI. Maps to: CM ZAPI: For "requested_fields", "filter" and Output mapping: $value =item C<< "keytime" >> Filled in for CMode CLI. =item C<< "severity" >> Filled in for CMode CLI/ZAPI. Maps to: CM ZAPI: For "requested_fields", "filter" and Output mapping: $value 7M CLI: Input: For "requested_fields": Not applicable, but the field will be populated in the CS object. For "filter": Applicable, Filtering will be done by Components. =item C<< description >> Description Filled in for CMode CLI. =item C<< action >> Corrective Action Filled in for CMode CLI. =back =over =item Exceptions =item C This exception is thrown when the time accross cluster is not synchronised. =back =head2 ADDITIONAL FILTER FIELDS The following options can only be passed in 'filter'. These are not valid for 'requested_fields' as these are not the attributes. =over =item C<< time-interval >> *Toggle Display of Time Spent in Execution, possible value(s) are true,false =back =cut package NACL::CS::EventLog; use strict; use warnings; use Params::Validate qw(validate); use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use constant DEFAULT_TIME_INTERVAL => 86400; use NACL::Exceptions::UnexpectedState; use NACL::APISet::Exceptions::ResponseException qw(:try); use NATE::BaseException; use NACL::C::Exceptions::TimeNotSynchronised; use NACL::CS::_Mixins::EventLog qw(:all); use base 'NACL::CS::ComponentState::ONTAP'; use Class::MethodMaker [ scalar => 'source', scalar => 'seqnum', scalar => 'time', scalar => 'message_name', scalar => 'messagename', scalar => 'node', scalar => 'event', scalar => 'event_xml_len', scalar => 'num_suppressed_since_last', scalar => 'ems_severity', scalar => 'kernelseqnum', scalar => 'kernelgen', scalar => 'keytime', scalar => 'severity', scalar => 'description', scalar => 'action', scalar => 'filter_name', ]; =head1 METHODS =head2 fetch my $EventLog_state = NACL::CS::EventLog->fetch(command_interface => $ci, ...); my @EventLog_states = NACL::CS::EventLog->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 Extra Options The C parameter accepts any of the attributes listed above. In addition to these, it also accepts a C parameter (this is used is 7Mode, ignored in CMode). The 7Mode command that is invoked ("ems log dump") does not provide a way to show all ems messages, only messages for a particular time interval up to the current time. The default provided by this module is for "time-interval" is to be 86400 seconds (1 day), meaning that it will by default only search the ems messages of the last day. We can increase the interval for which ems messages are retrieved through the C option in C. Like this: # Show for the last 1200 seconds NACL::CS::EventLog->fetch(%other_opts, filter => { 'time-interval' => 10000 }); Note that the maximum the command support is 10^18 seconds. =back Supports CMode CLI/ZAPI, 7Mode/Nodescope CLI. Invokes "ems-message-get-iter" API for CMode ZAPI. Invokes "ems log dump" command for 7Mode/Nodescope CLI. =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, choices => [ { method => '_fetch_cmode_cli', interface => 'CLI', set => 'CMode', }, { method => '_fetch_7mode_cli', interface => 'CLI', set => '7Mode|Nodescope', }, { method => '_fetch_cmode_zapi', interface => 'ZAPI', set => 'CMode', }, ], exception_text => 'No matching event log(s) found', show_cmd => 'event log show', ); $Log->exit() if $may_exit; return wantarray ? @state_objs : $state_objs[0]; } # sub _fetch_cmode_cli defined in NACL::CS::_Mixins::EventLog sub _fetch_7mode_cli { 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 %command_opts; my @state_objs; # default the 'time-interval' to 60 if not provided by the user. $command_opts{'time-interval'} = $filter->{'time-interval'} || DEFAULT_TIME_INTERVAL; $command_opts{'connectrec-timeout'} = $opts{'method-timeout'} || 1200; my $response = $apiset->ems_log_dump(%command_opts); my $ems_output = $response->get_parsed_output(); foreach my $row (@{$ems_output}) { next if ($row->{'header'}->{'is-nr'}); my $obj = $pkg->new(command_interface => $opts{command_interface}); my $state_fields = {}; $state_fields->{'source'} = $row->{'header'}->{'thread-name'}; $state_fields->{'severity'} = $row->{'header'}->{'priority'}; $state_fields->{'seqnum'} = $row->{'header'}->{'sequence-number'}; $state_fields->{'time'} = _convert_to_cmode_compatible_date_format( $row->{'header'}->{'date'}); $state_fields->{'node'} = $row->{'header'}->{'node-name'}; $state_fields->{'message-name'} = $row->{'event'}; $state_fields->{'messagename'} = $state_fields->{'message-name'}; # For most fields CMode returns the "event" field as a string of the form: # : param1="val1", param2="val2", ... # We build the event string to be of this form. 7Mode returns all the # parameters and their values in a hash (key name: "parameters") # To ensure we're building it in the correct order, we read # "attribute_list" (has the list of parameters in the order in # which they showed up) and read in the order provided there. my $parameters = $row->{parameters}; my $event = $row->{'event'} . ': '; # It appears that for certain entries there can be no event parameters # returned. In this case we will default the event string to be the # same as the message-name. # Check here whether the event string needs to be built or not if (defined $row->{attribute_list}) { my @attribute_list = split /,\s*/, $row->{attribute_list}; foreach my $attribute (@attribute_list) { $event .= $attribute . '=' . '"' . $parameters->{$attribute} . '", '; } } ## end if ( defined $row->{attribute_list... # In the case of the event string having been built emove the # trailing comma and space. # In the case of the event string having not been built, remove the # trailing space and colon chop $event; chop $event; $state_fields->{event} = $event; $state_fields->{'num_suppressed_since_last'} = $row->{'header'}->{'suppressed'}; $obj->_set_fields(row => $state_fields); push @state_objs, $obj; } ## end foreach my $row ( @{$ems_output... return @state_objs; } ## end sub _fetch_7mode_cli sub _fetch_cmode_zapi { my $pkg = shift; return $pkg->SUPER::_fetch_cmode_zapi( @_, api => 'ems_message_get_iter', copy => [ qw(source time node event severity event-xml-len num-suppressed-since-last ems-severity filter-name message-name) ], map => { 'seqnum' => 'seq-num', 'messagename' => 'message-name', 'kernelgen' => 'kernel-gen', 'kernelseqnum' => 'kernel-seq-num', }, ); } ## end sub _fetch_cmode_zapi sub _extra_filter_fields { $Log->enter() if $may_enter; $Log->exit() if $may_exit; return ['time-interval']; } # Helper method to convert the 7mode date format to cmode date format. sub _convert_to_cmode_compatible_date_format { my $date = shift; if ($date =~ /^\d{1,2}\/\d{1,2}\/\d{4}\s\d{1,2}:\d{1,2}:\d{1,2}$/) { # it matches the cmode format of date so return as is return $date; } elsif ($date =~ /^(\d{1,2})([a-zA-Z]{3})(\d{4})\s(\d{1,2}):(\d{1,2}):(\d{1,2})$/) { # convert the date from "DDMonYYYY HH:MM:SS" format to "MM/DD/YYYY HH:MM:SS" my ($day, $mon, $year, $hr, $min, $sec); my %month = ( 'Jan' => '01', 'Feb' => '02', 'Mar' => '03', 'Apr' => '04', 'May' => '05', 'Jun' => '06', 'Jul' => '07', 'Aug' => '08', 'Sep' => '09', 'Oct' => '10', 'Nov' => '11', 'Dec' => '12' ); $day = $1; $mon = $month{$2}; $year = $3; $hr = $4; $min = $5; $sec = $6; my $date_cmode = $mon . "/" . $day . "/" . $year . " " . $hr . ":" . $min . ":" . $sec; return $date_cmode; } else { $Log->exit() if $may_exit; NATE::BaseException->throw("Invalid date format: " . $date); } } ## end sub _convert_to_cmode_compatible_date_format sub _fetch_validate_spec { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my $fetch_validate_spec = { %{$pkg->SUPER::_fetch_validate_spec()}, $pkg->_extra_fetch_validate_spec(), }; $Log->exit() if $may_exit; return $fetch_validate_spec; } sub _fetch_backend_validate_spec { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my $fetch_validate_spec = { %{$pkg->SUPER::_fetch_backend_validate_spec()}, $pkg->_extra_fetch_validate_spec(), }; $Log->exit() if $may_exit; return $fetch_validate_spec; } 1;