splunk-sdk-ruby 0.1.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/CHANGELOG.md +28 -0
- data/README.md +5 -17
- data/examples/6_work_with_modular_inputs.rb +96 -0
- data/examples/example_modular_inputs.spl +0 -0
- data/lib/splunk-sdk-ruby/atomfeed.rb +1 -1
- data/lib/splunk-sdk-ruby/collection.rb +112 -109
- data/lib/splunk-sdk-ruby/collection/input_kinds.rb +136 -0
- data/lib/splunk-sdk-ruby/collection/jobs.rb +9 -1
- data/lib/splunk-sdk-ruby/context.rb +7 -4
- data/lib/splunk-sdk-ruby/entity.rb +37 -29
- data/lib/splunk-sdk-ruby/entity/job.rb +12 -4
- data/lib/splunk-sdk-ruby/entity/modular_input_kind.rb +47 -0
- data/lib/splunk-sdk-ruby/resultsreader.rb +71 -17
- data/lib/splunk-sdk-ruby/service.rb +37 -5
- data/lib/splunk-sdk-ruby/version.rb +1 -1
- data/lib/splunk-sdk-ruby/xml_shim.rb +11 -0
- data/splunk-sdk-ruby.gemspec +3 -2
- data/test/data/atom_test_data.json +457 -0
- data/test/{export_test_data.json → data/export_test_data.json} +302 -182
- data/test/data/resultsreader_test_data.json +1693 -0
- data/test/test_atomfeed.rb +20 -9
- data/test/test_configuration_file.rb +22 -0
- data/test/test_context.rb +1 -1
- data/test/test_helper.rb +27 -15
- data/test/test_index.rb +2 -3
- data/test/test_inputs.rb +211 -0
- data/test/test_jobs.rb +73 -2
- data/test/test_modular_input_kinds.rb +46 -0
- data/test/test_restarts.rb +10 -0
- data/test/test_resultsreader.rb +22 -7
- data/test/test_users.rb +8 -0
- data/test/test_xml_shim.rb +10 -0
- metadata +104 -101
- data/test/atom_test_data.rb +0 -472
- data/test/resultsreader_test_data.json +0 -1119
@@ -0,0 +1,136 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2011-2013 Splunk, Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"): you may
|
5
|
+
# not use this file except in compliance with the License. You may obtain
|
6
|
+
# a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations
|
14
|
+
# under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
require_relative '../collection'
|
18
|
+
|
19
|
+
##
|
20
|
+
# Provide a class representing a collection of input kinds.
|
21
|
+
#
|
22
|
+
module Splunk
|
23
|
+
##
|
24
|
+
# A collection of input types.
|
25
|
+
#
|
26
|
+
# Inputs in the Splunk REST API are arranged in what looks like a
|
27
|
+
# directory structure, as in
|
28
|
+
#
|
29
|
+
# monitor/
|
30
|
+
# tcp/
|
31
|
+
# cooked/
|
32
|
+
# raw/
|
33
|
+
# udp/
|
34
|
+
#
|
35
|
+
# You get the top level directory by calling +inputs+ on your +Service+.
|
36
|
+
# Then you can use it as if it were a Hash. If you fetch an entry that has
|
37
|
+
# subtypes, such as +tcp+, you get another +InputKinds+ containing the types
|
38
|
+
# in that entry. If you fetch an entry that doesn't have subtypes, such as
|
39
|
+
# "udp", then you get an +Inputs+ object (a subclass of +Collection+)
|
40
|
+
# containing specific inputs.
|
41
|
+
#
|
42
|
+
# *Example*:
|
43
|
+
#
|
44
|
+
# # Returns an InputKinds collection
|
45
|
+
# tcp_inputs = service.inputs["tcp"]
|
46
|
+
# tcp_inputs.has_key?("raw") # ==> true
|
47
|
+
# tcp_inputs.has_key?("cooked") # ==> true
|
48
|
+
#
|
49
|
+
# # A read only collection of all the inputs in Splunk.
|
50
|
+
# service.inputs["all"]
|
51
|
+
#
|
52
|
+
# # An Inputs object containing all the UDP inputs in Splunk.
|
53
|
+
# service.inputs["udp"]
|
54
|
+
#
|
55
|
+
class InputKinds < ReadOnlyCollection
|
56
|
+
def fetch(name, namespace=nil)
|
57
|
+
request_args = {:resource => @resource + [name]}
|
58
|
+
if not namespace.nil?
|
59
|
+
request_args[:namespace] = namespace
|
60
|
+
end
|
61
|
+
|
62
|
+
begin
|
63
|
+
response = @service.request(request_args)
|
64
|
+
rescue SplunkHTTPError => err
|
65
|
+
if err.code == 404
|
66
|
+
return nil
|
67
|
+
else
|
68
|
+
raise err
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
feed = AtomFeed.new(response.body)
|
73
|
+
if feed.metadata["links"].has_key?("create")
|
74
|
+
Inputs.new(@service, resource + [name])
|
75
|
+
elsif name == "all"
|
76
|
+
ReadOnlyCollection.new(@service, resource + [name])
|
77
|
+
else
|
78
|
+
InputKinds.new(@service, resource + [name])
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# A collection of specific inputs.
|
85
|
+
#
|
86
|
+
class Inputs < Collection
|
87
|
+
def initialize(service, resource)
|
88
|
+
super(service, resource)
|
89
|
+
@always_fetch = true
|
90
|
+
end
|
91
|
+
|
92
|
+
def create(name, args={})
|
93
|
+
body_args = args.clone()
|
94
|
+
body_args["name"] = name
|
95
|
+
|
96
|
+
request_args = {
|
97
|
+
:method => :POST,
|
98
|
+
:resource => @resource,
|
99
|
+
:body => body_args
|
100
|
+
}
|
101
|
+
if args.has_key?(:namespace)
|
102
|
+
request_args[:namespace] = body_args.delete(:namespace)
|
103
|
+
end
|
104
|
+
|
105
|
+
@service.request(request_args)
|
106
|
+
|
107
|
+
# If we have created a oneshot input, no actual entity
|
108
|
+
# is created. We return nil here in that case.
|
109
|
+
if @resource == ["data", "inputs", "oneshot"]
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
|
113
|
+
# TCP and UDP inputs have a key restrictToHost. If it is set
|
114
|
+
# then they are created with hostname:port as their resource
|
115
|
+
# instead of just port, and we must adjust our behavior
|
116
|
+
# accordingly.
|
117
|
+
if args.has_key?(:restrictToHost)
|
118
|
+
name = args[:restrictToHost] + ":" + name
|
119
|
+
end
|
120
|
+
|
121
|
+
fetch_args = {:method => :GET,
|
122
|
+
:resource => @resource + [name]}
|
123
|
+
if args.has_key?(:namespace)
|
124
|
+
fetch_args[:namespace] = args[:namespace]
|
125
|
+
end
|
126
|
+
response = @service.request(fetch_args)
|
127
|
+
|
128
|
+
feed = AtomFeed.new(response.body)
|
129
|
+
raise StandardError.new("No entities returned") if feed.entries.empty?
|
130
|
+
entity = atom_entry_to_entity(feed.entries[0])
|
131
|
+
raise StandardError.new("Found nil entity.") if entity.nil?
|
132
|
+
return entity
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
@@ -51,7 +51,7 @@ module Splunk
|
|
51
51
|
# Creates an asynchronous search job.
|
52
52
|
#
|
53
53
|
# The search job requires a _query_, and takes a hash of other, optional
|
54
|
-
# arguments, which are documented in the {Splunk REST documentation}[http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTsearch#search.2Fjobs
|
54
|
+
# arguments, which are documented in the {Splunk REST documentation}[http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTsearch#search.2Fjobs].
|
55
55
|
#
|
56
56
|
def create(query, args={})
|
57
57
|
if args.has_key?(:exec_mode)
|
@@ -81,6 +81,10 @@ module Splunk
|
|
81
81
|
def create_oneshot(query, args={})
|
82
82
|
args[:search] = query
|
83
83
|
args[:exec_mode] = 'oneshot'
|
84
|
+
# Suppress segmentation (<sg> tags in the XML response) by default:
|
85
|
+
if !args.has_key?(:segmentation)
|
86
|
+
args[:segmentation] = "none"
|
87
|
+
end
|
84
88
|
response = @service.request(:method => :POST,
|
85
89
|
:resource => @resource,
|
86
90
|
:body => args)
|
@@ -100,6 +104,10 @@ module Splunk
|
|
100
104
|
#
|
101
105
|
def create_export(query, args={})
|
102
106
|
args["search"] = query
|
107
|
+
# Suppress segmentation (<sg> tags in the XML response) by default:
|
108
|
+
if !args.has_key?(:segmentation)
|
109
|
+
args[:segmentation] = "none"
|
110
|
+
end
|
103
111
|
response = @service.request(:method => :GET,
|
104
112
|
:resource => @resource + ["export"],
|
105
113
|
:query => args)
|
@@ -232,13 +232,13 @@ module Splunk
|
|
232
232
|
# * +method+: The HTTP method to use (one of +:GET+, +:POST+, or +:DELETE+;
|
233
233
|
# default: +:GET+).
|
234
234
|
# * +namespace+: The namespace to request a resource from Splunk in. Must
|
235
|
-
# be a +Namespace+ object. (default: the value of
|
235
|
+
# be a +Namespace+ object. (default: the value of @+namespace+ on
|
236
236
|
# this +Context+)
|
237
237
|
# * +resource+: An array of strings specifying the components of the path
|
238
238
|
# to the resource after the namespace. The strings should not be URL
|
239
239
|
# encoded, as that will be handled by +request+. (default: [])
|
240
240
|
# * +query+: A hash containing the values to be encoded as
|
241
|
-
# the query (the part following
|
241
|
+
# the query (the part following "?") in the URL. Nothing should be URL
|
242
242
|
# encoded as +request+ will do the encoding. If you need to pass multiple
|
243
243
|
# values for the same key, insert them as an Array as the value of their
|
244
244
|
# key into the Hash, and they will be properly encoded as a sequence of
|
@@ -332,7 +332,7 @@ module Splunk
|
|
332
332
|
# make a request to.
|
333
333
|
# * +:method+: (+:GET+, +:POST+, or +:DELETE+) The HTTP method to use.
|
334
334
|
# * +query+: A hash containing the values to be encoded as
|
335
|
-
# the query (the part following
|
335
|
+
# the query (the part following "?") in the URL. Nothing should be URL
|
336
336
|
# encoded as +request+ will do the encoding. If you need to pass multiple
|
337
337
|
# values for the same key, insert them as an +Array+ as the value of their
|
338
338
|
# key into the Hash, and they will be properly encoded as a sequence of
|
@@ -445,7 +445,8 @@ module Splunk
|
|
445
445
|
" of the Splunk Ruby SDK"})
|
446
446
|
|
447
447
|
# Make the actual restart request.
|
448
|
-
request(:
|
448
|
+
request(:method => :POST,
|
449
|
+
:resource => ["server", "control", "restart"])
|
449
450
|
|
450
451
|
# Clear our old token, which will no longer work after the restart.
|
451
452
|
logout()
|
@@ -478,6 +479,8 @@ module Splunk
|
|
478
479
|
request(:resource => ["data", "indexes"])
|
479
480
|
rescue Errno::ECONNREFUSED, EOFError, Errno::ECONNRESET
|
480
481
|
return false
|
482
|
+
rescue OpenSSL::SSL::SSLError
|
483
|
+
return false
|
481
484
|
rescue SplunkHTTPError
|
482
485
|
# Splunk is up, because it responded with a proper HTTP error
|
483
486
|
# that our SplunkHTTPError parser understood.
|
@@ -21,24 +21,12 @@ require_relative 'synonyms'
|
|
21
21
|
|
22
22
|
module Splunk
|
23
23
|
##
|
24
|
-
#
|
25
|
-
#
|
26
|
-
# +Entity+ objects represent individual items such as indexes, users, roles,
|
27
|
-
# etc. They are usually contained within +Collection+ objects.
|
24
|
+
# ReadOnlyEntity represents entities that can be read, but not created or
|
25
|
+
# updated, via the REST API. The canonical example is a modular input kind.
|
28
26
|
#
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
# the fields containing the +Entity+'s state, such as the capabilities of
|
33
|
-
# a role or whether an app should check for updates, are accessible with
|
34
|
-
# the [] operator (for instance, +role+["+capabilities+"] or
|
35
|
-
# +app+["+check_for_updates+"]).
|
36
|
-
#
|
37
|
-
# +Entity+ objects cache their state, so each lookup of a field does not
|
38
|
-
# make a roundtrip to the server. The state may be refreshed by calling
|
39
|
-
# the +refresh+ method on the +Entity+.
|
40
|
-
#
|
41
|
-
class Entity
|
27
|
+
class ReadOnlyEntity
|
28
|
+
# ReadOnlyEntity was factored out of Entity to avoid having to add
|
29
|
+
# special behavior to modular input kinds.
|
42
30
|
extend Synonyms
|
43
31
|
|
44
32
|
def initialize(service, namespace, resource, name, state=nil) # :nodoc:
|
@@ -86,17 +74,6 @@ module Splunk
|
|
86
74
|
#
|
87
75
|
attr_reader :service
|
88
76
|
|
89
|
-
##
|
90
|
-
# Deletes this entity from the server.
|
91
|
-
#
|
92
|
-
# Returns: +nil+.
|
93
|
-
#
|
94
|
-
def delete()
|
95
|
-
@service.request({:method => :DELETE,
|
96
|
-
:namespace => @namespace,
|
97
|
-
:resource => @resource + [name]})
|
98
|
-
end
|
99
|
-
|
100
77
|
##
|
101
78
|
# Fetches the field _key_ on this entity.
|
102
79
|
#
|
@@ -138,7 +115,7 @@ module Splunk
|
|
138
115
|
# +Entity+. If you specify one or more +Strings+ or +Arrays+ of +Strings+,
|
139
116
|
# all the keys specified in the arguments will be returned in the +Hash+.
|
140
117
|
#
|
141
|
-
# Returns: a +Hash+ with +Strings+ as keys, and +Strings+ or +Hashes+ or
|
118
|
+
# Returns: a +Hash+ with +Strings+ as keys, and +Strings+ or +Hashes+ or
|
142
119
|
# +Arrays+ as values.
|
143
120
|
#
|
144
121
|
def read(*field_list)
|
@@ -187,6 +164,37 @@ module Splunk
|
|
187
164
|
@state = feed.entries[0]
|
188
165
|
self
|
189
166
|
end
|
167
|
+
end
|
168
|
+
|
169
|
+
##
|
170
|
+
# Class representing individual entities in Splunk.
|
171
|
+
#
|
172
|
+
# +Entity+ objects represent individual items such as indexes, users, roles,
|
173
|
+
# etc. They are usually contained within +Collection+ objects.
|
174
|
+
#
|
175
|
+
# The basic, identifying information for an +Entity+ (name, namespace, path
|
176
|
+
# of the collection containing entity, and the service it's on) is all
|
177
|
+
# accessible via getters (+name+, +namespace+, +resource+, +service+). All
|
178
|
+
# the fields containing the +Entity+'s state, such as the capabilities of
|
179
|
+
# a role or whether an app should check for updates, are accessible with
|
180
|
+
# the [] operator (for instance, +role+["+capabilities+"] or
|
181
|
+
# +app+["+check_for_updates+"]).
|
182
|
+
#
|
183
|
+
# +Entity+ objects cache their state, so each lookup of a field does not
|
184
|
+
# make a roundtrip to the server. The state may be refreshed by calling
|
185
|
+
# the +refresh+ method on the +Entity+.
|
186
|
+
#
|
187
|
+
class Entity < ReadOnlyEntity
|
188
|
+
##
|
189
|
+
# Deletes this entity from the server.
|
190
|
+
#
|
191
|
+
# Returns: +nil+.
|
192
|
+
#
|
193
|
+
def delete()
|
194
|
+
@service.request({:method => :DELETE,
|
195
|
+
:namespace => @namespace,
|
196
|
+
:resource => @resource + [name]})
|
197
|
+
end
|
190
198
|
|
191
199
|
##
|
192
200
|
# Updates the values on the Entity specified in the arguments.
|
@@ -105,10 +105,14 @@ module Splunk
|
|
105
105
|
# Returns: a stream that can be read with +ResultsReader+.
|
106
106
|
#
|
107
107
|
def events(args={})
|
108
|
+
# Suppress segmentation (<sg> tags in the XML response) by default:
|
109
|
+
if !args.has_key?(:segmentation)
|
110
|
+
args[:segmentation] = "none"
|
111
|
+
end
|
108
112
|
response = @service.request(
|
109
113
|
:method => :GET,
|
110
114
|
:resource => @resource + [sid, "events"],
|
111
|
-
:
|
115
|
+
:query => args)
|
112
116
|
return response.body
|
113
117
|
end
|
114
118
|
|
@@ -192,10 +196,14 @@ module Splunk
|
|
192
196
|
# Returns: a stream readable by +ResultsReader+.
|
193
197
|
#
|
194
198
|
def preview(args={})
|
199
|
+
# Suppress segmentation (<sg> tags in the XML response) by default:
|
200
|
+
if !args.has_key?(:segmentation)
|
201
|
+
args[:segmentation] = "none"
|
202
|
+
end
|
195
203
|
response = @service.request(:method => :GET,
|
196
204
|
:resource => @resource +
|
197
205
|
[sid, "results_preview"],
|
198
|
-
:
|
206
|
+
:query => args)
|
199
207
|
return response.body
|
200
208
|
end
|
201
209
|
|
@@ -247,7 +255,7 @@ module Splunk
|
|
247
255
|
end
|
248
256
|
|
249
257
|
##
|
250
|
-
# Return the +Job's
|
258
|
+
# Return the +Job+'s search id.
|
251
259
|
#
|
252
260
|
# Returns: a +String+.
|
253
261
|
#
|
@@ -268,7 +276,7 @@ module Splunk
|
|
268
276
|
# duration), and +:time+ (a string representing the bucket's time in human
|
269
277
|
# readable form).
|
270
278
|
#
|
271
|
-
# Returns: an +Array+ of
|
279
|
+
# Returns: an +Array+ of Hashes describing each time bucket.
|
272
280
|
#
|
273
281
|
def timeline(args={})
|
274
282
|
response = @service.request(:resource => @resource + [sid, "timeline"],
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2011-2013 Splunk, Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"): you may
|
5
|
+
# not use this file except in compliance with the License. You may obtain
|
6
|
+
# a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations
|
14
|
+
# under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
require_relative '../entity'
|
18
|
+
|
19
|
+
module Splunk
|
20
|
+
##
|
21
|
+
# Class representing a family of user defined inputs on the server.
|
22
|
+
#
|
23
|
+
# Modular input kinds define new, first-class input kinds on the server.
|
24
|
+
# This endpoint provides access to a list of the arguments and parameters
|
25
|
+
# of the various kinds defined. The actual inputs of these kinds can be
|
26
|
+
# accessed via the Serve#inputs method.
|
27
|
+
#
|
28
|
+
# Modular input kinds are read only, so there is no update or delete method
|
29
|
+
# on this class.
|
30
|
+
#
|
31
|
+
class ModularInputKind < ReadOnlyEntity
|
32
|
+
##
|
33
|
+
# Return a Hash of all the arguments support by this modular input kind.
|
34
|
+
#
|
35
|
+
# The keys in the Hash are the names of the arguments. The values are
|
36
|
+
# additional Hashes giving the metadata about each argument. The possible
|
37
|
+
# keys in those Hashes are +"title"+, +"description"+,
|
38
|
+
# +"required_on_create``+, +"required_on_edit"+, +"data_type"+. Each value is
|
39
|
+
# a string. It should be one of +"true"+ or +"false"+ for
|
40
|
+
# +"required_on_create"+ and +"required_on_edit"+, and one of +"boolean"+,
|
41
|
+
# +"string"+, or +"number"+ for +"data_type"+.
|
42
|
+
#
|
43
|
+
def arguments
|
44
|
+
@state["content"]["endpoint"]["args"]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -98,6 +98,18 @@ module Splunk
|
|
98
98
|
# in this set, in the order they should be displayed (if you're going
|
99
99
|
# to make a table or the like)
|
100
100
|
#
|
101
|
+
# The values yielded by calling +each+ and similar methods on +ResultsReader+
|
102
|
+
# are of class +Event+, which is a subclass of +Hash+ with one extra method,
|
103
|
+
# +segmented_raw+. The fields of the event are available as values in the hash,
|
104
|
+
# with no escaped characters and no XML tags. The +_raw+ field, however, is
|
105
|
+
# returned with extra XML specifying how terms should be highlighted for
|
106
|
+
# display, and this full XML form is available by called the +segmented_raw+
|
107
|
+
# method. The XML returned looks something like:
|
108
|
+
#
|
109
|
+
# "<v xml:space=\"preserve\" trunc=\"0\">127.0.0.1 - admin
|
110
|
+
# [11/Feb/2013:10:42:49.790 -0800] \"POST /services/search/jobs/export
|
111
|
+
# HTTP/1.1\" 200 440404 - - - 257ms</v>"
|
112
|
+
#
|
101
113
|
# *Example*:
|
102
114
|
#
|
103
115
|
# require 'splunk-sdk-ruby'
|
@@ -105,11 +117,12 @@ module Splunk
|
|
105
117
|
# service = Splunk::connect(:username => "admin", :password => "changeme")
|
106
118
|
#
|
107
119
|
# stream = service.jobs.create_oneshot("search index=_internal | head 10")
|
108
|
-
# reader = ResultsReader.new(stream)
|
120
|
+
# reader = Splunk::ResultsReader.new(stream)
|
109
121
|
# puts reader.is_preview?
|
110
122
|
# # Prints: false
|
111
123
|
# reader.each do |result|
|
112
|
-
# puts result
|
124
|
+
# puts result # Prints the fields in the result as a Hash
|
125
|
+
# puts result.segmented_raw() # Prints the XML version of the _raw field
|
113
126
|
# end
|
114
127
|
# # Prints a sequence of Hashes containing events.
|
115
128
|
#
|
@@ -137,7 +150,7 @@ module Splunk
|
|
137
150
|
def initialize(text_or_stream)
|
138
151
|
if text_or_stream.nil?
|
139
152
|
stream = StringIO.new("")
|
140
|
-
elsif
|
153
|
+
elsif text_or_stream.is_a?(ExportStream)
|
141
154
|
# The sensible behavior on streams from the export endpoints is to
|
142
155
|
# skip all preview results and concatenate all others. The export
|
143
156
|
# functions wrap their streams in ExportStream to mark that they need
|
@@ -154,11 +167,9 @@ module Splunk
|
|
154
167
|
stream = text_or_stream
|
155
168
|
end
|
156
169
|
|
157
|
-
if stream.eof?
|
170
|
+
if !stream.nil? and stream.eof?
|
158
171
|
@is_preview = nil
|
159
172
|
@fields = []
|
160
|
-
elsif stream.is_a?(ExportStream)
|
161
|
-
|
162
173
|
else
|
163
174
|
# We use a SAX parser. +listener+ is the event handler, but a SAX
|
164
175
|
# parser won't usually transfer control during parsing.
|
@@ -186,8 +197,7 @@ module Splunk
|
|
186
197
|
if @is_export
|
187
198
|
warn "[DEPRECATED] Do not use ResultsReader on the output of the " +
|
188
199
|
"export endpoint. Use MultiResultsReader instead."
|
189
|
-
|
190
|
-
enum = reader.each()
|
200
|
+
enum = @reader.each()
|
191
201
|
else
|
192
202
|
enum = Enumerator.new() do |yielder|
|
193
203
|
if !@iteration_fiber.nil? # Handle the case of empty files
|
@@ -219,6 +229,24 @@ module Splunk
|
|
219
229
|
end
|
220
230
|
end
|
221
231
|
|
232
|
+
##
|
233
|
+
# +Event+ represents a single event returned from a +ResultsReader+.
|
234
|
+
#
|
235
|
+
# +Event+ is a subclass of +Hash+, adding a single method +segmented_raw()+
|
236
|
+
# which returns a string containing the XML of the raw event, as opposed
|
237
|
+
# to the unescaped, raw strings returned by fetching a particular field
|
238
|
+
# via [].
|
239
|
+
#
|
240
|
+
class Event < Hash
|
241
|
+
@raw_xml = nil
|
242
|
+
|
243
|
+
attr_writer :raw_xml
|
244
|
+
|
245
|
+
def segmented_raw
|
246
|
+
@raw_xml
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
222
250
|
##
|
223
251
|
# +ResultsListener+ is the SAX event handler for +ResultsReader+.
|
224
252
|
#
|
@@ -269,7 +297,7 @@ module Splunk
|
|
269
297
|
elsif name == "result"
|
270
298
|
@state = :result
|
271
299
|
@current_offset = Integer(attributes["offset"])
|
272
|
-
@current_result =
|
300
|
+
@current_result = Event.new()
|
273
301
|
end
|
274
302
|
end,
|
275
303
|
:end_element => lambda do |name|
|
@@ -326,7 +354,23 @@ module Splunk
|
|
326
354
|
@current_value = nil
|
327
355
|
elsif name == "text" || name == "v"
|
328
356
|
@state = :field_values
|
329
|
-
@
|
357
|
+
@current_text = ""
|
358
|
+
s = ["v"] + attributes.map do |entry|
|
359
|
+
key, value = entry
|
360
|
+
# Nokogiri and REXML both drop the namespaces of attributes,
|
361
|
+
# and there is no way to recover them. To reconstruct the
|
362
|
+
# XML (since we can't get at its raw form) we put in the
|
363
|
+
# one instance of a namespace on an attribute that shows up
|
364
|
+
# in what Splunk returns. Yes, this is a terribly, ugly
|
365
|
+
# kludge.
|
366
|
+
if key == "space"
|
367
|
+
prefixed_key = "xml:space"
|
368
|
+
else
|
369
|
+
prefixed_key = key
|
370
|
+
end
|
371
|
+
"#{prefixed_key}=\"#{value}\""
|
372
|
+
end
|
373
|
+
@current_xml = "<" + s.join(" ") + ">"
|
330
374
|
end
|
331
375
|
end,
|
332
376
|
:end_element => lambda do |name|
|
@@ -346,6 +390,11 @@ module Splunk
|
|
346
390
|
else
|
347
391
|
@current_result[@current_field] = @current_value
|
348
392
|
end
|
393
|
+
|
394
|
+
if @current_field == "_raw"
|
395
|
+
@current_result.raw_xml = @current_xml
|
396
|
+
end
|
397
|
+
|
349
398
|
@current_field = nil
|
350
399
|
@current_value = nil
|
351
400
|
end
|
@@ -356,19 +405,23 @@ module Splunk
|
|
356
405
|
:end_element => lambda do |name|
|
357
406
|
if name == "text" || name == "v"
|
358
407
|
if @current_value == nil
|
359
|
-
@current_value = @
|
408
|
+
@current_value = @current_text
|
360
409
|
elsif @current_value.is_a?(Array)
|
361
|
-
@current_value << @
|
410
|
+
@current_value << @current_text
|
362
411
|
else
|
363
|
-
@current_value = [@current_value, @
|
412
|
+
@current_value = [@current_value, @current_text]
|
413
|
+
end
|
414
|
+
|
415
|
+
if name == "v"
|
416
|
+
@current_xml << "</v>"
|
364
417
|
end
|
365
418
|
|
366
|
-
@
|
419
|
+
@current_text = nil
|
367
420
|
@state = :result
|
368
421
|
elsif name == "sg"
|
369
422
|
# <sg> is emitted to delimit text that should be displayed
|
370
423
|
# highlighted. We preserve it in field values.
|
371
|
-
@
|
424
|
+
@current_xml << "</sg>"
|
372
425
|
end
|
373
426
|
end,
|
374
427
|
:start_element => lambda do |name, attributes|
|
@@ -378,11 +431,12 @@ module Splunk
|
|
378
431
|
"#{key}=\"#{value}\""
|
379
432
|
end
|
380
433
|
text = "<" + s.join(" ") + ">"
|
381
|
-
@
|
434
|
+
@current_xml << text
|
382
435
|
end
|
383
436
|
end,
|
384
437
|
:characters => lambda do |text|
|
385
|
-
@
|
438
|
+
@current_text << text
|
439
|
+
@current_xml << Splunk::escape_string(text)
|
386
440
|
end
|
387
441
|
}
|
388
442
|
}
|