How to create a setup form for a Web Framework app

Django Bindings has been deprecated. For more, see the Deprecation Notice.

If your app requires initial configuration, you can create a Django setup form that runs when your app is first launched. A Django setup form provides the ability for your users to fill out app-specific configuration when your app first launches, and to edit the form again later. You can also redirect other pages in your app to the setup form when settings have not yet been initialized. Read more about Django forms on the Django web site.

A setup form

A setup form contains Django form fields to accept and validate data of different types, such as character, date, or email data. Form fields accept various arguments so that you can set properties such as an initial value, a label, and a type of validation. Form fields also have an associated widget that determines the type of input control to use, such as a text box, a password text box, or a checkbox.

The Web Framework lets your app save these settings in different ways, such as in custom configuration files or as properties on Splunk entities. The Web Framework uses a configuration file (app.conf) to track whether your app's settings have been initialized. The app.conf file is located in your app's /default directory and contains an is_configured key that is set to 0. When the user fills out the app's setup form, a copy of the app.conf file is created in the app's /local directory with is_configured=1, indicating that settings have been initialized.

To create a setup form for your app, you'll need to:

For an end-to-end example with code, see Example setup form.

If you're already familiar with Django forms, note that the Web Framework uses an implementation with added features, which is called out in the examples. If you're already familiar with the legacy Advanced XML setup process (using setup.xml), you'll find this process to be somewhat similar but with variations.

 

Create a forms.py file that defines the setup form

Creating a setup form requires you to create a SetupForm class that defines each of your form fields including its properties. You'll also need to set additional Web Framework properties for interacting with Splunk.

For example, let's say you want fields that ask for an email address and a password:

  • To correctly validate the email address, use Django's EmailField form field, which uses a TextInput widget by default.
  • For the password, use Django's CharField form field, which also uses a TextInput widget by default. However, to mask characters, use the PasswordInput widget instead.

In addition to using the standard properties for Django's form fields and widgets, the Web Framework requires additional properties for each field depending on how and where you want to save the setting. Settings in Splunk use the REST API to communicate with the Splunk instance over HTTP. You'll need to find the corresponding POST endpoint to work with a particular setting. For more, see the REST API documentation.

    Custom properties

    If the setting is a custom property (such as an email address) that is not mapped to a Splunk entity, you can save the settings in a custom configuration file that will be located in your app's directory.

    Set the endpoint, entity, and field properties as follows:

    • endpoint: The REST API endpoint URL for configuration files, which is configs/conf-filename. For example, to save settings to a custom configuration file called myconf.conf, the endpoint value is "configs/conf-myconf".
    • entity: The name of the entity (not URL encoded). For a custom .conf file, this value is the stanza name.
    • field: The name of the entity field (not URL encoded). For a custom .conf file, this value is the key name.

    You'll also need to create a blank .conf file that contains the stanzas and keys that are used and save it to your app's /default directory. However, the setup form doesn't read from this file when first launched, so if you want an initial value, set the field's initial property.

    Splunk properties

    If the setting corresponds to a property of an existing Splunk entity (such as an index or input), the setting will be saved in the appropriate Splunk configuration file.

    Set the endpoint, entity, and field properties as follows:

    • endpoint: The REST API endpoint URL for the Splunk entity. For example, if you want to set a property on an existing index, use the data/indexes endpoint.
    • entity: The name of the entity (not URL encoded). For example, this value corresponds to the index name.
    • field: The name of the entity field (not URL encoded), which in this case corresponds to a request parameter. For example, use a parameter for data/indexes.

    Custom save and load functions

    If you want to write logic that retrieves and saves field values using custom load and save functions that you write, set the load and save properties as follows:

    • load: A function that takes (request, form_cls, field) parameters and returns the persisted value for the field. This function is called automatically by the setup form.
    • save: A function that takes (request, form, field, value) parameters and persists the specified value. This function is called automatically by the setup form.

    Within these functions use the Splunk SDK for Python to interact with Splunk.

