Skip to content

Commit aded811

Browse files
author
Shakeel Mohamed
committed
Everything is feature complete, now in review!
1 parent cf0581c commit aded811

File tree

8 files changed

+388
-52
lines changed

8 files changed

+388
-52
lines changed

splunklib/modularinput/input_definition.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@ def __init__ (self):
2929
self.metadata = {}
3030
self.inputs = {}
3131

32-
def parse_definition(self, stream):
32+
33+
def __eq__(self, other):
34+
if not isinstance(other, InputDefinition):
35+
return False
36+
return self.metadata == other.metadata and self.inputs == other.inputs
37+
38+
def parse_input_definition(stream):
3339
"""Parse a stream containing XML into an InputDefinition.
3440
3541
:param stream: stream containing XML to parse
@@ -47,9 +53,4 @@ def parse_definition(self, stream):
4753
else:
4854
definition.metadata[node.tag] = node.text
4955

50-
return definition
51-
52-
def __eq__(self, other):
53-
if not isinstance(other, InputDefinition):
54-
return False
55-
return self.metadata == other.metadata and self.inputs == other.inputs
56+
return definition

splunklib/modularinput/script.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Copyright 2011-2013 Splunk, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"): you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
from abc import ABCMeta, abstractmethod
16+
import sys
17+
from splunklib.modularinput.input_definition import InputDefinition, parse_input_definition
18+
from splunklib.modularinput.validation_definition import ValidationDefinition, parse_validation_definition
19+
from splunklib.modularinput.event_writer import EventWriter
20+
from splunklib.modularinput.scheme import Scheme
21+
22+
try:
23+
import xml.etree.cElementTree as ET
24+
except ImportError:
25+
import xml.etree.ElementTree as ET
26+
27+
28+
class Script(object):
29+
"""An abstract base class for implementing modular inputs.
30+
31+
Subclasses should override getScheme, StreamEvents,
32+
and optional configureValidator if the modular Input uses
33+
external validation.
34+
35+
The important function is run, which is used to run modular inputs
36+
37+
"""
38+
__metaclass__ = ABCMeta
39+
40+
def run(self, args):
41+
"""This function is stable, call run to run a modular input
42+
43+
:param args: String[] args from Java
44+
:return:
45+
"""
46+
return self.run_script(args, EventWriter(), sys.stdin)
47+
48+
def run_script(self, args, event_writer, input_stream):
49+
"""Handles all the specifics of running a modular input
50+
51+
:param args:
52+
:param event_writer:
53+
:param input_stream:
54+
:return:
55+
"""
56+
57+
try:
58+
if len(args) == 0:
59+
# This script is running as an input. Input definitions will be passed on stdin
60+
# as XML, and the script will write events on stdout and log entries on stderr.
61+
input_definition = parse_input_definition(input_stream)
62+
self.stream_events(input_definition, event_writer)
63+
event_writer.close()
64+
return 0
65+
66+
elif str(args[0]).lower() == "--scheme":
67+
# Splunk has requested XML specifying the scheme for this modular input.
68+
# Return it and exit.
69+
scheme = self.get_scheme()
70+
if scheme is None:
71+
event_writer.log(EventWriter.FATAL, "Modular input script returned a null scheme.")
72+
return 1
73+
else:
74+
event_writer.write_xml_document(scheme.to_XML())
75+
return 0
76+
77+
elif args[0].lower() == "--validate-arguments":
78+
validation_definition = parse_validation_definition(input_stream)
79+
try:
80+
self.validate_input(validation_definition)
81+
return 0
82+
except Exception as e:
83+
root = ET.Element("error")
84+
ET.SubElement(root, "message").text = e.message
85+
event_writer.write_xml_document(root)
86+
87+
return 1
88+
89+
err_string = "ERROR Invalid arguments to modular input script:" + ' '.join(args)
90+
event_writer._err.write(err_string)
91+
92+
except Exception as e:
93+
err_string = EventWriter.ERROR + e.message
94+
event_writer._err.write(err_string)
95+
return 1
96+
97+
@abstractmethod
98+
def get_scheme(self):
99+
"""The scheme defines the parameters understood by this modular input.
100+
101+
:return: a Scheme object representing the parameters for this modular input
102+
"""
103+
104+
def validate_input(self, definition):
105+
"""Handles external validation for modular input kinds. When Splunk
106+
called a modular input script in validation mode, it will pass in an XML document
107+
giving information about the Splunk instance (so you can call back into it if needed)
108+
and the name and parameters of the proposed input.
109+
110+
If this function does not throw an exception, the validation is assumed to succeed.
111+
Otherwise any error throws will be turned into a string and logged back to Splunk.
112+
113+
The default implementation always passes.
114+
115+
:param definition: The parameters for the proposed input passed by splunkd
116+
"""
117+
@abstractmethod
118+
def stream_events(self, inputs, ew):
119+
"""The method called to stream events into Splunk. It should do all of its output via
120+
EventWriter rather than assuming that there is a console attached.
121+
122+
:param inputs: an InputDefinition object
123+
:param ew: an object with methods to write events and log messages to Splunk
124+
"""
125+
return

splunklib/modularinput/validation_definition.py

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -30,48 +30,48 @@ def __init__(self):
3030
self.metadata = {}
3131
self.parameters = {}
3232

33-
def parse_definition(self, stream):
34-
"""Creates a ValidationDefinition from a provided stream containing XML.
33+
def __eq__(self, other):
34+
if not isinstance(other, ValidationDefinition):
35+
return False
36+
return self.metadata == other.metadata and self.parameters == other.parameters
3537

