Skip to content

Commit aededac

Browse files
committed
Merge branch 'feature/caching' into develop
2 parents 4bcb4c7 + b89e9e1 commit aededac

29 files changed

+649
-1195
lines changed

CHANGELOG.md

Lines changed: 92 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,106 @@
44

55
### Features
66

7-
* Added support for IPv6, can now connect to a splunkd listening on an IPv6
8-
address.
7+
* Support for IPv6, can now connect to a splunkd listening on an IPv6 address.
8+
* Improvements to entity state management
9+
* Improvements to usability of entity collections
910
* Improvements to unit tests
1011

1112
### Breaking changes
1213

13-
* Renamed the core module from `splunk` to `splunklib`. The Splunk product
14-
already ships with an internal Python module named `splunk` and the name
15-
conflict with the SDK prevented installing the SDK into Splunk Python sandbox
16-
for use by Splunk extensions.
14+
#### Module name
1715

18-
* Update all classes in the core library modules to use new-style classes
19-
* Rename Job.setpriority to Job.set_priority
20-
* Rename Job.setttl to Job.set_ttl
16+
The core module was renamed from `splunk` to `splunklib`. The Splunk product
17+
ships with an internal Python module named `splunk` and the name conflict
18+
with the SDK prevented installing the SDK into Splunk Python sandbox for use
19+
by Splunk extensions. This module name change enables the Python SDK to be
20+
installed on the Splunk server.
21+
22+
#### State caching
23+
24+
The client module was modified to enable Entity state caching which required
25+
changes to the `Entity` interface and changes to the typical usage pattern.
26+
27+
Previously, entity state values where retrieved with a call to `Entity.read`
28+
which would issue a round-trip to the server and return a dictionary of values
29+
corresponding to the entity `content` field and, in a similar way, a call to
30+
`Entity.readmeta` would issue in a round-trip and return a dictionary
31+
contianing entity metadata values.
32+
33+
With the change to enable state caching, the entity is instantiated with a
34+
copy of its entire state record, which can be accessed using a variety of
35+
properties:
36+
37+
* `Entity.state` returns the entire state record
38+
* `Entity.content` returns the content field of the state record
39+
* `Entity.metadata` returns the metadata field of the state record
40+
41+
`Entity.refresh` is a new method that issues a round-trip to the server
42+
and updates the local, cached state record.
43+
44+
`Entity.read` still exists but has been changed slightly to return the
45+
entire state record and not just the content field. Note that `read` does
46+
not update the cached state record. The `read` method is basically a thin
47+
wrapper over the corresponding HTTP GET that returns a parsed entity state
48+
record instaed of the raw HTTP response.
49+
50+
The entity _callable_ returns the `content` field as before, but now returns
51+
the value from the local state cache instead of issuing a round-trip as it
52+
did before.
53+
54+
It is important to note that refreshing the local state cache is always
55+
explicit and always requires a call to `Entity.refresh`. So, for example
56+
if you call `Entity.update` and then attempt to retrieve local values, you
57+
will not see the newly updated values, you will see the previously cached
58+
values. The interface is designed to give the caller complete control of
59+
when round-trips are issued and enable multiple updates to be made before
60+
refreshing the entity.
61+
62+
The `update` and action methods are all designed to support a _fluent_ style
63+
of programming, so for example you can write:
64+
65+
entity.update(attr=value).refresh()
2166

22-
* Naming context - previously the binding context (`binding.Context`) and all
23-
tests & samples took a single (optional) `namespace` argument that specified
24-
both the app and owner names to use for the binding context. However, the
25-
underlying Splunk REST API takes these as separate `app` and `owner` arguments
26-
and it turned out to be more convenient to reflect these arguments directly
27-
in the SDK, so the binding context (and all samples & test) now take separate
28-
(and optional) `app` and `owner` arguments instead of the prior `namespace`
29-
argument.
67+
And
3068

31-
You can find a detailed description of Splunk namespaces in the Splunk REST
32-
API reference under the section on accessing Splunk resources at:
69+
entity.disable().refresh()
70+
71+
An important benefit and one of the primary motivations for this change is
72+
that iterating a collection of entities now results in a single round-trip
73+
to the server, because every entity collection member is initialized with
74+
the result of the initial GET on the collection resource instead of requiring
75+
N+1 round-trips (one for each entity + one for the collection), which was the
76+
case in the previous model. This is a significant improvement for many
77+
common scenarios.
3378

34-
* http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTresources
79+
#### Collections
80+
81+
The `Collection` interface was changed so that `Collection.list` and the
82+
corresponding collection callable return a list of member `Entity` objects
83+
instead of a list of member entity names. This change was a result of user
84+
feedback indicating that people expected to see eg: `service.apps()` return
85+
a list of apps and not a list of app names.
86+
87+
#### Naming context
88+
89+
Previously the binding context (`binding.Context`) and all tests & samples took
90+
a single (optional) `namespace` argument that specified both the app and owner
91+
names to use for the binding context. However, the underlying Splunk REST API
92+
takes these as separate `app` and `owner` arguments and it turned out to be more
93+
convenient to reflect these arguments directly in the SDK, so the binding
94+
context (and all samples & test) now take separate (and optional) `app` and
95+
`owner` arguments instead of the prior `namespace` argument.
96+
97+
You can find a detailed description of Splunk namespaces in the Splunk REST
98+
API reference under the section on accessing Splunk resources at:
99+
100+
* http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTresources
101+
102+
#### Misc. API
103+
104+
* Update all classes in the core library modules to use new-style classes
105+
* Rename Job.setpriority to Job.set_priority
106+
* Rename Job.setttl to Job.set_ttl
35107

