##// END OF EJS Templates
sys-info: added helpers for faster loading of sys-info data
super-admin -
r4734:ee91c6f6 stable
parent child Browse files
Show More
@@ -1,797 +1,853 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2017-2020 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 26 import collections
27 from functools import wraps
28
27 29 import pkg_resources
28 30 import logging
29 31 import resource
30 32
31 33 from pyramid.compat import configparser
32 34
33 35 log = logging.getLogger(__name__)
34 36
35 37
36 38 psutil = None
37 39
38 40 try:
39 41 # cygwin cannot have yet psutil support.
40 42 import psutil as psutil
41 43 except ImportError:
42 44 pass
43 45
44 46
45 47 _NA = 'NOT AVAILABLE'
46 48
47 49 STATE_OK = 'ok'
48 50 STATE_ERR = 'error'
49 51 STATE_WARN = 'warning'
50 52
51 53 STATE_OK_DEFAULT = {'message': '', 'type': STATE_OK}
52 54
53 55
56 registered_helpers = {}
57
58
59 def register_sysinfo(func):
60 """
61 @register_helper
62 def db_check():
63 pass
64
65 db_check == registered_helpers['db_check']
66 """
67 global registered_helpers
68 registered_helpers[func.__name__] = func
69
70 @wraps(func)
71 def _wrapper(*args, **kwargs):
72 return func(*args, **kwargs)
73 return _wrapper
74
75
54 76 # HELPERS
55 77 def percentage(part, whole):
56 78 whole = float(whole)
57 79 if whole > 0:
58 80 return round(100 * float(part) / whole, 1)
59 81 return 0.0
60 82
61 83
62 84 def get_storage_size(storage_path):
63 85 sizes = []
64 86 for file_ in os.listdir(storage_path):
65 87 storage_file = os.path.join(storage_path, file_)
66 88 if os.path.isfile(storage_file):
67 89 try:
68 90 sizes.append(os.path.getsize(storage_file))
69 91 except OSError:
70 92 log.exception('Failed to get size of storage file %s', storage_file)
71 93 pass
72 94
73 95 return sum(sizes)
74 96
75 97
76 98 def get_resource(resource_type):
77 99 try:
78 100 return resource.getrlimit(resource_type)
79 101 except Exception:
80 102 return 'NOT_SUPPORTED'
81 103
82 104
83 105 def get_cert_path(ini_path):
84 106 default = '/etc/ssl/certs/ca-certificates.crt'
85 107 control_ca_bundle = os.path.join(
86 108 os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(ini_path)))),
87 109 '.rccontrol-profile/etc/ca-bundle.crt')
88 110 if os.path.isfile(control_ca_bundle):
89 111 default = control_ca_bundle
90 112
91 113 return default
92 114
93 115
94 116 class SysInfoRes(object):
95 117 def __init__(self, value, state=None, human_value=None):
96 118 self.value = value
97 119 self.state = state or STATE_OK_DEFAULT
98 120 self.human_value = human_value or value
99 121
100 122 def __json__(self):
101 123 return {
102 124 'value': self.value,
103 125 'state': self.state,
104 126 'human_value': self.human_value,
105 127 }
106 128
107 129 def get_value(self):
108 130 return self.__json__()
109 131
110 132 def __str__(self):
111 133 return '<SysInfoRes({})>'.format(self.__json__())
112 134
113 135
114 136 class SysInfo(object):
115 137
116 138 def __init__(self, func_name, **kwargs):
117 139 self.func_name = func_name
118 140 self.value = _NA
119 141 self.state = None
120 142 self.kwargs = kwargs or {}
121 143
122 144 def __call__(self):
123 145 computed = self.compute(**self.kwargs)
124 146 if not isinstance(computed, SysInfoRes):
125 147 raise ValueError(
126 148 'computed value for {} is not instance of '
127 149 '{}, got {} instead'.format(
128 150 self.func_name, SysInfoRes, type(computed)))
129 151 return computed.__json__()
130 152
131 153 def __str__(self):
132 154 return '<SysInfo({})>'.format(self.func_name)
133 155
134 156 def compute(self, **kwargs):
135 157 return self.func_name(**kwargs)
136 158
137 159
138 160 # SysInfo functions
161 @register_sysinfo
139 162 def python_info():
140 163 value = dict(version=' '.join(platform._sys_version()),
141 164 executable=sys.executable)
142 165 return SysInfoRes(value=value)
143 166
144 167
168 @register_sysinfo
145 169 def py_modules():
146 170 mods = dict([(p.project_name, {'version': p.version, 'location': p.location})
147 171 for p in pkg_resources.working_set])
148 172
149 173 value = sorted(mods.items(), key=lambda k: k[0].lower())
150 174 return SysInfoRes(value=value)
151 175
152 176
177 @register_sysinfo
153 178 def platform_type():
154 179 from rhodecode.lib.utils import safe_unicode, generate_platform_uuid
155 180
156 181 value = dict(
157 182 name=safe_unicode(platform.platform()),
158 183 uuid=generate_platform_uuid()
159 184 )
160 185 return SysInfoRes(value=value)
161 186
162 187
188 @register_sysinfo
163 189 def locale_info():
164 190 import locale
165 191
166 192 value = dict(
167 193 locale_default=locale.getdefaultlocale(),
168 194 locale_lc_all=locale.getlocale(locale.LC_ALL),
169 195 lang_env=os.environ.get('LANG'),
170 196 lc_all_env=os.environ.get('LC_ALL'),
171 197 local_archive_env=os.environ.get('LOCALE_ARCHIVE'),
172 198 )
173 199 human_value = 'LANG: {}, locale LC_ALL: {}, Default locales: {}'.format(
174 200 value['lang_env'], value['locale_lc_all'], value['locale_default'])
175 201 return SysInfoRes(value=value, human_value=human_value)
176 202
177 203
204 @register_sysinfo
178 205 def ulimit_info():
179 206 data = collections.OrderedDict([
180 207 ('cpu time (seconds)', get_resource(resource.RLIMIT_CPU)),
181 208 ('file size', get_resource(resource.RLIMIT_FSIZE)),
182 209 ('stack size', get_resource(resource.RLIMIT_STACK)),
183 210 ('core file size', get_resource(resource.RLIMIT_CORE)),
184 211 ('address space size', get_resource(resource.RLIMIT_AS)),
185 212 ('locked in mem size', get_resource(resource.RLIMIT_MEMLOCK)),
186 213 ('heap size', get_resource(resource.RLIMIT_DATA)),
187 214 ('rss size', get_resource(resource.RLIMIT_RSS)),
188 215 ('number of processes', get_resource(resource.RLIMIT_NPROC)),
189 216 ('open files', get_resource(resource.RLIMIT_NOFILE)),
190 217 ])
191 218
192 219 text = ', '.join('{}:{}'.format(k, v) for k, v in data.items())
193 220
194 221 value = {
195 222 'limits': data,
196 223 'text': text,
197 224 }
198 225 return SysInfoRes(value=value)
199 226
200 227
228 @register_sysinfo
201 229 def uptime():
202 230 from rhodecode.lib.helpers import age, time_to_datetime
203 231 from rhodecode.translation import TranslationString
204 232
205 233 value = dict(boot_time=0, uptime=0, text='')
206 234 state = STATE_OK_DEFAULT
207 235 if not psutil:
208 236 return SysInfoRes(value=value, state=state)
209 237
210 238 boot_time = psutil.boot_time()
211 239 value['boot_time'] = boot_time
212 240 value['uptime'] = time.time() - boot_time
213 241
214 242 date_or_age = age(time_to_datetime(boot_time))
215 243 if isinstance(date_or_age, TranslationString):
216 244 date_or_age = date_or_age.interpolate()
217 245
218 246 human_value = value.copy()
219 247 human_value['boot_time'] = time_to_datetime(boot_time)
220 248 human_value['uptime'] = age(time_to_datetime(boot_time), show_suffix=False)
221 249
222 250 human_value['text'] = u'Server started {}'.format(date_or_age)
223 251 return SysInfoRes(value=value, human_value=human_value)
224 252
225 253
254 @register_sysinfo
226 255 def memory():
227 256 from rhodecode.lib.helpers import format_byte_size_binary
228 257 value = dict(available=0, used=0, used_real=0, cached=0, percent=0,
229 258 percent_used=0, free=0, inactive=0, active=0, shared=0,
230 259 total=0, buffers=0, text='')
231 260
232 261 state = STATE_OK_DEFAULT
233 262 if not psutil:
234 263 return SysInfoRes(value=value, state=state)
235 264
236 265 value.update(dict(psutil.virtual_memory()._asdict()))
237 266 value['used_real'] = value['total'] - value['available']
238 267 value['percent_used'] = psutil._common.usage_percent(
239 268 value['used_real'], value['total'], 1)
240 269
241 270 human_value = value.copy()
242 271 human_value['text'] = '%s/%s, %s%% used' % (
243 272 format_byte_size_binary(value['used_real']),
244 273 format_byte_size_binary(value['total']),
245 274 value['percent_used'],)
246 275
247 276 keys = value.keys()[::]
248 277 keys.pop(keys.index('percent'))
249 278 keys.pop(keys.index('percent_used'))
250 279 keys.pop(keys.index('text'))
251 280 for k in keys:
252 281 human_value[k] = format_byte_size_binary(value[k])
253 282
254 283 if state['type'] == STATE_OK and value['percent_used'] > 90:
255 284 msg = 'Critical: your available RAM memory is very low.'
256 285 state = {'message': msg, 'type': STATE_ERR}
257 286
258 287 elif state['type'] == STATE_OK and value['percent_used'] > 70:
259 288 msg = 'Warning: your available RAM memory is running low.'
260 289 state = {'message': msg, 'type': STATE_WARN}
261 290
262 291 return SysInfoRes(value=value, state=state, human_value=human_value)
263 292
264 293
294 @register_sysinfo
265 295 def machine_load():
266 296 value = {'1_min': _NA, '5_min': _NA, '15_min': _NA, 'text': ''}
267 297 state = STATE_OK_DEFAULT
268 298 if not psutil:
269 299 return SysInfoRes(value=value, state=state)
270 300
271 301 # load averages
272 302 if hasattr(psutil.os, 'getloadavg'):
273 303 value.update(dict(
274 304 zip(['1_min', '5_min', '15_min'], psutil.os.getloadavg())))
275 305
276 306 human_value = value.copy()
277 307 human_value['text'] = '1min: {}, 5min: {}, 15min: {}'.format(
278 308 value['1_min'], value['5_min'], value['15_min'])
279 309
280 310 if state['type'] == STATE_OK and value['15_min'] > 5:
281 311 msg = 'Warning: your machine load is very high.'
282 312 state = {'message': msg, 'type': STATE_WARN}
283 313
284 314 return SysInfoRes(value=value, state=state, human_value=human_value)
285 315
286 316
317 @register_sysinfo
287 318 def cpu():
288 319 value = {'cpu': 0, 'cpu_count': 0, 'cpu_usage': []}
289 320 state = STATE_OK_DEFAULT
290 321
291 322 if not psutil:
292 323 return SysInfoRes(value=value, state=state)
293 324
294 325 value['cpu'] = psutil.cpu_percent(0.5)
295 326 value['cpu_usage'] = psutil.cpu_percent(0.5, percpu=True)
296 327 value['cpu_count'] = psutil.cpu_count()
297 328
298 329 human_value = value.copy()
299 330 human_value['text'] = '{} cores at {} %'.format(
300 331 value['cpu_count'], value['cpu'])
301 332
302 333 return SysInfoRes(value=value, state=state, human_value=human_value)
303 334
304 335
336 @register_sysinfo
305 337 def storage():
306 338 from rhodecode.lib.helpers import format_byte_size_binary
307 339 from rhodecode.model.settings import VcsSettingsModel
308 340 path = VcsSettingsModel().get_repos_location()
309 341
310 342 value = dict(percent=0, used=0, total=0, path=path, text='')
311 343 state = STATE_OK_DEFAULT
312 344 if not psutil:
313 345 return SysInfoRes(value=value, state=state)
314 346
315 347 try:
316 348 value.update(dict(psutil.disk_usage(path)._asdict()))
317 349 except Exception as e:
318 350 log.exception('Failed to fetch disk info')
319 351 state = {'message': str(e), 'type': STATE_ERR}
320 352
321 353 human_value = value.copy()
322 354 human_value['used'] = format_byte_size_binary(value['used'])
323 355 human_value['total'] = format_byte_size_binary(value['total'])
324 356 human_value['text'] = "{}/{}, {}% used".format(
325 357 format_byte_size_binary(value['used']),
326 358 format_byte_size_binary(value['total']),
327 359 value['percent'])
328 360
329 361 if state['type'] == STATE_OK and value['percent'] > 90:
330 362 msg = 'Critical: your disk space is very low.'
331 363 state = {'message': msg, 'type': STATE_ERR}
332 364
333 365 elif state['type'] == STATE_OK and value['percent'] > 70:
334 366 msg = 'Warning: your disk space is running low.'
335 367 state = {'message': msg, 'type': STATE_WARN}
336 368
337 369 return SysInfoRes(value=value, state=state, human_value=human_value)
338 370
339 371
372 @register_sysinfo
340 373 def storage_inodes():
341 374 from rhodecode.model.settings import VcsSettingsModel
342 375 path = VcsSettingsModel().get_repos_location()
343 376
344 377 value = dict(percent=0, free=0, used=0, total=0, path=path, text='')
345 378 state = STATE_OK_DEFAULT
346 379 if not psutil:
347 380 return SysInfoRes(value=value, state=state)
348 381
349 382 try:
350 383 i_stat = os.statvfs(path)
351 384 value['free'] = i_stat.f_ffree
352 385 value['used'] = i_stat.f_files-i_stat.f_favail
353 386 value['total'] = i_stat.f_files
354 387 value['percent'] = percentage(value['used'], value['total'])
355 388 except Exception as e:
356 389 log.exception('Failed to fetch disk inodes info')
357 390 state = {'message': str(e), 'type': STATE_ERR}
358 391
359 392 human_value = value.copy()
360 393 human_value['text'] = "{}/{}, {}% used".format(
361 394 value['used'], value['total'], value['percent'])
362 395
363 396 if state['type'] == STATE_OK and value['percent'] > 90:
364 397 msg = 'Critical: your disk free inodes are very low.'
365 398 state = {'message': msg, 'type': STATE_ERR}
366 399
367 400 elif state['type'] == STATE_OK and value['percent'] > 70:
368 401 msg = 'Warning: your disk free inodes are running low.'
369 402 state = {'message': msg, 'type': STATE_WARN}
370 403
371 404 return SysInfoRes(value=value, state=state, human_value=human_value)
372 405
373 406
407 @register_sysinfo
374 408 def storage_archives():
375 409 import rhodecode
376 410 from rhodecode.lib.utils import safe_str
377 411 from rhodecode.lib.helpers import format_byte_size_binary
378 412
379 413 msg = 'Enable this by setting ' \
380 414 'archive_cache_dir=/path/to/cache option in the .ini file'
381 415 path = safe_str(rhodecode.CONFIG.get('archive_cache_dir', msg))
382 416
383 417 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
384 418 state = STATE_OK_DEFAULT
385 419 try:
386 420 items_count = 0
387 421 used = 0
388 422 for root, dirs, files in os.walk(path):
389 423 if root == path:
390 424 items_count = len(files)
391 425
392 426 for f in files:
393 427 try:
394 428 used += os.path.getsize(os.path.join(root, f))
395 429 except OSError:
396 430 pass
397 431 value.update({
398 432 'percent': 100,
399 433 'used': used,
400 434 'total': used,
401 435 'items': items_count
402 436 })
403 437
404 438 except Exception as e:
405 439 log.exception('failed to fetch archive cache storage')
406 440 state = {'message': str(e), 'type': STATE_ERR}
407 441
408 442 human_value = value.copy()
409 443 human_value['used'] = format_byte_size_binary(value['used'])
410 444 human_value['total'] = format_byte_size_binary(value['total'])
411 445 human_value['text'] = "{} ({} items)".format(
412 446 human_value['used'], value['items'])
413 447
414 448 return SysInfoRes(value=value, state=state, human_value=human_value)
415 449
416 450
451 @register_sysinfo
417 452 def storage_gist():
418 453 from rhodecode.model.gist import GIST_STORE_LOC
419 454 from rhodecode.model.settings import VcsSettingsModel
420 455 from rhodecode.lib.utils import safe_str
421 456 from rhodecode.lib.helpers import format_byte_size_binary
422 457 path = safe_str(os.path.join(
423 458 VcsSettingsModel().get_repos_location(), GIST_STORE_LOC))
424 459
425 460 # gist storage
426 461 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
427 462 state = STATE_OK_DEFAULT
428 463
429 464 try:
430 465 items_count = 0
431 466 used = 0
432 467 for root, dirs, files in os.walk(path):
433 468 if root == path:
434 469 items_count = len(dirs)
435 470
436 471 for f in files:
437 472 try:
438 473 used += os.path.getsize(os.path.join(root, f))
439 474 except OSError:
440 475 pass
441 476 value.update({
442 477 'percent': 100,
443 478 'used': used,
444 479 'total': used,
445 480 'items': items_count
446 481 })
447 482 except Exception as e:
448 483 log.exception('failed to fetch gist storage items')
449 484 state = {'message': str(e), 'type': STATE_ERR}
450 485
451 486 human_value = value.copy()
452 487 human_value['used'] = format_byte_size_binary(value['used'])
453 488 human_value['total'] = format_byte_size_binary(value['total'])
454 489 human_value['text'] = "{} ({} items)".format(
455 490 human_value['used'], value['items'])
456 491
457 492 return SysInfoRes(value=value, state=state, human_value=human_value)
458 493
459 494
495 @register_sysinfo
460 496 def storage_temp():
461 497 import tempfile
462 498 from rhodecode.lib.helpers import format_byte_size_binary
463 499
464 500 path = tempfile.gettempdir()
465 501 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
466 502 state = STATE_OK_DEFAULT
467 503
468 504 if not psutil:
469 505 return SysInfoRes(value=value, state=state)
470 506
471 507 try:
472 508 value.update(dict(psutil.disk_usage(path)._asdict()))
473 509 except Exception as e:
474 510 log.exception('Failed to fetch temp dir info')
475 511 state = {'message': str(e), 'type': STATE_ERR}
476 512
477 513 human_value = value.copy()
478 514 human_value['used'] = format_byte_size_binary(value['used'])
479 515 human_value['total'] = format_byte_size_binary(value['total'])
480 516 human_value['text'] = "{}/{}, {}% used".format(
481 517 format_byte_size_binary(value['used']),
482 518 format_byte_size_binary(value['total']),
483 519 value['percent'])
484 520
485 521 return SysInfoRes(value=value, state=state, human_value=human_value)
486 522
487 523
524 @register_sysinfo
488 525 def search_info():
489 526 import rhodecode
490 527 from rhodecode.lib.index import searcher_from_config
491 528
492 529 backend = rhodecode.CONFIG.get('search.module', '')
493 530 location = rhodecode.CONFIG.get('search.location', '')
494 531
495 532 try:
496 533 searcher = searcher_from_config(rhodecode.CONFIG)
497 534 searcher = searcher.__class__.__name__
498 535 except Exception:
499 536 searcher = None
500 537
501 538 value = dict(
502 539 backend=backend, searcher=searcher, location=location, text='')
503 540 state = STATE_OK_DEFAULT
504 541
505 542 human_value = value.copy()
506 543 human_value['text'] = "backend:`{}`".format(human_value['backend'])
507 544
508 545 return SysInfoRes(value=value, state=state, human_value=human_value)
509 546
510 547
548 @register_sysinfo
511 549 def git_info():
512 550 from rhodecode.lib.vcs.backends import git
513 551 state = STATE_OK_DEFAULT
514 552 value = human_value = ''
515 553 try:
516 554 value = git.discover_git_version(raise_on_exc=True)
517 555 human_value = 'version reported from VCSServer: {}'.format(value)
518 556 except Exception as e:
519 557 state = {'message': str(e), 'type': STATE_ERR}
520 558
521 559 return SysInfoRes(value=value, state=state, human_value=human_value)
522 560
523 561
562 @register_sysinfo
524 563 def hg_info():
525 564 from rhodecode.lib.vcs.backends import hg
526 565 state = STATE_OK_DEFAULT
527 566 value = human_value = ''
528 567 try:
529 568 value = hg.discover_hg_version(raise_on_exc=True)
530 569 human_value = 'version reported from VCSServer: {}'.format(value)
531 570 except Exception as e:
532 571 state = {'message': str(e), 'type': STATE_ERR}
533 572 return SysInfoRes(value=value, state=state, human_value=human_value)
534 573
535 574
575 @register_sysinfo
536 576 def svn_info():
537 577 from rhodecode.lib.vcs.backends import svn
538 578 state = STATE_OK_DEFAULT
539 579 value = human_value = ''
540 580 try:
541 581 value = svn.discover_svn_version(raise_on_exc=True)
542 582 human_value = 'version reported from VCSServer: {}'.format(value)
543 583 except Exception as e:
544 584 state = {'message': str(e), 'type': STATE_ERR}
545 585 return SysInfoRes(value=value, state=state, human_value=human_value)
546 586
547 587
588 @register_sysinfo
548 589 def vcs_backends():
549 590 import rhodecode
550 591 value = rhodecode.CONFIG.get('vcs.backends')
551 592 human_value = 'Enabled backends in order: {}'.format(','.join(value))
552 593 return SysInfoRes(value=value, human_value=human_value)
553 594
554 595
596 @register_sysinfo
555 597 def vcs_server():
556 598 import rhodecode
557 599 from rhodecode.lib.vcs.backends import get_vcsserver_service_data
558 600
559 601 server_url = rhodecode.CONFIG.get('vcs.server')
560 602 enabled = rhodecode.CONFIG.get('vcs.server.enable')
561 603 protocol = rhodecode.CONFIG.get('vcs.server.protocol') or 'http'
562 604 state = STATE_OK_DEFAULT
563 605 version = None
564 606 workers = 0
565 607
566 608 try:
567 609 data = get_vcsserver_service_data()
568 610 if data and 'version' in data:
569 611 version = data['version']
570 612
571 613 if data and 'config' in data:
572 614 conf = data['config']
573 615 workers = conf.get('workers', 'NOT AVAILABLE')
574 616
575 617 connection = 'connected'
576 618 except Exception as e:
577 619 connection = 'failed'
578 620 state = {'message': str(e), 'type': STATE_ERR}
579 621
580 622 value = dict(
581 623 url=server_url,
582 624 enabled=enabled,
583 625 protocol=protocol,
584 626 connection=connection,
585 627 version=version,
586 628 text='',
587 629 )
588 630
589 631 human_value = value.copy()
590 632 human_value['text'] = \
591 633 '{url}@ver:{ver} via {mode} mode[workers:{workers}], connection:{conn}'.format(
592 634 url=server_url, ver=version, workers=workers, mode=protocol,
593 635 conn=connection)
594 636
595 637 return SysInfoRes(value=value, state=state, human_value=human_value)
596 638
597 639
640 @register_sysinfo
598 641 def vcs_server_config():
599 642 from rhodecode.lib.vcs.backends import get_vcsserver_service_data
600 643 state = STATE_OK_DEFAULT
601 644
602 645 value = {}
603 646 try:
604 647 data = get_vcsserver_service_data()
605 648 value = data['app_config']
606 649 except Exception as e:
607 650 state = {'message': str(e), 'type': STATE_ERR}
608 651
609 652 human_value = value.copy()
610 653 human_value['text'] = 'VCS Server config'
611 654
612 655 return SysInfoRes(value=value, state=state, human_value=human_value)
613 656
614 657
658 @register_sysinfo
615 659 def rhodecode_app_info():
616 660 import rhodecode
617 661 edition = rhodecode.CONFIG.get('rhodecode.edition')
618 662
619 663 value = dict(
620 664 rhodecode_version=rhodecode.__version__,
621 665 rhodecode_lib_path=os.path.abspath(rhodecode.__file__),
622 666 text=''
623 667 )
624 668 human_value = value.copy()
625 669 human_value['text'] = 'RhodeCode {edition}, version {ver}'.format(
626 670 edition=edition, ver=value['rhodecode_version']
627 671 )
628 672 return SysInfoRes(value=value, human_value=human_value)
629 673
630 674
675 @register_sysinfo
631 676 def rhodecode_config():
632 677 import rhodecode
633 678 path = rhodecode.CONFIG.get('__file__')
634 679 rhodecode_ini_safe = rhodecode.CONFIG.copy()
635 680 cert_path = get_cert_path(path)
636 681
637 682 try:
638 683 config = configparser.ConfigParser()
639 684 config.read(path)
640 685 parsed_ini = config
641 686 if parsed_ini.has_section('server:main'):
642 687 parsed_ini = dict(parsed_ini.items('server:main'))
643 688 except Exception:
644 689 log.exception('Failed to read .ini file for display')
645 690 parsed_ini = {}
646 691
647 692 rhodecode_ini_safe['server:main'] = parsed_ini
648 693
649 694 blacklist = [
650 695 'rhodecode_license_key',
651 696 'routes.map',
652 697 'sqlalchemy.db1.url',
653 698 'channelstream.secret',
654 699 'beaker.session.secret',
655 700 'rhodecode.encrypted_values.secret',
656 701 'rhodecode_auth_github_consumer_key',
657 702 'rhodecode_auth_github_consumer_secret',
658 703 'rhodecode_auth_google_consumer_key',
659 704 'rhodecode_auth_google_consumer_secret',
660 705 'rhodecode_auth_bitbucket_consumer_secret',
661 706 'rhodecode_auth_bitbucket_consumer_key',
662 707 'rhodecode_auth_twitter_consumer_secret',
663 708 'rhodecode_auth_twitter_consumer_key',
664 709
665 710 'rhodecode_auth_twitter_secret',
666 711 'rhodecode_auth_github_secret',
667 712 'rhodecode_auth_google_secret',
668 713 'rhodecode_auth_bitbucket_secret',
669 714
670 715 'appenlight.api_key',
671 716 ('app_conf', 'sqlalchemy.db1.url')
672 717 ]
673 718 for k in blacklist:
674 719 if isinstance(k, tuple):
675 720 section, key = k
676 721 if section in rhodecode_ini_safe:
677 722 rhodecode_ini_safe[section] = '**OBFUSCATED**'
678 723 else:
679 724 rhodecode_ini_safe.pop(k, None)
680 725
681 726 # TODO: maybe put some CONFIG checks here ?
682 727 return SysInfoRes(value={'config': rhodecode_ini_safe,
683 728 'path': path, 'cert_path': cert_path})
684 729
685 730
731 @register_sysinfo
686 732 def database_info():
687 733 import rhodecode
688 734 from sqlalchemy.engine import url as engine_url
689 735 from rhodecode.model.meta import Base as sql_base, Session
690 736 from rhodecode.model.db import DbMigrateVersion
691 737
692 738 state = STATE_OK_DEFAULT
693 739
694 740 db_migrate = DbMigrateVersion.query().filter(
695 741 DbMigrateVersion.repository_id == 'rhodecode_db_migrations').one()
696 742
697 743 db_url_obj = engine_url.make_url(rhodecode.CONFIG['sqlalchemy.db1.url'])
698 744
699 745 try:
700 746 engine = sql_base.metadata.bind
701 747 db_server_info = engine.dialect._get_server_version_info(
702 748 Session.connection(bind=engine))
703 749 db_version = '.'.join(map(str, db_server_info))
704 750 except Exception:
705 751 log.exception('failed to fetch db version')
706 752 db_version = 'UNKNOWN'
707 753
708 754 db_info = dict(
709 755 migrate_version=db_migrate.version,
710 756 type=db_url_obj.get_backend_name(),
711 757 version=db_version,
712 758 url=repr(db_url_obj)
713 759 )
714 760 current_version = db_migrate.version
715 761 expected_version = rhodecode.__dbversion__
716 762 if state['type'] == STATE_OK and current_version != expected_version:
717 763 msg = 'Critical: database schema mismatch, ' \
718 764 'expected version {}, got {}. ' \
719 765 'Please run migrations on your database.'.format(
720 766 expected_version, current_version)
721 767 state = {'message': msg, 'type': STATE_ERR}
722 768
723 769 human_value = db_info.copy()
724 770 human_value['url'] = "{} @ migration version: {}".format(
725 771 db_info['url'], db_info['migrate_version'])
726 772 human_value['version'] = "{} {}".format(db_info['type'], db_info['version'])
727 773 return SysInfoRes(value=db_info, state=state, human_value=human_value)
728 774
729 775
776 @register_sysinfo
730 777 def server_info(environ):
731 778 import rhodecode
732 779 from rhodecode.lib.base import get_server_ip_addr, get_server_port
733 780
734 781 value = {
735 782 'server_ip': '%s:%s' % (
736 783 get_server_ip_addr(environ, log_errors=False),
737 784 get_server_port(environ)
738 785 ),
739 786 'server_id': rhodecode.CONFIG.get('instance_id'),
740 787 }
741 788 return SysInfoRes(value=value)
742 789
743 790
791 @register_sysinfo
744 792 def usage_info():
745 793 from rhodecode.model.db import User, Repository
746 794 value = {
747 795 'users': User.query().count(),
748 796 'users_active': User.query().filter(User.active == True).count(),
749 797 'repositories': Repository.query().count(),
750 798 'repository_types': {
751 799 'hg': Repository.query().filter(
752 800 Repository.repo_type == 'hg').count(),
753 801 'git': Repository.query().filter(
754 802 Repository.repo_type == 'git').count(),
755 803 'svn': Repository.query().filter(
756 804 Repository.repo_type == 'svn').count(),
757 805 },
758 806 }
759 807 return SysInfoRes(value=value)
760 808
761 809
762 810 def get_system_info(environ):
763 811 environ = environ or {}
764 812 return {
765 813 'rhodecode_app': SysInfo(rhodecode_app_info)(),
766 814 'rhodecode_config': SysInfo(rhodecode_config)(),
767 815 'rhodecode_usage': SysInfo(usage_info)(),
768 816 'python': SysInfo(python_info)(),
769 817 'py_modules': SysInfo(py_modules)(),
770 818
771 819 'platform': SysInfo(platform_type)(),
772 820 'locale': SysInfo(locale_info)(),
773 821 'server': SysInfo(server_info, environ=environ)(),
774 822 'database': SysInfo(database_info)(),
775 823 'ulimit': SysInfo(ulimit_info)(),
776 824 'storage': SysInfo(storage)(),
777 825 'storage_inodes': SysInfo(storage_inodes)(),
778 826 'storage_archive': SysInfo(storage_archives)(),
779 827 'storage_gist': SysInfo(storage_gist)(),
780 828 'storage_temp': SysInfo(storage_temp)(),
781 829
782 830 'search': SysInfo(search_info)(),
783 831
784 832 'uptime': SysInfo(uptime)(),
785 833 'load': SysInfo(machine_load)(),
786 834 'cpu': SysInfo(cpu)(),
787 835 'memory': SysInfo(memory)(),
788 836
789 837 'vcs_backends': SysInfo(vcs_backends)(),
790 838 'vcs_server': SysInfo(vcs_server)(),
791 839
792 840 'vcs_server_config': SysInfo(vcs_server_config)(),
793 841
794 842 'git': SysInfo(git_info)(),
795 843 'hg': SysInfo(hg_info)(),
796 844 'svn': SysInfo(svn_info)(),
797 845 }
846
847
848 def load_system_info(key):
849 """
850 get_sys_info('vcs_server')
851 get_sys_info('database')
852 """
853 return SysInfo(registered_helpers[key])()
General Comments 0
You need to be logged in to leave comments. Login now