Commit 8e477a68 authored by Alain Takoudjou's avatar Alain Takoudjou Committed by Tristan Cavelier

monitor: show ressource consumption information

parent 0c5d67eb
...@@ -16,6 +16,8 @@ parts = ...@@ -16,6 +16,8 @@ parts =
monitor-template monitor-template
rss-bin rss-bin
run-apachedex run-apachedex
collect-tools
log-tools
[monitor-eggs] [monitor-eggs]
recipe = zc.recipe.egg recipe = zc.recipe.egg
...@@ -44,7 +46,7 @@ recipe = slapos.recipe.template ...@@ -44,7 +46,7 @@ recipe = slapos.recipe.template
url = ${:_profile_base_location_}/monitor.cfg.in url = ${:_profile_base_location_}/monitor.cfg.in
output = ${buildout:directory}/monitor.cfg output = ${buildout:directory}/monitor.cfg
filename = monitor.cfg filename = monitor.cfg
md5sum = 39c0b45d08399cf4a74fa586465fe8dc md5sum = 05ed0063a8de43d3711b23605cdab4d7
mode = 0644 mode = 0644
[monitor-bin] [monitor-bin]
...@@ -94,10 +96,18 @@ mode = 0644 ...@@ -94,10 +96,18 @@ mode = 0644
recipe = hexagonit.recipe.download recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/webfile-directory/${:filename} url = ${:_profile_base_location_}/webfile-directory/${:filename}
download-only = true download-only = true
md5sum = 223d8d98dd557ebc215be71f154629e7 md5sum = d2b85ac31cbbf49c78dc51e90ddf305f
filename = logfile.cgi.in filename = logfile.cgi.in
mode = 0644 mode = 0644
[ressources-cgi]
recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/webfile-directory/${:filename}
download-only = true
md5sum = 739a6e470ef174b9d0b523aa349860cb
filename = ressources.cgi.in
mode = 0644
[status-cgi] [status-cgi]
recipe = hexagonit.recipe.download recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/webfile-directory/${:filename} url = ${:_profile_base_location_}/webfile-directory/${:filename}
...@@ -159,6 +169,14 @@ md5sum = 7b38b3ab2d6c9ab657c8faf8e3f9b190 ...@@ -159,6 +169,14 @@ md5sum = 7b38b3ab2d6c9ab657c8faf8e3f9b190
filename = logTools.py filename = logTools.py
mode = 0644 mode = 0644
[collect-tools]
recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/${:filename}
download-only = true
md5sum = 3577a9bb13d49897a7e15422afdb11f4
filename = collect.py
mode = 0644
[errorlog-2rss] [errorlog-2rss]
recipe = hexagonit.recipe.download recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/${:filename} url = ${:_profile_base_location_}/${:filename}
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2014 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import sqlite3
import os
from time import strftime
from datetime import datetime, timedelta
class Database:
database_name = "collector.db"
table_list = ["user", "computer", "system", "disk", \
"temperature", "heating"]
def __init__(self, directory = None):
assert self.database_name is not None
self.uri = os.path.join(directory, self.database_name)
self.connection = None
self.cursor = None
def connect(self):
self.connection = sqlite3.connect(self.uri)
self.cursor = self.connection.cursor()
def close(self):
assert self.connection is not None
self.cursor.close()
self.connection.close()
def _execute(self, sql):
assert self.connection is not None
return self.cursor.execute(sql)
def select(self, table, date=None, columns="*", where=None):
""" Query database for a full table information """
if date is not None:
where_clause = " WHERE date = '%s' " % date
else:
where_clause = ""
if where is not None:
if where_clause == "":
where_clause += " WHERE 1 = 1 "
where_clause += " AND %s " % where
select_sql = "SELECT %s FROM %s %s " % (columns, table, where_clause)
return self._execute(select_sql)
def getPartitionCPULoadAverage(self, partition_id, date_scope):
self.connect()
query_result_cursor = self.select("user", date_scope,
columns="SUM(cpu_percent)",
where="partition = '%s'" % partition_id)
cpu_percent_sum = zip(*query_result_cursor)
if len(cpu_percent_sum) and cpu_percent_sum[0][0] is None:
return
query_result_cursor = self.select("user", date_scope,
columns="COUNT(DISTINCT time)",
where="partition = '%s'" % partition_id)
sample_amount = zip(*query_result_cursor)
self.close()
if len(sample_amount) and len(cpu_percent_sum):
return round(cpu_percent_sum[0][0]/sample_amount[0][0], 2)
def getPartitionUsedMemoryAverage(self, partition_id, date_scope):
self.connect()
query_result_cursor = self.select("user", date_scope,
columns="SUM(memory_rss)",
where="partition = '%s'" % partition_id)
memory_sum = zip(*query_result_cursor)
if len(memory_sum) and memory_sum[0][0] is None:
return
query_result_cursor = self.select("user", date_scope,
columns="COUNT(DISTINCT time)",
where="partition = '%s'" % partition_id)
sample_amount = zip(*query_result_cursor)
self.close()
if len(sample_amount) and len(memory_sum):
return round(memory_sum[0][0]/sample_amount[0][0], 2)
def getPartitionConsumption(self, partition_id, where=""):
self.connect()
comsumption_list = []
if where != "":
where = "and %s" % where
date_scope = datetime.now().strftime('%Y-%m-%d')
min_time = (datetime.now() - timedelta(minutes=1)).strftime('%H:%M:00')
max_time = (datetime.now() - timedelta(minutes=1)).strftime('%H:%M:59')
sql_query = """select count(pid), SUM(cpu_percent) as cpu_result, SUM(cpu_time),
MAX(cpu_num_threads), SUM(memory_percent), SUM(memory_rss), pid from user
where date='%s' and partition='%s' and (time between '%s' and '%s') %s
group by pid order by cpu_result desc""" % (
date_scope, partition_id, min_time, max_time, where)
query_result = self._execute(sql_query)
for result in query_result:
count = int(result[0])
if not count > 0:
continue
comsumption_list.append([result[6], round((result[1]/count), 2),
round((result[2]/count), 2),
round(result[3], 2), round((result[4]/count), 2),
round((result[5]/count), 2)])
self.close()
return comsumption_list
def getPartitionComsumptionStatus(self, partition_id, where=""):
self.connect()
if where != "":
where = " and %s" % where
date_scope = datetime.now().strftime('%Y-%m-%d')
min_time = (datetime.now() - timedelta(minutes=1)).strftime('%H:%M:00')
max_time = (datetime.now() - timedelta(minutes=1)).strftime('%H:%M:59')
sql_query = """select count(pid), SUM(cpu_percent), SUM(cpu_time),
SUM(cpu_num_threads), SUM(memory_percent), SUM(memory_rss) from user where
date='%s' and partition='%s' and (time between '%s' and '%s') %s""" % (
date_scope, partition_id, min_time, max_time, where)
query_result = self._execute(sql_query)
result_list = zip(*query_result)
self.close()
if len(result_list):
result = result_list #[0]
return {'total_process': result[0][0],
'cpu_percent': round(result[1][0], 2),
'cpu_time': round(result[2][0], 2),
'cpu_num_threads': round(result[3][0], 2),
'memory_percent': round(result[4][0], 2),
'memory_rss': round(result[5][0], 2)}
return None
...@@ -195,6 +195,19 @@ context = ...@@ -195,6 +195,19 @@ context =
[log-folder-cgi] [log-folder-cgi]
log-folder = $${monitor-directory:log} log-folder = $${monitor-directory:log}
[deploy-ressource-monitoring-cgi]
recipe = slapos.recipe.template:jinja2
template = ${ressources-cgi:location}/${ressources-cgi:filename}
rendered = $${monitor-directory:monitoring-cgi}/$${:filename}
filename = ressources.cgi
mode = 0744
# XXX - We need to find a proper way to set db_path here, maybe by using zero-parameters ??
context =
key monitor_bin monitor-parameters:executable
raw python_executable ${buildout:directory}/bin/${extra-eggs:interpreter}
key root_folder buildout:directory
raw db_path /srv/slapgrid/var/data-log/
[make-rss] [make-rss]
recipe = slapos.recipe.template:jinja2 recipe = slapos.recipe.template:jinja2
template = ${make-rss-script:output} template = ${make-rss-script:output}
...@@ -370,7 +383,7 @@ certificate = $${ca-httpd:cert-file} ...@@ -370,7 +383,7 @@ certificate = $${ca-httpd:cert-file}
key = $${ca-httpd:key-file} key = $${ca-httpd:key-file}
[httpd-environment] [httpd-environment]
PYTHONPATH = ${log-tools:location} PYTHONPATH = ${log-tools:location}:${collect-tools:location}
[monitor-httpd-configuration-file] [monitor-httpd-configuration-file]
recipe = slapos.recipe.template:jinja2 recipe = slapos.recipe.template:jinja2
......
...@@ -201,7 +201,7 @@ else: ...@@ -201,7 +201,7 @@ else:
<input type="hidden" name="size" id="size" value="%s" /> <input type="hidden" name="size" id="size" value="%s" />
</form> </form>
<div class="box"> <div class="box">
<h2 class="head">Tail: %s </h2> <h2 class="head">%s: %s </h2>
<div class="button"> <div class="button">
<button type="submit" class="pure-button pure-button-primary" id="return">Return</button> <button type="submit" class="pure-button pure-button-primary" id="return">Return</button>
<button type="submit" class="pure-button pure-button-primary" id="reload">Refresh</button> <button type="submit" class="pure-button pure-button-primary" id="reload">Refresh</button>
...@@ -209,7 +209,8 @@ else: ...@@ -209,7 +209,8 @@ else:
<div style='clear:both'></div> <div style='clear:both'></div>
<textarea id="logcontent" readonly>%s</textarea> <textarea id="logcontent" readonly>%s</textarea>
</div> </div>
""" % (script_path, logpath, action, pattern, size, title, log_content) """ % (script_path, logpath, action, pattern, size, action.upper(),
title, log_content)
if pattern: if pattern:
print "<p>Pattern string is: %s</p>" % pattern print "<p>Pattern string is: %s</p>" % pattern
print """ print """
......
#!{{ python_executable }}
import cgi
import cgitb
import json
import os
import pwd
from time import strftime
from datetime import datetime
import collect
cgitb.enable(display=0, logdir="/tmp/cgi.log")
form = cgi.FieldStorage()
db_path = "{{ db_path }}"
action = form.getvalue("action", "")
home = "{{ root_folder }}".strip()
if action:
db = collect.Database(directory=db_path)
stat_info = os.stat(home)
partition_user = pwd.getpwuid(stat_info.st_uid)[0]
result_dict = {}
date_scope = datetime.now().strftime('%Y-%m-%d')
if action == "refresh":
result_dict['consumption'] = db.getPartitionConsumption(partition_user)
result_dict['status'] = db.getPartitionComsumptionStatus(partition_user)
result_dict['cpu-load'] = db.getPartitionCPULoadAverage(
partition_user, date_scope)
result_dict['memory'] = db.getPartitionUsedMemoryAverage(
partition_user, date_scope)
print json.dumps(result_dict)
else:
print """<html><head>
<link rel="stylesheet" href="static/pure-min.css">
<link rel="stylesheet" href="static/style.css">
<script src="static/jquery-1.10.2.min.js"></script>
<style type="text/css">
.tg {border-collapse:collapse;border-spacing:0;border-color:#fff;border-width:1px;border-style:solid; width:100%}
.tg td{font-family:Arial, sans-serif;font-size:14px;padding:10px 5px;border-style:solid;border-width:0px;overflow:hidden;word-break:normal;border-color:#ccc;color:#333;background:transparent;}
.tg th{font-family:Arial, sans-serif;font-size:14px;font-weight:normal;padding:10px 5px;border-style:solid;border-width:0px;overflow:hidden;word-break:normal;border-color:#ccc;color:#333;background-color:#f0f0f0;}
.tg tr{background-color:#fff;}
.tg .tg-0ord{text-align:right}
.tg .tg-s6z2 td{text-align:center}
.tg .tg-zapm{background-color:#f9f9f9;}
.tg .tg-4eph{background-color:#f9f9f9}
.head{
background-color:#0078e7;
border:0px solid #ffffff;
text-align:left;
border-width:0px 0px 1px 1px;
font-size:18px;
font-family:Helvetica;
font-weight:normal;
color:#ffffff;
display: block;
padding: 10px;
margin: 0;
}
.box{
border: 1px solid #e8eaed;
padding: 5px;
}
</style>
<script language="javascript" type="text/javascript">
$(document).ready(function () {
var send = false;
autoRefresh();
function autoRefresh() {
refresh();
setTimeout(function(){ autoRefresh(); }, 60000);
}
function refresh() {
if (send) { return}
var dataPost = {'posting-script': "monitoring/ressources.cgi",
action: "refresh"};
send = true;
$("#msg").fadeIn();
$.ajax({
type: "POST",
url: '/index.cgi',
data: dataPost
})
.done(function(data) {
var result = JSON.parse(data);
var consump = result['consumption'], line = "", line2 = "";
var table1 = "", table1="", table3="", klass="";
table2 = '<tr><th class="tg-s6z2">Total Process</th>';
table2 += '<th class="tg-s6z2">CPU %</th><th class="tg-s6z2">CPU Time</th>';
table2 += '<th class="tg-s6z2">Threads</th><th class="tg-s6z2">Memory Usage</th>';
table2 += '<th class="tg-s6z2">Memory %</th></tr>';
table1 = '<tr><th class="tg-s6z2">Process PID</th>';
table1 += '<th class="tg-s6z2">CPU %</th><th class="tg-s6z2">CPU Time</th>';
table1 += '<th class="tg-s6z2">Threads</th><th class="tg-s6z2">Memory Usage</th>';
table1 += '<th class="tg-s6z2">Memory %</th></tr>';
table3 = '<tr><th class="tg-s6z2">CPU Load Average</th>';
table3 += '<th class="tg-s6z2">Memory Consumption Average</th></tr>';
line2 = "<tr class='tg-4eph tg-s6z2'>"
line2 += "<td>" + result['status']['total_process'] + "</td>";
line2 += "<td>" + result['status']['cpu_percent'] + "</td>";
line2 += "<td>" + result['status']['cpu_time'] + "</td>";
line2 += "<td>" + result['status']['cpu_num_threads'] + "</td>";
line2 += "<td>" + result['status']['memory_rss'] + "</td>";
line2 += "<td>" + result['status']['memory_percent'] + "</td>";
line2 += "</tr>";
for (var i=0; i<consump.length; i++) {
if (klass === "") {klass = 'tg-4eph ';}
else {klass = "";}
line += "<tr class='" + klass + "tg-s6z2'>"
line += "<td>" + consump[i][0] + "</td>";
line += "<td>" + consump[i][1] + "</td>";
line += "<td>" + consump[i][2] + "</td>";
line += "<td>" + consump[i][3] + "</td>";
line += "<td>" + consump[i][5] + "</td>";
line += "<td>" + consump[i][4] + "</td>";
line += "</tr>";
}
table3 += "<tr class='tg-4eph tg-s6z2'>"
table3 += "<td>" + result['cpu-load'] + "</td>";
table3 += "<td>" + result['memory'] + "</td></tr></table>";
$("#box3").html(table3);
$("#box2").html(table2 + line2 + '</table>');
$("#box1").html(table1 + line + '</table>');
})
.fail(function(jqXHR, exception) {
$("#error").html(jqXHR);
})
.always(function() {
send = false;
$("#msg").fadeOut();
});
}
$("#refresh").click(function() {
refresh();
});
});
</script>
</head><body>
<h1>Computer partition ressources monitoring</h1>
<div style="width:850px; padding: 10px 0;">
<div style='float:left; width: 500px'>
<table class="tg" id="box3">
<tr>
<th class="tg-s6z2">CPU Load Average</th>
<th class="tg-s6z2">Memory Consumption Average</th>
</tr>
</table>
</div>
<div style='float:left; padding-left:50px;'>
<button type="button" class="pure-button pure-button-primary" id="refresh">Refresh data</button>
<span style="padding: 5px; color:rgb(217, 39, 39); display:none" id="msg">Loading data...</span>
<br/>
<p>Note: Data are refreshed every minutes.</p>
</div>
</div>
<div style='clear:both'></div>
<h2>Total ressources consumption for partition (last minute)</h2>
<table class="tg" id="box2">
<tr>
<th class="tg-s6z2">Total Process</th>
<th class="tg-s6z2">CPU %</th>
<th class="tg-s6z2">CPU Time</th>
<th class="tg-s6z2">Threads</th>
<th class="tg-s6z2">Memory Usage</th>
<th class="tg-s6z2">Memory %</th>
</tr>
</table>
<h2>ressources consumption for partition by process pid (last minute)</h2>
<table class="tg" id="box1">
<tr>
<th class="tg-s6z2">Process PID</th>
<th class="tg-s6z2">CPU %</th>
<th class="tg-s6z2">CPU Time</th>
<th class="tg-s6z2">Threads</th>
<th class="tg-s6z2">Memory Usage</th>
<th class="tg-s6z2">Memory %</th>
</tr>
</table>
<p id="error"></p>
</body></html>"""
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment