Dashboards & Visualizations

Can you refresh a single module/chart without refreshing the whole dashboard?

Motivator

Is it possible to refresh a single module on a dashboard?

A client is interested in having a single dashboard with half static, backward looking charts, and half real-time, forward looking charts. They want to refresh the static ones every so often without losing the real-time graphs' history.

Explorer

How can I edit the dashboard code as advanced XML? I can only see simple XML when I try to edit the source? I am using Splunk 6.0 version. Any help would be helpful. Thank you

0 Karma

Splunk Employee
Splunk Employee

@karthikp1989, here are the docs pages for creating views & dashboards in Advanced XML: http://docs.splunk.com/Documentation/Splunk/latest/AdvancedDev/AdvancedIntro

0 Karma

Motivator

Please ask this as a new question - or search the existing questions - for best results.

0 Karma

Motivator

Note that this was only really needed before Splunk 4.3 introduced realtime charts' backfill functionality in this particular case.

0 Karma

New Member

so, how would I do this with Splunk 5? We have 5 real time charts and one chart not being able to be refreshed in real time (dbquery). I dont want to refresh the hole dashboard too, so how to refresh this particular chart every 5 minutes?

0 Karma

Influencer

This seems like the most obvious place to post this.

HiddenSavedSearches are not refreshed by AutoRefresh, this is because the search is defined when the page loads, and isn't changed by pushContextToChildren().

This cobbled together Module, when used in place of HiddenSavedSearch works around that problem.
Obviously you still need AutoRefresh upstream.

Credit goes to sideview for the pointers along the way

The controller module is a straight copy of HiddenSearchSwapper, as is the ajax call in the js file

DynamicHiddenSavedSearch.conf

[module]
className = Splunk.Module.DynamicHiddenSavedSearch
superClass = Splunk.Module
description = Given a saved search name, finds the last run search for that saved search. It will update its context with the latest latest instance of the saved search when refreshed.

[param:savedSearch]
required = True
label = This is the name of the saved search to use when looking up a searches from the saved search's history or when dispatching a new search.

DynamicHiddenSavedSearch.css

div.SplunkModule.HiddenSavedSearch {
    display: none;
    padding: 0;
    margin: 0;
}

DynamicHiddenSavedSearch.html

<%page args="module"/>
<%namespace name="lib" file="//lib.html" import="*"/>
% if module['params'].get('savedSearch'):
${lib.getSavedSearch(module, module['params'].get('savedSearch'), module['params'].get('useHistory'), APP['id'], cherrypy.session['user'].get('name'), VIEW['nativeObjectMode'])}
% endif

DynamicHiddenSavedSearch.js

Splunk.Module.DynamicHiddenSavedSearch = $.klass(Splunk.Module, {

  savedSearch: null,
  initialize: function($super, container) {
    $super(container);
    this.childEnforcement = Splunk.Module.ALWAYS_REQUIRE;

    this.messenger = Splunk.Messenger.System.getInstance();
    this.hide(this.HIDDEN_MODULE_KEY);

    this._params['boundingGroup']   = this.getGroupName();
    this.sid = null;

    var hashParams = Splunk.util.queryStringToProp(Splunk.util.getHash());
    var meta = this.container.closest('.dashboardCell').find('.paneledit').attr("data-sequence");
    var key = 'panel_' + meta + '.sid';
    if (meta && hashParams.hasOwnProperty(key)) {
       this.sid = hashParams[key];
       this.logger.info('Soft refresh; reuse job sid', this.sid);
       delete hashParams[key];
       window.location.hash = Splunk.util.propToQueryString(hashParams);
    }

  },

  getModifiedContext: function() {

    var context           = this.getContext();
    var jsonSearch        = this._getSavedSearch(this._params["savedSearch"]);
    jsonSearch["group"]   = this._params['boundingGroup'];
    var resurrectedSearch = Splunk.Search.resurrect(jsonSearch);

    if ( this._params['savedSearch'] != resurrectedSearch.getSavedSearchName() ) {
      this.messenger.send('error', 'splunk.search', _('Error resurrecting latest results for saved search ' + this._params["savedSearch"]));
      this.logger.debug('Failed to resurrect search object for ' + this._params["savedSearch"]);
      return context;
    } else {
      context.set("search",Splunk.Search.resurrect(jsonSearch));
      return context; 
    }

  },  

  _getSavedSearch: function(savedSearchName) {

    var newSearch;

    var targetURL  = Splunk.util.make_url(
                     'module',
                      Splunk.util.getConfigValue('SYSTEM_NAMESPACE'),
                      this.moduleType,
                      'render?savedSearchName=' + savedSearchName + '&client_app=search'
                    );

    $.ajax({

           async: false,
            type: 'GET',
             url: targetURL,
      beforeSend: function(xhr) {
                    xhr.setRequestHeader('X-Splunk-Module', this.moduleType);
                  },  

        complete: function(data) {
                    this.logger.debug('response OK from server');
                    newSearch = JSON.parse(data.responseText);
                  }.bind(this),

           error: function() {
                    this.messenger.send('error', 'splunk.search', _('Unable to get saved search from controller')); 
                    this.logger.debug('response ERROR from server');
                    return null;
                  }.bind(this)

    });   // end ajax requet

    return newSearch;

  }

});

DynamicHiddenSavedSearch.py

import cherrypy
import json
import logging
import splunk
import time
import controllers.module as module

logger = logging.getLogger('splunk.module.DynamicHiddenSavedSearch')

