##// END OF EJS Templates
system-info: added UUID placeholder for generating platform unique identifiers.
marcink -
r1115:6e32b207 default
parent child Browse files
Show More
@@ -1,838 +1,839 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 settings controller for rhodecode admin
23 settings controller for rhodecode admin
24 """
24 """
25
25
26 import collections
26 import collections
27 import logging
27 import logging
28 import urllib2
28 import urllib2
29
29
30 import datetime
30 import datetime
31 import formencode
31 import formencode
32 from formencode import htmlfill
32 from formencode import htmlfill
33 import packaging.version
33 import packaging.version
34 from pylons import request, tmpl_context as c, url, config
34 from pylons import request, tmpl_context as c, url, config
35 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
36 from pylons.i18n.translation import _, lazy_ugettext
36 from pylons.i18n.translation import _, lazy_ugettext
37 from pyramid.threadlocal import get_current_registry
37 from pyramid.threadlocal import get_current_registry
38 from webob.exc import HTTPBadRequest
38 from webob.exc import HTTPBadRequest
39
39
40 import rhodecode
40 import rhodecode
41 from rhodecode.admin.navigation import navigation_list
41 from rhodecode.admin.navigation import navigation_list
42 from rhodecode.lib import auth
42 from rhodecode.lib import auth
43 from rhodecode.lib import helpers as h
43 from rhodecode.lib import helpers as h
44 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
44 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
45 from rhodecode.lib.base import BaseController, render
45 from rhodecode.lib.base import BaseController, render
46 from rhodecode.lib.celerylib import tasks, run_task
46 from rhodecode.lib.celerylib import tasks, run_task
47 from rhodecode.lib.utils import repo2db_mapper
47 from rhodecode.lib.utils import repo2db_mapper
48 from rhodecode.lib.utils2 import (
48 from rhodecode.lib.utils2 import (
49 str2bool, safe_unicode, AttributeDict, safe_int)
49 str2bool, safe_unicode, AttributeDict, safe_int)
50 from rhodecode.lib.compat import OrderedDict
50 from rhodecode.lib.compat import OrderedDict
51 from rhodecode.lib.ext_json import json
51 from rhodecode.lib.ext_json import json
52 from rhodecode.lib.utils import jsonify
52 from rhodecode.lib.utils import jsonify
53
53
54 from rhodecode.model.db import RhodeCodeUi, Repository
54 from rhodecode.model.db import RhodeCodeUi, Repository
55 from rhodecode.model.forms import ApplicationSettingsForm, \
55 from rhodecode.model.forms import ApplicationSettingsForm, \
56 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
56 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
57 LabsSettingsForm, IssueTrackerPatternsForm
57 LabsSettingsForm, IssueTrackerPatternsForm
58 from rhodecode.model.repo_group import RepoGroupModel
58 from rhodecode.model.repo_group import RepoGroupModel
59
59
60 from rhodecode.model.scm import ScmModel
60 from rhodecode.model.scm import ScmModel
61 from rhodecode.model.notification import EmailNotificationModel
61 from rhodecode.model.notification import EmailNotificationModel
62 from rhodecode.model.meta import Session
62 from rhodecode.model.meta import Session
63 from rhodecode.model.settings import (
63 from rhodecode.model.settings import (
64 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
64 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
65 SettingsModel)
65 SettingsModel)
66
66
67 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
67 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
68 from rhodecode.svn_support.config_keys import generate_config
68 from rhodecode.svn_support.config_keys import generate_config
69
69
70
70
71 log = logging.getLogger(__name__)
71 log = logging.getLogger(__name__)
72
72
73
73
74 class SettingsController(BaseController):
74 class SettingsController(BaseController):
75 """REST Controller styled on the Atom Publishing Protocol"""
75 """REST Controller styled on the Atom Publishing Protocol"""
76 # To properly map this controller, ensure your config/routing.py
76 # To properly map this controller, ensure your config/routing.py
77 # file has a resource setup:
77 # file has a resource setup:
78 # map.resource('setting', 'settings', controller='admin/settings',
78 # map.resource('setting', 'settings', controller='admin/settings',
79 # path_prefix='/admin', name_prefix='admin_')
79 # path_prefix='/admin', name_prefix='admin_')
80
80
81 @LoginRequired()
81 @LoginRequired()
82 def __before__(self):
82 def __before__(self):
83 super(SettingsController, self).__before__()
83 super(SettingsController, self).__before__()
84 c.labs_active = str2bool(
84 c.labs_active = str2bool(
85 rhodecode.CONFIG.get('labs_settings_active', 'true'))
85 rhodecode.CONFIG.get('labs_settings_active', 'true'))
86 c.navlist = navigation_list(request)
86 c.navlist = navigation_list(request)
87
87
88 def _get_hg_ui_settings(self):
88 def _get_hg_ui_settings(self):
89 ret = RhodeCodeUi.query().all()
89 ret = RhodeCodeUi.query().all()
90
90
91 if not ret:
91 if not ret:
92 raise Exception('Could not get application ui settings !')
92 raise Exception('Could not get application ui settings !')
93 settings = {}
93 settings = {}
94 for each in ret:
94 for each in ret:
95 k = each.ui_key
95 k = each.ui_key
96 v = each.ui_value
96 v = each.ui_value
97 if k == '/':
97 if k == '/':
98 k = 'root_path'
98 k = 'root_path'
99
99
100 if k in ['push_ssl', 'publish']:
100 if k in ['push_ssl', 'publish']:
101 v = str2bool(v)
101 v = str2bool(v)
102
102
103 if k.find('.') != -1:
103 if k.find('.') != -1:
104 k = k.replace('.', '_')
104 k = k.replace('.', '_')
105
105
106 if each.ui_section in ['hooks', 'extensions']:
106 if each.ui_section in ['hooks', 'extensions']:
107 v = each.ui_active
107 v = each.ui_active
108
108
109 settings[each.ui_section + '_' + k] = v
109 settings[each.ui_section + '_' + k] = v
110 return settings
110 return settings
111
111
112 @HasPermissionAllDecorator('hg.admin')
112 @HasPermissionAllDecorator('hg.admin')
113 @auth.CSRFRequired()
113 @auth.CSRFRequired()
114 @jsonify
114 @jsonify
115 def delete_svn_pattern(self):
115 def delete_svn_pattern(self):
116 if not request.is_xhr:
116 if not request.is_xhr:
117 raise HTTPBadRequest()
117 raise HTTPBadRequest()
118
118
119 delete_pattern_id = request.POST.get('delete_svn_pattern')
119 delete_pattern_id = request.POST.get('delete_svn_pattern')
120 model = VcsSettingsModel()
120 model = VcsSettingsModel()
121 try:
121 try:
122 model.delete_global_svn_pattern(delete_pattern_id)
122 model.delete_global_svn_pattern(delete_pattern_id)
123 except SettingNotFound:
123 except SettingNotFound:
124 raise HTTPBadRequest()
124 raise HTTPBadRequest()
125
125
126 Session().commit()
126 Session().commit()
127 return True
127 return True
128
128
129 @HasPermissionAllDecorator('hg.admin')
129 @HasPermissionAllDecorator('hg.admin')
130 @auth.CSRFRequired()
130 @auth.CSRFRequired()
131 def settings_vcs_update(self):
131 def settings_vcs_update(self):
132 """POST /admin/settings: All items in the collection"""
132 """POST /admin/settings: All items in the collection"""
133 # url('admin_settings_vcs')
133 # url('admin_settings_vcs')
134 c.active = 'vcs'
134 c.active = 'vcs'
135
135
136 model = VcsSettingsModel()
136 model = VcsSettingsModel()
137 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
137 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
138 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
138 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
139
139
140 # TODO: Replace with request.registry after migrating to pyramid.
140 # TODO: Replace with request.registry after migrating to pyramid.
141 pyramid_settings = get_current_registry().settings
141 pyramid_settings = get_current_registry().settings
142 c.svn_proxy_generate_config = pyramid_settings[generate_config]
142 c.svn_proxy_generate_config = pyramid_settings[generate_config]
143
143
144 application_form = ApplicationUiSettingsForm()()
144 application_form = ApplicationUiSettingsForm()()
145
145
146 try:
146 try:
147 form_result = application_form.to_python(dict(request.POST))
147 form_result = application_form.to_python(dict(request.POST))
148 except formencode.Invalid as errors:
148 except formencode.Invalid as errors:
149 h.flash(
149 h.flash(
150 _("Some form inputs contain invalid data."),
150 _("Some form inputs contain invalid data."),
151 category='error')
151 category='error')
152 return htmlfill.render(
152 return htmlfill.render(
153 render('admin/settings/settings.html'),
153 render('admin/settings/settings.html'),
154 defaults=errors.value,
154 defaults=errors.value,
155 errors=errors.error_dict or {},
155 errors=errors.error_dict or {},
156 prefix_error=False,
156 prefix_error=False,
157 encoding="UTF-8",
157 encoding="UTF-8",
158 force_defaults=False
158 force_defaults=False
159 )
159 )
160
160
161 try:
161 try:
162 if c.visual.allow_repo_location_change:
162 if c.visual.allow_repo_location_change:
163 model.update_global_path_setting(
163 model.update_global_path_setting(
164 form_result['paths_root_path'])
164 form_result['paths_root_path'])
165
165
166 model.update_global_ssl_setting(form_result['web_push_ssl'])
166 model.update_global_ssl_setting(form_result['web_push_ssl'])
167 model.update_global_hook_settings(form_result)
167 model.update_global_hook_settings(form_result)
168
168
169 model.create_or_update_global_svn_settings(form_result)
169 model.create_or_update_global_svn_settings(form_result)
170 model.create_or_update_global_hg_settings(form_result)
170 model.create_or_update_global_hg_settings(form_result)
171 model.create_or_update_global_pr_settings(form_result)
171 model.create_or_update_global_pr_settings(form_result)
172 except Exception:
172 except Exception:
173 log.exception("Exception while updating settings")
173 log.exception("Exception while updating settings")
174 h.flash(_('Error occurred during updating '
174 h.flash(_('Error occurred during updating '
175 'application settings'), category='error')
175 'application settings'), category='error')
176 else:
176 else:
177 Session().commit()
177 Session().commit()
178 h.flash(_('Updated VCS settings'), category='success')
178 h.flash(_('Updated VCS settings'), category='success')
179 return redirect(url('admin_settings_vcs'))
179 return redirect(url('admin_settings_vcs'))
180
180
181 return htmlfill.render(
181 return htmlfill.render(
182 render('admin/settings/settings.html'),
182 render('admin/settings/settings.html'),
183 defaults=self._form_defaults(),
183 defaults=self._form_defaults(),
184 encoding="UTF-8",
184 encoding="UTF-8",
185 force_defaults=False)
185 force_defaults=False)
186
186
187 @HasPermissionAllDecorator('hg.admin')
187 @HasPermissionAllDecorator('hg.admin')
188 def settings_vcs(self):
188 def settings_vcs(self):
189 """GET /admin/settings: All items in the collection"""
189 """GET /admin/settings: All items in the collection"""
190 # url('admin_settings_vcs')
190 # url('admin_settings_vcs')
191 c.active = 'vcs'
191 c.active = 'vcs'
192 model = VcsSettingsModel()
192 model = VcsSettingsModel()
193 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
193 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
194 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
194 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
195
195
196 # TODO: Replace with request.registry after migrating to pyramid.
196 # TODO: Replace with request.registry after migrating to pyramid.
197 pyramid_settings = get_current_registry().settings
197 pyramid_settings = get_current_registry().settings
198 c.svn_proxy_generate_config = pyramid_settings[generate_config]
198 c.svn_proxy_generate_config = pyramid_settings[generate_config]
199
199
200 return htmlfill.render(
200 return htmlfill.render(
201 render('admin/settings/settings.html'),
201 render('admin/settings/settings.html'),
202 defaults=self._form_defaults(),
202 defaults=self._form_defaults(),
203 encoding="UTF-8",
203 encoding="UTF-8",
204 force_defaults=False)
204 force_defaults=False)
205
205
206 @HasPermissionAllDecorator('hg.admin')
206 @HasPermissionAllDecorator('hg.admin')
207 @auth.CSRFRequired()
207 @auth.CSRFRequired()
208 def settings_mapping_update(self):
208 def settings_mapping_update(self):
209 """POST /admin/settings/mapping: All items in the collection"""
209 """POST /admin/settings/mapping: All items in the collection"""
210 # url('admin_settings_mapping')
210 # url('admin_settings_mapping')
211 c.active = 'mapping'
211 c.active = 'mapping'
212 rm_obsolete = request.POST.get('destroy', False)
212 rm_obsolete = request.POST.get('destroy', False)
213 invalidate_cache = request.POST.get('invalidate', False)
213 invalidate_cache = request.POST.get('invalidate', False)
214 log.debug(
214 log.debug(
215 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
215 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
216
216
217 if invalidate_cache:
217 if invalidate_cache:
218 log.debug('invalidating all repositories cache')
218 log.debug('invalidating all repositories cache')
219 for repo in Repository.get_all():
219 for repo in Repository.get_all():
220 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
220 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
221
221
222 filesystem_repos = ScmModel().repo_scan()
222 filesystem_repos = ScmModel().repo_scan()
223 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
223 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
224 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
224 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
225 h.flash(_('Repositories successfully '
225 h.flash(_('Repositories successfully '
226 'rescanned added: %s ; removed: %s') %
226 'rescanned added: %s ; removed: %s') %
227 (_repr(added), _repr(removed)),
227 (_repr(added), _repr(removed)),
228 category='success')
228 category='success')
229 return redirect(url('admin_settings_mapping'))
229 return redirect(url('admin_settings_mapping'))
230
230
231 @HasPermissionAllDecorator('hg.admin')
231 @HasPermissionAllDecorator('hg.admin')
232 def settings_mapping(self):
232 def settings_mapping(self):
233 """GET /admin/settings/mapping: All items in the collection"""
233 """GET /admin/settings/mapping: All items in the collection"""
234 # url('admin_settings_mapping')
234 # url('admin_settings_mapping')
235 c.active = 'mapping'
235 c.active = 'mapping'
236
236
237 return htmlfill.render(
237 return htmlfill.render(
238 render('admin/settings/settings.html'),
238 render('admin/settings/settings.html'),
239 defaults=self._form_defaults(),
239 defaults=self._form_defaults(),
240 encoding="UTF-8",
240 encoding="UTF-8",
241 force_defaults=False)
241 force_defaults=False)
242
242
243 @HasPermissionAllDecorator('hg.admin')
243 @HasPermissionAllDecorator('hg.admin')
244 @auth.CSRFRequired()
244 @auth.CSRFRequired()
245 def settings_global_update(self):
245 def settings_global_update(self):
246 """POST /admin/settings/global: All items in the collection"""
246 """POST /admin/settings/global: All items in the collection"""
247 # url('admin_settings_global')
247 # url('admin_settings_global')
248 c.active = 'global'
248 c.active = 'global'
249 c.personal_repo_group_default_pattern = RepoGroupModel()\
249 c.personal_repo_group_default_pattern = RepoGroupModel()\
250 .get_personal_group_name_pattern()
250 .get_personal_group_name_pattern()
251 application_form = ApplicationSettingsForm()()
251 application_form = ApplicationSettingsForm()()
252 try:
252 try:
253 form_result = application_form.to_python(dict(request.POST))
253 form_result = application_form.to_python(dict(request.POST))
254 except formencode.Invalid as errors:
254 except formencode.Invalid as errors:
255 return htmlfill.render(
255 return htmlfill.render(
256 render('admin/settings/settings.html'),
256 render('admin/settings/settings.html'),
257 defaults=errors.value,
257 defaults=errors.value,
258 errors=errors.error_dict or {},
258 errors=errors.error_dict or {},
259 prefix_error=False,
259 prefix_error=False,
260 encoding="UTF-8",
260 encoding="UTF-8",
261 force_defaults=False)
261 force_defaults=False)
262
262
263 try:
263 try:
264 settings = [
264 settings = [
265 ('title', 'rhodecode_title', 'unicode'),
265 ('title', 'rhodecode_title', 'unicode'),
266 ('realm', 'rhodecode_realm', 'unicode'),
266 ('realm', 'rhodecode_realm', 'unicode'),
267 ('pre_code', 'rhodecode_pre_code', 'unicode'),
267 ('pre_code', 'rhodecode_pre_code', 'unicode'),
268 ('post_code', 'rhodecode_post_code', 'unicode'),
268 ('post_code', 'rhodecode_post_code', 'unicode'),
269 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
269 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
270 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
270 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
271 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
271 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
272 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
272 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
273 ]
273 ]
274 for setting, form_key, type_ in settings:
274 for setting, form_key, type_ in settings:
275 sett = SettingsModel().create_or_update_setting(
275 sett = SettingsModel().create_or_update_setting(
276 setting, form_result[form_key], type_)
276 setting, form_result[form_key], type_)
277 Session().add(sett)
277 Session().add(sett)
278
278
279 Session().commit()
279 Session().commit()
280 SettingsModel().invalidate_settings_cache()
280 SettingsModel().invalidate_settings_cache()
281 h.flash(_('Updated application settings'), category='success')
281 h.flash(_('Updated application settings'), category='success')
282 except Exception:
282 except Exception:
283 log.exception("Exception while updating application settings")
283 log.exception("Exception while updating application settings")
284 h.flash(
284 h.flash(
285 _('Error occurred during updating application settings'),
285 _('Error occurred during updating application settings'),
286 category='error')
286 category='error')
287
287
288 return redirect(url('admin_settings_global'))
288 return redirect(url('admin_settings_global'))
289
289
290 @HasPermissionAllDecorator('hg.admin')
290 @HasPermissionAllDecorator('hg.admin')
291 def settings_global(self):
291 def settings_global(self):
292 """GET /admin/settings/global: All items in the collection"""
292 """GET /admin/settings/global: All items in the collection"""
293 # url('admin_settings_global')
293 # url('admin_settings_global')
294 c.active = 'global'
294 c.active = 'global'
295 c.personal_repo_group_default_pattern = RepoGroupModel()\
295 c.personal_repo_group_default_pattern = RepoGroupModel()\
296 .get_personal_group_name_pattern()
296 .get_personal_group_name_pattern()
297
297
298 return htmlfill.render(
298 return htmlfill.render(
299 render('admin/settings/settings.html'),
299 render('admin/settings/settings.html'),
300 defaults=self._form_defaults(),
300 defaults=self._form_defaults(),
301 encoding="UTF-8",
301 encoding="UTF-8",
302 force_defaults=False)
302 force_defaults=False)
303
303
304 @HasPermissionAllDecorator('hg.admin')
304 @HasPermissionAllDecorator('hg.admin')
305 @auth.CSRFRequired()
305 @auth.CSRFRequired()
306 def settings_visual_update(self):
306 def settings_visual_update(self):
307 """POST /admin/settings/visual: All items in the collection"""
307 """POST /admin/settings/visual: All items in the collection"""
308 # url('admin_settings_visual')
308 # url('admin_settings_visual')
309 c.active = 'visual'
309 c.active = 'visual'
310 application_form = ApplicationVisualisationForm()()
310 application_form = ApplicationVisualisationForm()()
311 try:
311 try:
312 form_result = application_form.to_python(dict(request.POST))
312 form_result = application_form.to_python(dict(request.POST))
313 except formencode.Invalid as errors:
313 except formencode.Invalid as errors:
314 return htmlfill.render(
314 return htmlfill.render(
315 render('admin/settings/settings.html'),
315 render('admin/settings/settings.html'),
316 defaults=errors.value,
316 defaults=errors.value,
317 errors=errors.error_dict or {},
317 errors=errors.error_dict or {},
318 prefix_error=False,
318 prefix_error=False,
319 encoding="UTF-8",
319 encoding="UTF-8",
320 force_defaults=False
320 force_defaults=False
321 )
321 )
322
322
323 try:
323 try:
324 settings = [
324 settings = [
325 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
325 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
326 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
326 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
327 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
327 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
328 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
328 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
329 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
329 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
330 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
330 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
331 ('show_version', 'rhodecode_show_version', 'bool'),
331 ('show_version', 'rhodecode_show_version', 'bool'),
332 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
332 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
333 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
333 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
334 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
334 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
335 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
335 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
336 ('support_url', 'rhodecode_support_url', 'unicode'),
336 ('support_url', 'rhodecode_support_url', 'unicode'),
337 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
337 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
338 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
338 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
339 ]
339 ]
340 for setting, form_key, type_ in settings:
340 for setting, form_key, type_ in settings:
341 sett = SettingsModel().create_or_update_setting(
341 sett = SettingsModel().create_or_update_setting(
342 setting, form_result[form_key], type_)
342 setting, form_result[form_key], type_)
343 Session().add(sett)
343 Session().add(sett)
344
344
345 Session().commit()
345 Session().commit()
346 SettingsModel().invalidate_settings_cache()
346 SettingsModel().invalidate_settings_cache()
347 h.flash(_('Updated visualisation settings'), category='success')
347 h.flash(_('Updated visualisation settings'), category='success')
348 except Exception:
348 except Exception:
349 log.exception("Exception updating visualization settings")
349 log.exception("Exception updating visualization settings")
350 h.flash(_('Error occurred during updating '
350 h.flash(_('Error occurred during updating '
351 'visualisation settings'),
351 'visualisation settings'),
352 category='error')
352 category='error')
353
353
354 return redirect(url('admin_settings_visual'))
354 return redirect(url('admin_settings_visual'))
355
355
356 @HasPermissionAllDecorator('hg.admin')
356 @HasPermissionAllDecorator('hg.admin')
357 def settings_visual(self):
357 def settings_visual(self):
358 """GET /admin/settings/visual: All items in the collection"""
358 """GET /admin/settings/visual: All items in the collection"""
359 # url('admin_settings_visual')
359 # url('admin_settings_visual')
360 c.active = 'visual'
360 c.active = 'visual'
361
361
362 return htmlfill.render(
362 return htmlfill.render(
363 render('admin/settings/settings.html'),
363 render('admin/settings/settings.html'),
364 defaults=self._form_defaults(),
364 defaults=self._form_defaults(),
365 encoding="UTF-8",
365 encoding="UTF-8",
366 force_defaults=False)
366 force_defaults=False)
367
367
368 @HasPermissionAllDecorator('hg.admin')
368 @HasPermissionAllDecorator('hg.admin')
369 @auth.CSRFRequired()
369 @auth.CSRFRequired()
370 def settings_issuetracker_test(self):
370 def settings_issuetracker_test(self):
371 if request.is_xhr:
371 if request.is_xhr:
372 return h.urlify_commit_message(
372 return h.urlify_commit_message(
373 request.POST.get('test_text', ''),
373 request.POST.get('test_text', ''),
374 'repo_group/test_repo1')
374 'repo_group/test_repo1')
375 else:
375 else:
376 raise HTTPBadRequest()
376 raise HTTPBadRequest()
377
377
378 @HasPermissionAllDecorator('hg.admin')
378 @HasPermissionAllDecorator('hg.admin')
379 @auth.CSRFRequired()
379 @auth.CSRFRequired()
380 def settings_issuetracker_delete(self):
380 def settings_issuetracker_delete(self):
381 uid = request.POST.get('uid')
381 uid = request.POST.get('uid')
382 IssueTrackerSettingsModel().delete_entries(uid)
382 IssueTrackerSettingsModel().delete_entries(uid)
383 h.flash(_('Removed issue tracker entry'), category='success')
383 h.flash(_('Removed issue tracker entry'), category='success')
384 return redirect(url('admin_settings_issuetracker'))
384 return redirect(url('admin_settings_issuetracker'))
385
385
386 @HasPermissionAllDecorator('hg.admin')
386 @HasPermissionAllDecorator('hg.admin')
387 def settings_issuetracker(self):
387 def settings_issuetracker(self):
388 """GET /admin/settings/issue-tracker: All items in the collection"""
388 """GET /admin/settings/issue-tracker: All items in the collection"""
389 # url('admin_settings_issuetracker')
389 # url('admin_settings_issuetracker')
390 c.active = 'issuetracker'
390 c.active = 'issuetracker'
391 defaults = SettingsModel().get_all_settings()
391 defaults = SettingsModel().get_all_settings()
392
392
393 entry_key = 'rhodecode_issuetracker_pat_'
393 entry_key = 'rhodecode_issuetracker_pat_'
394
394
395 c.issuetracker_entries = {}
395 c.issuetracker_entries = {}
396 for k, v in defaults.items():
396 for k, v in defaults.items():
397 if k.startswith(entry_key):
397 if k.startswith(entry_key):
398 uid = k[len(entry_key):]
398 uid = k[len(entry_key):]
399 c.issuetracker_entries[uid] = None
399 c.issuetracker_entries[uid] = None
400
400
401 for uid in c.issuetracker_entries:
401 for uid in c.issuetracker_entries:
402 c.issuetracker_entries[uid] = AttributeDict({
402 c.issuetracker_entries[uid] = AttributeDict({
403 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
403 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
404 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
404 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
405 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
405 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
406 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
406 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
407 })
407 })
408
408
409 return render('admin/settings/settings.html')
409 return render('admin/settings/settings.html')
410
410
411 @HasPermissionAllDecorator('hg.admin')
411 @HasPermissionAllDecorator('hg.admin')
412 @auth.CSRFRequired()
412 @auth.CSRFRequired()
413 def settings_issuetracker_save(self):
413 def settings_issuetracker_save(self):
414 settings_model = IssueTrackerSettingsModel()
414 settings_model = IssueTrackerSettingsModel()
415
415
416 form = IssueTrackerPatternsForm()().to_python(request.POST)
416 form = IssueTrackerPatternsForm()().to_python(request.POST)
417 if form:
417 if form:
418 for uid in form.get('delete_patterns', []):
418 for uid in form.get('delete_patterns', []):
419 settings_model.delete_entries(uid)
419 settings_model.delete_entries(uid)
420
420
421 for pattern in form.get('patterns', []):
421 for pattern in form.get('patterns', []):
422 for setting, value, type_ in pattern:
422 for setting, value, type_ in pattern:
423 sett = settings_model.create_or_update_setting(
423 sett = settings_model.create_or_update_setting(
424 setting, value, type_)
424 setting, value, type_)
425 Session().add(sett)
425 Session().add(sett)
426
426
427 Session().commit()
427 Session().commit()
428
428
429 SettingsModel().invalidate_settings_cache()
429 SettingsModel().invalidate_settings_cache()
430 h.flash(_('Updated issue tracker entries'), category='success')
430 h.flash(_('Updated issue tracker entries'), category='success')
431 return redirect(url('admin_settings_issuetracker'))
431 return redirect(url('admin_settings_issuetracker'))
432
432
433 @HasPermissionAllDecorator('hg.admin')
433 @HasPermissionAllDecorator('hg.admin')
434 @auth.CSRFRequired()
434 @auth.CSRFRequired()
435 def settings_email_update(self):
435 def settings_email_update(self):
436 """POST /admin/settings/email: All items in the collection"""
436 """POST /admin/settings/email: All items in the collection"""
437 # url('admin_settings_email')
437 # url('admin_settings_email')
438 c.active = 'email'
438 c.active = 'email'
439
439
440 test_email = request.POST.get('test_email')
440 test_email = request.POST.get('test_email')
441
441
442 if not test_email:
442 if not test_email:
443 h.flash(_('Please enter email address'), category='error')
443 h.flash(_('Please enter email address'), category='error')
444 return redirect(url('admin_settings_email'))
444 return redirect(url('admin_settings_email'))
445
445
446 email_kwargs = {
446 email_kwargs = {
447 'date': datetime.datetime.now(),
447 'date': datetime.datetime.now(),
448 'user': c.rhodecode_user,
448 'user': c.rhodecode_user,
449 'rhodecode_version': c.rhodecode_version
449 'rhodecode_version': c.rhodecode_version
450 }
450 }
451
451
452 (subject, headers, email_body,
452 (subject, headers, email_body,
453 email_body_plaintext) = EmailNotificationModel().render_email(
453 email_body_plaintext) = EmailNotificationModel().render_email(
454 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
454 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
455
455
456 recipients = [test_email] if test_email else None
456 recipients = [test_email] if test_email else None
457
457
458 run_task(tasks.send_email, recipients, subject,
458 run_task(tasks.send_email, recipients, subject,
459 email_body_plaintext, email_body)
459 email_body_plaintext, email_body)
460
460
461 h.flash(_('Send email task created'), category='success')
461 h.flash(_('Send email task created'), category='success')
462 return redirect(url('admin_settings_email'))
462 return redirect(url('admin_settings_email'))
463
463
464 @HasPermissionAllDecorator('hg.admin')
464 @HasPermissionAllDecorator('hg.admin')
465 def settings_email(self):
465 def settings_email(self):
466 """GET /admin/settings/email: All items in the collection"""
466 """GET /admin/settings/email: All items in the collection"""
467 # url('admin_settings_email')
467 # url('admin_settings_email')
468 c.active = 'email'
468 c.active = 'email'
469 c.rhodecode_ini = rhodecode.CONFIG
469 c.rhodecode_ini = rhodecode.CONFIG
470
470
471 return htmlfill.render(
471 return htmlfill.render(
472 render('admin/settings/settings.html'),
472 render('admin/settings/settings.html'),
473 defaults=self._form_defaults(),
473 defaults=self._form_defaults(),
474 encoding="UTF-8",
474 encoding="UTF-8",
475 force_defaults=False)
475 force_defaults=False)
476
476
477 @HasPermissionAllDecorator('hg.admin')
477 @HasPermissionAllDecorator('hg.admin')
478 @auth.CSRFRequired()
478 @auth.CSRFRequired()
479 def settings_hooks_update(self):
479 def settings_hooks_update(self):
480 """POST or DELETE /admin/settings/hooks: All items in the collection"""
480 """POST or DELETE /admin/settings/hooks: All items in the collection"""
481 # url('admin_settings_hooks')
481 # url('admin_settings_hooks')
482 c.active = 'hooks'
482 c.active = 'hooks'
483 if c.visual.allow_custom_hooks_settings:
483 if c.visual.allow_custom_hooks_settings:
484 ui_key = request.POST.get('new_hook_ui_key')
484 ui_key = request.POST.get('new_hook_ui_key')
485 ui_value = request.POST.get('new_hook_ui_value')
485 ui_value = request.POST.get('new_hook_ui_value')
486
486
487 hook_id = request.POST.get('hook_id')
487 hook_id = request.POST.get('hook_id')
488 new_hook = False
488 new_hook = False
489
489
490 model = SettingsModel()
490 model = SettingsModel()
491 try:
491 try:
492 if ui_value and ui_key:
492 if ui_value and ui_key:
493 model.create_or_update_hook(ui_key, ui_value)
493 model.create_or_update_hook(ui_key, ui_value)
494 h.flash(_('Added new hook'), category='success')
494 h.flash(_('Added new hook'), category='success')
495 new_hook = True
495 new_hook = True
496 elif hook_id:
496 elif hook_id:
497 RhodeCodeUi.delete(hook_id)
497 RhodeCodeUi.delete(hook_id)
498 Session().commit()
498 Session().commit()
499
499
500 # check for edits
500 # check for edits
501 update = False
501 update = False
502 _d = request.POST.dict_of_lists()
502 _d = request.POST.dict_of_lists()
503 for k, v in zip(_d.get('hook_ui_key', []),
503 for k, v in zip(_d.get('hook_ui_key', []),
504 _d.get('hook_ui_value_new', [])):
504 _d.get('hook_ui_value_new', [])):
505 model.create_or_update_hook(k, v)
505 model.create_or_update_hook(k, v)
506 update = True
506 update = True
507
507
508 if update and not new_hook:
508 if update and not new_hook:
509 h.flash(_('Updated hooks'), category='success')
509 h.flash(_('Updated hooks'), category='success')
510 Session().commit()
510 Session().commit()
511 except Exception:
511 except Exception:
512 log.exception("Exception during hook creation")
512 log.exception("Exception during hook creation")
513 h.flash(_('Error occurred during hook creation'),
513 h.flash(_('Error occurred during hook creation'),
514 category='error')
514 category='error')
515
515
516 return redirect(url('admin_settings_hooks'))
516 return redirect(url('admin_settings_hooks'))
517
517
518 @HasPermissionAllDecorator('hg.admin')
518 @HasPermissionAllDecorator('hg.admin')
519 def settings_hooks(self):
519 def settings_hooks(self):
520 """GET /admin/settings/hooks: All items in the collection"""
520 """GET /admin/settings/hooks: All items in the collection"""
521 # url('admin_settings_hooks')
521 # url('admin_settings_hooks')
522 c.active = 'hooks'
522 c.active = 'hooks'
523
523
524 model = SettingsModel()
524 model = SettingsModel()
525 c.hooks = model.get_builtin_hooks()
525 c.hooks = model.get_builtin_hooks()
526 c.custom_hooks = model.get_custom_hooks()
526 c.custom_hooks = model.get_custom_hooks()
527
527
528 return htmlfill.render(
528 return htmlfill.render(
529 render('admin/settings/settings.html'),
529 render('admin/settings/settings.html'),
530 defaults=self._form_defaults(),
530 defaults=self._form_defaults(),
531 encoding="UTF-8",
531 encoding="UTF-8",
532 force_defaults=False)
532 force_defaults=False)
533
533
534 @HasPermissionAllDecorator('hg.admin')
534 @HasPermissionAllDecorator('hg.admin')
535 def settings_search(self):
535 def settings_search(self):
536 """GET /admin/settings/search: All items in the collection"""
536 """GET /admin/settings/search: All items in the collection"""
537 # url('admin_settings_search')
537 # url('admin_settings_search')
538 c.active = 'search'
538 c.active = 'search'
539
539
540 from rhodecode.lib.index import searcher_from_config
540 from rhodecode.lib.index import searcher_from_config
541 searcher = searcher_from_config(config)
541 searcher = searcher_from_config(config)
542 c.statistics = searcher.statistics()
542 c.statistics = searcher.statistics()
543
543
544 return render('admin/settings/settings.html')
544 return render('admin/settings/settings.html')
545
545
546 @HasPermissionAllDecorator('hg.admin')
546 @HasPermissionAllDecorator('hg.admin')
547 def settings_system(self):
547 def settings_system(self):
548 """GET /admin/settings/system: All items in the collection"""
548 """GET /admin/settings/system: All items in the collection"""
549 # url('admin_settings_system')
549 # url('admin_settings_system')
550 snapshot = str2bool(request.GET.get('snapshot'))
550 snapshot = str2bool(request.GET.get('snapshot'))
551 defaults = self._form_defaults()
551 defaults = self._form_defaults()
552
552
553 c.active = 'system'
553 c.active = 'system'
554 c.rhodecode_update_url = defaults.get('rhodecode_update_url')
554 c.rhodecode_update_url = defaults.get('rhodecode_update_url')
555 server_info = ScmModel().get_server_info(request.environ)
555 server_info = ScmModel().get_server_info(request.environ)
556
556
557 for key, val in server_info.iteritems():
557 for key, val in server_info.iteritems():
558 setattr(c, key, val)
558 setattr(c, key, val)
559
559
560 def val(name, subkey='human_value'):
560 def val(name, subkey='human_value'):
561 return server_info[name][subkey]
561 return server_info[name][subkey]
562
562
563 def state(name):
563 def state(name):
564 return server_info[name]['state']
564 return server_info[name]['state']
565
565
566 def val2(name):
566 def val2(name):
567 val = server_info[name]['human_value']
567 val = server_info[name]['human_value']
568 state = server_info[name]['state']
568 state = server_info[name]['state']
569 return val, state
569 return val, state
570
570
571 c.data_items = [
571 c.data_items = [
572 # update info
572 # update info
573 (_('Update info'), h.literal(
573 (_('Update info'), h.literal(
574 '<span class="link" id="check_for_update" >%s.</span>' % (
574 '<span class="link" id="check_for_update" >%s.</span>' % (
575 _('Check for updates')) +
575 _('Check for updates')) +
576 '<br/> <span >%s.</span>' % (_('Note: please make sure this server can access `%s` for the update link to work') % c.rhodecode_update_url)
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 # RhodeCode specific
579 # RhodeCode specific
580 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
580 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
581 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
581 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
582 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
582 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
583 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
583 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
584 ('', '', ''), # spacer
584 ('', '', ''), # spacer
585
585
586 # Database
586 # Database
587 (_('Database'), val('database')['url'], state('database')),
587 (_('Database'), val('database')['url'], state('database')),
588 (_('Database version'), val('database')['version'], state('database')),
588 (_('Database version'), val('database')['version'], state('database')),
589 ('', '', ''), # spacer
589 ('', '', ''), # spacer
590
590
591 # Platform/Python
591 # Platform/Python
592 (_('Platform'), val('platform'), state('platform')),
592 (_('Platform'), val('platform')['name'], state('platform')),
593 (_('Platform UUID'), val('platform')['uuid'], state('platform')),
593 (_('Python version'), val('python')['version'], state('python')),
594 (_('Python version'), val('python')['version'], state('python')),
594 (_('Python path'), val('python')['executable'], state('python')),
595 (_('Python path'), val('python')['executable'], state('python')),
595 ('', '', ''), # spacer
596 ('', '', ''), # spacer
596
597
597 # Systems stats
598 # Systems stats
598 (_('CPU'), val('cpu'), state('cpu')),
599 (_('CPU'), val('cpu'), state('cpu')),
599 (_('Load'), val('load')['text'], state('load')),
600 (_('Load'), val('load')['text'], state('load')),
600 (_('Memory'), val('memory')['text'], state('memory')),
601 (_('Memory'), val('memory')['text'], state('memory')),
601 (_('Uptime'), val('uptime')['text'], state('uptime')),
602 (_('Uptime'), val('uptime')['text'], state('uptime')),
602 ('', '', ''), # spacer
603 ('', '', ''), # spacer
603
604
604 # Repo storage
605 # Repo storage
605 (_('Storage location'), val('storage')['path'], state('storage')),
606 (_('Storage location'), val('storage')['path'], state('storage')),
606 (_('Storage info'), val('storage')['text'], state('storage')),
607 (_('Storage info'), val('storage')['text'], state('storage')),
607 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
608 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
608
609
609 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
610 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
610 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
611 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
611
612
612 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
613 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
613 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
614 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
614
615
615 (_('Search info'), val('search')['text'], state('search')),
616 (_('Search info'), val('search')['text'], state('search')),
616 (_('Search location'), val('search')['location'], state('search')),
617 (_('Search location'), val('search')['location'], state('search')),
617 ('', '', ''), # spacer
618 ('', '', ''), # spacer
618
619
619 # VCS specific
620 # VCS specific
620 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
621 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
621 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
622 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
622 (_('GIT'), val('git'), state('git')),
623 (_('GIT'), val('git'), state('git')),
623 (_('HG'), val('hg'), state('hg')),
624 (_('HG'), val('hg'), state('hg')),
624 (_('SVN'), val('svn'), state('svn')),
625 (_('SVN'), val('svn'), state('svn')),
625
626
626 ]
627 ]
627
628
628 # TODO: marcink, figure out how to allow only selected users to do this
629 # TODO: marcink, figure out how to allow only selected users to do this
629 c.allowed_to_snapshot = c.rhodecode_user.admin
630 c.allowed_to_snapshot = c.rhodecode_user.admin
630
631
631 if snapshot:
632 if snapshot:
632 if c.allowed_to_snapshot:
633 if c.allowed_to_snapshot:
633 c.data_items.pop(0) # remove server info
634 c.data_items.pop(0) # remove server info
634 return render('admin/settings/settings_system_snapshot.html')
635 return render('admin/settings/settings_system_snapshot.html')
635 else:
636 else:
636 h.flash('You are not allowed to do this', category='warning')
637 h.flash('You are not allowed to do this', category='warning')
637
638
638 return htmlfill.render(
639 return htmlfill.render(
639 render('admin/settings/settings.html'),
640 render('admin/settings/settings.html'),
640 defaults=defaults,
641 defaults=defaults,
641 encoding="UTF-8",
642 encoding="UTF-8",
642 force_defaults=False)
643 force_defaults=False)
643
644
644 @staticmethod
645 @staticmethod
645 def get_update_data(update_url):
646 def get_update_data(update_url):
646 """Return the JSON update data."""
647 """Return the JSON update data."""
647 ver = rhodecode.__version__
648 ver = rhodecode.__version__
648 log.debug('Checking for upgrade on `%s` server', update_url)
649 log.debug('Checking for upgrade on `%s` server', update_url)
649 opener = urllib2.build_opener()
650 opener = urllib2.build_opener()
650 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
651 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
651 response = opener.open(update_url)
652 response = opener.open(update_url)
652 response_data = response.read()
653 response_data = response.read()
653 data = json.loads(response_data)
654 data = json.loads(response_data)
654
655
655 return data
656 return data
656
657
657 @HasPermissionAllDecorator('hg.admin')
658 @HasPermissionAllDecorator('hg.admin')
658 def settings_system_update(self):
659 def settings_system_update(self):
659 """GET /admin/settings/system/updates: All items in the collection"""
660 """GET /admin/settings/system/updates: All items in the collection"""
660 # url('admin_settings_system_update')
661 # url('admin_settings_system_update')
661 defaults = self._form_defaults()
662 defaults = self._form_defaults()
662 update_url = defaults.get('rhodecode_update_url', '')
663 update_url = defaults.get('rhodecode_update_url', '')
663
664
664 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s)
665 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s)
665 try:
666 try:
666 data = self.get_update_data(update_url)
667 data = self.get_update_data(update_url)
667 except urllib2.URLError as e:
668 except urllib2.URLError as e:
668 log.exception("Exception contacting upgrade server")
669 log.exception("Exception contacting upgrade server")
669 return _err('Failed to contact upgrade server: %r' % e)
670 return _err('Failed to contact upgrade server: %r' % e)
670 except ValueError as e:
671 except ValueError as e:
671 log.exception("Bad data sent from update server")
672 log.exception("Bad data sent from update server")
672 return _err('Bad data sent from update server')
673 return _err('Bad data sent from update server')
673
674
674 latest = data['versions'][0]
675 latest = data['versions'][0]
675
676
676 c.update_url = update_url
677 c.update_url = update_url
677 c.latest_data = latest
678 c.latest_data = latest
678 c.latest_ver = latest['version']
679 c.latest_ver = latest['version']
679 c.cur_ver = rhodecode.__version__
680 c.cur_ver = rhodecode.__version__
680 c.should_upgrade = False
681 c.should_upgrade = False
681
682
682 if (packaging.version.Version(c.latest_ver) >
683 if (packaging.version.Version(c.latest_ver) >
683 packaging.version.Version(c.cur_ver)):
684 packaging.version.Version(c.cur_ver)):
684 c.should_upgrade = True
685 c.should_upgrade = True
685 c.important_notices = latest['general']
686 c.important_notices = latest['general']
686
687
687 return render('admin/settings/settings_system_update.html')
688 return render('admin/settings/settings_system_update.html')
688
689
689 @HasPermissionAllDecorator('hg.admin')
690 @HasPermissionAllDecorator('hg.admin')
690 def settings_supervisor(self):
691 def settings_supervisor(self):
691 c.rhodecode_ini = rhodecode.CONFIG
692 c.rhodecode_ini = rhodecode.CONFIG
692 c.active = 'supervisor'
693 c.active = 'supervisor'
693
694
694 c.supervisor_procs = OrderedDict([
695 c.supervisor_procs = OrderedDict([
695 (SUPERVISOR_MASTER, {}),
696 (SUPERVISOR_MASTER, {}),
696 ])
697 ])
697
698
698 c.log_size = 10240
699 c.log_size = 10240
699 supervisor = SupervisorModel()
700 supervisor = SupervisorModel()
700
701
701 _connection = supervisor.get_connection(
702 _connection = supervisor.get_connection(
702 c.rhodecode_ini.get('supervisor.uri'))
703 c.rhodecode_ini.get('supervisor.uri'))
703 c.connection_error = None
704 c.connection_error = None
704 try:
705 try:
705 _connection.supervisor.getAllProcessInfo()
706 _connection.supervisor.getAllProcessInfo()
706 except Exception as e:
707 except Exception as e:
707 c.connection_error = str(e)
708 c.connection_error = str(e)
708 log.exception("Exception reading supervisor data")
709 log.exception("Exception reading supervisor data")
709 return render('admin/settings/settings.html')
710 return render('admin/settings/settings.html')
710
711
711 groupid = c.rhodecode_ini.get('supervisor.group_id')
712 groupid = c.rhodecode_ini.get('supervisor.group_id')
712
713
713 # feed our group processes to the main
714 # feed our group processes to the main
714 for proc in supervisor.get_group_processes(_connection, groupid):
715 for proc in supervisor.get_group_processes(_connection, groupid):
715 c.supervisor_procs[proc['name']] = {}
716 c.supervisor_procs[proc['name']] = {}
716
717
717 for k in c.supervisor_procs.keys():
718 for k in c.supervisor_procs.keys():
718 try:
719 try:
719 # master process info
720 # master process info
720 if k == SUPERVISOR_MASTER:
721 if k == SUPERVISOR_MASTER:
721 _data = supervisor.get_master_state(_connection)
722 _data = supervisor.get_master_state(_connection)
722 _data['name'] = 'supervisor master'
723 _data['name'] = 'supervisor master'
723 _data['description'] = 'pid %s, id: %s, ver: %s' % (
724 _data['description'] = 'pid %s, id: %s, ver: %s' % (
724 _data['pid'], _data['id'], _data['ver'])
725 _data['pid'], _data['id'], _data['ver'])
725 c.supervisor_procs[k] = _data
726 c.supervisor_procs[k] = _data
726 else:
727 else:
727 procid = groupid + ":" + k
728 procid = groupid + ":" + k
728 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
729 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
729 except Exception as e:
730 except Exception as e:
730 log.exception("Exception reading supervisor data")
731 log.exception("Exception reading supervisor data")
731 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
732 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
732
733
733 return render('admin/settings/settings.html')
734 return render('admin/settings/settings.html')
734
735
735 @HasPermissionAllDecorator('hg.admin')
736 @HasPermissionAllDecorator('hg.admin')
736 def settings_supervisor_log(self, procid):
737 def settings_supervisor_log(self, procid):
737 import rhodecode
738 import rhodecode
738 c.rhodecode_ini = rhodecode.CONFIG
739 c.rhodecode_ini = rhodecode.CONFIG
739 c.active = 'supervisor_tail'
740 c.active = 'supervisor_tail'
740
741
741 supervisor = SupervisorModel()
742 supervisor = SupervisorModel()
742 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
743 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
743 groupid = c.rhodecode_ini.get('supervisor.group_id')
744 groupid = c.rhodecode_ini.get('supervisor.group_id')
744 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
745 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
745
746
746 c.log_size = 10240
747 c.log_size = 10240
747 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
748 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
748 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
749 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
749
750
750 return render('admin/settings/settings.html')
751 return render('admin/settings/settings.html')
751
752
752 @HasPermissionAllDecorator('hg.admin')
753 @HasPermissionAllDecorator('hg.admin')
753 @auth.CSRFRequired()
754 @auth.CSRFRequired()
754 def settings_labs_update(self):
755 def settings_labs_update(self):
755 """POST /admin/settings/labs: All items in the collection"""
756 """POST /admin/settings/labs: All items in the collection"""
756 # url('admin_settings/labs', method={'POST'})
757 # url('admin_settings/labs', method={'POST'})
757 c.active = 'labs'
758 c.active = 'labs'
758
759
759 application_form = LabsSettingsForm()()
760 application_form = LabsSettingsForm()()
760 try:
761 try:
761 form_result = application_form.to_python(dict(request.POST))
762 form_result = application_form.to_python(dict(request.POST))
762 except formencode.Invalid as errors:
763 except formencode.Invalid as errors:
763 h.flash(
764 h.flash(
764 _('Some form inputs contain invalid data.'),
765 _('Some form inputs contain invalid data.'),
765 category='error')
766 category='error')
766 return htmlfill.render(
767 return htmlfill.render(
767 render('admin/settings/settings.html'),
768 render('admin/settings/settings.html'),
768 defaults=errors.value,
769 defaults=errors.value,
769 errors=errors.error_dict or {},
770 errors=errors.error_dict or {},
770 prefix_error=False,
771 prefix_error=False,
771 encoding='UTF-8',
772 encoding='UTF-8',
772 force_defaults=False
773 force_defaults=False
773 )
774 )
774
775
775 try:
776 try:
776 session = Session()
777 session = Session()
777 for setting in _LAB_SETTINGS:
778 for setting in _LAB_SETTINGS:
778 setting_name = setting.key[len('rhodecode_'):]
779 setting_name = setting.key[len('rhodecode_'):]
779 sett = SettingsModel().create_or_update_setting(
780 sett = SettingsModel().create_or_update_setting(
780 setting_name, form_result[setting.key], setting.type)
781 setting_name, form_result[setting.key], setting.type)
781 session.add(sett)
782 session.add(sett)
782
783
783 except Exception:
784 except Exception:
784 log.exception('Exception while updating lab settings')
785 log.exception('Exception while updating lab settings')
785 h.flash(_('Error occurred during updating labs settings'),
786 h.flash(_('Error occurred during updating labs settings'),
786 category='error')
787 category='error')
787 else:
788 else:
788 Session().commit()
789 Session().commit()
789 SettingsModel().invalidate_settings_cache()
790 SettingsModel().invalidate_settings_cache()
790 h.flash(_('Updated Labs settings'), category='success')
791 h.flash(_('Updated Labs settings'), category='success')
791 return redirect(url('admin_settings_labs'))
792 return redirect(url('admin_settings_labs'))
792
793
793 return htmlfill.render(
794 return htmlfill.render(
794 render('admin/settings/settings.html'),
795 render('admin/settings/settings.html'),
795 defaults=self._form_defaults(),
796 defaults=self._form_defaults(),
796 encoding='UTF-8',
797 encoding='UTF-8',
797 force_defaults=False)
798 force_defaults=False)
798
799
799 @HasPermissionAllDecorator('hg.admin')
800 @HasPermissionAllDecorator('hg.admin')
800 def settings_labs(self):
801 def settings_labs(self):
801 """GET /admin/settings/labs: All items in the collection"""
802 """GET /admin/settings/labs: All items in the collection"""
802 # url('admin_settings_labs')
803 # url('admin_settings_labs')
803 if not c.labs_active:
804 if not c.labs_active:
804 redirect(url('admin_settings'))
805 redirect(url('admin_settings'))
805
806
806 c.active = 'labs'
807 c.active = 'labs'
807 c.lab_settings = _LAB_SETTINGS
808 c.lab_settings = _LAB_SETTINGS
808
809
809 return htmlfill.render(
810 return htmlfill.render(
810 render('admin/settings/settings.html'),
811 render('admin/settings/settings.html'),
811 defaults=self._form_defaults(),
812 defaults=self._form_defaults(),
812 encoding='UTF-8',
813 encoding='UTF-8',
813 force_defaults=False)
814 force_defaults=False)
814
815
815 def _form_defaults(self):
816 def _form_defaults(self):
816 defaults = SettingsModel().get_all_settings()
817 defaults = SettingsModel().get_all_settings()
817 defaults.update(self._get_hg_ui_settings())
818 defaults.update(self._get_hg_ui_settings())
818 defaults.update({
819 defaults.update({
819 'new_svn_branch': '',
820 'new_svn_branch': '',
820 'new_svn_tag': '',
821 'new_svn_tag': '',
821 })
822 })
822 return defaults
823 return defaults
823
824
824
825
825 # :param key: name of the setting including the 'rhodecode_' prefix
826 # :param key: name of the setting including the 'rhodecode_' prefix
826 # :param type: the RhodeCodeSetting type to use.
827 # :param type: the RhodeCodeSetting type to use.
827 # :param group: the i18ned group in which we should dispaly this setting
828 # :param group: the i18ned group in which we should dispaly this setting
828 # :param label: the i18ned label we should display for this setting
829 # :param label: the i18ned label we should display for this setting
829 # :param help: the i18ned help we should dispaly for this setting
830 # :param help: the i18ned help we should dispaly for this setting
830 LabSetting = collections.namedtuple(
831 LabSetting = collections.namedtuple(
831 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
832 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
832
833
833
834
834 # This list has to be kept in sync with the form
835 # This list has to be kept in sync with the form
835 # rhodecode.model.forms.LabsSettingsForm.
836 # rhodecode.model.forms.LabsSettingsForm.
836 _LAB_SETTINGS = [
837 _LAB_SETTINGS = [
837
838
838 ]
839 ]
@@ -1,607 +1,611 b''
1 import os
1 import os
2 import sys
2 import sys
3 import time
3 import time
4 import platform
4 import platform
5 import pkg_resources
5 import pkg_resources
6 import logging
6 import logging
7 import string
7 import string
8
8
9
9
10 log = logging.getLogger(__name__)
10 log = logging.getLogger(__name__)
11
11
12
12
13 psutil = None
13 psutil = None
14
14
15 try:
15 try:
16 # cygwin cannot have yet psutil support.
16 # cygwin cannot have yet psutil support.
17 import psutil as psutil
17 import psutil as psutil
18 except ImportError:
18 except ImportError:
19 pass
19 pass
20
20
21
21
22 _NA = 'NOT AVAILABLE'
22 _NA = 'NOT AVAILABLE'
23
23
24 STATE_OK = 'ok'
24 STATE_OK = 'ok'
25 STATE_ERR = 'error'
25 STATE_ERR = 'error'
26 STATE_WARN = 'warning'
26 STATE_WARN = 'warning'
27
27
28 STATE_OK_DEFAULT = {'message': '', 'type': STATE_OK}
28 STATE_OK_DEFAULT = {'message': '', 'type': STATE_OK}
29
29
30
30
31 # HELPERS
31 # HELPERS
32 def percentage(part, whole):
32 def percentage(part, whole):
33 whole = float(whole)
33 whole = float(whole)
34 if whole > 0:
34 if whole > 0:
35 return 100 * float(part) / whole
35 return 100 * float(part) / whole
36 return 0
36 return 0
37
37
38
38
39 def get_storage_size(storage_path):
39 def get_storage_size(storage_path):
40 sizes = []
40 sizes = []
41 for file_ in os.listdir(storage_path):
41 for file_ in os.listdir(storage_path):
42 storage_file = os.path.join(storage_path, file_)
42 storage_file = os.path.join(storage_path, file_)
43 if os.path.isfile(storage_file):
43 if os.path.isfile(storage_file):
44 try:
44 try:
45 sizes.append(os.path.getsize(storage_file))
45 sizes.append(os.path.getsize(storage_file))
46 except OSError:
46 except OSError:
47 log.exception('Failed to get size of storage file %s',
47 log.exception('Failed to get size of storage file %s',
48 storage_file)
48 storage_file)
49 pass
49 pass
50
50
51 return sum(sizes)
51 return sum(sizes)
52
52
53
53
54 class SysInfoRes(object):
54 class SysInfoRes(object):
55 def __init__(self, value, state=STATE_OK_DEFAULT, human_value=None):
55 def __init__(self, value, state=STATE_OK_DEFAULT, human_value=None):
56 self.value = value
56 self.value = value
57 self.state = state
57 self.state = state
58 self.human_value = human_value or value
58 self.human_value = human_value or value
59
59
60 def __json__(self):
60 def __json__(self):
61 return {
61 return {
62 'value': self.value,
62 'value': self.value,
63 'state': self.state,
63 'state': self.state,
64 'human_value': self.human_value,
64 'human_value': self.human_value,
65 }
65 }
66
66
67 def __str__(self):
67 def __str__(self):
68 return '<SysInfoRes({})>'.format(self.__json__())
68 return '<SysInfoRes({})>'.format(self.__json__())
69
69
70
70
71 class SysInfo(object):
71 class SysInfo(object):
72
72
73 def __init__(self, func_name, **kwargs):
73 def __init__(self, func_name, **kwargs):
74 self.func_name = func_name
74 self.func_name = func_name
75 self.value = _NA
75 self.value = _NA
76 self.state = None
76 self.state = None
77 self.kwargs = kwargs or {}
77 self.kwargs = kwargs or {}
78
78
79 def __call__(self):
79 def __call__(self):
80 computed = self.compute(**self.kwargs)
80 computed = self.compute(**self.kwargs)
81 if not isinstance(computed, SysInfoRes):
81 if not isinstance(computed, SysInfoRes):
82 raise ValueError(
82 raise ValueError(
83 'computed value for {} is not instance of '
83 'computed value for {} is not instance of '
84 '{}, got {} instead'.format(
84 '{}, got {} instead'.format(
85 self.func_name, SysInfoRes, type(computed)))
85 self.func_name, SysInfoRes, type(computed)))
86 return computed.__json__()
86 return computed.__json__()
87
87
88 def __str__(self):
88 def __str__(self):
89 return '<SysInfo({})>'.format(self.func_name)
89 return '<SysInfo({})>'.format(self.func_name)
90
90
91 def compute(self, **kwargs):
91 def compute(self, **kwargs):
92 return self.func_name(**kwargs)
92 return self.func_name(**kwargs)
93
93
94
94
95 # SysInfo functions
95 # SysInfo functions
96 def python_info():
96 def python_info():
97 value = dict(version=' '.join(platform._sys_version()),
97 value = dict(version=' '.join(platform._sys_version()),
98 executable=sys.executable)
98 executable=sys.executable)
99 return SysInfoRes(value=value)
99 return SysInfoRes(value=value)
100
100
101
101
102 def py_modules():
102 def py_modules():
103 mods = dict([(p.project_name, p.version)
103 mods = dict([(p.project_name, p.version)
104 for p in pkg_resources.working_set])
104 for p in pkg_resources.working_set])
105 value = sorted(mods.items(), key=lambda k: k[0].lower())
105 value = sorted(mods.items(), key=lambda k: k[0].lower())
106 return SysInfoRes(value=value)
106 return SysInfoRes(value=value)
107
107
108
108
109 def platform_type():
109 def platform_type():
110 from rhodecode.lib.utils import safe_unicode
110 from rhodecode.lib.utils import safe_unicode, generate_platform_uuid
111 value = safe_unicode(platform.platform())
111
112 value = dict(
113 name=safe_unicode(platform.platform()),
114 uuid=generate_platform_uuid()
115 )
112 return SysInfoRes(value=value)
116 return SysInfoRes(value=value)
113
117
114
118
115 def uptime():
119 def uptime():
116 from rhodecode.lib.helpers import age, time_to_datetime
120 from rhodecode.lib.helpers import age, time_to_datetime
117
121
118 value = dict(boot_time=0, uptime=0, text='')
122 value = dict(boot_time=0, uptime=0, text='')
119 state = STATE_OK_DEFAULT
123 state = STATE_OK_DEFAULT
120 if not psutil:
124 if not psutil:
121 return SysInfoRes(value=value, state=state)
125 return SysInfoRes(value=value, state=state)
122
126
123 boot_time = psutil.boot_time()
127 boot_time = psutil.boot_time()
124 value['boot_time'] = boot_time
128 value['boot_time'] = boot_time
125 value['uptime'] = time.time() - boot_time
129 value['uptime'] = time.time() - boot_time
126
130
127 human_value = value.copy()
131 human_value = value.copy()
128 human_value['boot_time'] = time_to_datetime(boot_time)
132 human_value['boot_time'] = time_to_datetime(boot_time)
129 human_value['uptime'] = age(time_to_datetime(boot_time), show_suffix=False)
133 human_value['uptime'] = age(time_to_datetime(boot_time), show_suffix=False)
130 human_value['text'] = 'Server started {}'.format(
134 human_value['text'] = 'Server started {}'.format(
131 age(time_to_datetime(boot_time)))
135 age(time_to_datetime(boot_time)))
132
136
133 return SysInfoRes(value=value, human_value=human_value)
137 return SysInfoRes(value=value, human_value=human_value)
134
138
135
139
136 def memory():
140 def memory():
137 from rhodecode.lib.helpers import format_byte_size_binary
141 from rhodecode.lib.helpers import format_byte_size_binary
138 value = dict(available=0, used=0, cached=0, percent=0, percent_used=0,
142 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,
143 free=0, inactive=0, active=0, shared=0, total=0, buffers=0,
140 text='')
144 text='')
141
145
142 state = STATE_OK_DEFAULT
146 state = STATE_OK_DEFAULT
143 if not psutil:
147 if not psutil:
144 return SysInfoRes(value=value, state=state)
148 return SysInfoRes(value=value, state=state)
145
149
146 value.update(dict(psutil.virtual_memory()._asdict()))
150 value.update(dict(psutil.virtual_memory()._asdict()))
147 value['percent_used'] = psutil._common.usage_percent(
151 value['percent_used'] = psutil._common.usage_percent(
148 (value['total'] - value['free']), value['total'], 1)
152 (value['total'] - value['free']), value['total'], 1)
149
153
150 human_value = value.copy()
154 human_value = value.copy()
151 human_value['text'] = '%s/%s, %s%% used' % (
155 human_value['text'] = '%s/%s, %s%% used' % (
152 format_byte_size_binary(value['used']),
156 format_byte_size_binary(value['used']),
153 format_byte_size_binary(value['total']),
157 format_byte_size_binary(value['total']),
154 value['percent_used'],)
158 value['percent_used'],)
155
159
156 keys = value.keys()[::]
160 keys = value.keys()[::]
157 keys.pop(keys.index('percent'))
161 keys.pop(keys.index('percent'))
158 keys.pop(keys.index('percent_used'))
162 keys.pop(keys.index('percent_used'))
159 keys.pop(keys.index('text'))
163 keys.pop(keys.index('text'))
160 for k in keys:
164 for k in keys:
161 human_value[k] = format_byte_size_binary(value[k])
165 human_value[k] = format_byte_size_binary(value[k])
162
166
163 if state['type'] == STATE_OK and value['percent_used'] > 90:
167 if state['type'] == STATE_OK and value['percent_used'] > 90:
164 msg = 'Critical: your available RAM memory is very low.'
168 msg = 'Critical: your available RAM memory is very low.'
165 state = {'message': msg, 'type': STATE_ERR}
169 state = {'message': msg, 'type': STATE_ERR}
166
170
167 elif state['type'] == STATE_OK and value['percent_used'] > 70:
171 elif state['type'] == STATE_OK and value['percent_used'] > 70:
168 msg = 'Warning: your available RAM memory is running low.'
172 msg = 'Warning: your available RAM memory is running low.'
169 state = {'message': msg, 'type': STATE_WARN}
173 state = {'message': msg, 'type': STATE_WARN}
170
174
171 return SysInfoRes(value=value, state=state, human_value=human_value)
175 return SysInfoRes(value=value, state=state, human_value=human_value)
172
176
173
177
174 def machine_load():
178 def machine_load():
175 value = {'1_min': _NA, '5_min': _NA, '15_min': _NA, 'text': ''}
179 value = {'1_min': _NA, '5_min': _NA, '15_min': _NA, 'text': ''}
176 state = STATE_OK_DEFAULT
180 state = STATE_OK_DEFAULT
177 if not psutil:
181 if not psutil:
178 return SysInfoRes(value=value, state=state)
182 return SysInfoRes(value=value, state=state)
179
183
180 # load averages
184 # load averages
181 if hasattr(psutil.os, 'getloadavg'):
185 if hasattr(psutil.os, 'getloadavg'):
182 value.update(dict(
186 value.update(dict(
183 zip(['1_min', '5_min', '15_min'], psutil.os.getloadavg())))
187 zip(['1_min', '5_min', '15_min'], psutil.os.getloadavg())))
184
188
185 human_value = value.copy()
189 human_value = value.copy()
186 human_value['text'] = '1min: {}, 5min: {}, 15min: {}'.format(
190 human_value['text'] = '1min: {}, 5min: {}, 15min: {}'.format(
187 value['1_min'], value['5_min'], value['15_min'])
191 value['1_min'], value['5_min'], value['15_min'])
188
192
189 if state['type'] == STATE_OK and value['15_min'] > 5:
193 if state['type'] == STATE_OK and value['15_min'] > 5:
190 msg = 'Warning: your machine load is very high.'
194 msg = 'Warning: your machine load is very high.'
191 state = {'message': msg, 'type': STATE_WARN}
195 state = {'message': msg, 'type': STATE_WARN}
192
196
193 return SysInfoRes(value=value, state=state, human_value=human_value)
197 return SysInfoRes(value=value, state=state, human_value=human_value)
194
198
195
199
196 def cpu():
200 def cpu():
197 value = 0
201 value = 0
198 state = STATE_OK_DEFAULT
202 state = STATE_OK_DEFAULT
199
203
200 if not psutil:
204 if not psutil:
201 return SysInfoRes(value=value, state=state)
205 return SysInfoRes(value=value, state=state)
202
206
203 value = psutil.cpu_percent(0.5)
207 value = psutil.cpu_percent(0.5)
204 human_value = '{} %'.format(value)
208 human_value = '{} %'.format(value)
205 return SysInfoRes(value=value, state=state, human_value=human_value)
209 return SysInfoRes(value=value, state=state, human_value=human_value)
206
210
207
211
208 def storage():
212 def storage():
209 from rhodecode.lib.helpers import format_byte_size_binary
213 from rhodecode.lib.helpers import format_byte_size_binary
210 from rhodecode.model.settings import VcsSettingsModel
214 from rhodecode.model.settings import VcsSettingsModel
211 path = VcsSettingsModel().get_repos_location()
215 path = VcsSettingsModel().get_repos_location()
212
216
213 value = dict(percent=0, used=0, total=0, path=path, text='')
217 value = dict(percent=0, used=0, total=0, path=path, text='')
214 state = STATE_OK_DEFAULT
218 state = STATE_OK_DEFAULT
215 if not psutil:
219 if not psutil:
216 return SysInfoRes(value=value, state=state)
220 return SysInfoRes(value=value, state=state)
217
221
218 try:
222 try:
219 value.update(dict(psutil.disk_usage(path)._asdict()))
223 value.update(dict(psutil.disk_usage(path)._asdict()))
220 except Exception as e:
224 except Exception as e:
221 log.exception('Failed to fetch disk info')
225 log.exception('Failed to fetch disk info')
222 state = {'message': str(e), 'type': STATE_ERR}
226 state = {'message': str(e), 'type': STATE_ERR}
223
227
224 human_value = value.copy()
228 human_value = value.copy()
225 human_value['used'] = format_byte_size_binary(value['used'])
229 human_value['used'] = format_byte_size_binary(value['used'])
226 human_value['total'] = format_byte_size_binary(value['total'])
230 human_value['total'] = format_byte_size_binary(value['total'])
227 human_value['text'] = "{}/{}, {}% used".format(
231 human_value['text'] = "{}/{}, {}% used".format(
228 format_byte_size_binary(value['used']),
232 format_byte_size_binary(value['used']),
229 format_byte_size_binary(value['total']),
233 format_byte_size_binary(value['total']),
230 value['percent'])
234 value['percent'])
231
235
232 if state['type'] == STATE_OK and value['percent'] > 90:
236 if state['type'] == STATE_OK and value['percent'] > 90:
233 msg = 'Critical: your disk space is very low.'
237 msg = 'Critical: your disk space is very low.'
234 state = {'message': msg, 'type': STATE_ERR}
238 state = {'message': msg, 'type': STATE_ERR}
235
239
236 elif state['type'] == STATE_OK and value['percent'] > 70:
240 elif state['type'] == STATE_OK and value['percent'] > 70:
237 msg = 'Warning: your disk space is running low.'
241 msg = 'Warning: your disk space is running low.'
238 state = {'message': msg, 'type': STATE_WARN}
242 state = {'message': msg, 'type': STATE_WARN}
239
243
240 return SysInfoRes(value=value, state=state, human_value=human_value)
244 return SysInfoRes(value=value, state=state, human_value=human_value)
241
245
242
246
243 def storage_inodes():
247 def storage_inodes():
244 from rhodecode.model.settings import VcsSettingsModel
248 from rhodecode.model.settings import VcsSettingsModel
245 path = VcsSettingsModel().get_repos_location()
249 path = VcsSettingsModel().get_repos_location()
246
250
247 value = dict(percent=0, free=0, used=0, total=0, path=path, text='')
251 value = dict(percent=0, free=0, used=0, total=0, path=path, text='')
248 state = STATE_OK_DEFAULT
252 state = STATE_OK_DEFAULT
249 if not psutil:
253 if not psutil:
250 return SysInfoRes(value=value, state=state)
254 return SysInfoRes(value=value, state=state)
251
255
252 try:
256 try:
253 i_stat = os.statvfs(path)
257 i_stat = os.statvfs(path)
254
258
255 value['used'] = i_stat.f_ffree
259 value['used'] = i_stat.f_ffree
256 value['free'] = i_stat.f_favail
260 value['free'] = i_stat.f_favail
257 value['total'] = i_stat.f_files
261 value['total'] = i_stat.f_files
258 value['percent'] = percentage(
262 value['percent'] = percentage(
259 value['used'], value['total'])
263 value['used'], value['total'])
260 except Exception as e:
264 except Exception as e:
261 log.exception('Failed to fetch disk inodes info')
265 log.exception('Failed to fetch disk inodes info')
262 state = {'message': str(e), 'type': STATE_ERR}
266 state = {'message': str(e), 'type': STATE_ERR}
263
267
264 human_value = value.copy()
268 human_value = value.copy()
265 human_value['text'] = "{}/{}, {}% used".format(
269 human_value['text'] = "{}/{}, {}% used".format(
266 value['used'], value['total'], value['percent'])
270 value['used'], value['total'], value['percent'])
267
271
268 if state['type'] == STATE_OK and value['percent'] > 90:
272 if state['type'] == STATE_OK and value['percent'] > 90:
269 msg = 'Critical: your disk free inodes are very low.'
273 msg = 'Critical: your disk free inodes are very low.'
270 state = {'message': msg, 'type': STATE_ERR}
274 state = {'message': msg, 'type': STATE_ERR}
271
275
272 elif state['type'] == STATE_OK and value['percent'] > 70:
276 elif state['type'] == STATE_OK and value['percent'] > 70:
273 msg = 'Warning: your disk free inodes are running low.'
277 msg = 'Warning: your disk free inodes are running low.'
274 state = {'message': msg, 'type': STATE_WARN}
278 state = {'message': msg, 'type': STATE_WARN}
275
279
276 return SysInfoRes(value=value, state=state)
280 return SysInfoRes(value=value, state=state)
277
281
278
282
279 def storage_archives():
283 def storage_archives():
280 import rhodecode
284 import rhodecode
281 from rhodecode.lib.utils import safe_str
285 from rhodecode.lib.utils import safe_str
282 from rhodecode.lib.helpers import format_byte_size_binary
286 from rhodecode.lib.helpers import format_byte_size_binary
283
287
284 msg = 'Enable this by setting ' \
288 msg = 'Enable this by setting ' \
285 'archive_cache_dir=/path/to/cache option in the .ini file'
289 'archive_cache_dir=/path/to/cache option in the .ini file'
286 path = safe_str(rhodecode.CONFIG.get('archive_cache_dir', msg))
290 path = safe_str(rhodecode.CONFIG.get('archive_cache_dir', msg))
287
291
288 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
292 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
289 state = STATE_OK_DEFAULT
293 state = STATE_OK_DEFAULT
290 try:
294 try:
291 items_count = 0
295 items_count = 0
292 used = 0
296 used = 0
293 for root, dirs, files in os.walk(path):
297 for root, dirs, files in os.walk(path):
294 if root == path:
298 if root == path:
295 items_count = len(files)
299 items_count = len(files)
296
300
297 for f in files:
301 for f in files:
298 try:
302 try:
299 used += os.path.getsize(os.path.join(root, f))
303 used += os.path.getsize(os.path.join(root, f))
300 except OSError:
304 except OSError:
301 pass
305 pass
302 value.update({
306 value.update({
303 'percent': 100,
307 'percent': 100,
304 'used': used,
308 'used': used,
305 'total': used,
309 'total': used,
306 'items': items_count
310 'items': items_count
307 })
311 })
308
312
309 except Exception as e:
313 except Exception as e:
310 log.exception('failed to fetch archive cache storage')
314 log.exception('failed to fetch archive cache storage')
311 state = {'message': str(e), 'type': STATE_ERR}
315 state = {'message': str(e), 'type': STATE_ERR}
312
316
313 human_value = value.copy()
317 human_value = value.copy()
314 human_value['used'] = format_byte_size_binary(value['used'])
318 human_value['used'] = format_byte_size_binary(value['used'])
315 human_value['total'] = format_byte_size_binary(value['total'])
319 human_value['total'] = format_byte_size_binary(value['total'])
316 human_value['text'] = "{} ({} items)".format(
320 human_value['text'] = "{} ({} items)".format(
317 human_value['used'], value['items'])
321 human_value['used'], value['items'])
318
322
319 return SysInfoRes(value=value, state=state, human_value=human_value)
323 return SysInfoRes(value=value, state=state, human_value=human_value)
320
324
321
325
322 def storage_gist():
326 def storage_gist():
323 from rhodecode.model.gist import GIST_STORE_LOC
327 from rhodecode.model.gist import GIST_STORE_LOC
324 from rhodecode.model.settings import VcsSettingsModel
328 from rhodecode.model.settings import VcsSettingsModel
325 from rhodecode.lib.utils import safe_str
329 from rhodecode.lib.utils import safe_str
326 from rhodecode.lib.helpers import format_byte_size_binary
330 from rhodecode.lib.helpers import format_byte_size_binary
327 path = safe_str(os.path.join(
331 path = safe_str(os.path.join(
328 VcsSettingsModel().get_repos_location(), GIST_STORE_LOC))
332 VcsSettingsModel().get_repos_location(), GIST_STORE_LOC))
329
333
330 # gist storage
334 # gist storage
331 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
335 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
332 state = STATE_OK_DEFAULT
336 state = STATE_OK_DEFAULT
333
337
334 try:
338 try:
335 items_count = 0
339 items_count = 0
336 used = 0
340 used = 0
337 for root, dirs, files in os.walk(path):
341 for root, dirs, files in os.walk(path):
338 if root == path:
342 if root == path:
339 items_count = len(dirs)
343 items_count = len(dirs)
340
344
341 for f in files:
345 for f in files:
342 try:
346 try:
343 used += os.path.getsize(os.path.join(root, f))
347 used += os.path.getsize(os.path.join(root, f))
344 except OSError:
348 except OSError:
345 pass
349 pass
346 value.update({
350 value.update({
347 'percent': 100,
351 'percent': 100,
348 'used': used,
352 'used': used,
349 'total': used,
353 'total': used,
350 'items': items_count
354 'items': items_count
351 })
355 })
352 except Exception as e:
356 except Exception as e:
353 log.exception('failed to fetch gist storage items')
357 log.exception('failed to fetch gist storage items')
354 state = {'message': str(e), 'type': STATE_ERR}
358 state = {'message': str(e), 'type': STATE_ERR}
355
359
356 human_value = value.copy()
360 human_value = value.copy()
357 human_value['used'] = format_byte_size_binary(value['used'])
361 human_value['used'] = format_byte_size_binary(value['used'])
358 human_value['total'] = format_byte_size_binary(value['total'])
362 human_value['total'] = format_byte_size_binary(value['total'])
359 human_value['text'] = "{} ({} items)".format(
363 human_value['text'] = "{} ({} items)".format(
360 human_value['used'], value['items'])
364 human_value['used'], value['items'])
361
365
362 return SysInfoRes(value=value, state=state, human_value=human_value)
366 return SysInfoRes(value=value, state=state, human_value=human_value)
363
367
364
368
365 def search_info():
369 def search_info():
366 import rhodecode
370 import rhodecode
367 from rhodecode.lib.index import searcher_from_config
371 from rhodecode.lib.index import searcher_from_config
368
372
369 backend = rhodecode.CONFIG.get('search.module', '')
373 backend = rhodecode.CONFIG.get('search.module', '')
370 location = rhodecode.CONFIG.get('search.location', '')
374 location = rhodecode.CONFIG.get('search.location', '')
371
375
372 try:
376 try:
373 searcher = searcher_from_config(rhodecode.CONFIG)
377 searcher = searcher_from_config(rhodecode.CONFIG)
374 searcher = searcher.__class__.__name__
378 searcher = searcher.__class__.__name__
375 except Exception:
379 except Exception:
376 searcher = None
380 searcher = None
377
381
378 value = dict(
382 value = dict(
379 backend=backend, searcher=searcher, location=location, text='')
383 backend=backend, searcher=searcher, location=location, text='')
380 state = STATE_OK_DEFAULT
384 state = STATE_OK_DEFAULT
381
385
382 human_value = value.copy()
386 human_value = value.copy()
383 human_value['text'] = "backend:`{}`".format(human_value['backend'])
387 human_value['text'] = "backend:`{}`".format(human_value['backend'])
384
388
385 return SysInfoRes(value=value, state=state, human_value=human_value)
389 return SysInfoRes(value=value, state=state, human_value=human_value)
386
390
387
391
388 def git_info():
392 def git_info():
389 from rhodecode.lib.vcs.backends import git
393 from rhodecode.lib.vcs.backends import git
390 state = STATE_OK_DEFAULT
394 state = STATE_OK_DEFAULT
391 value = human_value = ''
395 value = human_value = ''
392 try:
396 try:
393 value = git.discover_git_version(raise_on_exc=True)
397 value = git.discover_git_version(raise_on_exc=True)
394 human_value = 'version reported from VCSServer: {}'.format(value)
398 human_value = 'version reported from VCSServer: {}'.format(value)
395 except Exception as e:
399 except Exception as e:
396 state = {'message': str(e), 'type': STATE_ERR}
400 state = {'message': str(e), 'type': STATE_ERR}
397
401
398 return SysInfoRes(value=value, state=state, human_value=human_value)
402 return SysInfoRes(value=value, state=state, human_value=human_value)
399
403
400
404
401 def hg_info():
405 def hg_info():
402 from rhodecode.lib.vcs.backends import hg
406 from rhodecode.lib.vcs.backends import hg
403 state = STATE_OK_DEFAULT
407 state = STATE_OK_DEFAULT
404 value = human_value = ''
408 value = human_value = ''
405 try:
409 try:
406 value = hg.discover_hg_version(raise_on_exc=True)
410 value = hg.discover_hg_version(raise_on_exc=True)
407 human_value = 'version reported from VCSServer: {}'.format(value)
411 human_value = 'version reported from VCSServer: {}'.format(value)
408 except Exception as e:
412 except Exception as e:
409 state = {'message': str(e), 'type': STATE_ERR}
413 state = {'message': str(e), 'type': STATE_ERR}
410 return SysInfoRes(value=value, state=state, human_value=human_value)
414 return SysInfoRes(value=value, state=state, human_value=human_value)
411
415
412
416
413 def svn_info():
417 def svn_info():
414 from rhodecode.lib.vcs.backends import svn
418 from rhodecode.lib.vcs.backends import svn
415 state = STATE_OK_DEFAULT
419 state = STATE_OK_DEFAULT
416 value = human_value = ''
420 value = human_value = ''
417 try:
421 try:
418 value = svn.discover_svn_version(raise_on_exc=True)
422 value = svn.discover_svn_version(raise_on_exc=True)
419 human_value = 'version reported from VCSServer: {}'.format(value)
423 human_value = 'version reported from VCSServer: {}'.format(value)
420 except Exception as e:
424 except Exception as e:
421 state = {'message': str(e), 'type': STATE_ERR}
425 state = {'message': str(e), 'type': STATE_ERR}
422 return SysInfoRes(value=value, state=state, human_value=human_value)
426 return SysInfoRes(value=value, state=state, human_value=human_value)
423
427
424
428
425 def vcs_backends():
429 def vcs_backends():
426 import rhodecode
430 import rhodecode
427 value = map(
431 value = map(
428 string.strip, rhodecode.CONFIG.get('vcs.backends', '').split(','))
432 string.strip, rhodecode.CONFIG.get('vcs.backends', '').split(','))
429 human_value = 'Enabled backends in order: {}'.format(','.join(value))
433 human_value = 'Enabled backends in order: {}'.format(','.join(value))
430 return SysInfoRes(value=value, human_value=human_value)
434 return SysInfoRes(value=value, human_value=human_value)
431
435
432
436
433 def vcs_server():
437 def vcs_server():
434 import rhodecode
438 import rhodecode
435 from rhodecode.lib.vcs.backends import get_vcsserver_version
439 from rhodecode.lib.vcs.backends import get_vcsserver_version
436
440
437 server_url = rhodecode.CONFIG.get('vcs.server')
441 server_url = rhodecode.CONFIG.get('vcs.server')
438 enabled = rhodecode.CONFIG.get('vcs.server.enable')
442 enabled = rhodecode.CONFIG.get('vcs.server.enable')
439 protocol = rhodecode.CONFIG.get('vcs.server.protocol')
443 protocol = rhodecode.CONFIG.get('vcs.server.protocol')
440 state = STATE_OK_DEFAULT
444 state = STATE_OK_DEFAULT
441 version = None
445 version = None
442
446
443 try:
447 try:
444 version = get_vcsserver_version()
448 version = get_vcsserver_version()
445 connection = 'connected'
449 connection = 'connected'
446 except Exception as e:
450 except Exception as e:
447 connection = 'failed'
451 connection = 'failed'
448 state = {'message': str(e), 'type': STATE_ERR}
452 state = {'message': str(e), 'type': STATE_ERR}
449
453
450 value = dict(
454 value = dict(
451 url=server_url,
455 url=server_url,
452 enabled=enabled,
456 enabled=enabled,
453 protocol=protocol,
457 protocol=protocol,
454 connection=connection,
458 connection=connection,
455 version=version,
459 version=version,
456 text='',
460 text='',
457 )
461 )
458
462
459 human_value = value.copy()
463 human_value = value.copy()
460 human_value['text'] = \
464 human_value['text'] = \
461 '{url}@ver:{ver} via {mode} mode, connection:{conn}'.format(
465 '{url}@ver:{ver} via {mode} mode, connection:{conn}'.format(
462 url=server_url, ver=version, mode=protocol, conn=connection)
466 url=server_url, ver=version, mode=protocol, conn=connection)
463
467
464 return SysInfoRes(value=value, state=state, human_value=human_value)
468 return SysInfoRes(value=value, state=state, human_value=human_value)
465
469
466
470
467 def rhodecode_app_info():
471 def rhodecode_app_info():
468 import rhodecode
472 import rhodecode
469 edition = rhodecode.CONFIG.get('rhodecode.edition')
473 edition = rhodecode.CONFIG.get('rhodecode.edition')
470
474
471 value = dict(
475 value = dict(
472 rhodecode_version=rhodecode.__version__,
476 rhodecode_version=rhodecode.__version__,
473 rhodecode_lib_path=os.path.abspath(rhodecode.__file__),
477 rhodecode_lib_path=os.path.abspath(rhodecode.__file__),
474 text=''
478 text=''
475 )
479 )
476 human_value = value.copy()
480 human_value = value.copy()
477 human_value['text'] = 'RhodeCode {edition}, version {ver}'.format(
481 human_value['text'] = 'RhodeCode {edition}, version {ver}'.format(
478 edition=edition, ver=value['rhodecode_version']
482 edition=edition, ver=value['rhodecode_version']
479 )
483 )
480 return SysInfoRes(value=value, human_value=human_value)
484 return SysInfoRes(value=value, human_value=human_value)
481
485
482
486
483 def rhodecode_config():
487 def rhodecode_config():
484 import rhodecode
488 import rhodecode
485 path = rhodecode.CONFIG.get('__file__')
489 path = rhodecode.CONFIG.get('__file__')
486 rhodecode_ini_safe = rhodecode.CONFIG.copy()
490 rhodecode_ini_safe = rhodecode.CONFIG.copy()
487
491
488 blacklist = [
492 blacklist = [
489 'rhodecode_license_key',
493 'rhodecode_license_key',
490 'routes.map',
494 'routes.map',
491 'pylons.h',
495 'pylons.h',
492 'pylons.app_globals',
496 'pylons.app_globals',
493 'pylons.environ_config',
497 'pylons.environ_config',
494 'sqlalchemy.db1.url',
498 'sqlalchemy.db1.url',
495 'channelstream.secret',
499 'channelstream.secret',
496 'beaker.session.secret',
500 'beaker.session.secret',
497 'rhodecode.encrypted_values.secret',
501 'rhodecode.encrypted_values.secret',
498 'rhodecode_auth_github_consumer_key',
502 'rhodecode_auth_github_consumer_key',
499 'rhodecode_auth_github_consumer_secret',
503 'rhodecode_auth_github_consumer_secret',
500 'rhodecode_auth_google_consumer_key',
504 'rhodecode_auth_google_consumer_key',
501 'rhodecode_auth_google_consumer_secret',
505 'rhodecode_auth_google_consumer_secret',
502 'rhodecode_auth_bitbucket_consumer_secret',
506 'rhodecode_auth_bitbucket_consumer_secret',
503 'rhodecode_auth_bitbucket_consumer_key',
507 'rhodecode_auth_bitbucket_consumer_key',
504 'rhodecode_auth_twitter_consumer_secret',
508 'rhodecode_auth_twitter_consumer_secret',
505 'rhodecode_auth_twitter_consumer_key',
509 'rhodecode_auth_twitter_consumer_key',
506
510
507 'rhodecode_auth_twitter_secret',
511 'rhodecode_auth_twitter_secret',
508 'rhodecode_auth_github_secret',
512 'rhodecode_auth_github_secret',
509 'rhodecode_auth_google_secret',
513 'rhodecode_auth_google_secret',
510 'rhodecode_auth_bitbucket_secret',
514 'rhodecode_auth_bitbucket_secret',
511
515
512 'appenlight.api_key',
516 'appenlight.api_key',
513 ('app_conf', 'sqlalchemy.db1.url')
517 ('app_conf', 'sqlalchemy.db1.url')
514 ]
518 ]
515 for k in blacklist:
519 for k in blacklist:
516 if isinstance(k, tuple):
520 if isinstance(k, tuple):
517 section, key = k
521 section, key = k
518 if section in rhodecode_ini_safe:
522 if section in rhodecode_ini_safe:
519 rhodecode_ini_safe[section] = '**OBFUSCATED**'
523 rhodecode_ini_safe[section] = '**OBFUSCATED**'
520 else:
524 else:
521 rhodecode_ini_safe.pop(k, None)
525 rhodecode_ini_safe.pop(k, None)
522
526
523 # TODO: maybe put some CONFIG checks here ?
527 # TODO: maybe put some CONFIG checks here ?
524 return SysInfoRes(value={'config': rhodecode_ini_safe, 'path': path})
528 return SysInfoRes(value={'config': rhodecode_ini_safe, 'path': path})
525
529
526
530
527 def database_info():
531 def database_info():
528 import rhodecode
532 import rhodecode
529 from sqlalchemy.engine import url as engine_url
533 from sqlalchemy.engine import url as engine_url
530 from rhodecode.model.meta import Base as sql_base, Session
534 from rhodecode.model.meta import Base as sql_base, Session
531 from rhodecode.model.db import DbMigrateVersion
535 from rhodecode.model.db import DbMigrateVersion
532
536
533 state = STATE_OK_DEFAULT
537 state = STATE_OK_DEFAULT
534
538
535 db_migrate = DbMigrateVersion.query().filter(
539 db_migrate = DbMigrateVersion.query().filter(
536 DbMigrateVersion.repository_id == 'rhodecode_db_migrations').one()
540 DbMigrateVersion.repository_id == 'rhodecode_db_migrations').one()
537
541
538 db_url_obj = engine_url.make_url(rhodecode.CONFIG['sqlalchemy.db1.url'])
542 db_url_obj = engine_url.make_url(rhodecode.CONFIG['sqlalchemy.db1.url'])
539
543
540 try:
544 try:
541 engine = sql_base.metadata.bind
545 engine = sql_base.metadata.bind
542 db_server_info = engine.dialect._get_server_version_info(
546 db_server_info = engine.dialect._get_server_version_info(
543 Session.connection(bind=engine))
547 Session.connection(bind=engine))
544 db_version = '.'.join(map(str, db_server_info))
548 db_version = '.'.join(map(str, db_server_info))
545 except Exception:
549 except Exception:
546 log.exception('failed to fetch db version')
550 log.exception('failed to fetch db version')
547 db_version = 'UNKNOWN'
551 db_version = 'UNKNOWN'
548
552
549 db_info = dict(
553 db_info = dict(
550 migrate_version=db_migrate.version,
554 migrate_version=db_migrate.version,
551 type=db_url_obj.get_backend_name(),
555 type=db_url_obj.get_backend_name(),
552 version=db_version,
556 version=db_version,
553 url=repr(db_url_obj)
557 url=repr(db_url_obj)
554 )
558 )
555
559
556 human_value = db_info.copy()
560 human_value = db_info.copy()
557 human_value['url'] = "{} @ migration version: {}".format(
561 human_value['url'] = "{} @ migration version: {}".format(
558 db_info['url'], db_info['migrate_version'])
562 db_info['url'], db_info['migrate_version'])
559 human_value['version'] = "{} {}".format(db_info['type'], db_info['version'])
563 human_value['version'] = "{} {}".format(db_info['type'], db_info['version'])
560 return SysInfoRes(value=db_info, state=state, human_value=human_value)
564 return SysInfoRes(value=db_info, state=state, human_value=human_value)
561
565
562
566
563 def server_info(environ):
567 def server_info(environ):
564 import rhodecode
568 import rhodecode
565 from rhodecode.lib.base import get_server_ip_addr, get_server_port
569 from rhodecode.lib.base import get_server_ip_addr, get_server_port
566
570
567 value = {
571 value = {
568 'server_ip': '%s:%s' % (
572 'server_ip': '%s:%s' % (
569 get_server_ip_addr(environ, log_errors=False),
573 get_server_ip_addr(environ, log_errors=False),
570 get_server_port(environ)
574 get_server_port(environ)
571 ),
575 ),
572 'server_id': rhodecode.CONFIG.get('instance_id'),
576 'server_id': rhodecode.CONFIG.get('instance_id'),
573 }
577 }
574 return SysInfoRes(value=value)
578 return SysInfoRes(value=value)
575
579
576
580
577 def get_system_info(environ):
581 def get_system_info(environ):
578 environ = environ or {}
582 environ = environ or {}
579 return {
583 return {
580 'rhodecode_app': SysInfo(rhodecode_app_info)(),
584 'rhodecode_app': SysInfo(rhodecode_app_info)(),
581 'rhodecode_config': SysInfo(rhodecode_config)(),
585 'rhodecode_config': SysInfo(rhodecode_config)(),
582 'python': SysInfo(python_info)(),
586 'python': SysInfo(python_info)(),
583 'py_modules': SysInfo(py_modules)(),
587 'py_modules': SysInfo(py_modules)(),
584
588
585 'platform': SysInfo(platform_type)(),
589 'platform': SysInfo(platform_type)(),
586 'server': SysInfo(server_info, environ=environ)(),
590 'server': SysInfo(server_info, environ=environ)(),
587 'database': SysInfo(database_info)(),
591 'database': SysInfo(database_info)(),
588
592
589 'storage': SysInfo(storage)(),
593 'storage': SysInfo(storage)(),
590 'storage_inodes': SysInfo(storage_inodes)(),
594 'storage_inodes': SysInfo(storage_inodes)(),
591 'storage_archive': SysInfo(storage_archives)(),
595 'storage_archive': SysInfo(storage_archives)(),
592 'storage_gist': SysInfo(storage_gist)(),
596 'storage_gist': SysInfo(storage_gist)(),
593
597
594 'search': SysInfo(search_info)(),
598 'search': SysInfo(search_info)(),
595
599
596 'uptime': SysInfo(uptime)(),
600 'uptime': SysInfo(uptime)(),
597 'load': SysInfo(machine_load)(),
601 'load': SysInfo(machine_load)(),
598 'cpu': SysInfo(cpu)(),
602 'cpu': SysInfo(cpu)(),
599 'memory': SysInfo(memory)(),
603 'memory': SysInfo(memory)(),
600
604
601 'vcs_backends': SysInfo(vcs_backends)(),
605 'vcs_backends': SysInfo(vcs_backends)(),
602 'vcs_server': SysInfo(vcs_server)(),
606 'vcs_server': SysInfo(vcs_server)(),
603
607
604 'git': SysInfo(git_info)(),
608 'git': SysInfo(git_info)(),
605 'hg': SysInfo(hg_info)(),
609 'hg': SysInfo(hg_info)(),
606 'svn': SysInfo(svn_info)(),
610 'svn': SysInfo(svn_info)(),
607 }
611 }
@@ -1,1003 +1,1018 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Utilities library for RhodeCode
22 Utilities library for RhodeCode
23 """
23 """
24
24
25 import datetime
25 import datetime
26 import decorator
26 import decorator
27 import json
27 import json
28 import logging
28 import logging
29 import os
29 import os
30 import re
30 import re
31 import shutil
31 import shutil
32 import tempfile
32 import tempfile
33 import traceback
33 import traceback
34 import tarfile
34 import tarfile
35 import warnings
35 import warnings
36 import hashlib
36 from os.path import join as jn
37 from os.path import join as jn
37
38
38 import paste
39 import paste
39 import pkg_resources
40 import pkg_resources
40 from paste.script.command import Command, BadCommand
41 from paste.script.command import Command, BadCommand
41 from webhelpers.text import collapse, remove_formatting, strip_tags
42 from webhelpers.text import collapse, remove_formatting, strip_tags
42 from mako import exceptions
43 from mako import exceptions
43 from pyramid.threadlocal import get_current_registry
44 from pyramid.threadlocal import get_current_registry
44
45
45 from rhodecode.lib.fakemod import create_module
46 from rhodecode.lib.fakemod import create_module
46 from rhodecode.lib.vcs.backends.base import Config
47 from rhodecode.lib.vcs.backends.base import Config
47 from rhodecode.lib.vcs.exceptions import VCSError
48 from rhodecode.lib.vcs.exceptions import VCSError
48 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
49 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
49 from rhodecode.lib.utils2 import (
50 from rhodecode.lib.utils2 import (
50 safe_str, safe_unicode, get_current_rhodecode_user, md5)
51 safe_str, safe_unicode, get_current_rhodecode_user, md5)
51 from rhodecode.model import meta
52 from rhodecode.model import meta
52 from rhodecode.model.db import (
53 from rhodecode.model.db import (
53 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
54 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
54 from rhodecode.model.meta import Session
55 from rhodecode.model.meta import Session
55
56
56
57
57 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
58
59
59 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
60 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
60
61
61 # String which contains characters that are not allowed in slug names for
62 # String which contains characters that are not allowed in slug names for
62 # repositories or repository groups. It is properly escaped to use it in
63 # repositories or repository groups. It is properly escaped to use it in
63 # regular expressions.
64 # regular expressions.
64 SLUG_BAD_CHARS = re.escape('`?=[]\;\'"<>,/~!@#$%^&*()+{}|: ')
65 SLUG_BAD_CHARS = re.escape('`?=[]\;\'"<>,/~!@#$%^&*()+{}|: ')
65
66
66 # Regex that matches forbidden characters in repo/group slugs.
67 # Regex that matches forbidden characters in repo/group slugs.
67 SLUG_BAD_CHAR_RE = re.compile('[{}]'.format(SLUG_BAD_CHARS))
68 SLUG_BAD_CHAR_RE = re.compile('[{}]'.format(SLUG_BAD_CHARS))
68
69
69 # Regex that matches allowed characters in repo/group slugs.
70 # Regex that matches allowed characters in repo/group slugs.
70 SLUG_GOOD_CHAR_RE = re.compile('[^{}]'.format(SLUG_BAD_CHARS))
71 SLUG_GOOD_CHAR_RE = re.compile('[^{}]'.format(SLUG_BAD_CHARS))
71
72
72 # Regex that matches whole repo/group slugs.
73 # Regex that matches whole repo/group slugs.
73 SLUG_RE = re.compile('[^{}]+'.format(SLUG_BAD_CHARS))
74 SLUG_RE = re.compile('[^{}]+'.format(SLUG_BAD_CHARS))
74
75
75 _license_cache = None
76 _license_cache = None
76
77
77
78
78 def repo_name_slug(value):
79 def repo_name_slug(value):
79 """
80 """
80 Return slug of name of repository
81 Return slug of name of repository
81 This function is called on each creation/modification
82 This function is called on each creation/modification
82 of repository to prevent bad names in repo
83 of repository to prevent bad names in repo
83 """
84 """
84 replacement_char = '-'
85 replacement_char = '-'
85
86
86 slug = remove_formatting(value)
87 slug = remove_formatting(value)
87 slug = SLUG_BAD_CHAR_RE.sub(replacement_char, slug)
88 slug = SLUG_BAD_CHAR_RE.sub(replacement_char, slug)
88 slug = collapse(slug, replacement_char)
89 slug = collapse(slug, replacement_char)
89 return slug
90 return slug
90
91
91
92
92 #==============================================================================
93 #==============================================================================
93 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
94 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
94 #==============================================================================
95 #==============================================================================
95 def get_repo_slug(request):
96 def get_repo_slug(request):
96 _repo = request.environ['pylons.routes_dict'].get('repo_name')
97 _repo = request.environ['pylons.routes_dict'].get('repo_name')
97 if _repo:
98 if _repo:
98 _repo = _repo.rstrip('/')
99 _repo = _repo.rstrip('/')
99 return _repo
100 return _repo
100
101
101
102
102 def get_repo_group_slug(request):
103 def get_repo_group_slug(request):
103 _group = request.environ['pylons.routes_dict'].get('group_name')
104 _group = request.environ['pylons.routes_dict'].get('group_name')
104 if _group:
105 if _group:
105 _group = _group.rstrip('/')
106 _group = _group.rstrip('/')
106 return _group
107 return _group
107
108
108
109
109 def get_user_group_slug(request):
110 def get_user_group_slug(request):
110 _group = request.environ['pylons.routes_dict'].get('user_group_id')
111 _group = request.environ['pylons.routes_dict'].get('user_group_id')
111 try:
112 try:
112 _group = UserGroup.get(_group)
113 _group = UserGroup.get(_group)
113 if _group:
114 if _group:
114 _group = _group.users_group_name
115 _group = _group.users_group_name
115 except Exception:
116 except Exception:
116 log.debug(traceback.format_exc())
117 log.debug(traceback.format_exc())
117 #catch all failures here
118 #catch all failures here
118 pass
119 pass
119
120
120 return _group
121 return _group
121
122
122
123
123 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
124 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
124 """
125 """
125 Action logger for various actions made by users
126 Action logger for various actions made by users
126
127
127 :param user: user that made this action, can be a unique username string or
128 :param user: user that made this action, can be a unique username string or
128 object containing user_id attribute
129 object containing user_id attribute
129 :param action: action to log, should be on of predefined unique actions for
130 :param action: action to log, should be on of predefined unique actions for
130 easy translations
131 easy translations
131 :param repo: string name of repository or object containing repo_id,
132 :param repo: string name of repository or object containing repo_id,
132 that action was made on
133 that action was made on
133 :param ipaddr: optional ip address from what the action was made
134 :param ipaddr: optional ip address from what the action was made
134 :param sa: optional sqlalchemy session
135 :param sa: optional sqlalchemy session
135
136
136 """
137 """
137
138
138 if not sa:
139 if not sa:
139 sa = meta.Session()
140 sa = meta.Session()
140 # if we don't get explicit IP address try to get one from registered user
141 # if we don't get explicit IP address try to get one from registered user
141 # in tmpl context var
142 # in tmpl context var
142 if not ipaddr:
143 if not ipaddr:
143 ipaddr = getattr(get_current_rhodecode_user(), 'ip_addr', '')
144 ipaddr = getattr(get_current_rhodecode_user(), 'ip_addr', '')
144
145
145 try:
146 try:
146 if getattr(user, 'user_id', None):
147 if getattr(user, 'user_id', None):
147 user_obj = User.get(user.user_id)
148 user_obj = User.get(user.user_id)
148 elif isinstance(user, basestring):
149 elif isinstance(user, basestring):
149 user_obj = User.get_by_username(user)
150 user_obj = User.get_by_username(user)
150 else:
151 else:
151 raise Exception('You have to provide a user object or a username')
152 raise Exception('You have to provide a user object or a username')
152
153
153 if getattr(repo, 'repo_id', None):
154 if getattr(repo, 'repo_id', None):
154 repo_obj = Repository.get(repo.repo_id)
155 repo_obj = Repository.get(repo.repo_id)
155 repo_name = repo_obj.repo_name
156 repo_name = repo_obj.repo_name
156 elif isinstance(repo, basestring):
157 elif isinstance(repo, basestring):
157 repo_name = repo.lstrip('/')
158 repo_name = repo.lstrip('/')
158 repo_obj = Repository.get_by_repo_name(repo_name)
159 repo_obj = Repository.get_by_repo_name(repo_name)
159 else:
160 else:
160 repo_obj = None
161 repo_obj = None
161 repo_name = ''
162 repo_name = ''
162
163
163 user_log = UserLog()
164 user_log = UserLog()
164 user_log.user_id = user_obj.user_id
165 user_log.user_id = user_obj.user_id
165 user_log.username = user_obj.username
166 user_log.username = user_obj.username
166 action = safe_unicode(action)
167 action = safe_unicode(action)
167 user_log.action = action[:1200000]
168 user_log.action = action[:1200000]
168
169
169 user_log.repository = repo_obj
170 user_log.repository = repo_obj
170 user_log.repository_name = repo_name
171 user_log.repository_name = repo_name
171
172
172 user_log.action_date = datetime.datetime.now()
173 user_log.action_date = datetime.datetime.now()
173 user_log.user_ip = ipaddr
174 user_log.user_ip = ipaddr
174 sa.add(user_log)
175 sa.add(user_log)
175
176
176 log.info('Logging action:`%s` on repo:`%s` by user:%s ip:%s',
177 log.info('Logging action:`%s` on repo:`%s` by user:%s ip:%s',
177 action, safe_unicode(repo), user_obj, ipaddr)
178 action, safe_unicode(repo), user_obj, ipaddr)
178 if commit:
179 if commit:
179 sa.commit()
180 sa.commit()
180 except Exception:
181 except Exception:
181 log.error(traceback.format_exc())
182 log.error(traceback.format_exc())
182 raise
183 raise
183
184
184
185
185 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
186 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
186 """
187 """
187 Scans given path for repos and return (name,(type,path)) tuple
188 Scans given path for repos and return (name,(type,path)) tuple
188
189
189 :param path: path to scan for repositories
190 :param path: path to scan for repositories
190 :param recursive: recursive search and return names with subdirs in front
191 :param recursive: recursive search and return names with subdirs in front
191 """
192 """
192
193
193 # remove ending slash for better results
194 # remove ending slash for better results
194 path = path.rstrip(os.sep)
195 path = path.rstrip(os.sep)
195 log.debug('now scanning in %s location recursive:%s...', path, recursive)
196 log.debug('now scanning in %s location recursive:%s...', path, recursive)
196
197
197 def _get_repos(p):
198 def _get_repos(p):
198 dirpaths = _get_dirpaths(p)
199 dirpaths = _get_dirpaths(p)
199 if not _is_dir_writable(p):
200 if not _is_dir_writable(p):
200 log.warning('repo path without write access: %s', p)
201 log.warning('repo path without write access: %s', p)
201
202
202 for dirpath in dirpaths:
203 for dirpath in dirpaths:
203 if os.path.isfile(os.path.join(p, dirpath)):
204 if os.path.isfile(os.path.join(p, dirpath)):
204 continue
205 continue
205 cur_path = os.path.join(p, dirpath)
206 cur_path = os.path.join(p, dirpath)
206
207
207 # skip removed repos
208 # skip removed repos
208 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
209 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
209 continue
210 continue
210
211
211 #skip .<somethin> dirs
212 #skip .<somethin> dirs
212 if dirpath.startswith('.'):
213 if dirpath.startswith('.'):
213 continue
214 continue
214
215
215 try:
216 try:
216 scm_info = get_scm(cur_path)
217 scm_info = get_scm(cur_path)
217 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
218 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
218 except VCSError:
219 except VCSError:
219 if not recursive:
220 if not recursive:
220 continue
221 continue
221 #check if this dir containts other repos for recursive scan
222 #check if this dir containts other repos for recursive scan
222 rec_path = os.path.join(p, dirpath)
223 rec_path = os.path.join(p, dirpath)
223 if os.path.isdir(rec_path):
224 if os.path.isdir(rec_path):
224 for inner_scm in _get_repos(rec_path):
225 for inner_scm in _get_repos(rec_path):
225 yield inner_scm
226 yield inner_scm
226
227
227 return _get_repos(path)
228 return _get_repos(path)
228
229
229
230
230 def _get_dirpaths(p):
231 def _get_dirpaths(p):
231 try:
232 try:
232 # OS-independable way of checking if we have at least read-only
233 # OS-independable way of checking if we have at least read-only
233 # access or not.
234 # access or not.
234 dirpaths = os.listdir(p)
235 dirpaths = os.listdir(p)
235 except OSError:
236 except OSError:
236 log.warning('ignoring repo path without read access: %s', p)
237 log.warning('ignoring repo path without read access: %s', p)
237 return []
238 return []
238
239
239 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
240 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
240 # decode paths and suddenly returns unicode objects itself. The items it
241 # decode paths and suddenly returns unicode objects itself. The items it
241 # cannot decode are returned as strings and cause issues.
242 # cannot decode are returned as strings and cause issues.
242 #
243 #
243 # Those paths are ignored here until a solid solution for path handling has
244 # Those paths are ignored here until a solid solution for path handling has
244 # been built.
245 # been built.
245 expected_type = type(p)
246 expected_type = type(p)
246
247
247 def _has_correct_type(item):
248 def _has_correct_type(item):
248 if type(item) is not expected_type:
249 if type(item) is not expected_type:
249 log.error(
250 log.error(
250 u"Ignoring path %s since it cannot be decoded into unicode.",
251 u"Ignoring path %s since it cannot be decoded into unicode.",
251 # Using "repr" to make sure that we see the byte value in case
252 # Using "repr" to make sure that we see the byte value in case
252 # of support.
253 # of support.
253 repr(item))
254 repr(item))
254 return False
255 return False
255 return True
256 return True
256
257
257 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
258 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
258
259
259 return dirpaths
260 return dirpaths
260
261
261
262
262 def _is_dir_writable(path):
263 def _is_dir_writable(path):
263 """
264 """
264 Probe if `path` is writable.
265 Probe if `path` is writable.
265
266
266 Due to trouble on Cygwin / Windows, this is actually probing if it is
267 Due to trouble on Cygwin / Windows, this is actually probing if it is
267 possible to create a file inside of `path`, stat does not produce reliable
268 possible to create a file inside of `path`, stat does not produce reliable
268 results in this case.
269 results in this case.
269 """
270 """
270 try:
271 try:
271 with tempfile.TemporaryFile(dir=path):
272 with tempfile.TemporaryFile(dir=path):
272 pass
273 pass
273 except OSError:
274 except OSError:
274 return False
275 return False
275 return True
276 return True
276
277
277
278
278 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None):
279 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None):
279 """
280 """
280 Returns True if given path is a valid repository False otherwise.
281 Returns True if given path is a valid repository False otherwise.
281 If expect_scm param is given also, compare if given scm is the same
282 If expect_scm param is given also, compare if given scm is the same
282 as expected from scm parameter. If explicit_scm is given don't try to
283 as expected from scm parameter. If explicit_scm is given don't try to
283 detect the scm, just use the given one to check if repo is valid
284 detect the scm, just use the given one to check if repo is valid
284
285
285 :param repo_name:
286 :param repo_name:
286 :param base_path:
287 :param base_path:
287 :param expect_scm:
288 :param expect_scm:
288 :param explicit_scm:
289 :param explicit_scm:
289
290
290 :return True: if given path is a valid repository
291 :return True: if given path is a valid repository
291 """
292 """
292 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
293 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
293 log.debug('Checking if `%s` is a valid path for repository', repo_name)
294 log.debug('Checking if `%s` is a valid path for repository', repo_name)
294
295
295 try:
296 try:
296 if explicit_scm:
297 if explicit_scm:
297 detected_scms = [get_scm_backend(explicit_scm)]
298 detected_scms = [get_scm_backend(explicit_scm)]
298 else:
299 else:
299 detected_scms = get_scm(full_path)
300 detected_scms = get_scm(full_path)
300
301
301 if expect_scm:
302 if expect_scm:
302 return detected_scms[0] == expect_scm
303 return detected_scms[0] == expect_scm
303 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
304 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
304 return True
305 return True
305 except VCSError:
306 except VCSError:
306 log.debug('path: %s is not a valid repo !', full_path)
307 log.debug('path: %s is not a valid repo !', full_path)
307 return False
308 return False
308
309
309
310
310 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
311 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
311 """
312 """
312 Returns True if given path is a repository group, False otherwise
313 Returns True if given path is a repository group, False otherwise
313
314
314 :param repo_name:
315 :param repo_name:
315 :param base_path:
316 :param base_path:
316 """
317 """
317 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
318 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
318 log.debug('Checking if `%s` is a valid path for repository group',
319 log.debug('Checking if `%s` is a valid path for repository group',
319 repo_group_name)
320 repo_group_name)
320
321
321 # check if it's not a repo
322 # check if it's not a repo
322 if is_valid_repo(repo_group_name, base_path):
323 if is_valid_repo(repo_group_name, base_path):
323 log.debug('Repo called %s exist, it is not a valid '
324 log.debug('Repo called %s exist, it is not a valid '
324 'repo group' % repo_group_name)
325 'repo group' % repo_group_name)
325 return False
326 return False
326
327
327 try:
328 try:
328 # we need to check bare git repos at higher level
329 # we need to check bare git repos at higher level
329 # since we might match branches/hooks/info/objects or possible
330 # since we might match branches/hooks/info/objects or possible
330 # other things inside bare git repo
331 # other things inside bare git repo
331 scm_ = get_scm(os.path.dirname(full_path))
332 scm_ = get_scm(os.path.dirname(full_path))
332 log.debug('path: %s is a vcs object:%s, not valid '
333 log.debug('path: %s is a vcs object:%s, not valid '
333 'repo group' % (full_path, scm_))
334 'repo group' % (full_path, scm_))
334 return False
335 return False
335 except VCSError:
336 except VCSError:
336 pass
337 pass
337
338
338 # check if it's a valid path
339 # check if it's a valid path
339 if skip_path_check or os.path.isdir(full_path):
340 if skip_path_check or os.path.isdir(full_path):
340 log.debug('path: %s is a valid repo group !', full_path)
341 log.debug('path: %s is a valid repo group !', full_path)
341 return True
342 return True
342
343
343 log.debug('path: %s is not a valid repo group !', full_path)
344 log.debug('path: %s is not a valid repo group !', full_path)
344 return False
345 return False
345
346
346
347
347 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
348 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
348 while True:
349 while True:
349 ok = raw_input(prompt)
350 ok = raw_input(prompt)
350 if ok.lower() in ('y', 'ye', 'yes'):
351 if ok.lower() in ('y', 'ye', 'yes'):
351 return True
352 return True
352 if ok.lower() in ('n', 'no', 'nop', 'nope'):
353 if ok.lower() in ('n', 'no', 'nop', 'nope'):
353 return False
354 return False
354 retries = retries - 1
355 retries = retries - 1
355 if retries < 0:
356 if retries < 0:
356 raise IOError
357 raise IOError
357 print(complaint)
358 print(complaint)
358
359
359 # propagated from mercurial documentation
360 # propagated from mercurial documentation
360 ui_sections = [
361 ui_sections = [
361 'alias', 'auth',
362 'alias', 'auth',
362 'decode/encode', 'defaults',
363 'decode/encode', 'defaults',
363 'diff', 'email',
364 'diff', 'email',
364 'extensions', 'format',
365 'extensions', 'format',
365 'merge-patterns', 'merge-tools',
366 'merge-patterns', 'merge-tools',
366 'hooks', 'http_proxy',
367 'hooks', 'http_proxy',
367 'smtp', 'patch',
368 'smtp', 'patch',
368 'paths', 'profiling',
369 'paths', 'profiling',
369 'server', 'trusted',
370 'server', 'trusted',
370 'ui', 'web', ]
371 'ui', 'web', ]
371
372
372
373
373 def config_data_from_db(clear_session=True, repo=None):
374 def config_data_from_db(clear_session=True, repo=None):
374 """
375 """
375 Read the configuration data from the database and return configuration
376 Read the configuration data from the database and return configuration
376 tuples.
377 tuples.
377 """
378 """
378 from rhodecode.model.settings import VcsSettingsModel
379 from rhodecode.model.settings import VcsSettingsModel
379
380
380 config = []
381 config = []
381
382
382 sa = meta.Session()
383 sa = meta.Session()
383 settings_model = VcsSettingsModel(repo=repo, sa=sa)
384 settings_model = VcsSettingsModel(repo=repo, sa=sa)
384
385
385 ui_settings = settings_model.get_ui_settings()
386 ui_settings = settings_model.get_ui_settings()
386
387
387 for setting in ui_settings:
388 for setting in ui_settings:
388 if setting.active:
389 if setting.active:
389 log.debug(
390 log.debug(
390 'settings ui from db: [%s] %s=%s',
391 'settings ui from db: [%s] %s=%s',
391 setting.section, setting.key, setting.value)
392 setting.section, setting.key, setting.value)
392 config.append((
393 config.append((
393 safe_str(setting.section), safe_str(setting.key),
394 safe_str(setting.section), safe_str(setting.key),
394 safe_str(setting.value)))
395 safe_str(setting.value)))
395 if setting.key == 'push_ssl':
396 if setting.key == 'push_ssl':
396 # force set push_ssl requirement to False, rhodecode
397 # force set push_ssl requirement to False, rhodecode
397 # handles that
398 # handles that
398 config.append((
399 config.append((
399 safe_str(setting.section), safe_str(setting.key), False))
400 safe_str(setting.section), safe_str(setting.key), False))
400 if clear_session:
401 if clear_session:
401 meta.Session.remove()
402 meta.Session.remove()
402
403
403 # TODO: mikhail: probably it makes no sense to re-read hooks information.
404 # TODO: mikhail: probably it makes no sense to re-read hooks information.
404 # It's already there and activated/deactivated
405 # It's already there and activated/deactivated
405 skip_entries = []
406 skip_entries = []
406 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
407 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
407 if 'pull' not in enabled_hook_classes:
408 if 'pull' not in enabled_hook_classes:
408 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
409 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
409 if 'push' not in enabled_hook_classes:
410 if 'push' not in enabled_hook_classes:
410 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
411 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
411
412
412 config = [entry for entry in config if entry[:2] not in skip_entries]
413 config = [entry for entry in config if entry[:2] not in skip_entries]
413
414
414 return config
415 return config
415
416
416
417
417 def make_db_config(clear_session=True, repo=None):
418 def make_db_config(clear_session=True, repo=None):
418 """
419 """
419 Create a :class:`Config` instance based on the values in the database.
420 Create a :class:`Config` instance based on the values in the database.
420 """
421 """
421 config = Config()
422 config = Config()
422 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
423 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
423 for section, option, value in config_data:
424 for section, option, value in config_data:
424 config.set(section, option, value)
425 config.set(section, option, value)
425 return config
426 return config
426
427
427
428
428 def get_enabled_hook_classes(ui_settings):
429 def get_enabled_hook_classes(ui_settings):
429 """
430 """
430 Return the enabled hook classes.
431 Return the enabled hook classes.
431
432
432 :param ui_settings: List of ui_settings as returned
433 :param ui_settings: List of ui_settings as returned
433 by :meth:`VcsSettingsModel.get_ui_settings`
434 by :meth:`VcsSettingsModel.get_ui_settings`
434
435
435 :return: a list with the enabled hook classes. The order is not guaranteed.
436 :return: a list with the enabled hook classes. The order is not guaranteed.
436 :rtype: list
437 :rtype: list
437 """
438 """
438 enabled_hooks = []
439 enabled_hooks = []
439 active_hook_keys = [
440 active_hook_keys = [
440 key for section, key, value, active in ui_settings
441 key for section, key, value, active in ui_settings
441 if section == 'hooks' and active]
442 if section == 'hooks' and active]
442
443
443 hook_names = {
444 hook_names = {
444 RhodeCodeUi.HOOK_PUSH: 'push',
445 RhodeCodeUi.HOOK_PUSH: 'push',
445 RhodeCodeUi.HOOK_PULL: 'pull',
446 RhodeCodeUi.HOOK_PULL: 'pull',
446 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
447 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
447 }
448 }
448
449
449 for key in active_hook_keys:
450 for key in active_hook_keys:
450 hook = hook_names.get(key)
451 hook = hook_names.get(key)
451 if hook:
452 if hook:
452 enabled_hooks.append(hook)
453 enabled_hooks.append(hook)
453
454
454 return enabled_hooks
455 return enabled_hooks
455
456
456
457
457 def set_rhodecode_config(config):
458 def set_rhodecode_config(config):
458 """
459 """
459 Updates pylons config with new settings from database
460 Updates pylons config with new settings from database
460
461
461 :param config:
462 :param config:
462 """
463 """
463 from rhodecode.model.settings import SettingsModel
464 from rhodecode.model.settings import SettingsModel
464 app_settings = SettingsModel().get_all_settings()
465 app_settings = SettingsModel().get_all_settings()
465
466
466 for k, v in app_settings.items():
467 for k, v in app_settings.items():
467 config[k] = v
468 config[k] = v
468
469
469
470
470 def get_rhodecode_realm():
471 def get_rhodecode_realm():
471 """
472 """
472 Return the rhodecode realm from database.
473 Return the rhodecode realm from database.
473 """
474 """
474 from rhodecode.model.settings import SettingsModel
475 from rhodecode.model.settings import SettingsModel
475 realm = SettingsModel().get_setting_by_name('realm')
476 realm = SettingsModel().get_setting_by_name('realm')
476 return safe_str(realm.app_settings_value)
477 return safe_str(realm.app_settings_value)
477
478
478
479
479 def get_rhodecode_base_path():
480 def get_rhodecode_base_path():
480 """
481 """
481 Returns the base path. The base path is the filesystem path which points
482 Returns the base path. The base path is the filesystem path which points
482 to the repository store.
483 to the repository store.
483 """
484 """
484 from rhodecode.model.settings import SettingsModel
485 from rhodecode.model.settings import SettingsModel
485 paths_ui = SettingsModel().get_ui_by_section_and_key('paths', '/')
486 paths_ui = SettingsModel().get_ui_by_section_and_key('paths', '/')
486 return safe_str(paths_ui.ui_value)
487 return safe_str(paths_ui.ui_value)
487
488
488
489
489 def map_groups(path):
490 def map_groups(path):
490 """
491 """
491 Given a full path to a repository, create all nested groups that this
492 Given a full path to a repository, create all nested groups that this
492 repo is inside. This function creates parent-child relationships between
493 repo is inside. This function creates parent-child relationships between
493 groups and creates default perms for all new groups.
494 groups and creates default perms for all new groups.
494
495
495 :param paths: full path to repository
496 :param paths: full path to repository
496 """
497 """
497 from rhodecode.model.repo_group import RepoGroupModel
498 from rhodecode.model.repo_group import RepoGroupModel
498 sa = meta.Session()
499 sa = meta.Session()
499 groups = path.split(Repository.NAME_SEP)
500 groups = path.split(Repository.NAME_SEP)
500 parent = None
501 parent = None
501 group = None
502 group = None
502
503
503 # last element is repo in nested groups structure
504 # last element is repo in nested groups structure
504 groups = groups[:-1]
505 groups = groups[:-1]
505 rgm = RepoGroupModel(sa)
506 rgm = RepoGroupModel(sa)
506 owner = User.get_first_super_admin()
507 owner = User.get_first_super_admin()
507 for lvl, group_name in enumerate(groups):
508 for lvl, group_name in enumerate(groups):
508 group_name = '/'.join(groups[:lvl] + [group_name])
509 group_name = '/'.join(groups[:lvl] + [group_name])
509 group = RepoGroup.get_by_group_name(group_name)
510 group = RepoGroup.get_by_group_name(group_name)
510 desc = '%s group' % group_name
511 desc = '%s group' % group_name
511
512
512 # skip folders that are now removed repos
513 # skip folders that are now removed repos
513 if REMOVED_REPO_PAT.match(group_name):
514 if REMOVED_REPO_PAT.match(group_name):
514 break
515 break
515
516
516 if group is None:
517 if group is None:
517 log.debug('creating group level: %s group_name: %s',
518 log.debug('creating group level: %s group_name: %s',
518 lvl, group_name)
519 lvl, group_name)
519 group = RepoGroup(group_name, parent)
520 group = RepoGroup(group_name, parent)
520 group.group_description = desc
521 group.group_description = desc
521 group.user = owner
522 group.user = owner
522 sa.add(group)
523 sa.add(group)
523 perm_obj = rgm._create_default_perms(group)
524 perm_obj = rgm._create_default_perms(group)
524 sa.add(perm_obj)
525 sa.add(perm_obj)
525 sa.flush()
526 sa.flush()
526
527
527 parent = group
528 parent = group
528 return group
529 return group
529
530
530
531
531 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
532 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
532 """
533 """
533 maps all repos given in initial_repo_list, non existing repositories
534 maps all repos given in initial_repo_list, non existing repositories
534 are created, if remove_obsolete is True it also checks for db entries
535 are created, if remove_obsolete is True it also checks for db entries
535 that are not in initial_repo_list and removes them.
536 that are not in initial_repo_list and removes them.
536
537
537 :param initial_repo_list: list of repositories found by scanning methods
538 :param initial_repo_list: list of repositories found by scanning methods
538 :param remove_obsolete: check for obsolete entries in database
539 :param remove_obsolete: check for obsolete entries in database
539 """
540 """
540 from rhodecode.model.repo import RepoModel
541 from rhodecode.model.repo import RepoModel
541 from rhodecode.model.scm import ScmModel
542 from rhodecode.model.scm import ScmModel
542 from rhodecode.model.repo_group import RepoGroupModel
543 from rhodecode.model.repo_group import RepoGroupModel
543 from rhodecode.model.settings import SettingsModel
544 from rhodecode.model.settings import SettingsModel
544
545
545 sa = meta.Session()
546 sa = meta.Session()
546 repo_model = RepoModel()
547 repo_model = RepoModel()
547 user = User.get_first_super_admin()
548 user = User.get_first_super_admin()
548 added = []
549 added = []
549
550
550 # creation defaults
551 # creation defaults
551 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
552 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
552 enable_statistics = defs.get('repo_enable_statistics')
553 enable_statistics = defs.get('repo_enable_statistics')
553 enable_locking = defs.get('repo_enable_locking')
554 enable_locking = defs.get('repo_enable_locking')
554 enable_downloads = defs.get('repo_enable_downloads')
555 enable_downloads = defs.get('repo_enable_downloads')
555 private = defs.get('repo_private')
556 private = defs.get('repo_private')
556
557
557 for name, repo in initial_repo_list.items():
558 for name, repo in initial_repo_list.items():
558 group = map_groups(name)
559 group = map_groups(name)
559 unicode_name = safe_unicode(name)
560 unicode_name = safe_unicode(name)
560 db_repo = repo_model.get_by_repo_name(unicode_name)
561 db_repo = repo_model.get_by_repo_name(unicode_name)
561 # found repo that is on filesystem not in RhodeCode database
562 # found repo that is on filesystem not in RhodeCode database
562 if not db_repo:
563 if not db_repo:
563 log.info('repository %s not found, creating now', name)
564 log.info('repository %s not found, creating now', name)
564 added.append(name)
565 added.append(name)
565 desc = (repo.description
566 desc = (repo.description
566 if repo.description != 'unknown'
567 if repo.description != 'unknown'
567 else '%s repository' % name)
568 else '%s repository' % name)
568
569
569 db_repo = repo_model._create_repo(
570 db_repo = repo_model._create_repo(
570 repo_name=name,
571 repo_name=name,
571 repo_type=repo.alias,
572 repo_type=repo.alias,
572 description=desc,
573 description=desc,
573 repo_group=getattr(group, 'group_id', None),
574 repo_group=getattr(group, 'group_id', None),
574 owner=user,
575 owner=user,
575 enable_locking=enable_locking,
576 enable_locking=enable_locking,
576 enable_downloads=enable_downloads,
577 enable_downloads=enable_downloads,
577 enable_statistics=enable_statistics,
578 enable_statistics=enable_statistics,
578 private=private,
579 private=private,
579 state=Repository.STATE_CREATED
580 state=Repository.STATE_CREATED
580 )
581 )
581 sa.commit()
582 sa.commit()
582 # we added that repo just now, and make sure we updated server info
583 # we added that repo just now, and make sure we updated server info
583 if db_repo.repo_type == 'git':
584 if db_repo.repo_type == 'git':
584 git_repo = db_repo.scm_instance()
585 git_repo = db_repo.scm_instance()
585 # update repository server-info
586 # update repository server-info
586 log.debug('Running update server info')
587 log.debug('Running update server info')
587 git_repo._update_server_info()
588 git_repo._update_server_info()
588
589
589 db_repo.update_commit_cache()
590 db_repo.update_commit_cache()
590
591
591 config = db_repo._config
592 config = db_repo._config
592 config.set('extensions', 'largefiles', '')
593 config.set('extensions', 'largefiles', '')
593 ScmModel().install_hooks(
594 ScmModel().install_hooks(
594 db_repo.scm_instance(config=config),
595 db_repo.scm_instance(config=config),
595 repo_type=db_repo.repo_type)
596 repo_type=db_repo.repo_type)
596
597
597 removed = []
598 removed = []
598 if remove_obsolete:
599 if remove_obsolete:
599 # remove from database those repositories that are not in the filesystem
600 # remove from database those repositories that are not in the filesystem
600 for repo in sa.query(Repository).all():
601 for repo in sa.query(Repository).all():
601 if repo.repo_name not in initial_repo_list.keys():
602 if repo.repo_name not in initial_repo_list.keys():
602 log.debug("Removing non-existing repository found in db `%s`",
603 log.debug("Removing non-existing repository found in db `%s`",
603 repo.repo_name)
604 repo.repo_name)
604 try:
605 try:
605 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
606 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
606 sa.commit()
607 sa.commit()
607 removed.append(repo.repo_name)
608 removed.append(repo.repo_name)
608 except Exception:
609 except Exception:
609 # don't hold further removals on error
610 # don't hold further removals on error
610 log.error(traceback.format_exc())
611 log.error(traceback.format_exc())
611 sa.rollback()
612 sa.rollback()
612
613
613 def splitter(full_repo_name):
614 def splitter(full_repo_name):
614 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
615 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
615 gr_name = None
616 gr_name = None
616 if len(_parts) == 2:
617 if len(_parts) == 2:
617 gr_name = _parts[0]
618 gr_name = _parts[0]
618 return gr_name
619 return gr_name
619
620
620 initial_repo_group_list = [splitter(x) for x in
621 initial_repo_group_list = [splitter(x) for x in
621 initial_repo_list.keys() if splitter(x)]
622 initial_repo_list.keys() if splitter(x)]
622
623
623 # remove from database those repository groups that are not in the
624 # remove from database those repository groups that are not in the
624 # filesystem due to parent child relationships we need to delete them
625 # filesystem due to parent child relationships we need to delete them
625 # in a specific order of most nested first
626 # in a specific order of most nested first
626 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
627 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
627 nested_sort = lambda gr: len(gr.split('/'))
628 nested_sort = lambda gr: len(gr.split('/'))
628 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
629 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
629 if group_name not in initial_repo_group_list:
630 if group_name not in initial_repo_group_list:
630 repo_group = RepoGroup.get_by_group_name(group_name)
631 repo_group = RepoGroup.get_by_group_name(group_name)
631 if (repo_group.children.all() or
632 if (repo_group.children.all() or
632 not RepoGroupModel().check_exist_filesystem(
633 not RepoGroupModel().check_exist_filesystem(
633 group_name=group_name, exc_on_failure=False)):
634 group_name=group_name, exc_on_failure=False)):
634 continue
635 continue
635
636
636 log.info(
637 log.info(
637 'Removing non-existing repository group found in db `%s`',
638 'Removing non-existing repository group found in db `%s`',
638 group_name)
639 group_name)
639 try:
640 try:
640 RepoGroupModel(sa).delete(group_name, fs_remove=False)
641 RepoGroupModel(sa).delete(group_name, fs_remove=False)
641 sa.commit()
642 sa.commit()
642 removed.append(group_name)
643 removed.append(group_name)
643 except Exception:
644 except Exception:
644 # don't hold further removals on error
645 # don't hold further removals on error
645 log.exception(
646 log.exception(
646 'Unable to remove repository group `%s`',
647 'Unable to remove repository group `%s`',
647 group_name)
648 group_name)
648 sa.rollback()
649 sa.rollback()
649 raise
650 raise
650
651
651 return added, removed
652 return added, removed
652
653
653
654
654 def get_default_cache_settings(settings):
655 def get_default_cache_settings(settings):
655 cache_settings = {}
656 cache_settings = {}
656 for key in settings.keys():
657 for key in settings.keys():
657 for prefix in ['beaker.cache.', 'cache.']:
658 for prefix in ['beaker.cache.', 'cache.']:
658 if key.startswith(prefix):
659 if key.startswith(prefix):
659 name = key.split(prefix)[1].strip()
660 name = key.split(prefix)[1].strip()
660 cache_settings[name] = settings[key].strip()
661 cache_settings[name] = settings[key].strip()
661 return cache_settings
662 return cache_settings
662
663
663
664
664 # set cache regions for beaker so celery can utilise it
665 # set cache regions for beaker so celery can utilise it
665 def add_cache(settings):
666 def add_cache(settings):
666 from rhodecode.lib import caches
667 from rhodecode.lib import caches
667 cache_settings = {'regions': None}
668 cache_settings = {'regions': None}
668 # main cache settings used as default ...
669 # main cache settings used as default ...
669 cache_settings.update(get_default_cache_settings(settings))
670 cache_settings.update(get_default_cache_settings(settings))
670
671
671 if cache_settings['regions']:
672 if cache_settings['regions']:
672 for region in cache_settings['regions'].split(','):
673 for region in cache_settings['regions'].split(','):
673 region = region.strip()
674 region = region.strip()
674 region_settings = {}
675 region_settings = {}
675 for key, value in cache_settings.items():
676 for key, value in cache_settings.items():
676 if key.startswith(region):
677 if key.startswith(region):
677 region_settings[key.split('.')[1]] = value
678 region_settings[key.split('.')[1]] = value
678
679
679 caches.configure_cache_region(
680 caches.configure_cache_region(
680 region, region_settings, cache_settings)
681 region, region_settings, cache_settings)
681
682
682
683
683 def load_rcextensions(root_path):
684 def load_rcextensions(root_path):
684 import rhodecode
685 import rhodecode
685 from rhodecode.config import conf
686 from rhodecode.config import conf
686
687
687 path = os.path.join(root_path, 'rcextensions', '__init__.py')
688 path = os.path.join(root_path, 'rcextensions', '__init__.py')
688 if os.path.isfile(path):
689 if os.path.isfile(path):
689 rcext = create_module('rc', path)
690 rcext = create_module('rc', path)
690 EXT = rhodecode.EXTENSIONS = rcext
691 EXT = rhodecode.EXTENSIONS = rcext
691 log.debug('Found rcextensions now loading %s...', rcext)
692 log.debug('Found rcextensions now loading %s...', rcext)
692
693
693 # Additional mappings that are not present in the pygments lexers
694 # Additional mappings that are not present in the pygments lexers
694 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
695 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
695
696
696 # auto check if the module is not missing any data, set to default if is
697 # auto check if the module is not missing any data, set to default if is
697 # this will help autoupdate new feature of rcext module
698 # this will help autoupdate new feature of rcext module
698 #from rhodecode.config import rcextensions
699 #from rhodecode.config import rcextensions
699 #for k in dir(rcextensions):
700 #for k in dir(rcextensions):
700 # if not k.startswith('_') and not hasattr(EXT, k):
701 # if not k.startswith('_') and not hasattr(EXT, k):
701 # setattr(EXT, k, getattr(rcextensions, k))
702 # setattr(EXT, k, getattr(rcextensions, k))
702
703
703
704
704 def get_custom_lexer(extension):
705 def get_custom_lexer(extension):
705 """
706 """
706 returns a custom lexer if it is defined in rcextensions module, or None
707 returns a custom lexer if it is defined in rcextensions module, or None
707 if there's no custom lexer defined
708 if there's no custom lexer defined
708 """
709 """
709 import rhodecode
710 import rhodecode
710 from pygments import lexers
711 from pygments import lexers
711 # check if we didn't define this extension as other lexer
712 # check if we didn't define this extension as other lexer
712 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
713 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
713 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
714 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
714 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
715 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
715 return lexers.get_lexer_by_name(_lexer_name)
716 return lexers.get_lexer_by_name(_lexer_name)
716
717
717
718
718 #==============================================================================
719 #==============================================================================
719 # TEST FUNCTIONS AND CREATORS
720 # TEST FUNCTIONS AND CREATORS
720 #==============================================================================
721 #==============================================================================
721 def create_test_index(repo_location, config):
722 def create_test_index(repo_location, config):
722 """
723 """
723 Makes default test index.
724 Makes default test index.
724 """
725 """
725 import rc_testdata
726 import rc_testdata
726
727
727 rc_testdata.extract_search_index(
728 rc_testdata.extract_search_index(
728 'vcs_search_index', os.path.dirname(config['search.location']))
729 'vcs_search_index', os.path.dirname(config['search.location']))
729
730
730
731
731 def create_test_directory(test_path):
732 def create_test_directory(test_path):
732 """
733 """
733 Create test directory if it doesn't exist.
734 Create test directory if it doesn't exist.
734 """
735 """
735 if not os.path.isdir(test_path):
736 if not os.path.isdir(test_path):
736 log.debug('Creating testdir %s', test_path)
737 log.debug('Creating testdir %s', test_path)
737 os.makedirs(test_path)
738 os.makedirs(test_path)
738
739
739
740
740 def create_test_database(test_path, config):
741 def create_test_database(test_path, config):
741 """
742 """
742 Makes a fresh database.
743 Makes a fresh database.
743 """
744 """
744 from rhodecode.lib.db_manage import DbManage
745 from rhodecode.lib.db_manage import DbManage
745
746
746 # PART ONE create db
747 # PART ONE create db
747 dbconf = config['sqlalchemy.db1.url']
748 dbconf = config['sqlalchemy.db1.url']
748 log.debug('making test db %s', dbconf)
749 log.debug('making test db %s', dbconf)
749
750
750 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
751 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
751 tests=True, cli_args={'force_ask': True})
752 tests=True, cli_args={'force_ask': True})
752 dbmanage.create_tables(override=True)
753 dbmanage.create_tables(override=True)
753 dbmanage.set_db_version()
754 dbmanage.set_db_version()
754 # for tests dynamically set new root paths based on generated content
755 # for tests dynamically set new root paths based on generated content
755 dbmanage.create_settings(dbmanage.config_prompt(test_path))
756 dbmanage.create_settings(dbmanage.config_prompt(test_path))
756 dbmanage.create_default_user()
757 dbmanage.create_default_user()
757 dbmanage.create_test_admin_and_users()
758 dbmanage.create_test_admin_and_users()
758 dbmanage.create_permissions()
759 dbmanage.create_permissions()
759 dbmanage.populate_default_permissions()
760 dbmanage.populate_default_permissions()
760 Session().commit()
761 Session().commit()
761
762
762
763
763 def create_test_repositories(test_path, config):
764 def create_test_repositories(test_path, config):
764 """
765 """
765 Creates test repositories in the temporary directory. Repositories are
766 Creates test repositories in the temporary directory. Repositories are
766 extracted from archives within the rc_testdata package.
767 extracted from archives within the rc_testdata package.
767 """
768 """
768 import rc_testdata
769 import rc_testdata
769 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
770 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
770
771
771 log.debug('making test vcs repositories')
772 log.debug('making test vcs repositories')
772
773
773 idx_path = config['search.location']
774 idx_path = config['search.location']
774 data_path = config['cache_dir']
775 data_path = config['cache_dir']
775
776
776 # clean index and data
777 # clean index and data
777 if idx_path and os.path.exists(idx_path):
778 if idx_path and os.path.exists(idx_path):
778 log.debug('remove %s', idx_path)
779 log.debug('remove %s', idx_path)
779 shutil.rmtree(idx_path)
780 shutil.rmtree(idx_path)
780
781
781 if data_path and os.path.exists(data_path):
782 if data_path and os.path.exists(data_path):
782 log.debug('remove %s', data_path)
783 log.debug('remove %s', data_path)
783 shutil.rmtree(data_path)
784 shutil.rmtree(data_path)
784
785
785 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
786 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
786 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
787 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
787
788
788 # Note: Subversion is in the process of being integrated with the system,
789 # Note: Subversion is in the process of being integrated with the system,
789 # until we have a properly packed version of the test svn repository, this
790 # until we have a properly packed version of the test svn repository, this
790 # tries to copy over the repo from a package "rc_testdata"
791 # tries to copy over the repo from a package "rc_testdata"
791 svn_repo_path = rc_testdata.get_svn_repo_archive()
792 svn_repo_path = rc_testdata.get_svn_repo_archive()
792 with tarfile.open(svn_repo_path) as tar:
793 with tarfile.open(svn_repo_path) as tar:
793 tar.extractall(jn(test_path, SVN_REPO))
794 tar.extractall(jn(test_path, SVN_REPO))
794
795
795
796
796 #==============================================================================
797 #==============================================================================
797 # PASTER COMMANDS
798 # PASTER COMMANDS
798 #==============================================================================
799 #==============================================================================
799 class BasePasterCommand(Command):
800 class BasePasterCommand(Command):
800 """
801 """
801 Abstract Base Class for paster commands.
802 Abstract Base Class for paster commands.
802
803
803 The celery commands are somewhat aggressive about loading
804 The celery commands are somewhat aggressive about loading
804 celery.conf, and since our module sets the `CELERY_LOADER`
805 celery.conf, and since our module sets the `CELERY_LOADER`
805 environment variable to our loader, we have to bootstrap a bit and
806 environment variable to our loader, we have to bootstrap a bit and
806 make sure we've had a chance to load the pylons config off of the
807 make sure we've had a chance to load the pylons config off of the
807 command line, otherwise everything fails.
808 command line, otherwise everything fails.
808 """
809 """
809 min_args = 1
810 min_args = 1
810 min_args_error = "Please provide a paster config file as an argument."
811 min_args_error = "Please provide a paster config file as an argument."
811 takes_config_file = 1
812 takes_config_file = 1
812 requires_config_file = True
813 requires_config_file = True
813
814
814 def notify_msg(self, msg, log=False):
815 def notify_msg(self, msg, log=False):
815 """Make a notification to user, additionally if logger is passed
816 """Make a notification to user, additionally if logger is passed
816 it logs this action using given logger
817 it logs this action using given logger
817
818
818 :param msg: message that will be printed to user
819 :param msg: message that will be printed to user
819 :param log: logging instance, to use to additionally log this message
820 :param log: logging instance, to use to additionally log this message
820
821
821 """
822 """
822 if log and isinstance(log, logging):
823 if log and isinstance(log, logging):
823 log(msg)
824 log(msg)
824
825
825 def run(self, args):
826 def run(self, args):
826 """
827 """
827 Overrides Command.run
828 Overrides Command.run
828
829
829 Checks for a config file argument and loads it.
830 Checks for a config file argument and loads it.
830 """
831 """
831 if len(args) < self.min_args:
832 if len(args) < self.min_args:
832 raise BadCommand(
833 raise BadCommand(
833 self.min_args_error % {'min_args': self.min_args,
834 self.min_args_error % {'min_args': self.min_args,
834 'actual_args': len(args)})
835 'actual_args': len(args)})
835
836
836 # Decrement because we're going to lob off the first argument.
837 # Decrement because we're going to lob off the first argument.
837 # @@ This is hacky
838 # @@ This is hacky
838 self.min_args -= 1
839 self.min_args -= 1
839 self.bootstrap_config(args[0])
840 self.bootstrap_config(args[0])
840 self.update_parser()
841 self.update_parser()
841 return super(BasePasterCommand, self).run(args[1:])
842 return super(BasePasterCommand, self).run(args[1:])
842
843
843 def update_parser(self):
844 def update_parser(self):
844 """
845 """
845 Abstract method. Allows for the class' parser to be updated
846 Abstract method. Allows for the class' parser to be updated
846 before the superclass' `run` method is called. Necessary to
847 before the superclass' `run` method is called. Necessary to
847 allow options/arguments to be passed through to the underlying
848 allow options/arguments to be passed through to the underlying
848 celery command.
849 celery command.
849 """
850 """
850 raise NotImplementedError("Abstract Method.")
851 raise NotImplementedError("Abstract Method.")
851
852
852 def bootstrap_config(self, conf):
853 def bootstrap_config(self, conf):
853 """
854 """
854 Loads the pylons configuration.
855 Loads the pylons configuration.
855 """
856 """
856 from pylons import config as pylonsconfig
857 from pylons import config as pylonsconfig
857
858
858 self.path_to_ini_file = os.path.realpath(conf)
859 self.path_to_ini_file = os.path.realpath(conf)
859 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
860 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
860 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
861 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
861
862
862 def _init_session(self):
863 def _init_session(self):
863 """
864 """
864 Inits SqlAlchemy Session
865 Inits SqlAlchemy Session
865 """
866 """
866 logging.config.fileConfig(self.path_to_ini_file)
867 logging.config.fileConfig(self.path_to_ini_file)
867 from pylons import config
868 from pylons import config
868 from rhodecode.config.utils import initialize_database
869 from rhodecode.config.utils import initialize_database
869
870
870 # get to remove repos !!
871 # get to remove repos !!
871 add_cache(config)
872 add_cache(config)
872 initialize_database(config)
873 initialize_database(config)
873
874
874
875
875 @decorator.decorator
876 @decorator.decorator
876 def jsonify(func, *args, **kwargs):
877 def jsonify(func, *args, **kwargs):
877 """Action decorator that formats output for JSON
878 """Action decorator that formats output for JSON
878
879
879 Given a function that will return content, this decorator will turn
880 Given a function that will return content, this decorator will turn
880 the result into JSON, with a content-type of 'application/json' and
881 the result into JSON, with a content-type of 'application/json' and
881 output it.
882 output it.
882
883
883 """
884 """
884 from pylons.decorators.util import get_pylons
885 from pylons.decorators.util import get_pylons
885 from rhodecode.lib.ext_json import json
886 from rhodecode.lib.ext_json import json
886 pylons = get_pylons(args)
887 pylons = get_pylons(args)
887 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
888 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
888 data = func(*args, **kwargs)
889 data = func(*args, **kwargs)
889 if isinstance(data, (list, tuple)):
890 if isinstance(data, (list, tuple)):
890 msg = "JSON responses with Array envelopes are susceptible to " \
891 msg = "JSON responses with Array envelopes are susceptible to " \
891 "cross-site data leak attacks, see " \
892 "cross-site data leak attacks, see " \
892 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
893 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
893 warnings.warn(msg, Warning, 2)
894 warnings.warn(msg, Warning, 2)
894 log.warning(msg)
895 log.warning(msg)
895 log.debug("Returning JSON wrapped action output")
896 log.debug("Returning JSON wrapped action output")
896 return json.dumps(data, encoding='utf-8')
897 return json.dumps(data, encoding='utf-8')
897
898
898
899
899 class PartialRenderer(object):
900 class PartialRenderer(object):
900 """
901 """
901 Partial renderer used to render chunks of html used in datagrids
902 Partial renderer used to render chunks of html used in datagrids
902 use like::
903 use like::
903
904
904 _render = PartialRenderer('data_table/_dt_elements.html')
905 _render = PartialRenderer('data_table/_dt_elements.html')
905 _render('quick_menu', args, kwargs)
906 _render('quick_menu', args, kwargs)
906 PartialRenderer.h,
907 PartialRenderer.h,
907 c,
908 c,
908 _,
909 _,
909 ungettext
910 ungettext
910 are the template stuff initialized inside and can be re-used later
911 are the template stuff initialized inside and can be re-used later
911
912
912 :param tmpl_name: template path relate to /templates/ dir
913 :param tmpl_name: template path relate to /templates/ dir
913 """
914 """
914
915
915 def __init__(self, tmpl_name):
916 def __init__(self, tmpl_name):
916 import rhodecode
917 import rhodecode
917 from pylons import request, tmpl_context as c
918 from pylons import request, tmpl_context as c
918 from pylons.i18n.translation import _, ungettext
919 from pylons.i18n.translation import _, ungettext
919 from rhodecode.lib import helpers as h
920 from rhodecode.lib import helpers as h
920
921
921 self.tmpl_name = tmpl_name
922 self.tmpl_name = tmpl_name
922 self.rhodecode = rhodecode
923 self.rhodecode = rhodecode
923 self.c = c
924 self.c = c
924 self._ = _
925 self._ = _
925 self.ungettext = ungettext
926 self.ungettext = ungettext
926 self.h = h
927 self.h = h
927 self.request = request
928 self.request = request
928
929
929 def _mako_lookup(self):
930 def _mako_lookup(self):
930 _tmpl_lookup = self.rhodecode.CONFIG['pylons.app_globals'].mako_lookup
931 _tmpl_lookup = self.rhodecode.CONFIG['pylons.app_globals'].mako_lookup
931 return _tmpl_lookup.get_template(self.tmpl_name)
932 return _tmpl_lookup.get_template(self.tmpl_name)
932
933
933 def _update_kwargs_for_render(self, kwargs):
934 def _update_kwargs_for_render(self, kwargs):
934 """
935 """
935 Inject params required for Mako rendering
936 Inject params required for Mako rendering
936 """
937 """
937 _kwargs = {
938 _kwargs = {
938 '_': self._,
939 '_': self._,
939 'h': self.h,
940 'h': self.h,
940 'c': self.c,
941 'c': self.c,
941 'request': self.request,
942 'request': self.request,
942 'ungettext': self.ungettext,
943 'ungettext': self.ungettext,
943 }
944 }
944 _kwargs.update(kwargs)
945 _kwargs.update(kwargs)
945 return _kwargs
946 return _kwargs
946
947
947 def _render_with_exc(self, render_func, args, kwargs):
948 def _render_with_exc(self, render_func, args, kwargs):
948 try:
949 try:
949 return render_func.render(*args, **kwargs)
950 return render_func.render(*args, **kwargs)
950 except:
951 except:
951 log.error(exceptions.text_error_template().render())
952 log.error(exceptions.text_error_template().render())
952 raise
953 raise
953
954
954 def _get_template(self, template_obj, def_name):
955 def _get_template(self, template_obj, def_name):
955 if def_name:
956 if def_name:
956 tmpl = template_obj.get_def(def_name)
957 tmpl = template_obj.get_def(def_name)
957 else:
958 else:
958 tmpl = template_obj
959 tmpl = template_obj
959 return tmpl
960 return tmpl
960
961
961 def render(self, def_name, *args, **kwargs):
962 def render(self, def_name, *args, **kwargs):
962 lookup_obj = self._mako_lookup()
963 lookup_obj = self._mako_lookup()
963 tmpl = self._get_template(lookup_obj, def_name=def_name)
964 tmpl = self._get_template(lookup_obj, def_name=def_name)
964 kwargs = self._update_kwargs_for_render(kwargs)
965 kwargs = self._update_kwargs_for_render(kwargs)
965 return self._render_with_exc(tmpl, args, kwargs)
966 return self._render_with_exc(tmpl, args, kwargs)
966
967
967 def __call__(self, tmpl, *args, **kwargs):
968 def __call__(self, tmpl, *args, **kwargs):
968 return self.render(tmpl, *args, **kwargs)
969 return self.render(tmpl, *args, **kwargs)
969
970
970
971
971 def password_changed(auth_user, session):
972 def password_changed(auth_user, session):
972 # Never report password change in case of default user or anonymous user.
973 # Never report password change in case of default user or anonymous user.
973 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
974 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
974 return False
975 return False
975
976
976 password_hash = md5(auth_user.password) if auth_user.password else None
977 password_hash = md5(auth_user.password) if auth_user.password else None
977 rhodecode_user = session.get('rhodecode_user', {})
978 rhodecode_user = session.get('rhodecode_user', {})
978 session_password_hash = rhodecode_user.get('password', '')
979 session_password_hash = rhodecode_user.get('password', '')
979 return password_hash != session_password_hash
980 return password_hash != session_password_hash
980
981
981
982
982 def read_opensource_licenses():
983 def read_opensource_licenses():
983 global _license_cache
984 global _license_cache
984
985
985 if not _license_cache:
986 if not _license_cache:
986 licenses = pkg_resources.resource_string(
987 licenses = pkg_resources.resource_string(
987 'rhodecode', 'config/licenses.json')
988 'rhodecode', 'config/licenses.json')
988 _license_cache = json.loads(licenses)
989 _license_cache = json.loads(licenses)
989
990
990 return _license_cache
991 return _license_cache
991
992
992
993
993 def get_registry(request):
994 def get_registry(request):
994 """
995 """
995 Utility to get the pyramid registry from a request. During migration to
996 Utility to get the pyramid registry from a request. During migration to
996 pyramid we sometimes want to use the pyramid registry from pylons context.
997 pyramid we sometimes want to use the pyramid registry from pylons context.
997 Therefore this utility returns `request.registry` for pyramid requests and
998 Therefore this utility returns `request.registry` for pyramid requests and
998 uses `get_current_registry()` for pylons requests.
999 uses `get_current_registry()` for pylons requests.
999 """
1000 """
1000 try:
1001 try:
1001 return request.registry
1002 return request.registry
1002 except AttributeError:
1003 except AttributeError:
1003 return get_current_registry()
1004 return get_current_registry()
1005
1006
1007 def generate_platform_uuid():
1008 """
1009 Generates platform UUID based on it's name
1010 """
1011 import platform
1012
1013 try:
1014 uuid_list = [platform.platform()]
1015 return hashlib.sha256(':'.join(uuid_list)).hexdigest()
1016 except Exception as e:
1017 log.error('Failed to generate host uuid: %s' % e)
1018 return 'UNDEFINED'
General Comments 0
You need to be logged in to leave comments. Login now