scrapli 2023.7.30__py3-none-any.whl → 2024.7.30__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 (64) hide show
  1. scrapli/__init__.py +2 -1
  2. scrapli/channel/__init__.py +1 -0
  3. scrapli/channel/async_channel.py +35 -12
  4. scrapli/channel/base_channel.py +25 -3
  5. scrapli/channel/sync_channel.py +35 -12
  6. scrapli/decorators.py +1 -0
  7. scrapli/driver/__init__.py +1 -0
  8. scrapli/driver/base/__init__.py +1 -0
  9. scrapli/driver/base/async_driver.py +19 -13
  10. scrapli/driver/base/base_driver.py +121 -37
  11. scrapli/driver/base/sync_driver.py +19 -13
  12. scrapli/driver/core/__init__.py +1 -0
  13. scrapli/driver/core/arista_eos/__init__.py +1 -0
  14. scrapli/driver/core/arista_eos/async_driver.py +3 -0
  15. scrapli/driver/core/arista_eos/base_driver.py +3 -2
  16. scrapli/driver/core/arista_eos/sync_driver.py +3 -0
  17. scrapli/driver/core/cisco_iosxe/__init__.py +1 -0
  18. scrapli/driver/core/cisco_iosxe/async_driver.py +3 -0
  19. scrapli/driver/core/cisco_iosxe/base_driver.py +1 -0
  20. scrapli/driver/core/cisco_iosxe/sync_driver.py +3 -0
  21. scrapli/driver/core/cisco_iosxr/__init__.py +1 -0
  22. scrapli/driver/core/cisco_iosxr/async_driver.py +3 -0
  23. scrapli/driver/core/cisco_iosxr/base_driver.py +1 -0
  24. scrapli/driver/core/cisco_iosxr/sync_driver.py +3 -0
  25. scrapli/driver/core/cisco_nxos/__init__.py +1 -0
  26. scrapli/driver/core/cisco_nxos/async_driver.py +3 -0
  27. scrapli/driver/core/cisco_nxos/base_driver.py +9 -4
  28. scrapli/driver/core/cisco_nxos/sync_driver.py +3 -0
  29. scrapli/driver/core/juniper_junos/__init__.py +1 -0
  30. scrapli/driver/core/juniper_junos/async_driver.py +3 -0
  31. scrapli/driver/core/juniper_junos/base_driver.py +1 -0
  32. scrapli/driver/core/juniper_junos/sync_driver.py +3 -0
  33. scrapli/driver/generic/__init__.py +1 -0
  34. scrapli/driver/generic/async_driver.py +45 -3
  35. scrapli/driver/generic/base_driver.py +2 -1
  36. scrapli/driver/generic/sync_driver.py +45 -3
  37. scrapli/driver/network/__init__.py +1 -0
  38. scrapli/driver/network/async_driver.py +27 -0
  39. scrapli/driver/network/base_driver.py +1 -0
  40. scrapli/driver/network/sync_driver.py +27 -0
  41. scrapli/exceptions.py +1 -0
  42. scrapli/factory.py +22 -3
  43. scrapli/helper.py +76 -4
  44. scrapli/logging.py +1 -0
  45. scrapli/response.py +1 -0
  46. scrapli/ssh_config.py +1 -0
  47. scrapli/transport/base/__init__.py +1 -0
  48. scrapli/transport/base/async_transport.py +1 -0
  49. scrapli/transport/base/base_socket.py +1 -0
  50. scrapli/transport/base/base_transport.py +1 -0
  51. scrapli/transport/base/sync_transport.py +1 -0
  52. scrapli/transport/plugins/asyncssh/transport.py +4 -0
  53. scrapli/transport/plugins/asynctelnet/transport.py +13 -6
  54. scrapli/transport/plugins/paramiko/transport.py +1 -0
  55. scrapli/transport/plugins/ssh2/transport.py +6 -3
  56. scrapli/transport/plugins/system/ptyprocess.py +50 -13
  57. scrapli/transport/plugins/system/transport.py +27 -6
  58. scrapli/transport/plugins/telnet/transport.py +13 -7
  59. {scrapli-2023.7.30.dist-info → scrapli-2024.7.30.dist-info}/METADATA +74 -47
  60. scrapli-2024.7.30.dist-info/RECORD +74 -0
  61. {scrapli-2023.7.30.dist-info → scrapli-2024.7.30.dist-info}/WHEEL +1 -1
  62. scrapli-2023.7.30.dist-info/RECORD +0 -74
  63. {scrapli-2023.7.30.dist-info → scrapli-2024.7.30.dist-info}/LICENSE +0 -0
  64. {scrapli-2023.7.30.dist-info → scrapli-2024.7.30.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.generic.sync_driver"""
2
+
2
3
  import time
3
4
  from io import BytesIO
4
5
  from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union
@@ -16,6 +17,23 @@ if TYPE_CHECKING:
16
17
  )
17
18
 
18
19
 
20
+ def generic_on_open(conn: "GenericDriver") -> None:
21
+ """
22
+ GenericDriver default on-open -- drains initial login by running a simple get_prompt
23
+
24
+ Args:
25
+ conn: GenericDriver object
26
+
27
+ Returns:
28
+ None
29
+
30
+ Raises:
31
+ N/A
32
+
33
+ """
34
+ conn.get_prompt()
35
+
36
+
19
37
  class GenericDriver(Driver, BaseGenericDriver):
20
38
  def __init__(
21
39
  self,
@@ -35,10 +53,11 @@ class GenericDriver(Driver, BaseGenericDriver):
35
53
  timeout_ops: float = 30.0,
36
54
  comms_prompt_pattern: str = r"^\S{0,48}[#>$~@:\]]\s*$",
37
55
  comms_return_char: str = "\n",
56
+ comms_roughly_match_inputs: bool = False,
38
57
  ssh_config_file: Union[str, bool] = False,
39
58
  ssh_known_hosts_file: Union[str, bool] = False,
40
59
  on_init: Optional[Callable[..., Any]] = None,
41
- on_open: Optional[Callable[..., Any]] = None,
60
+ on_open: Optional[Callable[..., Any]] = generic_on_open,
42
61
  on_close: Optional[Callable[..., Any]] = None,
43
62
  transport: str = "system",
44
63
  transport_options: Optional[Dict[str, Any]] = None,
@@ -64,6 +83,7 @@ class GenericDriver(Driver, BaseGenericDriver):
64
83
  timeout_ops=timeout_ops,
65
84
  comms_prompt_pattern=comms_prompt_pattern,
66
85
  comms_return_char=comms_return_char,
86
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
67
87
  ssh_config_file=ssh_config_file,
68
88
  ssh_known_hosts_file=ssh_known_hosts_file,
69
89
  on_init=on_init,
@@ -102,6 +122,7 @@ class GenericDriver(Driver, BaseGenericDriver):
102
122
  strip_prompt: bool = True,
103
123
  failed_when_contains: Optional[Union[str, List[str]]] = None,
104
124
  eager: bool = False,
125
+ eager_input: bool = False,
105
126
  timeout_ops: Optional[float] = None,
106
127
  ) -> Response:
107
128
  """
@@ -118,6 +139,8 @@ class GenericDriver(Driver, BaseGenericDriver):
118
139
  eager: if eager is True we do not read until prompt is seen at each command sent to the
119
140
  channel. Do *not* use this unless you know what you are doing as it is possible that
120
141
  it can make scrapli less reliable!
142
+ eager_input: when true does *not* try to read our input off the channel -- generally
143
+ this should be left alone unless you know what you are doing!
121
144
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
122
145
  the duration of the operation, value is reset to initial value after operation is
123
146
  completed
@@ -142,7 +165,7 @@ class GenericDriver(Driver, BaseGenericDriver):
142
165
  failed_when_contains=failed_when_contains,
143
166
  )
