Commit 49fe4023 authored by Tristan Cavelier's avatar Tristan Cavelier

Add new check_user_memory promise

parent 3cfd6c69
#!/usr/bin/env python
"""
Check user memory usage according to a given threshold.
"""
import sys
import os
import argparse
from datetime import datetime, timedelta
from slapos.collect.db import Database
def escapeSqlStringValue(string):
return string.replace("'", "\\'")
def getMemoryInfo(database_path, moment, user):
"""
Gives `user` memory info dict at `moment`.
ex: for moment = 2017-08-13 01:02:03
it searches the results from 01:00:02 to 01:02:03
and take the latest one.
"""
date_str = moment.strftime("%Y-%m-%d")
# time is `(moment-1min) +/- 1min`
time_from_str = (moment - timedelta(minutes=2) - timedelta(seconds=1)).strftime("%H:%M:%S")
time_to_str = moment.strftime("%H:%M:%S")
if time_from_str > time_to_str:
# "23:59:59" > "00:02:00"
time_from_str = "00:00:00"
escaped_user = escapeSqlStringValue(user)
memory_info = {}
database = Database(database_path)
try:
database.connect()
result = list(database.select(
table="user",
date=date_str,
columns="memory_percent, memory_rss",
where="partition = '{}' AND time BETWEEN '{}' AND '{}'".format(escaped_user, time_from_str, time_to_str),
order="time DESC",
limit=1,
))
if len(result) == 0:
return (None, "No result found in collector.db.")
memory_info["percent"] = result[0][0]
memory_info["byte"] = int(result[0][1]) # in byte
finally:
database.close()
return (memory_info, "")
def checkMemoryUsage(database_path, moment, user, threshold, unit="byte"):
if unit not in ("byte", "percent"):
raise ValueError("invalid unit")
memory_info, error = getMemoryInfo(database_path, moment, user)
if error:
return (False, "error", error)
if unit == "byte":
if memory_info["byte"] <= threshold:
return (True, "User memory usage: {byte}B ({percent:.1f}%)".format(**memory_info), "")
return (False, "High user memory usage: {byte}B ({percent:.1f}%)".format(**memory_info), "")
else: # if unit == "percent":
if memory_info["percent"] <= threshold:
return (True, "User memory usage: {percent:.1f}% ({byte}B)".format(**memory_info), "")
return (False, "High user memory usage: {percent:.1f}% ({byte}B)".format(**memory_info), "")
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--collectordb", required=True,
help="the directory path of the 'collector.db' file.")
parser.add_argument("--user", required=True)
parser.add_argument("--threshold", required=True, type=float)
parser.add_argument("--unit", default="byte", choices=("byte", "percent"))
args = parser.parse_args()
database_path = args.collectordb
# --collectordb : can also be the path of 'collector.db' itself
if os.path.basename(database_path) == "collector.db":
database_path = os.path.dirname(database_path)
result, message, error = checkMemoryUsage(
database_path,
datetime.now(),
user=args.user,
threshold=args.threshold,
unit=args.unit,
)
if error:
print error
return 0
print message
return 0 if result else 1
if __name__ == "__main__":
sys.exit(main())
......@@ -3,6 +3,7 @@ BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS computer (cpu_num_core real, cpu_frequency real, cpu_type text, memory_size real, memory_type text, partition_list text, date text, time text, reported integer NULL DEFAULT 0);
INSERT INTO "computer" VALUES(12.0,0.0,'0',33705312256.0,'0','/dev/md0=280665088;/dev/md1=19534663680;/dev/md2=463722799104','2017-09-15','00:00:04',1);
CREATE TABLE IF NOT EXISTS user (partition text, pid real, process text, cpu_percent real, cpu_time real, cpu_num_threads real, memory_percent real, memory_rss real, io_rw_counter real, io_cycles_counter real, date text, time text, reported integer NULL DEFAULT 0);
INSERT INTO "user" VALUES('slaptestuser1',111.0,'process1',22.0,333.0,4.0,87.65432099725093,29544162598.0,7.0,8.0,'2017-09-15','00:01:09',0);
CREATE TABLE IF NOT EXISTS folder (partition text, disk_used real, date text, time text, reported integer NULL DEFAULT 0);
CREATE TABLE IF NOT EXISTS disk (partition text, used text, free text, mountpoint text, date text, time text, reported integer NULL DEFAULT 0);
CREATE TABLE IF NOT EXISTS temperature (sensor_id name, temperature real, alarm integer, date text, time text, reported integer NULL DEFAULT 0);
......
##############################################################################
#
# Copyright (c) 2018 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 General Public License
# as published by the Free Software Foundation; either version 3
# 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 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 unittest
import os
import sqlite3
from datetime import datetime
from slapos.test.promise import data
from slapos.promise.check_user_memory import getMemoryInfo, checkMemoryUsage
no_result_message = "No result found in collector.db."
class TestUserMemory(unittest.TestCase):
def setUp(self):
self.base_path = "/".join(data.__file__.split("/")[:-1])
self.status = "ok"
self.db_file = '/tmp/collector.db'
self.db_dir = os.path.dirname(self.db_file)
# populate db
self.conn = sqlite3.connect(self.db_file)
f = open(self.base_path+"/memtest.sql")
sql = f.read()
self.conn.executescript(sql)
self.conn.close()
def test_check_memory(self):
self.assertEquals(
({
'byte': 29544162598,
'percent': 87.65432099725093,
}, ""),
getMemoryInfo(
self.db_dir,
datetime(2017, 9, 15, 0, 2, 0),
'slaptestuser1',
),
)
self.assertEquals(
(True, "User memory usage: 29544162598B (87.7%)", ""),
checkMemoryUsage(
self.db_dir,
datetime(2017, 9, 15, 0, 2, 0),
'slaptestuser1',
threshold=29600000000,
),
)
self.assertEquals(
(True, "User memory usage: 87.7% (29544162598B)", ""),
checkMemoryUsage(
self.db_dir,
datetime(2017, 9, 15, 0, 2, 0),
'slaptestuser1',
threshold=88,
unit="percent",
),
)
self.assertEquals(
(False, "High user memory usage: 29544162598B (87.7%)", ""),
checkMemoryUsage(
self.db_dir,
datetime(2017, 9, 15, 0, 2, 0),
'slaptestuser1',
threshold=29500000000,
),
)
self.assertEquals(
(False, "High user memory usage: 87.7% (29544162598B)", ""),
checkMemoryUsage(
self.db_dir,
datetime(2017, 9, 15, 0, 2, 0),
'slaptestuser1',
threshold=87,
unit="percent",
),
)
def test_check_memory_with_unavailable_dates(self):
self.assertEquals(
(False, "error", no_result_message),
checkMemoryUsage(self.db_dir, datetime(2017, 9, 14, 18, 0 , 0), "slaptestuser1", 1.0),
)
def tearDown(self):
if os.path.exists(self.db_file):
os.remove(self.db_file)
if __name__ == '__main__':
unittest.main()
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