##// END OF EJS Templates
system-info: unified data structures for usage in API....
marcink -
r1112:ff9a513b default
parent child Browse files
Show More
@@ -1,837 +1,837 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 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 """
23 23 settings controller for rhodecode admin
24 24 """
25 25
26 26 import collections
27 27 import logging
28 28 import urllib2
29 29
30 30 import datetime
31 31 import formencode
32 32 from formencode import htmlfill
33 33 import packaging.version
34 34 from pylons import request, tmpl_context as c, url, config
35 35 from pylons.controllers.util import redirect
36 36 from pylons.i18n.translation import _, lazy_ugettext
37 37 from pyramid.threadlocal import get_current_registry
38 38 from webob.exc import HTTPBadRequest
39 39
40 40 import rhodecode
41 41 from rhodecode.admin.navigation import navigation_list
42 42 from rhodecode.lib import auth
43 43 from rhodecode.lib import helpers as h
44 44 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
45 45 from rhodecode.lib.base import BaseController, render
46 46 from rhodecode.lib.celerylib import tasks, run_task
47 47 from rhodecode.lib.utils import repo2db_mapper
48 48 from rhodecode.lib.utils2 import (
49 49 str2bool, safe_unicode, AttributeDict, safe_int)
50 50 from rhodecode.lib.compat import OrderedDict
51 51 from rhodecode.lib.ext_json import json
52 52 from rhodecode.lib.utils import jsonify
53 53
54 54 from rhodecode.model.db import RhodeCodeUi, Repository
55 55 from rhodecode.model.forms import ApplicationSettingsForm, \
56 56 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
57 57 LabsSettingsForm, IssueTrackerPatternsForm
58 58 from rhodecode.model.repo_group import RepoGroupModel
59 59
60 60 from rhodecode.model.scm import ScmModel
61 61 from rhodecode.model.notification import EmailNotificationModel
62 62 from rhodecode.model.meta import Session
63 63 from rhodecode.model.settings import (
64 64 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
65 65 SettingsModel)
66 66
67 67 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
68 68 from rhodecode.svn_support.config_keys import generate_config
69 69
70 70
71 71 log = logging.getLogger(__name__)
72 72
73 73
74 74 class SettingsController(BaseController):
75 75 """REST Controller styled on the Atom Publishing Protocol"""
76 76 # To properly map this controller, ensure your config/routing.py
77 77 # file has a resource setup:
78 78 # map.resource('setting', 'settings', controller='admin/settings',
79 79 # path_prefix='/admin', name_prefix='admin_')
80 80
81 81 @LoginRequired()
82 82 def __before__(self):
83 83 super(SettingsController, self).__before__()
84 84 c.labs_active = str2bool(
85 85 rhodecode.CONFIG.get('labs_settings_active', 'true'))
86 86 c.navlist = navigation_list(request)
87 87
88 88 def _get_hg_ui_settings(self):
89 89 ret = RhodeCodeUi.query().all()
90 90
91 91 if not ret:
92 92 raise Exception('Could not get application ui settings !')
93 93 settings = {}
94 94 for each in ret:
95 95 k = each.ui_key
96 96 v = each.ui_value
97 97 if k == '/':
98 98 k = 'root_path'
99 99
100 100 if k in ['push_ssl', 'publish']:
101 101 v = str2bool(v)
102 102
103 103 if k.find('.') != -1:
104 104 k = k.replace('.', '_')
105 105
106 106 if each.ui_section in ['hooks', 'extensions']:
107 107 v = each.ui_active
108 108
109 109 settings[each.ui_section + '_' + k] = v
110 110 return settings
111 111
112 112 @HasPermissionAllDecorator('hg.admin')
113 113 @auth.CSRFRequired()
114 114 @jsonify
115 115 def delete_svn_pattern(self):
116 116 if not request.is_xhr:
117 117 raise HTTPBadRequest()
118 118
119 119 delete_pattern_id = request.POST.get('delete_svn_pattern')
120 120 model = VcsSettingsModel()
121 121 try:
122 122 model.delete_global_svn_pattern(delete_pattern_id)
123 123 except SettingNotFound:
124 124 raise HTTPBadRequest()
125 125
126 126 Session().commit()
127 127 return True
128 128
129 129 @HasPermissionAllDecorator('hg.admin')
130 130 @auth.CSRFRequired()
131 131 def settings_vcs_update(self):
132 132 """POST /admin/settings: All items in the collection"""
133 133 # url('admin_settings_vcs')
134 134 c.active = 'vcs'
135 135
136 136 model = VcsSettingsModel()
137 137 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
138 138 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
139 139
140 140 # TODO: Replace with request.registry after migrating to pyramid.
141 141 pyramid_settings = get_current_registry().settings
142 142 c.svn_proxy_generate_config = pyramid_settings[generate_config]
143 143
144 144 application_form = ApplicationUiSettingsForm()()
145 145
146 146 try:
147 147 form_result = application_form.to_python(dict(request.POST))
148 148 except formencode.Invalid as errors:
149 149 h.flash(
150 150 _("Some form inputs contain invalid data."),
151 151 category='error')
152 152 return htmlfill.render(
153 153 render('admin/settings/settings.html'),
154 154 defaults=errors.value,
155 155 errors=errors.error_dict or {},
156 156 prefix_error=False,
157 157 encoding="UTF-8",
158 158 force_defaults=False
159 159 )
160 160
161 161 try:
162 162 if c.visual.allow_repo_location_change:
163 163 model.update_global_path_setting(
164 164 form_result['paths_root_path'])
165 165
166 166 model.update_global_ssl_setting(form_result['web_push_ssl'])
167 167 model.update_global_hook_settings(form_result)
168 168
169 169 model.create_or_update_global_svn_settings(form_result)
170 170 model.create_or_update_global_hg_settings(form_result)
171 171 model.create_or_update_global_pr_settings(form_result)
172 172 except Exception:
173 173 log.exception("Exception while updating settings")
174 174 h.flash(_('Error occurred during updating '
175 175 'application settings'), category='error')
176 176 else:
177 177 Session().commit()
178 178 h.flash(_('Updated VCS settings'), category='success')
179 179 return redirect(url('admin_settings_vcs'))
180 180
181 181 return htmlfill.render(
182 182 render('admin/settings/settings.html'),
183 183 defaults=self._form_defaults(),
184 184 encoding="UTF-8",
185 185 force_defaults=False)
186 186
187 187 @HasPermissionAllDecorator('hg.admin')
188 188 def settings_vcs(self):
189 189 """GET /admin/settings: All items in the collection"""
190 190 # url('admin_settings_vcs')
191 191 c.active = 'vcs'
192 192 model = VcsSettingsModel()
193 193 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
194 194 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
195 195
196 196 # TODO: Replace with request.registry after migrating to pyramid.
197 197 pyramid_settings = get_current_registry().settings
198 198 c.svn_proxy_generate_config = pyramid_settings[generate_config]
199 199
200 200 return htmlfill.render(
201 201 render('admin/settings/settings.html'),
202 202 defaults=self._form_defaults(),
203 203 encoding="UTF-8",
204 204 force_defaults=False)
205 205
206 206 @HasPermissionAllDecorator('hg.admin')
207 207 @auth.CSRFRequired()
208 208 def settings_mapping_update(self):
209 209 """POST /admin/settings/mapping: All items in the collection"""
210 210 # url('admin_settings_mapping')
211 211 c.active = 'mapping'
212 212 rm_obsolete = request.POST.get('destroy', False)
213 213 invalidate_cache = request.POST.get('invalidate', False)
214 214 log.debug(
215 215 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
216 216
217 217 if invalidate_cache:
218 218 log.debug('invalidating all repositories cache')
219 219 for repo in Repository.get_all():
220 220 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
221 221
222 222 filesystem_repos = ScmModel().repo_scan()
223 223 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
224 224 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
225 225 h.flash(_('Repositories successfully '
226 226 'rescanned added: %s ; removed: %s') %
227 227 (_repr(added), _repr(removed)),
228 228 category='success')
229 229 return redirect(url('admin_settings_mapping'))
230 230
231 231 @HasPermissionAllDecorator('hg.admin')
232 232 def settings_mapping(self):
233 233 """GET /admin/settings/mapping: All items in the collection"""
234 234 # url('admin_settings_mapping')
235 235 c.active = 'mapping'
236 236
237 237 return htmlfill.render(
238 238 render('admin/settings/settings.html'),
239 239 defaults=self._form_defaults(),
240 240 encoding="UTF-8",
241 241 force_defaults=False)
242 242
243 243 @HasPermissionAllDecorator('hg.admin')
244 244 @auth.CSRFRequired()
245 245 def settings_global_update(self):
246 246 """POST /admin/settings/global: All items in the collection"""
247 247 # url('admin_settings_global')
248 248 c.active = 'global'
249 249 c.personal_repo_group_default_pattern = RepoGroupModel()\
250 250 .get_personal_group_name_pattern()
251 251 application_form = ApplicationSettingsForm()()
252 252 try:
253 253 form_result = application_form.to_python(dict(request.POST))
254 254 except formencode.Invalid as errors:
255 255 return htmlfill.render(
256 256 render('admin/settings/settings.html'),
257 257 defaults=errors.value,
258 258 errors=errors.error_dict or {},
259 259 prefix_error=False,
260 260 encoding="UTF-8",
261 261 force_defaults=False)
262 262
263 263 try:
264 264 settings = [
265 265 ('title', 'rhodecode_title', 'unicode'),
266 266 ('realm', 'rhodecode_realm', 'unicode'),
267 267 ('pre_code', 'rhodecode_pre_code', 'unicode'),
268 268 ('post_code', 'rhodecode_post_code', 'unicode'),
269 269 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
270 270 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
271 271 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
272 272 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
273 273 ]
274 274 for setting, form_key, type_ in settings:
275 275 sett = SettingsModel().create_or_update_setting(
276 276 setting, form_result[form_key], type_)
277 277 Session().add(sett)
278 278
279 279 Session().commit()
280 280 SettingsModel().invalidate_settings_cache()
281 281 h.flash(_('Updated application settings'), category='success')
282 282 except Exception:
283 283 log.exception("Exception while updating application settings")
284 284 h.flash(
285 285 _('Error occurred during updating application settings'),
286 286 category='error')
287 287
288 288 return redirect(url('admin_settings_global'))
289 289
290 290 @HasPermissionAllDecorator('hg.admin')
291 291 def settings_global(self):
292 292 """GET /admin/settings/global: All items in the collection"""
293 293 # url('admin_settings_global')
294 294 c.active = 'global'
295 295 c.personal_repo_group_default_pattern = RepoGroupModel()\
296 296 .get_personal_group_name_pattern()
297 297
298 298 return htmlfill.render(
299 299 render('admin/settings/settings.html'),
300 300 defaults=self._form_defaults(),
301 301 encoding="UTF-8",
302 302 force_defaults=False)
303 303
304 304 @HasPermissionAllDecorator('hg.admin')
305 305 @auth.CSRFRequired()
306 306 def settings_visual_update(self):
307 307 """POST /admin/settings/visual: All items in the collection"""
308 308 # url('admin_settings_visual')
309 309 c.active = 'visual'
310 310 application_form = ApplicationVisualisationForm()()
311 311 try:
312 312 form_result = application_form.to_python(dict(request.POST))
313 313 except formencode.Invalid as errors:
314 314 return htmlfill.render(
315 315 render('admin/settings/settings.html'),
316 316 defaults=errors.value,
317 317 errors=errors.error_dict or {},
318 318 prefix_error=False,
319 319 encoding="UTF-8",
320 320 force_defaults=False
321 321 )
322 322
323 323 try:
324 324 settings = [
325 325 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
326 326 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
327 327 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
328 328 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
329 329 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
330 330 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
331 331 ('show_version', 'rhodecode_show_version', 'bool'),
332 332 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
333 333 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
334 334 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
335 335 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
336 336 ('support_url', 'rhodecode_support_url', 'unicode'),
337 337 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
338 338 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
339 339 ]
340 340 for setting, form_key, type_ in settings:
341 341 sett = SettingsModel().create_or_update_setting(
342 342 setting, form_result[form_key], type_)
343 343 Session().add(sett)
344 344
345 345 Session().commit()
346 346 SettingsModel().invalidate_settings_cache()
347 347 h.flash(_('Updated visualisation settings'), category='success')
348 348 except Exception:
349 349 log.exception("Exception updating visualization settings")
350 350 h.flash(_('Error occurred during updating '
351 351 'visualisation settings'),
352 352 category='error')
353 353
354 354 return redirect(url('admin_settings_visual'))
355 355
356 356 @HasPermissionAllDecorator('hg.admin')
357 357 def settings_visual(self):
358 358 """GET /admin/settings/visual: All items in the collection"""
359 359 # url('admin_settings_visual')
360 360 c.active = 'visual'
361 361
362 362 return htmlfill.render(
363 363 render('admin/settings/settings.html'),
364 364 defaults=self._form_defaults(),
365 365 encoding="UTF-8",
366 366 force_defaults=False)
367 367
368 368 @HasPermissionAllDecorator('hg.admin')
369 369 @auth.CSRFRequired()
370 370 def settings_issuetracker_test(self):
371 371 if request.is_xhr:
372 372 return h.urlify_commit_message(
373 373 request.POST.get('test_text', ''),
374 374 'repo_group/test_repo1')
375 375 else:
376 376 raise HTTPBadRequest()
377 377
378 378 @HasPermissionAllDecorator('hg.admin')
379 379 @auth.CSRFRequired()
380 380 def settings_issuetracker_delete(self):
381 381 uid = request.POST.get('uid')
382 382 IssueTrackerSettingsModel().delete_entries(uid)
383 383 h.flash(_('Removed issue tracker entry'), category='success')
384 384 return redirect(url('admin_settings_issuetracker'))
385 385
386 386 @HasPermissionAllDecorator('hg.admin')
387 387 def settings_issuetracker(self):
388 388 """GET /admin/settings/issue-tracker: All items in the collection"""
389 389 # url('admin_settings_issuetracker')
390 390 c.active = 'issuetracker'
391 391 defaults = SettingsModel().get_all_settings()
392 392
393 393 entry_key = 'rhodecode_issuetracker_pat_'
394 394
395 395 c.issuetracker_entries = {}
396 396 for k, v in defaults.items():
397 397 if k.startswith(entry_key):
398 398 uid = k[len(entry_key):]
399 399 c.issuetracker_entries[uid] = None
400 400
401 401 for uid in c.issuetracker_entries:
402 402 c.issuetracker_entries[uid] = AttributeDict({
403 403 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
404 404 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
405 405 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
406 406 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
407 407 })
408 408
409 409 return render('admin/settings/settings.html')
410 410
411 411 @HasPermissionAllDecorator('hg.admin')
412 412 @auth.CSRFRequired()
413 413 def settings_issuetracker_save(self):
414 414 settings_model = IssueTrackerSettingsModel()
415 415
416 416 form = IssueTrackerPatternsForm()().to_python(request.POST)
417 417 if form:
418 418 for uid in form.get('delete_patterns', []):
419 419 settings_model.delete_entries(uid)
420 420
421 421 for pattern in form.get('patterns', []):
422 422 for setting, value, type_ in pattern:
423 423 sett = settings_model.create_or_update_setting(
424 424 setting, value, type_)
425 425 Session().add(sett)
426 426
427 427 Session().commit()
428 428
429 429 SettingsModel().invalidate_settings_cache()
430 430 h.flash(_('Updated issue tracker entries'), category='success')
431 431 return redirect(url('admin_settings_issuetracker'))
432 432
433 433 @HasPermissionAllDecorator('hg.admin')
434 434 @auth.CSRFRequired()
435 435 def settings_email_update(self):
436 436 """POST /admin/settings/email: All items in the collection"""
437 437 # url('admin_settings_email')
438 438 c.active = 'email'
439 439
440 440 test_email = request.POST.get('test_email')
441 441
442 442 if not test_email:
443 443 h.flash(_('Please enter email address'), category='error')
444 444 return redirect(url('admin_settings_email'))
445 445
446 446 email_kwargs = {
447 447 'date': datetime.datetime.now(),
448 448 'user': c.rhodecode_user,
449 449 'rhodecode_version': c.rhodecode_version
450 450 }
451 451
452 452 (subject, headers, email_body,
453 453 email_body_plaintext) = EmailNotificationModel().render_email(
454 454 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
455 455
456 456 recipients = [test_email] if test_email else None
457 457
458 458 run_task(tasks.send_email, recipients, subject,
459 459 email_body_plaintext, email_body)
460 460
461 461 h.flash(_('Send email task created'), category='success')
462 462 return redirect(url('admin_settings_email'))
463 463
464 464 @HasPermissionAllDecorator('hg.admin')
465 465 def settings_email(self):
466 466 """GET /admin/settings/email: All items in the collection"""
467 467 # url('admin_settings_email')
468 468 c.active = 'email'
469 469 c.rhodecode_ini = rhodecode.CONFIG
470 470
471 471 return htmlfill.render(
472 472 render('admin/settings/settings.html'),
473 473 defaults=self._form_defaults(),
474 474 encoding="UTF-8",
475 475 force_defaults=False)
476 476
477 477 @HasPermissionAllDecorator('hg.admin')
478 478 @auth.CSRFRequired()
479 479 def settings_hooks_update(self):
480 480 """POST or DELETE /admin/settings/hooks: All items in the collection"""
481 481 # url('admin_settings_hooks')
482 482 c.active = 'hooks'
483 483 if c.visual.allow_custom_hooks_settings:
484 484 ui_key = request.POST.get('new_hook_ui_key')
485 485 ui_value = request.POST.get('new_hook_ui_value')
486 486
487 487 hook_id = request.POST.get('hook_id')
488 488 new_hook = False
489 489
490 490 model = SettingsModel()
491 491 try:
492 492 if ui_value and ui_key:
493 493 model.create_or_update_hook(ui_key, ui_value)
494 494 h.flash(_('Added new hook'), category='success')
495 495 new_hook = True
496 496 elif hook_id:
497 497 RhodeCodeUi.delete(hook_id)
498 498 Session().commit()
499 499
500 500 # check for edits
501 501 update = False
502 502 _d = request.POST.dict_of_lists()
503 503 for k, v in zip(_d.get('hook_ui_key', []),
504 504 _d.get('hook_ui_value_new', [])):
505 505 model.create_or_update_hook(k, v)
506 506 update = True
507 507
508 508 if update and not new_hook:
509 509 h.flash(_('Updated hooks'), category='success')
510 510 Session().commit()
511 511 except Exception:
512 512 log.exception("Exception during hook creation")
513 513 h.flash(_('Error occurred during hook creation'),
514 514 category='error')
515 515
516 516 return redirect(url('admin_settings_hooks'))
517 517
518 518 @HasPermissionAllDecorator('hg.admin')
519 519 def settings_hooks(self):
520 520 """GET /admin/settings/hooks: All items in the collection"""
521 521 # url('admin_settings_hooks')
522 522 c.active = 'hooks'
523 523
524 524 model = SettingsModel()
525 525 c.hooks = model.get_builtin_hooks()
526 526 c.custom_hooks = model.get_custom_hooks()
527 527
528 528 return htmlfill.render(
529 529 render('admin/settings/settings.html'),
530 530 defaults=self._form_defaults(),
531 531 encoding="UTF-8",
532 532 force_defaults=False)
533 533
534 534 @HasPermissionAllDecorator('hg.admin')
535 535 def settings_search(self):
536 536 """GET /admin/settings/search: All items in the collection"""
537 537 # url('admin_settings_search')
538 538 c.active = 'search'
539 539
540 540 from rhodecode.lib.index import searcher_from_config
541 541 searcher = searcher_from_config(config)
542 542 c.statistics = searcher.statistics()
543 543
544 544 return render('admin/settings/settings.html')
545 545
546 546 @HasPermissionAllDecorator('hg.admin')
547 547 def settings_system(self):
548 548 """GET /admin/settings/system: All items in the collection"""
549 549 # url('admin_settings_system')
550 550 snapshot = str2bool(request.GET.get('snapshot'))
551 551 defaults = self._form_defaults()
552 552
553 553 c.active = 'system'
554 554 c.rhodecode_update_url = defaults.get('rhodecode_update_url')
555 555 server_info = ScmModel().get_server_info(request.environ)
556 556
557 557 for key, val in server_info.iteritems():
558 558 setattr(c, key, val)
559 559
560 560 def val(name, subkey='human_value'):
561 561 return server_info[name][subkey]
562 562
563 563 def state(name):
564 564 return server_info[name]['state']
565 565
566 566 def val2(name):
567 567 val = server_info[name]['human_value']
568 568 state = server_info[name]['state']
569 569 return val, state
570 570
571 571 c.data_items = [
572 572 # update info
573 573 (_('Update info'), h.literal(
574 574 '<span class="link" id="check_for_update" >%s.</span>' % (
575 575 _('Check for updates')) +
576 576 '<br/> <span >%s.</span>' % (_('Note: please make sure this server can access `%s` for the update link to work') % c.rhodecode_update_url)
577 577 ), ''),
578 578
579 579 # RhodeCode specific
580 580 (_('RhodeCode Version'), c.rhodecode_version, ''),
581 581 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
582 582 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
583 583 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
584 584 ('', '', ''), # spacer
585 585
586 586 # Database
587 587 (_('Database'), val('database')['url'], state('database')),
588 588 (_('Database version'), val('database')['version'], state('database')),
589 589 ('', '', ''), # spacer
590 590
591 591 # Platform/Python
592 592 (_('Platform'), val('platform'), state('platform')),
593 593 (_('Python version'), val('python')['version'], state('python')),
594 594 (_('Python path'), val('python')['executable'], state('python')),
595 595 ('', '', ''), # spacer
596 596
597 597 # Systems stats
598 598 (_('CPU'), val('cpu'), state('cpu')),
599 (_('Load'), val('load'), state('load')),
600 (_('Memory'), val('memory'), state('memory')),
601 (_('Uptime'), val('uptime')['uptime'], state('uptime')),
599 (_('Load'), val('load')['text'], state('load')),
600 (_('Memory'), val('memory')['text'], state('memory')),
601 (_('Uptime'), val('uptime')['text'], state('uptime')),
602 602 ('', '', ''), # spacer
603 603
604 604 # Repo storage
605 605 (_('Storage location'), val('storage')['path'], state('storage')),
606 606 (_('Storage info'), val('storage')['text'], state('storage')),
607 607 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
608 608
609 609 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
610 610 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
611 611
612 612 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
613 613 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
614 614
615 (_('Search storage'), val('storage_search')['path'], state('storage_search')),
616 (_('Search info'), val('storage_search')['text'], state('storage_search')),
615 (_('Search info'), val('search')['text'], state('search')),
616 (_('Search location'), val('search')['location'], state('search')),
617 617 ('', '', ''), # spacer
618 618
619 619 # VCS specific
620 620 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
621 621 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
622 622 (_('GIT'), val('git'), state('git')),
623 623 (_('HG'), val('hg'), state('hg')),
624 624 (_('SVN'), val('svn'), state('svn')),
625 625
626 626 ]
627 627
628 628 # TODO: marcink, figure out how to allow only selected users to do this
629 629 c.allowed_to_snapshot = c.rhodecode_user.admin
630 630
631 631 if snapshot:
632 632 if c.allowed_to_snapshot:
633 633 return render('admin/settings/settings_system_snapshot.html')
634 634 else:
635 635 h.flash('You are not allowed to do this', category='warning')
636 636
637 637 return htmlfill.render(
638 638 render('admin/settings/settings.html'),
639 639 defaults=defaults,
640 640 encoding="UTF-8",
641 641 force_defaults=False)
642 642
643 643 @staticmethod
644 644 def get_update_data(update_url):
645 645 """Return the JSON update data."""
646 646 ver = rhodecode.__version__
647 647 log.debug('Checking for upgrade on `%s` server', update_url)
648 648 opener = urllib2.build_opener()
649 649 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
650 650 response = opener.open(update_url)
651 651 response_data = response.read()
652 652 data = json.loads(response_data)
653 653
654 654 return data
655 655
656 656 @HasPermissionAllDecorator('hg.admin')
657 657 def settings_system_update(self):
658 658 """GET /admin/settings/system/updates: All items in the collection"""
659 659 # url('admin_settings_system_update')
660 660 defaults = self._form_defaults()
661 661 update_url = defaults.get('rhodecode_update_url', '')
662 662
663 663 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s)
664 664 try:
665 665 data = self.get_update_data(update_url)
666 666 except urllib2.URLError as e:
667 667 log.exception("Exception contacting upgrade server")
668 668 return _err('Failed to contact upgrade server: %r' % e)
669 669 except ValueError as e:
670 670 log.exception("Bad data sent from update server")
671 671 return _err('Bad data sent from update server')
672 672
673 673 latest = data['versions'][0]
674 674
675 675 c.update_url = update_url
676 676 c.latest_data = latest
677 677 c.latest_ver = latest['version']
678 678 c.cur_ver = rhodecode.__version__
679 679 c.should_upgrade = False
680 680
681 681 if (packaging.version.Version(c.latest_ver) >
682 682 packaging.version.Version(c.cur_ver)):
683 683 c.should_upgrade = True
684 684 c.important_notices = latest['general']
685 685
686 686 return render('admin/settings/settings_system_update.html')
687 687
688 688 @HasPermissionAllDecorator('hg.admin')
689 689 def settings_supervisor(self):
690 690 c.rhodecode_ini = rhodecode.CONFIG
691 691 c.active = 'supervisor'
692 692
693 693 c.supervisor_procs = OrderedDict([
694 694 (SUPERVISOR_MASTER, {}),
695 695 ])
696 696
697 697 c.log_size = 10240
698 698 supervisor = SupervisorModel()
699 699
700 700 _connection = supervisor.get_connection(
701 701 c.rhodecode_ini.get('supervisor.uri'))
702 702 c.connection_error = None
703 703 try:
704 704 _connection.supervisor.getAllProcessInfo()
705 705 except Exception as e:
706 706 c.connection_error = str(e)
707 707 log.exception("Exception reading supervisor data")
708 708 return render('admin/settings/settings.html')
709 709
710 710 groupid = c.rhodecode_ini.get('supervisor.group_id')
711 711
712 712 # feed our group processes to the main
713 713 for proc in supervisor.get_group_processes(_connection, groupid):
714 714 c.supervisor_procs[proc['name']] = {}
715 715
716 716 for k in c.supervisor_procs.keys():
717 717 try:
718 718 # master process info
719 719 if k == SUPERVISOR_MASTER:
720 720 _data = supervisor.get_master_state(_connection)
721 721 _data['name'] = 'supervisor master'
722 722 _data['description'] = 'pid %s, id: %s, ver: %s' % (
723 723 _data['pid'], _data['id'], _data['ver'])
724 724 c.supervisor_procs[k] = _data
725 725 else:
726 726 procid = groupid + ":" + k
727 727 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
728 728 except Exception as e:
729 729 log.exception("Exception reading supervisor data")
730 730 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
731 731
732 732 return render('admin/settings/settings.html')
733 733
734 734 @HasPermissionAllDecorator('hg.admin')
735 735 def settings_supervisor_log(self, procid):
736 736 import rhodecode
737 737 c.rhodecode_ini = rhodecode.CONFIG
738 738 c.active = 'supervisor_tail'
739 739
740 740 supervisor = SupervisorModel()
741 741 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
742 742 groupid = c.rhodecode_ini.get('supervisor.group_id')
743 743 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
744 744
745 745 c.log_size = 10240
746 746 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
747 747 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
748 748
749 749 return render('admin/settings/settings.html')
750 750
751 751 @HasPermissionAllDecorator('hg.admin')
752 752 @auth.CSRFRequired()
753 753 def settings_labs_update(self):
754 754 """POST /admin/settings/labs: All items in the collection"""
755 755 # url('admin_settings/labs', method={'POST'})
756 756 c.active = 'labs'
757 757
758 758 application_form = LabsSettingsForm()()
759 759 try:
760 760 form_result = application_form.to_python(dict(request.POST))
761 761 except formencode.Invalid as errors:
762 762 h.flash(
763 763 _('Some form inputs contain invalid data.'),
764 764 category='error')
765 765 return htmlfill.render(
766 766 render('admin/settings/settings.html'),
767 767 defaults=errors.value,
768 768 errors=errors.error_dict or {},
769 769 prefix_error=False,
770 770 encoding='UTF-8',
771 771 force_defaults=False
772 772 )
773 773
774 774 try:
775 775 session = Session()
776 776 for setting in _LAB_SETTINGS:
777 777 setting_name = setting.key[len('rhodecode_'):]
778 778 sett = SettingsModel().create_or_update_setting(
779 779 setting_name, form_result[setting.key], setting.type)
780 780 session.add(sett)
781 781
782 782 except Exception:
783 783 log.exception('Exception while updating lab settings')
784 784 h.flash(_('Error occurred during updating labs settings'),
785 785 category='error')
786 786 else:
787 787 Session().commit()
788 788 SettingsModel().invalidate_settings_cache()
789 789 h.flash(_('Updated Labs settings'), category='success')
790 790 return redirect(url('admin_settings_labs'))
791 791
792 792 return htmlfill.render(
793 793 render('admin/settings/settings.html'),
794 794 defaults=self._form_defaults(),
795 795 encoding='UTF-8',
796 796 force_defaults=False)
797 797
798 798 @HasPermissionAllDecorator('hg.admin')
799 799 def settings_labs(self):
800 800 """GET /admin/settings/labs: All items in the collection"""
801 801 # url('admin_settings_labs')
802 802 if not c.labs_active:
803 803 redirect(url('admin_settings'))
804 804
805 805 c.active = 'labs'
806 806 c.lab_settings = _LAB_SETTINGS
807 807
808 808 return htmlfill.render(
809 809 render('admin/settings/settings.html'),
810 810 defaults=self._form_defaults(),
811 811 encoding='UTF-8',
812 812 force_defaults=False)
813 813
814 814 def _form_defaults(self):
815 815 defaults = SettingsModel().get_all_settings()
816 816 defaults.update(self._get_hg_ui_settings())
817 817 defaults.update({
818 818 'new_svn_branch': '',
819 819 'new_svn_tag': '',
820 820 })
821 821 return defaults
822 822
823 823
824 824 # :param key: name of the setting including the 'rhodecode_' prefix
825 825 # :param type: the RhodeCodeSetting type to use.
826 826 # :param group: the i18ned group in which we should dispaly this setting
827 827 # :param label: the i18ned label we should display for this setting
828 828 # :param help: the i18ned help we should dispaly for this setting
829 829 LabSetting = collections.namedtuple(
830 830 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
831 831
832 832
833 833 # This list has to be kept in sync with the form
834 834 # rhodecode.model.forms.LabsSettingsForm.
835 835 _LAB_SETTINGS = [
836 836
837 837 ]
@@ -1,582 +1,600 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 import string
7 8
8 9
9 10 log = logging.getLogger(__name__)
10 11
11 12
12 13 psutil = None
13 14
14 15 try:
15 16 # cygwin cannot have yet psutil support.
16 17 import psutil as psutil
17 18 except ImportError:
18 19 pass
19 20
20 21
21 22 _NA = 'NOT AVAILABLE'
22 23
23 24 STATE_OK = 'ok'
24 25 STATE_ERR = 'error'
25 26 STATE_WARN = 'warning'
26 27
27 28 STATE_OK_DEFAULT = {'message': '', 'type': STATE_OK}
28 29
29 30
30 31 # HELPERS
31 32 def percentage(part, whole):
32 33 whole = float(whole)
33 34 if whole > 0:
34 35 return 100 * float(part) / whole
35 36 return 0
36 37
37 38
38 39 def get_storage_size(storage_path):
39 40 sizes = []
40 41 for file_ in os.listdir(storage_path):
41 42 storage_file = os.path.join(storage_path, file_)
42 43 if os.path.isfile(storage_file):
43 44 try:
44 45 sizes.append(os.path.getsize(storage_file))
45 46 except OSError:
46 47 log.exception('Failed to get size of storage file %s',
47 48 storage_file)
48 49 pass
49 50
50 51 return sum(sizes)
51 52
52 53
53 54 class SysInfoRes(object):
54 55 def __init__(self, value, state=STATE_OK_DEFAULT, human_value=None):
55 56 self.value = value
56 57 self.state = state
57 58 self.human_value = human_value or value
58 59
59 60 def __json__(self):
60 61 return {
61 62 'value': self.value,
62 63 'state': self.state,
63 64 'human_value': self.human_value,
64 65 }
65 66
66 67 def __str__(self):
67 68 return '<SysInfoRes({})>'.format(self.__json__())
68 69
69 70
70 71 class SysInfo(object):
71 72
72 73 def __init__(self, func_name, **kwargs):
73 74 self.func_name = func_name
74 75 self.value = _NA
75 76 self.state = None
76 77 self.kwargs = kwargs or {}
77 78
78 79 def __call__(self):
79 80 computed = self.compute(**self.kwargs)
80 81 if not isinstance(computed, SysInfoRes):
81 82 raise ValueError(
82 83 'computed value for {} is not instance of '
83 84 '{}, got {} instead'.format(
84 85 self.func_name, SysInfoRes, type(computed)))
85 86 return computed.__json__()
86 87
87 88 def __str__(self):
88 89 return '<SysInfo({})>'.format(self.func_name)
89 90
90 91 def compute(self, **kwargs):
91 92 return self.func_name(**kwargs)
92 93
93 94
94 95 # SysInfo functions
95 96 def python_info():
96 value = {
97 'version': ' '.join(platform._sys_version()),
98 'executable': sys.executable
99 }
97 value = dict(version=' '.join(platform._sys_version()),
98 executable=sys.executable)
100 99 return SysInfoRes(value=value)
101 100
102 101
103 102 def py_modules():
104 103 mods = dict([(p.project_name, p.version)
105 104 for p in pkg_resources.working_set])
106 105 value = sorted(mods.items(), key=lambda k: k[0].lower())
107 106 return SysInfoRes(value=value)
108 107
109 108
110 109 def platform_type():
111 110 from rhodecode.lib.utils import safe_unicode
112 111 value = safe_unicode(platform.platform())
113 112 return SysInfoRes(value=value)
114 113
115 114
116 115 def uptime():
117 116 from rhodecode.lib.helpers import age, time_to_datetime
118 from rhodecode.translation import _
119 117
120 _uptime = _uptime_human = {'boot_time': 0, 'uptime': 0}
118 value = dict(boot_time=0, uptime=0, text='')
121 119 state = STATE_OK_DEFAULT
122 120 if not psutil:
123 return SysInfoRes(value=_uptime, state=state)
121 return SysInfoRes(value=value, state=state)
124 122
125 123 boot_time = psutil.boot_time()
126 _uptime['boot_time'] = boot_time
127 _uptime['uptime'] = time.time() - boot_time
124 value['boot_time'] = boot_time
125 value['uptime'] = time.time() - boot_time
128 126
129 _uptime_human['boot_time'] = time_to_datetime(boot_time)
130 _uptime_human['uptime'] = _('Server started {}').format(
127 human_value = value.copy()
128 human_value['boot_time'] = time_to_datetime(boot_time)
129 human_value['uptime'] = age(time_to_datetime(boot_time), show_suffix=False)
130 human_value['text'] = 'Server started {}'.format(
131 131 age(time_to_datetime(boot_time)))
132 132
133 return SysInfoRes(value=_uptime, human_value=_uptime_human)
133 return SysInfoRes(value=value, human_value=human_value)
134 134
135 135
136 136 def memory():
137 137 from rhodecode.lib.helpers import format_byte_size_binary
138 _memory = {'available': 0, 'used': 0, 'cached': 0, 'percent': 0,
139 'percent_used': 0, 'free': 0, 'inactive': 0, 'active': 0,
140 'shared': 0, 'total': 0, 'buffers': 0}
138 value = dict(available=0, used=0, cached=0, percent=0, percent_used=0,
139 free=0, inactive=0, active=0, shared=0, total=0, buffers=0,
140 text='')
141
141 142 state = STATE_OK_DEFAULT
142 143 if not psutil:
143 return SysInfoRes(value=_memory, state=state)
144 return SysInfoRes(value=value, state=state)
144 145
145 # memory
146 _memory = dict(psutil.virtual_memory()._asdict())
147 _memory['percent_used'] = psutil._common.usage_percent(
148 (_memory['total'] - _memory['free']), _memory['total'], 1)
146 value.update(dict(psutil.virtual_memory()._asdict()))
147 value['percent_used'] = psutil._common.usage_percent(
148 (value['total'] - value['free']), value['total'], 1)
149 149
150 try:
151 human_value = '%s/%s, %s%% used' % (
152 format_byte_size_binary(_memory['used']),
153 format_byte_size_binary(_memory['total']),
154 _memory['percent_used'],)
155 except TypeError:
156 human_value = 'NOT AVAILABLE'
150 human_value = value.copy()
151 human_value['text'] = '%s/%s, %s%% used' % (
152 format_byte_size_binary(value['used']),
153 format_byte_size_binary(value['total']),
154 value['percent_used'],)
157 155
158 if state['type'] == STATE_OK and _memory['percent_used'] > 90:
156 keys = value.keys()[::]
157 keys.pop(keys.index('percent'))
158 keys.pop(keys.index('percent_used'))
159 keys.pop(keys.index('text'))
160 for k in keys:
161 human_value[k] = format_byte_size_binary(value[k])
162
163 if state['type'] == STATE_OK and value['percent_used'] > 90:
159 164 msg = 'Critical: your available RAM memory is very low.'
160 165 state = {'message': msg, 'type': STATE_ERR}
161 166
162 elif state['type'] == STATE_OK and _memory['percent_used'] > 70:
167 elif state['type'] == STATE_OK and value['percent_used'] > 70:
163 168 msg = 'Warning: your available RAM memory is running low.'
164 169 state = {'message': msg, 'type': STATE_WARN}
165 170
166 return SysInfoRes(value=_memory, state=state, human_value=human_value)
171 return SysInfoRes(value=value, state=state, human_value=human_value)
167 172
168 173
169 174 def machine_load():
170 _load = {'1_min': _NA, '5_min': _NA, '15_min': _NA}
171
175 value = {'1_min': _NA, '5_min': _NA, '15_min': _NA, 'text': ''}
172 176 state = STATE_OK_DEFAULT
173 177 if not psutil:
174 return SysInfoRes(value=_load, state=state)
178 return SysInfoRes(value=value, state=state)
175 179
176 180 # load averages
177 181 if hasattr(psutil.os, 'getloadavg'):
178 _load = dict(zip(['1_min', '5_min', '15_min'], psutil.os.getloadavg()))
182 value.update(dict(
183 zip(['1_min', '5_min', '15_min'], psutil.os.getloadavg())))
179 184
180 human_value = '1min: %s, 5min: %s, 15min: %s' % (
181 _load['1_min'], _load['5_min'], _load['15_min'])
185 human_value = value.copy()
186 human_value['text'] = '1min: {}, 5min: {}, 15min: {}'.format(
187 value['1_min'], value['5_min'], value['15_min'])
182 188
183 # TODO: warn about too-much load 15 min
184 return SysInfoRes(value=_load, state=state, human_value=human_value)
189 if state['type'] == STATE_OK and value['15_min'] > 5:
190 msg = 'Warning: your machine load is very high.'
191 state = {'message': msg, 'type': STATE_WARN}
192
193 return SysInfoRes(value=value, state=state, human_value=human_value)
185 194
186 195
187 196 def cpu():
197 value = 0
188 198 state = STATE_OK_DEFAULT
189 cpu_value = 0
199
190 200 if not psutil:
191 return SysInfoRes(value=cpu_value, state=state)
201 return SysInfoRes(value=value, state=state)
192 202
193 cpu_value = psutil.cpu_percent(0.5)
194 human_value = '{} %'.format(cpu_value)
195 return SysInfoRes(value=cpu_value, state=state, human_value=human_value)
203 value = psutil.cpu_percent(0.5)
204 human_value = '{} %'.format(value)
205 return SysInfoRes(value=value, state=state, human_value=human_value)
196 206
197 207
198 208 def storage():
199 209 from rhodecode.lib.helpers import format_byte_size_binary
200 210 from rhodecode.model.settings import VcsSettingsModel
201 211 path = VcsSettingsModel().get_repos_location()
202 212
203 # disk storage
204 disk = {'percent': 0, 'used': 0, 'total': 0, 'path': path, 'text': ''}
213 value = dict(percent=0, used=0, total=0, path=path, text='')
205 214 state = STATE_OK_DEFAULT
206 215 if not psutil:
207 return SysInfoRes(value=disk, state=state)
216 return SysInfoRes(value=value, state=state)
208 217
209 218 try:
210 disk.update(dict(psutil.disk_usage(path)._asdict()))
219 value.update(dict(psutil.disk_usage(path)._asdict()))
211 220 except Exception as e:
212 221 log.exception('Failed to fetch disk info')
213 222 state = {'message': str(e), 'type': STATE_ERR}
214 223
215 human_value = disk
224 human_value = value.copy()
225 human_value['used'] = format_byte_size_binary(value['used'])
226 human_value['total'] = format_byte_size_binary(value['total'])
216 227 human_value['text'] = "{}/{}, {}% used".format(
217 format_byte_size_binary(disk['used']),
218 format_byte_size_binary(disk['total']),
219 (disk['percent']))
228 format_byte_size_binary(value['used']),
229 format_byte_size_binary(value['total']),
230 value['percent'])
220 231
221 if state['type'] == STATE_OK and disk['percent'] > 90:
232 if state['type'] == STATE_OK and value['percent'] > 90:
222 233 msg = 'Critical: your disk space is very low.'
223 234 state = {'message': msg, 'type': STATE_ERR}
224 235
225 elif state['type'] == STATE_OK and disk['percent'] > 70:
236 elif state['type'] == STATE_OK and value['percent'] > 70:
226 237 msg = 'Warning: your disk space is running low.'
227 238 state = {'message': msg, 'type': STATE_WARN}
228 239
229 return SysInfoRes(value=disk, state=state, human_value=human_value)
240 return SysInfoRes(value=value, state=state, human_value=human_value)
230 241
231 242
232 243 def storage_inodes():
233 244 from rhodecode.model.settings import VcsSettingsModel
234 245 path = VcsSettingsModel().get_repos_location()
235 246
236 _disk_inodes = dict(percent=0, free=0, used=0, total=0, path=path, text='')
247 value = dict(percent=0, free=0, used=0, total=0, path=path, text='')
237 248 state = STATE_OK_DEFAULT
238 249 if not psutil:
239 return SysInfoRes(value=_disk_inodes, state=state)
250 return SysInfoRes(value=value, state=state)
240 251
241 252 try:
242 253 i_stat = os.statvfs(path)
243 254
244 _disk_inodes['used'] = i_stat.f_ffree
245 _disk_inodes['free'] = i_stat.f_favail
246 _disk_inodes['total'] = i_stat.f_files
247 _disk_inodes['percent'] = percentage(
248 _disk_inodes['used'], _disk_inodes['total'])
255 value['used'] = i_stat.f_ffree
256 value['free'] = i_stat.f_favail
257 value['total'] = i_stat.f_files
258 value['percent'] = percentage(
259 value['used'], value['total'])
249 260 except Exception as e:
250 261 log.exception('Failed to fetch disk inodes info')
251 262 state = {'message': str(e), 'type': STATE_ERR}
252 263
253 human_value = _disk_inodes
264 human_value = value.copy()
254 265 human_value['text'] = "{}/{}, {}% used".format(
255 _disk_inodes['used'], _disk_inodes['total'], _disk_inodes['percent'])
266 value['used'], value['total'], value['percent'])
256 267
257 if state['type'] == STATE_OK and _disk_inodes['percent'] > 90:
268 if state['type'] == STATE_OK and value['percent'] > 90:
258 269 msg = 'Critical: your disk free inodes are very low.'
259 270 state = {'message': msg, 'type': STATE_ERR}
260 271
261 elif state['type'] == STATE_OK and _disk_inodes['percent'] > 70:
272 elif state['type'] == STATE_OK and value['percent'] > 70:
262 273 msg = 'Warning: your disk free inodes are running low.'
263 274 state = {'message': msg, 'type': STATE_WARN}
264 275
265 return SysInfoRes(value=_disk_inodes, state=state)
276 return SysInfoRes(value=value, state=state)
266 277
267 278
268 279 def storage_archives():
269 280 import rhodecode
270 281 from rhodecode.lib.utils import safe_str
271 282 from rhodecode.lib.helpers import format_byte_size_binary
272 283
273 284 msg = 'Enable this by setting ' \
274 285 'archive_cache_dir=/path/to/cache option in the .ini file'
275 286 path = safe_str(rhodecode.CONFIG.get('archive_cache_dir', msg))
276 287
277 disk_archive = dict(percent=0, used=0, total=0, items=0, path=path, text='')
288 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
278 289 state = STATE_OK_DEFAULT
279 290 try:
280 291 items_count = 0
281 292 used = 0
282 293 for root, dirs, files in os.walk(path):
283 294 if root == path:
284 items_count = len(dirs)
295 items_count = len(files)
285 296
286 297 for f in files:
287 298 try:
288 299 used += os.path.getsize(os.path.join(root, f))
289 300 except OSError:
290 301 pass
291 disk_archive.update({
302 value.update({
292 303 'percent': 100,
293 304 'used': used,
294 305 'total': used,
295 306 'items': items_count
296 307 })
297 308
298 309 except Exception as e:
299 310 log.exception('failed to fetch archive cache storage')
300 311 state = {'message': str(e), 'type': STATE_ERR}
301 312
302 human_value = disk_archive
303 human_value['text'] = "{} ({} items)".format(format_byte_size_binary(
304 disk_archive['used']), disk_archive['total'])
313 human_value = value.copy()
314 human_value['used'] = format_byte_size_binary(value['used'])
315 human_value['total'] = format_byte_size_binary(value['total'])
316 human_value['text'] = "{} ({} items)".format(
317 human_value['used'], value['items'])
305 318
306 return SysInfoRes(value=disk_archive, state=state, human_value=human_value)
319 return SysInfoRes(value=value, state=state, human_value=human_value)
307 320
308 321
309 322 def storage_gist():
310 323 from rhodecode.model.gist import GIST_STORE_LOC
311 324 from rhodecode.model.settings import VcsSettingsModel
312 325 from rhodecode.lib.utils import safe_str
313 326 from rhodecode.lib.helpers import format_byte_size_binary
314 327 path = safe_str(os.path.join(
315 328 VcsSettingsModel().get_repos_location(), GIST_STORE_LOC))
316 329
317 330 # gist storage
318 _disk_gist = dict(percent=0, used=0, total=0, items=0, path=path, text='')
331 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
319 332 state = STATE_OK_DEFAULT
320 333
321 334 try:
322 335 items_count = 0
323 336 used = 0
324 337 for root, dirs, files in os.walk(path):
325 338 if root == path:
326 339 items_count = len(dirs)
327 340
328 341 for f in files:
329 342 try:
330 343 used += os.path.getsize(os.path.join(root, f))
331 344 except OSError:
332 345 pass
333 _disk_gist.update({
346 value.update({
334 347 'percent': 100,
335 348 'used': used,
336 349 'total': used,
337 350 'items': items_count
338 351 })
339 352 except Exception as e:
340 353 log.exception('failed to fetch gist storage items')
341 354 state = {'message': str(e), 'type': STATE_ERR}
342 355
343 human_value = _disk_gist
344 human_value['text'] = "{} ({} items)".format(format_byte_size_binary(
345 _disk_gist['used']), _disk_gist['items'])
346 return SysInfoRes(value=_disk_gist, state=state, human_value=human_value)
356 human_value = value.copy()
357 human_value['used'] = format_byte_size_binary(value['used'])
358 human_value['total'] = format_byte_size_binary(value['total'])
359 human_value['text'] = "{} ({} items)".format(
360 human_value['used'], value['items'])
361
362 return SysInfoRes(value=value, state=state, human_value=human_value)
347 363
348 364
349 def storage_search():
365 def search_info():
350 366 import rhodecode
351 path = rhodecode.CONFIG.get('search.location', '')
367 from rhodecode.lib.index import searcher_from_config
352 368
353 # search index storage
354 _disk_index = dict(percent=0, used=0, total=0, path=path, text='')
355 state = STATE_OK_DEFAULT
369 backend = rhodecode.CONFIG.get('search.module', '')
370 location = rhodecode.CONFIG.get('search.location', '')
371
356 372 try:
357 search_index_storage_path_exists = os.path.isdir(path)
358 if search_index_storage_path_exists:
359 used = get_storage_size(path)
360 _disk_index.update({
361 'percent': 100,
362 'used': used,
363 'total': used,
364 })
365 except Exception as e:
366 log.exception('failed to fetch search index storage')
367 state = {'message': str(e), 'type': STATE_ERR}
373 searcher = searcher_from_config(rhodecode.CONFIG)
374 searcher = searcher.__class__.__name__
375 except Exception:
376 searcher = None
368 377
369 human_value = _disk_index
370 human_value['text'] = "{}/{}, {}% used".format(
371 _disk_index['used'], _disk_index['total'], _disk_index['percent'])
378 value = dict(
379 backend=backend, searcher=searcher, location=location, text='')
380 state = STATE_OK_DEFAULT
372 381
373 return SysInfoRes(value=_disk_index, state=state, human_value=human_value)
382 human_value = value.copy()
383 human_value['text'] = "backend:`{}`".format(human_value['backend'])
384
385 return SysInfoRes(value=value, state=state, human_value=human_value)
374 386
375 387
376 388 def git_info():
377 389 from rhodecode.lib.vcs.backends import git
378 390 state = STATE_OK_DEFAULT
379 391 value = human_value = ''
380 392 try:
381 393 value = git.discover_git_version(raise_on_exc=True)
382 394 human_value = 'version reported from VCSServer: {}'.format(value)
383 395 except Exception as e:
384 396 state = {'message': str(e), 'type': STATE_ERR}
385 397
386 398 return SysInfoRes(value=value, state=state, human_value=human_value)
387 399
388 400
389 401 def hg_info():
390 402 from rhodecode.lib.vcs.backends import hg
391 403 state = STATE_OK_DEFAULT
392 404 value = human_value = ''
393 405 try:
394 406 value = hg.discover_hg_version(raise_on_exc=True)
395 407 human_value = 'version reported from VCSServer: {}'.format(value)
396 408 except Exception as e:
397 409 state = {'message': str(e), 'type': STATE_ERR}
398 410 return SysInfoRes(value=value, state=state, human_value=human_value)
399 411
400 412
401 413 def svn_info():
402 414 from rhodecode.lib.vcs.backends import svn
403 415 state = STATE_OK_DEFAULT
404 416 value = human_value = ''
405 417 try:
406 418 value = svn.discover_svn_version(raise_on_exc=True)
407 419 human_value = 'version reported from VCSServer: {}'.format(value)
408 420 except Exception as e:
409 421 state = {'message': str(e), 'type': STATE_ERR}
410 422 return SysInfoRes(value=value, state=state, human_value=human_value)
411 423
412 424
413 425 def vcs_backends():
414 426 import rhodecode
415 value = rhodecode.CONFIG.get('vcs.backends', '').split(',')
427 value = map(
428 string.strip, rhodecode.CONFIG.get('vcs.backends', '').split(','))
416 429 human_value = 'Enabled backends in order: {}'.format(','.join(value))
417 430 return SysInfoRes(value=value, human_value=human_value)
418 431
419 432
420 433 def vcs_server():
421 434 import rhodecode
422 435 from rhodecode.lib.vcs.backends import get_vcsserver_version
423 436
424 437 server_url = rhodecode.CONFIG.get('vcs.server')
425 438 enabled = rhodecode.CONFIG.get('vcs.server.enable')
426 439 protocol = rhodecode.CONFIG.get('vcs.server.protocol')
427 440 state = STATE_OK_DEFAULT
428 441 version = None
429 442
430 443 try:
431 444 version = get_vcsserver_version()
432 445 connection = 'connected'
433 446 except Exception as e:
434 447 connection = 'failed'
435 448 state = {'message': str(e), 'type': STATE_ERR}
436 449
437 450 value = dict(
438 451 url=server_url,
439 452 enabled=enabled,
440 453 protocol=protocol,
441 454 connection=connection,
442 455 version=version,
443 456 text='',
444 457 )
445 458
446 human_value = value
459 human_value = value.copy()
447 460 human_value['text'] = \
448 461 '{url}@ver:{ver} via {mode} mode, connection:{conn}'.format(
449 462 url=server_url, ver=version, mode=protocol, conn=connection)
450 463
451 return SysInfoRes(value='', state=state, human_value=human_value)
464 return SysInfoRes(value=value, state=state, human_value=human_value)
452 465
453 466
454 467 def rhodecode_app_info():
455 468 import rhodecode
456 return SysInfoRes(value={'rhodecode_version': rhodecode.__version__})
469 value = dict(
470 rhodecode_version=rhodecode.__version__,
471 rhodecode_lib_path=os.path.abspath(rhodecode.__file__)
472 )
473 return SysInfoRes(value=value)
457 474
458 475
459 476 def rhodecode_config():
460 477 import rhodecode
461 478 path = rhodecode.CONFIG.get('__file__')
462 479 rhodecode_ini_safe = rhodecode.CONFIG.copy()
463 480
464 481 blacklist = [
465 482 'rhodecode_license_key',
466 483 'routes.map',
467 484 'pylons.h',
468 485 'pylons.app_globals',
469 486 'pylons.environ_config',
470 487 'sqlalchemy.db1.url',
471 488 'channelstream.secret',
472 489 'beaker.session.secret',
473 490 'rhodecode.encrypted_values.secret',
474 491 'rhodecode_auth_github_consumer_key',
475 492 'rhodecode_auth_github_consumer_secret',
476 493 'rhodecode_auth_google_consumer_key',
477 494 'rhodecode_auth_google_consumer_secret',
478 495 'rhodecode_auth_bitbucket_consumer_secret',
479 496 'rhodecode_auth_bitbucket_consumer_key',
480 497 'rhodecode_auth_twitter_consumer_secret',
481 498 'rhodecode_auth_twitter_consumer_key',
482 499
483 500 'rhodecode_auth_twitter_secret',
484 501 'rhodecode_auth_github_secret',
485 502 'rhodecode_auth_google_secret',
486 503 'rhodecode_auth_bitbucket_secret',
487 504
488 505 'appenlight.api_key',
489 506 ('app_conf', 'sqlalchemy.db1.url')
490 507 ]
491 508 for k in blacklist:
492 509 if isinstance(k, tuple):
493 510 section, key = k
494 511 if section in rhodecode_ini_safe:
495 512 rhodecode_ini_safe[section] = '**OBFUSCATED**'
496 513 else:
497 514 rhodecode_ini_safe.pop(k, None)
498 515
499 516 # TODO: maybe put some CONFIG checks here ?
500 517 return SysInfoRes(value={'config': rhodecode_ini_safe, 'path': path})
501 518
502 519
503 520 def database_info():
504 521 import rhodecode
505 522 from sqlalchemy.engine import url as engine_url
506 523 from rhodecode.model.meta import Base as sql_base, Session
507 524 from rhodecode.model.db import DbMigrateVersion
508 525
509 526 state = STATE_OK_DEFAULT
510 527
511 528 db_migrate = DbMigrateVersion.query().filter(
512 529 DbMigrateVersion.repository_id == 'rhodecode_db_migrations').one()
513 530
514 531 db_url_obj = engine_url.make_url(rhodecode.CONFIG['sqlalchemy.db1.url'])
515 532
516 533 try:
517 534 engine = sql_base.metadata.bind
518 535 db_server_info = engine.dialect._get_server_version_info(
519 536 Session.connection(bind=engine))
520 537 db_version = '.'.join(map(str, db_server_info))
521 538 except Exception:
522 539 log.exception('failed to fetch db version')
523 540 db_version = 'UNKNOWN'
524 541
525 542 db_info = dict(
526 543 migrate_version=db_migrate.version,
527 544 type=db_url_obj.get_backend_name(),
528 545 version=db_version,
529 546 url=repr(db_url_obj)
530 547 )
531 548
532 human_value = db_info
549 human_value = db_info.copy()
533 550 human_value['url'] = "{} @ migration version: {}".format(
534 551 db_info['url'], db_info['migrate_version'])
535 552 human_value['version'] = "{} {}".format(db_info['type'], db_info['version'])
536 553 return SysInfoRes(value=db_info, state=state, human_value=human_value)
537 554
538 555
539 556 def server_info(environ):
540 557 import rhodecode
541 558 from rhodecode.lib.base import get_server_ip_addr, get_server_port
542 559
543 560 value = {
544 561 'server_ip': '%s:%s' % (
545 562 get_server_ip_addr(environ, log_errors=False),
546 563 get_server_port(environ)
547 564 ),
548 565 'server_id': rhodecode.CONFIG.get('instance_id'),
549 566 }
550 567 return SysInfoRes(value=value)
551 568
552 569
553 570 def get_system_info(environ):
554 571 environ = environ or {}
555 572 return {
556 573 'rhodecode_app': SysInfo(rhodecode_app_info)(),
557 574 'rhodecode_config': SysInfo(rhodecode_config)(),
558 575 'python': SysInfo(python_info)(),
559 576 'py_modules': SysInfo(py_modules)(),
560 577
561 578 'platform': SysInfo(platform_type)(),
562 579 'server': SysInfo(server_info, environ=environ)(),
563 580 'database': SysInfo(database_info)(),
564 581
565 582 'storage': SysInfo(storage)(),
566 583 'storage_inodes': SysInfo(storage_inodes)(),
567 584 'storage_archive': SysInfo(storage_archives)(),
568 'storage_search': SysInfo(storage_search)(),
569 585 'storage_gist': SysInfo(storage_gist)(),
570 586
587 'search': SysInfo(search_info)(),
588
571 589 'uptime': SysInfo(uptime)(),
572 590 'load': SysInfo(machine_load)(),
573 591 'cpu': SysInfo(cpu)(),
574 592 'memory': SysInfo(memory)(),
575 593
576 594 'vcs_backends': SysInfo(vcs_backends)(),
577 595 'vcs_server': SysInfo(vcs_server)(),
578 596
579 597 'git': SysInfo(git_info)(),
580 598 'hg': SysInfo(hg_info)(),
581 599 'svn': SysInfo(svn_info)(),
582 600 }
General Comments 0
You need to be logged in to leave comments. Login now