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.
159 lines
4.6 KiB
159 lines
4.6 KiB
#!/usr/bin/env python
|
|
# Simple script to bounce the lookup-update-notify REST endpoint.
|
|
# Needed to trigger model replication in SHC environments.
|
|
#
|
|
# This needs to launch a subprocess for the simple reason that the
|
|
# interpreter that ML-SPL runs under (Splunk_SA_Scientific_Python)
|
|
# doesn't have OpenSSL. We marshall the required Splunk auth token
|
|
# through stdin to avoid leaks via environment variables/command line
|
|
# arguments/etc.
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
# Destination python path
|
|
SPLUNK_PYTHON_PATH = os.path.join(os.environ['SPLUNK_HOME'], 'bin', 'python3')
|
|
|
|
|
|
def _need_to_bounce():
|
|
return sys.executable != SPLUNK_PYTHON_PATH
|
|
|
|
|
|
def make_rest_call(
|
|
session_key, method, url, postargs=None, jsonargs=None, getargs=None, rawResult=False
|
|
):
|
|
"""
|
|
Make REST call using a new python interpreter if necessary.
|
|
|
|
If the current python interpreter is not Splunk's default, launch an external process to run the REST query
|
|
with the Splunk's default python interpreter (because we need the SSL package not available in the PSC python
|
|
interpreter). If the current python interpreter is already Splunk's default, just run the REST query.
|
|
|
|
Args:
|
|
payload (dict): request payload. If None, it will be read from stdin.
|
|
|
|
Returns:
|
|
reply (dict): Splunk REST response or False on error.
|
|
"""
|
|
import cexc
|
|
|
|
logger = cexc.get_logger(__name__)
|
|
|
|
payload = {
|
|
'session_key': session_key,
|
|
'url': url,
|
|
'method': method,
|
|
'postargs': postargs,
|
|
'jsonargs': jsonargs,
|
|
'getargs': getargs,
|
|
'rawResult': rawResult,
|
|
}
|
|
|
|
try:
|
|
if _need_to_bounce():
|
|
p = subprocess.Popen(
|
|
[SPLUNK_PYTHON_PATH, os.path.abspath(__file__)],
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
)
|
|
(stdoutdata, stderrdata) = p.communicate(json.dumps(payload).encode())
|
|
p.wait()
|
|
|
|
for errline in stderrdata.splitlines():
|
|
logger.debug('> %s', errline)
|
|
|
|
if p.returncode != 0:
|
|
raise RuntimeError(
|
|
"rest_bouncer subprocess exited with non-zero error code '%d'"
|
|
% p.returncode
|
|
)
|
|
|
|
reply = json.loads(stdoutdata)
|
|
else:
|
|
# No need to bounce. We already have the correct Python interpreter (Splunk's default).
|
|
reply = _make_rest_call_internal(payload)
|
|
|
|
except Exception as e:
|
|
logger.warn('rest_bouncer failure: %s: %s', type(e).__name__, str(e))
|
|
return False
|
|
|
|
return reply
|
|
|
|
|
|
def _make_rest_call_internal(payload=None):
|
|
"""
|
|
Make REST call via Splunk REST API
|
|
|
|
This does the real meat of the work.
|
|
|
|
Args:
|
|
payload (dict): request payload. If None, it will be read from stdin.
|
|
|
|
Returns:
|
|
reply (dict): Splunk REST response
|
|
"""
|
|
from splunk import rest, RESTException
|
|
|
|
import http.client
|
|
|
|
reply = {
|
|
'success': False,
|
|
'response': None,
|
|
'content': None,
|
|
'error_type': None,
|
|
'status': None,
|
|
}
|
|
|
|
# Read JSON payload from stdin
|
|
try:
|
|
if payload is None:
|
|
line = next(sys.stdin)
|
|
payload = json.loads(line)
|
|
|
|
session_key = payload['session_key']
|
|
method = payload['method']
|
|
url = payload['url']
|
|
postargs = payload['postargs']
|
|
jsonargs = payload['jsonargs']
|
|
getargs = payload['getargs']
|
|
rawResult = payload['rawResult']
|
|
|
|
response, content = rest.simpleRequest(
|
|
url,
|
|
method=method,
|
|
postargs=postargs,
|
|
sessionKey=session_key,
|
|
raiseAllErrors=False,
|
|
jsonargs=jsonargs,
|
|
getargs=getargs,
|
|
rawResult=rawResult,
|
|
)
|
|
|
|
reply['response'] = response
|
|
reply['content'] = content.decode('utf-8')
|
|
status = response.status
|
|
reply['status'] = status
|
|
|
|
if status > 199 and status < 300:
|
|
reply['success'] = True
|
|
except RESTException as e:
|
|
reply['error_type'] = type(e).__name__
|
|
reply['content'] = '{"messages":[{"type": "ERROR", "text": "%s"}]}' % str(e)
|
|
reply['status'] = e.statusCode
|
|
except Exception as e:
|
|
error_type = type(e).__name__
|
|
reply['content'] = '{"messages":[{"type": "%s", "text": "%s"}]}' % (error_type, str(e))
|
|
reply['error_type'] = error_type
|
|
reply['status'] = http.client.INTERNAL_SERVER_ERROR
|
|
return reply
|
|
|
|
|
|
def main():
|
|
print((json.dumps(_make_rest_call_internal())))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|