To create the setup form with fields

  1. Create the setup form in a new forms.py file in the $SPLUNK_HOME/etc/apps/your_app_name/django/your_app_name directory.
  2. Add an import statement to import the forms module as follows:
  3. from splunkdj.setup import forms

    Note  Setup form fields must be imported from the splunkdj.setup.forms module rather than Django's django.forms module. At this time it is not possible to mix regular Django fields with setup form fields.

  4. Create a SetupForm class that defines each of your form fields―for each setting in your setup form, choose a Django form field and set its properties (including a widget). Then, set the Web Framework properties for interacting with Splunk.
  5. The following example shows how you might define a setup form with email and password fields:

    from splunkdj.setup import forms
    
    class SetupForm(forms.Form):
        email = forms.EmailField(
            endpoint='configs/conf-mysetup', entity='customerinfo', field='email',
            max_length=100)
        password = forms.CharField(
            endpoint='configs/conf-mysetup', entity='customerinfo', field='password',
            max_length=100,
            widget=forms.PasswordInput(render_value=True))
    

    Note  Each time you modify .conf files directly―the stanzas, key names, or values―you need to restart Splunk to load the new configuration.

  6. If you are saving key-value pairs to a custom configuration file, you'll also need to create a blank .conf file that contains the stanzas and keys used by your settings form. Create your .conf file in $SPLUNK_HOME/etc/apps/your_app_name/default/. The following example shows how you might define a mysetup.conf file that saves email and password fields:
  7. # mysetup.conf configuration file
    
    [customerinfo]
    email =
    password =
    
 

Add a Django view for the setup form

To render the setup form, you'll need to create a Django setup view.

  1. Open the views.py file from the $SPLUNK_HOME/etc/apps/your_app_name/django/your_app_name directory.
  2. Add these imports at the top of the file:
  3. # Imports for the setup view
    from .forms import SetupForm
    from django.core.urlresolvers import reverse
    from splunkdj.setup import config_required
    from splunkdj.setup import create_setup_view_context
    
  4. Add the definition for a view called "setup" that will be rendered in a page template (setup.html):
  5. @render_to('your_app_name:setup.html')
    @login_required
    def setup(request):
        return create_setup_view_context(
            request,
            SetupForm,  # The form class to use
            reverse('your_app_name:home'))  # Where to redirect the user after completing setup
    

    This view passes the following variables to the template in the request:

    • form: The instance of SetupForm, which is populated with the latest values from splunkd.
    • configured: A Boolean that indicates whether the app has been configured. When the app has already been configured and the setup form is being used to edit the existing configuration, a Cancel button is displayed.

While you're modifying the views.py file, indicate any other views that require the app's settings to be configured by adding the @config_required decorator to its definition. Any Web Framework view with this decorator automatically redirects to the setup view when the app hasn't been configured yet. For example, add the decorator to the home page as follows:

@render_to('your_app_name:home.html')
@login_required
@config_required
def home(request):
    return {}

Remember to replace the your_app_name variable. For more about Django views, see URL mapping and Django views.

 

Create a page template to display the setup form

To render and display the setup form, you'll need to create a page template.

  1. Create a page template called setup.html in $SPLUNK_HOME/etc/apps/your_app_name/django/your_app_name/templates.
  2. Create this content block to render the form and display Save and Cancel buttons:
  3. {% block content %}
        <form class="splunkdj-setup-form" method="post" action="">
            {% csrf_token %}
            {{ form.as_p }}
            <input class="btn btn-primary" type="submit" value="Save" />
            {% if configured %}
                <a class="btn" href="{% url 'your_app_name:home' %}">Cancel</a>
            {% endif %}
        </form>
    {% endblock content %}
    

    A few notes about the code:

    • The {{ form.as_p }} context variable renders each form field and its label.
    • The {% csrf_token %} tag is used for CSRF protection.
    • The {% if configured %} section displays a Cancel button only when the app is already configured, which redirects to the Home page when clicked. For example, the user might return to the setup page again to edit the existing settings and is given the option to cancel.

Remember to replace the your_app_name variable.

 

Add a URL mapping for the setup template

To map the URL of the setup.html template, add a URL mapping to the urls.py file in the $SPLUNK_HOME/etc/apps/your_app_name/django/your_app_name directory. For example, insert the following line:

    url(r'^setup/$', 'your_app_name.views.setup', name='setup'),

Remember to replace the your_app_name variable. For more about Django URL mapping, see URL mapping and Django views.

 

Example setup form

This example shows how to create a simple setup form:

  • The first four fields in this form are saved as key-value pairs to a custom configuration file (myconf.conf).
  • The Max size field changes a setting for an existing index called "mytestindex".
  • The last date field accepts a date, and then extracts the month, day, and year as separate key-value pairs, also saved to myconf.conf.

Example setup form

