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,11 +1,12 @@
1
1
  """scrapli.driver.base.sync_driver"""
2
+
2
3
  from types import TracebackType
3
4
  from typing import Any, Optional, Type, TypeVar
4
5
 
5
6
  from scrapli.channel import Channel
6
7
  from scrapli.driver.base.base_driver import BaseDriver
7
- from scrapli.exceptions import ScrapliValueError
8
- from scrapli.transport import ASYNCIO_TRANSPORTS
8
+ from scrapli.exceptions import ScrapliConnectionError, ScrapliValueError
9
+ from scrapli.transport import ASYNCIO_TRANSPORTS, CORE_TRANSPORTS
9
10
 
10
11
  _T = TypeVar("_T", bound="Driver")
11
12
 
@@ -14,7 +15,7 @@ class Driver(BaseDriver):
14
15
  def __init__(self, **kwargs: Any):
15
16
  super().__init__(**kwargs)
16
17
 
17
- if self.transport_name in ASYNCIO_TRANSPORTS:
18
+ if self.transport_name in CORE_TRANSPORTS and self.transport_name in ASYNCIO_TRANSPORTS:
18
19
  raise ScrapliValueError(
19
20
  "provided transport is *not* an sync transport, must use an sync transport with"
20
21
  " the (sync)Driver(s)"
@@ -41,10 +42,22 @@ class Driver(BaseDriver):
41
42
  _T: a concrete implementation of the opened Driver object
42
43
 
43
44
  Raises:
44
- N/A
45
+ ScrapliConnectionError: if an exception occurs during opening
45
46
 
46
47
  """
47
- self.open()
48
+ try:
49
+ self.open()
50
+ except Exception as exc:
51
+ self.logger.critical(
52
+ "encountered exception during open in context manager, "
53
+ "attempting to close transport and channel"
54
+ )
55
+
56
+ self.transport.close()
57
+ self.channel.close()
58
+
59
+ raise ScrapliConnectionError(exc) from exc
60
+
48
61
  return self
49
62
 
50
63
  def __exit__(
@@ -94,14 +107,7 @@ class Driver(BaseDriver):
94
107
  auth_password=self.auth_password,
95
108
  auth_private_key_passphrase=self.auth_private_key_passphrase,
96
109
  )
97
- if (
98
- self.transport_name
99
- in (
100
- "telnet",
101
- "asynctelnet",
102
- )
103
- and not self.auth_bypass
104
- ):
110
+ if "telnet" in self.transport_name and not self.auth_bypass:
105
111
  self.channel.channel_authenticate_telnet(
106
112
  auth_username=self.auth_username, auth_password=self.auth_password
107
113
  )
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core"""
2
+
2
3
  from scrapli.driver.core.arista_eos import AsyncEOSDriver, EOSDriver
3
4
  from scrapli.driver.core.cisco_iosxe import AsyncIOSXEDriver, IOSXEDriver
4
5
  from scrapli.driver.core.cisco_iosxr import AsyncIOSXRDriver, IOSXRDriver
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.arista_eos"""
2
+
2
3
  from scrapli.driver.core.arista_eos.async_driver import AsyncEOSDriver
3
4
  from scrapli.driver.core.arista_eos.sync_driver import EOSDriver
4
5
 
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.arista_eos.async_driver"""
2
+
2
3
  from copy import deepcopy
3
4
  from io import BytesIO
4
5
  from typing import Any, Callable, Dict, List, Optional, Union
@@ -66,6 +67,7 @@ class AsyncEOSDriver(AsyncNetworkDriver, EOSDriverBase):
66
67
  timeout_transport: float = 30.0,
67
68
  timeout_ops: float = 30.0,
68
69
  comms_return_char: str = "\n",
70
+ comms_roughly_match_inputs: bool = False,
69
71
  ssh_config_file: Union[str, bool] = False,
70
72
  ssh_known_hosts_file: Union[str, bool] = False,
71
73
  on_init: Optional[Callable[..., Any]] = None,
@@ -146,6 +148,7 @@ class AsyncEOSDriver(AsyncNetworkDriver, EOSDriverBase):
146
148
  timeout_transport=timeout_transport,
147
149
  timeout_ops=timeout_ops,
148
150
  comms_return_char=comms_return_char,
151
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
149
152
  ssh_config_file=ssh_config_file,
150
153
  ssh_known_hosts_file=ssh_known_hosts_file,
151
154
  on_init=on_init,
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.arista_eos.base_driver"""
2
+
2
3
  import re
3
4
  from typing import Dict
4
5
 
@@ -31,7 +32,7 @@ PRIVS = {
31
32
  ),
32
33
  "configuration": (
33
34
  PrivilegeLevel(
34
- pattern=r"^[\w.\-@()/: ]{1,63}\(config[\w.\-@/:]{0,32}\)#\s?$",
35
+ pattern=r"^[\w.\-@()/: ]{1,63}\(config[\w.\-@/:]{0,63}\)#\s?$",
35
36
  name="configuration",
36
37
  previous_priv="privilege_exec",
37
38
  deescalate="end",
@@ -79,7 +80,7 @@ class EOSDriverBase:
79
80
  raise ScrapliValueError(msg)
80
81
  sess_prompt = re.escape(session_name[:6])
81
82
  pattern = (
82
- rf"^[a-z0-9.\-@()/: ]{{1,63}}\(config\-s\-{sess_prompt}[a-z0-9_.\-@/:]{{0,32}}\)#\s?$"
83
+ rf"^[a-z0-9.\-@()/: ]{{1,63}}\(config\-s\-{sess_prompt}[a-z0-9_.\-@/:]{{0,64}}\)#\s?$"
83
84
  )
84
85
  name = session_name
85
86
  config_session = PrivilegeLevel(
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.arista_eos.sync_driver"""
2
+
2
3
  from copy import deepcopy
3
4
  from io import BytesIO
4
5
  from typing import Any, Callable, Dict, List, Optional, Union
@@ -66,6 +67,7 @@ class EOSDriver(NetworkDriver, EOSDriverBase):
66
67
  timeout_transport: float = 30.0,
67
68
  timeout_ops: float = 30.0,
68
69
  comms_return_char: str = "\n",
70
+ comms_roughly_match_inputs: bool = False,
69
71
  ssh_config_file: Union[str, bool] = False,
70
72
  ssh_known_hosts_file: Union[str, bool] = False,
71
73
  on_init: Optional[Callable[..., Any]] = None,
@@ -146,6 +148,7 @@ class EOSDriver(NetworkDriver, EOSDriverBase):
146
148
  timeout_transport=timeout_transport,
147
149
  timeout_ops=timeout_ops,
148
150
  comms_return_char=comms_return_char,
151
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
149
152
  ssh_config_file=ssh_config_file,
150
153
  ssh_known_hosts_file=ssh_known_hosts_file,
151
154
  on_init=on_init,
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.cisco_iosxe"""
2
+
2
3
  from scrapli.driver.core.cisco_iosxe.async_driver import AsyncIOSXEDriver
3
4
  from scrapli.driver.core.cisco_iosxe.sync_driver import IOSXEDriver
4
5
 
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.cisco_iosxe.async_driver"""
2
+
2
3
  from copy import deepcopy
3
4
  from io import BytesIO
4
5
  from typing import Any, Callable, Dict, List, Optional, Union
@@ -66,6 +67,7 @@ class AsyncIOSXEDriver(AsyncNetworkDriver):
66
67
  timeout_transport: float = 30.0,
67
68
  timeout_ops: float = 30.0,
68
69
  comms_return_char: str = "\n",
70
+ comms_roughly_match_inputs: bool = False,
69
71
  ssh_config_file: Union[str, bool] = False,
70
72
  ssh_known_hosts_file: Union[str, bool] = False,
71
73
  on_init: Optional[Callable[..., Any]] = None,
@@ -143,6 +145,7 @@ class AsyncIOSXEDriver(AsyncNetworkDriver):
143
145
  timeout_transport=timeout_transport,
144
146
  timeout_ops=timeout_ops,
145
147
  comms_return_char=comms_return_char,
148
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
146
149
  ssh_config_file=ssh_config_file,
147
150
  ssh_known_hosts_file=ssh_known_hosts_file,
148
151
  on_init=on_init,
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.cisco_iosxe.base_driver"""
2
+
2
3
  from scrapli.driver.network.base_driver import PrivilegeLevel
3
4
 
4
5
  PRIVS = {
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.cisco_iosxe.sync_driver"""
2
+
2
3
  from copy import deepcopy
3
4
  from io import BytesIO
4
5
  from typing import Any, Callable, Dict, List, Optional, Union
@@ -66,6 +67,7 @@ class IOSXEDriver(NetworkDriver):
66
67
  timeout_transport: float = 30.0,
67
68
  timeout_ops: float = 30.0,
68
69
  comms_return_char: str = "\n",
70
+ comms_roughly_match_inputs: bool = False,
69
71
  ssh_config_file: Union[str, bool] = False,
70
72
  ssh_known_hosts_file: Union[str, bool] = False,
71
73
  on_init: Optional[Callable[..., Any]] = None,
@@ -143,6 +145,7 @@ class IOSXEDriver(NetworkDriver):
143
145
  timeout_transport=timeout_transport,
144
146
  timeout_ops=timeout_ops,
145
147
  comms_return_char=comms_return_char,
148
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
146
149
  ssh_config_file=ssh_config_file,
147
150
  ssh_known_hosts_file=ssh_known_hosts_file,
148
151
  on_init=on_init,
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.cisco_iosxr"""
2
+
2
3
  from scrapli.driver.core.cisco_iosxr.async_driver import AsyncIOSXRDriver
3
4
  from scrapli.driver.core.cisco_iosxr.sync_driver import IOSXRDriver
4
5
 
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.cisco_iosxr.async_driver"""
2
+
2
3
  from copy import deepcopy
3
4
  from io import BytesIO
4
5
  from typing import Any, Callable, Dict, List, Optional, Union
@@ -65,6 +66,7 @@ class AsyncIOSXRDriver(AsyncNetworkDriver):
65
66
  timeout_transport: float = 30.0,
66
67
  timeout_ops: float = 30.0,
67
68
  comms_return_char: str = "\n",
69
+ comms_roughly_match_inputs: bool = False,
68
70
  ssh_config_file: Union[str, bool] = False,
69
71
  ssh_known_hosts_file: Union[str, bool] = False,
70
72
  on_init: Optional[Callable[..., Any]] = None,
@@ -142,6 +144,7 @@ class AsyncIOSXRDriver(AsyncNetworkDriver):
142
144
  timeout_transport=timeout_transport,
143
145
  timeout_ops=timeout_ops,
144
146
  comms_return_char=comms_return_char,
147
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
145
148
  ssh_config_file=ssh_config_file,
146
149
  ssh_known_hosts_file=ssh_known_hosts_file,
147
150
  on_init=on_init,
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.cisco_iosxr.base_driver"""
2
+
2
3
  from scrapli.driver.network.base_driver import PrivilegeLevel
3
4
 
4
5
  PRIVS = {
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.cisco_iosxr.sync_driver"""
2
+
2
3
  from copy import deepcopy
3
4
  from io import BytesIO
4
5
  from typing import Any, Callable, Dict, List, Optional, Union
@@ -68,6 +69,7 @@ class IOSXRDriver(NetworkDriver):
68
69
  timeout_transport: float = 30.0,
69
70
  timeout_ops: float = 30.0,
70
71
  comms_return_char: str = "\n",
72
+ comms_roughly_match_inputs: bool = False,
71
73
  ssh_config_file: Union[str, bool] = False,
72
74
  ssh_known_hosts_file: Union[str, bool] = False,
73
75
  on_init: Optional[Callable[..., Any]] = None,
@@ -145,6 +147,7 @@ class IOSXRDriver(NetworkDriver):
145
147
  timeout_transport=timeout_transport,
146
148
  timeout_ops=timeout_ops,
147
149
  comms_return_char=comms_return_char,
150
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
148
151
  ssh_config_file=ssh_config_file,
149
152
  ssh_known_hosts_file=ssh_known_hosts_file,
150
153
  on_init=on_init,
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.cisco_nxos"""
2
+
2
3
  from scrapli.driver.core.cisco_nxos.async_driver import AsyncNXOSDriver
3
4
  from scrapli.driver.core.cisco_nxos.sync_driver import NXOSDriver
4
5
 
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.cisco_nxos.async_driver"""
2
+
2
3
  from copy import deepcopy
3
4
  from io import BytesIO
4
5
  from typing import Any, Callable, Dict, List, Optional, Union
@@ -66,6 +67,7 @@ class AsyncNXOSDriver(AsyncNetworkDriver, NXOSDriverBase):
66
67
  timeout_transport: float = 30.0,
67
68
  timeout_ops: float = 30.0,
68
69
  comms_return_char: str = "\n",
70
+ comms_roughly_match_inputs: bool = False,
69
71
  ssh_config_file: Union[str, bool] = False,
70
72
  ssh_known_hosts_file: Union[str, bool] = False,
71
73
  on_init: Optional[Callable[..., Any]] = None,
@@ -146,6 +148,7 @@ class AsyncNXOSDriver(AsyncNetworkDriver, NXOSDriverBase):
146
148
  timeout_transport=timeout_transport,
147
149
  timeout_ops=timeout_ops,
148
150
  comms_return_char=comms_return_char,
151
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
149
152
  ssh_config_file=ssh_config_file,
150
153
  ssh_known_hosts_file=ssh_known_hosts_file,
151
154
  on_init=on_init,
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.cisco_nxos.base_driver"""
2
+
2
3
  from typing import Dict
3
4
 
4
5
  from scrapli.driver.network.base_driver import PrivilegeLevel
@@ -7,7 +8,7 @@ from scrapli.exceptions import ScrapliValueError
7
8
  PRIVS = {
8
9
  "exec": (
9
10
  PrivilegeLevel(
10
- pattern=r"^[\w.\-]{1,63}>\s?$",
11
+ pattern=r"^[\w.\-]{1,63}(\(maint\-mode\))?>\s?$",
11
12
  name="exec",
12
13
  previous_priv="",
13
14
  deescalate="",
@@ -18,7 +19,7 @@ PRIVS = {
18
19
  ),
19
20
  "privilege_exec": (
20
21
  PrivilegeLevel(
21
- pattern=r"^[\w.\-]{1,63}#\s?$",
22
+ pattern=r"^[\w.\-]{1,63}(\(maint\-mode\))?#\s?$",
22
23
  name="privilege_exec",
23
24
  previous_priv="exec",
24
25
  deescalate="disable",
@@ -30,7 +31,7 @@ PRIVS = {
30
31
  ),
31
32
  "configuration": (
32
33
  PrivilegeLevel(
33
- pattern=r"^[\w.\-]{1,63}\(config[\w.\-@/:\+]{0,32}\)#\s?$",
34
+ pattern=r"^[\w.\-]{1,63}(\(maint\-mode\))?\(config[\w.\-@/:\+]{0,32}\)#\s?$",
34
35
  name="configuration",
35
36
  previous_priv="privilege_exec",
36
37
  deescalate="end",
@@ -46,7 +47,11 @@ PRIVS = {
46
47
  # for now doesnt seem to be a reason to differentiate between them, so just have one
47
48
  # giant pattern
48
49
  pattern=(
49
- r"(^[\w.\-]{1,63}\-tcl#\s?$)|" r"(^[\w.\-]{1,63}\(config\-tcl\)#\s?$)|" r"(^>\s?$)"
50
+ r"(^[\w.\-]{1,63}\-tcl#\s?$)|"
51
+ r"(^[\w.\-]{1,63}\(config\-tcl\)#\s?$)|"
52
+ r"(^>\s?$)|"
53
+ r"(^[\w.\-]{1,63}\(maint\-mode\-tcl\)#\s?$)|"
54
+ r"(^[\w.\-]{1,63}\(maint\-mode\)\(config\-tcl\)#\s?$)"
50
55
  ),
51
56
  name="tclsh",
52
57
  previous_priv="privilege_exec",
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.cisco_nxos.sync_driver"""
2
+
2
3
  from copy import deepcopy
3
4
  from io import BytesIO
4
5
  from typing import Any, Callable, Dict, List, Optional, Union
@@ -66,6 +67,7 @@ class NXOSDriver(NetworkDriver, NXOSDriverBase):
66
67
  timeout_transport: float = 30.0,
67
68
  timeout_ops: float = 30.0,
68
69
  comms_return_char: str = "\n",
70
+ comms_roughly_match_inputs: bool = False,
69
71
  ssh_config_file: Union[str, bool] = False,
70
72
  ssh_known_hosts_file: Union[str, bool] = False,
71
73
  on_init: Optional[Callable[..., Any]] = None,
@@ -146,6 +148,7 @@ class NXOSDriver(NetworkDriver, NXOSDriverBase):
146
148
  timeout_transport=timeout_transport,
147
149
  timeout_ops=timeout_ops,
148
150
  comms_return_char=comms_return_char,
151
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
149
152
  ssh_config_file=ssh_config_file,
150
153
  ssh_known_hosts_file=ssh_known_hosts_file,
151
154
  on_init=on_init,
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.juniper_junos"""
2
+
2
3
  from scrapli.driver.core.juniper_junos.async_driver import AsyncJunosDriver
3
4
  from scrapli.driver.core.juniper_junos.sync_driver import JunosDriver
4
5
 
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.juniper_junos.async_driver"""
2
+
2
3
  from copy import deepcopy
3
4
  from io import BytesIO
4
5
  from typing import Any, Callable, Dict, List, Optional, Union
@@ -67,6 +68,7 @@ class AsyncJunosDriver(AsyncNetworkDriver):
67
68
  timeout_transport: float = 30.0,
68
69
  timeout_ops: float = 30.0,
69
70
  comms_return_char: str = "\n",
71
+ comms_roughly_match_inputs: bool = False,
70
72
  ssh_config_file: Union[str, bool] = False,
71
73
  ssh_known_hosts_file: Union[str, bool] = False,
72
74
  on_init: Optional[Callable[..., Any]] = None,
@@ -144,6 +146,7 @@ class AsyncJunosDriver(AsyncNetworkDriver):
144
146
  timeout_transport=timeout_transport,
145
147
  timeout_ops=timeout_ops,
146
148
  comms_return_char=comms_return_char,
149
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
147
150
  ssh_config_file=ssh_config_file,
148
151
  ssh_known_hosts_file=ssh_known_hosts_file,
149
152
  on_init=on_init,
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.juniper_junos.base_driver"""
2
+
2
3
  from scrapli.driver.network.base_driver import PrivilegeLevel
3
4
 
4
5
  PRIVS = {
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.core.juniper_junos.driver"""
2
+
2
3
  from copy import deepcopy
3
4
  from io import BytesIO
4
5
  from typing import Any, Callable, Dict, List, Optional, Union
@@ -67,6 +68,7 @@ class JunosDriver(NetworkDriver):
67
68
  timeout_transport: float = 30.0,
68
69
  timeout_ops: float = 30.0,
69
70
  comms_return_char: str = "\n",
71
+ comms_roughly_match_inputs: bool = False,
70
72
  ssh_config_file: Union[str, bool] = False,
71
73
  ssh_known_hosts_file: Union[str, bool] = False,
72
74
  on_init: Optional[Callable[..., Any]] = None,
@@ -144,6 +146,7 @@ class JunosDriver(NetworkDriver):
144
146
  timeout_transport=timeout_transport,
145
147
  timeout_ops=timeout_ops,
146
148
  comms_return_char=comms_return_char,
149
+ comms_roughly_match_inputs=comms_roughly_match_inputs,
147
150
  ssh_config_file=ssh_config_file,
148
151
  ssh_known_hosts_file=ssh_known_hosts_file,
149
152
  on_init=on_init,
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.generic"""
2
+
2
3
  from scrapli.driver.generic.async_driver import AsyncGenericDriver
3
4
  from scrapli.driver.generic.base_driver import ReadCallback
4
5
  from scrapli.driver.generic.sync_driver import GenericDriver
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.generic.async_driver"""
2
+
2
3
  import asyncio
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
+ async def generic_on_open(conn: "AsyncGenericDriver") -> 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
+ await conn.get_prompt()
35
+
36
+
19
37
  class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
20
38
  def __init__(
21
39
  self,
@@ -35,10 +53,11 @@ class AsyncGenericDriver(AsyncDriver, 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 AsyncGenericDriver(AsyncDriver, 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,
@@ -101,6 +121,7 @@ class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
101
121
  strip_prompt: bool = True,
102
122
  failed_when_contains: Optional[Union[str, List[str]]] = None,
103
123
  eager: bool = False,
124
+ eager_input: bool = False,
104
125
  timeout_ops: Optional[float] = None,
105
126
  ) -> Response:
106
127
  """
@@ -117,6 +138,8 @@ class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
117
138
  eager: if eager is True we do not read until prompt is seen at each command sent to the
118
139
  channel. Do *not* use this unless you know what you are doing as it is possible that
119
140
  it can make scrapli less reliable!
141
+ eager_input: when true does *not* try to read our input off the channel -- generally
142
+ this should be left alone unless you know what you are doing!
120
143
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
121
144
  the duration of the operation, value is reset to initial value after operation is
122
145
  completed
@@ -141,7 +164,7 @@ class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
141
164
  failed_when_contains=failed_when_contains,
142
165
  )
143
166
  raw_response, processed_response = await self.channel.send_input(
144
- channel_input=command, strip_prompt=strip_prompt, eager=eager
167
+ channel_input=command, strip_prompt=strip_prompt, eager=eager, eager_input=eager_input
145
168
  )
146
169
  return self._post_send_command(
147
170
  raw_response=raw_response, processed_response=processed_response, response=response
@@ -153,6 +176,7 @@ class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
153
176
  *,
154
177
  strip_prompt: bool = True,
155
178
  failed_when_contains: Optional[Union[str, List[str]]] = None,
179
+ eager_input: bool = False,
156
180
  timeout_ops: Optional[float] = None,
157
181
  ) -> Response:
158
182
  """
@@ -162,6 +186,8 @@ class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
162
186
  command: string to send to device in privilege exec mode
163
187
  strip_prompt: strip prompt or not, defaults to True (yes, strip the prompt)
164
188
  failed_when_contains: string or list of strings indicating failure if found in response
189
+ eager_input: when true does *not* try to read our input off the channel -- generally
190
+ this should be left alone unless you know what you are doing!
165
191
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
166
192
  the duration of the operation, value is reset to initial value after operation is
167
193
  completed
@@ -177,6 +203,7 @@ class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
177
203
  command=command,
178
204
  strip_prompt=strip_prompt,
179
205
  failed_when_contains=failed_when_contains,
206
+ eager_input=eager_input,
180
207
  timeout_ops=timeout_ops,
181
208
  )
182
209
  return response
@@ -189,6 +216,7 @@ class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
189
216
  failed_when_contains: Optional[Union[str, List[str]]] = None,
190
217
  stop_on_failed: bool = False,
191
218
  eager: bool = False,
219
+ eager_input: bool = False,
192
220
  timeout_ops: Optional[float] = None,
193
221
  ) -> MultiResponse:
194
222
  """
@@ -203,6 +231,8 @@ class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
203
231
  eager: if eager is True we do not read until prompt is seen at each command sent to the
204
232
  channel. Do *not* use this unless you know what you are doing as it is possible that
205
233
  it can make scrapli less reliable!
234
+ eager_input: when true does *not* try to read our input off the channel -- generally
235
+ this should be left alone unless you know what you are doing!
206
236
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
207
237
  the duration of the operation, value is reset to initial value after operation is
208
238
  completed. Note that this is the timeout value PER COMMAND sent, not for the total
@@ -223,6 +253,7 @@ class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
223
253
  failed_when_contains=failed_when_contains,
224
254
  timeout_ops=timeout_ops,
225
255
  eager=eager,
256
+ eager_input=eager_input,
226
257
  )
227
258
  responses.append(response)
228
259
  if stop_on_failed and response.failed is True:
@@ -238,6 +269,7 @@ class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
238
269
  failed_when_contains=failed_when_contains,
239
270
  timeout_ops=timeout_ops,
240
271
  eager=False,
272
+ eager_input=eager_input,
241
273
  )
242
274
  responses.append(response)
243
275
 
@@ -251,6 +283,7 @@ class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
251
283
  failed_when_contains: Optional[Union[str, List[str]]] = None,
252
284
  stop_on_failed: bool = False,
253
285
  eager: bool = False,
286
+ eager_input: bool = False,
254
287
  timeout_ops: Optional[float] = None,
255
288
  ) -> MultiResponse:
256
289
  """
@@ -265,6 +298,8 @@ class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
265
298
  eager: if eager is True we do not read until prompt is seen at each command sent to the
266
299
  channel. Do *not* use this unless you know what you are doing as it is possible that
267
300
  it can make scrapli less reliable!
301
+ eager_input: when true does *not* try to read our input off the channel -- generally
302
+ this should be left alone unless you know what you are doing!
268
303
  timeout_ops: timeout ops value for this operation; only sets the timeout_ops value for
269
304
  the duration of the operation, value is reset to initial value after operation is
270
305
  completed. Note that this is the timeout value PER COMMAND sent, not for the total
@@ -285,6 +320,7 @@ class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
285
320
  failed_when_contains=failed_when_contains,
286
321
  stop_on_failed=stop_on_failed,
287
322
  eager=eager,
323
+ eager_input=eager_input,
288
324
  timeout_ops=timeout_ops,
289
325
  )
