##// END OF EJS Templates
processes: show how many workers we have set from configuration.
marcink -
r3155:82f2bc2a default
parent child Browse files
Show More
@@ -1,171 +1,183 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 import psutil
24 24 import signal
25 25 from pyramid.view import view_config
26 26
27 27 from rhodecode.apps._base import BaseAppView
28 28 from rhodecode.apps.admin.navigation import navigation_list
29 from rhodecode.lib import system_info
29 30 from rhodecode.lib.auth import (
30 31 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
31 32 from rhodecode.lib.utils2 import safe_int, StrictAttributeDict
32 33
33 34 log = logging.getLogger(__name__)
34 35
35 36
36 37 class AdminProcessManagementView(BaseAppView):
37 38 def load_default_context(self):
38 39 c = self._get_local_tmpl_context()
39 40 return c
40 41
41 42 def _format_proc(self, proc, with_children=False):
42 43 try:
43 44 mem = proc.memory_info()
44 45 proc_formatted = StrictAttributeDict({
45 46 'pid': proc.pid,
46 47 'name': proc.name(),
47 48 'mem_rss': mem.rss,
48 49 'mem_vms': mem.vms,
49 50 'cpu_percent': proc.cpu_percent(),
50 51 'create_time': proc.create_time(),
51 52 'cmd': ' '.join(proc.cmdline()),
52 53 })
53 54
54 55 if with_children:
55 56 proc_formatted.update({
56 57 'children': [self._format_proc(x)
57 58 for x in proc.children(recursive=True)]
58 59 })
59 60 except Exception:
60 61 log.exception('Failed to load proc')
61 62 proc_formatted = None
62 63 return proc_formatted
63 64
64 65 def get_processes(self):
65 66 proc_list = []
66 67 for p in psutil.process_iter():
67 68 if 'gunicorn' in p.name():
68 69 proc = self._format_proc(p, with_children=True)
69 70 if proc:
70 71 proc_list.append(proc)
71 72
72 73 return proc_list
73 74
75 def get_workers(self):
76 workers = None
77 try:
78 rc_config = system_info.rhodecode_config().value['config']
79 workers = rc_config['server:main'].get('workers')
80 except Exception:
81 pass
82
83 return workers or '?'
84
74 85 @LoginRequired()
75 86 @HasPermissionAllDecorator('hg.admin')
76 87 @view_config(
77 88 route_name='admin_settings_process_management', request_method='GET',
78 89 renderer='rhodecode:templates/admin/settings/settings.mako')
79 90 def process_management(self):
80 91 _ = self.request.translate
81 92 c = self.load_default_context()
82 93
83 94 c.active = 'process_management'
84 95 c.navlist = navigation_list(self.request)
85 96 c.gunicorn_processes = self.get_processes()
97 c.gunicorn_workers = self.get_workers()
86 98 return self._get_template_context(c)
87 99
88 100 @LoginRequired()
89 101 @HasPermissionAllDecorator('hg.admin')
90 102 @view_config(
91 103 route_name='admin_settings_process_management_data', request_method='GET',
92 104 renderer='rhodecode:templates/admin/settings/settings_process_management_data.mako')
93 105 def process_management_data(self):
94 106 _ = self.request.translate
95 107 c = self.load_default_context()
96 108 c.gunicorn_processes = self.get_processes()
97 109 return self._get_template_context(c)
98 110
99 111 @LoginRequired()
100 112 @HasPermissionAllDecorator('hg.admin')
101 113 @CSRFRequired()
102 114 @view_config(
103 115 route_name='admin_settings_process_management_signal',
104 116 request_method='POST', renderer='json_ext')
105 117 def process_management_signal(self):
106 118 pids = self.request.json.get('pids', [])
107 119 result = []
108 120
109 121 def on_terminate(proc):
110 122 msg = "process `PID:{}` terminated with exit code {}".format(
111 123 proc.pid, proc.returncode or 0)
112 124 result.append(msg)
113 125
114 126 procs = []
115 127 for pid in pids:
116 128 pid = safe_int(pid)
117 129 if pid:
118 130 try:
119 131 proc = psutil.Process(pid)
120 132 except psutil.NoSuchProcess:
121 133 continue
122 134
123 135 children = proc.children(recursive=True)
124 136 if children:
125 137 log.warning('Wont kill Master Process')
126 138 else:
127 139 procs.append(proc)
128 140
129 141 for p in procs:
130 142 try:
131 143 p.terminate()
132 144 except psutil.AccessDenied as e:
133 145 log.warning('Access denied: {}'.format(e))
134 146
135 147 gone, alive = psutil.wait_procs(procs, timeout=10, callback=on_terminate)
136 148 for p in alive:
137 149 try:
138 150 p.kill()
139 151 except psutil.AccessDenied as e:
140 152 log.warning('Access denied: {}'.format(e))
141 153
142 154 return {'result': result}
143 155
144 156 @LoginRequired()
145 157 @HasPermissionAllDecorator('hg.admin')
146 158 @CSRFRequired()
147 159 @view_config(
148 160 route_name='admin_settings_process_management_master_signal',
149 161 request_method='POST', renderer='json_ext')
150 162 def process_management_master_signal(self):
151 163 pid_data = self.request.json.get('pid_data', {})
152 164 pid = safe_int(pid_data['pid'])
153 165 action = pid_data['action']
154 166 if pid:
155 167 try:
156 168 proc = psutil.Process(pid)
157 169 except psutil.NoSuchProcess:
158 170 return {'result': 'failure_no_such_process'}
159 171
160 172 children = proc.children(recursive=True)
161 173 if children:
162 174 # master process
163 175 if action == '+' and len(children) <= 20:
164 176 proc.send_signal(signal.SIGTTIN)
165 177 elif action == '-' and len(children) >= 2:
166 178 proc.send_signal(signal.SIGTTOU)
167 179 else:
168 180 return {'result': 'failure_wrong_action'}
169 181 return {'result': 'success'}
170 182
171 183 return {'result': 'failure_not_master'}
@@ -1,145 +1,146 b''
1 1
2 2 <div id="update_notice" style="display: none; margin: -40px 0px 20px 0px">
3 3 <div>${_('Checking for updates...')}</div>
4 4 </div>
5 5
6 6
7 7 <div class="panel panel-default">
8 8 <div class="panel-heading">
9 9 <h3 class="panel-title">${_('Gunicorn process management')}</h3>
10 10 <div class="pull-right">
11 11 <a id="autoRefreshEnable" href="#autoRefreshEnable" onclick="enableAutoRefresh(); return false">${_('start auto refresh')}</a>
12 12 <a id="autoRefreshDisable" href="#autoRefreshDisable" onclick="disableAutoRefresh(); return false" style="display: none">${_('stop auto refresh')}</a>
13 13 </div>
14 14 </div>
15 15 <div class="panel-body" id="app">
16 16 <h3>List of Gunicorn processes on this machine</h3>
17 <p>RhodeCode workers set: ${c.gunicorn_workers}</p>
17 18 <%
18 19 def get_name(proc):
19 20 cmd = ' '.join(proc.cmdline())
20 21 if 'vcsserver.ini' in cmd:
21 22 return 'VCSServer'
22 23 elif 'rhodecode.ini' in cmd:
23 24 return 'RhodeCode'
24 25 return proc.name()
25 26 %>
26 27 <%include file='settings_process_management_data.mako'/>
27 28 </div>
28 29 </div>
29 30
30 31 <script>
31 32
32 33
33 34 restart = function(elem, pid) {
34 35
35 36 if ($(elem).hasClass('disabled')){
36 37 return;
37 38 }
38 39 $(elem).addClass('disabled');
39 40 $(elem).html('processing...');
40 41
41 42 $.ajax({
42 43 url: pyroutes.url('admin_settings_process_management_signal'),
43 44 headers: {
44 45 "X-CSRF-Token": CSRF_TOKEN,
45 46 },
46 47 data: JSON.stringify({'pids': [pid]}),
47 48 dataType: 'json',
48 49 type: 'POST',
49 50 contentType: "application/json; charset=utf-8",
50 51 success: function (data) {
51 52 $(elem).html(data.result);
52 53 $(elem).removeClass('disabled');
53 54 },
54 55 failure: function (data) {
55 56 $(elem).text('FAILED TO LOAD RESULT');
56 57 $(elem).removeClass('disabled');
57 58 },
58 59 error: function (data) {
59 60 $(elem).text('FAILED TO LOAD RESULT');
60 61 $(elem).removeClass('disabled');
61 62 }
62 63 })
63 64 };
64 65
65 66 var intervalID = null;
66 67 var currentRequest = null;
67 68
68 69 autoRefresh = function(value) {
69 70 var url = pyroutes.url('admin_settings_process_management_data');
70 71 var loadData = function() {
71 72 currentRequest = $.get(url)
72 73 .done(function(data) {
73 74
74 75 if(data.indexOf('id="processTimeStamp"') === -1){
75 76 clearInterval(intervalID);
76 77 $('#procList').html('ERROR LOADING DATA. PLEASE REFRESH THE PAGE');
77 78 return
78 79 }
79 80
80 81 currentRequest = null;
81 82 $('#procList').html(data);
82 83 timeagoActivate();
83 84 var beat = function(doCallback) {
84 85 var callback = function () {};
85 86 if (doCallback){
86 87 var callback = function () {beat(false)};
87 88 }
88 89 $('#processTimeStamp').animate({
89 90 opacity: $('#processTimeStamp').css('opacity') == '1' ? '0.3' : '1'
90 91 }, 500, callback);
91 92 };
92 93 beat(true)
93 94 });
94 95 };
95 96
96 97 if (value) {
97 98 intervalID = setInterval(loadData, 5000);
98 99 } else {
99 100 clearInterval(intervalID);
100 101 }
101 102 };
102 103
103 104 enableAutoRefresh = function() {
104 105 $('#autoRefreshEnable').hide();
105 106 $('#autoRefreshDisable').show();
106 107 autoRefresh(true)
107 108 };
108 109
109 110 disableAutoRefresh = function() {
110 111 $('#autoRefreshEnable').show();
111 112 $('#autoRefreshDisable').hide();
112 113 autoRefresh(false)
113 114 };
114 115
115 116 masterAction = function(pid, action) {
116 117 $.ajax({
117 118 url: pyroutes.url('admin_settings_process_management_master_signal'),
118 119 headers: {
119 120 "X-CSRF-Token": CSRF_TOKEN,
120 121 },
121 122 data: JSON.stringify({'pid_data': {'pid': pid, 'action': action}}),
122 123 dataType: 'json',
123 124 type: 'POST',
124 125 contentType: "application/json; charset=utf-8",
125 126 success: function (data) {
126 127
127 128 },
128 129 failure: function (data) {
129 130
130 131 },
131 132 error: function (data) {
132 133
133 134 }
134 135 })
135 136 };
136 137
137 138 addWorker = function(pid) {
138 139 masterAction(pid, '+');
139 140 };
140 141
141 142 removeWorker = function(pid) {
142 143 masterAction(pid, '-');
143 144 };
144 145
145 146 </script>
General Comments 0
You need to be logged in to leave comments. Login now