App Framework Code Samples

These are the complete code samples discussed in the Getting Started and Cookbook documents.

Getting Started Code Samples

Note: The getting started example is the same example used for Cookbook Example4.

app.conf
default.xml
Example.xml
CustomResultsTable.conf
CustomResultsTable.js
CustomResultsTable.py

app.conf

[ui]
is_visible = true
label = App Framework Tutorial

[package]
id = dev_tutorial

[launcher]
author = Splunk, Inc.
description = App Framework Tutorial App
version = 1.0

default.xml

<nav>
    <collection label="Examples">
        <view name="Example" />
    </collection>
</nav>

Example.xml

<view template="dashboard.html">
    <label>Example: Using a Custom Module</label>
    <module name="AccountBar" layoutPanel="appHeader"/>
    <module name="AppBar" layoutPanel="navigationHeader"/>
    <module name="Message" layoutPanel="messaging">
        <param name="filter">*</param>
        <param name="clearOnJobDispatch">False</param>
        <param name="maxSize">1</param>
    </module>

    <module name="StaticContentSample" layoutPanel="panel_row1_col1">
        <param name="text"><![CDATA[
            <h1>Example: Using a Custom Module</h1>
                <p>
                    This simple application searches the metrics log and lists the CPU utilization of each indexer stage, for the last seven days.
                </p>
                <p>
                    Search string:  <tt>index=_internal source=*metrics.log group=pipeline | stats sum(cpu_seconds) as totalCPU by name</tt>
                </p>
                <p>
                    Results are displayed using a custom module, <tt>CustomResultsTable</tt>.
                </p>
            ]]>
        </param>
        </module>

        <module name="HiddenSearch" layoutPanel="panel_row2_col1" group="CPU Utilization" autoRun="True">
            <param name="search">index=_internal source=*metrics.log group=pipeline | stats sum(cpu_seconds) as totalCPU by name</param>
            <param name="earliest">-7d</param>
            <module name="CustomResultsTable">
        </module>
    </module>
</view>

CustomResultsTable.conf

[module]
className = Splunk.Module.CustomResultsTable
superClass = Splunk.Module.DispatchingModule

description = Custom module for App Framework example.

CustomResultsTable.js

Splunk.Module.CustomResultsTable = $.klass(Splunk.Module.DispatchingModule, {

    initialize: function($super, container) {
        $super(container);
        this.myParam = this.getParam("myParam");
        this.resultsContainer = this.container;
    },

    onJobDone: function(event) {
        this.getResults();
    },

    getResultParams: function($super) {
        var params = $super();
        var context = this.getContext();
        var search  = context.get("search");
        var sid         = search.job.getSearchId();

        if (!sid) this.logger.error(this.moduleType, "Search ID is missing.");

        params.sid = sid;
        return params;
    },

    renderResults: function($super, htmlFragment) {
        if (!htmlFragment) {
            this.resultsContainer.html('No content available.');
            return;
        }
        this.resultsContainer.html(htmlFragment);
    }
})

CustomResultsTable.py

import controllers.module as module

import splunk, splunk.search, splunk.util, splunk.entity
import lib.util as util
import lib.i18n as i18n
import logging

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

import math
import cgi

MAX_MULTI_VALUE_COUNT = 50