144
167
  raw_response, processed_response = self.channel.send_input(
145
- channel_input=command, strip_prompt=strip_prompt, eager=eager
168
+ channel_input=command, strip_prompt=strip_prompt, eager=eager, eager_input=eager_input
146
169
  )
147
170
  return self._post_send_command(
148
171
  raw_response=raw_response, processed_response=processed_response, response=response
@@ -154,6 +177,7 @@ class GenericDriver(Driver, BaseGenericDriver):
154
177
  *,
155
178
  strip_prompt: bool = True,
156
179
  failed_when_contains: Optional[Union[str, List[str]]] = None,
180
+ eager_input: bool = False,
157
181
  timeout_ops: Optional[float] = None,
158
182
  ) -> Response:
159
183
  """
@@ -163,6 +187,8 @@ class GenericDriver(Driver, BaseGenericDriver):
163
187
  command: string to send to device in privilege exec mode
164
188
  strip_prompt: strip prompt or not, defaults to True (yes, strip the prompt)
165
189
  failed_when_contains: string or list of strings indicating failure if found in response
190
+ eager_input: when true does *not* try to read our input off the channel -- generally
191
+ this should be left alone unless you know what you are doing!
166
192
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
167
193
  the duration of the operation, value is reset to initial value after operation is
168
194
  completed
@@ -178,6 +204,7 @@ class GenericDriver(Driver, BaseGenericDriver):
178
204
  command=command,
179
205
  strip_prompt=strip_prompt,
180
206
  failed_when_contains=failed_when_contains,
207
+ eager_input=eager_input,
181
208
  timeout_ops=timeout_ops,
182
209
  )
183
210
  return response
@@ -190,6 +217,7 @@ class GenericDriver(Driver, BaseGenericDriver):
190
217
  failed_when_contains: Optional[Union[str, List[str]]] = None,
191
218
  stop_on_failed: bool = False,
192
219
  eager: bool = False,
220
+ eager_input: bool = False,
193
221
  timeout_ops: Optional[float] = None,
194
222
  ) -> MultiResponse:
195
223
  """
