From: Christos Sfakianakis csfakian@redhat.com
Add/edit methods in PingTestAndEvaluate to handle parallel scenarios: a) added "ping_init" to initiate a Tests.Ping instance for all scenarios b) edited "ping_test" to split between default and parallel scenarios c) added "parallel_ping_evaluate_and_report" as the analogous of "ping_evaluate_and_report" for the parallel case d) added "single_ping_evaluate_and_report" to report which ip's are used each time in the parallel case
In lnst.Recipes.BaseEnrtRecipe, allow the user to specify ping interval, count, packet size. Include additional parameters for specifying parallel scenarios, as well as bidirectional cases. Modify "generate_ping_configurations" method to account for parallel scenarios. Assume "ip_versions" and the 2 endpoints are compatible in that the latter share equal numbers of corresponding ip's and output error otherwise. Filter off link-local ipv6 addresses. Generate a list of ping configurations for each ip version specified in a parallel scenario, or the content of a single-element list in the default, non-parallel case.
Signed-off-by: Christos Sfakianakis csfakian@redhat.com --- lnst/RecipeCommon/Ping.py | 57 ++++++++++++++++++++++++-- lnst/Recipes/ENRT/BaseEnrtRecipe.py | 62 +++++++++++++++++++++++------ 2 files changed, 103 insertions(+), 16 deletions(-)
diff --git a/lnst/RecipeCommon/Ping.py b/lnst/RecipeCommon/Ping.py index f5cd652..194d984 100644 --- a/lnst/RecipeCommon/Ping.py +++ b/lnst/RecipeCommon/Ping.py @@ -1,5 +1,8 @@ +from copy import copy + from lnst.Controller.Recipe import BaseRecipe from lnst.Tests import Ping +from lnst.Controller.RecipeResults import ResultLevel
class PingConf(object): def __init__(self, @@ -44,22 +47,68 @@ class PingConf(object):
class PingTestAndEvaluate(BaseRecipe): def ping_test(self, ping_config): + #parallel scenario + if isinstance(ping_config, list): + results = {} + + running_ping_array = [] + for pingconf in ping_config: + ping, client = self.ping_init(pingconf) + running_ping = client.prepare_job(ping) + running_ping.start(bg = True) + running_ping_array.append((pingconf, running_ping)) + + for _, pingjob in running_ping_array: + try: + pingjob.wait() + finally: + pingjob.kill() + + for pingconf, pingjob in running_ping_array: + result = pingjob.result + results[pingconf] = result + + return results + + #non-parallel scenario + ping, client = self.ping_init(ping_config) + ping_job = client.run(ping) + return ping_job.result + + def ping_init(self, ping_config): client = ping_config.client destination = ping_config.destination - kwargs = self._generate_ping_kwargs(ping_config) ping = Ping(**kwargs) - - ping_job = client.run(ping) - return ping_job.result + return (ping, client)
def ping_evaluate_and_report(self, ping_config, results): + if isinstance(ping_config, list): + self.parallel_ping_evaluate_and_report(results) + return # do we want to use the "perf" measurements (store a baseline etc...) as well? if results["rate"] > 50: self.add_result(True, "Ping succesful", results) else: self.add_result(False, "Ping unsuccesful", results)
+ #parallel version of ping_evaluate_and_report + def parallel_ping_evaluate_and_report(self, results): + for pingconf, result in results.items(): + self.single_ping_evaluate_and_report(pingconf, result) + + #clarify source/destination in reporting for parallel scenarios + def single_ping_evaluate_and_report(self, ping_config, results): + fmt = "From: <{0.client.hostid} ({0.client_bind})> To: " \ + "<{0.destination.hostid} ({0.destination_address})>" + description = fmt.format(ping_config) + if results["rate"] > 50: + message = "Ping successful --- " + description + self.add_result(True, message, results) + else: + message = "Ping unsuccessful --- " + description + self.add_result(False, message, results) + def _generate_ping_kwargs(self, ping_config): kwargs = dict(dst=ping_config.destination_address, interface=ping_config.client_bind) diff --git a/lnst/Recipes/ENRT/BaseEnrtRecipe.py b/lnst/Recipes/ENRT/BaseEnrtRecipe.py index d7d1aec..cbab341 100644 --- a/lnst/Recipes/ENRT/BaseEnrtRecipe.py +++ b/lnst/Recipes/ENRT/BaseEnrtRecipe.py @@ -65,6 +65,13 @@ class EnrtSubConfiguration(object):
class BaseEnrtRecipe(PingTestAndEvaluate, PerfRecipe): ip_versions = Param(default=("ipv4", "ipv6")) + + ping_parallel = BoolParam(default=False) + ping_bidirect = BoolParam(default=False) + ping_count = IntParam(default = 100) + ping_interval = StrParam(default = 0.2) + ping_psize = IntParam(default = None) + perf_tests = Param(default=("tcp_stream", "udp_stream", "sctp_stream"))
offload_combinations = Param(default=( @@ -158,22 +165,53 @@ class BaseEnrtRecipe(PingTestAndEvaluate, PerfRecipe): def generate_ping_configurations(self, main_config, sub_config): client_nic = main_config.endpoint1 server_nic = main_config.endpoint2 - client_netns = client_nic.netns - server_netns = server_nic.netns + + count = self.params.ping_count + interval = self.params.ping_interval + size = self.params.ping_psize + common_args = {'count' : count, 'interval' : interval, 'size' : size}
for ipv in self.params.ip_versions: + kwargs = {} if ipv == "ipv4": - family = AF_INET + kwargs.update(family = AF_INET) elif ipv == "ipv6": - family = AF_INET6 - - client_bind = client_nic.ips_filter(family=family)[0] - server_bind = server_nic.ips_filter(family=family)[0] - - yield PingConf(client = client_netns, - client_bind = client_bind, - destination = server_netns, - destination_address = server_bind) + kwargs.update(family = AF_INET6) + kwargs.update(link_local = False) + + client_ips = client_nic.ips_filter(**kwargs) + server_ips = server_nic.ips_filter(**kwargs) + + if len(client_ips) != len(server_ips) or len(client_ips) * len(server_ips) == 0: + raise LnstError("Source/destination ip lists are of different size or empty.") + + number_of_ips = len(client_ips) + ping_conf_list = [] + client_nic.valid_ips = client_ips + server_nic.valid_ips = server_ips + for n in range(number_of_ips): + for client_nic, server_nic in [(client_nic, server_nic), (server_nic, client_nic)]: + client_bind = client_nic.valid_ips[n] + server_bind = server_nic.valid_ips[n] + + pconf = PingConf(client = client_nic.netns, + client_bind = client_bind, + destination = server_nic.netns, + destination_address = server_bind, + **common_args) + + ping_conf_list.append(pconf) + + if not self.params.ping_bidirect: + break + + if not self.params.ping_parallel: + break + + if not self.params.ping_bidirect and not self.params.ping_parallel: + yield ping_conf_list[0] + else: + yield ping_conf_list
def generate_perf_configurations(self, main_config, sub_config): client_nic = main_config.endpoint1
On Fri, Jan 18, 2019 at 06:56:10PM +0100, csfakian@redhat.com wrote:
From: Christos Sfakianakis csfakian@redhat.com
Add/edit methods in PingTestAndEvaluate to handle parallel scenarios: a) added "ping_init" to initiate a Tests.Ping instance for all scenarios b) edited "ping_test" to split between default and parallel scenarios c) added "parallel_ping_evaluate_and_report" as the analogous of "ping_evaluate_and_report" for the parallel case d) added "single_ping_evaluate_and_report" to report which ip's are used each time in the parallel case
In lnst.Recipes.BaseEnrtRecipe, allow the user to specify ping interval, count, packet size. Include additional parameters for specifying parallel scenarios, as well as bidirectional cases. Modify "generate_ping_configurations" method to account for parallel scenarios. Assume "ip_versions" and the 2 endpoints are compatible in that the latter share equal numbers of corresponding ip's and output error otherwise. Filter off link-local ipv6 addresses. Generate a list of ping configurations for each ip version specified in a parallel scenario, or the content of a single-element list in the default, non-parallel case.
Signed-off-by: Christos Sfakianakis csfakian@redhat.com
lnst/RecipeCommon/Ping.py | 57 ++++++++++++++++++++++++-- lnst/Recipes/ENRT/BaseEnrtRecipe.py | 62 +++++++++++++++++++++++------ 2 files changed, 103 insertions(+), 16 deletions(-)
diff --git a/lnst/RecipeCommon/Ping.py b/lnst/RecipeCommon/Ping.py index f5cd652..194d984 100644 --- a/lnst/RecipeCommon/Ping.py +++ b/lnst/RecipeCommon/Ping.py @@ -1,5 +1,8 @@ +from copy import copy
from lnst.Controller.Recipe import BaseRecipe from lnst.Tests import Ping +from lnst.Controller.RecipeResults import ResultLevel
class PingConf(object): def __init__(self, @@ -44,22 +47,68 @@ class PingConf(object):
class PingTestAndEvaluate(BaseRecipe): def ping_test(self, ping_config):
One more time :) you can just do this:
if not isinstance(ping_config, list): ping_config = [ping_config]
and the rest of the implementation is "as if this is a parallel scenario".
#parallel scenario
if isinstance(ping_config, list):
results = {}
running_ping_array = []
for pingconf in ping_config:
ping, client = self.ping_init(pingconf)
running_ping = client.prepare_job(ping)
running_ping.start(bg = True)
running_ping_array.append((pingconf, running_ping))
for _, pingjob in running_ping_array:
try:
pingjob.wait()
finally:
pingjob.kill()
for pingconf, pingjob in running_ping_array:
result = pingjob.result
results[pingconf] = result
return results
#non-parallel scenario
ping, client = self.ping_init(ping_config)
ping_job = client.run(ping)
return ping_job.result
- def ping_init(self, ping_config): client = ping_config.client destination = ping_config.destination
kwargs = self._generate_ping_kwargs(ping_config) ping = Ping(**kwargs)
ping_job = client.run(ping)
return ping_job.result
return (ping, client)
def ping_evaluate_and_report(self, ping_config, results):
This is the same situation as with the ping_test method. Just if not isinstance(ping_config, list): ping_config = [ping_config]
and continue with the assumtion that it's always a parallel scenario and merge this method with the "parallel_ping_evaluate_and_report" method.
And you then don't need the "non-parallel" evaluation because it will automatically be handled by the single_ping_evaluate_and_report method.
if isinstance(ping_config, list):
self.parallel_ping_evaluate_and_report(results)
return # do we want to use the "perf" measurements (store a baseline etc...) as well? if results["rate"] > 50: self.add_result(True, "Ping succesful", results) else: self.add_result(False, "Ping unsuccesful", results)
#parallel version of ping_evaluate_and_report
def parallel_ping_evaluate_and_report(self, results):
for pingconf, result in results.items():
self.single_ping_evaluate_and_report(pingconf, result)
#clarify source/destination in reporting for parallel scenarios
def single_ping_evaluate_and_report(self, ping_config, results):
fmt = "From: <{0.client.hostid} ({0.client_bind})> To: " \
"<{0.destination.hostid} ({0.destination_address})>"
description = fmt.format(ping_config)
if results["rate"] > 50:
message = "Ping successful --- " + description
self.add_result(True, message, results)
else:
message = "Ping unsuccessful --- " + description
self.add_result(False, message, results)
def _generate_ping_kwargs(self, ping_config): kwargs = dict(dst=ping_config.destination_address, interface=ping_config.client_bind)
diff --git a/lnst/Recipes/ENRT/BaseEnrtRecipe.py b/lnst/Recipes/ENRT/BaseEnrtRecipe.py index d7d1aec..cbab341 100644 --- a/lnst/Recipes/ENRT/BaseEnrtRecipe.py +++ b/lnst/Recipes/ENRT/BaseEnrtRecipe.py @@ -65,6 +65,13 @@ class EnrtSubConfiguration(object):
class BaseEnrtRecipe(PingTestAndEvaluate, PerfRecipe): ip_versions = Param(default=("ipv4", "ipv6"))
ping_parallel = BoolParam(default=False)
ping_bidirect = BoolParam(default=False)
ping_count = IntParam(default = 100)
ping_interval = StrParam(default = 0.2)
ping_psize = IntParam(default = None)
perf_tests = Param(default=("tcp_stream", "udp_stream", "sctp_stream"))
offload_combinations = Param(default=(
@@ -158,22 +165,53 @@ class BaseEnrtRecipe(PingTestAndEvaluate, PerfRecipe): def generate_ping_configurations(self, main_config, sub_config): client_nic = main_config.endpoint1 server_nic = main_config.endpoint2
client_netns = client_nic.netns
server_netns = server_nic.netns
count = self.params.ping_count
interval = self.params.ping_interval
size = self.params.ping_psize
common_args = {'count' : count, 'interval' : interval, 'size' : size} for ipv in self.params.ip_versions:
kwargs = {} if ipv == "ipv4":
family = AF_INET
kwargs.update(family = AF_INET) elif ipv == "ipv6":
family = AF_INET6
client_bind = client_nic.ips_filter(family=family)[0]
server_bind = server_nic.ips_filter(family=family)[0]
yield PingConf(client = client_netns,
client_bind = client_bind,
destination = server_netns,
destination_address = server_bind)
kwargs.update(family = AF_INET6)
kwargs.update(link_local = False)
client_ips = client_nic.ips_filter(**kwargs)
server_ips = server_nic.ips_filter(**kwargs)
if len(client_ips) != len(server_ips) or len(client_ips) * len(server_ips) == 0:
raise LnstError("Source/destination ip lists are of different size or empty.")
number_of_ips = len(client_ips)
ping_conf_list = []
client_nic.valid_ips = client_ips
server_nic.valid_ips = server_ips
The client_nic and server_nic objects are RemoteDevice objects, assigning to an attribute is supposed to mean that you configure something on the Slave, assigning to an undefined attribute is not defined and even if it works right now it might change and either stop working or start working differently and cause issues.
I think you're only assigning to valid_ips so you can reverse the source/destinations? In that case I think you can just do:
for e1_ip, e2_ip in zip(client_ips, server_ips): PingConf(e1_ip, e2_ip) PingConf(e2_ip, e1_ip)
with reversed host objects... Also it might be easier to just drop the client/server nomenclature in this method and just use something like endpoints or pair split into source/destination. It might make it easier to think about this bidirectionality where the client/server changes.
for n in range(number_of_ips):
for client_nic, server_nic in [(client_nic, server_nic), (server_nic, client_nic)]:
client_bind = client_nic.valid_ips[n]
server_bind = server_nic.valid_ips[n]
pconf = PingConf(client = client_nic.netns,
client_bind = client_bind,
destination = server_nic.netns,
destination_address = server_bind,
**common_args)
ping_conf_list.append(pconf)
if not self.params.ping_bidirect:
break
if not self.params.ping_parallel:
break
You doubled the checks here? Turning the switches to False will only generate a list of a single PingConf so why split the yield into two cases in the lines below?
Sorry, this is the first time I reviewed this part since the last time it was messed up due to tabs/spaces and weird copy paste errors...
-Ondrej
if not self.params.ping_bidirect and not self.params.ping_parallel:
yield ping_conf_list[0]
else:
yield ping_conf_list
def generate_perf_configurations(self, main_config, sub_config): client_nic = main_config.endpoint1
-- 2.17.1 _______________________________________________ LNST-developers mailing list -- lnst-developers@lists.fedorahosted.org To unsubscribe send an email to lnst-developers-leave@lists.fedorahosted.org Fedora Code of Conduct: https://getfedora.org/code-of-conduct.html List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedorahosted.org/archives/list/lnst-developers@lists.fedorahos...
lnst-developers@lists.fedorahosted.org