Developing for Splunk Enterprise

How to call a python script from an html view?

Explorer

We have a Splunk html view. We are trying to call a python script. Currently the code looks like this (retyped since our Splunk server doesn't have web access, I tried to avoid typos):

function callScript(){

   $.ajax({
                 type: "POST",
                 url: "bin/my_script.py",
                 data: {'flag': defaultTokenModel.get("form.flag"))
                 },
                 success: function() {
                     alert("success returned");
                 }
           });
    }

This doesn't work. The success message is returned, but the actual python script isn't called.

From what I can tell, it looks like there may be a method called ajax in simplifyxml/mvc.js which is conflicting with the JQuery method, though I'm not certain that is the case.

What is the proper way to make a script call like this?

1 Solution

SplunkTrust
SplunkTrust

This won't work as you think. Access to local folders within an app is not allowed for security and technical reasons.

When you make that POST call, you are actually trying to get a Web UI relative script! https:///en-US/app/myapp/bin/my_script.py for example. This just doesn't work.

So, you cannot directly call a python script from the web ui, it is not accessible. There is a way to do this. It is a method not fully documented, but involves creating a custom endpoint for Splunk, and then calling that endpoint.

Let me start with a quick tutorial. I'll gloss over a few things, make assumptions, drink some beer. Comment with questions. 😄

web.conf

[endpoint:my_script]

So, that is the only line you need in web.conf. This tells Splunk that you are creating a new web endpoint. The my_script definition needs to have the same name as the python file located in $APP_HOME/appserver/controllers. (Where $APP_HOME is the folder of the app you are creating this in - I'd suggest a new app, not search).

$APP_HOME/appserver/controllers/my_script.py

import logging
import os
import sys

# from splunk import AuthorizationFailed as AuthorizationFailed
import splunk.appserver.mrsparkle.controllers as controllers
import splunk.appserver.mrsparkle.lib.util as util
from splunk.appserver.mrsparkle.lib.util import make_splunkhome_path
from splunk.appserver.mrsparkle.lib.decorators import expose_page

_APPNAME = 'MyAppName'

def setup_logger(level):
    """
    Setup a logger for the REST handler.
    """

    logger = logging.getLogger('splunk.appserver.%s.controllers.my_script' % _APPNAME)
    logger.propagate = False  # Prevent the log messages from being duplicated in the python.log file
    logger.setLevel(level)
    file_handler = logging.handlers.RotatingFileHandler(
        make_splunkhome_path(['var', 'log', 'splunk', 'my_script_controller.log']), maxBytes=25000000, backupCount=5)
    formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)
    return logger


logger = setup_logger(logging.DEBUG)


class my_script(controllers.BaseController):
    # /custom/MyAppName/my_script/my_endpoint
    @expose_page(must_login=True, methods=['GET'])
    def my_endpoint(self, **kwargs):
        # DO YOUR THINGS WITH THE KWARGS PASSED

Whoa. WTF is that!? Python! YAY! So, now you have an endpoint enabled. Now you can call it from your HTML view.

 function callScript(){
    $.get("/custom/MyAppName/my_script/my_endpoint", 
               {'flag': defaultTokenModel.get("form.flag")) } )
                .done(function(data){
                  });
     }

Pay attention to these strings throughout this tutorial: my_script, my_endpoint, MyAppName. This all need to be changed to your specification, they are merely examples. Again, this should at least point you in the right direction on doing this call. If you have questions, please follow up here, or find me on IRC (#splunk on efnet - I'm alacer) or via the Splunk Slack channel for usergroups, just ping me @alacercogitatus.

View solution in original post

Splunk Employee
Splunk Employee

I am new to app development. I am not able to find out a way to add default token model to my js. It will be really helpful if you can guide me.

0 Karma

SplunkTrust
SplunkTrust

This won't work as you think. Access to local folders within an app is not allowed for security and technical reasons.

When you make that POST call, you are actually trying to get a Web UI relative script! https:///en-US/app/myapp/bin/my_script.py for example. This just doesn't work.

So, you cannot directly call a python script from the web ui, it is not accessible. There is a way to do this. It is a method not fully documented, but involves creating a custom endpoint for Splunk, and then calling that endpoint.

Let me start with a quick tutorial. I'll gloss over a few things, make assumptions, drink some beer. Comment with questions. 😄

web.conf

[endpoint:my_script]

So, that is the only line you need in web.conf. This tells Splunk that you are creating a new web endpoint. The my_script definition needs to have the same name as the python file located in $APP_HOME/appserver/controllers. (Where $APP_HOME is the folder of the app you are creating this in - I'd suggest a new app, not search).

$APP_HOME/appserver/controllers/my_script.py

import logging
import os
import sys

# from splunk import AuthorizationFailed as AuthorizationFailed
import splunk.appserver.mrsparkle.controllers as controllers
import splunk.appserver.mrsparkle.lib.util as util
from splunk.appserver.mrsparkle.lib.util import make_splunkhome_path
from splunk.appserver.mrsparkle.lib.decorators import expose_page

_APPNAME = 'MyAppName'

def setup_logger(level):
    """
    Setup a logger for the REST handler.
    """

    logger = logging.getLogger('splunk.appserver.%s.controllers.my_script' % _APPNAME)
    logger.propagate = False  # Prevent the log messages from being duplicated in the python.log file
    logger.setLevel(level)
    file_handler = logging.handlers.RotatingFileHandler(
        make_splunkhome_path(['var', 'log', 'splunk', 'my_script_controller.log']), maxBytes=25000000, backupCount=5)
    formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)
    return logger


logger = setup_logger(logging.DEBUG)


class my_script(controllers.BaseController):
    # /custom/MyAppName/my_script/my_endpoint
    @expose_page(must_login=True, methods=['GET'])
    def my_endpoint(self, **kwargs):
        # DO YOUR THINGS WITH THE KWARGS PASSED

Whoa. WTF is that!? Python! YAY! So, now you have an endpoint enabled. Now you can call it from your HTML view.

 function callScript(){
    $.get("/custom/MyAppName/my_script/my_endpoint", 
               {'flag': defaultTokenModel.get("form.flag")) } )
                .done(function(data){
                  });
     }