class CustomResultsTable(module.ModuleHandler):
    
    def generateResults(self, host_app, client_app, sid, count=1000, 
            offset=0, entity_name='results'):

        count = max(int(count), 0)
        offset = max(int(offset), 0)
        if not sid:
            raise Exception('CustomResultsTable.generateResults - sid not passed!')

        try:
            job = splunk.search.getJob(sid)
        except splunk.ResourceNotFound, e:
            logger.error('CustomResultsTable could not find the job %s. Exception: %s' % (sid, e))
            return _('<p class="resultStatusMessage">The job appears to have expired or has been canceled.</p>')
        
        output = []
        output.append('<div class="CustomResultsTableWrapper">')
        output.append('<table class="CustomResultsTable splTable">')
 
        fieldNames = [x for x in getattr(job, entity_name).fieldOrder if (not x.startswith('_'))]
        
        offset_start = offset
        if offset < 0 and count < abs(offset):
            offset_start = -count
        
        dataset = getattr(job, entity_name)[offset_start: offset+count]
        
        for i, result in enumerate(dataset):

            output.append('<tr>')
                
            for field in fieldNames:
                output.append('<td')
                fieldValues = result.get(field, None)
                if fieldValues:
                    renderedValues = [cgi.escape(x.value) for x in fieldValues[:MAX_MULTI_VALUE_COUNT]]
                    output.append('>%s</td>' % "".join(renderedValues))
                else:
                    output.append('></td>')
                
            output.append('</tr>')
        output.append('</table></div>')
 
        if (entity_name == 'results' and job.resultCount == 0):
            if job.isDone:
                output = self.generateStatusMessage(entity_name, 'nodata', job.id)
            else:
                output = self.generateStatusMessage(entity_name, 'waiting', job.id)
        else:
            output = ''.join(output)

        return output

Cookbook Code Samples

Example1.xml

Example2.xml

Example3.xml

Example4

Example5.xml
CustomJSONResults.conf
CustomJSONResults.js
CustomJSONResults.py

Example6.xml
TreeMap.conf
TreeMap.html
TreeMap.js
TreeMap.py

Example7.xml
ParameterizedTreeMap.conf
ParameterizedTreeMap.html
ParameterizedTreeMap.js
ParameterizedTreeMap.py

Example8.xml
web.conf
TutorialSetup.py
setup.html
success.html
failure.html

Example1.xml

<view template="dashboard.html">
  <label>Example 1: A Basic Application</label>
  <module name="AccountBar" layoutPanel="appHeader"/>
  <module name="AppBar" layoutPanel="navigationHeader"/>
  <module name="Message" layoutPanel="messaging">
    <param name="filter">*</param>
    <param name="clearOnJobDispatch">False</param>
    <param name="maxSize">1</param>
  </module>
  
  <module name="StaticContentSample" layoutPanel="panel_row1_col1">
    <param name="text"><![CDATA[
      <h1>Example 1: A Basic Application</h1>
        <p>  
          This simple application searches the metrics log and lists the CPU utilization of each indexer stage, for the last seven days.
        </p>
        <p>
          Search string:  <tt>index=_internal source=*metrics.log group=pipeline | stats sum(cpu_seconds) as totalCPU by name</tt>
        </p>
        <p>
          Results are displayed using the <tt>SimpleResultsTable</tt> module.
        </p>
    ]]></param>
  </module>
  
  <module name="HiddenSearch" layoutPanel="panel_row2_col1" group="CPU Utilization" autoRun="True">
    <param name="search">index=_internal source=*metrics.log group=pipeline | stats sum(cpu_seconds) as totalCPU by name</param>
    <param name="earliest">-7d</param>
    <module name="SimpleResultsTable"></module>
  </module>
</view>

default.xml

<nav>
  <collection label="Examples">
    <view name="Example1" />
    <view name="Example2" />
    <view name="Example3" />
    <view name="Example4" />
    <view name="Example5" />
    <view name="Example6" />
    <view name="Example7" />
    <view name="Example8" />
  </collection> 
</nav>

Example2.xml

<view template="dashboard.html" stylesheet="example2Styles.css">
  <label>Example 2: Changing the Look of an Application</label>
  <module name="AccountBar" layoutPanel="appHeader"/>
  <module name="AppBar" layoutPanel="navigationHeader"/>
  <module name="Message" layoutPanel="messaging">
    <param name="filter">*</param>
    <param name="clearOnJobDispatch">False</param>
    <param name="maxSize">1</param>
  </module>
 
  <module name="StaticContentSample" layoutPanel="panel_row1_col1">
    <param name="text"><![CDATA[
      <h1>Example 2: Changing the Look of an Application</h1>
        <p>  
          This example applies a custom CSS to the results table.
        </p>
    ]]></param>
  </module>
  
  <module name="HiddenSearch" layoutPanel="panel_row2_col1" group="CPU Utilization, another view" autoRun="True">
    <param name="search">index=_internal source=*metrics.log group=pipeline | stats sum(cpu_seconds) as totalCPU by name</param>
    <param name="earliest">-7d</param>
    <module name="SimpleResultsTable"></module>
  </module>
