Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into VULN-17383
Browse files Browse the repository at this point in the history
# Conflicts:
#	.github/workflows/test.yml
#	CHANGELOG.md
#	README.md
#	splunklib/__init__.py
  • Loading branch information
szymonjas committed Oct 29, 2024
2 parents 1f2b3bc + 0c9469d commit 41e8948
Show file tree
Hide file tree
Showing 10 changed files with 349 additions and 21 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Python CI

on:
[ push, pull_request ]
[ push, pull_request, workflow_dispatch ]

jobs:
build:
Expand All @@ -22,7 +22,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3

- name: Run docker-compose
- name: Run docker compose
run: SPLUNK_VERSION=${{matrix.splunk-version}} docker compose up -d

- name: Setup Python
Expand Down
12 changes: 0 additions & 12 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
# Splunk Enterprise SDK for Python Changelog

## Version 2.0.2

### Minor changes
* Added six.py file back


## Version 2.0.1

### Bug fixes
* [#567](https://github.com/splunk/splunk-sdk-python/issues/567) Moved "deprecation" dependency


## Version 2.0.0

### Feature updates
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# The Splunk Enterprise Software Development Kit for Python

#### Version 2.0.2
#### Version 2.0.0

The Splunk Enterprise Software Development Kit (SDK) for Python contains library code designed to enable developers to build applications using the Splunk platform.

Expand Down
2 changes: 1 addition & 1 deletion splunklib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ def setup_logging(level, log_format=DEFAULT_LOG_FORMAT, date_format=DEFAULT_DATE
datefmt=date_format)


__version_info__ = (2, 0, 2)
__version_info__ = (2, 0, 0)
__version__ = ".".join(map(str, __version_info__))
96 changes: 95 additions & 1 deletion splunklib/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
PATH_JOBS = "search/jobs/"
PATH_JOBS_V2 = "search/v2/jobs/"
PATH_LOGGER = "/services/server/logger/"
PATH_MACROS = "configs/conf-macros/"
PATH_MESSAGES = "messages/"
PATH_MODULAR_INPUTS = "data/modular-inputs"
PATH_ROLES = "authorization/roles/"
Expand Down Expand Up @@ -667,6 +668,15 @@ def saved_searches(self):
"""
return SavedSearches(self)

@property
def macros(self):
"""Returns the collection of macros.
:return: A :class:`Macros` collection of :class:`Macro`
entities.
"""
return Macros(self)

@property
def settings(self):
"""Returns the configuration settings for this instance of Splunk.
Expand Down Expand Up @@ -3440,6 +3450,90 @@ def create(self, name, search, **kwargs):
return Collection.create(self, name, search=search, **kwargs)


class Macro(Entity):
"""This class represents a search macro."""
def __init__(self, service, path, **kwargs):
Entity.__init__(self, service, path, **kwargs)

@property
def args(self):
"""Returns the macro arguments.
:return: The macro arguments.
:rtype: ``string``
"""
return self._state.content.get('args', '')

@property
def definition(self):
"""Returns the macro definition.
:return: The macro definition.
:rtype: ``string``
"""
return self._state.content.get('definition', '')

@property
def errormsg(self):
"""Returns the validation error message for the macro.
:return: The validation error message for the macro.
:rtype: ``string``
"""
return self._state.content.get('errormsg', '')

@property
def iseval(self):
"""Returns the eval-based definition status of the macro.
:return: The iseval value for the macro.
:rtype: ``string``
"""
return self._state.content.get('iseval', '0')

def update(self, definition=None, **kwargs):
"""Updates the server with any changes you've made to the current macro
along with any additional arguments you specify.
:param `definition`: The macro definition (optional).
:type definition: ``string``
:param `kwargs`: Additional arguments (optional). Available parameters are:
'disabled', 'iseval', 'validation', and 'errormsg'.
:type kwargs: ``dict``
:return: The :class:`Macro`.
"""
# Updates to a macro *require* that the definition be
# passed, so we pass the current definition if a value wasn't
# provided by the caller.
if definition is None: definition = self.content.definition
Entity.update(self, definition=definition, **kwargs)
return self

@property
def validation(self):
"""Returns the validation expression for the macro.
:return: The validation expression for the macro.
:rtype: ``string``
"""
return self._state.content.get('validation', '')


class Macros(Collection):
"""This class represents a collection of macros. Retrieve this
collection using :meth:`Service.macros`."""
def __init__(self, service):
Collection.__init__(
self, service, PATH_MACROS, item=Macro)

def create(self, name, definition, **kwargs):
""" Creates a macro.
:param name: The name for the macro.
:type name: ``string``
:param definition: The macro definition.
:type definition: ``string``
:param kwargs: Additional arguments (optional). Available parameters are:
'disabled', 'iseval', 'validation', and 'errormsg'.
:type kwargs: ``dict``
:return: The :class:`Macros` collection.
"""
return Collection.create(self, name, definition=definition, **kwargs)


class Settings(Entity):
"""This class represents configuration settings for a Splunk service.
Retrieve this collection using :meth:`Service.settings`."""
Expand Down Expand Up @@ -3905,4 +3999,4 @@ def batch_save(self, *documents):
data = json.dumps(documents)

return json.loads(
self._post('batch_save', headers=KVStoreCollectionData.JSON_HEADER, body=data).body.read().decode('utf-8'))
self._post('batch_save', headers=KVStoreCollectionData.JSON_HEADER, body=data).body.read().decode('utf-8'))
20 changes: 20 additions & 0 deletions splunklib/modularinput/event_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# under the License.

