Skip to content

Commit 828fc1e

Browse files
committed
Merge commit '4a1d31a9c5bd70759c19bf6628951810eab82ce2' into fix-searchcommands_app-example
# Conflicts: # examples/searchcommands_app/setup.py
2 parents c6a6dbc + 4a1d31a commit 828fc1e

19 files changed

+363
-25
lines changed

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
# Splunk Enterprise SDK for Python Changelog
22

3+
## Version 1.6.17
4+
5+
### Bug fixes
6+
7+
* [#383](https://github.com/splunk/splunk-sdk-python/pull/383) Implemented the possibility to provide a SSLContext object to the connect method
8+
* [#396](https://github.com/splunk/splunk-sdk-python/pull/396) Updated KVStore Methods to support dictionaries
9+
* [#397](https://github.com/splunk/splunk-sdk-python/pull/397) Added code changes for encoding '/' in _key parameter in kvstore.data APIs.
10+
* [#398](https://github.com/splunk/splunk-sdk-python/pull/398) Added dictionary support for KVStore "query" methods.
11+
* [#402](https://github.com/splunk/splunk-sdk-python/pull/402) Fixed regression introduced in 1.6.15 to once again allow processing of empty input records in custom search commands (fix [#376](https://github.com/splunk/splunk-sdk-python/issues/376))
12+
* [#404](https://github.com/splunk/splunk-sdk-python/pull/404) Fixed test case failure for 8.0 and latest(8.2.x) splunk version
13+
14+
### Minor changes
15+
16+
* [#381](https://github.com/splunk/splunk-sdk-python/pull/381) Updated current year in conf.py
17+
* [#389](https://github.com/splunk/splunk-sdk-python/pull/389) Fixed few typos
18+
* [#391](https://github.com/splunk/splunk-sdk-python/pull/391) Fixed spelling error in client.py
19+
* [#393](https://github.com/splunk/splunk-sdk-python/pull/393) Updated development status past 3
20+
* [#394](https://github.com/splunk/splunk-sdk-python/pull/394) Updated Readme steps to run examples
21+
* [#395](https://github.com/splunk/splunk-sdk-python/pull/395) Updated random_number.py
22+
* [#399](https://github.com/splunk/splunk-sdk-python/pull/399) Moved CI tests to GitHub Actions
23+
* [#403](https://github.com/splunk/splunk-sdk-python/pull/403) Removed usage of Easy_install to install SDK
24+
325
## Version 1.6.16
426

527
### Bug fixes

README.md

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
# The Splunk Enterprise Software Development Kit for Python
55

6-
#### Version 1.6.16
6+
#### Version 1.6.17
77

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

@@ -112,8 +112,18 @@ Save the file as **.splunkrc** in the current user's home directory.
112112

113113
Examples are located in the **/splunk-sdk-python/examples** directory. To run the examples at the command line, use the Python interpreter and include any arguments that are required by the example. In the commands below, replace "examplename" with the name of the specific example in the directory that you want to run:
114114

115+
Using username and Password
116+
115117
python examplename.py --username="admin" --password="changeme"
116118

119+
Using Bearer token
120+
121+
python examplename.py --bearerToken=<value>
122+
123+
Using Session key
124+
125+
python examplename.py --sessionKey="<value>"
126+
117127
If you saved your login credentials in the **.splunkrc** file, you can omit those arguments:
118128

119129
python examplename.py
@@ -150,6 +160,53 @@ The test suite uses Python's standard library, the built-in `unittest` library,
150160
|/tests | Source for unit tests |
151161
|/utils | Source for utilities shared by the examples and unit tests |
152162

163+
### Customization
164+
* When working with custom search commands such as Custom Streaming Commands or Custom Generating Commands, We may need to add new fields to the records based on certain conditions.
165+
* Structural changes like this may not be preserved.
166+
* Make sure to use ``add_field(record, fieldname, value)`` method from SearchCommand to add a new field and value to the record.
167+
* ___Note:__ Usage of ``add_field`` method is completely optional, if you are not facing any issues with field retention._
168+
169+
Do
170+
```python
171+
class CustomStreamingCommand(StreamingCommand):
172+
def stream(self, records):
173+
for index, record in enumerate(records):
174+
if index % 1 == 0:
175+
self.add_field(record, "odd_record", "true")
176+
yield record
177+
```
178+
179+
Don't
180+
```python
181+
class CustomStreamingCommand(StreamingCommand):
182+
def stream(self, records):
183+
for index, record in enumerate(records):
184+
if index % 1 == 0:
185+
record["odd_record"] = "true"
186+
yield record
187+
```
188+
### Customization for Generating Custom Search Command
189+
* Generating Custom Search Command is used to generate events using SDK code.
190+
* Make sure to use ``gen_record()`` method from SearchCommand to add a new record and pass event data as a key=value pair separated by , (mentioned in below example).
191+
192+
Do
193+
```python
194+
@Configuration()
195+
class GeneratorTest(GeneratingCommand):
196+
def generate(self):
197+
yield self.gen_record(_time=time.time(), one=1)
198+
yield self.gen_record(_time=time.time(), two=2)
199+
```
200+
201+
Don't
202+
```python
203+
@Configuration()
204+
class GeneratorTest(GeneratingCommand):
205+
def generate(self):
206+
yield {'_time': time.time(), 'one': 1}
207+
yield {'_time': time.time(), 'two': 2}
208+
```
209+
153210
### Changelog
154211

155212
The [CHANGELOG](CHANGELOG.md) contains a description of changes for each version of the SDK. For the latest version, see the [CHANGELOG.md](https://github.com/splunk/splunk-sdk-python/blob/master/CHANGELOG.md) on GitHub.

docs/searchcommands.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ splunklib.searchcommands
33

44
.. automodule:: splunklib.searchcommands
55

6-
.. autofunction:: dispatch(command_class[, argv=sys.argv, input_file=sys.stdin, output_file=sys.stdout, module_name=None])
6+
.. autofunction:: dispatch(command_class[, argv=sys.argv, input_file=sys.stdin, output_file=sys.stdout, module_name=None, allow_empty_input=True])
77

88
.. autoclass:: EventingCommand
99
:members:
@@ -31,7 +31,7 @@ splunklib.searchcommands
3131

3232
.. automethod:: splunklib.searchcommands::GeneratingCommand.generate
3333

34-
.. automethod:: splunklib.searchcommands::GeneratingCommand.process(args=sys.argv[, input_file=sys.stdin, output_file=sys.stdout])
34+
.. automethod:: splunklib.searchcommands::GeneratingCommand.process(args=sys.argv[, input_file=sys.stdin, output_file=sys.stdout, allow_empty_input=True])
3535

3636
.. autoclass:: ReportingCommand
3737
:members:
@@ -59,7 +59,7 @@ splunklib.searchcommands
5959
:inherited-members:
6060
:exclude-members: configuration_settings, fix_up, items, keys
6161

62-
.. automethod:: splunklib.searchcommands::StreamingCommand.process(args=sys.argv[, input_file=sys.stdin, output_file=sys.stdout])
62+
.. automethod:: splunklib.searchcommands::StreamingCommand.process(args=sys.argv[, input_file=sys.stdin, output_file=sys.stdout, allow_empty_input=True])
6363

6464
.. automethod:: splunklib.searchcommands::StreamingCommand.stream
6565

scripts/templates/splunkrc.template

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ password=$password
1010
scheme=$scheme
1111
# Your version of Splunk (default: 6.2)
1212
version=$version
13+
# Bearer token for authentication
14+
#bearerToken=<Bearer-token>
15+
# Session key for authentication
16+
#sessionKey=<Session-Key>

splunklib/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@
1616

1717
from __future__ import absolute_import
1818
from splunklib.six.moves import map
19-
__version_info__ = (1, 6, 16)
19+
__version_info__ = (1, 6, 17)
2020
__version__ = ".".join(map(str, __version_info__))

splunklib/binding.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import logging
3131
import socket
3232
import ssl
33-
import sys
3433
from base64 import b64encode
3534
from contextlib import contextmanager
3635
from datetime import datetime
@@ -39,7 +38,6 @@
3938
from xml.etree.ElementTree import XML
4039

4140
from splunklib import six
42-
from splunklib.six import StringIO
4341
from splunklib.six.moves import urllib
4442

4543
from .data import record
@@ -1391,7 +1389,7 @@ def request(url, message, **kwargs):
13911389
head = {
13921390
"Content-Length": str(len(body)),
13931391
"Host": host,
1394-
"User-Agent": "splunk-sdk-python/1.6.16",
1392+
"User-Agent": "splunk-sdk-python/1.6.17",
13951393
"Accept": "*/*",
13961394
"Connection": "Close",
13971395
} # defaults

splunklib/client.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ class Service(_BaseService):
403403
def __init__(self, **kwargs):
404404
super(Service, self).__init__(**kwargs)
405405
self._splunk_version = None
406+
self._kvstore_owner = None
406407

407408
@property
408409
def apps(self):
@@ -675,12 +676,34 @@ def splunk_version(self):
675676
self._splunk_version = tuple([int(p) for p in self.info['version'].split('.')])
676677
return self._splunk_version
677678

679+
@property
680+
def kvstore_owner(self):
681+
"""Returns the KVStore owner for this instance of Splunk.
682+
683+
By default is the kvstore owner is not set, it will return "nobody"
684+
:return: A string with the KVStore owner.
685+
"""
686+
if self._kvstore_owner is None:
687+
self._kvstore_owner = "nobody"
688+
return self._kvstore_owner
689+
690+
@kvstore_owner.setter
691+
def kvstore_owner(self, value):
692+
"""
693+
kvstore is refreshed, when the owner value is changed
694+
"""
695+
self._kvstore_owner = value
696+
self.kvstore
697+
678698
@property
679699
def kvstore(self):
680700
"""Returns the collection of KV Store collections.
681701
702+
sets the owner for the namespace, before retrieving the KVStore Collection
703+
682704
:return: A :class:`KVStoreCollections` collection of :class:`KVStoreCollection` entities.
683705
"""
706+
self.namespace['owner'] = self.kvstore_owner
684707
return KVStoreCollections(self)
685708

686709
@property

splunklib/modularinput/event_writer.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from __future__ import absolute_import
1616
import sys
1717

18-
from io import TextIOWrapper, TextIOBase
1918
from splunklib.six import ensure_str
2019
from .event import ET
2120

splunklib/searchcommands/generating_command.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# under the License.
1616

1717
from __future__ import absolute_import, division, print_function, unicode_literals
18+
import sys
1819

1920
from .decorators import ConfigurationSetting
2021
from .search_command import SearchCommand
@@ -212,13 +213,49 @@ def _execute(self, ifile, process):
212213

213214
def _execute_chunk_v2(self, process, chunk):
214215
count = 0
216+
records = []
215217
for row in process:
216-
self._record_writer.write_record(row)
218+
records.append(row)
217219
count += 1
218220
if count == self._record_writer._maxresultrows:
219-
self._finished = False
220-
return
221-
self._finished = True
221+
break
222+
223+
for row in records:
224+
self._record_writer.write_record(row)
225+
226+
if count == self._record_writer._maxresultrows:
227+
self._finished = False
228+
else:
229+
self._finished = True
230+
231+
def process(self, argv=sys.argv, ifile=sys.stdin, ofile=sys.stdout, allow_empty_input=True):
232+
""" Process data.
233+
234+
:param argv: Command line arguments.
235+
:type argv: list or tuple
236+
237+
:param ifile: Input data file.
238+
:type ifile: file
239+
240+
:param ofile: Output data file.
241+
:type ofile: file
242+
243+
:param allow_empty_input: For generating commands, it must be true. Doing otherwise will cause an error.
244+
:type allow_empty_input: bool
245+
246+
:return: :const:`None`
247+
:rtype: NoneType
248+
249+
"""
250+
251+
# Generating commands are expected to run on an empty set of inputs as the first command being run in a search,
252+
# also this class implements its own separate _execute_chunk_v2 method which does not respect allow_empty_input
253+
# so ensure that allow_empty_input is always True
254+
255+
if not allow_empty_input:
256+
raise ValueError("allow_empty_input cannot be False for Generating Commands")
257+
else:
258+
return super(GeneratingCommand, self).process(argv=argv, ifile=ifile, ofile=ofile, allow_empty_input=True)
222259

223260
# endregion
224261

splunklib/searchcommands/internals.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@ def __init__(self, ofile, maxresultrows=None):
508508
self._chunk_count = 0
509509
self._pending_record_count = 0
510510
self._committed_record_count = 0
511+
self.custom_fields = set()
511512

512513
@property
513514
def is_flushed(self):
@@ -572,6 +573,7 @@ def write_record(self, record):
572573

573574
def write_records(self, records):
574575
self._ensure_validity()
576+
records = list(records)
575577
write_record = self._write_record
576578
for record in records:
577579
write_record(record)
@@ -593,6 +595,7 @@ def _write_record(self, record):
593595

594596
if fieldnames is None:
595597
self._fieldnames = fieldnames = list(record.keys())
598+
self._fieldnames.extend([i for i in self.custom_fields if i not in self._fieldnames])
596599
value_list = imap(lambda fn: (str(fn), str('__mv_') + str(fn)), fieldnames)
597600
self._writerow(list(chain.from_iterable(value_list)))
598601

0 commit comments

Comments
 (0)