</view>

example2Styles.css

table.simpleResultsTable tbody
{
	background: #ebf2e6;
}
table.simpleResultsTable td
{
	color: #669;
	border-top: 1px dashed #fff;
}
table.simpleResultsTable tbody tr:hover td
{
	color: #339;
	background: #b3d09f;
}

Example3.xml

<view template="dashboard.html" stylesheet="example2Styles.css">
  <label>Example 3: Adding Dynamic Behavior to an Application</label>
  <module name="AccountBar" layoutPanel="appHeader"/>
  <module name="AppBar" layoutPanel="navigationHeader"/>
  <module name="Message" layoutPanel="messaging">
    <param name="filter">*</param>
    <param name="clearOnJobDispatch">False</param>
    <param name="maxSize">1</param>
  </module>
  
  <module name="StaticContentSample" layoutPanel="panel_row1_col1">
    <param name="text"><![CDATA[
      <h1>Example 3: Adding Dynamic Behavior to an Application</h1>
        <p>  
          This builds on Example2 by using custom JavaScript to handle clicking on a table row.  On click, the indexer stage name associated with the row is displayed.
        </p>
    ]]></param>
  </module>

  <module name="HiddenSearch" layoutPanel="panel_row2_col1" group="CPU Utilization, other behavior" autoRun="True">
    <param name="search">index=_internal source=*metrics.log group=pipeline | stats sum(cpu_seconds) as totalCPU by name</param>
    <param name="earliest">-7d</param>
    <module name="SimpleResultsTable">
      <param name="drilldown">row</param>
      <param name="displayMenu">true</param>
      <module name="NullModule"></module>
    </module>
  </module>
</view>

application.js

 switch (Splunk.util.getCurrentView()) {
    case "Example3":
        if (Splunk.Module.NullModule) {
            Splunk.Module.NullModule = $.klass(Splunk.Module.NullModule, {
                getModifiedContext: function() {
                    var context = this.getContext(), 
                        click = context.getAll('click');

                    alert (click.value);
                    return context;
                }
            });
        }
        break;
}

Example5.xml

<view template="dashboard.html">
  <label>Example 5: Client-side Rendering, with JSON</label>
  <module name="AccountBar" layoutPanel="appHeader"/>
  <module name="AppBar" layoutPanel="navigationHeader"/>
  <module name="Message" layoutPanel="messaging">
    <param name="filter">*</param>
    <param name="clearOnJobDispatch">False</param>
    <param name="maxSize">1</param>
  </module>
 
  <module name="StaticContentSample" layoutPanel="panel_row1_col1">
    <param name="text"><![CDATA[
      <h1>Example 5: Formatting results on the client, with JSON</h1>
        <p>  
          This simple application searches the metrics log and lists the CPU utilization of each indexer stage, for the last seven days.
        </p>
        <p>
          Search string:  <tt>index=_internal source=*metrics.log group=pipeline | stats sum(cpu_seconds) as totalCPU by name</tt>
        </p>
        <p>
          JSON-formatted results are returned and displayed in tabular form using th custom <tt>CustomJSONResults</tt> module.
        </p>
    ]]></param>
  </module>

  <module name="HiddenSearch" layoutPanel="panel_row2_col1" group="CPU Utilization" autoRun="True">
    <param name="search">index=_internal source=*metrics.log group=pipeline | stats sum(cpu_seconds) as totalCPU by name</param>
    <param name="earliest">-7d</param>
    <module name="CustomJSONResults"></module>
  </module>
</view>

CustomJSONResults.conf

