process-managemet: added simple page to monitor worker processes of RhodeCode.
This commit is contained in:
parent
af56c8a8da
commit
16cb1534cb
5 changed files with 186 additions and 0 deletions
|
|
@ -64,6 +64,13 @@ def admin_routes(config):
|
|||
name='admin_settings_sessions_cleanup',
|
||||
pattern='/settings/sessions/cleanup')
|
||||
|
||||
config.add_route(
|
||||
name='admin_settings_process_management',
|
||||
pattern='/settings/process_management')
|
||||
config.add_route(
|
||||
name='admin_settings_process_management_signal',
|
||||
pattern='/settings/process_management/signal')
|
||||
|
||||
# global permissions
|
||||
config.add_route(
|
||||
name='admin_permissions_ips',
|
||||
|
|
|
|||
|
|
@ -94,6 +94,8 @@ class NavigationRegistry(object):
|
|||
'global_integrations_home', pyramid=True),
|
||||
NavEntry('system', _('System Info'),
|
||||
'admin_settings_system', pyramid=True),
|
||||
NavEntry('process_management', _('Processes'),
|
||||
'admin_settings_process_management', pyramid=True),
|
||||
NavEntry('sessions', _('User Sessions'),
|
||||
'admin_settings_sessions', pyramid=True),
|
||||
NavEntry('open_source', _('Open Source Licenses'),
|
||||
|
|
|
|||
92
rhodecode/apps/admin/views/process_management.py
Normal file
92
rhodecode/apps/admin/views/process_management.py
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2016-2017 RhodeCode GmbH
|
||||
#
|
||||
# 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/
|
||||
|
||||
import logging
|
||||
|
||||
import psutil
|
||||
from pyramid.view import view_config
|
||||
|
||||
from rhodecode.apps._base import BaseAppView
|
||||
from rhodecode.apps.admin.navigation import navigation_list
|
||||
from rhodecode.lib import helpers as h
|
||||
from rhodecode.lib.auth import (
|
||||
LoginRequired, HasPermissionAllDecorator, CSRFRequired)
|
||||
from vcsserver.utils import safe_int
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AdminProcessManagementView(BaseAppView):
|
||||
def load_default_context(self):
|
||||
c = self._get_local_tmpl_context()
|
||||
self._register_global_c(c)
|
||||
return c
|
||||
|
||||
@LoginRequired()
|
||||
@HasPermissionAllDecorator('hg.admin')
|
||||
@view_config(
|
||||
route_name='admin_settings_process_management', request_method='GET',
|
||||
renderer='rhodecode:templates/admin/settings/settings.mako')
|
||||
def process_management(self):
|
||||
_ = self.request.translate
|
||||
c = self.load_default_context()
|
||||
|
||||
c.active = 'process_management'
|
||||
c.navlist = navigation_list(self.request)
|
||||
c.gunicorn_processes = (
|
||||
p for p in psutil.process_iter() if 'gunicorn' in p.name())
|
||||
return self._get_template_context(c)
|
||||
|
||||
@LoginRequired()
|
||||
@HasPermissionAllDecorator('hg.admin')
|
||||
@CSRFRequired()
|
||||
@view_config(
|
||||
route_name='admin_settings_process_management_signal',
|
||||
request_method='POST', renderer='json_ext')
|
||||
def process_management_signal(self):
|
||||
pids = self.request.json.get('pids', [])
|
||||
result = []
|
||||
def on_terminate(proc):
|
||||
msg = "process `PID:{}` terminated with exit code {}".format(
|
||||
proc.pid, proc.returncode)
|
||||
result.append(msg)
|
||||
|
||||
procs = []
|
||||
for pid in pids:
|
||||
pid = safe_int(pid)
|
||||
if pid:
|
||||
try:
|
||||
proc = psutil.Process(pid)
|
||||
except psutil.NoSuchProcess:
|
||||
continue
|
||||
|
||||
children = proc.children(recursive=True)
|
||||
if children:
|
||||
print('Wont kill Master Process')
|
||||
else:
|
||||
procs.append(proc)
|
||||
|
||||
for p in procs:
|
||||
p.terminate()
|
||||
gone, alive = psutil.wait_procs(procs, timeout=10, callback=on_terminate)
|
||||
for p in alive:
|
||||
p.kill()
|
||||
|
||||
return {'result': result}
|
||||
|
|
@ -73,6 +73,8 @@ function registerRCRoutes() {
|
|||
pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
|
||||
pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
|
||||
pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
|
||||
pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
|
||||
pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
|
||||
pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
|
||||
pyroutes.register('users', '/_admin/users', []);
|
||||
pyroutes.register('users_data', '/_admin/users_data', []);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
|
||||
<div id="update_notice" style="display: none; margin: -40px 0px 20px 0px">
|
||||
<div>${_('Checking for updates...')}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">${_('Gunicorn process management')}</h3>
|
||||
|
||||
</div>
|
||||
<div class="panel-body" id="app">
|
||||
<h3>List of Gunicorn processes on this machine</h3>
|
||||
<table>
|
||||
% for proc in c.gunicorn_processes:
|
||||
<% mem = proc.memory_info()%>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<code>
|
||||
${proc.pid} - ${proc.name()}
|
||||
</code>
|
||||
</td>
|
||||
<td>
|
||||
RSS:${h.format_byte_size_binary(mem.rss)}
|
||||
</td>
|
||||
<td>
|
||||
VMS:${h.format_byte_size_binary(mem.vms)}
|
||||
</td>
|
||||
<td>
|
||||
<% is_master = proc.children(recursive=True) %>
|
||||
% if is_master:
|
||||
MASTER
|
||||
% else:
|
||||
<a href="#restartProcess" onclick="restart(this, ${proc.pid});return false">
|
||||
restart
|
||||
</a>
|
||||
% endif
|
||||
</td>
|
||||
</tr>
|
||||
% endfor
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
restart = function(elem, pid) {
|
||||
|
||||
if ($(elem).hasClass('disabled')){
|
||||
return;
|
||||
}
|
||||
$(elem).addClass('disabled');
|
||||
$(elem).html('processing...');
|
||||
|
||||
$.ajax({
|
||||
url: pyroutes.url('admin_settings_process_management_signal'),
|
||||
headers: {
|
||||
"X-CSRF-Token": CSRF_TOKEN,
|
||||
},
|
||||
data: JSON.stringify({'pids': [pid]}),
|
||||
dataType: 'json',
|
||||
type: 'POST',
|
||||
contentType: "application/json; charset=utf-8",
|
||||
success: function (data) {
|
||||
$(elem).html(data.result);
|
||||
$(elem).removeClass('disabled');
|
||||
},
|
||||
failure: function (data) {
|
||||
$(elem).text('FAILED TO LOAD RESULT');
|
||||
$(elem).removeClass('disabled');
|
||||
},
|
||||
error: function (data) {
|
||||
$(elem).text('FAILED TO LOAD RESULT');
|
||||
$(elem).removeClass('disabled');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
Loading…
Add table
Add a link
Reference in a new issue