system_info.py
893 lines
| 27.8 KiB
| text/x-python
|
PythonLexer
r5608 | # Copyright (C) 2017-2024 RhodeCode GmbH | |||
r1552 | # | |||
# This program is free software: you can redistribute it and/or modify | ||||
# it under the terms of the GNU Affero General Public License, version 3 | ||||
# (only), as published by the Free Software Foundation. | ||||
# | ||||
# 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 Affero General Public License | ||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
# | ||||
# This program is dual-licensed. If you wish to learn more about the | ||||
# RhodeCode Enterprise Edition, including its added features, Support services, | ||||
# and proprietary license terms, please see https://rhodecode.com/licenses/ | ||||
r1111 | import os | |||
import sys | ||||
import time | ||||
import platform | ||||
r2674 | import collections | |||
r4938 | import psutil | |||
r4734 | from functools import wraps | |||
r1111 | import pkg_resources | |||
import logging | ||||
r2674 | import resource | |||
r1111 | ||||
r4927 | import configparser | |||
r1111 | ||||
r5145 | from rc_license.models import LicenseModel | |||
r5028 | from rhodecode.lib.str_utils import safe_str | |||
r1111 | log = logging.getLogger(__name__) | |||
_NA = 'NOT AVAILABLE' | ||||
r4938 | _NA_FLOAT = 0.0 | |||
r1111 | ||||
STATE_OK = 'ok' | ||||
STATE_ERR = 'error' | ||||
STATE_WARN = 'warning' | ||||
STATE_OK_DEFAULT = {'message': '', 'type': STATE_OK} | ||||
r4734 | registered_helpers = {} | |||
def register_sysinfo(func): | ||||
""" | ||||
@register_helper | ||||
def db_check(): | ||||
pass | ||||
db_check == registered_helpers['db_check'] | ||||
""" | ||||
global registered_helpers | ||||
registered_helpers[func.__name__] = func | ||||
@wraps(func) | ||||
def _wrapper(*args, **kwargs): | ||||
return func(*args, **kwargs) | ||||
return _wrapper | ||||
r1111 | # HELPERS | |||
r4938 | def percentage(part: (int, float), whole: (int, float)): | |||
r1111 | whole = float(whole) | |||
if whole > 0: | ||||
r1155 | return round(100 * float(part) / whole, 1) | |||
return 0.0 | ||||
r1111 | ||||
def get_storage_size(storage_path): | ||||
sizes = [] | ||||
for file_ in os.listdir(storage_path): | ||||
storage_file = os.path.join(storage_path, file_) | ||||
if os.path.isfile(storage_file): | ||||
try: | ||||
sizes.append(os.path.getsize(storage_file)) | ||||
except OSError: | ||||
r3337 | log.exception('Failed to get size of storage file %s', storage_file) | |||
r1111 | pass | |||
return sum(sizes) | ||||
r3184 | def get_resource(resource_type): | |||
try: | ||||
return resource.getrlimit(resource_type) | ||||
except Exception: | ||||
return 'NOT_SUPPORTED' | ||||
r3337 | def get_cert_path(ini_path): | |||
default = '/etc/ssl/certs/ca-certificates.crt' | ||||
control_ca_bundle = os.path.join( | ||||
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(ini_path)))), | ||||
r5421 | '/etc/ssl/certs/ca-certificates.crt') | |||
r3337 | if os.path.isfile(control_ca_bundle): | |||
default = control_ca_bundle | ||||
return default | ||||
r3900 | ||||
r1111 | class SysInfoRes(object): | |||
r3184 | def __init__(self, value, state=None, human_value=None): | |||
r1111 | self.value = value | |||
r3184 | self.state = state or STATE_OK_DEFAULT | |||
r1111 | self.human_value = human_value or value | |||
def __json__(self): | ||||
return { | ||||
'value': self.value, | ||||
'state': self.state, | ||||
'human_value': self.human_value, | ||||
} | ||||
r1295 | def get_value(self): | |||
return self.__json__() | ||||
r1111 | def __str__(self): | |||
r5096 | return f'<SysInfoRes({self.__json__()})>' | |||
r1111 | ||||
class SysInfo(object): | ||||
def __init__(self, func_name, **kwargs): | ||||
r4984 | self.function_name = func_name | |||
r1111 | self.value = _NA | |||
self.state = None | ||||
self.kwargs = kwargs or {} | ||||
def __call__(self): | ||||
computed = self.compute(**self.kwargs) | ||||
if not isinstance(computed, SysInfoRes): | ||||
raise ValueError( | ||||
'computed value for {} is not instance of ' | ||||
'{}, got {} instead'.format( | ||||
r4984 | self.function_name, SysInfoRes, type(computed))) | |||
r1111 | return computed.__json__() | |||
def __str__(self): | ||||
r5096 | return f'<SysInfo({self.function_name})>' | |||
r1111 | ||||
def compute(self, **kwargs): | ||||
r4984 | return self.function_name(**kwargs) | |||
r1111 | ||||
# SysInfo functions | ||||
r4734 | @register_sysinfo | |||
r1111 | def python_info(): | |||
r4938 | value = dict(version=f'{platform.python_version()}:{platform.python_implementation()}', | |||
r1112 | executable=sys.executable) | |||
r1111 | return SysInfoRes(value=value) | |||
r4734 | @register_sysinfo | |||
r1111 | def py_modules(): | |||
r4348 | mods = dict([(p.project_name, {'version': p.version, 'location': p.location}) | |||
r1111 | for p in pkg_resources.working_set]) | |||
r4348 | ||||
r1111 | value = sorted(mods.items(), key=lambda k: k[0].lower()) | |||
return SysInfoRes(value=value) | ||||
r4734 | @register_sysinfo | |||
r1111 | def platform_type(): | |||
r5028 | from rhodecode.lib.utils import generate_platform_uuid | |||
r1115 | ||||
value = dict( | ||||
r5028 | name=safe_str(platform.platform()), | |||
r1115 | uuid=generate_platform_uuid() | |||
) | ||||
r1111 | return SysInfoRes(value=value) | |||
r4734 | @register_sysinfo | |||
r2914 | def locale_info(): | |||
import locale | ||||
r4982 | def safe_get_locale(locale_name): | |||
try: | ||||
locale.getlocale(locale_name) | ||||
except TypeError: | ||||
return f'FAILED_LOCALE_GET:{locale_name}' | ||||
r2914 | value = dict( | |||
r5065 | locale_default=locale.getlocale(), | |||
r4982 | locale_lc_all=safe_get_locale(locale.LC_ALL), | |||
locale_lc_ctype=safe_get_locale(locale.LC_CTYPE), | ||||
r2914 | lang_env=os.environ.get('LANG'), | |||
lc_all_env=os.environ.get('LC_ALL'), | ||||
local_archive_env=os.environ.get('LOCALE_ARCHIVE'), | ||||
) | ||||
r4982 | human_value = \ | |||
f"LANG: {value['lang_env']}, \ | ||||
locale LC_ALL: {value['locale_lc_all']}, \ | ||||
locale LC_CTYPE: {value['locale_lc_ctype']}, \ | ||||
Default locales: {value['locale_default']}" | ||||
r2914 | return SysInfoRes(value=value, human_value=human_value) | |||
r4734 | @register_sysinfo | |||
r2673 | def ulimit_info(): | |||
r2674 | data = collections.OrderedDict([ | |||
r3184 | ('cpu time (seconds)', get_resource(resource.RLIMIT_CPU)), | |||
('file size', get_resource(resource.RLIMIT_FSIZE)), | ||||
('stack size', get_resource(resource.RLIMIT_STACK)), | ||||
('core file size', get_resource(resource.RLIMIT_CORE)), | ||||
('address space size', get_resource(resource.RLIMIT_AS)), | ||||
('locked in mem size', get_resource(resource.RLIMIT_MEMLOCK)), | ||||
('heap size', get_resource(resource.RLIMIT_DATA)), | ||||
('rss size', get_resource(resource.RLIMIT_RSS)), | ||||
('number of processes', get_resource(resource.RLIMIT_NPROC)), | ||||
('open files', get_resource(resource.RLIMIT_NOFILE)), | ||||
r2674 | ]) | |||
r2673 | ||||
r4983 | text = ', '.join(f'{k}:{v}' for k, v in data.items()) | |||
r2673 | ||||
value = { | ||||
r2674 | 'limits': data, | |||
r2673 | 'text': text, | |||
} | ||||
return SysInfoRes(value=value) | ||||
r4734 | @register_sysinfo | |||
r1111 | def uptime(): | |||
from rhodecode.lib.helpers import age, time_to_datetime | ||||
r1462 | from rhodecode.translation import TranslationString | |||
r1111 | ||||
r1112 | value = dict(boot_time=0, uptime=0, text='') | |||
r1111 | state = STATE_OK_DEFAULT | |||
boot_time = psutil.boot_time() | ||||
r1112 | value['boot_time'] = boot_time | |||
value['uptime'] = time.time() - boot_time | ||||
r1111 | ||||
r1462 | date_or_age = age(time_to_datetime(boot_time)) | |||
if isinstance(date_or_age, TranslationString): | ||||
date_or_age = date_or_age.interpolate() | ||||
r1112 | human_value = value.copy() | |||
human_value['boot_time'] = time_to_datetime(boot_time) | ||||
human_value['uptime'] = age(time_to_datetime(boot_time), show_suffix=False) | ||||
r1308 | ||||
r5096 | human_value['text'] = f'Server started {date_or_age}' | |||
r1112 | return SysInfoRes(value=value, human_value=human_value) | |||
r1111 | ||||
r4734 | @register_sysinfo | |||
r1111 | def memory(): | |||
from rhodecode.lib.helpers import format_byte_size_binary | ||||
r1116 | value = dict(available=0, used=0, used_real=0, cached=0, percent=0, | |||
percent_used=0, free=0, inactive=0, active=0, shared=0, | ||||
total=0, buffers=0, text='') | ||||
r1112 | ||||
r1111 | state = STATE_OK_DEFAULT | |||
r1112 | value.update(dict(psutil.virtual_memory()._asdict())) | |||
r1116 | value['used_real'] = value['total'] - value['available'] | |||
r5178 | value['percent_used'] = psutil._common.usage_percent(value['used_real'], value['total'], 1) | |||
r1111 | ||||
r1112 | human_value = value.copy() | |||
r5096 | human_value['text'] = '{}/{}, {}% used'.format( | |||
r1116 | format_byte_size_binary(value['used_real']), | |||
r1112 | format_byte_size_binary(value['total']), | |||
r5096 | value['percent_used']) | |||
r1111 | ||||
r4938 | keys = list(value.keys())[::] | |||
r1112 | keys.pop(keys.index('percent')) | |||
keys.pop(keys.index('percent_used')) | ||||
keys.pop(keys.index('text')) | ||||
for k in keys: | ||||
human_value[k] = format_byte_size_binary(value[k]) | ||||
if state['type'] == STATE_OK and value['percent_used'] > 90: | ||||
r1111 | msg = 'Critical: your available RAM memory is very low.' | |||
state = {'message': msg, 'type': STATE_ERR} | ||||
r1112 | elif state['type'] == STATE_OK and value['percent_used'] > 70: | |||
r1111 | msg = 'Warning: your available RAM memory is running low.' | |||
state = {'message': msg, 'type': STATE_WARN} | ||||
r1112 | return SysInfoRes(value=value, state=state, human_value=human_value) | |||
r1111 | ||||
r4734 | @register_sysinfo | |||
r1111 | def machine_load(): | |||
r4938 | value = {'1_min': _NA_FLOAT, '5_min': _NA_FLOAT, '15_min': _NA_FLOAT, 'text': ''} | |||
r1111 | state = STATE_OK_DEFAULT | |||
# load averages | ||||
if hasattr(psutil.os, 'getloadavg'): | ||||
r1112 | value.update(dict( | |||
r4983 | list(zip(['1_min', '5_min', '15_min'], psutil.os.getloadavg())) | |||
r4938 | )) | |||
r1111 | ||||
r1112 | human_value = value.copy() | |||
human_value['text'] = '1min: {}, 5min: {}, 15min: {}'.format( | ||||
value['1_min'], value['5_min'], value['15_min']) | ||||
r1111 | ||||
r4938 | if state['type'] == STATE_OK and value['15_min'] > 5.0: | |||
r1112 | msg = 'Warning: your machine load is very high.' | |||
state = {'message': msg, 'type': STATE_WARN} | ||||
return SysInfoRes(value=value, state=state, human_value=human_value) | ||||
r1111 | ||||
r4734 | @register_sysinfo | |||
r1111 | def cpu(): | |||
r1464 | value = {'cpu': 0, 'cpu_count': 0, 'cpu_usage': []} | |||
r1111 | state = STATE_OK_DEFAULT | |||
r1112 | ||||
r1464 | value['cpu'] = psutil.cpu_percent(0.5) | |||
value['cpu_usage'] = psutil.cpu_percent(0.5, percpu=True) | ||||
value['cpu_count'] = psutil.cpu_count() | ||||
human_value = value.copy() | ||||
r5421 | human_value['text'] = f'{value["cpu_count"]} cores at {value["cpu"]} %' | |||
r1464 | ||||
r1112 | return SysInfoRes(value=value, state=state, human_value=human_value) | |||
r1111 | ||||
r4734 | @register_sysinfo | |||
r1111 | def storage(): | |||
from rhodecode.lib.helpers import format_byte_size_binary | ||||
r5356 | from rhodecode.lib.utils import get_rhodecode_repo_store_path | |||
path = get_rhodecode_repo_store_path() | ||||
r1111 | ||||
r1112 | value = dict(percent=0, used=0, total=0, path=path, text='') | |||
r1111 | state = STATE_OK_DEFAULT | |||
try: | ||||
r1112 | value.update(dict(psutil.disk_usage(path)._asdict())) | |||
r1111 | except Exception as e: | |||
log.exception('Failed to fetch disk info') | ||||
state = {'message': str(e), 'type': STATE_ERR} | ||||
r1112 | human_value = value.copy() | |||
human_value['used'] = format_byte_size_binary(value['used']) | ||||
human_value['total'] = format_byte_size_binary(value['total']) | ||||
r1111 | human_value['text'] = "{}/{}, {}% used".format( | |||
r1112 | format_byte_size_binary(value['used']), | |||
format_byte_size_binary(value['total']), | ||||
value['percent']) | ||||
r1111 | ||||
r1112 | if state['type'] == STATE_OK and value['percent'] > 90: | |||
r1111 | msg = 'Critical: your disk space is very low.' | |||
state = {'message': msg, 'type': STATE_ERR} | ||||
r1112 | elif state['type'] == STATE_OK and value['percent'] > 70: | |||
r1111 | msg = 'Warning: your disk space is running low.' | |||
state = {'message': msg, 'type': STATE_WARN} | ||||
r1112 | return SysInfoRes(value=value, state=state, human_value=human_value) | |||
r1111 | ||||
r4734 | @register_sysinfo | |||
r1111 | def storage_inodes(): | |||
r5356 | from rhodecode.lib.utils import get_rhodecode_repo_store_path | |||
path = get_rhodecode_repo_store_path() | ||||
r1111 | ||||
r4938 | value = dict(percent=0.0, free=0, used=0, total=0, path=path, text='') | |||
r1111 | state = STATE_OK_DEFAULT | |||
try: | ||||
i_stat = os.statvfs(path) | ||||
r1219 | value['free'] = i_stat.f_ffree | |||
value['used'] = i_stat.f_files-i_stat.f_favail | ||||
r1112 | value['total'] = i_stat.f_files | |||
r1155 | value['percent'] = percentage(value['used'], value['total']) | |||
r1111 | except Exception as e: | |||
log.exception('Failed to fetch disk inodes info') | ||||
state = {'message': str(e), 'type': STATE_ERR} | ||||
r1112 | human_value = value.copy() | |||
r1111 | human_value['text'] = "{}/{}, {}% used".format( | |||
r1112 | value['used'], value['total'], value['percent']) | |||
r1111 | ||||
r1112 | if state['type'] == STATE_OK and value['percent'] > 90: | |||
r1111 | msg = 'Critical: your disk free inodes are very low.' | |||
state = {'message': msg, 'type': STATE_ERR} | ||||
r1112 | elif state['type'] == STATE_OK and value['percent'] > 70: | |||
r1111 | msg = 'Warning: your disk free inodes are running low.' | |||
state = {'message': msg, 'type': STATE_WARN} | ||||
r1155 | return SysInfoRes(value=value, state=state, human_value=human_value) | |||
r1111 | ||||
r4734 | @register_sysinfo | |||
r5516 | def storage_artifacts(): | |||
r1111 | import rhodecode | |||
from rhodecode.lib.helpers import format_byte_size_binary | ||||
r5433 | from rhodecode.lib.archive_cache import get_archival_cache_store | |||
r1111 | ||||
r5516 | backend_type = rhodecode.ConfigGet().get_str('archive_cache.backend.type') | |||
r5420 | ||||
r5516 | value = dict(percent=0, used=0, total=0, items=0, path='', text='', type=backend_type) | |||
r1111 | state = STATE_OK_DEFAULT | |||
try: | ||||
r5433 | d_cache = get_archival_cache_store(config=rhodecode.CONFIG) | |||
r5516 | backend_type = str(d_cache) | |||
r5420 | ||||
r5433 | total_files, total_size, _directory_stats = d_cache.get_statistics() | |||
r1111 | ||||
r1112 | value.update({ | |||
r1111 | 'percent': 100, | |||
r5421 | 'used': total_size, | |||
'total': total_size, | ||||
r5433 | 'items': total_files, | |||
r5516 | 'path': d_cache.storage_path, | |||
'type': backend_type | ||||
r1111 | }) | |||
except Exception as e: | ||||
log.exception('failed to fetch archive cache storage') | ||||
state = {'message': str(e), 'type': STATE_ERR} | ||||
r1112 | human_value = value.copy() | |||
human_value['used'] = format_byte_size_binary(value['used']) | ||||
human_value['total'] = format_byte_size_binary(value['total']) | ||||
r5516 | human_value['text'] = f"{human_value['used']} ({value['items']} items)" | |||
return SysInfoRes(value=value, state=state, human_value=human_value) | ||||
@register_sysinfo | ||||
def storage_archives(): | ||||
import rhodecode | ||||
from rhodecode.lib.helpers import format_byte_size_binary | ||||
import rhodecode.apps.file_store.utils as store_utils | ||||
from rhodecode import CONFIG | ||||
backend_type = rhodecode.ConfigGet().get_str(store_utils.config_keys.backend_type) | ||||
value = dict(percent=0, used=0, total=0, items=0, path='', text='', type=backend_type) | ||||
state = STATE_OK_DEFAULT | ||||
try: | ||||
f_store = store_utils.get_filestore_backend(config=CONFIG) | ||||
backend_type = str(f_store) | ||||
total_files, total_size, _directory_stats = f_store.get_statistics() | ||||
value.update({ | ||||
'percent': 100, | ||||
'used': total_size, | ||||
'total': total_size, | ||||
'items': total_files, | ||||
'path': f_store.storage_path, | ||||
'type': backend_type | ||||
}) | ||||
except Exception as e: | ||||
log.exception('failed to fetch archive cache storage') | ||||
state = {'message': str(e), 'type': STATE_ERR} | ||||
human_value = value.copy() | ||||
human_value['used'] = format_byte_size_binary(value['used']) | ||||
human_value['total'] = format_byte_size_binary(value['total']) | ||||
human_value['text'] = f"{human_value['used']} ({value['items']} items)" | ||||
r1111 | ||||
r1112 | return SysInfoRes(value=value, state=state, human_value=human_value) | |||
r1111 | ||||
r4734 | @register_sysinfo | |||
r1111 | def storage_gist(): | |||
from rhodecode.model.gist import GIST_STORE_LOC | ||||
r5356 | from rhodecode.lib.utils import safe_str, get_rhodecode_repo_store_path | |||
r5433 | from rhodecode.lib.helpers import format_byte_size_binary, get_directory_statistics | |||
r5421 | ||||
r1111 | path = safe_str(os.path.join( | |||
r5356 | get_rhodecode_repo_store_path(), GIST_STORE_LOC)) | |||
r1111 | ||||
# gist storage | ||||
r1112 | value = dict(percent=0, used=0, total=0, items=0, path=path, text='') | |||
r1111 | state = STATE_OK_DEFAULT | |||
try: | ||||
r5421 | total_files, total_size, _directory_stats = get_directory_statistics(path) | |||
r1112 | value.update({ | |||
r1111 | 'percent': 100, | |||
r5421 | 'used': total_size, | |||
'total': total_size, | ||||
'items': total_files | ||||
r1111 | }) | |||
except Exception as e: | ||||
log.exception('failed to fetch gist storage items') | ||||
state = {'message': str(e), 'type': STATE_ERR} | ||||
r1112 | human_value = value.copy() | |||
human_value['used'] = format_byte_size_binary(value['used']) | ||||
human_value['total'] = format_byte_size_binary(value['total']) | ||||
human_value['text'] = "{} ({} items)".format( | ||||
human_value['used'], value['items']) | ||||
return SysInfoRes(value=value, state=state, human_value=human_value) | ||||
r1111 | ||||
r4734 | @register_sysinfo | |||
r1124 | def storage_temp(): | |||
import tempfile | ||||
from rhodecode.lib.helpers import format_byte_size_binary | ||||
path = tempfile.gettempdir() | ||||
value = dict(percent=0, used=0, total=0, items=0, path=path, text='') | ||||
state = STATE_OK_DEFAULT | ||||
if not psutil: | ||||
return SysInfoRes(value=value, state=state) | ||||
try: | ||||
value.update(dict(psutil.disk_usage(path)._asdict())) | ||||
except Exception as e: | ||||
log.exception('Failed to fetch temp dir info') | ||||
state = {'message': str(e), 'type': STATE_ERR} | ||||
human_value = value.copy() | ||||
human_value['used'] = format_byte_size_binary(value['used']) | ||||
human_value['total'] = format_byte_size_binary(value['total']) | ||||
human_value['text'] = "{}/{}, {}% used".format( | ||||
format_byte_size_binary(value['used']), | ||||
format_byte_size_binary(value['total']), | ||||
value['percent']) | ||||
return SysInfoRes(value=value, state=state, human_value=human_value) | ||||
r4734 | @register_sysinfo | |||
r1112 | def search_info(): | |||
r1111 | import rhodecode | |||
r1112 | from rhodecode.lib.index import searcher_from_config | |||
r1111 | ||||
r1112 | backend = rhodecode.CONFIG.get('search.module', '') | |||
location = rhodecode.CONFIG.get('search.location', '') | ||||
r1111 | try: | |||
r1112 | searcher = searcher_from_config(rhodecode.CONFIG) | |||
searcher = searcher.__class__.__name__ | ||||
except Exception: | ||||
searcher = None | ||||
r1111 | ||||
r1112 | value = dict( | |||
backend=backend, searcher=searcher, location=location, text='') | ||||
state = STATE_OK_DEFAULT | ||||
r1111 | ||||
r1112 | human_value = value.copy() | |||
human_value['text'] = "backend:`{}`".format(human_value['backend']) | ||||
return SysInfoRes(value=value, state=state, human_value=human_value) | ||||
r1111 | ||||
r4734 | @register_sysinfo | |||
r1111 | def git_info(): | |||
from rhodecode.lib.vcs.backends import git | ||||
state = STATE_OK_DEFAULT | ||||
value = human_value = '' | ||||
try: | ||||
value = git.discover_git_version(raise_on_exc=True) | ||||
r5096 | human_value = f'version reported from VCSServer: {value}' | |||
r1111 | except Exception as e: | |||
state = {'message': str(e), 'type': STATE_ERR} | ||||
return SysInfoRes(value=value, state=state, human_value=human_value) | ||||
r4734 | @register_sysinfo | |||
r1111 | def hg_info(): | |||
from rhodecode.lib.vcs.backends import hg | ||||
state = STATE_OK_DEFAULT | ||||
value = human_value = '' | ||||
try: | ||||
value = hg.discover_hg_version(raise_on_exc=True) | ||||
r5096 | human_value = f'version reported from VCSServer: {value}' | |||
r1111 | except Exception as e: | |||
state = {'message': str(e), 'type': STATE_ERR} | ||||
return SysInfoRes(value=value, state=state, human_value=human_value) | ||||
r4734 | @register_sysinfo | |||
r1111 | def svn_info(): | |||
from rhodecode.lib.vcs.backends import svn | ||||
state = STATE_OK_DEFAULT | ||||
value = human_value = '' | ||||
try: | ||||
value = svn.discover_svn_version(raise_on_exc=True) | ||||
r5096 | human_value = f'version reported from VCSServer: {value}' | |||
r1111 | except Exception as e: | |||
state = {'message': str(e), 'type': STATE_ERR} | ||||
return SysInfoRes(value=value, state=state, human_value=human_value) | ||||
r4734 | @register_sysinfo | |||
r1111 | def vcs_backends(): | |||
import rhodecode | ||||
r2314 | value = rhodecode.CONFIG.get('vcs.backends') | |||
r1111 | human_value = 'Enabled backends in order: {}'.format(','.join(value)) | |||
return SysInfoRes(value=value, human_value=human_value) | ||||
r4734 | @register_sysinfo | |||
r1111 | def vcs_server(): | |||
import rhodecode | ||||
r1465 | from rhodecode.lib.vcs.backends import get_vcsserver_service_data | |||
r1111 | ||||
server_url = rhodecode.CONFIG.get('vcs.server') | ||||
enabled = rhodecode.CONFIG.get('vcs.server.enable') | ||||
r1182 | protocol = rhodecode.CONFIG.get('vcs.server.protocol') or 'http' | |||
r1111 | state = STATE_OK_DEFAULT | |||
version = None | ||||
r1465 | workers = 0 | |||
r1111 | ||||
try: | ||||
r1465 | data = get_vcsserver_service_data() | |||
if data and 'version' in data: | ||||
version = data['version'] | ||||
if data and 'config' in data: | ||||
conf = data['config'] | ||||
r1576 | workers = conf.get('workers', 'NOT AVAILABLE') | |||
r1465 | ||||
r1111 | connection = 'connected' | |||
except Exception as e: | ||||
connection = 'failed' | ||||
state = {'message': str(e), 'type': STATE_ERR} | ||||
value = dict( | ||||
url=server_url, | ||||
enabled=enabled, | ||||
protocol=protocol, | ||||
connection=connection, | ||||
version=version, | ||||
text='', | ||||
) | ||||
r1112 | human_value = value.copy() | |||
r1111 | human_value['text'] = \ | |||
r1465 | '{url}@ver:{ver} via {mode} mode[workers:{workers}], connection:{conn}'.format( | |||
url=server_url, ver=version, workers=workers, mode=protocol, | ||||
conn=connection) | ||||
r1111 | ||||
r1112 | return SysInfoRes(value=value, state=state, human_value=human_value) | |||
r1111 | ||||
r4734 | @register_sysinfo | |||
r3943 | def vcs_server_config(): | |||
from rhodecode.lib.vcs.backends import get_vcsserver_service_data | ||||
state = STATE_OK_DEFAULT | ||||
value = {} | ||||
try: | ||||
data = get_vcsserver_service_data() | ||||
value = data['app_config'] | ||||
except Exception as e: | ||||
state = {'message': str(e), 'type': STATE_ERR} | ||||
human_value = value.copy() | ||||
human_value['text'] = 'VCS Server config' | ||||
return SysInfoRes(value=value, state=state, human_value=human_value) | ||||
r5552 | @register_sysinfo | |||
def rhodecode_server_config(): | ||||
import rhodecode | ||||
state = STATE_OK_DEFAULT | ||||
config = rhodecode.CONFIG.copy() | ||||
secrets_lits = [ | ||||
f'rhodecode_{LicenseModel.LICENSE_DB_KEY}', | ||||
'sqlalchemy.db1.url', | ||||
'channelstream.secret', | ||||
'beaker.session.secret', | ||||
'rhodecode.encrypted_values.secret', | ||||
'appenlight.api_key', | ||||
'smtp_password', | ||||
'file_store.objectstore.secret', | ||||
'archive_cache.objectstore.secret', | ||||
'app.service_api.token', | ||||
] | ||||
for k in secrets_lits: | ||||
if k in config: | ||||
config[k] = '**OBFUSCATED**' | ||||
value = human_value = config | ||||
return SysInfoRes(value=value, state=state, human_value=human_value) | ||||
r3943 | ||||
r4734 | @register_sysinfo | |||
r1111 | def rhodecode_app_info(): | |||
import rhodecode | ||||
r1113 | edition = rhodecode.CONFIG.get('rhodecode.edition') | |||
r1112 | value = dict( | |||
rhodecode_version=rhodecode.__version__, | ||||
r1113 | rhodecode_lib_path=os.path.abspath(rhodecode.__file__), | |||
text='' | ||||
r1112 | ) | |||
r1113 | human_value = value.copy() | |||
human_value['text'] = 'RhodeCode {edition}, version {ver}'.format( | ||||
edition=edition, ver=value['rhodecode_version'] | ||||
) | ||||
return SysInfoRes(value=value, human_value=human_value) | ||||
r1111 | ||||
r4734 | @register_sysinfo | |||
r1111 | def rhodecode_config(): | |||
import rhodecode | ||||
path = rhodecode.CONFIG.get('__file__') | ||||
rhodecode_ini_safe = rhodecode.CONFIG.copy() | ||||
r3337 | cert_path = get_cert_path(path) | |||
r1111 | ||||
r1463 | try: | |||
r1465 | config = configparser.ConfigParser() | |||
r1463 | config.read(path) | |||
parsed_ini = config | ||||
if parsed_ini.has_section('server:main'): | ||||
parsed_ini = dict(parsed_ini.items('server:main')) | ||||
except Exception: | ||||
log.exception('Failed to read .ini file for display') | ||||
parsed_ini = {} | ||||
rhodecode_ini_safe['server:main'] = parsed_ini | ||||
r1111 | blacklist = [ | |||
r5145 | f'rhodecode_{LicenseModel.LICENSE_DB_KEY}', | |||
r1111 | 'routes.map', | |||
'sqlalchemy.db1.url', | ||||
'channelstream.secret', | ||||
'beaker.session.secret', | ||||
'rhodecode.encrypted_values.secret', | ||||
'rhodecode_auth_github_consumer_key', | ||||
'rhodecode_auth_github_consumer_secret', | ||||
'rhodecode_auth_google_consumer_key', | ||||
'rhodecode_auth_google_consumer_secret', | ||||
'rhodecode_auth_bitbucket_consumer_secret', | ||||
'rhodecode_auth_bitbucket_consumer_key', | ||||
'rhodecode_auth_twitter_consumer_secret', | ||||
'rhodecode_auth_twitter_consumer_key', | ||||
'rhodecode_auth_twitter_secret', | ||||
'rhodecode_auth_github_secret', | ||||
'rhodecode_auth_google_secret', | ||||
'rhodecode_auth_bitbucket_secret', | ||||
'appenlight.api_key', | ||||
('app_conf', 'sqlalchemy.db1.url') | ||||
] | ||||
for k in blacklist: | ||||
if isinstance(k, tuple): | ||||
section, key = k | ||||
if section in rhodecode_ini_safe: | ||||
rhodecode_ini_safe[section] = '**OBFUSCATED**' | ||||
else: | ||||
rhodecode_ini_safe.pop(k, None) | ||||
# TODO: maybe put some CONFIG checks here ? | ||||
r1729 | return SysInfoRes(value={'config': rhodecode_ini_safe, | |||
'path': path, 'cert_path': cert_path}) | ||||
r1111 | ||||
r4734 | @register_sysinfo | |||
r1111 | def database_info(): | |||
import rhodecode | ||||
from sqlalchemy.engine import url as engine_url | ||||
r5065 | from rhodecode.model import meta | |||
from rhodecode.model.meta import Session | ||||
r1111 | from rhodecode.model.db import DbMigrateVersion | |||
state = STATE_OK_DEFAULT | ||||
db_migrate = DbMigrateVersion.query().filter( | ||||
DbMigrateVersion.repository_id == 'rhodecode_db_migrations').one() | ||||
db_url_obj = engine_url.make_url(rhodecode.CONFIG['sqlalchemy.db1.url']) | ||||
try: | ||||
r5065 | engine = meta.get_engine() | |||
r1111 | db_server_info = engine.dialect._get_server_version_info( | |||
Session.connection(bind=engine)) | ||||
db_version = '.'.join(map(str, db_server_info)) | ||||
except Exception: | ||||
log.exception('failed to fetch db version') | ||||
db_version = 'UNKNOWN' | ||||
db_info = dict( | ||||
migrate_version=db_migrate.version, | ||||
type=db_url_obj.get_backend_name(), | ||||
version=db_version, | ||||
url=repr(db_url_obj) | ||||
) | ||||
r1575 | current_version = db_migrate.version | |||
expected_version = rhodecode.__dbversion__ | ||||
if state['type'] == STATE_OK and current_version != expected_version: | ||||
msg = 'Critical: database schema mismatch, ' \ | ||||
'expected version {}, got {}. ' \ | ||||
'Please run migrations on your database.'.format( | ||||
expected_version, current_version) | ||||
state = {'message': msg, 'type': STATE_ERR} | ||||
r1111 | ||||
r1112 | human_value = db_info.copy() | |||
r1111 | human_value['url'] = "{} @ migration version: {}".format( | |||
db_info['url'], db_info['migrate_version']) | ||||
human_value['version'] = "{} {}".format(db_info['type'], db_info['version']) | ||||
return SysInfoRes(value=db_info, state=state, human_value=human_value) | ||||
r4734 | @register_sysinfo | |||
r1111 | def server_info(environ): | |||
import rhodecode | ||||
from rhodecode.lib.base import get_server_ip_addr, get_server_port | ||||
value = { | ||||
r5096 | 'server_ip': '{}:{}'.format( | |||
r1111 | get_server_ip_addr(environ, log_errors=False), | |||
get_server_port(environ) | ||||
), | ||||
'server_id': rhodecode.CONFIG.get('instance_id'), | ||||
} | ||||
return SysInfoRes(value=value) | ||||
r4734 | @register_sysinfo | |||
r1391 | def usage_info(): | |||
r5178 | from rhodecode.model.db import User, Repository, true | |||
r1391 | value = { | |||
'users': User.query().count(), | ||||
r5178 | 'users_active': User.query().filter(User.active == true()).count(), | |||
r1391 | 'repositories': Repository.query().count(), | |||
'repository_types': { | ||||
'hg': Repository.query().filter( | ||||
Repository.repo_type == 'hg').count(), | ||||
'git': Repository.query().filter( | ||||
Repository.repo_type == 'git').count(), | ||||
'svn': Repository.query().filter( | ||||
Repository.repo_type == 'svn').count(), | ||||
}, | ||||
} | ||||
return SysInfoRes(value=value) | ||||
r1111 | def get_system_info(environ): | |||
environ = environ or {} | ||||
return { | ||||
'rhodecode_app': SysInfo(rhodecode_app_info)(), | ||||
'rhodecode_config': SysInfo(rhodecode_config)(), | ||||
r1391 | 'rhodecode_usage': SysInfo(usage_info)(), | |||
r1111 | 'python': SysInfo(python_info)(), | |||
'py_modules': SysInfo(py_modules)(), | ||||
'platform': SysInfo(platform_type)(), | ||||
r2914 | 'locale': SysInfo(locale_info)(), | |||
r1111 | 'server': SysInfo(server_info, environ=environ)(), | |||
'database': SysInfo(database_info)(), | ||||
r2673 | 'ulimit': SysInfo(ulimit_info)(), | |||
r1111 | 'storage': SysInfo(storage)(), | |||
'storage_inodes': SysInfo(storage_inodes)(), | ||||
'storage_archive': SysInfo(storage_archives)(), | ||||
r5516 | 'storage_artifacts': SysInfo(storage_artifacts)(), | |||
r1111 | 'storage_gist': SysInfo(storage_gist)(), | |||
r1124 | 'storage_temp': SysInfo(storage_temp)(), | |||
r1111 | ||||
r1112 | 'search': SysInfo(search_info)(), | |||
r1111 | 'uptime': SysInfo(uptime)(), | |||
'load': SysInfo(machine_load)(), | ||||
'cpu': SysInfo(cpu)(), | ||||
'memory': SysInfo(memory)(), | ||||
'vcs_backends': SysInfo(vcs_backends)(), | ||||
'vcs_server': SysInfo(vcs_server)(), | ||||
r3943 | 'vcs_server_config': SysInfo(vcs_server_config)(), | |||
r5552 | 'rhodecode_server_config': SysInfo(rhodecode_server_config)(), | |||
r3943 | ||||
r1111 | 'git': SysInfo(git_info)(), | |||
'hg': SysInfo(hg_info)(), | ||||
'svn': SysInfo(svn_info)(), | ||||
} | ||||
r4734 | ||||
def load_system_info(key): | ||||
""" | ||||
get_sys_info('vcs_server') | ||||
get_sys_info('database') | ||||
""" | ||||
return SysInfo(registered_helpers[key])() | ||||