##// END OF EJS Templates
user-sessions: added an API call to cleanup sessions.
marcink -
r1367:11dec75f default
parent child Browse files
Show More
@@ -0,0 +1,44 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2017-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import mock
22 import pytest
23
24 from rhodecode.lib.user_sessions import FileAuthSessions
25 from rhodecode.api.tests.utils import (
26 build_data, api_call, assert_ok, assert_error, crash)
27
28
29 @pytest.mark.usefixtures("testuser_api", "app")
30 class TestCleanupSessions(object):
31 def test_api_cleanup_sessions(self):
32 id_, params = build_data(self.apikey, 'cleanup_sessions')
33 response = api_call(self.app, params)
34
35 expected = {'backend': 'file sessions', 'sessions_removed': 0}
36 assert_ok(id_, expected, given=response.body)
37
38 @mock.patch.object(FileAuthSessions, 'clean_sessions', crash)
39 def test_api_cleanup_error(self):
40 id_, params = build_data(self.apikey, 'cleanup_sessions', )
41 response = api_call(self.app, params)
42
43 expected = 'Error occurred during session cleanup'
44 assert_error(id_, expected, given=response.body)
@@ -26,6 +26,8 b' from rhodecode.api import jsonrpc_method'
26 from rhodecode.api.utils import (
26 from rhodecode.api.utils import (
27 Optional, OAttr, has_superadmin_permission, get_user_or_error)
27 Optional, OAttr, has_superadmin_permission, get_user_or_error)
28 from rhodecode.lib.utils import repo2db_mapper
28 from rhodecode.lib.utils import repo2db_mapper
29 from rhodecode.lib import system_info
30 from rhodecode.lib import user_sessions
29 from rhodecode.model.db import UserIpMap
31 from rhodecode.model.db import UserIpMap
30 from rhodecode.model.scm import ScmModel
32 from rhodecode.model.scm import ScmModel
31
33
@@ -176,3 +178,67 b' def rescan_repos(request, apiuser, remov'
176 'Error occurred during rescan repositories action'
178 'Error occurred during rescan repositories action'
177 )
179 )
178
180
181
182 @jsonrpc_method()
183 def cleanup_sessions(request, apiuser, older_then=Optional(60)):
184 """
185 Triggers a session cleanup action.
186
187 If the ``older_then`` option is set, only sessions that hasn't been
188 accessed in the given number of days will be removed.
189
190 This command can only be run using an |authtoken| with admin rights to
191 the specified repository.
192
193 This command takes the following options:
194
195 :param apiuser: This is filled automatically from the |authtoken|.
196 :type apiuser: AuthUser
197 :param older_then: Deletes session that hasn't been accessed
198 in given number of days.
199 :type older_then: Optional(int)
200
201 Example output:
202
203 .. code-block:: bash
204
205 id : <id_given_in_input>
206 result: {
207 "backend": "<type of backend>",
208 "sessions_removed": <number_of_removed_sessions>
209 }
210 error : null
211
212 Example error output:
213
214 .. code-block:: bash
215
216 id : <id_given_in_input>
217 result : null
218 error : {
219 'Error occurred during session cleanup'
220 }
221
222 """
223 if not has_superadmin_permission(apiuser):
224 raise JSONRPCForbidden()
225
226 older_then = Optional.extract(older_then)
227 older_than_seconds = 60 * 60 * 24 * older_then
228
229 config = system_info.rhodecode_config().get_value()['value']['config']
230 session_model = user_sessions.get_session_handler(
231 config.get('beaker.session.type', 'memory'))(config)
232
233 backend = session_model.SESSION_TYPE
234 try:
235 cleaned = session_model.clean_sessions(
236 older_than_seconds=older_than_seconds)
237 return {'sessions_removed': cleaned, 'backend': backend}
238 except user_sessions.CleanupCommand as msg:
239 return {'cleanup_command': msg.message, 'backend': backend}
240 except Exception as e:
241 log.exception('Failed session cleanup')
242 raise JSONRPCError(
243 'Error occurred during session cleanup'
244 )
@@ -66,8 +66,10 b' class DbAuthSessions(BaseAuthSessions):'
66
66
67 def clean_sessions(self, older_than_seconds=None):
67 def clean_sessions(self, older_than_seconds=None):
68 expiry_date = self._seconds_to_date(older_than_seconds)
68 expiry_date = self._seconds_to_date(older_than_seconds)
69 to_remove = DbSession.query().filter(DbSession.accessed < expiry_date).count()
69 DbSession.query().filter(DbSession.accessed < expiry_date).delete()
70 DbSession.query().filter(DbSession.accessed < expiry_date).delete()
70 Session().commit()
71 Session().commit()
72 return to_remove
71
73
72
74
73 class FileAuthSessions(BaseAuthSessions):
75 class FileAuthSessions(BaseAuthSessions):
@@ -78,9 +80,11 b' class FileAuthSessions(BaseAuthSessions)'
78 return data_dir
80 return data_dir
79
81
80 def _count_on_filesystem(self, path, older_than=0, callback=None):
82 def _count_on_filesystem(self, path, older_than=0, callback=None):
81 value = dict(percent=0, used=0, total=0, items=0, path=path, text='')
83 value = dict(percent=0, used=0, total=0, items=0, callbacks=0,
84 path=path, text='')
82 items_count = 0
85 items_count = 0
83 used = 0
86 used = 0
87 callbacks = 0
84 cur_time = time.time()
88 cur_time = time.time()
85 for root, dirs, files in os.walk(path):
89 for root, dirs, files in os.walk(path):
86 for f in files:
90 for f in files:
@@ -91,6 +95,7 b' class FileAuthSessions(BaseAuthSessions)'
91 items_count += 1
95 items_count += 1
92 if callback:
96 if callback:
93 callback_res = callback(final_path)
97 callback_res = callback(final_path)
98 callbacks += 1
94 else:
99 else:
95 used += os.path.getsize(final_path)
100 used += os.path.getsize(final_path)
96 except OSError:
101 except OSError:
@@ -99,7 +104,8 b' class FileAuthSessions(BaseAuthSessions)'
99 'percent': 100,
104 'percent': 100,
100 'used': used,
105 'used': used,
101 'total': used,
106 'total': used,
102 'items': items_count
107 'items': items_count,
108 'callbacks': callbacks
103 })
109 })
104 return value
110 return value
105
111
@@ -128,9 +134,10 b' class FileAuthSessions(BaseAuthSessions)'
128 def remove_item(path):
134 def remove_item(path):
129 os.remove(path)
135 os.remove(path)
130
136
131 return self._count_on_filesystem(
137 stats = self._count_on_filesystem(
132 sessions_dir, older_than=older_than_seconds,
138 sessions_dir, older_than=older_than_seconds,
133 callback=remove_item)['items']
139 callback=remove_item)
140 return stats['callbacks']
134
141
135
142
136 class MemcachedAuthSessions(BaseAuthSessions):
143 class MemcachedAuthSessions(BaseAuthSessions):
General Comments 0
You need to be logged in to leave comments. Login now