def parseSearchToXML(search, hostPath=None, sessionKey=None, parseOnly='t', namespace=None, owner=None): """ Given a valid search string, return the XML from the splunk parsing endpoint that represents the search. """ if search == None or len(search) == 0: return None if not owner: owner = auth.getCurrentUser()['name'] uri = entity.buildEndpoint('/search/parser', namespace=namespace, owner=owner) if hostPath: uri = splunk.mergeHostPath(hostPath) + uri args = { 'q' : search, 'parse_only' : parseOnly } serverResponse, serverContent = rest.simpleRequest(uri, getargs=args, sessionKey=sessionKey) #print "SERVERCONTENT:", serverContent # normal messages from splunkd are propogated via SplunkdException; if 400 <= serverResponse.status < 500: root = et.fromstring(serverContent) extractedMessages = rest.extractMessages(root) for msg in extractedMessages: raise splunk.SearchException, msg['text'] return serverContent
def getPanel(self, namespace, view_id, panel_type, panel_sequence, **unused): ''' Returns a dashboard panel config GET /<namespace>/<view_id>/panel/<panel_sequence> --> panel config for panel at <panel_sequence> ==> returns a JSON response ''' output = jsonresponse.JsonResponse() try: username = cherrypy.session['user'].get('name') dash_id = en.buildEndpoint(VIEW_ENTITY_CLASS, view_id, namespace=namespace, owner=username) dashObject = Dashboard.get(dash_id) output.data = dashObject.get_panel(panel_sequence) except IndexError, e: cherrypy.response.status = 404 output.success = False output.addError( _('Requested panel %s does not exist') % panel_sequence)
def streamingExport(self, job, outputMode, **kwargs): ns = job.eaiacl['app'] sid = job.sid owner = job.eaiacl['owner'] request = {} request['output_mode'] = outputMode if ('fields' in kwargs): request['f'] = kwargs['fields'] postProcess = kwargs.get("search", False) if (postProcess): request['search'] = postProcess if 'output_time_format' in kwargs: request['output_time_format'] = kwargs['output_time_format'] else: request['output_time_format'] = i18n.ISO8609_MICROTIME # We're not going to read/write further from the user's session at this point # and streaming may take a while, so release the session read lock cherrypy.session.release_lock() # Don't buffer the (potentially sizeable) result in memory cherrypy.response.stream = True uri = en.buildEndpoint('search/jobs/%s/results/export' % job.sid, namespace=ns, owner=owner) stream = splunk.rest.streamingRequest(uri, getargs=request, postargs=None) return stream.readall() # returns a generator
def which_pdf_service(sessionKey, viewId=None, owner=None, namespace=None): pdfService = "none" if is_pdfgen_available(): if viewId is None or (len(viewId) == 0): pdfService = "pdfgen" else: import lxml.etree as et # this is either a simple dashboard, simple form, or advanced XML view # get the entity of the view and then check the root node entityId = entity.buildEndpoint('data/ui/views', viewId, namespace=namespace, owner=owner) viewEntity = entity.getEntity('data/ui/views', None, sessionKey=sessionKey, uri=entityId) data = viewEntity['eai:data'] if data: root = et.fromstring(unicode(data).encode('utf-8')) if root.tag == "dashboard": pdfService = "pdfgen" else: pdfService = "deprecated" else: pdfService = "deprecated" if pdfService is "deprecated" and not is_deprecated_service_available( sessionKey): pdfService = "none" return pdfService
def quick_and_dirty_call(uri, type, getargs, postargs, namespace, owner, method='GET', sessionKey=''): """""" qad_uri = buildEndpoint(uri, entityName='', namespace=namespace, owner=owner) try: serverResponse, serverContent = simpleRequest(qad_uri, sessionKey=sessionKey, getargs=getargs, postargs=postargs, method=method) except Exception, e: logger.debug('Could not construct quick_and_dirty uri: %s, %s' % (str(qad_uri), str(e))) raise CliArgError, 'Could not get app context'
def streamJobExport(self, job, assetType, **kwargs): """ Stream exported job results to the client (does not buffer the whole result in memory) """ ns = job.eaiacl["app"] sid = job.sid owner = job.eaiacl["owner"] uri = en.buildEndpoint("search/jobs/export", namespace=ns, owner=owner) request = {} request["output_mode"] = kwargs["outputMode"] request["f"] = kwargs["field_list"] try: count = int(kwargs.get("count")) if count > 0: request["count"] = count except ValueError: console.warn("Failed to parse count field for export count=%s" % count) pass # We're not going to read/write further from the user's session at this point # and streaming may take a while, so release the session read lock cherrypy.session.release_lock() # Don't buffer the (potentially sizeable) result in memory cherrypy.response.stream = True postargs = getargs = None if job.reportSearch is None and job.eventIsTruncated and (count == 0 or count > job.eventAvailableCount): # re-run the search to get the complete results uri = en.buildEndpoint("search/jobs/export", namespace=ns, owner=owner) request.update(job.request) if count > 0: request["search"] += "|head %s" % count postargs = request elif assetType == "event": # non-reporting search uri = en.buildEndpoint("search/jobs/%s/events/export" % job.sid, namespace=ns, owner=owner) getargs = request else: uri = en.buildEndpoint("search/jobs/%s/results/export" % job.sid, namespace=ns, owner=owner) getargs = request stream = rest.streamingRequest(uri, getargs=getargs, postargs=postargs) return stream.readall() # returns a generator
def setPanel(self, namespace, view_id, panel_type, panel_sequence, panel_class=None, action='edit', **panel_definition): ''' Provides management for view panel objects The HTTP signature for this method expects standard form params to match the property names used in the panel objects. All form params are inserted into a dict and passed into the panel object for processing POST /<namespace>/<view_id>/panel/<panel_sequence> &action=edit --> updates the existing panel at <panel_sequence> &action=delete --> deletes the panel at <panel_sequence> ==> returns JSON response ''' output = jsonresponse.JsonResponse() self.collatePanelOptions(panel_definition) try: username = cherrypy.session['user'].get('name') dash_id = en.buildEndpoint(VIEW_ENTITY_CLASS, view_id, namespace=namespace, owner=username) dashObject = Dashboard.get(dash_id) if action == 'edit': dashObject.set_panel(panel_sequence, panel_class, **panel_definition) elif action == 'delete': dashObject.delete_panel(panel_sequence) else: raise ValueError, 'Unknown action requested: %s' % action dashObject.save() output.addInfo(_('Successfully updated %s' % view_id)) except Exception, e: logger.exception(e) output.success = False output.addError( _('Unable to update panel at sequence %s: %s') % (panel_sequence, e))
def listJobs(self, restrictToSession=True, nocache=False, s=None, cachebuster=None, wait=True): ''' Returns a listing of jobs that the client needs to be aware of; listing is restricted by user session, and optionally filtered by a whitelist provided by the client ''' resp = JsonResponse() # dump out if no jobs are specified if not s: resp.data = [] return self.render_json(resp) if 0: uri = en.buildEndpoint('search/jobs', '') logger.error("uri: %s" % uri) serverResponse, serverContent = rest.simpleRequest( uri, getargs={ 'id': s, 'output_mode': 'json' }) return serverContent # normalize a single string into a list if isinstance(s, basestring): s = [s] # bypass the legacy sdk blocking for RUNNING state wait = splunk.util.normalizeBoolean(wait) # loop over all all requested jobs and ask server for status listing = [] for requestSID in s: try: job = splunk.search.getJob(requestSID, waitForRunning=wait) listing.append(job.toJsonable()) except splunk.ResourceNotFound: listing.append({'sid': requestSID, '__notfound__': True}) nocache = True # ensure we always bust the cache otherwise, multiple requests may not find out that the job doesn't exist resp.addError( _('Splunk could not find a job with sid=%s.') % requestSID) except Exception, e: logger.exception(e) resp.success = False resp.addError(str(e)) return self.render_json(resp)
def __init__(self): self.logger = log() self.session = requestsbak.Session() self.session.trust_env = False self.kvstoreUri = entity.buildEndpoint( entityClass=["storage", "collections", "data"], entityName="credentials", owner="nobody", namespace="SplunkAppForWazuh", hostPath=rest.makeSplunkdUri().strip("/")) self.sessionKey = splunk.getSessionKey()
def createTestUser(tz, sessionKey): uri = entity.buildEndpoint('authentication', 'users') userName = str(uuid.uuid1()) postargs = { "name": userName, "password": "******", "roles": "user", "tz": tz } (response, content) = rest.simpleRequest(uri, postargs=postargs, sessionKey=sessionKey) return userName
def delegate(self, delegate_endpoint, confInfo, method='POST', customAction='', args={}): user = self.context == admin.CONTEXT_APP_AND_USER and self.userName or "nobody" if self.requestedAction == admin.ACTION_CREATE: uri = en.buildEndpoint(delegate_endpoint, namespace=self.appName, owner=user) args['name'] = self.callerArgs.id else: uri = en.buildEndpoint(delegate_endpoint, entityName=self.callerArgs.id, namespace=self.appName, owner=self.userName) if len(customAction) > 0: uri += '/' + customAction # replace None with empty string to avoid being ignored by python rest api. SPL-57111 for k,v in self.callerArgs.items(): if k.startswith('_'): continue if isinstance(v, list): args[k] = v[0] if v[0] != None else '' else: args[k] = v if v != None else '' if method == 'GET': app = self.context != admin.CONTEXT_NONE and self.appName or "-" user = self.context == admin.CONTEXT_APP_AND_USER and self.userName or "-" thing = self.getEntities(None, None, uri, -1, app, user) for name, obj in thing.items(): ci = confInfo[name] for key, val in obj.items(): if not key.startswith('eai:'): ci[key] = str(val) if val else '' # fix perms if 'perms' in obj['eai:acl'] and not obj['eai:acl']['perms']: obj['eai:acl']['perms'] = {} ci.copyMetadata(obj) else: serverResponse, serverContent = self.simpleRequest(uri, method, args)
def getPreviewEvents(self, eventdata, stanza, attrs): filename = None try: f = tempfile.NamedTemporaryFile(delete=False) filename = f.name f.write(eventdata) f.close() # prefix all stanza attributes with 'props.' # give default sourcetype that prevents it from learning sourcetypes kwargs = { 'input.path': filename, 'output_mode': 'json', 'props.sourcetype': 'default' } for a, v in attrs.items(): if a == 'count': continue kwargs['props.%s' % a] = v uri = entity.buildEndpoint("indexing/preview", namespace=self._mynamespace, owner=self._owner) print "\tURL: %s ARGS: %s" % (uri, kwargs) serverResponse, serverContent = rest.simpleRequest( uri, method='POST', postargs=kwargs, sessionKey=self._sessionKey, raiseAllErrors=True) if serverResponse.status not in [200, 201]: raise Exception(serverResponse.status, serverResponse.messages) # {"messages":[{"type":"INFO","text":"1367872459.2"}]} json_content = json.loads(serverContent) jobid = json_content["messages"][0]["text"] job = splunk.search.getJob(jobid, sessionKey=self._sessionKey) events = job.events #print "\tJOB:", job #if len(events) == 0: # print "\tNO EVENTS!" #else: # print "\tEVENTS:", events[0] return events finally: if filename: os.unlink(filename)
def __init__(self): """Constructor.""" try: self.logger = log() self.session = requestsbak.Session() self.session.trust_env = False self.kvstoreUri = entity.buildEndpoint( entityClass=["storage", "collections", "data"], entityName="jobs", owner="nobody", namespace="SplunkAppForWazuh", hostPath=rest.makeSplunkdUri().strip("/") ) self.sessionKey = splunk.getSessionKey() except Exception as e: self.logger.error("bin.jobs_queu: Error in queue module constructor: %s" % (e))
def move(self, confInfo, **params): user, app = self.user_app() args = self.getCallerArgs() if hasattr(self, "encode"): args = self.encode(args) postArgs = {"app": args["app"], "user": args["user"]} path = entity.buildEndpoint( self.endpoint, entityName=self.callerArgs.id, namespace=app, owner=user ) path += "/move" response, _ = rest.simpleRequest( path, sessionKey=self.getSessionKey(), method="POST", postargs=postArgs ) if response.status != 200: exc = RESTException(response.status, response.messages) RH_Err.ctl(-1, exc, logLevel=logging.INFO)
def listJobs(self, restrictToSession=True, nocache=False, s=None, cachebuster=None): """ Returns a listing of jobs that the client needs to be aware of; listing is restricted by user session, and optionally filtered by a whitelist provided by the client """ resp = JsonResponse() # dump out if no jobs are specified if not s: resp.data = [] return self.render_json(resp) if 0: uri = en.buildEndpoint("search/jobs", "") logger.error("uri: %s" % uri) serverResponse, serverContent = rest.simpleRequest(uri, getargs={"id": s, "output_mode": "json"}) return serverContent # normalize a single string into a list if isinstance(s, basestring): s = [s] # loop over all all requested jobs and ask server for status listing = [] for requestSID in s: try: job = splunk.search.getJob(requestSID) listing.append(job.toJsonable()) except splunk.ResourceNotFound: listing.append({"sid": requestSID, "__notfound__": True}) nocache = ( True ) # ensure we always bust the cache otherwise, multiple requests may not find out that the job doesn't exist resp.addError(_("Splunk could not find a job with sid=%s.") % requestSID) except Exception, e: logger.exception(e) resp.success = False resp.addError(str(e)) return self.render_json(resp)
def listJobs(self, restrictToSession=True, nocache=False, s=None, cachebuster=None, wait=True): ''' Returns a listing of jobs that the client needs to be aware of; listing is restricted by user session, and optionally filtered by a whitelist provided by the client ''' resp = JsonResponse() # dump out if no jobs are specified if not s: resp.data = [] return self.render_json(resp) if 0: uri = en.buildEndpoint('search/jobs', '') logger.error("uri: %s" % uri) serverResponse, serverContent = rest.simpleRequest(uri, getargs={'id':s, 'output_mode':'json'}) return serverContent # normalize a single string into a list if isinstance(s, basestring): s = [s] # bypass the legacy sdk blocking for RUNNING state wait = splunk.util.normalizeBoolean(wait) # loop over all all requested jobs and ask server for status listing = [] for requestSID in s: try: job = splunk.search.getJob(requestSID, waitForRunning=wait) listing.append(job.toJsonable()) except splunk.ResourceNotFound: listing.append({'sid': requestSID, '__notfound__': True}) nocache = True # ensure we always bust the cache otherwise, multiple requests may not find out that the job doesn't exist resp.addError(_('Splunk could not find a job with sid=%s.') % requestSID) except Exception, e: logger.exception(e) resp.success = False resp.addError(str(e)) return self.render_json(resp)
def _apply(self, migration, force=False): """ invokes validate() and teardown() of an available migration plugin instance """ try: migration.validate() migration.teardown() except Exception as e: self.rollback() raise e else: # migration.dst is the resulting entity after the migration is applied dst_entity = migration.dst entity_path = self.migration_conf.dest_conf update = False uri = en.buildEndpoint(entity_path, entityName=dst_entity.name, namespace=NAMESPACE, owner=OWNER) if self._check_resource_exists(uri, sessionKey=self.session_key): if force: update = True else: raise ResourceExistsException if update: entity = en.Entity(entity_path, dst_entity.name, namespace=NAMESPACE, contents=dst_entity.content, owner=OWNER) else: entity = en.Entity(entity_path, '_new', namespace=NAMESPACE, contents=dst_entity.content, owner=OWNER) entity['name'] = dst_entity.name # to bypass a funny check in splunk.entity.getFullPath() entity['eai:acl'] = {} en.setEntity(entity, sessionKey=self.session_key) self.migration_conf.update_state(self.resource_url, migration.migration_level)
def migrate(self, src_endpoint, migration_ids, filter=None, force=False): entities = en.getEntities(src_endpoint, namespace='dbx', owner='nobody', search=filter, count=0) if not entities: print "No entity found" return # iterate source entires for key, entity in entities.iteritems(): for migration_id in migration_ids: path = en.buildEndpoint(entity.path, entityName=entity.name, namespace='dbx', owner='nobody') controller = MigrationController(migration_id, path, self.session_key) try: result = controller.migrate(force=force) if result is None: print "{} of {} has been migrated already. Use --force to overwrite.".format( migration_id, path) else: print "{} of {} has been migrated with following attributes:\n".format( migration_id, path) self.pretty_print(result) except ResourceExistsException: print "{} already exists in {} for DB Connect 2. Use --force to overwrite.".format( key, migration_id) except EntityCanNotBeMigrated: pass except Exception as e: print "an exception occurred while migrating {} from {}: {}".format( migration_id, path, e)
def realize(valuesForTemplate, ssContent, sessionKey, namespace, owner, argvals): stringsForPost = {} if ssContent.get('action.email.message'): stringsForPost['action.email.message'] = ssContent['action.email.message'] if ssContent.get('action.email.cc'): stringsForPost['action.email.cc'] = ssContent['action.email.cc'] if ssContent.get('action.email.bcc'): stringsForPost['action.email.bcc'] = ssContent['action.email.bcc'] if ssContent.get('action.email.to'): stringsForPost['action.email.to'] = ssContent['action.email.to'] if ssContent.get('action.email.subject'): stringsForPost['action.email.subject'] = ssContent['action.email.subject'] if ssContent.get('action.email.footer.text'): stringsForPost['action.email.footer.text'] = ssContent['action.email.footer.text'] realizeURI = entity.buildEndpoint([ 'template', 'realize' ]) postargs = valuesForTemplate postargs['output_mode'] = 'json' postargs['conf.recurse'] = 0 try: for key, value in stringsForPost.iteritems(): postargs['name'] = value headers, body = simpleRequest( realizeURI, method='POST', postargs=postargs, sessionKey=sessionKey ) body = json.loads(body) ssContent[key] = body['entry'][0]['content']['eai:data'] except Exception, e: logger.error(e) # SPL-96721: email subject didn't get replaced, reset it to ssname if ssContent.get('action.email.subject') == stringsForPost.get('action.email.subject'): ssContent['action.email.subject'] = "Splunk Alert:"+argvals['ssname'] ssContent['action.email.message'] = ssContent['action.email.message'] + "\n\nNOTE: The dynamic substitution of the email subject failed because of failed token substitution. A generic subject has been used instead. Please check splunkd.log for additional details."
def setPanel(self, namespace, view_id, panel_type, panel_sequence, panel_class=None, action='edit', **panel_definition): ''' Provides management for view panel objects The HTTP signature for this method expects standard form params to match the property names used in the panel objects. All form params are inserted into a dict and passed into the panel object for processing POST /<namespace>/<view_id>/panel/<panel_sequence> &action=edit --> updates the existing panel at <panel_sequence> &action=delete --> deletes the panel at <panel_sequence> ==> returns JSON response ''' output = jsonresponse.JsonResponse() self.collatePanelOptions(panel_definition) try: username = cherrypy.session['user'].get('name') dash_id = en.buildEndpoint(VIEW_ENTITY_CLASS, view_id, namespace=namespace, owner=username) dashObject = Dashboard.get(dash_id) if action == 'edit': dashObject.set_panel(panel_sequence, panel_class, **panel_definition) elif action == 'delete': dashObject.delete_panel(panel_sequence) else: raise ValueError, 'Unknown action requested: %s' % action dashObject.save() output.addInfo(_('Successfully updated %s' % view_id)) except Exception, e: logger.exception(e) output.success = False output.addError(_('Unable to update panel at sequence %s: %s') % (panel_sequence, e))
def createPanel(self, namespace, view_id, panel_type, panel_class, **panel_definition): ''' Create a new panel to add to an existing dashboard POST /<namespace>/<view_id>/<panel_type=panel> &panel_class={table | chart | html | event | list} &<panel_property>=<property_value> --> creates a new panel the <panel_property> is a direct map to the panel XML data ==> returns a JSON response ''' output = jsonresponse.JsonResponse() try: if panel_type != 'panel': raise ValueError, 'Only panel type "panel" is currently supported' # support all options self.collatePanelOptions(panel_definition) # get dashboard and create panel username = cherrypy.session['user'].get('name') dash_id = en.buildEndpoint(VIEW_ENTITY_CLASS, view_id, namespace=namespace, owner=username) dashObject = Dashboard.get(dash_id) dashObject.create_panel(type=panel_class, **panel_definition) dashObject.save() except Exception, e: logger.exception(e) output.success = False output.addError(_('Unable to add panel: %s') % e)
def parseSearchToXML(search, hostPath=None, sessionKey=None, parseOnly='t', timeline=None, namespace=None, owner=None): """ Given a valid search string, return the XML from the splunk parsing endpoint that represents the search. """ if search == None or len(search) == 0: return None if not owner: owner = auth.getCurrentUser()['name'] uri = entity.buildEndpoint('/search/parser', namespace=namespace, owner=owner) if hostPath: uri = splunk.mergeHostPath(hostPath) + uri args = {'q': search, 'parse_only': parseOnly} if timeline is not None: args['timeline'] = timeline serverResponse, serverContent = rest.simpleRequest(uri, getargs=args, sessionKey=sessionKey) #print "SERVERCONTENT:", serverContent # normal messages from splunkd are propogated via SplunkdException; if 400 <= serverResponse.status < 500: root = et.fromstring(serverContent) extractedMessages = rest.extractMessages(root) for msg in extractedMessages: raise splunk.SearchException, msg['text'] return serverContent
def getPanel(self, namespace, view_id, panel_type, panel_sequence, **unused): ''' Returns a dashboard panel config GET /<namespace>/<view_id>/panel/<panel_sequence> --> panel config for panel at <panel_sequence> ==> returns a JSON response ''' output = jsonresponse.JsonResponse() try: username = cherrypy.session['user'].get('name') dash_id = en.buildEndpoint(VIEW_ENTITY_CLASS, view_id, namespace=namespace, owner=username) dashObject = Dashboard.get(dash_id) output.data = dashObject.get_panel(panel_sequence) except IndexError, e: cherrypy.response.status = 404 output.success = False output.addError(_('Requested panel %s does not exist') % panel_sequence)
def createPanel(self, namespace, view_id, panel_type, panel_class, **panel_definition): ''' Create a new panel to add to an existing dashboard POST /<namespace>/<view_id>/<panel_type=panel> &panel_class={table | chart | html | event | list} &<panel_property>=<property_value> --> creates a new panel the <panel_property> is a direct map to the panel XML data ==> returns a JSON response ''' output = jsonresponse.JsonResponse() try: if panel_type != 'panel': raise ValueError, 'Only panel type "panel" is currently supported' # support all options self.collatePanelOptions(panel_definition) # get dashboard and create panel username = cherrypy.session['user'].get('name') dash_id = en.buildEndpoint(VIEW_ENTITY_CLASS, view_id, namespace=namespace, owner=username) dashObject = Dashboard.get(dash_id) dashObject.create_panel( type=panel_class, **panel_definition) dashObject.save() except Exception, e: logger.exception(e) output.success = False output.addError(_('Unable to add panel: %s') % e)
def realize(valuesForTemplate, ssContent, sessionKey, namespace, owner): stringsForPost = {} if ssContent.get('action.email.message'): stringsForPost['action.email.message'] = ssContent[ 'action.email.message'] if ssContent.get('action.email.cc'): stringsForPost['action.email.cc'] = ssContent['action.email.cc'] if ssContent.get('action.email.bcc'): stringsForPost['action.email.bcc'] = ssContent['action.email.bcc'] if ssContent.get('action.email.to'): stringsForPost['action.email.to'] = ssContent['action.email.to'] if ssContent.get('action.email.subject'): stringsForPost['action.email.subject'] = ssContent[ 'action.email.subject'] if ssContent.get('action.email.footer.text'): stringsForPost['action.email.footer.text'] = ssContent[ 'action.email.footer.text'] realizeURI = entity.buildEndpoint(['template', 'realize']) postargs = valuesForTemplate postargs['output_mode'] = 'json' postargs['conf.recurse'] = 0 try: for key, value in stringsForPost.iteritems(): postargs['name'] = value headers, body = simpleRequest(realizeURI, method='POST', postargs=postargs, sessionKey=sessionKey) body = json.loads(body) ssContent[key] = body['entry'][0]['content']['eai:data'] except Exception, e: logger.info(e)
def realize(valuesForTemplate, ssContent, sessionKey, namespace, owner): stringsForPost = {} if ssContent.get('action.email.message'): stringsForPost['action.email.message'] = ssContent['action.email.message'] if ssContent.get('action.email.cc'): stringsForPost['action.email.cc'] = ssContent['action.email.cc'] if ssContent.get('action.email.bcc'): stringsForPost['action.email.bcc'] = ssContent['action.email.bcc'] if ssContent.get('action.email.to'): stringsForPost['action.email.to'] = ssContent['action.email.to'] if ssContent.get('action.email.subject'): stringsForPost['action.email.subject'] = ssContent['action.email.subject'] if ssContent.get('action.email.footer.text'): stringsForPost['action.email.footer.text'] = ssContent['action.email.footer.text'] realizeURI = entity.buildEndpoint([ 'template', 'realize' ]) postargs = valuesForTemplate postargs['output_mode'] = 'json' postargs['conf.recurse'] = 0 try: for key, value in stringsForPost.iteritems(): postargs['name'] = value headers, body = simpleRequest( realizeURI, method='POST', postargs=postargs, sessionKey=sessionKey ) body = json.loads(body) ssContent[key] = body['entry'][0]['content']['eai:data'] except Exception, e: logger.info(e)
def getContainer(self, namespace, view_id, mode='', **unused): ''' Renders the dashboard edit page GET /<namespace>/<view_id> ==> HTML form page with dashboard edit form (labels, panels) and master panel edit form (hidden) GET /api/<namespace>/<view_id> ==> JSON structure of dashboard config (unused?) ''' # serve data feed output = jsonresponse.JsonResponse() try: username = cherrypy.session['user'].get('name') dash_id = en.buildEndpoint(VIEW_ENTITY_CLASS, view_id, namespace=namespace, owner=username) dashObject = Dashboard.get(dash_id) output.data = dashObject._obj.toJsonable() except splunk.ResourceNotFound: cherrypy.response.status = 404 output.success = False output.addError(_('View %s was not found') % view_id) return self.render_json(output) # serve template page if cherrypy.request.is_api: return self.render_json(output) else: cherrypy.response.headers['content-type'] = MIME_HTML # get supporting assets savedSearches = en.getEntities('saved/searches', namespace=namespace, count=-1, search="is_visible=1") for savedSearch in savedSearches: acl = savedSearches[savedSearch]['eai:acl'] if dashObject.metadata.sharing == 'user': continue if dashObject.metadata.sharing == 'app' and acl[ 'sharing'] == 'user': savedSearches[savedSearch]['dq'] = True continue if dashObject.metadata.sharing == 'global' and acl[ 'sharing'] != 'global': savedSearches[savedSearch]['dq'] = True continue if dashObject.metadata.perms['read'].count( '*') > 0 and acl['perms']['read'].count('*') == 0: savedSearches[savedSearch]['dq'] = True continue #dashboardObject = splunk.entity.getEntity('data/ui/views', view_id, namespace=APP['id'], owner=cherrypy.session['user'].get('name')) #dashOwner = dashboardObject['eai:acl'].get('owner', 'nobody') editLink = self.make_url( ['manager', namespace, 'data/ui/views', view_id], _qs=dict(action='edit', url=self.make_url(['app', namespace, view_id]), redirect_override="/app/%s/%s" % (namespace, view_id))) permissionsLink = self.make_url( [ 'manager', 'permissions', namespace, 'data/ui/views', view_id ], _qs=dict( uri=en.buildEndpoint('data/ui/views', view_id, namespace=namespace, owner=dashObject.metadata.owner))) return self.render_template( 'viewmaster/edit_dashboard.html', { 'namespace': namespace, 'view_id': view_id, 'view_object': dashObject._obj, 'APP': { 'id': namespace }, 'savedSearches': savedSearches, 'editLink': editLink, 'permissionsLink': permissionsLink, 'mode': mode, })
def createContainer(self, namespace, view_id='', view_label='', container_type='dashboard'): ''' Handles dashboard creation GET /<namespace> ==> HTML template form to input create data POST /<namespace> ==> Saves the HTML input values from GET == returns JSON response ''' # # serve template # if cherrypy.request.method == 'GET': return self.render_template('viewmaster/create_dashboard.html', {'namespace': namespace}) # # handle create view # output = jsonresponse.JsonResponse() # clean inputs view_id = re.sub(r'[^\w]', '', view_id) view_label = view_label.strip() or view_id if view_id == '': output.success = False output.addError(_('Dashboard ID cannot be empty')) return self.render_json(output) # check that view doesn't already exist try: username = cherrypy.session['user'].get('name') dash_id = en.buildEndpoint(VIEW_ENTITY_CLASS, view_id, namespace=namespace, owner=username) Dashboard.get(dash_id) output.success = False output.addError( _('Cannot create new %(container_type)s: %(view_id)s already exists') \ % {'container_type': container_type, 'view_id': view_id}) return self.render_json(output) except splunk.ResourceNotFound: pass # generate new try: view = Dashboard(namespace, username, view_id) view.label = view_label view.save() output.data = {'view_id': view_id, 'view_label': view_label} output.addInfo(_('Successfully created new %(container_type)s: %(view_id)s') \ % {'container_type': container_type, 'view_id': view_id}) logger.info('Created new %s: namespace=%s id=%s label=%s' % (container_type, namespace, view_id, view_label)) except Exception, e: logger.exception(e) output.success = False output.addError(_('Unable to create dashboard: %s') % e)
def streamJobExport(self, job, assetType, **kwargs): """ Stream exported job results to the client (does not buffer the whole result in memory) """ ns = job.eaiacl['app'] sid = job.sid owner = job.eaiacl['owner'] request = {} request['output_mode'] = kwargs['outputMode'] # SPL-79832 when exporting xml splunkd requires additional request argument # to generate valid xml if request['output_mode'] == "xml": request['export_xml_with_wrapper'] = 1 request['f'] = kwargs['field_list'] if 'output_time_format' in kwargs: request['output_time_format'] = kwargs['output_time_format'] else: request['output_time_format'] = i18n.ISO8609_MICROTIME try: count = int(kwargs.get('count')) if count>0: request['count'] = count except ValueError: logger.warn("Failed to parse count field for export count=%s" % count) pass # We're not going to read/write further from the user's session at this point # and streaming may take a while, so release the session read lock cherrypy.session.release_lock() # Don't buffer the (potentially sizeable) result in memory cherrypy.response.stream = True postargs = getargs = None if job.reportSearch is None and ((job.eventIsTruncated and (count == 0 or count > job.eventAvailableCount)) or job.isRemoteTimeline or not job.isDone): # re-run the search to get the complete results uri = en.buildEndpoint('search/jobs/export', namespace=ns, owner=owner) request.update(job.request) if count > 0: request['search'] += '|head %s' % count postargs = request # if re-running the search for exporting events, # ensure we do not run the search with following arguments ignoreArgs = ['auto_cancel', 'max_count'] for iarg in ignoreArgs: if iarg in postargs: logger.debug("Dropping argument %s from postargs" % iarg) del postargs[iarg] elif assetType == 'event': # non-reporting search uri = en.buildEndpoint('search/jobs/%s/events/export' % job.sid, namespace=ns, owner=owner) getargs = request else: uri = en.buildEndpoint('search/jobs/%s/results/export' % job.sid, namespace=ns, owner=owner) getargs = request export_timeout = cherrypy.config.get('export_timeout') logger.debug('Export timeout =%s' % export_timeout) if (export_timeout != None) : export_timeout = int(export_timeout) stream = rest.streamingRequest(uri, getargs=getargs, postargs=postargs, timeout=export_timeout) return stream.readall() # returns a generator
for ph in prehooks: rcHooks.__dict__[ph](cmd, obj, argList) eaiArgsList = rcHooks.map_args_cli_2_eai(args, {}, argList) logger.debug('after prehooks, eaiArgsList: %s' % str(eaiArgsList)) #first check if the eai_id needs to be obtained by another request!!! if type(lf_eai_id) == types.DictType: #oh crappp... eai_id_uri = buildEndpoint(lf_eai_id['uri'], entityName='', namespace=namespace, owner=owner, search=search, count=count, offset=offset, sort_key=sort_key, sort_dir=sort_dir) eai_id_method = GLOBAL_ACTIONS['%s' % lf_eai_id['type']] try: eai_id_serverResponse, eai_id_serverContent = simpleRequest( eai_id_uri, sessionKey=sessionKey, getargs='', postargs='', method=eai_id_method) except Exception, e: logger.debug(
def sendEmail(results, settings): keywords, argvals = splunk.Intersplunk.getKeywordsAndOptions() for key in argvals: argvals[key] = unquote(argvals[key]) namespace = settings['namespace'] owner = settings['owner'] sessionKey = settings['sessionKey'] sid = settings['sid'] ssname = argvals.get('ssname') isScheduledView = False if ssname: # populate content with savedsearch if '_ScheduledView__' in ssname or argvals.get('pdfview'): if '_ScheduledView__' in ssname: ssname = ssname.replace('_ScheduledView__', '') else: ssname = argvals.get('pdfview') uri = entity.buildEndpoint( [ 'scheduled', 'views', ssname], namespace=namespace, owner=owner ) isScheduledView = True else: uri = entity.buildEndpoint( [ 'saved', 'searches', ssname ], namespace=namespace, owner=owner ) responseHeaders, responseBody = simpleRequest(uri, method='GET', getargs={'output_mode':'json'}, sessionKey=sessionKey) savedSearch = json.loads(responseBody) ssContent = savedSearch['entry'][0]['content'] # set type of saved search if isScheduledView: ssContent['type'] = 'view' elif savedSearchJSONIsAlert(savedSearch): ssContent['type'] = 'alert' else: ssContent['type'] = 'report' # remap needed attributes that are not already on the content ssContent['name'] = ssname ssContent['app'] = savedSearch['entry'][0]['acl'].get('app') ssContent['owner'] = savedSearch['entry'][0]['acl'].get('owner') # The footer.text key will always exist for type alert and report. # It may not exist for scheduled views created before 6.1 therefore the schedule view default footer.text # should be set if the key does not exist. # This can be removed once migration has happened to ensure scheduled views always have the footer.text attribute ssContent['action.email.footer.text'] = ssContent.get('action.email.footer.text', "If you believe you've received this email in error, please see your Splunk administrator.\r\n\r\nsplunk > the engine for machine data") # The message key will always exist for type alert and report. # It may not exist for scheduled views created before 6.1 therefore the schedule view default message # should be set if the key does not exist. # This can be removed once migration has happened to ensure scheduled views always have the message.view attribute ssContent['action.email.message'] = ssContent.get('action.email.message.' + ssContent.get('type'), 'A PDF was generated for $name$') if normalizeBoolean(ssContent.get('action.email.useNSSubject', False)): ssContent['action.email.subject'] = ssContent['action.email.subject.' + ssContent.get('type')] # prior to 6.1 the results link was sent as the argval sslink, must check both results_link # and sslink for backwards compatibility ssContent['results_link'] = argvals.get('results_link', argvals.get('sslink', '')) if normalizeBoolean(ssContent['results_link']) and normalizeBoolean(ssContent['type']): split_results_path = urllib.splitquery(ssContent.get('results_link'))[0].split('/') view_path = '/'.join(split_results_path[:-1]) + '/' ssType = ssContent.get('type') if ssType == 'alert': ssContent['view_link'] = view_path + 'alert?' + urllib.urlencode({'s': savedSearch['entry'][0]['links'].get('alternate')}) elif ssType == 'report': ssContent['view_link'] = view_path + 'report?' + urllib.urlencode({'s': savedSearch['entry'][0]['links'].get('alternate'), 'sid': sid}) elif ssType == 'view': ssContent['view_link'] = view_path + ssContent['name'] else: ssContent['view_link'] = view_path + 'search' else: #assumes that if no ssname then called from searchbar ssContent = { 'type': 'searchCommand', 'view_link': '', 'action.email.sendresults': False, 'action.email.sendpdf': False, 'action.email.sendcsv': False, 'action.email.inline': True, 'action.email.format': 'table', 'action.email.subject': 'Splunk Results', 'action.email.footer.text': "If you believe you've received this email in error, please see your Splunk administrator.\r\n\r\nsplunk > the engine for machine data" } ssContent['trigger_date'] = None ssContent['trigger_timeHMS'] = None ssContent['trigger_time'] = argvals.get('trigger_time') ssContent if normalizeBoolean(ssContent['trigger_time']): try: triggerSeconds = time.localtime(float(ssContent['trigger_time'])) ssContent['trigger_date'] = time.strftime("%B %d, %Y", triggerSeconds) ssContent['trigger_timeHMS'] = time.strftime("%I:%M:%S", triggerSeconds) except Exception, e: logger.info(e)
def deleteTestUser(userName, sessionKey): uri = entity.buildEndpoint(['authentication', 'users'], userName) (response, content) = rest.simpleRequest(uri, sessionKey=sessionKey, method="DELETE")
def streamJobExport(job, kwargs): """ Stream exported job results to the client (does not buffer the whole result in memory) """ ns = job.eaiacl['app'] owner = job.eaiacl['owner'] uri = en.buildEndpoint('search/jobs/export', namespace=ns, owner=owner) request = {} request['output_mode'] = kwargs['output_mode'] try: count = int(kwargs.get('count')) if count > 0: request['count'] = count except ValueError: logger.warn("Failed to parse count field for export, count=%s" % str(kwargs.get('count'))) pass try: if 'offset' in kwargs: offset = int(kwargs.get('offset')) request['offset'] = offset except ValueError: logger.warn("Failed to parse offset field for export, offset=%s" % str(kwargs.get('offset'))) pass try: if 'field_list' in kwargs: request['f'] = kwargs.get('field_list') except: logger.warn( "Failed to parse field_list field for export, field_list=%s" % str(kwargs.get('field_list'))) postargs = getargs = None #SPL-51662 if job.reportSearch is None and ( (job.eventIsTruncated and (count == 0 or count > job.eventAvailableCount)) or splunk.util.normalizeBoolean(job.isRemoteTimeline)): # re-run the search to get the complete results uri = en.buildEndpoint('search/jobs/export', namespace=ns, owner=owner) request.update(job.request) if count > 0: request['search'] += '|head %s' % count postargs = request elif job.reportSearch: uri = en.buildEndpoint('search/jobs/%s/results/export' % job.sid, namespace=ns, owner=owner) getargs = request else: uri = en.buildEndpoint('search/jobs/%s/events/export' % job.sid, namespace=ns, owner=owner) getargs = request stream = rest.streamingRequest(uri, getargs=getargs, postargs=postargs) return stream.readall() # returns a generator
import argparse import getpass import xml.etree.ElementTree as ET import splunk.entity as entity import splunk.rest as rest parser = argparse.ArgumentParser(description='GT LDAP') parser.add_argument( '--update', action='store_true', ) args = parser.parse_args() u = raw_input("Splunk User:"******"username": u, "password": p }) root = ET.fromstring(c) sessionkey = root[0].text uri = entity.buildEndpoint(['storage', 'passwords']) user = raw_input("REST User:"******"REST Host:") password = getpass.getpass(prompt="REST Password:"******"name": user, "realm": host, "password": password} if args.update: uri = entity.buildEndpoint( ['storage', 'passwords', "%s:%s:" % (host, user)])
def sendEmail(results, settings): keywords, argvals = splunk.Intersplunk.getKeywordsAndOptions() for key in argvals: argvals[key] = unquote(argvals[key]) namespace = settings['namespace'] owner = settings['owner'] sessionKey = settings['sessionKey'] sid = settings['sid'] ssname = argvals.get('ssname') isScheduledView = False if ssname: # populate content with savedsearch if '_ScheduledView__' in ssname or argvals.get('pdfview'): if '_ScheduledView__' in ssname: ssname = ssname.replace('_ScheduledView__', '') else: ssname = argvals.get('pdfview') uri = entity.buildEndpoint(['scheduled', 'views', ssname], namespace=namespace, owner=owner) isScheduledView = True else: uri = entity.buildEndpoint(['saved', 'searches', ssname], namespace=namespace, owner=owner) responseHeaders, responseBody = simpleRequest( uri, method='GET', getargs={'output_mode': 'json'}, sessionKey=sessionKey) savedSearch = json.loads(responseBody) ssContent = savedSearch['entry'][0]['content'] # set type of saved search if isScheduledView: ssContent['type'] = 'view' elif savedSearchJSONIsAlert(savedSearch): ssContent['type'] = 'alert' else: ssContent['type'] = 'report' # remap needed attributes that are not already on the content ssContent['name'] = ssname ssContent['app'] = savedSearch['entry'][0]['acl'].get('app') ssContent['owner'] = savedSearch['entry'][0]['acl'].get('owner') # The footer.text key will always exist for type alert and report. # It may not exist for scheduled views created before 6.1 therefore the schedule view default footer.text # should be set if the key does not exist. # This can be removed once migration has happened to ensure scheduled views always have the footer.text attribute ssContent['action.email.footer.text'] = ssContent.get( 'action.email.footer.text', "If you believe you've received this email in error, please see your Splunk administrator.\r\n\r\nsplunk > the engine for machine data" ) # The message key will always exist for type alert and report. # It may not exist for scheduled views created before 6.1 therefore the schedule view default message # should be set if the key does not exist. # This can be removed once migration has happened to ensure scheduled views always have the message.view attribute ssContent['action.email.message'] = ssContent.get( 'action.email.message.' + ssContent.get('type'), 'A PDF was generated for $name$') if normalizeBoolean(ssContent.get('action.email.useNSSubject', False)): ssContent['action.email.subject'] = ssContent[ 'action.email.subject.' + ssContent.get('type')] # prior to 6.1 the results link was sent as the argval sslink, must check both results_link # and sslink for backwards compatibility ssContent['results_link'] = argvals.get('results_link', argvals.get('sslink', '')) if normalizeBoolean(ssContent['results_link']) and normalizeBoolean( ssContent['type']): split_results_path = urllib.splitquery( ssContent.get('results_link'))[0].split('/') view_path = '/'.join(split_results_path[:-1]) + '/' ssType = ssContent.get('type') if ssType == 'alert': ssContent[ 'view_link'] = view_path + 'alert?' + urllib.urlencode({ 's': savedSearch['entry'][0]['links'].get('alternate') }) elif ssType == 'report': ssContent[ 'view_link'] = view_path + 'report?' + urllib.urlencode({ 's': savedSearch['entry'][0]['links'].get('alternate'), 'sid': sid }) elif ssType == 'view': ssContent['view_link'] = view_path + ssContent['name'] else: ssContent['view_link'] = view_path + 'search' else: #assumes that if no ssname then called from searchbar ssContent = { 'type': 'searchCommand', 'view_link': '', 'action.email.sendresults': False, 'action.email.sendpdf': False, 'action.email.sendcsv': False, 'action.email.inline': True, 'action.email.format': 'table', 'action.email.subject': 'Splunk Results', 'action.email.footer.text': "If you believe you've received this email in error, please see your Splunk administrator.\r\n\r\nsplunk > the engine for machine data" } ssContent['trigger_date'] = None ssContent['trigger_timeHMS'] = None ssContent['trigger_time'] = argvals.get('trigger_time') ssContent if normalizeBoolean(ssContent['trigger_time']): try: triggerSeconds = time.localtime(float(ssContent['trigger_time'])) ssContent['trigger_date'] = time.strftime("%B %d, %Y", triggerSeconds) ssContent['trigger_timeHMS'] = time.strftime( "%I:%M:%S", triggerSeconds) except Exception, e: logger.info(e)
ssContent['action.email.use_tls'] = normalizeBoolean( argvals.get('use_tls')) ssContent['graceful'] = normalizeBoolean(argvals.get('graceful', 0)) #if there is no results_link then do not incude it if not normalizeBoolean(ssContent.get('results_link')): ssContent['action.email.include.results_link'] = False #need for backwards compatibility format = ssContent.get('action.email.format') if format == 'html' or format == 'plain' or format == 'text': ssContent['action.email.format'] = 'table' #fetch server info uriToServerInfo = entity.buildEndpoint(['server', 'info']) serverInfoHeaders, serverInfoBody = simpleRequest( uriToServerInfo, method='GET', getargs={'output_mode': 'json'}, sessionKey=sessionKey) serverInfo = json.loads(serverInfoBody) serverInfoContent = serverInfo['entry'][0]['content'] #fetch job info jobResponseHeaders = {} jobResponseBody = {'entry': [{'content': {}}]} if sid: uriToJob = entity.buildEndpoint(['search', 'jobs', sid], namespace=namespace,
def dispatchSavedSearch(searchName, namespace=None, owner=None, hostPath=None, sessionKey=None, forceHistoricSearch=False, overrideNowTime=None): """ start a saved search returns a tuple (sid, dispatchedJob) dispatchedJob is true unless the saved search job id returned is the historical artifact of a scheduled search """ savedSearchModel = getSavedSearch(searchName, namespace=namespace, owner=owner, sessionKey=sessionKey) # check for scheduled saved search if savedSearchModel.schedule and savedSearchModel.schedule.is_scheduled: historicJob = saved.getJobForSavedSearch(searchName, namespace=namespace, owner=owner, hostPath=hostPath, sessionKey=sessionKey, useHistory=True) if historicJob != None: return (historicJob, False) args = {} if forceHistoricSearch: # don't dispatch a RT search. override dispatch arguments with non-RT timerange logger.debug("dispatchSavedSearch namespace=%s owner=%s" % (namespace, owner)) if savedSearchModel.dispatch and savedSearchModel.dispatch.earliest_time: args[ 'dispatch.earliest_time'] = savedSearchModel.dispatch.earliest_time.replace( u'rt', '') if savedSearchModel.dispatch and savedSearchModel.dispatch.latest_time: args[ 'dispatch.latest_time'] = savedSearchModel.dispatch.latest_time.replace( u'rt', '') logger.debug("dispatchSavedSearch args=%s" % str(args)) if overrideNowTime != None: args['dispatch.now'] = int(overrideNowTime) # assemble params uri = entity.buildEndpoint(SAVED_SEARCH_ENTITY_CLASS, searchName, namespace, owner) uri = uri + "/dispatch" logger.debug("dispatchSavedSearch> uri: " + uri) serverResponse, serverContent = rest.simpleRequest(uri, method="POST", sessionKey=sessionKey, rawResult=True, postargs=args) logger.debug("dispatchSavedSearch> serverResponse: " + str(serverResponse)) logger.debug("dispatchSavedSearch> serverContent: " + str(serverContent)) root = et.fromstring(serverContent) # catch quota overage; TODO: do something nicer if serverResponse.status == 503: extractedMessages = rest.extractMessages(root) for msg in extractedMessages: raise splunk.QuotaExceededException, msg['text'] # normal messages from splunkd are propogated via SplunkdException; if 400 <= serverResponse.status < 600: extractedMessages = rest.extractMessages(root) for msg in extractedMessages: raise splunk.SearchException, msg['text'] # get the search ID sid = root.findtext('sid').strip() # instantiate result object result = splunk.search.SearchJob(sid, hostPath, sessionKey, namespace, owner, dispatchArgs=args) return (result, True)
if argvals.get('use_tls'): ssContent['action.email.use_tls'] = normalizeBoolean(argvals.get('use_tls')) ssContent['graceful'] = normalizeBoolean(argvals.get('graceful', 0)) #if there is no results_link then do not incude it if not normalizeBoolean(ssContent.get('results_link')): ssContent['action.email.include.results_link'] = False #need for backwards compatibility format = ssContent.get('action.email.format') if format == 'html' or format == 'plain' or format == 'text': ssContent['action.email.format'] = 'table' #fetch server info uriToServerInfo = entity.buildEndpoint(['server', 'info']) serverInfoHeaders, serverInfoBody = simpleRequest(uriToServerInfo, method='GET', getargs={'output_mode':'json'}, sessionKey=sessionKey) serverInfo = json.loads(serverInfoBody) serverInfoContent = serverInfo['entry'][0]['content'] #fetch job info jobResponseHeaders = {} jobResponseBody = { 'entry': [ { 'content': {} } ] } if sid:
def setContainer(self, namespace, view_id, action, view_json=None): ''' Provides support to modify dashboard configs POST /<namespace>/<view_id> &action=delete --> deletes the current view &action=edit --> updates the current view config (view JSON object) ==> returns a JSON response ''' output = jsonresponse.JsonResponse() try: username = cherrypy.session['user'].get('name') dash_id = en.buildEndpoint(VIEW_ENTITY_CLASS, view_id, namespace=namespace, owner=username) dashObject = Dashboard.get(dash_id) if action == 'delete': dashObject.delete() output.addInfo(_('Successfully deleted %s') % view_id) elif action == 'edit': # convert incoming JSON to native struct; clean strings, view_json = json.loads(view_json) if view_json.get('label'): dashObject.label = view_json['label'].strip() if view_json.get('refresh'): dashObject.refresh = int(view_json['refresh'].strip()) # handle panel reordering; wrap number of columns to the max # column constraint newPanelDefinition = [] for row in view_json['new_panel_sequence']: newRow = [] for seq in row: newRow.append(dashObject._obj.getPanelBySequence(seq)) if len( newRow ) >= splunk.models.dashboard.MAX_DASHBOARD_ROW_SIZE: newPanelDefinition.append(newRow) newRow = [] if len(row) > 0: newPanelDefinition.append(newRow) dashObject._obj.rows = newPanelDefinition # ensure that the row grouping array is synced if len(dashObject._obj.rowGrouping) < len( dashObject._obj.rows): dashObject._obj.rowGrouping.extend( [None] * (len(dashObject._obj.rows) - len(dashObject._obj.rowGrouping))) # commit dashObject.save() output.addInfo(_('Successfully updated %s') % view_id) else: output.success = False output.addError( _('Unrecognized dashboard action: %s; cannot process') % action) logger.error( 'Unrecognized dashboard action: %s; cannot process' % action) except splunk.ResourceNotFound: cherrypy.response.status = 404 output.addWarn(_('"%s" was not found; no action taken') % view_id) except Exception, e: output.success = False output.addError(_('Unable to update view %s: %s') % (view_id, e)) logger.exception(e)
def streamJobExport(self, job, assetType, **kwargs): """ Stream exported job results to the client (does not buffer the whole result in memory) """ ns = job.eaiacl['app'] sid = job.sid owner = job.eaiacl['owner'] request = {} request['output_mode'] = kwargs['outputMode'] # SPL-79832 when exporting xml splunkd requires additional request argument # to generate valid xml if request['output_mode'] == "xml": request['export_xml_with_wrapper'] = 1 request['f'] = kwargs['field_list'] if 'output_time_format' in kwargs: request['output_time_format'] = kwargs['output_time_format'] else: request['output_time_format'] = i18n.ISO8609_MICROTIME try: count = int(kwargs.get('count')) if count > 0: request['count'] = count except ValueError: logger.warn("Failed to parse count field for export count=%s" % count) pass # We're not going to read/write further from the user's session at this point # and streaming may take a while, so release the session read lock cherrypy.session.release_lock() # Don't buffer the (potentially sizeable) result in memory cherrypy.response.stream = True postargs = getargs = None if job.reportSearch is None and ( (job.eventIsTruncated and (count == 0 or count > job.eventAvailableCount)) or job.isRemoteTimeline or not job.isDone): # re-run the search to get the complete results uri = en.buildEndpoint('search/jobs/export', namespace=ns, owner=owner) request.update(job.request) if count > 0: request['search'] += '|head %s' % count postargs = request # if re-running the search for exporting events, # ensure we do not run the search with following arguments ignoreArgs = ['auto_cancel', 'max_count'] for iarg in ignoreArgs: if iarg in postargs: logger.debug("Dropping argument %s from postargs" % iarg) del postargs[iarg] elif assetType == 'event': # non-reporting search uri = en.buildEndpoint('search/jobs/%s/events/export' % job.sid, namespace=ns, owner=owner) getargs = request else: uri = en.buildEndpoint('search/jobs/%s/results/export' % job.sid, namespace=ns, owner=owner) getargs = request export_timeout = cherrypy.config.get('export_timeout') logger.debug('Export timeout =%s' % export_timeout) if (export_timeout != None): export_timeout = int(export_timeout) stream = rest.streamingRequest(uri, getargs=getargs, postargs=postargs, timeout=export_timeout) return stream.readall() # returns a generator
def getContainer(self, namespace, view_id, mode='', **unused): ''' Renders the dashboard edit page GET /<namespace>/<view_id> ==> HTML form page with dashboard edit form (labels, panels) and master panel edit form (hidden) GET /api/<namespace>/<view_id> ==> JSON structure of dashboard config (unused?) ''' # serve data feed output = jsonresponse.JsonResponse() try: username = cherrypy.session['user'].get('name') dash_id = en.buildEndpoint(VIEW_ENTITY_CLASS, view_id, namespace=namespace, owner=username) dashObject = Dashboard.get(dash_id) output.data = dashObject._obj.toJsonable() except splunk.ResourceNotFound: cherrypy.response.status = 404 output.success = False output.addError(_('View %s was not found') % view_id) return self.render_json(output) # serve template page if cherrypy.request.is_api: return self.render_json(output) else: cherrypy.response.headers['content-type'] = MIME_HTML # get supporting assets savedSearches = en.getEntities('saved/searches', namespace=namespace, count=-1, search="is_visible=1") for savedSearch in savedSearches: acl = savedSearches[savedSearch]['eai:acl'] if dashObject.metadata.sharing=='user': continue if dashObject.metadata.sharing=='app' and acl['sharing']=='user': savedSearches[savedSearch]['dq'] = True continue if dashObject.metadata.sharing=='global' and acl['sharing']!='global': savedSearches[savedSearch]['dq'] = True continue if hasattr(dashObject.metadata.perms, 'read') and hasattr(acl['perms'], 'read'): if dashObject.metadata.perms['read'].count('*')>0 and acl['perms']['read'].count('*')==0: savedSearches[savedSearch]['dq'] = True continue #dashboardObject = splunk.entity.getEntity('data/ui/views', view_id, namespace=APP['id'], owner=cherrypy.session['user'].get('name')) #dashOwner = dashboardObject['eai:acl'].get('owner', 'nobody') editLink = self.make_url( ['manager', namespace, 'data/ui/views', view_id], _qs=dict( action='edit', url=self.make_url(['app', namespace, view_id]), redirect_override="/app/%s/%s" % (namespace, view_id) ) ) permissionsLink = self.make_url( ['manager', 'permissions', namespace, 'data/ui/views', view_id], _qs=dict( uri=en.buildEndpoint('data/ui/views', view_id, namespace=namespace, owner=dashObject.metadata.owner) ) ) return self.render_template('viewmaster/edit_dashboard.html', { 'namespace': namespace, 'view_id': view_id, 'view_object': dashObject._obj, 'APP': {'id': namespace}, 'savedSearches': savedSearches, 'editLink': editLink, 'permissionsLink': permissionsLink, 'mode': mode, })
#eaiArgsList will eventually become the getargs or postargs to send as part of the REST call prehooks = layeredFind(endpoint, cmd, obj, 'prehooks') or [] for ph in prehooks: rcHooks.__dict__[ph](cmd, obj, argList) eaiArgsList = rcHooks.map_args_cli_2_eai(args, {}, argList) logger.debug('after prehooks, eaiArgsList: %s' % str(eaiArgsList)) #first check if the eai_id needs to be obtained by another request!!! if type(lf_eai_id) == types.DictType: #oh crappp... eai_id_uri = buildEndpoint(lf_eai_id['uri'], entityName='', namespace=namespace, owner=owner, search=search, count=count, offset=offset, sort_key=sort_key, sort_dir=sort_dir) eai_id_method = GLOBAL_ACTIONS['%s' % lf_eai_id['type']] try: eai_id_serverResponse, eai_id_serverContent = simpleRequest(eai_id_uri, sessionKey=sessionKey, getargs='', postargs='', method=eai_id_method) except Exception, e: logger.debug('Could not construct eai_id: %s, %s' % str(lf_eai_id), str(e)) raise CliArgError, 'Could not construct eai_id' #check the returned status code if it is ok try: checkStatus(type=lf_eai_id['type'], serverResponse=eai_id_serverResponse) except Exception, e: logger.debug('Could not construct eai_id: %s, %s' % (str(lf_eai_id), str(e))) raise CliArgError, 'Could not construct eai_id'
def setContainer(self, namespace, view_id, action, view_json=None): ''' Provides support to modify dashboard configs POST /<namespace>/<view_id> &action=delete --> deletes the current view &action=edit --> updates the current view config (view JSON object) ==> returns a JSON response ''' output = jsonresponse.JsonResponse() try: username = cherrypy.session['user'].get('name') dash_id = en.buildEndpoint(VIEW_ENTITY_CLASS, view_id, namespace=namespace, owner=username) dashObject = Dashboard.get(dash_id) if action == 'delete': dashObject.delete() output.addInfo(_('Successfully deleted %s') % view_id) elif action == 'edit': # convert incoming JSON to native struct; clean strings, view_json = json.loads(view_json) if view_json.get('label'): dashObject.label = view_json['label'].strip() if view_json.get('refresh'): dashObject.refresh = int(view_json['refresh'].strip()) # handle panel reordering; wrap number of columns to the max # column constraint newPanelDefinition = [] for row in view_json['new_panel_sequence']: newRow = [] for seq in row: newRow.append(dashObject._obj.getPanelBySequence(seq)) if len(newRow) >= splunk.models.dashboard.MAX_DASHBOARD_ROW_SIZE: newPanelDefinition.append(newRow) newRow = [] if len(row) > 0: newPanelDefinition.append(newRow) dashObject._obj.rows = newPanelDefinition # ensure that the row grouping array is synced if len(dashObject._obj.rowGrouping) < len(dashObject._obj.rows): dashObject._obj.rowGrouping.extend([None] * (len(dashObject._obj.rows) - len(dashObject._obj.rowGrouping))) # commit dashObject.save() output.addInfo(_('Successfully updated %s') % view_id) else: output.success = False output.addError(_('Unrecognized dashboard action: %s; cannot process') % action) logger.error('Unrecognized dashboard action: %s; cannot process' % action) except splunk.ResourceNotFound: cherrypy.response.status = 404 output.addWarn(_('"%s" was not found; no action taken') % view_id) except Exception, e: output.success = False output.addError(_('Unable to update view %s: %s') % (view_id, e)) logger.exception(e)
def get_tokens(searchinfo): tokens = {} # Get the host of the splunkd service splunkd_host = searchinfo.splunkd_uri[searchinfo.splunkd_uri.index("//") + 2:searchinfo.splunkd_uri.rindex(":")] splunkd_port = searchinfo.splunkd_uri[searchinfo.splunkd_uri.rindex(":") + 1:] tokens = {'splunkd_host': splunkd_host, 'splunkd_port': splunkd_port} # Get the search job attributes if searchinfo.sid: job_uri = en.buildEndpoint(['search', 'jobs', searchinfo.sid], namespace=searchinfo.app, owner=searchinfo.owner) job_response = simpleRequest(job_uri, method='GET', getargs={'output_mode': 'json'}, sessionKey=searchinfo.session_key)[1] search_job = json.loads(job_response) job_content = search_job['entry'][0]['content'] else: job_content = {} for key, value in list(job_content.items()): if value is not None: tokens['job.' + key] = json.dumps(value, default=lambda o: o.__dict__) #eprint("job_content=" + json.dumps(job_content)) if 'label' in list(job_content.keys()): tokens['name'] = job_content['label'] # Get the saved search properties entityClass = ['saved', 'searches'] uri = en.buildEndpoint(entityClass, namespace=searchinfo.app, owner=searchinfo.owner) responseBody = simpleRequest(uri, method='GET', getargs={'output_mode': 'json'}, sessionKey=searchinfo.session_key)[1] saved_search = json.loads(responseBody) ss_content = saved_search['entry'][0]['content'] #eprint("SSContent=" + json.dumps(ss_content)) for key, value in list(ss_content.items()): if not key.startswith('display.'): if value is not None: tokens[key] = json.dumps(value, default=lambda o: o.__dict__) tokens['owner'] = searchinfo.owner tokens['app'] = searchinfo.app #tokens['results_link'] = 'http://127.0.0.1:8000/en-US/app/search/search?sid=1622650709.10799' # Parse all of the nested objects (recursive function) for t, tv in list(tokens.items()): tokens = merge_two_dicts(tokens, parse_nested_json(t, tv)) #for t, tv in list(tokens.items()): # if type(tv) == str: # eprint(t + '=' + tv) # else: # eprint(t + "(type " + str(type(tv)) + ") = " + str(tv)) return tokens
def _build_endpoint_uri(self, entities): return entity.buildEndpoint(entities, namespace=self._app_name, owner="nobody")