summaryrefslogtreecommitdiff
path: root/scripts/scalability/test_controller
diff options
context:
space:
mode:
authordrebs <drebs@riseup.net>2017-10-28 07:58:51 -0200
committerdrebs <drebs@riseup.net>2017-11-02 09:36:31 -0200
commit21584bc33fdc672a0f59436ba5d66f66439d6366 (patch)
tree8e8b58fbfc82ef9898cd8cc6f6e2960b121be3d1 /scripts/scalability/test_controller
parente3879ac206e66437cefbe968e77f757239640681 (diff)
[benchmarks] add code for stressing the server
Diffstat (limited to 'scripts/scalability/test_controller')
-rw-r--r--scripts/scalability/test_controller/client/Blobs.conf34
-rw-r--r--scripts/scalability/test_controller/client/logs/.empty0
-rw-r--r--scripts/scalability/test_controller/client/makefile19
-rw-r--r--scripts/scalability/test_controller/client/results/.empty0
-rwxr-xr-xscripts/scalability/test_controller/client/templates.py22
-rw-r--r--scripts/scalability/test_controller/client/test_Blobs.py80
-rw-r--r--scripts/scalability/test_controller/server/blobs.py50
-rw-r--r--scripts/scalability/test_controller/server/server.tac31
-rw-r--r--scripts/scalability/test_controller/utils.py23
9 files changed, 239 insertions, 20 deletions
diff --git a/scripts/scalability/test_controller/client/Blobs.conf b/scripts/scalability/test_controller/client/Blobs.conf
new file mode 100644
index 00000000..d5a2761f
--- /dev/null
+++ b/scripts/scalability/test_controller/client/Blobs.conf
@@ -0,0 +1,34 @@
+[main]
+title=Blobs Scalability Tests
+description=Upload and download blobs
+url=http://giraffe.cdev.bitmask.net:2424/
+size=10
+templates_dir=/tmp/templates
+
+[test_upload]
+description=Upload blobs stress test
+
+[test_download]
+description=Download blobs stress test
+
+[ftest]
+log_to = console file
+log_path = logs/blobs-test.log
+result_path = results/blobs-test.xml
+sleep_time_min = 0
+sleep_time_max = 0
+
+[bench]
+cycles = 25:50:75:100:125:150:175:200
+duration = 30
+#startup_delay = 0
+#sleep_time = 0
+#cycle_time = 0
+startup_delay = 0.01
+sleep_time = 0.01
+cycle_time = 1
+log_to = console file
+log_path = logs/blobs-bench.log
+result_path = results/blobs-bench.xml
+sleep_time_min = 0
+sleep_time_max = 0
diff --git a/scripts/scalability/test_controller/client/logs/.empty b/scripts/scalability/test_controller/client/logs/.empty
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/scripts/scalability/test_controller/client/logs/.empty
diff --git a/scripts/scalability/test_controller/client/makefile b/scripts/scalability/test_controller/client/makefile
new file mode 100644
index 00000000..33e67e25
--- /dev/null
+++ b/scripts/scalability/test_controller/client/makefile
@@ -0,0 +1,19 @@
+bench: upload download
+
+upload:
+ curl -X POST "http://127.0.0.1:7001/blobs?action=delete"
+ fl-run-bench -f test_Blobs.py Blobs.test_upload
+ fl-build-report --html results/blobs-bench.xml
+
+download:
+ curl -X POST "http://127.0.0.1:7001/blobs?action=create&size=10&amount=5000"
+ fl-run-bench -f test_Blobs.py Blobs.test_download
+ fl-build-report --html results/blobs-bench.xml
+
+test_upload:
+ curl -X POST "http://127.0.0.1:7001/blobs?action=delete"
+ fl-run-bench -c 1 --duration 10 -f test_Blobs.py Blobs.test_upload
+
+test_download:
+ curl -X POST "http://127.0.0.1:7001/blobs?action=create&size=10"
+ fl-run-bench -c 1 --duration 10 -f test_Blobs.py Blobs.test_download
diff --git a/scripts/scalability/test_controller/client/results/.empty b/scripts/scalability/test_controller/client/results/.empty
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/scripts/scalability/test_controller/client/results/.empty
diff --git a/scripts/scalability/test_controller/client/templates.py b/scripts/scalability/test_controller/client/templates.py
index dcf51fe1..434eda67 100755
--- a/scripts/scalability/test_controller/client/templates.py
+++ b/scripts/scalability/test_controller/client/templates.py
@@ -1,10 +1,7 @@
#!/usr/bin/env python
-import base64
-import errno
import mock
import os
-import random
from argparse import ArgumentParser
from io import BytesIO
@@ -14,6 +11,8 @@ from twisted.internet import reactor, defer
from leap.soledad.client._db.blobs import BlobManager
+from test_controller.utils import mkdir_p, payload
+
DEFAULT_TARGET_DIR = './blob-templates'
@@ -33,28 +32,11 @@ def _get_encrypt_function(user, path):
return manager._encrypt_and_upload
-def payload(size):
- random.seed(1337) # same seed to avoid different bench results
- payload_bytes = bytearray(random.getrandbits(8) for _ in xrange(size))
- # encode as base64 to avoid ascii encode/decode errors
- return base64.b64encode(payload_bytes)[:size] # remove b64 overhead
-
-
def _encrypt(path, data):
encrypt = _get_encrypt_function('user-0', path)
return encrypt('blob', BytesIO(data))
-def mkdir_p(path):
- try:
- os.makedirs(path)
- except OSError as exc: # Python >2.5
- if exc.errno == errno.EEXIST and os.path.isdir(path):
- pass
- else:
- raise
-
-
def create_blob_templates(target_dir, amount, size):
mkdir_p(target_dir)
data = payload(size)
diff --git a/scripts/scalability/test_controller/client/test_Blobs.py b/scripts/scalability/test_controller/client/test_Blobs.py
new file mode 100644
index 00000000..5f31dbc8
--- /dev/null
+++ b/scripts/scalability/test_controller/client/test_Blobs.py
@@ -0,0 +1,80 @@
+import base64
+import os
+import subprocess
+import unittest
+import urlparse
+import threading
+
+from funkload.FunkLoadTestCase import FunkLoadTestCase
+from webunit.utility import Upload
+
+
+SIZE = 100 # KB
+DELETE_ON_SETUP = False
+DELETE_ON_TEARDOWN = False
+
+
+def _get_auth_header(user_id):
+ encoded = base64.b64encode('%s:%s-token' % (user_id, user_id))
+ return 'Token %s' % encoded
+
+
+def _ensure_template(templates_dir, size):
+ fname = '%dK-0.blob' % size
+ fpath = os.path.join(templates_dir, fname)
+ if not os.path.isfile(fpath):
+ dirname = os.path.dirname(os.path.realpath(__file__))
+ executable = os.path.join(dirname, 'templates.py')
+ code = subprocess.check_call([
+ executable,
+ '--amount', '1',
+ '--size', str(size),
+ templates_dir,
+ ])
+ assert code == 0, 'failed creating template'
+ return fpath
+
+
+class Blobs(FunkLoadTestCase):
+
+ next_user_id = 0
+ lock = threading.Lock()
+
+ def _get_next_user_id(self):
+ with Blobs.lock:
+ user_id = Blobs.next_user_id
+ Blobs.next_user_id += 1
+ Blobs.next_user_id %= 5000
+ return user_id
+
+ def setUp(self):
+ user_id = self._get_next_user_id()
+ base_url = self.conf_get('main', 'url')
+ self.url = urlparse.urljoin(base_url, 'blobs/%s/0' % user_id)
+ self.setHeader('Authorization', _get_auth_header(user_id))
+ templates_dir = self.conf_get('main', 'templates_dir')
+ size = self.conf_getInt('main', 'size')
+ fpath = _ensure_template(templates_dir, size)
+ self.upload = ['file', Upload(fpath)]
+ if DELETE_ON_SETUP:
+ ret = self.delete(self.url, description='Delete blob on setUp')
+ self.assert_(ret.code in [404, 200], 'expected 404 or 200')
+
+ def tearDown(self):
+ if DELETE_ON_TEARDOWN:
+ ret = self.delete(self.url, description='Delete blob on tearDown')
+ self.assert_(ret.code in [404, 200], 'expected 404 or 200')
+ self.clearHeaders()
+
+ def test_upload(self):
+ ret = self.put(self.url, params=[self.upload],
+ description='Upload blob')
+ self.assert_(ret.code == 200, "expecting a 200")
+
+ def test_download(self):
+ ret = self.get(self.url, description='Download blob')
+ self.assert_(ret.code == 200, "expecting a 200")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/scripts/scalability/test_controller/server/blobs.py b/scripts/scalability/test_controller/server/blobs.py
new file mode 100644
index 00000000..2a60018d
--- /dev/null
+++ b/scripts/scalability/test_controller/server/blobs.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+
+import shutil
+import os
+
+from argparse import ArgumentParser
+
+from test_controller.utils import mkdir_p, payload
+
+
+def _create_blob(path, data):
+ if not os.path.isfile(path):
+ with open(path, 'w') as f:
+ f.write(data)
+
+
+def create_blobs(target_dir, amount, size):
+ data = payload(size * 1000)
+ for i in xrange(amount):
+ basedir = os.path.join(target_dir, '%d/default/0/0/0' % i)
+ mkdir_p(basedir)
+ _create_blob(os.path.join(basedir, '0'), data)
+
+
+def delete_blobs(target_dir):
+ if not os.path.isdir(target_dir):
+ return
+ for f in os.listdir(target_dir):
+ if f.isdigit():
+ directory = os.path.join(target_dir, f)
+ shutil.rmtree(directory)
+
+
+def parse_args():
+ parser = ArgumentParser()
+ parser.add_argument(
+ 'target_dir',
+ help='The target directory where templates will be written to.')
+ parser.add_argument(
+ '--amount', default=1000, type=int,
+ help='The amount of users to create blobs to.')
+ parser.add_argument(
+ '--size', default=1000, type=int,
+ help='The size of each template in KB.')
+ return parser.parse_args()
+
+
+if __name__ == '__main__':
+ args = parse_args()
+ create_blobs(args.target_dir, args.amount, args.size * 1000)
diff --git a/scripts/scalability/test_controller/server/server.tac b/scripts/scalability/test_controller/server/server.tac
index 3d5ea8cc..d5176319 100644
--- a/scripts/scalability/test_controller/server/server.tac
+++ b/scripts/scalability/test_controller/server/server.tac
@@ -43,9 +43,12 @@ import psutil
from twisted.application import service, internet
from twisted.web import resource, server
from twisted.internet.task import LoopingCall
+from twisted.internet.threads import deferToThread
from twisted.logger import Logger
from test_controller.server.user_dbs import ensure_dbs
+from test_controller.server.blobs import create_blobs
+from test_controller.server.blobs import delete_blobs
DEFAULT_HTTP_PORT = 7001
@@ -224,6 +227,33 @@ class SetupResource(resource.Resource):
request.finish()
+class BlobsResource(resource.Resource):
+
+ def render_POST(self, request):
+ action = (request.args.get('action') or ['create']).pop()
+ amount = int((request.args.get('amount') or [1000]).pop())
+ size = int((request.args.get('size') or [1000]).pop())
+ if action == 'create':
+ d = deferToThread(create_blobs, '/tmp/soledad-server/blobs',
+ amount, size)
+ elif action == 'delete':
+ d = deferToThread(delete_blobs, '/tmp/soledad-server/blobs')
+ d.addCallback(self._success, request)
+ d.addErrback(self._error, request)
+ return server.NOT_DONE_YET
+
+ def _success(self, _, request):
+ request.write(SUCCESS)
+ request.finish()
+
+ def _error(self, e, request):
+ message = e.getErrorMessage() if e.getErrorMessage() else repr(e)
+ logger.error('Error processing request: %s' % message)
+ request.setResponseCode(500)
+ request.write(json.dumps({'error': str(e)}))
+ request.finish()
+
+
class Root(resource.Resource):
def __init__(self):
@@ -231,6 +261,7 @@ class Root(resource.Resource):
self.putChild('mem', MonitorResource(MemoryWatcher))
self.putChild('cpu', MonitorResource(CpuWatcher))
self.putChild('setup', SetupResource())
+ self.putChild('blobs', BlobsResource())
application = service.Application("Resource Monitor")
diff --git a/scripts/scalability/test_controller/utils.py b/scripts/scalability/test_controller/utils.py
new file mode 100644
index 00000000..ebe55571
--- /dev/null
+++ b/scripts/scalability/test_controller/utils.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+import base64
+import errno
+import os
+import random
+
+
+def payload(size):
+ random.seed(1337) # same seed to avoid different bench results
+ payload_bytes = bytearray(random.getrandbits(8) for _ in xrange(size))
+ # encode as base64 to avoid ascii encode/decode errors
+ return base64.b64encode(payload_bytes)[:size] # remove b64 overhead
+
+
+def mkdir_p(path):
+ try:
+ os.makedirs(path)
+ except OSError as exc: # Python >2.5
+ if exc.errno == errno.EEXIST and os.path.isdir(path):
+ pass
+ else:
+ raise