[module]
# The JavaScript name of the module
className = Splunk.Module.CustomJSONResults

# The module class to subclass from
superClass = Splunk.Module.DispatchingModule

description = this module waits for the search to complete, formats JSON data, and renders results in a tabular format.

[param:myParam]
required = False
default = none
label = This is an example parameter (not currently used).

CustomJSONResults.js

Splunk.Module.CustomJSONResults = $.klass(Splunk.Module.DispatchingModule, {

    initialize: function($super, container) {
        $super(container);
        this.myParam = this.getParam("myParam");
        this.resultsContainer = this.container;
    },

    onJobDone: function(event) {
        this.getResults();
    },

    getResultParams: function($super) {
        var params = $super();
        var context = this.getContext();
        var search = context.get("search");
        var sid = search.job.getSearchId();

        if (!sid) this.logger.error(this.moduleType, "Assertion Failed.");

        params.sid = sid;
        return params;
    },

    renderResults: function($super, results) {
        if(!results) {
	    this.resultsContainer.html('No content available.');
            return;
        }
        
        for (var key in results) {
            console.debug("key = %s, val = %s", key, results[key]);
        }
        
        htmlFragment = '<div class="CustomResultsTableWrapper">';
        htmlFragment += '<table class="CustomResultsTable splTable">';
        for (var key in results) {
            htmlFragment += '<tr>';
            htmlFragment += '<td>' + key  + '</td><td>' + results[key]  + '</td>';
            htmlFragment += '</tr>';
        }
        htmlFragment += '</table></div>';
        this.resultsContainer.html(htmlFragment);
    }
})

CustomJSONResults.py

import cherrypy
import controllers.module as module

import splunk, splunk.search, splunk.util, splunk.entity
import json
from splunk.appserver.mrsparkle.lib import jsonresponse
import lib.util as util
import lib.i18n as i18n

import logging
logger = logging.getLogger('splunk.module.CustomJSONResults')

import math
import cgi

class CustomJSONResults(module.ModuleHandler):
    
    def generateResults(self, host_app, client_app, sid, count=1000, 
            offset=0, entity_name='results'):

        count = max(int(count), 0)
        offset = max(int(offset), 0)
        if not sid:
            raise Exception('CustomJSONResults.generateResults - sid not passed!')

        try:
            job = splunk.search.getJob(sid)
        except splunk.ResourceNotFound, e:
            logger.error('CustomJSONResults could not find the job %s. Exception: %s' % (sid, e))
            return _('<p class="resultStatusMessage">Could not retrieve search data.</p>')
        
        fieldNames = [x for x in getattr(job, entity_name).fieldOrder if (not x.startswith('_'))]
        
        dataset = getattr(job, entity_name)[offset: offset+count]

        outputJSON = {}
        for i, result in enumerate(dataset):
            outputJSON[str(result.get('name', None))] = str(result.get('totalCPU', None))

        cherrypy.response.headers['Content-Type'] = 'text/json'
        return json.dumps(outputJSON, sort_keys=True)
        
    def render_json(self, response_data, set_mime='text/json'):
        cherrypy.response.headers['Content-Type'] = set_mime

        if isinstance(response_data, jsonresponse.JsonResponse):
            response = response_data.toJson().replace("</", "<\\/")
        else:
            response = json.dumps(response_data).replace("</", "<\\/")

        return ' ' * 256  + '\n' + response

Example6.xml

