mininet-sample/core/experiment.py

451 lines
18 KiB
Python
Raw Normal View History

2020-06-25 12:56:47 +00:00
from .parameter import Parameter
2020-06-24 10:28:44 +00:00
from topos.multi_interface import MultiInterfaceTopo
2020-06-26 06:52:56 +00:00
class ExperimentParameter(Parameter):
2020-06-25 12:56:47 +00:00
"""
2020-06-26 06:52:56 +00:00
Handler for experiment parameters stored in configuration files
2020-06-25 12:56:47 +00:00
"""
RMEM = "rmem"
WMEM = "wmem"
SCHED = "sched"
CC = "congctrl"
AUTOCORK = "autocork"
2020-06-25 15:01:58 +00:00
EARLY_RETRANS = "earlyRetrans"
2020-06-25 12:56:47 +00:00
KERNELPM = "kpm"
KERNELPMC = "kpmc" #kernel path manager client / server
KERNELPMS = "kpms"
USERPMC = "upmc"
USERPMS = "upms" #userspace path manager client / server
2020-06-25 15:01:58 +00:00
USERPMC_ARGS = "upmc_args"
USERPMS_ARGS = "upms_args"
CLIENT_PCAP = "clientPcap"
SERVER_PCAP = "serverPcap"
SNAPLEN_PCAP = "snaplenPcap"
XP_TYPE = "xpType"
PING_COUNT = "pingCount"
PRIO_PATH0 = "prioPath0"
PRIO_PATH1 = "prioPath1"
BACKUP_PATH0 = "backupPath0"
BACKUP_PATH1 = "backupPath1"
2020-06-25 12:56:47 +00:00
EXPIRATION = "expiration"
2020-06-25 15:01:58 +00:00
BUFFER_AUTOTUNING = "bufferAutotuning"
2020-06-25 12:56:47 +00:00
METRIC = "metric"
# Global sysctl keys
SYSCTL_KEY = {
RMEM: "net.ipv4.tcp_rmem",
WMEM: "net.ipv4.tcp_wmem",
KERNELPM: "net.mptcp.mptcp_path_manager",
SCHED: "net.mptcp.mptcp_scheduler",
CC: "net.ipv4.tcp_congestion_control",
AUTOCORK: "net.ipv4.tcp_autocorking",
2020-06-25 15:01:58 +00:00
EARLY_RETRANS: "net.ipv4.tcp_early_retrans",
2020-06-25 12:56:47 +00:00
EXPIRATION: "net.mptcp.mptcp_sched_expiration",
2020-06-25 15:01:58 +00:00
BUFFER_AUTOTUNING: "net.ipv4.tcp_moderate_rcvbuf",
2020-06-25 12:56:47 +00:00
}
# sysctl keys specific to client and server, independently
SYSCTL_KEY_CLIENT = {
KERNELPMC: "net.mptcp.mptcp_path_manager",
}
SYSCTL_KEY_SERVER = {
KERNELPMS: "net.mptcp.mptcp_path_manager",
}
2020-06-26 06:52:56 +00:00
# Default values for unspecified experiment parameters
2020-06-25 12:56:47 +00:00
DEFAULT_PARAMETERS = {
RMEM: "10240 87380 16777216",
WMEM: "4096 16384 4194304",
KERNELPM: "fullmesh",
KERNELPMC: "fullmesh",
KERNELPMS: "fullmesh",
USERPMC: "fullmesh",
USERPMS: "fullmesh",
2020-06-25 15:01:58 +00:00
USERPMC_ARGS: "",
USERPMS_ARGS: "",
2020-06-25 12:56:47 +00:00
CC: "olia",
SCHED: "default",
AUTOCORK: "1",
2020-06-25 15:01:58 +00:00
EARLY_RETRANS: "3",
2020-06-25 12:56:47 +00:00
EXPIRATION: "300",
2020-06-25 15:01:58 +00:00
BUFFER_AUTOTUNING: "1",
2020-06-25 12:56:47 +00:00
METRIC: "-1",
2020-06-25 15:01:58 +00:00
CLIENT_PCAP: "no",
SERVER_PCAP: "no",
SNAPLEN_PCAP: "65535", # Default snapping value of tcpdump
XP_TYPE: "none",
PING_COUNT: "5",
PRIO_PATH0: "0",
PRIO_PATH1: "0",
BACKUP_PATH0: "0",
BACKUP_PATH1: "0",
2020-06-25 12:56:47 +00:00
}
def __init__(self, parameter_filename):
2020-06-26 06:52:56 +00:00
super(ExperimentParameter, self).__init__(parameter_filename)
self.default_parameters = ExperimentParameter.DEFAULT_PARAMETERS
2020-06-25 12:56:47 +00:00
def get(self, key):
2020-06-26 06:52:56 +00:00
val = super(ExperimentParameter, self).get(key)
2020-06-25 12:56:47 +00:00
if val is None:
if key in self.default_parameters:
return self.default_parameters[key]
else:
raise Exception("Parameter not found " + key)
else:
return val
2020-06-26 06:52:56 +00:00
class Experiment(object):
2020-06-24 14:11:54 +00:00
"""
2020-06-26 06:52:56 +00:00
Base class to instantiate an experiment to perform.
2020-06-24 14:11:54 +00:00
This class is not instantiable as it. You must define a child class with the
`NAME` attribute.
2020-06-26 06:52:56 +00:00
By default, an Experiment relies on an instance of ExperimentParameter to
collect the parameters from the experiment configuration file. However, an
experiment may introduce specific parameters in the configuration file. In
2020-06-25 12:56:47 +00:00
such case, the inherinting class must override the `PARAMETER_CLASS` class
2020-06-26 06:52:56 +00:00
variable to point to another class inheriting from ExperimentParameter.
2020-06-25 12:56:47 +00:00
Attributes:
2020-06-26 06:52:56 +00:00
experiment_parameter Instance of ExperimentParameter
topo Instance of Topo
topo_config Instance of TopoConfig
2020-06-24 14:11:54 +00:00
"""
2020-06-26 06:52:56 +00:00
PARAMETER_CLASS = ExperimentParameter
2020-06-24 14:11:54 +00:00
2020-06-26 06:52:56 +00:00
def __init__(self, experiment_parameter_filename, topo, topo_config):
2020-06-25 12:56:47 +00:00
"""
2020-06-26 06:52:56 +00:00
Instantiation of this base class only load the experiment parameter
2020-06-25 12:56:47 +00:00
"""
2020-06-26 06:52:56 +00:00
self.experiment_parameter = self.__class__.PARAMETER_CLASS(experiment_parameter_filename)
self.topo = topo
self.topo_config = topo_config
2020-06-25 12:56:47 +00:00
def load_parameters(self):
"""
2020-06-26 06:52:56 +00:00
Load the parameter of interest from self.experiment_parameter
2020-06-25 12:56:47 +00:00
"""
# Nothing to do in the base class
pass
def classic_run(self):
"""
Default function to perform the experiment. It consists into three phases:
- A preparation phase through `prepare()` (generating experiment files,...)
2020-06-26 06:52:56 +00:00
- A running phase through `run()` (where the actual experiment takes place)
- A cleaning phase through `clean()` (stopping traffic, removing generated files,...)
"""
2020-06-23 11:20:07 +00:00
self.prepare()
self.run()
self.clean()
2020-06-23 11:20:07 +00:00
def prepare(self):
"""
2020-06-26 06:52:56 +00:00
Prepare the environment to run the experiment.
Typically, when you inherit from this class, you want to extend this
method, while still calling this parent function.
2020-06-26 06:52:56 +00:00
TODO: split experiment traffic and protocol configuration
"""
self.setup_sysctl()
self.run_userspace_path_manager() # TODO to move elsewhere
self.topo_config.configure_network()
self.change_metric() # TODO to move elsewhere
self.put_priority_on_paths() # TODO to move elsewhere
self.disable_tso()
2020-06-23 11:20:07 +00:00
self.runTcpDump()
self.runNetemAt()
def change_metric(self):
"""
Function only meaningful for MPTCP and its specific scheduler
"""
2020-06-26 06:52:56 +00:00
metric = self.experiment_parameter.get(ExperimentParameter.METRIC)
2020-06-23 11:20:07 +00:00
if int(metric) >= 0:
self.topo.command_global(
"echo {} > /sys/module/mptcp_sched_metric/parameters/metric".format(metric))
def put_priority_on_paths(self):
"""
Function only meaningful for MPTCP
"""
2020-06-24 10:28:44 +00:00
# Only meaningful if mpTopo is instance of MultiInterfaceTopo
if isinstance(self.topo, MultiInterfaceTopo):
2020-06-26 06:52:56 +00:00
prioPath0 = self.experiment_parameter.get(ExperimentParameter.PRIO_PATH0)
prioPath1 = self.experiment_parameter.get(ExperimentParameter.PRIO_PATH1)
2020-06-23 11:20:07 +00:00
if not prioPath0 == prioPath1:
self.topo.command_to(self.topo_config.client, "/home/mininet/iproute/ip/ip link set dev " +
self.topo_config.getClientInterface(0) + " priority " + str(prioPath0))
self.topo.command_to(self.topo_config.router, "/home/mininet/iproute/ip/ip link set dev " +
self.topo_config.getRouterInterfaceSwitch(0) + " priority " +
2020-06-23 11:20:07 +00:00
str(prioPath0))
self.topo.command_to(self.topo_config.client, "/home/mininet/iproute/ip/ip link set dev " +
self.topo_config.getClientInterface(1) + " priority " + str(prioPath1))
self.topo.command_to(self.topo_config.router, "/home/mininet/iproute/ip/ip link set dev " +
self.topo_config.getRouterInterfaceSwitch(1) + " priority " +
2020-06-23 11:20:07 +00:00
str(prioPath1))
2020-06-26 06:52:56 +00:00
backupPath0 = self.experiment_parameter.get(ExperimentParameter.BACKUP_PATH0)
2020-06-23 11:20:07 +00:00
if int(backupPath0) > 0:
self.topo.command_to(self.topo_config.client, self.topo_config.interfaceBUPCommand(self.topo_config.getClientInterface(0)))
self.topo.command_to(self.topo_config.router, self.topo_config.interfaceBUPCommand(self.topo_config.getRouterInterfaceSwitch(0)))
2020-06-26 06:52:56 +00:00
backupPath1 = self.experiment_parameter.get(ExperimentParameter.BACKUP_PATH1)
2020-06-23 11:20:07 +00:00
if int(backupPath1) > 0:
self.topo.command_to(self.topo_config.client, self.topo_config.interfaceBUPCommand(self.topo_config.getClientInterface(1)))
self.topo.command_to(self.topo_config.router, self.topo_config.interfaceBUPCommand(self.topo_config.getRouterInterfaceSwitch(1)))
def disable_tso(self):
links = self.topo.getLinkCharacteristics()
2020-06-23 11:20:07 +00:00
i = 0
for l in links:
lname = self.topo_config.getMidLeftName(i)
rname = self.topo_config.getMidRightName(i)
lbox = self.topo.get_host(lname)
lif = self.topo_config.getMidL2RInterface(i)
rif = self.topo_config.getMidR2LInterface(i)
rbox = self.topo.get_host(rname)
2020-06-23 11:20:07 +00:00
print(str(lname) + " " + str(lif))
print(str(rname) + " " + str(rif))
print("boxes " + str(lbox) + " " + str(rbox))
cmd = "ethtool -K " + lif + " tso off"
print(cmd)
self.topo.command_to(lbox, cmd)
2020-06-23 11:20:07 +00:00
cmd = "ethtool -K " + rif + " tso off"
print(cmd)
self.topo.command_to(rbox, cmd)
2020-06-23 11:20:07 +00:00
i = i + 1
2020-06-23 11:20:07 +00:00
# And for the server
cmd = "ethtool -K " + self.topo_config.getServerInterface() + " tso off"
2020-06-23 11:20:07 +00:00
print(cmd)
self.topo.command_to(self.topo_config.server, cmd)
cmd = "ethtool -K " + self.topo_config.getRouterInterfaceSwitch(self.topo_config.getClientInterfaceCount()) + " tso off"
2020-06-23 11:20:07 +00:00
print(cmd)
self.topo.command_to(self.topo_config.router, cmd)
def run_userspace_path_manager(self):
2020-06-26 06:52:56 +00:00
if self.experiment_parameter.get(ExperimentParameter.KERNELPMC) != "netlink":
2020-06-23 11:20:07 +00:00
print("Client : Error, I can't change the userspace pm if the kernel pm is not netlink !")
else:
2020-06-26 06:52:56 +00:00
upmc = self.experiment_parameter.get(ExperimentParameter.USERPMC)
upmca = self.experiment_parameter.get(ExperimentParameter.USERPMC_ARGS)
self.topo.command_to(self.topo_config.client, upmc + \
2020-06-23 11:20:07 +00:00
" " + upmca + " &>upmc.log &")
2020-06-26 06:52:56 +00:00
if self.experiment_parameter.get(ExperimentParameter.KERNELPMS) != "netlink":
2020-06-23 11:20:07 +00:00
print("Server : Error, I can't change the userspace pm if the kernel pm is not netlink !")
else:
2020-06-26 06:52:56 +00:00
upms = self.experiment_parameter.get(ExperimentParameter.USERPMS)
upmsa = self.experiment_parameter.get(ExperimentParameter.USERPMS_ARGS)
self.topo.command_to(self.topo_config.server, upms + \
2020-06-23 11:20:07 +00:00
" " + upmsa + " &>upms.log &")
2020-06-23 11:20:07 +00:00
def cleanUserspacePM(self):
2020-06-26 06:52:56 +00:00
if self.experiment_parameter.get(ExperimentParameter.KERNELPMC) != "netlink":
2020-06-23 11:20:07 +00:00
print("Client : Error, I can't change the userspace pm if the kernel pm is not netlink !")
else:
2020-06-26 06:52:56 +00:00
upmc = self.experiment_parameter.get(ExperimentParameter.USERPMC)
self.topo.command_to(self.topo_config.client, "killall " + upmc)
2020-06-26 06:52:56 +00:00
if self.experiment_parameter.get(ExperimentParameter.KERNELPMS) != "netlink":
2020-06-23 11:20:07 +00:00
print("Server : Error, I can't change the userspace pm if the kernel pm is not netlink !")
else:
2020-06-26 06:52:56 +00:00
upms = self.experiment_parameter.get(ExperimentParameter.USERPMS)
self.topo.command_to(self.topo_config.server, "killall " + upms)
2020-06-23 11:20:07 +00:00
def runNetemAt(self):
if not self.topo.changeNetem == "yes":
2020-06-23 11:20:07 +00:00
print("I don't need to change netem")
return
print("Will change netem config on the fly")
links = self.topo.getLinkCharacteristics()
2020-06-23 11:20:07 +00:00
i = 0
for l in links:
lname = self.topo_config.getMidLeftName(i)
rname = self.topo_config.getMidRightName(i)
lbox = self.topo.get_host(lname)
lif = self.topo_config.getMidL2RInterface(i)
rif = self.topo_config.getMidR2LInterface(i)
rbox = self.topo.get_host(rname)
2020-06-23 11:20:07 +00:00
print(str(lname) + " " + str(lif))
print(str(rname) + " " + str(rif))
print("boxes " + str(lbox) + " " + str(rbox))
cmd = l.buildBwCmd(lif)
print(cmd)
self.topo.command_to(lbox, cmd)
2020-06-23 11:20:07 +00:00
cmd = l.buildBwCmd(rif)
print(cmd)
self.topo.command_to(rbox, cmd)
ilif = self.topo_config.getMidL2RIncomingInterface(i)
irif = self.topo_config.getMidR2LIncomingInterface(i)
2020-06-23 11:20:07 +00:00
cmd = l.buildPolicingCmd(ilif)
print(cmd)
self.topo.command_to(lbox, cmd)
2020-06-23 11:20:07 +00:00
cmd = l.buildPolicingCmd(irif)
print(cmd)
self.topo.command_to(rbox, cmd)
2020-06-23 11:20:07 +00:00
cmd = l.buildNetemCmd(irif)
print(cmd)
self.topo.command_to(rbox, cmd)
2020-06-23 11:20:07 +00:00
cmd = l.buildNetemCmd(ilif)
print(cmd)
self.topo.command_to(lbox, cmd)
2020-06-23 11:20:07 +00:00
i = i + 1
2020-06-23 11:20:07 +00:00
def run(self):
pass
2020-06-23 11:20:07 +00:00
def clean(self):
self.topo.command_to(self.topo_config.client,
2020-06-23 11:20:07 +00:00
"killall tcpdump")
self.topo.command_to(self.topo_config.server,
2020-06-23 11:20:07 +00:00
"killall tcpdump")
self.backUpSysctl()
self.cleanUserspacePM()
pass
def setup_sysctl(self):
self.save_sysctl()
self.write_sysctl()
def save_sysctl(self):
2020-06-23 11:20:07 +00:00
self.sysctlBUP = {}
2020-06-26 06:52:56 +00:00
self._save_sysctl(ExperimentParameter.SYSCTL_KEY, self.sysctlBUP)
2020-06-23 11:20:07 +00:00
self.sysctlBUPC = {}
2020-06-26 06:52:56 +00:00
self._save_sysctl(ExperimentParameter.SYSCTL_KEY_CLIENT, self.sysctlBUPC,
ns = True, who = self.topo_config.client)
2020-06-23 11:20:07 +00:00
self.sysctlBUPS = {}
2020-06-26 06:52:56 +00:00
self._save_sysctl(ExperimentParameter.SYSCTL_KEY_SERVER, self.sysctlBUPS,
ns = True, who = self.topo_config.server)
def _save_sysctl(self, sysctlDic, sysctlBUP, ns = False, who = None):
2020-06-23 11:20:07 +00:00
for k in sysctlDic:
2020-06-25 12:56:47 +00:00
SYSCTL_KEY = sysctlDic[k]
cmd = self.cmdReadSysctl(SYSCTL_KEY)
2020-06-23 11:20:07 +00:00
if not ns:
val = self.topo.command_global(cmd)
2020-06-23 11:20:07 +00:00
else:
val = self.topo.command_to(who, cmd)
2020-06-23 11:20:07 +00:00
if val == "Error":
2020-06-25 12:56:47 +00:00
print("oooops can't get sysctl " + SYSCTL_KEY)
2020-06-23 11:20:07 +00:00
else:
# For Python3 compatibility
if type(val) is bytes:
val = val.decode()
2020-06-23 11:20:07 +00:00
sysctlBUP[k] = val.split(" ",2)[2][:-1]
2020-06-23 11:20:07 +00:00
def cmdReadSysctl(self, key):
s = "sysctl " + key
return s
def cmd_write_sysctl(self, key, value):
2020-06-23 11:20:07 +00:00
s = self.cmdReadSysctl(key)
s = s + "=\"" + str(value) + "\""
return s
def write_sysctl(self):
2020-06-26 06:52:56 +00:00
self._write_sysctl(ExperimentParameter.SYSCTL_KEY, self.sysctlBUP)
self._write_sysctl(ExperimentParameter.SYSCTL_KEY_CLIENT, self.sysctlBUPC,
ns = True, who = self.topo_config.client)
2020-06-26 06:52:56 +00:00
self._write_sysctl(ExperimentParameter.SYSCTL_KEY_SERVER, self.sysctlBUPS,
ns = True, who = self.topo_config.server)
def _write_sysctl(self, sysctlDic, sysctlBUP, ns = False, who = None):
2020-06-23 11:20:07 +00:00
for k in sysctlBUP:
2020-06-25 12:56:47 +00:00
SYSCTL_KEY = sysctlDic[k]
2020-06-26 06:52:56 +00:00
sysctlValue = self.experiment_parameter.get(k)
2020-06-25 12:56:47 +00:00
cmd = self.cmd_write_sysctl(SYSCTL_KEY,sysctlValue)
2020-06-23 11:20:07 +00:00
if not ns:
val = self.topo.command_global(cmd)
2020-06-23 11:20:07 +00:00
else:
val = self.topo.command_to(who, cmd)
2020-06-23 11:20:07 +00:00
if val == "Error":
2020-06-25 12:56:47 +00:00
print("oooops can't set sysctl " + SYSCTL_KEY)
2020-06-23 11:20:07 +00:00
def backUpSysctl(self):
2020-06-26 06:52:56 +00:00
self._backUpSysctl(ExperimentParameter.SYSCTL_KEY, self.sysctlBUP)
self._backUpSysctl(ExperimentParameter.SYSCTL_KEY_CLIENT, self.sysctlBUPC,
ns = True, who = self.topo_config.client)
2020-06-26 06:52:56 +00:00
self._backUpSysctl(ExperimentParameter.SYSCTL_KEY_SERVER, self.sysctlBUPS,
ns = True, who = self.topo_config.server)
2020-06-23 11:20:07 +00:00
def _backUpSysctl(self, sysctlDic, sysctlBUP, ns = False, who = None):
for k in sysctlBUP:
2020-06-25 12:56:47 +00:00
SYSCTL_KEY = sysctlDic[k]
2020-06-23 11:20:07 +00:00
sysctlValue = sysctlBUP[k]
2020-06-25 12:56:47 +00:00
cmd = self.cmd_write_sysctl(SYSCTL_KEY,sysctlValue)
2020-06-23 11:20:07 +00:00
if not ns:
val = self.topo.command_global(cmd)
2020-06-23 11:20:07 +00:00
else:
val = self.topo.command_to(who, cmd)
2020-06-23 11:20:07 +00:00
if val == "Error":
2020-06-25 12:56:47 +00:00
print("oooops can't set sysctl " + SYSCTL_KEY)
2020-06-23 11:20:07 +00:00
def runTcpDump(self):
#todo : replace filename by cst
2020-06-26 06:52:56 +00:00
cpcap = self.experiment_parameter.get(ExperimentParameter.CLIENT_PCAP)
spcap = self.experiment_parameter.get(ExperimentParameter.SERVER_PCAP)
snaplenpcap = self.experiment_parameter.get(ExperimentParameter.SNAPLEN_PCAP)
2020-06-23 11:20:07 +00:00
if cpcap == "yes" :
self.topo.command_to(self.topo_config.client,
2020-06-23 11:20:07 +00:00
"tcpdump -i any -s " + snaplenpcap + " -w client.pcap &")
if spcap == "yes" :
self.topo.command_to(self.topo_config.server,
2020-06-23 11:20:07 +00:00
"tcpdump -i any -s " + snaplenpcap + " -w server.pcap &")
if spcap == "yes" or cpcap == "yes":
self.topo.command_to(self.topo_config.client,"sleep 5")
2020-06-25 12:56:47 +00:00
2020-06-26 06:52:56 +00:00
class RandomFileParameter(ExperimentParameter):
2020-06-25 12:56:47 +00:00
"""
2020-06-26 06:52:56 +00:00
Parameters for the RandomFileExperiment
2020-06-25 12:56:47 +00:00
"""
FILE = "file" # file to fetch; if random, we create a file with random data called random.
RANDOM_SIZE = "file_size" # in KB
2020-06-26 06:52:56 +00:00
def __init__(self, experiment_parameter_filename):
super(RandomFileParameter, self).__init__(experiment_parameter_filename)
2020-06-25 12:56:47 +00:00
self.default_parameters.update({
RandomFileParameter.FILE: "random",
RandomFileParameter.RANDOM_SIZE: "1024",
})
2020-06-26 06:52:56 +00:00
class RandomFileExperiment(Experiment):
2020-06-25 12:56:47 +00:00
"""
2020-06-26 06:52:56 +00:00
Enable a experiment to use random files
2020-06-25 12:56:47 +00:00
This class is not directly instantiable
"""
PARAMETER_CLASS = RandomFileParameter
2020-06-26 06:52:56 +00:00
def __init__(self, experiment_parameter_filename, topo, topo_config):
super(RandomFileExperiment, self).__init__(experiment_parameter_filename, topo, topo_config)
2020-06-25 12:56:47 +00:00
self.load_parameters()
self.ping()
def load_parameters(self):
2020-06-26 06:52:56 +00:00
super(RandomFileExperiment, self).load_parameters()
self.file = self.experiment_parameter.get(RandomFileParameter.FILE)
self.random_size = self.experiment_parameter.get(RandomFileParameter.RANDOM_SIZE)
2020-06-25 12:56:47 +00:00
def prepare(self):
2020-06-26 06:52:56 +00:00
super(RandomFileExperiment, self).prepare()
2020-06-25 12:56:47 +00:00
if self.file == "random":
self.topo.command_to(self.topo_config.client,
"dd if=/dev/urandom of=random bs=1K count={}".format(self.random_size))
def clean(self):
2020-06-26 06:52:56 +00:00
super(RandomFileExperiment, self).clean()
2020-06-25 12:56:47 +00:00
if self.file == "random":
self.topo.command_to(self.topo_config.client, "rm random*")