aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/model/current/schema/test_case.info.schema.json72
-rw-r--r--docs/model/current/schema/test_case.info.schema.yaml69
-rw-r--r--docs/model/current/schema/todos.txt8
-rw-r--r--docs/model/current/schema/yaml2json.py9
-rw-r--r--docs/model/current/top.rst4
-rw-r--r--resources/libraries/python/Constants.py4
-rw-r--r--resources/libraries/python/HoststackUtil.py23
-rw-r--r--resources/libraries/python/model/ExportResult.py158
-rwxr-xr-xresources/tools/ab/ABFork.py4
-rw-r--r--resources/tools/ab/ABTools.py87
10 files changed, 287 insertions, 151 deletions
diff --git a/docs/model/current/schema/test_case.info.schema.json b/docs/model/current/schema/test_case.info.schema.json
index 7bdfa27f8d..bdfea59907 100644
--- a/docs/model/current/schema/test_case.info.schema.json
+++ b/docs/model/current/schema/test_case.info.schema.json
@@ -1,5 +1,5 @@
{
- "$id": "https://fd.io/FIXME/CSIT/UTI/test_case/info/1.2.0",
+ "$id": "https://fd.io/FIXME/CSIT/UTI/test_case/info/1.3.0",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "Schema for output of test case.",
"allOf": [
@@ -159,9 +159,51 @@
}
},
"required": [
+ "type",
"loss",
"aggregate_rate"
]
+ },
+ {
+ "description": "Result type HOSTSTACK case.",
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "hoststack"
+ },
+ "bandwidth": {
+ "description": "Goodput measured in bits per second.",
+ "$ref": "#/$defs/types/bandwidth"
+ },
+ "completed_requests": {
+ "description": "Number of completed requests.",
+ "$ref": "#/$defs/types/count_requests"
+ },
+ "failed_requests": {
+ "description": "Number of failed requests.",
+ "$ref": "#/$defs/types/count_requests"
+ },
+ "retransmits": {
+ "description": "Number of retransmits.",
+ "$ref": "#/$defs/types/count_packets"
+ },
+ "latency": {
+ "description": "Value and unit of latency.",
+ "$ref": "#/$defs/types/value_with_unit"
+ },
+ "duration": {
+ "description": "The relative time difference (in seconds) between program start and end.",
+ "$ref": "#/$defs/types/time_quantity"
+ },
+ "rate": {
+ "description": "RPS or CPS rate, with corresponding unit, as reported by TG.",
+ "$ref": "#/$defs/types/rate_without_bandwidth"
+ }
+ },
+ "required": [
+ "type",
+ "bandwidth"
+ ]
}
]
}
@@ -224,7 +266,7 @@
"version": {
"description": "CSIT model version (semver format) the exporting code adhered to.",
"type": "string",
- "const": "1.2.0"
+ "const": "1.3.0"
}
},
"required": [
@@ -306,11 +348,12 @@
"maxItems": 0
},
"rate_unit": {
- "description": "Packets per second (pps) or connections per second (cps).",
+ "description": "Packets per second (pps), connections per second (cps), requests per second (rps).",
"type": "string",
"enum": [
"pps",
- "cps"
+ "cps",
+ "rps"
]
},
"bandwidth_unit": {
@@ -340,6 +383,27 @@
}
]
},
+ "count_requests": {
+ "description": "Type, for counting requests.",
+ "allOf": [
+ {
+ "$ref": "#/$defs/types/value_with_unit"
+ },
+ {
+ "properties": {
+ "value": {
+ "description": "A number of requests of interest."
+ },
+ "unit": {
+ "description": "Unit suitable for displaying request counts.",
+ "enum": [
+ "requests"
+ ]
+ }
+ }
+ }
+ ]
+ },
"time_quantity": {
"description": "Reusable type, for various time quantites.",
"allOf": [
diff --git a/docs/model/current/schema/test_case.info.schema.yaml b/docs/model/current/schema/test_case.info.schema.yaml
index 022061aa39..bc5a35007f 100644
--- a/docs/model/current/schema/test_case.info.schema.yaml
+++ b/docs/model/current/schema/test_case.info.schema.yaml
@@ -1,4 +1,4 @@
-# Copyright (c) 2022 Cisco and/or its affiliates.
+# Copyright (c) 2023 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
@@ -13,7 +13,7 @@
---
-$id: https://fd.io/FIXME/CSIT/UTI/test_case/info/1.2.0
+$id: https://fd.io/FIXME/CSIT/UTI/test_case/info/1.3.0
$schema: https://json-schema.org/draft/2020-12/schema
description: >-
Schema for output of test case.
@@ -234,8 +234,48 @@ allOf:
packet rate.
$ref: "#/$defs/types/packet_with_time"
required:
+ - type
- loss
- aggregate_rate
+ - description: >-
+ Result type HOSTSTACK case.
+ additionalProperties: false
+ properties:
+ type:
+ const: hoststack
+ bandwidth:
+ description: >-
+ Goodput measured in bits per second.
+ $ref: "#/$defs/types/bandwidth"
+ completed_requests:
+ description: >-
+ Number of completed requests.
+ $ref: "#/$defs/types/count_requests"
+ failed_requests:
+ description: >-
+ Number of failed requests.
+ $ref: "#/$defs/types/count_requests"
+ retransmits:
+ description: >-
+ Number of retransmits.
+ $ref: "#/$defs/types/count_packets"
+ latency:
+ description: >-
+ Value and unit of latency.
+ $ref: "#/$defs/types/value_with_unit"
+ duration:
+ description: >-
+ The relative time difference (in seconds)
+ between program start and end.
+ $ref: "#/$defs/types/time_quantity"
+ rate:
+ description: >-
+ RPS or CPS rate, with corresponding unit, as
+ reported by TG.
+ $ref: "#/$defs/types/rate_without_bandwidth"
+ required:
+ - type
+ - bandwidth
start_time:
description: >-
UTC date and time in RFC 3339 format, specifying calendar time
@@ -325,7 +365,7 @@ allOf:
CSIT model version (semver format)
the exporting code adhered to.
type: string
- const: 1.2.0
+ const: 1.3.0
required:
- duration
- dut_type
@@ -388,11 +428,14 @@ $defs:
maxItems: 0
rate_unit:
description: >-
- Packets per second (pps) or connections per second (cps).
+ Packets per second (pps),
+ connections per second (cps),
+ requests per second (rps).
type: string
enum:
- pps
- cps
+ - rps
bandwidth_unit:
description: >-
Unit of measurement for bandwidth values.
@@ -414,6 +457,20 @@ $defs:
Unit suitable for displaying packet counts.
enum:
- packets
+ count_requests:
+ description: >-
+ Type, for counting requests.
+ allOf:
+ - $ref: "#/$defs/types/value_with_unit"
+ - properties:
+ value:
+ description: >-
+ A number of requests of interest.
+ unit:
+ description: >-
+ Unit suitable for displaying request counts.
+ enum:
+ - requests
time_quantity:
description: >-
Reusable type, for various time quantites.
@@ -477,8 +534,8 @@ $defs:
- properties:
value:
description: >-
- Bandwidth value computed
- from the corresponding rate.
+ Bandwidth value computed from the corresponding
+ rate.
unit:
$ref: "#/$defs/types/bandwidth_unit"
rate_with_bandwidth:
diff --git a/docs/model/current/schema/todos.txt b/docs/model/current/schema/todos.txt
deleted file mode 100644
index 91e8bb49bb..0000000000
--- a/docs/model/current/schema/todos.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-
-Add description with link to methodology for MRR, NDRPDR and SOAK.
-
-Add multiplicity field to MRR result, so PAL can detect incomplete samples.
-
-Add link explaining our L1 bandwidth calculation.
-
-Add a link to URL explaining how to decode the hdrh data.
diff --git a/docs/model/current/schema/yaml2json.py b/docs/model/current/schema/yaml2json.py
index 1b69ba9c92..6899928847 100644
--- a/docs/model/current/schema/yaml2json.py
+++ b/docs/model/current/schema/yaml2json.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2021 Cisco and/or its affiliates.
+# Copyright (c) 2023 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
@@ -11,10 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Utility to convert from .schema.yaml to .schema.json.
-
-TODO: Read the input file name from command line argument.
-"""
+"""Utility to convert from .schema.yaml to .schema.json."""
import glob
import json
@@ -24,4 +21,4 @@ import yaml
for filename in glob.glob(u"*.schema.yaml"):
name = filename[:-5]
with open(f"{name}.yaml", u"r") as fin, open(f"{name}.json", u"w") as fout:
- json.dump(yaml.load(fin.read()), fout, indent=2)
+ json.dump(yaml.safe_load(fin.read()), fout, indent=2)
diff --git a/docs/model/current/top.rst b/docs/model/current/top.rst
index 3f7f976adf..3f09710ceb 100644
--- a/docs/model/current/top.rst
+++ b/docs/model/current/top.rst
@@ -1,5 +1,5 @@
..
- Copyright (c) 2022 Cisco and/or its affiliates.
+ Copyright (c) 2023 Cisco and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
@@ -22,7 +22,7 @@ especially the export side (UTI), not import side (PAL).
Version
~~~~~~~
-This document is valid for CSIT model version 1.2.0.
+This document is valid for CSIT model version 1.3.0.
It is recommended to use semantic versioning: https://semver.org/
That means, if the new model misses a field present in the old model,
diff --git a/resources/libraries/python/Constants.py b/resources/libraries/python/Constants.py
index 0ae92f68ac..2bac8dc1cb 100644
--- a/resources/libraries/python/Constants.py
+++ b/resources/libraries/python/Constants.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2022 Cisco and/or its affiliates.
+# Copyright (c) 2023 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
@@ -120,7 +120,7 @@ class Constants:
"""Constants used in CSIT."""
# Version for CSIT data model. See docs/model/.
- MODEL_VERSION = u"1.2.0"
+ MODEL_VERSION = u"1.3.0"
# Global off-switch in case JSON export is large or slow.
EXPORT_JSON = get_optimistic_bool_from_env(u"EXPORT_JSON")
diff --git a/resources/libraries/python/HoststackUtil.py b/resources/libraries/python/HoststackUtil.py
index 7e6ba56913..35acdd70ee 100644
--- a/resources/libraries/python/HoststackUtil.py
+++ b/resources/libraries/python/HoststackUtil.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2021 Cisco and/or its affiliates.
+# Copyright (c) 2023 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
@@ -17,9 +17,12 @@ from time import sleep
from robot.api import logger
from resources.libraries.python.Constants import Constants
-from resources.libraries.python.ssh import exec_cmd, exec_cmd_no_error
-from resources.libraries.python.PapiExecutor import PapiSocketExecutor
from resources.libraries.python.DUTSetup import DUTSetup
+from resources.libraries.python.model.ExportResult import (
+ export_hoststack_results
+)
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
+from resources.libraries.python.ssh import exec_cmd, exec_cmd_no_error
class HoststackUtil():
"""Utilities for Host Stack tests."""
@@ -84,7 +87,6 @@ class HoststackUtil():
ip_address = f" {iperf3_attributes[u'ip_address']}" if u"ip_address" \
in iperf3_attributes else u""
iperf3_cmd[u"name"] = u"iperf3"
- # TODO: Use OptionString library.
iperf3_cmd[u"args"] = f"--{iperf3_attributes[u'role']}{ip_address} " \
f"--interval 0{json_results} " \
f"--version{iperf3_attributes[u'ip_version']}"
@@ -289,7 +291,6 @@ class HoststackUtil():
cmd = f"sh -c 'strace -qqe trace=none -p {program_pid}'"
exec_cmd(node, cmd, sudo=True)
# Wait a bit for stdout/stderr to be flushed to log files
- # TODO: see if sub-second sleep works e.g. sleep(0.1)
sleep(1)
@staticmethod
@@ -346,7 +347,6 @@ class HoststackUtil():
f"bits/sec, pkt-drop-rate {nsim_attr[u'packets_per_drop']} " \
f"pkts/drop\n"
- # TODO: Incorporate show error stats into results analysis
test_results += \
f"\n{role} VPP 'show errors' on host {node[u'host']}:\n" \
f"{PapiSocketExecutor.run_cli_cmd(node, u'show error')}\n"
@@ -364,11 +364,13 @@ class HoststackUtil():
if u"JSON stats" in program_stdout and \
u'"has_failed": "0"' in program_stdout:
json_start = program_stdout.find(u"{")
- #TODO: Fix parsing once vpp_echo produces valid
- # JSON output. Truncate for now.
json_end = program_stdout.find(u',\n "closing"')
json_results = f"{program_stdout[json_start:json_end]}\n}}"
program_json = json.loads(json_results)
+ export_hoststack_results(
+ bandwidth=program_json["rx_bits_per_second"],
+ duration=program_json["time"]
+ )
else:
test_results += u"Invalid test data output!\n" + program_stdout
return (True, test_results)
@@ -376,6 +378,11 @@ class HoststackUtil():
test_results += program_stdout
iperf3_json = json.loads(program_stdout)
program_json = iperf3_json[u"intervals"][0][u"sum"]
+ export_hoststack_results(
+ bandwidth=program_json["bits_per_second"],
+ duration=program_json["seconds"],
+ retransmits=program_json["retransmits"]
+ )
else:
test_results += u"Unknown HostStack Test Program!\n" + \
program_stdout
diff --git a/resources/libraries/python/model/ExportResult.py b/resources/libraries/python/model/ExportResult.py
index b5de27ebe5..dd0968419a 100644
--- a/resources/libraries/python/model/ExportResult.py
+++ b/resources/libraries/python/model/ExportResult.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2022 Cisco and/or its affiliates.
+# Copyright (c) 2023 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
@@ -18,7 +18,7 @@ from robot.libraries.BuiltIn import BuiltIn
from resources.libraries.python.model.util import descend, get_export_data
-def export_dut_type_and_version(dut_type=u"unknown", dut_version=u"unknown"):
+def export_dut_type_and_version(dut_type="unknown", dut_version="unknown"):
"""Export the arguments as dut type and version.
Robot tends to convert "none" into None, hence the unusual default values.
@@ -32,32 +32,32 @@ def export_dut_type_and_version(dut_type=u"unknown", dut_version=u"unknown"):
:type dut_version: Optiona[str]
:raises RuntimeError: If value is neither in argument not robot variable.
"""
- if dut_type == u"unknown":
- dut_type = BuiltIn().get_variable_value(u"\\${DUT_TYPE}", u"unknown")
- if dut_type == u"unknown":
- raise RuntimeError(u"Dut type not provided.")
+ if dut_type == "unknown":
+ dut_type = BuiltIn().get_variable_value("\\${DUT_TYPE}", "unknown")
+ if dut_type == "unknown":
+ raise RuntimeError("Dut type not provided.")
else:
# We want to set a variable in higher level suite setup
# to be available to test setup several levels lower.
BuiltIn().set_suite_variable(
- u"\\${DUT_TYPE}", dut_type, u"children=True"
+ "\\${DUT_TYPE}", dut_type, "children=True"
)
- if dut_version == u"unknown":
+ if dut_version == "unknown":
dut_version = BuiltIn().get_variable_value(
- u"\\${DUT_VERSION}", u"unknown"
+ "\\${DUT_VERSION}", "unknown"
)
- if dut_type == u"unknown":
- raise RuntimeError(u"Dut version not provided.")
+ if dut_type == "unknown":
+ raise RuntimeError("Dut version not provided.")
else:
BuiltIn().set_suite_variable(
- u"\\${DUT_VERSION}", dut_version, u"children=True"
+ "\\${DUT_VERSION}", dut_version, "children=True"
)
data = get_export_data()
- data[u"dut_type"] = dut_type.lower()
- data[u"dut_version"] = dut_version
+ data["dut_type"] = dut_type.lower()
+ data["dut_version"] = dut_version
-def export_tg_type_and_version(tg_type=u"unknown", tg_version=u"unknown"):
+def export_tg_type_and_version(tg_type="unknown", tg_version="unknown"):
"""Export the arguments as tg type and version.
Robot tends to convert "none" into None, hence the unusual default values.
@@ -71,29 +71,29 @@ def export_tg_type_and_version(tg_type=u"unknown", tg_version=u"unknown"):
:type tg_version: Optiona[str]
:raises RuntimeError: If value is neither in argument not robot variable.
"""
- if tg_type == u"unknown":
- tg_type = BuiltIn().get_variable_value(u"\\${TG_TYPE}", u"unknown")
- if tg_type == u"unknown":
- raise RuntimeError(u"TG type not provided.")
+ if tg_type == "unknown":
+ tg_type = BuiltIn().get_variable_value("\\${TG_TYPE}", "unknown")
+ if tg_type == "unknown":
+ raise RuntimeError("TG type not provided!")
else:
# We want to set a variable in higher level suite setup
# to be available to test setup several levels lower.
BuiltIn().set_suite_variable(
- u"\\${TG_TYPE}", tg_type, u"children=True"
+ "\\${TG_TYPE}", tg_type, "children=True"
)
- if tg_version == u"unknown":
+ if tg_version == "unknown":
tg_version = BuiltIn().get_variable_value(
- u"\\${TG_VERSION}", u"unknown"
+ "\\${TG_VERSION}", "unknown"
)
- if tg_type == u"unknown":
- raise RuntimeError(u"TG version not provided.")
+ if tg_type == "unknown":
+ raise RuntimeError("TG version not provided!")
else:
BuiltIn().set_suite_variable(
- u"\\${TG_VERSION}", tg_version, u"children=True"
+ "\\${TG_VERSION}", tg_version, "children=True"
)
data = get_export_data()
- data[u"tg_type"] = tg_type.lower()
- data[u"tg_version"] = tg_version
+ data["tg_type"] = tg_type.lower()
+ data["tg_version"] = tg_version
def append_mrr_value(mrr_value, unit):
@@ -109,10 +109,10 @@ def append_mrr_value(mrr_value, unit):
if not unit:
return
data = get_export_data()
- data[u"result"][u"type"] = u"mrr"
- rate_node = descend(descend(data[u"result"], u"receive_rate"), "rate")
- rate_node[u"unit"] = str(unit)
- values_list = descend(rate_node, u"values", list)
+ data["result"]["type"] = "mrr"
+ rate_node = descend(descend(data["result"], "receive_rate"), "rate")
+ rate_node["unit"] = str(unit)
+ values_list = descend(rate_node, "values", list)
values_list.append(float(mrr_value))
@@ -139,17 +139,17 @@ def export_search_bound(text, value, unit, bandwidth=None):
"""
value = float(value)
text = str(text).lower()
- result_type = u"soak" if u"plrsearch" in text else u"ndrpdr"
- upper_or_lower = u"upper" if u"upper" in text else u"lower"
- ndr_or_pdr = u"ndr" if u"ndr" in text else u"pdr"
+ result_type = "soak" if "plrsearch" in text else "ndrpdr"
+ upper_or_lower = "upper" if "upper" in text else "lower"
+ ndr_or_pdr = "ndr" if "ndr" in text else "pdr"
- result_node = get_export_data()[u"result"]
- result_node[u"type"] = result_type
+ result_node = get_export_data()["result"]
+ result_node["type"] = result_type
rate_item = dict(rate=dict(value=value, unit=unit))
if bandwidth:
- rate_item[u"bandwidth"] = dict(value=float(bandwidth), unit=u"bps")
- if result_type == u"soak":
- descend(result_node, u"critical_rate")[upper_or_lower] = rate_item
+ rate_item["bandwidth"] = dict(value=float(bandwidth), unit="bps")
+ if result_type == "soak":
+ descend(result_node, "critical_rate")[upper_or_lower] = rate_item
return
descend(result_node, ndr_or_pdr)[upper_or_lower] = rate_item
@@ -169,14 +169,14 @@ def _add_latency(result_node, percent, whichward, latency_string):
:type whichward: str
:latency_string: str
"""
- l_min, l_avg, l_max, l_hdrh = latency_string.split(u"/", 3)
+ l_min, l_avg, l_max, l_hdrh = latency_string.split("/", 3)
whichward_node = descend(result_node, f"latency_{whichward}")
percent_node = descend(whichward_node, f"pdr_{percent}")
- percent_node[u"min"] = int(l_min)
- percent_node[u"avg"] = int(l_avg)
- percent_node[u"max"] = int(l_max)
- percent_node[u"hdrh"] = l_hdrh
- percent_node[u"unit"] = u"us"
+ percent_node["min"] = int(l_min)
+ percent_node["avg"] = int(l_avg)
+ percent_node["max"] = int(l_max)
+ percent_node["hdrh"] = l_hdrh
+ percent_node["unit"] = "us"
def export_ndrpdr_latency(text, latency):
@@ -195,23 +195,25 @@ def export_ndrpdr_latency(text, latency):
:type text: str
:type latency: 1-tuple or 2-tuple of str
"""
- result_node = get_export_data()[u"result"]
+ result_node = get_export_data()["result"]
percent = 0
- if u"90" in text:
+ if "90" in text:
percent = 90
- elif u"50" in text:
+ elif "50" in text:
percent = 50
- elif u"10" in text:
+ elif "10" in text:
percent = 10
- _add_latency(result_node, percent, u"forward", latency[0])
+ _add_latency(result_node, percent, "forward", latency[0])
# Else TRex does not support latency measurement for this traffic profile.
if len(latency) < 2:
return
- _add_latency(result_node, percent, u"reverse", latency[1])
+ _add_latency(result_node, percent, "reverse", latency[1])
def export_reconf_result(packet_rate, packet_loss, bandwidth):
- """Export the results from a reconf test.
+ """Export the RECONF type results.
+
+ Result type is set to reconf.
:param packet_rate: Aggregate offered load in packets per second.
:param packet_loss: How many of the packets were dropped or unsent.
@@ -246,6 +248,56 @@ def export_reconf_result(packet_rate, packet_loss, bandwidth):
)
+def export_hoststack_results(
+ bandwidth, rate=None, rate_unit=None, latency=None,
+ failed_requests=None, completed_requests=None, retransmits=None,
+ duration=None
+):
+ """Export the HOSTSTACK type results.
+
+ Result type is set to hoststack.
+
+ :param bandwidth: Measured transfer rate using bps as a unit.
+ :param rate: Resulting rate measured by the test. [Optional]
+ :param rate_unit: CPS or RPS. [Optional]
+ :param latency: Measure latency. [Optional]
+ :param failed_requests: Number of failed requests. [Optional]
+ :param completed_requests: Number of completed requests. [Optional]
+ :param retransmits: Retransmitted TCP packets. [Optional]
+ :param duration: Measurment duration. [Optional]
+ :type bandwidth: float
+ :type rate: float
+ :type rate_unit: str
+ :type latency: float
+ :type failed_requests: int
+ :type completed_requests: int
+ :type retransmits: int
+ :type duration: float
+ """
+ result_node = get_export_data()["result"]
+ result_node["type"] = "hoststack"
+
+ result_node["bandwidth"] = dict(unit="bps", value=bandwidth)
+ if rate is not None:
+ result_node["rate"] = \
+ dict(unit=rate_unit, value=rate)
+ if latency is not None:
+ result_node["latency"] = \
+ dict(unit="ms", value=latency)
+ if failed_requests is not None:
+ result_node["failed_requests"] = \
+ dict(unit="requests", value=failed_requests)
+ if completed_requests is not None:
+ result_node["completed_requests"] = \
+ dict(unit="requests", value=completed_requests)
+ if retransmits is not None:
+ result_node["retransmits"] = \
+ dict(unit="packets", value=retransmits)
+ if duration is not None:
+ result_node["duration"] = \
+ dict(unit="s", value=duration)
+
+
def append_telemetry(telemetry_item):
"""Append telemetry entry to proper place so it is dumped into json.
@@ -253,4 +305,4 @@ def append_telemetry(telemetry_item):
:type telemetry_item: str
"""
data = get_export_data()
- data[u"telemetry"].append(telemetry_item)
+ data["telemetry"].append(telemetry_item)
diff --git a/resources/tools/ab/ABFork.py b/resources/tools/ab/ABFork.py
index 8436ed38be..55288a9c92 100755
--- a/resources/tools/ab/ABFork.py
+++ b/resources/tools/ab/ABFork.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2021 Intel and/or its affiliates.
+# Copyright (c) 2023 Intel and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
@@ -136,7 +136,7 @@ def main():
# Output results.
print(f"Transfer Rate: {round(info_list[6], 2)} [Kbytes/sec]")
print(f"Latency: {round(info_list[4] / 8, 2)} ms")
- print(f"Connection {mode} rate:{round(info_list[3], 2)} per sec")
+ print(f"Connection {mode} rate: {round(info_list[3], 2)} per sec")
print(f"Total data transferred: {round(info_list[2])} bytes")
print(f"Completed requests: {round(info_list[0])} ")
print(f"Failed requests: {round(info_list[1])} ")
diff --git a/resources/tools/ab/ABTools.py b/resources/tools/ab/ABTools.py
index 54aff19e92..f3e97a720a 100644
--- a/resources/tools/ab/ABTools.py
+++ b/resources/tools/ab/ABTools.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2022 Intel and/or its affiliates.
+# Copyright (c) 2023 Intel and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
@@ -13,11 +13,13 @@
"""ab implementation into CSIT framework."""
-from robot.api import logger
-from resources.libraries.python.topology import NodeType
from resources.libraries.python.Constants import Constants
-from resources.libraries.python.ssh import exec_cmd_no_error
+from resources.libraries.python.model.ExportResult import (
+ export_hoststack_results
+)
from resources.libraries.python.OptionString import OptionString
+from resources.libraries.python.ssh import exec_cmd_no_error
+from resources.libraries.python.topology import NodeType
class ABTools:
@@ -153,66 +155,31 @@ class ABTools:
port=port,
mode=rps_cps,
)
- stdout, _ = exec_cmd_no_error(tg_node, cmd, timeout=180, sudo=True,
- message=u"ab runtime error!")
- log_msg = ABTools._parse_ab_output(stdout, rps_cps, tls_tcp)
-
- logger.info(log_msg)
-
- return log_msg
-
- @staticmethod
- def _parse_ab_output(msg, rps_cps, tls_tcp):
- """Parse the ab stdout with the results.
-
- :param msg: Ab Stdout.
- :param rps_cps: RPS or CPS.
- :param tls_tcp: TLS or TCP.
- :type msg: str
- :type rps_cps: str
- :type tls_tcp: str
- :return: Message with measured data.
- :rtype: str
- """
-
- msg_lst = msg.splitlines(keepends=False)
-
- total_cps = u""
- latency = u""
- processing = u""
- complete_req = u""
- failed_req = u""
- total_bytes = u""
- rate = u""
-
- if tls_tcp == u"tls":
- log_msg = u"\nMeasured HTTPS values:\n"
- else:
- log_msg = u"\nMeasured HTTP values:\n"
+ stdout, _ = exec_cmd_no_error(
+ tg_node, cmd, timeout=180, sudo=True, message=u"ab runtime error!"
+ )
- for line in msg_lst:
+ rate_unit = rps_cps
+ rate = None
+ bandwidth = None
+ latency = None
+ completed_requests = None
+ failed_requests = None
+ for line in stdout.splitlines():
if f"Connection {rps_cps} rate:" in line:
- # rps (cps)
- total_cps = line + u"\n"
+ rate = float(line.split(u" ")[3])
elif u"Transfer Rate:" in line:
- # Rate
- rate = line + u"\n"
+ bandwidth = float(line.split(u" ")[2]) * 8000
elif u"Latency:" in line:
- # Latency
- latency = line + u"\n"
- elif u"Total data transferred" in line:
- total_bytes = line + u"\n"
- elif u"Completed requests" in line:
- complete_req = line + u"\n"
+ latency = float(line.split(u" ")[1])
+ elif u"Completed requests:" in line:
+ completed_requests = int(line.split(u" ")[2])
elif u"Failed requests" in line:
- failed_req = line + u"\n"
+ failed_requests = int(line.split(u" ")[2])
- log_msg += rate
- log_msg += latency
- log_msg += processing
- log_msg += complete_req
- log_msg += failed_req
- log_msg += total_bytes
- log_msg += total_cps
+ export_hoststack_results(
+ rate, rate_unit, bandwidth, latency, failed_requests,
+ completed_requests
+ )
- return log_msg
+ return stdout