@@ -204,6 +232,8 @@ class GenericDriver(Driver, BaseGenericDriver):
204
232
  eager: if eager is True we do not read until prompt is seen at each command sent to the
205
233
  channel. Do *not* use this unless you know what you are doing as it is possible that
206
234
  it can make scrapli less reliable!
235
+ eager_input: when true does *not* try to read our input off the channel -- generally
236
+ this should be left alone unless you know what you are doing!
207
237
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
208
238
  the duration of the operation, value is reset to initial value after operation is
209
239
  completed. Note that this is the timeout value PER COMMAND sent, not for the total
@@ -224,6 +254,7 @@ class GenericDriver(Driver, BaseGenericDriver):
224
254
  failed_when_contains=failed_when_contains,
225
255
  timeout_ops=timeout_ops,
226
256
  eager=eager,
257
+ eager_input=eager_input,
227
258
  )
228
259
  responses.append(response)
229
260
  if stop_on_failed and response.failed is True:
@@ -239,6 +270,7 @@ class GenericDriver(Driver, BaseGenericDriver):
239
270
  failed_when_contains=failed_when_contains,
240
271
  timeout_ops=timeout_ops,
241
272
  eager=False,
273
+ eager_input=eager_input,
242
274
  )
243
275
  responses.append(response)
244
276
 
@@ -252,6 +284,7 @@ class GenericDriver(Driver, BaseGenericDriver):
252
284
  failed_when_contains: Optional[Union[str, List[str]]] = None,
253
285
  stop_on_failed: bool = False,
254
286
  eager: bool = False,
287
+ eager_input: bool = False,
255
288
  timeout_ops: Optional[float] = None,
256
289
  ) -> MultiResponse:
257
290
  """
@@ -266,6 +299,8 @@ class GenericDriver(Driver, BaseGenericDriver):
266
299
  eager: if eager is True we do not read until prompt is seen at each command sent to the
267
300
  channel. Do *not* use this unless you know what you are doing as it is possible that
268
301
  it can make scrapli less reliable!
302
+ eager_input: when true does *not* try to read our input off the channel -- generally
303
+ this should be left alone unless you know what you are doing!
269
304
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
270
305
  the duration of the operation, value is reset to initial value after operation is
271
306
  completed. Note that this is the timeout value PER COMMAND sent, not for the total
@@ -286,6 +321,7 @@ class GenericDriver(Driver, BaseGenericDriver):
286
321
  failed_when_contains=failed_when_contains,
287
322
  stop_on_failed=stop_on_failed,
288
323
  eager=eager,
324
+ eager_input=eager_input,
289
325
  timeout_ops=timeout_ops,
290
326
  )
291
327
 
@@ -531,7 +567,13 @@ class GenericDriver(Driver, BaseGenericDriver):
531
567
  """
532
568
  if initial_input is not None:
533
569
  self.channel.write(channel_input=f"{initial_input}{self.comms_return_char}")
534
- return self.read_callback(callbacks=callbacks, initial_input=None)
570
+ return self.read_callback(
571
+ callbacks=callbacks,
572
+ initial_input=None,
573
+ read_output=read_output,
574
+ read_delay=read_delay,
575
+ read_timeout=read_timeout,
576
+ )
535
577
 
536
578
  original_transport_timeout = self.timeout_transport
537
579
 
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.network"""
2
+
2
3
  from scrapli.driver.network.async_driver import AsyncNetworkDriver
3
4
  from scrapli.driver.network.sync_driver import NetworkDriver
4
5
 
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.network.async_driver"""
2
+
2
3
  from collections import defaultdict
