##// END OF EJS Templates
system-info: added ulimit to system info.
marcink -
r2673:4b2a46a3 default
parent child Browse files
Show More
@@ -1,197 +1,200 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 import urllib2
23 23
24 24 from pyramid.view import view_config
25 25
26 26 import rhodecode
27 27 from rhodecode.apps._base import BaseAppView
28 28 from rhodecode.apps.admin.navigation import navigation_list
29 29 from rhodecode.lib import helpers as h
30 30 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
31 31 from rhodecode.lib.utils2 import str2bool
32 32 from rhodecode.lib import system_info
33 33 from rhodecode.model.update import UpdateModel
34 34
35 35 log = logging.getLogger(__name__)
36 36
37 37
38 38 class AdminSystemInfoSettingsView(BaseAppView):
39 39 def load_default_context(self):
40 40 c = self._get_local_tmpl_context()
41 41 return c
42 42
43 43 @LoginRequired()
44 44 @HasPermissionAllDecorator('hg.admin')
45 45 @view_config(
46 46 route_name='admin_settings_system', request_method='GET',
47 47 renderer='rhodecode:templates/admin/settings/settings.mako')
48 48 def settings_system_info(self):
49 49 _ = self.request.translate
50 50 c = self.load_default_context()
51 51
52 52 c.active = 'system'
53 53 c.navlist = navigation_list(self.request)
54 54
55 55 # TODO(marcink), figure out how to allow only selected users to do this
56 56 c.allowed_to_snapshot = self._rhodecode_user.admin
57 57
58 58 snapshot = str2bool(self.request.params.get('snapshot'))
59 59
60 60 c.rhodecode_update_url = UpdateModel().get_update_url()
61 61 server_info = system_info.get_system_info(self.request.environ)
62 62
63 63 for key, val in server_info.items():
64 64 setattr(c, key, val)
65 65
66 66 def val(name, subkey='human_value'):
67 67 return server_info[name][subkey]
68 68
69 69 def state(name):
70 70 return server_info[name]['state']
71 71
72 72 def val2(name):
73 73 val = server_info[name]['human_value']
74 74 state = server_info[name]['state']
75 75 return val, state
76 76
77 77 update_info_msg = _('Note: please make sure this server can '
78 78 'access `${url}` for the update link to work',
79 79 mapping=dict(url=c.rhodecode_update_url))
80 80 version = UpdateModel().get_stored_version()
81 81 is_outdated = UpdateModel().is_outdated(
82 82 rhodecode.__version__, version)
83 83 update_state = {
84 84 'type': 'warning',
85 85 'message': 'New version available: {}'.format(version)
86 86 } \
87 87 if is_outdated else {}
88 88 c.data_items = [
89 89 # update info
90 90 (_('Update info'), h.literal(
91 91 '<span class="link" id="check_for_update" >%s.</span>' % (
92 92 _('Check for updates')) +
93 93 '<br/> <span >%s.</span>' % (update_info_msg)
94 94 ), ''),
95 95
96 96 # RhodeCode specific
97 97 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
98 98 (_('Latest version'), version, update_state),
99 99 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
100 100 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
101 101 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
102 102 (_('RhodeCode Certificate'), val('rhodecode_config')['cert_path'], state('rhodecode_config')),
103 103 (_('Workers'), val('rhodecode_config')['config']['server:main'].get('workers', '?'), state('rhodecode_config')),
104 104 (_('Worker Type'), val('rhodecode_config')['config']['server:main'].get('worker_class', 'sync'), state('rhodecode_config')),
105 105 ('', '', ''), # spacer
106 106
107 107 # Database
108 108 (_('Database'), val('database')['url'], state('database')),
109 109 (_('Database version'), val('database')['version'], state('database')),
110 110 ('', '', ''), # spacer
111 111
112 112 # Platform/Python
113 113 (_('Platform'), val('platform')['name'], state('platform')),
114 114 (_('Platform UUID'), val('platform')['uuid'], state('platform')),
115 115 (_('Python version'), val('python')['version'], state('python')),
116 116 (_('Python path'), val('python')['executable'], state('python')),
117 117 ('', '', ''), # spacer
118 118
119 119 # Systems stats
120 120 (_('CPU'), val('cpu')['text'], state('cpu')),
121 121 (_('Load'), val('load')['text'], state('load')),
122 122 (_('Memory'), val('memory')['text'], state('memory')),
123 123 (_('Uptime'), val('uptime')['text'], state('uptime')),
124 124 ('', '', ''), # spacer
125 125
126 # ulimit
127 (_('Ulimit'), val('ulimit')['text'], state('ulimit')),
128
126 129 # Repo storage
127 130 (_('Storage location'), val('storage')['path'], state('storage')),
128 131 (_('Storage info'), val('storage')['text'], state('storage')),
129 132 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
130 133
131 134 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
132 135 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
133 136
134 137 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
135 138 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
136 139
137 140 (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')),
138 141 (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')),
139 142
140 143 (_('Search info'), val('search')['text'], state('search')),
141 144 (_('Search location'), val('search')['location'], state('search')),
142 145 ('', '', ''), # spacer
143 146
144 147 # VCS specific
145 148 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
146 149 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
147 150 (_('GIT'), val('git'), state('git')),
148 151 (_('HG'), val('hg'), state('hg')),
149 152 (_('SVN'), val('svn'), state('svn')),
150 153
151 154 ]
152 155
153 156 if snapshot:
154 157 if c.allowed_to_snapshot:
155 158 c.data_items.pop(0) # remove server info
156 159 self.request.override_renderer = 'admin/settings/settings_system_snapshot.mako'
157 160 else:
158 161 h.flash('You are not allowed to do this', category='warning')
159 162 return self._get_template_context(c)
160 163
161 164 @LoginRequired()
162 165 @HasPermissionAllDecorator('hg.admin')
163 166 @view_config(
164 167 route_name='admin_settings_system_update', request_method='GET',
165 168 renderer='rhodecode:templates/admin/settings/settings_system_update.mako')
166 169 def settings_system_info_check_update(self):
167 170 _ = self.request.translate
168 171 c = self.load_default_context()
169 172
170 173 update_url = UpdateModel().get_update_url()
171 174
172 175 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s)
173 176 try:
174 177 data = UpdateModel().get_update_data(update_url)
175 178 except urllib2.URLError as e:
176 179 log.exception("Exception contacting upgrade server")
177 180 self.request.override_renderer = 'string'
178 181 return _err('Failed to contact upgrade server: %r' % e)
179 182 except ValueError as e:
180 183 log.exception("Bad data sent from update server")
181 184 self.request.override_renderer = 'string'
182 185 return _err('Bad data sent from update server')
183 186
184 187 latest = data['versions'][0]
185 188
186 189 c.update_url = update_url
187 190 c.latest_data = latest
188 191 c.latest_ver = latest['version']
189 192 c.cur_ver = rhodecode.__version__
190 193 c.should_upgrade = False
191 194
192 195 is_oudated = UpdateModel().is_outdated(c.cur_ver, c.latest_ver)
193 196 if is_oudated:
194 197 c.should_upgrade = True
195 198 c.important_notices = latest['general']
196 199 UpdateModel().store_version(latest['version'])
197 200 return self._get_template_context(c)
@@ -1,722 +1,747 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2017-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
22 22 import os
23 23 import sys
24 24 import time
25 25 import platform
26 import subprocess32
26 27 import pkg_resources
27 28 import logging
28 29
29 30 from pyramid.compat import configparser
30 31
31 32 log = logging.getLogger(__name__)
32 33
33 34
34 35 psutil = None
35 36
36 37 try:
37 38 # cygwin cannot have yet psutil support.
38 39 import psutil as psutil
39 40 except ImportError:
40 41 pass
41 42
42 43
43 44 _NA = 'NOT AVAILABLE'
44 45
45 46 STATE_OK = 'ok'
46 47 STATE_ERR = 'error'
47 48 STATE_WARN = 'warning'
48 49
49 50 STATE_OK_DEFAULT = {'message': '', 'type': STATE_OK}
50 51
51 52
52 53 # HELPERS
53 54 def percentage(part, whole):
54 55 whole = float(whole)
55 56 if whole > 0:
56 57 return round(100 * float(part) / whole, 1)
57 58 return 0.0
58 59
59 60
60 61 def get_storage_size(storage_path):
61 62 sizes = []
62 63 for file_ in os.listdir(storage_path):
63 64 storage_file = os.path.join(storage_path, file_)
64 65 if os.path.isfile(storage_file):
65 66 try:
66 67 sizes.append(os.path.getsize(storage_file))
67 68 except OSError:
68 69 log.exception('Failed to get size of storage file %s',
69 70 storage_file)
70 71 pass
71 72
72 73 return sum(sizes)
73 74
74 75
75 76 class SysInfoRes(object):
76 77 def __init__(self, value, state=STATE_OK_DEFAULT, human_value=None):
77 78 self.value = value
78 79 self.state = state
79 80 self.human_value = human_value or value
80 81
81 82 def __json__(self):
82 83 return {
83 84 'value': self.value,
84 85 'state': self.state,
85 86 'human_value': self.human_value,
86 87 }
87 88
88 89 def get_value(self):
89 90 return self.__json__()
90 91
91 92 def __str__(self):
92 93 return '<SysInfoRes({})>'.format(self.__json__())
93 94
94 95
95 96 class SysInfo(object):
96 97
97 98 def __init__(self, func_name, **kwargs):
98 99 self.func_name = func_name
99 100 self.value = _NA
100 101 self.state = None
101 102 self.kwargs = kwargs or {}
102 103
103 104 def __call__(self):
104 105 computed = self.compute(**self.kwargs)
105 106 if not isinstance(computed, SysInfoRes):
106 107 raise ValueError(
107 108 'computed value for {} is not instance of '
108 109 '{}, got {} instead'.format(
109 110 self.func_name, SysInfoRes, type(computed)))
110 111 return computed.__json__()
111 112
112 113 def __str__(self):
113 114 return '<SysInfo({})>'.format(self.func_name)
114 115
115 116 def compute(self, **kwargs):
116 117 return self.func_name(**kwargs)
117 118
118 119
119 120 # SysInfo functions
120 121 def python_info():
121 122 value = dict(version=' '.join(platform._sys_version()),
122 123 executable=sys.executable)
123 124 return SysInfoRes(value=value)
124 125
125 126
126 127 def py_modules():
127 128 mods = dict([(p.project_name, p.version)
128 129 for p in pkg_resources.working_set])
129 130 value = sorted(mods.items(), key=lambda k: k[0].lower())
130 131 return SysInfoRes(value=value)
131 132
132 133
133 134 def platform_type():
134 135 from rhodecode.lib.utils import safe_unicode, generate_platform_uuid
135 136
136 137 value = dict(
137 138 name=safe_unicode(platform.platform()),
138 139 uuid=generate_platform_uuid()
139 140 )
140 141 return SysInfoRes(value=value)
141 142
142 143
144 def ulimit_info():
145 data = {}
146
147 text = 'ulimit -a unavailable'
148 try:
149 result = subprocess32.check_output(
150 ['ulimit -a'], timeout=10, stderr=subprocess32.STDOUT,
151 shell=True)
152
153 for line in result.splitlines():
154 key, val = line.split(' ', 1)
155 data[key.strip()] = val.strip()
156 text = ', '.join('{}:{}'.format(k,v) for k,v in data.items())
157 except Exception:
158 log.exception('ulimit check problem')
159
160 value = {
161 'ulimit': data,
162 'text': text,
163 }
164 return SysInfoRes(value=value)
165
166
143 167 def uptime():
144 168 from rhodecode.lib.helpers import age, time_to_datetime
145 169 from rhodecode.translation import TranslationString
146 170
147 171 value = dict(boot_time=0, uptime=0, text='')
148 172 state = STATE_OK_DEFAULT
149 173 if not psutil:
150 174 return SysInfoRes(value=value, state=state)
151 175
152 176 boot_time = psutil.boot_time()
153 177 value['boot_time'] = boot_time
154 178 value['uptime'] = time.time() - boot_time
155 179
156 180 date_or_age = age(time_to_datetime(boot_time))
157 181 if isinstance(date_or_age, TranslationString):
158 182 date_or_age = date_or_age.interpolate()
159 183
160 184 human_value = value.copy()
161 185 human_value['boot_time'] = time_to_datetime(boot_time)
162 186 human_value['uptime'] = age(time_to_datetime(boot_time), show_suffix=False)
163 187
164 188 human_value['text'] = u'Server started {}'.format(date_or_age)
165 189 return SysInfoRes(value=value, human_value=human_value)
166 190
167 191
168 192 def memory():
169 193 from rhodecode.lib.helpers import format_byte_size_binary
170 194 value = dict(available=0, used=0, used_real=0, cached=0, percent=0,
171 195 percent_used=0, free=0, inactive=0, active=0, shared=0,
172 196 total=0, buffers=0, text='')
173 197
174 198 state = STATE_OK_DEFAULT
175 199 if not psutil:
176 200 return SysInfoRes(value=value, state=state)
177 201
178 202 value.update(dict(psutil.virtual_memory()._asdict()))
179 203 value['used_real'] = value['total'] - value['available']
180 204 value['percent_used'] = psutil._common.usage_percent(
181 205 value['used_real'], value['total'], 1)
182 206
183 207 human_value = value.copy()
184 208 human_value['text'] = '%s/%s, %s%% used' % (
185 209 format_byte_size_binary(value['used_real']),
186 210 format_byte_size_binary(value['total']),
187 211 value['percent_used'],)
188 212
189 213 keys = value.keys()[::]
190 214 keys.pop(keys.index('percent'))
191 215 keys.pop(keys.index('percent_used'))
192 216 keys.pop(keys.index('text'))
193 217 for k in keys:
194 218 human_value[k] = format_byte_size_binary(value[k])
195 219
196 220 if state['type'] == STATE_OK and value['percent_used'] > 90:
197 221 msg = 'Critical: your available RAM memory is very low.'
198 222 state = {'message': msg, 'type': STATE_ERR}
199 223
200 224 elif state['type'] == STATE_OK and value['percent_used'] > 70:
201 225 msg = 'Warning: your available RAM memory is running low.'
202 226 state = {'message': msg, 'type': STATE_WARN}
203 227
204 228 return SysInfoRes(value=value, state=state, human_value=human_value)
205 229
206 230
207 231 def machine_load():
208 232 value = {'1_min': _NA, '5_min': _NA, '15_min': _NA, 'text': ''}
209 233 state = STATE_OK_DEFAULT
210 234 if not psutil:
211 235 return SysInfoRes(value=value, state=state)
212 236
213 237 # load averages
214 238 if hasattr(psutil.os, 'getloadavg'):
215 239 value.update(dict(
216 240 zip(['1_min', '5_min', '15_min'], psutil.os.getloadavg())))
217 241
218 242 human_value = value.copy()
219 243 human_value['text'] = '1min: {}, 5min: {}, 15min: {}'.format(
220 244 value['1_min'], value['5_min'], value['15_min'])
221 245
222 246 if state['type'] == STATE_OK and value['15_min'] > 5:
223 247 msg = 'Warning: your machine load is very high.'
224 248 state = {'message': msg, 'type': STATE_WARN}
225 249
226 250 return SysInfoRes(value=value, state=state, human_value=human_value)
227 251
228 252
229 253 def cpu():
230 254 value = {'cpu': 0, 'cpu_count': 0, 'cpu_usage': []}
231 255 state = STATE_OK_DEFAULT
232 256
233 257 if not psutil:
234 258 return SysInfoRes(value=value, state=state)
235 259
236 260 value['cpu'] = psutil.cpu_percent(0.5)
237 261 value['cpu_usage'] = psutil.cpu_percent(0.5, percpu=True)
238 262 value['cpu_count'] = psutil.cpu_count()
239 263
240 264 human_value = value.copy()
241 265 human_value['text'] = '{} cores at {} %'.format(
242 266 value['cpu_count'], value['cpu'])
243 267
244 268 return SysInfoRes(value=value, state=state, human_value=human_value)
245 269
246 270
247 271 def storage():
248 272 from rhodecode.lib.helpers import format_byte_size_binary
249 273 from rhodecode.model.settings import VcsSettingsModel
250 274 path = VcsSettingsModel().get_repos_location()
251 275
252 276 value = dict(percent=0, used=0, total=0, path=path, text='')
253 277 state = STATE_OK_DEFAULT
254 278 if not psutil:
255 279 return SysInfoRes(value=value, state=state)
256 280
257 281 try:
258 282 value.update(dict(psutil.disk_usage(path)._asdict()))
259 283 except Exception as e:
260 284 log.exception('Failed to fetch disk info')
261 285 state = {'message': str(e), 'type': STATE_ERR}
262 286
263 287 human_value = value.copy()
264 288 human_value['used'] = format_byte_size_binary(value['used'])
265 289 human_value['total'] = format_byte_size_binary(value['total'])
266 290 human_value['text'] = "{}/{}, {}% used".format(
267 291 format_byte_size_binary(value['used']),
268 292 format_byte_size_binary(value['total']),
269 293 value['percent'])
270 294
271 295 if state['type'] == STATE_OK and value['percent'] > 90:
272 296 msg = 'Critical: your disk space is very low.'
273 297 state = {'message': msg, 'type': STATE_ERR}
274 298
275 299 elif state['type'] == STATE_OK and value['percent'] > 70:
276 300 msg = 'Warning: your disk space is running low.'
277 301 state = {'message': msg, 'type': STATE_WARN}
278 302
279 303 return SysInfoRes(value=value, state=state, human_value=human_value)
280 304
281 305
282 306 def storage_inodes():
283 307 from rhodecode.model.settings import VcsSettingsModel
284 308 path = VcsSettingsModel().get_repos_location()
285 309
286 310 value = dict(percent=0, free=0, used=0, total=0, path=path, text='')
287 311 state = STATE_OK_DEFAULT
288 312 if not psutil:
289 313 return SysInfoRes(value=value, state=state)
290 314
291 315 try:
292 316 i_stat = os.statvfs(path)
293 317 value['free'] = i_stat.f_ffree
294 318 value['used'] = i_stat.f_files-i_stat.f_favail
295 319 value['total'] = i_stat.f_files
296 320 value['percent'] = percentage(value['used'], value['total'])
297 321 except Exception as e:
298 322 log.exception('Failed to fetch disk inodes info')
299 323 state = {'message': str(e), 'type': STATE_ERR}
300 324
301 325 human_value = value.copy()
302 326 human_value['text'] = "{}/{}, {}% used".format(
303 327 value['used'], value['total'], value['percent'])
304 328
305 329 if state['type'] == STATE_OK and value['percent'] > 90:
306 330 msg = 'Critical: your disk free inodes are very low.'
307 331 state = {'message': msg, 'type': STATE_ERR}
308 332
309 333 elif state['type'] == STATE_OK and value['percent'] > 70:
310 334 msg = 'Warning: your disk free inodes are running low.'
311 335 state = {'message': msg, 'type': STATE_WARN}
312 336
313 337 return SysInfoRes(value=value, state=state, human_value=human_value)
314 338
315 339
316 340 def storage_archives():
317 341 import rhodecode
318 342 from rhodecode.lib.utils import safe_str
319 343 from rhodecode.lib.helpers import format_byte_size_binary
320 344
321 345 msg = 'Enable this by setting ' \
322 346 'archive_cache_dir=/path/to/cache option in the .ini file'
323 347 path = safe_str(rhodecode.CONFIG.get('archive_cache_dir', msg))
324 348
325 349 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
326 350 state = STATE_OK_DEFAULT
327 351 try:
328 352 items_count = 0
329 353 used = 0
330 354 for root, dirs, files in os.walk(path):
331 355 if root == path:
332 356 items_count = len(files)
333 357
334 358 for f in files:
335 359 try:
336 360 used += os.path.getsize(os.path.join(root, f))
337 361 except OSError:
338 362 pass
339 363 value.update({
340 364 'percent': 100,
341 365 'used': used,
342 366 'total': used,
343 367 'items': items_count
344 368 })
345 369
346 370 except Exception as e:
347 371 log.exception('failed to fetch archive cache storage')
348 372 state = {'message': str(e), 'type': STATE_ERR}
349 373
350 374 human_value = value.copy()
351 375 human_value['used'] = format_byte_size_binary(value['used'])
352 376 human_value['total'] = format_byte_size_binary(value['total'])
353 377 human_value['text'] = "{} ({} items)".format(
354 378 human_value['used'], value['items'])
355 379
356 380 return SysInfoRes(value=value, state=state, human_value=human_value)
357 381
358 382
359 383 def storage_gist():
360 384 from rhodecode.model.gist import GIST_STORE_LOC
361 385 from rhodecode.model.settings import VcsSettingsModel
362 386 from rhodecode.lib.utils import safe_str
363 387 from rhodecode.lib.helpers import format_byte_size_binary
364 388 path = safe_str(os.path.join(
365 389 VcsSettingsModel().get_repos_location(), GIST_STORE_LOC))
366 390
367 391 # gist storage
368 392 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
369 393 state = STATE_OK_DEFAULT
370 394
371 395 try:
372 396 items_count = 0
373 397 used = 0
374 398 for root, dirs, files in os.walk(path):
375 399 if root == path:
376 400 items_count = len(dirs)
377 401
378 402 for f in files:
379 403 try:
380 404 used += os.path.getsize(os.path.join(root, f))
381 405 except OSError:
382 406 pass
383 407 value.update({
384 408 'percent': 100,
385 409 'used': used,
386 410 'total': used,
387 411 'items': items_count
388 412 })
389 413 except Exception as e:
390 414 log.exception('failed to fetch gist storage items')
391 415 state = {'message': str(e), 'type': STATE_ERR}
392 416
393 417 human_value = value.copy()
394 418 human_value['used'] = format_byte_size_binary(value['used'])
395 419 human_value['total'] = format_byte_size_binary(value['total'])
396 420 human_value['text'] = "{} ({} items)".format(
397 421 human_value['used'], value['items'])
398 422
399 423 return SysInfoRes(value=value, state=state, human_value=human_value)
400 424
401 425
402 426 def storage_temp():
403 427 import tempfile
404 428 from rhodecode.lib.helpers import format_byte_size_binary
405 429
406 430 path = tempfile.gettempdir()
407 431 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
408 432 state = STATE_OK_DEFAULT
409 433
410 434 if not psutil:
411 435 return SysInfoRes(value=value, state=state)
412 436
413 437 try:
414 438 value.update(dict(psutil.disk_usage(path)._asdict()))
415 439 except Exception as e:
416 440 log.exception('Failed to fetch temp dir info')
417 441 state = {'message': str(e), 'type': STATE_ERR}
418 442
419 443 human_value = value.copy()
420 444 human_value['used'] = format_byte_size_binary(value['used'])
421 445 human_value['total'] = format_byte_size_binary(value['total'])
422 446 human_value['text'] = "{}/{}, {}% used".format(
423 447 format_byte_size_binary(value['used']),
424 448 format_byte_size_binary(value['total']),
425 449 value['percent'])
426 450
427 451 return SysInfoRes(value=value, state=state, human_value=human_value)
428 452
429 453
430 454 def search_info():
431 455 import rhodecode
432 456 from rhodecode.lib.index import searcher_from_config
433 457
434 458 backend = rhodecode.CONFIG.get('search.module', '')
435 459 location = rhodecode.CONFIG.get('search.location', '')
436 460
437 461 try:
438 462 searcher = searcher_from_config(rhodecode.CONFIG)
439 463 searcher = searcher.__class__.__name__
440 464 except Exception:
441 465 searcher = None
442 466
443 467 value = dict(
444 468 backend=backend, searcher=searcher, location=location, text='')
445 469 state = STATE_OK_DEFAULT
446 470
447 471 human_value = value.copy()
448 472 human_value['text'] = "backend:`{}`".format(human_value['backend'])
449 473
450 474 return SysInfoRes(value=value, state=state, human_value=human_value)
451 475
452 476
453 477 def git_info():
454 478 from rhodecode.lib.vcs.backends import git
455 479 state = STATE_OK_DEFAULT
456 480 value = human_value = ''
457 481 try:
458 482 value = git.discover_git_version(raise_on_exc=True)
459 483 human_value = 'version reported from VCSServer: {}'.format(value)
460 484 except Exception as e:
461 485 state = {'message': str(e), 'type': STATE_ERR}
462 486
463 487 return SysInfoRes(value=value, state=state, human_value=human_value)
464 488
465 489
466 490 def hg_info():
467 491 from rhodecode.lib.vcs.backends import hg
468 492 state = STATE_OK_DEFAULT
469 493 value = human_value = ''
470 494 try:
471 495 value = hg.discover_hg_version(raise_on_exc=True)
472 496 human_value = 'version reported from VCSServer: {}'.format(value)
473 497 except Exception as e:
474 498 state = {'message': str(e), 'type': STATE_ERR}
475 499 return SysInfoRes(value=value, state=state, human_value=human_value)
476 500
477 501
478 502 def svn_info():
479 503 from rhodecode.lib.vcs.backends import svn
480 504 state = STATE_OK_DEFAULT
481 505 value = human_value = ''
482 506 try:
483 507 value = svn.discover_svn_version(raise_on_exc=True)
484 508 human_value = 'version reported from VCSServer: {}'.format(value)
485 509 except Exception as e:
486 510 state = {'message': str(e), 'type': STATE_ERR}
487 511 return SysInfoRes(value=value, state=state, human_value=human_value)
488 512
489 513
490 514 def vcs_backends():
491 515 import rhodecode
492 516 value = rhodecode.CONFIG.get('vcs.backends')
493 517 human_value = 'Enabled backends in order: {}'.format(','.join(value))
494 518 return SysInfoRes(value=value, human_value=human_value)
495 519
496 520
497 521 def vcs_server():
498 522 import rhodecode
499 523 from rhodecode.lib.vcs.backends import get_vcsserver_service_data
500 524
501 525 server_url = rhodecode.CONFIG.get('vcs.server')
502 526 enabled = rhodecode.CONFIG.get('vcs.server.enable')
503 527 protocol = rhodecode.CONFIG.get('vcs.server.protocol') or 'http'
504 528 state = STATE_OK_DEFAULT
505 529 version = None
506 530 workers = 0
507 531
508 532 try:
509 533 data = get_vcsserver_service_data()
510 534 if data and 'version' in data:
511 535 version = data['version']
512 536
513 537 if data and 'config' in data:
514 538 conf = data['config']
515 539 workers = conf.get('workers', 'NOT AVAILABLE')
516 540
517 541 connection = 'connected'
518 542 except Exception as e:
519 543 connection = 'failed'
520 544 state = {'message': str(e), 'type': STATE_ERR}
521 545
522 546 value = dict(
523 547 url=server_url,
524 548 enabled=enabled,
525 549 protocol=protocol,
526 550 connection=connection,
527 551 version=version,
528 552 text='',
529 553 )
530 554
531 555 human_value = value.copy()
532 556 human_value['text'] = \
533 557 '{url}@ver:{ver} via {mode} mode[workers:{workers}], connection:{conn}'.format(
534 558 url=server_url, ver=version, workers=workers, mode=protocol,
535 559 conn=connection)
536 560
537 561 return SysInfoRes(value=value, state=state, human_value=human_value)
538 562
539 563
540 564 def rhodecode_app_info():
541 565 import rhodecode
542 566 edition = rhodecode.CONFIG.get('rhodecode.edition')
543 567
544 568 value = dict(
545 569 rhodecode_version=rhodecode.__version__,
546 570 rhodecode_lib_path=os.path.abspath(rhodecode.__file__),
547 571 text=''
548 572 )
549 573 human_value = value.copy()
550 574 human_value['text'] = 'RhodeCode {edition}, version {ver}'.format(
551 575 edition=edition, ver=value['rhodecode_version']
552 576 )
553 577 return SysInfoRes(value=value, human_value=human_value)
554 578
555 579
556 580 def rhodecode_config():
557 581 import rhodecode
558 582 path = rhodecode.CONFIG.get('__file__')
559 583 rhodecode_ini_safe = rhodecode.CONFIG.copy()
560 584
561 585 try:
562 586 config = configparser.ConfigParser()
563 587 config.read(path)
564 588 parsed_ini = config
565 589 if parsed_ini.has_section('server:main'):
566 590 parsed_ini = dict(parsed_ini.items('server:main'))
567 591 except Exception:
568 592 log.exception('Failed to read .ini file for display')
569 593 parsed_ini = {}
570 594
571 595 cert_path = os.path.join(
572 596 os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(path)))),
573 597 '.rccontrol-profile/etc/ca-bundle.crt')
574 598
575 599 rhodecode_ini_safe['server:main'] = parsed_ini
576 600
577 601 blacklist = [
578 602 'rhodecode_license_key',
579 603 'routes.map',
580 604 'sqlalchemy.db1.url',
581 605 'channelstream.secret',
582 606 'beaker.session.secret',
583 607 'rhodecode.encrypted_values.secret',
584 608 'rhodecode_auth_github_consumer_key',
585 609 'rhodecode_auth_github_consumer_secret',
586 610 'rhodecode_auth_google_consumer_key',
587 611 'rhodecode_auth_google_consumer_secret',
588 612 'rhodecode_auth_bitbucket_consumer_secret',
589 613 'rhodecode_auth_bitbucket_consumer_key',
590 614 'rhodecode_auth_twitter_consumer_secret',
591 615 'rhodecode_auth_twitter_consumer_key',
592 616
593 617 'rhodecode_auth_twitter_secret',
594 618 'rhodecode_auth_github_secret',
595 619 'rhodecode_auth_google_secret',
596 620 'rhodecode_auth_bitbucket_secret',
597 621
598 622 'appenlight.api_key',
599 623 ('app_conf', 'sqlalchemy.db1.url')
600 624 ]
601 625 for k in blacklist:
602 626 if isinstance(k, tuple):
603 627 section, key = k
604 628 if section in rhodecode_ini_safe:
605 629 rhodecode_ini_safe[section] = '**OBFUSCATED**'
606 630 else:
607 631 rhodecode_ini_safe.pop(k, None)
608 632
609 633 # TODO: maybe put some CONFIG checks here ?
610 634 return SysInfoRes(value={'config': rhodecode_ini_safe,
611 635 'path': path, 'cert_path': cert_path})
612 636
613 637
614 638 def database_info():
615 639 import rhodecode
616 640 from sqlalchemy.engine import url as engine_url
617 641 from rhodecode.model.meta import Base as sql_base, Session
618 642 from rhodecode.model.db import DbMigrateVersion
619 643
620 644 state = STATE_OK_DEFAULT
621 645
622 646 db_migrate = DbMigrateVersion.query().filter(
623 647 DbMigrateVersion.repository_id == 'rhodecode_db_migrations').one()
624 648
625 649 db_url_obj = engine_url.make_url(rhodecode.CONFIG['sqlalchemy.db1.url'])
626 650
627 651 try:
628 652 engine = sql_base.metadata.bind
629 653 db_server_info = engine.dialect._get_server_version_info(
630 654 Session.connection(bind=engine))
631 655 db_version = '.'.join(map(str, db_server_info))
632 656 except Exception:
633 657 log.exception('failed to fetch db version')
634 658 db_version = 'UNKNOWN'
635 659
636 660 db_info = dict(
637 661 migrate_version=db_migrate.version,
638 662 type=db_url_obj.get_backend_name(),
639 663 version=db_version,
640 664 url=repr(db_url_obj)
641 665 )
642 666 current_version = db_migrate.version
643 667 expected_version = rhodecode.__dbversion__
644 668 if state['type'] == STATE_OK and current_version != expected_version:
645 669 msg = 'Critical: database schema mismatch, ' \
646 670 'expected version {}, got {}. ' \
647 671 'Please run migrations on your database.'.format(
648 672 expected_version, current_version)
649 673 state = {'message': msg, 'type': STATE_ERR}
650 674
651 675 human_value = db_info.copy()
652 676 human_value['url'] = "{} @ migration version: {}".format(
653 677 db_info['url'], db_info['migrate_version'])
654 678 human_value['version'] = "{} {}".format(db_info['type'], db_info['version'])
655 679 return SysInfoRes(value=db_info, state=state, human_value=human_value)
656 680
657 681
658 682 def server_info(environ):
659 683 import rhodecode
660 684 from rhodecode.lib.base import get_server_ip_addr, get_server_port
661 685
662 686 value = {
663 687 'server_ip': '%s:%s' % (
664 688 get_server_ip_addr(environ, log_errors=False),
665 689 get_server_port(environ)
666 690 ),
667 691 'server_id': rhodecode.CONFIG.get('instance_id'),
668 692 }
669 693 return SysInfoRes(value=value)
670 694
671 695
672 696 def usage_info():
673 697 from rhodecode.model.db import User, Repository
674 698 value = {
675 699 'users': User.query().count(),
676 700 'users_active': User.query().filter(User.active == True).count(),
677 701 'repositories': Repository.query().count(),
678 702 'repository_types': {
679 703 'hg': Repository.query().filter(
680 704 Repository.repo_type == 'hg').count(),
681 705 'git': Repository.query().filter(
682 706 Repository.repo_type == 'git').count(),
683 707 'svn': Repository.query().filter(
684 708 Repository.repo_type == 'svn').count(),
685 709 },
686 710 }
687 711 return SysInfoRes(value=value)
688 712
689 713
714
690 715 def get_system_info(environ):
691 716 environ = environ or {}
692 717 return {
693 718 'rhodecode_app': SysInfo(rhodecode_app_info)(),
694 719 'rhodecode_config': SysInfo(rhodecode_config)(),
695 720 'rhodecode_usage': SysInfo(usage_info)(),
696 721 'python': SysInfo(python_info)(),
697 722 'py_modules': SysInfo(py_modules)(),
698 723
699 724 'platform': SysInfo(platform_type)(),
700 725 'server': SysInfo(server_info, environ=environ)(),
701 726 'database': SysInfo(database_info)(),
702
727 'ulimit': SysInfo(ulimit_info)(),
703 728 'storage': SysInfo(storage)(),
704 729 'storage_inodes': SysInfo(storage_inodes)(),
705 730 'storage_archive': SysInfo(storage_archives)(),
706 731 'storage_gist': SysInfo(storage_gist)(),
707 732 'storage_temp': SysInfo(storage_temp)(),
708 733
709 734 'search': SysInfo(search_info)(),
710 735
711 736 'uptime': SysInfo(uptime)(),
712 737 'load': SysInfo(machine_load)(),
713 738 'cpu': SysInfo(cpu)(),
714 739 'memory': SysInfo(memory)(),
715 740
716 741 'vcs_backends': SysInfo(vcs_backends)(),
717 742 'vcs_server': SysInfo(vcs_server)(),
718 743
719 744 'git': SysInfo(git_info)(),
720 745 'hg': SysInfo(hg_info)(),
721 746 'svn': SysInfo(svn_info)(),
722 747 }
General Comments 0
You need to be logged in to leave comments. Login now