<view template="dashboard.html">
  <label>Example 6: Rendering a Treemap Visualization</label>
  <module name="AccountBar" layoutPanel="appHeader"/>
  <module name="AppBar" layoutPanel="navigationHeader"/>
  <module name="Message" layoutPanel="messaging">
    <param name="filter">*</param>
    <param name="clearOnJobDispatch">False</param>
    <param name="maxSize">1</param>
  </module>
  
  <module name="StaticContentSample" layoutPanel="panel_row1_col1">
    <param name="text"><![CDATA[
      <h1>Example 6: Rendering a Treemap Visualization</h1>
        <p>  
          This simple application searches the metrics log and lists the CPU utilization of each processor of each indexer stage.
        </p>
        <p>  
          Search string: <tt>index=_internal source=*metrics.log group=pipeline | stats sum(cpu_seconds) as totalCPU by name, processor</tt>
        </p>
        <p>  
          JSON-formatted results are returned and rendered as a treemap using the custom <tt>TreeMap</tt> module. Treemap rectangles are sized by CPU utilization, with processors grouped by indexer type.
        </p>
    ]]></param>
  </module>
  
  <module name="HiddenSearch" layoutPanel="panel_row2_col1" group="CPU utilization thresholds" autoRun="True">
    <param name="search">index=_internal source=*metrics.log group=pipeline | stats sum(cpu_seconds) as totalCPU by name, processor</param>
    <param name="earliest">-7d</param>
    <module name="TreeMap"></module>
  </module>
</view>

TreeMap.conf

[module]
# The JavaScript name of the module
className = Splunk.Module.TreeMap

# The module class to subclass from
superClass = Splunk.Module.DispatchingModule

description = this module waits for the search to complete, formats JSON data, and renders results in a tabular format.

[param:myParam]
required = False
default = none
label = This is an example parameter (not currently used).

TreeMap.html

<%page args="module" expression_filter="h"/>
<%namespace name="lib" file="//lib.html" import="*"/>
<%lib:script_tags files="${['/static/app/%s/protovis.js' % APP['id']]}" />

    <div id="TreeMapID" class="TreeMapResults"></div>

TreeMap.js

// A treemap renderer
Splunk.Module.TreeMap = $.klass(Splunk.Module.DispatchingModule, {

    initialize: function($super, container) {
        $super(container);
        this.myParam = this.getParam("myParam");
        this.resultsContainer = this.container;
    },

    onJobDone: function(event) {
        this.getResults();
    },

    getResultParams: function($super) {
        var params = $super();
        var context = this.getContext();
        var search = context.get("search");
        var sid = search.job.getSearchId();

        if (!sid) this.logger.error(this.moduleType, "Assertion Failed.");

        params.sid = sid;
        return params;
    },

    renderResults: function($super, results) {

        if(!results) {
	    this.resultsContainer.html('No content available.');
            return;
        }
        var re = "";
        var color = pv.Colors.category19().by(function(d){return d.parentNode.nodeName});
        var nodes = pv.dom(results).root("flare").nodes();
        
        var vis = new pv.Panel().width(920).height(420).canvas(document.getElementById('TreeMapID'));
        
        var treemap = vis.add(pv.Layout.Treemap).nodes(nodes).round(true);
        
        treemap.leaf.add(pv.Panel).fillStyle(function(d){return color(d).alpha(1)}).strokeStyle("#fff").lineWidth(1).antialias(false);
        
        treemap.label.add(pv.Label).textStyle(function(d){return pv.rgb(0, 0, 0, 1)});
        
        vis.render();
    }
})

TreeMap.py

import cherrypy
import controllers.module as module

import splunk, splunk.search, splunk.util, splunk.entity
import json
from splunk.appserver.mrsparkle.lib import jsonresponse
import lib.util as util
import lib.i18n as i18n

import logging
logger = logging.getLogger('splunk.module.TreeMap')

import math
import cgi

