2018-05-11 00:32:04 +03:00
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# Copyright (c) 2014 Mozilla Corporation
import json
import random
import requests
import sys
2018-05-29 19:03:34 +03:00
import logging
2018-05-11 00:32:04 +03:00
from configlib import getConfig , OptionParser
from datetime import datetime , date , timedelta
2018-05-17 18:55:05 +03:00
2018-10-16 22:45:04 +03:00
from mozdef_util . elasticsearch_client import ElasticsearchClient
from mozdef_util . utilities . logger import logger
2018-05-11 00:32:04 +03:00
2018-05-17 18:55:05 +03:00
2018-05-11 00:32:04 +03:00
def esConnect ( conn ) :
''' open or re-open a connection to elastic search '''
2018-05-17 18:55:05 +03:00
return ElasticsearchClient ( ( list ( ' {0} ' . format ( s ) for s in options . esservers ) ) )
2018-05-11 00:32:04 +03:00
def isJVMMemoryHigh ( ) :
2018-05-22 21:03:25 +03:00
url = " {0} /_nodes/stats?pretty=true " . format ( random . choice ( options . esservers ) )
2018-05-11 00:32:04 +03:00
r = requests . get ( url )
logger . debug ( r )
if r . status_code == 200 :
2018-05-17 18:55:05 +03:00
nodestats = r . json ( )
2018-05-11 00:32:04 +03:00
for node in nodestats [ ' nodes ' ] :
2018-05-17 18:55:05 +03:00
loadaverage = nodestats [ ' nodes ' ] [ node ] [ ' os ' ] [ ' cpu ' ] [ ' load_average ' ]
cpuusage = nodestats [ ' nodes ' ] [ node ] [ ' os ' ] [ ' cpu ' ] [ ' percent ' ]
nodename = nodestats [ ' nodes ' ] [ node ] [ ' name ' ]
jvmused = nodestats [ ' nodes ' ] [ node ] [ ' jvm ' ] [ ' mem ' ] [ ' heap_used_percent ' ]
logger . debug ( ' {0} : cpu {1} % jvm {2} % lo ad average: {3} ' . format ( nodename , cpuusage , jvmused , loadaverage ) )
if jvmused > options . jvmlimit :
logger . info ( ' {0} : cpu {1} % jvm {2} % lo ad average: {3} recommending cache clear ' . format ( nodename , cpuusage , jvmused , loadaverage ) )
2018-05-11 00:32:04 +03:00
return True
return False
else :
logger . error ( r )
return False
2018-05-17 18:55:05 +03:00
2018-05-11 00:32:04 +03:00
def clearESCache ( ) :
2018-05-17 18:55:05 +03:00
es = esConnect ( None )
indexes = es . get_indices ( )
2018-05-11 00:32:04 +03:00
# assums index names like events-YYYYMMDD etc.
# used to avoid operating on current indexes
dtNow = datetime . utcnow ( )
indexSuffix = date . strftime ( dtNow , ' % Y % m %d ' )
previousSuffix = date . strftime ( dtNow - timedelta ( days = 1 ) , ' % Y % m %d ' )
2018-05-17 18:55:05 +03:00
for targetindex in sorted ( indexes ) :
2018-05-11 00:57:50 +03:00
if indexSuffix not in targetindex and previousSuffix not in targetindex :
2018-05-23 02:21:05 +03:00
url = ' {0} / {1} /_stats ' . format ( random . choice ( options . esservers ) , targetindex )
2018-05-11 00:32:04 +03:00
r = requests . get ( url )
if r . status_code == 200 :
indexstats = json . loads ( r . text )
if indexstats [ ' _all ' ] [ ' total ' ] [ ' search ' ] [ ' query_current ' ] == 0 :
fielddata = indexstats [ ' _all ' ] [ ' total ' ] [ ' fielddata ' ] [ ' memory_size_in_bytes ' ]
if fielddata > 0 :
logger . info ( ' target: {0} : field data {1} ' . format ( targetindex , indexstats [ ' _all ' ] [ ' total ' ] [ ' fielddata ' ] [ ' memory_size_in_bytes ' ] ) )
2018-05-23 02:21:05 +03:00
clearurl = ' {0} / {1} /_cache/clear ' . format ( random . choice ( options . esservers ) , targetindex )
2018-05-11 00:32:04 +03:00
clearRequest = requests . post ( clearurl )
logger . info ( clearRequest . text )
2018-05-11 00:57:50 +03:00
# stop at one?
2018-05-11 00:32:04 +03:00
if options . conservative :
return
else :
logger . debug ( ' {0} : <ignoring due to current search > field data {1} ' . format ( targetindex , indexstats [ ' _all ' ] [ ' total ' ] [ ' fielddata ' ] [ ' memory_size_in_bytes ' ] ) )
else :
logger . error ( ' {0} returned {1} ' . format ( url , r . status_code ) )
2018-05-17 18:55:05 +03:00
2018-05-11 00:32:04 +03:00
def main ( ) :
if options . checkjvmmemory :
if isJVMMemoryHigh ( ) :
logger . info ( ' initiating cache clearing ' )
clearESCache ( )
else :
clearESCache ( )
2018-05-17 18:55:05 +03:00
2018-05-11 00:32:04 +03:00
def initConfig ( ) :
2018-05-11 00:57:50 +03:00
# elastic search servers
2018-05-11 00:32:04 +03:00
options . esservers = list ( ' {0} ' . format ( s ) for s in getConfig ( ' esservers ' , ' http://localhost:9200 ' , options . configfile ) . split ( ' , ' ) )
# memory watermark, set to 90 (percent) by default
options . jvmlimit = getConfig ( ' jvmlimit ' , 90 , options . configfile )
# be conservative? if set only clears cache for the first index found with no searches and cached field data
# if false, will continue to clear for any index not matching the date suffix.
options . conservative = getConfig ( ' conservative ' , True , options . configfile )
2018-05-17 18:55:05 +03:00
# check jvm memory first? or just clear cache
2018-05-11 00:32:04 +03:00
options . checkjvmmemory = getConfig ( ' checkjvmmemory ' , True , options . configfile )
2018-05-17 18:55:05 +03:00
2018-05-11 00:32:04 +03:00
if __name__ == ' __main__ ' :
# configure ourselves
parser = OptionParser ( )
parser . add_option ( " -c " , dest = ' configfile ' , default = sys . argv [ 0 ] . replace ( ' .py ' , ' .conf ' ) , help = " configuration file to use " )
( options , args ) = parser . parse_args ( )
initConfig ( )
2018-05-29 19:03:34 +03:00
logger . level = logging . WARNING
2018-05-11 00:32:04 +03:00
logger . debug ( ' starting ' )
main ( )