3
4
  from io import BytesIO
4
5
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union
@@ -29,6 +30,7 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
29
30
  timeout_transport: float = 30.0,
30
31
  timeout_ops: float = 30.0,
31
32
  comms_return_char: str = "\n",
33
+ comms_roughly_match_inputs: bool = False,
32
34
  ssh_config_file: Union[str, bool] = False,
33
35
  ssh_known_hosts_file: Union[str, bool] = False,
34
36
  on_init: Optional[Callable[..., Any]] = None,
@@ -64,6 +66,7 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
64
66
  timeout_transport=timeout_transport,
65
67
  timeout_ops=timeout_ops,
66
68
  comms_return_char=comms_return_char,
69
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
67
70
  ssh_config_file=ssh_config_file,
68
71
  ssh_known_hosts_file=ssh_known_hosts_file,
69
72
  on_init=on_init,
@@ -219,6 +222,7 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
219
222
  *,
220
223
  strip_prompt: bool = True,
221
224
  failed_when_contains: Optional[Union[str, List[str]]] = None,
225
+ eager_input: bool = False,
222
226
  timeout_ops: Optional[float] = None,
223
227
  ) -> Response:
224
228
  """
@@ -230,6 +234,8 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
230
234
  command: string to send to device in privilege exec mode
231
235
  strip_prompt: True/False strip prompt from returned output
232
236
  failed_when_contains: string or list of strings indicating failure if found in response
237
+ eager_input: when true does *not* try to read our input off the channel -- generally
238
+ this should be left alone unless you know what you are doing!
233
239
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
234
240
  the duration of the operation, value is reset to initial value after operation is
235
241
  completed
@@ -250,6 +256,7 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
250
256
  command=command,
251
257
  strip_prompt=strip_prompt,
252
258
  failed_when_contains=failed_when_contains,
259
+ eager_input=eager_input,
253
260
  timeout_ops=timeout_ops,
254
261
  )
255
262
  self._update_response(response)
@@ -264,6 +271,7 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
264
271
  failed_when_contains: Optional[Union[str, List[str]]] = None,
265
272
  stop_on_failed: bool = False,
266
273
  eager: bool = False,
274
+ eager_input: bool = False,
267
275
  timeout_ops: Optional[float] = None,
268
276
  ) -> MultiResponse:
269
277
  """
@@ -280,6 +288,8 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
280
288
  eager: if eager is True we do not read until prompt is seen at each command sent to the
281
289
  channel. Do *not* use this unless you know what you are doing as it is possible that
282
290
  it can make scrapli less reliable!
291
+ eager_input: when true does *not* try to read our input off the channel -- generally
292
+ this should be left alone unless you know what you are doing!
283
293
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
284
294
  the duration of the operation, value is reset to initial value after operation is
285
295
  completed. Note that this is the timeout value PER COMMAND sent, not for the total
@@ -303,6 +313,7 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
303
313
  failed_when_contains=failed_when_contains,
304
314
  stop_on_failed=stop_on_failed,
305
315
  eager=eager,
316
+ eager_input=eager_input,
306
317
  timeout_ops=timeout_ops,
307
318
  )
308
319
 
@@ -319,6 +330,7 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
319
330
  failed_when_contains: Optional[Union[str, List[str]]] = None,
320
331
  stop_on_failed: bool = False,
321
332
  eager: bool = False,
333
+ eager_input: bool = False,
322
334
  timeout_ops: Optional[float] = None,
323
335
  ) -> MultiResponse:
324
336
  """
@@ -333,6 +345,8 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
333
345
  eager: if eager is True we do not read until prompt is seen at each command sent to the
334
346
  channel. Do *not* use this unless you know what you are doing as it is possible that
335
347
  it can make scrapli less reliable!
348
+ eager_input: when true does *not* try to read our input off the channel -- generally
349
+ this should be left alone unless you know what you are doing!
336
350
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
337
351
  the duration of the operation, value is reset to initial value after operation is
338
352
  completed. Note that this is the timeout value PER COMMAND sent, not for the total
@@ -356,6 +370,7 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
356
370
  failed_when_contains=failed_when_contains,
357
371
  stop_on_failed=stop_on_failed,
358
372
  eager=eager,
373
+ eager_input=eager_input,
359
374
  timeout_ops=timeout_ops,
360
375
  )
361
376
 
@@ -477,6 +492,7 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
477
492
  stop_on_failed: bool = False,
478
493
  privilege_level: str = "",
479
494
  eager: bool = False,
495
+ eager_input: bool = False,
480
496
  timeout_ops: Optional[float] = None,
481
497
  ) -> MultiResponse:
482
498
  """
