File Coverage

blib/lib/Splunklib/Intersplunk.pm
Criterion Covered Total %
statement 18 149 12.0
branch 0 44 0.0
condition 0 17 0.0
subroutine 6 14 42.8
pod 6 6 100.0
total 30 230 13.0


line stmt bran cond sub pod time code
1             #
2             # $Id: Intersplunk.pm,v 927d5bf7d37e 2015/10/03 12:57:38 gomor $
3             #
4             package Splunklib::Intersplunk;
5 1     1   594 use strict;
  1         2  
  1         20  
6 1     1   4 use warnings;
  1         2  
  1         22  
7              
8 1     1   5 use base qw(Exporter);
  1         1  
  1         101  
9             our @EXPORT_OK = qw(readResults outputResults isGetInfo outputGetInfo);
10              
11 1     1   5440 use Data::Dumper;
  1         9485  
  1         110  
12 1     1   2346 use Text::CSV_XS;
  1         17099  
  1         68  
13 1     1   764 use URI::Escape;
  1         1349  
  1         1969  
14              
15             my $Debug = 0;
16              
17             #
18             # Intersplunk format
19             #
20             # Converted from /opt/splunk/lib/python2.7/site-packages/splunk/Intersplunk.py
21             # readResults() and outputResults() function.
22             #
23              
24             sub _csv_reader {
25 0     0     my $csvr = Text::CSV_XS->new({
26             binary => 1,
27             sep_char => ',',
28             #allow_loose_escapes => 1,
29             });
30 0 0         if (! defined($csvr)) {
31             # XXX: error handler
32 0           return;
33             }
34              
35 0           return $csvr;
36             }
37              
38             sub _csv_writer {
39 0     0     my ($ary, $stdout) = @_;
40              
41 0           my $results = $ary->[0];
42 0           my $header = $ary->[1];
43 0           my $lookup = $ary->[2];
44              
45 0           my $output_debug;
46 0 0         if ($Debug) {
47 0           open(my $output_debug, '>>', '/tmp/output-ta-base64pl-debug.txt');
48             }
49              
50 0 0         my $csv = Text::CSV_XS->new({
51             binary => 1,
52             sep_char => ',',
53             }) or die "Cannot use CSV: ".Text::CSV->error_diag();
54              
55             # Header
56 0           $csv->print($stdout, $header);
57 0           print $stdout "\r\n";
58 0 0         if ($Debug) {
59 0           $csv->print($output_debug, $header);
60 0           print $output_debug "\r\n";
61             }
62              
63             # Content
64 0 0         if ($Debug) {
65 0           for my $result (@$results) {
66 0           $csv->print($stdout, $result);
67 0           print $stdout "\r\n";
68 0           $csv->print($output_debug, $result);
69 0           print $output_debug "\r\n";
70             }
71             }
72             else {
73 0           for my $result (@$results) {
74 0           $csv->print($stdout, $result);
75 0           print $stdout "\r\n";
76             }
77             }
78              
79 0           return 1;
80             }
81              
82             sub getEncodedMV {
83 0     0 1   my ($s) = @_;
84              
85             # XXX: TODO
86              
87 0           return 1;
88             }
89              
90             sub decodeMV {
91 0     0 1   my ($s, $vals) = @_;
92              
93             # XXX: TODO
94              
95 0 0         if (! length($s)) {
96 0           return;
97             }
98              
99 0           my $tok = '';
100 0           my $inval = 0;
101              
102             # XXX: todo
103             #my $i = 0;
104             #while ($i < length($s)) {
105             #if (! $inval) {
106             #if (
107             #}
108             #}
109              
110 0           return 1;
111             }
112              
113             #
114             # Raw intersplunk input example:
115             #
116             # splunkVersion:6.2.6
117             # allowStream:1
118             # keywords:%22%22%22index%3A%3Alocal_system%22%22%20%22
119             # search:search%20index%3Dlocal_system%20%7C%20fields%20_raw%20%7C%20base64pl%20field%3D_raw
120             # sid:1443870176.14
121             # realtime:0
122             # preview:0
123             # truncated:0
124             #
125             # "_bkt","_cd","_indextime","_raw","_serial","_si","_sourcetype","_time"
126             # "local_system~17~5004F7D1-06C9-44F3-A726-29190625C311","17:207",1443870169,"Oct 3 13:02:48 messiah sudo: pam_unix(sudo:session): session closed for user root",0,"messiah
127             # local_system",syslog,1443870168
128              
129             sub readResults {
130 0     0 1   my ($stdin, $settings, $has_header) = @_;
131              
132 0   0       $settings ||= {}; # No settings by default
133 0   0       $has_header ||= 1; # Header by default
134              
135 0           my $input_debug;
136 0 0         if ($Debug) {
137 0           open(my $input_debug, '>', '/tmp/input-intersplunk-debug.txt');
138             }
139              
140 0 0         if ($has_header) {
141 0 0         if ($Debug) {
142 0           while (my $line = <$stdin>) {
143 0           print $input_debug $line;
144 0           chomp($line);
145 0 0         last if $line =~ /^\s*$/;
146 0           $line = URI::Escape::uri_unescape($line);
147 0           my ($k, $v) = split(/:/, $line);
148 0           $settings->{$k} = $v;
149             }
150             }
151             else {
152 0           while (my $line = <$stdin>) {
153 0           chomp($line);
154 0 0         last if $line =~ /^\s*$/;
155 0           $line = URI::Escape::uri_unescape($line);
156 0           my ($k, $v) = split(/:/, $line);
157 0           $settings->{$k} = $v;
158             }
159             }
160             }
161              
162 0           my $csvr = _csv_reader();
163              
164 0           my $csvw;
165 0 0         if ($Debug) {
166 0 0         $csvw = Text::CSV_XS->new({
167             binary => 1,
168             sep_char => ',',
169             }) or die "Cannot use CSV: ".Text::CSV->error_diag();
170             }
171              
172 0           my $results = [];
173 0           my $header = [];
174 0           my $first = 1;
175 0           my @mv_fields = ();
176 0           my $lookup = {};
177 0           while (my $line = $csvr->getline($stdin)) {
178             # Activate when $Debug=1
179             # Off by default for perf issues.
180             #$csvw->print($output_debug, $line);
181             #print $output_debug "\r\n";
182 0 0         if ($first) {
183 0           $header = $line;
184 0           $first = 0;
185              
186             # Check which fields are multivalued (for a field 'foo', '__mv_foo' also exists)
187 0           my %h_header = map { $_ => 1 } @$header;
  0            
188 0           for my $field (@$header) {
189 0 0         if (exists($h_header{"__mv_$field"})) {
190 0           push @mv_fields, $field;
191             }
192             }
193              
194 0           next;
195             }
196              
197             # We must maintain field order
198 0           my $pos = 0;
199 0           for my $hdr (@$header) {
200 0           $lookup->{$hdr} = $pos;
201 0           $pos++;
202             }
203 0           my $result = $line;
204              
205 0           for my $key (@mv_fields) {
206 0           my $mv_key = "__mv_$key";
207 0 0 0       if (exists($result->[$lookup->{$key}]) && exists($result->[$lookup->{$mv_key}])) {
208             # Expand the value of __mv_[key] to a list, store it in key, and delete __mv_[key]
209 0           my $vals = [];
210 0 0         if (decodeMV($result->[$lookup->{$mv_key}], $vals)) {
211             #$result{$key} = $vals;
212             #if (@{$result{$key}} == 1) {
213             #$result{$key} = $result{$key}->[0];
214             #}
215             #delete $result{$mv_key};
216             # XXX: todo
217             }
218             }
219             }
220              
221 0           push @$results, $result;
222             }
223              
224 0           return [ $results, $header, $lookup ];
225             }
226              
227             sub isGetInfo {
228 0     0 1   my ($args) = @_;
229              
230 0 0 0       if (@$args >= 1 && $args->[0] eq '__GETINFO__') {
    0 0        
231 0           shift @$args; # Strip it
232 0           return 1;
233             }
234             elsif (@$args >= 1 && $args->[0] eq '__EXECUTE__') {
235 0           shift @$args; # Strip it
236 0           return 0;
237             }
238             else {
239             # XXX: error handling
240 0           exit(0);
241             }
242              
243 0           return 0;
244             }
245              
246             sub outputGetInfo {
247 0     0 1   my ($settings, $stdout) = @_;
248              
249             # Below is the correct field order to use.
250             # We currently don't follow on the output, but it seems to be ok.
251 0   0       $settings ||= {
252             changes_colorder => 1,
253             clear_required_fields => 0,
254             enableheader => 1,
255             generating => 0,
256             local => 0,
257             maxinputs => 0,
258             needs_empty_results => 1,
259             outputheader => 1,
260             overrides_timeorder => 0,
261             passauth => 0,
262             perf_warn_limit => 0,
263             required_fields => '',
264             requires_srinfo => 0,
265             retainsevents => 1,
266             run_in_preview => 1,
267             stderr_dest => 'log',
268             streaming => 1,
269             supports_multivalues => 1,
270             supports_rawargs => 1,
271             __mv_changes_colorder => '',
272             __mv_clear_required_fields => '',
273             __mv_enableheader => '',
274             __mv_generating => '',
275             __mv_local => '',
276             __mv_maxinputs => '',
277             __mv_needs_empty_results => '',
278             __mv_outputheader => '',
279             __mv_overrides_timeorder => '',
280             __mv_passauth => '',
281             __mv_perf_warn_limit => '',
282             __mv_required_fields => '',
283             __mv_requires_srinfo => '',
284             __mv_retainsevents => '',
285             __mv_run_in_preview => '',
286             __mv_stderr_dest => '',
287             __mv_streaming => '',
288             __mv_supports_multivalues => '',
289             __mv_supports_rawargs => '',
290             };
291              
292 0           my $header = '';
293 0           my $values = '';
294 0           for my $k (sort { $a cmp $b } keys %$settings) {
  0            
295 0           $header .= "$k,";
296 0           $values .= $settings->{$k}.",";
297             }
298 0           $header =~ s/,$//;
299 0           $values =~ s/,$//;
300              
301 0           print $stdout "\r\n";
302 0           print $stdout "$header\r\n";
303 0           print $stdout "$values\r\n";
304              
305 0           return 1;
306             }
307              
308             sub outputResults {
309 0     0 1   my ($ary, $messages, $fields, $mvdelim, $stdout) = @_;
310              
311 0   0       $mvdelim ||= '\n';
312              
313 0           my $results = $ary->[0];
314 0           my $header = $ary->[1];
315 0           my $lookup = $ary->[2];
316              
317             #
318             # Example message header
319             #
320             # $messages = {
321             # streaming_preop' => '0',
322             # streaming' => '0',
323             # generating' => '0',
324             # retainsevents' => '0',
325             # requires_preop' => '0',
326             # generates_timeorder' => '0',
327             # overrides_timeorder' => '0',
328             # clear_required_fields' => '0',
329             # };
330              
331 0 0         if (defined($messages)) {
332             # message header is everything before the first empty line, similar to the input
333             # header format. also key = value, with stripping of whitespace
334 0           for my $level (sort { $a <=> $b } keys %$messages) {
  0            
335 0           print $stdout $level."=".$messages->{$level}."\r\n";
336             }
337 0           print $stdout "\r\n";
338             }
339              
340 0 0         if (@$results == 0) {
341 0           return;
342             }
343              
344 0           my $s = {};
345 0           my $l = [];
346             # Check each entry to see if it is a list (multivalued).
347             # If so, set the multivalued key to the proper encoding.
348             # Replace the list with a newline separated string of the values.
349 0           for my $result (@$results) {
350             #for my $key (keys %$result) {
351 0           for my $key (@$result) {
352             # XXX: todo
353             #if (ref($result->{$key}) eq 'ARRAY') {
354             #$result->{"__mv_$key"} = getEncodedMV($result->{$key});
355             #$result->{$key} = join($mvdelim, @{$result->{$key}});
356             #}
357              
358             #if (! exists($s->{$key})) {
359             #$s->{$key} = 1;
360             #push @$l, $key;
361             #}
362             }
363             }
364              
365 0           my $h;
366 0 0         if (! $fields) {
367 0           $h = $header;
368             }
369             else {
370 0           $h = $fields;
371             }
372              
373 0           _csv_writer($ary, $stdout);
374              
375 0           return 1;
376             }
377              
378             1;
379              
380             __END__