##// END OF EJS Templates
process-managemet: added simple page to monitor worker processes of RhodeCode.
marcink -
r1885:629a1200 default
parent child Browse files
Show More
@@ -0,0 +1,92 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 import psutil
24 from pyramid.view import view_config
25
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps.admin.navigation import navigation_list
28 from rhodecode.lib import helpers as h
29 from rhodecode.lib.auth import (
30 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
31 from vcsserver.utils import safe_int
32
33 log = logging.getLogger(__name__)
34
35
36 class AdminProcessManagementView(BaseAppView):
37 def load_default_context(self):
38 c = self._get_local_tmpl_context()
39 self._register_global_c(c)
40 return c
41
42 @LoginRequired()
43 @HasPermissionAllDecorator('hg.admin')
44 @view_config(
45 route_name='admin_settings_process_management', request_method='GET',
46 renderer='rhodecode:templates/admin/settings/settings.mako')
47 def process_management(self):
48 _ = self.request.translate
49 c = self.load_default_context()
50
51 c.active = 'process_management'
52 c.navlist = navigation_list(self.request)
53 c.gunicorn_processes = (
54 p for p in psutil.process_iter() if 'gunicorn' in p.name())
55 return self._get_template_context(c)
56
57 @LoginRequired()
58 @HasPermissionAllDecorator('hg.admin')
59 @CSRFRequired()
60 @view_config(
61 route_name='admin_settings_process_management_signal',
62 request_method='POST', renderer='json_ext')
63 def process_management_signal(self):
64 pids = self.request.json.get('pids', [])
65 result = []
66 def on_terminate(proc):
67 msg = "process `PID:{}` terminated with exit code {}".format(
68 proc.pid, proc.returncode)
69 result.append(msg)
70
71 procs = []
72 for pid in pids:
73 pid = safe_int(pid)
74 if pid:
75 try:
76 proc = psutil.Process(pid)
77 except psutil.NoSuchProcess:
78 continue
79
80 children = proc.children(recursive=True)
81 if children:
82 print('Wont kill Master Process')
83 else:
84 procs.append(proc)
85
86 for p in procs:
87 p.terminate()
88 gone, alive = psutil.wait_procs(procs, timeout=10, callback=on_terminate)
89 for p in alive:
90 p.kill()
91
92 return {'result': result}
@@ -0,0 +1,83 b''
1
2 <div id="update_notice" style="display: none; margin: -40px 0px 20px 0px">
3 <div>${_('Checking for updates...')}</div>
4 </div>
5
6
7 <div class="panel panel-default">
8 <div class="panel-heading">
9 <h3 class="panel-title">${_('Gunicorn process management')}</h3>
10
11 </div>
12 <div class="panel-body" id="app">
13 <h3>List of Gunicorn processes on this machine</h3>
14 <table>
15 % for proc in c.gunicorn_processes:
16 <% mem = proc.memory_info()%>
17
18 <tr>
19 <td>
20 <code>
21 ${proc.pid} - ${proc.name()}
22 </code>
23 </td>
24 <td>
25 RSS:${h.format_byte_size_binary(mem.rss)}
26 </td>
27 <td>
28 VMS:${h.format_byte_size_binary(mem.vms)}
29 </td>
30 <td>
31 <% is_master = proc.children(recursive=True) %>
32 % if is_master:
33 MASTER
34 % else:
35 <a href="#restartProcess" onclick="restart(this, ${proc.pid});return false">
36 restart
37 </a>
38 % endif
39 </td>
40 </tr>
41 % endfor
42 </table>
43 </div>
44 </div>
45
46
47 <script>
48
49
50 restart = function(elem, pid) {
51
52 if ($(elem).hasClass('disabled')){
53 return;
54 }
55 $(elem).addClass('disabled');
56 $(elem).html('processing...');
57
58 $.ajax({
59 url: pyroutes.url('admin_settings_process_management_signal'),
60 headers: {
61 "X-CSRF-Token": CSRF_TOKEN,
62 },
63 data: JSON.stringify({'pids': [pid]}),
64 dataType: 'json',
65 type: 'POST',
66 contentType: "application/json; charset=utf-8",
67 success: function (data) {
68 $(elem).html(data.result);
69 $(elem).removeClass('disabled');
70 },
71 failure: function (data) {
72 $(elem).text('FAILED TO LOAD RESULT');
73 $(elem).removeClass('disabled');
74 },
75 error: function (data) {
76 $(elem).text('FAILED TO LOAD RESULT');
77 $(elem).removeClass('disabled');
78 }
79 })
80 }
81
82
83 </script>
@@ -64,6 +64,13 b' def admin_routes(config):'
64 name='admin_settings_sessions_cleanup',
64 name='admin_settings_sessions_cleanup',
65 pattern='/settings/sessions/cleanup')
65 pattern='/settings/sessions/cleanup')
66
66
67 config.add_route(
68 name='admin_settings_process_management',
69 pattern='/settings/process_management')
70 config.add_route(
71 name='admin_settings_process_management_signal',
72 pattern='/settings/process_management/signal')
73
67 # global permissions
74 # global permissions
68 config.add_route(
75 config.add_route(
69 name='admin_permissions_ips',
76 name='admin_permissions_ips',
@@ -94,6 +94,8 b' class NavigationRegistry(object):'
94 'global_integrations_home', pyramid=True),
94 'global_integrations_home', pyramid=True),
95 NavEntry('system', _('System Info'),
95 NavEntry('system', _('System Info'),
96 'admin_settings_system', pyramid=True),
96 'admin_settings_system', pyramid=True),
97 NavEntry('process_management', _('Processes'),
98 'admin_settings_process_management', pyramid=True),
97 NavEntry('sessions', _('User Sessions'),
99 NavEntry('sessions', _('User Sessions'),
98 'admin_settings_sessions', pyramid=True),
100 'admin_settings_sessions', pyramid=True),
99 NavEntry('open_source', _('Open Source Licenses'),
101 NavEntry('open_source', _('Open Source Licenses'),
@@ -73,6 +73,8 b' function registerRCRoutes() {'
73 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
73 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
74 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
74 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
75 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
75 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
76 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
77 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
76 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
78 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
77 pyroutes.register('users', '/_admin/users', []);
79 pyroutes.register('users', '/_admin/users', []);
78 pyroutes.register('users_data', '/_admin/users_data', []);
80 pyroutes.register('users_data', '/_admin/users_data', []);
General Comments 0
You need to be logged in to leave comments. Login now