##// END OF EJS Templates
processes: use 200ms CPU measure time to get actual results, previous implementation showed 0 most of the time.
marcink -
r3911:16d5221a default
parent child Browse files
Show More
@@ -1,182 +1,182 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 import psutil
23 import psutil
24 import signal
24 import signal
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26
26
27 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.apps._base.navigation import navigation_list
28 from rhodecode.apps._base.navigation import navigation_list
29 from rhodecode.lib import system_info
29 from rhodecode.lib import system_info
30 from rhodecode.lib.auth import (
30 from rhodecode.lib.auth import (
31 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
31 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
32 from rhodecode.lib.utils2 import safe_int, StrictAttributeDict
32 from rhodecode.lib.utils2 import safe_int, StrictAttributeDict
33
33
34 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
35
35
36
36
37 class AdminProcessManagementView(BaseAppView):
37 class AdminProcessManagementView(BaseAppView):
38 def load_default_context(self):
38 def load_default_context(self):
39 c = self._get_local_tmpl_context()
39 c = self._get_local_tmpl_context()
40 return c
40 return c
41
41
42 def _format_proc(self, proc, with_children=False):
42 def _format_proc(self, proc, with_children=False):
43 try:
43 try:
44 mem = proc.memory_info()
44 mem = proc.memory_info()
45 proc_formatted = StrictAttributeDict({
45 proc_formatted = StrictAttributeDict({
46 'pid': proc.pid,
46 'pid': proc.pid,
47 'name': proc.name(),
47 'name': proc.name(),
48 'mem_rss': mem.rss,
48 'mem_rss': mem.rss,
49 'mem_vms': mem.vms,
49 'mem_vms': mem.vms,
50 'cpu_percent': proc.cpu_percent(),
50 'cpu_percent': proc.cpu_percent(interval=0.2),
51 'create_time': proc.create_time(),
51 'create_time': proc.create_time(),
52 'cmd': ' '.join(proc.cmdline()),
52 'cmd': ' '.join(proc.cmdline()),
53 })
53 })
54
54
55 if with_children:
55 if with_children:
56 proc_formatted.update({
56 proc_formatted.update({
57 'children': [self._format_proc(x)
57 'children': [self._format_proc(x)
58 for x in proc.children(recursive=True)]
58 for x in proc.children(recursive=True)]
59 })
59 })
60 except Exception:
60 except Exception:
61 log.exception('Failed to load proc')
61 log.exception('Failed to load proc')
62 proc_formatted = None
62 proc_formatted = None
63 return proc_formatted
63 return proc_formatted
64
64
65 def get_processes(self):
65 def get_processes(self):
66 proc_list = []
66 proc_list = []
67 for p in psutil.process_iter():
67 for p in psutil.process_iter():
68 if 'gunicorn' in p.name():
68 if 'gunicorn' in p.name():
69 proc = self._format_proc(p, with_children=True)
69 proc = self._format_proc(p, with_children=True)
70 if proc:
70 if proc:
71 proc_list.append(proc)
71 proc_list.append(proc)
72
72
73 return proc_list
73 return proc_list
74
74
75 def get_workers(self):
75 def get_workers(self):
76 workers = None
76 workers = None
77 try:
77 try:
78 rc_config = system_info.rhodecode_config().value['config']
78 rc_config = system_info.rhodecode_config().value['config']
79 workers = rc_config['server:main'].get('workers')
79 workers = rc_config['server:main'].get('workers')
80 except Exception:
80 except Exception:
81 pass
81 pass
82
82
83 return workers or '?'
83 return workers or '?'
84
84
85 @LoginRequired()
85 @LoginRequired()
86 @HasPermissionAllDecorator('hg.admin')
86 @HasPermissionAllDecorator('hg.admin')
87 @view_config(
87 @view_config(
88 route_name='admin_settings_process_management', request_method='GET',
88 route_name='admin_settings_process_management', request_method='GET',
89 renderer='rhodecode:templates/admin/settings/settings.mako')
89 renderer='rhodecode:templates/admin/settings/settings.mako')
90 def process_management(self):
90 def process_management(self):
91 _ = self.request.translate
91 _ = self.request.translate
92 c = self.load_default_context()
92 c = self.load_default_context()
93
93
94 c.active = 'process_management'
94 c.active = 'process_management'
95 c.navlist = navigation_list(self.request)
95 c.navlist = navigation_list(self.request)
96 c.gunicorn_processes = self.get_processes()
96 c.gunicorn_processes = self.get_processes()
97 c.gunicorn_workers = self.get_workers()
97 c.gunicorn_workers = self.get_workers()
98 return self._get_template_context(c)
98 return self._get_template_context(c)
99
99
100 @LoginRequired()
100 @LoginRequired()
101 @HasPermissionAllDecorator('hg.admin')
101 @HasPermissionAllDecorator('hg.admin')
102 @view_config(
102 @view_config(
103 route_name='admin_settings_process_management_data', request_method='GET',
103 route_name='admin_settings_process_management_data', request_method='GET',
104 renderer='rhodecode:templates/admin/settings/settings_process_management_data.mako')
104 renderer='rhodecode:templates/admin/settings/settings_process_management_data.mako')
105 def process_management_data(self):
105 def process_management_data(self):
106 _ = self.request.translate
106 _ = self.request.translate
107 c = self.load_default_context()
107 c = self.load_default_context()
108 c.gunicorn_processes = self.get_processes()
108 c.gunicorn_processes = self.get_processes()
109 return self._get_template_context(c)
109 return self._get_template_context(c)
110
110
111 @LoginRequired()
111 @LoginRequired()
112 @HasPermissionAllDecorator('hg.admin')
112 @HasPermissionAllDecorator('hg.admin')
113 @CSRFRequired()
113 @CSRFRequired()
114 @view_config(
114 @view_config(
115 route_name='admin_settings_process_management_signal',
115 route_name='admin_settings_process_management_signal',
116 request_method='POST', renderer='json_ext')
116 request_method='POST', renderer='json_ext')
117 def process_management_signal(self):
117 def process_management_signal(self):
118 pids = self.request.json.get('pids', [])
118 pids = self.request.json.get('pids', [])
119 result = []
119 result = []
120
120
121 def on_terminate(proc):
121 def on_terminate(proc):
122 msg = "terminated"
122 msg = "terminated"
123 result.append(msg)
123 result.append(msg)
124
124
125 procs = []
125 procs = []
126 for pid in pids:
126 for pid in pids:
127 pid = safe_int(pid)
127 pid = safe_int(pid)
128 if pid:
128 if pid:
129 try:
129 try:
130 proc = psutil.Process(pid)
130 proc = psutil.Process(pid)
131 except psutil.NoSuchProcess:
131 except psutil.NoSuchProcess:
132 continue
132 continue
133
133
134 children = proc.children(recursive=True)
134 children = proc.children(recursive=True)
135 if children:
135 if children:
136 log.warning('Wont kill Master Process')
136 log.warning('Wont kill Master Process')
137 else:
137 else:
138 procs.append(proc)
138 procs.append(proc)
139
139
140 for p in procs:
140 for p in procs:
141 try:
141 try:
142 p.terminate()
142 p.terminate()
143 except psutil.AccessDenied as e:
143 except psutil.AccessDenied as e:
144 log.warning('Access denied: {}'.format(e))
144 log.warning('Access denied: {}'.format(e))
145
145
146 gone, alive = psutil.wait_procs(procs, timeout=10, callback=on_terminate)
146 gone, alive = psutil.wait_procs(procs, timeout=10, callback=on_terminate)
147 for p in alive:
147 for p in alive:
148 try:
148 try:
149 p.kill()
149 p.kill()
150 except psutil.AccessDenied as e:
150 except psutil.AccessDenied as e:
151 log.warning('Access denied: {}'.format(e))
151 log.warning('Access denied: {}'.format(e))
152
152
153 return {'result': result}
153 return {'result': result}
154
154
155 @LoginRequired()
155 @LoginRequired()
156 @HasPermissionAllDecorator('hg.admin')
156 @HasPermissionAllDecorator('hg.admin')
157 @CSRFRequired()
157 @CSRFRequired()
158 @view_config(
158 @view_config(
159 route_name='admin_settings_process_management_master_signal',
159 route_name='admin_settings_process_management_master_signal',
160 request_method='POST', renderer='json_ext')
160 request_method='POST', renderer='json_ext')
161 def process_management_master_signal(self):
161 def process_management_master_signal(self):
162 pid_data = self.request.json.get('pid_data', {})
162 pid_data = self.request.json.get('pid_data', {})
163 pid = safe_int(pid_data['pid'])
163 pid = safe_int(pid_data['pid'])
164 action = pid_data['action']
164 action = pid_data['action']
165 if pid:
165 if pid:
166 try:
166 try:
167 proc = psutil.Process(pid)
167 proc = psutil.Process(pid)
168 except psutil.NoSuchProcess:
168 except psutil.NoSuchProcess:
169 return {'result': 'failure_no_such_process'}
169 return {'result': 'failure_no_such_process'}
170
170
171 children = proc.children(recursive=True)
171 children = proc.children(recursive=True)
172 if children:
172 if children:
173 # master process
173 # master process
174 if action == '+' and len(children) <= 20:
174 if action == '+' and len(children) <= 20:
175 proc.send_signal(signal.SIGTTIN)
175 proc.send_signal(signal.SIGTTIN)
176 elif action == '-' and len(children) >= 2:
176 elif action == '-' and len(children) >= 2:
177 proc.send_signal(signal.SIGTTOU)
177 proc.send_signal(signal.SIGTTOU)
178 else:
178 else:
179 return {'result': 'failure_wrong_action'}
179 return {'result': 'failure_wrong_action'}
180 return {'result': 'success'}
180 return {'result': 'success'}
181
181
182 return {'result': 'failure_not_master'}
182 return {'result': 'failure_not_master'}
General Comments 0
You need to be logged in to leave comments. Login now