290
326
 
@@ -530,7 +566,13 @@ class AsyncGenericDriver(AsyncDriver, BaseGenericDriver):
530
566
  """
531
567
  if initial_input is not None:
532
568
  self.channel.write(channel_input=f"{initial_input}{self.comms_return_char}")
533
- return await self.read_callback(callbacks=callbacks, initial_input=None)
569
+ return await self.read_callback(
570
+ callbacks=callbacks,
571
+ initial_input=None,
572
+ read_output=read_output,
573
+ read_delay=read_delay,
574
+ read_timeout=read_timeout,
575
+ )
534
576
 
535
577
  original_transport_timeout = self.timeout_transport
536
578
 
@@ -1,4 +1,5 @@
1
1
  """scrapli.driver.generic.base_driver"""
2
+
2
3
  import re
3
4
  from typing import (
4
5
  TYPE_CHECKING,
@@ -337,7 +338,7 @@ class BaseGenericDriver:
337
338
  raise ScrapliTypeError(f"`{caller}` expects a string path to a file, got {type(file)}")
338
339
  resolved_file = resolve_file(file)
339
340
 
340
- with open(resolved_file, "r", encoding="utf-8") as f:
341
+ with open(resolved_file, encoding="utf-8") as f:
341
342
  commands = f.read().splitlines()
342
343
 
343
344
  return commands