From 722634adf45e84082cbab70246c42c72af28d282 Mon Sep 17 00:00:00 2001 From: Alexandre Dulaunoy Date: Mon, 25 Jul 2016 07:49:14 +0000 Subject: [PATCH 01/89] Prod config --- bin/launch_scripts.sh | 4 ++-- bin/packages/modules.cfg | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/bin/launch_scripts.sh b/bin/launch_scripts.sh index c7f4228f..33930f6f 100755 --- a/bin/launch_scripts.sh +++ b/bin/launch_scripts.sh @@ -9,8 +9,8 @@ sleep 0.1 echo -e $GREEN"\t* Launching ZMQ scripts"$DEFAULT screen -S "Script" -X screen -t "Global" bash -c './Global.py; read x' -sleep 0.1 -screen -S "Script" -X screen -t "Duplicate" bash -c './Duplicate.py; read x' +#sleep 0.1 +#screen -S "Script" -X screen -t "Duplicate" bash -c './Duplicate.py; read x' sleep 0.1 screen -S "Script" -X screen -t "Attribute" bash -c './Attribute.py; read x' sleep 0.1 diff --git a/bin/packages/modules.cfg b/bin/packages/modules.cfg index 9d8d6637..c5bd23c6 100644 --- a/bin/packages/modules.cfg +++ b/bin/packages/modules.cfg @@ -2,8 +2,8 @@ subscribe = ZMQ_Global publish = Redis_Global -[Duplicates] -subscribe = Redis_Global +#[Duplicates] +#subscribe = Redis_Global [Indexer] subscribe = Redis_Global @@ -27,7 +27,7 @@ subscribe = Redis_Words [Categ] subscribe = Redis_Global -publish = Redis_CreditCards,Redis_Mail,Redis_Onion,Redis_Web,Redis_Credential,Redis_SourceCode,Redis_Cve +publish = Redis_CreditCards,Redis_Mail,Redis_Onion,Redis_Web,Redis_Credential [CreditCards] subscribe = Redis_CreditCards @@ -50,20 +50,20 @@ publish = Redis_Url,ZMQ_Url [WebStats] subscribe = Redis_Url -[Release] -subscribe = Redis_Global +#[Release] +#subscribe = Redis_Global [Credential] subscribe = Redis_Credential -[Cve] -subscribe = Redis_Cve +#[Cve] +#subscribe = Redis_Cve -[Phone] -subscribe = Redis_Global +#[SourceCode] +#subscribe = Redis_SourceCode -[SourceCode] -subscribe = Redis_SourceCode +#[Phone] +#subscribe = Redis_Global -[Keys] -subscribe = Redis_Global +#[Keys] +#subscribe = Redis_Global From 5f30eef1ecf21794e5150427976f79081aad5c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Mon, 25 Jul 2016 11:38:41 +0200 Subject: [PATCH 02/89] Update travis --- .travis.yml | 66 ++-------------------------------------------- installing_deps.sh | 32 +++++++++++++--------- 2 files changed, 22 insertions(+), 76 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1ce79b41..554d0967 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,27 +5,7 @@ python: sudo: required -dist: trusty - -addons: - apt: - packages: - # General dependencies - - python-pip - - python-virtualenv - - python-dev - - g++ - - python-tk - - unzip - - libsnappy-dev - # Needed for bloomfilters - - libssl-dev - - python-numpy - - libfreetype6-dev - # Leveldb - - libgmp-dev - - libev-dev - - cmake +cache: pip env: - AIL_HOME=$TRAVIS_BUILD_DIR AIL_BIN=$TRAVIS_BUILD_DIR/bin/ \ @@ -35,49 +15,7 @@ env: install: - - pip install -U pip - # DNS - - sudo apt-get install -y libadns1 libadns1-dev screen - # required for mathplotlib - - test ! -L /usr/include/ft2build.h && sudo ln -s freetype2/ft2build.h /usr/include/ - - pip install distribute - # Redis - - test ! -d redis/ && git clone https://github.com/antirez/redis.git - - pushd redis - - git checkout 3.2 - - make - - popd - # Redis leveldb - - test ! -d redis-leveldb/ && git clone https://github.com/KDr2/redis-leveldb.git - - pushd redis-leveldb/ - - git submodule init - - git submodule update - - make - - popd - # Faup - - test ! -d faup && git clone https://github.com/stricaud/faup.git - - pushd faup/ - - test ! -d build && mkdir build - - cd build - - cmake .. && make - - sudo make install - - echo '/usr/local/lib' | sudo tee -a /etc/ld.so.conf.d/faup.conf - - sudo ldconfig - - popd - # PyFaup - - pushd faup/src/lib/bindings/python/ - - python setup.py install - - popd - # Set config - - cp bin/packages/config.cfg.sample bin/packages/config.cfg - - mkdir -p $AIL_HOME/{PASTES,Blooms,dumps} - - mkdir -p $AIL_HOME/LEVEL_DB_DATA/{2016,2015,2014,2013} - - pip install -r pip_packages_requirement.txt - - python -m textblob.download_corpora - - pushd var/www/ - - ./update_thirdparty.sh - - popd - + - ./installing_deps.sh script: - pushd bin diff --git a/installing_deps.sh b/installing_deps.sh index ae4f3fc8..d68a029e 100755 --- a/installing_deps.sh +++ b/installing_deps.sh @@ -29,7 +29,7 @@ make popd # Faup -test ! -d faup && git clone https://github.com/stricaud/faup.git +test ! -d faup/ && git clone https://github.com/stricaud/faup.git pushd faup/ test ! -d build && mkdir build cd build @@ -51,21 +51,29 @@ if [ ! -f bin/packages/config.cfg ]; then cp bin/packages/config.cfg.sample bin/packages/config.cfg fi -virtualenv AILENV - -echo export AIL_HOME=$(pwd) >> ./AILENV/bin/activate -echo export AIL_BIN=$(pwd)/bin/ >> ./AILENV/bin/activate -echo export AIL_FLASK=$(pwd)/var/www/ >> ./AILENV/bin/activate -echo export AIL_REDIS=$(pwd)/redis/src/ >> ./AILENV/bin/activate -echo export AIL_LEVELDB=$(pwd)/redis-leveldb/ >> ./AILENV/bin/activate - -. ./AILENV/bin/activate - mkdir -p $AIL_HOME/{PASTES,Blooms,dumps} mkdir -p $AIL_HOME/LEVEL_DB_DATA/2016 +pushd var/www/ +./update_thirdparty.sh +popd + +if [ -z "$VIRTUAL_ENV" ]; then + + virtualenv AILENV + + echo export AIL_HOME=$(pwd) >> ./AILENV/bin/activate + echo export AIL_BIN=$(pwd)/bin/ >> ./AILENV/bin/activate + echo export AIL_FLASK=$(pwd)/var/www/ >> ./AILENV/bin/activate + echo export AIL_REDIS=$(pwd)/redis/src/ >> ./AILENV/bin/activate + echo export AIL_LEVELDB=$(pwd)/redis-leveldb/ >> ./AILENV/bin/activate + + . ./AILENV/bin/activate + +fi + pip install -U pip -pip install -r pip_packages_requirement.txt +pip install -U -r pip_packages_requirement.txt # Pyfaup pushd faup/src/lib/bindings/python/ From 5d4a02e706b78f403cd97dcafabd8c047e985645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Mon, 25 Jul 2016 11:49:29 +0200 Subject: [PATCH 03/89] Fix install script --- installing_deps.sh | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/installing_deps.sh b/installing_deps.sh index d68a029e..63227bdf 100755 --- a/installing_deps.sh +++ b/installing_deps.sh @@ -17,10 +17,6 @@ sudo apt-get install libadns1 libadns1-dev #Needed for redis-lvlDB sudo apt-get install libev-dev libgmp-dev -#needed for mathplotlib -test ! -L /usr/include/ft2build.h && sudo ln -s freetype2/ft2build.h /usr/include/ -sudo easy_install -U distribute - # REDIS # test ! -d redis/ && git clone https://github.com/antirez/redis.git pushd redis/ @@ -51,9 +47,6 @@ if [ ! -f bin/packages/config.cfg ]; then cp bin/packages/config.cfg.sample bin/packages/config.cfg fi -mkdir -p $AIL_HOME/{PASTES,Blooms,dumps} -mkdir -p $AIL_HOME/LEVEL_DB_DATA/2016 - pushd var/www/ ./update_thirdparty.sh popd @@ -72,6 +65,9 @@ if [ -z "$VIRTUAL_ENV" ]; then fi +mkdir -p $AIL_HOME/{PASTES,Blooms,dumps} +mkdir -p $AIL_HOME/LEVEL_DB_DATA/2016 + pip install -U pip pip install -U -r pip_packages_requirement.txt From b8bc9c0f17a22f58100b89016dccb13ad35b6d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Mon, 25 Jul 2016 11:55:14 +0200 Subject: [PATCH 04/89] Add dependency --- installing_deps.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/installing_deps.sh b/installing_deps.sh index 63227bdf..16a68f05 100755 --- a/installing_deps.sh +++ b/installing_deps.sh @@ -17,6 +17,9 @@ sudo apt-get install libadns1 libadns1-dev #Needed for redis-lvlDB sudo apt-get install libev-dev libgmp-dev +# ssdeep +sudo apt-get install libfuzzy-dev + # REDIS # test ! -d redis/ && git clone https://github.com/antirez/redis.git pushd redis/ From 181c372131b3870f0749248c95054bfbb25f2216 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 26 Aug 2016 11:10:39 +0200 Subject: [PATCH 05/89] Updated dependencies --- var/www/templates/terms_management.html | 1 - var/www/templates/terms_plot_top.html | 1 - var/www/update_thirdparty.sh | 9 +++++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/var/www/templates/terms_management.html b/var/www/templates/terms_management.html index 32a51c32..e4e6e5f1 100644 --- a/var/www/templates/terms_management.html +++ b/var/www/templates/terms_management.html @@ -12,7 +12,6 @@ - diff --git a/var/www/templates/terms_plot_top.html b/var/www/templates/terms_plot_top.html index fd7a2be8..cf11bc88 100644 --- a/var/www/templates/terms_plot_top.html +++ b/var/www/templates/terms_plot_top.html @@ -12,7 +12,6 @@ - diff --git a/var/www/update_thirdparty.sh b/var/www/update_thirdparty.sh index 6ad2744f..94134a62 100755 --- a/var/www/update_thirdparty.sh +++ b/var/www/update_thirdparty.sh @@ -39,12 +39,17 @@ wget https://raw.githubusercontent.com/flot/flot/master/jquery.flot.pie.js -O ./ wget https://raw.githubusercontent.com/flot/flot/master/jquery.flot.time.js -O ./static/js/jquery.flot.time.js wget https://raw.githubusercontent.com/flot/flot/master/jquery.flot.stack.js -O ./static/js/jquery.flot.stack.js -#Ressources for sparkline and canvasJS +#Ressources for sparkline and canvasJS and slider wget http://omnipotent.net/jquery.sparkline/2.1.2/jquery.sparkline.min.js -O ./static/js/jquery.sparkline.min.js +mkdir temp wget http://canvasjs.com/fdm/chart/ -O temp/canvasjs.zip unzip temp/canvasjs.zip -d temp/ -mkdir temp mv temp/jquery.canvasjs.min.js ./static/js/jquery.canvasjs.min.js + +wget https://jqueryui.com/resources/download/jquery-ui-1.12.0.zip -O temp/jquery-ui.zip +unzip temp/jquery-ui.zip -d temp/ +mv temp/jquery-ui-1.12.0/jquery-ui.min.js ./static/js/jquery-ui.min.js +mv temp/jquery-ui-1.12.0/jquery-ui.min.css ./static/js/jquery-ui.min.css rm -rf temp mkdir -p ./static/image From 5c565068a6b4f8bec1ae1be8ece52a91d0c73e16 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 26 Aug 2016 11:25:11 +0200 Subject: [PATCH 06/89] Removed auto-reload in dashboard if window in undefined --- var/www/static/js/indexjavascript.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/var/www/static/js/indexjavascript.js b/var/www/static/js/indexjavascript.js index 1bdb29eb..55e14081 100644 --- a/var/www/static/js/indexjavascript.js +++ b/var/www/static/js/indexjavascript.js @@ -244,12 +244,8 @@ function create_queue_table() { Tablediv.appendChild(table); } -$(document).ready(function () { - if (typeof glob_tabvar == "undefined") - location.reload(); - if (typeof glob_tabvar.row1 == "undefined") - location.reload(); +function load_queues() { var data = []; var data2 = []; var tmp_tab = []; @@ -375,7 +371,19 @@ $(document).ready(function () { // something went wrong, hide the canvas container document.getElementById('myCanvasContainer').style.display = 'none'; } +} +function manage_undefined() { + if (typeof glob_tabvar == "undefined") + setTimeout(function() { if (typeof glob_tabvar == "undefined") { manage_undefined(); } else { load_queues(); } }, 1000); + else if (typeof glob_tabvar.row1 == "undefined") + setTimeout(function() { if (typeof glob_tabvar.row1 == "undefined") { manage_undefined(); } else { load_queues(); } }, 1000); + else + load_queues(); +} + +$(document).ready(function () { + manage_undefined(); }); From 0c760d763b95ccb62ba891857a24a2722dabe138 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 26 Aug 2016 15:27:37 +0200 Subject: [PATCH 07/89] Added support of PID in ModuleInformation and index.html --- ...warning_paste.py => BrowseWarningPaste.py} | 2 +- bin/Helper.py | 10 ++--- bin/LAUNCH.sh | 2 +- bin/ModuleInformation.py | 39 +++++++++++++------ bin/packages/modules.cfg | 2 +- var/www/Flask_server.py | 14 ++----- var/www/static/js/indexjavascript.js | 32 ++++++++++----- var/www/templates/index.html | 13 ++++++- 8 files changed, 73 insertions(+), 41 deletions(-) rename bin/{Browse_warning_paste.py => BrowseWarningPaste.py} (97%) diff --git a/bin/Browse_warning_paste.py b/bin/BrowseWarningPaste.py similarity index 97% rename from bin/Browse_warning_paste.py rename to bin/BrowseWarningPaste.py index 49444979..01704902 100755 --- a/bin/Browse_warning_paste.py +++ b/bin/BrowseWarningPaste.py @@ -24,7 +24,7 @@ if __name__ == "__main__": publisher.port = 6380 publisher.channel = "Script" - config_section = 'Browse_warning_paste' + config_section = 'BrowseWarningPaste' p = Process(config_section) diff --git a/bin/Helper.py b/bin/Helper.py index e7338ceb..05b73bf3 100755 --- a/bin/Helper.py +++ b/bin/Helper.py @@ -119,13 +119,7 @@ class Process(object): port=self.config.get('RedisPubSub', 'port'), db=self.config.get('RedisPubSub', 'db')) - self.moduleNum = 1 - for i in range(1, 50): - curr_num = self.r_temp.get("MODULE_"+self.subscriber_name + "_" + str(i)) - if curr_num is None: - self.moduleNum = i - break - + self.moduleNum = os.getpid() def populate_set_in(self): @@ -158,12 +152,14 @@ class Process(object): path = "?" value = str(timestamp) + ", " + path self.r_temp.set("MODULE_"+self.subscriber_name + "_" + str(self.moduleNum), value) + self.r_temp.sadd("MODULE_TYPE_"+self.subscriber_name, str(self.moduleNum)) return message except: path = "?" value = str(timestamp) + ", " + path self.r_temp.set("MODULE_"+self.subscriber_name + "_" + str(self.moduleNum), value) + self.r_temp.sadd("MODULE_TYPE_"+self.subscriber_name, str(self.moduleNum)) return message def populate_set_out(self, msg, channel=None): diff --git a/bin/LAUNCH.sh b/bin/LAUNCH.sh index b50a75d6..d7c31472 100755 --- a/bin/LAUNCH.sh +++ b/bin/LAUNCH.sh @@ -158,7 +158,7 @@ function launching_scripts { sleep 0.1 screen -S "Script" -X screen -t "SQLInjectionDetection" bash -c './SQLInjectionDetection.py; read x' sleep 0.1 - screen -S "Script" -X screen -t "Browse_warning_paste" bash -c './Browse_warning_paste.py; read x' + screen -S "Script" -X screen -t "BrowseWarningPaste" bash -c './BrowseWarningPaste.py; read x' sleep 0.1 screen -S "Script" -X screen -t "SentimentAnalysis" bash -c './SentimentAnalysis.py; read x' diff --git a/bin/ModuleInformation.py b/bin/ModuleInformation.py index d783418f..1538f57a 100755 --- a/bin/ModuleInformation.py +++ b/bin/ModuleInformation.py @@ -29,6 +29,7 @@ import textwrap threshold_stucked_module = 60*60*1 #1 hour log_filename = "../logs/moduleInfo.log" command_search_pid = "ps a -o pid,cmd | grep {}" +command_search_name = "ps a -o pid,cmd | grep {}" command_restart_module = "screen -S \"Script\" -X screen -t \"{}\" bash -c \"./{}.py; read x\"" @@ -45,6 +46,23 @@ def clearRedisModuleInfo(): for k in server.keys("MODULE_*"): server.delete(k) +def cleanRedis(): + for k in server.keys("MODULE_TYPE_*"): + moduleName = k[12:].split('_')[0] + for pid in server.smembers(k): + flag_pid_valid = False + proc = Popen([command_search_name.format(pid)], stdin=PIPE, stdout=PIPE, bufsize=1, shell=True) + for line in proc.stdout: + splittedLine = line.split() + if ('python2' in splittedLine or 'python' in splittedLine) and "./"+moduleName+".py" in splittedLine: + flag_pid_valid = True + + if not flag_pid_valid: + print flag_pid_valid, 'cleaning', pid, 'in', k + server.srem(k, pid) + time.sleep(5) + + def kill_module(module): print '' print '-> trying to kill module:', module @@ -76,8 +94,8 @@ if __name__ == "__main__": parser = argparse.ArgumentParser(description='Show info concerning running modules and log suspected stucked modules. May be use to automatically kill and restart stucked one.') parser.add_argument('-r', '--refresh', type=int, required=False, default=1, help='Refresh rate') - parser.add_argument('-k', '--autokill', type=int, required=True, default=1, help='Enable auto kill option (1 for TRUE, anything else for FALSE)') - parser.add_argument('-c', '--clear', type=int, required=False, default=1, help='Clear the current module information (Used to clear data from old launched modules)') + parser.add_argument('-k', '--autokill', type=int, required=False, default=0, help='Enable auto kill option (1 for TRUE, anything else for FALSE)') + parser.add_argument('-c', '--clear', type=int, required=False, default=0, help='Clear the current module information (Used to clear data from old launched modules)') args = parser.parse_args() @@ -99,6 +117,7 @@ if __name__ == "__main__": if args.clear == 1: clearRedisModuleInfo() + lastTime = datetime.datetime.now() module_file_array = set() with open('../doc/all_modules.txt', 'r') as module_file: @@ -108,20 +127,15 @@ if __name__ == "__main__": while True: all_queue = set() - curr_range = 50 printarray1 = [] printarray2 = [] printarray3 = [] for queue, card in server.hgetall("queues").iteritems(): all_queue.add(queue) key = "MODULE_" + queue + "_" - for i in range(1, 50): - curr_num = server.get("MODULE_"+ queue + "_" + str(i)) - if curr_num is None: - curr_range = i - break + keySet = "MODULE_TYPE_" + queue - for moduleNum in range(1, curr_range): + for moduleNum in server.smembers(keySet): value = server.get(key + str(moduleNum)) if value is not None: timestamp, path = value.split(", ") @@ -147,8 +161,8 @@ if __name__ == "__main__": printarray1.sort(lambda x,y: cmp(x[4], y[4]), reverse=True) printarray2.sort(lambda x,y: cmp(x[4], y[4]), reverse=True) - printarray1.insert(0,["Queue", "#", "Amount", "Paste start time", "Processing time for current paste (H:M:S)", "Paste hash"]) - printarray2.insert(0,["Queue", "#","Amount", "Paste start time", "Time since idle (H:M:S)", "Last paste hash"]) + printarray1.insert(0,["Queue", "PID", "Amount", "Paste start time", "Processing time for current paste (H:M:S)", "Paste hash"]) + printarray2.insert(0,["Queue", "PID","Amount", "Paste start time", "Time since idle (H:M:S)", "Last paste hash"]) printarray3.insert(0,["Queue", "State"]) os.system('clear') @@ -195,4 +209,7 @@ if __name__ == "__main__": print '\n' print t3.table + if (datetime.datetime.now() - lastTime).total_seconds() > args.refresh*5: + lastTime = datetime.datetime.now() + cleanRedis() time.sleep(args.refresh) diff --git a/bin/packages/modules.cfg b/bin/packages/modules.cfg index 0243038a..782536c5 100644 --- a/bin/packages/modules.cfg +++ b/bin/packages/modules.cfg @@ -63,7 +63,7 @@ publish = Redis_BrowseWarningPaste,Redis_Duplicate [ModuleStats] subscribe = Redis_ModuleStats -[Browse_warning_paste] +[BrowseWarningPaste] subscribe = Redis_BrowseWarningPaste #[send_to_queue] diff --git a/var/www/Flask_server.py b/var/www/Flask_server.py index 3c4346f1..4715aaac 100755 --- a/var/www/Flask_server.py +++ b/var/www/Flask_server.py @@ -81,19 +81,13 @@ def event_stream(): def get_queues(r): # We may want to put the llen in a pipeline to do only one query. - data = [(queue, int(card)) for queue, card in r.hgetall("queues").iteritems()] newData = [] - - curr_range = 50 - for queue, card in data: + for queue, card in r.hgetall("queues").iteritems(): key = "MODULE_" + queue + "_" - for i in range(1, 50): - curr_num = r.get("MODULE_"+ queue + "_" + str(i)) - if curr_num is None: - curr_range = i - break + keySet = "MODULE_TYPE_" + queue - for moduleNum in range(1, curr_range): + for moduleNum in r.smembers(keySet): + value = r.get(key + str(moduleNum)) if value is not None: timestamp, path = value.split(", ") diff --git a/var/www/static/js/indexjavascript.js b/var/www/static/js/indexjavascript.js index 55e14081..d145df65 100644 --- a/var/www/static/js/indexjavascript.js +++ b/var/www/static/js/indexjavascript.js @@ -207,7 +207,7 @@ function create_queue_table() { table.appendChild(tableHead); table.appendChild(tableBody); var heading = new Array(); - heading[0] = "Queue Name" + heading[0] = "Queue Name.PID" heading[1] = "Amount" var tr = document.createElement('TR'); tableHead.appendChild(tr); @@ -255,13 +255,17 @@ function load_queues() { var x = new Date(); for (i = 0; i < glob_tabvar.row1.length; i++){ - if (glob_tabvar.row1[i][0] == 'Categ' || glob_tabvar.row1[i][0] == 'Curve'){ - tmp_tab2.push(0); - curves_labels2.push(glob_tabvar.row1[i][0]); + if (glob_tabvar.row1[i][0].split(".")[0] == 'Categ' || glob_tabvar.row1[i][0].split(".")[0] == 'Curve'){ + if (curves_labels2.indexOf(glob_tabvar.row1[i][0].split(".")[0]) == -1) { + tmp_tab2.push(0); + curves_labels2.push(glob_tabvar.row1[i][0].split(".")); + } } else { - tmp_tab.push(0); - curves_labels.push(glob_tabvar.row1[i][0]); + if (curves_labels.indexOf(glob_tabvar.row1[i][0].split(".")[0]) == -1) { + tmp_tab.push(0); + curves_labels.push(glob_tabvar.row1[i][0].split(".")); + } } } tmp_tab.unshift(x); @@ -320,19 +324,29 @@ function load_queues() { update_values(); if($('#button-toggle-queues').prop('checked')){ + $("#queue-color-legend").show(); create_queue_table(); } else{ $("#queueing").html(''); + $("#queue-color-legend").hide(); } + queues_pushed = [] for (i = 0; i < (glob_tabvar.row1).length; i++){ - if (glob_tabvar.row1[i][0] == 'Categ' || glob_tabvar.row1[i][0] == 'Curve'){ - tmp_values2.push(glob_tabvar.row1[i][1]); + if (glob_tabvar.row1[i][0].split(".")[0] == 'Categ' || glob_tabvar.row1[i][0].split(".")[0] == 'Curve'){ + if (queues_pushed.indexOf(glob_tabvar.row1[i][0].split(".")[0]) == -1) { + queues_pushed.push(glob_tabvar.row1[i][0].split(".")); + tmp_values2.push(glob_tabvar.row1[i][1]); + } } else { - tmp_values.push(glob_tabvar.row1[i][1]); + if (curves_labels.indexOf(glob_tabvar.row1[i][0].split(".")[0]) == -1) { + queues_pushed.push(glob_tabvar.row1[i][0].split(".")); + tmp_values.push(glob_tabvar.row1[i][1]); + } + } } tmp_values.unshift(x); diff --git a/var/www/templates/index.html b/var/www/templates/index.html index 28c3aff9..65b9c0c8 100644 --- a/var/www/templates/index.html +++ b/var/www/templates/index.html @@ -66,7 +66,18 @@ Display queues
-
+
+ + + + + + + + +
Working queues
Idling queues
Stucked queues
+
+
From 79be8ab9345a57861a4f1d2f325ac195dbcf172d Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 26 Aug 2016 15:59:53 +0200 Subject: [PATCH 08/89] Fixed introduced bug relative to chart in index webpage. --- var/www/static/js/indexjavascript.js | 14 +++++++------- var/www/templates/index.html | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/var/www/static/js/indexjavascript.js b/var/www/static/js/indexjavascript.js index d145df65..e527aafa 100644 --- a/var/www/static/js/indexjavascript.js +++ b/var/www/static/js/indexjavascript.js @@ -258,13 +258,13 @@ function load_queues() { if (glob_tabvar.row1[i][0].split(".")[0] == 'Categ' || glob_tabvar.row1[i][0].split(".")[0] == 'Curve'){ if (curves_labels2.indexOf(glob_tabvar.row1[i][0].split(".")[0]) == -1) { tmp_tab2.push(0); - curves_labels2.push(glob_tabvar.row1[i][0].split(".")); + curves_labels2.push(glob_tabvar.row1[i][0].split(".")[0]); } } else { if (curves_labels.indexOf(glob_tabvar.row1[i][0].split(".")[0]) == -1) { tmp_tab.push(0); - curves_labels.push(glob_tabvar.row1[i][0].split(".")); + curves_labels.push(glob_tabvar.row1[i][0].split(".")[0]); } } } @@ -337,14 +337,14 @@ function load_queues() { for (i = 0; i < (glob_tabvar.row1).length; i++){ if (glob_tabvar.row1[i][0].split(".")[0] == 'Categ' || glob_tabvar.row1[i][0].split(".")[0] == 'Curve'){ if (queues_pushed.indexOf(glob_tabvar.row1[i][0].split(".")[0]) == -1) { - queues_pushed.push(glob_tabvar.row1[i][0].split(".")); - tmp_values2.push(glob_tabvar.row1[i][1]); + queues_pushed.push(glob_tabvar.row1[i][0].split(".")[0]); + tmp_values2.push(parseInt(glob_tabvar.row1[i][1])); } } else { - if (curves_labels.indexOf(glob_tabvar.row1[i][0].split(".")[0]) == -1) { - queues_pushed.push(glob_tabvar.row1[i][0].split(".")); - tmp_values.push(glob_tabvar.row1[i][1]); + if (queues_pushed.indexOf(glob_tabvar.row1[i][0].split(".")[0]) == -1) { + queues_pushed.push(glob_tabvar.row1[i][0].split(".")[0]); + tmp_values.push(parseInt(glob_tabvar.row1[i][1])); } } diff --git a/var/www/templates/index.html b/var/www/templates/index.html index 65b9c0c8..8bbb59e1 100644 --- a/var/www/templates/index.html +++ b/var/www/templates/index.html @@ -66,14 +66,14 @@ Display queues
-
- +
+
- - - + + +
Working queues
Idling queues
Stucked queues
Working queues
Idling queues
Stucked queues
From 78e0ee8667d5384b0659514502819fa3d7822146 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 26 Aug 2016 16:51:35 +0200 Subject: [PATCH 09/89] Updated screenshot --- doc/screenshots/terms-plot.png | Bin 31830 -> 30946 bytes doc/screenshots/terms-top.png | Bin 88453 -> 114022 bytes doc/screenshots/trending-module.png | Bin 55384 -> 57608 bytes doc/screenshots/trending-web.png | Bin 58762 -> 67444 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/screenshots/terms-plot.png b/doc/screenshots/terms-plot.png index 32647906a1ac946e38d1ebf891ff28e45b32a999..4d33466d5f57294fe03246eae9799bb5a92dbad9 100644 GIT binary patch literal 30946 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

W^8Qy?Abe%OLgGn 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%0DW;vzezSAR2jBI@+=t;+6U?K(A*Jg&J@KMKqA`L% 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%0DC<8H4pyV{P-WU?xr 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

From 4eafca29366194be5110064a525d4164dff4caa8 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 13 Oct 2016 17:13:08 +0200 Subject: [PATCH 23/89] Added pystemon instruction section --- README.md | 26 ++++++++++++++++++++++++++ bin/feeder/pystemon-feeder.py | 0 2 files changed, 26 insertions(+) mode change 100644 => 100755 bin/feeder/pystemon-feeder.py diff --git a/README.md b/README.md index f5cdb7b4..7483a57f 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,28 @@ Eventually you can browse the status of the AIL framework website at the followi ``http://localhost:7000/`` +How to +====== + +How to feed the AIL framework +----------------------------- + +For now, there are two different way to feed AIL with data: +1. Be a collaborator of CIRCL and ask to access our feed. Then, it will be sent to the static IP your are using for AIL. +2. You can setup [pystemon](https://github.com/CIRCL/pystemon) and use the custom feeder provided by AIL + +#Feeding AIL with pystemon +AIL is a analysis tool, not a collector! +However, if you want to collect some paste and feed them to AIL, here is the procedure but moderate your queries quantity!! + +Here are the steps to setup your pystemon and feed data to AIL: +1. Clone the [pystemon's git repository](https://github.com/CIRCL/pystemon) +2. Install its python dependencies inside your virtual environment +3. Launch pystemon ``` ./pystemon ``` +4. Edit the file bin/feeder/pystemon-feeder.py and modify the pystemonpath path accordingly +5. Launch pystemon-feeder ``` ./pystemon-feeder.py ``` + + How to create a new module -------------------------- @@ -117,6 +139,10 @@ Feel free to fork the code, play with it, make some patches or add additional an To contribute your module, feel free to pull your contribution. +Overview and License +==================== + + Redis and LevelDB overview -------------------------- diff --git a/bin/feeder/pystemon-feeder.py b/bin/feeder/pystemon-feeder.py old mode 100644 new mode 100755 From f615333aae4866e72ba0d0c9a847823dd0ba9871 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 13 Oct 2016 17:17:09 +0200 Subject: [PATCH 24/89] fixed typos --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7483a57f..6cb4462b 100644 --- a/README.md +++ b/README.md @@ -109,17 +109,23 @@ How to feed the AIL framework For now, there are two different way to feed AIL with data: 1. Be a collaborator of CIRCL and ask to access our feed. Then, it will be sent to the static IP your are using for AIL. + 2. You can setup [pystemon](https://github.com/CIRCL/pystemon) and use the custom feeder provided by AIL #Feeding AIL with pystemon -AIL is a analysis tool, not a collector! -However, if you want to collect some paste and feed them to AIL, here is the procedure but moderate your queries quantity!! +AIL is an analysis tool, not a collector! +However, if you want to collect some paste and feed them to AIL, the procedure is described below. +Nevertheless, moderate your queries! Here are the steps to setup your pystemon and feed data to AIL: 1. Clone the [pystemon's git repository](https://github.com/CIRCL/pystemon) + 2. Install its python dependencies inside your virtual environment + 3. Launch pystemon ``` ./pystemon ``` + 4. Edit the file bin/feeder/pystemon-feeder.py and modify the pystemonpath path accordingly + 5. Launch pystemon-feeder ``` ./pystemon-feeder.py ``` From c88b77c37240e22acc1b735931fd6c4a512885af Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 13 Oct 2016 17:20:25 +0200 Subject: [PATCH 25/89] fixed typos 2 --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6cb4462b..faa65fe9 100644 --- a/README.md +++ b/README.md @@ -115,16 +115,18 @@ For now, there are two different way to feed AIL with data: #Feeding AIL with pystemon AIL is an analysis tool, not a collector! However, if you want to collect some paste and feed them to AIL, the procedure is described below. + Nevertheless, moderate your queries! Here are the steps to setup your pystemon and feed data to AIL: + 1. Clone the [pystemon's git repository](https://github.com/CIRCL/pystemon) 2. Install its python dependencies inside your virtual environment 3. Launch pystemon ``` ./pystemon ``` -4. Edit the file bin/feeder/pystemon-feeder.py and modify the pystemonpath path accordingly +4. Edit the file ```bin/feeder/pystemon-feeder.py``` and modify the pystemonpath path accordingly 5. Launch pystemon-feeder ``` ./pystemon-feeder.py ``` From 343f79886f4b8223f52160ce8b422c8055dff680 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 13 Oct 2016 17:21:31 +0200 Subject: [PATCH 26/89] fixed typo 3 --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index faa65fe9..77bf8a90 100644 --- a/README.md +++ b/README.md @@ -108,11 +108,12 @@ How to feed the AIL framework ----------------------------- For now, there are two different way to feed AIL with data: + 1. Be a collaborator of CIRCL and ask to access our feed. Then, it will be sent to the static IP your are using for AIL. 2. You can setup [pystemon](https://github.com/CIRCL/pystemon) and use the custom feeder provided by AIL -#Feeding AIL with pystemon +###Feeding AIL with pystemon AIL is an analysis tool, not a collector! However, if you want to collect some paste and feed them to AIL, the procedure is described below. From c805fee61d5209dbfec9aac95b086594f24dd5ba Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 13 Oct 2016 17:26:20 +0200 Subject: [PATCH 27/89] fixed typo 4 --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 77bf8a90..cf638e05 100644 --- a/README.md +++ b/README.md @@ -107,15 +107,15 @@ How to How to feed the AIL framework ----------------------------- -For now, there are two different way to feed AIL with data: +For the moment, there are two different ways to feed AIL with data: -1. Be a collaborator of CIRCL and ask to access our feed. Then, it will be sent to the static IP your are using for AIL. +1. Be a collaborator of CIRCL and ask to access our feed. It will be sent to the static IP your are using for AIL. -2. You can setup [pystemon](https://github.com/CIRCL/pystemon) and use the custom feeder provided by AIL +2. You can setup [pystemon](https://github.com/CIRCL/pystemon) and use the custom feeder provided by AIL (see below). ###Feeding AIL with pystemon AIL is an analysis tool, not a collector! -However, if you want to collect some paste and feed them to AIL, the procedure is described below. +However, if you want to collect some pastes and feed them to AIL, the procedure is described below. Nevertheless, moderate your queries! From 39ec7a2839abef2bce09524521d10ea4302aaeeb Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 13 Oct 2016 17:26:55 +0200 Subject: [PATCH 28/89] fixed typo 5 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf638e05..df1b8218 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ However, if you want to collect some pastes and feed them to AIL, the procedure Nevertheless, moderate your queries! -Here are the steps to setup your pystemon and feed data to AIL: +Here are the steps to setup pystemon and feed data to AIL: 1. Clone the [pystemon's git repository](https://github.com/CIRCL/pystemon) From 5a4ea734a2cb84256dbbd4b9580d178d421f8a3d Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 14 Oct 2016 14:26:33 +0200 Subject: [PATCH 29/89] Fixed path issues in doc + added dependencies related to ssdeep --- bin/ModuleInformation.py | 3 ++- doc/generate_graph_data.py | 8 ++++++-- doc/generate_modules_data_flow_graph.sh | 2 +- doc/module-data-flow.png | Bin 155381 -> 173014 bytes installing_deps.sh | 4 ++++ 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/bin/ModuleInformation.py b/bin/ModuleInformation.py index 1538f57a..df07bf14 100755 --- a/bin/ModuleInformation.py +++ b/bin/ModuleInformation.py @@ -120,7 +120,8 @@ if __name__ == "__main__": lastTime = datetime.datetime.now() module_file_array = set() - with open('../doc/all_modules.txt', 'r') as module_file: + path_allmod = os.path.join(os.environ['AIL_HOME'], 'doc/all_modules.txt') + with open(path_allmod, 'r') as module_file: for line in module_file: module_file_array.add(line[:-1]) diff --git a/doc/generate_graph_data.py b/doc/generate_graph_data.py index c1b4b98e..282fa849 100755 --- a/doc/generate_graph_data.py +++ b/doc/generate_graph_data.py @@ -1,6 +1,8 @@ #!/usr/bin/env python2 # -*-coding:UTF-8 -* +import os + content = "" modules = {} all_modules = [] @@ -8,7 +10,9 @@ curr_module = "" streamingPub = {} streamingSub = {} -with open('../bin/packages/modules.cfg', 'r') as f: +path = os.path.join(os.environ['AIL_BIN'], 'packages/modules.cfg') +path2 = os.path.join(os.environ['AIL_HOME'], 'doc/all_modules.txt') +with open(path, 'r') as f: for line in f: if line[0] != '#': if line[0] == '[': @@ -32,7 +36,7 @@ with open('../bin/packages/modules.cfg', 'r') as f: continue output_set_graph = set() - with open('all_modules.txt', 'w') as f2: + with open(path2, 'w') as f2: for e in all_modules: f2.write(e+"\n") diff --git a/doc/generate_modules_data_flow_graph.sh b/doc/generate_modules_data_flow_graph.sh index 90a578e0..bf758fde 100755 --- a/doc/generate_modules_data_flow_graph.sh +++ b/doc/generate_modules_data_flow_graph.sh @@ -1,3 +1,3 @@ #!/bin/bash -python generate_graph_data.py | dot -T png -o module-data-flow.png +python $AIL_HOME/doc/generate_graph_data.py | dot -T png -o $AIL_HOME/doc/module-data-flow.png diff --git a/doc/module-data-flow.png b/doc/module-data-flow.png index 73ed3b1d6d0cc57a125415352bd41fe13d03f82e..cea4cef57dc06889d1cd3307e91925efeb779665 100644 GIT binary patch literal 173014 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 z%0D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 literal 155381 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%0AqkF^G3eK@y-^bx7;TM>a3w*Z&!Alv_g*d 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 diff --git a/installing_deps.sh b/installing_deps.sh index b3df3221..74a1b000 100755 --- a/installing_deps.sh +++ b/installing_deps.sh @@ -24,6 +24,7 @@ sudo apt-get install graphviz -y sudo easy_install -U distribute # ssdeep sudo apt-get install libfuzzy-dev +sudo apt-get install build-essential libffi-dev automake autoconf libtool -y # REDIS # test ! -d redis/ && git clone https://github.com/antirez/redis.git @@ -104,3 +105,6 @@ python setup.py install # Download the necessary NLTK corpora and sentiment vader HOME=$(pwd) python -m textblob.download_corpora python -m nltk.downloader vader_lexicon + +#Create the file all_module and update the graph in doc +$AIL_HOME/doc/generate_modules_data_flow_graph.sh From 91678179fd72cd303aafc7bacf80204d7729f981 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 14 Oct 2016 16:33:54 +0200 Subject: [PATCH 30/89] Added msgs when queues or feed is not running/coming --- var/www/static/js/indexjavascript.js | 54 +++++++++++++++++----------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/var/www/static/js/indexjavascript.js b/var/www/static/js/indexjavascript.js index e527aafa..90e6e46c 100644 --- a/var/www/static/js/indexjavascript.js +++ b/var/www/static/js/indexjavascript.js @@ -219,28 +219,42 @@ function create_queue_table() { tr.appendChild(th); } - for(i = 0; i < (glob_tabvar.row1).length;i++){ - var tr = document.createElement('TR') - for(j = 0; j < 2; j++){ - var td = document.createElement('TD') - var moduleNum = j == 0 ? "." + glob_tabvar.row1[i][3] : ""; - td.appendChild(document.createTextNode(glob_tabvar.row1[i][j] + moduleNum)); - tr.appendChild(td) - } - // Used to decide the color of the row - // We have glob_tabvar.row1[][j] with: - // - j=0: ModuleName - // - j=1: queueLength - // - j=2: LastProcessedPasteTime - // - j=3: Number of the module belonging in the same category - if (parseInt(glob_tabvar.row1[i][2]) > 60*2 && parseInt(glob_tabvar.row1[i][1]) > 2) - tr.className += " danger"; - else if (parseInt(glob_tabvar.row1[i][2]) > 60*1) - tr.className += " warning"; - else - tr.className += " success"; + if ((glob_tabvar.row1).length == 0) { + var tr = document.createElement('TR'); + var td = document.createElement('TD'); + var td2 = document.createElement('TD'); + td.appendChild(document.createTextNode("No running queues")); + td2.appendChild(document.createTextNode("Or no feed")); + td.className += " danger"; + td2.className += " danger"; + tr.appendChild(td); + tr.appendChild(td2); tableBody.appendChild(tr); } + else { + for(i = 0; i < (glob_tabvar.row1).length;i++){ + var tr = document.createElement('TR') + for(j = 0; j < 2; j++){ + var td = document.createElement('TD') + var moduleNum = j == 0 ? "." + glob_tabvar.row1[i][3] : ""; + td.appendChild(document.createTextNode(glob_tabvar.row1[i][j] + moduleNum)); + tr.appendChild(td) + } + // Used to decide the color of the row + // We have glob_tabvar.row1[][j] with: + // - j=0: ModuleName + // - j=1: queueLength + // - j=2: LastProcessedPasteTime + // - j=3: Number of the module belonging in the same category + if (parseInt(glob_tabvar.row1[i][2]) > 60*2 && parseInt(glob_tabvar.row1[i][1]) > 2) + tr.className += " danger"; + else if (parseInt(glob_tabvar.row1[i][2]) > 60*1) + tr.className += " warning"; + else + tr.className += " success"; + tableBody.appendChild(tr); + } + } Tablediv.appendChild(table); } From 7c8d414948452cbfc0456cf8ab011bac965fa8a6 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 14 Oct 2016 16:47:22 +0200 Subject: [PATCH 31/89] modified provider name into no data when there is no data --- .../sentiment_analysis_trending.html | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/var/www/templates/sentiment_analysis_trending.html b/var/www/templates/sentiment_analysis_trending.html index e1788f35..b20c3696 100644 --- a/var/www/templates/sentiment_analysis_trending.html +++ b/var/www/templates/sentiment_analysis_trending.html @@ -205,24 +205,24 @@ - worst1 - best1 + no data + no data - worst2 - best2 + no data + no data - worst3 - best3 + no data + no data - worst4 - best4 + no data + no data - worst5 - best5 + no data + no data From 149a43128dcd1dff46dc7bd3dfefab95dbe67fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Sat, 22 Oct 2016 18:33:19 -0400 Subject: [PATCH 32/89] Use latest version off tlsh #82 fixed upstream --- installing_deps.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/installing_deps.sh b/installing_deps.sh index 74a1b000..9ef7480a 100755 --- a/installing_deps.sh +++ b/installing_deps.sh @@ -98,7 +98,6 @@ popd # Py tlsh pushd tlsh/py_ext -git checkout a67c69b0cdfd168c62c159d41b8a3612ee2b0df1 # temporary, latest commit breaks the python module python setup.py build python setup.py install From 815104f2f4d950e8bf45804fd18446f4a1f35aa3 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 27 Oct 2016 09:05:56 +0200 Subject: [PATCH 33/89] Fixed missing dependency in sentimentAnalysis --- installing_deps.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/installing_deps.sh b/installing_deps.sh index 74a1b000..94e8f936 100755 --- a/installing_deps.sh +++ b/installing_deps.sh @@ -105,6 +105,7 @@ python setup.py install # Download the necessary NLTK corpora and sentiment vader HOME=$(pwd) python -m textblob.download_corpora python -m nltk.downloader vader_lexicon +python -m nltk.downloader punkt #Create the file all_module and update the graph in doc $AIL_HOME/doc/generate_modules_data_flow_graph.sh From 1826b170ec508fc6554ef433a09906fd62d01936 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 27 Oct 2016 11:27:26 +0200 Subject: [PATCH 34/89] Added support of local paste view in dashboard for Mails event only --- bin/Mail.py | 4 ++-- var/www/static/js/indexjavascript.js | 17 +++++++++++++++-- var/www/templates/index.html | 2 ++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/bin/Mail.py b/bin/Mail.py index 2b3ed5fc..161082b0 100755 --- a/bin/Mail.py +++ b/bin/Mail.py @@ -55,9 +55,9 @@ if __name__ == "__main__": list(MX_values[1]))) pprint.pprint(MX_values) - to_print = 'Mails;{};{};{};Checked {} e-mail(s)'.\ + to_print = 'Mails;{};{};{};Checked {} e-mail(s);{}'.\ format(PST.p_source, PST.p_date, PST.p_name, - MX_values[0]) + MX_values[0], PST.p_path) if MX_values[0] > is_critical: publisher.warning(to_print) #Send to duplicate diff --git a/var/www/static/js/indexjavascript.js b/var/www/static/js/indexjavascript.js index 90e6e46c..19980a69 100644 --- a/var/www/static/js/indexjavascript.js +++ b/var/www/static/js/indexjavascript.js @@ -109,11 +109,11 @@ function create_log_table(obj_json) { var pdate = document.createElement('TD') var nam = document.createElement('TD') var msage = document.createElement('TD') + var inspect = document.createElement('TD') var chansplit = obj_json.channel.split('.'); var parsedmess = obj_json.data.split(';'); - if (parsedmess[0] == "Global"){ var paste_processed = parsedmess[4].split(" ")[2]; window.paste_num_tabvar = paste_processed; @@ -139,7 +139,7 @@ function create_log_table(obj_json) { source_url = "http://"+parsedmess[1]+"/"+parsedmess[3].split(".")[0]; } source_link.setAttribute("HREF",source_url); - source_link.setAttribute("TARGET", "_blank") + source_link.setAttribute("TARGET", "_blank"); source_link.appendChild(document.createTextNode(parsedmess[1])); src.appendChild(source_link); @@ -169,6 +169,18 @@ function create_log_table(obj_json) { msage.appendChild(document.createTextNode(message.join(" "))); + var paste_path = parsedmess[5]; + var url_to_saved_paste = url_showSavedPath+"?paste="+paste_path+"&num=0"; + + var action_icon_a = document.createElement("A"); + action_icon_a.setAttribute("TARGET", "_blank"); + action_icon_a.setAttribute("HREF", url_to_saved_paste); + var action_icon_span = document.createElement('SPAN'); + action_icon_span.className = "fa fa-search-plus"; + action_icon_a.appendChild(action_icon_span); + + inspect.appendChild(action_icon_a); + tr.appendChild(time) tr.appendChild(chan); tr.appendChild(level); @@ -177,6 +189,7 @@ function create_log_table(obj_json) { tr.appendChild(pdate); tr.appendChild(nam); tr.appendChild(msage); + tr.appendChild(inspect); if (tr.className == document.getElementById("checkbox_log_info").value && document.getElementById("checkbox_log_info").checked == true) { tableBody.appendChild(tr); diff --git a/var/www/templates/index.html b/var/www/templates/index.html index 5d8639cf..66c38a2c 100644 --- a/var/www/templates/index.html +++ b/var/www/templates/index.html @@ -140,6 +140,7 @@ Date Paste name Message + Actions @@ -153,6 +154,7 @@ + + + + + + + + +
+
+
+

Paste: {{ request.args.get('paste') }}

+ +
+ +
+ -

Paste: {{ request.args.get('num') }}

-

{{ request.args.get('paste') }}

- -

+
@@ -46,11 +65,16 @@

No Duplicate

{% else %}

Duplicate list:

- +
{% set i = 0 %} + - + + + + + {% for dup_path in duplicate_list %} @@ -59,6 +83,7 @@ {% set i = i + 1 %} {% endfor %} +
Hash typePaste infoHash typePaste infoPath
{{ hashtype_list[i] }}
{% endif %}

Content:

@@ -66,6 +91,9 @@
- +
+ From 7e7e679ab685bcec1912f6df7b2e489b05c8fbcf Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 27 Oct 2016 15:53:45 +0200 Subject: [PATCH 37/89] Added dataTable for duplicate in show saved paste --- var/www/templates/show_saved_paste.html | 133 ++++++++++++------------ 1 file changed, 65 insertions(+), 68 deletions(-) diff --git a/var/www/templates/show_saved_paste.html b/var/www/templates/show_saved_paste.html index c1393aa6..b164bcb1 100644 --- a/var/www/templates/show_saved_paste.html +++ b/var/www/templates/show_saved_paste.html @@ -19,79 +19,76 @@ -
+
+
+
+

Paste: {{ request.args.get('paste') }}

+ +
+
+
- - - - - - - + + + + + + + + + + + -
-
-
-

Paste: {{ request.args.get('paste') }}

- -
-
- -
+

Paste: {{ request.args.get('paste') }}

+ + @@ -88,7 +81,6 @@ - + + From f51808d91491855bbb1038ca92b6b619902e59e9 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 28 Oct 2016 09:48:22 +0200 Subject: [PATCH 40/89] Added support of html formatting in search result dynamic loading table --- var/www/templates/search.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/var/www/templates/search.html b/var/www/templates/search.html index 8f393302..b984ecdc 100644 --- a/var/www/templates/search.html +++ b/var/www/templates/search.html @@ -153,10 +153,10 @@ for(i=0; i "+ data.path_array[i] +"", data.date_array[i], data.size_array[i], - data.preview_array[i] + "

" ] ).draw( false ); } if (data.moreData == true) From c95000866db2ab371d7ad02f8932c2d06e1c562b Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 28 Oct 2016 14:21:08 +0200 Subject: [PATCH 41/89] Dynamic table in search now load all the data + fixed bugs where tooltip where not shown on other than the first page displayed and tooltip interpret html (not supposed to...) --- var/www/Flask_server.py | 13 ++++++++----- var/www/templates/search.html | 34 ++++++++++++++++++++++++---------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/var/www/Flask_server.py b/var/www/Flask_server.py index 73306ac6..a193fc16 100755 --- a/var/www/Flask_server.py +++ b/var/www/Flask_server.py @@ -328,7 +328,6 @@ def search(): paste_size = [] # Search filename - print r_serv_pasteName.smembers(q[0]) for path in r_serv_pasteName.smembers(q[0]): print path r.append(path) @@ -351,7 +350,7 @@ def search(): from whoosh.qparser import QueryParser with ix.searcher() as searcher: query = QueryParser("content", ix.schema).parse(" ".join(q)) - results = searcher.search_page(query, 1, pagelen=20) + results = searcher.search_page(query, 1, pagelen=10) for x in results: r.append(x.items()[0][1]) paste = Paste.Paste(x.items()[0][1]) @@ -370,7 +369,7 @@ def get_more_search_result(): query = request.form['query'] q = [] q.append(query) - offset = request.form['offset'] + offset = int(request.form['offset']) path_array = [] preview_array = [] @@ -386,7 +385,7 @@ def get_more_search_result(): from whoosh.qparser import QueryParser with ix.searcher() as searcher: query = QueryParser("content", ix.schema).parse(" ".join(q)) - results = searcher.search_page(query, offset, pagelen=20) + results = searcher.search_page(query, offset, pagelen=10) for x in results: path_array.append(x.items()[0][1]) paste = Paste.Paste(x.items()[0][1]) @@ -402,7 +401,11 @@ def get_more_search_result(): to_return["preview_array"] = preview_array to_return["date_array"] = date_array to_return["size_array"] = size_array - to_return["moreData"] = False + if len(path_array) < 10: #pagelength + to_return["moreData"] = False + else: + to_return["moreData"] = True + return jsonify(to_return) diff --git a/var/www/templates/search.html b/var/www/templates/search.html index b984ecdc..b1f8cbab 100644 --- a/var/www/templates/search.html +++ b/var/www/templates/search.html @@ -84,7 +84,8 @@
- {{ r|length }} Results for "{{ query }}" + {{ r|length }} Results for "{{ query }} +
@@ -130,12 +131,16 @@ @@ -145,25 +150,34 @@ // Loop to recover all the data from get_more_search_results // And add it dynamically top the dataTable - function load_search_data(search_table, prev_query, offset) { - $.post( "{{ url_for('get_more_search_result') }}", { query: prev_query, offset: offset }).done(function( data ) { - console.log( "Data Loaded: " ) - console.log( data ); + function load_search_data(init_num_of_elements_in_table, search_table, prev_query, offset) { + var options = { query: prev_query, offset: offset }; + console.log(options); + $.post( "{{ url_for('get_more_search_result') }}", options).done(function( data ) { for(i=0; i "+ data.path_array[i] +"", data.date_array[i], data.size_array[i], - "

" + "

" ] ).draw( false ); } + $("#numberOfRes").text(parseInt($("#numberOfRes").text()) + data.path_array.length); if (data.moreData == true) - load_search_data(prev_query, offset+i); + load_search_data(init_num_of_elements_in_table, search_table, prev_query, offset+10); + else { + $("#loading_gif_search").hide(); + } }); } + $('#myTable').on( 'page.dt', function () { + setTimeout(function(){ $('[data-toggle="tooltip"]').tooltip(); }, 300); + } ); + From acdd1367a36ce8b1c1985b6d9bc7a61c4344205f Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 28 Oct 2016 16:55:56 +0200 Subject: [PATCH 42/89] Added event source for getImportantPaste --- var/www/Flask_server.py | 37 +++++++++++++++- .../templates/important_paste_by_module.html | 42 ++++++++++++++++++- var/www/templates/search.html | 2 - 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/var/www/Flask_server.py b/var/www/Flask_server.py index a193fc16..7a630ccc 100755 --- a/var/www/Flask_server.py +++ b/var/www/Flask_server.py @@ -78,6 +78,28 @@ def event_stream(): if msg['type'] == 'pmessage' and level != "DEBUG": yield 'data: %s\n\n' % json.dumps(msg) +def event_stream_getImportantPasteByModule(module_name): + index = 0 + all_pastes_list = getPastebyType(r_serv_db, module_name) + for path in all_pastes_list: + index += 1 + paste = Paste.Paste(path) + content = paste.get_p_content().decode('utf8', 'ignore') + content_range = max_preview_char if len(content)>max_preview_char else len(content)-1 + curr_date = str(paste._get_p_date()) + curr_date = curr_date[0:4]+'/'+curr_date[4:6]+'/'+curr_date[6:] + data = {} + data["module"] = module_name + data["index"] = index + data["path"] = path + data["content"] = content[0:content_range] + data["linenum"] = paste.get_lines_info()[0] + data["date"] = curr_date + data["char_to_display"] = max_preview_modal + data["finished"] = True if index == len(all_pastes_list) else False + print index + yield 'data: %s\n\n' % json.dumps(data) + def get_queues(r): # We may want to put the llen in a pipeline to do only one query. @@ -452,8 +474,9 @@ def importantPasteByModule(): paste_date = [] paste_linenum = [] all_path = [] + allPastes = getPastebyType(r_serv_db, module_name) - for path in getPastebyType(r_serv_db, module_name): + for path in allPastes[0:10]: all_path.append(path) paste = Paste.Paste(path) content = paste.get_p_content().decode('utf8', 'ignore') @@ -464,7 +487,17 @@ def importantPasteByModule(): paste_date.append(curr_date) paste_linenum.append(paste.get_lines_info()[0]) - return render_template("important_paste_by_module.html", all_path=all_path, content=all_content, paste_date=paste_date, paste_linenum=paste_linenum, char_to_display=max_preview_modal) + if len(allPastes) > 10: + finished = "" + else: + finished = "display: none;" + + return render_template("important_paste_by_module.html", moduleName=module_name, all_path=all_path, content=all_content, paste_date=paste_date, paste_linenum=paste_linenum, char_to_display=max_preview_modal, finished=finished) + +@app.route("/_getImportantPasteByModule") +def getImportantPasteByModule(): + module_name = request.args.get('moduleName') + return flask.Response(event_stream_getImportantPasteByModule(module_name), mimetype="text/event-stream") @app.route("/moduletrending/") def moduletrending(): diff --git a/var/www/templates/important_paste_by_module.html b/var/www/templates/important_paste_by_module.html index 061648c4..c1664283 100644 --- a/var/www/templates/important_paste_by_module.html +++ b/var/www/templates/important_paste_by_module.html @@ -1,4 +1,5 @@ -
+ +
@@ -25,10 +26,47 @@

+ + + + diff --git a/var/www/templates/search.html b/var/www/templates/search.html index b1f8cbab..ed50ba89 100644 --- a/var/www/templates/search.html +++ b/var/www/templates/search.html @@ -131,8 +131,6 @@ @@ -186,37 +182,6 @@ var char_to_display = {{ char_to_display }}; var start_index = 0; - // On click, get html content from url and update the corresponding modal - $("[data-toggle='modal']").on("click", function (event) { - event.preventDefault(); - var modal=$(this); - var url = " {{ url_for('showpreviewpaste') }}?paste=" + $(this).attr('data-path') + "&num=" + $(this).attr('data-num'); - $.get(url, function (data) { - $("#mymodalbody").html(data); - var button = $(''); - button.tooltip(); - $("#mymodalbody").children(".panel-default").append(button); - - $("#button_show_path").attr('href', $(modal).attr('data-url')); - $("#button_show_path").show('fast'); - $("#loading-gif-modal").css("visibility", "hidden"); // Hide the loading GIF - if ($("[data-initsize]").attr('data-initsize') < char_to_display) { // All the content is displayed - nothing_to_display(); - } - // On click, donwload all paste's content - $("#load-more-button").on("click", function (event) { - if (complete_paste == null) { //Donwload only once - $.get("{{ url_for('getmoredata') }}"+"?paste="+$(modal).attr('data-path'), function(data, status){ - complete_paste = data; - update_preview(); - }); - } else { - update_preview(); - } - }); - }); - }); - // When the modal goes out, refresh it to normal content $("#mymodal").on('hidden.bs.modal', function () { $("#mymodalbody").html("

Loading paste information...

"); @@ -254,5 +219,45 @@ new_content.show('fast'); $("#load-more-button").hide(); } + + + $('#myTable').on( 'draw.dt', function () { + // Bind tooltip each time we draw a new page + $('[data-toggle="tooltip"]').tooltip(); + // On click, get html content from url and update the corresponding modal + $("[data-toggle='modal']").off('click.openmodal').on("click.openmodal", function (event) { + var modal=$(this); + var url = " {{ url_for('showpreviewpaste') }}?paste=" + $(this).attr('data-path') + "&num=" + $(this).attr('data-num'); + $.get(url, function (data) { + + // clear data by removing html, body, head tags. prevent dark modal background stack bug. + var cleared_data = data.split("")[1].split("")[0]; + $("#mymodalbody").html(cleared_data); + + var button = $(''); + button.tooltip(); + $("#mymodalbody").children(".panel-default").append(button); + + $("#button_show_path").attr('href', $(modal).attr('data-url')); + $("#button_show_path").show('fast'); + $("#loading-gif-modal").css("visibility", "hidden"); // Hide the loading GIF + if ($("[data-initsize]").attr('data-initsize') < char_to_display) { // All the content is displayed + nothing_to_display(); + } + // On click, donwload all paste's content + $("#load-more-button").off('click.download').on("click.download", function (event) { + if (complete_paste == null) { //Donwload only once + $.get("{{ url_for('getmoredata') }}"+"?paste="+$(modal).attr('data-path'), function(data, status){ + complete_paste = data; + update_preview(); + }); + } else { + update_preview(); + } + }); + }); + }); + } ); + From 7763bfb4c79c8a466268fc418cbe0a1cde951bba Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 10 Nov 2016 15:39:45 +0100 Subject: [PATCH 46/89] Pastes dataTable now display only the latest clicked paste even if there were discarded queries --- var/www/Flask_server.py | 3 +- .../templates/important_paste_by_module.html | 59 +++++++++++-------- var/www/templates/search.html | 59 +++++++++++-------- 3 files changed, 73 insertions(+), 48 deletions(-) diff --git a/var/www/Flask_server.py b/var/www/Flask_server.py index 79474155..e45bc407 100755 --- a/var/www/Flask_server.py +++ b/var/www/Flask_server.py @@ -811,7 +811,8 @@ def showsavedpaste(): @app.route("/showpreviewpaste/") def showpreviewpaste(): - return showpaste(max_preview_modal) + num = request.args.get('num', '') + return "|num|"+num+"|num|"+showpaste(max_preview_modal) @app.route("/getmoredata/") diff --git a/var/www/templates/important_paste_by_module.html b/var/www/templates/important_paste_by_module.html index 1f3b1bae..5b4bd8c1 100644 --- a/var/www/templates/important_paste_by_module.html +++ b/var/www/templates/important_paste_by_module.html @@ -59,7 +59,10 @@ function deploy_source() { From df8d9780880351984555256787d494ed1077bdc1 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 24 Nov 2016 13:31:31 +0100 Subject: [PATCH 51/89] Added dynamic data loading in dataTable in search.html --- var/www/Flask_server.py | 16 ++++-- .../templates/important_paste_by_module.html | 2 +- var/www/templates/search.html | 51 ++++++++++++++----- 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/var/www/Flask_server.py b/var/www/Flask_server.py index 2d3aa196..2b01b6fa 100755 --- a/var/www/Flask_server.py +++ b/var/www/Flask_server.py @@ -347,6 +347,7 @@ def search(): c = [] #preview of the paste content paste_date = [] paste_size = [] + num_elem_to_get = 50 # Search filename for path in r_serv_pasteName.smembers(q[0]): @@ -370,7 +371,7 @@ def search(): from whoosh.qparser import QueryParser with ix.searcher() as searcher: query = QueryParser("content", ix.schema).parse(" ".join(q)) - results = searcher.search_page(query, 1, pagelen=10) + results = searcher.search_page(query, 1, pagelen=num_elem_to_get) for x in results: r.append(x.items()[0][1]) paste = Paste.Paste(x.items()[0][1]) @@ -381,7 +382,10 @@ def search(): curr_date = curr_date[0:4]+'/'+curr_date[4:6]+'/'+curr_date[6:] paste_date.append(curr_date) paste_size.append(paste._get_p_size()) - return render_template("search.html", r=r, c=c, query=request.form['query'], paste_date=paste_date, paste_size=paste_size, char_to_display=max_preview_modal) + results = searcher.search(query) + num_res = len(results) + + return render_template("search.html", r=r, c=c, query=request.form['query'], paste_date=paste_date, paste_size=paste_size, char_to_display=max_preview_modal, num_res=num_res) @app.route("/get_more_search_result", methods=['POST']) @@ -389,7 +393,8 @@ def get_more_search_result(): query = request.form['query'] q = [] q.append(query) - offset = int(request.form['offset']) + page_offset = int(request.form['page_offset']) + num_elem_to_get = 50 path_array = [] preview_array = [] @@ -405,7 +410,7 @@ def get_more_search_result(): from whoosh.qparser import QueryParser with ix.searcher() as searcher: query = QueryParser("content", ix.schema).parse(" ".join(q)) - results = searcher.search_page(query, offset, pagelen=10) + results = searcher.search_page(query, page_offset, num_elem_to_get) for x in results: path_array.append(x.items()[0][1]) paste = Paste.Paste(x.items()[0][1]) @@ -421,7 +426,8 @@ def get_more_search_result(): to_return["preview_array"] = preview_array to_return["date_array"] = date_array to_return["size_array"] = size_array - if len(path_array) < 10: #pagelength + print "len(path_array)="+str(len(path_array)) + if len(path_array) < num_elem_to_get: #pagelength to_return["moreData"] = False else: to_return["moreData"] = True diff --git a/var/www/templates/important_paste_by_module.html b/var/www/templates/important_paste_by_module.html index 535925a1..ca98ce59 100644 --- a/var/www/templates/important_paste_by_module.html +++ b/var/www/templates/important_paste_by_module.html @@ -28,7 +28,7 @@
- +

diff --git a/var/www/templates/search.html b/var/www/templates/search.html index 22bb31d2..e7351ca1 100644 --- a/var/www/templates/search.html +++ b/var/www/templates/search.html @@ -83,7 +83,6 @@
{{ r|length }} Results for "{{ query }} -
@@ -114,7 +113,11 @@ {% endfor %}
#
+
+ + Totalling {{ num_res }} items
+
@@ -123,6 +126,7 @@
+
@@ -130,17 +134,29 @@ var search_table; var last_clicked_paste; var can_change_modal_content = true; + var page_offset; + var offset; + var all_loaded; + var init_num_of_elements_in_table; + var query; + var pagelen = 50; $(document).ready(function(){ $('[data-toggle="tooltip"]').tooltip(); $("#button_show_path").hide(); - var search_table = $('#myTable').DataTable(); + search_table = $('#myTable').DataTable(); - var prev_query = "{{ query }}"; - var offset = 2; - var init_num_of_elements_in_table = parseInt("{{ r|length }}"); // Comes from the file search - load_search_data(init_num_of_elements_in_table, search_table, prev_query, offset); + query = "{{ query }}"; + offset = 0; + page_offset = 2; //page 1 already loaded + all_loaded = false; + init_num_of_elements_in_table = parseInt("{{ r|length }}"); // Comes from the file search + + + if (init_num_of_elements_in_table == pagelen) { + $("#load_more_json_button1").show(); + } }); @@ -149,27 +165,36 @@ // Loop to recover all the data from get_more_search_results // And add it dynamically top the dataTable + function add_entries() { //Used to disable the button before going to the big loop + $("#load_more_json_button1").attr('disabled','disabled'); + setTimeout(function() { load_search_50_data();}, 50); + } - function load_search_data(init_num_of_elements_in_table, search_table, prev_query, offset) { - var options = { query: prev_query, offset: offset }; + function load_search_50_data() { + var options = { query: query, page_offset: page_offset }; $.post( "{{ url_for('get_more_search_result') }}", options).done(function( data ) { for(i=0; i "+ data.path_array[i] +"
", data.date_array[i], data.size_array[i], "

" ] ).draw( false ); } + offset = offset + data.path_array.length; + page_offset = page_offset+1; $("#numberOfRes").text(parseInt($("#numberOfRes").text()) + data.path_array.length); - if (data.moreData == true) - load_search_data(init_num_of_elements_in_table, search_table, prev_query, offset+10); - else { - $("#loading_gif_search").hide(); + if (data.moreData == true) { + //continue + } else { + all_loaded = true; + $("#load_more_json_button1").hide(); } + $("#load_more_json_button1").removeAttr('disabled'); + return data.path_array.length; }); } From 5d269ea1eebcacf75e9cf58f8d5d84592e23335f Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 24 Nov 2016 15:05:29 +0100 Subject: [PATCH 52/89] Added date in Duplicate for better distinguish duplicate. Also, added a timeout for the dataTable in duplicate inside the modal. --- bin/Duplicates.py | 7 +++++-- var/www/Flask_server.py | 16 +++++++++++++--- var/www/templates/search.html | 3 ++- var/www/templates/show_saved_paste.html | 2 ++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/bin/Duplicates.py b/bin/Duplicates.py index cd9e1f97..50def29f 100755 --- a/bin/Duplicates.py +++ b/bin/Duplicates.py @@ -131,8 +131,10 @@ if __name__ == "__main__": # index of paste index_current = r_serv_dico.get(dico_hash) paste_path = r_serv_dico.get(index_current) + paste_date = r_serv_dico.get(index_current+'_date') + paste_date = paste_date if paste_date != None else "No date available" if paste_path != None: - hash_dico[dico_hash] = (hash_type, paste_path, percent) + hash_dico[dico_hash] = (hash_type, paste_path, percent, paste_date) print '['+hash_type+'] '+'comparing: ' + str(PST.p_path[44:]) + ' and ' + str(paste_path[44:]) + ' percentage: ' + str(percent) except Exception,e: @@ -142,6 +144,7 @@ if __name__ == "__main__": # Add paste in DB after checking to prevent its analysis twice # hash_type_i -> index_i AND index_i -> PST.PATH r_serv1.set(index, PST.p_path) + r_serv1.set(index+'_date', PST._get_p_date()) r_serv1.sadd("INDEX", index) # Adding hashes in Redis for hash_type, paste_hash in paste_hashes.iteritems(): @@ -152,7 +155,7 @@ if __name__ == "__main__": # if there is data in this dictionnary if len(hash_dico) != 0: - # paste_tuple = (paste_path, percent) + # paste_tuple = (hash_type, date, paste_path, percent) for dico_hash, paste_tuple in hash_dico.items(): dupl.append(paste_tuple) diff --git a/var/www/Flask_server.py b/var/www/Flask_server.py index 2b01b6fa..fcd67a21 100755 --- a/var/www/Flask_server.py +++ b/var/www/Flask_server.py @@ -143,6 +143,7 @@ def showpaste(content_range): p_duplicate_full_list = json.loads(paste._get_p_duplicate()) p_duplicate_list = [] p_simil_list = [] + p_date_list = [] p_hashtype_list = [] @@ -170,20 +171,29 @@ def showpaste(content_range): hash_types = str(hash_types).replace("[","").replace("]","") if len(hash_types)==1 else str(hash_types) comp_vals = str(comp_vals).replace("[","").replace("]","") if len(comp_vals)==1 else str(comp_vals) - new_dup_list.append([hash_types.replace("'", ""), p_duplicate_full_list[dup_list_index][1], comp_vals]) + if len(p_duplicate_full_list[dup_list_index]) > 3: + try: + date_paste = str(int(p_duplicate_full_list[dup_list_index][3])) + date_paste = date_paste[0:4]+"-"+date_paste[4:6]+"-"+date_paste[6:8] + except ValueError: + date_paste = str(p_duplicate_full_list[dup_list_index][3]) + else: + date_paste = "No date available" + new_dup_list.append([hash_types.replace("'", ""), p_duplicate_full_list[dup_list_index][1], comp_vals, date_paste]) # Create the list to pass to the webpage for dup_list in new_dup_list: - hash_type, path, simil_percent = dup_list + hash_type, path, simil_percent, date_paste = dup_list p_duplicate_list.append(path) p_simil_list.append(simil_percent) p_hashtype_list.append(hash_type) + p_date_list.append(date_paste) if content_range != 0: p_content = p_content[0:content_range] - return render_template("show_saved_paste.html", date=p_date, source=p_source, encoding=p_encoding, language=p_language, size=p_size, mime=p_mime, lineinfo=p_lineinfo, content=p_content, initsize=len(p_content), duplicate_list = p_duplicate_list, simil_list = p_simil_list, hashtype_list = p_hashtype_list) + return render_template("show_saved_paste.html", date=p_date, source=p_source, encoding=p_encoding, language=p_language, size=p_size, mime=p_mime, lineinfo=p_lineinfo, content=p_content, initsize=len(p_content), duplicate_list = p_duplicate_list, simil_list = p_simil_list, hashtype_list = p_hashtype_list, date_list=p_date_list) def getPastebyType(server, module_name): all_path = [] diff --git a/var/www/templates/search.html b/var/www/templates/search.html index e7351ca1..9b43967d 100644 --- a/var/www/templates/search.html +++ b/var/www/templates/search.html @@ -115,7 +115,7 @@
- Totalling {{ num_res }} items + Totalling {{ num_res }} results related to paste content
@@ -266,6 +266,7 @@ // clear data by removing html, body, head tags. prevent dark modal background stack bug. var cleared_data = data.split("")[1].split("")[0]; $("#mymodalbody").html(cleared_data); + setTimeout(function() { $('#tableDup').DataTable(); }, 150); var button = $(''); button.tooltip(); diff --git a/var/www/templates/show_saved_paste.html b/var/www/templates/show_saved_paste.html index 6ab209ca..ef955bfe 100644 --- a/var/www/templates/show_saved_paste.html +++ b/var/www/templates/show_saved_paste.html @@ -61,6 +61,7 @@ Hash type Paste info + Date Path @@ -69,6 +70,7 @@ {{ hashtype_list[i] }} Similarity: {{ simil_list[i] }}% + {{ date_list[i] }} {{ dup_path }} {% set i = i + 1 %} From 224fbc8084735a444e10f5edb832e3b242eea24d Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 24 Nov 2016 16:58:32 +0100 Subject: [PATCH 53/89] Better handle stuck modules. Differentiate between not running and no info and tries to restart stuck ones. --- bin/ModuleInformation.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/bin/ModuleInformation.py b/bin/ModuleInformation.py index df07bf14..7520f695 100755 --- a/bin/ModuleInformation.py +++ b/bin/ModuleInformation.py @@ -36,11 +36,11 @@ command_restart_module = "screen -S \"Script\" -X screen -t \"{}\" bash -c \"./{ def getPid(module): p = Popen([command_search_pid.format(module+".py")], stdin=PIPE, stdout=PIPE, bufsize=1, shell=True) for line in p.stdout: + print line splittedLine = line.split() if 'python2' in splittedLine: return int(splittedLine[0]) - else: - return None + return None def clearRedisModuleInfo(): for k in server.keys("MODULE_*"): @@ -87,7 +87,9 @@ def kill_module(module): p2 = Popen([command_restart_module.format(module, module)], stdin=PIPE, stdout=PIPE, bufsize=1, shell=True) else: print 'killing failed!' - time.sleep(7) + else: + print 'Module does not exist' + time.sleep(5) if __name__ == "__main__": @@ -120,6 +122,7 @@ if __name__ == "__main__": lastTime = datetime.datetime.now() module_file_array = set() + no_info_modules = {} path_allmod = os.path.join(os.environ['AIL_HOME'], 'doc/all_modules.txt') with open(path_allmod, 'r') as module_file: for line in module_file: @@ -158,7 +161,19 @@ if __name__ == "__main__": for curr_queue in module_file_array: if curr_queue not in all_queue: - printarray3.append([curr_queue, "Not running"]) + printarray3.append([curr_queue, "Not running"]) + else: + if len(list(server.smembers('MODULE_TYPE_'+curr_queue))) == 0: + if curr_queue not in no_info_modules: + no_info_modules[curr_queue] = int(time.time()) + printarray3.append([curr_queue, "No data"]) + else: + #If no info since long time, try to kill + if int(time.time()) - no_info_modules[curr_queue] > threshold_stucked_module: + kill_module(curr_queue) + no_info_modules[curr_queue] = int(time.time()) + printarray3.append([curr_queue, "Stuck or idle, restarting in " + str(threshold_stucked_module - (int(time.time()) - no_info_modules[curr_queue])) + "s"]) + printarray1.sort(lambda x,y: cmp(x[4], y[4]), reverse=True) printarray2.sort(lambda x,y: cmp(x[4], y[4]), reverse=True) From a3255d168cdbb66c3569bbc0801ac57a66398eeb Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 25 Nov 2016 11:54:16 +0100 Subject: [PATCH 54/89] ModuleInfo correctly handle CurveManageTopSets, Changed queue coloring in dashboard, ModuleInformation now have a history of executed command and better handle module killing. --- bin/CurveManageTopSets.py | 18 ++++++ bin/ModuleInformation.py | 89 ++++++++++++++++++++++++---- var/www/Flask_server.py | 3 +- var/www/static/js/indexjavascript.js | 4 +- var/www/templates/index.html | 1 + 5 files changed, 100 insertions(+), 15 deletions(-) diff --git a/bin/CurveManageTopSets.py b/bin/CurveManageTopSets.py index 8f316333..f4aacd94 100755 --- a/bin/CurveManageTopSets.py +++ b/bin/CurveManageTopSets.py @@ -17,6 +17,7 @@ Requirements import redis import time +import datetime import copy from pubsublogger import publisher from packages import lib_words @@ -87,6 +88,11 @@ def manage_top_set(): for elem in array_month: server_term.zadd(top_termFreq_setName_month[0], float(elem[1]), elem[0]) + timestamp = int(time.mktime(datetime.datetime.now().timetuple())) + value = str(timestamp) + ", " + "-" + r_temp.set("MODULE_"+ "CurveManageTopSets" + "_" + str(os.getpid()), value) + print "refreshed module" + if __name__ == '__main__': @@ -105,6 +111,18 @@ if __name__ == '__main__': cfg = ConfigParser.ConfigParser() cfg.read(configfile) + + # For Module Manager + r_temp = redis.StrictRedis( + host=cfg.get('RedisPubSub', 'host'), + port=cfg.getint('RedisPubSub', 'port'), + db=cfg.getint('RedisPubSub', 'db')) + + timestamp = int(time.mktime(datetime.datetime.now().timetuple())) + value = str(timestamp) + ", " + "-" + r_temp.set("MODULE_"+ "CurveManageTopSets" + "_" + str(os.getpid()), value) + r_temp.sadd("MODULE_TYPE_"+ "CurveManageTopSets" , str(os.getpid())) + server_term = redis.StrictRedis( host=cfg.get("Redis_Level_DB_TermFreq", "host"), port=cfg.getint("Redis_Level_DB_TermFreq", "port"), diff --git a/bin/ModuleInformation.py b/bin/ModuleInformation.py index 7520f695..d874898b 100755 --- a/bin/ModuleInformation.py +++ b/bin/ModuleInformation.py @@ -26,12 +26,16 @@ from terminaltables import AsciiTable import textwrap # CONFIG VARIABLES -threshold_stucked_module = 60*60*1 #1 hour +threshold_stucked_module = 60*10*1 #1 hour +kill_retry_threshold = 60 #1m log_filename = "../logs/moduleInfo.log" command_search_pid = "ps a -o pid,cmd | grep {}" command_search_name = "ps a -o pid,cmd | grep {}" command_restart_module = "screen -S \"Script\" -X screen -t \"{}\" bash -c \"./{}.py; read x\"" +printarrayGlob = [None]*14 +printarrayGlob.insert(0, ["Time", "Module", "PID", "Action"]) +lastTimeKillCommand = {} def getPid(module): p = Popen([command_search_pid.format(module+".py")], stdin=PIPE, stdout=PIPE, bufsize=1, shell=True) @@ -45,6 +49,9 @@ def getPid(module): def clearRedisModuleInfo(): for k in server.keys("MODULE_*"): server.delete(k) + inst_time = datetime.datetime.fromtimestamp(int(time.time())) + printarrayGlob.insert(1, [inst_time, "*", "-", "Cleared redis module info"]) + printarrayGlob.pop() def cleanRedis(): for k in server.keys("MODULE_TYPE_*"): @@ -60,36 +67,76 @@ def cleanRedis(): if not flag_pid_valid: print flag_pid_valid, 'cleaning', pid, 'in', k server.srem(k, pid) - time.sleep(5) + inst_time = datetime.datetime.fromtimestamp(int(time.time())) + printarrayGlob.insert(1, [inst_time, moduleName, pid, "Cleared invalid pid in " + k]) + printarrayGlob.pop() + #time.sleep(5) -def kill_module(module): +def kill_module(module, pid): print '' print '-> trying to kill module:', module - pid = getPid(module) + if pid is None: + print 'pid was None' + printarrayGlob.insert(1, [0, module, pid, "PID was None"]) + printarrayGlob.pop() + pid = getPid(module) + else: #Verify that the pid is at least in redis + if server.exists("MODULE_"+module+"_"+str(pid)) == 0: + return + + lastTimeKillCommand[pid] = int(time.time()) if pid is not None: - os.kill(pid, signal.SIGUSR1) + try: + os.kill(pid, signal.SIGUSR1) + except OSError: + print pid, 'already killed' + inst_time = datetime.datetime.fromtimestamp(int(time.time())) + printarrayGlob.insert(1, [inst_time, module, pid, "Already killed"]) + printarrayGlob.pop() + return time.sleep(1) if getPid(module) is None: print module, 'has been killed' print 'restarting', module, '...' p2 = Popen([command_restart_module.format(module, module)], stdin=PIPE, stdout=PIPE, bufsize=1, shell=True) + inst_time = datetime.datetime.fromtimestamp(int(time.time())) + printarrayGlob.insert(1, [inst_time, module, pid, "Killed"]) + printarrayGlob.insert(1, [inst_time, module, "?", "Restarted"]) + printarrayGlob.pop() + printarrayGlob.pop() else: print 'killing failed, retrying...' - time.sleep(3) + inst_time = datetime.datetime.fromtimestamp(int(time.time())) + printarrayGlob.insert(1, [inst_time, module, pid, "Killing #1 failed."]) + printarrayGlob.pop() + + time.sleep(1) os.kill(pid, signal.SIGUSR1) time.sleep(1) if getPid(module) is None: print module, 'has been killed' print 'restarting', module, '...' p2 = Popen([command_restart_module.format(module, module)], stdin=PIPE, stdout=PIPE, bufsize=1, shell=True) + inst_time = datetime.datetime.fromtimestamp(int(time.time())) + printarrayGlob.insert(1, [inst_time, module, pid, "Killed"]) + printarrayGlob.insert(1, [inst_time, module, "?", "Restarted"]) + printarrayGlob.pop() + printarrayGlob.pop() else: print 'killing failed!' + inst_time = datetime.datetime.fromtimestamp(int(time.time())) + printarrayGlob.insert(1, [inst_time, module, pid, "Killing failed!"]) + printarrayGlob.pop() else: print 'Module does not exist' - time.sleep(5) + inst_time = datetime.datetime.fromtimestamp(int(time.time())) + printarrayGlob.insert(1, [inst_time, module, pid, "Killing failed, module not found"]) + printarrayGlob.pop() + #time.sleep(5) + cleanRedis() if __name__ == "__main__": @@ -110,6 +157,8 @@ if __name__ == "__main__": cfg = ConfigParser.ConfigParser() cfg.read(configfile) + threshold_stucked_module = cfg.getint("Module_ModuleInformation", "threshold_stucked_module") + # REDIS # server = redis.StrictRedis( host=cfg.get("Redis_Queues", "host"), @@ -128,6 +177,8 @@ if __name__ == "__main__": for line in module_file: module_file_array.add(line[:-1]) + cleanRedis() + while True: all_queue = set() @@ -151,8 +202,12 @@ if __name__ == "__main__": if int((datetime.datetime.now() - startTime_readable).total_seconds()) > threshold_stucked_module: log = open(log_filename, 'a') log.write(json.dumps([queue, card, str(startTime_readable), str(processed_time_readable), path]) + "\n") - if args.autokill == 1: - kill_module(queue) + try: + last_kill_try = time.time() - lastTimeKillCommand[moduleNum] + except KeyError: + last_kill_try = kill_retry_threshold+1 + if args.autokill == 1 and last_kill_try > kill_retry_threshold : + kill_module(queue, int(moduleNum)) printarray1.append([str(queue), str(moduleNum), str(card), str(startTime_readable), str(processed_time_readable), str(path)]) @@ -170,13 +225,13 @@ if __name__ == "__main__": else: #If no info since long time, try to kill if int(time.time()) - no_info_modules[curr_queue] > threshold_stucked_module: - kill_module(curr_queue) + kill_module(curr_queue, None) no_info_modules[curr_queue] = int(time.time()) printarray3.append([curr_queue, "Stuck or idle, restarting in " + str(threshold_stucked_module - (int(time.time()) - no_info_modules[curr_queue])) + "s"]) - printarray1.sort(lambda x,y: cmp(x[4], y[4]), reverse=True) - printarray2.sort(lambda x,y: cmp(x[4], y[4]), reverse=True) + printarray1.sort(lambda x,y: cmp(x[0], y[0]), reverse=False) + printarray2.sort(lambda x,y: cmp(x[0], y[0]), reverse=False) printarray1.insert(0,["Queue", "PID", "Amount", "Paste start time", "Processing time for current paste (H:M:S)", "Paste hash"]) printarray2.insert(0,["Queue", "PID","Amount", "Paste start time", "Time since idle (H:M:S)", "Last paste hash"]) printarray3.insert(0,["Queue", "State"]) @@ -219,11 +274,21 @@ if __name__ == "__main__": t3 = AsciiTable(printarray3, title="Not running queues") t3.column_max_width(1) + printarray4 = [] + for elem in printarrayGlob: + if elem is not None: + printarray4.append(elem) + + t4 = AsciiTable(printarray4, title="Last actions") + t4.column_max_width(1) + print t1.table print '\n' print t2.table print '\n' print t3.table + print '\n' + print t4.table if (datetime.datetime.now() - lastTime).total_seconds() > args.refresh*5: lastTime = datetime.datetime.now() diff --git a/var/www/Flask_server.py b/var/www/Flask_server.py index fcd67a21..4746117e 100755 --- a/var/www/Flask_server.py +++ b/var/www/Flask_server.py @@ -448,7 +448,8 @@ def get_more_search_result(): @app.route("/") def index(): default_minute = cfg.get("Flask", "minute_processed_paste") - return render_template("index.html", default_minute = default_minute) + threshold_stucked_module = cfg.getint("Module_ModuleInformation", "threshold_stucked_module") + return render_template("index.html", default_minute = default_minute, threshold_stucked_module=threshold_stucked_module) @app.route("/monitoring/") diff --git a/var/www/static/js/indexjavascript.js b/var/www/static/js/indexjavascript.js index a289f5ae..8d50ea9d 100644 --- a/var/www/static/js/indexjavascript.js +++ b/var/www/static/js/indexjavascript.js @@ -259,9 +259,9 @@ function create_queue_table() { // - j=1: queueLength // - j=2: LastProcessedPasteTime // - j=3: Number of the module belonging in the same category - if (parseInt(glob_tabvar.row1[i][2]) > 60*2 && parseInt(glob_tabvar.row1[i][1]) > 2) + if (parseInt(glob_tabvar.row1[i][2]) > window.threshold_stucked_module && parseInt(glob_tabvar.row1[i][1]) > 2) tr.className += " danger"; - else if (parseInt(glob_tabvar.row1[i][2]) > 60*1) + else if (parseInt(glob_tabvar.row1[i][1]) == 0) tr.className += " warning"; else tr.className += " success"; diff --git a/var/www/templates/index.html b/var/www/templates/index.html index 66c38a2c..74b45c01 100644 --- a/var/www/templates/index.html +++ b/var/www/templates/index.html @@ -20,6 +20,7 @@ From 1abba4dcf98ad26b776d4d515d2c00c3dc46907c Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 8 Dec 2016 08:44:10 +0100 Subject: [PATCH 59/89] Added support of re-plotting the plotted terms --- var/www/templates/terms_plot_tool.html | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/var/www/templates/terms_plot_tool.html b/var/www/templates/terms_plot_tool.html index fc5ab017..0205a89f 100644 --- a/var/www/templates/terms_plot_tool.html +++ b/var/www/templates/terms_plot_tool.html @@ -72,7 +72,7 @@
- Date: + Date:
@@ -252,26 +252,28 @@ function addData() { } -function replot(duration) { - console.log(plotted_terms); +function replot() { graph_data = []; + promises = []; for(i=0; i From 570324060e7e02e49027a670e44ef58d89a104dc Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 8 Dec 2016 09:13:31 +0100 Subject: [PATCH 60/89] terms top_sets correctly supports blacklisted terms --- bin/CurveManageTopSets.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/CurveManageTopSets.py b/bin/CurveManageTopSets.py index 8f316333..979df7ca 100755 --- a/bin/CurveManageTopSets.py +++ b/bin/CurveManageTopSets.py @@ -44,13 +44,14 @@ def manage_top_set(): startDate = datetime.datetime.now() startDate = startDate.replace(hour=0, minute=0, second=0, microsecond=0) startDate = calendar.timegm(startDate.timetuple()) + blacklist_size = int(server_term.scard(BlackListTermsSet_Name)) dico = {} - # Retreive top data (2*max_card) from days sets + # Retreive top data (max_card + blacklist_size) from days sets for timestamp in range(startDate, startDate - top_termFreq_setName_month[1]*oneDay, -oneDay): curr_set = top_termFreq_setName_day[0] + str(timestamp) - array_top_day = server_term.zrevrangebyscore(curr_set, '+inf', '-inf', withscores=True, start=0, num=top_term_freq_max_set_cardinality*2) + array_top_day = server_term.zrevrangebyscore(curr_set, '+inf', '-inf', withscores=True, start=0, num=top_term_freq_max_set_cardinality+blacklist_size) for word, value in array_top_day: if word not in server_term.smembers(BlackListTermsSet_Name): From 73d4f9e082ba000893a183cc546e9c74496f3d9a Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 8 Dec 2016 10:05:07 +0100 Subject: [PATCH 61/89] Webstats should correctly updates top_progression_zset (Not fully tested because not enough data. Will be tested latter) --- bin/WebStats.py | 62 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/bin/WebStats.py b/bin/WebStats.py index d8ff0876..1c41b64d 100755 --- a/bin/WebStats.py +++ b/bin/WebStats.py @@ -38,35 +38,55 @@ def get_date_range(num_day): date_list.append(date.substract_day(i)) return date_list +# Compute the progression for one keyword +def compute_progression_word(keyword): + date_range = get_date_range(num_day) + # check if this keyword is eligible for progression + keyword_total_sum = 0 + value_list = [] + for date in date_range: # get value up to date_range + curr_value = server.hget(keyword, date) + value_list.append(int(curr_value if curr_value is not None else 0)) + keyword_total_sum += int(curr_value) if curr_value is not None else 0 + oldest_value = value_list[-1] if value_list[-1] != 0 else 1 #Avoid zero division + + # The progression is based on the ratio: value[i] / value[i-1] + keyword_increase = 0 + value_list_reversed = value_list[:] + value_list_reversed.reverse() + for i in range(1, len(value_list_reversed)): + divisor = value_list_reversed[i-1] if value_list_reversed[i-1] != 0 else 1 + keyword_increase += value_list_reversed[i] / divisor + + return (keyword_increase, keyword_total_sum) + + +''' + recompute the set top_progression zset + - Compute the current field progression + - re-compute the current progression for each first 2*max_set_cardinality fields in the top_progression_zset +''' def compute_progression(server, field_name, num_day, url_parsed): - redis_progression_name = 'top_progression_'+field_name - redis_progression_name_set = 'top_progression_'+field_name+'_set' + redis_progression_name_set = "z_top_progression_"+field_name keyword = url_parsed[field_name] if keyword is not None: - date_range = get_date_range(num_day) - # check if this keyword is eligible for progression - keyword_total_sum = 0 - value_list = [] - for date in date_range: # get value up to date_range - curr_value = server.hget(keyword, date) - value_list.append(int(curr_value if curr_value is not None else 0)) - keyword_total_sum += int(curr_value) if curr_value is not None else 0 - oldest_value = value_list[-1] if value_list[-1] != 0 else 1 #Avoid zero division + #compute the progression of the current word + keyword_increase, keyword_total_sum = compute_progression_word(keyword) - # The progression is based on the ratio: value[i] / value[i-1] - keyword_increase = 0 - value_list_reversed = value_list[:] - value_list_reversed.reverse() - for i in range(1, len(value_list_reversed)): - divisor = value_list_reversed[i-1] if value_list_reversed[i-1] != 0 else 1 - keyword_increase += value_list_reversed[i] / divisor + #re-compute the progression of 2*max_set_cardinality + current_top = server.zrevrangebyscore(redis_progression_name_set, '+inf', '-inf', withscores=True, start=0, num=2*max_set_cardinality) + for word, value in array_top_day: + word_inc, word_tot_sum = compute_progression_word(word) + server.zrem(redis_progression_name_set, word) + if (word_tot_sum > threshold_total_sum) and (word_inc > threshold_increase): + server.zadd(redis_progression_name_set, float(word_inc), word) - # filter + # filter before adding if (keyword_total_sum > threshold_total_sum) and (keyword_increase > threshold_increase): - - server.zadd("z_top_progression_"+field_name, float(keyword_increase), keyword) + server.zadd(redis_progression_name_set, float(keyword_increase), keyword) + if __name__ == '__main__': From 8daa72789e09549ba567688b528b7cd85ded52a7 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 9 Dec 2016 08:46:37 +0100 Subject: [PATCH 62/89] Splitted Flask_server into module related to website sections --- var/www/Flask_browsepastes.py | 100 ++++ var/www/Flask_config.py | 65 +++ var/www/Flask_dashboard.py | 65 +++ var/www/Flask_search.py | 124 +++++ var/www/Flask_sentiment.py | 137 ++++++ var/www/Flask_server.py | 821 +------------------------------ var/www/Flask_showpaste.py | 114 +++++ var/www/Flask_terms.py | 240 +++++++++ var/www/Flask_trendingcharts.py | 73 +++ var/www/Flask_trendingmodules.py | 111 +++++ 10 files changed, 1045 insertions(+), 805 deletions(-) create mode 100644 var/www/Flask_browsepastes.py create mode 100644 var/www/Flask_config.py create mode 100644 var/www/Flask_dashboard.py create mode 100644 var/www/Flask_search.py create mode 100644 var/www/Flask_sentiment.py create mode 100644 var/www/Flask_showpaste.py create mode 100644 var/www/Flask_terms.py create mode 100644 var/www/Flask_trendingcharts.py create mode 100644 var/www/Flask_trendingmodules.py diff --git a/var/www/Flask_browsepastes.py b/var/www/Flask_browsepastes.py new file mode 100644 index 00000000..e5aa5b84 --- /dev/null +++ b/var/www/Flask_browsepastes.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python2 +# -*-coding:UTF-8 -* + +''' + Flask functions and routes for the trending modules page +''' +import redis +import json +import flask +from flask import Flask, render_template, jsonify, request + +import Paste + +# ============ VARIABLES ============ +import Flask_config + +app = Flask_config.app +cfg = Flask_config.cfg +max_preview_char = Flask_config.max_preview_char +max_preview_modal = Flask_config.max_preview_modal +r_serv_db = Flask_config.r_serv_db +# ============ FUNCTIONS ============ + +def getPastebyType(server, module_name): + all_path = [] + for path in server.smembers('WARNING_'+module_name): + all_path.append(path) + return all_path + + +def event_stream_getImportantPasteByModule(module_name): + index = 0 + all_pastes_list = getPastebyType(r_serv_db, module_name) + for path in all_pastes_list: + index += 1 + paste = Paste.Paste(path) + content = paste.get_p_content().decode('utf8', 'ignore') + content_range = max_preview_char if len(content)>max_preview_char else len(content)-1 + curr_date = str(paste._get_p_date()) + curr_date = curr_date[0:4]+'/'+curr_date[4:6]+'/'+curr_date[6:] + data = {} + data["module"] = module_name + data["index"] = index + data["path"] = path + data["content"] = content[0:content_range] + data["linenum"] = paste.get_lines_info()[0] + data["date"] = curr_date + data["char_to_display"] = max_preview_modal + data["finished"] = True if index == len(all_pastes_list) else False + yield 'retry: 100000\ndata: %s\n\n' % json.dumps(data) #retry to avoid reconnection of the browser + +# ============ ROUTES ============ + +@app.route("/browseImportantPaste/", methods=['GET']) +def browseImportantPaste(): + module_name = request.args.get('moduleName') + return render_template("browse_important_paste.html") + + +@app.route("/importantPasteByModule/", methods=['GET']) +def importantPasteByModule(): + module_name = request.args.get('moduleName') + + all_content = [] + paste_date = [] + paste_linenum = [] + all_path = [] + allPastes = getPastebyType(r_serv_db, module_name) + + for path in allPastes[0:10]: + all_path.append(path) + paste = Paste.Paste(path) + content = paste.get_p_content().decode('utf8', 'ignore') + content_range = max_preview_char if len(content)>max_preview_char else len(content)-1 + all_content.append(content[0:content_range].replace("\"", "\'").replace("\r", " ").replace("\n", " ")) + curr_date = str(paste._get_p_date()) + curr_date = curr_date[0:4]+'/'+curr_date[4:6]+'/'+curr_date[6:] + paste_date.append(curr_date) + paste_linenum.append(paste.get_lines_info()[0]) + + if len(allPastes) > 10: + finished = "" + else: + finished = "display: none;" + + return render_template("important_paste_by_module.html", + moduleName=module_name, + all_path=all_path, + content=all_content, + paste_date=paste_date, + paste_linenum=paste_linenum, + char_to_display=max_preview_modal, + finished=finished) + +@app.route("/_getImportantPasteByModule") +def getImportantPasteByModule(): + module_name = request.args.get('moduleName') + return flask.Response(event_stream_getImportantPasteByModule(module_name), mimetype="text/event-stream") + + diff --git a/var/www/Flask_config.py b/var/www/Flask_config.py new file mode 100644 index 00000000..c15e4dca --- /dev/null +++ b/var/www/Flask_config.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python2 +# -*-coding:UTF-8 -* + +''' + Flask global variables shared accross modules +''' +import ConfigParser +import redis +import os + +# FLASK # +app = None + +# CONFIG # +configfile = os.path.join(os.environ['AIL_BIN'], 'packages/config.cfg') +if not os.path.exists(configfile): + raise Exception('Unable to find the configuration file. \ + Did you set environment variables? \ + Or activate the virtualenv.') + +cfg = ConfigParser.ConfigParser() +cfg.read(configfile) + + +# REDIS # +r_serv = redis.StrictRedis( + host=cfg.get("Redis_Queues", "host"), + port=cfg.getint("Redis_Queues", "port"), + db=cfg.getint("Redis_Queues", "db")) + +r_serv_log = redis.StrictRedis( + host=cfg.get("Redis_Log", "host"), + port=cfg.getint("Redis_Log", "port"), + db=cfg.getint("Redis_Log", "db")) + +r_serv_charts = redis.StrictRedis( + host=cfg.get("Redis_Level_DB_Trending", "host"), + port=cfg.getint("Redis_Level_DB_Trending", "port"), + db=cfg.getint("Redis_Level_DB_Trending", "db")) + +r_serv_db = redis.StrictRedis( + host=cfg.get("Redis_Level_DB", "host"), + port=cfg.getint("Redis_Level_DB", "port"), + db=cfg.getint("Redis_Level_DB", "db")) + +r_serv_sentiment = redis.StrictRedis( + host=cfg.get("Redis_Level_DB_Sentiment", "host"), + port=cfg.getint("Redis_Level_DB_Sentiment", "port"), + db=cfg.getint("Redis_Level_DB_Sentiment", "db")) + +r_serv_term = redis.StrictRedis( + host=cfg.get("Redis_Level_DB_TermFreq", "host"), + port=cfg.getint("Redis_Level_DB_TermFreq", "port"), + db=cfg.getint("Redis_Level_DB_TermFreq", "db")) + +r_serv_pasteName = redis.StrictRedis( + host=cfg.get("Redis_Paste_Name", "host"), + port=cfg.getint("Redis_Paste_Name", "port"), + db=cfg.getint("Redis_Paste_Name", "db")) + +# VARIABLES # +max_preview_char = int(cfg.get("Flask", "max_preview_char")) # Maximum number of character to display in the tooltip +max_preview_modal = int(cfg.get("Flask", "max_preview_modal")) # Maximum number of character to display in the modal + +tlsh_to_percent = 1000.0 #Use to display the estimated percentage instead of a raw value diff --git a/var/www/Flask_dashboard.py b/var/www/Flask_dashboard.py new file mode 100644 index 00000000..b6bcb219 --- /dev/null +++ b/var/www/Flask_dashboard.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python2 +# -*-coding:UTF-8 -* + +''' + Flask functions and routes for the dashboard page +''' +import flask +from flask import Flask, render_template, jsonify, request + +# ============ VARIABLES ============ +import Flask_config + +app = Flask_config.app +cfg = Flask_config.cfg +r_serv = Flask_config.r_serv +r_serv_log = Flask_config.r_serv_log +# ============ FUNCTIONS ============ + +def event_stream(): + pubsub = r_serv_log.pubsub() + pubsub.psubscribe("Script" + '.*') + for msg in pubsub.listen(): + level = msg['channel'].split('.')[1] + if msg['type'] == 'pmessage' and level != "DEBUG": + yield 'data: %s\n\n' % json.dumps(msg) + +def get_queues(r): + # We may want to put the llen in a pipeline to do only one query. + newData = [] + for queue, card in r.hgetall("queues").iteritems(): + key = "MODULE_" + queue + "_" + keySet = "MODULE_TYPE_" + queue + + for moduleNum in r.smembers(keySet): + + value = r.get(key + str(moduleNum)) + if value is not None: + timestamp, path = value.split(", ") + if timestamp is not None: + startTime_readable = datetime.datetime.fromtimestamp(int(timestamp)) + processed_time_readable = str((datetime.datetime.now() - startTime_readable)).split('.')[0] + seconds = int((datetime.datetime.now() - startTime_readable).total_seconds()) + newData.append( (queue, card, seconds, moduleNum) ) + else: + newData.append( (queue, cards, 0, moduleNum) ) + + return newData + +# ============ ROUTES ============ + +@app.route("/_logs") +def logs(): + return flask.Response(event_stream(), mimetype="text/event-stream") + + +@app.route("/_stuff", methods=['GET']) +def stuff(): + return jsonify(row1=get_queues(r_serv)) + + +@app.route("/") +def index(): + default_minute = cfg.get("Flask", "minute_processed_paste") + threshold_stucked_module = cfg.getint("Module_ModuleInformation", "threshold_stucked_module") + return render_template("index.html", default_minute = default_minute, threshold_stucked_module=threshold_stucked_module) diff --git a/var/www/Flask_search.py b/var/www/Flask_search.py new file mode 100644 index 00000000..b5c60898 --- /dev/null +++ b/var/www/Flask_search.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python2 +# -*-coding:UTF-8 -* + +''' + Flask functions and routes for the trending modules page +''' +import redis +import json +import os +import flask +from flask import Flask, render_template, jsonify, request + +import Paste + +# ============ VARIABLES ============ +import Flask_config + +app = Flask_config.app +cfg = Flask_config.cfg +r_serv_pasteName = Flask_config.r_serv_pasteName +max_preview_char = Flask_config.max_preview_char +max_preview_modal = Flask_config.max_preview_modal +# ============ FUNCTIONS ============ + + +# ============ ROUTES ============ + +@app.route("/search", methods=['POST']) +def search(): + query = request.form['query'] + q = [] + q.append(query) + r = [] #complete path + c = [] #preview of the paste content + paste_date = [] + paste_size = [] + num_elem_to_get = 50 + + # Search filename + for path in r_serv_pasteName.smembers(q[0]): + r.append(path) + paste = Paste.Paste(path) + content = paste.get_p_content().decode('utf8', 'ignore') + content_range = max_preview_char if len(content)>max_preview_char else len(content)-1 + c.append(content[0:content_range]) + curr_date = str(paste._get_p_date()) + curr_date = curr_date[0:4]+'/'+curr_date[4:6]+'/'+curr_date[6:] + paste_date.append(curr_date) + paste_size.append(paste._get_p_size()) + + # Search full line + from whoosh import index + from whoosh.fields import Schema, TEXT, ID + schema = Schema(title=TEXT(stored=True), path=ID(stored=True), content=TEXT) + + indexpath = os.path.join(os.environ['AIL_HOME'], cfg.get("Indexer", "path")) + ix = index.open_dir(indexpath) + from whoosh.qparser import QueryParser + with ix.searcher() as searcher: + query = QueryParser("content", ix.schema).parse(" ".join(q)) + results = searcher.search_page(query, 1, pagelen=num_elem_to_get) + for x in results: + r.append(x.items()[0][1]) + paste = Paste.Paste(x.items()[0][1]) + content = paste.get_p_content().decode('utf8', 'ignore') + content_range = max_preview_char if len(content)>max_preview_char else len(content)-1 + c.append(content[0:content_range]) + curr_date = str(paste._get_p_date()) + curr_date = curr_date[0:4]+'/'+curr_date[4:6]+'/'+curr_date[6:] + paste_date.append(curr_date) + paste_size.append(paste._get_p_size()) + results = searcher.search(query) + num_res = len(results) + + return render_template("search.html", r=r, c=c, query=request.form['query'], paste_date=paste_date, paste_size=paste_size, char_to_display=max_preview_modal, num_res=num_res) + + +@app.route("/get_more_search_result", methods=['POST']) +def get_more_search_result(): + query = request.form['query'] + q = [] + q.append(query) + page_offset = int(request.form['page_offset']) + num_elem_to_get = 50 + + path_array = [] + preview_array = [] + date_array = [] + size_array = [] + + from whoosh import index + from whoosh.fields import Schema, TEXT, ID + schema = Schema(title=TEXT(stored=True), path=ID(stored=True), content=TEXT) + + indexpath = os.path.join(os.environ['AIL_HOME'], cfg.get("Indexer", "path")) + ix = index.open_dir(indexpath) + from whoosh.qparser import QueryParser + with ix.searcher() as searcher: + query = QueryParser("content", ix.schema).parse(" ".join(q)) + results = searcher.search_page(query, page_offset, num_elem_to_get) + for x in results: + path_array.append(x.items()[0][1]) + paste = Paste.Paste(x.items()[0][1]) + content = paste.get_p_content().decode('utf8', 'ignore') + content_range = max_preview_char if len(content)>max_preview_char else len(content)-1 + preview_array.append(content[0:content_range]) + curr_date = str(paste._get_p_date()) + curr_date = curr_date[0:4]+'/'+curr_date[4:6]+'/'+curr_date[6:] + date_array.append(curr_date) + size_array.append(paste._get_p_size()) + to_return = {} + to_return["path_array"] = path_array + to_return["preview_array"] = preview_array + to_return["date_array"] = date_array + to_return["size_array"] = size_array + print "len(path_array)="+str(len(path_array)) + if len(path_array) < num_elem_to_get: #pagelength + to_return["moreData"] = False + else: + to_return["moreData"] = True + + return jsonify(to_return) + + diff --git a/var/www/Flask_sentiment.py b/var/www/Flask_sentiment.py new file mode 100644 index 00000000..275cce39 --- /dev/null +++ b/var/www/Flask_sentiment.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python2 +# -*-coding:UTF-8 -* + +''' + Flask functions and routes for the trending modules page +''' +import redis +import datetime +import calendar +from Date import Date +import flask +from flask import Flask, render_template, jsonify, request + +import Paste + +# ============ VARIABLES ============ +import Flask_config + +app = Flask_config.app +cfg = Flask_config.cfg +r_serv_charts = Flask_config.r_serv_charts +r_serv_sentiment = Flask_config.r_serv_sentiment +# ============ FUNCTIONS ============ + +def get_date_range(num_day): + curr_date = datetime.date.today() + date = Date(str(curr_date.year)+str(curr_date.month).zfill(2)+str(curr_date.day).zfill(2)) + date_list = [] + + for i in range(0, num_day+1): + date_list.append(date.substract_day(i)) + return date_list + + +# ============ ROUTES ============ + +@app.route("/sentiment_analysis_trending/") +def sentiment_analysis_trending(): + return render_template("sentiment_analysis_trending.html") + + +@app.route("/sentiment_analysis_getplotdata/", methods=['GET']) +def sentiment_analysis_getplotdata(): + # Get the top providers based on number of pastes + oneHour = 60*60 + sevenDays = oneHour*24*7 + dateStart = datetime.datetime.now() + dateStart = dateStart.replace(minute=0, second=0, microsecond=0) + dateStart_timestamp = calendar.timegm(dateStart.timetuple()) + + getAllProviders = request.args.get('getProviders') + provider = request.args.get('provider') + allProvider = request.args.get('all') + if getAllProviders == 'True': + if allProvider == "True": + range_providers = r_serv_charts.smembers('all_provider_set') + return jsonify(list(range_providers)) + else: + range_providers = r_serv_charts.zrevrangebyscore('providers_set_'+ get_date_range(0)[0], '+inf', '-inf', start=0, num=8) + # if empty, get yesterday top providers + range_providers = r_serv_charts.zrevrangebyscore('providers_set_'+ get_date_range(1)[1], '+inf', '-inf', start=0, num=8) if range_providers == [] else range_providers + # if still empty, takes from all providers + if range_providers == []: + print 'today provider empty' + range_providers = r_serv_charts.smembers('all_provider_set') + return jsonify(list(range_providers)) + + elif provider is not None: + to_return = {} + + cur_provider_name = provider + '_' + list_date = {} + for cur_timestamp in range(int(dateStart_timestamp), int(dateStart_timestamp)-sevenDays-oneHour, -oneHour): + cur_set_name = cur_provider_name + str(cur_timestamp) + + list_value = [] + for cur_id in r_serv_sentiment.smembers(cur_set_name): + cur_value = r_serv_sentiment.get(cur_id) + list_value.append(cur_value) + list_date[cur_timestamp] = list_value + to_return[provider] = list_date + + return jsonify(to_return) + return "Bad request" + + + +@app.route("/sentiment_analysis_plot_tool/") +def sentiment_analysis_plot_tool(): + return render_template("sentiment_analysis_plot_tool.html") + + + +@app.route("/sentiment_analysis_plot_tool_getdata/", methods=['GET']) +def sentiment_analysis_plot_tool_getdata(): + getProviders = request.args.get('getProviders') + + if getProviders == 'True': + providers = [] + for cur_provider in r_serv_charts.smembers('all_provider_set'): + providers.append(cur_provider) + return jsonify(providers) + + else: + query = request.args.get('query') + query = query.split(',') + Qdate = request.args.get('Qdate') + + date1 = (Qdate.split('-')[0]).split('.') + date1 = datetime.date(int(date1[2]), int(date1[1]), int(date1[0])) + + date2 = (Qdate.split('-')[1]).split('.') + date2 = datetime.date(int(date2[2]), int(date2[1]), int(date2[0])) + + timestamp1 = calendar.timegm(date1.timetuple()) + timestamp2 = calendar.timegm(date2.timetuple()) + + oneHour = 60*60 + oneDay = oneHour*24 + + to_return = {} + for cur_provider in query: + list_date = {} + cur_provider_name = cur_provider + '_' + for cur_timestamp in range(int(timestamp1), int(timestamp2)+oneDay, oneHour): + cur_set_name = cur_provider_name + str(cur_timestamp) + + list_value = [] + for cur_id in r_serv_sentiment.smembers(cur_set_name): + cur_value = r_serv_sentiment.get(cur_id) + list_value.append(cur_value) + list_date[cur_timestamp] = list_value + to_return[cur_provider] = list_date + + return jsonify(to_return) + + diff --git a/var/www/Flask_server.py b/var/www/Flask_server.py index 0eb04b47..4ece0c75 100755 --- a/var/www/Flask_server.py +++ b/var/www/Flask_server.py @@ -15,231 +15,30 @@ sys.path.append(os.path.join(os.environ['AIL_BIN'], 'packages/')) import Paste from Date import Date +# Import config +import Flask_config + # CONFIG # -tlsh_to_percent = 1000.0 #Use to display the estimated percentage instead of a raw value +cfg = Flask_config.cfg -configfile = os.path.join(os.environ['AIL_BIN'], 'packages/config.cfg') -if not os.path.exists(configfile): - raise Exception('Unable to find the configuration file. \ - Did you set environment variables? \ - Or activate the virtualenv.') - -cfg = ConfigParser.ConfigParser() -cfg.read(configfile) - -max_preview_char = int(cfg.get("Flask", "max_preview_char")) # Maximum number of character to display in the tooltip -max_preview_modal = int(cfg.get("Flask", "max_preview_modal")) # Maximum number of character to display in the modal - -# REDIS # -r_serv = redis.StrictRedis( - host=cfg.get("Redis_Queues", "host"), - port=cfg.getint("Redis_Queues", "port"), - db=cfg.getint("Redis_Queues", "db")) - -r_serv_log = redis.StrictRedis( - host=cfg.get("Redis_Log", "host"), - port=cfg.getint("Redis_Log", "port"), - db=cfg.getint("Redis_Log", "db")) - -r_serv_charts = redis.StrictRedis( - host=cfg.get("Redis_Level_DB_Trending", "host"), - port=cfg.getint("Redis_Level_DB_Trending", "port"), - db=cfg.getint("Redis_Level_DB_Trending", "db")) - -r_serv_db = redis.StrictRedis( - host=cfg.get("Redis_Level_DB", "host"), - port=cfg.getint("Redis_Level_DB", "port"), - db=cfg.getint("Redis_Level_DB", "db")) - -r_serv_sentiment = redis.StrictRedis( - host=cfg.get("Redis_Level_DB_Sentiment", "host"), - port=cfg.getint("Redis_Level_DB_Sentiment", "port"), - db=cfg.getint("Redis_Level_DB_Sentiment", "db")) - -r_serv_term = redis.StrictRedis( - host=cfg.get("Redis_Level_DB_TermFreq", "host"), - port=cfg.getint("Redis_Level_DB_TermFreq", "port"), - db=cfg.getint("Redis_Level_DB_TermFreq", "db")) - -r_serv_pasteName = redis.StrictRedis( - host=cfg.get("Redis_Paste_Name", "host"), - port=cfg.getint("Redis_Paste_Name", "port"), - db=cfg.getint("Redis_Paste_Name", "db")) - - -app = Flask(__name__, static_url_path='/static/') - - -def event_stream(): - pubsub = r_serv_log.pubsub() - pubsub.psubscribe("Script" + '.*') - for msg in pubsub.listen(): - level = msg['channel'].split('.')[1] - if msg['type'] == 'pmessage' and level != "DEBUG": - yield 'data: %s\n\n' % json.dumps(msg) - -def event_stream_getImportantPasteByModule(module_name): - index = 0 - all_pastes_list = getPastebyType(r_serv_db, module_name) - for path in all_pastes_list: - index += 1 - paste = Paste.Paste(path) - content = paste.get_p_content().decode('utf8', 'ignore') - content_range = max_preview_char if len(content)>max_preview_char else len(content)-1 - curr_date = str(paste._get_p_date()) - curr_date = curr_date[0:4]+'/'+curr_date[4:6]+'/'+curr_date[6:] - data = {} - data["module"] = module_name - data["index"] = index - data["path"] = path - data["content"] = content[0:content_range] - data["linenum"] = paste.get_lines_info()[0] - data["date"] = curr_date - data["char_to_display"] = max_preview_modal - data["finished"] = True if index == len(all_pastes_list) else False - yield 'retry: 100000\ndata: %s\n\n' % json.dumps(data) #retry to avoid reconnection of the browser - - -def get_queues(r): - # We may want to put the llen in a pipeline to do only one query. - newData = [] - for queue, card in r.hgetall("queues").iteritems(): - key = "MODULE_" + queue + "_" - keySet = "MODULE_TYPE_" + queue - - for moduleNum in r.smembers(keySet): - - value = r.get(key + str(moduleNum)) - if value is not None: - timestamp, path = value.split(", ") - if timestamp is not None: - startTime_readable = datetime.datetime.fromtimestamp(int(timestamp)) - processed_time_readable = str((datetime.datetime.now() - startTime_readable)).split('.')[0] - seconds = int((datetime.datetime.now() - startTime_readable).total_seconds()) - newData.append( (queue, card, seconds, moduleNum) ) - else: - newData.append( (queue, cards, 0, moduleNum) ) - - return newData +Flask_config.app = Flask(__name__, static_url_path='/static/') +app = Flask_config.app +# import routes and functions from modules +import Flask_dashboard +import Flask_trendingcharts +import Flask_trendingmodules +import Flask_browsepastes +import Flask_sentiment +import Flask_terms +import Flask_search +import Flask_showpaste def list_len(s): return len(s) app.jinja_env.filters['list_len'] = list_len -def showpaste(content_range): - requested_path = request.args.get('paste', '') - paste = Paste.Paste(requested_path) - p_date = str(paste._get_p_date()) - p_date = p_date[6:]+'/'+p_date[4:6]+'/'+p_date[0:4] - p_source = paste.p_source - p_encoding = paste._get_p_encoding() - p_language = paste._get_p_language() - p_size = paste.p_size - p_mime = paste.p_mime - p_lineinfo = paste.get_lines_info() - p_content = paste.get_p_content().decode('utf-8', 'ignore') - p_duplicate_full_list = json.loads(paste._get_p_duplicate()) - p_duplicate_list = [] - p_simil_list = [] - p_date_list = [] - p_hashtype_list = [] - - - for dup_list in p_duplicate_full_list: - if dup_list[0] == "tlsh": - dup_list[2] = int(((tlsh_to_percent - float(dup_list[2])) / tlsh_to_percent)*100) - else: - dup_list[2] = int(dup_list[2]) - - p_duplicate_full_list.sort(lambda x,y: cmp(x[2], y[2]), reverse=True) - - # Combine multiple duplicate paste name and format for display - new_dup_list = [] - dup_list_removed = [] - for dup_list_index in range(0, len(p_duplicate_full_list)): - if dup_list_index in dup_list_removed: - continue - indices = [i for i, x in enumerate(p_duplicate_full_list) if x[1] == p_duplicate_full_list[dup_list_index][1]] - hash_types = [] - comp_vals = [] - for i in indices: - hash_types.append(p_duplicate_full_list[i][0].encode('utf8')) - comp_vals.append(p_duplicate_full_list[i][2]) - dup_list_removed.append(i) - - hash_types = str(hash_types).replace("[","").replace("]","") if len(hash_types)==1 else str(hash_types) - comp_vals = str(comp_vals).replace("[","").replace("]","") if len(comp_vals)==1 else str(comp_vals) - if len(p_duplicate_full_list[dup_list_index]) > 3: - try: - date_paste = str(int(p_duplicate_full_list[dup_list_index][3])) - date_paste = date_paste[0:4]+"-"+date_paste[4:6]+"-"+date_paste[6:8] - except ValueError: - date_paste = str(p_duplicate_full_list[dup_list_index][3]) - else: - date_paste = "No date available" - new_dup_list.append([hash_types.replace("'", ""), p_duplicate_full_list[dup_list_index][1], comp_vals, date_paste]) - - # Create the list to pass to the webpage - for dup_list in new_dup_list: - hash_type, path, simil_percent, date_paste = dup_list - p_duplicate_list.append(path) - p_simil_list.append(simil_percent) - p_hashtype_list.append(hash_type) - p_date_list.append(date_paste) - - if content_range != 0: - p_content = p_content[0:content_range] - - - return render_template("show_saved_paste.html", date=p_date, source=p_source, encoding=p_encoding, language=p_language, size=p_size, mime=p_mime, lineinfo=p_lineinfo, content=p_content, initsize=len(p_content), duplicate_list = p_duplicate_list, simil_list = p_simil_list, hashtype_list = p_hashtype_list, date_list=p_date_list) - -def getPastebyType(server, module_name): - all_path = [] - for path in server.smembers('WARNING_'+module_name): - all_path.append(path) - return all_path - - -def get_date_range(num_day): - curr_date = datetime.date.today() - date = Date(str(curr_date.year)+str(curr_date.month).zfill(2)+str(curr_date.day).zfill(2)) - date_list = [] - - for i in range(0, num_day+1): - date_list.append(date.substract_day(i)) - return date_list - -# Iterate over elements in the module provided and return the today data or the last data -# return format: [('passed_days', num_of_passed_days), ('elem_name1', elem_value1), ('elem_name2', elem_value2)]] -def get_top_relevant_data(server, module_name): - days = 0 - for date in get_date_range(15): - redis_progression_name_set = 'top_'+ module_name +'_set_' + date - member_set = server.zrevrangebyscore(redis_progression_name_set, '+inf', '-inf', withscores=True) - if len(member_set) == 0: #No data for this date - days += 1 - else: - member_set.insert(0, ("passed_days", days)) - return member_set - - -def Term_getValueOverRange(word, startDate, num_day): - passed_days = 0 - oneDay = 60*60*24 - to_return = [] - curr_to_return = 0 - for timestamp in range(startDate, startDate - max(num_day)*oneDay, -oneDay): - value = r_serv_term.hget(timestamp, word) - curr_to_return += int(value) if value is not None else 0 - for i in num_day: - if passed_days == i-1: - to_return.append(curr_to_return) - passed_days += 1 - return to_return - - # ========= CACHE CONTROL ======== @app.after_request def add_header(response): @@ -251,595 +50,7 @@ def add_header(response): response.headers['Cache-Control'] = 'public, max-age=0' return response -# ============ ROUTES ============ - -@app.route("/_logs") -def logs(): - return flask.Response(event_stream(), mimetype="text/event-stream") - - -@app.route("/_stuff", methods=['GET']) -def stuff(): - return jsonify(row1=get_queues(r_serv)) - -@app.route("/_progressionCharts", methods=['GET']) -def progressionCharts(): - attribute_name = request.args.get('attributeName') - trending_name = request.args.get('trendingName') - bar_requested = True if request.args.get('bar') == "true" else False - - if (bar_requested): - num_day = int(request.args.get('days')) - bar_values = [] - - date_range = get_date_range(num_day) - # Retreive all data from the last num_day - for date in date_range: - curr_value = r_serv_charts.hget(attribute_name, date) - bar_values.append([date[0:4]+'/'+date[4:6]+'/'+date[6:8], int(curr_value if curr_value is not None else 0)]) - bar_values.insert(0, attribute_name) - return jsonify(bar_values) - - else: - redis_progression_name = "z_top_progression_" + trending_name - keyw_value = r_serv_charts.zrevrangebyscore(redis_progression_name, '+inf', '-inf', withscores=True, start=0, num=10) - return jsonify(keyw_value) - -@app.route("/_moduleCharts", methods=['GET']) -def modulesCharts(): - keyword_name = request.args.get('keywordName') - module_name = request.args.get('moduleName') - bar_requested = True if request.args.get('bar') == "true" else False - - if (bar_requested): - num_day = int(request.args.get('days')) - bar_values = [] - - date_range = get_date_range(num_day) - # Retreive all data from the last num_day - for date in date_range: - curr_value = r_serv_charts.hget(date, module_name+'-'+keyword_name) - bar_values.append([date[0:4]+'/'+date[4:6]+'/'+date[6:8], int(curr_value if curr_value is not None else 0)]) - bar_values.insert(0, keyword_name) - return jsonify(bar_values) - - else: - member_set = get_top_relevant_data(r_serv_charts, module_name) - if len(member_set) == 0: - member_set.append(("No relevant data", int(100))) - return jsonify(member_set) - - -@app.route("/_providersChart", methods=['GET']) -def providersChart(): - keyword_name = request.args.get('keywordName') - module_name = request.args.get('moduleName') - bar_requested = True if request.args.get('bar') == "true" else False - - if (bar_requested): - num_day = int(request.args.get('days')) - bar_values = [] - - date_range = get_date_range(num_day) - # Retreive all data from the last num_day - for date in date_range: - curr_value_size = r_serv_charts.hget(keyword_name+'_'+'size', date) - curr_value_num = r_serv_charts.hget(keyword_name+'_'+'num', date) - curr_value_size_avg = r_serv_charts.hget(keyword_name+'_'+'avg', date) - if module_name == "size": - curr_value = float(curr_value_size_avg if curr_value_size_avg is not None else 0) - else: - curr_value = float(curr_value_num if curr_value_num is not None else 0.0) - - bar_values.append([date[0:4]+'/'+date[4:6]+'/'+date[6:8], curr_value]) - bar_values.insert(0, keyword_name) - return jsonify(bar_values) - - else: - #redis_provider_name_set = 'top_size_set' if module_name == "size" else 'providers_set' - redis_provider_name_set = 'top_avg_size_set_' if module_name == "size" else 'providers_set_' - redis_provider_name_set = redis_provider_name_set + get_date_range(0)[0] - - member_set = r_serv_charts.zrevrangebyscore(redis_provider_name_set, '+inf', '-inf', withscores=True, start=0, num=8) - # Member set is a list of (value, score) pairs - if len(member_set) == 0: - member_set.append(("No relevant data", float(100))) - return jsonify(member_set) - - - -@app.route("/search", methods=['POST']) -def search(): - query = request.form['query'] - q = [] - q.append(query) - r = [] #complete path - c = [] #preview of the paste content - paste_date = [] - paste_size = [] - num_elem_to_get = 50 - - # Search filename - for path in r_serv_pasteName.smembers(q[0]): - r.append(path) - paste = Paste.Paste(path) - content = paste.get_p_content().decode('utf8', 'ignore') - content_range = max_preview_char if len(content)>max_preview_char else len(content)-1 - c.append(content[0:content_range]) - curr_date = str(paste._get_p_date()) - curr_date = curr_date[0:4]+'/'+curr_date[4:6]+'/'+curr_date[6:] - paste_date.append(curr_date) - paste_size.append(paste._get_p_size()) - - # Search full line - from whoosh import index - from whoosh.fields import Schema, TEXT, ID - schema = Schema(title=TEXT(stored=True), path=ID(stored=True), content=TEXT) - - indexpath = os.path.join(os.environ['AIL_HOME'], cfg.get("Indexer", "path")) - ix = index.open_dir(indexpath) - from whoosh.qparser import QueryParser - with ix.searcher() as searcher: - query = QueryParser("content", ix.schema).parse(" ".join(q)) - results = searcher.search_page(query, 1, pagelen=num_elem_to_get) - for x in results: - r.append(x.items()[0][1]) - paste = Paste.Paste(x.items()[0][1]) - content = paste.get_p_content().decode('utf8', 'ignore') - content_range = max_preview_char if len(content)>max_preview_char else len(content)-1 - c.append(content[0:content_range]) - curr_date = str(paste._get_p_date()) - curr_date = curr_date[0:4]+'/'+curr_date[4:6]+'/'+curr_date[6:] - paste_date.append(curr_date) - paste_size.append(paste._get_p_size()) - results = searcher.search(query) - num_res = len(results) - - return render_template("search.html", r=r, c=c, query=request.form['query'], paste_date=paste_date, paste_size=paste_size, char_to_display=max_preview_modal, num_res=num_res) - - -@app.route("/get_more_search_result", methods=['POST']) -def get_more_search_result(): - query = request.form['query'] - q = [] - q.append(query) - page_offset = int(request.form['page_offset']) - num_elem_to_get = 50 - - path_array = [] - preview_array = [] - date_array = [] - size_array = [] - - from whoosh import index - from whoosh.fields import Schema, TEXT, ID - schema = Schema(title=TEXT(stored=True), path=ID(stored=True), content=TEXT) - - indexpath = os.path.join(os.environ['AIL_HOME'], cfg.get("Indexer", "path")) - ix = index.open_dir(indexpath) - from whoosh.qparser import QueryParser - with ix.searcher() as searcher: - query = QueryParser("content", ix.schema).parse(" ".join(q)) - results = searcher.search_page(query, page_offset, num_elem_to_get) - for x in results: - path_array.append(x.items()[0][1]) - paste = Paste.Paste(x.items()[0][1]) - content = paste.get_p_content().decode('utf8', 'ignore') - content_range = max_preview_char if len(content)>max_preview_char else len(content)-1 - preview_array.append(content[0:content_range]) - curr_date = str(paste._get_p_date()) - curr_date = curr_date[0:4]+'/'+curr_date[4:6]+'/'+curr_date[6:] - date_array.append(curr_date) - size_array.append(paste._get_p_size()) - to_return = {} - to_return["path_array"] = path_array - to_return["preview_array"] = preview_array - to_return["date_array"] = date_array - to_return["size_array"] = size_array - print "len(path_array)="+str(len(path_array)) - if len(path_array) < num_elem_to_get: #pagelength - to_return["moreData"] = False - else: - to_return["moreData"] = True - - return jsonify(to_return) - - -@app.route("/") -def index(): - default_minute = cfg.get("Flask", "minute_processed_paste") - threshold_stucked_module = cfg.getint("Module_ModuleInformation", "threshold_stucked_module") - return render_template("index.html", default_minute = default_minute, threshold_stucked_module=threshold_stucked_module) - - -@app.route("/monitoring/") -def monitoring(): - for queue in r_serv.smembers("queues"): - return render_template("Queue_live_Monitoring.html", last_value=queue) - - -@app.route("/wordstrending/") -def wordstrending(): - default_display = cfg.get("Flask", "default_display") - return render_template("Wordstrending.html", default_display = default_display) - - -@app.route("/protocolstrending/") -def protocolstrending(): - default_display = cfg.get("Flask", "default_display") - return render_template("Protocolstrending.html", default_display = default_display) - - -@app.route("/trending/") -def trending(): - default_display = cfg.get("Flask", "default_display") - return render_template("Trending.html", default_display = default_display) - -@app.route("/browseImportantPaste/", methods=['GET']) -def browseImportantPaste(): - module_name = request.args.get('moduleName') - return render_template("browse_important_paste.html") - - -@app.route("/importantPasteByModule/", methods=['GET']) -def importantPasteByModule(): - module_name = request.args.get('moduleName') - - all_content = [] - paste_date = [] - paste_linenum = [] - all_path = [] - allPastes = getPastebyType(r_serv_db, module_name) - - for path in allPastes[0:10]: - all_path.append(path) - paste = Paste.Paste(path) - content = paste.get_p_content().decode('utf8', 'ignore') - content_range = max_preview_char if len(content)>max_preview_char else len(content)-1 - all_content.append(content[0:content_range].replace("\"", "\'").replace("\r", " ").replace("\n", " ")) - curr_date = str(paste._get_p_date()) - curr_date = curr_date[0:4]+'/'+curr_date[4:6]+'/'+curr_date[6:] - paste_date.append(curr_date) - paste_linenum.append(paste.get_lines_info()[0]) - - if len(allPastes) > 10: - finished = "" - else: - finished = "display: none;" - - return render_template("important_paste_by_module.html", moduleName=module_name, all_path=all_path, content=all_content, paste_date=paste_date, paste_linenum=paste_linenum, char_to_display=max_preview_modal, finished=finished) - -@app.route("/_getImportantPasteByModule") -def getImportantPasteByModule(): - module_name = request.args.get('moduleName') - return flask.Response(event_stream_getImportantPasteByModule(module_name), mimetype="text/event-stream") - -@app.route("/moduletrending/") -def moduletrending(): - return render_template("Moduletrending.html") - -@app.route("/sentiment_analysis_trending/") -def sentiment_analysis_trending(): - return render_template("sentiment_analysis_trending.html") - - -@app.route("/sentiment_analysis_getplotdata/", methods=['GET']) -def sentiment_analysis_getplotdata(): - # Get the top providers based on number of pastes - oneHour = 60*60 - sevenDays = oneHour*24*7 - dateStart = datetime.datetime.now() - dateStart = dateStart.replace(minute=0, second=0, microsecond=0) - dateStart_timestamp = calendar.timegm(dateStart.timetuple()) - - getAllProviders = request.args.get('getProviders') - provider = request.args.get('provider') - allProvider = request.args.get('all') - if getAllProviders == 'True': - if allProvider == "True": - range_providers = r_serv_charts.smembers('all_provider_set') - return jsonify(list(range_providers)) - else: - range_providers = r_serv_charts.zrevrangebyscore('providers_set_'+ get_date_range(0)[0], '+inf', '-inf', start=0, num=8) - # if empty, get yesterday top providers - range_providers = r_serv_charts.zrevrangebyscore('providers_set_'+ get_date_range(1)[1], '+inf', '-inf', start=0, num=8) if range_providers == [] else range_providers - # if still empty, takes from all providers - if range_providers == []: - print 'today provider empty' - range_providers = r_serv_charts.smembers('all_provider_set') - return jsonify(range_providers) - - elif provider is not None: - to_return = {} - - cur_provider_name = provider + '_' - list_date = {} - for cur_timestamp in range(int(dateStart_timestamp), int(dateStart_timestamp)-sevenDays-oneHour, -oneHour): - cur_set_name = cur_provider_name + str(cur_timestamp) - - list_value = [] - for cur_id in r_serv_sentiment.smembers(cur_set_name): - cur_value = r_serv_sentiment.get(cur_id) - list_value.append(cur_value) - list_date[cur_timestamp] = list_value - to_return[provider] = list_date - - return jsonify(to_return) - return "Bad request" - - - -@app.route("/sentiment_analysis_plot_tool/") -def sentiment_analysis_plot_tool(): - return render_template("sentiment_analysis_plot_tool.html") - - - -@app.route("/sentiment_analysis_plot_tool_getdata/", methods=['GET']) -def sentiment_analysis_plot_tool_getdata(): - getProviders = request.args.get('getProviders') - - if getProviders == 'True': - providers = [] - for cur_provider in r_serv_charts.smembers('all_provider_set'): - providers.append(cur_provider) - return jsonify(providers) - - else: - query = request.args.get('query') - query = query.split(',') - Qdate = request.args.get('Qdate') - - date1 = (Qdate.split('-')[0]).split('.') - date1 = datetime.date(int(date1[2]), int(date1[1]), int(date1[0])) - - date2 = (Qdate.split('-')[1]).split('.') - date2 = datetime.date(int(date2[2]), int(date2[1]), int(date2[0])) - - timestamp1 = calendar.timegm(date1.timetuple()) - timestamp2 = calendar.timegm(date2.timetuple()) - - oneHour = 60*60 - oneDay = oneHour*24 - - to_return = {} - for cur_provider in query: - list_date = {} - cur_provider_name = cur_provider + '_' - for cur_timestamp in range(int(timestamp1), int(timestamp2)+oneDay, oneHour): - cur_set_name = cur_provider_name + str(cur_timestamp) - - list_value = [] - for cur_id in r_serv_sentiment.smembers(cur_set_name): - cur_value = r_serv_sentiment.get(cur_id) - list_value.append(cur_value) - list_date[cur_timestamp] = list_value - to_return[cur_provider] = list_date - - return jsonify(to_return) - - -@app.route("/terms_management/") -def terms_management(): - TrackedTermsSet_Name = "TrackedSetTermSet" - BlackListTermsSet_Name = "BlackListSetTermSet" - TrackedTermsDate_Name = "TrackedTermDate" - BlackListTermsDate_Name = "BlackListTermDate" - - today = datetime.datetime.now() - today = today.replace(hour=0, minute=0, second=0, microsecond=0) - today_timestamp = calendar.timegm(today.timetuple()) - - track_list = [] - track_list_values = [] - track_list_num_of_paste = [] - for tracked_term in r_serv_term.smembers(TrackedTermsSet_Name): - track_list.append(tracked_term) - value_range = Term_getValueOverRange(tracked_term, today_timestamp, [1, 7, 31]) - - term_date = r_serv_term.hget(TrackedTermsDate_Name, tracked_term) - - set_paste_name = "tracked_" + tracked_term - track_list_num_of_paste.append(r_serv_term.scard(set_paste_name)) - term_date = datetime.datetime.utcfromtimestamp(int(term_date)) if term_date is not None else "No date recorded" - value_range.append(term_date) - track_list_values.append(value_range) - - - black_list = [] - for blacked_term in r_serv_term.smembers(BlackListTermsSet_Name): - term_date = r_serv_term.hget(BlackListTermsDate_Name, blacked_term) - term_date = datetime.datetime.utcfromtimestamp(int(term_date)) if term_date is not None else "No date recorded" - black_list.append([blacked_term, term_date]) - - return render_template("terms_management.html", black_list=black_list, track_list=track_list, track_list_values=track_list_values, track_list_num_of_paste=track_list_num_of_paste) - - -@app.route("/terms_management_query_paste/") -def terms_management_query_paste(): - term = request.args.get('term') - TrackedTermsSet_Name = "TrackedSetTermSet" - paste_info = [] - - set_paste_name = "tracked_" + term - track_list_path = r_serv_term.smembers(set_paste_name) - - for path in track_list_path: - paste = Paste.Paste(path) - p_date = str(paste._get_p_date()) - p_date = p_date[6:]+'/'+p_date[4:6]+'/'+p_date[0:4] - p_source = paste.p_source - p_encoding = paste._get_p_encoding() - p_size = paste.p_size - p_mime = paste.p_mime - p_lineinfo = paste.get_lines_info() - p_content = paste.get_p_content().decode('utf-8', 'ignore') - if p_content != 0: - p_content = p_content[0:400] - paste_info.append({"path": path, "date": p_date, "source": p_source, "encoding": p_encoding, "size": p_size, "mime": p_mime, "lineinfo": p_lineinfo, "content": p_content}) - - return jsonify(paste_info) - - -@app.route("/terms_management_query/") -def terms_management_query(): - TrackedTermsDate_Name = "TrackedTermDate" - BlackListTermsDate_Name = "BlackListTermDate" - term = request.args.get('term') - section = request.args.get('section') - - today = datetime.datetime.now() - today = today.replace(hour=0, minute=0, second=0, microsecond=0) - today_timestamp = calendar.timegm(today.timetuple()) - value_range = Term_getValueOverRange(term, today_timestamp, [1, 7, 31]) - - if section == "followTerm": - term_date = r_serv_term.hget(TrackedTermsDate_Name, term) - elif section == "blacklistTerm": - term_date = r_serv_term.hget(BlackListTermsDate_Name, term) - - term_date = datetime.datetime.utcfromtimestamp(int(term_date)) if term_date is not None else "No date recorded" - value_range.append(str(term_date)) - return jsonify(value_range) - - -@app.route("/terms_management_action/", methods=['GET']) -def terms_management_action(): - TrackedTermsSet_Name = "TrackedSetTermSet" - TrackedTermsDate_Name = "TrackedTermDate" - BlackListTermsDate_Name = "BlackListTermDate" - BlackListTermsSet_Name = "BlackListSetTermSet" - - today = datetime.datetime.now() - today = today.replace(microsecond=0) - today_timestamp = calendar.timegm(today.timetuple()) - - - section = request.args.get('section') - action = request.args.get('action') - term = request.args.get('term') - if action is None or term is None: - return "None" - else: - if section == "followTerm": - if action == "add": - r_serv_term.sadd(TrackedTermsSet_Name, term.lower()) - r_serv_term.hset(TrackedTermsDate_Name, term, today_timestamp) - else: - r_serv_term.srem(TrackedTermsSet_Name, term.lower()) - elif section == "blacklistTerm": - if action == "add": - r_serv_term.sadd(BlackListTermsSet_Name, term.lower()) - r_serv_term.hset(BlackListTermsDate_Name, term, today_timestamp) - else: - r_serv_term.srem(BlackListTermsSet_Name, term.lower()) - else: - return "None" - - to_return = {} - to_return["section"] = section - to_return["action"] = action - to_return["term"] = term - return jsonify(to_return) - - - -@app.route("/terms_plot_tool/") -def terms_plot_tool(): - term = request.args.get('term') - if term is not None: - return render_template("terms_plot_tool.html", term=term) - else: - return render_template("terms_plot_tool.html", term="") - - -@app.route("/terms_plot_tool_data/") -def terms_plot_tool_data(): - oneDay = 60*60*24 - range_start = datetime.datetime.utcfromtimestamp(int(float(request.args.get('range_start')))) if request.args.get('range_start') is not None else 0; - range_start = range_start.replace(hour=0, minute=0, second=0, microsecond=0) - range_start = calendar.timegm(range_start.timetuple()) - range_end = datetime.datetime.utcfromtimestamp(int(float(request.args.get('range_end')))) if request.args.get('range_end') is not None else 0; - range_end = range_end.replace(hour=0, minute=0, second=0, microsecond=0) - range_end = calendar.timegm(range_end.timetuple()) - term = request.args.get('term') - - if term is None: - return "None" - else: - value_range = [] - for timestamp in range(range_start, range_end+oneDay, oneDay): - value = r_serv_term.hget(timestamp, term) - curr_value_range = int(value) if value is not None else 0 - value_range.append([timestamp, curr_value_range]) - value_range.insert(0,term) - return jsonify(value_range) - - -@app.route("/terms_plot_top/") -def terms_plot_top(): - return render_template("terms_plot_top.html") - - -@app.route("/terms_plot_top_data/") -def terms_plot_top_data(): - oneDay = 60*60*24 - today = datetime.datetime.now() - today = today.replace(hour=0, minute=0, second=0, microsecond=0) - today_timestamp = calendar.timegm(today.timetuple()) - - set_day = "TopTermFreq_set_day_" + str(today_timestamp) - set_week = "TopTermFreq_set_week"; - set_month = "TopTermFreq_set_month"; - - the_set = request.args.get('set') - num_day = int(request.args.get('num_day')) - if the_set is None: - return "None" - else: - to_return = [] - if the_set == "TopTermFreq_set_day": - the_set += "_" + str(today_timestamp) - - for term, tot_value in r_serv_term.zrevrangebyscore(the_set, '+inf', '-inf', withscores=True, start=0, num=20): - position = {} - position['day'] = r_serv_term.zrevrank(set_day, term) - position['day'] = position['day']+1 if position['day'] is not None else "<20" - position['week'] = r_serv_term.zrevrank(set_week, term) - position['week'] = position['week']+1 if position['week'] is not None else "<20" - position['month'] = r_serv_term.zrevrank(set_month, term) - position['month'] = position['month']+1 if position['month'] is not None else "<20" - value_range = [] - for timestamp in range(today_timestamp, today_timestamp - num_day*oneDay, -oneDay): - value = r_serv_term.hget(timestamp, term) - curr_value_range = int(value) if value is not None else 0 - value_range.append([timestamp, curr_value_range]) - - to_return.append([term, value_range, tot_value, position]) - - return jsonify(to_return) - - - -@app.route("/showsavedpaste/") #completely shows the paste in a new tab -def showsavedpaste(): - return showpaste(0) - - -@app.route("/showpreviewpaste/") -def showpreviewpaste(): - num = request.args.get('num', '') - return "|num|"+num+"|num|"+showpaste(max_preview_modal) - - -@app.route("/getmoredata/") -def getmoredata(): - requested_path = request.args.get('paste', '') - paste = Paste.Paste(requested_path) - p_content = paste.get_p_content().decode('utf-8', 'ignore') - to_return = p_content[max_preview_modal-1:] - return to_return - +# ============ MAIN ============ if __name__ == "__main__": app.run(host='0.0.0.0', port=7000, threaded=True) diff --git a/var/www/Flask_showpaste.py b/var/www/Flask_showpaste.py new file mode 100644 index 00000000..71e2a4e2 --- /dev/null +++ b/var/www/Flask_showpaste.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python2 +# -*-coding:UTF-8 -* + +''' + Flask functions and routes for the trending modules page +''' +import redis +import json +import flask +from flask import Flask, render_template, jsonify, request + +import Paste + +# ============ VARIABLES ============ +import Flask_config + +app = Flask_config.app +cfg = Flask_config.cfg +r_serv_pasteName = Flask_config.r_serv_pasteName +max_preview_char = Flask_config.max_preview_char +max_preview_modal = Flask_config.max_preview_modal +tlsh_to_percent = Flask_config.tlsh_to_percent +# ============ FUNCTIONS ============ + +def showpaste(content_range): + requested_path = request.args.get('paste', '') + paste = Paste.Paste(requested_path) + p_date = str(paste._get_p_date()) + p_date = p_date[6:]+'/'+p_date[4:6]+'/'+p_date[0:4] + p_source = paste.p_source + p_encoding = paste._get_p_encoding() + p_language = paste._get_p_language() + p_size = paste.p_size + p_mime = paste.p_mime + p_lineinfo = paste.get_lines_info() + p_content = paste.get_p_content().decode('utf-8', 'ignore') + p_duplicate_full_list = json.loads(paste._get_p_duplicate()) + p_duplicate_list = [] + p_simil_list = [] + p_date_list = [] + p_hashtype_list = [] + + + for dup_list in p_duplicate_full_list: + if dup_list[0] == "tlsh": + dup_list[2] = int(((tlsh_to_percent - float(dup_list[2])) / tlsh_to_percent)*100) + else: + dup_list[2] = int(dup_list[2]) + + p_duplicate_full_list.sort(lambda x,y: cmp(x[2], y[2]), reverse=True) + + # Combine multiple duplicate paste name and format for display + new_dup_list = [] + dup_list_removed = [] + for dup_list_index in range(0, len(p_duplicate_full_list)): + if dup_list_index in dup_list_removed: + continue + indices = [i for i, x in enumerate(p_duplicate_full_list) if x[1] == p_duplicate_full_list[dup_list_index][1]] + hash_types = [] + comp_vals = [] + for i in indices: + hash_types.append(p_duplicate_full_list[i][0].encode('utf8')) + comp_vals.append(p_duplicate_full_list[i][2]) + dup_list_removed.append(i) + + hash_types = str(hash_types).replace("[","").replace("]","") if len(hash_types)==1 else str(hash_types) + comp_vals = str(comp_vals).replace("[","").replace("]","") if len(comp_vals)==1 else str(comp_vals) + if len(p_duplicate_full_list[dup_list_index]) > 3: + try: + date_paste = str(int(p_duplicate_full_list[dup_list_index][3])) + date_paste = date_paste[0:4]+"-"+date_paste[4:6]+"-"+date_paste[6:8] + except ValueError: + date_paste = str(p_duplicate_full_list[dup_list_index][3]) + else: + date_paste = "No date available" + new_dup_list.append([hash_types.replace("'", ""), p_duplicate_full_list[dup_list_index][1], comp_vals, date_paste]) + + # Create the list to pass to the webpage + for dup_list in new_dup_list: + hash_type, path, simil_percent, date_paste = dup_list + p_duplicate_list.append(path) + p_simil_list.append(simil_percent) + p_hashtype_list.append(hash_type) + p_date_list.append(date_paste) + + if content_range != 0: + p_content = p_content[0:content_range] + + + return render_template("show_saved_paste.html", date=p_date, source=p_source, encoding=p_encoding, language=p_language, size=p_size, mime=p_mime, lineinfo=p_lineinfo, content=p_content, initsize=len(p_content), duplicate_list = p_duplicate_list, simil_list = p_simil_list, hashtype_list = p_hashtype_list, date_list=p_date_list) + + + +# ============ ROUTES ============ + +@app.route("/showsavedpaste/") #completely shows the paste in a new tab +def showsavedpaste(): + return showpaste(0) + + +@app.route("/showpreviewpaste/") +def showpreviewpaste(): + num = request.args.get('num', '') + return "|num|"+num+"|num|"+showpaste(max_preview_modal) + + +@app.route("/getmoredata/") +def getmoredata(): + requested_path = request.args.get('paste', '') + paste = Paste.Paste(requested_path) + p_content = paste.get_p_content().decode('utf-8', 'ignore') + to_return = p_content[max_preview_modal-1:] + return to_return + diff --git a/var/www/Flask_terms.py b/var/www/Flask_terms.py new file mode 100644 index 00000000..f5416ddc --- /dev/null +++ b/var/www/Flask_terms.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python2 +# -*-coding:UTF-8 -* + +''' + Flask functions and routes for the trending modules page +''' +import redis +import datetime +import calendar +import flask +from flask import Flask, render_template, jsonify, request + +import Paste + +# ============ VARIABLES ============ +import Flask_config + +app = Flask_config.app +cfg = Flask_config.cfg +r_serv_term = Flask_config.r_serv_term +# ============ FUNCTIONS ============ + +def Term_getValueOverRange(word, startDate, num_day): + passed_days = 0 + oneDay = 60*60*24 + to_return = [] + curr_to_return = 0 + for timestamp in range(startDate, startDate - max(num_day)*oneDay, -oneDay): + value = r_serv_term.hget(timestamp, word) + curr_to_return += int(value) if value is not None else 0 + for i in num_day: + if passed_days == i-1: + to_return.append(curr_to_return) + passed_days += 1 + return to_return + + +# ============ ROUTES ============ + +@app.route("/terms_management/") +def terms_management(): + TrackedTermsSet_Name = "TrackedSetTermSet" + BlackListTermsSet_Name = "BlackListSetTermSet" + TrackedTermsDate_Name = "TrackedTermDate" + BlackListTermsDate_Name = "BlackListTermDate" + + today = datetime.datetime.now() + today = today.replace(hour=0, minute=0, second=0, microsecond=0) + today_timestamp = calendar.timegm(today.timetuple()) + + track_list = [] + track_list_values = [] + track_list_num_of_paste = [] + for tracked_term in r_serv_term.smembers(TrackedTermsSet_Name): + track_list.append(tracked_term) + value_range = Term_getValueOverRange(tracked_term, today_timestamp, [1, 7, 31]) + + term_date = r_serv_term.hget(TrackedTermsDate_Name, tracked_term) + + set_paste_name = "tracked_" + tracked_term + track_list_num_of_paste.append(r_serv_term.scard(set_paste_name)) + term_date = datetime.datetime.utcfromtimestamp(int(term_date)) if term_date is not None else "No date recorded" + value_range.append(term_date) + track_list_values.append(value_range) + + + black_list = [] + for blacked_term in r_serv_term.smembers(BlackListTermsSet_Name): + term_date = r_serv_term.hget(BlackListTermsDate_Name, blacked_term) + term_date = datetime.datetime.utcfromtimestamp(int(term_date)) if term_date is not None else "No date recorded" + black_list.append([blacked_term, term_date]) + + return render_template("terms_management.html", black_list=black_list, track_list=track_list, track_list_values=track_list_values, track_list_num_of_paste=track_list_num_of_paste) + + +@app.route("/terms_management_query_paste/") +def terms_management_query_paste(): + term = request.args.get('term') + TrackedTermsSet_Name = "TrackedSetTermSet" + paste_info = [] + + set_paste_name = "tracked_" + term + track_list_path = r_serv_term.smembers(set_paste_name) + + for path in track_list_path: + paste = Paste.Paste(path) + p_date = str(paste._get_p_date()) + p_date = p_date[6:]+'/'+p_date[4:6]+'/'+p_date[0:4] + p_source = paste.p_source + p_encoding = paste._get_p_encoding() + p_size = paste.p_size + p_mime = paste.p_mime + p_lineinfo = paste.get_lines_info() + p_content = paste.get_p_content().decode('utf-8', 'ignore') + if p_content != 0: + p_content = p_content[0:400] + paste_info.append({"path": path, "date": p_date, "source": p_source, "encoding": p_encoding, "size": p_size, "mime": p_mime, "lineinfo": p_lineinfo, "content": p_content}) + + return jsonify(paste_info) + + +@app.route("/terms_management_query/") +def terms_management_query(): + TrackedTermsDate_Name = "TrackedTermDate" + BlackListTermsDate_Name = "BlackListTermDate" + term = request.args.get('term') + section = request.args.get('section') + + today = datetime.datetime.now() + today = today.replace(hour=0, minute=0, second=0, microsecond=0) + today_timestamp = calendar.timegm(today.timetuple()) + value_range = Term_getValueOverRange(term, today_timestamp, [1, 7, 31]) + + if section == "followTerm": + term_date = r_serv_term.hget(TrackedTermsDate_Name, term) + elif section == "blacklistTerm": + term_date = r_serv_term.hget(BlackListTermsDate_Name, term) + + term_date = datetime.datetime.utcfromtimestamp(int(term_date)) if term_date is not None else "No date recorded" + value_range.append(str(term_date)) + return jsonify(value_range) + + +@app.route("/terms_management_action/", methods=['GET']) +def terms_management_action(): + TrackedTermsSet_Name = "TrackedSetTermSet" + TrackedTermsDate_Name = "TrackedTermDate" + BlackListTermsDate_Name = "BlackListTermDate" + BlackListTermsSet_Name = "BlackListSetTermSet" + + today = datetime.datetime.now() + today = today.replace(microsecond=0) + today_timestamp = calendar.timegm(today.timetuple()) + + + section = request.args.get('section') + action = request.args.get('action') + term = request.args.get('term') + if action is None or term is None: + return "None" + else: + if section == "followTerm": + if action == "add": + r_serv_term.sadd(TrackedTermsSet_Name, term.lower()) + r_serv_term.hset(TrackedTermsDate_Name, term, today_timestamp) + else: + r_serv_term.srem(TrackedTermsSet_Name, term.lower()) + elif section == "blacklistTerm": + if action == "add": + r_serv_term.sadd(BlackListTermsSet_Name, term.lower()) + r_serv_term.hset(BlackListTermsDate_Name, term, today_timestamp) + else: + r_serv_term.srem(BlackListTermsSet_Name, term.lower()) + else: + return "None" + + to_return = {} + to_return["section"] = section + to_return["action"] = action + to_return["term"] = term + return jsonify(to_return) + + + +@app.route("/terms_plot_tool/") +def terms_plot_tool(): + term = request.args.get('term') + if term is not None: + return render_template("terms_plot_tool.html", term=term) + else: + return render_template("terms_plot_tool.html", term="") + + +@app.route("/terms_plot_tool_data/") +def terms_plot_tool_data(): + oneDay = 60*60*24 + range_start = datetime.datetime.utcfromtimestamp(int(float(request.args.get('range_start')))) if request.args.get('range_start') is not None else 0; + range_start = range_start.replace(hour=0, minute=0, second=0, microsecond=0) + range_start = calendar.timegm(range_start.timetuple()) + range_end = datetime.datetime.utcfromtimestamp(int(float(request.args.get('range_end')))) if request.args.get('range_end') is not None else 0; + range_end = range_end.replace(hour=0, minute=0, second=0, microsecond=0) + range_end = calendar.timegm(range_end.timetuple()) + term = request.args.get('term') + + if term is None: + return "None" + else: + value_range = [] + for timestamp in range(range_start, range_end+oneDay, oneDay): + value = r_serv_term.hget(timestamp, term) + curr_value_range = int(value) if value is not None else 0 + value_range.append([timestamp, curr_value_range]) + value_range.insert(0,term) + return jsonify(value_range) + + +@app.route("/terms_plot_top/") +def terms_plot_top(): + return render_template("terms_plot_top.html") + + +@app.route("/terms_plot_top_data/") +def terms_plot_top_data(): + oneDay = 60*60*24 + today = datetime.datetime.now() + today = today.replace(hour=0, minute=0, second=0, microsecond=0) + today_timestamp = calendar.timegm(today.timetuple()) + + set_day = "TopTermFreq_set_day_" + str(today_timestamp) + set_week = "TopTermFreq_set_week"; + set_month = "TopTermFreq_set_month"; + + the_set = request.args.get('set') + num_day = int(request.args.get('num_day')) + if the_set is None: + return "None" + else: + to_return = [] + if the_set == "TopTermFreq_set_day": + the_set += "_" + str(today_timestamp) + + for term, tot_value in r_serv_term.zrevrangebyscore(the_set, '+inf', '-inf', withscores=True, start=0, num=20): + position = {} + position['day'] = r_serv_term.zrevrank(set_day, term) + position['day'] = position['day']+1 if position['day'] is not None else "<20" + position['week'] = r_serv_term.zrevrank(set_week, term) + position['week'] = position['week']+1 if position['week'] is not None else "<20" + position['month'] = r_serv_term.zrevrank(set_month, term) + position['month'] = position['month']+1 if position['month'] is not None else "<20" + value_range = [] + for timestamp in range(today_timestamp, today_timestamp - num_day*oneDay, -oneDay): + value = r_serv_term.hget(timestamp, term) + curr_value_range = int(value) if value is not None else 0 + value_range.append([timestamp, curr_value_range]) + + to_return.append([term, value_range, tot_value, position]) + + return jsonify(to_return) + + diff --git a/var/www/Flask_trendingcharts.py b/var/www/Flask_trendingcharts.py new file mode 100644 index 00000000..fdb1a3d4 --- /dev/null +++ b/var/www/Flask_trendingcharts.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python2 +# -*-coding:UTF-8 -* + +''' + Flask functions and routes for the trending charts page +''' +import redis +import datetime +from Date import Date +import flask +from flask import Flask, render_template, jsonify, request + +# ============ VARIABLES ============ +import Flask_config + +app = Flask_config.app +cfg = Flask_config.cfg +r_serv_charts = Flask_config.r_serv_charts +# ============ FUNCTIONS ============ + +def get_date_range(num_day): + curr_date = datetime.date.today() + date = Date(str(curr_date.year)+str(curr_date.month).zfill(2)+str(curr_date.day).zfill(2)) + date_list = [] + + for i in range(0, num_day+1): + date_list.append(date.substract_day(i)) + return date_list + + +# ============ ROUTES ============ + +@app.route("/_progressionCharts", methods=['GET']) +def progressionCharts(): + attribute_name = request.args.get('attributeName') + trending_name = request.args.get('trendingName') + bar_requested = True if request.args.get('bar') == "true" else False + + if (bar_requested): + num_day = int(request.args.get('days')) + bar_values = [] + + date_range = get_date_range(num_day) + # Retreive all data from the last num_day + for date in date_range: + curr_value = r_serv_charts.hget(attribute_name, date) + bar_values.append([date[0:4]+'/'+date[4:6]+'/'+date[6:8], int(curr_value if curr_value is not None else 0)]) + bar_values.insert(0, attribute_name) + return jsonify(bar_values) + + else: + redis_progression_name = "z_top_progression_" + trending_name + keyw_value = r_serv_charts.zrevrangebyscore(redis_progression_name, '+inf', '-inf', withscores=True, start=0, num=10) + return jsonify(keyw_value) + +@app.route("/wordstrending/") +def wordstrending(): + default_display = cfg.get("Flask", "default_display") + return render_template("Wordstrending.html", default_display = default_display) + + +@app.route("/protocolstrending/") +def protocolstrending(): + default_display = cfg.get("Flask", "default_display") + return render_template("Protocolstrending.html", default_display = default_display) + + +@app.route("/trending/") +def trending(): + default_display = cfg.get("Flask", "default_display") + return render_template("Trending.html", default_display = default_display) + + diff --git a/var/www/Flask_trendingmodules.py b/var/www/Flask_trendingmodules.py new file mode 100644 index 00000000..73cef7f5 --- /dev/null +++ b/var/www/Flask_trendingmodules.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python2 +# -*-coding:UTF-8 -* + +''' + Flask functions and routes for the trending modules page +''' +import redis +import datetime +import flask +from flask import Flask, render_template, jsonify, request + +# ============ VARIABLES ============ +import Flask_config + +app = Flask_config.app +cfg = Flask_config.cfg +r_serv_charts = Flask_config.r_serv_charts +# ============ FUNCTIONS ============ + +# Iterate over elements in the module provided and return the today data or the last data +# return format: [('passed_days', num_of_passed_days), ('elem_name1', elem_value1), ('elem_name2', elem_value2)]] +def get_top_relevant_data(server, module_name): + days = 0 + for date in get_date_range(15): + redis_progression_name_set = 'top_'+ module_name +'_set_' + date + member_set = server.zrevrangebyscore(redis_progression_name_set, '+inf', '-inf', withscores=True) + if len(member_set) == 0: #No data for this date + days += 1 + else: + member_set.insert(0, ("passed_days", days)) + return member_set + + +def get_date_range(num_day): + curr_date = datetime.date.today() + date = Date(str(curr_date.year)+str(curr_date.month).zfill(2)+str(curr_date.day).zfill(2)) + date_list = [] + + for i in range(0, num_day+1): + date_list.append(date.substract_day(i)) + return date_list + +# ============ ROUTES ============ + +@app.route("/_moduleCharts", methods=['GET']) +def modulesCharts(): + keyword_name = request.args.get('keywordName') + module_name = request.args.get('moduleName') + bar_requested = True if request.args.get('bar') == "true" else False + + if (bar_requested): + num_day = int(request.args.get('days')) + bar_values = [] + + date_range = get_date_range(num_day) + # Retreive all data from the last num_day + for date in date_range: + curr_value = r_serv_charts.hget(date, module_name+'-'+keyword_name) + bar_values.append([date[0:4]+'/'+date[4:6]+'/'+date[6:8], int(curr_value if curr_value is not None else 0)]) + bar_values.insert(0, keyword_name) + return jsonify(bar_values) + + else: + member_set = get_top_relevant_data(r_serv_charts, module_name) + if len(member_set) == 0: + member_set.append(("No relevant data", int(100))) + return jsonify(member_set) + + +@app.route("/_providersChart", methods=['GET']) +def providersChart(): + keyword_name = request.args.get('keywordName') + module_name = request.args.get('moduleName') + bar_requested = True if request.args.get('bar') == "true" else False + + if (bar_requested): + num_day = int(request.args.get('days')) + bar_values = [] + + date_range = get_date_range(num_day) + # Retreive all data from the last num_day + for date in date_range: + curr_value_size = r_serv_charts.hget(keyword_name+'_'+'size', date) + curr_value_num = r_serv_charts.hget(keyword_name+'_'+'num', date) + curr_value_size_avg = r_serv_charts.hget(keyword_name+'_'+'avg', date) + if module_name == "size": + curr_value = float(curr_value_size_avg if curr_value_size_avg is not None else 0) + else: + curr_value = float(curr_value_num if curr_value_num is not None else 0.0) + + bar_values.append([date[0:4]+'/'+date[4:6]+'/'+date[6:8], curr_value]) + bar_values.insert(0, keyword_name) + return jsonify(bar_values) + + else: + #redis_provider_name_set = 'top_size_set' if module_name == "size" else 'providers_set' + redis_provider_name_set = 'top_avg_size_set_' if module_name == "size" else 'providers_set_' + redis_provider_name_set = redis_provider_name_set + get_date_range(0)[0] + + member_set = r_serv_charts.zrevrangebyscore(redis_provider_name_set, '+inf', '-inf', withscores=True, start=0, num=8) + # Member set is a list of (value, score) pairs + if len(member_set) == 0: + member_set.append(("No relevant data", float(100))) + return jsonify(member_set) + + +@app.route("/moduletrending/") +def moduletrending(): + return render_template("Moduletrending.html") + + From 0054be5bab79856627f4d6b6cf31e406ad3410a1 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 9 Dec 2016 08:50:36 +0100 Subject: [PATCH 63/89] Added missing json dependency in dashboard --- var/www/Flask_dashboard.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/var/www/Flask_dashboard.py b/var/www/Flask_dashboard.py index b6bcb219..0c975f47 100644 --- a/var/www/Flask_dashboard.py +++ b/var/www/Flask_dashboard.py @@ -4,6 +4,8 @@ ''' Flask functions and routes for the dashboard page ''' +import json + import flask from flask import Flask, render_template, jsonify, request From d30f3ca6c7828b937a3ce86df0ef8270c1652ec1 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 9 Dec 2016 13:53:57 +0100 Subject: [PATCH 64/89] Added missing depency datetime in dashboard --- var/www/Flask_dashboard.py | 1 + 1 file changed, 1 insertion(+) diff --git a/var/www/Flask_dashboard.py b/var/www/Flask_dashboard.py index 0c975f47..79307f9c 100644 --- a/var/www/Flask_dashboard.py +++ b/var/www/Flask_dashboard.py @@ -6,6 +6,7 @@ ''' import json +import datetime import flask from flask import Flask, render_template, jsonify, request From d37b243e1dcfea8c605719eca024a4631be080cf Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 9 Dec 2016 15:13:47 +0100 Subject: [PATCH 65/89] Added legend + added more info if restarting disabled --- bin/ModuleInformation.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/bin/ModuleInformation.py b/bin/ModuleInformation.py index 5eb81e84..acc23a85 100755 --- a/bin/ModuleInformation.py +++ b/bin/ModuleInformation.py @@ -247,15 +247,16 @@ if __name__ == "__main__": printarray3.append([curr_queue, "No data"]) else: #If no info since long time, try to kill - if args.autokill == 1 and int(time.time()) - no_info_modules[curr_queue] > threshold_stucked_module: - kill_module(curr_queue, None) - no_info_modules[curr_queue] = int(time.time()) - printarray3.append([curr_queue, "Stuck or idle, restarting in " + str(threshold_stucked_module - (int(time.time()) - no_info_modules[curr_queue])) + "s"]) + if args.autokill == 1: + if int(time.time()) - no_info_modules[curr_queue] > threshold_stucked_module: + kill_module(curr_queue, None) + no_info_modules[curr_queue] = int(time.time()) + printarray3.append([curr_queue, "Stuck or idle, restarting in " + str(abs(threshold_stucked_module - (int(time.time()) - no_info_modules[curr_queue]))) + "s"]) + else: + printarray3.append([curr_queue, "Stuck or idle, restarting disabled"]) - #printarray1.sort(lambda x,y: cmp(x[0], y[0]), reverse=False) printarray1.sort(key=lambda x: x[0][9:], reverse=False) - #printarray2.sort(lambda x,y: cmp(x[0], y[0]), reverse=False) printarray2.sort(key=lambda x: x[0][9:], reverse=False) printarray1.insert(0,["Queue", "PID", "Amount", "Paste start time", "Processing time for current paste (H:M:S)", "Paste hash"]) printarray2.insert(0,["Queue", "PID","Amount", "Paste start time", "Time since idle (H:M:S)", "Last paste hash"]) @@ -307,6 +308,12 @@ if __name__ == "__main__": t4 = AsciiTable(printarray4, title="Last actions") t4.column_max_width(1) + legend_array = [["Color", "Meaning"], [Back.RED+Style.BRIGHT+" "*10+Style.RESET_ALL, "Time >=" +str(threshold_stucked_module)+Style.RESET_ALL], [Back.MAGENTA+Style.BRIGHT+" "*10+Style.RESET_ALL, "Time >=" +str(threshold_stucked_module)+" while idle"+Style.RESET_ALL], [Back.YELLOW+Style.BRIGHT+" "*10+Style.RESET_ALL, "Time >=" +str(threshold_stucked_module/2)+Style.RESET_ALL], [Back.GREEN+Style.BRIGHT+" "*10+Style.RESET_ALL, "Time <" +str(threshold_stucked_module)]] + legend = AsciiTable(legend_array, title="Legend") + legend.column_max_width(1) + + print legend.table + print '\n' print t1.table print '\n' print t2.table From 73938cf41bd3fe15ba21045eaa76977d6837a9a5 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 22 Dec 2016 08:45:53 +0100 Subject: [PATCH 66/89] Prepared moduleInfo to handle module click with curses (will come back later). --- bin/ModuleInformation.py | 285 +++++++++++++++++++++------------------ 1 file changed, 155 insertions(+), 130 deletions(-) diff --git a/bin/ModuleInformation.py b/bin/ModuleInformation.py index acc23a85..fc219815 100755 --- a/bin/ModuleInformation.py +++ b/bin/ModuleInformation.py @@ -25,9 +25,9 @@ import json from terminaltables import AsciiTable import textwrap from colorama import Fore, Back, Style, init +import curses # CONFIG VARIABLES -threshold_stucked_module = 60*10*1 #1 hour kill_retry_threshold = 60 #1m log_filename = "../logs/moduleInfo.log" command_search_pid = "ps a -o pid,cmd | grep {}" @@ -39,6 +39,15 @@ printarrayGlob = [None]*14 printarrayGlob.insert(0, ["Time", "Module", "PID", "Action"]) lastTimeKillCommand = {} +#Curses init +#stdscr = curses.initscr() +#curses.cbreak() +#stdscr.keypad(1) + +# GLOBAL +last_refresh = 0 + + def getPid(module): p = Popen([command_search_pid.format(module+".py")], stdin=PIPE, stdout=PIPE, bufsize=1, shell=True) for line in p.stdout: @@ -145,23 +154,33 @@ def get_color(time, idle): temp = time.split(':') time = int(temp[0])*3600 + int(temp[1])*60 + int(temp[2]) - if time >= threshold_stucked_module: + if time >= args.treshold: if not idle: return Back.RED + Style.BRIGHT else: return Back.MAGENTA + Style.BRIGHT - elif time > threshold_stucked_module/2: + elif time > args.treshold/2: return Back.YELLOW + Style.BRIGHT else: return Back.GREEN + Style.BRIGHT else: return Style.RESET_ALL +def waiting_refresh(): + global last_refresh + if time.time() - last_refresh < args.refresh: + return False + else: + last_refresh = time.time() + return True + + if __name__ == "__main__": parser = argparse.ArgumentParser(description='Show info concerning running modules and log suspected stucked modules. May be use to automatically kill and restart stucked one.') parser.add_argument('-r', '--refresh', type=int, required=False, default=1, help='Refresh rate') + parser.add_argument('-t', '--treshold', type=int, required=False, default=60*10*1, help='Refresh rate') parser.add_argument('-k', '--autokill', type=int, required=False, default=0, help='Enable auto kill option (1 for TRUE, anything else for FALSE)') parser.add_argument('-c', '--clear', type=int, required=False, default=0, help='Clear the current module information (Used to clear data from old launched modules)') @@ -176,8 +195,6 @@ if __name__ == "__main__": cfg = ConfigParser.ConfigParser() cfg.read(configfile) - threshold_stucked_module = cfg.getint("Module_ModuleInformation", "threshold_stucked_module") - # REDIS # server = redis.StrictRedis( host=cfg.get("Redis_Queues", "host"), @@ -199,130 +216,138 @@ if __name__ == "__main__": cleanRedis() while True: + if waiting_refresh(): - all_queue = set() - printarray1 = [] - printarray2 = [] - printarray3 = [] - for queue, card in server.hgetall("queues").iteritems(): - all_queue.add(queue) - key = "MODULE_" + queue + "_" - keySet = "MODULE_TYPE_" + queue - array_module_type = [] - - for moduleNum in server.smembers(keySet): - value = server.get(key + str(moduleNum)) - if value is not None: - timestamp, path = value.split(", ") - if timestamp is not None and path is not None: - startTime_readable = datetime.datetime.fromtimestamp(int(timestamp)) - processed_time_readable = str((datetime.datetime.now() - startTime_readable)).split('.')[0] - - if int(card) > 0: - if int((datetime.datetime.now() - startTime_readable).total_seconds()) > threshold_stucked_module: - log = open(log_filename, 'a') - log.write(json.dumps([queue, card, str(startTime_readable), str(processed_time_readable), path]) + "\n") - try: - last_kill_try = time.time() - lastTimeKillCommand[moduleNum] - except KeyError: - last_kill_try = kill_retry_threshold+1 - if args.autokill == 1 and last_kill_try > kill_retry_threshold : - kill_module(queue, int(moduleNum)) - - array_module_type.append([get_color(processed_time_readable, False) + str(queue), str(moduleNum), str(card), str(startTime_readable), str(processed_time_readable), str(path) + get_color(None, False)]) - + #key = '' + #while key != 'q': + # key = stdsrc.getch() + # stdscr.refresh() + + all_queue = set() + printarray1 = [] + printarray2 = [] + printarray3 = [] + for queue, card in server.hgetall("queues").iteritems(): + all_queue.add(queue) + key = "MODULE_" + queue + "_" + keySet = "MODULE_TYPE_" + queue + array_module_type = [] + + for moduleNum in server.smembers(keySet): + value = server.get(key + str(moduleNum)) + if value is not None: + timestamp, path = value.split(", ") + if timestamp is not None and path is not None: + startTime_readable = datetime.datetime.fromtimestamp(int(timestamp)) + processed_time_readable = str((datetime.datetime.now() - startTime_readable)).split('.')[0] + + if int(card) > 0: + if int((datetime.datetime.now() - startTime_readable).total_seconds()) > args.treshold: + log = open(log_filename, 'a') + log.write(json.dumps([queue, card, str(startTime_readable), str(processed_time_readable), path]) + "\n") + try: + last_kill_try = time.time() - lastTimeKillCommand[moduleNum] + except KeyError: + last_kill_try = kill_retry_threshold+1 + if args.autokill == 1 and last_kill_try > kill_retry_threshold : + kill_module(queue, int(moduleNum)) + + array_module_type.append([get_color(processed_time_readable, False) + str(queue), str(moduleNum), str(card), str(startTime_readable), str(processed_time_readable), str(path) + get_color(None, False)]) + + else: + printarray2.append([get_color(processed_time_readable, True) + str(queue), str(moduleNum), str(card), str(startTime_readable), str(processed_time_readable), str(path) + get_color(None, True)]) + array_module_type.sort(lambda x,y: cmp(x[4], y[4]), reverse=True) + for e in array_module_type: + printarray1.append(e) + + for curr_queue in module_file_array: + if curr_queue not in all_queue: + printarray3.append([curr_queue, "Not running"]) + else: + if len(list(server.smembers('MODULE_TYPE_'+curr_queue))) == 0: + if curr_queue not in no_info_modules: + no_info_modules[curr_queue] = int(time.time()) + printarray3.append([curr_queue, "No data"]) else: - printarray2.append([get_color(processed_time_readable, True) + str(queue), str(moduleNum), str(card), str(startTime_readable), str(processed_time_readable), str(path) + get_color(None, True)]) - array_module_type.sort(lambda x,y: cmp(x[4], y[4]), reverse=True) - for e in array_module_type: - printarray1.append(e) - - for curr_queue in module_file_array: - if curr_queue not in all_queue: - printarray3.append([curr_queue, "Not running"]) - else: - if len(list(server.smembers('MODULE_TYPE_'+curr_queue))) == 0: - if curr_queue not in no_info_modules: - no_info_modules[curr_queue] = int(time.time()) - printarray3.append([curr_queue, "No data"]) - else: - #If no info since long time, try to kill - if args.autokill == 1: - if int(time.time()) - no_info_modules[curr_queue] > threshold_stucked_module: - kill_module(curr_queue, None) - no_info_modules[curr_queue] = int(time.time()) - printarray3.append([curr_queue, "Stuck or idle, restarting in " + str(abs(threshold_stucked_module - (int(time.time()) - no_info_modules[curr_queue]))) + "s"]) - else: - printarray3.append([curr_queue, "Stuck or idle, restarting disabled"]) - - - printarray1.sort(key=lambda x: x[0][9:], reverse=False) - printarray2.sort(key=lambda x: x[0][9:], reverse=False) - printarray1.insert(0,["Queue", "PID", "Amount", "Paste start time", "Processing time for current paste (H:M:S)", "Paste hash"]) - printarray2.insert(0,["Queue", "PID","Amount", "Paste start time", "Time since idle (H:M:S)", "Last paste hash"]) - printarray3.insert(0,["Queue", "State"]) - - os.system('clear') - t1 = AsciiTable(printarray1, title="Working queues") - t1.column_max_width(1) - if not t1.ok: - longest_col = t1.column_widths.index(max(t1.column_widths)) - max_length_col = t1.column_max_width(longest_col) - if max_length_col > 0: - for i, content in enumerate(t1.table_data): - if len(content[longest_col]) > max_length_col: - temp = '' - for l in content[longest_col].splitlines(): - if len(l) > max_length_col: - temp += '\n'.join(textwrap.wrap(l, max_length_col)) + '\n' - else: - temp += l + '\n' - content[longest_col] = temp.strip() - t1.table_data[i] = content - - t2 = AsciiTable(printarray2, title="Idling queues") - t2.column_max_width(1) - if not t2.ok: - longest_col = t2.column_widths.index(max(t2.column_widths)) - max_length_col = t2.column_max_width(longest_col) - if max_length_col > 0: - for i, content in enumerate(t2.table_data): - if len(content[longest_col]) > max_length_col: - temp = '' - for l in content[longest_col].splitlines(): - if len(l) > max_length_col: - temp += '\n'.join(textwrap.wrap(l, max_length_col)) + '\n' - else: - temp += l + '\n' - content[longest_col] = temp.strip() - t2.table_data[i] = content - - t3 = AsciiTable(printarray3, title="Not running queues") - t3.column_max_width(1) - - printarray4 = [] - for elem in printarrayGlob: - if elem is not None: - printarray4.append(elem) - - t4 = AsciiTable(printarray4, title="Last actions") - t4.column_max_width(1) - - legend_array = [["Color", "Meaning"], [Back.RED+Style.BRIGHT+" "*10+Style.RESET_ALL, "Time >=" +str(threshold_stucked_module)+Style.RESET_ALL], [Back.MAGENTA+Style.BRIGHT+" "*10+Style.RESET_ALL, "Time >=" +str(threshold_stucked_module)+" while idle"+Style.RESET_ALL], [Back.YELLOW+Style.BRIGHT+" "*10+Style.RESET_ALL, "Time >=" +str(threshold_stucked_module/2)+Style.RESET_ALL], [Back.GREEN+Style.BRIGHT+" "*10+Style.RESET_ALL, "Time <" +str(threshold_stucked_module)]] - legend = AsciiTable(legend_array, title="Legend") - legend.column_max_width(1) - - print legend.table - print '\n' - print t1.table - print '\n' - print t2.table - print '\n' - print t3.table - print '\n' - print t4.table - - if (datetime.datetime.now() - lastTime).total_seconds() > args.refresh*5: - lastTime = datetime.datetime.now() - cleanRedis() - time.sleep(args.refresh) + #If no info since long time, try to kill + if args.autokill == 1: + if int(time.time()) - no_info_modules[curr_queue] > args.treshold: + kill_module(curr_queue, None) + no_info_modules[curr_queue] = int(time.time()) + printarray3.append([curr_queue, "Stuck or idle, restarting in " + str(abs(args.treshold - (int(time.time()) - no_info_modules[curr_queue]))) + "s"]) + else: + printarray3.append([curr_queue, "Stuck or idle, restarting disabled"]) + + ## FIXME To add: + ## Button KILL Process using Curses + + printarray1.sort(key=lambda x: x[0][9:], reverse=False) + printarray2.sort(key=lambda x: x[0][9:], reverse=False) + printarray1.insert(0,["Queue", "PID", "Amount", "Paste start time", "Processing time for current paste (H:M:S)", "Paste hash"]) + printarray2.insert(0,["Queue", "PID","Amount", "Paste start time", "Time since idle (H:M:S)", "Last paste hash"]) + printarray3.insert(0,["Queue", "State"]) + + os.system('clear') + t1 = AsciiTable(printarray1, title="Working queues") + t1.column_max_width(1) + if not t1.ok: + longest_col = t1.column_widths.index(max(t1.column_widths)) + max_length_col = t1.column_max_width(longest_col) + if max_length_col > 0: + for i, content in enumerate(t1.table_data): + if len(content[longest_col]) > max_length_col: + temp = '' + for l in content[longest_col].splitlines(): + if len(l) > max_length_col: + temp += '\n'.join(textwrap.wrap(l, max_length_col)) + '\n' + else: + temp += l + '\n' + content[longest_col] = temp.strip() + t1.table_data[i] = content + + t2 = AsciiTable(printarray2, title="Idling queues") + t2.column_max_width(1) + if not t2.ok: + longest_col = t2.column_widths.index(max(t2.column_widths)) + max_length_col = t2.column_max_width(longest_col) + if max_length_col > 0: + for i, content in enumerate(t2.table_data): + if len(content[longest_col]) > max_length_col: + temp = '' + for l in content[longest_col].splitlines(): + if len(l) > max_length_col: + temp += '\n'.join(textwrap.wrap(l, max_length_col)) + '\n' + else: + temp += l + '\n' + content[longest_col] = temp.strip() + t2.table_data[i] = content + + t3 = AsciiTable(printarray3, title="Not running queues") + t3.column_max_width(1) + + printarray4 = [] + for elem in printarrayGlob: + if elem is not None: + printarray4.append(elem) + + t4 = AsciiTable(printarray4, title="Last actions") + t4.column_max_width(1) + + legend_array = [["Color", "Meaning"], [Back.RED+Style.BRIGHT+" "*10+Style.RESET_ALL, "Time >=" +str(args.treshold)+Style.RESET_ALL], [Back.MAGENTA+Style.BRIGHT+" "*10+Style.RESET_ALL, "Time >=" +str(args.treshold)+" while idle"+Style.RESET_ALL], [Back.YELLOW+Style.BRIGHT+" "*10+Style.RESET_ALL, "Time >=" +str(args.treshold/2)+Style.RESET_ALL], [Back.GREEN+Style.BRIGHT+" "*10+Style.RESET_ALL, "Time <" +str(args.treshold)]] + legend = AsciiTable(legend_array, title="Legend") + legend.column_max_width(1) + + print legend.table + print '\n' + print t1.table + print '\n' + print t2.table + print '\n' + print t3.table + print '\n' + print t4.table + + if (datetime.datetime.now() - lastTime).total_seconds() > args.refresh*5: + lastTime = datetime.datetime.now() + cleanRedis() + #time.sleep(args.refresh) From 9df12cdae828133e4e3efd08219764932daa09af Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 22 Dec 2016 08:57:45 +0100 Subject: [PATCH 67/89] Added sub-flask in a directory --- var/www/Flask_server.py | 1 + var/www/{ => Flasks}/Flask_browsepastes.py | 0 var/www/{ => Flasks}/Flask_config.py | 0 var/www/Flasks/Flask_corpus.py | 219 ++++++++++++++++++ var/www/{ => Flasks}/Flask_dashboard.py | 0 var/www/{ => Flasks}/Flask_search.py | 0 var/www/{ => Flasks}/Flask_sentiment.py | 0 var/www/{ => Flasks}/Flask_showpaste.py | 0 var/www/{ => Flasks}/Flask_terms.py | 0 var/www/{ => Flasks}/Flask_trendingcharts.py | 0 var/www/{ => Flasks}/Flask_trendingmodules.py | 0 11 files changed, 220 insertions(+) rename var/www/{ => Flasks}/Flask_browsepastes.py (100%) rename var/www/{ => Flasks}/Flask_config.py (100%) create mode 100644 var/www/Flasks/Flask_corpus.py rename var/www/{ => Flasks}/Flask_dashboard.py (100%) rename var/www/{ => Flasks}/Flask_search.py (100%) rename var/www/{ => Flasks}/Flask_sentiment.py (100%) rename var/www/{ => Flasks}/Flask_showpaste.py (100%) rename var/www/{ => Flasks}/Flask_terms.py (100%) rename var/www/{ => Flasks}/Flask_trendingcharts.py (100%) rename var/www/{ => Flasks}/Flask_trendingmodules.py (100%) diff --git a/var/www/Flask_server.py b/var/www/Flask_server.py index 4ece0c75..2ee00295 100755 --- a/var/www/Flask_server.py +++ b/var/www/Flask_server.py @@ -12,6 +12,7 @@ import flask import os import sys sys.path.append(os.path.join(os.environ['AIL_BIN'], 'packages/')) +sys.path.append('./Flasks/') import Paste from Date import Date diff --git a/var/www/Flask_browsepastes.py b/var/www/Flasks/Flask_browsepastes.py similarity index 100% rename from var/www/Flask_browsepastes.py rename to var/www/Flasks/Flask_browsepastes.py diff --git a/var/www/Flask_config.py b/var/www/Flasks/Flask_config.py similarity index 100% rename from var/www/Flask_config.py rename to var/www/Flasks/Flask_config.py diff --git a/var/www/Flasks/Flask_corpus.py b/var/www/Flasks/Flask_corpus.py new file mode 100644 index 00000000..7805e66e --- /dev/null +++ b/var/www/Flasks/Flask_corpus.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python2 +# -*-coding:UTF-8 -* + +''' + Flask functions and routes for the trending modules page +''' +import redis +import datetime +import calendar +import flask +from flask import Flask, render_template, jsonify, request + +import Paste + +# ============ VARIABLES ============ +import Flask_config + +app = Flask_config.app +cfg = Flask_config.cfg +r_serv_corpus = Flask_config.r_serv_corpus +# ============ FUNCTIONS ============ + +def Corpus_getValueOverRange(word, startDate, num_day): + passed_days = 0 + oneDay = 60*60*24 + to_return = [] + curr_to_return = 0 + for timestamp in range(startDate, startDate - max(num_day)*oneDay, -oneDay): + value = r_serv_corpus.hget(timestamp, word) + curr_to_return += int(value) if value is not None else 0 + for i in num_day: + if passed_days == i-1: + to_return.append(curr_to_return) + passed_days += 1 + return to_return + + +# ============ ROUTES ============ + +@app.route("/corpus_management/") +def corpus_management(): + TrackedCorpusSet_Name = "TrackedSetCorpusSet" + TrackedCorpusDate_Name = "TrackedCorpusDate" + + today = datetime.datetime.now() + today = today.replace(hour=0, minute=0, second=0, microsecond=0) + today_timestamp = calendar.timegm(today.timetuple()) + + track_list = [] + track_list_values = [] + track_list_num_of_paste = [] + for tracked_corpus in r_serv_corpus.smembers(TrackedCorpusSet_Name): + track_list.append(tracked_corpus) + value_range = Corpus_getValueOverRange(tracked_corpus, today_timestamp, [1, 7, 31]) + + corpus_date = r_serv_corpus.hget(TrackedCorpusDate_Name, tracked_corpus) + + set_paste_name = "tracked_" + tracked_corpus + track_list_num_of_paste.append(r_serv_corpus.scard(set_paste_name)) + corpus_date = datetime.datetime.utcfromtimestamp(int(corpus_date)) if corpus_date is not None else "No date recorded" + value_range.append(corpus_date) + track_list_values.append(value_range) + + + return render_template("corpus_management.html", black_list=black_list, track_list=track_list, track_list_values=track_list_values, track_list_num_of_paste=track_list_num_of_paste) + + +@app.route("/corpus_management_query_paste/") +def corpus_management_query_paste(): + corpus = request.args.get('corpus') + TrackedCorpusSet_Name = "TrackedSetCorpusSet" + paste_info = [] + + set_paste_name = "tracked_" + corpus + track_list_path = r_serv_corpus.smembers(set_paste_name) + + for path in track_list_path: + paste = Paste.Paste(path) + p_date = str(paste._get_p_date()) + p_date = p_date[6:]+'/'+p_date[4:6]+'/'+p_date[0:4] + p_source = paste.p_source + p_encoding = paste._get_p_encoding() + p_size = paste.p_size + p_mime = paste.p_mime + p_lineinfo = paste.get_lines_info() + p_content = paste.get_p_content().decode('utf-8', 'ignore') + if p_content != 0: + p_content = p_content[0:400] + paste_info.append({"path": path, "date": p_date, "source": p_source, "encoding": p_encoding, "size": p_size, "mime": p_mime, "lineinfo": p_lineinfo, "content": p_content}) + + return jsonify(paste_info) + + +@app.route("/corpus_management_query/") +def corpus_management_query(): + TrackedCorpusDate_Name = "TrackedCorpusDate" + corpus = request.args.get('corpus') + + today = datetime.datetime.now() + today = today.replace(hour=0, minute=0, second=0, microsecond=0) + today_timestamp = calendar.timegm(today.timetuple()) + value_range = corpus_getValueOverRange(corpus, today_timestamp, [1, 7, 31]) + + corpus_date = r_serv_corpus.hget(TrackedCorpusDate_Name, corpus) + + corpus_date = datetime.datetime.utcfromtimestamp(int(corpus_date)) if corpus_date is not None else "No date recorded" + value_range.append(str(corpus_date)) + return jsonify(value_range) + + +@app.route("/corpus_management_action/", methods=['GET']) +def corpus_management_action(): + TrackedCorpusSet_Name = "TrackedSetCorpusSet" + TrackedCorpusDate_Name = "TrackedCorpusDate" + + today = datetime.datetime.now() + today = today.replace(microsecond=0) + today_timestamp = calendar.timegm(today.timetuple()) + + + section = request.args.get('section') + action = request.args.get('action') + corpus = request.args.get('corpus') + if action is None or corpus is None: + return "None" + else: + if section == "followCorpus": + if action == "add": + r_serv_corpus.sadd(TrackedCorpusSet_Name, corpus.lower()) + r_serv_corpus.hset(TrackedCorpusDate_Name, corpus, today_timestamp) + else: + r_serv_corpus.srem(TrackedCorpusSet_Name, corpus.lower()) + else: + return "None" + + to_return = {} + to_return["section"] = section + to_return["action"] = action + to_return["corpus"] = corpus + return jsonify(to_return) + + + +@app.route("/corpus_plot_tool/") +def corpus_plot_tool(): + corpus = request.args.get('corpus') + if corpus is not None: + return render_template("corpus_plot_tool.html", corpus=corpus) + else: + return render_template("corpus_plot_tool.html", corpus="") + + +@app.route("/corpus_plot_tool_data/") +def corpus_plot_tool_data(): + oneDay = 60*60*24 + range_start = datetime.datetime.utcfromtimestamp(int(float(request.args.get('range_start')))) if request.args.get('range_start') is not None else 0; + range_start = range_start.replace(hour=0, minute=0, second=0, microsecond=0) + range_start = calendar.timegm(range_start.timetuple()) + range_end = datetime.datetime.utcfromtimestamp(int(float(request.args.get('range_end')))) if request.args.get('range_end') is not None else 0; + range_end = range_end.replace(hour=0, minute=0, second=0, microsecond=0) + range_end = calendar.timegm(range_end.timetuple()) + corpus = request.args.get('corpus') + + if corpus is None: + return "None" + else: + value_range = [] + for timestamp in range(range_start, range_end+oneDay, oneDay): + value = r_serv_corpus.hget(timestamp, corpus) + curr_value_range = int(value) if value is not None else 0 + value_range.append([timestamp, curr_value_range]) + value_range.insert(0,corpus) + return jsonify(value_range) + + +@app.route("/corpus_plot_top/") +def corpus_plot_top(): + return render_template("corpus_plot_top.html") + + +@app.route("/corpus_plot_top_data/") +def corpus_plot_top_data(): + oneDay = 60*60*24 + today = datetime.datetime.now() + today = today.replace(hour=0, minute=0, second=0, microsecond=0) + today_timestamp = calendar.timegm(today.timetuple()) + + set_day = "TopCorpusFreq_set_day_" + str(today_timestamp) + set_week = "TopCorpusFreq_set_week"; + set_month = "TopCorpusFreq_set_month"; + + the_set = request.args.get('set') + num_day = int(request.args.get('num_day')) + if the_set is None: + return "None" + else: + to_return = [] + if the_set == "TopCorpusFreq_set_day": + the_set += "_" + str(today_timestamp) + + for corpus, tot_value in r_serv_corpus.zrevrangebyscore(the_set, '+inf', '-inf', withscores=True, start=0, num=20): + position = {} + position['day'] = r_serv_corpus.zrevrank(set_day, corpus) + position['day'] = position['day']+1 if position['day'] is not None else "<20" + position['week'] = r_serv_corpus.zrevrank(set_week, corpus) + position['week'] = position['week']+1 if position['week'] is not None else "<20" + position['month'] = r_serv_corpus.zrevrank(set_month, corpus) + position['month'] = position['month']+1 if position['month'] is not None else "<20" + value_range = [] + for timestamp in range(today_timestamp, today_timestamp - num_day*oneDay, -oneDay): + value = r_serv_corpus.hget(timestamp, corpus) + curr_value_range = int(value) if value is not None else 0 + value_range.append([timestamp, curr_value_range]) + + to_return.append([corpus, value_range, tot_value, position]) + + return jsonify(to_return) + + diff --git a/var/www/Flask_dashboard.py b/var/www/Flasks/Flask_dashboard.py similarity index 100% rename from var/www/Flask_dashboard.py rename to var/www/Flasks/Flask_dashboard.py diff --git a/var/www/Flask_search.py b/var/www/Flasks/Flask_search.py similarity index 100% rename from var/www/Flask_search.py rename to var/www/Flasks/Flask_search.py diff --git a/var/www/Flask_sentiment.py b/var/www/Flasks/Flask_sentiment.py similarity index 100% rename from var/www/Flask_sentiment.py rename to var/www/Flasks/Flask_sentiment.py diff --git a/var/www/Flask_showpaste.py b/var/www/Flasks/Flask_showpaste.py similarity index 100% rename from var/www/Flask_showpaste.py rename to var/www/Flasks/Flask_showpaste.py diff --git a/var/www/Flask_terms.py b/var/www/Flasks/Flask_terms.py similarity index 100% rename from var/www/Flask_terms.py rename to var/www/Flasks/Flask_terms.py diff --git a/var/www/Flask_trendingcharts.py b/var/www/Flasks/Flask_trendingcharts.py similarity index 100% rename from var/www/Flask_trendingcharts.py rename to var/www/Flasks/Flask_trendingcharts.py diff --git a/var/www/Flask_trendingmodules.py b/var/www/Flasks/Flask_trendingmodules.py similarity index 100% rename from var/www/Flask_trendingmodules.py rename to var/www/Flasks/Flask_trendingmodules.py From faa8f8302de31eaeb8fa680fa7767193e8cfe066 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 22 Dec 2016 09:28:55 +0100 Subject: [PATCH 68/89] Removed typo in the number of remaining pastes to display in browseImportantPaste --- var/www/Flasks/Flask_browsepastes.py | 4 ++-- .../templates/important_paste_by_module.html | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/var/www/Flasks/Flask_browsepastes.py b/var/www/Flasks/Flask_browsepastes.py index e5aa5b84..b393ab9e 100644 --- a/var/www/Flasks/Flask_browsepastes.py +++ b/var/www/Flasks/Flask_browsepastes.py @@ -79,9 +79,9 @@ def importantPasteByModule(): paste_linenum.append(paste.get_lines_info()[0]) if len(allPastes) > 10: - finished = "" + finished = False else: - finished = "display: none;" + finished = True return render_template("important_paste_by_module.html", moduleName=module_name, diff --git a/var/www/templates/important_paste_by_module.html b/var/www/templates/important_paste_by_module.html index ca98ce59..3a1c1c64 100644 --- a/var/www/templates/important_paste_by_module.html +++ b/var/www/templates/important_paste_by_module.html @@ -29,7 +29,7 @@
-
+

@@ -38,13 +38,15 @@ var json_array = []; var all_data_received = false; var curr_numElem; -var elem_added = 0; -var tot_num_entry = 0; +var elem_added = 10; //10 elements are added by default in the page loading +var tot_num_entry = 10; //10 elements are added by default in the page loading function deploy_source() { var button_load_more_displayed = false; if(typeof(EventSource) !== "undefined" && typeof(source) !== "") { + $("#load_more_json_button1").show(); + $("#load_more_json_button2").show(); var source = new EventSource("{{ url_for('getImportantPasteByModule') }}"+"?moduleName="+moduleName); source.onmessage = function(event) { var feed = jQuery.parseJSON( event.data ); @@ -52,8 +54,6 @@ function deploy_source() { if (feed.index > curr_numElem & feed.module == moduleName) { // Avoid doubling the pastes json_array.push(feed); tot_num_entry++; - $("#load_more_json_button1").show(); - $("#load_more_json_button2").show(); $("#nbr_entry").text(tot_num_entry + " entries available, " + (tot_num_entry - elem_added) + " not displayed"); $("#myTable_"+moduleName).attr('data-numElem', curr_numElem+1); @@ -125,7 +125,14 @@ $(document).ready(function(){ search_table = $('#myTable_'+moduleName).DataTable({ "order": [[ 2, "desc" ]] }); - deploy_source(); + if( "{{ finished }}" == "True"){ + $("#load_more_json_button1").hide(); + $("#load_more_json_button2").hide(); + $("#nbr_entry").hide(); + $("#loading_gif_browse").hide(); + } else { + deploy_source(); + } }); From 7438f16c631184c631e9a5ad4e4eac861850cb8b Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Thu, 22 Dec 2016 10:06:35 +0100 Subject: [PATCH 69/89] Added new config --- bin/packages/config.cfg.sample | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bin/packages/config.cfg.sample b/bin/packages/config.cfg.sample index e74b5da2..566cf22c 100644 --- a/bin/packages/config.cfg.sample +++ b/bin/packages/config.cfg.sample @@ -36,6 +36,9 @@ threshold_duplicate_tlsh = 100 #Minimum size of the paste considered min_paste_size = 0.3 +[Module_ModuleInformation] +#Threshold to deduce if a module is stuck or not, in seconds. +threshold_stucked_module=600 ##### Redis ##### [Redis_Cache] From e70b9cd15cc62d53077cbc783f3cd257c48f5e97 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 23 Dec 2016 10:31:26 +0100 Subject: [PATCH 70/89] Added basic mixer with confirugable behavior. It handles muliple feeders and performs some basic stats on them. --- bin/Global.py | 2 + bin/Helper.py | 27 ++++-- bin/LAUNCH.sh | 2 + bin/Mixer.py | 172 +++++++++++++++++++++++++++++++++ bin/packages/config.cfg.sample | 14 +++ bin/packages/modules.cfg | 6 +- 6 files changed, 213 insertions(+), 10 deletions(-) create mode 100755 bin/Mixer.py diff --git a/bin/Global.py b/bin/Global.py index 9cacbc88..a29c3b86 100755 --- a/bin/Global.py +++ b/bin/Global.py @@ -72,6 +72,8 @@ if __name__ == '__main__': os.makedirs(dirname) with open(filename, 'wb') as f: + print gzip64encoded + print base64.standard_b64decode(gzip64encoded) f.write(base64.standard_b64decode(gzip64encoded)) p.populate_set_out(filename) processed_paste+=1 diff --git a/bin/Helper.py b/bin/Helper.py index 05b73bf3..d6441b9a 100755 --- a/bin/Helper.py +++ b/bin/Helper.py @@ -32,7 +32,7 @@ class PubSub(object): self.config.read(configfile) self.redis_sub = False self.zmq_sub = False - self.subscriber = None + self.subscribers = None self.publishers = {'Redis': [], 'ZMQ': []} def setup_subscribe(self, conn_name): @@ -46,14 +46,19 @@ class PubSub(object): host=self.config.get('RedisPubSub', 'host'), port=self.config.get('RedisPubSub', 'port'), db=self.config.get('RedisPubSub', 'db')) - self.subscriber = r.pubsub(ignore_subscribe_messages=True) - self.subscriber.psubscribe(channel) + self.subscribers = r.pubsub(ignore_subscribe_messages=True) + self.subscribers.psubscribe(channel) elif conn_name.startswith('ZMQ'): self.zmq_sub = True context = zmq.Context() - self.subscriber = context.socket(zmq.SUB) - self.subscriber.connect(self.config.get(conn_name, 'address')) - self.subscriber.setsockopt(zmq.SUBSCRIBE, channel) + + self.subscribers = [] + addresses = self.config.get(conn_name, 'address') + for address in addresses.split(','): + new_sub = context.socket(zmq.SUB) + new_sub.connect(address) + new_sub.setsockopt(zmq.SUBSCRIBE, channel) + self.subscribers.append(new_sub) def setup_publish(self, conn_name): if self.config.has_section(conn_name): @@ -83,13 +88,17 @@ class PubSub(object): def subscribe(self): if self.redis_sub: - for msg in self.subscriber.listen(): + for msg in self.subscribers.listen(): if msg.get('data', None) is not None: yield msg['data'] elif self.zmq_sub: while True: - msg = self.subscriber.recv() - yield msg.split(' ', 1)[1] + for sub in self.subscribers: + try: + msg = sub.recv(zmq.NOBLOCK) + yield msg.split(' ', 1)[1] + except zmq.error.Again as e: + pass else: raise Exception('No subscribe function defined') diff --git a/bin/LAUNCH.sh b/bin/LAUNCH.sh index d7c31472..f020f3a7 100755 --- a/bin/LAUNCH.sh +++ b/bin/LAUNCH.sh @@ -114,6 +114,8 @@ function launching_scripts { screen -S "Script" -X screen -t "ModuleInformation" bash -c './ModuleInformation.py -k 0 -c 1; read x' sleep 0.1 + screen -S "Script" -X screen -t "Mixer" bash -c './Mixer.py; read x' + sleep 0.1 screen -S "Script" -X screen -t "Global" bash -c './Global.py; read x' sleep 0.1 screen -S "Script" -X screen -t "Duplicates" bash -c './Duplicates.py; read x' diff --git a/bin/Mixer.py b/bin/Mixer.py new file mode 100755 index 00000000..e1804263 --- /dev/null +++ b/bin/Mixer.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# -*-coding:UTF-8 -* +""" +The ZMQ_Feed_Q Module +===================== + +This module is consuming the Redis-list created by the ZMQ_Feed_Q Module. + +This module take all the feeds provided in the config. +Depending on the configuration, this module will process the feed as follow: + operation_mode 1: "Avoid any duplicate from any sources" + - The module maintain a list of content for each paste + - If the content is new, process it + - Else, do not process it but keep track for statistics on duplicate + + operation_mode 2: "Keep duplicate coming from different sources" + - The module maintain a list of name given to the paste by the feeder + - If the name has not yet been seen, process it + - Elseif, the saved content associated with the paste is not the same, process it + - Else, do not process it but keep track for statistics on duplicate + +Note that the hash of the content is defined as the gzip64encoded + +Requirements +------------ + +*Need running Redis instances. +*Need the ZMQ_Feed_Q Module running to be able to work properly. + +""" +import base64 +import os +import time +from pubsublogger import publisher +import redis +import ConfigParser + +from Helper import Process + + +# CONFIG # +refresh_time = 30 + + + +if __name__ == '__main__': + publisher.port = 6380 + publisher.channel = 'Script' + + config_section = 'Mixer' + + p = Process(config_section) + + configfile = os.path.join(os.environ['AIL_BIN'], 'packages/config.cfg') + if not os.path.exists(configfile): + raise Exception('Unable to find the configuration file. \ + Did you set environment variables? \ + Or activate the virtualenv.') + + cfg = ConfigParser.ConfigParser() + cfg.read(configfile) + + # REDIS # + server = redis.StrictRedis( + host=cfg.get("Redis_Mixer", "host"), + port=cfg.getint("Redis_Mixer", "port"), + db=cfg.getint("Redis_Mixer", "db")) + + # LOGGING # + publisher.info("Feed Script started to receive & publish.") + + # OTHER CONFIG # + operation_mode = cfg.getint("Module_Mixer", "operation_mode") + ttl_key = cfg.getint("Module_Mixer", "ttl_duplicate") + + # STATS # + processed_paste = 0 + processed_paste_per_feeder = {} + duplicated_paste_per_feeder = {} + time_1 = time.time() + + + while True: + + message = p.get_from_set() + if message is not None: + splitted = message.split() + if len(splitted) == 2: + paste, gzip64encoded = splitted + try: + feeder_name, paste_name = paste.split('>') + feeder_name.replace(" ","") + except ValueError as e: + feeder_name = "unnamed_feeder" + paste_name = paste + + # Processed paste + processed_paste += 1 + try: + processed_paste_per_feeder[feeder_name] += 1 + except KeyError as e: + # new feeder + processed_paste_per_feeder[feeder_name] = 1 + duplicated_paste_per_feeder[feeder_name] = 0 + + relay_message = "{0} {1}".format(paste_name, gzip64encoded) + + # Avoid any duplicate coming from any sources + if operation_mode == 1: + if server.exists(gzip64encoded): # Content already exists + #STATS + duplicated_paste_per_feeder[feeder_name] += 1 + else: # New content + p.populate_set_out(relay_message) + server.sadd(gzip64encoded, feeder_name) + server.expire(gzip64encoded, ttl_key) + + + # Keep duplicate coming from different sources + else: + # Filter to avoid duplicate + content = server.get('HASH_'+paste_name) + if content is None: + # New content + # Store in redis for filtering + server.set('HASH_'+paste_name, content) + server.sadd(paste_name, feeder_name) + server.expire(paste_name, ttl_key) + server.expire('HASH_'+paste_name, ttl_key) + p.populate_set_out(relay_message) + else: + if gzip64encoded != content: + # Same paste name but different content + #STATS + duplicated_paste_per_feeder[feeder_name] += 1 + server.sadd(paste_name, feeder_name) + server.expire(paste_name, ttl_key) + p.populate_set_out(relay_message) + else: + # Already processed + # Keep track of processed pastes + #STATS + duplicated_paste_per_feeder[feeder_name] += 1 + continue + + else: + # TODO Store the name of the empty paste inside a Redis-list. + print "Empty Paste: not processed" + publisher.debug("Empty Paste: {0} not processed".format(message)) + else: + print "Empty Queues: Waiting..." + if int(time.time() - time_1) > refresh_time: + to_print = 'Mixer; ; ; ;mixer_all Processed {0} paste(s) in {1}sec'.format(processed_paste, refresh_time) + print to_print + publisher.info(to_print) + processed_paste = 0 + + for feeder, count in processed_paste_per_feeder.iteritems(): + to_print = 'Mixer; ; ; ;mixer_{0} {0} Processed {1} paste(s) in {2}sec'.format(feeder, count, refresh_time) + print to_print + publisher.info(to_print) + processed_paste_per_feeder[feeder] = 0 + + for feeder, count in duplicated_paste_per_feeder.iteritems(): + to_print = 'Mixer; ; ; ;mixer_{0} {0} Duplicated {1} paste(s) in {2}sec'.format(feeder, count, refresh_time) + print to_print + publisher.info(to_print) + duplicated_paste_per_feeder[feeder] = 0 + + time_1 = time.time() + time.sleep(0.5) + continue diff --git a/bin/packages/config.cfg.sample b/bin/packages/config.cfg.sample index 566cf22c..880bf169 100644 --- a/bin/packages/config.cfg.sample +++ b/bin/packages/config.cfg.sample @@ -40,6 +40,12 @@ min_paste_size = 0.3 #Threshold to deduce if a module is stuck or not, in seconds. threshold_stucked_module=600 +[Module_Mixer] +#Define the configuration of the mixer, possible value: 1 or 2 +operation_mode = 1 +#Define the time that a paste will be considerate duplicate. in seconds (1day = 86400) +ttl_duplicate = 86400 + ##### Redis ##### [Redis_Cache] host = localhost @@ -66,6 +72,12 @@ host = localhost port = 6379 db = 2 +[Redis_Mixer] +host = localhost +port = 6381 +db = 1 +channel = 102 + ##### LevelDB ##### [Redis_Level_DB_Curve] host = localhost @@ -111,6 +123,8 @@ path = indexdir ############################################################################### +# For multiple feed, add them with "," without space +# e.g.: tcp://127.0.0.1:5556,tcp://127.0.0.1:5557 [ZMQ_Global] #address = tcp://crf.circl.lu:5556 address = tcp://127.0.0.1:5556 diff --git a/bin/packages/modules.cfg b/bin/packages/modules.cfg index 53bbb2a6..dee8baff 100644 --- a/bin/packages/modules.cfg +++ b/bin/packages/modules.cfg @@ -1,5 +1,9 @@ -[Global] +[Mixer] subscribe = ZMQ_Global +publish = Redis_Mixer + +[Global] +subscribe = Redis_Mixer publish = Redis_Global,Redis_ModuleStats [Duplicates] From 97292e08994c105a01d30589cb56353950e63ab9 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 23 Dec 2016 15:44:46 +0100 Subject: [PATCH 71/89] Updated web interface to handle new mixer module. and fixed one dependency bug. --- bin/Global.py | 4 +- bin/Mixer.py | 2 +- var/www/Flasks/Flask_trendingmodules.py | 1 + var/www/static/js/indexjavascript.js | 103 ++++++++++++++++-------- var/www/templates/index.html | 51 +++++++----- 5 files changed, 107 insertions(+), 54 deletions(-) diff --git a/bin/Global.py b/bin/Global.py index a29c3b86..bab45b47 100755 --- a/bin/Global.py +++ b/bin/Global.py @@ -59,7 +59,7 @@ if __name__ == '__main__': if int(time.time() - time_1) > 30: to_print = 'Global; ; ; ;glob Processed {0} paste(s)'.format(processed_paste) print to_print - publisher.info(to_print) + #publisher.info(to_print) time_1 = time.time() processed_paste = 0 time.sleep(1) @@ -72,8 +72,6 @@ if __name__ == '__main__': os.makedirs(dirname) with open(filename, 'wb') as f: - print gzip64encoded - print base64.standard_b64decode(gzip64encoded) f.write(base64.standard_b64decode(gzip64encoded)) p.populate_set_out(filename) processed_paste+=1 diff --git a/bin/Mixer.py b/bin/Mixer.py index e1804263..558cf4ec 100755 --- a/bin/Mixer.py +++ b/bin/Mixer.py @@ -150,7 +150,7 @@ if __name__ == '__main__': else: print "Empty Queues: Waiting..." if int(time.time() - time_1) > refresh_time: - to_print = 'Mixer; ; ; ;mixer_all Processed {0} paste(s) in {1}sec'.format(processed_paste, refresh_time) + to_print = 'Mixer; ; ; ;mixer_all All_feeders Processed {0} paste(s) in {1}sec'.format(processed_paste, refresh_time) print to_print publisher.info(to_print) processed_paste = 0 diff --git a/var/www/Flasks/Flask_trendingmodules.py b/var/www/Flasks/Flask_trendingmodules.py index 73cef7f5..06cb65c3 100644 --- a/var/www/Flasks/Flask_trendingmodules.py +++ b/var/www/Flasks/Flask_trendingmodules.py @@ -6,6 +6,7 @@ ''' import redis import datetime +from Date import Date import flask from flask import Flask, render_template, jsonify, request diff --git a/var/www/static/js/indexjavascript.js b/var/www/static/js/indexjavascript.js index 8d50ea9d..3710e00b 100644 --- a/var/www/static/js/indexjavascript.js +++ b/var/www/static/js/indexjavascript.js @@ -1,10 +1,16 @@ -var time_since_last_pastes_num; +var time_since_last_pastes_num = {}; +var data_for_processed_paste = { "global": [] }; +var list_feeder = ["global"]; +var htmltext_graph_container = "
"; +window.paste_num_tabvar_all = {}; //If we do not received info from global, set pastes_num to 0 function checkIfReceivedData(){ - if ((new Date().getTime() - time_since_last_pastes_num) > 45*1000) - window.paste_num_tabvar = 0; - setTimeout(checkIfReceivedData, 45*1000); + for (i in list_feeder) { + if ((new Date().getTime() - time_since_last_pastes_num[list_feeder[i]]) > 45*1000) + window.paste_num_tabvar_all[list_feeder[i]] = 0; + setTimeout(checkIfReceivedData, 45*1000); + } } setTimeout(checkIfReceivedData, 45*1000); @@ -24,34 +30,41 @@ function update_values() { // Plot and update the number of processed pastes -$(function() { - var data = []; +// BEGIN PROCESSED PASTES var default_minute = (typeof window.default_minute !== "undefined") ? parseInt(window.default_minute) : 10; var totalPoints = 60*parseInt(default_minute); //60s*minute var curr_max = 0; - function getData() { - if (data.length > 0){ - var data_old = data[0]; - data = data.slice(1); - curr_max = curr_max == data_old ? Math.max.apply(null, data) : curr_max; + function getData(dataset) { + var curr_data; + if (data_for_processed_paste[dataset] === undefined) { // create feeder dataset if not exists yet + data_for_processed_paste[dataset] = []; + } + curr_data = data_for_processed_paste[dataset]; + + if (curr_data.length > 0){ + var data_old = curr_data[0]; + curr_data = curr_data.slice(1); + curr_max = curr_max == data_old ? Math.max.apply(null, curr_data) : curr_max; } - while (data.length < totalPoints) { - var y = (typeof window.paste_num_tabvar !== "undefined") ? parseInt(window.paste_num_tabvar) : 0; + while (curr_data.length < totalPoints) { + //var y = (typeof window.paste_num_tabvar_all[dataset] !== "undefined") ? parseInt(window.paste_num_tabvar_all[dataset]) : 0; + var y = (typeof window.paste_num_tabvar_all[dataset] !== "undefined") ? parseInt(window.paste_num_tabvar_all[dataset]) : 0; curr_max = y > curr_max ? y : curr_max; - data.push(y); + curr_data.push(y); } // Zip the generated y values with the x values var res = []; - for (var i = 0; i < data.length; ++i) { - res.push([i, data[i]]) - } + for (var i = 0; i < curr_data.length; ++i) { + res.push([i, curr_data[i]]) + } + data_for_processed_paste[dataset] = curr_data; return res; } var updateInterval = 1000; - var options = { + var options_processed_pastes = { series: { shadowSize: 1 }, lines: { fill: true, fillColor: { colors: [ { opacity: 1 }, { opacity: 0.1 } ] }}, yaxis: { min: 0, max: 40 }, @@ -61,17 +74,19 @@ $(function() { borderWidth: 0 }, }; - var plot = $.plot("#realtimechart", [ getData() ], options); - - function update() { - plot.setData([getData()]); - plot.getOptions().yaxes[0].max = curr_max; - plot.setupGrid(); - plot.draw(); - setTimeout(update, updateInterval); + var total_proc = $.plot("#global", [ getData("global") ], options_processed_pastes); + + function update_processed_pastes(graph, dataset) { + graph.setData([getData(dataset)]); + graph.getOptions().yaxes[0].max = curr_max; + graph.setupGrid(); + graph.draw(); + setTimeout(function(){ update_processed_pastes(graph, dataset); }, updateInterval); } - update(); -}); + update_processed_pastes(total_proc, "global"); + + +// END PROCESSED PASTES function initfunc( csvay, scroot) { window.csv = csvay; @@ -114,10 +129,34 @@ function create_log_table(obj_json) { var chansplit = obj_json.channel.split('.'); var parsedmess = obj_json.data.split(';'); - if (parsedmess[0] == "Global"){ - var paste_processed = parsedmess[4].split(" ")[2]; - window.paste_num_tabvar = paste_processed; - time_since_last_pastes_num = new Date().getTime(); + + if (parsedmess[0] == "Mixer"){ + var feeder = parsedmess[4].split(" ")[1]; + var paste_processed = parsedmess[4].split(" ")[3]; + var msg_type = parsedmess[4].split(" ")[2]; + + if (feeder == "All_feeders"){ + window.paste_num_tabvar_all["global"] = paste_processed; + time_since_last_pastes_num["global"] = new Date().getTime(); + } else { + + if (list_feeder.indexOf(feeder) == -1) { + list_feeder.push(feeder); + //ADD HTML CONTAINER + PLOT THE GRAPH, ADD IT TO A LIST CONTAING THE PLOTED GRAPH + $("#panelbody").append(""+feeder+""); + $("#panelbody").append("
" + htmltext_graph_container.replace("$1", feeder+"Proc") + htmltext_graph_container.replace("$1", feeder+"Dup")+"
"); + var new_feederProc = $.plot("#"+feeder+"Proc", [ getData(feeder+"Proc") ], options_processed_pastes); + options_processed_pastes.colors = ["#edc240"]; + var new_feederDup = $.plot("#"+feeder+"Dup", [ getData(feeder+"Dup") ], options_processed_pastes); + options_processed_pastes.colors = ["#a971ff"]; + update_processed_pastes(new_feederProc, feeder+"Proc"); + update_processed_pastes(new_feederDup, feeder+"Dup"); + } + + var feederName = msg_type == "Duplicated" ? feeder+"Dup" : feeder+"Proc"; + window.paste_num_tabvar_all[feederName] = paste_processed; + time_since_last_pastes_num[feederName] = new Date().getTime(); + } return; } diff --git a/var/www/templates/index.html b/var/www/templates/index.html index 74b45c01..278bc873 100644 --- a/var/www/templates/index.html +++ b/var/www/templates/index.html @@ -52,10 +52,10 @@
- Pastes since {{ default_minute }} min + Total pastes since {{ default_minute }} min
-
+
@@ -91,23 +91,38 @@
-
-
-
- Queues Monitor -
-
-
-
-
-
- +
+
+
+ Feeder(s) Monitor: Processed pastes and filtered duplicated
- -
- -
- +
+ + +
+ +
+ +
+ +
+
+
+ Queues Monitor +
+
+
+
+
+
+
+ +
+ +
+ +
+
From 03dddbc359a420b60d5dfb82597c55cc46da254a Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 23 Dec 2016 16:01:30 +0100 Subject: [PATCH 72/89] Reduced refresh rate of processed_pastes, synchro graphs and adjusted max on each graphs. --- var/www/static/js/indexjavascript.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/var/www/static/js/indexjavascript.js b/var/www/static/js/indexjavascript.js index 3710e00b..5e5d477d 100644 --- a/var/www/static/js/indexjavascript.js +++ b/var/www/static/js/indexjavascript.js @@ -33,7 +33,7 @@ function update_values() { // BEGIN PROCESSED PASTES var default_minute = (typeof window.default_minute !== "undefined") ? parseInt(window.default_minute) : 10; var totalPoints = 60*parseInt(default_minute); //60s*minute - var curr_max = 0; + var curr_max = {"global": 0}; function getData(dataset) { var curr_data; @@ -44,14 +44,14 @@ function update_values() { if (curr_data.length > 0){ var data_old = curr_data[0]; - curr_data = curr_data.slice(1); - curr_max = curr_max == data_old ? Math.max.apply(null, curr_data) : curr_max; + curr_data = curr_data.slice(10); + curr_max[dataset] = curr_max[dataset] == data_old ? Math.max.apply(null, curr_data) : curr_max[dataset]; } while (curr_data.length < totalPoints) { //var y = (typeof window.paste_num_tabvar_all[dataset] !== "undefined") ? parseInt(window.paste_num_tabvar_all[dataset]) : 0; var y = (typeof window.paste_num_tabvar_all[dataset] !== "undefined") ? parseInt(window.paste_num_tabvar_all[dataset]) : 0; - curr_max = y > curr_max ? y : curr_max; + curr_max[dataset] = y > curr_max[dataset] ? y : curr_max[dataset]; curr_data.push(y); } // Zip the generated y values with the x values @@ -63,7 +63,7 @@ function update_values() { return res; } - var updateInterval = 1000; + var updateInterval = 10000; var options_processed_pastes = { series: { shadowSize: 1 }, lines: { fill: true, fillColor: { colors: [ { opacity: 1 }, { opacity: 0.1 } ] }}, @@ -74,16 +74,14 @@ function update_values() { borderWidth: 0 }, }; - var total_proc = $.plot("#global", [ getData("global") ], options_processed_pastes); function update_processed_pastes(graph, dataset) { graph.setData([getData(dataset)]); - graph.getOptions().yaxes[0].max = curr_max; + graph.getOptions().yaxes[0].max = curr_max[dataset]; graph.setupGrid(); graph.draw(); setTimeout(function(){ update_processed_pastes(graph, dataset); }, updateInterval); } - update_processed_pastes(total_proc, "global"); // END PROCESSED PASTES @@ -136,6 +134,8 @@ function create_log_table(obj_json) { var msg_type = parsedmess[4].split(" ")[2]; if (feeder == "All_feeders"){ + var total_proc = $.plot("#global", [ getData("global") ], options_processed_pastes); + update_processed_pastes(total_proc, "global"); window.paste_num_tabvar_all["global"] = paste_processed; time_since_last_pastes_num["global"] = new Date().getTime(); } else { From a18c046deac8863d159482e005f977973b04acef Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Fri, 23 Dec 2016 16:15:05 +0100 Subject: [PATCH 73/89] Fixed bug multiple refresh instances and harmonized interface. --- var/www/static/js/indexjavascript.js | 9 ++++++--- var/www/templates/index.html | 8 ++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/var/www/static/js/indexjavascript.js b/var/www/static/js/indexjavascript.js index 5e5d477d..1f727f9b 100644 --- a/var/www/static/js/indexjavascript.js +++ b/var/www/static/js/indexjavascript.js @@ -1,6 +1,6 @@ var time_since_last_pastes_num = {}; var data_for_processed_paste = { "global": [] }; -var list_feeder = ["global"]; +var list_feeder = []; var htmltext_graph_container = "
"; window.paste_num_tabvar_all = {}; @@ -134,8 +134,11 @@ function create_log_table(obj_json) { var msg_type = parsedmess[4].split(" ")[2]; if (feeder == "All_feeders"){ - var total_proc = $.plot("#global", [ getData("global") ], options_processed_pastes); - update_processed_pastes(total_proc, "global"); + if(list_feeder.indexOf("global") == -1) { + list_feeder.push("global"); + var total_proc = $.plot("#global", [ getData("global") ], options_processed_pastes); + update_processed_pastes(total_proc, "global"); + } window.paste_num_tabvar_all["global"] = paste_processed; time_since_last_pastes_num["global"] = new Date().getTime(); } else { diff --git a/var/www/templates/index.html b/var/www/templates/index.html index 278bc873..90d7134b 100644 --- a/var/www/templates/index.html +++ b/var/www/templates/index.html @@ -91,12 +91,12 @@
-
+
- Feeder(s) Monitor: Processed pastes and filtered duplicated + Feeder(s) Monitor: Processed pastes and filtered duplicated
-
+
@@ -105,7 +105,7 @@
-
+
Queues Monitor From 1c2169d3bcc1687851f0dd0bbc8e1ac0436e2d10 Mon Sep 17 00:00:00 2001 From: Mokaddem Date: Mon, 26 Dec 2016 16:16:44 +0100 Subject: [PATCH 74/89] Improved interface for multiple feeds and refresh_script. --- bin/Mixer.py | 1 + var/www/static/js/indexjavascript.js | 108 ++++++++++++++++++--------- var/www/templates/index.html | 5 +- 3 files changed, 78 insertions(+), 36 deletions(-) diff --git a/bin/Mixer.py b/bin/Mixer.py index 558cf4ec..ddfb1399 100755 --- a/bin/Mixer.py +++ b/bin/Mixer.py @@ -150,6 +150,7 @@ if __name__ == '__main__': else: print "Empty Queues: Waiting..." if int(time.time() - time_1) > refresh_time: + print processed_paste_per_feeder to_print = 'Mixer; ; ; ;mixer_all All_feeders Processed {0} paste(s) in {1}sec'.format(processed_paste, refresh_time) print to_print publisher.info(to_print) diff --git a/var/www/static/js/indexjavascript.js b/var/www/static/js/indexjavascript.js index 1f727f9b..b85f62b0 100644 --- a/var/www/static/js/indexjavascript.js +++ b/var/www/static/js/indexjavascript.js @@ -1,19 +1,25 @@ var time_since_last_pastes_num = {}; -var data_for_processed_paste = { "global": [] }; +var data_for_processed_paste = { }; var list_feeder = []; -var htmltext_graph_container = "
"; window.paste_num_tabvar_all = {}; -//If we do not received info from global, set pastes_num to 0 +//If we do not received info from mixer, set pastes_num to 0 function checkIfReceivedData(){ for (i in list_feeder) { - if ((new Date().getTime() - time_since_last_pastes_num[list_feeder[i]]) > 45*1000) - window.paste_num_tabvar_all[list_feeder[i]] = 0; - setTimeout(checkIfReceivedData, 45*1000); + if(list_feeder[i] == "global"){ + if ((new Date().getTime() - time_since_last_pastes_num[list_feeder[i]]) > 35*1000){ + window.paste_num_tabvar_all[list_feeder[i]] = 0; + } + } else { + if ((new Date().getTime() - time_since_last_pastes_num["Proc"+list_feeder[i]]) > 35*1000){ + window.paste_num_tabvar_all["Proc"+list_feeder[i]] = 0; + window.paste_num_tabvar_all["Dup"+list_feeder[i]] = 0; + } + } } + setTimeout(checkIfReceivedData, 35*1000); } -setTimeout(checkIfReceivedData, 45*1000); function initfunc( csvay, scroot) { window.csv = csvay; @@ -32,24 +38,17 @@ function update_values() { // Plot and update the number of processed pastes // BEGIN PROCESSED PASTES var default_minute = (typeof window.default_minute !== "undefined") ? parseInt(window.default_minute) : 10; - var totalPoints = 60*parseInt(default_minute); //60s*minute + var totalPoints = 2*parseInt(default_minute); //60s*minute var curr_max = {"global": 0}; - - function getData(dataset) { - var curr_data; - if (data_for_processed_paste[dataset] === undefined) { // create feeder dataset if not exists yet - data_for_processed_paste[dataset] = []; - } - curr_data = data_for_processed_paste[dataset]; + function fetch_data(dataset, curr_data, feeder_name) { if (curr_data.length > 0){ var data_old = curr_data[0]; - curr_data = curr_data.slice(10); + curr_data = curr_data.slice(1); curr_max[dataset] = curr_max[dataset] == data_old ? Math.max.apply(null, curr_data) : curr_max[dataset]; } while (curr_data.length < totalPoints) { - //var y = (typeof window.paste_num_tabvar_all[dataset] !== "undefined") ? parseInt(window.paste_num_tabvar_all[dataset]) : 0; var y = (typeof window.paste_num_tabvar_all[dataset] !== "undefined") ? parseInt(window.paste_num_tabvar_all[dataset]) : 0; curr_max[dataset] = y > curr_max[dataset] ? y : curr_max[dataset]; curr_data.push(y); @@ -60,27 +59,61 @@ function update_values() { res.push([i, curr_data[i]]) } data_for_processed_paste[dataset] = curr_data; - return res; + return { label: feeder_name, data: res }; + } + + function getData(dataset_group, graph_type) { + var curr_data; + + var all_res = []; + if (dataset_group == "global") { + if (data_for_processed_paste["global"] === undefined) { // create feeder dataset if not exists yet + data_for_processed_paste["global"] = []; + } + curr_data = data_for_processed_paste["global"]; + all_res.push(fetch_data("global", curr_data, "global")); + } else { + + for(d_i in list_feeder) { + if(list_feeder[d_i] == "global") { + continue; + } + + dataset = graph_type+list_feeder[d_i]; + if (data_for_processed_paste[dataset] === undefined) { // create feeder dataset if not exists yet + data_for_processed_paste[dataset] = []; + } + curr_data = data_for_processed_paste[dataset]; + all_res.push(fetch_data(dataset, curr_data, list_feeder[d_i])); + } + + } + return all_res; } var updateInterval = 10000; var options_processed_pastes = { - series: { shadowSize: 1 }, - lines: { fill: true, fillColor: { colors: [ { opacity: 1 }, { opacity: 0.1 } ] }}, + series: { shadowSize: 0 , + lines: { fill: true, fillColor: { colors: [ { opacity: 1 }, { opacity: 0.1 } ] }} + }, yaxis: { min: 0, max: 40 }, - colors: ["#a971ff"], + xaxis: { ticks: [[0, 0], [2, 1], [4, 2], [6, 3], [8, 4], [10, 5], [12, 6], [14, 7], [16, 8], [18, 9], [20, 10]] }, grid: { tickColor: "#dddddd", borderWidth: 0 }, + legend: { + show: true, + position: "nw", + } }; - function update_processed_pastes(graph, dataset) { - graph.setData([getData(dataset)]); + function update_processed_pastes(graph, dataset, graph_type) { + graph.setData(getData(dataset, graph_type)); graph.getOptions().yaxes[0].max = curr_max[dataset]; graph.setupGrid(); graph.draw(); - setTimeout(function(){ update_processed_pastes(graph, dataset); }, updateInterval); + setTimeout(function(){ update_processed_pastes(graph, dataset, graph_type); }, updateInterval); } @@ -136,8 +169,20 @@ function create_log_table(obj_json) { if (feeder == "All_feeders"){ if(list_feeder.indexOf("global") == -1) { list_feeder.push("global"); - var total_proc = $.plot("#global", [ getData("global") ], options_processed_pastes); + + options_processed_pastes.legend.show = false; + var total_proc = $.plot("#global", [ getData("global", null) ], options_processed_pastes); + options_processed_pastes.legend.show = true; + options_processed_pastes.series.lines = { show: true }; + data_for_processed_paste["global"] = Array(totalPoints+1).join(0).split(''); + + var feederProc = $.plot("#Proc_feeder", [ getData(feeder, "Proc") ], options_processed_pastes); + var feederDup = $.plot("#Dup_feeder", [ getData(feeder, "Dup") ], options_processed_pastes); + + update_processed_pastes(feederProc, "feeder", "Proc"); + update_processed_pastes(feederDup, "feeder", "Dup"); update_processed_pastes(total_proc, "global"); + setTimeout(checkIfReceivedData, 45*1000); } window.paste_num_tabvar_all["global"] = paste_processed; time_since_last_pastes_num["global"] = new Date().getTime(); @@ -145,18 +190,11 @@ function create_log_table(obj_json) { if (list_feeder.indexOf(feeder) == -1) { list_feeder.push(feeder); - //ADD HTML CONTAINER + PLOT THE GRAPH, ADD IT TO A LIST CONTAING THE PLOTED GRAPH - $("#panelbody").append(""+feeder+""); - $("#panelbody").append("
" + htmltext_graph_container.replace("$1", feeder+"Proc") + htmltext_graph_container.replace("$1", feeder+"Dup")+"
"); - var new_feederProc = $.plot("#"+feeder+"Proc", [ getData(feeder+"Proc") ], options_processed_pastes); - options_processed_pastes.colors = ["#edc240"]; - var new_feederDup = $.plot("#"+feeder+"Dup", [ getData(feeder+"Dup") ], options_processed_pastes); - options_processed_pastes.colors = ["#a971ff"]; - update_processed_pastes(new_feederProc, feeder+"Proc"); - update_processed_pastes(new_feederDup, feeder+"Dup"); + data_for_processed_paste["Proc"+feeder] = Array(totalPoints+1).join(0).split(''); + data_for_processed_paste["Dup"+feeder] = Array(totalPoints+1).join(0).split(''); } - var feederName = msg_type == "Duplicated" ? feeder+"Dup" : feeder+"Proc"; + var feederName = msg_type == "Duplicated" ? "Dup"+feeder : "Proc"+feeder; window.paste_num_tabvar_all[feederName] = paste_processed; time_since_last_pastes_num[feederName] = new Date().getTime(); } diff --git a/var/www/templates/index.html b/var/www/templates/index.html index 90d7134b..a943369e 100644 --- a/var/www/templates/index.html +++ b/var/www/templates/index.html @@ -17,6 +17,7 @@ +