Advertisement
phoenixdigital

Splunk Eventgen - eventgenconfig.py

Aug 9th, 2015
283
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 33.32 KB | None | 0 0
  1. from __future__ import division
  2. from ConfigParser import ConfigParser
  3. import os
  4. import datetime
  5. import sys
  6. import re
  7. import __main__
  8. import logging, logging.handlers
  9. import json
  10. import pprint
  11. import copy
  12. from eventgensamples import Sample, Token
  13. import urllib
  14.  
  15. # 5/10/12 CS Some people consider Singleton to be lazy.  Dunno, I like it for convenience.
  16. # My general thought on that sort of stuff is if you don't like it, reimplement it.  I'll consider
  17. # your patch.
  18. class Config:
  19.     """Reads configuration from files or Splunk REST endpoint and stores them in a 'Borg' global.
  20.    Borg is a variation on the Singleton design pattern which allows us to continually instantiate
  21.    the configuration object throughout the application and maintain state."""
  22.     # Stolen from http://code.activestate.com/recipes/66531/
  23.     # This implements a Borg patterns, similar to Singleton
  24.     # It allows numerous instantiations but always shared state
  25.     __sharedState = {}
  26.  
  27.     # Internal vars
  28.     _firsttime = True
  29.     _confDict = None
  30.     _isOwnApp = False
  31.  
  32.     # Externally used vars
  33.     debug = False
  34.     runOnce = False
  35.     splunkEmbedded = False
  36.     sessionKey = None
  37.     grandparentdir = None
  38.     greatgrandparentdir = None
  39.     samples = [ ]
  40.     sampleDir = None
  41.  
  42.     # Config file options.  We do not define defaults here, rather we pull them in
  43.     # from either the eventgen.conf in the SA-Eventgen app (embedded)
  44.     # or the eventgen_defaults file in the lib directory (standalone)
  45.     # These are only options which are valid in the 'global' stanza
  46.     # 5/22 CS Except for blacklist, we define that in code, since splunk complains about it in
  47.     # the config files
  48.     disabled = None
  49.     blacklist = ".*\.part"
  50.     spoolDir = None
  51.     spoolFile = None
  52.     breaker = None
  53.     sampletype = None
  54.     interval = None
  55.     delay = None
  56.     count = None
  57.     bundlelines = None
  58.     earliest = None
  59.     latest = None
  60.     hourOfDayRate = None
  61.     dayOfWeekRate = None
  62.     randomizeCount = None
  63.     randomizeEvents = None
  64.     outputMode = None
  65.     fileName = None
  66.     fileMaxBytes = None
  67.     fileBackupFiles = None
  68.     splunkHost = None
  69.     splunkPort = None
  70.     splunkMethod = None
  71.     index = None
  72.     source = None
  73.     host = None
  74.     hostRegex = None
  75.     sourcetype = None
  76.     projectID = None
  77.     accessToken = None
  78.     mode = None
  79.     backfill = None
  80.     backfillSearch = None
  81.     backfillSearchUrl = None
  82.     minuteOfHourRate = None
  83.     timezone = datetime.timedelta(days=1)
  84.     dayOfMonthRate = None
  85.     monthOfYearRate = None
  86.     timeField = None
  87.  
  88.     ## Validations
  89.     _validSettings = ['disabled', 'blacklist', 'spoolDir', 'spoolFile', 'breaker', 'sampletype' , 'interval',
  90.                     'delay', 'count', 'bundlelines', 'earliest', 'latest', 'eai:acl', 'hourOfDayRate',
  91.                     'dayOfWeekRate', 'randomizeCount', 'randomizeEvents', 'outputMode', 'fileName', 'fileMaxBytes',
  92.                     'fileBackupFiles', 'splunkHost', 'splunkPort', 'splunkMethod', 'splunkUser', 'splunkPass',
  93.                     'index', 'source', 'sourcetype', 'host', 'hostRegex', 'projectID', 'accessToken', 'mode',
  94.                     'backfill', 'backfillSearch', 'eai:userName', 'eai:appName', 'timeMultiple', 'debug',
  95.                     'minuteOfHourRate', 'timezone', 'dayOfMonthRate', 'monthOfYearRate', 'timeField']
  96.     _validTokenTypes = {'token': 0, 'replacementType': 1, 'replacement': 2, 'replacementIncrementAmount':3, 'replacementIncrementAmountRandomness':4, 'hourOfDayMultiplier':5, 'dayOfWeekMultiplier':6}
  97.     _validHostTokens = {'token': 0, 'replacement': 1}
  98.     _validReplacementTypes = ['static', 'timestamp', 'replaytimestamp', 'random', 'rated', 'file', 'mvfile', 'integerid', 'randomRated']
  99.     _validOutputModes = ['spool', 'file', 'splunkstream', 'stormstream']
  100.     _validSplunkMethods = ['http', 'https']
  101.     _validSampleTypes = ['raw', 'csv']
  102.     _validModes = ['sample', 'replay']
  103.     _intSettings = ['interval', 'count', 'fileMaxBytes', 'fileBackupFiles', 'splunkPort']
  104.     _floatSettings = ['randomizeCount', 'delay', 'timeMultiple']
  105.     _boolSettings = ['disabled', 'randomizeEvents', 'bundlelines']
  106.     _jsonSettings = ['hourOfDayRate', 'dayOfWeekRate', 'minuteOfHourRate', 'dayOfMonthRate', 'monthOfYearRate', 'hourOfDayMultiplier', 'dayOfWeekMultiplier']
  107.     _defaultableSettings = ['disabled', 'spoolDir', 'spoolFile', 'breaker', 'sampletype', 'interval', 'delay',
  108.                             'count', 'bundlelines', 'earliest', 'latest', 'hourOfDayRate', 'dayOfWeekRate',
  109.                             'randomizeCount', 'randomizeEvents', 'outputMode', 'fileMaxBytes', 'fileBackupFiles',
  110.                             'splunkHost', 'splunkPort', 'splunkMethod', 'index', 'source', 'sourcetype', 'host', 'hostRegex',
  111.                             'projectID', 'accessToken', 'mode', 'minuteOfHourRate', 'timeMultiple', 'dayOfMonthRate',
  112.                             'monthOfYearRate', 'timeField']
  113.  
  114.     def __init__(self):
  115.         """Setup Config object.  Sets up Logging and path related variables."""
  116.         # Rebind the internal datastore of the class to an Instance variable
  117.         self.__dict__ = self.__sharedState
  118.         if self._firsttime:
  119.             # Setup logger
  120.             logger = logging.getLogger('eventgen')
  121.             logger.propagate = False # Prevent the log messages from being duplicated in the python.log file
  122.             logger.setLevel(logging.INFO)
  123.             formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
  124.             streamHandler = logging.StreamHandler(sys.stdout)
  125.             streamHandler.setFormatter(formatter)
  126.             logger.addHandler(streamHandler)
  127.  
  128.             # Having logger as a global is just damned convenient
  129.             globals()['logger'] = logger
  130.  
  131.             # Determine some path names in our environment
  132.             self.grandparentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  133.             self.greatgrandparentdir = os.path.dirname(self.grandparentdir)
  134.  
  135.             # Determine if we're running as our own Splunk app or embedded in another
  136.             appName = self.grandparentdir.split(os.sep)[-1].lower()
  137.             if appName == 'sa-eventgen' or appName == 'eventgen':
  138.                 self._isOwnApp = True
  139.             self._firsttime = False
  140.  
  141.     def __str__(self):
  142.         """Only used for debugging, outputs a pretty printed representation of our Config"""
  143.         # Eliminate recursive going back to parent
  144.         temp = dict([ (key, value) for (key, value) in self.__dict__.items() if key != 'samples' ])
  145.         return 'Config:'+pprint.pformat(temp)+'\nSamples:\n'+pprint.pformat(self.samples)
  146.  
  147.     def __repr__(self):
  148.         return self.__str__()
  149.  
  150.     def makeSplunkEmbedded(self, sessionKey=None, runOnce=False):
  151.         """Setup operations for being Splunk Embedded.  This is legacy operations mode, just a little bit obfuscated now.
  152.        We wait 5 seconds for a sessionKey or 'debug' on stdin, and if we time out then we run in standalone mode.
  153.        If we're not Splunk embedded, we operate simpler.  No rest handler for configurations. We only read configs
  154.        in our parent app's directory.  In standalone mode, we read eventgen-standalone.conf and will skip eventgen.conf if
  155.        we detect SA-Eventgen is installed. """
  156.  
  157.         fileHandler = logging.handlers.RotatingFileHandler(os.environ['SPLUNK_HOME'] + '/var/log/splunk/eventgen.log', maxBytes=25000000, backupCount=5)
  158.         formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
  159.         fileHandler.setFormatter(formatter)
  160.         # fileHandler.setLevel(logging.DEBUG)
  161.         logger.handlers = [ ] # Remove existing StreamHandler if we're embedded
  162.         logger.addHandler(fileHandler)
  163.         logger.info("Running as Splunk embedded")
  164.         import splunk.auth as auth
  165.         import splunk.entity as entity
  166.         # 5/7/12 CS For some reason Splunk will not import the modules into global in its copy of python
  167.         # This is a hacky workaround, but it does fix the problem
  168.         globals()['auth'] = locals()['auth']
  169.         # globals()['bundle'] = locals()['bundle']
  170.         globals()['entity'] = locals()['entity']
  171.         # globals()['rest'] = locals()['rest']
  172.         # globals()['util'] = locals()['util']
  173.  
  174.         if sessionKey == None or runOnce == True:
  175.             self.runOnce = True
  176.             self.sessionKey = auth.getSessionKey('admin', 'changeme')
  177.         else:
  178.             self.sessionKey = sessionKey
  179.  
  180.         self.splunkEmbedded = True
  181.  
  182.  
  183.     def parse(self):
  184.         """Parse configs from Splunk REST Handler or from files.
  185.        We get called manually instead of in __init__ because we need find out if we're Splunk embedded before
  186.        we figure out how to configure ourselves.
  187.        """
  188.         logger.debug("Parsing configuration files.")
  189.         self._buildConfDict()
  190.         # Set defaults config instance variables to 'global' section
  191.         # This establishes defaults for other stanza settings
  192.         for key, value in self._confDict['global'].items():
  193.             value = self._validateSetting('global', key, value)
  194.             setattr(self, key, value)
  195.  
  196.         del self._confDict['global']
  197.         if 'default' in self._confDict:
  198.             del self._confDict['default']
  199.  
  200.         tempsamples = [ ]
  201.         tempsamples2 = [ ]
  202.  
  203.         # Now iterate for the rest of the samples we've found
  204.         # We'll create Sample objects for each of them
  205.         for stanza, settings in self._confDict.items():
  206.             sampleexists = False
  207.             for sample in self.samples:
  208.                 if sample.name == stanza:
  209.                     sampleexists = True
  210.  
  211.             # If we see the sample in two places, use the first and ignore the second
  212.             if not sampleexists:
  213.                 s = Sample(stanza)
  214.                 for key, value in settings.items():
  215.                     oldvalue = value
  216.                     try:
  217.                         value = self._validateSetting(stanza, key, value)
  218.                     except ValueError:
  219.                         # If we're improperly formatted, skip to the next item
  220.                         continue
  221.                     # If we're a tuple, then this must be a token
  222.                     if type(value) == tuple:
  223.                         # Token indices could be out of order, so we must check to
  224.                         # see whether we have enough items in the list to update the token
  225.                         # In general this will keep growing the list by whatever length we need
  226.                         if(key.find("host.") > -1):
  227.                             # logger.info("hostToken.{} = {}".format(value[1],oldvalue))
  228.                             if not isinstance(s.hostToken, Token):
  229.                                 s.hostToken = Token(s)
  230.                                 # default hard-coded for host replacement
  231.                                 s.hostToken.replacementType = 'file'
  232.                             setattr(s.hostToken, value[0], oldvalue)
  233.                         elif(key.find("token.") > -1):
  234.                             if len(s.tokens) <= value[0]:
  235.                                 x = (value[0]+1) - len(s.tokens)
  236.                                 s.tokens.extend([None for i in xrange(0, x)])
  237.                             if not isinstance(s.tokens[value[0]], Token):
  238.                                 s.tokens[value[0]] = Token(s)
  239. #                            logger.info("token[{}].{} = {}".format(value[0],value[1],value[2]))
  240.                             setattr(s.tokens[value[0]], value[1], value[2])
  241.                         else:
  242.                             if len(s.tokens) <= value[0]:
  243.                                 x = (value[0]+1) - len(s.tokens)
  244.                                 s.tokens.extend([None for i in xrange(0, x)])
  245.                             if not isinstance(s.tokens[value[0]], Token):
  246.                                 s.tokens[value[0]] = Token(s)
  247.                             # logger.info("token[{}].{} = {}".format(value[0],value[1],oldvalue))
  248.                             setattr(s.tokens[value[0]], value[1], oldvalue)
  249.                     elif key == 'eai:acl':
  250.                         setattr(s, 'app', value['app'])
  251.                     else:
  252.                         setattr(s, key, value)
  253.                         # 6/22/12 CS Need a way to show a setting was set by the original
  254.                         # config read
  255.                         s._lockedSettings.append(key)
  256.                         # logger.debug("Appending '%s' to locked settings for sample '%s'" % (key, s.name))
  257.  
  258.  
  259.                 # Validate all the tokens are fully setup, can't do this in _validateSettings
  260.                 # because they come over multiple lines
  261.                 # Don't error out at this point, just log it and remove the token and move on
  262.                 deleteidx = [ ]
  263.                 for i in xrange(0, len(s.tokens)):
  264.                     t = s.tokens[i]
  265.                     # If the index doesn't exist at all
  266.                     if t == None:
  267.                         logger.info("Token at index %s invalid" % i)
  268.                         # Can't modify list in place while we're looping through it
  269.                         # so create a list to remove later
  270.                         deleteidx.append(i)
  271.                     elif t.token == None or t.replacementType == None or t.replacement == None:
  272.                         logger.info("Token at index %s invalid" % i)
  273.                         deleteidx.append(i)
  274.                 newtokens = [ ]
  275.                 for i in xrange(0, len(s.tokens)):
  276.                     if i not in deleteidx:
  277.                         newtokens.append(s.tokens[i])
  278.                 s.tokens = newtokens
  279.  
  280.                 # Must have eai:acl key to determine app name which determines where actual files are
  281.                 if s.app == None:
  282.                     logger.error("App not set for sample '%s' in stanza '%s'" % (s.name, stanza))
  283.                     raise ValueError("App not set for sample '%s' in stanza '%s'" % (s.name, stanza))
  284.  
  285.                 # Set defaults for items not included in the config file
  286.                 for setting in self._defaultableSettings:
  287.                     if getattr(s, setting) == None:
  288.                         setattr(s, setting, getattr(self, setting))
  289.  
  290.                 # Append to temporary holding list
  291.                 if not s.disabled:
  292.                     s._priority = len(tempsamples)+1
  293.                     tempsamples.append(s)
  294.  
  295.         # 6/22/12 CS Rewriting the config matching code yet again to handling flattening better.
  296.         # In this case, we're now going to match all the files first, create a sample for each of them
  297.         # and then take the match from the sample seen last in the config file, and apply settings from
  298.         # every other match to that one.
  299.         for s in tempsamples:
  300.             # Now we need to match this up to real files.  May generate multiple copies of the sample.
  301.             foundFiles = [ ]
  302.  
  303.             if self.splunkEmbedded and self._isOwnApp:
  304.                 self.sampleDir = os.path.join(self.greatgrandparentdir, s.app, 'samples')
  305.             else:
  306.                 self.sampleDir = os.path.join(os.getcwd(), 'samples')
  307.                 if not os.path.exists(self.sampleDir):
  308.                     newSampleDir = os.path.join(os.sep.join(os.getcwd().split(os.sep)[:-1]), 'samples')
  309.                     logger.error("Path not found for samples '%s', trying '%s'" % (self.sampleDir, newSampleDir))
  310.                     self.sampleDir = newSampleDir
  311.  
  312.                     if not os.path.exists(self.sampleDir):
  313.                         newSampleDir = self.sampleDir = os.path.join(self.grandparentdir, 'samples')
  314.                         logger.error("Path not found for samples '%s', trying '%s'" % (self.sampleDir, newSampleDir))
  315.                         self.sampleDir = newSampleDir
  316.  
  317.             # Now that we know where samples will be written,
  318.             # Loop through tokens and load state for any that are integerid replacementType
  319.             for token in s.tokens:
  320.                 if token.replacementType == 'integerid':
  321.                     try:
  322.                         stateFile = open(os.path.join(self.sampleDir, 'state.'+urllib.pathname2url(token.token)), 'rU')
  323.                         token.replacement = stateFile.read()
  324.                         stateFile.close()
  325.                     # The file doesn't exist, use the default value in the config
  326.                     except (IOError, ValueError):
  327.                         token.replacement = token.replacement
  328.  
  329.  
  330.             if os.path.exists(self.sampleDir):
  331.                 sampleFiles = os.listdir(self.sampleDir)
  332.                 for sample in sampleFiles:
  333.                     results = re.match(s.name, sample)
  334.                     if results != None:
  335.                         samplePath = os.path.join(self.sampleDir, sample)
  336.                         if os.path.isfile(samplePath):
  337.                             logger.debug("Found sample file '%s' for app '%s' using config '%s' with priority '%s'; adding to list" \
  338.                                 % (sample, s.app, s.name, s._priority) )
  339.                             foundFiles.append(samplePath)
  340.             # If we didn't find any files, log about it
  341.             if len(foundFiles) == 0:
  342.                 logger.error("Sample '%s' in config but no matching files" % s.name)
  343.             for f in foundFiles:
  344.                 news = copy.deepcopy(s)
  345.                 news.filePath = f
  346.                 # Override <SAMPLE> with real name
  347.                 if s.outputMode == 'spool' and s.spoolFile == self.spoolFile:
  348.                     news.spoolFile = f.split(os.sep)[-1]
  349.                 if s.outputMode == 'file' and s.fileName == None and s.spoolFile == self.spoolFile:
  350.                     news.fileName = os.path.join(s.spoolDir, f.split(os.sep)[-1])
  351.                 elif s.outputMode == 'file' and s.fileName == None and s.spoolFile != None:
  352.                     news.fileName = os.path.join(s.spoolDir, s.spoolFile)
  353.                 # Override s.name with file name.  Usually they'll match unless we've been a regex
  354.                 # 6/22/12 CS Save original name for later matching
  355.                 news._origName = news.name
  356.                 news.name = f.split(os.sep)[-1]
  357.                 if not news.disabled:
  358.                     tempsamples2.append(news)
  359.                 else:
  360.                     logger.info("Sample '%s' for app '%s' is marked disabled." % (news.name, news.app))
  361.  
  362.         # Clear tempsamples, we're going to reuse it
  363.         tempsamples = [ ]
  364.  
  365.         # We're now going go through the samples and attempt to apply any matches from other stanzas
  366.         # This allows us to specify a wildcard at the beginning of the file and get more specific as we go on
  367.  
  368.         # Loop through all samples, create a list of the master samples
  369.         for s in tempsamples2:
  370.             foundHigherPriority = False
  371.             othermatches = [ ]
  372.             # If we're an exact match, don't go looking for higher priorities
  373.             if not s.name == s._origName:
  374.                 for matchs in tempsamples2:
  375.                     if matchs.filePath == s.filePath and s._origName != matchs._origName:
  376.                         # We have a match, now determine if we're higher priority or not
  377.                             # If this is a longer pattern or our match is an exact match
  378.                             # then we're a higher priority match
  379.                         if len(matchs._origName) > len(s._origName) or matchs.name == matchs._origName:
  380.                             # if s._priority < matchs._priority:
  381.                             logger.debug("Found higher priority for sample '%s' with priority '%s' from sample '%s' with priority '%s'" \
  382.                                         % (s._origName, s._priority, matchs._origName, matchs._priority))
  383.                             foundHigherPriority = True
  384.                             break
  385.                         else:
  386.                             othermatches.append(matchs._origName)
  387.             if not foundHigherPriority:
  388.                 logger.debug("Chose sample '%s' from samples '%s' for file '%s'" \
  389.                             % (s._origName, othermatches, s.name))
  390.                 tempsamples.append(s)
  391.  
  392.         # Now we have two lists, tempsamples which contains only the highest priority matches, and
  393.         # tempsamples2 which contains all matches.  We need to now flatten the config in order to
  394.         # take all the configs which might match.
  395.  
  396.         # Reversing tempsamples2 in order to look from the bottom of the file towards the top
  397.         # We want entries lower in the file to override entries higher in the file
  398.  
  399.         tempsamples2.reverse()
  400.  
  401.         # Loop through all samples
  402.         for s in tempsamples:
  403.             # Now loop through the samples we've matched with files to see if we apply to any of them
  404.             for overridesample in tempsamples2:
  405.                 if s.filePath == overridesample.filePath and s._origName != overridesample._origName:
  406.                     # Now we're going to loop through all valid settings and set them assuming
  407.                     # the more specific object that we've matched doesn't already have them set
  408.                     for settingname in self._validSettings:
  409.                         if settingname not in ['eai:acl', 'blacklist', 'disabled', 'name']:
  410.                             sourcesetting = getattr(overridesample, settingname)
  411.                             destsetting = getattr(s, settingname)
  412.                             # We want to check that the setting we're copying to hasn't been
  413.                             # set, otherwise keep the more specific value
  414.                             # 6/22/12 CS Added support for non-overrideable (locked) settings
  415.                             # logger.debug("Locked settings: %s" % pprint.pformat(matchs._lockedSettings))
  416.                             # if settingname in matchs._lockedSettings:
  417.                             #     logger.debug("Matched setting '%s' in sample '%s' lockedSettings" \
  418.                             #         % (settingname, matchs.name))
  419.                             if (destsetting == None or destsetting == getattr(self, settingname)) \
  420.                                     and sourcesetting != None and sourcesetting != getattr(self, settingname) \
  421.                                     and not settingname in s._lockedSettings:
  422.                                 logger.debug("Overriding setting '%s' with value '%s' from sample '%s' to sample '%s' in app '%s'" \
  423.                                                 % (settingname, sourcesetting, overridesample._origName, s.name, s.app))
  424.                                 setattr(s, settingname, sourcesetting)
  425.  
  426.                     # Now prepend all the tokens to the beginning of the list so they'll be sure to match first
  427.                     newtokens = copy.deepcopy(s.tokens)
  428.                     # logger.debug("Prepending tokens from sample '%s' to sample '%s' in app '%s': %s" \
  429.                     #             % (overridesample._origName, s.name, s.app, pprint.pformat(newtokens)))
  430.                     newtokens.extend(copy.deepcopy(overridesample.tokens))
  431.                     s.tokens = newtokens
  432.  
  433.         # We've added replay mode, so lets loop through the samples again and set the earliest and latest
  434.         # settings for any samples that were set to replay mode
  435.         for s in tempsamples:
  436.             if s.mode == 'replay':
  437.                 logger.debug("Setting defaults for replay samples")
  438.                 s.earliest = 'now'
  439.                 s.latest = 'now'
  440.                 s.count = 1
  441.                 s.randomizeCount = None
  442.                 s.hourOfDayRate = None
  443.                 s.dayOfWeekRate = None
  444.                 s.minuteOfHourRate = None
  445.                 s.interval = 0
  446.  
  447.         self.samples = tempsamples
  448.         self._confDict = None
  449.  
  450.         logger.debug("Finished parsing.  Config str:\n%s" % self)
  451.  
  452.  
  453.  
  454.     def _validateSetting(self, stanza, key, value):
  455.         """Validates settings to ensure they won't cause errors further down the line.
  456.        Returns a parsed value (if the value is something other than a string).
  457.        If we've read a token, which is a complex config, returns a tuple of parsed values."""
  458.         logger.debug("Validating setting for '%s' with value '%s' in stanza '%s'" % (key, value, stanza))
  459.         if key.find('token.') > -1:
  460.             results = re.match('token\.(\d+)\.(\w+)', key)
  461.             if results != None:
  462.                 groups = results.groups()
  463.                 if groups[1] not in self._validTokenTypes:
  464.                     logger.error("Could not parse token index '%s' token type '%s' in stanza '%s'" % \
  465.                                     (groups[0], groups[1], stanza))
  466.                     raise ValueError("Could not parse token index '%s' token type '%s' in stanza '%s'" % \
  467.                                     (groups[0], groups[1], stanza))
  468.                 if groups[1] == 'replacementType':
  469.                     if value not in self._validReplacementTypes:
  470.                         logger.error("Invalid replacementType '%s' for token index '%s' in stanza '%s'" % \
  471.                                     (value, groups[0], stanza))
  472.                         raise ValueError("Could not parse token index '%s' token type '%s' in stanza '%s'" % \
  473.                                     (groups[0], groups[1], stanza))
  474.                 if groups[1] == 'hourOfDayMultiplier':
  475.                     try:
  476.                         value = json.loads(value)
  477.                     except:
  478.                         logger.error("Could not parse json for '%s' in stanza '%s'" % (key, stanza))
  479.                         raise ValueError("Could not parse json for '%s' in stanza '%s'" % (key, stanza))
  480.                 if groups[1] == 'dayOfWeekMultiplier':
  481.                     try:
  482.                         value = json.loads(value)
  483.                     except:
  484.                         logger.error("Could not parse json for '%s' in stanza '%s'" % (key, stanza))
  485.                         raise ValueError("Could not parse json for '%s' in stanza '%s'" % (key, stanza))
  486.                 return (int(groups[0]), groups[1], value)
  487.         elif key.find('host.') > -1:
  488.             results = re.match('host\.(\w+)', key)
  489.             if results != None:
  490.                 groups = results.groups()
  491.                 if groups[0] not in self._validHostTokens:
  492.                     logger.error("Could not parse host token type '%s' in stanza '%s'" % (groups[0], stanza))
  493.                     raise ValueError("Could not parse host token type '%s' in stanza '%s'" % (groups[0], stanza))
  494.                 return (groups[0], value)
  495.         elif key in self._validSettings:
  496.             if key in self._intSettings:
  497.                 try:
  498.                     value = int(value)
  499.                 except:
  500.                     logger.error("Could not parse int for '%s' in stanza '%s'" % (key, stanza))
  501.                     raise ValueError("Could not parse int for '%s' in stanza '%s'" % (key, stanza))
  502.             elif key in self._floatSettings:
  503.                 try:
  504.                     value = float(value)
  505.                 except:
  506.                     logger.error("Could not parse float for '%s' in stanza '%s'" % (key, stanza))
  507.                     raise ValueError("Could not parse float for '%s' in stanza '%s'" % (key, stanza))
  508.             elif key in self._boolSettings:
  509.                 try:
  510.                     # Splunk gives these to us as a string '0' which bool thinks is True
  511.                     # ConfigParser gives 'false', so adding more strings
  512.                     if value in ('0', 'false', 'False'):
  513.                         value = 0
  514.                     value = bool(value)
  515.                 except:
  516.                     logger.error("Could not parse bool for '%s' in stanza '%s'" % (key, stanza))
  517.                     raise ValueError("Could not parse bool for '%s' in stanza '%s'" % (key, stanza))
  518.             elif key in self._jsonSettings:
  519.                 try:
  520.                     value = json.loads(value)
  521.                 except:
  522.                     logger.error("Could not parse json for '%s' in stanza '%s'" % (key, stanza))
  523.                     raise ValueError("Could not parse json for '%s' in stanza '%s'" % (key, stanza))
  524.             elif key == 'outputMode':
  525.                 if not value in self._validOutputModes:
  526.                     logger.error("outputMode invalid in stanza '%s'" % stanza)
  527.                     raise ValueError("outputMode invalid in stanza '%s'" % stanza)
  528.             elif key == 'splunkMethod':
  529.                 if not value in self._validSplunkMethods:
  530.                     logger.error("splunkMethod invalid in stanza '%s'" % stanza)
  531.                     raise ValueError("splunkMethod invalid in stanza '%s'" % stanza)
  532.             elif key == 'sampletype':
  533.                 if not value in self._validSampleTypes:
  534.                     logger.error("sampletype is invalid in stanza '%s'" % stanza)
  535.                     raise ValueError("sampletype is invalid in stanza '%s'" % stanza)
  536.             elif key == 'mode':
  537.                 if not value in self._validModes:
  538.                     logger.error("mode is invalid in stanza '%s'" % stanza)
  539.                     raise ValueError("mode is invalid in stanza '%s'" % stanza)
  540.             elif key == 'timezone':
  541.                 logger.info("Parsing timezone '%s' for stanza '%s'" % (value, stanza))
  542.                 if value.find('local') >= 0:
  543.                     value = datetime.timedelta(days=1)
  544.                 else:
  545.                     try:
  546.                         # Separate the hours and minutes (note: minutes = the int value - the hour portion)
  547.                         if int(value) > 0:
  548.                             mod = 100
  549.                         else:
  550.                             mod = -100
  551.                         value = datetime.timedelta(hours=int(int(value) / 100.0), minutes=int(value) % mod )
  552.                     except:
  553.                         logger.error("Could not parse timezone '%s' for '%s' in stanza '%s'" % (value, key, stanza))
  554.                         raise ValueError("Could not parse timezone '%s' for '%s' in stanza '%s'" % (value, key, stanza))
  555.                 logger.info("Parsed timezone '%s' for stanza '%s'" % (value, stanza))
  556.         else:
  557.             # Notifying only if the setting isn't valid and continuing on
  558.             # This will allow future settings to be added and be backwards compatible
  559.             logger.warn("Key '%s' in stanza '%s' is not a valid setting" % (key, stanza))
  560.         return value
  561.  
  562.     def _buildConfDict(self):
  563.         """Build configuration dictionary that we will use """
  564.         if self.splunkEmbedded and self._isOwnApp:
  565.             logger.info('Retrieving eventgen configurations from /configs/eventgen')
  566.             self._confDict = entity.getEntities('configs/eventgen', count=-1, sessionKey=self.sessionKey)
  567.         else:
  568.             logger.info('Retrieving eventgen configurations with ConfigParser()')
  569.             # We assume we're in a bin directory and that there are default and local directories
  570.             conf = ConfigParser()
  571.             # Make case sensitive
  572.             conf.optionxform = str
  573.             currentdir = os.getcwd()
  574.  
  575.             # If we're running standalone (and thusly using configParser)
  576.             # only pick up eventgen-standalone.conf.
  577.             conffiles = [ ]
  578.             if len(sys.argv) > 1:
  579.                 if len(sys.argv[1]) > 0:
  580.                     if os.path.exists(sys.argv[1]):
  581.                         conffiles = [os.path.join(self.grandparentdir, 'default', 'eventgen.conf'),
  582.                                     sys.argv[1]]
  583.             if len(conffiles) == 0:
  584.                 conffiles = [os.path.join(self.grandparentdir, 'default', 'eventgen.conf'),
  585.                             os.path.join(self.grandparentdir, 'local', 'eventgen.conf')]
  586.  
  587.             logger.debug('Reading configuration files for non-splunkembedded: %s' % conffiles)
  588.             conf.read(conffiles)
  589.  
  590.             sections = conf.sections()
  591.             ret = { }
  592.             orig = { }
  593.             for section in sections:
  594.                 ret[section] = dict(conf.items(section))
  595.                 # For compatibility with Splunk's configs, need to add the app name to an eai:acl key
  596.                 ret[section]['eai:acl'] = { 'app': self.grandparentdir.split(os.sep)[-1] }
  597.                 # orig[section] = dict(conf.items(section))
  598.                 # ret[section] = { }
  599.                 # for item in orig[section]:
  600.                 #     results = re.match('(token\.\d+)\.(\w+)', item)
  601.                 #     if results != None:
  602.                 #         ret[section][item] = orig[section][item]
  603.                 #     else:
  604.                 #         if item.lower() in [x.lower() for x in self._validSettings]:
  605.                 #             newitem = self._validSettings[[x.lower() for x in self._validSettings].index(item.lower())]
  606.                 #         ret[section][newitem] = orig[section][item]
  607.             self._confDict = ret
  608.  
  609.         # Have to look in the data structure before normalization between what Splunk returns
  610.         # versus what ConfigParser returns.
  611.         if self._confDict['global']['debug'].lower() == 'true' \
  612.                 or self._confDict['global']['debug'].lower() == '1':
  613.             logger.setLevel(logging.DEBUG)
  614.         logger.debug("ConfDict returned %s" % pprint.pformat(dict(self._confDict)))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement