You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
669 lines
24 KiB
669 lines
24 KiB
5 months ago
|
from __future__ import absolute_import
|
||
|
|
||
|
import sys
|
||
|
import copy
|
||
|
import re
|
||
|
import time
|
||
|
import types
|
||
|
import stat
|
||
|
import os
|
||
|
|
||
|
import splunk.safe_lxml_etree as etree
|
||
|
|
||
|
from splunk.rcCmds import remote_cmds, GLOBAL_ARGS, GLOBAL_ACTIONS, GLOBAL_DEFAULTS
|
||
|
from splunk.entity import buildEndpoint
|
||
|
from splunk.rest import simpleRequest
|
||
|
from splunk.search import dispatch, getJob, listJobs
|
||
|
from splunk.bundle import getConf
|
||
|
import splunk
|
||
|
import splunk.auth as auth
|
||
|
import splunk.rcDisplay
|
||
|
import logging as logger
|
||
|
from splunk import rcHooks
|
||
|
|
||
|
#documentation et all...
|
||
|
__doc__ = """
|
||
|
utility functions that form the core of the rcBridge
|
||
|
"""
|
||
|
__version__ = "1..0.0"
|
||
|
__copyright__ = """ Version 4.0"""
|
||
|
__author__ = 'Jimmy John'
|
||
|
|
||
|
#display charateristics map
|
||
|
DISPLAY_CHARS = {
|
||
|
'settings':splunk.rcDisplay.displaySettings,
|
||
|
'user':splunk.rcDisplay.displayUser,
|
||
|
'monitor':splunk.rcDisplay.displayMonitor,
|
||
|
'tail':splunk.rcDisplay.displayMonitor,
|
||
|
'oneshot':splunk.rcDisplay.displayOneshot,
|
||
|
'index':splunk.rcDisplay.displayIndex,
|
||
|
'udp':splunk.rcDisplay.displayUdp,
|
||
|
'tcp':splunk.rcDisplay.displayTcp,
|
||
|
'saved-search':splunk.rcDisplay.displaySavedsearch,
|
||
|
'forward-server':splunk.rcDisplay.displayForwardServer,
|
||
|
'search:search':splunk.rcDisplay.displaySyncSearch,
|
||
|
'jobs':splunk.rcDisplay.displayJobs,
|
||
|
'search:dispatch':splunk.rcDisplay.displaySyncSearch,
|
||
|
'distributed-search':splunk.rcDisplay.displayDistribSearch,
|
||
|
'app':splunk.rcDisplay.displayApp,
|
||
|
'auth-method':splunk.rcDisplay.displayAuthMethod,
|
||
|
'role-mappings':splunk.rcDisplay.displayRoleMappings,
|
||
|
'deployments':splunk.rcDisplay.displayDeployment,
|
||
|
'distributed-search':splunk.rcDisplay.displayDistSearch,
|
||
|
'help':splunk.rcDisplay.displayHelp,
|
||
|
}
|
||
|
|
||
|
#mapping b/w cmds and their associated endpoints
|
||
|
CMD_ENDPOINT_MAP = {
|
||
|
'show:*': 'settings',
|
||
|
'set:*': 'settings',
|
||
|
'enable:web-ssl': 'settings',
|
||
|
'disable:web-ssl': 'settings',
|
||
|
'enable:webserver': 'settings',
|
||
|
'disable:webserver': 'settings',
|
||
|
'login': 'login',
|
||
|
'*:user': 'user',
|
||
|
'add:oneshot': 'oneshot',
|
||
|
'*:monitor': 'monitor',
|
||
|
'*:tail': 'monitor',
|
||
|
'*:index': 'index',
|
||
|
'*:udp': 'udp',
|
||
|
'*:tcp': 'tcp',
|
||
|
'*:saved-search': 'saved-search',
|
||
|
'*:forward-server': 'forward-server',
|
||
|
'search:*': 'search',
|
||
|
'dispatch:*': 'search',
|
||
|
'show:jobs': 'jobs',
|
||
|
'*:jobs': 'jobs',
|
||
|
'*:local-index': 'forward-server',
|
||
|
'*:app': 'app',
|
||
|
'*:auth-method': 'auth-method',
|
||
|
'reload:auth': 'auth-method',
|
||
|
'list:role-mappings': 'role-mappings',
|
||
|
'*:search-server': 'distributed-search',
|
||
|
'*:dist-search': 'distributed-search',
|
||
|
'*:deploy-server': 'deployments',
|
||
|
'*:deploy-clients': 'deployments',
|
||
|
'*:deploy-client': 'deployments',
|
||
|
'*:deploy-poll': 'deployments',
|
||
|
'*:exec': 'scripted',
|
||
|
'*:scripted': 'scripted',
|
||
|
'help': 'help', #special case even though help is not really an endpoint
|
||
|
}
|
||
|
|
||
|
BLACKLIST = ['set:auth-method', 'remove:auth-method', 'set:server-type', 'remove:index', 'list:app', 'add:app', 'edit:forward-server']
|
||
|
|
||
|
WHITELIST = {
|
||
|
'set': ['datastore-dir', 'default-hostname', 'default-index', 'server-type', 'deploy-poll'],
|
||
|
'show': ['datastore-dir', 'default-hostname', 'default-index', 'server-type', 'jobs', 'auth-method', 'config', 'license', 'deploy-poll'],
|
||
|
}
|
||
|
|
||
|
RE_HANDLER_ERR_MSG = re.compile("In handler '.*?':")
|
||
|
|
||
|
# -----------------------------
|
||
|
# -----------------------------
|
||
|
class CliException(Exception):
|
||
|
"""
|
||
|
base class for all cli exceptions.
|
||
|
"""
|
||
|
pass
|
||
|
|
||
|
# --------------------------
|
||
|
# --------------------------
|
||
|
class CliArgError(CliException):
|
||
|
"""
|
||
|
thrown when enough args not present to construct the complete uri
|
||
|
"""
|
||
|
pass
|
||
|
|
||
|
# ---------------------------------
|
||
|
# ---------------------------------
|
||
|
class NoEndpointError(CliException):
|
||
|
"""
|
||
|
thrown when no endpoint exists
|
||
|
"""
|
||
|
pass
|
||
|
|
||
|
# ---------------------------------------
|
||
|
# ---------------------------------------
|
||
|
class InvalidStatusCodeError(CliException):
|
||
|
"""
|
||
|
if create did not return 201
|
||
|
if edit/list/delete did not return 200
|
||
|
"""
|
||
|
pass
|
||
|
|
||
|
# --------------------------
|
||
|
def checkStatus(**kwargs):
|
||
|
"""
|
||
|
checks the returned HTTP status code, if it is desired for the 'type' of action performed.
|
||
|
"""
|
||
|
etype = kwargs['type']
|
||
|
server_response = kwargs['serverResponse']
|
||
|
|
||
|
logger.debug('In checkStatus: type: %s, server_response: %s' % (etype, server_response))
|
||
|
|
||
|
#when listing, editing or deleting we should get 200 / when creating we should get 201
|
||
|
if (etype in ['list', 'remove', 'edit'] and server_response.status != 200) or (etype == 'create' and server_response.status != 201):
|
||
|
err_txt = ''
|
||
|
for err in server_response.messages:
|
||
|
|
||
|
#err messages look like: "In handler '<some name>': <some msg>". Need to get rid of the first part to display better
|
||
|
if RE_HANDLER_ERR_MSG.match(err['text']):
|
||
|
err['text'] = re.sub(RE_HANDLER_ERR_MSG, '', err['text'])
|
||
|
|
||
|
err_txt = (err['text'])
|
||
|
|
||
|
raise InvalidStatusCodeError(err_txt)
|
||
|
|
||
|
# --------------------------------------------------
|
||
|
def layeredFind(endpoint, cmd, obj, target, parm=''):
|
||
|
"""
|
||
|
goes through the inheritance heirarchy as determined by the remote_cmds dict and returns the final value of the target.
|
||
|
"""
|
||
|
|
||
|
#first look in _common
|
||
|
try:
|
||
|
target_val = remote_cmds['_common'][cmd][target]
|
||
|
except:
|
||
|
target_val = '' # no target to be inherited
|
||
|
|
||
|
#then look in the base...
|
||
|
try:
|
||
|
target_val = remote_cmds[endpoint]['_common'][target]
|
||
|
except:
|
||
|
pass # no target to be inherited
|
||
|
|
||
|
#now check the next level i.e cmd
|
||
|
try:
|
||
|
target_val = remote_cmds[endpoint]['%s' % cmd][target]
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
try:
|
||
|
#now check level, i.e. cmd:object eg. show:license
|
||
|
if ('%s:%s' % (cmd, obj)) in remote_cmds[endpoint]:
|
||
|
try:
|
||
|
target_val = remote_cmds[endpoint]['%s:%s' % (cmd, obj)][target]
|
||
|
except:
|
||
|
pass
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
logger.debug('layeredFind:%s: %s' % (target, target_val))
|
||
|
|
||
|
return target_val
|
||
|
|
||
|
# -------------------------------------
|
||
|
def handleHelp(endpoint, restArgList):
|
||
|
"""
|
||
|
takes care of the help cmd
|
||
|
"""
|
||
|
|
||
|
if 'cmdname' not in restArgList: #in case they type ./splunk help
|
||
|
restArgList['cmdname'] = 'help'
|
||
|
|
||
|
help_text = layeredFind('help', 'help', '', restArgList['cmdname']) #the target appears with key as cmdname
|
||
|
|
||
|
if not help_text:
|
||
|
#not a common help text, so dig through the dict to see if you can find the appropriate one
|
||
|
#find the bugger using a one line list comprehension...haha...
|
||
|
|
||
|
try:
|
||
|
l = [remote_cmds[k]['_common']['help'][restArgList['cmdname']] for k in remote_cmds if k != '_common' if 'help' in remote_cmds[k]['_common'] if restArgList['cmdname'] in remote_cmds[k]['_common']['help']]
|
||
|
except:
|
||
|
l = '' #if something blows up here, just show no help rather than blowing up with a stacktrace
|
||
|
|
||
|
try:
|
||
|
help_text = l[0]
|
||
|
except IndexError:
|
||
|
help_text = ''
|
||
|
|
||
|
#Call the appropriate display function...
|
||
|
try:
|
||
|
DISPLAY_CHARS[endpoint](help_text = help_text, cmdname=restArgList['cmdname'])
|
||
|
except KeyError as e:
|
||
|
raise
|
||
|
|
||
|
return
|
||
|
|
||
|
# ----------------------------------------------------------------
|
||
|
def dispatchJob(search, sessionKey, namespace, owner, argList):
|
||
|
"""
|
||
|
helpers fun used by both sync/async search
|
||
|
"""
|
||
|
|
||
|
search = search.strip()
|
||
|
|
||
|
argListRem = copy.deepcopy(argList)
|
||
|
|
||
|
if len(search) == 0 or search[0] != '|':
|
||
|
search = "search " + search
|
||
|
|
||
|
#the remaining keys if any need to be passed in to the dispatch call so the endpoint can handle it. eg. the 'id' arg
|
||
|
for remove_key in ['maxout', 'buckets', 'maxtime', 'authstr', 'terms']:
|
||
|
try:
|
||
|
argListRem.pop(remove_key)
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
#rename the maxout/buckets/maxtime args - SPL-20794/SPL-20916
|
||
|
argListRem['max_count'] = argList.get('maxout', 100)
|
||
|
argListRem['status_buckets'] = argList.get('buckets', 0)
|
||
|
argListRem['max_time'] = argList.get('maxtime', 0)
|
||
|
|
||
|
argListRem['sessionKey'] = sessionKey
|
||
|
argListRem['namespace'] = namespace
|
||
|
argListRem['owner'] = owner
|
||
|
|
||
|
try:
|
||
|
searchjob = dispatch(search, **argListRem)
|
||
|
except splunk.SearchException as e:
|
||
|
raise
|
||
|
except:
|
||
|
raise #maybe somebody pressed Ctrl-C
|
||
|
|
||
|
return searchjob
|
||
|
|
||
|
# -------------------------------------------------------------------------------
|
||
|
def handleAsyncSearch(search, sessionKey, namespace, owner, argList, dotSplunk):
|
||
|
"""
|
||
|
handles the async search, TODO by S&I
|
||
|
"""
|
||
|
|
||
|
try:
|
||
|
searchjob = dispatchJob(search, sessionKey, namespace, owner, argList)
|
||
|
|
||
|
DISPLAY_CHARS['search:search'](detach='true', jid=searchjob.id)
|
||
|
except KeyboardInterrupt as e:
|
||
|
try:
|
||
|
searchjob.cancel()
|
||
|
logger.debug('Async Search job with id "%s" cancelled due to KeyboardInterrupt' % searchjob.id)
|
||
|
except NameError:
|
||
|
pass #no job to cancel
|
||
|
|
||
|
# --------------------------------------------------------------------
|
||
|
def handleSyncSearch(search, sessionKey, namespace, owner, argList):
|
||
|
"""
|
||
|
handles the search cmd
|
||
|
"""
|
||
|
|
||
|
try:
|
||
|
searchjob = dispatchJob(search, sessionKey, namespace, owner, argList)
|
||
|
|
||
|
|
||
|
sleep_time = 0.01
|
||
|
while not (searchjob.isDone):
|
||
|
if searchjob.isZombie:
|
||
|
logger.error('The search process seems to have crashed...')
|
||
|
sys.exit(1)
|
||
|
|
||
|
time.sleep(sleep_time)
|
||
|
if sleep_time >= 1:
|
||
|
sleep_time = 1
|
||
|
else:
|
||
|
sleep_time = sleep_time * 2
|
||
|
|
||
|
DISPLAY_CHARS['search:search'](searchjob = searchjob, detach='false', **argList)
|
||
|
#SPL-23022
|
||
|
if 'timeout' not in argList:
|
||
|
searchjob.cancel()
|
||
|
except KeyboardInterrupt as e:
|
||
|
try:
|
||
|
searchjob.cancel()
|
||
|
logger.error('Sync Search job with id "%s" cancelled due to KeyboardInterrupt' % searchjob.id)
|
||
|
except NameError:
|
||
|
pass #no job to cancel
|
||
|
|
||
|
# -----------------------------------------------------------
|
||
|
def handleShowConf(confName, sessionKey, namespace, owner):
|
||
|
"""
|
||
|
handles the show config <confName> cmd
|
||
|
"""
|
||
|
conf = getConf(confName, sessionKey=sessionKey, namespace=namespace, owner=owner)
|
||
|
DISPLAY_CHARS['settings'](conf=conf, cmd='show', obj='config')
|
||
|
|
||
|
# ------------------------------------------------------
|
||
|
def handleRemoveJobsAll(sessionKey, namespace, owner):
|
||
|
"""
|
||
|
current hack for removing all asyn jobs - to be removed when EAI endpoint gets written to do this...
|
||
|
"""
|
||
|
|
||
|
jobs = listJobs(sessionKey=sessionKey, namespace=namespace, owner=owner)
|
||
|
cancelled_jobs = []
|
||
|
for job in jobs:
|
||
|
j = getJob(job['sid'])
|
||
|
j.cancel()
|
||
|
cancelled_jobs.append(job['sid'])
|
||
|
|
||
|
#Call the appropriate display function...
|
||
|
try:
|
||
|
DISPLAY_CHARS['jobs'](cmd='remove', obj='jobs', eaiArgsList={'jobid':cancelled_jobs})
|
||
|
except KeyError as e:
|
||
|
logger.debug('endpoint: jobs')
|
||
|
logger.debug(str(e))
|
||
|
raise
|
||
|
|
||
|
|
||
|
# -----------------------------------------------
|
||
|
def sanitizeArgs(target, argsMap, argsDict):
|
||
|
"""
|
||
|
takes a dictionary which are the get/post args and removes any keys within this which was used to construct the uri
|
||
|
"""
|
||
|
|
||
|
logger.debug('In sanitizeArgs: target: %s, argsMap: %s, argsDict: %s' % (target, argsMap, argsDict))
|
||
|
|
||
|
remove_key = re.findall('%\((.*?)\)s', target)
|
||
|
if remove_key:
|
||
|
try:
|
||
|
for k in remove_key:
|
||
|
try:
|
||
|
#first check if this arg name has been mapped to an eai name
|
||
|
argsDict.pop(argsMap[k])
|
||
|
except KeyError:
|
||
|
#ok, maybe this argument name did not need any mapping ie. it was the same
|
||
|
argsDict.pop(k)
|
||
|
except KeyError:
|
||
|
#there is something in the eai_id that has not been converted. Bail out...
|
||
|
raise CliArgError('%s parameter not provided' % k)
|
||
|
|
||
|
except Exception as e:
|
||
|
logger.debug(str(e))
|
||
|
pass
|
||
|
|
||
|
# --------------------------------------
|
||
|
def _validateURI(target, restArgList):
|
||
|
"""
|
||
|
ensures that all %(<key>)s in the uri/eai_id can be honoured
|
||
|
"""
|
||
|
if not isinstance(target, dict):
|
||
|
eai_key_list = re.findall('%\((.*?)\)s', target)
|
||
|
logger.debug('eai_key_list: %s' % str(eai_key_list))
|
||
|
if eai_key_list:
|
||
|
for k in eai_key_list:
|
||
|
if k not in restArgList:
|
||
|
raise CliArgError('%s parameter not provided' % k)
|
||
|
|
||
|
def quick_and_dirty_call(uri, type, getargs, postargs, namespace, owner, method='GET', sessionKey=''):
|
||
|
""""""
|
||
|
qad_uri = buildEndpoint(uri, entityName='', namespace=namespace, owner=owner)
|
||
|
|
||
|
try:
|
||
|
serverResponse, serverContent = simpleRequest(qad_uri, sessionKey=sessionKey, getargs=getargs, postargs=postargs, method=method)
|
||
|
except Exception as e:
|
||
|
logger.debug('Could not construct quick_and_dirty uri: %s, %s' % (str(qad_uri), str(e)))
|
||
|
raise CliArgError('Could not get app context')
|
||
|
|
||
|
#check the returned status code if it is ok
|
||
|
try:
|
||
|
checkStatus(type=type, serverResponse=serverResponse)
|
||
|
except Exception as e:
|
||
|
logger.debug('Could not construct quick_And_dirty uri: %s, %s' % (str(qad_uri), str(e)))
|
||
|
raise CliArgError('Could not get app context')
|
||
|
|
||
|
return serverContent
|
||
|
|
||
|
|
||
|
|
||
|
# -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
def makeRestCall(cmd=None, restArgList=None, obj=None, sessionKey=None, entityName=None, search=None, count=None, offset=None, sort_key=None, sort_dir=None, dotSplunk=None, timeout=None, token=False):
|
||
|
"""
|
||
|
main entry point for the outside world. Will make the REST call and return the results after formatting etc
|
||
|
"""
|
||
|
|
||
|
logger.debug('cmd: %s, obj: %s, restArgList: %s' % (cmd, obj, str(restArgList)))
|
||
|
|
||
|
#if blacklisted i.e not to be run remotely, exit immediately
|
||
|
if '%s:%s' % (cmd, obj) in BLACKLIST:
|
||
|
raise NoEndpointError
|
||
|
elif cmd in WHITELIST:
|
||
|
if obj not in WHITELIST[cmd]:
|
||
|
splunk.rcDisplay.displayGenericError(cmd=cmd, obj=obj)
|
||
|
|
||
|
#look for the endpoint
|
||
|
try:
|
||
|
endpoint = CMD_ENDPOINT_MAP['%s:%s' % (cmd, obj)] #first try a key match with cmd and obj
|
||
|
except KeyError:
|
||
|
try:
|
||
|
endpoint = CMD_ENDPOINT_MAP['*:%s' % obj] #now with *:obj i.e for all commands of this obj eg. add user, edit user, remove user, list user etc
|
||
|
except KeyError:
|
||
|
try:
|
||
|
endpoint = CMD_ENDPOINT_MAP['%s:*' % cmd] #now with cmd:* i.e for all objects of this cmd eg. show web-port, show servername, show splunkd-port etc
|
||
|
except KeyError:
|
||
|
try:
|
||
|
endpoint = CMD_ENDPOINT_MAP['%s' % cmd] #finally with only cmd
|
||
|
except KeyError:
|
||
|
raise NoEndpointError
|
||
|
|
||
|
logger.debug('endpoint: %s' % endpoint)
|
||
|
|
||
|
#the argList is used to construct the arguments to the POST/GET. We don't need the auth credential in there. So pop it out, if present.
|
||
|
|
||
|
#if there are any default parms that need to be included for this eai request, tag them on
|
||
|
argList = {}
|
||
|
try:
|
||
|
argList.update(layeredFind(endpoint, cmd, obj, 'default_eai_parms'))
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
argList.update(restArgList)
|
||
|
|
||
|
if 'authstr' in argList:
|
||
|
argList.pop('authstr')
|
||
|
logger.debug('authstr poped from argList')
|
||
|
|
||
|
#get namespace/owner, if present
|
||
|
try:
|
||
|
#SPL-22621
|
||
|
namespace = argList['app']
|
||
|
#this arg should not go to the endpoint
|
||
|
argList.pop('app')
|
||
|
except:
|
||
|
#requirements out of SPL-24442
|
||
|
#check if there is a app_context is required
|
||
|
app_context = layeredFind(endpoint, cmd, obj, 'app_context')
|
||
|
|
||
|
if app_context:
|
||
|
ac_uri = app_context['uri']
|
||
|
ac_helper = app_context['helper']
|
||
|
ac_type = app_context['type']
|
||
|
|
||
|
#make a GET request to the uri
|
||
|
servResp = quick_and_dirty_call(ac_uri, ac_type, {'count':'-1'}, {}, '', '', method='GET', sessionKey=sessionKey)
|
||
|
#print servResp
|
||
|
try:
|
||
|
namespace = rcHooks.__dict__[app_context['helper']](servResp, argList)
|
||
|
except Exception as e:
|
||
|
logger.debug('ERROR:' + str(e))
|
||
|
|
||
|
elif obj in ['saved-search', 'dist-search', 'search-server', 'jobs']:
|
||
|
namespace = 'search'
|
||
|
else:
|
||
|
namespace = ''
|
||
|
|
||
|
logger.debug('namespace: %s' % namespace)
|
||
|
|
||
|
try:
|
||
|
owner = argList['owner']
|
||
|
#this arg should not go to the endpoint
|
||
|
argList.pop('owner')
|
||
|
except:
|
||
|
owner = 'admin'
|
||
|
|
||
|
#Building the uri...
|
||
|
uri = layeredFind(endpoint, cmd, obj, 'uri')
|
||
|
lf_eai_id = layeredFind(endpoint, cmd, obj, 'eai_id')
|
||
|
|
||
|
required = layeredFind(endpoint, cmd, obj, 'required')
|
||
|
|
||
|
#if u cannot build the uri/eai_id, bail out...
|
||
|
_validateURI(uri, argList)
|
||
|
_validateURI(lf_eai_id, argList)
|
||
|
|
||
|
for field in required:
|
||
|
if field not in argList:
|
||
|
logger.error("Required field '%s' missing" % field)
|
||
|
sys.exit(1)
|
||
|
|
||
|
#used later on to ensure the key in %(key)s is removed from the list of args sent to the endpoint
|
||
|
uri_copy = uri
|
||
|
|
||
|
#the help is a special case. No rest call etc required currently
|
||
|
if cmd == 'help':
|
||
|
return handleHelp(endpoint, restArgList)
|
||
|
#extraction of properties has already been written for us, so reuse it
|
||
|
elif '%s:%s' % (cmd, obj) == 'show:config':
|
||
|
try:
|
||
|
return handleShowConf(restArgList['name'], sessionKey, namespace, owner)
|
||
|
except splunk.ResourceNotFound:
|
||
|
#can throw this error if we try and show a non-existent config
|
||
|
splunk.rcDisplay.displayResourceError(cmd=cmd, obj=obj, uri=restArgList['name'], serverContent=None)
|
||
|
return
|
||
|
#show:default-index has already been done for us, reuse it
|
||
|
elif '%s:%s' % (cmd, obj) == 'show:default-index':
|
||
|
defIndexList = []
|
||
|
try:
|
||
|
#first get the role associated with this user
|
||
|
roles = auth.getUser(auth.getCurrentUser()['name'], sessionKey=sessionKey)['roles']
|
||
|
#get details of each role
|
||
|
for role in roles:
|
||
|
indexes = auth.getRole(role, sessionKey=sessionKey)['srchIndexesDefault']
|
||
|
for index in indexes:
|
||
|
defIndexList.append(index)
|
||
|
except:
|
||
|
pass
|
||
|
DISPLAY_CHARS[endpoint](cmd=cmd, obj=obj, sessionKey=sessionKey, defIndex=defIndexList)
|
||
|
|
||
|
#handle sync/async search
|
||
|
elif cmd in ['search', 'dispatch']:
|
||
|
if not restArgList['terms'].strip():
|
||
|
splunk.rcDisplay.displayGenericError(cmd=cmd, terms='')
|
||
|
return
|
||
|
if 'detach' in restArgList and restArgList['detach'] == 'true':
|
||
|
return handleAsyncSearch(restArgList['terms'], sessionKey, namespace, owner, restArgList, dotSplunk)
|
||
|
else:
|
||
|
return handleSyncSearch(restArgList['terms'], sessionKey, namespace, owner, restArgList)
|
||
|
#hack for removing all async jobs, to be removed when an EAI endpoint is provided to do this
|
||
|
elif cmd == 'remove' and obj == 'jobs' and ('jobid' in restArgList) and restArgList['jobid'] == 'all':
|
||
|
return handleRemoveJobsAll(sessionKey, namespace, owner)
|
||
|
else:
|
||
|
|
||
|
#Building the args taking into account the cli/eai mapping if required...
|
||
|
|
||
|
#args contains the mapping b/w cli/eai names
|
||
|
args = layeredFind(endpoint, cmd, obj, 'args') or {}
|
||
|
|
||
|
#start with the pre-hooks...
|
||
|
|
||
|
#eaiArgsList will eventually become the getargs or postargs to send as part of the REST call
|
||
|
|
||
|
prehooks = layeredFind(endpoint, cmd, obj, 'prehooks') or []
|
||
|
|
||
|
for ph in prehooks:
|
||
|
rcHooks.__dict__[ph](cmd, obj, argList)
|
||
|
|
||
|
eaiArgsList = rcHooks.map_args_cli_2_eai(args, {}, argList)
|
||
|
|
||
|
logger.debug('after prehooks, eaiArgsList: %s' % str(eaiArgsList))
|
||
|
|
||
|
|
||
|
#first check if the eai_id needs to be obtained by another request!!!
|
||
|
if isinstance(lf_eai_id, dict):
|
||
|
#oh crappp...
|
||
|
eai_id_uri = buildEndpoint(lf_eai_id['uri'], entityName='', namespace=namespace, owner=owner, search=search, count=count, offset=offset, sort_key=sort_key, sort_dir=sort_dir)
|
||
|
eai_id_method = GLOBAL_ACTIONS['%s' % lf_eai_id['type']]
|
||
|
|
||
|
try:
|
||
|
eai_id_serverResponse, eai_id_serverContent = simpleRequest(eai_id_uri, sessionKey=sessionKey, getargs='', postargs='', method=eai_id_method)
|
||
|
except Exception as e:
|
||
|
logger.debug('Could not construct eai_id: %s, %s' % str(lf_eai_id), str(e))
|
||
|
raise CliArgError('Could not construct eai_id')
|
||
|
|
||
|
#check the returned status code if it is ok
|
||
|
try:
|
||
|
checkStatus(type=lf_eai_id['type'], serverResponse=eai_id_serverResponse)
|
||
|
except Exception as e:
|
||
|
logger.debug('Could not construct eai_id: %s, %s' % (str(lf_eai_id), str(e)))
|
||
|
raise CliArgError('Could not construct eai_id')
|
||
|
|
||
|
eai_id = splunk.rcDisplay.extractLevel1Feed(serverContent=eai_id_serverContent, filter=[lf_eai_id['filter']])
|
||
|
|
||
|
#this essentially means %(name)s % argList i.e the contents of key 'name' in dict argList
|
||
|
elif lf_eai_id:
|
||
|
try:
|
||
|
eai_id_tmp = '%s' % lf_eai_id
|
||
|
eai_id = eai_id_tmp % argList
|
||
|
#hack for auth-method ldap, allowing users to type lower case as well
|
||
|
if eai_id == 'ldap' and obj == 'auth-method':
|
||
|
eai_id = 'LDAP'
|
||
|
except:
|
||
|
eai_id = lf_eai_id
|
||
|
else:
|
||
|
eai_id = ''
|
||
|
|
||
|
try:
|
||
|
uri_tmp = '%s' % uri
|
||
|
uri = uri_tmp % argList
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
logger.debug('Before buildEndpoint uri: %s' % uri)
|
||
|
logger.debug('Before buildEndpoint entityName: %s' % eai_id)
|
||
|
|
||
|
uri = buildEndpoint(uri, entityName=eai_id, namespace=namespace, owner=owner, search=search, count=count, offset=offset, sort_key=sort_key, sort_dir=sort_dir)
|
||
|
logger.debug('uri: %s' % uri)
|
||
|
|
||
|
etype = layeredFind(endpoint, cmd, obj, 'type')
|
||
|
if not etype:
|
||
|
try:
|
||
|
etype = GLOBAL_DEFAULTS[cmd]
|
||
|
except KeyError:
|
||
|
raise NoEndpointError('No endpoint with cmd: %s, obj: %s' % (cmd, obj))
|
||
|
logger.debug('Using default value of type: %s' % etype)
|
||
|
|
||
|
method = GLOBAL_ACTIONS['%s' % etype]
|
||
|
|
||
|
logger.debug('eaiArgsList: %s', eaiArgsList)
|
||
|
|
||
|
postargs = getargs = {}
|
||
|
if method == 'POST':
|
||
|
postargs = copy.deepcopy(eaiArgsList)
|
||
|
elif method == 'GET':
|
||
|
getargs = copy.deepcopy(eaiArgsList)
|
||
|
#hack for list monitor cmd, to allow it to show internal log files as well...
|
||
|
if cmd == 'list' and obj in ['monitor', 'tail'] and 'show-hidden' in eaiArgsList:
|
||
|
getargs.pop('show-hidden')
|
||
|
|
||
|
#if the postargs/getargs contain any parameter that was used to build the uri, pop them out as they do not need to be sent
|
||
|
if not isinstance(lf_eai_id, dict):
|
||
|
sanitizeArgs(lf_eai_id, args, postargs)
|
||
|
sanitizeArgs(lf_eai_id, args, getargs)
|
||
|
sanitizeArgs(uri_copy, args, postargs)
|
||
|
sanitizeArgs(uri_copy, args, getargs)
|
||
|
|
||
|
logger.debug('postargs: %s', postargs)
|
||
|
logger.debug('getargs: %s', getargs)
|
||
|
|
||
|
try:
|
||
|
serverResponse, serverContent = simpleRequest(uri, sessionKey=sessionKey, getargs=getargs, postargs=postargs, method=method, timeout=timeout, token=token)
|
||
|
except splunk.ResourceNotFound as e:
|
||
|
#will reach here if we try to POST to a non existent url. eg. edit a non-existent monitor, show a non-existent config etc
|
||
|
splunk.rcDisplay.displayResourceError(cmd=cmd, obj=obj, uri=uri, serverContent=e)
|
||
|
return
|
||
|
except Exception as e:
|
||
|
raise
|
||
|
|
||
|
#check the returned status code if it is ok
|
||
|
try:
|
||
|
checkStatus(type=etype, serverResponse=serverResponse)
|
||
|
except InvalidStatusCodeError as e:
|
||
|
raise
|
||
|
|
||
|
#Call the appropriate display function...
|
||
|
try:
|
||
|
DISPLAY_CHARS[endpoint](cmd=cmd, obj=obj, type=etype, serverResponse=serverResponse, serverContent=serverContent, sessionKey=sessionKey, eaiArgsList=eaiArgsList)
|
||
|
except KeyError as e:
|
||
|
logger.debug('endpoint: %s' % endpoint)
|
||
|
logger.debug(str(e))
|
||
|
raise
|