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