Skip to content

Commit 41e8948

Browse files
committed
Merge remote-tracking branch 'origin/develop' into VULN-17383
# Conflicts: # .github/workflows/test.yml # CHANGELOG.md # README.md # splunklib/__init__.py
2 parents 1f2b3bc + 0c9469d commit 41e8948

File tree

10 files changed

+349
-21
lines changed

10 files changed

+349
-21
lines changed

.github/workflows/test.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: Python CI
22

33
on:
4-
[ push, pull_request ]
4+
[ push, pull_request, workflow_dispatch ]
55

66
jobs:
77
build:
@@ -22,7 +22,7 @@ jobs:
2222
- name: Checkout code
2323
uses: actions/checkout@v3
2424

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

2828
- name: Setup Python

CHANGELOG.md

-12
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,5 @@
11
# Splunk Enterprise SDK for Python Changelog
22

3-
## Version 2.0.2
4-
5-
### Minor changes
6-
* Added six.py file back
7-
8-
9-
## Version 2.0.1
10-
11-
### Bug fixes
12-
* [#567](https://github.com/splunk/splunk-sdk-python/issues/567) Moved "deprecation" dependency
13-
14-
153
## Version 2.0.0
164

175
### Feature updates

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
# The Splunk Enterprise Software Development Kit for Python
66

7-
#### Version 2.0.2
7+
#### Version 2.0.0
88

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

splunklib/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@ def setup_logging(level, log_format=DEFAULT_LOG_FORMAT, date_format=DEFAULT_DATE
3030
datefmt=date_format)
3131

3232

33-
__version_info__ = (2, 0, 2)
33+
__version_info__ = (2, 0, 0)
3434
__version__ = ".".join(map(str, __version_info__))

splunklib/client.py

+95-1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
PATH_JOBS = "search/jobs/"
102102
PATH_JOBS_V2 = "search/v2/jobs/"
103103
PATH_LOGGER = "/services/server/logger/"
104+
PATH_MACROS = "configs/conf-macros/"
104105
PATH_MESSAGES = "messages/"
105106
PATH_MODULAR_INPUTS = "data/modular-inputs"
106107
PATH_ROLES = "authorization/roles/"
@@ -667,6 +668,15 @@ def saved_searches(self):
667668
"""
668669
return SavedSearches(self)
669670

671+
@property
672+
def macros(self):
673+
"""Returns the collection of macros.
674+
675+
:return: A :class:`Macros` collection of :class:`Macro`
676+
entities.
677+
"""
678+
return Macros(self)
679+
670680
@property
671681
def settings(self):
672682
"""Returns the configuration settings for this instance of Splunk.
@@ -3440,6 +3450,90 @@ def create(self, name, search, **kwargs):
34403450
return Collection.create(self, name, search=search, **kwargs)
34413451

34423452

3453+
class Macro(Entity):
3454+
"""This class represents a search macro."""
3455+
def __init__(self, service, path, **kwargs):
3456+
Entity.__init__(self, service, path, **kwargs)
3457+
3458+
@property
3459+
def args(self):
3460+
"""Returns the macro arguments.
3461+
:return: The macro arguments.
3462+
:rtype: ``string``
3463+
"""
3464+
return self._state.content.get('args', '')
3465+
3466+
@property
3467+
def definition(self):
3468+
"""Returns the macro definition.
3469+
:return: The macro definition.
3470+
:rtype: ``string``
3471+
"""
3472+
return self._state.content.get('definition', '')
3473+
3474+
@property
3475+
def errormsg(self):
3476+
"""Returns the validation error message for the macro.
3477+
:return: The validation error message for the macro.
3478+
:rtype: ``string``
3479+
"""
3480+
return self._state.content.get('errormsg', '')
3481+
3482+
@property
3483+
def iseval(self):
3484+
"""Returns the eval-based definition status of the macro.
3485+
:return: The iseval value for the macro.
3486+
:rtype: ``string``
3487+
"""
3488+
return self._state.content.get('iseval', '0')
3489+
3490+
def update(self, definition=None, **kwargs):
3491+
"""Updates the server with any changes you've made to the current macro
3492+
along with any additional arguments you specify.
3493+
:param `definition`: The macro definition (optional).
3494+
:type definition: ``string``
3495+
:param `kwargs`: Additional arguments (optional). Available parameters are:
3496+
'disabled', 'iseval', 'validation', and 'errormsg'.
3497+
:type kwargs: ``dict``
3498+
:return: The :class:`Macro`.
3499+
"""
3500+
# Updates to a macro *require* that the definition be
3501+
# passed, so we pass the current definition if a value wasn't
3502+
# provided by the caller.
3503+
if definition is None: definition = self.content.definition
3504+
Entity.update(self, definition=definition, **kwargs)
3505+
return self
3506+
3507+
@property
3508+
def validation(self):
3509+
"""Returns the validation expression for the macro.
3510+
:return: The validation expression for the macro.
3511+
:rtype: ``string``
3512+
"""
3513+
return self._state.content.get('validation', '')
3514+
3515+
3516+
class Macros(Collection):
3517+
"""This class represents a collection of macros. Retrieve this
3518+
collection using :meth:`Service.macros`."""
3519+
def __init__(self, service):
3520+
Collection.__init__(
3521+
self, service, PATH_MACROS, item=Macro)
3522+
3523+
def create(self, name, definition, **kwargs):
3524+
""" Creates a macro.
3525+
:param name: The name for the macro.
3526+
:type name: ``string``
3527+
:param definition: The macro definition.
3528+
:type definition: ``string``
3529+
:param kwargs: Additional arguments (optional). Available parameters are:
3530+
'disabled', 'iseval', 'validation', and 'errormsg'.
3531+
:type kwargs: ``dict``
3532+
:return: The :class:`Macros` collection.
3533+
"""
3534+
return Collection.create(self, name, definition=definition, **kwargs)
3535+
3536+
34433537
class Settings(Entity):
34443538
"""This class represents configuration settings for a Splunk service.
34453539
Retrieve this collection using :meth:`Service.settings`."""
@@ -3905,4 +3999,4 @@ def batch_save(self, *documents):
39053999
data = json.dumps(documents)
39064000

39074001
return json.loads(
3908-
self._post('batch_save', headers=KVStoreCollectionData.JSON_HEADER, body=data).body.read().decode('utf-8'))
4002+
self._post('batch_save', headers=KVStoreCollectionData.JSON_HEADER, body=data).body.read().decode('utf-8'))

