From 31b527a9acc8607e5e03927b3b646b7c832e7058 Mon Sep 17 00:00:00 2001 From: drebs Date: Mon, 2 May 2016 20:25:45 -0300 Subject: refactor and make complete test run with script --- scripts/__init__.py | 0 scripts/create_payload.py | 29 +++++++++++ scripts/get_client_stats.py | 20 ++++++++ scripts/get_ping_rate.py | 19 +++++++ scripts/measure_perf_series.py | 31 +++++++++++ scripts/preload_server_database.py | 39 ++++++++++++++ scripts/server_with_soledad_syncer.py | 47 +++++++++++++++++ scripts/soledad_sync.py | 96 +++++++++++++++++++++++++++++++++++ scripts/sync_stats.gnuplot | 23 +++++++++ 9 files changed, 304 insertions(+) create mode 100644 scripts/__init__.py create mode 100755 scripts/create_payload.py create mode 100755 scripts/get_client_stats.py create mode 100755 scripts/get_ping_rate.py create mode 100755 scripts/measure_perf_series.py create mode 100755 scripts/preload_server_database.py create mode 100644 scripts/server_with_soledad_syncer.py create mode 100644 scripts/soledad_sync.py create mode 100755 scripts/sync_stats.gnuplot (limited to 'scripts') diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/create_payload.py b/scripts/create_payload.py new file mode 100755 index 0000000..d051661 --- /dev/null +++ b/scripts/create_payload.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +""" +Create a payload file in disk to use during tests. Reads the name of the file +and the intended size of the payload from the "defaults.conf" file: + + PAYLOAD = /path/to/payload/file + PAYLOAD_SIZE = 500 # in Kb +""" + +import os +from ConfigParser import ConfigParser + +parser = ConfigParser() +parser.read('defaults.conf') + +PAYLOAD = parser.get('sync', 'payload') +PAYLOAD_SIZE = int(parser.get('sync', 'payload_size')) * 1024 + +if os.path.isfile(PAYLOAD): + os.unlink(PAYLOAD) + +content = 'a' * 1024 + +with open(PAYLOAD, 'w') as f: + length = 0 + while length < PAYLOAD_SIZE: + f.write(content) + length += len(content) diff --git a/scripts/get_client_stats.py b/scripts/get_client_stats.py new file mode 100755 index 0000000..59f9aa6 --- /dev/null +++ b/scripts/get_client_stats.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +""" +Get stats from dummy server running Soledad Client and spit them out. +""" + +import commands +import urllib +import psutil + +stats = urllib.urlopen('http://localhost:8080/stats').read().split() + +pid, sync_phase, sync_exchange_phase = stats + +res = commands.getoutput("ps -p " + pid + " -o \%cpu,\%mem") +splitted = res.split() +cpu = splitted[2] +mem = splitted[3] + +print cpu, mem, sync_phase, sync_exchange_phase diff --git a/scripts/get_ping_rate.py b/scripts/get_ping_rate.py new file mode 100755 index 0000000..19d63d1 --- /dev/null +++ b/scripts/get_ping_rate.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +""" +Measure the connection rate to the dummy server running Soledad Client using +httperf and spit it out. +""" + +import commands +import re + +# ORIGINAL, STANDARD MEASURE (TOTALLY RANDOM) +#res = commands.getoutput( + #'httperf --server localhost --port 8080 --num-calls 5 --num-conns 20 ' + #'--uri /ping | grep "Connection rate"') + +res = commands.getoutput( + 'httperf --server localhost --port 8080 --num-calls 5 --num-conns 10 ' + '--uri /ping | grep "Connection rate"') +print re.findall('[-+]?([0-9]*\.?[0-9]+)', res)[0] diff --git a/scripts/measure_perf_series.py b/scripts/measure_perf_series.py new file mode 100755 index 0000000..587ff7a --- /dev/null +++ b/scripts/measure_perf_series.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +import commands +import datetime + +# How many points to measure. Make sure that you leave the baseline +# running for a significative amount before triggering the sync, it's possible +# you have significant variability in there. +POINTS = 400 + +SCALE = 50 + +commands.getoutput('echo time req/s cpu mem sync_phase sync_exchange_phase > ./out/series.log') +start = datetime.datetime.now() + +for i in range(POINTS): + value = commands.getoutput('./scripts/get_ping_rate.py') + print "Step", i, "...", value + stats = commands.getoutput('./scripts/get_client_stats.py') + cpu, mem, sync_phase, sync_exchange_phase = stats.split() + sync_phase = str(int(sync_phase)*SCALE) + sync_exchange_phase = str(int(sync_exchange_phase)*SCALE) + now = datetime.datetime.now() + secs = (now - start).total_seconds() + try: + # make it so the script exits succesfully when the server is dead + commands.getoutput( + 'echo %s\t%s\t%s\t%s\t%s\t%s >> ./out/series.log' \ + % (secs, value, cpu, mem, sync_phase, sync_exchange_phase)) + except ValueError: + break diff --git a/scripts/preload_server_database.py b/scripts/preload_server_database.py new file mode 100755 index 0000000..95e8cfd --- /dev/null +++ b/scripts/preload_server_database.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +""" +Preload the server database with a certain amount of documents so we can +receive them during the sync process and gather meaningful statistics for that +specific phase of sync. + +Gets uuid, payload file path and amount of documents to preload from +"defaults.conf" config file: + + UUID = some-uuid + PAYLOAD = /path/to/payload/file + NUM_DOCS = 100 +""" + +import os +from ConfigParser import ConfigParser +from leap.soledad.common.couch import CouchDatabase + +parser = ConfigParser() +parser.read('defaults.conf') + +UUID = parser.get('client', 'uuid') +PAYLOAD = parser.get('sync', 'payload') +NUM_DOCS = int(parser.get('sync', 'num_docs')) + +db = CouchDatabase.open_database( + 'http://127.0.0.1:5984/user-%s' % UUID, + False) # should create database? + +payload = None +if os.path.isfile(PAYLOAD): + with open(PAYLOAD, 'r') as f: + payload = f.read() + +for i in xrange(NUM_DOCS): + db.create_doc({'payload': payload}) + +db.close() diff --git a/scripts/server_with_soledad_syncer.py b/scripts/server_with_soledad_syncer.py new file mode 100644 index 0000000..2890e04 --- /dev/null +++ b/scripts/server_with_soledad_syncer.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python + + +import os +from klein import run, route, resource +import soledad_sync as sync +from twisted.internet import reactor +import datetime + + +@route('/create-docs') +def create_docs(request): + d = sync.create_docs() + return d + + +@route('/start-sync') +def start_sync(request): + d = sync.start_sync() + return d + + +@route('/ping') +def ping(request): + return 'easy!' + + +@route('/pid') +def pid(request): + return str(os.getpid()) + + +@route('/stop') +def stop(request): + reactor.callLater(1, reactor.stop) + return '' + +@route('/stats') +def stats(request): + pid = os.getpid() + sync_phase, sync_exchange_phase = sync.stats() + return "%d %d %d" % (pid, sync_phase, sync_exchange_phase) + + +if __name__ == "__main__": + run("localhost", 8080) diff --git a/scripts/soledad_sync.py b/scripts/soledad_sync.py new file mode 100644 index 0000000..8c6442f --- /dev/null +++ b/scripts/soledad_sync.py @@ -0,0 +1,96 @@ +import os +import json +from ConfigParser import ConfigParser +from leap.soledad.client.api import Soledad +from twisted.internet import defer, reactor +from twisted.internet.task import deferLater + + +# get configs from file +parser = ConfigParser() +parser.read('defaults.conf') + +HOST = parser.get('server', 'host') + +UUID = parser.get('client', 'uuid') +CLIENT_BASEDIR = parser.get('client', 'basedir') +PASSPHRASE = parser.get('client', 'passphrase') + +NUM_DOCS = int(parser.get('sync', 'num_docs')) +PAYLOAD = parser.get('sync', 'payload') +AUTH_TOKEN = parser.get('sync', 'auth_token') + +STATS_FILE = parser.get('test', 'stats_file') + + +DO_THESEUS = os.environ.get('THESEUS', False) + + +def _get_soledad_instance_from_uuid(uuid, passphrase, basedir, server_url, + cert_file, token): + secrets_path = os.path.join(basedir, '%s.secret' % uuid) + local_db_path = os.path.join(basedir, '%s.db' % uuid) + return Soledad( + uuid, + unicode(passphrase), + secrets_path=secrets_path, + local_db_path=local_db_path, + server_url=server_url, + cert_file=cert_file, + auth_token=token, + defer_encryption=True, + syncable=True) + + +def _get_soledad_instance(): + return _get_soledad_instance_from_uuid( + UUID, PASSPHRASE, CLIENT_BASEDIR, HOST, '', AUTH_TOKEN) + +s = _get_soledad_instance() + + +def create_docs(): + global s + # get content for docs + payload = 'a' * 10 + if os.path.isfile(PAYLOAD): + with open(PAYLOAD, 'r') as f: + payload = f.read() + + # create docs + cd = [] + for i in range(NUM_DOCS): + cd.append(s.create_doc({'payload': payload})) + d = defer.gatherResults(cd) + + d.addCallback(lambda _: s.get_all_docs()) + d.addCallback(lambda result: "%d docs created, %d docs on db" % (NUM_DOCS, result[0])) + #d.addCallback(lambda _: s.close()) + + return d + + +def start_sync(): + global s + + if DO_THESEUS: + from theseus import Tracer + t = Tracer() + t.install() + + def stop_tracing(_): + if DO_THESEUS: + with open('callgrind.theseus', 'wb') as outfile: + t.write_data(outfile) + print "STOPPED TRACING, DUMPED IN CALLGRIND.THESEUS<<<<" + + cd = [] + + d = s.sync() + d.addCallback(stop_tracing) + + return d + +def stats(): + global s + return s.sync_stats() diff --git a/scripts/sync_stats.gnuplot b/scripts/sync_stats.gnuplot new file mode 100755 index 0000000..7fdf29a --- /dev/null +++ b/scripts/sync_stats.gnuplot @@ -0,0 +1,23 @@ +#!/usr/bin/gnuplot + +infile="$0" +outfile="$1" + +# maybe save image file +if (outfile ne '') \ + set term png size 1000,400; \ + set output "./out/sync-stats.png" + +# make the graph beautiful +set title 'Soledad Sync Phases' +set xtics 10 +set ytics 50 +set grid +set key outside + +# plot! +plot for [col=2:6] infile using 1:col with linespoints title columnheader + +# pause when not saving image file +if (outfile eq '') \ + pause -1 -- cgit v1.2.3