splunk-sdk-ruby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/CHANGELOG.md +160 -0
  2. data/Gemfile +8 -0
  3. data/LICENSE +177 -0
  4. data/README.md +310 -0
  5. data/Rakefile +40 -0
  6. data/examples/1_connect.rb +51 -0
  7. data/examples/2_manage.rb +103 -0
  8. data/examples/3_blocking_searches.rb +82 -0
  9. data/examples/4_asynchronous_searches.rb +79 -0
  10. data/examples/5_stream_data_to_splunk.rb +79 -0
  11. data/lib/splunk-sdk-ruby.rb +47 -0
  12. data/lib/splunk-sdk-ruby/ambiguous_entity_reference.rb +28 -0
  13. data/lib/splunk-sdk-ruby/atomfeed.rb +323 -0
  14. data/lib/splunk-sdk-ruby/collection.rb +417 -0
  15. data/lib/splunk-sdk-ruby/collection/apps.rb +35 -0
  16. data/lib/splunk-sdk-ruby/collection/case_insensitive_collection.rb +58 -0
  17. data/lib/splunk-sdk-ruby/collection/configuration_file.rb +50 -0
  18. data/lib/splunk-sdk-ruby/collection/configurations.rb +80 -0
  19. data/lib/splunk-sdk-ruby/collection/jobs.rb +136 -0
  20. data/lib/splunk-sdk-ruby/collection/messages.rb +51 -0
  21. data/lib/splunk-sdk-ruby/context.rb +522 -0
  22. data/lib/splunk-sdk-ruby/entity.rb +260 -0
  23. data/lib/splunk-sdk-ruby/entity/index.rb +191 -0
  24. data/lib/splunk-sdk-ruby/entity/job.rb +339 -0
  25. data/lib/splunk-sdk-ruby/entity/message.rb +36 -0
  26. data/lib/splunk-sdk-ruby/entity/saved_search.rb +71 -0
  27. data/lib/splunk-sdk-ruby/entity/stanza.rb +45 -0
  28. data/lib/splunk-sdk-ruby/entity_not_ready.rb +26 -0
  29. data/lib/splunk-sdk-ruby/illegal_operation.rb +27 -0
  30. data/lib/splunk-sdk-ruby/namespace.rb +239 -0
  31. data/lib/splunk-sdk-ruby/resultsreader.rb +716 -0
  32. data/lib/splunk-sdk-ruby/service.rb +339 -0
  33. data/lib/splunk-sdk-ruby/splunk_http_error.rb +49 -0
  34. data/lib/splunk-sdk-ruby/synonyms.rb +50 -0
  35. data/lib/splunk-sdk-ruby/version.rb +27 -0
  36. data/lib/splunk-sdk-ruby/xml_shim.rb +117 -0
  37. data/splunk-sdk-ruby.gemspec +27 -0
  38. data/test/atom_test_data.rb +472 -0
  39. data/test/data/atom/atom_feed_with_message.xml +19 -0
  40. data/test/data/atom/atom_with_feed.xml +99 -0
  41. data/test/data/atom/atom_with_several_entries.xml +101 -0
  42. data/test/data/atom/atom_with_simple_entries.xml +30 -0
  43. data/test/data/atom/atom_without_feed.xml +248 -0
  44. data/test/data/export/4.2.5/export_results.xml +88 -0
  45. data/test/data/export/4.3.5/export_results.xml +87 -0
  46. data/test/data/export/5.0.1/export_results.xml +78 -0
  47. data/test/data/export/5.0.1/nonreporting.xml +232 -0
  48. data/test/data/results/4.2.5/results-empty.xml +0 -0
  49. data/test/data/results/4.2.5/results-preview.xml +255 -0
  50. data/test/data/results/4.2.5/results.xml +336 -0
  51. data/test/data/results/4.3.5/results-empty.xml +0 -0
  52. data/test/data/results/4.3.5/results-preview.xml +1057 -0
  53. data/test/data/results/4.3.5/results.xml +626 -0
  54. data/test/data/results/5.0.2/results-empty.xml +1 -0
  55. data/test/data/results/5.0.2/results-empty_preview.xml +1 -0
  56. data/test/data/results/5.0.2/results-preview.xml +448 -0
  57. data/test/data/results/5.0.2/results.xml +501 -0
  58. data/test/export_test_data.json +360 -0
  59. data/test/resultsreader_test_data.json +1119 -0
  60. data/test/services.server.info.xml +43 -0
  61. data/test/services.xml +111 -0
  62. data/test/test_atomfeed.rb +71 -0
  63. data/test/test_collection.rb +278 -0
  64. data/test/test_configuration_file.rb +124 -0
  65. data/test/test_context.rb +119 -0
  66. data/test/test_entity.rb +95 -0
  67. data/test/test_helper.rb +250 -0
  68. data/test/test_http_error.rb +52 -0
  69. data/test/test_index.rb +91 -0
  70. data/test/test_jobs.rb +319 -0
  71. data/test/test_messages.rb +17 -0
  72. data/test/test_namespace.rb +188 -0
  73. data/test/test_restarts.rb +49 -0
  74. data/test/test_resultsreader.rb +106 -0
  75. data/test/test_roles.rb +41 -0
  76. data/test/test_saved_searches.rb +119 -0
  77. data/test/test_service.rb +65 -0
  78. data/test/test_users.rb +33 -0
  79. data/test/test_xml_shim.rb +28 -0
  80. data/test/testfile.txt +1 -0
  81. metadata +200 -0
