From f4c77e9ddda64faa1715ab31322ab8a5e698d380 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 19 May 2015 06:26:10 -0700 Subject: [PATCH 01/28] step one in validation --- src/mpIterator.py | 152 ++++++++++++++++++++++++++++++++++++++++++ src/mpTcptraceData.py | 46 +++++++++++++ src/validation.yml | 8 +++ 3 files changed, 206 insertions(+) create mode 100755 src/mpIterator.py create mode 100755 src/mpTcptraceData.py create mode 100644 src/validation.yml diff --git a/src/mpIterator.py b/src/mpIterator.py new file mode 100755 index 0000000..c20149d --- /dev/null +++ b/src/mpIterator.py @@ -0,0 +1,152 @@ +#!/usr/bin/python + +import sys, getopt +from mpXpRunner import MpXpRunner +from mpTopo import MpTopo + +from shutil import copy +import os +from subprocess import call + +import datetime +from mpTcptraceData import * + +from yaml import load, dump + +topoParamFile = None +xpParamFile = None +topoBuilder = "mininet" + +class MinValueValidation: + def __init__(self, compared): + self.compared=compared + def validate(self, value): + return self.compared<=value + +class NumberOfFlowsTest: + def __init__(self, yml, trace): + self.yml = yml["validations"] + self.trace = trace + def validate(self): + print self.yml + tested_value = self.trace.number_of_flows + for k,v in self.yml.iteritems(): + name = k.title().replace("_","")+"Validation" + tester_klass=globals()[name] + tester = tester_klass(v) + if tester.validate(tested_value): + print "SUCCESS" + else: + print "FAIL" + print k,v + + + +class TcptraceValidator: + def __init__(self, yml, trace): + self.yml = yml["tcptrace"] + self.trace = trace + def validate(self): + for test in self.yml: + name=test["test"].title().replace("_","")+"Test" + klass = globals()[name] + r = klass(test, self.trace) + r.validate() + + + +def printHelp(): + print("Help Menu") + +def parseArgs(argv): + global topoParamFile + global xpParamFile + try: + opts, args = getopt.getopt(argv, "ht:x:", ["topoParam=","xp="]) + except getopt.GetoptError: + printHelp() + sys.exit(1) + for opt, arg in opts: + if opt == "-h": + printHelp() + sys.exit(1) + elif opt in ("-x","--xp"): + xpParamFile = arg + elif opt in ("-t","--topoParam"): + print("hey") + topoParamFile = arg + if topoParamFile is None: + print("Missing the topo...") + printHelp() + sys.exit(1) +def write_entry(f, key, val): + f.write("{}:{}\n".format(key,val)) + +def generateTopo(): + path="/tmp/topo" + f=open(path,"w") + # delay, queueSize (in packets), bw + write_entry(f, "path_0", "10,15,5") + write_entry(f, "path_1", "10,15,5") + write_entry(f, "topoType", "MultiIf") + f.close() + return path + +def generateXp(): + path="/tmp/xp" + f=open(path,"w") + write_entry(f, "xpType", "nc") + write_entry(f, "kpm", "fullmesh") + write_entry(f, "kpms", "netlink") + write_entry(f, "kpmc", "netlink") + write_entry(f, "upmc", "fullmesh") +# write_entry(f, "upmc_args", "-t 600000 -i 500 -c 7800") + write_entry(f, "ddCount", "10000") + write_entry(f, "clientPcap", "yes") + write_entry(f, "ncClientPort_0", "0:33400") + write_entry(f, "rmem","300000 300000 300000") + f.close() + return path + +timestamp=datetime.datetime.now().isoformat() +#topoFile=generateTopo() +#print(topoFile) +#xpFile=generateXp() +#print(xpFile) + +topoFile="./conf/topo/simple_para" +xpFile="./conf/xp/4_nc" + +#MpXpRunner(MpTopo.mininetBuilder, topoFile, xpFile) + +destDir="/tmp/dest" +if not os.path.exists(destDir): + os.makedirs(destDir) + +#copy log files +copy("client.pcap",destDir) +#copy xp and topo +copy(topoFile,destDir) +copy(xpFile,destDir) + +#os.chdir(destDir) +#print(os.getcwd()) +#call(["/usr/local/bin/mptcptrace", "-f", "/tmp/dest/client.pcap", "-G20", "-F3", "-r7", "-s", "-S", "-a"]) + +t = TcptraceData("/tmp/dest/client.pcap") +print "Number of flows:", t.number_of_flows +print "Time for fist flow:", t.first_packet(1) + +validation_file="validation.yml" +with open(validation_file, 'r') as f: + validations = load(f) +print validations + +tcptrace_validator = TcptraceValidator(validations, t ) +print "WILL VALIDATE" +tcptrace_validator.validate() + +#for v in validations["tcptrace"]: +# print dump(v) + +# /usr/local/bin/mptcptrace -f /tmp/dest/client.pcap -G20 -F3 -r7 -s -S -a diff --git a/src/mpTcptraceData.py b/src/mpTcptraceData.py new file mode 100755 index 0000000..56a8e18 --- /dev/null +++ b/src/mpTcptraceData.py @@ -0,0 +1,46 @@ +#!/usr/bin/python + + +from subprocess import check_output +import csv + +from io import StringIO + + + +class TcptraceData: + def __init__(self, pcap_file): + self.pcap_file=pcap_file + csv_content = check_output(["tcptrace", "-l", "--csv", pcap_file]) + tcptrace_reader = csv.reader(filter(lambda l: len(l)>0 and l[0]!="#",csv_content.splitlines())) + cells=list(tcptrace_reader) + self.headers=cells[0] + self.flows=cells[1:] + self.number_of_flows=len(self.flows) + # gets cell corresponding to flow with header column + # flow 0 = first one, from 1=subflows + def get(self, flow, column): + if flow>self.number_of_flows-1: + raise Exception("Bad flow index") + return self.flows[flow][self.headers.index(column)] + # returns first packet time of flow + def first_packet(self, flow): + return float(self.flows[flow][self.header_index("first_packet")])-float(self.flows[0][self.header_index("first_packet")]) + # util: get column index based on header name + def header_index(self, column): + return self.headers.index(column) + + + + + + + +#t = TcptraceData("client.pcap") +#print t.number_of_flows +#print t.first_packet(1) + + + + + diff --git a/src/validation.yml b/src/validation.yml new file mode 100644 index 0000000..010eb61 --- /dev/null +++ b/src/validation.yml @@ -0,0 +1,8 @@ +tcptrace: + - test: "number_of_flows" + validations: + min_value: 2 +# - test: "flows" +# validations: +# - index: 1 +# min_delay: 7000 From c7cfde7d65519c4d5b0b035c8d39891ccc4bebb7 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 19 May 2015 06:50:02 -0700 Subject: [PATCH 02/28] step 2 --- src/conf/xp/4_nc | 12 +++++++++--- src/mpExperienceNCPV.py | 2 +- src/mpIterator.py | 31 +++++++++++++++++++++---------- src/validation.yml | 12 +++++++----- 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/conf/xp/4_nc b/src/conf/xp/4_nc index 5740ad4..f2cffd1 100644 --- a/src/conf/xp/4_nc +++ b/src/conf/xp/4_nc @@ -1,5 +1,11 @@ -xpType:nc +xpType:ncpv +rmem:300000 300000 300000 ncClientPort_0:33400 clientPcap:yes -ddCount:15000 -rmem:300000 300000 300000 +pvRateLimit:600k +ddCount:10000 +kpm:fullmesh +kpms:netlink +kpmc:netlink +upmc:fullmesh +#upmc_args: -n 5 diff --git a/src/mpExperienceNCPV.py b/src/mpExperienceNCPV.py index 4b984ba..1681241 100644 --- a/src/mpExperienceNCPV.py +++ b/src/mpExperienceNCPV.py @@ -9,7 +9,7 @@ class MpExperienceNCPV(MpExperience): SERVER_NC_LOG = "netcat_server" CLIENT_NC_LOG = "netcat_client" NC_BIN = "netcat" - PV_BIN = "/home/bhesmans/Documents/git/pv/pv" + PV_BIN = "pv" def __init__(self, xpParamFile, mpTopo, mpConfig): MpExperience.__init__(self, xpParamFile, mpTopo, mpConfig) diff --git a/src/mpIterator.py b/src/mpIterator.py index c20149d..52ebad9 100755 --- a/src/mpIterator.py +++ b/src/mpIterator.py @@ -18,28 +18,39 @@ xpParamFile = None topoBuilder = "mininet" class MinValueValidation: - def __init__(self, compared): - self.compared=compared + def __init__(self, yml): + self.compared=yml["target"] def validate(self, value): return self.compared<=value -class NumberOfFlowsTest: +class MinDelayValidation: + def __init__(self, v): + self.compared=v["target"] + def validate(self, flow): + return self.compared<=flow[5] +class TcptraceTest: def __init__(self, yml, trace): self.yml = yml["validations"] self.trace = trace def validate(self): print self.yml - tested_value = self.trace.number_of_flows - for k,v in self.yml.iteritems(): - name = k.title().replace("_","")+"Validation" - tester_klass=globals()[name] - tester = tester_klass(v) + for val in self.yml: + print "val: ", val + tested_value = self.get_tested_value(val) + klass_name=val["name"].title().replace("_","")+"Validation" + tester_klass=globals()[klass_name] + tester = tester_klass(val) if tester.validate(tested_value): print "SUCCESS" else: print "FAIL" - print k,v - +class NumberOfFlowsTest(TcptraceTest): + def get_tested_value(self, val): + return self.trace.number_of_flows + +class FlowsTest(TcptraceTest): + def get_tested_value(self, val): + return self.trace.flows[val["index"]] class TcptraceValidator: diff --git a/src/validation.yml b/src/validation.yml index 010eb61..3327d18 100644 --- a/src/validation.yml +++ b/src/validation.yml @@ -1,8 +1,10 @@ tcptrace: - test: "number_of_flows" validations: - min_value: 2 -# - test: "flows" -# validations: -# - index: 1 -# min_delay: 7000 + - name: "min_value" + target: 2 + - test: "flows" + validations: + - name: "min_delay" + index: 1 + target: 7000 From 0c6ca7a476f8027f0d6f39545ab07a999220762b Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 19 May 2015 07:01:14 -0700 Subject: [PATCH 03/28] step 2 --- src/mpIterator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mpIterator.py b/src/mpIterator.py index 52ebad9..c0f9b84 100755 --- a/src/mpIterator.py +++ b/src/mpIterator.py @@ -28,6 +28,8 @@ class MinDelayValidation: self.compared=v["target"] def validate(self, flow): return self.compared<=flow[5] + + class TcptraceTest: def __init__(self, yml, trace): self.yml = yml["validations"] @@ -35,7 +37,6 @@ class TcptraceTest: def validate(self): print self.yml for val in self.yml: - print "val: ", val tested_value = self.get_tested_value(val) klass_name=val["name"].title().replace("_","")+"Validation" tester_klass=globals()[klass_name] From 33b75b5922c581afa3458376c2ec9d9092226f0c Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 19 May 2015 07:21:06 -0700 Subject: [PATCH 04/28] renamed Validator in Checker --- src/mpIterator.py | 49 ++++++++++++++++------------------------------- 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/src/mpIterator.py b/src/mpIterator.py index c0f9b84..e532514 100755 --- a/src/mpIterator.py +++ b/src/mpIterator.py @@ -17,12 +17,18 @@ topoParamFile = None xpParamFile = None topoBuilder = "mininet" +# A checker runs tests, and a test is made of multiple validations + + + +# checks a value passed is greater or equel (generic) class MinValueValidation: def __init__(self, yml): self.compared=yml["target"] def validate(self, value): return self.compared<=value +# individual flow validation (specific) class MinDelayValidation: def __init__(self, v): self.compared=v["target"] @@ -30,12 +36,15 @@ class MinDelayValidation: return self.compared<=flow[5] + +# class testing tcptrace results +# the inheriting class should implement get_tested_value(self, yml) class TcptraceTest: def __init__(self, yml, trace): self.yml = yml["validations"] self.trace = trace + # performs a validation found in the yml file. def validate(self): - print self.yml for val in self.yml: tested_value = self.get_tested_value(val) klass_name=val["name"].title().replace("_","")+"Validation" @@ -46,19 +55,19 @@ class TcptraceTest: else: print "FAIL" class NumberOfFlowsTest(TcptraceTest): - def get_tested_value(self, val): + def get_tested_value(self, yml): return self.trace.number_of_flows class FlowsTest(TcptraceTest): - def get_tested_value(self, val): - return self.trace.flows[val["index"]] + def get_tested_value(self, yml): + return self.trace.flows[yml["index"]] -class TcptraceValidator: +class TcptraceChecker: def __init__(self, yml, trace): self.yml = yml["tcptrace"] self.trace = trace - def validate(self): + def check(self): for test in self.yml: name=test["test"].title().replace("_","")+"Test" klass = globals()[name] @@ -67,30 +76,6 @@ class TcptraceValidator: -def printHelp(): - print("Help Menu") - -def parseArgs(argv): - global topoParamFile - global xpParamFile - try: - opts, args = getopt.getopt(argv, "ht:x:", ["topoParam=","xp="]) - except getopt.GetoptError: - printHelp() - sys.exit(1) - for opt, arg in opts: - if opt == "-h": - printHelp() - sys.exit(1) - elif opt in ("-x","--xp"): - xpParamFile = arg - elif opt in ("-t","--topoParam"): - print("hey") - topoParamFile = arg - if topoParamFile is None: - print("Missing the topo...") - printHelp() - sys.exit(1) def write_entry(f, key, val): f.write("{}:{}\n".format(key,val)) @@ -154,9 +139,9 @@ with open(validation_file, 'r') as f: validations = load(f) print validations -tcptrace_validator = TcptraceValidator(validations, t ) +tcptrace_checker = TcptraceChecker(validations, t ) print "WILL VALIDATE" -tcptrace_validator.validate() +tcptrace_checker.check() #for v in validations["tcptrace"]: # print dump(v) From a91bbc479184dc3d4d5f2e181b8191e9f23eb508 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Wed, 20 May 2015 11:31:03 -0700 Subject: [PATCH 05/28] restructured and improved code --- src/mpIterator.py | 207 ++++++++++++---------------- src/{ => tests/base}/validation.yml | 2 +- 2 files changed, 92 insertions(+), 117 deletions(-) rename src/{ => tests/base}/validation.yml (88%) diff --git a/src/mpIterator.py b/src/mpIterator.py index e532514..8eee00b 100755 --- a/src/mpIterator.py +++ b/src/mpIterator.py @@ -1,5 +1,7 @@ #!/usr/bin/python +# apt-get install python-configglue + import sys, getopt from mpXpRunner import MpXpRunner from mpTopo import MpTopo @@ -9,141 +11,114 @@ import os from subprocess import call import datetime -from mpTcptraceData import * +# currently all checkers and validations and defined in this file +from mpValidations import * from yaml import load, dump -topoParamFile = None -xpParamFile = None -topoBuilder = "mininet" - -# A checker runs tests, and a test is made of multiple validations +from optparse import OptionParser +# Define supported options +parser = OptionParser() +parser.add_option("-t", "--tests", dest="tests_dir", + help="Directory holding tests", metavar="TESTSDIR" , default="./tests") +parser.add_option("-l", "--logs", dest="logs_dir", + help="Directory where to log", metavar="LOGSDIR" , default="./logs") -# checks a value passed is greater or equel (generic) -class MinValueValidation: - def __init__(self, yml): - self.compared=yml["target"] - def validate(self, value): - return self.compared<=value +(options, args) = parser.parse_args() -# individual flow validation (specific) -class MinDelayValidation: - def __init__(self, v): - self.compared=v["target"] - def validate(self, flow): - return self.compared<=flow[5] - - - -# class testing tcptrace results -# the inheriting class should implement get_tested_value(self, yml) -class TcptraceTest: - def __init__(self, yml, trace): - self.yml = yml["validations"] - self.trace = trace - # performs a validation found in the yml file. - def validate(self): - for val in self.yml: - tested_value = self.get_tested_value(val) - klass_name=val["name"].title().replace("_","")+"Validation" - tester_klass=globals()[klass_name] - tester = tester_klass(val) - if tester.validate(tested_value): - print "SUCCESS" - else: - print "FAIL" -class NumberOfFlowsTest(TcptraceTest): - def get_tested_value(self, yml): - return self.trace.number_of_flows - -class FlowsTest(TcptraceTest): - def get_tested_value(self, yml): - return self.trace.flows[yml["index"]] - - -class TcptraceChecker: - def __init__(self, yml, trace): - self.yml = yml["tcptrace"] - self.trace = trace - def check(self): - for test in self.yml: - name=test["test"].title().replace("_","")+"Test" - klass = globals()[name] - r = klass(test, self.trace) - r.validate() - - - -def write_entry(f, key, val): - f.write("{}:{}\n".format(key,val)) - -def generateTopo(): - path="/tmp/topo" - f=open(path,"w") - # delay, queueSize (in packets), bw - write_entry(f, "path_0", "10,15,5") - write_entry(f, "path_1", "10,15,5") - write_entry(f, "topoType", "MultiIf") - f.close() - return path - -def generateXp(): - path="/tmp/xp" - f=open(path,"w") - write_entry(f, "xpType", "nc") - write_entry(f, "kpm", "fullmesh") - write_entry(f, "kpms", "netlink") - write_entry(f, "kpmc", "netlink") - write_entry(f, "upmc", "fullmesh") -# write_entry(f, "upmc_args", "-t 600000 -i 500 -c 7800") - write_entry(f, "ddCount", "10000") - write_entry(f, "clientPcap", "yes") - write_entry(f, "ncClientPort_0", "0:33400") - write_entry(f, "rmem","300000 300000 300000") - f.close() - return path +# initialise flags values +tests_dir=options.tests_dir.rstrip("/") +logs_dir=options.logs_dir.rstrip("/") +# take timestamp, used as subdirectory in logs_dir timestamp=datetime.datetime.now().isoformat() -#topoFile=generateTopo() -#print(topoFile) -#xpFile=generateXp() -#print(xpFile) -topoFile="./conf/topo/simple_para" -xpFile="./conf/xp/4_nc" +for test_name in [name for name in os.listdir(tests_dir) if os.path.isdir(os.path.join(tests_dir, name))]: + # initialise files defining the experience and test + test_dir = tests_dir + "/" + test_name + xpFile = test_dir+"/xp" + topoFile = test_dir+"/topo" + validation_file=test_dir+"/validation.yml" + destDir=logs_dir+"/"+timestamp+"/"+test_name + if not os.path.exists(destDir): + os.makedirs(destDir) -#MpXpRunner(MpTopo.mininetBuilder, topoFile, xpFile) + print "Running " + test_dir + # run the experience + MpXpRunner(MpTopo.mininetBuilder, topoFile, xpFile) -destDir="/tmp/dest" -if not os.path.exists(destDir): - os.makedirs(destDir) + #copy xp, topo and validation to log + copy(topoFile,destDir) + copy(xpFile,destDir) + copy(validation_file,destDir) + #copy log files + for l in ["client.pcap" ,"command.log" ,"upmc.log" ,"upms.log" ,"client.pcap" ,"netcat_server_0.log" ,"netcat_client_0.log"]: + copy(l,destDir) -#copy log files -copy("client.pcap",destDir) -#copy xp and topo -copy(topoFile,destDir) -copy(xpFile,destDir) + # Run validations + with open(validation_file, 'r') as f: + validations = load(f) + for k in validations.keys(): + # Identify checker class + name = k.title().replace("_","")+"Checker" + klass= globals()[name] + # instantiate checker with validations and test_name + checker = klass(validations, test_name) + if checker.check(): + print checker.logs + else: + print checker.logs -#os.chdir(destDir) -#print(os.getcwd()) -#call(["/usr/local/bin/mptcptrace", "-f", "/tmp/dest/client.pcap", "-G20", "-F3", "-r7", "-s", "-S", "-a"]) -t = TcptraceData("/tmp/dest/client.pcap") -print "Number of flows:", t.number_of_flows -print "Time for fist flow:", t.first_packet(1) -validation_file="validation.yml" -with open(validation_file, 'r') as f: - validations = load(f) -print validations -tcptrace_checker = TcptraceChecker(validations, t ) -print "WILL VALIDATE" -tcptrace_checker.check() + + +#tcptrace_checker = TcptraceChecker(validations, t ) +#print "WILL VALIDATE" +#tcptrace_checker.check() #for v in validations["tcptrace"]: # print dump(v) # /usr/local/bin/mptcptrace -f /tmp/dest/client.pcap -G20 -F3 -r7 -s -S -a + + + +# Here are functions that can be used to generate topo and xp files: +#def write_entry(f, key, val): +# f.write("{}:{}\n".format(key,val)) +# +#def generateTopo(): +# path="/tmp/topo" +# f=open(path,"w") +# # delay, queueSize (in packets), bw +# write_entry(f, "path_0", "10,15,5") +# write_entry(f, "path_1", "10,15,5") +# write_entry(f, "topoType", "MultiIf") +# f.close() +# return path +# +#def generateXp(): +# path="/tmp/xp" +# f=open(path,"w") +# write_entry(f, "xpType", "nc") +# write_entry(f, "kpm", "fullmesh") +# write_entry(f, "kpms", "netlink") +# write_entry(f, "kpmc", "netlink") +# write_entry(f, "upmc", "fullmesh") +## write_entry(f, "upmc_args", "-t 600000 -i 500 -c 7800") +# write_entry(f, "ddCount", "10000") +# write_entry(f, "clientPcap", "yes") +# write_entry(f, "ncClientPort_0", "0:33400") +# write_entry(f, "rmem","300000 300000 300000") +# f.close() +# return path + +#topoFile=generateTopo() +#print(topoFile) +#xpFile=generateXp() +#print(xpFile) + diff --git a/src/validation.yml b/src/tests/base/validation.yml similarity index 88% rename from src/validation.yml rename to src/tests/base/validation.yml index 3327d18..9a5725e 100644 --- a/src/validation.yml +++ b/src/tests/base/validation.yml @@ -7,4 +7,4 @@ tcptrace: validations: - name: "min_delay" index: 1 - target: 7000 + target: 29000 From 14272f5000a41c68d93d35f8f0c0fa8f69051915 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Wed, 20 May 2015 11:37:50 -0700 Subject: [PATCH 06/28] added mpValidations.py --- src/mpValidations.py | 90 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/mpValidations.py diff --git a/src/mpValidations.py b/src/mpValidations.py new file mode 100644 index 0000000..a1fcac0 --- /dev/null +++ b/src/mpValidations.py @@ -0,0 +1,90 @@ + + +from mpTcptraceData import * + + +# A checker runs tests, and a test is made of multiple validations + + +# For a validation, the value to compare to is the target value from the yaml +# The validation takes place in the validate method, which takes +# as argument a value from which to extract the value to compare or the value itself +class Validation: + def __init__(self, yml): + self.compared=yml["target"] + def name(self): + return self.__class__.__name__ + +# checks a value passed is greater or equal (generic) +class MinValueValidation(Validation): + def validate(self, value): + return self.compared<=value + +# individual flow validation (specific) +# gets flow_spec = (index, flows) where index is the index of the flow to validate, and flows is the array of flows +class MinDelayValidation(Validation): + def validate(self, flow_spec): + (index,flows) = flow_spec + val = float(flows[index][5])-float(flows[0][5]) + return self.compared<=val + + + +# Base class testing tcptrace results +# the inheriting class should implement get_tested_value(self, yml) +# The validate method iterates one the validations mentioned for the test in the yml file. +class TcptraceTest: + def __init__(self, yml, trace): + self.yml = yml["validations"] + self.trace = trace + # performs a validation found in the yml file. + def validate(self): + is_ok = True + self.logs = "" + for val in self.yml: + tested_value = self.get_tested_value(val) + klass_name=val["name"].title().replace("_","")+"Validation" + tester_klass=globals()[klass_name] + tester = tester_klass(val) + if tester.validate(tested_value): + self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" OK\n" + return True + else: + self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" FAILS\n" + is_ok = False + return is_ok + def name(self): + return self.__class__.__name__ + +# get_tested_value returns the number of flows +class NumberOfFlowsTest(TcptraceTest): + def get_tested_value(self, yml): + return self.trace.number_of_flows + +# get_tested_value returns index of the flow to validate, and the list of flows +class FlowsTest(TcptraceTest): + def get_tested_value(self, yml): + return (yml["index"],self.trace.flows) + + +# Runs tests based on tcptrace +class TcptraceChecker: + def __init__(self, yml, test_id): + self.yml = yml["tcptrace"] + self.trace = TcptraceData("/tmp/dest/client.pcap") + self.test_id = test_id + def check(self): + is_ok = True + self.logs=self.test_id+"\n" + for test in self.yml: + name=test["test"].title().replace("_","")+"Test" + klass = globals()[name] + r = klass(test, self.trace) + if r.validate(): + self.logs = self.logs + " *" + r.name() + " SUCCESS\n" + self.logs = self.logs + r.logs + else: + self.logs = self.logs + " *" + self.test_id + " " + r.name() + " FAIL\n" + self.logs = self.logs + r.logs + + From 73934f79f7caf1ca6f974f676351853b2533828b Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Wed, 20 May 2015 11:49:13 -0700 Subject: [PATCH 07/28] added example test topo and xp --- src/tests/base/topo | 9 +++++++++ src/tests/base/xp | 11 +++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/tests/base/topo create mode 100644 src/tests/base/xp diff --git a/src/tests/base/topo b/src/tests/base/topo new file mode 100644 index 0000000..06cece4 --- /dev/null +++ b/src/tests/base/topo @@ -0,0 +1,9 @@ +desc:Simple configuration with two para link +topoType:MultiIf +leftSubnet:10.0. +rightSubnet:10.1. +#path_x:delay,queueSize(may be calc),bw +path_0:10,10,5 +path_1:40,40,5 +path_2:30,30,2 +path_3:20,20,1 diff --git a/src/tests/base/xp b/src/tests/base/xp new file mode 100644 index 0000000..f2cffd1 --- /dev/null +++ b/src/tests/base/xp @@ -0,0 +1,11 @@ +xpType:ncpv +rmem:300000 300000 300000 +ncClientPort_0:33400 +clientPcap:yes +pvRateLimit:600k +ddCount:10000 +kpm:fullmesh +kpms:netlink +kpmc:netlink +upmc:fullmesh +#upmc_args: -n 5 From 0a47b540f38fc5c017764ad777ac49c0b111782d Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Wed, 20 May 2015 12:29:48 -0700 Subject: [PATCH 08/28] added comments --- src/mpValidations.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mpValidations.py b/src/mpValidations.py index a1fcac0..58782fd 100644 --- a/src/mpValidations.py +++ b/src/mpValidations.py @@ -14,6 +14,8 @@ class Validation: self.compared=yml["target"] def name(self): return self.__class__.__name__ + def validate(self,value): + raise Exception("Method not implemented") # checks a value passed is greater or equal (generic) class MinValueValidation(Validation): @@ -32,6 +34,8 @@ class MinDelayValidation(Validation): # Base class testing tcptrace results # the inheriting class should implement get_tested_value(self, yml) +# the get_tested_value should return the value that all validations of this test will use +# the validations get this value as argument of their validate method # The validate method iterates one the validations mentioned for the test in the yml file. class TcptraceTest: def __init__(self, yml, trace): @@ -55,6 +59,8 @@ class TcptraceTest: return is_ok def name(self): return self.__class__.__name__ + def get_tested_value(self,yml): + raise Exception("Method not implemented") # get_tested_value returns the number of flows class NumberOfFlowsTest(TcptraceTest): From 47b1110f3f37411f534c243adf7d4d6beed534df Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 26 May 2015 09:43:53 -0700 Subject: [PATCH 09/28] multiple validations in one test ok, client.pcap path correction, added max_value and exact_value validation of number of flows --- src/mpIterator.py | 2 +- src/mpValidations.py | 15 ++++++++++++--- src/tests/base/validation.yml | 6 +++++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/mpIterator.py b/src/mpIterator.py index 8eee00b..30c4731 100755 --- a/src/mpIterator.py +++ b/src/mpIterator.py @@ -65,7 +65,7 @@ for test_name in [name for name in os.listdir(tests_dir) if os.path.isdir(os.pat name = k.title().replace("_","")+"Checker" klass= globals()[name] # instantiate checker with validations and test_name - checker = klass(validations, test_name) + checker = klass(validations, test_name, destDir) if checker.check(): print checker.logs else: diff --git a/src/mpValidations.py b/src/mpValidations.py index 58782fd..c8ff105 100644 --- a/src/mpValidations.py +++ b/src/mpValidations.py @@ -21,6 +21,14 @@ class Validation: class MinValueValidation(Validation): def validate(self, value): return self.compared<=value +# checks a value passed is greater or equal (generic) +class MaxValueValidation(Validation): + def validate(self, value): + return self.compared>=value +# checks a value passed is greater or equal (generic) +class ExactValueValidation(Validation): + def validate(self, value): + return self.compared==value # individual flow validation (specific) # gets flow_spec = (index, flows) where index is the index of the flow to validate, and flows is the array of flows @@ -45,14 +53,15 @@ class TcptraceTest: def validate(self): is_ok = True self.logs = "" + print self.yml for val in self.yml: + print val["name"] tested_value = self.get_tested_value(val) klass_name=val["name"].title().replace("_","")+"Validation" tester_klass=globals()[klass_name] tester = tester_klass(val) if tester.validate(tested_value): self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" OK\n" - return True else: self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" FAILS\n" is_ok = False @@ -75,9 +84,9 @@ class FlowsTest(TcptraceTest): # Runs tests based on tcptrace class TcptraceChecker: - def __init__(self, yml, test_id): + def __init__(self, yml, test_id, destDir): self.yml = yml["tcptrace"] - self.trace = TcptraceData("/tmp/dest/client.pcap") + self.trace = TcptraceData(destDir+"/client.pcap") self.test_id = test_id def check(self): is_ok = True diff --git a/src/tests/base/validation.yml b/src/tests/base/validation.yml index 9a5725e..07f8c80 100644 --- a/src/tests/base/validation.yml +++ b/src/tests/base/validation.yml @@ -3,8 +3,12 @@ tcptrace: validations: - name: "min_value" target: 2 + - name: "max_value" + target: 4 + - name: "exact_value" + target: 4 - test: "flows" validations: - name: "min_delay" index: 1 - target: 29000 + target: 2000 From 552393562c79df95b35af3b864a401c4cf895e05 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 26 May 2015 09:59:37 -0700 Subject: [PATCH 10/28] FlowsTest works with TcpTraceData instance --- src/mpValidations.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mpValidations.py b/src/mpValidations.py index c8ff105..d54ec4f 100644 --- a/src/mpValidations.py +++ b/src/mpValidations.py @@ -34,8 +34,8 @@ class ExactValueValidation(Validation): # gets flow_spec = (index, flows) where index is the index of the flow to validate, and flows is the array of flows class MinDelayValidation(Validation): def validate(self, flow_spec): - (index,flows) = flow_spec - val = float(flows[index][5])-float(flows[0][5]) + (index,trace) = flow_spec + val = trace.first_packet(index)-trace.first_packet(0) return self.compared<=val @@ -79,7 +79,7 @@ class NumberOfFlowsTest(TcptraceTest): # get_tested_value returns index of the flow to validate, and the list of flows class FlowsTest(TcptraceTest): def get_tested_value(self, yml): - return (yml["index"],self.trace.flows) + return (yml["index"],self.trace) # Runs tests based on tcptrace From 1e4f7f55bde9f86d59e2ff187ed63d302e5572cd Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 26 May 2015 10:12:46 -0700 Subject: [PATCH 11/28] added min_delay_between validation --- src/mpValidations.py | 12 ++++++++++++ src/tests/base/validation.yml | 12 +++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/mpValidations.py b/src/mpValidations.py index d54ec4f..d3e0205 100644 --- a/src/mpValidations.py +++ b/src/mpValidations.py @@ -38,6 +38,17 @@ class MinDelayValidation(Validation): val = trace.first_packet(index)-trace.first_packet(0) return self.compared<=val +# individual flow validation (specific) +# gets flow_spec = ( [ index0, index1] , flows) where: +# - index0 is the index of the flow taken as reference for timing +# - index1 is the flow for which we want to validate the timing +# - flows is the array of flows +class MinDelayBetweenValidation(Validation): + def validate(self, flow_spec): + ([index0, index1] ,trace) = flow_spec + val = trace.first_packet(index1)-trace.first_packet(index0) + return self.compared<=val + # Base class testing tcptrace results @@ -77,6 +88,7 @@ class NumberOfFlowsTest(TcptraceTest): return self.trace.number_of_flows # get_tested_value returns index of the flow to validate, and the list of flows +# index can be a compound value, as in the case of min_delay_between where it is an array of indexes class FlowsTest(TcptraceTest): def get_tested_value(self, yml): return (yml["index"],self.trace) diff --git a/src/tests/base/validation.yml b/src/tests/base/validation.yml index 07f8c80..423d9fc 100644 --- a/src/tests/base/validation.yml +++ b/src/tests/base/validation.yml @@ -3,12 +3,22 @@ tcptrace: validations: - name: "min_value" target: 2 + desc: "Open minimum 2 flows" - name: "max_value" target: 4 + desc: "Open maximum 4 flows" - name: "exact_value" target: 4 + desc: "Open exactly 4 flows" - test: "flows" validations: - name: "min_delay" index: 1 - target: 2000 + target: 2 + desc: "Minimum 2 seconds delay between opening of first and second flow" + - name: "min_delay_between" + index: + - 1 + - 2 + target: 1 + desc: "Minimum 1 second delay between opening of second and third flow" From 31b165edf092fbc32a3204b20cf11b6a3ac1ba8a Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 26 May 2015 10:13:08 -0700 Subject: [PATCH 12/28] base test uses delay open pm --- src/tests/base/xp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/base/xp b/src/tests/base/xp index f2cffd1..3a15d5a 100644 --- a/src/tests/base/xp +++ b/src/tests/base/xp @@ -7,5 +7,5 @@ ddCount:10000 kpm:fullmesh kpms:netlink kpmc:netlink -upmc:fullmesh -#upmc_args: -n 5 +upmc:delayOpen +upmc_args: -c 2000 -d From 9617cad4a01711dab6dedcd5fa184a874d545928 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 26 May 2015 10:30:56 -0700 Subject: [PATCH 13/28] added docs, validation desc attribute, and more explicit message in case of FAIL --- src/mpValidations.py | 2 +- src/tests/base/validation.yml | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mpValidations.py b/src/mpValidations.py index d3e0205..804c242 100644 --- a/src/mpValidations.py +++ b/src/mpValidations.py @@ -74,7 +74,7 @@ class TcptraceTest: if tester.validate(tested_value): self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" OK\n" else: - self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" FAILS\n" + self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" FAILS "+ val["desc"] +"\n" is_ok = False return is_ok def name(self): diff --git a/src/tests/base/validation.yml b/src/tests/base/validation.yml index 423d9fc..6949102 100644 --- a/src/tests/base/validation.yml +++ b/src/tests/base/validation.yml @@ -8,14 +8,18 @@ tcptrace: target: 4 desc: "Open maximum 4 flows" - name: "exact_value" - target: 4 + target: 5 desc: "Open exactly 4 flows" + # This tests the flows opened by the connection. See class FlowsTest - test: "flows" validations: + # validates time between first packets of the first stream and the stream for which the index is passed. - name: "min_delay" index: 1 target: 2 desc: "Minimum 2 seconds delay between opening of first and second flow" + # validates time between first packets of streams for which the index is passed. + # index is now a compound value, handled by the validate method of the class MinDelayBetweenValidation - name: "min_delay_between" index: - 1 From 83788f5c08648cdb8e03827611a7d71eb5123a65 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 26 May 2015 10:44:34 -0700 Subject: [PATCH 14/28] handle exceptions --- src/mpValidations.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/mpValidations.py b/src/mpValidations.py index 804c242..aa53fd7 100644 --- a/src/mpValidations.py +++ b/src/mpValidations.py @@ -71,11 +71,14 @@ class TcptraceTest: klass_name=val["name"].title().replace("_","")+"Validation" tester_klass=globals()[klass_name] tester = tester_klass(val) - if tester.validate(tested_value): - self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" OK\n" - else: - self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" FAILS "+ val["desc"] +"\n" - is_ok = False + try: + if tester.validate(tested_value): + self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" OK\n" + else: + self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" FAILS "+ val["desc"] +"\n" + is_ok = False + except Exception as e: + self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" FAILS (EXCEPTION:"+ str(e) + ") "+ val["desc"] +"\n" return is_ok def name(self): return self.__class__.__name__ From ac1e857f799467267541f10cebe9dbe36a332111 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 26 May 2015 12:00:03 -0700 Subject: [PATCH 15/28] generic tcptrace flows value comparison testing (difference currently) --- src/mpTcptraceData.py | 13 ++++++++++++- src/mpValidations.py | 30 +++++++++++++++++++++--------- src/tests/base/validation.yml | 9 ++++++++- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/mpTcptraceData.py b/src/mpTcptraceData.py index 56a8e18..c2bdaf6 100755 --- a/src/mpTcptraceData.py +++ b/src/mpTcptraceData.py @@ -5,6 +5,8 @@ from subprocess import check_output import csv from io import StringIO +import re + @@ -22,7 +24,16 @@ class TcptraceData: def get(self, flow, column): if flow>self.number_of_flows-1: raise Exception("Bad flow index") - return self.flows[flow][self.headers.index(column)] + value = self.flows[flow][self.headers.index(column)] + if re.search("[a-zA-Z]", value): + return value + elif re.search("\.", value): + return float(value) + elif re.search("^[0-9]+$", value): + return int(value) + else: + return value + # returns first packet time of flow def first_packet(self, flow): return float(self.flows[flow][self.header_index("first_packet")])-float(self.flows[0][self.header_index("first_packet")]) diff --git a/src/mpValidations.py b/src/mpValidations.py index aa53fd7..13cfda6 100644 --- a/src/mpValidations.py +++ b/src/mpValidations.py @@ -20,22 +20,27 @@ class Validation: # checks a value passed is greater or equal (generic) class MinValueValidation(Validation): def validate(self, value): + self.value = value return self.compared<=value # checks a value passed is greater or equal (generic) class MaxValueValidation(Validation): def validate(self, value): + self.value = value return self.compared>=value # checks a value passed is greater or equal (generic) class ExactValueValidation(Validation): def validate(self, value): + self.value = value return self.compared==value # individual flow validation (specific) # gets flow_spec = (index, flows) where index is the index of the flow to validate, and flows is the array of flows class MinDelayValidation(Validation): def validate(self, flow_spec): - (index,trace) = flow_spec + (yml,trace) = flow_spec + index=yml["index"] val = trace.first_packet(index)-trace.first_packet(0) + self.value = val return self.compared<=val # individual flow validation (specific) @@ -45,10 +50,19 @@ class MinDelayValidation(Validation): # - flows is the array of flows class MinDelayBetweenValidation(Validation): def validate(self, flow_spec): - ([index0, index1] ,trace) = flow_spec + (yml ,trace) = flow_spec + [index0, index1] = yml["index"] val = trace.first_packet(index1)-trace.first_packet(index0) + self.value = val return self.compared<=val +class AttributeMinimumDifferenceValidation(Validation): + def validate(self, flow_spec): + (yml ,trace) = flow_spec + [index0, index1] = yml["index"] + val = trace.get(index1, yml["attribute"]) - trace.get(index0, yml["attribute"]) + self.value = val + return self.compared<=val # Base class testing tcptrace results @@ -64,21 +78,19 @@ class TcptraceTest: def validate(self): is_ok = True self.logs = "" - print self.yml for val in self.yml: - print val["name"] tested_value = self.get_tested_value(val) klass_name=val["name"].title().replace("_","")+"Validation" tester_klass=globals()[klass_name] tester = tester_klass(val) try: if tester.validate(tested_value): - self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" OK\n" + self.logs=self.logs+ " " + " OK :" + val["desc"] +" - " + tester.name()+ " value : " + str(tester.value) +" vs " + str(val["target"]) + "\n" else: - self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" FAILS "+ val["desc"] +"\n" + self.logs=self.logs+ " " + " FAIL:" + val["desc"] +" - " + tester.name()+ " value : " + str(tester.value) +" vs " + str(val["target"]) + "\n" is_ok = False except Exception as e: - self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " -" +tester.name()+" FAILS (EXCEPTION:"+ str(e) + ") "+ val["desc"] +"\n" + self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " EXCP:" + val["desc"] +" - " + tester.name()+ " " + str(e) + "\n" return is_ok def name(self): return self.__class__.__name__ @@ -94,7 +106,7 @@ class NumberOfFlowsTest(TcptraceTest): # index can be a compound value, as in the case of min_delay_between where it is an array of indexes class FlowsTest(TcptraceTest): def get_tested_value(self, yml): - return (yml["index"],self.trace) + return (yml,self.trace) # Runs tests based on tcptrace @@ -111,7 +123,7 @@ class TcptraceChecker: klass = globals()[name] r = klass(test, self.trace) if r.validate(): - self.logs = self.logs + " *" + r.name() + " SUCCESS\n" + self.logs = self.logs + " *" + self.test_id + " " + r.name() + " SUCCESS\n" self.logs = self.logs + r.logs else: self.logs = self.logs + " *" + self.test_id + " " + r.name() + " FAIL\n" diff --git a/src/tests/base/validation.yml b/src/tests/base/validation.yml index 6949102..05690c6 100644 --- a/src/tests/base/validation.yml +++ b/src/tests/base/validation.yml @@ -8,7 +8,7 @@ tcptrace: target: 4 desc: "Open maximum 4 flows" - name: "exact_value" - target: 5 + target: 4 desc: "Open exactly 4 flows" # This tests the flows opened by the connection. See class FlowsTest - test: "flows" @@ -26,3 +26,10 @@ tcptrace: - 2 target: 1 desc: "Minimum 1 second delay between opening of second and third flow" + - name: "attribute_minimum_difference" + attribute: "first_packet" + index: + - 0 + - 1 + target: 2 + desc: "first packet delay 2nd flow" From 20f12886034174701275df341323d75798c2c064 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 26 May 2015 12:19:16 -0700 Subject: [PATCH 16/28] added number of packets ration validator --- src/mpValidations.py | 39 ++++++++++++++++++++++++++++++----- src/tests/base/validation.yml | 14 +++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/mpValidations.py b/src/mpValidations.py index 13cfda6..d8187a4 100644 --- a/src/mpValidations.py +++ b/src/mpValidations.py @@ -16,6 +16,9 @@ class Validation: return self.__class__.__name__ def validate(self,value): raise Exception("Method not implemented") + def setup(self): + raise Exception("Method not implemented") + # checks a value passed is greater or equal (generic) class MinValueValidation(Validation): @@ -56,13 +59,39 @@ class MinDelayBetweenValidation(Validation): self.value = val return self.compared<=val -class AttributeMinimumDifferenceValidation(Validation): - def validate(self, flow_spec): +class AttributeValidation(Validation): + def setup(self, flow_spec): (yml ,trace) = flow_spec [index0, index1] = yml["index"] - val = trace.get(index1, yml["attribute"]) - trace.get(index0, yml["attribute"]) - self.value = val - return self.compared<=val + self.val0 = trace.get(index0, yml["attribute"]) + self.val1 = trace.get(index1, yml["attribute"]) + +class AttributeMinimumDifferenceValidation(AttributeValidation): + def validate(self, flow_spec): + self.setup(flow_spec) + self.value = self.val1 - self.val0 + return self.compared<=self.value + +class AttributeMaximumDifferenceValidation(AttributeValidation): + def validate(self, flow_spec): + self.setup(flow_spec) + self.value = self.val1 - self.val0 + return self.compared>=self.value + + +class AttributeMinimumRatioValidation(AttributeValidation): + def validate(self, flow_spec): + self.setup(flow_spec) + self.value = float(self.val1)/+float(self.val1) + return self.compared<=self.value + +class AttributeMaximumRatioValidation(AttributeValidation): + def validate(self, flow_spec): + self.setup(flow_spec) + self.value = float(self.val1)/float(self.val0) + return self.compared>=self.value + + # Base class testing tcptrace results diff --git a/src/tests/base/validation.yml b/src/tests/base/validation.yml index 05690c6..2336c5a 100644 --- a/src/tests/base/validation.yml +++ b/src/tests/base/validation.yml @@ -20,6 +20,8 @@ tcptrace: desc: "Minimum 2 seconds delay between opening of first and second flow" # validates time between first packets of streams for which the index is passed. # index is now a compound value, handled by the validate method of the class MinDelayBetweenValidation + # first index value is the base flow we compare to + # second index is the flow we want to validate - name: "min_delay_between" index: - 1 @@ -28,8 +30,20 @@ tcptrace: desc: "Minimum 1 second delay between opening of second and third flow" - name: "attribute_minimum_difference" attribute: "first_packet" + # first index value is the base flow we compare to + # second index is the flow we want to validate index: - 0 - 1 target: 2 desc: "first packet delay 2nd flow" + - name: "attribute_maximum_ratio" + attribute: "total_packets_a2b" + # first index value is the base flow we compare to + # second index is the flow we want to validate + index: + - 0 + - 1 + # 5% + target: 0.05 + desc: "a->b packet ration flow 2 compard to flow1" From a064674dc8a830ecd5b98500a15e4535b4d433d0 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 26 May 2015 15:04:15 -0700 Subject: [PATCH 17/28] preparing to add mptcptrace based validations --- src/mpValidations.py | 49 ++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/src/mpValidations.py b/src/mpValidations.py index d8187a4..8afc69c 100644 --- a/src/mpValidations.py +++ b/src/mpValidations.py @@ -92,17 +92,7 @@ class AttributeMaximumRatioValidation(AttributeValidation): return self.compared>=self.value - - -# Base class testing tcptrace results -# the inheriting class should implement get_tested_value(self, yml) -# the get_tested_value should return the value that all validations of this test will use -# the validations get this value as argument of their validate method -# The validate method iterates one the validations mentioned for the test in the yml file. -class TcptraceTest: - def __init__(self, yml, trace): - self.yml = yml["validations"] - self.trace = trace +class Tester: # performs a validation found in the yml file. def validate(self): is_ok = True @@ -126,6 +116,18 @@ class TcptraceTest: def get_tested_value(self,yml): raise Exception("Method not implemented") + + +# Base class testing tcptrace results +# the inheriting class should implement get_tested_value(self, yml) +# the get_tested_value should return the value that all validations of this test will use +# the validations get this value as argument of their validate method +# The validate method iterates one the validations mentioned for the test in the yml file. +class TcptraceTest(Tester): + def __init__(self, yml, trace): + self.yml = yml["validations"] + self.trace = trace + # get_tested_value returns the number of flows class NumberOfFlowsTest(TcptraceTest): def get_tested_value(self, yml): @@ -137,13 +139,7 @@ class FlowsTest(TcptraceTest): def get_tested_value(self, yml): return (yml,self.trace) - -# Runs tests based on tcptrace -class TcptraceChecker: - def __init__(self, yml, test_id, destDir): - self.yml = yml["tcptrace"] - self.trace = TcptraceData(destDir+"/client.pcap") - self.test_id = test_id +class Checker: def check(self): is_ok = True self.logs=self.test_id+"\n" @@ -158,4 +154,21 @@ class TcptraceChecker: self.logs = self.logs + " *" + self.test_id + " " + r.name() + " FAIL\n" self.logs = self.logs + r.logs +# Runs tests based on tcptrace +# It (in the method inherited from its parent class) instanciates the ...Test class passing it the TcptraceData instance +class TcptraceChecker(Checker): + def __init__(self, yml, test_id, destDir): + self.yml = yml["tcptrace"] + self.trace = TcptraceData(destDir+"/client.pcap") + self.test_id = test_id + + +# Runs tests based on mptcptrace +# It (in the method inherited from its parent class) instanciates the ...Test class passing it the MptcptraceData instance +class MptcptraceChecker(Checker): + def __init__(self, yml, test_id, destDir): + self.yml = yml["mptcptrace"] + self.trace = MptcptraceData(destDir+"/client.pcap") + self.test_id = test_id + From fc3a064ad42f7ee9d60c9201a449d036325d68bf Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 26 May 2015 16:28:19 -0700 Subject: [PATCH 18/28] first shot at mptcptrace csv validation --- src/mpIterator.py | 2 ++ src/mpMptcptraceData.py | 47 +++++++++++++++++++++++++++++++++++ src/mpValidations.py | 38 +++++++++++++++++++++++++--- src/tests/base/validation.yml | 8 ++++++ 4 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 src/mpMptcptraceData.py diff --git a/src/mpIterator.py b/src/mpIterator.py index 30c4731..d428728 100755 --- a/src/mpIterator.py +++ b/src/mpIterator.py @@ -35,6 +35,8 @@ logs_dir=options.logs_dir.rstrip("/") # take timestamp, used as subdirectory in logs_dir timestamp=datetime.datetime.now().isoformat() +#timestamp = "2015-05-26T15:42:45.419949" + for test_name in [name for name in os.listdir(tests_dir) if os.path.isdir(os.path.join(tests_dir, name))]: # initialise files defining the experience and test test_dir = tests_dir + "/" + test_name diff --git a/src/mpMptcptraceData.py b/src/mpMptcptraceData.py new file mode 100644 index 0000000..5b63763 --- /dev/null +++ b/src/mpMptcptraceData.py @@ -0,0 +1,47 @@ +#!/usr/bin/python + + +from subprocess import check_call +import csv + +from io import StringIO +import re +import os +import numpy as np + + + + +class MptcptraceData: + def __init__(self, pcap_file): + self.pcap_file=pcap_file + self.base_dir = os.path.dirname(pcap_file) + working_dir = os.getcwd() + + # generate CSVs + os.chdir(self.base_dir) + print self.base_dir + print os.getcwd() + check_call(["sudo" , "/usr/local/bin/mptcptrace" , "-f", os.path.basename(pcap_file) , "-G20", "-F3", "-r7", "-s", "-S", "-a", "-w2"]) + os.chdir(working_dir) + # accessing the attribute corresponding to the filename will parse the csv and return its cells + def __getattr__(self, name): + csv_file = self.base_dir+"/"+name+".csv" + print "opening csv file " + csv_file + if os.path.isfile(csv_file): + a = np.genfromtxt (csv_file, delimiter=",") + setattr(self, name, a) + return getattr(self,name) + else: + raise AttributeError("No csv file for unknown attribute "+name) + + + # gets cell corresponding to flow with header column + # flow 0 = first one, from 1=subflows + def get(self, name): + if hasattr(self,name): + return getattr(self,name) + else: + return self.__get_attr__(name) + + diff --git a/src/mpValidations.py b/src/mpValidations.py index 8afc69c..b776d41 100644 --- a/src/mpValidations.py +++ b/src/mpValidations.py @@ -2,6 +2,8 @@ from mpTcptraceData import * +import numpy as np + # A checker runs tests, and a test is made of multiple validations @@ -91,8 +93,23 @@ class AttributeMaximumRatioValidation(AttributeValidation): self.value = float(self.val1)/float(self.val0) return self.compared>=self.value +class IncreasingValueValidation(AttributeValidation): + def validate(self, values): + previous = 0 + for i,v in enumerate(values): + #print i, "{:10.6f}".format(previous), "{:10.6f}".format(v) + if v=self.value + class Tester: + def __init__(self, yml, trace): + self.yml = yml["validations"] + self.trace = trace # performs a validation found in the yml file. def validate(self): is_ok = True @@ -123,10 +140,8 @@ class Tester: # the get_tested_value should return the value that all validations of this test will use # the validations get this value as argument of their validate method # The validate method iterates one the validations mentioned for the test in the yml file. -class TcptraceTest(Tester): - def __init__(self, yml, trace): - self.yml = yml["validations"] - self.trace = trace +class TcptraceTest(Tester): + pass # get_tested_value returns the number of flows class NumberOfFlowsTest(TcptraceTest): @@ -139,6 +154,20 @@ class FlowsTest(TcptraceTest): def get_tested_value(self, yml): return (yml,self.trace) + + + +class MptcptraceTest(Tester): + pass + +import code +# get_tested_value returns the number of flows +class ColumnValuesTest(TcptraceTest): + def get_tested_value(self, yml): + a = self.trace.get(yml["csv"]) + code.interact(local=locals()) + return a[:,yml["column"]] + class Checker: def check(self): is_ok = True @@ -162,6 +191,7 @@ class TcptraceChecker(Checker): self.trace = TcptraceData(destDir+"/client.pcap") self.test_id = test_id +from mpMptcptraceData import * # Runs tests based on mptcptrace # It (in the method inherited from its parent class) instanciates the ...Test class passing it the MptcptraceData instance diff --git a/src/tests/base/validation.yml b/src/tests/base/validation.yml index 2336c5a..3cc145e 100644 --- a/src/tests/base/validation.yml +++ b/src/tests/base/validation.yml @@ -1,3 +1,11 @@ +mptcptrace: + - test: "column_values" + validations: + - name: "increasing_value" + csv: "c2s_seq_1" + column: 2 + target: irrelevant + desc: "dummy: check sequence numbers grow" tcptrace: - test: "number_of_flows" validations: From cdb954756f2662a70c2efb449abca70f51a12ed8 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Wed, 27 May 2015 09:13:31 -0700 Subject: [PATCH 19/28] validation do not require the target attribute in yml --- src/mpValidations.py | 47 ++++++++++++++++++++++++----------- src/tests/base/validation.yml | 3 +-- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/mpValidations.py b/src/mpValidations.py index b776d41..d41f012 100644 --- a/src/mpValidations.py +++ b/src/mpValidations.py @@ -5,6 +5,12 @@ from mpTcptraceData import * import numpy as np +# to get a REPL: +#import code +#code.interact(local=locals()) + + + # A checker runs tests, and a test is made of multiple validations @@ -13,7 +19,10 @@ import numpy as np # as argument a value from which to extract the value to compare or the value itself class Validation: def __init__(self, yml): - self.compared=yml["target"] + if "target" in yml: + self.compared=yml["target"] + else: + self.compared=None def name(self): return self.__class__.__name__ def validate(self,value): @@ -38,9 +47,11 @@ class ExactValueValidation(Validation): self.value = value return self.compared==value -# individual flow validation (specific) -# gets flow_spec = (index, flows) where index is the index of the flow to validate, and flows is the array of flows +# individual flow validations (used with FlowsTest) +################################################### + class MinDelayValidation(Validation): + # receives flow_spec = (index, flows) where index is the index of the flow to validate, and flows is the array of flows def validate(self, flow_spec): (yml,trace) = flow_spec index=yml["index"] @@ -48,12 +59,11 @@ class MinDelayValidation(Validation): self.value = val return self.compared<=val -# individual flow validation (specific) -# gets flow_spec = ( [ index0, index1] , flows) where: -# - index0 is the index of the flow taken as reference for timing -# - index1 is the flow for which we want to validate the timing -# - flows is the array of flows class MinDelayBetweenValidation(Validation): + # gets flow_spec = ( [ index0, index1] , flows) where: + # - index0 is the index of the flow taken as reference for timing + # - index1 is the flow for which we want to validate the timing + # - flows is the array of flows def validate(self, flow_spec): (yml ,trace) = flow_spec [index0, index1] = yml["index"] @@ -93,17 +103,23 @@ class AttributeMaximumRatioValidation(AttributeValidation): self.value = float(self.val1)/float(self.val0) return self.compared>=self.value +# mptcptrace csv validations +############################ + +# validates all values passed have increasing values +# it is the Tester's get_tested_value method that does the work +# to extract the values list from the trace. class IncreasingValueValidation(AttributeValidation): def validate(self, values): previous = 0 for i,v in enumerate(values): #print i, "{:10.6f}".format(previous), "{:10.6f}".format(v) if v=self.value + return True class Tester: @@ -119,11 +135,16 @@ class Tester: klass_name=val["name"].title().replace("_","")+"Validation" tester_klass=globals()[klass_name] tester = tester_klass(val) + if "target" in val: + target=val["target"] + else: + target=None + try: if tester.validate(tested_value): - self.logs=self.logs+ " " + " OK :" + val["desc"] +" - " + tester.name()+ " value : " + str(tester.value) +" vs " + str(val["target"]) + "\n" + self.logs=self.logs+ " " + " OK :" + val["desc"] +" - " + tester.name()+ " value : " + str(tester.value) + ("" if target==None else " vs target " + str(val["target"])) + "\n" else: - self.logs=self.logs+ " " + " FAIL:" + val["desc"] +" - " + tester.name()+ " value : " + str(tester.value) +" vs " + str(val["target"]) + "\n" + self.logs=self.logs+ " " + " FAIL:" + val["desc"] +" - " + tester.name()+ " value : " + str(tester.value) + ("" if target==None else " vs target " + str(val["target"])) + "\n" is_ok = False except Exception as e: self.logs=self.logs+ ("" if self.logs=="" else "\n ")+ " EXCP:" + val["desc"] +" - " + tester.name()+ " " + str(e) + "\n" @@ -160,12 +181,10 @@ class FlowsTest(TcptraceTest): class MptcptraceTest(Tester): pass -import code # get_tested_value returns the number of flows class ColumnValuesTest(TcptraceTest): def get_tested_value(self, yml): a = self.trace.get(yml["csv"]) - code.interact(local=locals()) return a[:,yml["column"]] class Checker: diff --git a/src/tests/base/validation.yml b/src/tests/base/validation.yml index 3cc145e..f1f5929 100644 --- a/src/tests/base/validation.yml +++ b/src/tests/base/validation.yml @@ -4,7 +4,6 @@ mptcptrace: - name: "increasing_value" csv: "c2s_seq_1" column: 2 - target: irrelevant desc: "dummy: check sequence numbers grow" tcptrace: - test: "number_of_flows" @@ -52,6 +51,6 @@ tcptrace: index: - 0 - 1 - # 5% + # flows[1].packets_a2b/flows[2].packets_a2b < 5% target: 0.05 desc: "a->b packet ration flow 2 compard to flow1" From 793f51be69ff6d50b894ba6f77188547e8e219ee Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Wed, 27 May 2015 10:59:35 -0700 Subject: [PATCH 20/28] moved to a CsvTester doing all tests --- src/mpMptcptraceData.py | 2 +- src/mpTcptraceData.py | 44 ++---------- src/mpValidations.py | 126 ++++++++++------------------------ src/tests/base/validation.yml | 80 +++++++++------------ 4 files changed, 80 insertions(+), 172 deletions(-) diff --git a/src/mpMptcptraceData.py b/src/mpMptcptraceData.py index 5b63763..e69eaeb 100644 --- a/src/mpMptcptraceData.py +++ b/src/mpMptcptraceData.py @@ -38,7 +38,7 @@ class MptcptraceData: # gets cell corresponding to flow with header column # flow 0 = first one, from 1=subflows - def get(self, name): + def get_csv(self, name): if hasattr(self,name): return getattr(self,name) else: diff --git a/src/mpTcptraceData.py b/src/mpTcptraceData.py index c2bdaf6..99fb2da 100755 --- a/src/mpTcptraceData.py +++ b/src/mpTcptraceData.py @@ -6,6 +6,7 @@ import csv from io import StringIO import re +import numpy as np @@ -15,43 +16,12 @@ class TcptraceData: self.pcap_file=pcap_file csv_content = check_output(["tcptrace", "-l", "--csv", pcap_file]) tcptrace_reader = csv.reader(filter(lambda l: len(l)>0 and l[0]!="#",csv_content.splitlines())) - cells=list(tcptrace_reader) + cells=np.array(list(tcptrace_reader)) + #drop header row + cells= cells[1:] + self.cells = cells self.headers=cells[0] self.flows=cells[1:] self.number_of_flows=len(self.flows) - # gets cell corresponding to flow with header column - # flow 0 = first one, from 1=subflows - def get(self, flow, column): - if flow>self.number_of_flows-1: - raise Exception("Bad flow index") - value = self.flows[flow][self.headers.index(column)] - if re.search("[a-zA-Z]", value): - return value - elif re.search("\.", value): - return float(value) - elif re.search("^[0-9]+$", value): - return int(value) - else: - return value - - # returns first packet time of flow - def first_packet(self, flow): - return float(self.flows[flow][self.header_index("first_packet")])-float(self.flows[0][self.header_index("first_packet")]) - # util: get column index based on header name - def header_index(self, column): - return self.headers.index(column) - - - - - - - -#t = TcptraceData("client.pcap") -#print t.number_of_flows -#print t.first_packet(1) - - - - - + def get_csv(self, name): + return self.cells diff --git a/src/mpValidations.py b/src/mpValidations.py index d41f012..6b7722b 100644 --- a/src/mpValidations.py +++ b/src/mpValidations.py @@ -47,72 +47,42 @@ class ExactValueValidation(Validation): self.value = value return self.compared==value -# individual flow validations (used with FlowsTest) -################################################### -class MinDelayValidation(Validation): - # receives flow_spec = (index, flows) where index is the index of the flow to validate, and flows is the array of flows - def validate(self, flow_spec): - (yml,trace) = flow_spec - index=yml["index"] - val = trace.first_packet(index)-trace.first_packet(0) - self.value = val - return self.compared<=val - -class MinDelayBetweenValidation(Validation): - # gets flow_spec = ( [ index0, index1] , flows) where: - # - index0 is the index of the flow taken as reference for timing - # - index1 is the flow for which we want to validate the timing - # - flows is the array of flows - def validate(self, flow_spec): - (yml ,trace) = flow_spec - [index0, index1] = yml["index"] - val = trace.first_packet(index1)-trace.first_packet(index0) - self.value = val - return self.compared<=val - -class AttributeValidation(Validation): - def setup(self, flow_spec): - (yml ,trace) = flow_spec - [index0, index1] = yml["index"] - self.val0 = trace.get(index0, yml["attribute"]) - self.val1 = trace.get(index1, yml["attribute"]) - -class AttributeMinimumDifferenceValidation(AttributeValidation): - def validate(self, flow_spec): - self.setup(flow_spec) - self.value = self.val1 - self.val0 +# the method get_tested_value of the tester returns the value passed to validate. +# the CsvTester returns an array of values +class MinDifferenceValidation(Validation): + def validate(self, value): + v = value.flatten() + if len(v)>2: + raise Exception("MinDifferenceValidation requires 2 values maximum, not "+ str(len(v))) + self.value = float(v[1])-float(v[0]) return self.compared<=self.value - -class AttributeMaximumDifferenceValidation(AttributeValidation): - def validate(self, flow_spec): - self.setup(flow_spec) - self.value = self.val1 - self.val0 - return self.compared>=self.value - - -class AttributeMinimumRatioValidation(AttributeValidation): - def validate(self, flow_spec): - self.setup(flow_spec) - self.value = float(self.val1)/+float(self.val1) +class MinRowsValidation(Validation): + def validate(self, value): + self.value = len(value) return self.compared<=self.value - -class AttributeMaximumRatioValidation(AttributeValidation): - def validate(self, flow_spec): - self.setup(flow_spec) - self.value = float(self.val1)/float(self.val0) +class MaxRowsValidation(Validation): + def validate(self, value): + self.value = len(value) + return self.compared>=self.value +class ExactRowsValidation(Validation): + def validate(self, value): + self.value = len(value) + return self.compared==self.value +class MaxRatioValidation(Validation): + def validate(self, value): + v = value.flatten() + if len(v)>2: + raise Exception("MinDifferenceValidation requires 2 values maximum, not "+ str(len(v))) + self.value = float(v[1])/(float(v[0])+float(v[1])) return self.compared>=self.value - -# mptcptrace csv validations -############################ - # validates all values passed have increasing values # it is the Tester's get_tested_value method that does the work # to extract the values list from the trace. -class IncreasingValueValidation(AttributeValidation): +class IncreasingValuesValidation(Validation): def validate(self, values): previous = 0 - for i,v in enumerate(values): + for i,v in enumerate(values.flatten()): #print i, "{:10.6f}".format(previous), "{:10.6f}".format(v) if vb packet ration flow 2 compard to flow1" + desc: "max ration of packet a->b on flow 1 compared to flow 0." From ded9226e2a5b9809e9832fc21bd1314061669c27 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Mon, 1 Jun 2015 10:19:30 -0700 Subject: [PATCH 21/28] accept validations.yml without tests and validations, to simply run and log trace --- src/mpIterator.py | 21 +++++++++++---------- src/mpValidations.py | 21 +++++++++++---------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/mpIterator.py b/src/mpIterator.py index d428728..1ada58e 100755 --- a/src/mpIterator.py +++ b/src/mpIterator.py @@ -62,16 +62,17 @@ for test_name in [name for name in os.listdir(tests_dir) if os.path.isdir(os.pat # Run validations with open(validation_file, 'r') as f: validations = load(f) - for k in validations.keys(): - # Identify checker class - name = k.title().replace("_","")+"Checker" - klass= globals()[name] - # instantiate checker with validations and test_name - checker = klass(validations, test_name, destDir) - if checker.check(): - print checker.logs - else: - print checker.logs + if validations!=None: + for k in validations.keys(): + # Identify checker class + name = k.title().replace("_","")+"Checker" + klass= globals()[name] + # instantiate checker with validations and test_name + checker = klass(validations, test_name, destDir) + if checker.check(): + print checker.logs + else: + print checker.logs diff --git a/src/mpValidations.py b/src/mpValidations.py index 6b7722b..563fa99 100644 --- a/src/mpValidations.py +++ b/src/mpValidations.py @@ -141,16 +141,17 @@ class Checker: def check(self): is_ok = True self.logs=self.test_id+"\n" - for test in self.yml: - name=test["test"].title().replace("_","")+"Test" - klass = globals()[name] - r = klass(test, self.trace) - if r.validate(): - self.logs = self.logs + " *" + self.test_id + " " + r.name() + " SUCCESS\n" - self.logs = self.logs + r.logs - else: - self.logs = self.logs + " *" + self.test_id + " " + r.name() + " FAIL\n" - self.logs = self.logs + r.logs + if self.yml!=None: + for test in self.yml: + name=test["test"].title().replace("_","")+"Test" + klass = globals()[name] + r = klass(test, self.trace) + if r.validate(): + self.logs = self.logs + " *" + self.test_id + " " + r.name() + " SUCCESS\n" + self.logs = self.logs + r.logs + else: + self.logs = self.logs + " *" + self.test_id + " " + r.name() + " FAIL\n" + self.logs = self.logs + r.logs # Runs tests based on tcptrace # It (in the method inherited from its parent class) instanciates the ...Test class passing it the TcptraceData instance From e2fefd0067d462bcb3a267149900c130f623b626 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Mon, 1 Jun 2015 16:17:47 -0700 Subject: [PATCH 22/28] added burst block aggregator --- src/burst_tests/first/topo | 7 ++++ src/burst_tests/first/validation.yml | 5 +++ src/burst_tests/first/xp | 14 +++++++ src/mpBurstBlocks.py | 63 ++++++++++++++++++++++++++++ src/mpIterator.py | 21 ++++++++-- 5 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 src/burst_tests/first/topo create mode 100644 src/burst_tests/first/validation.yml create mode 100644 src/burst_tests/first/xp create mode 100644 src/mpBurstBlocks.py diff --git a/src/burst_tests/first/topo b/src/burst_tests/first/topo new file mode 100644 index 0000000..be0ac21 --- /dev/null +++ b/src/burst_tests/first/topo @@ -0,0 +1,7 @@ +desc:Simple configuration with two para link +topoType:MultiIf +leftSubnet:10.0. +rightSubnet:10.1. +#path_x:delay,queueSize(may be calc),bw +path_0:10,10,5 +path_1:1000,40,5 diff --git a/src/burst_tests/first/validation.yml b/src/burst_tests/first/validation.yml new file mode 100644 index 0000000..893c105 --- /dev/null +++ b/src/burst_tests/first/validation.yml @@ -0,0 +1,5 @@ +checkers: + mptcptrace: + tcptrace: +aggregators: + - burst_blocks diff --git a/src/burst_tests/first/xp b/src/burst_tests/first/xp new file mode 100644 index 0000000..ec29c38 --- /dev/null +++ b/src/burst_tests/first/xp @@ -0,0 +1,14 @@ +xpType:ncpv +ncClientPort_0:33400 +clientPcap:yes +pvRateLimit:64k +ddCount:1000 +kpmc:fullmesh + +#kpmc:netlink +#upmc:fullmeshIfNeededRate +#upmc_args: -c 500 -i 1000 -t 60000 + +kpms:fullmesh +pvG:1000000 +pvZ:1000000 diff --git a/src/mpBurstBlocks.py b/src/mpBurstBlocks.py new file mode 100644 index 0000000..91c9856 --- /dev/null +++ b/src/mpBurstBlocks.py @@ -0,0 +1,63 @@ +import numpy as np +import os as os + +class BurstBlocksAggregator: + def __init__(self, yml, test_name, dest_dir): + # csv_file="c2s_seq_1.csv" + # minimum delay to observe to identify the beginning of a block + self.min_block_sep = 0.1 + self.headers = [ "ts", "map_begin", "subflow", "is_seq", "map-end", "is_reinject" ] + self.log = open(dest_dir+"/burst_block_aggregator.log","w") + self.csv_file=dest_dir+"/"+"c2s_seq_1.csv" + self.a = np.genfromtxt (self.csv_file, delimiter=",") + self.blocks=[] + self.times=[] + self.extract_blocks() + self.extract_times() + + def c(self,column): + """Return column index corresponding to name passed as argument""" + return self.headers.index(column) + def extract_blocks(self): + # beginning of block. First block starts at packet 0 + b=0 + # iteration, we can start at packet 1 + i=1 + while i0.1: + print >>self.log, "previous block:", "{:10.8f}".format(self.a[i-1][self.c("ts")]), "seq:", self.a[i-1][self.c("map_begin")] + print >>self.log, "found block starting at ", "{:10.8f}".format(self.a[i][self.c("ts")]), "seq:", self.a[i][self.c("map_begin")] + print >>self.log, "next block:", "{:10.8f}".format(self.a[i+1][self.c("ts")]), "seq:", self.a[i+1][self.c("map_begin")] + print >>self.log,"--------------------------------------" + # the ranges we use here are inclusive, ie the range contains both elements. + self.blocks.append((b,i-1)) + b=i + i=i+1 + self.blocks.append((b,i-1)) + print >>self.log, "# blocks: ", len(self.blocks) + def extract_times(self): + for i in range(len(self.blocks)): + first,last = self.blocks[i] + t1 = self.a[first][self.c("ts")] + # +1 because our ranges are inclusive + packets = self.a[first:last+1] + j=0 + biggest_ack=-1 + while jpackets[biggest_ack][self.c("map_begin")]: + biggest_ack=j + j=j+1 + self.times.append([first, first+biggest_ack, packets[biggest_ack][self.c("ts")] - packets[0][self.c("ts")], packets[0][self.c("ts")], packets[biggest_ack][self.c("ts")]]) + self.times = np.array(self.times) + np.set_printoptions(precision=6) + block_times= self.times[:,2] + block_times.sort() + self.block_times=block_times[1:-2] + def __del__(self): + self.log.close() + + def __str__(self): + return str(self.block_times) + "\nmean:\t" + str(self.block_times.mean()) +"\nstd:\t"+ str(self.block_times.std()) diff --git a/src/mpIterator.py b/src/mpIterator.py index 1ada58e..66697af 100755 --- a/src/mpIterator.py +++ b/src/mpIterator.py @@ -13,6 +13,7 @@ from subprocess import call import datetime # currently all checkers and validations and defined in this file from mpValidations import * +from mpBurstBlocks import * from yaml import load, dump @@ -25,16 +26,22 @@ parser.add_option("-t", "--tests", dest="tests_dir", help="Directory holding tests", metavar="TESTSDIR" , default="./tests") parser.add_option("-l", "--logs", dest="logs_dir", help="Directory where to log", metavar="LOGSDIR" , default="./logs") +parser.add_option("-r", "--repeat", dest="repeat", action="store_true", + help="Reuse existing logs", metavar="REPEAT" , default=False) (options, args) = parser.parse_args() # initialise flags values tests_dir=options.tests_dir.rstrip("/") logs_dir=options.logs_dir.rstrip("/") +repeat = options.repeat # take timestamp, used as subdirectory in logs_dir timestamp=datetime.datetime.now().isoformat() +if repeat: + print "not implemented" + timestamp="2015-06-01T14:57:31.617534" #timestamp = "2015-05-26T15:42:45.419949" for test_name in [name for name in os.listdir(tests_dir) if os.path.isdir(os.path.join(tests_dir, name))]: @@ -49,7 +56,8 @@ for test_name in [name for name in os.listdir(tests_dir) if os.path.isdir(os.pat print "Running " + test_dir # run the experience - MpXpRunner(MpTopo.mininetBuilder, topoFile, xpFile) + if not repeat: + MpXpRunner(MpTopo.mininetBuilder, topoFile, xpFile) #copy xp, topo and validation to log copy(topoFile,destDir) @@ -63,16 +71,23 @@ for test_name in [name for name in os.listdir(tests_dir) if os.path.isdir(os.pat with open(validation_file, 'r') as f: validations = load(f) if validations!=None: - for k in validations.keys(): + for k in validations["checkers"].keys(): # Identify checker class name = k.title().replace("_","")+"Checker" klass= globals()[name] # instantiate checker with validations and test_name - checker = klass(validations, test_name, destDir) + checker = klass(validations["checkers"], test_name, destDir) if checker.check(): print checker.logs else: print checker.logs + for k in validations["aggregators"]: + # Identify checker class + name = k.title().replace("_","")+"Aggregator" + klass= globals()[name] + # instantiate checker with validations and test_name + agg = klass(validations, test_name, destDir) + print agg From 3119592ea2c39d1866e84e73ab82b8d953eef3d8 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Mon, 1 Jun 2015 23:50:05 -0700 Subject: [PATCH 23/28] only consider data packets in sending block identifications --- src/mpBurstBlocks.py | 75 ++++++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/src/mpBurstBlocks.py b/src/mpBurstBlocks.py index 91c9856..98ecc79 100644 --- a/src/mpBurstBlocks.py +++ b/src/mpBurstBlocks.py @@ -6,7 +6,7 @@ class BurstBlocksAggregator: # csv_file="c2s_seq_1.csv" # minimum delay to observe to identify the beginning of a block self.min_block_sep = 0.1 - self.headers = [ "ts", "map_begin", "subflow", "is_seq", "map-end", "is_reinject" ] + self.headers = [ "ts", "map_begin", "subflow", "is_seq", "map_end", "is_reinject" ] self.log = open(dest_dir+"/burst_block_aggregator.log","w") self.csv_file=dest_dir+"/"+"c2s_seq_1.csv" self.a = np.genfromtxt (self.csv_file, delimiter=",") @@ -23,39 +23,74 @@ class BurstBlocksAggregator: b=0 # iteration, we can start at packet 1 i=1 + previous=0 while i0.1: - print >>self.log, "previous block:", "{:10.8f}".format(self.a[i-1][self.c("ts")]), "seq:", self.a[i-1][self.c("map_begin")] - print >>self.log, "found block starting at ", "{:10.8f}".format(self.a[i][self.c("ts")]), "seq:", self.a[i][self.c("map_begin")] - print >>self.log, "next block:", "{:10.8f}".format(self.a[i+1][self.c("ts")]), "seq:", self.a[i+1][self.c("map_begin")] - print >>self.log,"--------------------------------------" - # the ranges we use here are inclusive, ie the range contains both elements. - self.blocks.append((b,i-1)) - b=i + if self.a[i][self.c("is_seq")]==1: + # in this case we look for the start of a new sending block + if b==None: + b=i + print >>self.log, "previous seq packet:", "{:10.8f}".format(self.a[previous][self.c("ts")]), "seq:", self.a[previous][self.c("map_begin")] + print >>self.log, "found block starting at ", "{:10.8f}".format(self.a[i][self.c("ts")]), "seq:", self.a[i][self.c("map_begin")] + # we know the start of the block and look for its last packet + elif self.a[i][self.c("ts")]-self.a[previous][self.c("ts")]>0.1: + print >>self.log, "next block:", "{:10.8f}".format(self.a[i+1][self.c("ts")]), "seq:", self.a[i+1][self.c("map_begin")] + print >>self.log,"--------------------------------------" + # the ranges we use here are inclusive, ie the range contains both elements. + self.blocks.append((b,previous)) + b=i + # keep track of previous seq packet + previous=i i=i+1 - self.blocks.append((b,i-1)) + self.blocks.append((b,previous)) print >>self.log, "# blocks: ", len(self.blocks) def extract_times(self): for i in range(len(self.blocks)): + print >>self.log, "Block " + str(i) + print >>self.log, "---------------------" first,last = self.blocks[i] + print >>self.log, "first packet[" + str(first) +"] at:", "{:10.6f}".format(self.a[first][self.c("ts")]), "seq:", self.a[first][self.c("map_begin")] + print >>self.log, "last packet [" + str(last) +"] at :", "{:10.6f}".format(self.a[last][self.c("ts")]), "seq:", self.a[last][self.c("map_begin")] t1 = self.a[first][self.c("ts")] # +1 because our ranges are inclusive packets = self.a[first:last+1] - j=0 - biggest_ack=-1 - while jpackets[biggest_ack][self.c("map_begin")]: - biggest_ack=j - j=j+1 - self.times.append([first, first+biggest_ack, packets[biggest_ack][self.c("ts")] - packets[0][self.c("ts")], packets[0][self.c("ts")], packets[biggest_ack][self.c("ts")]]) + biggest_seq_index=self.find_biggest_seq_in_block(packets) + biggest_seq = packets[biggest_seq_index][self.c("map_end")] + print >>self.log, "biggest_seq = " + str(biggest_seq) + ack_index, ack_packet=self.find_ack_for_seq(biggest_seq, biggest_seq_index) + print >>self.log, "ack time = " + "{:10.6f}".format(self.a[ack_index][self.c("ts")]) + print >>self.log, "ack index = " + str(ack_index) + print >>self.log, "block time = " + "{:10.6f}".format(ack_packet[self.c("ts")] - packets[0][self.c("ts")]) + self.times.append([first, ack_index, ack_packet[self.c("ts")] - packets[0][self.c("ts")] , packets[0][self.c("ts")], ack_packet[self.c("ts")] ]) + print >>self.log, "############################" + print >>self.log, "---------------------------------------------" + print >>self.log, "block times = " + str(self.times) self.times = np.array(self.times) np.set_printoptions(precision=6) block_times= self.times[:,2] block_times.sort() self.block_times=block_times[1:-2] + def find_ack_for_seq(self, seq, start_index): + i=start_index + while i=seq: + return (i,self.a[i]) + i=i+1 + return None + + def find_biggest_seq_in_block(self, packets): + biggest_seq=-1 + j=0 + while jpackets[biggest_seq][self.c("map_begin")]: + biggest_seq=j + j=j+1 + return biggest_seq + def __del__(self): self.log.close() From a06c1de8056271f7f71a99702b1ab4f3e88eff5d Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 2 Jun 2015 01:03:31 -0700 Subject: [PATCH 24/28] added flows ratios, and plot --- src/mpBurstBlocks.py | 68 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/src/mpBurstBlocks.py b/src/mpBurstBlocks.py index 98ecc79..d5263f2 100644 --- a/src/mpBurstBlocks.py +++ b/src/mpBurstBlocks.py @@ -1,5 +1,6 @@ import numpy as np import os as os +import matplotlib.pyplot as plt class BurstBlocksAggregator: def __init__(self, yml, test_name, dest_dir): @@ -12,8 +13,14 @@ class BurstBlocksAggregator: self.a = np.genfromtxt (self.csv_file, delimiter=",") self.blocks=[] self.times=[] + self.packets_per_flow=[] + self.flows_ratios = [] + self.subflows=[] + self.flows_ratios=[] self.extract_blocks() self.extract_times() + self.extract_flows_packets() + self.extract_flows_ratios() def c(self,column): """Return column index corresponding to name passed as argument""" @@ -67,8 +74,52 @@ class BurstBlocksAggregator: self.times = np.array(self.times) np.set_printoptions(precision=6) block_times= self.times[:,2] - block_times.sort() - self.block_times=block_times[1:-2] + self.block_times=block_times + # this was to drop the smallest and biggest values from the mean + # block_times.sort() + # self.block_times=block_times[1:-2] + def extract_flows_packets(self): + for i in range(len(self.blocks)): + # will hold number of packets per flow for this block + r={} + print >>self.log, "Block " + str(i) + print >>self.log, "---------------------" + first,last = self.blocks[i] + # +1 because our ranges are inclusive + packets = self.a[first:last+1] + for p in packets: + if p[self.c("is_seq")]==0: + continue + flow = int(p[self.c("subflow")]) + if flow in r.keys(): + r[flow]+=1 + else: + r[flow]=1 + self.packets_per_flow.append(r) + print >>self.log, r + print >>self.log, "############################" + print >>self.log, "---------------------------------------------" + # now set values to 0 as needed for block that didn't send on some subflows + sublist = [ h.keys() for h in self.packets_per_flow] + all_subflows = list( set ( [item for sublist in self.packets_per_flow for item in sublist] )) + self.subflows= all_subflows + for h in self.packets_per_flow: + for f in all_subflows: + if not f in h.keys(): + h[f]=0 + + def extract_flows_ratios(self): + # reset value + self.flows_ratios = [] + # for each block compute the ratio + for count in self.packets_per_flow: + total_packets = sum(count.values()) + h = {} + for s in self.subflows: + h[s]=count[s]/float(total_packets) + self.flows_ratios.append(h) + + def find_ack_for_seq(self, seq, start_index): i=start_index while i Date: Tue, 2 Jun 2015 13:31:57 -0700 Subject: [PATCH 25/28] user min_block_sep parameter, now set to 3 --- src/mpBurstBlocks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mpBurstBlocks.py b/src/mpBurstBlocks.py index d5263f2..d0bd49b 100644 --- a/src/mpBurstBlocks.py +++ b/src/mpBurstBlocks.py @@ -6,7 +6,7 @@ class BurstBlocksAggregator: def __init__(self, yml, test_name, dest_dir): # csv_file="c2s_seq_1.csv" # minimum delay to observe to identify the beginning of a block - self.min_block_sep = 0.1 + self.min_block_sep = 0.3 self.headers = [ "ts", "map_begin", "subflow", "is_seq", "map_end", "is_reinject" ] self.log = open(dest_dir+"/burst_block_aggregator.log","w") self.csv_file=dest_dir+"/"+"c2s_seq_1.csv" @@ -39,7 +39,7 @@ class BurstBlocksAggregator: print >>self.log, "previous seq packet:", "{:10.8f}".format(self.a[previous][self.c("ts")]), "seq:", self.a[previous][self.c("map_begin")] print >>self.log, "found block starting at ", "{:10.8f}".format(self.a[i][self.c("ts")]), "seq:", self.a[i][self.c("map_begin")] # we know the start of the block and look for its last packet - elif self.a[i][self.c("ts")]-self.a[previous][self.c("ts")]>0.1: + elif self.a[i][self.c("ts")]-self.a[previous][self.c("ts")]>self.min_block_sep: print >>self.log, "next block:", "{:10.8f}".format(self.a[i+1][self.c("ts")]), "seq:", self.a[i+1][self.c("map_begin")] print >>self.log,"--------------------------------------" # the ranges we use here are inclusive, ie the range contains both elements. From 944720a29e4060623b0a3cd6c97ea24c30f82f5d Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Tue, 2 Jun 2015 16:41:25 -0700 Subject: [PATCH 26/28] identify blocks by sequence numbers, not very good --- src/mpBurstBlocks.py | 49 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/src/mpBurstBlocks.py b/src/mpBurstBlocks.py index d0bd49b..7a98c0f 100644 --- a/src/mpBurstBlocks.py +++ b/src/mpBurstBlocks.py @@ -25,30 +25,57 @@ class BurstBlocksAggregator: def c(self,column): """Return column index corresponding to name passed as argument""" return self.headers.index(column) +# def extract_blocks(self): +# # beginning of block. First block starts at packet 0 +# b=0 +# # iteration, we can start at packet 1 +# i=1 +# previous=0 +# while i>self.log, "previous seq packet:", "{:10.8f}".format(self.a[previous][self.c("ts")]), "seq:", self.a[previous][self.c("map_begin")] +# print >>self.log, "found block starting at ", "{:10.8f}".format(self.a[i][self.c("ts")]), "seq:", self.a[i][self.c("map_begin")] +# # we know the start of the block and look for its last packet +# elif self.a[i][self.c("ts")]-self.a[previous][self.c("ts")]>self.min_block_sep: +# print >>self.log, "next block:", "{:10.8f}".format(self.a[i+1][self.c("ts")]), "seq:", self.a[i+1][self.c("map_begin")] +# print >>self.log,"--------------------------------------" +# # the ranges we use here are inclusive, ie the range contains both elements. +# self.blocks.append((b,previous)) +# b=i +# # keep track of previous seq packet +# previous=i +# i=i+1 +# self.blocks.append((b,previous)) +# print >>self.log, "# blocks: ", len(self.blocks) +# detect blocks based on number of bytes sent def extract_blocks(self): # beginning of block. First block starts at packet 0 b=0 # iteration, we can start at packet 1 i=1 - previous=0 + last_end=None while i>self.log, "previous seq packet:", "{:10.8f}".format(self.a[previous][self.c("ts")]), "seq:", self.a[previous][self.c("map_begin")] - print >>self.log, "found block starting at ", "{:10.8f}".format(self.a[i][self.c("ts")]), "seq:", self.a[i][self.c("map_begin")] + if b==None and last_end!=None: + if self.a[i][self.c("map_begin")]==self.a[last_end][self.c("map_end")]: + b=i + print >>self.log, "found block starting at ", "{:10.8f}".format(self.a[i][self.c("ts")]), "seq:", self.a[i][self.c("map_begin")] # we know the start of the block and look for its last packet - elif self.a[i][self.c("ts")]-self.a[previous][self.c("ts")]>self.min_block_sep: - print >>self.log, "next block:", "{:10.8f}".format(self.a[i+1][self.c("ts")]), "seq:", self.a[i+1][self.c("map_begin")] + elif self.a[i][self.c("map_end")]-self.a[b][self.c("map_begin")]==65536: + print >>self.log, "found block ending at ", "{:10.8f}".format(self.a[i][self.c("ts")]), "end seq:", self.a[i][self.c("map_end")] print >>self.log,"--------------------------------------" # the ranges we use here are inclusive, ie the range contains both elements. - self.blocks.append((b,previous)) - b=i + self.blocks.append((b,i)) + last_end = i + b=None + print self.blocks # keep track of previous seq packet - previous=i i=i+1 - self.blocks.append((b,previous)) + self.blocks.append((b,i-1)) print >>self.log, "# blocks: ", len(self.blocks) def extract_times(self): for i in range(len(self.blocks)): From ef9464fab2ce389bff4b1e982c8d483488b4a332 Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Wed, 3 Jun 2015 09:41:23 -0700 Subject: [PATCH 27/28] looking at first packet of block with (seq - first transmission seq) modulo 65536 = 1428 --- src/mpBurstBlocks.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/mpBurstBlocks.py b/src/mpBurstBlocks.py index 7a98c0f..c996501 100644 --- a/src/mpBurstBlocks.py +++ b/src/mpBurstBlocks.py @@ -56,26 +56,20 @@ class BurstBlocksAggregator: b=0 # iteration, we can start at packet 1 i=1 - last_end=None + first_seq=self.a[i][self.c("map_begin")] + previous_seq=None while i>self.log, "found block starting at ", "{:10.8f}".format(self.a[i][self.c("ts")]), "seq:", self.a[i][self.c("map_begin")] - # we know the start of the block and look for its last packet - elif self.a[i][self.c("map_end")]-self.a[b][self.c("map_begin")]==65536: - print >>self.log, "found block ending at ", "{:10.8f}".format(self.a[i][self.c("ts")]), "end seq:", self.a[i][self.c("map_end")] + if (self.a[i][self.c("map_end")]-self.a[0][self.c("map_begin")])%65536==1428: + print >>self.log, "found block beginning at ", "{:10.8f}".format(self.a[i][self.c("ts")]), "end seq:", self.a[i][self.c("map_end")] print >>self.log,"--------------------------------------" - # the ranges we use here are inclusive, ie the range contains both elements. - self.blocks.append((b,i)) - last_end = i - b=None - print self.blocks + self.blocks.append((b,previous_seq)) + b = i # keep track of previous seq packet + previous_seq=i i=i+1 self.blocks.append((b,i-1)) + print self.blocks print >>self.log, "# blocks: ", len(self.blocks) def extract_times(self): for i in range(len(self.blocks)): @@ -152,7 +146,7 @@ class BurstBlocksAggregator: while i=seq: + if self.a[i][self.c("map_begin")]>seq: return (i,self.a[i]) i=i+1 return None From 180c37dadc2a5c9e462c1362904acaf9fcd7316b Mon Sep 17 00:00:00 2001 From: Raphael Bauduin Date: Mon, 8 Jun 2015 11:38:02 -0700 Subject: [PATCH 28/28] buggy code, but for reference if we want to reimplement it --- src/mpBurstBlocks.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/mpBurstBlocks.py b/src/mpBurstBlocks.py index c996501..11a4e7f 100644 --- a/src/mpBurstBlocks.py +++ b/src/mpBurstBlocks.py @@ -51,24 +51,34 @@ class BurstBlocksAggregator: # self.blocks.append((b,previous)) # print >>self.log, "# blocks: ", len(self.blocks) # detect blocks based on number of bytes sent + +#!!!!!!!!!!!!!!!!!!!!!!!!!!! +# buggy code !!!!!!!!!!!!!!! +#!!!!!!!!!!!!!!!!!!!!!!!!!!! def extract_blocks(self): # beginning of block. First block starts at packet 0 b=0 # iteration, we can start at packet 1 i=1 + previous_mod=0 first_seq=self.a[i][self.c("map_begin")] previous_seq=None while iself.a[previous_seq][self.c("map_begin")]: print >>self.log, "found block beginning at ", "{:10.8f}".format(self.a[i][self.c("ts")]), "end seq:", self.a[i][self.c("map_end")] print >>self.log,"--------------------------------------" self.blocks.append((b,previous_seq)) b = i - # keep track of previous seq packet - previous_seq=i + # keep track of previous seq packet + #print >>self.log, "recording previous seq at time ", "{:10.8f}".format(self.a[i][self.c("ts")]), "and end_seq:", self.a[i][self.c("map_end")] + if self.a[i][self.c("map_begin")]>self.a[previous_seq][self.c("map_begin")]: + previous_mod=(self.a[i][self.c("map_end")]-self.a[0][self.c("map_begin")])%65536 + previous_seq=i i=i+1 - self.blocks.append((b,i-1)) + print "last block previous_seq = " + str(previous_seq) + print >>self.log, "Rcording last block end at time ", "{:10.8f}".format(self.a[previous_seq][self.c("ts")]), "and end_seq:", self.a[previous_seq][self.c("map_end")] + self.blocks.append((b,previous_seq)) print self.blocks print >>self.log, "# blocks: ", len(self.blocks) def extract_times(self): @@ -149,6 +159,8 @@ class BurstBlocksAggregator: if self.a[i][self.c("map_begin")]>seq: return (i,self.a[i]) i=i+1 + print "Did not find ack for:" + print "seq : " + str(seq) return None def find_biggest_seq_in_block(self, packets): @@ -161,6 +173,7 @@ class BurstBlocksAggregator: elif packets[j][self.c("map_begin")]>packets[biggest_seq][self.c("map_begin")]: biggest_seq=j j=j+1 + print >>self.log, "biggest seq in block: " + str(packets[biggest_seq][self.c("map_end")]) return biggest_seq def __del__(self):