Skip to content

Commit 1a3e290

Browse files
authored
Merge pull request #595 from splunk/release/2.1.0
Release/2.1.0
2 parents c6f7d32 + d36db5e commit 1a3e290

File tree

13 files changed

+374
-127
lines changed

13 files changed

+374
-127
lines changed

.github/workflows/test.yml

+4-4
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:
@@ -11,7 +11,7 @@ jobs:
1111
matrix:
1212
os:
1313
- ubuntu-latest
14-
python: [ 3.7, 3.9]
14+
python: [ 3.7, 3.9, 3.13]
1515
splunk-version:
1616
- "8.1"
1717
- "8.2"
@@ -22,8 +22,8 @@ jobs:
2222
- name: Checkout code
2323
uses: actions/checkout@v3
2424

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

2828
- name: Setup Python
2929
uses: actions/setup-python@v4

CHANGELOG.md

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

3+
## Version 2.1.0
4+
5+
### Changes
6+
* [#516](https://github.com/splunk/splunk-sdk-python/pull/516) Added support for macros
7+
* Remove deprecated `wrap_socket` in `Contex` class.
8+
* Added explicit support for self signed certificates in https
9+
* Enforce minimal required tls version in https connection
10+
* Add support for python 3.13
11+
* [#559](https://github.com/splunk/splunk-sdk-python/pull/559/) Add exception logging
12+
313
## Version 2.0.2
414

515
### Minor changes

README.md

+3-3
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.1.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

@@ -25,9 +25,9 @@ The Splunk Enterprise SDK for Python contains library code, and its examples are
2525

2626
Here's what you need to get going with the Splunk Enterprise SDK for Python.
2727

28-
* Python 3.7 or Python 3.9
28+
* Python 3.7, Python 3.9 and Python 3.13
2929

30-
The Splunk Enterprise SDK for Python is compatible with python3 and has been tested with Python v3.7 and v3.9.
30+
The Splunk Enterprise SDK for Python is compatible with python3 and has been tested with Python v3.7, v3.9 and v3.13.
3131

3232
* Splunk Enterprise 9.2 or 8.2
3333

setup.py

+1-110
Original file line numberDiff line numberDiff line change
@@ -14,124 +14,15 @@
1414
# License for the specific language governing permissions and limitations
1515
# under the License.
1616

17-
from setuptools import setup, Command
18-
19-
import os
20-
import sys
17+
from setuptools import setup
2118

2219
import splunklib
2320

24-
failed = False
25-
26-
def run_test_suite():
27-
import unittest
28-
29-
def mark_failed():
30-
global failed
31-
failed = True
32-
33-
class _TrackingTextTestResult(unittest._TextTestResult):
34-
def addError(self, test, err):
35-
unittest._TextTestResult.addError(self, test, err)
36-
mark_failed()
37-
38-
def addFailure(self, test, err):
39-
unittest._TextTestResult.addFailure(self, test, err)
40-
mark_failed()
41-
42-
class TrackingTextTestRunner(unittest.TextTestRunner):
43-
def _makeResult(self):
44-
return _TrackingTextTestResult(
45-
self.stream, self.descriptions, self.verbosity)
46-
47-
original_cwd = os.path.abspath(os.getcwd())
48-
os.chdir('tests')
49-
suite = unittest.defaultTestLoader.discover('.')
50-
runner = TrackingTextTestRunner(verbosity=2)
51-
runner.run(suite)
52-
os.chdir(original_cwd)
53-
54-
return failed
55-
56-
57-
def run_test_suite_with_junit_output():
58-
try:
59-
import unittest2 as unittest
60-
except ImportError:
61-
import unittest
62-
import xmlrunner
63-
original_cwd = os.path.abspath(os.getcwd())
64-
os.chdir('tests')
65-
suite = unittest.defaultTestLoader.discover('.')
66-
xmlrunner.XMLTestRunner(output='../test-reports').run(suite)
67-
os.chdir(original_cwd)
68-
69-
70-
class CoverageCommand(Command):
71-
"""setup.py command to run code coverage of the test suite."""
72-
description = "Create an HTML coverage report from running the full test suite."
73-
user_options = []
74-
75-
def initialize_options(self):
76-
pass
77-
78-
def finalize_options(self):
79-
pass
80-
81-
def run(self):
82-
try:
83-
import coverage
84-
except ImportError:
85-
print("Could not import coverage. Please install it and try again.")
86-
exit(1)
87-
cov = coverage.coverage(source=['splunklib'])
88-
cov.start()
89-
run_test_suite()
90-
cov.stop()
91-
cov.html_report(directory='coverage_report')
92-
93-
94-
class TestCommand(Command):
95-
"""setup.py command to run the whole test suite."""
96-
description = "Run test full test suite."
97-
user_options = []
98-
99-
def initialize_options(self):
100-
pass
101-
102-
def finalize_options(self):
103-
pass
104-
105-
def run(self):
106-
failed = run_test_suite()
107-
if failed:
108-
sys.exit(1)
109-
110-
111-
class JunitXmlTestCommand(Command):
112-
"""setup.py command to run the whole test suite."""
113-
description = "Run test full test suite with JUnit-formatted output."
114-
user_options = []
115-
116-
def initialize_options(self):
117-
pass
118-
119-
def finalize_options(self):
120-
pass
121-
122-
def run(self):
123-
run_test_suite_with_junit_output()
124-
125-
12621
setup(
12722
author="Splunk, Inc.",
12823

12924
author_email="[email protected]",
13025

131-
cmdclass={'coverage': CoverageCommand,
132-
'test': TestCommand,
133-
'testjunit': JunitXmlTestCommand},
134-
13526
description="The Splunk Software Development Kit for Python.",
13627

13728
license="http://www.apache.org/licenses/LICENSE-2.0",

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, 1, 0)
3434
__version__ = ".".join(map(str, __version_info__))

splunklib/binding.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,8 @@ class Context:
465465
:type scheme: "https" or "http"
466466
:param verify: Enable (True) or disable (False) SSL verification for https connections.
467467
:type verify: ``Boolean``
468+
:param self_signed_certificate: Specifies if self signed certificate is used
469+
:type self_signed_certificate: ``Boolean``
468470
:param sharing: The sharing mode for the namespace (the default is "user").
469471
:type sharing: "global", "system", "app", or "user"
470472
:param owner: The owner context of the namespace (optional, the default is "None").
@@ -526,6 +528,7 @@ def __init__(self, handler=None, **kwargs):
526528
self.bearerToken = kwargs.get("splunkToken", "")
527529
self.autologin = kwargs.get("autologin", False)
528530
self.additional_headers = kwargs.get("headers", [])
531+
self._self_signed_certificate = kwargs.get("self_signed_certificate", True)
529532

530533
# Store any cookies in the self.http._cookies dict
531534
if "cookie" in kwargs and kwargs['cookie'] not in [None, _NoAuthenticationToken]:
@@ -604,7 +607,11 @@ def connect(self):
604607
"""
605608
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
606609
if self.scheme == "https":
607-
sock = ssl.wrap_socket(sock)
610+
context = ssl.create_default_context()
611+
context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
612+
context.check_hostname = not self._self_signed_certificate
613+
context.verify_mode = ssl.CERT_NONE if self._self_signed_certificate else ssl.CERT_REQUIRED
614+
sock = context.wrap_socket(sock, server_hostname=self.host)
608615
sock.connect((socket.gethostbyname(self.host), self.port))
609616
return sock
610617

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.

0 commit comments

Comments
 (0)