mfd-dcb 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mfd_dcb/__init__.py +6 -0
- mfd_dcb/const.py +7 -0
- mfd_dcb/data_structures.py +12 -0
- mfd_dcb/dcb_wrapper.py +268 -0
- mfd_dcb/dcbnl_wrapper.py +144 -0
- mfd_dcb/dcbtool_wrapper.py +276 -0
- mfd_dcb/enums.py +28 -0
- mfd_dcb/exceptions.py +21 -0
- mfd_dcb/freebsd.py +146 -0
- mfd_dcb/linux.py +777 -0
- mfd_dcb/mfd_dcb.py +48 -0
- mfd_dcb/windows.py +987 -0
- mfd_dcb-0.1.0.dist-info/METADATA +244 -0
- mfd_dcb-0.1.0.dist-info/RECORD +18 -0
- mfd_dcb-0.1.0.dist-info/WHEEL +5 -0
- mfd_dcb-0.1.0.dist-info/licenses/AUTHORS.md +7 -0
- mfd_dcb-0.1.0.dist-info/licenses/LICENSE.md +8 -0
- mfd_dcb-0.1.0.dist-info/top_level.txt +2 -0
mfd_dcb/__init__.py
ADDED
mfd_dcb/const.py
ADDED
mfd_dcb/dcb_wrapper.py
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# Copyright (C) 2026 Intel Corporation
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""Module for the iproute2 dcb tool."""
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
9
|
+
from mfd_const.qos import LOCAL_ETS, LOCAL_PFC, LOCAL_APP
|
|
10
|
+
from .exceptions import DcbExecutionError, DcbExecutionProcessError
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from mfd_connect import RPyCConnection
|
|
14
|
+
|
|
15
|
+
APP_SELECTORS = (
|
|
16
|
+
"default-prio",
|
|
17
|
+
"ethtype-prio",
|
|
18
|
+
"stream-port-prio",
|
|
19
|
+
"dgram-port-prio",
|
|
20
|
+
"port-prio",
|
|
21
|
+
"dscp-prio",
|
|
22
|
+
"pcp-prio",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DcbCli:
|
|
27
|
+
"""DcbCli class provides DCB configuration read-out using the iproute2 dcb tool (dcbnl netlink)."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, interface_name: str, connection: "RPyCConnection"):
|
|
30
|
+
"""Initialize class parameters.
|
|
31
|
+
|
|
32
|
+
:param interface_name: tested interface
|
|
33
|
+
:param connection: connection
|
|
34
|
+
"""
|
|
35
|
+
self.interface_name = interface_name
|
|
36
|
+
self._connection = connection
|
|
37
|
+
|
|
38
|
+
def get_dcb(self, numeric: bool = True) -> dict[str, dict[str, Any]]:
|
|
39
|
+
"""Get complete DCB configuration for ETS, PFC and APP TLVs.
|
|
40
|
+
|
|
41
|
+
:param numeric: when True (default), APP protocol values are read numerically (``dcb -N app show``)
|
|
42
|
+
:return: Dictionary of QoS configuration
|
|
43
|
+
"""
|
|
44
|
+
qos = {}
|
|
45
|
+
qos.update(self.get_ets())
|
|
46
|
+
qos.update(self.get_pfc())
|
|
47
|
+
qos.update(self.get_app(numeric=numeric))
|
|
48
|
+
return qos
|
|
49
|
+
|
|
50
|
+
def get_ets(self) -> dict[str, dict[str, Any]]:
|
|
51
|
+
"""Get local ETS configuration of the interface via ``dcb ets show``.
|
|
52
|
+
|
|
53
|
+
:return: Dictionary of local ETS configuration
|
|
54
|
+
"""
|
|
55
|
+
output = self._run("ets")
|
|
56
|
+
tc_tsa = self._extract_pairs(output, "tc-tsa")
|
|
57
|
+
tc_bw = self._extract_pairs(output, "tc-bw")
|
|
58
|
+
prio_tc = self._extract_pairs(output, "prio-tc")
|
|
59
|
+
|
|
60
|
+
ets_config: dict[str, dict[str, Any]] = {}
|
|
61
|
+
for tc, tsa in tc_tsa.items():
|
|
62
|
+
ets_config[str(tc)] = {
|
|
63
|
+
"TSA": tsa.upper(),
|
|
64
|
+
"Bandwidth": int(tc_bw.get(tc, 0)),
|
|
65
|
+
"Priorities": [],
|
|
66
|
+
}
|
|
67
|
+
for prio, tc in prio_tc.items():
|
|
68
|
+
ets_config.setdefault(
|
|
69
|
+
str(tc),
|
|
70
|
+
{"TSA": "", "Bandwidth": int(tc_bw.get(tc, 0)), "Priorities": []},
|
|
71
|
+
)
|
|
72
|
+
ets_config[str(tc)]["Priorities"].append(prio)
|
|
73
|
+
return {LOCAL_ETS: ets_config}
|
|
74
|
+
|
|
75
|
+
def get_pfc(self) -> dict[str, list[bool]]:
|
|
76
|
+
"""Get local PFC configuration of the interface via ``dcb pfc show``.
|
|
77
|
+
|
|
78
|
+
:return: Dictionary of local PFC configuration
|
|
79
|
+
"""
|
|
80
|
+
output = self._run("pfc")
|
|
81
|
+
prio_pfc = self._extract_pairs(output, "prio-pfc")
|
|
82
|
+
pfc_config = [False] * 8
|
|
83
|
+
for prio, state in prio_pfc.items():
|
|
84
|
+
if 0 <= prio < 8:
|
|
85
|
+
pfc_config[prio] = state == "on"
|
|
86
|
+
return {LOCAL_PFC: pfc_config}
|
|
87
|
+
|
|
88
|
+
def get_app(self, numeric: bool = True) -> dict[str, dict[str, Any]]:
|
|
89
|
+
"""Get local APP table of the interface via ``dcb app show``.
|
|
90
|
+
|
|
91
|
+
:param numeric: when True (default), protocol values are read numerically via ``dcb -N app show``.
|
|
92
|
+
:return: Dictionary of local APP configuration
|
|
93
|
+
"""
|
|
94
|
+
output = self._run("app", numeric=numeric)
|
|
95
|
+
app_config: dict[int, dict[str, Any]] = {}
|
|
96
|
+
for no, (selector, protocol, priority) in enumerate(self._extract_app_entries(output)):
|
|
97
|
+
app_config[no] = {
|
|
98
|
+
"selector": selector.replace("-prio", ""),
|
|
99
|
+
"priority": int(priority),
|
|
100
|
+
"protocol": protocol,
|
|
101
|
+
}
|
|
102
|
+
return {LOCAL_APP: app_config}
|
|
103
|
+
|
|
104
|
+
def set_ets(
|
|
105
|
+
self,
|
|
106
|
+
user_priority_mapping: list[tuple],
|
|
107
|
+
bandwidth_per_traffic_class: list[int],
|
|
108
|
+
mode: str = "ieee",
|
|
109
|
+
) -> None:
|
|
110
|
+
"""Set ETS configuration via ``dcb ets set``.
|
|
111
|
+
|
|
112
|
+
:param user_priority_mapping: User Priority mapping to Traffic Classes, list of tuples, each tuple is a next
|
|
113
|
+
Traffic Class with given User Priorities as integer values, e.g. [(0, 1), (2, 3), (5, 4), (6, 7)]
|
|
114
|
+
:param bandwidth_per_traffic_class: list of bandwidth percentage values, one per Traffic Class
|
|
115
|
+
:param mode: accepted for a common setter API across wrappers; not used by the iproute2 dcb tool
|
|
116
|
+
:raises DcbExecutionError: when the command writes to stderr
|
|
117
|
+
"""
|
|
118
|
+
tc_tsa = " ".join(f"{tc}:ets" for tc in range(len(user_priority_mapping)))
|
|
119
|
+
tc_bw = " ".join(f"{tc}:{bw}" for tc, bw in enumerate(bandwidth_per_traffic_class))
|
|
120
|
+
prio_tc = " ".join(f"{prio}:{tc}" for tc, priorities in enumerate(user_priority_mapping) for prio in priorities)
|
|
121
|
+
command = f"dcb ets set dev {self.interface_name} willing off tc-tsa {tc_tsa} tc-bw {tc_bw} prio-tc {prio_tc}"
|
|
122
|
+
self._execute_dcb_command(command)
|
|
123
|
+
|
|
124
|
+
def set_pfc(self, pfc_per_priority: list[bool], mode: str = "ieee") -> None:
|
|
125
|
+
"""Set Priority Flow Control via ``dcb pfc set``.
|
|
126
|
+
|
|
127
|
+
:param pfc_per_priority: list of 8 Bool values for each priority, True is for PFC ON
|
|
128
|
+
:param mode: accepted for a common setter API across wrappers; not used by the iproute2 dcb tool
|
|
129
|
+
:raises DcbExecutionError: when the command writes to stderr
|
|
130
|
+
"""
|
|
131
|
+
prio_pfc = " ".join(f"{prio}:{'on' if pfc else 'off'}" for prio, pfc in enumerate(pfc_per_priority))
|
|
132
|
+
command = f"dcb pfc set dev {self.interface_name} prio-pfc {prio_pfc}"
|
|
133
|
+
self._execute_dcb_command(command)
|
|
134
|
+
|
|
135
|
+
def set_app(self, proto_prio_map: dict | list[tuple], *, selector: str = "dscp-prio") -> None:
|
|
136
|
+
"""Add APP (protocol to priority) entries via ``dcb app add``.
|
|
137
|
+
|
|
138
|
+
:param proto_prio_map: mapping of protocol to priority, e.g. {26: 3} (DSCP 26 -> priority 3, RoCEv2)
|
|
139
|
+
:param selector: APP selector, e.g. "dscp-prio" (default), "ethtype-prio", "port-prio"
|
|
140
|
+
:raises DcbExecutionError: when the mapping is invalid or the command writes to stderr
|
|
141
|
+
"""
|
|
142
|
+
proto_prio = self._format_app_map(proto_prio_map)
|
|
143
|
+
self._execute_dcb_command(f"dcb app add dev {self.interface_name} {selector} {proto_prio}")
|
|
144
|
+
|
|
145
|
+
def del_app(self, proto_prio_map: dict | list[tuple], *, selector: str = "dscp-prio") -> None:
|
|
146
|
+
"""Delete specific APP (protocol to priority) entries via ``dcb app del``.
|
|
147
|
+
|
|
148
|
+
:param proto_prio_map: mapping of protocol to priority to remove, e.g. {26: 3}
|
|
149
|
+
:param selector: APP selector, e.g. "dscp-prio" (default), "ethtype-prio", "port-prio"
|
|
150
|
+
:raises DcbExecutionError: when the mapping is invalid or the command writes to stderr
|
|
151
|
+
"""
|
|
152
|
+
proto_prio = self._format_app_map(proto_prio_map)
|
|
153
|
+
self._execute_dcb_command(f"dcb app del dev {self.interface_name} {selector} {proto_prio}")
|
|
154
|
+
|
|
155
|
+
def replace_app(self, proto_prio_map: dict | list[tuple], *, selector: str = "dscp-prio") -> None:
|
|
156
|
+
"""Replace APP (protocol to priority) entries via ``dcb app replace``.
|
|
157
|
+
|
|
158
|
+
:param proto_prio_map: mapping of protocol to the new priority, e.g. {26: 5} (DSCP 26 -> priority 5)
|
|
159
|
+
:param selector: APP selector, e.g. "dscp-prio" (default), "ethtype-prio", "port-prio"
|
|
160
|
+
:raises DcbExecutionError: when the mapping is invalid or the command writes to stderr
|
|
161
|
+
"""
|
|
162
|
+
proto_prio = self._format_app_map(proto_prio_map)
|
|
163
|
+
self._execute_dcb_command(f"dcb app replace dev {self.interface_name} {selector} {proto_prio}")
|
|
164
|
+
|
|
165
|
+
def flush_app(self, selector: str | None = "dscp-prio") -> None:
|
|
166
|
+
"""Flush the APP table via ``dcb app flush``.
|
|
167
|
+
|
|
168
|
+
:param selector: APP selector to flush; when None, all APP entries are flushed
|
|
169
|
+
:raises DcbExecutionError: when the command writes to stderr
|
|
170
|
+
"""
|
|
171
|
+
command = f"dcb app flush dev {self.interface_name}"
|
|
172
|
+
if selector:
|
|
173
|
+
command += f" {selector}"
|
|
174
|
+
self._execute_dcb_command(command)
|
|
175
|
+
|
|
176
|
+
@staticmethod
|
|
177
|
+
def _format_app_map(proto_prio_map: dict | list[tuple]) -> str:
|
|
178
|
+
"""Format a protocol-to-priority mapping into the ``dcb app`` key argument.
|
|
179
|
+
|
|
180
|
+
:param proto_prio_map: mapping of protocol to priority, e.g. {26: 3, 30: 3}, [(26, 3)] or {"0x8906": 3}
|
|
181
|
+
:return: space separated "PROTO:PRIO" pairs, e.g. "26:3 30:3"
|
|
182
|
+
:raises DcbExecutionError: when the mapping is empty or malformed
|
|
183
|
+
"""
|
|
184
|
+
pairs = proto_prio_map.items() if isinstance(proto_prio_map, dict) else proto_prio_map
|
|
185
|
+
try:
|
|
186
|
+
formatted = " ".join(f"{proto}:{int(prio)}" for proto, prio in pairs)
|
|
187
|
+
except (TypeError, ValueError) as err:
|
|
188
|
+
raise DcbExecutionError(f"Invalid proto_prio_map: {proto_prio_map}") from err
|
|
189
|
+
if not formatted:
|
|
190
|
+
raise DcbExecutionError("proto_prio_map must contain at least one protocol:priority entry")
|
|
191
|
+
return formatted
|
|
192
|
+
|
|
193
|
+
def _execute_dcb_command(self, cmd_list: list[str] | str) -> None:
|
|
194
|
+
"""Execute iproute2 dcb tool command(s) and validate the return code and stderr.
|
|
195
|
+
|
|
196
|
+
:param cmd_list: command or list of commands to execute
|
|
197
|
+
:raises DcbExecutionProcessError: when a command returns a non-zero return code
|
|
198
|
+
:raises DcbExecutionError: when a command writes to stderr
|
|
199
|
+
"""
|
|
200
|
+
cmd_list = cmd_list if isinstance(cmd_list, list) else [cmd_list]
|
|
201
|
+
for cmd in cmd_list:
|
|
202
|
+
output = self._connection.execute_command(
|
|
203
|
+
cmd,
|
|
204
|
+
custom_exception=DcbExecutionProcessError,
|
|
205
|
+
expected_return_codes=[0],
|
|
206
|
+
)
|
|
207
|
+
if output.stderr:
|
|
208
|
+
raise DcbExecutionError(f"Error while executing '{cmd}': {output.stderr}")
|
|
209
|
+
|
|
210
|
+
def _run(self, attribute: str, numeric: bool = False) -> str:
|
|
211
|
+
"""Run ``dcb <attribute> show`` for the interface and return stdout.
|
|
212
|
+
|
|
213
|
+
:param attribute: dcb object to read - pfc, ets or app
|
|
214
|
+
:param numeric: when True, pass the global ``-N`` option so values are printed numerically
|
|
215
|
+
:return: command stdout
|
|
216
|
+
:raises DcbExecutionError: when the command reports an error on stderr
|
|
217
|
+
"""
|
|
218
|
+
numeric_opt = "-N " if numeric else ""
|
|
219
|
+
command = f"dcb {numeric_opt}{attribute} show dev {self.interface_name}"
|
|
220
|
+
output = self._connection.execute_command(
|
|
221
|
+
command,
|
|
222
|
+
custom_exception=DcbExecutionProcessError,
|
|
223
|
+
expected_return_codes=[0],
|
|
224
|
+
)
|
|
225
|
+
if output.stderr:
|
|
226
|
+
raise DcbExecutionError(f"Error while executing dcb {command}: {output.stderr}")
|
|
227
|
+
return output.stdout
|
|
228
|
+
|
|
229
|
+
@staticmethod
|
|
230
|
+
def _extract_app_entries(output: str) -> list[tuple[str, str, str]]:
|
|
231
|
+
"""Extract APP table entries as an ordered sequence of ``(selector, protocol, priority)``.
|
|
232
|
+
|
|
233
|
+
:param output: ``dcb app show`` stdout
|
|
234
|
+
:return: ordered list of ``(selector, protocol, priority)`` tuples
|
|
235
|
+
"""
|
|
236
|
+
entries: list[tuple[str, str, str]] = []
|
|
237
|
+
current_selector = None
|
|
238
|
+
for token in output.split():
|
|
239
|
+
if token in APP_SELECTORS:
|
|
240
|
+
current_selector = token
|
|
241
|
+
continue
|
|
242
|
+
if not current_selector:
|
|
243
|
+
continue
|
|
244
|
+
if current_selector == "default-prio":
|
|
245
|
+
entries.append((current_selector, "0", token))
|
|
246
|
+
elif ":" in token:
|
|
247
|
+
protocol, _, priority = token.rpartition(":")
|
|
248
|
+
entries.append((current_selector, protocol, priority))
|
|
249
|
+
return entries
|
|
250
|
+
|
|
251
|
+
@staticmethod
|
|
252
|
+
def _extract_pairs(output: str, keyword: str) -> dict[Any, str]:
|
|
253
|
+
"""Extract the ``KEY:VALUE`` pairs that follow a dcb attribute keyword.
|
|
254
|
+
|
|
255
|
+
:param output: dcb command stdout
|
|
256
|
+
:param keyword: attribute keyword to read the pairs for, e.g. "prio-pfc", "tc-tsa", "dscp-prio"
|
|
257
|
+
:return: mapping of parsed key to value string
|
|
258
|
+
"""
|
|
259
|
+
match = re.search(rf"(?<![-\w]){re.escape(keyword)}\s+((?:\S+:\S+\s*)+)", output)
|
|
260
|
+
pairs: dict[Any, str] = {}
|
|
261
|
+
if not match:
|
|
262
|
+
return pairs
|
|
263
|
+
for token in match.group(1).split():
|
|
264
|
+
if ":" not in token:
|
|
265
|
+
break
|
|
266
|
+
key, _, value = token.partition(":")
|
|
267
|
+
pairs[int(key) if key.isdigit() else key] = value
|
|
268
|
+
return pairs
|
mfd_dcb/dcbnl_wrapper.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Copyright (C) 2025 Intel Corporation
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""Module for dcbnl."""
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
9
|
+
from mfd_const.qos import LOCAL_ETS, LOCAL_PFC, LOCAL_APP, DCB_TOOL_PATH_LNX
|
|
10
|
+
from .exceptions import DcbExecutionError, DcbExecutionProcessError
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from mfd_connect import RPyCConnection
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Dcbnl:
|
|
17
|
+
"""Dcbnl class provides DCB configuration using dcbnl python script."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, interface_name: str, connection: "RPyCConnection"):
|
|
20
|
+
"""Initialize class parameters.
|
|
21
|
+
|
|
22
|
+
:param interface_name: tested interface
|
|
23
|
+
:param connection: connection
|
|
24
|
+
"""
|
|
25
|
+
self.interface_name = interface_name
|
|
26
|
+
self._connection = connection
|
|
27
|
+
|
|
28
|
+
def get_dcb(self) -> dict[str, dict[str, Any]]:
|
|
29
|
+
"""Get complete DCB configuration for ETS, PFC and APP TLVs, local and remote.
|
|
30
|
+
|
|
31
|
+
:return: Dictionary of QoS configuration
|
|
32
|
+
"""
|
|
33
|
+
qos = {}
|
|
34
|
+
qos.update(self.get_ets())
|
|
35
|
+
qos.update(self.get_pfc(from_cache=True))
|
|
36
|
+
qos.update(self.get_app(from_cache=True))
|
|
37
|
+
return qos
|
|
38
|
+
|
|
39
|
+
def get_ets(self, from_cache: bool = False) -> dict[str, dict[str, Any]]:
|
|
40
|
+
"""Get local ETS TLV configuration of the interface_name.
|
|
41
|
+
|
|
42
|
+
:param from_cache: use current configuration output
|
|
43
|
+
:return: Dictionary of local ETS TLV configuration
|
|
44
|
+
"""
|
|
45
|
+
if not from_cache:
|
|
46
|
+
self._read_qos_configuration()
|
|
47
|
+
return self._parse_ets(LOCAL_ETS)
|
|
48
|
+
|
|
49
|
+
def get_pfc(self, from_cache: bool = False) -> dict[str, bool]:
|
|
50
|
+
"""Get local PFC TLV configuration of the interface_name.
|
|
51
|
+
|
|
52
|
+
:param from_cache: use current configuration output
|
|
53
|
+
:return: Dictionary of local PFC TLV configuration
|
|
54
|
+
"""
|
|
55
|
+
if not from_cache:
|
|
56
|
+
self._read_qos_configuration()
|
|
57
|
+
return self._parse_pfc(LOCAL_PFC)
|
|
58
|
+
|
|
59
|
+
def get_app(self, from_cache: bool = False) -> dict[str, dict[str, Any]]:
|
|
60
|
+
"""Get local APP TLV configuration of the interface_name.
|
|
61
|
+
|
|
62
|
+
:param from_cache: use current configuration output
|
|
63
|
+
:return: Dictionary of local APP TLV configuration
|
|
64
|
+
"""
|
|
65
|
+
if not from_cache:
|
|
66
|
+
self._read_qos_configuration()
|
|
67
|
+
return self._parse_app(LOCAL_APP)
|
|
68
|
+
|
|
69
|
+
def _read_qos_configuration(self) -> None:
|
|
70
|
+
"""Run dcbnl.py script for given interface_name and store it in self._qos_output for future use.
|
|
71
|
+
|
|
72
|
+
dcbnl.py - Netlink message generation/parsing - DCB_TOOL_PATH_LNX = /tmp/tools/dcb
|
|
73
|
+
:raises DcbExecutionError: when return code of bash command is unexpected
|
|
74
|
+
"""
|
|
75
|
+
dcbnl_path = self._connection.path(DCB_TOOL_PATH_LNX, "dcbnl.py")
|
|
76
|
+
command = f"{self._connection.modules().sys.executable} {dcbnl_path} {self.interface_name}"
|
|
77
|
+
output = self._connection.execute_command(
|
|
78
|
+
command, custom_exception=DcbExecutionProcessError, expected_return_codes=[0]
|
|
79
|
+
)
|
|
80
|
+
if output.stderr:
|
|
81
|
+
raise DcbExecutionError(f"Error while executing dcbnl {command}: {output.stderr}")
|
|
82
|
+
self._qos_output = output.stdout
|
|
83
|
+
|
|
84
|
+
def _parse_ets(self, key: str) -> dict[str, dict[str, int]]:
|
|
85
|
+
"""Get local or remote QoS traffic classes as dictionary.
|
|
86
|
+
|
|
87
|
+
:param key: key in configuration dictionary for ETS
|
|
88
|
+
:return: Dictionary of QoS traffic classes configuration
|
|
89
|
+
"""
|
|
90
|
+
ets_config = {}
|
|
91
|
+
current_tc = ""
|
|
92
|
+
for line in self._qos_output.splitlines():
|
|
93
|
+
line = line.strip()
|
|
94
|
+
exp = re.compile(r"tc: (?P<tc>[0-7]) tsa: (?P<tsa>ets|strict)(?:, bw: (?P<bw>\d+)%)")
|
|
95
|
+
match = exp.match(line)
|
|
96
|
+
if match:
|
|
97
|
+
current_tc = match.group("tc")
|
|
98
|
+
ets_config[current_tc] = {
|
|
99
|
+
"TSA": match.group("tsa").upper(),
|
|
100
|
+
"Bandwidth": int(match.group("bw") or "0"),
|
|
101
|
+
"Priorities": [],
|
|
102
|
+
}
|
|
103
|
+
continue
|
|
104
|
+
match = re.match(r"up:\s+(?P<up>[0-7])", line)
|
|
105
|
+
if match:
|
|
106
|
+
up = int(match.group("up"))
|
|
107
|
+
ets_config[current_tc]["Priorities"].append(up)
|
|
108
|
+
continue
|
|
109
|
+
return {key: ets_config}
|
|
110
|
+
|
|
111
|
+
def _parse_pfc(self, key: str) -> dict[str, bool]:
|
|
112
|
+
"""Get operational or remote QoS flow control as dictionary.
|
|
113
|
+
|
|
114
|
+
:param key: key in configuration dictionary for PFC
|
|
115
|
+
:return: Dictionary of QoS flow control configuration
|
|
116
|
+
"""
|
|
117
|
+
pfc_config = [False] * 8
|
|
118
|
+
match = re.search(r"pfc_enable:\s+(?P<pfc>0x[0-9a-fA-F]+)", self._qos_output)
|
|
119
|
+
if match:
|
|
120
|
+
pfc_bin = int(match.group("pfc"), 16)
|
|
121
|
+
for i in range(8):
|
|
122
|
+
pfc_config[i] = pfc_bin & (pow(2, i)) > 0
|
|
123
|
+
return {key: pfc_config}
|
|
124
|
+
|
|
125
|
+
def _parse_app(self, key: str) -> dict[str, dict[str, str]]:
|
|
126
|
+
"""Get operational or remote QoS apps table.
|
|
127
|
+
|
|
128
|
+
:param key: key in configuration dictionary for PFC
|
|
129
|
+
:return: Dictionary of QoS flow control configuration
|
|
130
|
+
"""
|
|
131
|
+
app_config = {}
|
|
132
|
+
no = 0
|
|
133
|
+
for line in self._qos_output.splitlines():
|
|
134
|
+
line = line.strip()
|
|
135
|
+
exp = re.compile(r"selector: (?P<selector>\d+) priority: (?P<priority>\d+) protocol: (?P<protocol>\S*)")
|
|
136
|
+
match = exp.match(line)
|
|
137
|
+
if match:
|
|
138
|
+
app_config[no] = {
|
|
139
|
+
"selector": int(match.group("selector")),
|
|
140
|
+
"priority": int(match.group("priority")),
|
|
141
|
+
"protocol": match.group("protocol"),
|
|
142
|
+
}
|
|
143
|
+
no += 1
|
|
144
|
+
return {key: app_config}
|