@@ -498,6 +514,8 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
498
514
  eager: if eager is True we do not read until prompt is seen at each command sent to the
499
515
  channel. Do *not* use this unless you know what you are doing as it is possible that
500
516
  it can make scrapli less reliable!
517
+ eager_input: when true does *not* try to read our input off the channel -- generally
518
+ this should be left alone unless you know what you are doing!
501
519
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
502
520
  the duration of the operation, value is reset to initial value after operation is
503
521
  completed. Note that this is the timeout value PER CONFIG sent, not for the total
@@ -525,6 +543,7 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
525
543
  failed_when_contains=failed_when_contains,
526
544
  stop_on_failed=stop_on_failed,
527
545
  eager=eager,
546
+ eager_input=eager_input,
528
547
  timeout_ops=timeout_ops,
529
548
  )
530
549
 
@@ -542,6 +561,7 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
542
561
  stop_on_failed: bool = False,
543
562
  privilege_level: str = "",
544
563
  eager: bool = False,
564
+ eager_input: bool = False,
545
565
  timeout_ops: Optional[float] = None,
546
566
  ) -> Response:
547
567
  """
@@ -563,6 +583,8 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
563
583
  eager: if eager is True we do not read until prompt is seen at each command sent to the
564
584
  channel. Do *not* use this unless you know what you are doing as it is possible that
565
585
  it can make scrapli less reliable!
586
+ eager_input: when true does *not* try to read our input off the channel -- generally
587
+ this should be left alone unless you know what you are doing!
566
588
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
567
589
  the duration of the operation, value is reset to initial value after operation is
568
590
  completed. Note that this is the timeout value PER CONFIG sent, not for the total
@@ -585,6 +607,7 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
585
607
  stop_on_failed=stop_on_failed,
586
608
  privilege_level=privilege_level,
587
609
  eager=eager,
610
+ eager_input=eager_input,
588
611
  timeout_ops=timeout_ops,
589
612
  )
590
613
  return self._post_send_config(config=config, multi_response=multi_response)
@@ -598,6 +621,7 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
598
621
  stop_on_failed: bool = False,
599
622
  privilege_level: str = "",
600
623
  eager: bool = False,
624
+ eager_input: bool = False,
601
625
  timeout_ops: Optional[float] = None,
602
626
  ) -> MultiResponse:
603
627
  """
@@ -619,6 +643,8 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
619
643
  eager: if eager is True we do not read until prompt is seen at each command sent to the
620
644
  channel. Do *not* use this unless you know what you are doing as it is possible that
621
645
  it can make scrapli less reliable!
646
+ eager_input: when true does *not* try to read our input off the channel -- generally
647
+ this should be left alone unless you know what you are doing!
622
648
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
623
649
  the duration of the operation, value is reset to initial value after operation is
624
650
  completed. Note that this is the timeout value PER CONFIG sent, not for the total
@@ -640,5 +666,6 @@ class AsyncNetworkDriver(AsyncGenericDriver, BaseNetworkDriver):
640
666
  stop_on_failed=stop_on_failed,
641
667
  privilege_level=privilege_level,
642
668
  eager=eager,
669
+ eager_input=eager_input,
643
670
  timeout_ops=timeout_ops,
644
671
  )
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.network.base_driver"""
2
+
2
3
  import re
3
4
  from collections import defaultdict
4
5
  from datetime import datetime
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.network.sync_driver"""
2
+
2
3
  from collections import defaultdict
3
4
  from io import BytesIO
4
5
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union
@@ -29,6 +30,7 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
29
30
  timeout_transport: float = 30.0,
30
31
  timeout_ops: float = 30.0,
31
32
  comms_return_char: str = "\n",
33
+ comms_roughly_match_inputs: bool = False,
32
34
  ssh_config_file: Union[str, bool] = False,
33
35
  ssh_known_hosts_file: Union[str, bool] = False,
34
36
  on_init: Optional[Callable[..., Any]] = None,
@@ -64,6 +66,7 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
64
66
  timeout_transport=timeout_transport,
65
67
  timeout_ops=timeout_ops,
66
68
  comms_return_char=comms_return_char,
69
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
67
70
  ssh_config_file=ssh_config_file,
68
71
  ssh_known_hosts_file=ssh_known_hosts_file,
69
72
  on_init=on_init,
@@ -219,6 +222,7 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
219
222
  *,