import sys
import traceback

from splunklib.utils import ensure_str
from .event import ET
Expand Down Expand Up @@ -66,6 +67,25 @@ def log(self, severity, message):
self._err.write(f"{severity} {message}\n")
self._err.flush()

def log_exception(self, message, exception=None, severity=None):
"""Logs messages about the exception thrown by this modular input to Splunk.
These messages will show up in Splunk's internal logs.
:param message: ``string``, message to log.
:param exception: ``Exception``, exception thrown by this modular input; if none, sys.exc_info() is used
:param severity: ``string``, severity of message, see severities defined as class constants. Default severity: ERROR
"""
if exception is not None:
tb_str = traceback.format_exception(type(exception), exception, exception.__traceback__)
else:
tb_str = traceback.format_exc()

if severity is None:
severity = EventWriter.ERROR

self._err.write(("%s %s - %s" % (severity, message, tb_str)).replace("\n", " "))
self._err.flush()

def write_xml_document(self, document):
"""Writes a string representation of an
``ElementTree`` object to the output stream.
Expand Down
7 changes: 3 additions & 4 deletions splunklib/modularinput/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,12 @@ def run_script(self, args, event_writer, input_stream):
event_writer.write_xml_document(root)

return 1
err_string = "ERROR Invalid arguments to modular input script:" + ' '.join(
args)
event_writer._err.write(err_string)
event_writer.log(EventWriter.ERROR, "Invalid arguments to modular input script:" + ' '.join(
args))
return 1

except Exception as e:
event_writer.log(EventWriter.ERROR, str(e))
event_writer.log_exception(str(e))
return 1

@property
Expand Down
28 changes: 28 additions & 0 deletions tests/modularinput/test_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
# under the License.


import re
import sys
from io import StringIO

import pytest

Expand Down Expand Up @@ -150,3 +152,29 @@ def test_write_xml_is_sane(capsys):
found_xml = ET.fromstring(captured.out)

assert xml_compare(expected_xml, found_xml)


def test_log_exception():
out, err = StringIO(), StringIO()
ew = EventWriter(out, err)

exc = Exception("Something happened!")

try:
raise exc
except Exception:
ew.log_exception("ex1")

assert out.getvalue() == ""

# Remove paths and line
err = re.sub(r'File "[^"]+', 'File "...', err.getvalue())
err = re.sub(r'line \d+', 'line 123', err)

# One line
assert err == (
'ERROR ex1 - Traceback (most recent call last): '
' File "...", line 123, in test_log_exception '
' raise exc '
'Exception: Something happened! '
)
35 changes: 35 additions & 0 deletions tests/modularinput/test_script.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import sys

import io
import re
import xml.etree.ElementTree as ET
from splunklib.client import Service
from splunklib.modularinput import Script, EventWriter, Scheme, Argument, Event
Expand Down Expand Up @@ -228,3 +229,37 @@ def stream_events(self, inputs, ew):
assert output.err == ""
assert isinstance(script.service, Service)
assert script.service.authority == script.authority_uri


def test_log_script_exception(monkeypatch):
out, err = io.StringIO(), io.StringIO()

# Override abstract methods
class NewScript(Script):
def get_scheme(self):
return None

def stream_events(self, inputs, ew):
raise RuntimeError("Some error")

script = NewScript()
input_configuration = data_open("data/conf_with_2_inputs.xml")

ew = EventWriter(out, err)

assert script.run_script([TEST_SCRIPT_PATH], ew, input_configuration) == 1

# Remove paths and line numbers
err = re.sub(r'File "[^"]+', 'File "...', err.getvalue())
err = re.sub(r'line \d+', 'line 123', err)

assert out.getvalue() == ""
assert err == (
'ERROR Some error - '
'Traceback (most recent call last): '
' File "...", line 123, in run_script '
' self.stream_events(self._input_definition, event_writer) '
' File "...", line 123, in stream_events '
' raise RuntimeError("Some error") '
'RuntimeError: Some error '
)
Loading

0 comments on commit 41e8948

Please sign in to comment.