|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
# Copyright 2010 - 2017 RhodeCode GmbH and the AppEnlight project authors
|
|
|
#
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
# You may obtain a copy of the License at
|
|
|
#
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
#
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
# See the License for the specific language governing permissions and
|
|
|
# limitations under the License.
|
|
|
|
|
|
import logging
|
|
|
import os
|
|
|
import pkg_resources
|
|
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
|
|
import psutil
|
|
|
import redis
|
|
|
|
|
|
from pyramid.view import view_config
|
|
|
from appenlight.models import DBSession
|
|
|
from appenlight.models import Datastores
|
|
|
from appenlight.lib.redis_keys import REDIS_KEYS
|
|
|
|
|
|
|
|
|
def bytes2human(total):
|
|
|
giga = 1024.0 ** 3
|
|
|
mega = 1024.0 ** 2
|
|
|
kilo = 1024.0
|
|
|
if giga <= total:
|
|
|
return "{:0.1f}G".format(total / giga)
|
|
|
elif mega <= total:
|
|
|
return "{:0.1f}M".format(total / mega)
|
|
|
else:
|
|
|
return "{:0.1f}K".format(total / kilo)
|
|
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
route_name="section_view",
|
|
|
match_param=["section=admin_section", "view=system"],
|
|
|
renderer="json",
|
|
|
permission="root_administration",
|
|
|
)
|
|
|
def system(request):
|
|
|
current_time = datetime.utcnow().replace(second=0, microsecond=0) - timedelta(
|
|
|
minutes=1
|
|
|
)
|
|
|
# global app counter
|
|
|
processed_reports = request.registry.redis_conn.get(
|
|
|
REDIS_KEYS["counters"]["reports_per_minute"].format(current_time)
|
|
|
)
|
|
|
processed_reports = int(processed_reports) if processed_reports else 0
|
|
|
processed_logs = request.registry.redis_conn.get(
|
|
|
REDIS_KEYS["counters"]["logs_per_minute"].format(current_time)
|
|
|
)
|
|
|
processed_logs = int(processed_logs) if processed_logs else 0
|
|
|
processed_metrics = request.registry.redis_conn.get(
|
|
|
REDIS_KEYS["counters"]["metrics_per_minute"].format(current_time)
|
|
|
)
|
|
|
processed_metrics = int(processed_metrics) if processed_metrics else 0
|
|
|
|
|
|
waiting_reports = 0
|
|
|
waiting_logs = 0
|
|
|
waiting_metrics = 0
|
|
|
waiting_other = 0
|
|
|
|
|
|
if "redis" in request.registry.settings["celery.broker_type"]:
|
|
|
redis_client = redis.StrictRedis.from_url(
|
|
|
request.registry.settings["celery.broker_url"]
|
|
|
)
|
|
|
waiting_reports = redis_client.llen("reports")
|
|
|
waiting_logs = redis_client.llen("logs")
|
|
|
waiting_metrics = redis_client.llen("metrics")
|
|
|
waiting_other = redis_client.llen("default")
|
|
|
|
|
|
# process
|
|
|
def replace_inf(val):
|
|
|
return val if val != psutil.RLIM_INFINITY else "unlimited"
|
|
|
|
|
|
p = psutil.Process()
|
|
|
fd = p.rlimit(psutil.RLIMIT_NOFILE)
|
|
|
memlock = p.rlimit(psutil.RLIMIT_MEMLOCK)
|
|
|
self_info = {
|
|
|
"fds": {"soft": replace_inf(fd[0]), "hard": replace_inf(fd[1])},
|
|
|
"memlock": {"soft": replace_inf(memlock[0]), "hard": replace_inf(memlock[1])},
|
|
|
}
|
|
|
|
|
|
# disks
|
|
|
disks = []
|
|
|
for part in psutil.disk_partitions(all=False):
|
|
|
if os.name == "nt":
|
|
|
if "cdrom" in part.opts or part.fstype == "":
|
|
|
continue
|
|
|
usage = psutil.disk_usage(part.mountpoint)
|
|
|
disks.append(
|
|
|
{
|
|
|
"device": part.device,
|
|
|
"total": bytes2human(usage.total),
|
|
|
"used": bytes2human(usage.used),
|
|
|
"free": bytes2human(usage.free),
|
|
|
"percentage": int(usage.percent),
|
|
|
"mountpoint": part.mountpoint,
|
|
|
"fstype": part.fstype,
|
|
|
}
|
|
|
)
|
|
|
|
|
|
# memory
|
|
|
memory_v = psutil.virtual_memory()
|
|
|
memory_s = psutil.swap_memory()
|
|
|
|
|
|
memory = {
|
|
|
"total": bytes2human(memory_v.total),
|
|
|
"available": bytes2human(memory_v.available),
|
|
|
"percentage": memory_v.percent,
|
|
|
"used": bytes2human(memory_v.used),
|
|
|
"free": bytes2human(memory_v.free),
|
|
|
"active": bytes2human(memory_v.active),
|
|
|
"inactive": bytes2human(memory_v.inactive),
|
|
|
"buffers": bytes2human(memory_v.buffers),
|
|
|
"cached": bytes2human(memory_v.cached),
|
|
|
"swap_total": bytes2human(memory_s.total),
|
|
|
"swap_used": bytes2human(memory_s.used),
|
|
|
}
|
|
|
|
|
|
# load
|
|
|
system_load = os.getloadavg()
|
|
|
|
|
|
# processes
|
|
|
min_mem = 1024 * 1024 * 40 # 40MB
|
|
|
process_info = []
|
|
|
for p in psutil.process_iter():
|
|
|
mem_used = p.memory_info().rss
|
|
|
if mem_used < min_mem:
|
|
|
continue
|
|
|
process_info.append(
|
|
|
{
|
|
|
"owner": p.username(),
|
|
|
"pid": p.pid,
|
|
|
"cpu": round(p.cpu_percent(interval=0), 1),
|
|
|
"mem_percentage": round(p.memory_percent(), 1),
|
|
|
"mem_usage": bytes2human(mem_used),
|
|
|
"name": p.name(),
|
|
|
"command": " ".join(p.cmdline()),
|
|
|
}
|
|
|
)
|
|
|
process_info = sorted(process_info, key=lambda x: x["mem_percentage"], reverse=True)
|
|
|
|
|
|
# pg tables
|
|
|
|
|
|
db_size_query = """
|
|
|
SELECT tablename, pg_total_relation_size(tablename::text) size
|
|
|
FROM pg_tables WHERE tablename NOT LIKE 'pg_%' AND
|
|
|
tablename NOT LIKE 'sql_%' ORDER BY size DESC;"""
|
|
|
|
|
|
db_tables = []
|
|
|
for row in DBSession.execute(db_size_query):
|
|
|
db_tables.append(
|
|
|
{"size_human": bytes2human(row.size), "table_name": row.tablename}
|
|
|
)
|
|
|
|
|
|
# es indices
|
|
|
es_indices = []
|
|
|
result = Datastores.es.indices.stats(metric=["store, docs"])
|
|
|
for ix, stats in result["indices"].items():
|
|
|
size = stats["primaries"]["store"]["size_in_bytes"]
|
|
|
es_indices.append({"name": ix, "size": size, "size_human": bytes2human(size)})
|
|
|
|
|
|
# packages
|
|
|
|
|
|
packages = (
|
|
|
{"name": p.project_name, "version": p.version}
|
|
|
for p in pkg_resources.working_set
|
|
|
)
|
|
|
|
|
|
return {
|
|
|
"db_tables": db_tables,
|
|
|
"es_indices": sorted(es_indices, key=lambda x: x["size"], reverse=True),
|
|
|
"process_info": process_info,
|
|
|
"system_load": system_load,
|
|
|
"disks": disks,
|
|
|
"memory": memory,
|
|
|
"packages": sorted(packages, key=lambda x: x["name"].lower()),
|
|
|
"current_time": current_time,
|
|
|
"queue_stats": {
|
|
|
"processed_reports": processed_reports,
|
|
|
"processed_logs": processed_logs,
|
|
|
"processed_metrics": processed_metrics,
|
|
|
"waiting_reports": waiting_reports,
|
|
|
"waiting_logs": waiting_logs,
|
|
|
"waiting_metrics": waiting_metrics,
|
|
|
"waiting_other": waiting_other,
|
|
|
},
|
|
|
"self_info": self_info,
|
|
|
}
|
|
|
|