tdl-xoa-driver 1.4.0__py3-none-any.whl → 1.5.0b1__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.
Files changed (46) hide show
  1. {tdl_xoa_driver-1.4.0.dist-info → tdl_xoa_driver-1.5.0b1.dist-info}/METADATA +2 -2
  2. {tdl_xoa_driver-1.4.0.dist-info → tdl_xoa_driver-1.5.0b1.dist-info}/RECORD +45 -33
  3. xoa_driver/__init__.py +2 -2
  4. xoa_driver/enums.py +2 -0
  5. xoa_driver/exceptions.py +2 -0
  6. xoa_driver/functions/anlt.py +2 -0
  7. xoa_driver/functions/anlt_ll_debug.py +2 -0
  8. xoa_driver/functions/cli/__init__.py +21 -0
  9. xoa_driver/functions/cli/_cli_manager.py +541 -0
  10. xoa_driver/functions/cli/_config_block.py +334 -0
  11. xoa_driver/functions/cli/_socket_driver.py +111 -0
  12. xoa_driver/functions/cli/port_config.py +107 -0
  13. xoa_driver/functions/cli/test_case_config.py +172 -0
  14. xoa_driver/functions/cmis/__init__.py +8 -0
  15. xoa_driver/functions/cmis/_constants.py +25 -0
  16. xoa_driver/functions/cmis/_replies.py +600 -0
  17. xoa_driver/functions/cmis/_utils.py +49 -0
  18. xoa_driver/functions/cmis/cdb.py +1266 -0
  19. xoa_driver/functions/exceptions.py +2 -0
  20. xoa_driver/functions/headers.py +2 -0
  21. xoa_driver/functions/mgmt.py +42 -19
  22. xoa_driver/functions/tools.py +9 -3
  23. xoa_driver/hlfuncs.py +6 -2
  24. xoa_driver/internals/commands/c_commands.py +6 -10
  25. xoa_driver/internals/commands/enums.py +25 -1
  26. xoa_driver/internals/commands/pr_commands.py +17 -16
  27. xoa_driver/internals/commands/px_commands.py +54 -54
  28. xoa_driver/internals/core/transporter/logger/__state_on_user.py +1 -1
  29. xoa_driver/internals/exceptions/modules.py +4 -3
  30. xoa_driver/internals/hli/modules/modules_l23/family_edun.py +82 -0
  31. xoa_driver/internals/hli/modules/modules_l23/family_g.py +1 -1
  32. xoa_driver/internals/hli/modules/modules_l23/family_l1.py +19 -0
  33. xoa_driver/internals/hli/ports/port_l23/family_edun.py +82 -0
  34. xoa_driver/internals/hli/ports/port_l23/family_l1.py +6 -0
  35. xoa_driver/internals/state_storage/modules_state.py +20 -0
  36. xoa_driver/internals/state_storage/testers_state.py +10 -0
  37. xoa_driver/lli.py +1 -0
  38. xoa_driver/misc.py +1 -0
  39. xoa_driver/modules.py +22 -0
  40. xoa_driver/ports.py +22 -0
  41. xoa_driver/testers.py +2 -0
  42. xoa_driver/utils.py +2 -0
  43. xoa_driver/functions/cli.py +0 -581
  44. {tdl_xoa_driver-1.4.0.dist-info → tdl_xoa_driver-1.5.0b1.dist-info}/WHEEL +0 -0
  45. {tdl_xoa_driver-1.4.0.dist-info → tdl_xoa_driver-1.5.0b1.dist-info}/licenses/LICENSE +0 -0
  46. {tdl_xoa_driver-1.4.0.dist-info → tdl_xoa_driver-1.5.0b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,334 @@
1
+ from xoa_driver import __version__, __name__
2
+ from typing import Union, List
3
+ from enum import Enum
4
+ from dataclasses import dataclass
5
+
6
+ class ConfigMetadataType(Enum):
7
+ DEFAULT = "XENA"
8
+ PORT = "XENAPORT"
9
+ MODULE = "XENAMODULE"
10
+ TESTCASE = "XENACASE"
11
+
12
+ @dataclass
13
+ class BaseConfigMetadata:
14
+ type: ConfigMetadataType = ConfigMetadataType.DEFAULT
15
+ formated_version: int = 3
16
+ saved_by: str = __name__ + __version__
17
+
18
+ def to_str(self) -> str:
19
+ header_lines = [
20
+ f";{self.type.value}",
21
+ f";FormatVersion: {self.formated_version}",
22
+ f";Savedby: {self.saved_by}",
23
+ ]
24
+ return "\n".join(header_lines)
25
+
26
+ # Module config metadata dataclass
27
+ @dataclass
28
+ class TestCaseMetadata(BaseConfigMetadata):
29
+ type: ConfigMetadataType = ConfigMetadataType.TESTCASE
30
+ testbed_name: str = "<testbed>"
31
+ chassis_name: str = "<chassis_name>"
32
+ chassis_sn: str = "<chassis_sn>"
33
+ chassis_version_str: str = "<chassis_version_str>"
34
+ module_name: str = "<module_name>"
35
+ module_model: str = "<module_model>"
36
+ module_sn: str = "<module_sn>"
37
+ module_version_str: str = "<module_version_str>"
38
+ module_revision: str = "<module_revision>"
39
+
40
+ def to_str(self) -> str:
41
+ header_lines = [
42
+ f";{self.type.value}",
43
+ f";FormatVersion: {super().formated_version}",
44
+ f";Savedby: {super().saved_by}",
45
+ f";Testbed: {self.testbed_name}",
46
+ ]
47
+ return "\n".join(header_lines)
48
+
49
+ # Module config metadata dataclass
50
+ @dataclass
51
+ class ModuleConfigMetadata(BaseConfigMetadata):
52
+ type: ConfigMetadataType = ConfigMetadataType.MODULE
53
+ testbed_name: str = "<testbed>"
54
+ chassis_name: str = "<chassis_name>"
55
+ chassis_sn: str = "<chassis_sn>"
56
+ chassis_version_str: str = "<chassis_version_str>"
57
+ module_name: str = "<module_name>"
58
+ module_model: str = "<module_model>"
59
+ module_sn: str = "<module_sn>"
60
+ module_version_str: str = "<module_version_str>"
61
+ module_revision: str = "<module_revision>"
62
+ module_id: str = "<module_id>"
63
+
64
+ def to_str(self) -> str:
65
+ header_lines = [
66
+ f";{self.type.value}",
67
+ f";FormatVersion: {super().formated_version}",
68
+ f";Savedby: {super().saved_by}",
69
+ f";ChassisName: {self.chassis_name}",
70
+ f";ChassisSerial: {self.chassis_sn}",
71
+ f";ChassisVersion: {self.chassis_version_str}",
72
+ f";ModuleName: {self.module_name}",
73
+ f";ModuleModel: {self.module_model.replace('_', '')}",
74
+ f";ModuleSerial: {self.module_sn}",
75
+ f";ModuleVersion: {self.module_version_str}",
76
+ f";ModuleRevision: {self.module_revision}",
77
+ f";Module: {self.module_id}",
78
+ ]
79
+ return "\n".join(header_lines)
80
+
81
+ # Port config metadata dataclass
82
+ @dataclass
83
+ class PortConfigMetadata(BaseConfigMetadata):
84
+ type = ConfigMetadataType.PORT
85
+ testbed_name: str = "<testbed>"
86
+ chassis_name: str = "<chassis_name>"
87
+ chassis_sn: str = "<chassis_sn>"
88
+ chassis_version_str: str = "<chassis_version_str>"
89
+ module_name: str = "<module_name>"
90
+ module_model: str = "<module_model>"
91
+ module_sn: str = "<module_sn>"
92
+ module_version_str: str = "<module_version_str>"
93
+ module_revision: str = "<module_revision>"
94
+ port_id: str = "<port_id>"
95
+
96
+ def to_str(self) -> str:
97
+ header_lines = [
98
+ f";{self.type.value}",
99
+ f";FormatVersion: {super().formated_version}",
100
+ f";Savedby: {super().saved_by}",
101
+ f";ChassisName: {self.chassis_name}",
102
+ f";ChassisSerial: {self.chassis_sn}",
103
+ f";ChassisVersion: {self.chassis_version_str}",
104
+ f";ModuleName: {self.module_name}",
105
+ f";ModuleModel: {self.module_model.replace('_', '')}",
106
+ f";ModuleSerial: {self.module_sn}",
107
+ f";ModuleVersion: {self.module_version_str}",
108
+ f";ModuleRevision: {self.module_revision}",
109
+ f";Port: {self.port_id}",
110
+ ]
111
+ return "\n".join(header_lines)
112
+
113
+ # Config block
114
+ class ConfigBlock:
115
+ def __init__(self):
116
+ self._config_block_str: str
117
+ self._metadata: Union[TestCaseMetadata, PortConfigMetadata, ModuleConfigMetadata]
118
+ self._commands: List[str] = []
119
+
120
+ @property
121
+ def type(self) -> ConfigMetadataType:
122
+ return self._metadata.type
123
+
124
+ @type.setter
125
+ def type(self, v: ConfigMetadataType) -> None:
126
+ if v == ConfigMetadataType.MODULE:
127
+ self._metadata = ModuleConfigMetadata()
128
+ self._metadata.type = v
129
+ elif v == ConfigMetadataType.PORT:
130
+ self._metadata = PortConfigMetadata()
131
+ self._metadata.type = v
132
+ elif v == ConfigMetadataType.TESTCASE:
133
+ self._metadata = TestCaseMetadata()
134
+ self._metadata.type = v
135
+ else:
136
+ raise ValueError("Invalid config type")
137
+
138
+ @property
139
+ def config_block_str(self) -> str:
140
+ header_str = self._metadata.to_str()
141
+ if len(self._commands) == 0:
142
+ return header_str
143
+ else:
144
+ commands_str = "\n".join(self._commands)
145
+ return header_str + "\n" + commands_str
146
+
147
+ @config_block_str.setter
148
+ def config_block_str(self, v: str) -> None:
149
+ self._config_block_str = v
150
+
151
+ metadata_list = []
152
+ line_list = self._config_block_str.splitlines()
153
+
154
+ for line in line_list:
155
+ if line.startswith(';'):
156
+ metadata_list.append(line)
157
+ else:
158
+ self._commands.append(line)
159
+
160
+ if line_list[0].startswith(f";{ConfigMetadataType.MODULE.value}"):
161
+ self._metadata = ModuleConfigMetadata()
162
+ for line in metadata_list:
163
+ if line.startswith(f";ChassisName:"):
164
+ self._metadata.chassis_name = line.split(" ")[1].strip()
165
+ elif line.startswith(f";ChassisSerial:"):
166
+ self._metadata.chassis_sn = line.split(" ")[1].strip()
167
+ elif line.startswith(f";ChassisVersion:"):
168
+ self._metadata.chassis_version_str = line.split(" ")[1].strip()
169
+ elif line.startswith(f";ModuleName:"):
170
+ self._metadata.module_name = line.split(" ")[1].strip()
171
+ elif line.startswith(f";ModuleModel:"):
172
+ self._metadata.module_model = line.split(" ")[1].strip()
173
+ elif line.startswith(f";ModuleSerial:"):
174
+ self._metadata.module_sn = line.split(" ")[1].strip()
175
+ elif line.startswith(f";ModuleVersion:"):
176
+ self._metadata.module_version_str = line.split(" ")[1].strip()
177
+ elif line.startswith(f";ModuleRevision:"):
178
+ self._metadata.module_revision = line.split(" ")[1].strip()
179
+ elif line.startswith(f";Module:"):
180
+ self._metadata.module_id = line.split(" ")[1].strip()
181
+
182
+ if line_list[0].startswith(f";{ConfigMetadataType.PORT.value}"):
183
+ self._metadata = PortConfigMetadata()
184
+ for line in metadata_list:
185
+ if line.startswith(f";ChassisName:"):
186
+ self._metadata.chassis_name = line.split(" ")[1].strip()
187
+ elif line.startswith(f";ChassisSerial:"):
188
+ self._metadata.chassis_sn = line.split(" ")[1].strip()
189
+ elif line.startswith(f";ChassisVersion:"):
190
+ self._metadata.chassis_version_str = line.split(" ")[1].strip()
191
+ elif line.startswith(f";ModuleName:"):
192
+ self._metadata.module_name = line.split(" ")[1].strip()
193
+ elif line.startswith(f";ModuleModel:"):
194
+ self._metadata.module_model = line.split(" ")[1].strip()
195
+ elif line.startswith(f";ModuleSerial:"):
196
+ self._metadata.module_sn = line.split(" ")[1].strip()
197
+ elif line.startswith(f";ModuleVersion:"):
198
+ self._metadata.module_version_str = line.split(" ")[1].strip()
199
+ elif line.startswith(f";ModuleRevision:"):
200
+ self._metadata.module_revision = line.split(" ")[1].strip()
201
+ elif line.startswith(f";Port:"):
202
+ self._metadata.port_id = line.split(" ")[1].strip()
203
+
204
+ @property
205
+ def testbed_name(self) -> str:
206
+ return self._metadata.testbed_name
207
+
208
+ @testbed_name.setter
209
+ def testbed_name(self, v: str) -> None:
210
+ self._metadata.testbed_name = v
211
+
212
+ @property
213
+ def chassis_name(self) -> str:
214
+ return self._metadata.chassis_name
215
+
216
+ @chassis_name.setter
217
+ def chassis_name(self, v: str) -> None:
218
+ self._metadata.chassis_name = v
219
+
220
+ @property
221
+ def chassis_sn(self) -> str:
222
+ return self._metadata.chassis_sn
223
+
224
+ @chassis_sn.setter
225
+ def chassis_sn(self, v: str) -> None:
226
+ self._metadata.chassis_sn = v
227
+
228
+ @property
229
+ def chassis_version_str(self) -> str:
230
+ return self._metadata.chassis_version_str
231
+
232
+ @chassis_version_str.setter
233
+ def chassis_version_str(self, v: str) -> None:
234
+ self._metadata.chassis_version_str = v
235
+
236
+ @property
237
+ def module_name(self) -> str:
238
+ return self._metadata.module_name
239
+
240
+ @module_name.setter
241
+ def module_name(self, v: str) -> None:
242
+ self._metadata.module_name = v
243
+
244
+ @property
245
+ def module_model(self) -> str:
246
+ return self._metadata.module_model
247
+
248
+ @module_model.setter
249
+ def module_model(self, v: str) -> None:
250
+ self._metadata.module_model = v
251
+
252
+ @property
253
+ def module_sn(self) -> str:
254
+ return self._metadata.module_sn
255
+
256
+ @module_sn.setter
257
+ def module_sn(self, v: str) -> None:
258
+ self._metadata.module_sn = v
259
+
260
+ @property
261
+ def module_version_str(self) -> str:
262
+ return self._metadata.module_version_str
263
+
264
+ @module_version_str.setter
265
+ def module_version_str(self, v: str) -> None:
266
+ self._metadata.module_version_str = v
267
+
268
+ @property
269
+ def module_revision(self) -> str:
270
+ return self._metadata.module_revision
271
+
272
+ @module_revision.setter
273
+ def module_revision(self, v: str) -> None:
274
+ self._metadata.module_revision = v
275
+
276
+ @property
277
+ def module_id(self) -> str:
278
+ if isinstance(self._metadata, ModuleConfigMetadata):
279
+ return self._metadata.module_id
280
+ return ""
281
+
282
+ @module_id.setter
283
+ def module_id(self, v: str) -> None:
284
+ if isinstance(self._metadata, ModuleConfigMetadata):
285
+ self._metadata.module_id = v
286
+ else:
287
+ raise AttributeError("module_id is only available for ModuleConfigMetadata")
288
+
289
+ @property
290
+ def port_id(self) -> str:
291
+ if isinstance(self._metadata, PortConfigMetadata):
292
+ return self._metadata.port_id
293
+ return ""
294
+
295
+ @port_id.setter
296
+ def port_id(self, v: str) -> None:
297
+ if isinstance(self._metadata, PortConfigMetadata):
298
+ self._metadata.port_id = v
299
+ else:
300
+ raise AttributeError("port_id is only available for PortConfigMetadata")
301
+
302
+ @property
303
+ def metadata(self) -> Union[TestCaseMetadata, PortConfigMetadata, ModuleConfigMetadata]:
304
+ return self._metadata
305
+
306
+ @property
307
+ def commands(self) -> List[str]:
308
+ return self._commands
309
+
310
+ @commands.setter
311
+ def commands(self, v: str) -> None:
312
+ # Remove the <SYNC> tag
313
+ v = v.replace('<SYNC>', '')
314
+
315
+ if self.type == ConfigMetadataType.PORT:
316
+ v = v.replace(f"{self.port_id} ", '')
317
+ v = f"P_RESET\n" + v
318
+
319
+ if self.type == ConfigMetadataType.MODULE:
320
+ v = v.replace(f"{self.module_id} ", '')
321
+
322
+ self._commands = v.splitlines()
323
+
324
+ # Determine type of config block
325
+ def config_block_type(config_block_str: str) -> ConfigMetadataType:
326
+ line_list = config_block_str.splitlines()
327
+ if line_list[0].startswith(f";{ConfigMetadataType.MODULE.value}"):
328
+ return ConfigMetadataType.MODULE
329
+ elif line_list[0].startswith(f";{ConfigMetadataType.PORT.value}"):
330
+ return ConfigMetadataType.PORT
331
+ elif line_list[0].startswith(f";{ConfigMetadataType.TESTCASE.value}"):
332
+ return ConfigMetadataType.TESTCASE
333
+ else:
334
+ return ConfigMetadataType.DEFAULT
@@ -0,0 +1,111 @@
1
+ import socket
2
+ import time
3
+ import logging
4
+ from typing import Optional, Callable, Union, List, Dict, Any
5
+
6
+ class ServerUnavaliable(Exception):
7
+ pass
8
+
9
+ class SimpleSocket(object):
10
+ def __init__(self, hostname: str, port: int = 22611, timeout: int = 20) -> None:
11
+ self.server_addr = (hostname, port)
12
+ self.is_connected = False
13
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
14
+ self.sock.settimeout(timeout)
15
+ self.retry_connect = 3
16
+ self._connect()
17
+
18
+ def __del__(self) -> None:
19
+ self.sock.close()
20
+
21
+ def _connect(self):
22
+ try:
23
+ if hasattr(self, "sock"):
24
+ connid = 1
25
+ err = self.sock.connect_ex(self.server_addr)
26
+ if err == 0:
27
+ self.retry_connect = 3
28
+ self.is_connected = True
29
+ else:
30
+ self.reconnect()
31
+
32
+ except socket.error as msg:
33
+ logging.error(f"[Socket connection error in <_connect>] Cannot connect to { self.server_addr[0] }, error: {msg}\n")
34
+ self.reconnect()
35
+
36
+ def reconnect(self):
37
+ time.sleep(5)
38
+ self.retry_connect -= 1
39
+ if self.retry_connect > 0:
40
+ self._connect()
41
+ else:
42
+ raise ServerUnavaliable(f'Cannot connect to <{ self.server_addr[0] }>, host is unavailable')
43
+
44
+ def close(self):
45
+ if hasattr(self, "sock"):
46
+ self.sock.close()
47
+
48
+ # Send a string command to a socket
49
+ def send_only(self, cmd: str):
50
+ """Send command string to server"""
51
+ if hasattr(self, "sock") and self.is_connected:
52
+ sent = self.sock.send((cmd + '\n').encode('utf-8'))
53
+ if sent == 0:
54
+ raise RuntimeError("Socket connection broken in <send_command>")
55
+
56
+ # Send a string command to a socket and return a string response from the socket
57
+ def send_and_response(self, cmd: str, sync_on: bool) -> str:
58
+ """Send a command string to server and return the response"""
59
+ if hasattr(self, "sock") and self.is_connected:
60
+ try:
61
+ sent = self.sock.send((cmd + '\n').encode('utf-8'))
62
+ if sent == 0:
63
+ raise RuntimeError("Socket connection broken in <ask>")
64
+ if sync_on:
65
+ terminator = b'<SYNC>\n'
66
+ buffer = b''
67
+ while True:
68
+ chunk = self.sock.recv(4096)
69
+ if not chunk:
70
+ continue
71
+ buffer += chunk
72
+ if buffer.endswith(terminator):
73
+ break
74
+ return buffer.decode('utf-8')
75
+ else:
76
+ chunk = self.sock.recv(4096)
77
+ while not chunk:
78
+ chunk = self.sock.recv(4096)
79
+ return chunk.decode('utf-8')
80
+ except socket.error as msg:
81
+ logging.error(f"[Socket connection error in <ask>] { msg }")
82
+ return ''
83
+ return ''
84
+
85
+
86
+ # Send a long string data to the socket and return a long string of response from the socket
87
+ def send_and_response_multiple(self, cmd:str, num:int) -> str:
88
+ """Send a number of commands to server and return the responses"""
89
+ if hasattr(self, "sock") and self.is_connected:
90
+ try:
91
+ self.sock.sendall((cmd).encode('utf-8'))
92
+ chunk = self.sock.recv(4096)
93
+ while not chunk:
94
+ chunk = self.sock.recv(4096)
95
+ data = chunk.decode('utf-8')
96
+ while True:
97
+ if data.count('\n') < num:
98
+ data2 = self.sock.recv(4096).decode('utf-8')
99
+ if data2:
100
+ data = data + data2
101
+ else:
102
+ break
103
+ return data
104
+ except socket.error as msg:
105
+ logging.error(f"[Socket connection error in <ask_multi>] { msg }")
106
+ return ''
107
+ return ''
108
+
109
+ def set_keepalives(self):
110
+ if hasattr(self, "sock") and self.is_connected:
111
+ self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 2)
@@ -0,0 +1,107 @@
1
+ import asyncio
2
+ from xoa_driver import testers, ports
3
+ from ._cli_manager import XOACLIManager
4
+ from ._config_block import *
5
+
6
+ async def save_port_config(tester: testers.L23Tester, port: ports.GenericL23Port, path: str, debug=False, halt_on_error=False) -> str:
7
+ """Save configuration from the test port to the specified filepath
8
+
9
+ :param tester: Chassis object
10
+ :type tester: testers.L23Tester
11
+ :param port: Port object to save configuration from
12
+ :type port: ports.GenericL23Port
13
+ :param save_path: File path to save the port configuration
14
+ :type save_path: str
15
+ :return: Port configuration string
16
+ :rtype: str
17
+ """
18
+
19
+ tester_ip = tester.info.host
20
+ resp = await tester.password.get()
21
+ tester_password = resp.password
22
+ port_index = f"{port.kind.module_id}/{port.kind.port_id}"
23
+ module = tester.modules.obtain(port.kind.module_id)
24
+
25
+ # Connect to the tester on tcp port 22611
26
+ xm = XOACLIManager(host=tester_ip, debug=debug, halt_on_error=halt_on_error)
27
+
28
+ # Log on and set username
29
+ xm.logon_set_owner(tester_password)
30
+
31
+ # Get full port configuration
32
+ raw_resp = xm.get_port_full_config_raw(port_index)
33
+
34
+ # Create config block
35
+ port_config_block = ConfigBlock()
36
+ # Fill in metadata
37
+ port_config_block.type = ConfigMetadataType.PORT
38
+ port_config_block.chassis_name = tester.info.name
39
+ port_config_block.chassis_sn = str(tester.info.serial_number)
40
+ port_config_block.chassis_version_str = tester.info.version_string
41
+ port_config_block.module_name = module.info.model_name
42
+ port_config_block.module_model = module.info.model
43
+ port_config_block.module_sn = module.info.serial_number
44
+ port_config_block.module_version_str = module.info.version_string
45
+ port_config_block.module_revision = module.info.revision
46
+ port_config_block.port_id = port_index
47
+ port_config_block.commands = raw_resp[0]
48
+
49
+ # Save configuration to file
50
+ result = port_config_block.config_block_str
51
+ with open(path, 'w+', newline='') as xpcfile:
52
+ xpcfile.write(result)
53
+ return result
54
+
55
+ async def load_port_config(tester: testers.L23Tester, port: ports.GenericL23Port, path: str, debug=False, halt_on_error=False) -> None:
56
+ """Load configuration to the test port from the specified filepath
57
+
58
+ :param tester: Chassis object
59
+ :type tester: testers.L23Tester
60
+ :param port: Port object to load configuration to
61
+ :type port: ports.GenericL23Port
62
+ :param load_path: File path to load the port configuration from
63
+ :type load_path: str
64
+ """
65
+
66
+ tester_ip = tester.info.host
67
+ resp = await tester.password.get()
68
+ tester_password = resp.password
69
+ port_index = f"{port.kind.module_id}/{port.kind.port_id}"
70
+
71
+ # Connect to the tester on tcp port 22611
72
+ xm = XOACLIManager(host=tester_ip, debug=debug, halt_on_error=halt_on_error)
73
+
74
+ # Log on and set username
75
+ xm.logon_set_owner(tester_password)
76
+
77
+ # Reserve the port before applying configuration
78
+ xm.reserve_port(port_index)
79
+
80
+ # Read configuration from file
81
+ with open(path, 'r', newline='') as xpcfile:
82
+ config_data = xpcfile.read()
83
+
84
+ # Deserialize config block and send CLI commands
85
+ config_block = ConfigBlock()
86
+ config_block.config_block_str = config_data
87
+ cli_cmds = config_block.commands
88
+ for cmd in cli_cmds:
89
+ if cmd.strip(): # Ensure the command is not empty
90
+ xm.send(cmd=f"{port_index} {cmd}", sync_on=False)
91
+
92
+ # Free the port after applying configuration
93
+ xm.free_port(port_index)
94
+
95
+ async def port_config_from_file(tester: testers.L23Tester, port: ports.GenericL23Port, path: str, debug=False, halt_on_error=False) -> None:
96
+ """Load port configuration from the specifiied filepath. This function is a wrapper around load_port_config to provide backward compatibility.
97
+
98
+ :param tester: Chassis object
99
+ :type tester: testers.L23Tester
100
+ :param port: Port object to load configuration to
101
+ :type port: ports.GenericL23Port
102
+ :param load_path: File path to load the port configuration from
103
+ :type load_path: str
104
+ """
105
+ await load_port_config(tester, port, path, debug=debug, halt_on_error=halt_on_error)
106
+
107
+