##// END OF EJS Templates
compat: use py3 compatible configparser in all places.
marcink -
r2355:1176c026 default
parent child
Show More
@@ -1,207 +1,207
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 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 re
22 import re
23 import logging
23 import logging
24 import datetime
24 import datetime
25 import ConfigParser
25 from pyramid.compat import configparser
26
26
27 from rhodecode.model.db import Session, User, UserSshKeys
27 from rhodecode.model.db import Session, User, UserSshKeys
28 from rhodecode.model.scm import ScmModel
28 from rhodecode.model.scm import ScmModel
29
29
30 from .hg import MercurialServer
30 from .hg import MercurialServer
31 from .git import GitServer
31 from .git import GitServer
32 from .svn import SubversionServer
32 from .svn import SubversionServer
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35
35
36 class SshWrapper(object):
36 class SshWrapper(object):
37
37
38 def __init__(self, command, connection_info, mode,
38 def __init__(self, command, connection_info, mode,
39 user, user_id, key_id, shell, ini_path, env):
39 user, user_id, key_id, shell, ini_path, env):
40 self.command = command
40 self.command = command
41 self.connection_info = connection_info
41 self.connection_info = connection_info
42 self.mode = mode
42 self.mode = mode
43 self.user = user
43 self.user = user
44 self.user_id = user_id
44 self.user_id = user_id
45 self.key_id = key_id
45 self.key_id = key_id
46 self.shell = shell
46 self.shell = shell
47 self.ini_path = ini_path
47 self.ini_path = ini_path
48 self.env = env
48 self.env = env
49
49
50 self.config = self.parse_config(ini_path)
50 self.config = self.parse_config(ini_path)
51 self.server_impl = None
51 self.server_impl = None
52
52
53 def parse_config(self, config_path):
53 def parse_config(self, config_path):
54 parser = ConfigParser.ConfigParser()
54 parser = configparser.ConfigParser()
55 parser.read(config_path)
55 parser.read(config_path)
56 return parser
56 return parser
57
57
58 def update_key_access_time(self, key_id):
58 def update_key_access_time(self, key_id):
59 key = UserSshKeys().query().filter(
59 key = UserSshKeys().query().filter(
60 UserSshKeys.ssh_key_id == key_id).scalar()
60 UserSshKeys.ssh_key_id == key_id).scalar()
61 if key:
61 if key:
62 key.accessed_on = datetime.datetime.utcnow()
62 key.accessed_on = datetime.datetime.utcnow()
63 Session().add(key)
63 Session().add(key)
64 Session().commit()
64 Session().commit()
65 log.debug('Update key `%s` access time', key_id)
65 log.debug('Update key `%s` access time', key_id)
66
66
67 def get_connection_info(self):
67 def get_connection_info(self):
68 """
68 """
69 connection_info
69 connection_info
70
70
71 Identifies the client and server ends of the connection.
71 Identifies the client and server ends of the connection.
72 The variable contains four space-separated values: client IP address,
72 The variable contains four space-separated values: client IP address,
73 client port number, server IP address, and server port number.
73 client port number, server IP address, and server port number.
74 """
74 """
75 conn = dict(
75 conn = dict(
76 client_ip=None,
76 client_ip=None,
77 client_port=None,
77 client_port=None,
78 server_ip=None,
78 server_ip=None,
79 server_port=None,
79 server_port=None,
80 )
80 )
81
81
82 info = self.connection_info.split(' ')
82 info = self.connection_info.split(' ')
83 if len(info) == 4:
83 if len(info) == 4:
84 conn['client_ip'] = info[0]
84 conn['client_ip'] = info[0]
85 conn['client_port'] = info[1]
85 conn['client_port'] = info[1]
86 conn['server_ip'] = info[2]
86 conn['server_ip'] = info[2]
87 conn['server_port'] = info[3]
87 conn['server_port'] = info[3]
88
88
89 return conn
89 return conn
90
90
91 def get_repo_details(self, mode):
91 def get_repo_details(self, mode):
92 vcs_type = mode if mode in ['svn', 'hg', 'git'] else None
92 vcs_type = mode if mode in ['svn', 'hg', 'git'] else None
93 mode = mode
93 mode = mode
94 repo_name = None
94 repo_name = None
95
95
96 hg_pattern = r'^hg\s+\-R\s+(\S+)\s+serve\s+\-\-stdio$'
96 hg_pattern = r'^hg\s+\-R\s+(\S+)\s+serve\s+\-\-stdio$'
97 hg_match = re.match(hg_pattern, self.command)
97 hg_match = re.match(hg_pattern, self.command)
98 if hg_match is not None:
98 if hg_match is not None:
99 vcs_type = 'hg'
99 vcs_type = 'hg'
100 repo_name = hg_match.group(1).strip('/')
100 repo_name = hg_match.group(1).strip('/')
101 return vcs_type, repo_name, mode
101 return vcs_type, repo_name, mode
102
102
103 git_pattern = (
103 git_pattern = (
104 r'^git-(receive-pack|upload-pack)\s\'[/]?(\S+?)(|\.git)\'$')
104 r'^git-(receive-pack|upload-pack)\s\'[/]?(\S+?)(|\.git)\'$')
105 git_match = re.match(git_pattern, self.command)
105 git_match = re.match(git_pattern, self.command)
106 if git_match is not None:
106 if git_match is not None:
107 vcs_type = 'git'
107 vcs_type = 'git'
108 repo_name = git_match.group(2).strip('/')
108 repo_name = git_match.group(2).strip('/')
109 mode = git_match.group(1)
109 mode = git_match.group(1)
110 return vcs_type, repo_name, mode
110 return vcs_type, repo_name, mode
111
111
112 svn_pattern = r'^svnserve -t'
112 svn_pattern = r'^svnserve -t'
113 svn_match = re.match(svn_pattern, self.command)
113 svn_match = re.match(svn_pattern, self.command)
114
114
115 if svn_match is not None:
115 if svn_match is not None:
116 vcs_type = 'svn'
116 vcs_type = 'svn'
117 # Repo name should be extracted from the input stream
117 # Repo name should be extracted from the input stream
118 return vcs_type, repo_name, mode
118 return vcs_type, repo_name, mode
119
119
120 return vcs_type, repo_name, mode
120 return vcs_type, repo_name, mode
121
121
122 def serve(self, vcs, repo, mode, user, permissions):
122 def serve(self, vcs, repo, mode, user, permissions):
123 store = ScmModel().repos_path
123 store = ScmModel().repos_path
124
124
125 log.debug(
125 log.debug(
126 'VCS detected:`%s` mode: `%s` repo_name: %s', vcs, mode, repo)
126 'VCS detected:`%s` mode: `%s` repo_name: %s', vcs, mode, repo)
127
127
128 if vcs == 'hg':
128 if vcs == 'hg':
129 server = MercurialServer(
129 server = MercurialServer(
130 store=store, ini_path=self.ini_path,
130 store=store, ini_path=self.ini_path,
131 repo_name=repo, user=user,
131 repo_name=repo, user=user,
132 user_permissions=permissions, config=self.config, env=self.env)
132 user_permissions=permissions, config=self.config, env=self.env)
133 self.server_impl = server
133 self.server_impl = server
134 return server.run()
134 return server.run()
135
135
136 elif vcs == 'git':
136 elif vcs == 'git':
137 server = GitServer(
137 server = GitServer(
138 store=store, ini_path=self.ini_path,
138 store=store, ini_path=self.ini_path,
139 repo_name=repo, repo_mode=mode, user=user,
139 repo_name=repo, repo_mode=mode, user=user,
140 user_permissions=permissions, config=self.config, env=self.env)
140 user_permissions=permissions, config=self.config, env=self.env)
141 self.server_impl = server
141 self.server_impl = server
142 return server.run()
142 return server.run()
143
143
144 elif vcs == 'svn':
144 elif vcs == 'svn':
145 server = SubversionServer(
145 server = SubversionServer(
146 store=store, ini_path=self.ini_path,
146 store=store, ini_path=self.ini_path,
147 repo_name=None, user=user,
147 repo_name=None, user=user,
148 user_permissions=permissions, config=self.config, env=self.env)
148 user_permissions=permissions, config=self.config, env=self.env)
149 self.server_impl = server
149 self.server_impl = server
150 return server.run()
150 return server.run()
151
151
152 else:
152 else:
153 raise Exception('Unrecognised VCS: {}'.format(vcs))
153 raise Exception('Unrecognised VCS: {}'.format(vcs))
154
154
155 def wrap(self):
155 def wrap(self):
156 mode = self.mode
156 mode = self.mode
157 user = self.user
157 user = self.user
158 user_id = self.user_id
158 user_id = self.user_id
159 key_id = self.key_id
159 key_id = self.key_id
160 shell = self.shell
160 shell = self.shell
161
161
162 scm_detected, scm_repo, scm_mode = self.get_repo_details(mode)
162 scm_detected, scm_repo, scm_mode = self.get_repo_details(mode)
163
163
164 log.debug(
164 log.debug(
165 'Mode: `%s` User: `%s:%s` Shell: `%s` SSH Command: `\"%s\"` '
165 'Mode: `%s` User: `%s:%s` Shell: `%s` SSH Command: `\"%s\"` '
166 'SCM_DETECTED: `%s` SCM Mode: `%s` SCM Repo: `%s`',
166 'SCM_DETECTED: `%s` SCM Mode: `%s` SCM Repo: `%s`',
167 mode, user, user_id, shell, self.command,
167 mode, user, user_id, shell, self.command,
168 scm_detected, scm_mode, scm_repo)
168 scm_detected, scm_mode, scm_repo)
169
169
170 # update last access time for this key
170 # update last access time for this key
171 self.update_key_access_time(key_id)
171 self.update_key_access_time(key_id)
172
172
173 log.debug('SSH Connection info %s', self.get_connection_info())
173 log.debug('SSH Connection info %s', self.get_connection_info())
174
174
175 if shell and self.command is None:
175 if shell and self.command is None:
176 log.info(
176 log.info(
177 'Dropping to shell, no command given and shell is allowed')
177 'Dropping to shell, no command given and shell is allowed')
178 os.execl('/bin/bash', '-l')
178 os.execl('/bin/bash', '-l')
179 exit_code = 1
179 exit_code = 1
180
180
181 elif scm_detected:
181 elif scm_detected:
182 user = User.get(user_id)
182 user = User.get(user_id)
183 if not user:
183 if not user:
184 log.warning('User with id %s not found', user_id)
184 log.warning('User with id %s not found', user_id)
185 exit_code = -1
185 exit_code = -1
186 return exit_code
186 return exit_code
187
187
188 auth_user = user.AuthUser()
188 auth_user = user.AuthUser()
189 permissions = auth_user.permissions['repositories']
189 permissions = auth_user.permissions['repositories']
190
190
191 try:
191 try:
192 exit_code, is_updated = self.serve(
192 exit_code, is_updated = self.serve(
193 scm_detected, scm_repo, scm_mode, user, permissions)
193 scm_detected, scm_repo, scm_mode, user, permissions)
194 except Exception:
194 except Exception:
195 log.exception('Error occurred during execution of SshWrapper')
195 log.exception('Error occurred during execution of SshWrapper')
196 exit_code = -1
196 exit_code = -1
197
197
198 elif self.command is None and shell is False:
198 elif self.command is None and shell is False:
199 log.error('No Command given.')
199 log.error('No Command given.')
200 exit_code = -1
200 exit_code = -1
201
201
202 else:
202 else:
203 log.error(
203 log.error(
204 'Unhandled Command: "%s" Aborting.', self.command)
204 'Unhandled Command: "%s" Aborting.', self.command)
205 exit_code = -1
205 exit_code = -1
206
206
207 return exit_code
207 return exit_code
@@ -1,62 +1,62
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 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 pytest
22 import pytest
23 import ConfigParser
23 from pyramid.compat import configparser
24
24
25 from rhodecode.apps.ssh_support.lib.ssh_wrapper import SshWrapper
25 from rhodecode.apps.ssh_support.lib.ssh_wrapper import SshWrapper
26 from rhodecode.lib.utils2 import AttributeDict
26 from rhodecode.lib.utils2 import AttributeDict
27
27
28
28
29 @pytest.fixture
29 @pytest.fixture
30 def dummy_conf_file(tmpdir):
30 def dummy_conf_file(tmpdir):
31 conf = ConfigParser.ConfigParser()
31 conf = configparser.ConfigParser()
32 conf.add_section('app:main')
32 conf.add_section('app:main')
33 conf.set('app:main', 'ssh.executable.hg', '/usr/bin/hg')
33 conf.set('app:main', 'ssh.executable.hg', '/usr/bin/hg')
34 conf.set('app:main', 'ssh.executable.git', '/usr/bin/git')
34 conf.set('app:main', 'ssh.executable.git', '/usr/bin/git')
35 conf.set('app:main', 'ssh.executable.svn', '/usr/bin/svnserve')
35 conf.set('app:main', 'ssh.executable.svn', '/usr/bin/svnserve')
36
36
37 f_path = os.path.join(str(tmpdir), 'ssh_wrapper_test.ini')
37 f_path = os.path.join(str(tmpdir), 'ssh_wrapper_test.ini')
38 with open(f_path, 'wb') as f:
38 with open(f_path, 'wb') as f:
39 conf.write(f)
39 conf.write(f)
40
40
41 return os.path.join(f_path)
41 return os.path.join(f_path)
42
42
43
43
44 @pytest.fixture
44 @pytest.fixture
45 def dummy_env():
45 def dummy_env():
46 return {
46 return {
47 'request':
47 'request':
48 AttributeDict(host_url='http://localhost', script_name='/')
48 AttributeDict(host_url='http://localhost', script_name='/')
49 }
49 }
50
50
51
51
52 @pytest.fixture
52 @pytest.fixture
53 def dummy_user():
53 def dummy_user():
54 return AttributeDict(username='test_user')
54 return AttributeDict(username='test_user')
55
55
56
56
57 @pytest.fixture
57 @pytest.fixture
58 def ssh_wrapper(app, dummy_conf_file, dummy_env):
58 def ssh_wrapper(app, dummy_conf_file, dummy_env):
59 conn_info = '127.0.0.1 22 10.0.0.1 443'
59 conn_info = '127.0.0.1 22 10.0.0.1 443'
60 return SshWrapper(
60 return SshWrapper(
61 'random command', conn_info, 'auto', 'admin', '1', key_id='1',
61 'random command', conn_info, 'auto', 'admin', '1', key_id='1',
62 shell=False, ini_path=dummy_conf_file, env=dummy_env)
62 shell=False, ini_path=dummy_conf_file, env=dummy_env)
@@ -1,49 +1,49
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 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 ConfigParser
22 from pyramid.compat import configparser
23 from pyramid.paster import bootstrap as pyramid_bootstrap
23 from pyramid.paster import bootstrap as pyramid_bootstrap, setup_logging # noqa
24 from pyramid.request import Request
24 from pyramid.request import Request
25
25
26
26
27 def get_config(ini_path, **kwargs):
27 def get_config(ini_path, **kwargs):
28 parser = ConfigParser.ConfigParser(**kwargs)
28 parser = configparser.ConfigParser(**kwargs)
29 parser.read(ini_path)
29 parser.read(ini_path)
30 return parser
30 return parser
31
31
32
32
33 def get_app_config(ini_path):
33 def get_app_config(ini_path):
34 from paste.deploy.loadwsgi import appconfig
34 from paste.deploy.loadwsgi import appconfig
35 return appconfig('config:{}'.format(ini_path), relative_to=os.getcwd())
35 return appconfig('config:{}'.format(ini_path), relative_to=os.getcwd())
36
36
37
37
38 def bootstrap(config_uri, request=None, options=None):
38 def bootstrap(config_uri, request=None, options=None):
39
39
40 config = get_config(config_uri)
40 config = get_config(config_uri)
41 base_url = 'http://rhodecode.local'
41 base_url = 'http://rhodecode.local'
42 try:
42 try:
43 base_url = config.get('app:main', 'app.base_url')
43 base_url = config.get('app:main', 'app.base_url')
44 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
44 except (configparser.NoSectionError, configparser.NoOptionError):
45 pass
45 pass
46
46
47 request = request or Request.blank('/', base_url=base_url)
47 request = request or Request.blank('/', base_url=base_url)
48
48
49 return pyramid_bootstrap(config_uri, request=request, options=options)
49 return pyramid_bootstrap(config_uri, request=request, options=options)
@@ -1,723 +1,722
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2017 RhodeCode GmbH
3 # Copyright (C) 2017-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import os
22 import os
23 import sys
23 import sys
24 import time
24 import time
25 import platform
25 import platform
26 import pkg_resources
26 import pkg_resources
27 import logging
27 import logging
28 import string
29
28
29 from pyramid.compat import configparser
30
30
31 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
32
32
33
33
34 psutil = None
34 psutil = None
35
35
36 try:
36 try:
37 # cygwin cannot have yet psutil support.
37 # cygwin cannot have yet psutil support.
38 import psutil as psutil
38 import psutil as psutil
39 except ImportError:
39 except ImportError:
40 pass
40 pass
41
41
42
42
43 _NA = 'NOT AVAILABLE'
43 _NA = 'NOT AVAILABLE'
44
44
45 STATE_OK = 'ok'
45 STATE_OK = 'ok'
46 STATE_ERR = 'error'
46 STATE_ERR = 'error'
47 STATE_WARN = 'warning'
47 STATE_WARN = 'warning'
48
48
49 STATE_OK_DEFAULT = {'message': '', 'type': STATE_OK}
49 STATE_OK_DEFAULT = {'message': '', 'type': STATE_OK}
50
50
51
51
52 # HELPERS
52 # HELPERS
53 def percentage(part, whole):
53 def percentage(part, whole):
54 whole = float(whole)
54 whole = float(whole)
55 if whole > 0:
55 if whole > 0:
56 return round(100 * float(part) / whole, 1)
56 return round(100 * float(part) / whole, 1)
57 return 0.0
57 return 0.0
58
58
59
59
60 def get_storage_size(storage_path):
60 def get_storage_size(storage_path):
61 sizes = []
61 sizes = []
62 for file_ in os.listdir(storage_path):
62 for file_ in os.listdir(storage_path):
63 storage_file = os.path.join(storage_path, file_)
63 storage_file = os.path.join(storage_path, file_)
64 if os.path.isfile(storage_file):
64 if os.path.isfile(storage_file):
65 try:
65 try:
66 sizes.append(os.path.getsize(storage_file))
66 sizes.append(os.path.getsize(storage_file))
67 except OSError:
67 except OSError:
68 log.exception('Failed to get size of storage file %s',
68 log.exception('Failed to get size of storage file %s',
69 storage_file)
69 storage_file)
70 pass
70 pass
71
71
72 return sum(sizes)
72 return sum(sizes)
73
73
74
74
75 class SysInfoRes(object):
75 class SysInfoRes(object):
76 def __init__(self, value, state=STATE_OK_DEFAULT, human_value=None):
76 def __init__(self, value, state=STATE_OK_DEFAULT, human_value=None):
77 self.value = value
77 self.value = value
78 self.state = state
78 self.state = state
79 self.human_value = human_value or value
79 self.human_value = human_value or value
80
80
81 def __json__(self):
81 def __json__(self):
82 return {
82 return {
83 'value': self.value,
83 'value': self.value,
84 'state': self.state,
84 'state': self.state,
85 'human_value': self.human_value,
85 'human_value': self.human_value,
86 }
86 }
87
87
88 def get_value(self):
88 def get_value(self):
89 return self.__json__()
89 return self.__json__()
90
90
91 def __str__(self):
91 def __str__(self):
92 return '<SysInfoRes({})>'.format(self.__json__())
92 return '<SysInfoRes({})>'.format(self.__json__())
93
93
94
94
95 class SysInfo(object):
95 class SysInfo(object):
96
96
97 def __init__(self, func_name, **kwargs):
97 def __init__(self, func_name, **kwargs):
98 self.func_name = func_name
98 self.func_name = func_name
99 self.value = _NA
99 self.value = _NA
100 self.state = None
100 self.state = None
101 self.kwargs = kwargs or {}
101 self.kwargs = kwargs or {}
102
102
103 def __call__(self):
103 def __call__(self):
104 computed = self.compute(**self.kwargs)
104 computed = self.compute(**self.kwargs)
105 if not isinstance(computed, SysInfoRes):
105 if not isinstance(computed, SysInfoRes):
106 raise ValueError(
106 raise ValueError(
107 'computed value for {} is not instance of '
107 'computed value for {} is not instance of '
108 '{}, got {} instead'.format(
108 '{}, got {} instead'.format(
109 self.func_name, SysInfoRes, type(computed)))
109 self.func_name, SysInfoRes, type(computed)))
110 return computed.__json__()
110 return computed.__json__()
111
111
112 def __str__(self):
112 def __str__(self):
113 return '<SysInfo({})>'.format(self.func_name)
113 return '<SysInfo({})>'.format(self.func_name)
114
114
115 def compute(self, **kwargs):
115 def compute(self, **kwargs):
116 return self.func_name(**kwargs)
116 return self.func_name(**kwargs)
117
117
118
118
119 # SysInfo functions
119 # SysInfo functions
120 def python_info():
120 def python_info():
121 value = dict(version=' '.join(platform._sys_version()),
121 value = dict(version=' '.join(platform._sys_version()),
122 executable=sys.executable)
122 executable=sys.executable)
123 return SysInfoRes(value=value)
123 return SysInfoRes(value=value)
124
124
125
125
126 def py_modules():
126 def py_modules():
127 mods = dict([(p.project_name, p.version)
127 mods = dict([(p.project_name, p.version)
128 for p in pkg_resources.working_set])
128 for p in pkg_resources.working_set])
129 value = sorted(mods.items(), key=lambda k: k[0].lower())
129 value = sorted(mods.items(), key=lambda k: k[0].lower())
130 return SysInfoRes(value=value)
130 return SysInfoRes(value=value)
131
131
132
132
133 def platform_type():
133 def platform_type():
134 from rhodecode.lib.utils import safe_unicode, generate_platform_uuid
134 from rhodecode.lib.utils import safe_unicode, generate_platform_uuid
135
135
136 value = dict(
136 value = dict(
137 name=safe_unicode(platform.platform()),
137 name=safe_unicode(platform.platform()),
138 uuid=generate_platform_uuid()
138 uuid=generate_platform_uuid()
139 )
139 )
140 return SysInfoRes(value=value)
140 return SysInfoRes(value=value)
141
141
142
142
143 def uptime():
143 def uptime():
144 from rhodecode.lib.helpers import age, time_to_datetime
144 from rhodecode.lib.helpers import age, time_to_datetime
145 from rhodecode.translation import TranslationString
145 from rhodecode.translation import TranslationString
146
146
147 value = dict(boot_time=0, uptime=0, text='')
147 value = dict(boot_time=0, uptime=0, text='')
148 state = STATE_OK_DEFAULT
148 state = STATE_OK_DEFAULT
149 if not psutil:
149 if not psutil:
150 return SysInfoRes(value=value, state=state)
150 return SysInfoRes(value=value, state=state)
151
151
152 boot_time = psutil.boot_time()
152 boot_time = psutil.boot_time()
153 value['boot_time'] = boot_time
153 value['boot_time'] = boot_time
154 value['uptime'] = time.time() - boot_time
154 value['uptime'] = time.time() - boot_time
155
155
156 date_or_age = age(time_to_datetime(boot_time))
156 date_or_age = age(time_to_datetime(boot_time))
157 if isinstance(date_or_age, TranslationString):
157 if isinstance(date_or_age, TranslationString):
158 date_or_age = date_or_age.interpolate()
158 date_or_age = date_or_age.interpolate()
159
159
160 human_value = value.copy()
160 human_value = value.copy()
161 human_value['boot_time'] = time_to_datetime(boot_time)
161 human_value['boot_time'] = time_to_datetime(boot_time)
162 human_value['uptime'] = age(time_to_datetime(boot_time), show_suffix=False)
162 human_value['uptime'] = age(time_to_datetime(boot_time), show_suffix=False)
163
163
164 human_value['text'] = u'Server started {}'.format(date_or_age)
164 human_value['text'] = u'Server started {}'.format(date_or_age)
165 return SysInfoRes(value=value, human_value=human_value)
165 return SysInfoRes(value=value, human_value=human_value)
166
166
167
167
168 def memory():
168 def memory():
169 from rhodecode.lib.helpers import format_byte_size_binary
169 from rhodecode.lib.helpers import format_byte_size_binary
170 value = dict(available=0, used=0, used_real=0, cached=0, percent=0,
170 value = dict(available=0, used=0, used_real=0, cached=0, percent=0,
171 percent_used=0, free=0, inactive=0, active=0, shared=0,
171 percent_used=0, free=0, inactive=0, active=0, shared=0,
172 total=0, buffers=0, text='')
172 total=0, buffers=0, text='')
173
173
174 state = STATE_OK_DEFAULT
174 state = STATE_OK_DEFAULT
175 if not psutil:
175 if not psutil:
176 return SysInfoRes(value=value, state=state)
176 return SysInfoRes(value=value, state=state)
177
177
178 value.update(dict(psutil.virtual_memory()._asdict()))
178 value.update(dict(psutil.virtual_memory()._asdict()))
179 value['used_real'] = value['total'] - value['available']
179 value['used_real'] = value['total'] - value['available']
180 value['percent_used'] = psutil._common.usage_percent(
180 value['percent_used'] = psutil._common.usage_percent(
181 value['used_real'], value['total'], 1)
181 value['used_real'], value['total'], 1)
182
182
183 human_value = value.copy()
183 human_value = value.copy()
184 human_value['text'] = '%s/%s, %s%% used' % (
184 human_value['text'] = '%s/%s, %s%% used' % (
185 format_byte_size_binary(value['used_real']),
185 format_byte_size_binary(value['used_real']),
186 format_byte_size_binary(value['total']),
186 format_byte_size_binary(value['total']),
187 value['percent_used'],)
187 value['percent_used'],)
188
188
189 keys = value.keys()[::]
189 keys = value.keys()[::]
190 keys.pop(keys.index('percent'))
190 keys.pop(keys.index('percent'))
191 keys.pop(keys.index('percent_used'))
191 keys.pop(keys.index('percent_used'))
192 keys.pop(keys.index('text'))
192 keys.pop(keys.index('text'))
193 for k in keys:
193 for k in keys:
194 human_value[k] = format_byte_size_binary(value[k])
194 human_value[k] = format_byte_size_binary(value[k])
195
195
196 if state['type'] == STATE_OK and value['percent_used'] > 90:
196 if state['type'] == STATE_OK and value['percent_used'] > 90:
197 msg = 'Critical: your available RAM memory is very low.'
197 msg = 'Critical: your available RAM memory is very low.'
198 state = {'message': msg, 'type': STATE_ERR}
198 state = {'message': msg, 'type': STATE_ERR}
199
199
200 elif state['type'] == STATE_OK and value['percent_used'] > 70:
200 elif state['type'] == STATE_OK and value['percent_used'] > 70:
201 msg = 'Warning: your available RAM memory is running low.'
201 msg = 'Warning: your available RAM memory is running low.'
202 state = {'message': msg, 'type': STATE_WARN}
202 state = {'message': msg, 'type': STATE_WARN}
203
203
204 return SysInfoRes(value=value, state=state, human_value=human_value)
204 return SysInfoRes(value=value, state=state, human_value=human_value)
205
205
206
206
207 def machine_load():
207 def machine_load():
208 value = {'1_min': _NA, '5_min': _NA, '15_min': _NA, 'text': ''}
208 value = {'1_min': _NA, '5_min': _NA, '15_min': _NA, 'text': ''}
209 state = STATE_OK_DEFAULT
209 state = STATE_OK_DEFAULT
210 if not psutil:
210 if not psutil:
211 return SysInfoRes(value=value, state=state)
211 return SysInfoRes(value=value, state=state)
212
212
213 # load averages
213 # load averages
214 if hasattr(psutil.os, 'getloadavg'):
214 if hasattr(psutil.os, 'getloadavg'):
215 value.update(dict(
215 value.update(dict(
216 zip(['1_min', '5_min', '15_min'], psutil.os.getloadavg())))
216 zip(['1_min', '5_min', '15_min'], psutil.os.getloadavg())))
217
217
218 human_value = value.copy()
218 human_value = value.copy()
219 human_value['text'] = '1min: {}, 5min: {}, 15min: {}'.format(
219 human_value['text'] = '1min: {}, 5min: {}, 15min: {}'.format(
220 value['1_min'], value['5_min'], value['15_min'])
220 value['1_min'], value['5_min'], value['15_min'])
221
221
222 if state['type'] == STATE_OK and value['15_min'] > 5:
222 if state['type'] == STATE_OK and value['15_min'] > 5:
223 msg = 'Warning: your machine load is very high.'
223 msg = 'Warning: your machine load is very high.'
224 state = {'message': msg, 'type': STATE_WARN}
224 state = {'message': msg, 'type': STATE_WARN}
225
225
226 return SysInfoRes(value=value, state=state, human_value=human_value)
226 return SysInfoRes(value=value, state=state, human_value=human_value)
227
227
228
228
229 def cpu():
229 def cpu():
230 value = {'cpu': 0, 'cpu_count': 0, 'cpu_usage': []}
230 value = {'cpu': 0, 'cpu_count': 0, 'cpu_usage': []}
231 state = STATE_OK_DEFAULT
231 state = STATE_OK_DEFAULT
232
232
233 if not psutil:
233 if not psutil:
234 return SysInfoRes(value=value, state=state)
234 return SysInfoRes(value=value, state=state)
235
235
236 value['cpu'] = psutil.cpu_percent(0.5)
236 value['cpu'] = psutil.cpu_percent(0.5)
237 value['cpu_usage'] = psutil.cpu_percent(0.5, percpu=True)
237 value['cpu_usage'] = psutil.cpu_percent(0.5, percpu=True)
238 value['cpu_count'] = psutil.cpu_count()
238 value['cpu_count'] = psutil.cpu_count()
239
239
240 human_value = value.copy()
240 human_value = value.copy()
241 human_value['text'] = '{} cores at {} %'.format(
241 human_value['text'] = '{} cores at {} %'.format(
242 value['cpu_count'], value['cpu'])
242 value['cpu_count'], value['cpu'])
243
243
244 return SysInfoRes(value=value, state=state, human_value=human_value)
244 return SysInfoRes(value=value, state=state, human_value=human_value)
245
245
246
246
247 def storage():
247 def storage():
248 from rhodecode.lib.helpers import format_byte_size_binary
248 from rhodecode.lib.helpers import format_byte_size_binary
249 from rhodecode.model.settings import VcsSettingsModel
249 from rhodecode.model.settings import VcsSettingsModel
250 path = VcsSettingsModel().get_repos_location()
250 path = VcsSettingsModel().get_repos_location()
251
251
252 value = dict(percent=0, used=0, total=0, path=path, text='')
252 value = dict(percent=0, used=0, total=0, path=path, text='')
253 state = STATE_OK_DEFAULT
253 state = STATE_OK_DEFAULT
254 if not psutil:
254 if not psutil:
255 return SysInfoRes(value=value, state=state)
255 return SysInfoRes(value=value, state=state)
256
256
257 try:
257 try:
258 value.update(dict(psutil.disk_usage(path)._asdict()))
258 value.update(dict(psutil.disk_usage(path)._asdict()))
259 except Exception as e:
259 except Exception as e:
260 log.exception('Failed to fetch disk info')
260 log.exception('Failed to fetch disk info')
261 state = {'message': str(e), 'type': STATE_ERR}
261 state = {'message': str(e), 'type': STATE_ERR}
262
262
263 human_value = value.copy()
263 human_value = value.copy()
264 human_value['used'] = format_byte_size_binary(value['used'])
264 human_value['used'] = format_byte_size_binary(value['used'])
265 human_value['total'] = format_byte_size_binary(value['total'])
265 human_value['total'] = format_byte_size_binary(value['total'])
266 human_value['text'] = "{}/{}, {}% used".format(
266 human_value['text'] = "{}/{}, {}% used".format(
267 format_byte_size_binary(value['used']),
267 format_byte_size_binary(value['used']),
268 format_byte_size_binary(value['total']),
268 format_byte_size_binary(value['total']),
269 value['percent'])
269 value['percent'])
270
270
271