##// END OF EJS Templates
system-info: use real memory usage based on new psutil api available from 4.0 release....
marcink -
r1116:6aa0fbf7 default
parent child Browse files
Show More
@@ -1,611 +1,612 b''
1 1 import os
2 2 import sys
3 3 import time
4 4 import platform
5 5 import pkg_resources
6 6 import logging
7 7 import string
8 8
9 9
10 10 log = logging.getLogger(__name__)
11 11
12 12
13 13 psutil = None
14 14
15 15 try:
16 16 # cygwin cannot have yet psutil support.
17 17 import psutil as psutil
18 18 except ImportError:
19 19 pass
20 20
21 21
22 22 _NA = 'NOT AVAILABLE'
23 23
24 24 STATE_OK = 'ok'
25 25 STATE_ERR = 'error'
26 26 STATE_WARN = 'warning'
27 27
28 28 STATE_OK_DEFAULT = {'message': '', 'type': STATE_OK}
29 29
30 30
31 31 # HELPERS
32 32 def percentage(part, whole):
33 33 whole = float(whole)
34 34 if whole > 0:
35 35 return 100 * float(part) / whole
36 36 return 0
37 37
38 38
39 39 def get_storage_size(storage_path):
40 40 sizes = []
41 41 for file_ in os.listdir(storage_path):
42 42 storage_file = os.path.join(storage_path, file_)
43 43 if os.path.isfile(storage_file):
44 44 try:
45 45 sizes.append(os.path.getsize(storage_file))
46 46 except OSError:
47 47 log.exception('Failed to get size of storage file %s',
48 48 storage_file)
49 49 pass
50 50
51 51 return sum(sizes)
52 52
53 53
54 54 class SysInfoRes(object):
55 55 def __init__(self, value, state=STATE_OK_DEFAULT, human_value=None):
56 56 self.value = value
57 57 self.state = state
58 58 self.human_value = human_value or value
59 59
60 60 def __json__(self):
61 61 return {
62 62 'value': self.value,
63 63 'state': self.state,
64 64 'human_value': self.human_value,
65 65 }
66 66
67 67 def __str__(self):
68 68 return '<SysInfoRes({})>'.format(self.__json__())
69 69
70 70
71 71 class SysInfo(object):
72 72
73 73 def __init__(self, func_name, **kwargs):
74 74 self.func_name = func_name
75 75 self.value = _NA
76 76 self.state = None
77 77 self.kwargs = kwargs or {}
78 78
79 79 def __call__(self):
80 80 computed = self.compute(**self.kwargs)
81 81 if not isinstance(computed, SysInfoRes):
82 82 raise ValueError(
83 83 'computed value for {} is not instance of '
84 84 '{}, got {} instead'.format(
85 85 self.func_name, SysInfoRes, type(computed)))
86 86 return computed.__json__()
87 87
88 88 def __str__(self):
89 89 return '<SysInfo({})>'.format(self.func_name)
90 90
91 91 def compute(self, **kwargs):
92 92 return self.func_name(**kwargs)
93 93
94 94
95 95 # SysInfo functions
96 96 def python_info():
97 97 value = dict(version=' '.join(platform._sys_version()),
98 98 executable=sys.executable)
99 99 return SysInfoRes(value=value)
100 100
101 101
102 102 def py_modules():
103 103 mods = dict([(p.project_name, p.version)
104 104 for p in pkg_resources.working_set])
105 105 value = sorted(mods.items(), key=lambda k: k[0].lower())
106 106 return SysInfoRes(value=value)
107 107
108 108
109 109 def platform_type():
110 110 from rhodecode.lib.utils import safe_unicode, generate_platform_uuid
111 111
112 112 value = dict(
113 113 name=safe_unicode(platform.platform()),
114 114 uuid=generate_platform_uuid()
115 115 )
116 116 return SysInfoRes(value=value)
117 117
118 118
119 119 def uptime():
120 120 from rhodecode.lib.helpers import age, time_to_datetime
121 121
122 122 value = dict(boot_time=0, uptime=0, text='')
123 123 state = STATE_OK_DEFAULT
124 124 if not psutil:
125 125 return SysInfoRes(value=value, state=state)
126 126
127 127 boot_time = psutil.boot_time()
128 128 value['boot_time'] = boot_time
129 129 value['uptime'] = time.time() - boot_time
130 130
131 131 human_value = value.copy()
132 132 human_value['boot_time'] = time_to_datetime(boot_time)
133 133 human_value['uptime'] = age(time_to_datetime(boot_time), show_suffix=False)
134 134 human_value['text'] = 'Server started {}'.format(
135 135 age(time_to_datetime(boot_time)))
136 136
137 137 return SysInfoRes(value=value, human_value=human_value)
138 138
139 139
140 140 def memory():
141 141 from rhodecode.lib.helpers import format_byte_size_binary
142 value = dict(available=0, used=0, cached=0, percent=0, percent_used=0,
143 free=0, inactive=0, active=0, shared=0, total=0, buffers=0,
144 text='')
142 value = dict(available=0, used=0, used_real=0, cached=0, percent=0,
143 percent_used=0, free=0, inactive=0, active=0, shared=0,
144 total=0, buffers=0, text='')
145 145
146 146 state = STATE_OK_DEFAULT
147 147 if not psutil:
148 148 return SysInfoRes(value=value, state=state)
149 149
150 150 value.update(dict(psutil.virtual_memory()._asdict()))
151 value['used_real'] = value['total'] - value['available']
151 152 value['percent_used'] = psutil._common.usage_percent(
152 (value['total'] - value['free']), value['total'], 1)
153 value['used_real'], value['total'], 1)
153 154
154 155 human_value = value.copy()
155 156 human_value['text'] = '%s/%s, %s%% used' % (
156 format_byte_size_binary(value['used']),
157 format_byte_size_binary(value['used_real']),
157 158 format_byte_size_binary(value['total']),
158 159 value['percent_used'],)
159 160
160 161 keys = value.keys()[::]
161 162 keys.pop(keys.index('percent'))
162 163 keys.pop(keys.index('percent_used'))
163 164 keys.pop(keys.index('text'))
164 165 for k in keys:
165 166 human_value[k] = format_byte_size_binary(value[k])
166 167
167 168 if state['type'] == STATE_OK and value['percent_used'] > 90:
168 169 msg = 'Critical: your available RAM memory is very low.'
169 170 state = {'message': msg, 'type': STATE_ERR}
170 171
171 172 elif state['type'] == STATE_OK and value['percent_used'] > 70:
172 173 msg = 'Warning: your available RAM memory is running low.'
173 174 state = {'message': msg, 'type': STATE_WARN}
174 175
175 176 return SysInfoRes(value=value, state=state, human_value=human_value)
176 177
177 178
178 179 def machine_load():
179 180 value = {'1_min': _NA, '5_min': _NA, '15_min': _NA, 'text': ''}
180 181 state = STATE_OK_DEFAULT
181 182 if not psutil:
182 183 return SysInfoRes(value=value, state=state)
183 184
184 185 # load averages
185 186 if hasattr(psutil.os, 'getloadavg'):
186 187 value.update(dict(
187 188 zip(['1_min', '5_min', '15_min'], psutil.os.getloadavg())))
188 189
189 190 human_value = value.copy()
190 191 human_value['text'] = '1min: {}, 5min: {}, 15min: {}'.format(
191 192 value['1_min'], value['5_min'], value['15_min'])
192 193
193 194 if state['type'] == STATE_OK and value['15_min'] > 5:
194 195 msg = 'Warning: your machine load is very high.'
195 196 state = {'message': msg, 'type': STATE_WARN}
196 197
197 198 return SysInfoRes(value=value, state=state, human_value=human_value)
198 199
199 200
200 201 def cpu():
201 202 value = 0
202 203 state = STATE_OK_DEFAULT
203 204
204 205 if not psutil:
205 206 return SysInfoRes(value=value, state=state)
206 207
207 208 value = psutil.cpu_percent(0.5)
208 209 human_value = '{} %'.format(value)
209 210 return SysInfoRes(value=value, state=state, human_value=human_value)
210 211
211 212
212 213 def storage():
213 214 from rhodecode.lib.helpers import format_byte_size_binary
214 215 from rhodecode.model.settings import VcsSettingsModel
215 216 path = VcsSettingsModel().get_repos_location()
216 217
217 218 value = dict(percent=0, used=0, total=0, path=path, text='')
218 219 state = STATE_OK_DEFAULT
219 220 if not psutil:
220 221 return SysInfoRes(value=value, state=state)
221 222
222 223 try:
223 224 value.update(dict(psutil.disk_usage(path)._asdict()))
224 225 except Exception as e:
225 226 log.exception('Failed to fetch disk info')
226 227 state = {'message': str(e), 'type': STATE_ERR}
227 228
228 229 human_value = value.copy()
229 230 human_value['used'] = format_byte_size_binary(value['used'])
230 231 human_value['total'] = format_byte_size_binary(value['total'])
231 232 human_value['text'] = "{}/{}, {}% used".format(
232 233 format_byte_size_binary(value['used']),
233 234 format_byte_size_binary(value['total']),
234 235 value['percent'])
235 236
236 237 if state['type'] == STATE_OK and value['percent'] > 90:
237 238 msg = 'Critical: your disk space is very low.'
238 239 state = {'message': msg, 'type': STATE_ERR}
239 240
240 241 elif state['type'] == STATE_OK and value['percent'] > 70:
241 242 msg = 'Warning: your disk space is running low.'
242 243 state = {'message': msg, 'type': STATE_WARN}
243 244
244 245 return SysInfoRes(value=value, state=state, human_value=human_value)
245 246
246 247
247 248 def storage_inodes():
248 249 from rhodecode.model.settings import VcsSettingsModel
249 250 path = VcsSettingsModel().get_repos_location()
250 251
251 252 value = dict(percent=0, free=0, used=0, total=0, path=path, text='')
252 253 state = STATE_OK_DEFAULT
253 254 if not psutil:
254 255 return SysInfoRes(value=value, state=state)
255 256
256 257 try:
257 258 i_stat = os.statvfs(path)
258 259
259 260 value['used'] = i_stat.f_ffree
260 261 value['free'] = i_stat.f_favail
261 262 value['total'] = i_stat.f_files
262 263 value['percent'] = percentage(
263 264 value['used'], value['total'])
264 265 except Exception as e:
265 266 log.exception('Failed to fetch disk inodes info')
266 267 state = {'message': str(e), 'type': STATE_ERR}
267 268
268 269 human_value = value.copy()
269 270 human_value['text'] = "{}/{}, {}% used".format(
270 271 value['used'], value['total'], value['percent'])
271 272
272 273 if state['type'] == STATE_OK and value['percent'] > 90:
273 274 msg = 'Critical: your disk free inodes are very low.'
274 275 state = {'message': msg, 'type': STATE_ERR}
275 276
276 277 elif state['type'] == STATE_OK and value['percent'] > 70:
277 278 msg = 'Warning: your disk free inodes are running low.'
278 279 state = {'message': msg, 'type': STATE_WARN}
279 280
280 281 return SysInfoRes(value=value, state=state)
281 282
282 283
283 284 def storage_archives():
284 285 import rhodecode
285 286 from rhodecode.lib.utils import safe_str
286 287 from rhodecode.lib.helpers import format_byte_size_binary
287 288
288 289 msg = 'Enable this by setting ' \
289 290 'archive_cache_dir=/path/to/cache option in the .ini file'
290 291 path = safe_str(rhodecode.CONFIG.get('archive_cache_dir', msg))
291 292
292 293 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
293 294 state = STATE_OK_DEFAULT
294 295 try:
295 296 items_count = 0
296 297 used = 0
297 298 for root, dirs, files in os.walk(path):
298 299 if root == path:
299 300 items_count = len(files)
300 301
301 302 for f in files:
302 303 try:
303 304 used += os.path.getsize(os.path.join(root, f))
304 305 except OSError:
305 306 pass
306 307 value.update({
307 308 'percent': 100,
308 309 'used': used,
309 310 'total': used,
310 311 'items': items_count
311 312 })
312 313
313 314 except Exception as e:
314 315 log.exception('failed to fetch archive cache storage')
315 316 state = {'message': str(e), 'type': STATE_ERR}
316 317
317 318 human_value = value.copy()
318 319 human_value['used'] = format_byte_size_binary(value['used'])
319 320 human_value['total'] = format_byte_size_binary(value['total'])
320 321 human_value['text'] = "{} ({} items)".format(
321 322 human_value['used'], value['items'])
322 323
323 324 return SysInfoRes(value=value, state=state, human_value=human_value)
324 325
325 326
326 327 def storage_gist():
327 328 from rhodecode.model.gist import GIST_STORE_LOC
328 329 from rhodecode.model.settings import VcsSettingsModel
329 330 from rhodecode.lib.utils import safe_str
330 331 from rhodecode.lib.helpers import format_byte_size_binary
331 332 path = safe_str(os.path.join(
332 333 VcsSettingsModel().get_repos_location(), GIST_STORE_LOC))
333 334
334 335 # gist storage
335 336 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
336 337 state = STATE_OK_DEFAULT
337 338
338 339 try:
339 340 items_count = 0
340 341 used = 0
341 342 for root, dirs, files in os.walk(path):
342 343 if root == path:
343 344 items_count = len(dirs)
344 345
345 346 for f in files:
346 347 try:
347 348 used += os.path.getsize(os.path.join(root, f))
348 349 except OSError:
349 350 pass
350 351 value.update({
351 352 'percent': 100,
352 353 'used': used,
353 354 'total': used,
354 355 'items': items_count
355 356 })
356 357 except Exception as e:
357 358 log.exception('failed to fetch gist storage items')
358 359 state = {'message': str(e), 'type': STATE_ERR}
359 360
360 361 human_value = value.copy()
361 362 human_value['used'] = format_byte_size_binary(value['used'])
362 363 human_value['total'] = format_byte_size_binary(value['total'])
363 364 human_value['text'] = "{} ({} items)".format(
364 365 human_value['used'], value['items'])
365 366
366 367 return SysInfoRes(value=value, state=state, human_value=human_value)
367 368
368 369
369 370 def search_info():
370 371 import rhodecode
371 372 from rhodecode.lib.index import searcher_from_config
372 373
373 374 backend = rhodecode.CONFIG.get('search.module', '')
374 375 location = rhodecode.CONFIG.get('search.location', '')
375 376
376 377 try:
377 378 searcher = searcher_from_config(rhodecode.CONFIG)
378 379 searcher = searcher.__class__.__name__
379 380 except Exception:
380 381 searcher = None
381 382
382 383 value = dict(
383 384 backend=backend, searcher=searcher, location=location, text='')
384 385 state = STATE_OK_DEFAULT
385 386
386 387 human_value = value.copy()
387 388 human_value['text'] = "backend:`{}`".format(human_value['backend'])
388 389
389 390 return SysInfoRes(value=value, state=state, human_value=human_value)
390 391
391 392
392 393 def git_info():
393 394 from rhodecode.lib.vcs.backends import git
394 395 state = STATE_OK_DEFAULT
395 396 value = human_value = ''
396 397 try:
397 398 value = git.discover_git_version(raise_on_exc=True)
398 399 human_value = 'version reported from VCSServer: {}'.format(value)
399 400 except Exception as e:
400 401 state = {'message': str(e), 'type': STATE_ERR}
401 402
402 403 return SysInfoRes(value=value, state=state, human_value=human_value)
403 404
404 405
405 406 def hg_info():
406 407 from rhodecode.lib.vcs.backends import hg
407 408 state = STATE_OK_DEFAULT
408 409 value = human_value = ''
409 410 try:
410 411 value = hg.discover_hg_version(raise_on_exc=True)
411 412 human_value = 'version reported from VCSServer: {}'.format(value)
412 413 except Exception as e:
413 414 state = {'message': str(e), 'type': STATE_ERR}
414 415 return SysInfoRes(value=value, state=state, human_value=human_value)
415 416
416 417
417 418 def svn_info():
418 419 from rhodecode.lib.vcs.backends import svn
419 420 state = STATE_OK_DEFAULT
420 421 value = human_value = ''
421 422 try:
422 423 value = svn.discover_svn_version(raise_on_exc=True)
423 424 human_value = 'version reported from VCSServer: {}'.format(value)
424 425 except Exception as e:
425 426 state = {'message': str(e), 'type': STATE_ERR}
426 427 return SysInfoRes(value=value, state=state, human_value=human_value)
427 428
428 429
429 430 def vcs_backends():
430 431 import rhodecode
431 432 value = map(
432 433 string.strip, rhodecode.CONFIG.get('vcs.backends', '').split(','))
433 434 human_value = 'Enabled backends in order: {}'.format(','.join(value))
434 435 return SysInfoRes(value=value, human_value=human_value)
435 436
436 437
437 438 def vcs_server():
438 439 import rhodecode
439 440 from rhodecode.lib.vcs.backends import get_vcsserver_version
440 441
441 442 server_url = rhodecode.CONFIG.get('vcs.server')
442 443 enabled = rhodecode.CONFIG.get('vcs.server.enable')
443 444 protocol = rhodecode.CONFIG.get('vcs.server.protocol')
444 445 state = STATE_OK_DEFAULT
445 446 version = None
446 447
447 448 try:
448 449 version = get_vcsserver_version()
449 450 connection = 'connected'
450 451 except Exception as e:
451 452 connection = 'failed'
452 453 state = {'message': str(e), 'type': STATE_ERR}
453 454
454 455 value = dict(
455 456 url=server_url,
456 457 enabled=enabled,
457 458 protocol=protocol,
458 459 connection=connection,
459 460 version=version,
460 461 text='',
461 462 )
462 463
463 464 human_value = value.copy()
464 465 human_value['text'] = \
465 466 '{url}@ver:{ver} via {mode} mode, connection:{conn}'.format(
466 467 url=server_url, ver=version, mode=protocol, conn=connection)
467 468
468 469 return SysInfoRes(value=value, state=state, human_value=human_value)
469 470
470 471
471 472 def rhodecode_app_info():
472 473 import rhodecode
473 474 edition = rhodecode.CONFIG.get('rhodecode.edition')
474 475
475 476 value = dict(
476 477 rhodecode_version=rhodecode.__version__,
477 478 rhodecode_lib_path=os.path.abspath(rhodecode.__file__),
478 479 text=''
479 480 )
480 481 human_value = value.copy()
481 482 human_value['text'] = 'RhodeCode {edition}, version {ver}'.format(
482 483 edition=edition, ver=value['rhodecode_version']
483 484 )
484 485 return SysInfoRes(value=value, human_value=human_value)
485 486
486 487
487 488 def rhodecode_config():
488 489 import rhodecode
489 490 path = rhodecode.CONFIG.get('__file__')
490 491 rhodecode_ini_safe = rhodecode.CONFIG.copy()
491 492
492 493 blacklist = [
493 494 'rhodecode_license_key',
494 495 'routes.map',
495 496 'pylons.h',
496 497 'pylons.app_globals',
497 498 'pylons.environ_config',
498 499 'sqlalchemy.db1.url',
499 500 'channelstream.secret',
500 501 'beaker.session.secret',
501 502 'rhodecode.encrypted_values.secret',
502 503 'rhodecode_auth_github_consumer_key',
503 504 'rhodecode_auth_github_consumer_secret',
504 505 'rhodecode_auth_google_consumer_key',
505 506 'rhodecode_auth_google_consumer_secret',
506 507 'rhodecode_auth_bitbucket_consumer_secret',
507 508 'rhodecode_auth_bitbucket_consumer_key',
508 509 'rhodecode_auth_twitter_consumer_secret',
509 510 'rhodecode_auth_twitter_consumer_key',
510 511
511 512 'rhodecode_auth_twitter_secret',
512 513 'rhodecode_auth_github_secret',
513 514 'rhodecode_auth_google_secret',
514 515 'rhodecode_auth_bitbucket_secret',
515 516
516 517 'appenlight.api_key',
517 518 ('app_conf', 'sqlalchemy.db1.url')
518 519 ]
519 520 for k in blacklist:
520 521 if isinstance(k, tuple):
521 522 section, key = k
522 523 if section in rhodecode_ini_safe:
523 524 rhodecode_ini_safe[section] = '**OBFUSCATED**'
524 525 else:
525 526 rhodecode_ini_safe.pop(k, None)
526 527
527 528 # TODO: maybe put some CONFIG checks here ?
528 529 return SysInfoRes(value={'config': rhodecode_ini_safe, 'path': path})
529 530
530 531
531 532 def database_info():
532 533 import rhodecode
533 534 from sqlalchemy.engine import url as engine_url
534 535 from rhodecode.model.meta import Base as sql_base, Session
535 536 from rhodecode.model.db import DbMigrateVersion
536 537
537 538 state = STATE_OK_DEFAULT
538 539
539 540 db_migrate = DbMigrateVersion.query().filter(
540 541 DbMigrateVersion.repository_id == 'rhodecode_db_migrations').one()
541 542
542 543 db_url_obj = engine_url.make_url(rhodecode.CONFIG['sqlalchemy.db1.url'])
543 544
544 545 try:
545 546 engine = sql_base.metadata.bind
546 547 db_server_info = engine.dialect._get_server_version_info(
547 548 Session.connection(bind=engine))
548 549 db_version = '.'.join(map(str, db_server_info))
549 550 except Exception:
550 551 log.exception('failed to fetch db version')
551 552 db_version = 'UNKNOWN'
552 553
553 554 db_info = dict(
554 555 migrate_version=db_migrate.version,
555 556 type=db_url_obj.get_backend_name(),
556 557 version=db_version,
557 558 url=repr(db_url_obj)
558 559 )
559 560
560 561 human_value = db_info.copy()
561 562 human_value['url'] = "{} @ migration version: {}".format(
562 563 db_info['url'], db_info['migrate_version'])
563 564 human_value['version'] = "{} {}".format(db_info['type'], db_info['version'])
564 565 return SysInfoRes(value=db_info, state=state, human_value=human_value)
565 566
566 567
567 568 def server_info(environ):
568 569 import rhodecode
569 570 from rhodecode.lib.base import get_server_ip_addr, get_server_port
570 571
571 572 value = {
572 573 'server_ip': '%s:%s' % (
573 574 get_server_ip_addr(environ, log_errors=False),
574 575 get_server_port(environ)
575 576 ),
576 577 'server_id': rhodecode.CONFIG.get('instance_id'),
577 578 }
578 579 return SysInfoRes(value=value)
579 580
580 581
581 582 def get_system_info(environ):
582 583 environ = environ or {}
583 584 return {
584 585 'rhodecode_app': SysInfo(rhodecode_app_info)(),
585 586 'rhodecode_config': SysInfo(rhodecode_config)(),
586 587 'python': SysInfo(python_info)(),
587 588 'py_modules': SysInfo(py_modules)(),
588 589
589 590 'platform': SysInfo(platform_type)(),
590 591 'server': SysInfo(server_info, environ=environ)(),
591 592 'database': SysInfo(database_info)(),
592 593
593 594 'storage': SysInfo(storage)(),
594 595 'storage_inodes': SysInfo(storage_inodes)(),
595 596 'storage_archive': SysInfo(storage_archives)(),
596 597 'storage_gist': SysInfo(storage_gist)(),
597 598
598 599 'search': SysInfo(search_info)(),
599 600
600 601 'uptime': SysInfo(uptime)(),
601 602 'load': SysInfo(machine_load)(),
602 603 'cpu': SysInfo(cpu)(),
603 604 'memory': SysInfo(memory)(),
604 605
605 606 'vcs_backends': SysInfo(vcs_backends)(),
606 607 'vcs_server': SysInfo(vcs_server)(),
607 608
608 609 'git': SysInfo(git_info)(),
609 610 'hg': SysInfo(hg_info)(),
610 611 'svn': SysInfo(svn_info)(),
611 612 }
General Comments 0
You need to be logged in to leave comments. Login now