##// END OF EJS Templates
fix(license-import): don't crash if license file is not present, and only run this code for EE
super-admin -
r5538:aa408762 default
parent child Browse files
Show More
@@ -1,414 +1,422 b''
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 import io
18 import io
19 import shlex
19 import shlex
20
20
21 import math
21 import math
22 import re
22 import re
23 import os
23 import os
24 import datetime
24 import datetime
25 import logging
25 import logging
26 import queue
26 import queue
27 import subprocess
27 import subprocess
28
28
29
29
30 from dateutil.parser import parse
30 from dateutil.parser import parse
31 from pyramid.interfaces import IRoutesMapper
31 from pyramid.interfaces import IRoutesMapper
32 from pyramid.settings import asbool
32 from pyramid.settings import asbool
33 from pyramid.path import AssetResolver
33 from pyramid.path import AssetResolver
34 from threading import Thread
34 from threading import Thread
35
35
36 from rhodecode.config.jsroutes import generate_jsroutes_content
36 from rhodecode.config.jsroutes import generate_jsroutes_content
37 from rhodecode.lib.base import get_auth_user
37 from rhodecode.lib.base import get_auth_user
38 from rhodecode.lib.celerylib.loader import set_celery_conf
38 from rhodecode.lib.celerylib.loader import set_celery_conf
39
39
40 import rhodecode
40 import rhodecode
41
41
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 def add_renderer_globals(event):
46 def add_renderer_globals(event):
47 from rhodecode.lib import helpers
47 from rhodecode.lib import helpers
48
48
49 # TODO: When executed in pyramid view context the request is not available
49 # TODO: When executed in pyramid view context the request is not available
50 # in the event. Find a better solution to get the request.
50 # in the event. Find a better solution to get the request.
51 from pyramid.threadlocal import get_current_request
51 from pyramid.threadlocal import get_current_request
52 request = event['request'] or get_current_request()
52 request = event['request'] or get_current_request()
53
53
54 # Add Pyramid translation as '_' to context
54 # Add Pyramid translation as '_' to context
55 event['_'] = request.translate
55 event['_'] = request.translate
56 event['_ungettext'] = request.plularize
56 event['_ungettext'] = request.plularize
57 event['h'] = helpers
57 event['h'] = helpers
58
58
59
59
60 def set_user_lang(event):
60 def set_user_lang(event):
61 request = event.request
61 request = event.request
62 cur_user = getattr(request, 'user', None)
62 cur_user = getattr(request, 'user', None)
63
63
64 if cur_user:
64 if cur_user:
65 user_lang = cur_user.get_instance().user_data.get('language')
65 user_lang = cur_user.get_instance().user_data.get('language')
66 if user_lang:
66 if user_lang:
67 log.debug('lang: setting current user:%s language to: %s', cur_user, user_lang)
67 log.debug('lang: setting current user:%s language to: %s', cur_user, user_lang)
68 event.request._LOCALE_ = user_lang
68 event.request._LOCALE_ = user_lang
69
69
70
70
71 def update_celery_conf(event):
71 def update_celery_conf(event):
72 log.debug('Setting celery config from new request')
72 log.debug('Setting celery config from new request')
73 set_celery_conf(request=event.request, registry=event.request.registry)
73 set_celery_conf(request=event.request, registry=event.request.registry)
74
74
75
75
76 def add_request_user_context(event):
76 def add_request_user_context(event):
77 """
77 """
78 Adds auth user into request context
78 Adds auth user into request context
79 """
79 """
80
80
81 request = event.request
81 request = event.request
82 # access req_id as soon as possible
82 # access req_id as soon as possible
83 req_id = request.req_id
83 req_id = request.req_id
84
84
85 if hasattr(request, 'vcs_call'):
85 if hasattr(request, 'vcs_call'):
86 # skip vcs calls
86 # skip vcs calls
87 return
87 return
88
88
89 if hasattr(request, 'rpc_method'):
89 if hasattr(request, 'rpc_method'):
90 # skip api calls
90 # skip api calls
91 return
91 return
92
92
93 auth_user, auth_token = get_auth_user(request)
93 auth_user, auth_token = get_auth_user(request)
94 request.user = auth_user
94 request.user = auth_user
95 request.user_auth_token = auth_token
95 request.user_auth_token = auth_token
96 request.environ['rc_auth_user'] = auth_user
96 request.environ['rc_auth_user'] = auth_user
97 request.environ['rc_auth_user_id'] = str(auth_user.user_id)
97 request.environ['rc_auth_user_id'] = str(auth_user.user_id)
98 request.environ['rc_req_id'] = req_id
98 request.environ['rc_req_id'] = req_id
99
99
100
100
101 def reset_log_bucket(event):
101 def reset_log_bucket(event):
102 """
102 """
103 reset the log bucket on new request
103 reset the log bucket on new request
104 """
104 """
105 request = event.request
105 request = event.request
106 request.req_id_records_init()
106 request.req_id_records_init()
107
107
108
108
109 def scan_repositories_if_enabled(event):
109 def scan_repositories_if_enabled(event):
110 """
110 """
111 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
111 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
112 does a repository scan if enabled in the settings.
112 does a repository scan if enabled in the settings.
113 """
113 """
114
114 settings = event.app.registry.settings
115 settings = event.app.registry.settings
115 vcs_server_enabled = settings['vcs.server.enable']
116 vcs_server_enabled = settings['vcs.server.enable']
116 import_on_startup = settings['startup.import_repos']
117 import_on_startup = settings['startup.import_repos']
118
117 if vcs_server_enabled and import_on_startup:
119 if vcs_server_enabled and import_on_startup:
118 from rhodecode.model.scm import ScmModel
120 from rhodecode.model.scm import ScmModel
119 from rhodecode.lib.utils import repo2db_mapper
121 from rhodecode.lib.utils import repo2db_mapper
120 scm = ScmModel()
122 scm = ScmModel()
121 repositories = scm.repo_scan(scm.repos_path)
123 repositories = scm.repo_scan(scm.repos_path)
122 repo2db_mapper(repositories, remove_obsolete=False)
124 repo2db_mapper(repositories, remove_obsolete=False)
123
125
124
126
125 def write_metadata_if_needed(event):
127 def write_metadata_if_needed(event):
126 """
128 """
127 Writes upgrade metadata
129 Writes upgrade metadata
128 """
130 """
129 import rhodecode
131 import rhodecode
130 from rhodecode.lib import system_info
132 from rhodecode.lib import system_info
131 from rhodecode.lib import ext_json
133 from rhodecode.lib import ext_json
132
134
133 fname = '.rcmetadata.json'
135 fname = '.rcmetadata.json'
134 ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__'))
136 ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__'))
135 metadata_destination = os.path.join(ini_loc, fname)
137 metadata_destination = os.path.join(ini_loc, fname)
136
138
137 def get_update_age():
139 def get_update_age():
138 now = datetime.datetime.utcnow()
140 now = datetime.datetime.utcnow()
139
141
140 with open(metadata_destination, 'rb') as f:
142 with open(metadata_destination, 'rb') as f:
141 data = ext_json.json.loads(f.read())
143 data = ext_json.json.loads(f.read())
142 if 'created_on' in data:
144 if 'created_on' in data:
143 update_date = parse(data['created_on'])
145 update_date = parse(data['created_on'])
144 diff = now - update_date
146 diff = now - update_date
145 return diff.total_seconds() / 60.0
147 return diff.total_seconds() / 60.0
146
148
147 return 0
149 return 0
148
150
149 def write():
151 def write():
150 configuration = system_info.SysInfo(
152 configuration = system_info.SysInfo(
151 system_info.rhodecode_config)()['value']
153 system_info.rhodecode_config)()['value']
152 license_token = configuration['config']['license_token']
154 license_token = configuration['config']['license_token']
153
155
154 setup = dict(
156 setup = dict(
155 workers=configuration['config']['server:main'].get(
157 workers=configuration['config']['server:main'].get(
156 'workers', '?'),
158 'workers', '?'),
157 worker_type=configuration['config']['server:main'].get(
159 worker_type=configuration['config']['server:main'].get(
158 'worker_class', 'sync'),
160 'worker_class', 'sync'),
159 )
161 )
160 dbinfo = system_info.SysInfo(system_info.database_info)()['value']
162 dbinfo = system_info.SysInfo(system_info.database_info)()['value']
161 del dbinfo['url']
163 del dbinfo['url']
162
164
163 metadata = dict(
165 metadata = dict(
164 desc='upgrade metadata info',
166 desc='upgrade metadata info',
165 license_token=license_token,
167 license_token=license_token,
166 created_on=datetime.datetime.utcnow().isoformat(),
168 created_on=datetime.datetime.utcnow().isoformat(),
167 usage=system_info.SysInfo(system_info.usage_info)()['value'],
169 usage=system_info.SysInfo(system_info.usage_info)()['value'],
168 platform=system_info.SysInfo(system_info.platform_type)()['value'],
170 platform=system_info.SysInfo(system_info.platform_type)()['value'],
169 database=dbinfo,
171 database=dbinfo,
170 cpu=system_info.SysInfo(system_info.cpu)()['value'],
172 cpu=system_info.SysInfo(system_info.cpu)()['value'],
171 memory=system_info.SysInfo(system_info.memory)()['value'],
173 memory=system_info.SysInfo(system_info.memory)()['value'],
172 setup=setup
174 setup=setup
173 )
175 )
174
176
175 with open(metadata_destination, 'wb') as f:
177 with open(metadata_destination, 'wb') as f:
176 f.write(ext_json.json.dumps(metadata))
178 f.write(ext_json.json.dumps(metadata))
177
179
178 settings = event.app.registry.settings
180 settings = event.app.registry.settings
179 if settings.get('metadata.skip'):
181 if settings.get('metadata.skip'):
180 return
182 return
181
183
182 # only write this every 24h, workers restart caused unwanted delays
184 # only write this every 24h, workers restart caused unwanted delays
183 try:
185 try:
184 age_in_min = get_update_age()
186 age_in_min = get_update_age()
185 except Exception:
187 except Exception:
186 age_in_min = 0
188 age_in_min = 0
187
189
188 if age_in_min > 60 * 60 * 24:
190 if age_in_min > 60 * 60 * 24:
189 return
191 return
190
192
191 try:
193 try:
192 write()
194 write()
193 except Exception:
195 except Exception:
194 pass
196 pass
195
197
196
198
197 def write_usage_data(event):
199 def write_usage_data(event):
198 import rhodecode
200 import rhodecode
199 from rhodecode.lib import system_info
201 from rhodecode.lib import system_info
200 from rhodecode.lib import ext_json
202 from rhodecode.lib import ext_json
201
203
202 settings = event.app.registry.settings
204 settings = event.app.registry.settings
203 instance_tag = settings.get('metadata.write_usage_tag')
205 instance_tag = settings.get('metadata.write_usage_tag')
204 if not settings.get('metadata.write_usage'):
206 if not settings.get('metadata.write_usage'):
205 return
207 return
206
208
207 def get_update_age(dest_file):
209 def get_update_age(dest_file):
208 now = datetime.datetime.now(datetime.UTC)
210 now = datetime.datetime.now(datetime.UTC)
209
211
210 with open(dest_file, 'rb') as f:
212 with open(dest_file, 'rb') as f:
211 data = ext_json.json.loads(f.read())
213 data = ext_json.json.loads(f.read())
212 if 'created_on' in data:
214 if 'created_on' in data:
213 update_date = parse(data['created_on'])
215 update_date = parse(data['created_on'])
214 diff = now - update_date
216 diff = now - update_date
215 return math.ceil(diff.total_seconds() / 60.0)
217 return math.ceil(diff.total_seconds() / 60.0)
216
218
217 return 0
219 return 0
218
220
219 utc_date = datetime.datetime.now(datetime.UTC)
221 utc_date = datetime.datetime.now(datetime.UTC)
220 hour_quarter = int(math.ceil((utc_date.hour + utc_date.minute/60.0) / 6.))
222 hour_quarter = int(math.ceil((utc_date.hour + utc_date.minute/60.0) / 6.))
221 fname = f'.rc_usage_{utc_date.year}{utc_date.month:02d}{utc_date.day:02d}_{hour_quarter}.json'
223 fname = f'.rc_usage_{utc_date.year}{utc_date.month:02d}{utc_date.day:02d}_{hour_quarter}.json'
222 ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__'))
224 ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__'))
223
225
224 usage_dir = os.path.join(ini_loc, '.rcusage')
226 usage_dir = os.path.join(ini_loc, '.rcusage')
225 if not os.path.isdir(usage_dir):
227 if not os.path.isdir(usage_dir):
226 os.makedirs(usage_dir)
228 os.makedirs(usage_dir)
227 usage_metadata_destination = os.path.join(usage_dir, fname)
229 usage_metadata_destination = os.path.join(usage_dir, fname)
228
230
229 try:
231 try:
230 age_in_min = get_update_age(usage_metadata_destination)
232 age_in_min = get_update_age(usage_metadata_destination)
231 except Exception:
233 except Exception:
232 age_in_min = 0
234 age_in_min = 0
233
235
234 # write every 6th hour
236 # write every 6th hour
235 if age_in_min and age_in_min < 60 * 6:
237 if age_in_min and age_in_min < 60 * 6:
236 log.debug('Usage file created %s minutes ago, skipping (threshold: %s minutes)...',
238 log.debug('Usage file created %s minutes ago, skipping (threshold: %s minutes)...',
237 age_in_min, 60 * 6)
239 age_in_min, 60 * 6)
238 return
240 return
239
241
240 def write(dest_file):
242 def write(dest_file):
241 configuration = system_info.SysInfo(system_info.rhodecode_config)()['value']
243 configuration = system_info.SysInfo(system_info.rhodecode_config)()['value']
242 license_token = configuration['config']['license_token']
244 license_token = configuration['config']['license_token']
243
245
244 metadata = dict(
246 metadata = dict(
245 desc='Usage data',
247 desc='Usage data',
246 instance_tag=instance_tag,
248 instance_tag=instance_tag,
247 license_token=license_token,
249 license_token=license_token,
248 created_on=datetime.datetime.utcnow().isoformat(),
250 created_on=datetime.datetime.utcnow().isoformat(),
249 usage=system_info.SysInfo(system_info.usage_info)()['value'],
251 usage=system_info.SysInfo(system_info.usage_info)()['value'],
250 )
252 )
251
253
252 with open(dest_file, 'wb') as f:
254 with open(dest_file, 'wb') as f:
253 f.write(ext_json.formatted_json(metadata))
255 f.write(ext_json.formatted_json(metadata))
254
256
255 try:
257 try:
256 log.debug('Writing usage file at: %s', usage_metadata_destination)
258 log.debug('Writing usage file at: %s', usage_metadata_destination)
257 write(usage_metadata_destination)
259 write(usage_metadata_destination)
258 except Exception:
260 except Exception:
259 pass
261 pass
260
262
261
263
262 def write_js_routes_if_enabled(event):
264 def write_js_routes_if_enabled(event):
263 registry = event.app.registry
265 registry = event.app.registry
264
266
265 mapper = registry.queryUtility(IRoutesMapper)
267 mapper = registry.queryUtility(IRoutesMapper)
266 _argument_prog = re.compile(r'\{(.*?)\}|:\((.*)\)')
268 _argument_prog = re.compile(r'\{(.*?)\}|:\((.*)\)')
267
269
268 def _extract_route_information(route):
270 def _extract_route_information(route):
269 """
271 """
270 Convert a route into tuple(name, path, args), eg:
272 Convert a route into tuple(name, path, args), eg:
271 ('show_user', '/profile/%(username)s', ['username'])
273 ('show_user', '/profile/%(username)s', ['username'])
272 """
274 """
273
275
274 route_path = route.pattern
276 route_path = route.pattern
275 pattern = route.pattern
277 pattern = route.pattern
276
278
277 def replace(matchobj):
279 def replace(matchobj):
278 if matchobj.group(1):
280 if matchobj.group(1):
279 return "%%(%s)s" % matchobj.group(1).split(':')[0]
281 return "%%(%s)s" % matchobj.group(1).split(':')[0]
280 else:
282 else:
281 return "%%(%s)s" % matchobj.group(2)
283 return "%%(%s)s" % matchobj.group(2)
282
284
283 route_path = _argument_prog.sub(replace, route_path)
285 route_path = _argument_prog.sub(replace, route_path)
284
286
285 if not route_path.startswith('/'):
287 if not route_path.startswith('/'):
286 route_path = f'/{route_path}'
288 route_path = f'/{route_path}'
287
289
288 return (
290 return (
289 route.name,
291 route.name,
290 route_path,
292 route_path,
291 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
293 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
292 for arg in _argument_prog.findall(pattern)]
294 for arg in _argument_prog.findall(pattern)]
293 )
295 )
294
296
295 def get_routes():
297 def get_routes():
296 # pyramid routes
298 # pyramid routes
297 for route in mapper.get_routes():
299 for route in mapper.get_routes():
298 if not route.name.startswith('__'):
300 if not route.name.startswith('__'):
299 yield _extract_route_information(route)
301 yield _extract_route_information(route)
300
302
301 if asbool(registry.settings.get('generate_js_files', 'false')):
303 if asbool(registry.settings.get('generate_js_files', 'false')):
302 static_path = AssetResolver().resolve('rhodecode:public').abspath()
304 static_path = AssetResolver().resolve('rhodecode:public').abspath()
303 jsroutes = get_routes()
305 jsroutes = get_routes()
304 jsroutes_file_content = generate_jsroutes_content(jsroutes)
306 jsroutes_file_content = generate_jsroutes_content(jsroutes)
305 jsroutes_file_path = os.path.join(
307 jsroutes_file_path = os.path.join(
306 static_path, 'js', 'rhodecode', 'routes.js')
308 static_path, 'js', 'rhodecode', 'routes.js')
307
309
308 try:
310 try:
309 with open(jsroutes_file_path, 'w', encoding='utf-8') as f:
311 with open(jsroutes_file_path, 'w', encoding='utf-8') as f:
310 f.write(jsroutes_file_content)
312 f.write(jsroutes_file_content)
311 log.debug('generated JS files in %s', jsroutes_file_path)
313 log.debug('generated JS files in %s', jsroutes_file_path)
312 except Exception:
314 except Exception:
313 log.exception('Failed to write routes.js into %s', jsroutes_file_path)
315 log.exception('Failed to write routes.js into %s', jsroutes_file_path)
314
316
315
317
316 def import_license_if_present(event):
318 def import_license_if_present(event):
317 """
319 """
318 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
320 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
319 does a import license key based on a presence of the file.
321 does a import license key based on a presence of the file.
320 """
322 """
321 settings = event.app.registry.settings
323 settings = event.app.registry.settings
322
324
325 rhodecode_edition_id = settings.get('rhodecode.edition_id')
323 license_file_path = settings.get('license.import_path')
326 license_file_path = settings.get('license.import_path')
324 force = settings.get('license.import_path_mode') == 'force'
327 force = settings.get('license.import_path_mode') == 'force'
325 if license_file_path:
328
329 if license_file_path and rhodecode_edition_id == 'EE':
330 log.debug('license.import_path= is set importing license from %s', license_file_path)
326 from rhodecode.model.meta import Session
331 from rhodecode.model.meta import Session
327 from rhodecode.model.license import apply_license_from_file
332 from rhodecode.model.license import apply_license_from_file
333 try:
328 apply_license_from_file(license_file_path, force=force)
334 apply_license_from_file(license_file_path, force=force)
329 Session().commit()
335 Session().commit()
336 except OSError:
337 log.exception('Failed to import license from %s, make sure this file exists', license_file_path)
330
338
331
339
332 class Subscriber(object):
340 class Subscriber(object):
333 """
341 """
334 Base class for subscribers to the pyramid event system.
342 Base class for subscribers to the pyramid event system.
335 """
343 """
336 def __call__(self, event):
344 def __call__(self, event):
337 self.run(event)
345 self.run(event)
338
346
339 def run(self, event):
347 def run(self, event):
340 raise NotImplementedError('Subclass has to implement this.')
348 raise NotImplementedError('Subclass has to implement this.')
341
349
342
350
343 class AsyncSubscriber(Subscriber):
351 class AsyncSubscriber(Subscriber):
344 """
352 """
345 Subscriber that handles the execution of events in a separate task to not
353 Subscriber that handles the execution of events in a separate task to not
346 block the execution of the code which triggers the event. It puts the
354 block the execution of the code which triggers the event. It puts the
347 received events into a queue from which the worker process takes them in
355 received events into a queue from which the worker process takes them in
348 order.
356 order.
349 """
357 """
350 def __init__(self):
358 def __init__(self):
351 self._stop = False
359 self._stop = False
352 self._eventq = queue.Queue()
360 self._eventq = queue.Queue()
353 self._worker = self.create_worker()
361 self._worker = self.create_worker()
354 self._worker.start()
362 self._worker.start()
355
363
356 def __call__(self, event):
364 def __call__(self, event):
357 self._eventq.put(event)
365 self._eventq.put(event)
358
366
359 def create_worker(self):
367 def create_worker(self):
360 worker = Thread(target=self.do_work)
368 worker = Thread(target=self.do_work)
361 worker.daemon = True
369 worker.daemon = True
362 return worker
370 return worker
363
371
364 def stop_worker(self):
372 def stop_worker(self):
365 self._stop = False
373 self._stop = False
366 self._eventq.put(None)
374 self._eventq.put(None)
367 self._worker.join()
375 self._worker.join()
368
376
369 def do_work(self):
377 def do_work(self):
370 while not self._stop:
378 while not self._stop:
371 event = self._eventq.get()
379 event = self._eventq.get()
372 if event is not None:
380 if event is not None:
373 self.run(event)
381 self.run(event)
374
382
375
383
376 class AsyncSubprocessSubscriber(AsyncSubscriber):
384 class AsyncSubprocessSubscriber(AsyncSubscriber):
377 """
385 """
378 Subscriber that uses the subprocess module to execute a command if an
386 Subscriber that uses the subprocess module to execute a command if an
379 event is received. Events are handled asynchronously::
387 event is received. Events are handled asynchronously::
380
388
381 subscriber = AsyncSubprocessSubscriber('ls -la', timeout=10)
389 subscriber = AsyncSubprocessSubscriber('ls -la', timeout=10)
382 subscriber(dummyEvent) # running __call__(event)
390 subscriber(dummyEvent) # running __call__(event)
383
391
384 """
392 """
385
393
386 def __init__(self, cmd, timeout=None):
394 def __init__(self, cmd, timeout=None):
387 if not isinstance(cmd, (list, tuple)):
395 if not isinstance(cmd, (list, tuple)):
388 cmd = shlex.split(cmd)
396 cmd = shlex.split(cmd)
389 super().__init__()
397 super().__init__()
390 self._cmd = cmd
398 self._cmd = cmd
391 self._timeout = timeout
399 self._timeout = timeout
392
400
393 def run(self, event):
401 def run(self, event):
394 cmd = self._cmd
402 cmd = self._cmd
395 timeout = self._timeout
403 timeout = self._timeout
396 log.debug('Executing command %s.', cmd)
404 log.debug('Executing command %s.', cmd)
397
405
398 try:
406 try:
399 output = subprocess.check_output(
407 output = subprocess.check_output(
400 cmd, timeout=timeout, stderr=subprocess.STDOUT)
408 cmd, timeout=timeout, stderr=subprocess.STDOUT)
401 log.debug('Command finished %s', cmd)
409 log.debug('Command finished %s', cmd)
402 if output:
410 if output:
403 log.debug('Command output: %s', output)
411 log.debug('Command output: %s', output)
404 except subprocess.TimeoutExpired as e:
412 except subprocess.TimeoutExpired as e:
405 log.exception('Timeout while executing command.')
413 log.exception('Timeout while executing command.')
406 if e.output:
414 if e.output:
407 log.error('Command output: %s', e.output)
415 log.error('Command output: %s', e.output)
408 except subprocess.CalledProcessError as e:
416 except subprocess.CalledProcessError as e:
409 log.exception('Error while executing command.')
417 log.exception('Error while executing command.')
410 if e.output:
418 if e.output:
411 log.error('Command output: %s', e.output)
419 log.error('Command output: %s', e.output)
412 except Exception:
420 except Exception:
413 log.exception(
421 log.exception(
414 'Exception while executing command %s.', cmd)
422 'Exception while executing command %s.', cmd)
General Comments 0
You need to be logged in to leave comments. Login now