##// END OF EJS Templates
hosting: added usage writers for hosting needs.
marcink -
r4473:fbaf80e4 default
parent child Browse files
Show More
@@ -1,761 +1,763 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 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 import os
21 import os
22 import sys
22 import sys
23 import logging
23 import logging
24 import collections
24 import collections
25 import tempfile
25 import tempfile
26 import time
26 import time
27
27
28 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
29 import pyramid.events
29 import pyramid.events
30 from pyramid.wsgi import wsgiapp
30 from pyramid.wsgi import wsgiapp
31 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.authorization import ACLAuthorizationPolicy
32 from pyramid.config import Configurator
32 from pyramid.config import Configurator
33 from pyramid.settings import asbool, aslist
33 from pyramid.settings import asbool, aslist
34 from pyramid.httpexceptions import (
34 from pyramid.httpexceptions import (
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
36 from pyramid.renderers import render_to_response
36 from pyramid.renderers import render_to_response
37
37
38 from rhodecode.model import meta
38 from rhodecode.model import meta
39 from rhodecode.config import patches
39 from rhodecode.config import patches
40 from rhodecode.config import utils as config_utils
40 from rhodecode.config import utils as config_utils
41 from rhodecode.config.environment import load_pyramid_environment
41 from rhodecode.config.environment import load_pyramid_environment
42
42
43 import rhodecode.events
43 import rhodecode.events
44 from rhodecode.lib.middleware.vcs import VCSMiddleware
44 from rhodecode.lib.middleware.vcs import VCSMiddleware
45 from rhodecode.lib.request import Request
45 from rhodecode.lib.request import Request
46 from rhodecode.lib.vcs import VCSCommunicationError
46 from rhodecode.lib.vcs import VCSCommunicationError
47 from rhodecode.lib.exceptions import VCSServerUnavailable
47 from rhodecode.lib.exceptions import VCSServerUnavailable
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
50 from rhodecode.lib.celerylib.loader import configure_celery
50 from rhodecode.lib.celerylib.loader import configure_celery
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
52 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
52 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
53 from rhodecode.lib.exc_tracking import store_exception
53 from rhodecode.lib.exc_tracking import store_exception
54 from rhodecode.subscribers import (
54 from rhodecode.subscribers import (
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
56 write_metadata_if_needed, inject_app_settings)
56 write_metadata_if_needed, write_usage_data, inject_app_settings)
57
57
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 def is_http_error(response):
62 def is_http_error(response):
63 # error which should have traceback
63 # error which should have traceback
64 return response.status_code > 499
64 return response.status_code > 499
65
65
66
66
67 def should_load_all():
67 def should_load_all():
68 """
68 """
69 Returns if all application components should be loaded. In some cases it's
69 Returns if all application components should be loaded. In some cases it's
70 desired to skip apps loading for faster shell script execution
70 desired to skip apps loading for faster shell script execution
71 """
71 """
72 ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER')
72 ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER')
73 if ssh_cmd:
73 if ssh_cmd:
74 return False
74 return False
75
75
76 return True
76 return True
77
77
78
78
79 def make_pyramid_app(global_config, **settings):
79 def make_pyramid_app(global_config, **settings):
80 """
80 """
81 Constructs the WSGI application based on Pyramid.
81 Constructs the WSGI application based on Pyramid.
82
82
83 Specials:
83 Specials:
84
84
85 * The application can also be integrated like a plugin via the call to
85 * The application can also be integrated like a plugin via the call to
86 `includeme`. This is accompanied with the other utility functions which
86 `includeme`. This is accompanied with the other utility functions which
87 are called. Changing this should be done with great care to not break
87 are called. Changing this should be done with great care to not break
88 cases when these fragments are assembled from another place.
88 cases when these fragments are assembled from another place.
89
89
90 """
90 """
91
91
92 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
92 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
93 # will be replaced by the value of the environment variable "NAME" in this case.
93 # will be replaced by the value of the environment variable "NAME" in this case.
94 start_time = time.time()
94 start_time = time.time()
95
95
96 debug = asbool(global_config.get('debug'))
96 debug = asbool(global_config.get('debug'))
97 if debug:
97 if debug:
98 enable_debug()
98 enable_debug()
99
99
100 environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()}
100 environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()}
101
101
102 global_config = _substitute_values(global_config, environ)
102 global_config = _substitute_values(global_config, environ)
103 settings = _substitute_values(settings, environ)
103 settings = _substitute_values(settings, environ)
104
104
105 sanitize_settings_and_apply_defaults(global_config, settings)
105 sanitize_settings_and_apply_defaults(global_config, settings)
106
106
107 config = Configurator(settings=settings)
107 config = Configurator(settings=settings)
108
108
109 # Apply compatibility patches
109 # Apply compatibility patches
110 patches.inspect_getargspec()
110 patches.inspect_getargspec()
111
111
112 load_pyramid_environment(global_config, settings)
112 load_pyramid_environment(global_config, settings)
113
113
114 # Static file view comes first
114 # Static file view comes first
115 includeme_first(config)
115 includeme_first(config)
116
116
117 includeme(config)
117 includeme(config)
118
118
119 pyramid_app = config.make_wsgi_app()
119 pyramid_app = config.make_wsgi_app()
120 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
120 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
121 pyramid_app.config = config
121 pyramid_app.config = config
122
122
123 config.configure_celery(global_config['__file__'])
123 config.configure_celery(global_config['__file__'])
124 # creating the app uses a connection - return it after we are done
124 # creating the app uses a connection - return it after we are done
125 meta.Session.remove()
125 meta.Session.remove()
126 total_time = time.time() - start_time
126 total_time = time.time() - start_time
127 log.info('Pyramid app `%s` created and configured in %.2fs',
127 log.info('Pyramid app `%s` created and configured in %.2fs',
128 pyramid_app.func_name, total_time)
128 pyramid_app.func_name, total_time)
129
129
130 return pyramid_app
130 return pyramid_app
131
131
132
132
133 def not_found_view(request):
133 def not_found_view(request):
134 """
134 """
135 This creates the view which should be registered as not-found-view to
135 This creates the view which should be registered as not-found-view to
136 pyramid.
136 pyramid.
137 """
137 """
138
138
139 if not getattr(request, 'vcs_call', None):
139 if not getattr(request, 'vcs_call', None):
140 # handle like regular case with our error_handler
140 # handle like regular case with our error_handler
141 return error_handler(HTTPNotFound(), request)
141 return error_handler(HTTPNotFound(), request)
142
142
143 # handle not found view as a vcs call
143 # handle not found view as a vcs call
144 settings = request.registry.settings
144 settings = request.registry.settings
145 ae_client = getattr(request, 'ae_client', None)
145 ae_client = getattr(request, 'ae_client', None)
146 vcs_app = VCSMiddleware(
146 vcs_app = VCSMiddleware(
147 HTTPNotFound(), request.registry, settings,
147 HTTPNotFound(), request.registry, settings,
148 appenlight_client=ae_client)
148 appenlight_client=ae_client)
149
149
150 return wsgiapp(vcs_app)(None, request)
150 return wsgiapp(vcs_app)(None, request)
151
151
152
152
153 def error_handler(exception, request):
153 def error_handler(exception, request):
154 import rhodecode
154 import rhodecode
155 from rhodecode.lib import helpers
155 from rhodecode.lib import helpers
156
156
157 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
157 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
158
158
159 base_response = HTTPInternalServerError()
159 base_response = HTTPInternalServerError()
160 # prefer original exception for the response since it may have headers set
160 # prefer original exception for the response since it may have headers set
161 if isinstance(exception, HTTPException):
161 if isinstance(exception, HTTPException):
162 base_response = exception
162 base_response = exception
163 elif isinstance(exception, VCSCommunicationError):
163 elif isinstance(exception, VCSCommunicationError):
164 base_response = VCSServerUnavailable()
164 base_response = VCSServerUnavailable()
165
165
166 if is_http_error(base_response):
166 if is_http_error(base_response):
167 log.exception(
167 log.exception(
168 'error occurred handling this request for path: %s', request.path)
168 'error occurred handling this request for path: %s', request.path)
169
169
170 error_explanation = base_response.explanation or str(base_response)
170 error_explanation = base_response.explanation or str(base_response)
171 if base_response.status_code == 404:
171 if base_response.status_code == 404:
172 error_explanation += " Optionally you don't have permission to access this page."
172 error_explanation += " Optionally you don't have permission to access this page."
173 c = AttributeDict()
173 c = AttributeDict()
174 c.error_message = base_response.status
174 c.error_message = base_response.status
175 c.error_explanation = error_explanation
175 c.error_explanation = error_explanation
176 c.visual = AttributeDict()
176 c.visual = AttributeDict()
177
177
178 c.visual.rhodecode_support_url = (
178 c.visual.rhodecode_support_url = (
179 request.registry.settings.get('rhodecode_support_url') or
179 request.registry.settings.get('rhodecode_support_url') or
180 request.route_url('rhodecode_support')
180 request.route_url('rhodecode_support')
181 )
181 )
182 c.redirect_time = 0
182 c.redirect_time = 0
183 c.rhodecode_name = rhodecode_title
183 c.rhodecode_name = rhodecode_title
184 if not c.rhodecode_name:
184 if not c.rhodecode_name:
185 c.rhodecode_name = 'Rhodecode'
185 c.rhodecode_name = 'Rhodecode'
186
186
187 c.causes = []
187 c.causes = []
188 if is_http_error(base_response):
188 if is_http_error(base_response):
189 c.causes.append('Server is overloaded.')
189 c.causes.append('Server is overloaded.')
190 c.causes.append('Server database connection is lost.')
190 c.causes.append('Server database connection is lost.')
191 c.causes.append('Server expected unhandled error.')
191 c.causes.append('Server expected unhandled error.')
192
192
193 if hasattr(base_response, 'causes'):
193 if hasattr(base_response, 'causes'):
194 c.causes = base_response.causes
194 c.causes = base_response.causes
195
195
196 c.messages = helpers.flash.pop_messages(request=request)
196 c.messages = helpers.flash.pop_messages(request=request)
197
197
198 exc_info = sys.exc_info()
198 exc_info = sys.exc_info()
199 c.exception_id = id(exc_info)
199 c.exception_id = id(exc_info)
200 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
200 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
201 or base_response.status_code > 499
201 or base_response.status_code > 499
202 c.exception_id_url = request.route_url(
202 c.exception_id_url = request.route_url(
203 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
203 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
204
204
205 if c.show_exception_id:
205 if c.show_exception_id:
206 store_exception(c.exception_id, exc_info)
206 store_exception(c.exception_id, exc_info)
207
207
208 response = render_to_response(
208 response = render_to_response(
209 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
209 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
210 response=base_response)
210 response=base_response)
211
211
212 return response
212 return response
213
213
214
214
215 def includeme_first(config):
215 def includeme_first(config):
216 # redirect automatic browser favicon.ico requests to correct place
216 # redirect automatic browser favicon.ico requests to correct place
217 def favicon_redirect(context, request):
217 def favicon_redirect(context, request):
218 return HTTPFound(
218 return HTTPFound(
219 request.static_path('rhodecode:public/images/favicon.ico'))
219 request.static_path('rhodecode:public/images/favicon.ico'))
220
220
221 config.add_view(favicon_redirect, route_name='favicon')
221 config.add_view(favicon_redirect, route_name='favicon')
222 config.add_route('favicon', '/favicon.ico')
222 config.add_route('favicon', '/favicon.ico')
223
223
224 def robots_redirect(context, request):
224 def robots_redirect(context, request):
225 return HTTPFound(
225 return HTTPFound(
226 request.static_path('rhodecode:public/robots.txt'))
226 request.static_path('rhodecode:public/robots.txt'))
227
227
228 config.add_view(robots_redirect, route_name='robots')
228 config.add_view(robots_redirect, route_name='robots')
229 config.add_route('robots', '/robots.txt')
229 config.add_route('robots', '/robots.txt')
230
230
231 config.add_static_view(
231 config.add_static_view(
232 '_static/deform', 'deform:static')
232 '_static/deform', 'deform:static')
233 config.add_static_view(
233 config.add_static_view(
234 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
234 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
235
235
236
236
237 def includeme(config):
237 def includeme(config):
238 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
238 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
239 settings = config.registry.settings
239 settings = config.registry.settings
240 config.set_request_factory(Request)
240 config.set_request_factory(Request)
241
241
242 # plugin information
242 # plugin information
243 config.registry.rhodecode_plugins = collections.OrderedDict()
243 config.registry.rhodecode_plugins = collections.OrderedDict()
244
244
245 config.add_directive(
245 config.add_directive(
246 'register_rhodecode_plugin', register_rhodecode_plugin)
246 'register_rhodecode_plugin', register_rhodecode_plugin)
247
247
248 config.add_directive('configure_celery', configure_celery)
248 config.add_directive('configure_celery', configure_celery)
249
249
250 if asbool(settings.get('appenlight', 'false')):
250 if asbool(settings.get('appenlight', 'false')):
251 config.include('appenlight_client.ext.pyramid_tween')
251 config.include('appenlight_client.ext.pyramid_tween')
252
252
253 load_all = should_load_all()
253 load_all = should_load_all()
254
254
255 # Includes which are required. The application would fail without them.
255 # Includes which are required. The application would fail without them.
256 config.include('pyramid_mako')
256 config.include('pyramid_mako')
257 config.include('rhodecode.lib.rc_beaker')
257 config.include('rhodecode.lib.rc_beaker')
258 config.include('rhodecode.lib.rc_cache')
258 config.include('rhodecode.lib.rc_cache')
259
259
260 config.include('rhodecode.apps._base.navigation')
260 config.include('rhodecode.apps._base.navigation')
261 config.include('rhodecode.apps._base.subscribers')
261 config.include('rhodecode.apps._base.subscribers')
262 config.include('rhodecode.tweens')
262 config.include('rhodecode.tweens')
263 config.include('rhodecode.authentication')
263 config.include('rhodecode.authentication')
264
264
265 if load_all:
265 if load_all:
266 config.include('rhodecode.integrations')
266 config.include('rhodecode.integrations')
267
267
268 if load_all:
268 if load_all:
269 # load CE authentication plugins
269 # load CE authentication plugins
270 config.include('rhodecode.authentication.plugins.auth_crowd')
270 config.include('rhodecode.authentication.plugins.auth_crowd')
271 config.include('rhodecode.authentication.plugins.auth_headers')
271 config.include('rhodecode.authentication.plugins.auth_headers')
272 config.include('rhodecode.authentication.plugins.auth_jasig_cas')
272 config.include('rhodecode.authentication.plugins.auth_jasig_cas')
273 config.include('rhodecode.authentication.plugins.auth_ldap')
273 config.include('rhodecode.authentication.plugins.auth_ldap')
274 config.include('rhodecode.authentication.plugins.auth_pam')
274 config.include('rhodecode.authentication.plugins.auth_pam')
275 config.include('rhodecode.authentication.plugins.auth_rhodecode')
275 config.include('rhodecode.authentication.plugins.auth_rhodecode')
276 config.include('rhodecode.authentication.plugins.auth_token')
276 config.include('rhodecode.authentication.plugins.auth_token')
277
277
278 # Auto discover authentication plugins and include their configuration.
278 # Auto discover authentication plugins and include their configuration.
279 if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')):
279 if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')):
280 from rhodecode.authentication import discover_legacy_plugins
280 from rhodecode.authentication import discover_legacy_plugins
281 discover_legacy_plugins(config)
281 discover_legacy_plugins(config)
282
282
283 # apps
283 # apps
284 if load_all:
284 if load_all:
285 config.include('rhodecode.apps._base')
285 config.include('rhodecode.apps._base')
286 config.include('rhodecode.apps.hovercards')
286 config.include('rhodecode.apps.hovercards')
287 config.include('rhodecode.apps.ops')
287 config.include('rhodecode.apps.ops')
288 config.include('rhodecode.apps.admin')
288 config.include('rhodecode.apps.admin')
289 config.include('rhodecode.apps.channelstream')
289 config.include('rhodecode.apps.channelstream')
290 config.include('rhodecode.apps.file_store')
290 config.include('rhodecode.apps.file_store')
291 config.include('rhodecode.apps.login')
291 config.include('rhodecode.apps.login')
292 config.include('rhodecode.apps.home')
292 config.include('rhodecode.apps.home')
293 config.include('rhodecode.apps.journal')
293 config.include('rhodecode.apps.journal')
294 config.include('rhodecode.apps.repository')
294 config.include('rhodecode.apps.repository')
295 config.include('rhodecode.apps.repo_group')
295 config.include('rhodecode.apps.repo_group')
296 config.include('rhodecode.apps.user_group')
296 config.include('rhodecode.apps.user_group')
297 config.include('rhodecode.apps.search')
297 config.include('rhodecode.apps.search')
298 config.include('rhodecode.apps.user_profile')
298 config.include('rhodecode.apps.user_profile')
299 config.include('rhodecode.apps.user_group_profile')
299 config.include('rhodecode.apps.user_group_profile')
300 config.include('rhodecode.apps.my_account')
300 config.include('rhodecode.apps.my_account')
301 config.include('rhodecode.apps.svn_support')
301 config.include('rhodecode.apps.svn_support')
302 config.include('rhodecode.apps.ssh_support')
302 config.include('rhodecode.apps.ssh_support')
303 config.include('rhodecode.apps.gist')
303 config.include('rhodecode.apps.gist')
304 config.include('rhodecode.apps.debug_style')
304 config.include('rhodecode.apps.debug_style')
305 config.include('rhodecode.api')
305 config.include('rhodecode.api')
306
306
307 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
307 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
308 config.add_translation_dirs('rhodecode:i18n/')
308 config.add_translation_dirs('rhodecode:i18n/')
309 settings['default_locale_name'] = settings.get('lang', 'en')
309 settings['default_locale_name'] = settings.get('lang', 'en')
310
310
311 # Add subscribers.
311 # Add subscribers.
312 if load_all:
312 if load_all:
313 config.add_subscriber(inject_app_settings,
313 config.add_subscriber(inject_app_settings,
314 pyramid.events.ApplicationCreated)
314 pyramid.events.ApplicationCreated)
315 config.add_subscriber(scan_repositories_if_enabled,
315 config.add_subscriber(scan_repositories_if_enabled,
316 pyramid.events.ApplicationCreated)
316 pyramid.events.ApplicationCreated)
317 config.add_subscriber(write_metadata_if_needed,
317 config.add_subscriber(write_metadata_if_needed,
318 pyramid.events.ApplicationCreated)
318 pyramid.events.ApplicationCreated)
319 config.add_subscriber(write_usage_data,
320 pyramid.events.ApplicationCreated)
319 config.add_subscriber(write_js_routes_if_enabled,
321 config.add_subscriber(write_js_routes_if_enabled,
320 pyramid.events.ApplicationCreated)
322 pyramid.events.ApplicationCreated)
321
323
322 # request custom methods
324 # request custom methods
323 config.add_request_method(
325 config.add_request_method(
324 'rhodecode.lib.partial_renderer.get_partial_renderer',
326 'rhodecode.lib.partial_renderer.get_partial_renderer',
325 'get_partial_renderer')
327 'get_partial_renderer')
326
328
327 config.add_request_method(
329 config.add_request_method(
328 'rhodecode.lib.request_counter.get_request_counter',
330 'rhodecode.lib.request_counter.get_request_counter',
329 'request_count')
331 'request_count')
330
332
331 # Set the authorization policy.
333 # Set the authorization policy.
332 authz_policy = ACLAuthorizationPolicy()
334 authz_policy = ACLAuthorizationPolicy()
333 config.set_authorization_policy(authz_policy)
335 config.set_authorization_policy(authz_policy)
334
336
335 # Set the default renderer for HTML templates to mako.
337 # Set the default renderer for HTML templates to mako.
336 config.add_mako_renderer('.html')
338 config.add_mako_renderer('.html')
337
339
338 config.add_renderer(
340 config.add_renderer(
339 name='json_ext',
341 name='json_ext',
340 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
342 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
341
343
342 # include RhodeCode plugins
344 # include RhodeCode plugins
343 includes = aslist(settings.get('rhodecode.includes', []))
345 includes = aslist(settings.get('rhodecode.includes', []))
344 for inc in includes:
346 for inc in includes:
345 config.include(inc)
347 config.include(inc)
346
348
347 # custom not found view, if our pyramid app doesn't know how to handle
349 # custom not found view, if our pyramid app doesn't know how to handle
348 # the request pass it to potential VCS handling ap
350 # the request pass it to potential VCS handling ap
349 config.add_notfound_view(not_found_view)
351 config.add_notfound_view(not_found_view)
350 if not settings.get('debugtoolbar.enabled', False):
352 if not settings.get('debugtoolbar.enabled', False):
351 # disabled debugtoolbar handle all exceptions via the error_handlers
353 # disabled debugtoolbar handle all exceptions via the error_handlers
352 config.add_view(error_handler, context=Exception)
354 config.add_view(error_handler, context=Exception)
353
355
354 # all errors including 403/404/50X
356 # all errors including 403/404/50X
355 config.add_view(error_handler, context=HTTPError)
357 config.add_view(error_handler, context=HTTPError)
356
358
357
359
358 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
360 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
359 """
361 """
360 Apply outer WSGI middlewares around the application.
362 Apply outer WSGI middlewares around the application.
361 """
363 """
362 registry = config.registry
364 registry = config.registry
363 settings = registry.settings
365 settings = registry.settings
364
366
365 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
367 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
366 pyramid_app = HttpsFixup(pyramid_app, settings)
368 pyramid_app = HttpsFixup(pyramid_app, settings)
367
369
368 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
370 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
369 pyramid_app, settings)
371 pyramid_app, settings)
370 registry.ae_client = _ae_client
372 registry.ae_client = _ae_client
371
373
372 if settings['gzip_responses']:
374 if settings['gzip_responses']:
373 pyramid_app = make_gzip_middleware(
375 pyramid_app = make_gzip_middleware(
374 pyramid_app, settings, compress_level=1)
376 pyramid_app, settings, compress_level=1)
375
377
376 # this should be the outer most middleware in the wsgi stack since
378 # this should be the outer most middleware in the wsgi stack since
377 # middleware like Routes make database calls
379 # middleware like Routes make database calls
378 def pyramid_app_with_cleanup(environ, start_response):
380 def pyramid_app_with_cleanup(environ, start_response):
379 try:
381 try:
380 return pyramid_app(environ, start_response)
382 return pyramid_app(environ, start_response)
381 finally:
383 finally:
382 # Dispose current database session and rollback uncommitted
384 # Dispose current database session and rollback uncommitted
383 # transactions.
385 # transactions.
384 meta.Session.remove()
386 meta.Session.remove()
385
387
386 # In a single threaded mode server, on non sqlite db we should have
388 # In a single threaded mode server, on non sqlite db we should have
387 # '0 Current Checked out connections' at the end of a request,
389 # '0 Current Checked out connections' at the end of a request,
388 # if not, then something, somewhere is leaving a connection open
390 # if not, then something, somewhere is leaving a connection open
389 pool = meta.Base.metadata.bind.engine.pool
391 pool = meta.Base.metadata.bind.engine.pool
390 log.debug('sa pool status: %s', pool.status())
392 log.debug('sa pool status: %s', pool.status())
391 log.debug('Request processing finalized')
393 log.debug('Request processing finalized')
392
394
393 return pyramid_app_with_cleanup
395 return pyramid_app_with_cleanup
394
396
395
397
396 def sanitize_settings_and_apply_defaults(global_config, settings):
398 def sanitize_settings_and_apply_defaults(global_config, settings):
397 """
399 """
398 Applies settings defaults and does all type conversion.
400 Applies settings defaults and does all type conversion.
399
401
400 We would move all settings parsing and preparation into this place, so that
402 We would move all settings parsing and preparation into this place, so that
401 we have only one place left which deals with this part. The remaining parts
403 we have only one place left which deals with this part. The remaining parts
402 of the application would start to rely fully on well prepared settings.
404 of the application would start to rely fully on well prepared settings.
403
405
404 This piece would later be split up per topic to avoid a big fat monster
406 This piece would later be split up per topic to avoid a big fat monster
405 function.
407 function.
406 """
408 """
407
409
408 settings.setdefault('rhodecode.edition', 'Community Edition')
410 settings.setdefault('rhodecode.edition', 'Community Edition')
409
411
410 if 'mako.default_filters' not in settings:
412 if 'mako.default_filters' not in settings:
411 # set custom default filters if we don't have it defined
413 # set custom default filters if we don't have it defined
412 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
414 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
413 settings['mako.default_filters'] = 'h_filter'
415 settings['mako.default_filters'] = 'h_filter'
414
416
415 if 'mako.directories' not in settings:
417 if 'mako.directories' not in settings:
416 mako_directories = settings.setdefault('mako.directories', [
418 mako_directories = settings.setdefault('mako.directories', [
417 # Base templates of the original application
419 # Base templates of the original application
418 'rhodecode:templates',
420 'rhodecode:templates',
419 ])
421 ])
420 log.debug(
422 log.debug(
421 "Using the following Mako template directories: %s",
423 "Using the following Mako template directories: %s",
422 mako_directories)
424 mako_directories)
423
425
424 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
426 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
425 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
427 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
426 raw_url = settings['beaker.session.url']
428 raw_url = settings['beaker.session.url']
427 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
429 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
428 settings['beaker.session.url'] = 'redis://' + raw_url
430 settings['beaker.session.url'] = 'redis://' + raw_url
429
431
430 # Default includes, possible to change as a user
432 # Default includes, possible to change as a user
431 pyramid_includes = settings.setdefault('pyramid.includes', [])
433 pyramid_includes = settings.setdefault('pyramid.includes', [])
432 log.debug(
434 log.debug(
433 "Using the following pyramid.includes: %s",
435 "Using the following pyramid.includes: %s",
434 pyramid_includes)
436 pyramid_includes)
435
437
436 # TODO: johbo: Re-think this, usually the call to config.include
438 # TODO: johbo: Re-think this, usually the call to config.include
437 # should allow to pass in a prefix.
439 # should allow to pass in a prefix.
438 settings.setdefault('rhodecode.api.url', '/_admin/api')
440 settings.setdefault('rhodecode.api.url', '/_admin/api')
439 settings.setdefault('__file__', global_config.get('__file__'))
441 settings.setdefault('__file__', global_config.get('__file__'))
440
442
441 # Sanitize generic settings.
443 # Sanitize generic settings.
442 _list_setting(settings, 'default_encoding', 'UTF-8')
444 _list_setting(settings, 'default_encoding', 'UTF-8')
443 _bool_setting(settings, 'is_test', 'false')
445 _bool_setting(settings, 'is_test', 'false')
444 _bool_setting(settings, 'gzip_responses', 'false')
446 _bool_setting(settings, 'gzip_responses', 'false')
445
447
446 # Call split out functions that sanitize settings for each topic.
448 # Call split out functions that sanitize settings for each topic.
447 _sanitize_appenlight_settings(settings)
449 _sanitize_appenlight_settings(settings)
448 _sanitize_vcs_settings(settings)
450 _sanitize_vcs_settings(settings)
449 _sanitize_cache_settings(settings)
451 _sanitize_cache_settings(settings)
450
452
451 # configure instance id
453 # configure instance id
452 config_utils.set_instance_id(settings)
454 config_utils.set_instance_id(settings)
453
455
454 return settings
456 return settings
455
457
456
458
457 def enable_debug():
459 def enable_debug():
458 """
460 """
459 Helper to enable debug on running instance
461 Helper to enable debug on running instance
460 :return:
462 :return:
461 """
463 """
462 import tempfile
464 import tempfile
463 import textwrap
465 import textwrap
464 import logging.config
466 import logging.config
465
467
466 ini_template = textwrap.dedent("""
468 ini_template = textwrap.dedent("""
467 #####################################
469 #####################################
468 ### DEBUG LOGGING CONFIGURATION ####
470 ### DEBUG LOGGING CONFIGURATION ####
469 #####################################
471 #####################################
470 [loggers]
472 [loggers]
471 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
473 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
472
474
473 [handlers]
475 [handlers]
474 keys = console, console_sql
476 keys = console, console_sql
475
477
476 [formatters]
478 [formatters]
477 keys = generic, color_formatter, color_formatter_sql
479 keys = generic, color_formatter, color_formatter_sql
478
480
479 #############
481 #############
480 ## LOGGERS ##
482 ## LOGGERS ##
481 #############
483 #############
482 [logger_root]
484 [logger_root]
483 level = NOTSET
485 level = NOTSET
484 handlers = console
486 handlers = console
485
487
486 [logger_sqlalchemy]
488 [logger_sqlalchemy]
487 level = INFO
489 level = INFO
488 handlers = console_sql
490 handlers = console_sql
489 qualname = sqlalchemy.engine
491 qualname = sqlalchemy.engine
490 propagate = 0
492 propagate = 0
491
493
492 [logger_beaker]
494 [logger_beaker]
493 level = DEBUG
495 level = DEBUG
494 handlers =
496 handlers =
495 qualname = beaker.container
497 qualname = beaker.container
496 propagate = 1
498 propagate = 1
497
499
498 [logger_rhodecode]
500 [logger_rhodecode]
499 level = DEBUG
501 level = DEBUG
500 handlers =
502 handlers =
501 qualname = rhodecode
503 qualname = rhodecode
502 propagate = 1
504 propagate = 1
503
505
504 [logger_ssh_wrapper]
506 [logger_ssh_wrapper]
505 level = DEBUG
507 level = DEBUG
506 handlers =
508 handlers =
507 qualname = ssh_wrapper
509 qualname = ssh_wrapper
508 propagate = 1
510 propagate = 1
509
511
510 [logger_celery]
512 [logger_celery]
511 level = DEBUG
513 level = DEBUG
512 handlers =
514 handlers =
513 qualname = celery
515 qualname = celery
514
516
515
517
516 ##############
518 ##############
517 ## HANDLERS ##
519 ## HANDLERS ##
518 ##############
520 ##############
519
521
520 [handler_console]
522 [handler_console]
521 class = StreamHandler
523 class = StreamHandler
522 args = (sys.stderr, )
524 args = (sys.stderr, )
523 level = DEBUG
525 level = DEBUG
524 formatter = color_formatter
526 formatter = color_formatter
525
527
526 [handler_console_sql]
528 [handler_console_sql]
527 # "level = DEBUG" logs SQL queries and results.
529 # "level = DEBUG" logs SQL queries and results.
528 # "level = INFO" logs SQL queries.
530 # "level = INFO" logs SQL queries.
529 # "level = WARN" logs neither. (Recommended for production systems.)
531 # "level = WARN" logs neither. (Recommended for production systems.)
530 class = StreamHandler
532 class = StreamHandler
531 args = (sys.stderr, )
533 args = (sys.stderr, )
532 level = WARN
534 level = WARN
533 formatter = color_formatter_sql
535 formatter = color_formatter_sql
534
536
535 ################
537 ################
536 ## FORMATTERS ##
538 ## FORMATTERS ##
537 ################
539 ################
538
540
539 [formatter_generic]
541 [formatter_generic]
540 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
542 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
541 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
543 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
542 datefmt = %Y-%m-%d %H:%M:%S
544 datefmt = %Y-%m-%d %H:%M:%S
543
545
544 [formatter_color_formatter]
546 [formatter_color_formatter]
545 class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter
547 class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter
546 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
548 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
547 datefmt = %Y-%m-%d %H:%M:%S
549 datefmt = %Y-%m-%d %H:%M:%S
548
550
549 [formatter_color_formatter_sql]
551 [formatter_color_formatter_sql]
550 class = rhodecode.lib.logging_formatter.ColorFormatterSql
552 class = rhodecode.lib.logging_formatter.ColorFormatterSql
551 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
553 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
552 datefmt = %Y-%m-%d %H:%M:%S
554 datefmt = %Y-%m-%d %H:%M:%S
553 """)
555 """)
554
556
555 with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini',
557 with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini',
556 delete=False) as f:
558 delete=False) as f:
557 log.info('Saved Temporary DEBUG config at %s', f.name)
559 log.info('Saved Temporary DEBUG config at %s', f.name)
558 f.write(ini_template)
560 f.write(ini_template)
559
561
560 logging.config.fileConfig(f.name)
562 logging.config.fileConfig(f.name)
561 log.debug('DEBUG MODE ON')
563 log.debug('DEBUG MODE ON')
562 os.remove(f.name)
564 os.remove(f.name)
563
565
564
566
565 def _sanitize_appenlight_settings(settings):
567 def _sanitize_appenlight_settings(settings):
566 _bool_setting(settings, 'appenlight', 'false')
568 _bool_setting(settings, 'appenlight', 'false')
567
569
568
570
569 def _sanitize_vcs_settings(settings):
571 def _sanitize_vcs_settings(settings):
570 """
572 """
571 Applies settings defaults and does type conversion for all VCS related
573 Applies settings defaults and does type conversion for all VCS related
572 settings.
574 settings.
573 """
575 """
574 _string_setting(settings, 'vcs.svn.compatible_version', '')
576 _string_setting(settings, 'vcs.svn.compatible_version', '')
575 _string_setting(settings, 'vcs.hooks.protocol', 'http')
577 _string_setting(settings, 'vcs.hooks.protocol', 'http')
576 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
578 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
577 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
579 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
578 _string_setting(settings, 'vcs.server', '')
580 _string_setting(settings, 'vcs.server', '')
579 _string_setting(settings, 'vcs.server.protocol', 'http')
581 _string_setting(settings, 'vcs.server.protocol', 'http')
580 _bool_setting(settings, 'startup.import_repos', 'false')
582 _bool_setting(settings, 'startup.import_repos', 'false')
581 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
583 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
582 _bool_setting(settings, 'vcs.server.enable', 'true')
584 _bool_setting(settings, 'vcs.server.enable', 'true')
583 _bool_setting(settings, 'vcs.start_server', 'false')
585 _bool_setting(settings, 'vcs.start_server', 'false')
584 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
586 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
585 _int_setting(settings, 'vcs.connection_timeout', 3600)
587 _int_setting(settings, 'vcs.connection_timeout', 3600)
586
588
587 # Support legacy values of vcs.scm_app_implementation. Legacy
589 # Support legacy values of vcs.scm_app_implementation. Legacy
588 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
590 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
589 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
591 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
590 scm_app_impl = settings['vcs.scm_app_implementation']
592 scm_app_impl = settings['vcs.scm_app_implementation']
591 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
593 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
592 settings['vcs.scm_app_implementation'] = 'http'
594 settings['vcs.scm_app_implementation'] = 'http'
593
595
594
596
595 def _sanitize_cache_settings(settings):
597 def _sanitize_cache_settings(settings):
596 temp_store = tempfile.gettempdir()
598 temp_store = tempfile.gettempdir()
597 default_cache_dir = os.path.join(temp_store, 'rc_cache')
599 default_cache_dir = os.path.join(temp_store, 'rc_cache')
598
600
599 # save default, cache dir, and use it for all backends later.
601 # save default, cache dir, and use it for all backends later.
600 default_cache_dir = _string_setting(
602 default_cache_dir = _string_setting(
601 settings,
603 settings,
602 'cache_dir',
604 'cache_dir',
603 default_cache_dir, lower=False, default_when_empty=True)
605 default_cache_dir, lower=False, default_when_empty=True)
604
606
605 # ensure we have our dir created
607 # ensure we have our dir created
606 if not os.path.isdir(default_cache_dir):
608 if not os.path.isdir(default_cache_dir):
607 os.makedirs(default_cache_dir, mode=0o755)
609 os.makedirs(default_cache_dir, mode=0o755)
608
610
609 # exception store cache
611 # exception store cache
610 _string_setting(
612 _string_setting(
611 settings,
613 settings,
612 'exception_tracker.store_path',
614 'exception_tracker.store_path',
613 temp_store, lower=False, default_when_empty=True)
615 temp_store, lower=False, default_when_empty=True)
614 _bool_setting(
616 _bool_setting(
615 settings,
617 settings,
616 'exception_tracker.send_email',
618 'exception_tracker.send_email',
617 'false')
619 'false')
618 _string_setting(
620 _string_setting(
619 settings,
621 settings,
620 'exception_tracker.email_prefix',
622 'exception_tracker.email_prefix',
621 '[RHODECODE ERROR]', lower=False, default_when_empty=True)
623 '[RHODECODE ERROR]', lower=False, default_when_empty=True)
622
624
623 # cache_perms
625 # cache_perms
624 _string_setting(
626 _string_setting(
625 settings,
627 settings,
626 'rc_cache.cache_perms.backend',
628 'rc_cache.cache_perms.backend',
627 'dogpile.cache.rc.file_namespace', lower=False)
629 'dogpile.cache.rc.file_namespace', lower=False)
628 _int_setting(
630 _int_setting(
629 settings,
631 settings,
630 'rc_cache.cache_perms.expiration_time',
632 'rc_cache.cache_perms.expiration_time',
631 60)
633 60)
632 _string_setting(
634 _string_setting(
633 settings,
635 settings,
634 'rc_cache.cache_perms.arguments.filename',
636 'rc_cache.cache_perms.arguments.filename',
635 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
637 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
636
638
637 # cache_repo
639 # cache_repo
638 _string_setting(
640 _string_setting(
639 settings,
641 settings,
640 'rc_cache.cache_repo.backend',
642 'rc_cache.cache_repo.backend',
641 'dogpile.cache.rc.file_namespace', lower=False)
643 'dogpile.cache.rc.file_namespace', lower=False)
642 _int_setting(
644 _int_setting(
643 settings,
645 settings,
644 'rc_cache.cache_repo.expiration_time',
646 'rc_cache.cache_repo.expiration_time',
645 60)
647 60)
646 _string_setting(
648 _string_setting(
647 settings,
649 settings,
648 'rc_cache.cache_repo.arguments.filename',
650 'rc_cache.cache_repo.arguments.filename',
649 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
651 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
650
652
651 # cache_license
653 # cache_license
652 _string_setting(
654 _string_setting(
653 settings,
655 settings,
654 'rc_cache.cache_license.backend',
656 'rc_cache.cache_license.backend',
655 'dogpile.cache.rc.file_namespace', lower=False)
657 'dogpile.cache.rc.file_namespace', lower=False)
656 _int_setting(
658 _int_setting(
657 settings,
659 settings,
658 'rc_cache.cache_license.expiration_time',
660 'rc_cache.cache_license.expiration_time',
659 5*60)
661 5*60)
660 _string_setting(
662 _string_setting(
661 settings,
663 settings,
662 'rc_cache.cache_license.arguments.filename',
664 'rc_cache.cache_license.arguments.filename',
663 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
665 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
664
666
665 # cache_repo_longterm memory, 96H
667 # cache_repo_longterm memory, 96H
666 _string_setting(
668 _string_setting(
667 settings,
669 settings,
668 'rc_cache.cache_repo_longterm.backend',
670 'rc_cache.cache_repo_longterm.backend',
669 'dogpile.cache.rc.memory_lru', lower=False)
671 'dogpile.cache.rc.memory_lru', lower=False)
670 _int_setting(
672 _int_setting(
671 settings,
673 settings,
672 'rc_cache.cache_repo_longterm.expiration_time',
674 'rc_cache.cache_repo_longterm.expiration_time',
673 345600)
675 345600)
674 _int_setting(
676 _int_setting(
675 settings,
677 settings,
676 'rc_cache.cache_repo_longterm.max_size',
678 'rc_cache.cache_repo_longterm.max_size',
677 10000)
679 10000)
678
680
679 # sql_cache_short
681 # sql_cache_short
680 _string_setting(
682 _string_setting(
681 settings,
683 settings,
682 'rc_cache.sql_cache_short.backend',
684 'rc_cache.sql_cache_short.backend',
683 'dogpile.cache.rc.memory_lru', lower=False)
685 'dogpile.cache.rc.memory_lru', lower=False)
684 _int_setting(
686 _int_setting(
685 settings,
687 settings,
686 'rc_cache.sql_cache_short.expiration_time',
688 'rc_cache.sql_cache_short.expiration_time',
687 30)
689 30)
688 _int_setting(
690 _int_setting(
689 settings,
691 settings,
690 'rc_cache.sql_cache_short.max_size',
692 'rc_cache.sql_cache_short.max_size',
691 10000)
693 10000)
692
694
693
695
694 def _int_setting(settings, name, default):
696 def _int_setting(settings, name, default):
695 settings[name] = int(settings.get(name, default))
697 settings[name] = int(settings.get(name, default))
696 return settings[name]
698 return settings[name]
697
699
698
700
699 def _bool_setting(settings, name, default):
701 def _bool_setting(settings, name, default):
700 input_val = settings.get(name, default)
702 input_val = settings.get(name, default)
701 if isinstance(input_val, unicode):
703 if isinstance(input_val, unicode):
702 input_val = input_val.encode('utf8')
704 input_val = input_val.encode('utf8')
703 settings[name] = asbool(input_val)
705 settings[name] = asbool(input_val)
704 return settings[name]
706 return settings[name]
705
707
706
708
707 def _list_setting(settings, name, default):
709 def _list_setting(settings, name, default):
708 raw_value = settings.get(name, default)
710 raw_value = settings.get(name, default)
709
711
710 old_separator = ','
712 old_separator = ','
711 if old_separator in raw_value:
713 if old_separator in raw_value:
712 # If we get a comma separated list, pass it to our own function.
714 # If we get a comma separated list, pass it to our own function.
713 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
715 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
714 else:
716 else:
715 # Otherwise we assume it uses pyramids space/newline separation.
717 # Otherwise we assume it uses pyramids space/newline separation.
716 settings[name] = aslist(raw_value)
718 settings[name] = aslist(raw_value)
717 return settings[name]
719 return settings[name]
718
720
719
721
720 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
722 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
721 value = settings.get(name, default)
723 value = settings.get(name, default)
722
724
723 if default_when_empty and not value:
725 if default_when_empty and not value:
724 # use default value when value is empty
726 # use default value when value is empty
725 value = default
727 value = default
726
728
727 if lower:
729 if lower:
728 value = value.lower()
730 value = value.lower()
729 settings[name] = value
731 settings[name] = value
730 return settings[name]
732 return settings[name]
731
733
732
734
733 def _substitute_values(mapping, substitutions):
735 def _substitute_values(mapping, substitutions):
734 result = {}
736 result = {}
735
737
736 try:
738 try:
737 for key, value in mapping.items():
739 for key, value in mapping.items():
738 # initialize without substitution first
740 # initialize without substitution first
739 result[key] = value
741 result[key] = value
740
742
741 # Note: Cannot use regular replacements, since they would clash
743 # Note: Cannot use regular replacements, since they would clash
742 # with the implementation of ConfigParser. Using "format" instead.
744 # with the implementation of ConfigParser. Using "format" instead.
743 try:
745 try:
744 result[key] = value.format(**substitutions)
746 result[key] = value.format(**substitutions)
745 except KeyError as e:
747 except KeyError as e:
746 env_var = '{}'.format(e.args[0])
748 env_var = '{}'.format(e.args[0])
747
749
748 msg = 'Failed to substitute: `{key}={{{var}}}` with environment entry. ' \
750 msg = 'Failed to substitute: `{key}={{{var}}}` with environment entry. ' \
749 'Make sure your environment has {var} set, or remove this ' \
751 'Make sure your environment has {var} set, or remove this ' \
750 'variable from config file'.format(key=key, var=env_var)
752 'variable from config file'.format(key=key, var=env_var)
751
753
752 if env_var.startswith('ENV_'):
754 if env_var.startswith('ENV_'):
753 raise ValueError(msg)
755 raise ValueError(msg)
754 else:
756 else:
755 log.warning(msg)
757 log.warning(msg)
756
758
757 except ValueError as e:
759 except ValueError as e:
758 log.warning('Failed to substitute ENV variable: %s', e)
760 log.warning('Failed to substitute ENV variable: %s', e)
759 result = mapping
761 result = mapping
760
762
761 return result
763 return result
@@ -1,328 +1,395 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 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 import io
20 import io
21 import math
21 import re
22 import re
22 import os
23 import os
23 import datetime
24 import datetime
24 import logging
25 import logging
25 import Queue
26 import Queue
26 import subprocess32
27 import subprocess32
27
28
28
29
29 from dateutil.parser import parse
30 from dateutil.parser import parse
30 from pyramid.threadlocal import get_current_request
31 from pyramid.threadlocal import get_current_request
31 from pyramid.interfaces import IRoutesMapper
32 from pyramid.interfaces import IRoutesMapper
32 from pyramid.settings import asbool
33 from pyramid.settings import asbool
33 from pyramid.path import AssetResolver
34 from pyramid.path import AssetResolver
34 from threading import Thread
35 from threading import Thread
35
36
36 from rhodecode.translation import _ as tsf
37 from rhodecode.translation import _ as tsf
37 from rhodecode.config.jsroutes import generate_jsroutes_content
38 from rhodecode.config.jsroutes import generate_jsroutes_content
38 from rhodecode.lib import auth
39 from rhodecode.lib import auth
39 from rhodecode.lib.base import get_auth_user
40 from rhodecode.lib.base import get_auth_user
40
41
41 import rhodecode
42 import rhodecode
42
43
43
44
44 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
45
46
46
47
47 def add_renderer_globals(event):
48 def add_renderer_globals(event):
48 from rhodecode.lib import helpers
49 from rhodecode.lib import helpers
49
50
50 # TODO: When executed in pyramid view context the request is not available
51 # TODO: When executed in pyramid view context the request is not available
51 # in the event. Find a better solution to get the request.
52 # in the event. Find a better solution to get the request.
52 request = event['request'] or get_current_request()
53 request = event['request'] or get_current_request()
53
54
54 # Add Pyramid translation as '_' to context
55 # Add Pyramid translation as '_' to context
55 event['_'] = request.translate
56 event['_'] = request.translate
56 event['_ungettext'] = request.plularize
57 event['_ungettext'] = request.plularize
57 event['h'] = helpers
58 event['h'] = helpers
58
59
59
60
60 def add_localizer(event):
61 def add_localizer(event):
61 request = event.request
62 request = event.request
62 localizer = request.localizer
63 localizer = request.localizer
63
64
64 def auto_translate(*args, **kwargs):
65 def auto_translate(*args, **kwargs):
65 return localizer.translate(tsf(*args, **kwargs))
66 return localizer.translate(tsf(*args, **kwargs))
66
67
67 request.translate = auto_translate
68 request.translate = auto_translate
68 request.plularize = localizer.pluralize
69 request.plularize = localizer.pluralize
69
70
70
71
71 def set_user_lang(event):
72 def set_user_lang(event):
72 request = event.request
73 request = event.request
73 cur_user = getattr(request, 'user', None)
74 cur_user = getattr(request, 'user', None)
74
75
75 if cur_user:
76 if cur_user:
76 user_lang = cur_user.get_instance().user_data.get('language')
77 user_lang = cur_user.get_instance().user_data.get('language')
77 if user_lang:
78 if user_lang:
78 log.debug('lang: setting current user:%s language to: %s', cur_user, user_lang)
79 log.debug('lang: setting current user:%s language to: %s', cur_user, user_lang)
79 event.request._LOCALE_ = user_lang
80 event.request._LOCALE_ = user_lang
80
81
81
82
82 def add_request_user_context(event):
83 def add_request_user_context(event):
83 """
84 """
84 Adds auth user into request context
85 Adds auth user into request context
85 """
86 """
86 request = event.request
87 request = event.request
87 # access req_id as soon as possible
88 # access req_id as soon as possible
88 req_id = request.req_id
89 req_id = request.req_id
89
90
90 if hasattr(request, 'vcs_call'):
91 if hasattr(request, 'vcs_call'):
91 # skip vcs calls
92 # skip vcs calls
92 return
93 return
93
94
94 if hasattr(request, 'rpc_method'):
95 if hasattr(request, 'rpc_method'):
95 # skip api calls
96 # skip api calls
96 return
97 return
97
98
98 auth_user, auth_token = get_auth_user(request)
99 auth_user, auth_token = get_auth_user(request)
99 request.user = auth_user
100 request.user = auth_user
100 request.user_auth_token = auth_token
101 request.user_auth_token = auth_token
101 request.environ['rc_auth_user'] = auth_user
102 request.environ['rc_auth_user'] = auth_user
102 request.environ['rc_auth_user_id'] = auth_user.user_id
103 request.environ['rc_auth_user_id'] = auth_user.user_id
103 request.environ['rc_req_id'] = req_id
104 request.environ['rc_req_id'] = req_id
104
105
105
106
106 def inject_app_settings(event):
107 def inject_app_settings(event):
107 settings = event.app.registry.settings
108 settings = event.app.registry.settings
108 # inject info about available permissions
109 # inject info about available permissions
109 auth.set_available_permissions(settings)
110 auth.set_available_permissions(settings)
110
111
111
112
112 def scan_repositories_if_enabled(event):
113 def scan_repositories_if_enabled(event):
113 """
114 """
114 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
115 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
115 does a repository scan if enabled in the settings.
116 does a repository scan if enabled in the settings.
116 """
117 """
117 settings = event.app.registry.settings
118 settings = event.app.registry.settings
118 vcs_server_enabled = settings['vcs.server.enable']
119 vcs_server_enabled = settings['vcs.server.enable']
119 import_on_startup = settings['startup.import_repos']
120 import_on_startup = settings['startup.import_repos']
120 if vcs_server_enabled and import_on_startup:
121 if vcs_server_enabled and import_on_startup:
121 from rhodecode.model.scm import ScmModel
122 from rhodecode.model.scm import ScmModel
122 from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_base_path
123 from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_base_path
123 repositories = ScmModel().repo_scan(get_rhodecode_base_path())
124 repositories = ScmModel().repo_scan(get_rhodecode_base_path())
124 repo2db_mapper(repositories, remove_obsolete=False)
125 repo2db_mapper(repositories, remove_obsolete=False)
125
126
126
127
127 def write_metadata_if_needed(event):
128 def write_metadata_if_needed(event):
128 """
129 """
129 Writes upgrade metadata
130 Writes upgrade metadata
130 """
131 """
131 import rhodecode
132 import rhodecode
132 from rhodecode.lib import system_info
133 from rhodecode.lib import system_info
133 from rhodecode.lib import ext_json
134 from rhodecode.lib import ext_json
134
135
135 fname = '.rcmetadata.json'
136 fname = '.rcmetadata.json'
136 ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__'))
137 ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__'))
137 metadata_destination = os.path.join(ini_loc, fname)
138 metadata_destination = os.path.join(ini_loc, fname)
138
139
139 def get_update_age():
140 def get_update_age():
140 now = datetime.datetime.utcnow()
141 now = datetime.datetime.utcnow()
141
142
142 with open(metadata_destination, 'rb') as f:
143 with open(metadata_destination, 'rb') as f:
143 data = ext_json.json.loads(f.read())
144 data = ext_json.json.loads(f.read())
144 if 'created_on' in data:
145 if 'created_on' in data:
145 update_date = parse(data['created_on'])
146 update_date = parse(data['created_on'])
146 diff = now - update_date
147 diff = now - update_date
147 return diff.total_seconds() / 60.0
148 return diff.total_seconds() / 60.0
148
149
149 return 0
150 return 0
150
151
151 def write():
152 def write():
152 configuration = system_info.SysInfo(
153 configuration = system_info.SysInfo(
153 system_info.rhodecode_config)()['value']
154 system_info.rhodecode_config)()['value']
154 license_token = configuration['config']['license_token']
155 license_token = configuration['config']['license_token']
155
156
156 setup = dict(
157 setup = dict(
157 workers=configuration['config']['server:main'].get(
158 workers=configuration['config']['server:main'].get(
158 'workers', '?'),
159 'workers', '?'),
159 worker_type=configuration['config']['server:main'].get(
160 worker_type=configuration['config']['server:main'].get(
160 'worker_class', 'sync'),
161 'worker_class', 'sync'),
161 )
162 )
162 dbinfo = system_info.SysInfo(system_info.database_info)()['value']
163 dbinfo = system_info.SysInfo(system_info.database_info)()['value']
163 del dbinfo['url']
164 del dbinfo['url']
164
165
165 metadata = dict(
166 metadata = dict(
166 desc='upgrade metadata info',
167 desc='upgrade metadata info',
167 license_token=license_token,
168 license_token=license_token,
168 created_on=datetime.datetime.utcnow().isoformat(),
169 created_on=datetime.datetime.utcnow().isoformat(),
169 usage=system_info.SysInfo(system_info.usage_info)()['value'],
170 usage=system_info.SysInfo(system_info.usage_info)()['value'],
170 platform=system_info.SysInfo(system_info.platform_type)()['value'],
171 platform=system_info.SysInfo(system_info.platform_type)()['value'],
171 database=dbinfo,
172 database=dbinfo,
172 cpu=system_info.SysInfo(system_info.cpu)()['value'],
173 cpu=system_info.SysInfo(system_info.cpu)()['value'],
173 memory=system_info.SysInfo(system_info.memory)()['value'],
174 memory=system_info.SysInfo(system_info.memory)()['value'],
174 setup=setup
175 setup=setup
175 )
176 )
176
177
177 with open(metadata_destination, 'wb') as f:
178 with open(metadata_destination, 'wb') as f:
178 f.write(ext_json.json.dumps(metadata))
179 f.write(ext_json.json.dumps(metadata))
179
180
180 settings = event.app.registry.settings
181 settings = event.app.registry.settings
181 if settings.get('metadata.skip'):
182 if settings.get('metadata.skip'):
182 return
183 return
183
184
184 # only write this every 24h, workers restart caused unwanted delays
185 # only write this every 24h, workers restart caused unwanted delays
185 try:
186 try:
186 age_in_min = get_update_age()
187 age_in_min = get_update_age()
187 except Exception:
188 except Exception:
188 age_in_min = 0
189 age_in_min = 0
189
190
190 if age_in_min > 60 * 60 * 24:
191 if age_in_min > 60 * 60 * 24:
191 return
192 return
192
193
193 try:
194 try:
194 write()
195 write()
195 except Exception:
196 except Exception:
196 pass
197 pass
197
198
198
199
200 def write_usage_data(event):
201 import rhodecode
202 from rhodecode.lib import system_info
203 from rhodecode.lib import ext_json
204
205 settings = event.app.registry.settings
206 instance_tag = settings.get('metadata.write_usage_tag')
207 if not settings.get('metadata.write_usage'):
208 return
209
210 def get_update_age(dest_file):
211 now = datetime.datetime.utcnow()
212
213 with open(dest_file, 'rb') as f:
214 data = ext_json.json.loads(f.read())
215 if 'created_on' in data:
216 update_date = parse(data['created_on'])
217 diff = now - update_date
218 return math.ceil(diff.total_seconds() / 60.0)
219
220 return 0
221
222 utc_date = datetime.datetime.utcnow()
223 hour_quarter = int(math.ceil((utc_date.hour + utc_date.minute/60.0) / 6.))
224 fname = '.rc_usage_{date.year}{date.month:02d}{date.day:02d}_{hour}.json'.format(
225 date=utc_date, hour=hour_quarter)
226 ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__'))
227
228 usage_dir = os.path.join(ini_loc, '.rcusage')
229 if not os.path.isdir(usage_dir):
230 os.makedirs(usage_dir)
231 usage_metadata_destination = os.path.join(usage_dir, fname)
232
233 try:
234 age_in_min = get_update_age(usage_metadata_destination)
235 except Exception:
236 age_in_min = 0
237
238 # write every 6th hour
239 if age_in_min and age_in_min < 60 * 6:
240 log.debug('Usage file created %s minutes ago, skipping (threashold: %s)...',
241 age_in_min, 60 * 6)
242 return
243
244 def write(dest_file):
245 configuration = system_info.SysInfo(system_info.rhodecode_config)()['value']
246 license_token = configuration['config']['license_token']
247
248 metadata = dict(
249 desc='Usage data',
250 instance_tag=instance_tag,
251 license_token=license_token,
252 created_on=datetime.datetime.utcnow().isoformat(),
253 usage=system_info.SysInfo(system_info.usage_info)()['value'],
254 )
255
256 with open(dest_file, 'wb') as f:
257 f.write(ext_json.json.dumps(metadata, indent=2, sort_keys=True))
258
259 try:
260 log.debug('Writing usage file at: %s', usage_metadata_destination)
261 write(usage_metadata_destination)
262 except Exception:
263 pass
264
265
199 def write_js_routes_if_enabled(event):
266 def write_js_routes_if_enabled(event):
200 registry = event.app.registry
267 registry = event.app.registry
201
268
202 mapper = registry.queryUtility(IRoutesMapper)
269 mapper = registry.queryUtility(IRoutesMapper)
203 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
270 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
204
271
205 def _extract_route_information(route):
272 def _extract_route_information(route):
206 """
273 """
207 Convert a route into tuple(name, path, args), eg:
274 Convert a route into tuple(name, path, args), eg:
208 ('show_user', '/profile/%(username)s', ['username'])
275 ('show_user', '/profile/%(username)s', ['username'])
209 """
276 """
210
277
211 routepath = route.pattern
278 routepath = route.pattern
212 pattern = route.pattern
279 pattern = route.pattern
213
280
214 def replace(matchobj):
281 def replace(matchobj):
215 if matchobj.group(1):
282 if matchobj.group(1):
216 return "%%(%s)s" % matchobj.group(1).split(':')[0]
283 return "%%(%s)s" % matchobj.group(1).split(':')[0]
217 else:
284 else:
218 return "%%(%s)s" % matchobj.group(2)
285 return "%%(%s)s" % matchobj.group(2)
219
286
220 routepath = _argument_prog.sub(replace, routepath)
287 routepath = _argument_prog.sub(replace, routepath)
221
288
222 if not routepath.startswith('/'):
289 if not routepath.startswith('/'):
223 routepath = '/'+routepath
290 routepath = '/'+routepath
224
291
225 return (
292 return (
226 route.name,
293 route.name,
227 routepath,
294 routepath,
228 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
295 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
229 for arg in _argument_prog.findall(pattern)]
296 for arg in _argument_prog.findall(pattern)]
230 )
297 )
231
298
232 def get_routes():
299 def get_routes():
233 # pyramid routes
300 # pyramid routes
234 for route in mapper.get_routes():
301 for route in mapper.get_routes():
235 if not route.name.startswith('__'):
302 if not route.name.startswith('__'):
236 yield _extract_route_information(route)
303 yield _extract_route_information(route)
237
304
238 if asbool(registry.settings.get('generate_js_files', 'false')):
305 if asbool(registry.settings.get('generate_js_files', 'false')):
239 static_path = AssetResolver().resolve('rhodecode:public').abspath()
306 static_path = AssetResolver().resolve('rhodecode:public').abspath()
240 jsroutes = get_routes()
307 jsroutes = get_routes()
241 jsroutes_file_content = generate_jsroutes_content(jsroutes)
308 jsroutes_file_content = generate_jsroutes_content(jsroutes)
242 jsroutes_file_path = os.path.join(
309 jsroutes_file_path = os.path.join(
243 static_path, 'js', 'rhodecode', 'routes.js')
310 static_path, 'js', 'rhodecode', 'routes.js')
244
311
245 try:
312 try:
246 with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f:
313 with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f:
247 f.write(jsroutes_file_content)
314 f.write(jsroutes_file_content)
248 except Exception:
315 except Exception:
249 log.exception('Failed to write routes.js into %s', jsroutes_file_path)
316 log.exception('Failed to write routes.js into %s', jsroutes_file_path)
250
317
251
318
252 class Subscriber(object):
319 class Subscriber(object):
253 """
320 """
254 Base class for subscribers to the pyramid event system.
321 Base class for subscribers to the pyramid event system.
255 """
322 """
256 def __call__(self, event):
323 def __call__(self, event):
257 self.run(event)
324 self.run(event)
258
325
259 def run(self, event):
326 def run(self, event):
260 raise NotImplementedError('Subclass has to implement this.')
327 raise NotImplementedError('Subclass has to implement this.')
261
328
262
329
263 class AsyncSubscriber(Subscriber):
330 class AsyncSubscriber(Subscriber):
264 """
331 """
265 Subscriber that handles the execution of events in a separate task to not
332 Subscriber that handles the execution of events in a separate task to not
266 block the execution of the code which triggers the event. It puts the
333 block the execution of the code which triggers the event. It puts the
267 received events into a queue from which the worker process takes them in
334 received events into a queue from which the worker process takes them in
268 order.
335 order.
269 """
336 """
270 def __init__(self):
337 def __init__(self):
271 self._stop = False
338 self._stop = False
272 self._eventq = Queue.Queue()
339 self._eventq = Queue.Queue()
273 self._worker = self.create_worker()
340 self._worker = self.create_worker()
274 self._worker.start()
341 self._worker.start()
275
342
276 def __call__(self, event):
343 def __call__(self, event):
277 self._eventq.put(event)
344 self._eventq.put(event)
278
345
279 def create_worker(self):
346 def create_worker(self):
280 worker = Thread(target=self.do_work)
347 worker = Thread(target=self.do_work)
281 worker.daemon = True
348 worker.daemon = True
282 return worker
349 return worker
283
350
284 def stop_worker(self):
351 def stop_worker(self):
285 self._stop = False
352 self._stop = False
286 self._eventq.put(None)
353 self._eventq.put(None)
287 self._worker.join()
354 self._worker.join()
288
355
289 def do_work(self):
356 def do_work(self):
290 while not self._stop:
357 while not self._stop:
291 event = self._eventq.get()
358 event = self._eventq.get()
292 if event is not None:
359 if event is not None:
293 self.run(event)
360 self.run(event)
294
361
295
362
296 class AsyncSubprocessSubscriber(AsyncSubscriber):
363 class AsyncSubprocessSubscriber(AsyncSubscriber):
297 """
364 """
298 Subscriber that uses the subprocess32 module to execute a command if an
365 Subscriber that uses the subprocess32 module to execute a command if an
299 event is received. Events are handled asynchronously.
366 event is received. Events are handled asynchronously.
300 """
367 """
301
368
302 def __init__(self, cmd, timeout=None):
369 def __init__(self, cmd, timeout=None):
303 super(AsyncSubprocessSubscriber, self).__init__()
370 super(AsyncSubprocessSubscriber, self).__init__()
304 self._cmd = cmd
371 self._cmd = cmd
305 self._timeout = timeout
372 self._timeout = timeout
306
373
307 def run(self, event):
374 def run(self, event):
308 cmd = self._cmd
375 cmd = self._cmd
309 timeout = self._timeout
376 timeout = self._timeout
310 log.debug('Executing command %s.', cmd)
377 log.debug('Executing command %s.', cmd)
311
378
312 try:
379 try:
313 output = subprocess32.check_output(
380 output = subprocess32.check_output(
314 cmd, timeout=timeout, stderr=subprocess32.STDOUT)
381 cmd, timeout=timeout, stderr=subprocess32.STDOUT)
315 log.debug('Command finished %s', cmd)
382 log.debug('Command finished %s', cmd)
316 if output:
383 if output:
317 log.debug('Command output: %s', output)
384 log.debug('Command output: %s', output)
318 except subprocess32.TimeoutExpired as e:
385 except subprocess32.TimeoutExpired as e:
319 log.exception('Timeout while executing command.')
386 log.exception('Timeout while executing command.')
320 if e.output:
387 if e.output:
321 log.error('Command output: %s', e.output)
388 log.error('Command output: %s', e.output)
322 except subprocess32.CalledProcessError as e:
389 except subprocess32.CalledProcessError as e:
323 log.exception('Error while executing command.')
390 log.exception('Error while executing command.')
324 if e.output:
391 if e.output:
325 log.error('Command output: %s', e.output)
392 log.error('Command output: %s', e.output)
326 except:
393 except:
327 log.exception(
394 log.exception(
328 'Exception while executing command %s.', cmd)
395 'Exception while executing command %s.', cmd)
General Comments 0
You need to be logged in to leave comments. Login now