220
223
  strip_prompt: bool = True,
221
224
  failed_when_contains: Optional[Union[str, List[str]]] = None,
225
+ eager_input: bool = False,
222
226
  timeout_ops: Optional[float] = None,
223
227
  ) -> Response:
224
228
  """
@@ -230,6 +234,8 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
230
234
  command: string to send to device in privilege exec mode
231
235
  strip_prompt: True/False strip prompt from returned output
232
236
  failed_when_contains: string or list of strings indicating failure if found in response
237
+ eager_input: when true does *not* try to read our input off the channel -- generally
238
+ this should be left alone unless you know what you are doing!
233
239
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
234
240
  the duration of the operation, value is reset to initial value after operation is
235
241
  completed
@@ -250,6 +256,7 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
250
256
  command=command,
251
257
  strip_prompt=strip_prompt,
252
258
  failed_when_contains=failed_when_contains,
259
+ eager_input=eager_input,
253
260
  timeout_ops=timeout_ops,
254
261
  )
255
262
  self._update_response(response)
@@ -264,6 +271,7 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
264
271
  failed_when_contains: Optional[Union[str, List[str]]] = None,
265
272
  stop_on_failed: bool = False,
266
273
  eager: bool = False,
274
+ eager_input: bool = False,
267
275
  timeout_ops: Optional[float] = None,
268
276
  ) -> MultiResponse:
269
277
  """
@@ -280,6 +288,8 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
280
288
  eager: if eager is True we do not read until prompt is seen at each command sent to the
281
289
  channel. Do *not* use this unless you know what you are doing as it is possible that
282
290
  it can make scrapli less reliable!
291
+ eager_input: when true does *not* try to read our input off the channel -- generally
292
+ this should be left alone unless you know what you are doing!
283
293
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
284
294
  the duration of the operation, value is reset to initial value after operation is
285
295
  completed. Note that this is the timeout value PER COMMAND sent, not for the total
@@ -303,6 +313,7 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
303
313
  failed_when_contains=failed_when_contains,
304
314
  stop_on_failed=stop_on_failed,
305
315
  eager=eager,
316
+ eager_input=eager_input,
306
317
  timeout_ops=timeout_ops,
307
318
  )
308
319
 
@@ -319,6 +330,7 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
319
330
  failed_when_contains: Optional[Union[str, List[str]]] = None,
320
331
  stop_on_failed: bool = False,
321
332
  eager: bool = False,
333
+ eager_input: bool = False,
322
334
  timeout_ops: Optional[float] = None,
323
335
  ) -> MultiResponse:
324
336
  """
@@ -333,6 +345,8 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
333
345
  eager: if eager is True we do not read until prompt is seen at each command sent to the
334
346
  channel. Do *not* use this unless you know what you are doing as it is possible that
335
347
  it can make scrapli less reliable!
348
+ eager_input: when true does *not* try to read our input off the channel -- generally
349
+ this should be left alone unless you know what you are doing!
336
350
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
337
351
  the duration of the operation, value is reset to initial value after operation is
338
352
  completed. Note that this is the timeout value PER COMMAND sent, not for the total
@@ -356,6 +370,7 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
356
370
  failed_when_contains=failed_when_contains,
357
371
  stop_on_failed=stop_on_failed,
358
372
  eager=eager,
373
+ eager_input=eager_input,
359
374
  timeout_ops=timeout_ops,
360
375
  )
361
376
 
@@ -477,6 +492,7 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
477
492
  stop_on_failed: bool = False,
478
493
  privilege_level: str = "",
479
494
  eager: bool = False,
495
+ eager_input: bool = False,
480
496
  timeout_ops: Optional[float] = None,
481
497
  ) -> MultiResponse:
482
498
  """
@@ -498,6 +514,8 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
498
514
  eager: if eager is True we do not read until prompt is seen at each command sent to the
499
515
  channel. Do *not* use this unless you know what you are doing as it is possible that
500
516
  it can make scrapli less reliable!
517
+ eager_input: when true does *not* try to read our input off the channel -- generally
518
+ this should be left alone unless you know what you are doing!
501
519
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
502
520
  the duration of the operation, value is reset to initial value after operation is
503
521
  completed. Note that this is the timeout value PER CONFIG sent, not for the total
@@ -525,6 +543,7 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
525
543
  failed_when_contains=failed_when_contains,
526
544
  stop_on_failed=stop_on_failed,
527
545
  eager=eager,
546
+ eager_input=eager_input,
528
547
  timeout_ops=timeout_ops,
529
548
  )