36-
The XML typically will look like
38+
def parse_validation_definition(stream):
39+
"""Creates a ValidationDefinition from a provided stream containing XML.
3740
38-
<items>
39-
<server_host>myHost</server_host>
40-
<server_uri>https://127.0.0.1:8089</server_uri>
41-
<session_key>123102983109283019283</session_key>
42-
<checkpoint_dir>/opt/splunk/var/lib/splunk/modinputs</checkpoint_dir>
43-
<item name="myScheme">
44-
<param name="param1">value1</param>
45-
<param_list name="param2">
46-
<value>value2</value>
47-
<value>value3</value>
48-
<value>value4</value>
49-
</param_list>
50-
</item>
51-
</items>
41+
The XML typically will look like
5242
53-
:param stream: stream containing XML to parse
54-
:return definition: a ValidationDefinition object
55-
"""
43+
<items>
44+
<server_host>myHost</server_host>
45+
<server_uri>https://127.0.0.1:8089</server_uri>
46+
<session_key>123102983109283019283</session_key>
47+
<checkpoint_dir>/opt/splunk/var/lib/splunk/modinputs</checkpoint_dir>
48+
<item name="myScheme">
49+
<param name="param1">value1</param>
50+
<param_list name="param2">
51+
<value>value2</value>
52+
<value>value3</value>
53+
<value>value4</value>
54+
</param_list>
55+
</item>
56+
</items>
5657
57-
definition = ValidationDefinition()
58+
:param stream: stream containing XML to parse
59+
:return definition: a ValidationDefinition object
60+
"""
5861

59-
# parse XML from the stream, then get the root node
60-
root = ET.parse(stream).getroot()
62+
definition = ValidationDefinition()
6163

62-
for node in root:
63-
# lone item node
64-
if node.tag == "item":
65-
# name from item node
66-
definition.metadata["name"] = node.get("name")
67-
definition.parameters = parse_xml_data(node, "")
68-
else:
69-
# Store anything else in metadata
70-
definition.metadata[node.tag] = node.text
64+
# parse XML from the stream, then get the root node
65+
root = ET.parse(stream).getroot()
7166

72-
return definition
67+
for node in root:
68+
# lone item node
69+
if node.tag == "item":
70+
# name from item node
71+
definition.metadata["name"] = node.get("name")
72+
definition.parameters = parse_xml_data(node, "")
73+
else:
74+
# Store anything else in metadata
75+
definition.metadata[node.tag] = node.text
7376

74-
def __eq__(self, other):
75-
if not isinstance(other, ValidationDefinition):
76-
return False
77-
return self.metadata == other.metadata and self.parameters == other.parameters
77+
return definition
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<error><message>Big fat validation error!</message></error>

tests/modularinput/modularinput_testlib.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414
# License for the specific language governing permissions and limitations
1515
# under the License.
1616

17-
# Utility file for common functions and imports
18-
17+
# Utility file for unit tests, import common functions and modules
1918
try:
2019
import unittest2 as unittest
2120
except ImportError:

tests/modularinput/test_input_definition.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
# under the License.
1616

1717
from tests.modularinput.modularinput_testlib import unittest
18-
from splunklib.modularinput.input_definition import InputDefinition
18+
from splunklib.modularinput.input_definition import InputDefinition, parse_input_definition
1919

2020
class InputDefinitionTestCase(unittest.TestCase):
2121

2222
def test_parse_inputdef_with_zero_inputs(self):
2323
"""Check parsing of XML that contains only metadata"""
2424

25-
found = InputDefinition().parse_definition(open("data/conf_with_0_inputs.xml"))
25+
found = parse_input_definition(open("data/conf_with_0_inputs.xml"))
2626

2727
expectedDefinition = InputDefinition()
2828
expectedDefinition.metadata = {
@@ -37,7 +37,7 @@ def test_parse_inputdef_with_zero_inputs(self):
3737
def test_parse_inputdef_with_two_inputs(self):
3838
"""Check parsing of XML that contains 2 inputs"""
3939

40-
found = InputDefinition().parse_definition(open("data/conf_with_2_inputs.xml"))
40+
found = parse_input_definition(open("data/conf_with_2_inputs.xml"))
4141

4242
expectedDefinition = InputDefinition()
4343
expectedDefinition.metadata = {
@@ -67,7 +67,7 @@ def test_attempt_to_parse_malformed_input_definition_will_throw_exception(self):
6767
"""Does malformed XML cause the expected exception."""
6868

6969
with self.assertRaises(ValueError):
70-
found = InputDefinition().parse_definition(open("data/conf_with_invalid_inputs.xml"))
70+
found = parse_input_definition(open("data/conf_with_invalid_inputs.xml"))
7171

7272
if __name__ == "__main__":
7373
unittest.main()

0 commit comments

Comments
 (0)