class TreeMap(module.ModuleHandler):
    
    def generateResults(self, host_app, client_app, sid, count=1000, 
            offset=0, entity_name='results'):

        count = max(int(count), 0)
        offset = max(int(offset), 0)
        if not sid:
            raise Exception('TreeMap.generateResults - sid not passed!')

        try:
            job = splunk.search.getJob(sid)
        except splunk.ResourceNotFound, e:
            logger.error('TreeMap could not find job %s. Exception: %s' % (sid, e))
            return _('<p class="resultStatusMessage">Could not get search data.</p>')
        
        dataset = getattr(job, entity_name)[offset: offset+count]

        outputJSON = {}
        for i, result in enumerate(dataset):
            tdict = {}
            tdict[str(result.get('processor', None))] = str(result.get('totalCPU', None))
            name = str(result.get('name', None))
            if name not in outputJSON:
                outputJSON[name] = dict()
            outputJSON[name].update(tdict)

        cherrypy.response.headers['Content-Type'] = 'text/json'
        return json.dumps(outputJSON, sort_keys=True)
        
    def render_json(self, response_data, set_mime='text/json'):
        cherrypy.response.headers['Content-Type'] = set_mime

        if isinstance(response_data, jsonresponse.JsonResponse):
            response = response_data.toJson().replace("</", "<\\/")
        else:
            response = json.dumps(response_data).replace("</", "<\\/")

        return ' ' * 256  + '\n' + response

Example7.xml

<view template="dashboard.html">
  <label>Example 7: Parameterizing a Module</label>
  <module name="AccountBar" layoutPanel="appHeader"/>
  <module name="AppBar" layoutPanel="navigationHeader"/>
  <module name="Message" layoutPanel="messaging">
    <param name="filter">*</param>
    <param name="clearOnJobDispatch">False</param>
    <param name="maxSize">1</param>
  </module>
  
  <module name="StaticContentSample" layoutPanel="panel_row1_col1">
    <param name="text"><![CDATA[
      <h1>Example 7: Parameterizing a Module</h1>
        <p>  
          This example shows you how to statically parameterize your module when the app starts by specifying properties in the module configuration file.
        </p>
        <p>  
          Search string: <tt>index=_internal source=*metrics.log group=pipeline | stats sum(cpu_seconds) as totalCPU by name, processor</tt>
        </p>
        <p>
          Example6 used hardcoded values for the treemap height and width.  This example lets you specify those values in the <tt>ParameterizedTreeMap.conf</tt> configuration file.
        </p>
    ]]></param>
  </module>
  
  <module name="HiddenSearch" layoutPanel="panel_row2_col1" group="Threshold events" autoRun="True">
    <param name="search">index=_internal source=*metrics.log group=pipeline | stats sum(cpu_seconds) as totalCPU by name, processor</param>
    <param name="earliest">-7d</param>
    <module name="ParameterizedTreeMap"></module>
  </module>
</view>

ParameterizedTreeMap.conf

[module]
# The JavaScript name of the module
className = Splunk.Module.ParameterizedTreeMap

# The module class to subclass from
superClass = Splunk.Module.DispatchingModule

[param:tmapWidth]
required = False
default = 640
label = This parameter defines the treemap width.
description = this module waits for the search to complete, formats JSON data, and renders a treemap.

[param:tmapHeight]
required = False
default = 320
label = This parameter defines the treemap height.

ParameterizedTreeMap.html

<%page args="module" expression_filter="h"/>
<%namespace name="lib" file="//lib.html" import="*"/>
<%lib:script_tags files="${['/static/app/%s/protovis.js' % APP['id']]}" />

    <div id="ParameterizedTreeMapID" class="ParameterizedTreeMapResults"></div>

ParameterizedTreeMap.js

// A treemap renderer
Splunk.Module.ParameterizedTreeMap = $.klass(Splunk.Module.DispatchingModule, {

    initialize: function($super, container) {
        $super(container);
        this.resultsContainer = this.container;
        this.tmapHeight = this.getParam('tmapHeight', 920);
        this.tmapWidth = this.getParam('tmapWidth', 420);
    },

    onJobDone: function(event) {
        this.getResults();
    },

    getResultParams: function($super) {
        var params = $super();
        var context = this.getContext();
        var search = context.get("search");
        var sid = search.job.getSearchId();

        if (!sid) this.logger.error(this.moduleType, "Assertion Failed.");

        params.sid = sid;
        return params;
    },

    renderResults: function($super, results) {

        if(!results) {
	    this.resultsContainer.html('No content available.');
            return;
        }

        var re = "";
        var color = pv.Colors.category19().by(function(d){return d.parentNode.nodeName});
        var nodes = pv.dom(results).root("flare").nodes();
        
        var vis = new pv.Panel().width(this.tmapWidth).height(this.tmapHeight).canvas(document.getElementById('ParameterizedTreeMapID'));
        
        var treemap = vis.add(pv.Layout.Treemap).nodes(nodes).round(true);
        
        treemap.leaf.add(pv.Panel).fillStyle(function(d){return color(d).alpha(1)}).strokeStyle("#fff").lineWidth(1).antialias(false);
        
        treemap.label.add(pv.Label).textStyle(function(d){return pv.rgb(0, 0, 0, 1)});
        
        vis.render();
    }
})