class DynamicHiddenSavedSearch(module.ModuleHandler):

    def generateResults(self, host_app=None, client_app=None, savedSearchName=None, useHistory=None):

        if savedSearchName: 
            jsonSearch = None
        owner = 'nobody'
            try: 
                savedSearchObject = splunk.search.getSavedSearch(label = savedSearchName, namespace = client_app, owner = owner)

                jsonSearch = splunk.appserver.mrsparkle.util.resurrectFromSavedSearch(
                    savedSearchObject = savedSearchObject,
                    hostPath = splunk.mergeHostPath(),
                    namespace = client_app,
                    owner = owner)

                job = splunk.search.getJobForSavedSearch(
                    savedSearchName,
                    useHistory="True", 
                    namespace=client_app,
                    owner=owner,
                    search='name=scheduler*')

                if (job):
                    jsonSearch["job"] = job.toJsonable(timeFormat='unix')

                return json.dumps(jsonSearch)

            except Exception, e:
                logger.exception(e)
                return ""
        else:
            logger.warn('savedSearchName was not passed from the caller')
            return ""

Builder

Is this still the best/only way to auto-refresh a saved search?

Influencer

Another option is to add a custom module to splunk that can refresh periodically. Let's call the module AutoRefresh. To do this you have to create the following directory structure (in an app, eg. search or better a custom one):

$SPLUNK_HOME/etc/apps/someapp
| - appserver
   | - modules
      |- AutoRefresh
        | - AutoRefresh.conf
        | - AutoRefresh.css
        | - AutoRefresh.js

AutoRefresh.conf:

[module]
className = Splunk.Module.AutoRefresh
superClass = Splunk.Module
description = This module refreshes it's descending modules periodically.

[param:invervalInSeconds]
required = True
label = This parameters defines the inverval in seconds in which the descending search modules should be reloaded.

AutoRefresh.css:

div.SplunkModule.AutoRefresh { display: none; margin: 0; padding: 0; }

AutoRefresh.js:

Splunk.namespace("Module");
Splunk.Module.AutoRefresh = $.klass(Splunk.Module, {
    initialize: function($super, container) {
        $super(container);
        var interval = (+this._params['invervalInSeconds'])*1000;
        this._timer = setInterval(this.refresh.bind(this), interval);
    },
    refresh: function() {
        this.pushContextToChildren();
    }
});

Once you've added those folders and files you have to restart Splunk. Then you'll be able to use the module in your advanced xml views like this:

... truncated ...

<module name="GenericHeader" layoutPanel="panel_row1_col1">
    <param name="label">Status</param>
    <module name="AutoRefresh">
        <param name="invervalInSeconds">10</param>
        <module name="HiddenSearch" layoutPanel="panel_row1_col1_grp1" autoRun="True">
                    ...
        </module>
    </module>
</module>
...

In this example all (search-)descendants of the AutoRefresh module would be refresh every 10 seconds.

Path Finder

Thaks a lot for this module! 🙂

However, it seems like it is not possible to have a $foo$ variable as the value for the "intervalInSeconds" parameter, or is it?

I've even tried to put a ValueSetter between the Sideview Pulldown and this Autorefresh. Unfortunatley without any succes...

0 Karma

Splunk Employee
Splunk Employee

Excellent! thank you.

0 Karma

Communicator

thanks @ziegfried for your premade module.

0 Karma

Communicator

If an AutoRefresh modules contains another AutoRefresh module, is there a way so that parent auto refresh module does not refresh any module under a child AutoRefresh module?

Very useful module though.

0 Karma

Super Champion

Any chance such a module will be built-in in the future?

SplunkTrust
SplunkTrust

Yep, it can be done if you're using the advanced XML.

I cant think of an app offhand that does it but im sure there is one. You just put a SubmitButton module into the XML as follows:

Example:
1) an ordinary dashboard panel in the Advanced XML, with no SubmitButton

<module name="HiddenSearch" layoutPanel="panel_row1_col1" group="No SubmitButton.. :(" autoRun="True">
  <param name="search">* | top sourcetype | fields - percent</param>
  <param name="earliest">-30s</param>
  <module name="HiddenChartFormatter">
    <param name="charting.chart">bar</param>
    <param name="charting.primaryAxisTitle.text">Sourcetype</param>
    <param name="charting.secondaryAxisTitle.text">## of events</param>

    <module name="FlashChart">
    </module>

  </module>
</module>

2) same panel, but with a SubmitButton

<module name="HiddenSearch" layoutPanel="panel_row1_col1" group="with the SubmitButton :)" autoRun="True">
  <param name="search">* | top sourcetype | fields - percent</param>
  <param name="earliest">-30s</param>
  <module name="HiddenChartFormatter">
    <param name="charting.chart">bar</param>
    <param name="charting.primaryAxisTitle.text">Sourcetype</param>
    <param name="charting.secondaryAxisTitle.text">## of events</param>

    <module name="SubmitButton">
      <param name="label">Refresh</param>

      <module name="FlashChart">
      </module>
    </module>
  </module>
</module>

Make sure that the module tag for the SubmitButton encloses the FlashChart module and also that it encloses any other modules that will be displaying data for this particular job -- most typically headers. As is always the case with the containment of module tags, if someone isnt enclosed by a module, then they know nothing about that module. So in this example if a SimpleResultsHeader is not enclosed by the SubmitButton, then it wont be refreshed when users click on it.