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

#!/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()