diff --git a/rhodecode/apps/admin/__init__.py b/rhodecode/apps/admin/__init__.py --- a/rhodecode/apps/admin/__init__.py +++ b/rhodecode/apps/admin/__init__.py @@ -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', diff --git a/rhodecode/apps/admin/navigation.py b/rhodecode/apps/admin/navigation.py --- a/rhodecode/apps/admin/navigation.py +++ b/rhodecode/apps/admin/navigation.py @@ -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'), diff --git a/rhodecode/apps/admin/views/process_management.py b/rhodecode/apps/admin/views/process_management.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/admin/views/process_management.py @@ -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 . +# +# 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} diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -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', []); diff --git a/rhodecode/templates/admin/settings/settings_process_management.mako b/rhodecode/templates/admin/settings/settings_process_management.mako new file mode 100644 --- /dev/null +++ b/rhodecode/templates/admin/settings/settings_process_management.mako @@ -0,0 +1,83 @@ + + + + +
+
+

${_('Gunicorn process management')}

+ +
+
+

List of Gunicorn processes on this machine

+ + % for proc in c.gunicorn_processes: + <% mem = proc.memory_info()%> + + + + + + + + % endfor +
+ + ${proc.pid} - ${proc.name()} + + + RSS:${h.format_byte_size_binary(mem.rss)} + + VMS:${h.format_byte_size_binary(mem.vms)} + + <% is_master = proc.children(recursive=True) %> + % if is_master: + MASTER + % else: + + restart + + % endif +
+
+
+ + +