ParameterizedTreeMap.py

#
# Splunk UI module python renderer
# This module is imported by the module loader (lib.module.ModuleMapper) into
# the splunk.appserver.mrsparkle.controllers.module.* namespace.
#

import cherrypy
import controllers.module as module

import splunk, splunk.search, splunk.util, splunk.entity
import json
from splunk.appserver.mrsparkle.lib import jsonresponse
import lib.util as util
import lib.i18n as i18n

import logging
logger = logging.getLogger('splunk.module.ParameterizedTreeMap')

import math
import cgi

class ParameterizedTreeMap(module.ModuleHandler):
    
    def generateResults(self, host_app, client_app, sid, count=1000, 
            offset=0, entity_name='results'):

        count = max(int(count), 0)
        offset = max(int(offset), 0)
        if not sid:
            raise Exception('ParameterizedTreeMap.generateResults - sid not passed!')

        try:
            job = splunk.search.getJob(sid)
        except splunk.ResourceNotFound, e:
            logger.error('ParameterizedTreeMap could not find job %s. Exception: %s' % (sid, e))
            return _('

Could not get search data.

') dataset = getattr(job, entity_name)[offset: offset+count] outputJSON = {} for i, result in enumerate(dataset): tdict = {} tdict[str(result.get('processor', None))] = str(result.get('totalCPU', None)) name = str(result.get('name', None)) if name not in outputJSON: outputJSON[name] = dict() outputJSON[name].update(tdict) cherrypy.response.headers['Content-Type'] = 'text/json' return json.dumps(outputJSON, sort_keys=True) # return self.render_json(outputJSON) def render_json(self, response_data, set_mime='text/json'): cherrypy.response.headers['Content-Type'] = set_mime if isinstance(response_data, jsonresponse.JsonResponse): response = response_data.toJson().replace("</", "<\\/") else: response = json.dumps(response_data).replace("</", "<\\/") # Pad with 256 bytes of whitespace for IE security issue. See SPL-34355 return ' ' * 256 + '\n' + response

Example8.xml

<view template="dashboard.html">
  <label>Example 8: Setting up an App (MVC)</label>
  <module name="AccountBar" layoutPanel="appHeader"/>
  <module name="AppBar" layoutPanel="navigationHeader"/>
  <module name="Message" layoutPanel="messaging">
    <param name="filter">*</param>
    <param name="clearOnJobDispatch">False</param>
    <param name="maxSize">1</param>
  </module>
  
  <module name="StaticContentSample" layoutPanel="panel_row1_col1">
    <param name="text"><![CDATA[
      <h1>Example 8: Setting up an App (MVC)</h1>
        <p>  
          App setup involves configuring configuration objects using HTML forms.  This differs from Module Controllers, which facilitate asynchronous context, search, and job processing.  (Setup demonstrates how to use the MVC architectural pattern implemented by the App Framework.)
        </p>
    ]]></param>
  </module>
  
  <module name="IFrameInclude" layoutPanel="panel_row2_col1">
    <param name="src">/custom/dev_tutorial/TutorialSetup/show</param>
  </module>
</view>

web.conf

[endpoint:TutorialSetup]

TutorialSetup.py

import logging
import os
import sys
import cherrypy

import splunk
import splunk.bundle as bundle
import splunk.appserver.mrsparkle.controllers as controllers
import splunk.appserver.mrsparkle.lib.util as util
from splunk.appserver.mrsparkle.lib.decorators import expose_page

from splunk.models.event_type import EventType

logger = logging.getLogger('splunk.appserver.mrsparkle.controllers.TutorialSetup')

class TutorialSetup(controllers.BaseController):
    '''App Framework Tutorial Setup Controller'''

    @expose_page(must_login=True, methods=['GET']) 
    def show(self, **kwargs):
        
        return self.render_template('/dev_tutorial:/templates/setup.html')

    @expose_page(must_login=True, trim_spaces=True, methods=['POST']) 
    def save(self, **params):

        form_content = {}
        user = cherrypy.session['user']['name'] 

        for key, val in params.iteritems():
            try:
                if (key != 'splunk_form_key'):
                    form_content[key] = EventType.get(EventType.build_id(key, 'dev_tutorial', user))
                    form_content[key].search = val
            except Exception, ex:
                form_content[key] = EventType('dev_tutorial', user, key)
            
        for key in form_content.keys():
            try:
                form_content[key].passive_save()
            except splunk.AuthorizationFailed:
                logger.info('User %s is unauthorized to perform setup on %s' % (user, 'dev_tutorial'))
            except Exception, ex:
                logger.info(ex)
                logger.info('Failed to save eventtype %s' % key)
      
        return self.render_template('/dev_tutorial:/templates/success.html')

setup.html

<%page expression_filter="h"/>
<%inherit file="//layout/base.html" />
<%namespace name="lib" file="//lib.html" import="*"/>
<%namespace name="helpers" file="//view/_helpers.html" import="*"/>
<%!
    import logging
    logger = logging.getLogger('splunk.module.setup')
%>

<%def name="gen_form(method='POST', action=None)">
    <form  method="${method}"  action="${action if action else ''}">
</%def>

<div id="setup">
  <p class="pageDesc">Select the indexer stages you want to display:</p>
  ${gen_form(method="POST", action=make_url(['custom', 'dev_tutorial', 'TutorialSetup', 'save']))}
  ${csrf_hidden_input()}
    <input type="checkbox" name="dev-null" value="Yes" /> dev-null<br />
    <input type="checkbox" name="archivepipe" value="Yes" /> archivepipe<br />
    <input type="checkbox" name="fschangemanager" value="Yes" /> fschangemanager<br />
    <input type="checkbox" name="indexerpipe" value="Yes" checked/> indexerpipe<br />
    <input type="checkbox" name="merging" value="Yes" checked/> merging<br />
    <input type="checkbox" name="parsing" value="Yes" checked/> parsing<br />
    <input type="checkbox" name="scheduler" value="Yes" /> scheduler<br />
    <input type="checkbox" name="typing" value="Yes" checked/> typing<br />
    <input type="checkbox" name="tcp" value="Yes" /> tcp<br />
    <input type="checkbox" name="udp" value="Yes" /> udp<br />

    <p>Click the button to save your configuration:</p>
    <input class="splButton-primary" type="submit" value="Save"></input>
  </form>
  
</div>

success.html

<%inherit file="//layout/base.html" />
<%namespace name="lib" file="//lib.html" import="*"/>
<div id="success">
  <h2>Great Success!</h2>
  <p>Your App Framework tutorial settings have been saved.</p>
  <p>Click "OK" to go to the App Framework Tutorial home page.</p>
  <a target="_parent" href="${make_url('/app/dev_tutorial')}" class="splButton-primary"><span class="cancel">OK</span></a>
</div>

failure.html

<%inherit file="//layout/base.html" />
<%namespace name="lib" file="//lib.html" import="*"/>
<div id="failure">
  <h2>Failure</h2>
  <p>App Framework Tutorial settings could not be loaded successfully.  Click "OK" to return to setup.</p>
  <a target="_parent" href="${make_url('/app/dev_tutorial/Example8')}"><span class="splButton-primary cancel">OK</span></a>
</div>