Pay attention to these strings throughout this tutorial: my_script, my_endpoint, MyAppName. This all need to be changed to your specification, they are merely examples. Again, this should at least point you in the right direction on doing this call. If you have questions, please follow up here, or find me on IRC (#splunk on efnet - I'm alacer) or via the Splunk Slack channel for usergroups, just ping me @alacercogitatus.

View solution in original post

Communicator

@alacercogitatus: Thanks for posting this. I was actually looking for this.

I tried the way you mentioned above, but seems to be something went wrong, I am seeing 404 error in my browser console and my HTML view shows nothing.

http://localhost:8000/en-US/custom/MyAppName/my_script/my_endpoint?_=1462458361563 Failed to load resource: the server responded with a status of 404 (Not Found).

Can you help me to resolve this?

0 Karma

SplunkTrust
SplunkTrust

Did you restart ? Once you make the changes to create the endpoint, you have to restart.

0 Karma

Communicator

I did restarted, but no luck.

0 Karma

SplunkTrust
SplunkTrust

Check that the Python is compiling, ($SPLUNK_HOME/bin/splunk cmd python ./my_script.py) and then check the internal splunk logs for errors and warnings.

0 Karma

Communicator

Seems to be python compiling is issue, I am very new to python, have to see how this compiling stuff works.
Also, do I need to compile each time when I modified python code? will that won't work, If I hit HTML view directly in browser?

0 Karma

SplunkTrust
SplunkTrust

You don't need to "compile" each time, it's just a simple way to check that you have proper syntax. Fix the syntax, and it should work after a restart.

0 Karma

Communicator

My Python code compiles well. Nothing is there in that .py file, simple print statement.
Still I am seeing 404 error with python call - http://localhost:8000/en-US/custom/MyApp/my_script/my_endpoint.

Any other suggestions?

0 Karma

Communicator

It works now. Thanks

0 Karma

Explorer

Oh, can you let me know how you fixed it? I am having the same problem despite my python script compiling.
Thanks!

0 Karma

Communicator

Added "cherrypy.response.headers['Content-Type'] = 'text/plain'" statement in "my_endpoint" definition.

0 Karma

Explorer

That didn't do anything. I don't think its necessary. Still getting the same error. Also, there's nothing in the generated log file.

0 Karma

Explorer

Just getting back to this. I did this but it didn't quite work for me, I'm getting a 500 error when I try to post (my original example was a post not a get, I went ahead and coverted over to POSTS). I think the problem is that I have the wrong URL to post to...

Currently my URL to the HTML page is something like

hostname/splunk/en-us/app/MyAppName/html_file

your example says that the url starts at custom, but I wasn't expecting custom, and I don't know what that URL is relative to. If I want to connect directly to my rest url to verify something is happening what URL should I be using?

Sorry to ask such a basic question, this is the sort of thing I shouldn't need help with. but I feel blind here, not sure where any documentation is to check on my own; I've been looking lol.

0 Karma

SplunkTrust
SplunkTrust

So to construct the URL, you need 3 things. 1) Name of your App 2) Name of the Endpoint 3) name of the function.

Then it goes such like: /custom/APPNAME/ENDPOINT/FUNCTION The /custom denotes that this is a custom endpoint and is required for this to work. So if your "script" (located in $APP_HOME/appserver/controllers/) is called html_file.py, and the function in that file is called post_this , then your URL in the HTML dashboard will be /custom/MyAppName/html_file/post_this. Done.

0 Karma

Path Finder

Hi,
I tried the above but in my python, i have the error below:

ImportError: No module named cherrypy

isn't this cherrypy packaged with splunk as default? or do i need to install it?

0 Karma

SplunkTrust
SplunkTrust

System installed python will not have that module, and I think that is what you tried to do. Use Splunk's python instead: $SPLUNK_HOME/bin/splunk cmd python myscript.py

0 Karma

Path Finder

Hi alacercogitatus!

I really appreciate your quick response. Thank you.
I will try that! but which folder should myscript.py be in to do that?
I'm wondering cause i am trying ajax, aswell, to call python script but I don't know what kind of 'URL' i should write for that.

0 Karma

Builder

This is an interesting example, doubly so for me since I've done something very similar but in a different way. Please take a look at this post:

Return binary data (such as images) via REST API

There, I used [expose:...] stanzas in web.conf and my additions to restmap.conf. This made my endpoint visible under /services via admin port, which is something I feel is not quite appropriate and very cumbersome. Where is your approach documented?

0 Karma

Explorer

just so you know the post you mentioned isn't linked, and a quick search doesn't reveal it; making it hard to check it out 🙂 My good friend google says the link is https://answers.splunk.com/answers/306952/return-binary-data-such-as-images-via-rest-api-or.html

and yes I wish there was better documentation. I feel blind following suggestions here without full understanding of them. but I can't find any documentation of these things even after they are explained to me, so maybe the documentation doesn't entirely exist?

0 Karma