36108
### Bug fixes
37109

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,13 @@ The second layer is referred to as the _client_ layer and builds on the
140140
_binding_ layer to provide a friendlier interface to Splunk that abstracts
141141
away some of the lower level details of the _binding_ layer.
142142

143-
from pprint import pprint
144-
145143
import splunk.client as client
146144

147145
# host defaults to localhost and port defaults to 8089
148146
service = client.connect(username="admin", password="changeme")
149147

150148
for user in service.users:
151-
pprint(user())
149+
print user.name
152150

153151
### Unit tests
154152

examples/analytics/input.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,17 @@ def __init__(self, application_name, splunk_info, index = ANALYTICS_INDEX_NAME):
3737
self.splunk = client.connect(**splunk_info)
3838
self.index = index
3939

40-
if self.index not in self.splunk.indexes.list():
40+
if not self.splunk.indexes.contains(self.index):
4141
self.splunk.indexes.create(self.index)
42+
assert(self.splunk.indexes.contains(self.index))
4243

43-
assert(self.index in self.splunk.indexes.list())
44-
45-
if ANALYTICS_SOURCETYPE not in self.splunk.confs["props"].list():
44+
if not self.splunk.confs['props'].contains(ANALYTICS_SOURCETYPE):
4645
self.splunk.confs["props"].create(ANALYTICS_SOURCETYPE)
4746
stanza = self.splunk.confs["props"][ANALYTICS_SOURCETYPE]
4847
stanza.submit("LINE_BREAKER = (%s)" % EVENT_TERMINATOR)
4948
stanza.submit("CHARSET = UTF-8")
5049
stanza.submit("SHOULD_LINEMERGE = false")
50+
assert(self.splunk.confs['props'].contains(ANALYTICS_SOURCETYPE))
5151

5252
@staticmethod
5353
def encode(props):

examples/conf.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,7 @@ def list(self, opts):
111111
for stanza in conf:
112112
if (spres and argv[1] == stanza.name) or not spres:
113113
print "[%s]" % stanza.name
114-
entity = stanza.read()
115-
for key, value in entity.iteritems():
114+
for key, value in stanza.content.iteritems():
116115
if (kpres and argv[2] == key) or not kpres:
117116
print "%s = %s" % (key, value)
118117
print

examples/dashboard/feed.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,6 @@ def iterate(job):
161161
return (created_job, lambda job: iterate(job))
162162

163163
def main(argv):
164-
global urllib2
165-
usage = "async.py <sync | async>"
166-
167164
# Parse the command line args.
168165
opts = parse(argv, {}, ".splunkrc")
169166

examples/follow.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def follow(job, count, items):
3333
total = count()
3434
if total <= offset:
3535
time.sleep(1) # Wait for something to show up
36+
job.refresh()
3637
continue
3738
stream = items(offset+1)
3839
reader = results.ResultsReader(stream)
@@ -63,13 +64,12 @@ def main():
6364
# Wait for the job to transition out of QUEUED and PARSING so that
6465
# we can if its a transforming search, or not.
6566
while True:
66-
entity = job.read()
67-
state = entity['dispatchState']
68-
if state not in ['QUEUED', 'PARSING']:
67+
job.refresh()
68+
if job['dispatchState'] not in ['QUEUED', 'PARSING']:
6969
break
7070
time.sleep(2) # Wait
7171

72-
if entity['reportSearch'] is not None: # Is it a transforming search?
72+
if job['reportSearch'] is not None: # Is it a transforming search?
7373
count = lambda: int(job['numPreviews'])
7474
items = lambda _: job.preview()
7575
else:

examples/handlers/handler_certs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,5 @@ def request(url, message, **kwargs):
110110
opts = utils.parse(sys.argv[1:], RULES, ".splunkrc")
111111
ca_file = opts.kwargs['ca_file']
112112
service = client.connect(handler=handler(ca_file), **opts.kwargs)
113-
pprint(service.apps.list())
113+
pprint([app.name for app in service.apps])
114114

examples/handlers/handler_debug.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,4 @@ def request(url, message, **kwargs):
3838

3939
opts = utils.parse(sys.argv[1:], {}, ".splunkrc")
4040
service = client.connect(handler=handler(), **opts.kwargs)
41-
pprint(service.apps.list())
42-
41+
pprint([app.name for app in service.apps])

examples/handlers/handler_proxy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def handler(proxy):
6969
proxy = opts.kwargs['proxy']
7070
try:
7171
service = client.connect(handler=handler(proxy), **opts.kwargs)
72-
pprint(service.apps.list())
72+
pprint([app.name for app in service.apps])
7373
except urllib2.URLError as e:
7474
if e.reason.errno == 1 and sys.version_info < (2, 6, 3):
7575
# There is a bug in Python < 2.6.3 that does not allow proxies with

examples/handlers/handler_urllib2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,5 @@ def request(url, message, **kwargs):
4343

4444
opts = utils.parse(sys.argv[1:], {}, ".splunkrc")
4545
service = client.connect(handler=request, **opts.kwargs)
46-
pprint(service.apps.list())
46+
pprint([app.name for app in service.apps])
4747

0 commit comments

Comments
 (0)