530
549
 
@@ -542,6 +561,7 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
542
561
  stop_on_failed: bool = False,
543
562
  privilege_level: str = "",
544
563
  eager: bool = False,
564
+ eager_input: bool = False,
545
565
  timeout_ops: Optional[float] = None,
546
566
  ) -> Response:
547
567
  """
@@ -563,6 +583,8 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
563
583
  eager: if eager is True we do not read until prompt is seen at each command sent to the
564
584
  channel. Do *not* use this unless you know what you are doing as it is possible that
565
585
  it can make scrapli less reliable!
586
+ eager_input: when true does *not* try to read our input off the channel -- generally
587
+ this should be left alone unless you know what you are doing!
566
588
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
567
589
  the duration of the operation, value is reset to initial value after operation is
568
590
  completed. Note that this is the timeout value PER CONFIG sent, not for the total
@@ -585,6 +607,7 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
585
607
  stop_on_failed=stop_on_failed,
586
608
  privilege_level=privilege_level,
587
609
  eager=eager,
610
+ eager_input=eager_input,
588
611
  timeout_ops=timeout_ops,
589
612
  )
590
613
  return self._post_send_config(config=config, multi_response=multi_response)
@@ -598,6 +621,7 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
598
621
  stop_on_failed: bool = False,
599
622
  privilege_level: str = "",
600
623
  eager: bool = False,
624
+ eager_input: bool = False,
601
625
  timeout_ops: Optional[float] = None,
602
626
  ) -> MultiResponse:
603
627
  """
@@ -619,6 +643,8 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
619
643
  eager: if eager is True we do not read until prompt is seen at each command sent to the
620
644
  channel. Do *not* use this unless you know what you are doing as it is possible that
621
645
  it can make scrapli less reliable!
646
+ eager_input: when true does *not* try to read our input off the channel -- generally
647
+ this should be left alone unless you know what you are doing!
622
648
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
623
649
  the duration of the operation, value is reset to initial value after operation is
624
650
  completed. Note that this is the timeout value PER CONFIG sent, not for the total
@@ -640,5 +666,6 @@ class NetworkDriver(GenericDriver, BaseNetworkDriver):
640
666
  stop_on_failed=stop_on_failed,
641
667
  privilege_level=privilege_level,
642
668
  eager=eager,
669
+ eager_input=eager_input,
643
670
  timeout_ops=timeout_ops,
644
671
  )
scrapli/exceptions.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """scrapli.exceptions"""
2
+
2
3
  from typing import Optional
3
4
 
4
5
 
scrapli/factory.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """scrapli.factory"""
2
+
2
3
  import importlib
3
4
  from copy import deepcopy
4
5
  from io import BytesIO
@@ -26,7 +27,7 @@ from scrapli.exceptions import (
26
27
  )
27
28
  from scrapli.helper import format_user_warning
28
29
  from scrapli.logging import logger
29
- from scrapli.transport import ASYNCIO_TRANSPORTS
30
+ from scrapli.transport import ASYNCIO_TRANSPORTS, CORE_TRANSPORTS
30
31
 
31
32
 
