Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / test / stress.py @ f12bcb3d

History | View | Annotate | Download (7.3 kB)

1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3

    
4
# Copyright 2013 GRNET S.A. All rights reserved.
5
#
6
# Redistribution and use in source and binary forms, with or
7
# without modification, are permitted provided that the following
8
# conditions are met:
9
#
10
#   1. Redistributions of source code must retain the above
11
#      copyright notice, this list of conditions and the following
12
#      disclaimer.
13
#
14
#   2. Redistributions in binary form must reproduce the above
15
#      copyright notice, this list of conditions and the following
16
#      disclaimer in the documentation and/or other materials
17
#      provided with the distribution.
18
#
19
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
20
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
23
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
# POSSIBILITY OF SUCH DAMAGE.
31
#
32
# The views and conclusions contained in the software and
33
# documentation are those of the authors and should not be
34
# interpreted as representing official policies, either expressed
35
# or implied, of GRNET S.A.
36

    
37
import os
38
from optparse import OptionParser
39
from time import sleep
40
import threading
41
import datetime
42
from random import choice, randint
43
import logging
44

    
45
path = os.path.dirname(os.path.realpath(__file__))
46
os.environ['SYNNEFO_SETTINGS_DIR'] = path + '/settings'
47
os.environ['DJANGO_SETTINGS_MODULE'] = 'synnefo.settings'
48

    
49
from astakos.im.models import AstakosUser
50
from astakos.im.functions import get_related_project_id, ProjectError
51
from astakos.im import quotas
52
from views import submit, approve, join, leave
53
from snf_django.lib.db.transaction import commit_on_success_strict
54

    
55
USERS = {}
56
PROJECTS = {}
57

    
58
logger = logging.getLogger(__name__)
59
logger.setLevel(logging.INFO)
60

    
61

    
62
def random_name():
63
    alphabet = u'abcdef_123490αβγδεζ'
64
    length = randint(1, 15)
65
    return ''.join(choice(alphabet) for _ in xrange(length))
66

    
67

    
68
def random_email():
69
    alphabet = u'abcdef_123490'
70
    length = randint(1, 10)
71
    first = ''.join(choice(alphabet) for _ in xrange(length))
72

    
73
    alphabet = u'abcdef'
74
    length = randint(2, 4)
75
    last = ''.join(choice(alphabet) for _ in xrange(length))
76
    return first + '@' + last + '.com'
77

    
78

    
79
def new_user():
80
    email = random_email()
81
    defaults = {'first_name': random_name(),
82
                'last_name': random_name(),
83
                'is_active': True,
84
                }
85
    u, created = AstakosUser.objects.get_or_create(
86
        email=email, defaults=defaults)
87
    if created:
88
        quotas.qh_sync_user(u)
89
        return u.id, u.email
90
    return None
91

    
92

    
93
@commit_on_success_strict()
94
def new_users(count):
95
    for i in range(count):
96
        while True:
97
            result = new_user()
98
            if result is not None:
99
                uid, email = result
100
                USERS[uid] = email
101
                break
102

    
103

    
104
class SubmitApproveT(threading.Thread):
105
    def __init__(self, *args, **kwargs):
106
        self.repeat = kwargs.pop('repeat', 1)
107
        threading.Thread.__init__(self, *args, **kwargs)
108

    
109
    def run(self):
110
        owner = choice(USERS.keys())
111
        p_name = random_name()
112
        submit_and_approve(p_name, owner, None, self.repeat,
113
                           prefix=self.name)
114

    
115

    
116
def submit_and_approve(name, user_id, prec, repeat, prefix=""):
117
    if prefix:
118
        prefix += ' '
119

    
120
    for i in range(repeat):
121
        try:
122
            now = datetime.datetime.now()
123
            logger.info('%s%s: submitting with precursor %s'
124
                        % (prefix, now, prec))
125
            app_id = submit(name, user_id, prec)
126
            prec = app_id
127
        except ProjectError as e:
128
            logger.info('Limit reached')
129
        except Exception as e:
130
            logger.exception(e)
131
            continue
132
        try:
133
            now = datetime.datetime.now()
134
            pid = get_related_project_id(app_id)
135
            logger.info('%s%s: approving application %s of project %s'
136
                        % (prefix, now, app_id, pid))
137
            approve(app_id)
138
            PROJECTS[pid] = True
139
        except Exception as e:
140
            logger.exception(e)
141

    
142

    
143
class JoinLeaveT(threading.Thread):
144
    def __init__(self, *args, **kwargs):
145
        self.repeat = kwargs.pop('repeat', 1)
146
        threading.Thread.__init__(self, *args, **kwargs)
147

    
148
    def run(self):
149
        owner = choice(USERS.keys())
150
        while True:
151
            projects = PROJECTS.keys()
152
            if projects:
153
                pid = choice(projects)
154
                break
155
            sleep(0.1)
156
        join_and_leave(pid, owner, self.repeat, prefix=self.name)
157

    
158

    
159
def join_and_leave(proj_id, user_id, repeat, prefix=""):
160
    if prefix:
161
        prefix += ' '
162

    
163
    for i in range(repeat):
164
        try:
165
            now = datetime.datetime.now()
166
            logger.info('%s%s: user %s joining project %s'
167
                        % (prefix, now, user_id, proj_id))
168
            join(proj_id, user_id)
169
        except ProjectError as e:
170
            logger.info('Membership already exists')
171
        except Exception as e:
172
            logger.exception(e)
173

    
174
        try:
175
            now = datetime.datetime.now()
176
            logger.info('%s%s: user %s leaving project %s'
177
                        % (prefix, now, user_id, proj_id))
178
            leave(proj_id, user_id)
179
        except ProjectError as e:
180
            logger.info('No such membership')
181
        except Exception as e:
182
            logger.exception(e)
183

    
184

    
185
def test(users, projects, memb, repeat):
186
    logging.basicConfig()
187

    
188
    new_users(users)
189

    
190
    for i in range(projects):
191
        SubmitApproveT(repeat=repeat).start()
192

    
193
    for i in range(memb):
194
        JoinLeaveT(repeat=repeat).start()
195

    
196
    for thread in threading.enumerate():
197
        if thread is not threading.currentThread():
198
            thread.join()
199

    
200

    
201
def main():
202
    parser = OptionParser()
203
    parser.add_option('--users',
204
                      dest='users',
205
                      default=2,
206
                      help="Number of users (default=2)")
207
    parser.add_option('--projects',
208
                      dest='projects',
209
                      default=2,
210
                      help="Number of projects (default=2)")
211
    parser.add_option('--memb',
212
                      dest='memb',
213
                      default=2,
214
                      help="Number of membership requests (default=2)")
215
    parser.add_option('--repeat',
216
                      dest='repeat',
217
                      default=20,
218
                      help="Number of iterations (default=20)")
219
    parser.add_option('-q', '--quiet',
220
                      action='store_true',
221
                      dest='quiet',
222
                      default=False,
223
                      help="Print only errors")
224

    
225
    (options, args) = parser.parse_args()
226

    
227
    if options.quiet:
228
        logger.setLevel(logging.WARNING)
229

    
230
    users = int(options.users)
231
    projects = int(options.projects)
232
    memb = int(options.memb)
233
    repeat = int(options.repeat)
234
    test(users, projects, memb, repeat)
235

    
236

    
237
if __name__ == "__main__":
238
    main()