To use this example setup form:

  1. Create a Web Framework app called "mytestapp" (see How to create a Web Framework app).
  2. Create a test index in Splunk called "mytestindex". (In Splunk Web, click Settings, Indexes, then New. Enter the name "mytestindex" and accept the default options.)
  3. Create the new files or modify the existing files as indicated below, then restart Splunk.
  4. In Splunk Web, go to the Home page of the "mytestapp" app (http://<localhost:port>/dj/mytestapp/home/) to see the setup form.

Forms.py

Create this file as $SPLUNK_HOME/etc/apps/mytestapp/django/mytestapp/forms.py:

from splunkdj.setup import forms   # Replaces 'from django import forms'
import datetime

def load_extraction_date(request, form_cls, field):
    service = request.service # for communicating with splunkd
    date_extraction = service.confs['myconf']['date_extraction']
    try:
        return datetime.date(
            int(date_extraction['year']),
            int(date_extraction['month']),
            int(date_extraction['day']))
    except ValueError:
        # Return an empty field value for the default non-integer placeholders
        return None

def save_extraction_date(request, form, field, value):
    service = request.service
    service.confs['myconf']['date_extraction'].update(
        year=value.year,
        month=value.month,
        day=value.day)

def what_is_today():
    now = datetime.datetime.now()
    today = now.strftime("%m/%d/%Y")
    return today

class SetupForm(forms.Form):
    # The cust fields are saved in a custom myconf.conf file
    custname = forms.CharField(
        label="Customer name",
        endpoint="configs/conf-myconf", entity="customerinfo", field="custname",
        max_length=100)
    custdate = forms.DateField(
        label="Date of birth",
        endpoint="configs/conf-myconf", entity="customerinfo", field="custdate",
        initial="mm/dd/yyyy")
    custemail = forms.EmailField(
        label="Email address",
        endpoint="configs/conf-myconf", entity="customerinfo", field="custemail",
        max_length=100)
    custpassword = forms.CharField(
        label="Password",
        endpoint="configs/conf-myconf", entity="customerinfo", field="custpassword",
        max_length=100,
        widget=forms.PasswordInput(render_value=True))
   
    # This field changes the size of an index called "mytestindex"
    indexsize = forms.IntegerField(
        label="Max size (MB) for the 'mytestindex' index",
        endpoint="data/indexes/", entity="mytestindex", field="maxTotalDataSizeMB")
   
    # This field shows how to use the load and save functions to extract parts of a date
    extraction_date = forms.DateField(
        label="Today's date",
        load=load_extraction_date, save=save_extraction_date,
        initial=what_is_today)

Myconf.conf

Create this file as $SPLUNK_HOME/etc/apps/mytestapp/default/myconf.conf:

#
# My app configuration file
#

[customerinfo]
custname =
custdate =
custemail =
custpassword =

[date_extraction]
year = __YEAR__
month = __MONTH__
day = __DAY__

Views.py

Modify the existing $SPLUNK_HOME/etc/apps/mytestapp/django/mytestapp/views.py file by adding the code in bold:

from django.contrib.auth.decorators import login_required
from splunkdj.decorators.render import render_to

# Imports for the setup view
from .forms import SetupForm
from django.core.urlresolvers import reverse
from splunkdj.setup import config_required
from splunkdj.setup import create_setup_view_context

@render_to('mytestapp:home.html')
@login_required
@config_required
def home(request):
    return {
        "message": "Hello World from mytestapp!",
        "app_name": "mytestapp"
    }

@render_to('mytestapp:setup.html')
@login_required
def setup(request):
    return create_setup_view_context(
        request,
        SetupForm, # This name is arbitrary, but if you change it, update it everywhere
        reverse('mytestapp:home'))

Setup.html

Create this file as $SPLUNK_HOME/etc/apps/mytestapp/django/mytestapp/templates/setup.html:

{% extends "splunkdj:base_with_app_bar.html" %}

{% load splunkmvc %}

{% block title %}{{app_name}} Setup{% endblock title %}

{% block css %}
    <link rel="stylesheet" type="text/css" href="{{STATIC_URL}}{{app_name}}/custom.css" />
    <link rel="stylesheet" type="text/css" href="{{STATIC_URL}}splunkjs/css/dashboard.css" />
    <style>       
        .main-area {
            margin: 0px auto;
            margin-top: 30px;
            margin-bottom: 30px;
            padding: 10px;
            width: 400px;
        }   
    </style>
{% endblock css %}

{% block content %}
    <div>
        <div class="main-area">
            <form class="splunkdj-setup-form" method="post" action="">
                {% csrf_token %}
                <h2>Setup Form</h2>
                <p>Enter your information, then click <b>Save</b> to continue.</p></br>
               
                {{ form.as_p }}
                <input class="btn btn-primary" type="submit" value="Save" />
                {% if configured %}
                    <a class="btn" href="{% url 'mytestapp:home' %}">Cancel</a>
                {% endif %}
            </form>
        </div>
    </div>
{% endblock content %}

Urls.py

Modify the existing $SPLUNK_HOME/etc/apps/mytestapp/django/mytestapp/urls.py by adding the code in bold:

from django.conf.urls import patterns, include, url
from splunkdj.utility.views import render_template as render

urlpatterns = patterns('',
    url(r'^home/$', 'mytestapp.views.home', name='home'),
    url(r'^setup/$', 'mytestapp.views.setup', name='setup')
)