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.
- {tdl_xoa_driver-1.4.0.dist-info → tdl_xoa_driver-1.5.0b1.dist-info}/METADATA +2 -2
- {tdl_xoa_driver-1.4.0.dist-info → tdl_xoa_driver-1.5.0b1.dist-info}/RECORD +45 -33
- xoa_driver/__init__.py +2 -2
- xoa_driver/enums.py +2 -0
- xoa_driver/exceptions.py +2 -0
- xoa_driver/functions/anlt.py +2 -0
- xoa_driver/functions/anlt_ll_debug.py +2 -0
- xoa_driver/functions/cli/__init__.py +21 -0
- xoa_driver/functions/cli/_cli_manager.py +541 -0
- xoa_driver/functions/cli/_config_block.py +334 -0
- xoa_driver/functions/cli/_socket_driver.py +111 -0
- xoa_driver/functions/cli/port_config.py +107 -0
- xoa_driver/functions/cli/test_case_config.py +172 -0
- xoa_driver/functions/cmis/__init__.py +8 -0
- xoa_driver/functions/cmis/_constants.py +25 -0
- xoa_driver/functions/cmis/_replies.py +600 -0
- xoa_driver/functions/cmis/_utils.py +49 -0
- xoa_driver/functions/cmis/cdb.py +1266 -0
- xoa_driver/functions/exceptions.py +2 -0
- xoa_driver/functions/headers.py +2 -0
- xoa_driver/functions/mgmt.py +42 -19
- xoa_driver/functions/tools.py +9 -3
- xoa_driver/hlfuncs.py +6 -2
- xoa_driver/internals/commands/c_commands.py +6 -10
- xoa_driver/internals/commands/enums.py +25 -1
- xoa_driver/internals/commands/pr_commands.py +17 -16
- xoa_driver/internals/commands/px_commands.py +54 -54
- xoa_driver/internals/core/transporter/logger/__state_on_user.py +1 -1
- xoa_driver/internals/exceptions/modules.py +4 -3
- xoa_driver/internals/hli/modules/modules_l23/family_edun.py +82 -0
- xoa_driver/internals/hli/modules/modules_l23/family_g.py +1 -1
- xoa_driver/internals/hli/modules/modules_l23/family_l1.py +19 -0
- xoa_driver/internals/hli/ports/port_l23/family_edun.py +82 -0
- xoa_driver/internals/hli/ports/port_l23/family_l1.py +6 -0
- xoa_driver/internals/state_storage/modules_state.py +20 -0
- xoa_driver/internals/state_storage/testers_state.py +10 -0
- xoa_driver/lli.py +1 -0
- xoa_driver/misc.py +1 -0
- xoa_driver/modules.py +22 -0
- xoa_driver/ports.py +22 -0
- xoa_driver/testers.py +2 -0
- xoa_driver/utils.py +2 -0
- xoa_driver/functions/cli.py +0 -581
- {tdl_xoa_driver-1.4.0.dist-info → tdl_xoa_driver-1.5.0b1.dist-info}/WHEEL +0 -0
- {tdl_xoa_driver-1.4.0.dist-info → tdl_xoa_driver-1.5.0b1.dist-info}/licenses/LICENSE +0 -0
- {tdl_xoa_driver-1.4.0.dist-info → tdl_xoa_driver-1.5.0b1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import time
|
|
4
|
+
import threading
|
|
5
|
+
import inspect
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Optional, Callable, Union, List, Dict, Any
|
|
8
|
+
from ._socket_driver import SimpleSocket
|
|
9
|
+
LOGFILE = "XENALOG"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _redact_sensitive(cmd: str) -> str:
|
|
13
|
+
"""Redact password or sensitive data from CLI command strings."""
|
|
14
|
+
# Redact values in commands likely to contain passwords (e.g., C_LOGON, or generically matching quoted args to C_LOGON)
|
|
15
|
+
import re
|
|
16
|
+
# Redact anything in C_LOGON "..."
|
|
17
|
+
cmd = re.sub(r'(C_LOGON\s*")([^"]+)(")', r'\1***REDACTED***\3', cmd)
|
|
18
|
+
# (Optional) Redact anything that looks like password="..."
|
|
19
|
+
cmd = re.sub(r'(password\s*=\s*")([^"]+)(")', r'\1***REDACTED***\3', cmd, flags=re.IGNORECASE)
|
|
20
|
+
# (Optional) Redact any --password=... arguments
|
|
21
|
+
cmd = re.sub(r'(--password=)(\S+)', r'\1***REDACTED***', cmd, flags=re.IGNORECASE)
|
|
22
|
+
return cmd
|
|
23
|
+
|
|
24
|
+
def errexit(msg):
|
|
25
|
+
logging.error(f"Error: { msg }, exiting...")
|
|
26
|
+
sys.exit(1)
|
|
27
|
+
|
|
28
|
+
# Keepalive thread to ensure TCP connection is kept open
|
|
29
|
+
# Do not edit this
|
|
30
|
+
class KeepAliveThread(threading.Thread):
|
|
31
|
+
message = ''
|
|
32
|
+
def __init__(self, connection: "XenaSocketDriver", interval: int = 10):
|
|
33
|
+
threading.Thread.__init__(self)
|
|
34
|
+
self.connection = connection
|
|
35
|
+
self.interval = interval
|
|
36
|
+
self.finished = threading.Event()
|
|
37
|
+
self.daemon = True
|
|
38
|
+
logging.info('[KeepAliveThread] Thread initiated, interval %d seconds' % (self.interval))
|
|
39
|
+
|
|
40
|
+
def stop(self):
|
|
41
|
+
self.finished.set()
|
|
42
|
+
self.join()
|
|
43
|
+
|
|
44
|
+
def run(self):
|
|
45
|
+
while not self.finished.is_set():
|
|
46
|
+
self.finished.wait(self.interval)
|
|
47
|
+
self.connection.send_and_response(self.message)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# Low level driver for TCP/IP based query
|
|
51
|
+
# Do not edit this
|
|
52
|
+
class XenaSocketDriver(SimpleSocket):
|
|
53
|
+
def __init__(self, hostname: str, tcp_port: int = 22611):
|
|
54
|
+
super(XenaSocketDriver, self).__init__(hostname=hostname, port=tcp_port)
|
|
55
|
+
self.set_keepalives()
|
|
56
|
+
self.access_semaphore = threading.Semaphore(1)
|
|
57
|
+
|
|
58
|
+
def send_only(self, cmd: str):
|
|
59
|
+
with self.access_semaphore:
|
|
60
|
+
super().send_only(cmd)
|
|
61
|
+
|
|
62
|
+
def send_and_response(self, cmd: str, sync_on: bool = False) -> str:
|
|
63
|
+
with self.access_semaphore:
|
|
64
|
+
reply = super().send_and_response(cmd, sync_on=sync_on).strip('\n')
|
|
65
|
+
return reply
|
|
66
|
+
|
|
67
|
+
def send_and_response_multiple(self, cmd: str, num: int):
|
|
68
|
+
with self.access_semaphore:
|
|
69
|
+
reply = super().send_and_response_multiple(cmd, num)
|
|
70
|
+
return reply
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# Xena supplied class example for Scripting via Python3
|
|
74
|
+
# Feel free to add functions below
|
|
75
|
+
class XOACLIManager:
|
|
76
|
+
|
|
77
|
+
CHASSIS_RESERVATION = lambda self: f"C_RESERVATION ?"
|
|
78
|
+
CHASSIS_RELEASE = lambda self: f"C_RESERVATION RELEASE"
|
|
79
|
+
CHASSIS_RELINQUISH = lambda self: f"C_RESERVATION RELINQUISH"
|
|
80
|
+
CHASSIS_RESERVE = lambda self: f"C_RESERVATION RESERVE"
|
|
81
|
+
|
|
82
|
+
MODULE_RESERVATION = lambda self, mod: f"{ mod } M_RESERVATION ?"
|
|
83
|
+
MODULE_RELEASE = lambda self, mod: f"{ mod } M_RESERVATION RELEASE"
|
|
84
|
+
MODULE_RELINQUISH = lambda self, mod: f"{ mod } M_RESERVATION RELINQUISH"
|
|
85
|
+
MODULE_RESERVE = lambda self, mod: f"{ mod } M_RESERVATION RESERVE"
|
|
86
|
+
|
|
87
|
+
PORT_RESET = lambda self, port: f"{ port } P_RESET"
|
|
88
|
+
PORT_RESERVATION = lambda self, port: f"{ port } P_RESERVATION ?"
|
|
89
|
+
PORT_RESERVE = lambda self, port: f"{ port } P_RESERVATION RESERVE"
|
|
90
|
+
PORT_RELINQUISH = lambda self, port: f"{ port } P_RESERVATION RELINQUISH"
|
|
91
|
+
PORT_RELEASE = lambda self, port: f"{ port } P_RESERVATION RELEASE"
|
|
92
|
+
PORT_SPEEDSELECTION = lambda self, port, speed: f"{ port } P_SPEEDSELECTION {speed}"
|
|
93
|
+
|
|
94
|
+
PORT_TRAFFIC_ON = lambda self, port: f"{ port } P_TRAFFIC ON"
|
|
95
|
+
PORT_TRAFFIC_OFF = lambda self, port: f"{ port } P_TRAFFIC OFF"
|
|
96
|
+
|
|
97
|
+
def __init__(self, host: str, debug: bool = False, halt_on_error: bool = False) -> None:
|
|
98
|
+
self.host = host
|
|
99
|
+
self.debug_enabled = debug
|
|
100
|
+
self.halt_on_error_enabled = halt_on_error
|
|
101
|
+
self.is_log_cmd_empty = False
|
|
102
|
+
self.cmd_trace_list = []
|
|
103
|
+
self.logfile_path = os.environ.get(LOGFILE)
|
|
104
|
+
if self.logfile_path != None:
|
|
105
|
+
self.is_log_cmd_empty = True
|
|
106
|
+
|
|
107
|
+
self.driver = XenaSocketDriver(self.host)
|
|
108
|
+
# self.keepalive_thread = KeepAliveThread(self.driver)
|
|
109
|
+
# self.keepalive_thread.start()
|
|
110
|
+
|
|
111
|
+
def __del__(self):
|
|
112
|
+
if self.is_log_cmd_empty:
|
|
113
|
+
if self.logfile_path:
|
|
114
|
+
with open(self.logfile_path, 'w') as log_file:
|
|
115
|
+
for cmd in self.cmd_trace_list:
|
|
116
|
+
log_file.write(f"{ cmd }\n")
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
def __enter__(self):
|
|
120
|
+
return self
|
|
121
|
+
|
|
122
|
+
def __exit__(self, exc_type, exc, tb):
|
|
123
|
+
self.driver.close()
|
|
124
|
+
# self.keepalive_thread.stop()
|
|
125
|
+
|
|
126
|
+
## Enable debug - prints commands and errors
|
|
127
|
+
def logging_on(self) -> None:
|
|
128
|
+
"""Enable debug output on terminal
|
|
129
|
+
"""
|
|
130
|
+
self.debug_enabled = True
|
|
131
|
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s.%(msecs)03d %(message)s', datefmt='%m/%d/%Y %I:%M:%S')
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
## Disable debug (default) - no printed output
|
|
135
|
+
def logging_off(self) -> None:
|
|
136
|
+
"""Disable debug output on terminal
|
|
137
|
+
"""
|
|
138
|
+
self.debug_enabled = False
|
|
139
|
+
# Reset logging configuration by removing all handlers
|
|
140
|
+
for handler in logging.root.handlers[:]:
|
|
141
|
+
logging.root.removeHandler(handler)
|
|
142
|
+
logging.basicConfig(level=logging.NOTSET, force=True)
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
def debug_message(self, msg: str) -> None:
|
|
146
|
+
"""Print debug message if debug is enabled"""
|
|
147
|
+
if self.debug_enabled == True:
|
|
148
|
+
# If the message contains a sensitive command, do NOT log it
|
|
149
|
+
import re
|
|
150
|
+
# Check for C_LOGON or password in string, as a safeguard
|
|
151
|
+
if re.search(r'C_LOGON\s*".*"', msg) or re.search(r'password\s*=\s*".*"', msg, flags=re.IGNORECASE) or re.search(r'--password=\S+', msg, flags=re.IGNORECASE):
|
|
152
|
+
logging.info(f"{time.time()} [Sensitive command redacted]")
|
|
153
|
+
print(f"{time.time()} [Sensitive command redacted]")
|
|
154
|
+
return
|
|
155
|
+
# Redact as an additional precaution
|
|
156
|
+
safe_msg = _redact_sensitive(msg)
|
|
157
|
+
logging.info(f"{time.time()} {safe_msg}")
|
|
158
|
+
print(f"{time.time()} {safe_msg}")
|
|
159
|
+
def log_command(self, cmd:str) -> None:
|
|
160
|
+
"""Place the command in the log cmd list for later logging
|
|
161
|
+
|
|
162
|
+
:param cmd: CLI command string
|
|
163
|
+
:type cmd: str
|
|
164
|
+
"""
|
|
165
|
+
if self.is_log_cmd_empty == True:
|
|
166
|
+
self.cmd_trace_list.append(_redact_sensitive(cmd))
|
|
167
|
+
|
|
168
|
+
## Enable halt on error - calls sys.exit(1) upon error
|
|
169
|
+
def enable_halt_on_error(self) -> None:
|
|
170
|
+
"""Enable halt on error: calls sys.exit(1) upon error."""
|
|
171
|
+
self.halt_on_error_enabled = True
|
|
172
|
+
|
|
173
|
+
## Disable halt on error (default)
|
|
174
|
+
def disable_halt_on_error(self) -> None:
|
|
175
|
+
"""Disable halt on error: does not call sys.exit(1) upon error."""
|
|
176
|
+
self.halt_on_error_enabled = False
|
|
177
|
+
|
|
178
|
+
## Print diagnostics msg and halt
|
|
179
|
+
def errexit(self, msg:str):
|
|
180
|
+
"""Print error message and exit program if halt on error is enabled"""
|
|
181
|
+
# self.keepalive_thread.stop()
|
|
182
|
+
if self.halt_on_error_enabled == 1:
|
|
183
|
+
logging.error(f"\nError: { msg }, exiting...\n")
|
|
184
|
+
sys.exit(1)
|
|
185
|
+
else:
|
|
186
|
+
raise Exception(f"\nError: { msg }, exiting...\n")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
#####################################################################
|
|
190
|
+
# #
|
|
191
|
+
# Send and Expect wrappers #
|
|
192
|
+
# #
|
|
193
|
+
#####################################################################
|
|
194
|
+
|
|
195
|
+
## Send command and return response
|
|
196
|
+
def send(self, cmd:str, sync_on: bool = False) -> str:
|
|
197
|
+
"""Send command and return response"""
|
|
198
|
+
if self.driver.is_connected == False:
|
|
199
|
+
self.driver = XenaSocketDriver(self.host)
|
|
200
|
+
res = self.driver.send_and_response(cmd, sync_on=sync_on)
|
|
201
|
+
self.debug_message(f"send() : { cmd }")
|
|
202
|
+
self.debug_message(f"send() received: { res }")
|
|
203
|
+
self.log_command(cmd)
|
|
204
|
+
return res
|
|
205
|
+
|
|
206
|
+
## Send one command and expect to receive a specified response
|
|
207
|
+
def send_expect(self, cmd:str, resp:str) -> bool:
|
|
208
|
+
"""Send command and expect response (typically <OK>)"""
|
|
209
|
+
|
|
210
|
+
self.debug_message(f"send_expect({ resp }): {_redact_sensitive(cmd)}")
|
|
211
|
+
self.log_command(cmd)
|
|
212
|
+
try:
|
|
213
|
+
if self.driver.is_connected == False:
|
|
214
|
+
self.driver = XenaSocketDriver(self.host)
|
|
215
|
+
res = self.driver.send_and_response(cmd)
|
|
216
|
+
if res.rstrip('\n') == resp:
|
|
217
|
+
return True
|
|
218
|
+
else:
|
|
219
|
+
self.debug_message("send_expect() failed")
|
|
220
|
+
self.debug_message(f" Expected: { resp }")
|
|
221
|
+
self.debug_message(f" Received: { res }")
|
|
222
|
+
# self.errexit(f"Halting in line {inspect.currentframe().f_back.f_lineno}")
|
|
223
|
+
return False
|
|
224
|
+
except Exception as e:
|
|
225
|
+
exc_type, exc_obj, exc_tb = sys.exc_info()
|
|
226
|
+
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] if exc_tb else "Unknown"
|
|
227
|
+
if exc_tb is not None:
|
|
228
|
+
logging.error(exc_type, fname, exc_tb.tb_lineno)
|
|
229
|
+
else:
|
|
230
|
+
logging.error(exc_type)
|
|
231
|
+
return False
|
|
232
|
+
|
|
233
|
+
## Send one command and expect to receive <OK> as a response
|
|
234
|
+
def send_expect_ok(self, cmd:str) -> bool:
|
|
235
|
+
"""Send commands and expect <OK>"""
|
|
236
|
+
|
|
237
|
+
if isinstance(cmd, str):
|
|
238
|
+
return self.send_expect(cmd, "<OK>")
|
|
239
|
+
else:
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
## Send command and match response with specified string
|
|
243
|
+
def send_and_match(self, cmd:str, match_resp:str) -> bool:
|
|
244
|
+
""" Send command and match response with specified string"""
|
|
245
|
+
self.debug_message(f"send_and_match() : { cmd }")
|
|
246
|
+
self.log_command(cmd)
|
|
247
|
+
|
|
248
|
+
if self.driver.is_connected == False:
|
|
249
|
+
self.driver = XenaSocketDriver(self.host)
|
|
250
|
+
res = self.driver.send_and_response(cmd)
|
|
251
|
+
if match_resp in res:
|
|
252
|
+
return True
|
|
253
|
+
else:
|
|
254
|
+
self.debug_message("send_and_match() failed")
|
|
255
|
+
self.debug_message(f" Expected: { match_resp }")
|
|
256
|
+
self.debug_message(f" Got : { res }")
|
|
257
|
+
frame = inspect.currentframe()
|
|
258
|
+
if frame and frame.f_back:
|
|
259
|
+
self.errexit(f"Halting in line { frame.f_back.f_lineno }")
|
|
260
|
+
else:
|
|
261
|
+
self.errexit("Halting due to an unknown error")
|
|
262
|
+
return False
|
|
263
|
+
|
|
264
|
+
## Send multiple commands in batch and return all responses
|
|
265
|
+
def send_multi_commands(self, cmdlist: list, batch = True) -> list:
|
|
266
|
+
"""Send multiple commands in batch and return all responses"""
|
|
267
|
+
if not isinstance(cmdlist, list):
|
|
268
|
+
raise ValueError('\'cmdlist\' - must be a instance of list')
|
|
269
|
+
cmd = ''
|
|
270
|
+
num = len(cmdlist)
|
|
271
|
+
self.debug_message(f"{num} commands to send to xenaserver")
|
|
272
|
+
|
|
273
|
+
if batch == True:
|
|
274
|
+
for command in cmdlist:
|
|
275
|
+
cmd = cmd + command + '\n'
|
|
276
|
+
|
|
277
|
+
self.debug_message(f"send() : { cmd }")
|
|
278
|
+
if self.driver.is_connected == False:
|
|
279
|
+
self.driver = XenaSocketDriver(self.host)
|
|
280
|
+
res = self.driver.send_and_response_multiple(cmd, num)
|
|
281
|
+
def mapper(v): return f"{ v[0] }: { v[1] }"
|
|
282
|
+
mes = "\n".join( list( map(mapper, list( zip(cmdlist, res.split('\n')) ) ) ) )
|
|
283
|
+
self.debug_message(f"send() received: { mes }")
|
|
284
|
+
|
|
285
|
+
return res.splitlines()
|
|
286
|
+
else:
|
|
287
|
+
results = []
|
|
288
|
+
for command in cmdlist:
|
|
289
|
+
cmd = command
|
|
290
|
+
self.debug_message(f"send() : { cmd }")
|
|
291
|
+
if self.driver.is_connected == False:
|
|
292
|
+
self.driver = XenaSocketDriver(self.host)
|
|
293
|
+
res = self.driver.send_and_response(cmd)
|
|
294
|
+
self.debug_message(f"send() received: { res }")
|
|
295
|
+
self.log_command(cmd)
|
|
296
|
+
results.append(res)
|
|
297
|
+
return results
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
#####################################################################
|
|
302
|
+
# #
|
|
303
|
+
# Xena Scripting API specific commands #
|
|
304
|
+
# #
|
|
305
|
+
#####################################################################
|
|
306
|
+
|
|
307
|
+
#############################################################
|
|
308
|
+
# Chassis Commands #
|
|
309
|
+
#############################################################
|
|
310
|
+
|
|
311
|
+
# Logon
|
|
312
|
+
def log_on(self, pwd: str) -> bool:
|
|
313
|
+
return self.send_expect_ok(f"C_LOGON \"{ pwd }\"")
|
|
314
|
+
|
|
315
|
+
# Logoff
|
|
316
|
+
def log_off(self) -> None:
|
|
317
|
+
self.send("C_LOGOFF")
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
## Logon and set owner
|
|
321
|
+
def logon_set_owner(self, pwd: str, owner: str = "XOACLI") -> bool:
|
|
322
|
+
if self.log_on(pwd):
|
|
323
|
+
return self.send_expect_ok(f"C_OWNER \"{ owner }\"")
|
|
324
|
+
return False
|
|
325
|
+
|
|
326
|
+
## Logon to chassis, set user name and password, then reserve ports
|
|
327
|
+
def logon_reserve_ports(self, ports: Union[List[str], str], pwd: str, owner: str = "XOACLI") -> None:
|
|
328
|
+
if isinstance(ports, str): ports = [ports]
|
|
329
|
+
assert self.logon_set_owner(pwd, owner)
|
|
330
|
+
self.reserve_port(ports)
|
|
331
|
+
|
|
332
|
+
# Reserve chassis if it is not mine, else do nothing.
|
|
333
|
+
def reserve_chassis(self):
|
|
334
|
+
res = self.send( self.CHASSIS_RESERVATION() ).split()[1]
|
|
335
|
+
|
|
336
|
+
if "RESERVED_BY_OTHER" in res:
|
|
337
|
+
self.debug_message("Chassis is reserved by other - relinquish")
|
|
338
|
+
self.send_expect_ok( self.CHASSIS_RELINQUISH() )
|
|
339
|
+
self.send_expect_ok( self.CHASSIS_RESERVE() )
|
|
340
|
+
|
|
341
|
+
elif "RELEASED" in res:
|
|
342
|
+
self.send_expect_ok( self.CHASSIS_RESERVE() )
|
|
343
|
+
|
|
344
|
+
# Reserve chassis, release or relinquish first if necessary
|
|
345
|
+
def free_chassis(self):
|
|
346
|
+
res = self.send( self.CHASSIS_RESERVATION() ).split()[1]
|
|
347
|
+
|
|
348
|
+
if "RESERVED_BY_YOU" in res:
|
|
349
|
+
self.debug_message("Chassis is reserved by me - release")
|
|
350
|
+
self.send_expect_ok( self.CHASSIS_RELEASE() )
|
|
351
|
+
|
|
352
|
+
elif "RESERVED_BY_OTHER" in res:
|
|
353
|
+
self.debug_message("Chassis is reserved by other - relinquish")
|
|
354
|
+
self.send_expect_ok( self.CHASSIS_RELINQUISH() )
|
|
355
|
+
|
|
356
|
+
elif "RELEASED" in res:
|
|
357
|
+
self.debug_message("Chassis is released - do nothing")
|
|
358
|
+
else:
|
|
359
|
+
frame = inspect.currentframe()
|
|
360
|
+
if frame and frame.f_back:
|
|
361
|
+
self.errexit(f"Halting in line { frame.f_back.f_lineno }")
|
|
362
|
+
else:
|
|
363
|
+
self.errexit("Halting due to an unknown error")
|
|
364
|
+
|
|
365
|
+
# Start traffic on ports simultaneously
|
|
366
|
+
def start_chassis_traffic(self, ports: Union[List[str], str]):
|
|
367
|
+
if isinstance(ports, str): ports = ports.split()
|
|
368
|
+
param = []
|
|
369
|
+
for port in ports:
|
|
370
|
+
param += port.split("/")
|
|
371
|
+
|
|
372
|
+
param = ' '.join([str(elem) for elem in param])
|
|
373
|
+
self.send_expect_ok( f"C_TRAFFIC ON {param}")
|
|
374
|
+
|
|
375
|
+
# Start traffic on ports simultaneously
|
|
376
|
+
def stop_chassis_traffic(self, ports: Union[List[str], str]):
|
|
377
|
+
if isinstance(ports, str): ports = ports.split()
|
|
378
|
+
param = []
|
|
379
|
+
for port in ports:
|
|
380
|
+
param += port.split("/")
|
|
381
|
+
|
|
382
|
+
param = ' '.join([str(elem) for elem in param])
|
|
383
|
+
self.send_expect_ok( f"C_TRAFFIC OFF {param}")
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
#############################################################
|
|
387
|
+
# Module Commands #
|
|
388
|
+
#############################################################
|
|
389
|
+
|
|
390
|
+
# Release module from me or relinquish module from others
|
|
391
|
+
def free_module(self, module_id: str):
|
|
392
|
+
res = self.send(self.MODULE_RESERVATION(module_id)).split()[2]
|
|
393
|
+
|
|
394
|
+
if "RESERVED_BY_YOU" in res:
|
|
395
|
+
self.debug_message("Module is reserved by me - release")
|
|
396
|
+
self.send_expect_ok(self.MODULE_RELEASE(module_id))
|
|
397
|
+
|
|
398
|
+
elif "RESERVED_BY_OTHER" in res:
|
|
399
|
+
self.debug_message("Module is reserved by other - relinquish")
|
|
400
|
+
self.send_expect_ok(self.MODULE_RELINQUISH(module_id))
|
|
401
|
+
|
|
402
|
+
elif "RELEASED" in res:
|
|
403
|
+
self.debug_message("Module is released - do nothing")
|
|
404
|
+
|
|
405
|
+
else:
|
|
406
|
+
frame = inspect.currentframe()
|
|
407
|
+
if frame and frame.f_back:
|
|
408
|
+
self.errexit(f"Halting in line { frame.f_back.f_lineno }")
|
|
409
|
+
else:
|
|
410
|
+
self.errexit("Halting due to an unknown error")
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def reserve_module(self, module_id: str):
|
|
414
|
+
"""
|
|
415
|
+
Reserve the module if it is not mine, else do nothing
|
|
416
|
+
"""
|
|
417
|
+
res = self.send(self.MODULE_RESERVATION(module_id)).split()[2]
|
|
418
|
+
|
|
419
|
+
if "RESERVED_BY_OTHER" in res:
|
|
420
|
+
self.debug_message("Module is reserved by other - relinquish")
|
|
421
|
+
self.send_expect_ok(self.MODULE_RELINQUISH(module_id))
|
|
422
|
+
self.send_expect_ok( self.MODULE_RESERVE(module_id) )
|
|
423
|
+
|
|
424
|
+
elif "RELEASED" in res:
|
|
425
|
+
self.send_expect_ok( self.MODULE_RESERVE(module_id) )
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
#############################################################
|
|
429
|
+
# Port Commands #
|
|
430
|
+
#############################################################
|
|
431
|
+
|
|
432
|
+
# Wait for port to be 'released' - with timeout of 1 minute
|
|
433
|
+
def wait_port_to_release(self, port_ids: Union[List[str], str], timeout_s: int = 63):
|
|
434
|
+
if isinstance(port_ids, str): port_ids = port_ids.split()
|
|
435
|
+
for port in port_ids:
|
|
436
|
+
timeout = time.time() + timeout_s # 60sec + total 3sec of slipping time by 1s
|
|
437
|
+
while True:
|
|
438
|
+
res = self.send( self.PORT_RESERVATION(port) )
|
|
439
|
+
if 'RELEASED' in res:
|
|
440
|
+
break
|
|
441
|
+
elif time.time() > timeout:
|
|
442
|
+
raise TimeoutError('port_wait_release: Waiting for changing of port reservation interval is terminated!')
|
|
443
|
+
else:
|
|
444
|
+
time.sleep(0.1)
|
|
445
|
+
|
|
446
|
+
# Reserve a port - if port is reserved, release or relinquish, then reserve
|
|
447
|
+
def reserve_port(self, port_ids: Union[List[str], str]) -> None:
|
|
448
|
+
if isinstance(port_ids, str): port_ids = port_ids.split()
|
|
449
|
+
for port in port_ids:
|
|
450
|
+
res = self.send( self.PORT_RESERVATION(port) )
|
|
451
|
+
if 'RESERVED_BY_OTHER' in res:
|
|
452
|
+
self.debug_message(f"Port { port } is reserved by other - relinquish")
|
|
453
|
+
self.send_expect_ok( self.PORT_RELINQUISH(port) )
|
|
454
|
+
self.wait_port_to_release(port)
|
|
455
|
+
self.send_expect_ok( self.PORT_RESERVE(port) )
|
|
456
|
+
elif 'RELEASED' in res:
|
|
457
|
+
self.send_expect_ok( self.PORT_RESERVE(port) )
|
|
458
|
+
|
|
459
|
+
# Set a port/ports free.
|
|
460
|
+
def free_port(self, port_ids: Union[List[str], str]) -> None:
|
|
461
|
+
if isinstance(port_ids, str): port_ids = port_ids.split()
|
|
462
|
+
for port in port_ids:
|
|
463
|
+
res = self.send( self.PORT_RESERVATION(port) )
|
|
464
|
+
if "RESERVED_BY_OTHER" in res:
|
|
465
|
+
self.send_expect_ok( self.PORT_RELINQUISH(port) )
|
|
466
|
+
if "RESERVED_BY_YOU" in res:
|
|
467
|
+
self.send_expect_ok( self.PORT_RELEASE(port) )
|
|
468
|
+
|
|
469
|
+
# Reset a port/ports
|
|
470
|
+
def reset_port(self, port_ids: Union[List[str], str], wait_after_reset_s: int) -> None:
|
|
471
|
+
if isinstance(port_ids, str): port_ids = port_ids.split()
|
|
472
|
+
self.reserve_port(port_ids)
|
|
473
|
+
for port in port_ids:
|
|
474
|
+
res = self.send_expect_ok( self.PORT_RESET(port) )
|
|
475
|
+
|
|
476
|
+
time.sleep(wait_after_reset_s)
|
|
477
|
+
|
|
478
|
+
## Start traffic on ports
|
|
479
|
+
def start_port_traffic(self, port_ids: Union[List[str], str]) -> None:
|
|
480
|
+
if isinstance(port_ids, str): port_list: List[str] = port_ids.split()
|
|
481
|
+
else: port_list = port_ids
|
|
482
|
+
for port in port_list:
|
|
483
|
+
res = self.send_expect_ok(self.PORT_TRAFFIC_ON(port) )
|
|
484
|
+
|
|
485
|
+
## Stop traffic on ports
|
|
486
|
+
def stop_port_traffic(self, port_ids: Union[List[str], str]) -> None:
|
|
487
|
+
if isinstance(port_ids, str): port_list: List[str] = port_ids.split()
|
|
488
|
+
else: port_list = port_ids
|
|
489
|
+
for port in port_list:
|
|
490
|
+
res = self.send_expect_ok(self.PORT_TRAFFIC_OFF(port) )
|
|
491
|
+
|
|
492
|
+
## Port speed selection
|
|
493
|
+
def select_port_speed_mode(self, port_ids: Union[List[str], str], speed_mode: str = "AUTO") -> None:
|
|
494
|
+
if isinstance(port_ids, str): port_list: List[str] = port_ids.split()
|
|
495
|
+
else: port_list = port_ids
|
|
496
|
+
for port in port_list:
|
|
497
|
+
res = self.send_expect_ok(self.PORT_SPEEDSELECTION(port, speed_mode) )
|
|
498
|
+
|
|
499
|
+
## Get port configuration
|
|
500
|
+
def get_port_full_config_raw(self, port_ids: Union[List[str], str]) -> List[str]:
|
|
501
|
+
"""Get full config data of one or multiple ports
|
|
502
|
+
|
|
503
|
+
:param port_ids: list of port ids in format "module_id/port_id"
|
|
504
|
+
:type port_ids: Union[List[str], str]
|
|
505
|
+
:return: list of port full config strs
|
|
506
|
+
:rtype: List[str]
|
|
507
|
+
"""
|
|
508
|
+
self.send(cmd = f"SYNC ON")
|
|
509
|
+
result = []
|
|
510
|
+
if isinstance(port_ids, str): port_id_list: List[str] = port_ids.split()
|
|
511
|
+
else: port_id_list = port_ids
|
|
512
|
+
for port_id in port_id_list:
|
|
513
|
+
resp = ''
|
|
514
|
+
# Send P_FULLCONFIG ? command and get response
|
|
515
|
+
# It includes port configuration and stream configurations
|
|
516
|
+
resp = self.send(cmd = f"{port_id} P_FULLCONFIG ?", sync_on=True)
|
|
517
|
+
result.append(resp)
|
|
518
|
+
self.send(cmd = f"SYNC OFF")
|
|
519
|
+
return result
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
## Get module configuration
|
|
523
|
+
def get_module_full_config_raw(self, module_ids: Union[List[str], str]) -> List[str]:
|
|
524
|
+
"""Get full config data of one or multile modules
|
|
525
|
+
|
|
526
|
+
:param module_ids: list of module ids
|
|
527
|
+
:type module_ids: Union[List[str], str]
|
|
528
|
+
:return: list of module full config strs
|
|
529
|
+
:rtype: List[str]
|
|
530
|
+
"""
|
|
531
|
+
self.send(cmd = f"SYNC ON")
|
|
532
|
+
result = []
|
|
533
|
+
if isinstance(module_ids, str): module_id_list: List[str] = module_ids.split()
|
|
534
|
+
else: module_id_list = module_ids
|
|
535
|
+
for module_id in module_id_list:
|
|
536
|
+
resp = ''
|
|
537
|
+
resp = self.send(cmd = f"{module_id} M_CONFIG ?", sync_on=True)
|
|
538
|
+
result.append(resp)
|
|
539
|
+
self.send(cmd = f"SYNC OFF")
|
|
540
|
+
return result
|
|
541
|
+
|