@@ -0,0 +1,339 @@
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
+ ##
18
+ # Provides an +Entity+ subclass to represent search jobs.
19
+ #
20
+
21
+ require_relative '../entity'
22
+ require_relative '../namespace'
23
+
24
+ module Splunk
25
+ ##
26
+ # A class to represent a Splunk asynchronous search job.
27
+ #
28
+ # When you create a job, you need to wait for it to be ready before you can
29
+ # interrogate it in an useful way. Typically, you will write something like
30
+ #
31
+ # job = @service.jobs.create("search *")
32
+ # while !job.is_ready?
33
+ # sleep(0.2)
34
+ # end
35
+ # # Now the job is ready to use.
36
+ #
37
+ # The most important methods on +Job+ beyond those provided by +Entity+
38
+ # are those that fetch results (+results+, +preview+), and those that control
39
+ # the job's execution (+cancel+, +pause+, +unpause+, +finalize+).
40
+ #
41
+ # Note that jobs are created with preview disabled by default. You need to
42
+ # call +enable_preview+ and wait for the field +"isPreviewEnabled"+ to be
43
+ # +"1"+ before you will get anything useful from +preview+.
44
+ #
45
+ class Job < Entity
46
+ def initialize(service, sid, state=nil)
47
+ @sid = sid
48
+ begin
49
+ super(service, Splunk::namespace(:sharing => "global"), PATH_JOBS, sid, state)
50
+ rescue EntityNotReady
51
+ # Jobs may not be ready (and so cannot be refreshed) when they are
52
+ # first created, so Entity#initialize may throw an EntityNotReady
53
+ # exception. It's nothing to be concerned about for jobs.
54
+ end
55
+ end
56
+
57
+ ##
58
+ # Cancels this search job.
59
+ #
60
+ # Cancelling the job stops the search and deletes the results cache.
61
+ #
62
+ # Returns nothing.
63
+ #
64
+ def cancel
65
+ begin
66
+ control(:action => "cancel")
67
+ rescue SplunkHTTPError => err
68
+ if err.code == 404
69
+ return self # Job already cancelled; cancelling twice is a nop.
70
+ else
71
+ raise err
72
+ end
73
+ end
74
+ end
75
+
76
+ ##
77
+ # Issues a control request to this job.
78
+ #
79
+ # _args_ should be a hash with at least the key +:action+ (with a value such
80
+ # as +"cancel"+ or +"setpriority"+).
81
+ #
82
+ # Returns: the +Job+.
83
+ #
84
+ def control(args) # :nodoc:
85
+ @service.request(:method => :POST,
86
+ :namespace => @namespace,
87
+ :resource => @resource + [sid, "control"],
88
+ :body => args)
89
+ self
90
+ end
91
+
92
+ ##
93
+ # Returns the raw events found by this search job.
94
+ #
95
+ # These events are the data from the search pipeline before the first
96
+ # "transforming" search command. This is the primary method for a client
97
+ # to fetch a set of _untransformed_ events from a search job. This call is
98
+ # only valid if the search has no transforming commands.
99
+ #
100
+ # If the job is not yet finished, this will return an empty set of events.
101
+ #
102
+ # See the {REST docs for this call}[http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTsearch#search.2Fjobs.2F.7Bsearch_id.7D.2Fevents]
103
+ # for more info on valid parameters and results.
104
+ #
105
+ # Returns: a stream that can be read with +ResultsReader+.
106
+ #
107
+ def events(args={})
108
+ response = @service.request(
109
+ :method => :GET,
110
+ :resource => @resource + [sid, "events"],
111
+ :body => args)
112
+ return response.body
113
+ end
114
+
115
+ ##
116
+ # Enables preview generation for this job.
117
+ #
118
+ # Enabling previews may slow the search down considerably, but will
119
+ # make the +preview+ method return events before the job is finished.
120
+ #
121
+ # Returns: the +Job+.
122
+ #
123
+ def enable_preview
124
+ control(:action => "enablepreview")
125
+ end
126
+
127
+ ##
128
+ # Finalizes this search job.
129
+ #
130
+ # Stops the search and provides whatever results have been obtained so far.
131
+ # (retrievable via +results+).
132
+ #
133
+ # Returns: the +Job+.
134
+ #
135
+ def finalize
136
+ control(:action => "finalize")
137
+ end
138
+
139
+ ##
140
+ # Returns whether the search job is done.
141
+ #
142
+ # +is_done+ refreshes the +Job+, so its answer is always current.
143
+ #
144
+ # Returns: +true+ or +false+.
145
+ #
146
+ def is_done?()
147
+ begin
148
+ refresh()
149
+ return fetch("isDone") == "1"
150
+ rescue EntityNotReady
151
+ return false
152
+ end
153
+ end
154
+
155
+ ##
156
+ # Returns whether the search job is ready.
157
+ #
158
+ # +is_ready+ refreshes the +Job+, so once the job is ready, you need
159
+ # not call +refresh+ an additional time.
160
+ #
161
+ # Returns: +true+ or +false+.
162
+ #
163
+ def is_ready?()
164
+ begin
165
+ refresh()
166
+ return true
167
+ rescue EntityNotReady
168
+ return false
169
+ end
170
+ end
171
+
172
+ ##
173
+ # Pauses this search job.
174
+ #
175
+ # Use +unpause+ to resume.
176
+ #
177
+ # Returns: the +Job+.
178
+ #
179
+ def pause
180
+ control(:action => "pause")
181
+ end
182
+
183
+ # Returns a set of preview events from this +Job+.
184
+ #
185
+ # If the search job is finished, this method is identical to +results+.
186
+ # Otherwise, it will return an empty results set unless preview is enabled
187
+ # (for instance, by calling +enable_preview+).
188
+ #
189
+ # See the {REST docs for this call}[http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTsearch#search.2Fjobs.2F.7Bsearch_id.7D.2Fresults_preview]
190
+ # for more info on valid parameters and results.
191
+ #
192
+ # Returns: a stream readable by +ResultsReader+.
193
+ #
194
+ def preview(args={})
195
+ response = @service.request(:method => :GET,
196
+ :resource => @resource +
197
+ [sid, "results_preview"],
198
+ :body => args)
199
+ return response.body
200
+ end
201
+
202
+ ##
203
+ # Returns search results for this job.
204
+ #
205
+ # These are the results after all processing from the search pipeline is
206
+ # finished, including transforming search commands.
207
+ #
208
+ # The results set will be empty unless the job is done.
209
+ #
210
+ # Returns: a stream readable by +ResultsReader+.
211
+ #
212
+ def results(args={})
213
+ response = @service.request(:resource => @resource + [sid, "results"],
214
+ :body => args)
215
+ return response.body
216
+ end
217
+
218
+ ##
219
+ # Returns the search log for this search job.
220
+ #
221
+ # The search log is a syslog style file documenting the job.
222
+ #
223
+ # See the {REST docs for this call}[http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTsearch#search.2Fjobs.2F.7Bsearch_id.7D.2Fsearch.log]
224
+ # for more info on valid parameters and results.
225
+ #
226
+ # Returns: a stream containing the log.
227
+ #
228
+ def searchlog(args={})
229
+ response = @service.request(:resource => @resource + [sid, "search.log"],
230
+ :body => args)
231
+ return response.body
232
+ end
233
+
234
+ ##
235
+ # Sets the priority of this search Job.
236
+ #
237
+ # _value_ can be 0-10, but unless the Splunk instance is running as
238
+ # root or administrator, you can only reduce the priority.
239
+ #
240
+ # Arguments:
241
+ # * _value_: an Integer from 0 to 10.
242
+ #
243
+ # Returns: the +Job+.
244
+ #
245
+ def set_priority(value)
246
+ control(:action => "setpriority", :priority => value)
247
+ end
248
+
249
+ ##
250
+ # Return the +Job's+ search id.
251
+ #
252
+ # Returns: a +String+.
253
+ #
254
+ attr_reader :sid
255
+
256
+ ##
257
+ # Returns the distribution over time of the events available so far.
258
+ #
259
+ # See the {REST docs for this call}[http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTsearch#search.2Fjobs.2F.7Bsearch_id.7D.2Ftimeline]
260
+ # for more info on valid parameters and results.
261
+ #
262
+ # Each bucket is represented as a Hash with the following fields:
263
+ # +:available_buckets+, +:event_count+, +:time_in_seconds+ (number of
264
+ # seconds since the epoch), +:bucket_duration+ (how much time this bucket
265
+ # covers), +:finished_scanning+ (is scanning for events in this bucket
266
+ # complete), +:earliest_timezone+ and +:latest_timezone+ (which can be
267
+ # different, for example when daylight savings time starts during a bucket's
268
+ # duration), and +:time+ (a string representing the bucket's time in human
269
+ # readable form).
270
+ #
271
+ # Returns: an +Array+ of +Hash+es describing each time bucket.
272
+ #
273
+ def timeline(args={})
274
+ response = @service.request(:resource => @resource + [sid, "timeline"],
275
+ :body => args)
276
+ if $splunk_xml_library == :nokogiri
277
+ doc = Nokogiri::XML(response.body)
278
+ matches = doc.xpath("/timeline/bucket").map() do |bucket|
279
+ {:available_buckets => Integer(bucket.attributes["a"].to_s),
280
+ :event_count => Integer(bucket.attributes["c"].to_s),
281
+ :time_in_seconds => Float(bucket.attributes["t"].to_s),
282
+ :bucket_duration => Integer(bucket.attributes["d"].to_s),
283
+ :finished_scanning => Integer(bucket.attributes["f"].to_s),
284
+ :earliest_timezone => Integer(bucket.attributes["etz"].to_s),
285
+ :latest_timezone => Integer(bucket.attributes["ltz"].to_s),
286
+ :time => bucket.children.to_s}
287
+ end
288
+ return matches
289
+ else
290
+ doc = REXML::Document.new(response.body)
291
+ matches = []
292
+ matches = doc.elements.each("/timeline/bucket") do |bucket|
293
+ {:available_buckets => Integer(bucket.attributes["a"]),
294
+ :event_count => Integer(bucket.attributes["c"]),
295
+ :time_in_seconds => Float(bucket.attributes["t"]),
296
+ :bucket_duration => Integer(bucket.attributes["d"]),
297
+ :finished_scanning => Integer(bucket.attributes["f"]),
298
+ :earliest_timezone => Integer(bucket.attributes["etz"]),
299
+ :latest_timezone => Integer(bucket.attributes["ltz"]),
300
+ :time => bucket.children.join("")}
301
+ end
302
+ return matches
303
+ end
304
+ end
305
+
306
+ ##
307
+ # Resets the time to live for this Job.
308
+ #
309
+ # Calling touch resets the remaining time to live for the Job to its
310
+ # original value.
311
+ #
312
+ # Returns: the +Job+.
313
+ #
314
+ def touch
315
+ control(:action => "touch")
316
+ end
317
+
318
+ ##
319
+ # Sets the time to live (TTL) of this +Job+.
320
+ #
321
+ # The time to live is a number in seconds saying how long the search job
322
+ # should be on the Splunk system before being deleted.
323
+ #
324
+ # Returns: the +Job+.
325
+ #
326
+ def set_ttl(value)
327
+ control(:action => "setttl", :ttl => value)
328
+ end
329
+
330
+ ##
331
+ # Resumes execution of this Job.
332
+ #
333
+ # Returns: the +Job+.
334
+ #
335
+ def unpause
336
+ control(:action => "unpause")
337
+ end
338
+ end
339
+ end
@@ -0,0 +1,36 @@
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 to represent system wide messages.
22
+ #
23
+ # +Message+ differs from +Entity+ only in having a method +value+ to fetch
24
+ # the detail of the message.
25
+ #
26
+ class Message < Entity
27
+ ##
28
+ # Returns the message's details.
29
+ #
30
+ # Returns: a +String+.
31
+ #
32
+ def value
33
+ fetch(@name)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,71 @@
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 saved searches.
22
+ #
23
+ class SavedSearch < Entity
24
+ ##
25
+ #
26
+ #
27
+ def dispatch(args={})
28
+ response = @service.request(:method => :POST,
29
+ :namespace => @namespace,
30
+ :resource => @resource + [name, "dispatch"],
31
+ :body => args)
32
+ sid = Splunk::text_at_xpath("//response/sid", response.body)
33
+ return Job.new(@service, sid)
34
+ end
35
+
36
+ ##
37
+ # Returns a list of the jobs dispatched from this saved search.
38
+ #
39
+ # Returns: an +Array+ of +Job+ objects.
40
+ #
41
+ def history()
42
+ response = @service.request(:namespace => @namespace,
43
+ :resource => @resource + [@name, "history"])
44
+ feed = AtomFeed.new(response.body)
45
+ return feed.entries.map do |entry|
46
+ Job.new(@service, entry["title"], entry)
47
+ end
48
+ end
49
+
50
+ ##
51
+ # Updates the state of this saved search.
52
+ #
53
+ # See the method on +Entity+ for documentation.
54
+ #
55
+ def update(args) # :nodoc:
56
+ # In versions before Splunk 5.0, updating a saved search requires passing
57
+ # a +search+ argument, or it will return an error or set the search to
58
+ # empty. This is fixed in 5.0, but while the 4.x series is supported, we
59
+ # set the search field on args if it's not already set. This, of course,
60
+ # has a race condition if someone else has set the search since the last
61
+ # time the entity was refreshed.
62
+ #
63
+ # You might want to check if "search" is in the +requiredFields+ list
64
+ # on the entity, but that isn't always returned.
65
+ if !args.has_key?(:search) && !args.has_key?("search")
66
+ args[:search] = fetch("search")
67
+ end
68
+ super(args)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,45 @@
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
+ ##
18
+ # Provides a subclass of +Entity+ to represent stanzas in configuration files.
19
+ #
20
+
21
+ module Splunk
22
+ ##
23
+ # A class representing a stanza in a configuration file.
24
+ #
25
+ # +Stanza+ differs from +Entity+ only in providing a +length+ method
26
+ # to count the number of keys in it.
27
+ #
28
+ class Stanza < Entity
29
+ synonym "submit", "update"
30
+
31
+ ##
32
+ # Returns the number of elements in the stanza.
33
+ #
34
+ # The actual Atom feed returned will have extra fields giving metadata
35
+ # about the stanza, which will not be counted.
36
+ #
37
+ # Returns: a nonnegative integer.
38
+ #
39
+ def length()
40
+ @state["content"].
41
+ reject() { |k| k.start_with?("eai") || k == "disabled" }.
42
+ length()
43
+ end
44
+ end
45
+ end