[DISCUSSION] python version requirement
by Ondrej Lichtner
Hi all,
since we've moved to python3 that is actively developed and versions
move between various long term/short term support cycles, we should also
adapt LNST to this cycle of updating which minimal version of python
LNST requires.
TL;DR: The main questions I'm asking are:
* how do we _implement_ a python version requirement?
* how do we _upgrade_/_migrate_ in the future?
* how do we _document_ a python version requirement?
* which version do we want to use _now_?
More context:
I think at the moment we have a "soft" requirement for python3.6. Soft
because we:
* probably haven't tested on anything older
* it's not explicitly configured/documented anywhere
At the same time, there are now at least two reasons to start thinking
about moving to python3.8:
* I remember Perry asking about the f-string feature introduced in 3.8
* while working with Adrian on the TRex refactoring I started thinking
about a feature for the lnst.Tests package that I've had in mind for a
while, which requires a 3.7 feature
* python3.8 is the current version on Fedora32, is available in RHEL8
(via dnf install python38), and python3.7 was skipped
The lnst.Tests feature I'm thinking of is "lazy" and "dynamic" loading
of BaseTestModule derived modules - for example at the moment, if a
Recipe imports any module from lnst.Tests (e.g. lnst.Tests.Ping), the
entire package is parsed and "loaded", which means that the python
environment will also parse and load lnst.Tests.TRex. This means that a
basic hello world recipe that simply calls Ping, will in some way
require load time dependencies of TRex.
The "lazy" and "dynamic" loading of test modules would ensure that when
a recipe calls:
from lnst.Tests import Ping
Only the Ping module will be parsed, loaded and imported, and nothing
else. And the dynamicity here could mean that we could be able to extend
test modules exported by the lnst.Tests package via the lnst-ctl config
file, for example for user/tester implemented test modules that are not
tracked in the main lnst repository.
I wrote a rough patch to experiment with this:
---
diff --git a/lnst/Tests/__init__.py b/lnst/Tests/__init__.py
index f7c6c90..a39b6f4 100644
--- a/lnst/Tests/__init__.py
+++ b/lnst/Tests/__init__.py
@@ -12,8 +12,26 @@
olichtne(a)redhat.com (Ondrej Lichtner)
"""
-from lnst.Tests.Ping import Ping
-from lnst.Tests.PacketAssert import PacketAssert
-from lnst.Tests.Iperf import IperfClient, IperfServer
+# from lnst.Tests.Ping import Ping
+# from lnst.Tests.PacketAssert import PacketAssert
+# from lnst.Tests.Iperf import IperfClient, IperfServer
+import importlib
+
+lazy_load_modules = {
+ "Ping": "lnst.Tests.Ping",
+ "PacketAssert": "lnst.Tests.PacketAssert",
+ "IperfClient": "lnst.Tests.Iperf",
+ "IperfServer": "lnst.Tests.Iperf",
+}
+
+
+def __getattr__(name):
+ if name not in lazy_load_modules:
+ raise ImportError("Cannot import {}".format(name))
+ mod = importlib.import_module(lazy_load_modules[name])
+ globals()[name] = getattr(mod, name)
+ return globals()[name]
+
+
+# #TODO add support for test classes from lnst-ctl.conf
-#TODO add support for test classes from lnst-ctl.conf
---
However this requires the ability to define __getattr__ for a module,
which is introduced as a python3.7 feature via PEP562 [0].
-Ondrej
[0] https://www.python.org/dev/peps/pep-0562/
2 years, 8 months
[PATCH v2 1/4] Parameters.py: Added ChoiceParam
by pgagne@redhat.com
From: Perry Gagne <pgagne(a)redhat.com>
Added parameter for representing an option that can only
be one of a select group of values.
Signed-off-by: Perry Gagne <pgagne(a)redhat.com>
---
lnst/Common/Parameters.py | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/lnst/Common/Parameters.py b/lnst/Common/Parameters.py
index f73fd3a..e2b3063 100644
--- a/lnst/Common/Parameters.py
+++ b/lnst/Common/Parameters.py
@@ -153,6 +153,30 @@ class ListParam(Param):
.format(item, str(e)))
return value
+
+class ChoiceParam(Param):
+ """Choice Param
+ This parameter is used for sitiuation where a param can have one of
+ a specified set of valid values. For example:
+
+ >>> flow_type = ChoiceParam(type=StrParam, choices=set('tcp_rr', 'udp_rr', 'tcp_crr'))
+
+ The type check will fail if the specified value does not pass both the specified
+ subtype `type_check` or is not one of the specified choices.
+ """
+ def __init__(self, type=None, choices=set(), **kwargs):
+ self._type = type() if type is not None else None
+ self._choices = choices
+ super().__init__(**kwargs)
+
+ def type_check(self, value):
+ if self._type is not None:
+ value = self._type.type_check(value)
+ if value not in self._choices:
+ raise ParamError(f"Value '{value}' not one of {self._choices}")
+ return value
+
+
class Parameters(object):
def __init__(self):
self._attrs = {}
--
2.26.2
2 years, 9 months
photo shooting for your products
by Jason
Hi,
Hope all is well.
I am reaching you to check if you have photo shooting needs for your
products.
Our product photography studio is specially designed and outfitted with
professional cameras, lighting, reflectors, diffusers, soft boxes, props,
and all the tools needed to allow our team to take the product photos.
When our ecommerce photography studio is combined with the experience and
passion
of our product photographers, and post production team we have everything
needed to make your product looks good.
Our studio utilizes the latest in photographic and digital technology.
Our fully streamlined process allows us to produce a high quality product
in a streamlined process, which in turn allows us to pass on the savings to
you.
Our post production editing process is state of the art.
We use professional-grade displays and calibrate our monitors regularly
using
the very latest in advanced color calibration tools. Our photo studio is
setup to provide highly accurate color reproduction which is essential for
creating the product pictures.
Our product shoot photographers have extensive experience with all types of
products, to deliver
high quality product photos.
Looking forward to start cooperation with your company.
Thanks and Regards,
Jason Miller
2 years, 9 months
[PATCH 0/4] ShortLivedConnections using Neper
by pgagne@redhat.com
From: Perry Gagne <pgagne(a)redhat.com>
This set of patches updates ShortlivedConnectionsRecipe to use a new
Tool based around the Google neper project.
It also contains a new "ChoiceParam" for use when you have a parameter
that can be one of a defined set of things.
Perry Gagne (4):
Parameters.py: Added ChoiceParam
Utils.py: Add pairwise itertools recipe
Neper support for RR style tests
ShortLivedConnectionsRecipe.py: Update to use Neper
lnst/Common/Parameters.py | 30 +++
lnst/Common/Utils.py | 12 +
.../Perf/Measurements/NeperFlowMeasurement.py | 206 ++++++++++++++++++
.../Perf/Measurements/__init__.py | 1 +
lnst/Recipes/ENRT/LatencyEnrtRecipe.py | 47 ++++
.../NeperMeasurementGenerator.py | 109 +++++++++
.../ENRT/ShortLivedConnectionsRecipe.py | 8 +-
lnst/Tests/Neper.py | 128 +++++++++++
8 files changed, 537 insertions(+), 4 deletions(-)
create mode 100644 lnst/RecipeCommon/Perf/Measurements/NeperFlowMeasurement.py
create mode 100644 lnst/Recipes/ENRT/LatencyEnrtRecipe.py
create mode 100644 lnst/Recipes/ENRT/MeasurementGenerators/NeperMeasurementGenerator.py
create mode 100644 lnst/Tests/Neper.py
--
2.26.2
2 years, 9 months
[PATCH v2 00/13] Enable parallel iperf testing
by Jan Tluka
This patchset is another attempt to implement parallel iperf testing.
In comparison to the first RFC the changes are less intrusive and reuses
the original Flow concept.
Motivation:
The current parallel implementation that can be achieved by specifying
the perf_parallel_streams recipe parameter works correctly however the
limitation is that there's only one iperf process that creates multiple
connections and that process (and all the connections) can be handled by
a single CPU at the same time. In our internal testing this proved to
report very variable CPU utilization numbers.
This patchset extends the IperfFlowMeasurementGenerator with additional
recipe parameters:
* perf_parallel_processes
* perf_tool_cpu_policy
Additionaly some of the parameters were modified to support parallelism:
* perf_tool_cpu is now a ListParam
* dev_intr_cpu is now a ListParam
The patch set includes also update of DevInterruptHWConfigMixin that is
required for this test scenario to provide reproducible results.
Jan Tluka (13):
RecipeCommon.Perf.Measurements.BaseFlowMeasurement.Flow: add
receiver_port
RecipeCommon.Perf.Measurements.IperfFlowMeasurement: configure
receiver_port
Recipes.ENRT.MeasurementGenerators.IperfMeasurementGenerator: adapt to
Flow port changes
Recipes.ENRT.ConfigMixins.Reversible: adapt to Flow port changes
Recipes.ENRT.MeasurementGenerators.IperfMeasurementGenerator: add
_create_perf_flows
Recipes.ENRT.MeasurementGenerators.IperfMeasurementGenerator: add
perf_parallel_processes parameter
IperfMeasurementGenerator: adjust cpu parameters for parallel iperf
support
RecipeCommon.Perf.Measurements.IperfFlowMeasurement: adjust cpupin for
both server and client
RecipeCommon.Perf.Measurements.IperfFlowMeasurement: add
aggregate_multi_flow_results()
RecipeCommon.Perf.Evaluators.BaselineFlowAverageEvaluator: override
group_results
RecipeCommon.Perf.Measurements.BaseFlowMeasurement: report also
aggregated results
Recipes.ENRT.MeasurementGenerators.IperfMeasurementGenerator: fix
issue with unspecified perf_tool_cpu param
Recipes.ENRT.ConfigMixins.DevInterruptHWConfigMixin: change
dev_intr_cpu to ListParam
.../BaselineFlowAverageEvaluator.py | 10 ++
.../Perf/Measurements/BaseFlowMeasurement.py | 16 +++-
.../Perf/Measurements/IperfFlowMeasurement.py | 93 +++++++++++++++----
.../ConfigMixins/DevInterruptHWConfigMixin.py | 26 +++---
.../ConfigMixins/PerfReversibleFlowMixin.py | 34 +++++--
.../IperfMeasurementGenerator.py | 83 +++++++++++++----
6 files changed, 208 insertions(+), 54 deletions(-)
--
2.26.2
2 years, 9 months
[PATCH v2 1/2] Recipes.ENRT.IpsecEspAhCompRecipe: mahe "ciphers" and "hashes" into parameters
by olichtne@redhat.com
From: Ondrej Lichtner <olichtne(a)redhat.com>
To be able to differentiate these different recipe configurations in an
external database we need to make these two class variables into recipe
parameters.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Recipes/ENRT/IpsecEspAhCompRecipe.py | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/lnst/Recipes/ENRT/IpsecEspAhCompRecipe.py b/lnst/Recipes/ENRT/IpsecEspAhCompRecipe.py
index 59cb09d..dcf08c4 100644
--- a/lnst/Recipes/ENRT/IpsecEspAhCompRecipe.py
+++ b/lnst/Recipes/ENRT/IpsecEspAhCompRecipe.py
@@ -3,7 +3,7 @@
import copy
from lnst.Common.IpAddress import ipaddress
from lnst.Common.IpAddress import AF_INET, AF_INET6
-from lnst.Common.Parameters import StrParam
+from lnst.Common.Parameters import StrParam, Param
from lnst.Common.LnstError import LnstError
from lnst.Controller import HostReq, DeviceReq, RecipeParam
from lnst.Recipes.ENRT.BaremetalEnrtRecipe import BaremetalEnrtRecipe
@@ -26,11 +26,12 @@ class IpsecEspAhCompRecipe(CommonHWSubConfigMixin, BaremetalEnrtRecipe,
host2 = HostReq()
host2.eth0 = DeviceReq(label="to_switch", driver=RecipeParam("driver"))
- ciphers = [('aes', 128), ('aes', 256)]
- hashes = [('hmac(md5)', 128), ('sha256', 256)]
- spi_values = ["0x00000001", "0x00000002", "0x00000003", "0x00000004"]
+ ciphers = Param(default=[('aes', 128), ('aes', 256)])
+ hashes = Param(default=[('hmac(md5)', 128), ('sha256', 256)])
ipsec_mode = StrParam(default="transport")
+ spi_values = ["0x00000001", "0x00000002", "0x00000003", "0x00000004"]
+
def test_wide_configuration(self):
host1, host2 = self.matched.host1, self.matched.host2
@@ -93,8 +94,8 @@ def generate_sub_configurations(self, config):
ip1 = config.endpoint1.ips_filter(family=family)[0]
ip2 = config.endpoint2.ips_filter(family=family)[0]
- for ciph_alg, ciph_len in self.ciphers:
- for hash_alg, hash_len in self.hashes:
+ for ciph_alg, ciph_len in self.params.ciphers:
+ for hash_alg, hash_len in self.params.hashes:
ciph_key = generate_key(ciph_len)
hash_key = generate_key(hash_len)
new_config = copy.copy(subconf)
--
2.30.0
2 years, 9 months
[PATCH 1/2] Recipes.ENRT.IpsecEspAhCompRecipe: mahe "ciphers" and "hashes" into parameters
by olichtne@redhat.com
From: Ondrej Lichtner <olichtne(a)redhat.com>
To be able to differentiate these different recipe configurations in an
external database we need to make these two class variables into recipe
parameters.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Recipes/ENRT/IpsecEspAhCompRecipe.py | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/lnst/Recipes/ENRT/IpsecEspAhCompRecipe.py b/lnst/Recipes/ENRT/IpsecEspAhCompRecipe.py
index 59cb09d..dcf08c4 100644
--- a/lnst/Recipes/ENRT/IpsecEspAhCompRecipe.py
+++ b/lnst/Recipes/ENRT/IpsecEspAhCompRecipe.py
@@ -3,7 +3,7 @@
import copy
from lnst.Common.IpAddress import ipaddress
from lnst.Common.IpAddress import AF_INET, AF_INET6
-from lnst.Common.Parameters import StrParam
+from lnst.Common.Parameters import StrParam, Param
from lnst.Common.LnstError import LnstError
from lnst.Controller import HostReq, DeviceReq, RecipeParam
from lnst.Recipes.ENRT.BaremetalEnrtRecipe import BaremetalEnrtRecipe
@@ -26,11 +26,12 @@ class IpsecEspAhCompRecipe(CommonHWSubConfigMixin, BaremetalEnrtRecipe,
host2 = HostReq()
host2.eth0 = DeviceReq(label="to_switch", driver=RecipeParam("driver"))
- ciphers = [('aes', 128), ('aes', 256)]
- hashes = [('hmac(md5)', 128), ('sha256', 256)]
- spi_values = ["0x00000001", "0x00000002", "0x00000003", "0x00000004"]
+ ciphers = Param(default=[('aes', 128), ('aes', 256)])
+ hashes = Param(default=[('hmac(md5)', 128), ('sha256', 256)])
ipsec_mode = StrParam(default="transport")
+ spi_values = ["0x00000001", "0x00000002", "0x00000003", "0x00000004"]
+
def test_wide_configuration(self):
host1, host2 = self.matched.host1, self.matched.host2
@@ -93,8 +94,8 @@ def generate_sub_configurations(self, config):
ip1 = config.endpoint1.ips_filter(family=family)[0]
ip2 = config.endpoint2.ips_filter(family=family)[0]
- for ciph_alg, ciph_len in self.ciphers:
- for hash_alg, hash_len in self.hashes:
+ for ciph_alg, ciph_len in self.params.ciphers:
+ for hash_alg, hash_len in self.params.hashes:
ciph_key = generate_key(ciph_len)
hash_key = generate_key(hash_len)
new_config = copy.copy(subconf)
--
2.30.0
2 years, 9 months
[PATCH] Recipes.ENRT.SimpleMacsecRecipe: fix flow combination generation
by olichtne@redhat.com
From: Ondrej Lichtner <olichtne(a)redhat.com>
The SimpleMacsecRecipe was overriding the generate_flow_combinations
method originally defined in BaseEnrtRecipe because there was no
separation of flow_endpoints yet.
This method override also contained a bug up until now - the
"ips_filter" was only using "family" filtering, but for ipv6 it should
also have used "is_link_local=False". This lead to undeteremenistic
behaviour when simetimes the link local address was used for the flow
measurement.
The solution is to remove this method and us the inherited method from
the IperfMeasurementGenerator class that has since been improved to use
"generate_perf_endpoints" which is added to the recipe instead.
I also added a wait_tentative_ips() call after calling "up()" on both
the eth and the macsec device in "apply_sub_configuration()" as this can
help avoid race condition issues when an ipv6 address isn't ready.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Recipes/ENRT/SimpleMacsecRecipe.py | 36 +++----------------------
1 file changed, 3 insertions(+), 33 deletions(-)
diff --git a/lnst/Recipes/ENRT/SimpleMacsecRecipe.py b/lnst/Recipes/ENRT/SimpleMacsecRecipe.py
index a903dea..75161fb 100644
--- a/lnst/Recipes/ENRT/SimpleMacsecRecipe.py
+++ b/lnst/Recipes/ENRT/SimpleMacsecRecipe.py
@@ -105,6 +105,7 @@ def apply_sub_configuration(self, config):
"/64"))
host.eth0.up()
host.msec0.up()
+ self.wait_tentative_ips([host.eth0, host.msec0])
def remove_sub_configuration(self, config):
if config.encrypt:
@@ -201,39 +202,8 @@ def generate_perf_configurations(self, config):
yield perf_conf
- def generate_flow_combinations(self, config):
- client_nic = config.host1.msec0
- server_nic = config.host2.msec0
- client_netns = client_nic.netns
- server_netns = server_nic.netns
-
- for ipv in self.params.ip_versions:
- if ipv == "ipv4":
- 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]
-
- for perf_test in self.params.perf_tests:
- for size in self.params.perf_msg_sizes:
- pstreams = self.params.perf_parallel_streams
- flow = PerfFlow(
- type = perf_test,
- generator = client_netns,
- generator_bind = client_bind,
- generator_nic = client_nic,
- receiver = server_netns,
- receiver_bind = server_bind,
- receiver_nic = server_nic,
- msg_size = size,
- duration = self.params.perf_duration,
- parallel_streams = pstreams,
- cpupin = self.params.perf_tool_cpu if (
- "perf_tool_cpu" in self.params) else None
- )
- yield [flow]
+ def generate_perf_endpoints(self, config):
+ return [(self.matched.host1.msec0, self.matched.host2.msec0)]
@property
def mtu_hw_config_dev_list(self):
--
2.30.0
2 years, 9 months
[PATCH 0/8] RFC: Refactor IperfFlowMeasurement for parallel iperf testing
by Jan Tluka
This patchset refactors IperfFlowMeasurement and
IperfFlowMeasurementGenerator classes to allow parallel iperf testing.
The current parallel implementation that can be achieved by specifying
the perf_parallel_streams recipe parameter works correctly however the
limitation is that there's only one iperf process that creates multiple
connections and that process (and all the connections) can be handled by
a single CPU at the same time. In our internal testing this proved to
report very variable CPU utilization numbers.
This patchset extends the IperfFlowMeasurementGenerator with additional
recipe parameters, the perf_parallel_processes and
perf_parallel_processes_cpus, to support paralell iperf testing.
The patch set includes also update of DevInterruptHWConfigMixin that is
required for this test scenario to provide reproducible results.
Jan Tluka (8):
Perf.Measurements.BaseFlowMeasurement.NetworkFlowTest: change flow to
contain a list of server/client jobs
Perf.Measurements.IperfFlowMeasurement: adapt to changes of
NetworkFlowTest
TRexFlowMeasurement: adapt to changes of NetworkFlowTest
Recipes.ENRT.MeasurementGenerators.IperfMeasurementGenerator: add
perf_parallel_processes parameter
Perf.Measurements.IperfFlowMeasurement: use parallel_perf_processes
parameter
Recipes.ENRT.MeasurementGenerators.IperfMeasurementGenerator: add
parallel_perf_processes_cpus parameter
Perf.Measurements.IperfFlowMeasurement: use parallel_processes_cpus
parameter
Recipes.ENRT.ConfigMixins.DevInterruptHWConfigMixin: change
dev_intr_cpu to dev_intr_cpus
.../Perf/Measurements/BaseFlowMeasurement.py | 28 +++++--
.../Perf/Measurements/IperfFlowMeasurement.py | 82 +++++++++++++------
.../Perf/Measurements/TRexFlowMeasurement.py | 31 ++++---
.../ConfigMixins/DevInterruptHWConfigMixin.py | 38 +++++----
.../IperfMeasurementGenerator.py | 19 +++++
5 files changed, 138 insertions(+), 60 deletions(-)
--
2.26.2
2 years, 9 months
[PATCH] IperfFlowMeasurement: fix parse_job_cpu duration get, again
by olichtne@redhat.com
From: Ondrej Lichtner <olichtne(a)redhat.com>
One more fix for accessing the test duration when parsing test results
for cpu utilization of the iperf process.
The json dictionaries are inconsistent between tcp/udp stream on how
they report the end of test data - udp reports only a "sum" dictionary,
tcp only reports a "sum_sent" and "sum_received" dictionaries. So
instead we look at the test start and the *requested* test duration. Not
as precise as the actual duration of the test but probably good enough
considering how the duration is used in this case...
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/RecipeCommon/Perf/Measurements/IperfFlowMeasurement.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnst/RecipeCommon/Perf/Measurements/IperfFlowMeasurement.py b/lnst/RecipeCommon/Perf/Measurements/IperfFlowMeasurement.py
index 87ef70f..722b49e 100644
--- a/lnst/RecipeCommon/Perf/Measurements/IperfFlowMeasurement.py
+++ b/lnst/RecipeCommon/Perf/Measurements/IperfFlowMeasurement.py
@@ -187,5 +187,5 @@ def _parse_job_cpu(self, job):
else:
cpu_percent = job.result["data"]["end"]["cpu_utilization_percent"]["host_total"]
job_start = job.result["data"]["start"]["timestamp"]["timesecs"]
- duration = job.result["data"]["end"]["sum"]["seconds"]
+ duration = job.result["data"]["start"]["test_start"]["duration"]
return PerfInterval(cpu_percent*duration, duration, "cpu_percent", job_start)
--
2.30.0
2 years, 10 months