diff options
Diffstat (limited to 'scripts/scalability/test_controller')
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 |