32
33
  def _build_provided_kwargs_dict( # pylint: disable=R0914
@@ -44,6 +45,7 @@ def _build_provided_kwargs_dict( # pylint: disable=R0914
44
45
  timeout_transport: Optional[float],
45
46
  timeout_ops: Optional[float],
46
47
  comms_return_char: Optional[str],
48
+ comms_roughly_match_inputs: Optional[bool],
47
49
  ssh_config_file: Optional[Union[str, bool]],
48
50
  ssh_known_hosts_file: Optional[Union[str, bool]],
49
51
  on_init: Optional[Callable[..., Any]],
@@ -97,6 +99,7 @@ def _build_provided_kwargs_dict( # pylint: disable=R0914
97
99
  "timeout_transport": timeout_transport,
98
100
  "timeout_ops": timeout_ops,
99
101
  "comms_return_char": comms_return_char,
102
+ "comms_roughly_match_inputs": comms_roughly_match_inputs,
100
103
  "ssh_config_file": ssh_config_file,
101
104
  "ssh_known_hosts_file": ssh_known_hosts_file,
102
105
  "on_init": on_init,
@@ -351,6 +354,7 @@ class Scrapli(NetworkDriver):
351
354
  timeout_transport: Optional[float] = None,
352
355
  timeout_ops: Optional[float] = None,
353
356
  comms_return_char: Optional[str] = None,
357
+ comms_roughly_match_inputs: Optional[bool] = None,
354
358
  ssh_config_file: Optional[Union[str, bool]] = None,
355
359
  ssh_known_hosts_file: Optional[Union[str, bool]] = None,
356
360
  on_init: Optional[Callable[..., Any]] = None,
@@ -388,6 +392,12 @@ class Scrapli(NetworkDriver):
388
392
  timeout_transport: timeout for ssh|telnet transport in seconds
389
393
  timeout_ops: timeout for ssh channel operations
390
394
  comms_return_char: character to use to send returns to host
395
+ comms_roughly_match_inputs: indicates if the channel should "roughly" match inputs sent
396
+ to the device. If False (default) inputs are strictly checked, as in any input
397
+ *must* be read back exactly on the channel. When set to True all input chars *must*
398
+ be read back in order in the output and all chars must be present, but the *exact*
399
+ input string does not need to be seen. This can be useful if a device echoes back
400
+ extra characters or rewrites the terminal during command input.
391
401
  ssh_config_file: string to path for ssh config file, True to use default ssh config file
392
402
  or False to ignore default ssh config file
393
403
  ssh_known_hosts_file: string to path for ssh known hosts file, True to use default known
@@ -455,7 +465,7 @@ class Scrapli(NetworkDriver):
455
465
  """
456
466
  logger.debug("Scrapli factory initialized")
457
467
 
458
- if transport in ASYNCIO_TRANSPORTS:
468
+ if transport in CORE_TRANSPORTS and transport in ASYNCIO_TRANSPORTS:
459
469
  raise ScrapliValueError("Use 'AsyncScrapli' if using an async transport!")
460
470
 
461
471
  if not isinstance(platform, str):
@@ -474,6 +484,7 @@ class Scrapli(NetworkDriver):
474
484
  timeout_transport=timeout_transport,
475
485
  timeout_ops=timeout_ops,
476
486
  comms_return_char=comms_return_char,
487
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
477
488
  ssh_config_file=ssh_config_file,
478
489
  ssh_known_hosts_file=ssh_known_hosts_file,
479
490
  on_init=on_init,
@@ -642,6 +653,7 @@ class AsyncScrapli(AsyncNetworkDriver):
642
653
  timeout_transport: Optional[float] = None,
643
654
  timeout_ops: Optional[float] = None,
644
655
  comms_return_char: Optional[str] = None,
656
+ comms_roughly_match_inputs: Optional[bool] = None,
645
657
  ssh_config_file: Optional[Union[str, bool]] = None,
646
658
  ssh_known_hosts_file: Optional[Union[str, bool]] = None,
647
659
  on_init: Optional[Callable[..., Any]] = None,
@@ -679,6 +691,12 @@ class AsyncScrapli(AsyncNetworkDriver):
679
691
  timeout_transport: timeout for ssh|telnet transport in seconds
680
692
  timeout_ops: timeout for ssh channel operations
681
693
  comms_return_char: character to use to send returns to host
694
+ comms_roughly_match_inputs: indicates if the channel should "roughly" match inputs sent
695
+ to the device. If False (default) inputs are strictly checked, as in any input
696
+ *must* be read back exactly on the channel. When set to True all input chars *must*
697
+ be read back in order in the output and all chars must be present, but the *exact*
698
+ input string does not need to be seen. This can be useful if a device echoes back
699
+ extra characters or rewrites the terminal during command input.
682
700
  ssh_config_file: string to path for ssh config file, True to use default ssh config file
683
701
  or False to ignore default ssh config file
684
702
  ssh_known_hosts_file: string to path for ssh known hosts file, True to use default known
@@ -746,7 +764,7 @@ class AsyncScrapli(AsyncNetworkDriver):
746
764
  """
747
765
  logger.debug("AsyncScrapli factory initialized")
748
766
 
749
- if transport not in ASYNCIO_TRANSPORTS:
767
+ if transport in CORE_TRANSPORTS and transport not in ASYNCIO_TRANSPORTS:
750
768
  raise ScrapliValueError("Use 'Scrapli' if using a synchronous transport!")
751
769
 
752
770
  if not isinstance(platform, str):
@@ -765,6 +783,7 @@ class AsyncScrapli(AsyncNetworkDriver):
765
783
  timeout_transport=timeout_transport,
766
784
  timeout_ops=timeout_ops,
767
785
  comms_return_char=comms_return_char,
786
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
768
787
  ssh_config_file=ssh_config_file,
769
788
  ssh_known_hosts_file=ssh_known_hosts_file,
770
789
  on_init=on_init,