splunklib/modularinput/event_writer.py

+20
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# under the License.
1414

1515
import sys
16+
import traceback
1617

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

70+
def log_exception(self, message, exception=None, severity=None):
71+
"""Logs messages about the exception thrown by this modular input to Splunk.
72+
These messages will show up in Splunk's internal logs.
73+
74+
:param message: ``string``, message to log.
75+
:param exception: ``Exception``, exception thrown by this modular input; if none, sys.exc_info() is used
76+
:param severity: ``string``, severity of message, see severities defined as class constants. Default severity: ERROR
77+
"""
78+
if exception is not None:
79+
tb_str = traceback.format_exception(type(exception), exception, exception.__traceback__)
80+
else:
81+
tb_str = traceback.format_exc()
82+
83+
if severity is None:
84+
severity = EventWriter.ERROR
85+
86+
self._err.write(("%s %s - %s" % (severity, message, tb_str)).replace("\n", " "))
87+
self._err.flush()
88+
6989
def write_xml_document(self, document):
7090
"""Writes a string representation of an
7191
``ElementTree`` object to the output stream.

splunklib/modularinput/script.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,12 @@ def run_script(self, args, event_writer, input_stream):
9191
event_writer.write_xml_document(root)
9292

9393
return 1
94-
err_string = "ERROR Invalid arguments to modular input script:" + ' '.join(
95-
args)
96-
event_writer._err.write(err_string)
94+
event_writer.log(EventWriter.ERROR, "Invalid arguments to modular input script:" + ' '.join(
95+
args))
9796
return 1
9897

9998
except Exception as e:
100-
event_writer.log(EventWriter.ERROR, str(e))
99+
event_writer.log_exception(str(e))
101100
return 1
102101

103102
@property

tests/modularinput/test_event.py

+28
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
# under the License.
1616

1717

18+
import re
1819
import sys
20+
from io import StringIO
1921

2022
import pytest
2123

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

152154
assert xml_compare(expected_xml, found_xml)
155+
156+
157+
def test_log_exception():
158+
out, err = StringIO(), StringIO()
159+
ew = EventWriter(out, err)
160+
161+
exc = Exception("Something happened!")
162+
163+
try:
164+
raise exc
165+
except Exception:
166+
ew.log_exception("ex1")
167+
168+
assert out.getvalue() == ""
169+
170+
# Remove paths and line
171+
err = re.sub(r'File "[^"]+', 'File "...', err.getvalue())
172+
err = re.sub(r'line \d+', 'line 123', err)
173+
174+
# One line
175+
assert err == (
176+
'ERROR ex1 - Traceback (most recent call last): '
177+
' File "...", line 123, in test_log_exception '
178+
' raise exc '
179+
'Exception: Something happened! '
180+
)

tests/modularinput/test_script.py

+35
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import sys
22

33
import io
4+
import re
45
import xml.etree.ElementTree as ET
56
from splunklib.client import Service
67
from splunklib.modularinput import Script, EventWriter, Scheme, Argument, Event
@@ -228,3 +229,37 @@ def stream_events(self, inputs, ew):
228229
assert output.err == ""
229230
assert isinstance(script.service, Service)
230231
assert script.service.authority == script.authority_uri
232+
233+
234+
def test_log_script_exception(monkeypatch):
235+
out, err = io.StringIO(), io.StringIO()
236+
237+
# Override abstract methods
238+
class NewScript(Script):
239+
def get_scheme(self):
240+
return None
241+
242+
def stream_events(self, inputs, ew):
243+
raise RuntimeError("Some error")
244+
245+
script = NewScript()
246+
input_configuration = data_open("data/conf_with_2_inputs.xml")
247+
248+
ew = EventWriter(out, err)
249+
250+
assert script.run_script([TEST_SCRIPT_PATH], ew, input_configuration) == 1
251+
252+
# Remove paths and line numbers
253+
err = re.sub(r'File "[^"]+', 'File "...', err.getvalue())
254+
err = re.sub(r'line \d+', 'line 123', err)
255+
256+
assert out.getvalue() == ""
257+
assert err == (
258+
'ERROR Some error - '
259+
'Traceback (most recent call last): '
260+
' File "...", line 123, in run_script '
261+
' self.stream_events(self._input_definition, event_writer) '
262+
' File "...", line 123, in stream_events '
263+
' raise RuntimeError("Some error") '
264+
'RuntimeError: Some error '
265+
)

0 commit comments

Comments
 (0)