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