Skip to content

Commit a205cbc

Browse files
committed
Merge branch 'develop' into release/1.7.3
2 parents 869de77 + de265fd commit a205cbc

File tree

7 files changed

+85
-32
lines changed

7 files changed

+85
-32
lines changed

.env

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ scheme=https
1111
# Your version of Splunk (default: 6.2)
1212
version=9.0
1313
# Bearer token for authentication
14-
#bearerToken="<Bearer-token>"
14+
#splunkToken="<Bearer-token>"
1515
# Session key for authentication
16-
#sessionKey="<Session-Key>"
16+
#token="<Session-Key>"

README.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ here is an example of .env file:
111111
# Your version of Splunk Enterprise
112112
version=9.0
113113
# Bearer token for authentication
114-
#bearerToken=<Bearer-token>
114+
#splunkToken=<Bearer-token>
115115
# Session key for authentication
116-
#sessionKey=<Session-Key>
116+
#token=<Session-Key>
117117

118118
#### SDK examples
119119

@@ -209,7 +209,39 @@ class GeneratorTest(GeneratingCommand):
209209
checkpoint_dir = inputs.metadata["checkpoint_dir"]
210210
```
211211

212-
#### Optional:Set up logging for splunklib
212+
### Access service object in Custom Search Command & Modular Input apps
213+
214+
#### Custom Search Commands
215+
* The service object is created from the Splunkd URI and session key passed to the command invocation the search results info file.
216+
* Service object can be accessed using `self.service` in `generate`/`transform`/`stream`/`reduce` methods depending on the Custom Search Command.
217+
* For Generating Custom Search Command
218+
```python
219+
def generate(self):
220+
# other code
221+
222+
# access service object that can be used to connect Splunk Service
223+
service = self.service
224+
# to get Splunk Service Info
225+
info = service.info
226+
```
227+
228+
229+
230+
#### Modular Inputs app:
231+
* The service object is created from the Splunkd URI and session key passed to the command invocation on the modular input stream respectively.
232+
* It is available as soon as the `Script.stream_events` method is called.
233+
```python
234+
def stream_events(self, inputs, ew):
235+
# other code
236+
237+
# access service object that can be used to connect Splunk Service
238+
service = self.service
239+
# to get Splunk Service Info
240+
info = service.info
241+
```
242+
243+
244+
### Optional:Set up logging for splunklib
213245
+ The default level is WARNING, which means that only events of this level and above will be visible
214246
+ To change a logging level we can call setup_logging() method and pass the logging level as an argument.
215247
+ Optional: we can also pass log format and date format string as a method argument to modify default format

scripts/templates/env.template

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ scheme=$scheme
1111
# Your version of Splunk (default: 6.2)
1212
version=$version
1313
# Bearer token for authentication
14-
#bearerToken=<Bearer-token>
14+
#splunkToken=<Bearer-token>
1515
# Session key for authentication
16-
#sessionKey=<Session-Key>
16+
#token=<Session-Key>

splunklib/binding.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,8 @@ def _authority(scheme=DEFAULT_SCHEME, host=DEFAULT_HOST, port=DEFAULT_PORT):
347347
"http://splunk.utopia.net:471"
348348
349349
"""
350-
if ':' in host:
350+
# check if host is an IPv6 address and not enclosed in [ ]
351+
if ':' in host and not (host.startswith('[') and host.endswith(']')):
351352
# IPv6 addresses must be enclosed in [ ] in order to be well
352353
# formed.
353354
host = '[' + host + ']'

splunklib/client.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3709,24 +3709,31 @@ class KVStoreCollections(Collection):
37093709
def __init__(self, service):
37103710
Collection.__init__(self, service, 'storage/collections/config', item=KVStoreCollection)
37113711

3712-
def create(self, name, indexes = {}, fields = {}, **kwargs):
3712+
def __getitem__(self, item):
3713+
res = Collection.__getitem__(self, item)
3714+
for k, v in res.content.items():
3715+
if "accelerated_fields" in k:
3716+
res.content[k] = json.loads(v)
3717+
return res
3718+
3719+
def create(self, name, accelerated_fields={}, fields={}, **kwargs):
37133720
"""Creates a KV Store Collection.
37143721
37153722
:param name: name of collection to create
37163723
:type name: ``string``
3717-
:param indexes: dictionary of index definitions
3718-
:type indexes: ``dict``
3724+
:param accelerated_fields: dictionary of accelerated_fields definitions
3725+
:type accelerated_fields: ``dict``
37193726
:param fields: dictionary of field definitions
37203727
:type fields: ``dict``
37213728
:param kwargs: a dictionary of additional parameters specifying indexes and field definitions
37223729
:type kwargs: ``dict``
37233730
37243731
:return: Result of POST request
37253732
"""
3726-
for k, v in six.iteritems(indexes):
3733+
for k, v in six.iteritems(accelerated_fields):
37273734
if isinstance(v, dict):
37283735
v = json.dumps(v)
3729-
kwargs['index.' + k] = v
3736+
kwargs['accelerated_fields.' + k] = v
37303737
for k, v in six.iteritems(fields):
37313738
kwargs['field.' + k] = v
37323739
return self.post(name=name, **kwargs)
@@ -3740,18 +3747,20 @@ def data(self):
37403747
"""
37413748
return KVStoreCollectionData(self)
37423749

3743-
def update_index(self, name, value):
3744-
"""Changes the definition of a KV Store index.
3750+
def update_accelerated_field(self, name, value):
3751+
"""Changes the definition of a KV Store accelerated_field.
37453752
3746-
:param name: name of index to change
3753+
:param name: name of accelerated_fields to change
37473754
:type name: ``string``
3748-
:param value: new index definition
3749-
:type value: ``dict`` or ``string``
3755+
:param value: new accelerated_fields definition
3756+
:type value: ``dict``
37503757
37513758
:return: Result of POST request
37523759
"""
37533760
kwargs = {}
3754-
kwargs['index.' + name] = value if isinstance(value, six.string_types) else json.dumps(value)
3761+
if isinstance(value, dict):
3762+
value = json.dumps(value)
3763+
kwargs['accelerated_fields.' + name] = value
37553764
return self.post(**kwargs)
37563765

37573766
def update_field(self, name, value):

tests/test_binding.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,12 @@ def test_ipv6_host(self):
190190
host="2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
191191
"https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8089")
192192

193+
def test_ipv6_host_enclosed(self):
194+
self.assertEqual(
195+
binding._authority(
196+
host="[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"),
197+
"https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8089")
198+
193199
def test_all_fields(self):
194200
self.assertEqual(
195201
binding._authority(

tests/test_kvstore_conf.py

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

1717
from __future__ import absolute_import
18+
19+
import json
1820
from tests import testlib
1921
try:
2022
import unittest
@@ -42,13 +44,27 @@ def test_create_delete_collection(self):
4244
self.confs['test'].delete()
4345
self.assertTrue(not 'test' in self.confs)
4446

47+
def test_create_fields(self):
48+
self.confs.create('test', accelerated_fields={'ind1':{'a':1}}, fields={'a':'number1'})
49+
self.assertEqual(self.confs['test']['field.a'], 'number1')
50+
self.assertEqual(self.confs['test']['accelerated_fields.ind1'], {"a": 1})
51+
self.confs['test'].delete()
52+
4553
def test_update_collection(self):
4654
self.confs.create('test')
47-
self.confs['test'].post(**{'accelerated_fields.ind1': '{"a": 1}', 'field.a': 'number'})
55+
val = {"a": 1}
56+
self.confs['test'].post(**{'accelerated_fields.ind1': json.dumps(val), 'field.a': 'number'})
4857
self.assertEqual(self.confs['test']['field.a'], 'number')
49-
self.assertEqual(self.confs['test']['accelerated_fields.ind1'], '{"a": 1}')
58+
self.assertEqual(self.confs['test']['accelerated_fields.ind1'], {"a": 1})
5059
self.confs['test'].delete()
5160

61+
def test_update_accelerated_fields(self):
62+
self.confs.create('test', accelerated_fields={'ind1':{'a':1}})
63+
self.assertEqual(self.confs['test']['accelerated_fields.ind1'], {'a': 1})
64+
# update accelerated_field value
65+
self.confs['test'].update_accelerated_field('ind1', {'a': -1})
66+
self.assertEqual(self.confs['test']['accelerated_fields.ind1'], {'a': -1})
67+
self.confs['test'].delete()
5268

5369
def test_update_fields(self):
5470
self.confs.create('test')
@@ -77,17 +93,6 @@ def test_overlapping_collections(self):
7793
self.confs['test'].delete()
7894
self.confs['test'].delete()
7995

80-
"""
81-
def test_create_accelerated_fields_fields(self):
82-
self.confs.create('test', indexes={'foo': '{"foo": 1}', 'bar': {'bar': -1}}, **{'field.foo': 'string'})
83-
self.assertEqual(self.confs['test']['accelerated_fields.foo'], '{"foo": 1}')
84-
self.assertEqual(self.confs['test']['field.foo'], 'string')
85-
self.assertRaises(client.HTTPError, lambda: self.confs['test'].post(**{'accelerated_fields.foo': 'THIS IS INVALID'}))
86-
self.assertEqual(self.confs['test']['accelerated_fields.foo'], '{"foo": 1}')
87-
self.confs['test'].update_accelerated_fields('foo', '')
88-
self.assertEqual(self.confs['test']['accelerated_fields.foo'], None)
89-
"""
90-
9196
def tearDown(self):
9297
if ('test' in self.confs):
9398
self.confs['test'].delete()

0 commit comments

Comments
 (0)