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