##// END OF EJS Templates
system-info: expose data about vcsserver.
dan -
r3943:2703bca5 default
parent child Browse files
Show More
@@ -1,202 +1,206 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import urllib2
23 23
24 24 from pyramid.view import view_config
25 25
26 26 import rhodecode
27 27 from rhodecode.apps._base import BaseAppView
28 28 from rhodecode.apps._base.navigation import navigation_list
29 29 from rhodecode.lib import helpers as h
30 30 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
31 31 from rhodecode.lib.utils2 import str2bool
32 32 from rhodecode.lib import system_info
33 33 from rhodecode.model.update import UpdateModel
34 34
35 35 log = logging.getLogger(__name__)
36 36
37 37
38 38 class AdminSystemInfoSettingsView(BaseAppView):
39 39 def load_default_context(self):
40 40 c = self._get_local_tmpl_context()
41 41 return c
42 42
43 43 @LoginRequired()
44 44 @HasPermissionAllDecorator('hg.admin')
45 45 @view_config(
46 46 route_name='admin_settings_system', request_method='GET',
47 47 renderer='rhodecode:templates/admin/settings/settings.mako')
48 48 def settings_system_info(self):
49 49 _ = self.request.translate
50 50 c = self.load_default_context()
51 51
52 52 c.active = 'system'
53 53 c.navlist = navigation_list(self.request)
54 54
55 55 # TODO(marcink), figure out how to allow only selected users to do this
56 56 c.allowed_to_snapshot = self._rhodecode_user.admin
57 57
58 58 snapshot = str2bool(self.request.params.get('snapshot'))
59 59
60 60 c.rhodecode_update_url = UpdateModel().get_update_url()
61 61 server_info = system_info.get_system_info(self.request.environ)
62 62
63 63 for key, val in server_info.items():
64 64 setattr(c, key, val)
65 65
66 66 def val(name, subkey='human_value'):
67 67 return server_info[name][subkey]
68 68
69 69 def state(name):
70 70 return server_info[name]['state']
71 71
72 72 def val2(name):
73 73 val = server_info[name]['human_value']
74 74 state = server_info[name]['state']
75 75 return val, state
76 76
77 77 update_info_msg = _('Note: please make sure this server can '
78 78 'access `${url}` for the update link to work',
79 79 mapping=dict(url=c.rhodecode_update_url))
80 80 version = UpdateModel().get_stored_version()
81 81 is_outdated = UpdateModel().is_outdated(
82 82 rhodecode.__version__, version)
83 83 update_state = {
84 84 'type': 'warning',
85 85 'message': 'New version available: {}'.format(version)
86 86 } \
87 87 if is_outdated else {}
88 88 c.data_items = [
89 89 # update info
90 90 (_('Update info'), h.literal(
91 91 '<span class="link" id="check_for_update" >%s.</span>' % (
92 92 _('Check for updates')) +
93 93 '<br/> <span >%s.</span>' % (update_info_msg)
94 94 ), ''),
95 95
96 96 # RhodeCode specific
97 97 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
98 98 (_('Latest version'), version, update_state),
99 99 (_('RhodeCode Base URL'), val('rhodecode_config')['config'].get('app.base_url'), state('rhodecode_config')),
100 100 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
101 101 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
102 102 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
103 103 (_('RhodeCode Certificate'), val('rhodecode_config')['cert_path'], state('rhodecode_config')),
104 104 (_('Workers'), val('rhodecode_config')['config']['server:main'].get('workers', '?'), state('rhodecode_config')),
105 105 (_('Worker Type'), val('rhodecode_config')['config']['server:main'].get('worker_class', 'sync'), state('rhodecode_config')),
106 106 ('', '', ''), # spacer
107 107
108 108 # Database
109 109 (_('Database'), val('database')['url'], state('database')),
110 110 (_('Database version'), val('database')['version'], state('database')),
111 111 ('', '', ''), # spacer
112 112
113 113 # Platform/Python
114 114 (_('Platform'), val('platform')['name'], state('platform')),
115 115 (_('Platform UUID'), val('platform')['uuid'], state('platform')),
116 116 (_('Lang'), val('locale'), state('locale')),
117 117 (_('Python version'), val('python')['version'], state('python')),
118 118 (_('Python path'), val('python')['executable'], state('python')),
119 119 ('', '', ''), # spacer
120 120
121 121 # Systems stats
122 122 (_('CPU'), val('cpu')['text'], state('cpu')),
123 123 (_('Load'), val('load')['text'], state('load')),
124 124 (_('Memory'), val('memory')['text'], state('memory')),
125 125 (_('Uptime'), val('uptime')['text'], state('uptime')),
126 126 ('', '', ''), # spacer
127 127
128 128 # ulimit
129 129 (_('Ulimit'), val('ulimit')['text'], state('ulimit')),
130 130
131 131 # Repo storage
132 132 (_('Storage location'), val('storage')['path'], state('storage')),
133 133 (_('Storage info'), val('storage')['text'], state('storage')),
134 134 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
135 135
136 136 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
137 137 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
138 138
139 139 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
140 140 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
141 141
142 142 (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')),
143 143 (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')),
144 144
145 145 (_('Search info'), val('search')['text'], state('search')),
146 146 (_('Search location'), val('search')['location'], state('search')),
147 147 ('', '', ''), # spacer
148 148
149 149 # VCS specific
150 150 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
151 151 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
152 152 (_('GIT'), val('git'), state('git')),
153 153 (_('HG'), val('hg'), state('hg')),
154 154 (_('SVN'), val('svn'), state('svn')),
155 155
156 156 ]
157 157
158 c.vcsserver_data_items = [
159 (k, v) for k,v in (val('vcs_server_config') or {}).items()
160 ]
161
158 162 if snapshot:
159 163 if c.allowed_to_snapshot:
160 164 c.data_items.pop(0) # remove server info
161 165 self.request.override_renderer = 'admin/settings/settings_system_snapshot.mako'
162 166 else:
163 167 h.flash('You are not allowed to do this', category='warning')
164 168 return self._get_template_context(c)
165 169
166 170 @LoginRequired()
167 171 @HasPermissionAllDecorator('hg.admin')
168 172 @view_config(
169 173 route_name='admin_settings_system_update', request_method='GET',
170 174 renderer='rhodecode:templates/admin/settings/settings_system_update.mako')
171 175 def settings_system_info_check_update(self):
172 176 _ = self.request.translate
173 177 c = self.load_default_context()
174 178
175 179 update_url = UpdateModel().get_update_url()
176 180
177 181 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s)
178 182 try:
179 183 data = UpdateModel().get_update_data(update_url)
180 184 except urllib2.URLError as e:
181 185 log.exception("Exception contacting upgrade server")
182 186 self.request.override_renderer = 'string'
183 187 return _err('Failed to contact upgrade server: %r' % e)
184 188 except ValueError as e:
185 189 log.exception("Bad data sent from update server")
186 190 self.request.override_renderer = 'string'
187 191 return _err('Bad data sent from update server')
188 192
189 193 latest = data['versions'][0]
190 194
191 195 c.update_url = update_url
192 196 c.latest_data = latest
193 197 c.latest_ver = latest['version']
194 198 c.cur_ver = rhodecode.__version__
195 199 c.should_upgrade = False
196 200
197 201 is_oudated = UpdateModel().is_outdated(c.cur_ver, c.latest_ver)
198 202 if is_oudated:
199 203 c.should_upgrade = True
200 204 c.important_notices = latest['general']
201 205 UpdateModel().store_version(latest['version'])
202 206 return self._get_template_context(c)
@@ -1,777 +1,796 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2017-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import os
23 23 import sys
24 24 import time
25 25 import platform
26 26 import collections
27 27 import pkg_resources
28 28 import logging
29 29 import resource
30 30
31 31 from pyramid.compat import configparser
32 32
33 33 log = logging.getLogger(__name__)
34 34
35 35
36 36 psutil = None
37 37
38 38 try:
39 39 # cygwin cannot have yet psutil support.
40 40 import psutil as psutil
41 41 except ImportError:
42 42 pass
43 43
44 44
45 45 _NA = 'NOT AVAILABLE'
46 46
47 47 STATE_OK = 'ok'
48 48 STATE_ERR = 'error'
49 49 STATE_WARN = 'warning'
50 50
51 51 STATE_OK_DEFAULT = {'message': '', 'type': STATE_OK}
52 52
53 53
54 54 # HELPERS
55 55 def percentage(part, whole):
56 56 whole = float(whole)
57 57 if whole > 0:
58 58 return round(100 * float(part) / whole, 1)
59 59 return 0.0
60 60
61 61
62 62 def get_storage_size(storage_path):
63 63 sizes = []
64 64 for file_ in os.listdir(storage_path):
65 65 storage_file = os.path.join(storage_path, file_)
66 66 if os.path.isfile(storage_file):
67 67 try:
68 68 sizes.append(os.path.getsize(storage_file))
69 69 except OSError:
70 70 log.exception('Failed to get size of storage file %s', storage_file)
71 71 pass
72 72
73 73 return sum(sizes)
74 74
75 75
76 76 def get_resource(resource_type):
77 77 try:
78 78 return resource.getrlimit(resource_type)
79 79 except Exception:
80 80 return 'NOT_SUPPORTED'
81 81
82 82
83 83 def get_cert_path(ini_path):
84 84 default = '/etc/ssl/certs/ca-certificates.crt'
85 85 control_ca_bundle = os.path.join(
86 86 os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(ini_path)))),
87 87 '.rccontrol-profile/etc/ca-bundle.crt')
88 88 if os.path.isfile(control_ca_bundle):
89 89 default = control_ca_bundle
90 90
91 91 return default
92 92
93 93
94 94 class SysInfoRes(object):
95 95 def __init__(self, value, state=None, human_value=None):
96 96 self.value = value
97 97 self.state = state or STATE_OK_DEFAULT
98 98 self.human_value = human_value or value
99 99
100 100 def __json__(self):
101 101 return {
102 102 'value': self.value,
103 103 'state': self.state,
104 104 'human_value': self.human_value,
105 105 }
106 106
107 107 def get_value(self):
108 108 return self.__json__()
109 109
110 110 def __str__(self):
111 111 return '<SysInfoRes({})>'.format(self.__json__())
112 112
113 113
114 114 class SysInfo(object):
115 115
116 116 def __init__(self, func_name, **kwargs):
117 117 self.func_name = func_name
118 118 self.value = _NA
119 119 self.state = None
120 120 self.kwargs = kwargs or {}
121 121
122 122 def __call__(self):
123 123 computed = self.compute(**self.kwargs)
124 124 if not isinstance(computed, SysInfoRes):
125 125 raise ValueError(
126 126 'computed value for {} is not instance of '
127 127 '{}, got {} instead'.format(
128 128 self.func_name, SysInfoRes, type(computed)))
129 129 return computed.__json__()
130 130
131 131 def __str__(self):
132 132 return '<SysInfo({})>'.format(self.func_name)
133 133
134 134 def compute(self, **kwargs):
135 135 return self.func_name(**kwargs)
136 136
137 137
138 138 # SysInfo functions
139 139 def python_info():
140 140 value = dict(version=' '.join(platform._sys_version()),
141 141 executable=sys.executable)
142 142 return SysInfoRes(value=value)
143 143
144 144
145 145 def py_modules():
146 146 mods = dict([(p.project_name, p.version)
147 147 for p in pkg_resources.working_set])
148 148 value = sorted(mods.items(), key=lambda k: k[0].lower())
149 149 return SysInfoRes(value=value)
150 150
151 151
152 152 def platform_type():
153 153 from rhodecode.lib.utils import safe_unicode, generate_platform_uuid
154 154
155 155 value = dict(
156 156 name=safe_unicode(platform.platform()),
157 157 uuid=generate_platform_uuid()
158 158 )
159 159 return SysInfoRes(value=value)
160 160
161 161
162 162 def locale_info():
163 163 import locale
164 164
165 165 value = dict(
166 166 locale_default=locale.getdefaultlocale(),
167 167 locale_lc_all=locale.getlocale(locale.LC_ALL),
168 168 lang_env=os.environ.get('LANG'),
169 169 lc_all_env=os.environ.get('LC_ALL'),
170 170 local_archive_env=os.environ.get('LOCALE_ARCHIVE'),
171 171 )
172 172 human_value = 'LANG: {}, locale LC_ALL: {}, Default locales: {}'.format(
173 173 value['lang_env'], value['locale_lc_all'], value['locale_default'])
174 174 return SysInfoRes(value=value, human_value=human_value)
175 175
176 176
177 177 def ulimit_info():
178 178 data = collections.OrderedDict([
179 179 ('cpu time (seconds)', get_resource(resource.RLIMIT_CPU)),
180 180 ('file size', get_resource(resource.RLIMIT_FSIZE)),
181 181 ('stack size', get_resource(resource.RLIMIT_STACK)),
182 182 ('core file size', get_resource(resource.RLIMIT_CORE)),
183 183 ('address space size', get_resource(resource.RLIMIT_AS)),
184 184 ('locked in mem size', get_resource(resource.RLIMIT_MEMLOCK)),
185 185 ('heap size', get_resource(resource.RLIMIT_DATA)),
186 186 ('rss size', get_resource(resource.RLIMIT_RSS)),
187 187 ('number of processes', get_resource(resource.RLIMIT_NPROC)),
188 188 ('open files', get_resource(resource.RLIMIT_NOFILE)),
189 189 ])
190 190
191 191 text = ', '.join('{}:{}'.format(k, v) for k, v in data.items())
192 192
193 193 value = {
194 194 'limits': data,
195 195 'text': text,
196 196 }
197 197 return SysInfoRes(value=value)
198 198
199 199
200 200 def uptime():
201 201 from rhodecode.lib.helpers import age, time_to_datetime
202 202 from rhodecode.translation import TranslationString
203 203
204 204 value = dict(boot_time=0, uptime=0, text='')
205 205 state = STATE_OK_DEFAULT
206 206 if not psutil:
207 207 return SysInfoRes(value=value, state=state)
208 208
209 209 boot_time = psutil.boot_time()
210 210 value['boot_time'] = boot_time
211 211 value['uptime'] = time.time() - boot_time
212 212
213 213 date_or_age = age(time_to_datetime(boot_time))
214 214 if isinstance(date_or_age, TranslationString):
215 215 date_or_age = date_or_age.interpolate()
216 216
217 217 human_value = value.copy()
218 218 human_value['boot_time'] = time_to_datetime(boot_time)
219 219 human_value['uptime'] = age(time_to_datetime(boot_time), show_suffix=False)
220 220
221 221 human_value['text'] = u'Server started {}'.format(date_or_age)
222 222 return SysInfoRes(value=value, human_value=human_value)
223 223
224 224
225 225 def memory():
226 226 from rhodecode.lib.helpers import format_byte_size_binary
227 227 value = dict(available=0, used=0, used_real=0, cached=0, percent=0,
228 228 percent_used=0, free=0, inactive=0, active=0, shared=0,
229 229 total=0, buffers=0, text='')
230 230
231 231 state = STATE_OK_DEFAULT
232 232 if not psutil:
233 233 return SysInfoRes(value=value, state=state)
234 234
235 235 value.update(dict(psutil.virtual_memory()._asdict()))
236 236 value['used_real'] = value['total'] - value['available']
237 237 value['percent_used'] = psutil._common.usage_percent(
238 238 value['used_real'], value['total'], 1)
239 239
240 240 human_value = value.copy()
241 241 human_value['text'] = '%s/%s, %s%% used' % (
242 242 format_byte_size_binary(value['used_real']),
243 243 format_byte_size_binary(value['total']),
244 244 value['percent_used'],)
245 245
246 246 keys = value.keys()[::]
247 247 keys.pop(keys.index('percent'))
248 248 keys.pop(keys.index('percent_used'))
249 249 keys.pop(keys.index('text'))
250 250 for k in keys:
251 251 human_value[k] = format_byte_size_binary(value[k])
252 252
253 253 if state['type'] == STATE_OK and value['percent_used'] > 90:
254 254 msg = 'Critical: your available RAM memory is very low.'
255 255 state = {'message': msg, 'type': STATE_ERR}
256 256
257 257 elif state['type'] == STATE_OK and value['percent_used'] > 70:
258 258 msg = 'Warning: your available RAM memory is running low.'
259 259 state = {'message': msg, 'type': STATE_WARN}
260 260
261 261 return SysInfoRes(value=value, state=state, human_value=human_value)
262 262
263 263
264 264 def machine_load():
265 265 value = {'1_min': _NA, '5_min': _NA, '15_min': _NA, 'text': ''}
266 266 state = STATE_OK_DEFAULT
267 267 if not psutil:
268 268 return SysInfoRes(value=value, state=state)
269 269
270 270 # load averages
271 271 if hasattr(psutil.os, 'getloadavg'):
272 272 value.update(dict(
273 273 zip(['1_min', '5_min', '15_min'], psutil.os.getloadavg())))
274 274
275 275 human_value = value.copy()
276 276 human_value['text'] = '1min: {}, 5min: {}, 15min: {}'.format(
277 277 value['1_min'], value['5_min'], value['15_min'])
278 278
279 279 if state['type'] == STATE_OK and value['15_min'] > 5:
280 280 msg = 'Warning: your machine load is very high.'
281 281 state = {'message': msg, 'type': STATE_WARN}
282 282
283 283 return SysInfoRes(value=value, state=state, human_value=human_value)
284 284
285 285
286 286 def cpu():
287 287 value = {'cpu': 0, 'cpu_count': 0, 'cpu_usage': []}
288 288 state = STATE_OK_DEFAULT
289 289
290 290 if not psutil:
291 291 return SysInfoRes(value=value, state=state)
292 292
293 293 value['cpu'] = psutil.cpu_percent(0.5)
294 294 value['cpu_usage'] = psutil.cpu_percent(0.5, percpu=True)
295 295 value['cpu_count'] = psutil.cpu_count()
296 296
297 297 human_value = value.copy()
298 298 human_value['text'] = '{} cores at {} %'.format(
299 299 value['cpu_count'], value['cpu'])
300 300
301 301 return SysInfoRes(value=value, state=state, human_value=human_value)
302 302
303 303
304 304 def storage():
305 305 from rhodecode.lib.helpers import format_byte_size_binary
306 306 from rhodecode.model.settings import VcsSettingsModel
307 307 path = VcsSettingsModel().get_repos_location()
308 308
309 309 value = dict(percent=0, used=0, total=0, path=path, text='')
310 310 state = STATE_OK_DEFAULT
311 311 if not psutil:
312 312 return SysInfoRes(value=value, state=state)
313 313
314 314 try:
315 315 value.update(dict(psutil.disk_usage(path)._asdict()))
316 316 except Exception as e:
317 317 log.exception('Failed to fetch disk info')
318 318 state = {'message': str(e), 'type': STATE_ERR}
319 319
320 320 human_value = value.copy()
321 321 human_value['used'] = format_byte_size_binary(value['used'])
322 322 human_value['total'] = format_byte_size_binary(value['total'])
323 323 human_value['text'] = "{}/{}, {}% used".format(
324 324 format_byte_size_binary(value['used']),
325 325 format_byte_size_binary(value['total']),
326 326 value['percent'])
327 327
328 328 if state['type'] == STATE_OK and value['percent'] > 90:
329 329 msg = 'Critical: your disk space is very low.'
330 330 state = {'message': msg, 'type': STATE_ERR}
331 331
332 332 elif state['type'] == STATE_OK and value['percent'] > 70:
333 333 msg = 'Warning: your disk space is running low.'
334 334 state = {'message': msg, 'type': STATE_WARN}
335 335
336 336 return SysInfoRes(value=value, state=state, human_value=human_value)
337 337
338 338
339 339 def storage_inodes():
340 340 from rhodecode.model.settings import VcsSettingsModel
341 341 path = VcsSettingsModel().get_repos_location()
342 342
343 343 value = dict(percent=0, free=0, used=0, total=0, path=path, text='')
344 344 state = STATE_OK_DEFAULT
345 345 if not psutil:
346 346 return SysInfoRes(value=value, state=state)
347 347
348 348 try:
349 349 i_stat = os.statvfs(path)
350 350 value['free'] = i_stat.f_ffree
351 351 value['used'] = i_stat.f_files-i_stat.f_favail
352 352 value['total'] = i_stat.f_files
353 353 value['percent'] = percentage(value['used'], value['total'])
354 354 except Exception as e:
355 355 log.exception('Failed to fetch disk inodes info')
356 356 state = {'message': str(e), 'type': STATE_ERR}
357 357
358 358 human_value = value.copy()
359 359 human_value['text'] = "{}/{}, {}% used".format(
360 360 value['used'], value['total'], value['percent'])
361 361
362 362 if state['type'] == STATE_OK and value['percent'] > 90:
363 363 msg = 'Critical: your disk free inodes are very low.'
364 364 state = {'message': msg, 'type': STATE_ERR}
365 365
366 366 elif state['type'] == STATE_OK and value['percent'] > 70:
367 367 msg = 'Warning: your disk free inodes are running low.'
368 368 state = {'message': msg, 'type': STATE_WARN}
369 369
370 370 return SysInfoRes(value=value, state=state, human_value=human_value)
371 371
372 372
373 373 def storage_archives():
374 374 import rhodecode
375 375 from rhodecode.lib.utils import safe_str
376 376 from rhodecode.lib.helpers import format_byte_size_binary
377 377
378 378 msg = 'Enable this by setting ' \
379 379 'archive_cache_dir=/path/to/cache option in the .ini file'
380 380 path = safe_str(rhodecode.CONFIG.get('archive_cache_dir', msg))
381 381
382 382 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
383 383 state = STATE_OK_DEFAULT
384 384 try:
385 385 items_count = 0
386 386 used = 0
387 387 for root, dirs, files in os.walk(path):
388 388 if root == path:
389 389 items_count = len(files)
390 390
391 391 for f in files:
392 392 try:
393 393 used += os.path.getsize(os.path.join(root, f))
394 394 except OSError:
395 395 pass
396 396 value.update({
397 397 'percent': 100,
398 398 'used': used,
399 399 'total': used,
400 400 'items': items_count
401 401 })
402 402
403 403 except Exception as e:
404 404 log.exception('failed to fetch archive cache storage')
405 405 state = {'message': str(e), 'type': STATE_ERR}
406 406
407 407 human_value = value.copy()
408 408 human_value['used'] = format_byte_size_binary(value['used'])
409 409 human_value['total'] = format_byte_size_binary(value['total'])
410 410 human_value['text'] = "{} ({} items)".format(
411 411 human_value['used'], value['items'])
412 412
413 413 return SysInfoRes(value=value, state=state, human_value=human_value)
414 414
415 415
416 416 def storage_gist():
417 417 from rhodecode.model.gist import GIST_STORE_LOC
418 418 from rhodecode.model.settings import VcsSettingsModel
419 419 from rhodecode.lib.utils import safe_str
420 420 from rhodecode.lib.helpers import format_byte_size_binary
421 421 path = safe_str(os.path.join(
422 422 VcsSettingsModel().get_repos_location(), GIST_STORE_LOC))
423 423
424 424 # gist storage
425 425 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
426 426 state = STATE_OK_DEFAULT
427 427
428 428 try:
429 429 items_count = 0
430 430 used = 0
431 431 for root, dirs, files in os.walk(path):
432 432 if root == path:
433 433 items_count = len(dirs)
434 434
435 435 for f in files:
436 436 try:
437 437 used += os.path.getsize(os.path.join(root, f))
438 438 except OSError:
439 439 pass
440 440 value.update({
441 441 'percent': 100,
442 442 'used': used,
443 443 'total': used,
444 444 'items': items_count
445 445 })
446 446 except Exception as e:
447 447 log.exception('failed to fetch gist storage items')
448 448 state = {'message': str(e), 'type': STATE_ERR}
449 449
450 450 human_value = value.copy()
451 451 human_value['used'] = format_byte_size_binary(value['used'])
452 452 human_value['total'] = format_byte_size_binary(value['total'])
453 453 human_value['text'] = "{} ({} items)".format(
454 454 human_value['used'], value['items'])
455 455
456 456 return SysInfoRes(value=value, state=state, human_value=human_value)
457 457
458 458
459 459 def storage_temp():
460 460 import tempfile
461 461 from rhodecode.lib.helpers import format_byte_size_binary
462 462
463 463 path = tempfile.gettempdir()
464 464 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
465 465 state = STATE_OK_DEFAULT
466 466
467 467 if not psutil:
468 468 return SysInfoRes(value=value, state=state)
469 469
470 470 try:
471 471 value.update(dict(psutil.disk_usage(path)._asdict()))
472 472 except Exception as e:
473 473 log.exception('Failed to fetch temp dir info')
474 474 state = {'message': str(e), 'type': STATE_ERR}
475 475
476 476 human_value = value.copy()
477 477 human_value['used'] = format_byte_size_binary(value['used'])
478 478 human_value['total'] = format_byte_size_binary(value['total'])
479 479 human_value['text'] = "{}/{}, {}% used".format(
480 480 format_byte_size_binary(value['used']),
481 481 format_byte_size_binary(value['total']),
482 482 value['percent'])
483 483
484 484 return SysInfoRes(value=value, state=state, human_value=human_value)
485 485
486 486
487 487 def search_info():
488 488 import rhodecode
489 489 from rhodecode.lib.index import searcher_from_config
490 490
491 491 backend = rhodecode.CONFIG.get('search.module', '')
492 492 location = rhodecode.CONFIG.get('search.location', '')
493 493
494 494 try:
495 495 searcher = searcher_from_config(rhodecode.CONFIG)
496 496 searcher = searcher.__class__.__name__
497 497 except Exception:
498 498 searcher = None
499 499
500 500 value = dict(
501 501 backend=backend, searcher=searcher, location=location, text='')
502 502 state = STATE_OK_DEFAULT
503 503
504 504 human_value = value.copy()
505 505 human_value['text'] = "backend:`{}`".format(human_value['backend'])
506 506
507 507 return SysInfoRes(value=value, state=state, human_value=human_value)
508 508
509 509
510 510 def git_info():
511 511 from rhodecode.lib.vcs.backends import git
512 512 state = STATE_OK_DEFAULT
513 513 value = human_value = ''
514 514 try:
515 515 value = git.discover_git_version(raise_on_exc=True)
516 516 human_value = 'version reported from VCSServer: {}'.format(value)
517 517 except Exception as e:
518 518 state = {'message': str(e), 'type': STATE_ERR}
519 519
520 520 return SysInfoRes(value=value, state=state, human_value=human_value)
521 521
522 522
523 523 def hg_info():
524 524 from rhodecode.lib.vcs.backends import hg
525 525 state = STATE_OK_DEFAULT
526 526 value = human_value = ''
527 527 try:
528 528 value = hg.discover_hg_version(raise_on_exc=True)
529 529 human_value = 'version reported from VCSServer: {}'.format(value)
530 530 except Exception as e:
531 531 state = {'message': str(e), 'type': STATE_ERR}
532 532 return SysInfoRes(value=value, state=state, human_value=human_value)
533 533
534 534
535 535 def svn_info():
536 536 from rhodecode.lib.vcs.backends import svn
537 537 state = STATE_OK_DEFAULT
538 538 value = human_value = ''
539 539 try:
540 540 value = svn.discover_svn_version(raise_on_exc=True)
541 541 human_value = 'version reported from VCSServer: {}'.format(value)
542 542 except Exception as e:
543 543 state = {'message': str(e), 'type': STATE_ERR}
544 544 return SysInfoRes(value=value, state=state, human_value=human_value)
545 545
546 546
547 547 def vcs_backends():
548 548 import rhodecode
549 549 value = rhodecode.CONFIG.get('vcs.backends')
550 550 human_value = 'Enabled backends in order: {}'.format(','.join(value))
551 551 return SysInfoRes(value=value, human_value=human_value)
552 552
553 553
554 554 def vcs_server():
555 555 import rhodecode
556 556 from rhodecode.lib.vcs.backends import get_vcsserver_service_data
557 557
558 558 server_url = rhodecode.CONFIG.get('vcs.server')
559 559 enabled = rhodecode.CONFIG.get('vcs.server.enable')
560 560 protocol = rhodecode.CONFIG.get('vcs.server.protocol') or 'http'
561 561 state = STATE_OK_DEFAULT
562 562 version = None
563 563 workers = 0
564 564
565 565 try:
566 566 data = get_vcsserver_service_data()
567 567 if data and 'version' in data:
568 568 version = data['version']
569 569
570 570 if data and 'config' in data:
571 571 conf = data['config']
572 572 workers = conf.get('workers', 'NOT AVAILABLE')
573 573
574 574 connection = 'connected'
575 575 except Exception as e:
576 576 connection = 'failed'
577 577 state = {'message': str(e), 'type': STATE_ERR}
578 578
579 579 value = dict(
580 580 url=server_url,
581 581 enabled=enabled,
582 582 protocol=protocol,
583 583 connection=connection,
584 584 version=version,
585 585 text='',
586 586 )
587 587
588 588 human_value = value.copy()
589 589 human_value['text'] = \
590 590 '{url}@ver:{ver} via {mode} mode[workers:{workers}], connection:{conn}'.format(
591 591 url=server_url, ver=version, workers=workers, mode=protocol,
592 592 conn=connection)
593 593
594 594 return SysInfoRes(value=value, state=state, human_value=human_value)
595 595
596 596
597 def vcs_server_config():
598 from rhodecode.lib.vcs.backends import get_vcsserver_service_data
599 state = STATE_OK_DEFAULT
600
601 value = {}
602 try:
603 data = get_vcsserver_service_data()
604 value = data['app_config']
605 except Exception as e:
606 state = {'message': str(e), 'type': STATE_ERR}
607
608 human_value = value.copy()
609 human_value['text'] = 'VCS Server config'
610
611 return SysInfoRes(value=value, state=state, human_value=human_value)
612
613
597 614 def rhodecode_app_info():
598 615 import rhodecode
599 616 edition = rhodecode.CONFIG.get('rhodecode.edition')
600 617
601 618 value = dict(
602 619 rhodecode_version=rhodecode.__version__,
603 620 rhodecode_lib_path=os.path.abspath(rhodecode.__file__),
604 621 text=''
605 622 )
606 623 human_value = value.copy()
607 624 human_value['text'] = 'RhodeCode {edition}, version {ver}'.format(
608 625 edition=edition, ver=value['rhodecode_version']
609 626 )
610 627 return SysInfoRes(value=value, human_value=human_value)
611 628
612 629
613 630 def rhodecode_config():
614 631 import rhodecode
615 632 path = rhodecode.CONFIG.get('__file__')
616 633 rhodecode_ini_safe = rhodecode.CONFIG.copy()
617 634 cert_path = get_cert_path(path)
618 635
619 636 try:
620 637 config = configparser.ConfigParser()
621 638 config.read(path)
622 639 parsed_ini = config
623 640 if parsed_ini.has_section('server:main'):
624 641 parsed_ini = dict(parsed_ini.items('server:main'))
625 642 except Exception:
626 643 log.exception('Failed to read .ini file for display')
627 644 parsed_ini = {}
628 645
629 646 rhodecode_ini_safe['server:main'] = parsed_ini
630 647
631 648 blacklist = [
632 649 'rhodecode_license_key',
633 650 'routes.map',
634 651 'sqlalchemy.db1.url',
635 652 'channelstream.secret',
636 653 'beaker.session.secret',
637 654 'rhodecode.encrypted_values.secret',
638 655 'rhodecode_auth_github_consumer_key',
639 656 'rhodecode_auth_github_consumer_secret',
640 657 'rhodecode_auth_google_consumer_key',
641 658 'rhodecode_auth_google_consumer_secret',
642 659 'rhodecode_auth_bitbucket_consumer_secret',
643 660 'rhodecode_auth_bitbucket_consumer_key',
644 661 'rhodecode_auth_twitter_consumer_secret',
645 662 'rhodecode_auth_twitter_consumer_key',
646 663
647 664 'rhodecode_auth_twitter_secret',
648 665 'rhodecode_auth_github_secret',
649 666 'rhodecode_auth_google_secret',
650 667 'rhodecode_auth_bitbucket_secret',
651 668
652 669 'appenlight.api_key',
653 670 ('app_conf', 'sqlalchemy.db1.url')
654 671 ]
655 672 for k in blacklist:
656 673 if isinstance(k, tuple):
657 674 section, key = k
658 675 if section in rhodecode_ini_safe:
659 676 rhodecode_ini_safe[section] = '**OBFUSCATED**'
660 677 else:
661 678 rhodecode_ini_safe.pop(k, None)
662 679
663 680 # TODO: maybe put some CONFIG checks here ?
664 681 return SysInfoRes(value={'config': rhodecode_ini_safe,
665 682 'path': path, 'cert_path': cert_path})
666 683
667 684
668 685 def database_info():
669 686 import rhodecode
670 687 from sqlalchemy.engine import url as engine_url
671 688 from rhodecode.model.meta import Base as sql_base, Session
672 689 from rhodecode.model.db import DbMigrateVersion
673 690
674 691 state = STATE_OK_DEFAULT
675 692
676 693 db_migrate = DbMigrateVersion.query().filter(
677 694 DbMigrateVersion.repository_id == 'rhodecode_db_migrations').one()
678 695
679 696 db_url_obj = engine_url.make_url(rhodecode.CONFIG['sqlalchemy.db1.url'])
680 697
681 698 try:
682 699 engine = sql_base.metadata.bind
683 700 db_server_info = engine.dialect._get_server_version_info(
684 701 Session.connection(bind=engine))
685 702 db_version = '.'.join(map(str, db_server_info))
686 703 except Exception:
687 704 log.exception('failed to fetch db version')
688 705 db_version = 'UNKNOWN'
689 706
690 707 db_info = dict(
691 708 migrate_version=db_migrate.version,
692 709 type=db_url_obj.get_backend_name(),
693 710 version=db_version,
694 711 url=repr(db_url_obj)
695 712 )
696 713 current_version = db_migrate.version
697 714 expected_version = rhodecode.__dbversion__
698 715 if state['type'] == STATE_OK and current_version != expected_version:
699 716 msg = 'Critical: database schema mismatch, ' \
700 717 'expected version {}, got {}. ' \
701 718 'Please run migrations on your database.'.format(
702 719 expected_version, current_version)
703 720 state = {'message': msg, 'type': STATE_ERR}
704 721
705 722 human_value = db_info.copy()
706 723 human_value['url'] = "{} @ migration version: {}".format(
707 724 db_info['url'], db_info['migrate_version'])
708 725 human_value['version'] = "{} {}".format(db_info['type'], db_info['version'])
709 726 return SysInfoRes(value=db_info, state=state, human_value=human_value)
710 727
711 728
712 729 def server_info(environ):
713 730 import rhodecode
714 731 from rhodecode.lib.base import get_server_ip_addr, get_server_port
715 732
716 733 value = {
717 734 'server_ip': '%s:%s' % (
718 735 get_server_ip_addr(environ, log_errors=False),
719 736 get_server_port(environ)
720 737 ),
721 738 'server_id': rhodecode.CONFIG.get('instance_id'),
722 739 }
723 740 return SysInfoRes(value=value)
724 741
725 742
726 743 def usage_info():
727 744 from rhodecode.model.db import User, Repository
728 745 value = {
729 746 'users': User.query().count(),
730 747 'users_active': User.query().filter(User.active == True).count(),
731 748 'repositories': Repository.query().count(),
732 749 'repository_types': {
733 750 'hg': Repository.query().filter(
734 751 Repository.repo_type == 'hg').count(),
735 752 'git': Repository.query().filter(
736 753 Repository.repo_type == 'git').count(),
737 754 'svn': Repository.query().filter(
738 755 Repository.repo_type == 'svn').count(),
739 756 },
740 757 }
741 758 return SysInfoRes(value=value)
742 759
743 760
744 761 def get_system_info(environ):
745 762 environ = environ or {}
746 763 return {
747 764 'rhodecode_app': SysInfo(rhodecode_app_info)(),
748 765 'rhodecode_config': SysInfo(rhodecode_config)(),
749 766 'rhodecode_usage': SysInfo(usage_info)(),
750 767 'python': SysInfo(python_info)(),
751 768 'py_modules': SysInfo(py_modules)(),
752 769
753 770 'platform': SysInfo(platform_type)(),
754 771 'locale': SysInfo(locale_info)(),
755 772 'server': SysInfo(server_info, environ=environ)(),
756 773 'database': SysInfo(database_info)(),
757 774 'ulimit': SysInfo(ulimit_info)(),
758 775 'storage': SysInfo(storage)(),
759 776 'storage_inodes': SysInfo(storage_inodes)(),
760 777 'storage_archive': SysInfo(storage_archives)(),
761 778 'storage_gist': SysInfo(storage_gist)(),
762 779 'storage_temp': SysInfo(storage_temp)(),
763 780
764 781 'search': SysInfo(search_info)(),
765 782
766 783 'uptime': SysInfo(uptime)(),
767 784 'load': SysInfo(machine_load)(),
768 785 'cpu': SysInfo(cpu)(),
769 786 'memory': SysInfo(memory)(),
770 787
771 788 'vcs_backends': SysInfo(vcs_backends)(),
772 789 'vcs_server': SysInfo(vcs_server)(),
773 790
791 'vcs_server_config': SysInfo(vcs_server_config)(),
792
774 793 'git': SysInfo(git_info)(),
775 794 'hg': SysInfo(hg_info)(),
776 795 'svn': SysInfo(svn_info)(),
777 796 }
@@ -1,57 +1,71 b''
1 1
2 2 <div id="update_notice" style="display: none; margin: -40px 0px 20px 0px">
3 3 <div>${_('Checking for updates...')}</div>
4 4 </div>
5 5
6 6
7 7 <div class="panel panel-default">
8 8 <div class="panel-heading">
9 9 <h3 class="panel-title">${_('System Info')}</h3>
10 10 % if c.allowed_to_snapshot:
11 11 <a href="${h.route_path('admin_settings_system', _query={'snapshot':1})}" class="panel-edit">${_('create summary snapshot')}</a>
12 12 % endif
13 13 </div>
14 14 <div class="panel-body">
15 15 <dl class="dl-horizontal settings">
16 16 % for dt, dd, warn in c.data_items:
17 17 <dt>${dt}${':' if dt else '---'}</dt>
18 18 <dd>${dd}${'' if dt else '---'}
19 19 % if warn and warn['message']:
20 20 <div class="alert-${warn['type']}">
21 21 <strong>${warn['message']}</strong>
22 22 </div>
23 23 % endif
24 24 </dd>
25 25 % endfor
26 26 </dl>
27 27 </div>
28 28 </div>
29 29
30 30 <div class="panel panel-default">
31 31 <div class="panel-heading">
32 <h3 class="panel-title">${_('VCS Server')}</h3>
33 </div>
34 <div class="panel-body">
35 <dl class="dl-horizontal settings">
36 % for dt, dd in c.vcsserver_data_items:
37 <dt>${dt}${':' if dt else '---'}</dt>
38 <dd>${dd}${'' if dt else '---'}</dd>
39 % endfor
40 </dl>
41 </div>
42 </div>
43
44 <div class="panel panel-default">
45 <div class="panel-heading">
32 46 <h3 class="panel-title">${_('Python Packages')}</h3>
33 47 </div>
34 48 <div class="panel-body">
35 49 <table class="table">
36 50 <colgroup>
37 51 <col class='label'>
38 52 <col class='content'>
39 53 </colgroup>
40 54 <tbody>
41 55 % for key, value in c.py_modules['human_value']:
42 56 <tr>
43 57 <td>${key}</td>
44 58 <td>${value}</td>
45 59 </tr>
46 60 % endfor
47 61 </tbody>
48 62 </table>
49 63 </div>
50 64 </div>
51 65
52 66 <script>
53 67 $('#check_for_update').click(function(e){
54 68 $('#update_notice').show();
55 69 $('#update_notice').load("${h.route_path('admin_settings_system_update')}");
56 70 })
57 71 </script>
General Comments 0
You need to be logged in to leave comments. Login now