# Copyright 2011-2014 Splunk, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"): you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from abc import ABCMeta, abstractmethod from urlparse import urlsplit import sys from splunklib.client import Service from splunklib.modularinput.event_writer import EventWriter from splunklib.modularinput.input_definition import InputDefinition from splunklib.modularinput.validation_definition import ValidationDefinition try: import xml.etree.cElementTree as ET except ImportError: import xml.etree.ElementTree as ET class Script(object): """An abstract base class for implementing modular inputs. Subclasses should override ``get_scheme``, ``stream_events``, and optionally ``validate_input`` if the modular input uses external validation. The ``run`` function is used to run modular inputs; it typically should not be overridden. """ __metaclass__ = ABCMeta def __init__(self): self._input_definition = None self._service = None def run(self, args): """Runs this modular input :param args: List of command line arguments passed to this script. :returns: An integer to be used as the exit value of this program. """ # call the run_script function, which handles the specifics of running # a modular input return self.run_script(args, EventWriter(), sys.stdin) def run_script(self, args, event_writer, input_stream): """Handles all the specifics of running a modular input :param args: List of command line arguments passed to this script. :param event_writer: An ``EventWriter`` object for writing events. :param input_stream: An input stream for reading inputs. :returns: An integer to be used as the exit value of this program. """ try: if len(args) == 1: # This script is running as an input. Input definitions will be # passed on stdin as XML, and the script will write events on # stdout and log entries on stderr. self._input_definition = InputDefinition.parse(input_stream) self.stream_events(self._input_definition, event_writer) event_writer.close() return 0 elif str(args[1]).lower() == "--scheme": # Splunk has requested XML specifying the scheme for this # modular input Return it and exit. scheme = self.get_scheme() if scheme is None: event_writer.log( EventWriter.FATAL, "Modular input script returned a null scheme.") return 1 else: event_writer.write_xml_document(scheme.to_xml()) return 0 elif args[1].lower() == "--validate-arguments": validation_definition = ValidationDefinition.parse(input_stream) try: self.validate_input(validation_definition) return 0 except Exception as e: root = ET.Element("error") ET.SubElement(root, "message").text = e.message event_writer.write_xml_document(root) return 1 else: err_string = "ERROR Invalid arguments to modular input script:" + ' '.join(args) event_writer._err.write(err_string) except Exception as e: err_string = EventWriter.ERROR + e.message event_writer._err.write(err_string) return 1 @property def service(self): """ Returns a Splunk service object for this script invocation. The service object is created from the Splunkd URI and session key passed to the command invocation on the modular input stream. It is available as soon as the :code:`Script.stream_events` method is called. :return: :class:splunklib.client.Service. A value of None is returned, if you call this method before the :code:`Script.stream_events` method is called. """ if self._service is not None: return self._service if self._input_definition is None: return None splunkd_uri = self._input_definition.metadata["server_uri"] session_key = self._input_definition.metadata["session_key"] scheme, netloc, _, _, _ = urlsplit(splunkd_uri, allow_fragments=False) splunkd_host, splunkd_port = netloc.split(':') self._service = Service( scheme=scheme, host=splunkd_host, port=splunkd_port, token=session_key) return self._service @abstractmethod def get_scheme(self): """The scheme defines the parameters understood by this modular input. :return: a ``Scheme`` object representing the parameters for this modular input. """ def validate_input(self, definition): """Handles external validation for modular input kinds. When Splunk calls a modular input script in validation mode, it will pass in an XML document giving information about the Splunk instance (so you can call back into it if needed) and the name and parameters of the proposed input. If this function does not throw an exception, the validation is assumed to succeed. Otherwise any errors thrown will be turned into a string and logged back to Splunk. The default implementation always passes. :param definition: The parameters for the proposed input passed by splunkd. """ pass @abstractmethod def stream_events(self, inputs, ew): """The method called to stream events into Splunk. It should do all of its output via EventWriter rather than assuming that there is a console attached. :param inputs: An ``InputDefinition`` object. :param ew: An object with methods to write events and log messages to Splunk. """