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