We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
cryptography 3.1 library breaks tplink drivers
I've just updated to netmiko 3.3.2 from 3.3.0, and paramiko 2.7.2 before and after the netmiko upgrade.  My custom ssh driver worked fine with netmiko 3.3.0. The stack trace is interesting for a couple reasons:

The actual error "AttributeError: module 'cryptography.utils' has no attribute 'bit_length'".  Looking back through the stack trace, there's a "raise e" from paramiko/transport.py line 660 in start_client().  The reported error is the actual exception raised at that point.

I don't get how the code goes from raising that exception and ends up in the new tplink driver.

I think I've installed all the latest module versions using pip to recreate the environment.

Debugging ideas are much appreciated!

```
Traceback (most recent call last):
(...deleted portion of stack trace...)
 File "/usr/local/lib/python3.6/site-packages/netmiko/ssh_dispatcher.py, line 324, in ConnectHandler
   return ConnectionClass(*args, **kwargs)
 File "/usr/local/lib/python3.6/site-packages/netmiko/<vendor>/<driverfile>, line 189, in __init__
   self.client.connect(self.host, port=22, username=self.username, password=self.password, look_for_keys=False, timeout=self.timeout)
 File "/usr/local/lib/python3.6/site-packages/paramiko/client.py, line 406, in connect
   t.start_client(timeout=timeout)
 File "/usr/local/lib/python3.6/site-packages/paramiko/transport.py, line 660, in start_client
   raise e
 File "/usr/local/lib/python3.6/site-packages/paramiko/transport.py, line 2075, in run
   self.kex_engine.parse_next(ptype, m)
 File "/usr/local/lib/python3.6/site-packages/paramiko/kex_group1.py line 75, in parse_next
   return self._parse_kexdh_reply(m)
 File "/usr/local/lib/python3.6/site-packages/paramiko/kex_group1.py line 120, in _parse_kexdh_reply
   self.transport._verify_key(host_key, sig)
 File "/usr/local/lib/python3.6/site-packages/paramiko/transport.py, line 1886, in _verify_key
   if not key.verify_ssh_sig(self.H, Message(sig)):
 File "/usr/local/lib/python3.6/site-packages/paramiko/dsskey.py, line 153, in verify_ssh_sig
   ).public_key(backend=default_backend())
 File "/usr/local/lib64/python3.6/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py, line 212, in public_key
   return backend.load_dsa_public_numbers(self)
 File "/usr/local/lib64/python3.6/site-packages/cryptography/hazmat/backends/openssl/backend.py, line 873, in load_dsa_public_numbers
   dsa._check_dsa_parameters(numbers.parameter_numbers)
 File "/usr/local/lib/python3.6/site-packages/netmiko/tplink/tplink_jetstream.py, line 145, in _override_check_dsa_parameters
   if crypto_utils.bit_length(parameters.q) not in [160, 256]:
AttributeError: module 'cryptography.utils' has no attribute 'bit_length'
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `netmiko/tplink/tplink_jetstream.py`
Content:
```
1 import re
2 import time
3 
4 from cryptography import utils as crypto_utils
5 from cryptography.hazmat.primitives.asymmetric import dsa
6 
7 from netmiko import log
8 from netmiko.cisco_base_connection import CiscoSSHConnection
9 from netmiko.ssh_exception import NetmikoTimeoutException
10 
11 
12 class TPLinkJetStreamBase(CiscoSSHConnection):
13     def __init__(self, **kwargs):
14         # TP-Link doesn't have a way to set terminal width which breaks cmd_verify
15         if kwargs.get("global_cmd_verify") is None:
16             kwargs["global_cmd_verify"] = False
17         # TP-Link uses "\r\n" as default_enter for SSH and Telnet
18         if kwargs.get("default_enter") is None:
19             kwargs["default_enter"] = "\r\n"
20         return super().__init__(**kwargs)
21 
22     def session_preparation(self):
23         """
24         Prepare the session after the connection has been established.
25         """
26         delay_factor = self.select_delay_factor(delay_factor=0)
27         time.sleep(0.3 * delay_factor)
28         self.clear_buffer()
29         self._test_channel_read(pattern=r"[>#]")
30         self.set_base_prompt()
31         self.enable()
32         self.disable_paging()
33         # Clear the read buffer
34         time.sleep(0.3 * self.global_delay_factor)
35         self.clear_buffer()
36 
37     def enable(self, cmd="", pattern="ssword", re_flags=re.IGNORECASE):
38         """
39         TPLink JetStream requires you to first execute "enable" and then execute "enable-admin".
40         This is necessary as "configure" is generally only available at "enable-admin" level
41 
42         If the user does not have the Admin role, he will need to execute enable-admin to really
43         enable all functions.
44         """
45 
46         # If end-user passes in "cmd" execute that using normal process.
47         if cmd:
48             return super().enable(cmd=cmd, pattern=pattern, re_flags=re_flags)
49 
50         output = ""
51         msg = (
52             "Failed to enter enable mode. Please ensure you pass "
53             "the 'secret' argument to ConnectHandler."
54         )
55 
56         cmds = ["enable", "enable-admin"]
57         if not self.check_enable_mode():
58             for cmd in cmds:
59                 self.write_channel(self.normalize_cmd(cmd))
60                 try:
61                     output += self.read_until_prompt_or_pattern(
62                         pattern=pattern, re_flags=re_flags
63                     )
64                     self.write_channel(self.normalize_cmd(self.secret))
65                     output += self.read_until_prompt()
66                 except NetmikoTimeoutException:
67                     raise ValueError(msg)
68                 if not self.check_enable_mode():
69                     raise ValueError(msg)
70         return output
71 
72     def config_mode(self, config_command="configure"):
73         """Enter configuration mode."""
74         return super().config_mode(config_command=config_command)
75 
76     def exit_config_mode(self, exit_config="exit", pattern=r"#"):
77         """
78         Exit config mode.
79 
80         Like the Mellanox equipment, the TP-Link Jetstream does not
81         support a single command to completely exit the configuration mode.
82 
83         Consequently, need to keep checking and sending "exit".
84         """
85         output = ""
86         check_count = 12
87         while check_count >= 0:
88             if self.check_config_mode():
89                 self.write_channel(self.normalize_cmd(exit_config))
90                 output += self.read_until_pattern(pattern=pattern)
91             else:
92                 break
93             check_count -= 1
94 
95         if self.check_config_mode():
96             raise ValueError("Failed to exit configuration mode")
97             log.debug(f"exit_config_mode: {output}")
98 
99         return output
100 
101     def check_config_mode(self, check_string="(config", pattern=r"#"):
102         """Check whether device is in configuration mode. Return a boolean."""
103         return super().check_config_mode(check_string=check_string, pattern=pattern)
104 
105     def set_base_prompt(
106         self, pri_prompt_terminator=">", alt_prompt_terminator="#", delay_factor=1
107     ):
108         """
109         Sets self.base_prompt
110 
111         Used as delimiter for stripping of trailing prompt in output.
112 
113         Should be set to something that is general and applies in multiple
114         contexts. For TP-Link this will be the router prompt with > or #
115         stripped off.
116 
117         This will be set on logging in, but not when entering system-view
118         """
119         return super().set_base_prompt(
120             pri_prompt_terminator=pri_prompt_terminator,
121             alt_prompt_terminator=alt_prompt_terminator,
122             delay_factor=delay_factor,
123         )
124 
125 
126 class TPLinkJetStreamSSH(TPLinkJetStreamBase):
127     def _override_check_dsa_parameters(parameters):
128         """
129         Override check_dsa_parameters from cryptography's dsa.py
130 
131         Without this the error below occurs:
132 
133         ValueError: p must be exactly 1024, 2048, or 3072 bits long
134 
135         Allows for shorter or longer parameters.p to be returned
136         from the server's host key. This is a HORRIBLE hack and a
137         security risk, please remove if possible!
138 
139         By now, with firmware:
140 
141         2.0.5 Build 20200109 Rel.36203(s)
142 
143         It's still not possible to remove this hack.
144         """
145         if crypto_utils.bit_length(parameters.q) not in [160, 256]:
146             raise ValueError("q must be exactly 160 or 256 bits long")
147 
148         if not (1 < parameters.g < parameters.p):
149             raise ValueError("g, p don't satisfy 1 < g < p.")
150 
151     dsa._check_dsa_parameters = _override_check_dsa_parameters
152 
153 
154 class TPLinkJetStreamTelnet(TPLinkJetStreamBase):
155     def telnet_login(
156         self,
157         pri_prompt_terminator="#",
158         alt_prompt_terminator=">",
159         username_pattern=r"User:",
160         pwd_pattern=r"Password:",
161         delay_factor=1,
162         max_loops=60,
163     ):
164         """Telnet login: can be username/password or just password."""
165         super().telnet_login(
166             pri_prompt_terminator=pri_prompt_terminator,
167             alt_prompt_terminator=alt_prompt_terminator,
168             username_pattern=username_pattern,
169             pwd_pattern=pwd_pattern,
170             delay_factor=delay_factor,
171             max_loops=max_loops,
172         )
173
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/netmiko/tplink/tplink_jetstream.py b/netmiko/tplink/tplink_jetstream.py
--- a/netmiko/tplink/tplink_jetstream.py
+++ b/netmiko/tplink/tplink_jetstream.py
@@ -1,7 +1,6 @@
 import re
 import time
 
-from cryptography import utils as crypto_utils
 from cryptography.hazmat.primitives.asymmetric import dsa
 
 from netmiko import log
@@ -142,7 +141,7 @@
 
         It's still not possible to remove this hack.
         """
-        if crypto_utils.bit_length(parameters.q) not in [160, 256]:
+        if parameters.q.bit_length() not in [160, 256]:
             raise ValueError("q must be exactly 160 or 256 bits long")
 
         if not (1 < parameters.g < parameters.p):

{"golden_diff": "diff --git a/netmiko/tplink/tplink_jetstream.py b/netmiko/tplink/tplink_jetstream.py\n--- a/netmiko/tplink/tplink_jetstream.py\n+++ b/netmiko/tplink/tplink_jetstream.py\n@@ -1,7 +1,6 @@\n import re\n import time\n \n-from cryptography import utils as crypto_utils\n from cryptography.hazmat.primitives.asymmetric import dsa\n \n from netmiko import log\n@@ -142,7 +141,7 @@\n \n         It's still not possible to remove this hack.\n         \"\"\"\n-        if crypto_utils.bit_length(parameters.q) not in [160, 256]:\n+        if parameters.q.bit_length() not in [160, 256]:\n             raise ValueError(\"q must be exactly 160 or 256 bits long\")\n \n         if not (1 < parameters.g < parameters.p):\n", "issue": "cryptography 3.1 library breaks tplink drivers\nI've just updated to netmiko 3.3.2 from 3.3.0, and paramiko 2.7.2 before and after the netmiko upgrade.  My custom ssh driver worked fine with netmiko 3.3.0. The stack trace is interesting for a couple reasons:\r\n\r\nThe actual error \"AttributeError: module 'cryptography.utils' has no attribute 'bit_length'\".  Looking back through the stack trace, there's a \"raise e\" from paramiko/transport.py line 660 in start_client().  The reported error is the actual exception raised at that point.\r\n\r\nI don't get how the code goes from raising that exception and ends up in the new tplink driver.\r\n\r\nI think I've installed all the latest module versions using pip to recreate the environment.\r\n\r\nDebugging ideas are much appreciated!\r\n\r\n```\r\nTraceback (most recent call last):\r\n(...deleted portion of stack trace...)\r\n\u00a0File \"/usr/local/lib/python3.6/site-packages/netmiko/ssh_dispatcher.py, line 324, in ConnectHandler\r\n\u00a0\u00a0 return ConnectionClass(*args, **kwargs)\r\n\u00a0File \"/usr/local/lib/python3.6/site-packages/netmiko/<vendor>/<driverfile>, line 189, in __init__\r\n\u00a0\u00a0 self.client.connect(self.host, port=22, username=self.username, password=self.password, look_for_keys=False, timeout=self.timeout)\r\n\u00a0File \"/usr/local/lib/python3.6/site-packages/paramiko/client.py, line 406, in connect\r\n\u00a0\u00a0 t.start_client(timeout=timeout)\r\n\u00a0File \"/usr/local/lib/python3.6/site-packages/paramiko/transport.py, line 660, in start_client\r\n\u00a0\u00a0 raise e\r\n\u00a0File \"/usr/local/lib/python3.6/site-packages/paramiko/transport.py, line 2075, in run\r\n\u00a0\u00a0 self.kex_engine.parse_next(ptype, m)\r\n\u00a0File \"/usr/local/lib/python3.6/site-packages/paramiko/kex_group1.py line 75, in parse_next\r\n\u00a0\u00a0 return self._parse_kexdh_reply(m)\r\n\u00a0File \"/usr/local/lib/python3.6/site-packages/paramiko/kex_group1.py line 120, in _parse_kexdh_reply\r\n\u00a0\u00a0 self.transport._verify_key(host_key, sig)\r\n\u00a0File \"/usr/local/lib/python3.6/site-packages/paramiko/transport.py, line 1886, in _verify_key\r\n\u00a0\u00a0 if not key.verify_ssh_sig(self.H, Message(sig)):\r\n\u00a0File \"/usr/local/lib/python3.6/site-packages/paramiko/dsskey.py, line 153, in verify_ssh_sig\r\n\u00a0\u00a0 ).public_key(backend=default_backend())\r\n\u00a0File \"/usr/local/lib64/python3.6/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py, line 212, in public_key\r\n\u00a0\u00a0 return backend.load_dsa_public_numbers(self)\r\n\u00a0File \"/usr/local/lib64/python3.6/site-packages/cryptography/hazmat/backends/openssl/backend.py, line 873, in load_dsa_public_numbers\r\n\u00a0\u00a0 dsa._check_dsa_parameters(numbers.parameter_numbers)\r\n\u00a0File \"/usr/local/lib/python3.6/site-packages/netmiko/tplink/tplink_jetstream.py, line 145, in _override_check_dsa_parameters\r\n\u00a0\u00a0 if crypto_utils.bit_length(parameters.q) not in [160, 256]:\r\nAttributeError: module 'cryptography.utils' has no attribute 'bit_length'\r\n```\n", "before_files": [{"content": "import re\nimport time\n\nfrom cryptography import utils as crypto_utils\nfrom cryptography.hazmat.primitives.asymmetric import dsa\n\nfrom netmiko import log\nfrom netmiko.cisco_base_connection import CiscoSSHConnection\nfrom netmiko.ssh_exception import NetmikoTimeoutException\n\n\nclass TPLinkJetStreamBase(CiscoSSHConnection):\n    def __init__(self, **kwargs):\n        # TP-Link doesn't have a way to set terminal width which breaks cmd_verify\n        if kwargs.get(\"global_cmd_verify\") is None:\n            kwargs[\"global_cmd_verify\"] = False\n        # TP-Link uses \"\\r\\n\" as default_enter for SSH and Telnet\n        if kwargs.get(\"default_enter\") is None:\n            kwargs[\"default_enter\"] = \"\\r\\n\"\n        return super().__init__(**kwargs)\n\n    def session_preparation(self):\n        \"\"\"\n        Prepare the session after the connection has been established.\n        \"\"\"\n        delay_factor = self.select_delay_factor(delay_factor=0)\n        time.sleep(0.3 * delay_factor)\n        self.clear_buffer()\n        self._test_channel_read(pattern=r\"[>#]\")\n        self.set_base_prompt()\n        self.enable()\n        self.disable_paging()\n        # Clear the read buffer\n        time.sleep(0.3 * self.global_delay_factor)\n        self.clear_buffer()\n\n    def enable(self, cmd=\"\", pattern=\"ssword\", re_flags=re.IGNORECASE):\n        \"\"\"\n        TPLink JetStream requires you to first execute \"enable\" and then execute \"enable-admin\".\n        This is necessary as \"configure\" is generally only available at \"enable-admin\" level\n\n        If the user does not have the Admin role, he will need to execute enable-admin to really\n        enable all functions.\n        \"\"\"\n\n        # If end-user passes in \"cmd\" execute that using normal process.\n        if cmd:\n            return super().enable(cmd=cmd, pattern=pattern, re_flags=re_flags)\n\n        output = \"\"\n        msg = (\n            \"Failed to enter enable mode. Please ensure you pass \"\n            \"the 'secret' argument to ConnectHandler.\"\n        )\n\n        cmds = [\"enable\", \"enable-admin\"]\n        if not self.check_enable_mode():\n            for cmd in cmds:\n                self.write_channel(self.normalize_cmd(cmd))\n                try:\n                    output += self.read_until_prompt_or_pattern(\n                        pattern=pattern, re_flags=re_flags\n                    )\n                    self.write_channel(self.normalize_cmd(self.secret))\n                    output += self.read_until_prompt()\n                except NetmikoTimeoutException:\n                    raise ValueError(msg)\n                if not self.check_enable_mode():\n                    raise ValueError(msg)\n        return output\n\n    def config_mode(self, config_command=\"configure\"):\n        \"\"\"Enter configuration mode.\"\"\"\n        return super().config_mode(config_command=config_command)\n\n    def exit_config_mode(self, exit_config=\"exit\", pattern=r\"#\"):\n        \"\"\"\n        Exit config mode.\n\n        Like the Mellanox equipment, the TP-Link Jetstream does not\n        support a single command to completely exit the configuration mode.\n\n        Consequently, need to keep checking and sending \"exit\".\n        \"\"\"\n        output = \"\"\n        check_count = 12\n        while check_count >= 0:\n            if self.check_config_mode():\n                self.write_channel(self.normalize_cmd(exit_config))\n                output += self.read_until_pattern(pattern=pattern)\n            else:\n                break\n            check_count -= 1\n\n        if self.check_config_mode():\n            raise ValueError(\"Failed to exit configuration mode\")\n            log.debug(f\"exit_config_mode: {output}\")\n\n        return output\n\n    def check_config_mode(self, check_string=\"(config\", pattern=r\"#\"):\n        \"\"\"Check whether device is in configuration mode. Return a boolean.\"\"\"\n        return super().check_config_mode(check_string=check_string, pattern=pattern)\n\n    def set_base_prompt(\n        self, pri_prompt_terminator=\">\", alt_prompt_terminator=\"#\", delay_factor=1\n    ):\n        \"\"\"\n        Sets self.base_prompt\n\n        Used as delimiter for stripping of trailing prompt in output.\n\n        Should be set to something that is general and applies in multiple\n        contexts. For TP-Link this will be the router prompt with > or #\n        stripped off.\n\n        This will be set on logging in, but not when entering system-view\n        \"\"\"\n        return super().set_base_prompt(\n            pri_prompt_terminator=pri_prompt_terminator,\n            alt_prompt_terminator=alt_prompt_terminator,\n            delay_factor=delay_factor,\n        )\n\n\nclass TPLinkJetStreamSSH(TPLinkJetStreamBase):\n    def _override_check_dsa_parameters(parameters):\n        \"\"\"\n        Override check_dsa_parameters from cryptography's dsa.py\n\n        Without this the error below occurs:\n\n        ValueError: p must be exactly 1024, 2048, or 3072 bits long\n\n        Allows for shorter or longer parameters.p to be returned\n        from the server's host key. This is a HORRIBLE hack and a\n        security risk, please remove if possible!\n\n        By now, with firmware:\n\n        2.0.5 Build 20200109 Rel.36203(s)\n\n        It's still not possible to remove this hack.\n        \"\"\"\n        if crypto_utils.bit_length(parameters.q) not in [160, 256]:\n            raise ValueError(\"q must be exactly 160 or 256 bits long\")\n\n        if not (1 < parameters.g < parameters.p):\n            raise ValueError(\"g, p don't satisfy 1 < g < p.\")\n\n    dsa._check_dsa_parameters = _override_check_dsa_parameters\n\n\nclass TPLinkJetStreamTelnet(TPLinkJetStreamBase):\n    def telnet_login(\n        self,\n        pri_prompt_terminator=\"#\",\n        alt_prompt_terminator=\">\",\n        username_pattern=r\"User:\",\n        pwd_pattern=r\"Password:\",\n        delay_factor=1,\n        max_loops=60,\n    ):\n        \"\"\"Telnet login: can be username/password or just password.\"\"\"\n        super().telnet_login(\n            pri_prompt_terminator=pri_prompt_terminator,\n            alt_prompt_terminator=alt_prompt_terminator,\n            username_pattern=username_pattern,\n            pwd_pattern=pwd_pattern,\n            delay_factor=delay_factor,\n            max_loops=max_loops,\n        )\n", "path": "netmiko/tplink/tplink_jetstream.py"}], "after_files": [{"content": "import re\nimport time\n\nfrom cryptography.hazmat.primitives.asymmetric import dsa\n\nfrom netmiko import log\nfrom netmiko.cisco_base_connection import CiscoSSHConnection\nfrom netmiko.ssh_exception import NetmikoTimeoutException\n\n\nclass TPLinkJetStreamBase(CiscoSSHConnection):\n    def __init__(self, **kwargs):\n        # TP-Link doesn't have a way to set terminal width which breaks cmd_verify\n        if kwargs.get(\"global_cmd_verify\") is None:\n            kwargs[\"global_cmd_verify\"] = False\n        # TP-Link uses \"\\r\\n\" as default_enter for SSH and Telnet\n        if kwargs.get(\"default_enter\") is None:\n            kwargs[\"default_enter\"] = \"\\r\\n\"\n        return super().__init__(**kwargs)\n\n    def session_preparation(self):\n        \"\"\"\n        Prepare the session after the connection has been established.\n        \"\"\"\n        delay_factor = self.select_delay_factor(delay_factor=0)\n        time.sleep(0.3 * delay_factor)\n        self.clear_buffer()\n        self._test_channel_read(pattern=r\"[>#]\")\n        self.set_base_prompt()\n        self.enable()\n        self.disable_paging()\n        # Clear the read buffer\n        time.sleep(0.3 * self.global_delay_factor)\n        self.clear_buffer()\n\n    def enable(self, cmd=\"\", pattern=\"ssword\", re_flags=re.IGNORECASE):\n        \"\"\"\n        TPLink JetStream requires you to first execute \"enable\" and then execute \"enable-admin\".\n        This is necessary as \"configure\" is generally only available at \"enable-admin\" level\n\n        If the user does not have the Admin role, he will need to execute enable-admin to really\n        enable all functions.\n        \"\"\"\n\n        # If end-user passes in \"cmd\" execute that using normal process.\n        if cmd:\n            return super().enable(cmd=cmd, pattern=pattern, re_flags=re_flags)\n\n        output = \"\"\n        msg = (\n            \"Failed to enter enable mode. Please ensure you pass \"\n            \"the 'secret' argument to ConnectHandler.\"\n        )\n\n        cmds = [\"enable\", \"enable-admin\"]\n        if not self.check_enable_mode():\n            for cmd in cmds:\n                self.write_channel(self.normalize_cmd(cmd))\n                try:\n                    output += self.read_until_prompt_or_pattern(\n                        pattern=pattern, re_flags=re_flags\n                    )\n                    self.write_channel(self.normalize_cmd(self.secret))\n                    output += self.read_until_prompt()\n                except NetmikoTimeoutException:\n                    raise ValueError(msg)\n                if not self.check_enable_mode():\n                    raise ValueError(msg)\n        return output\n\n    def config_mode(self, config_command=\"configure\"):\n        \"\"\"Enter configuration mode.\"\"\"\n        return super().config_mode(config_command=config_command)\n\n    def exit_config_mode(self, exit_config=\"exit\", pattern=r\"#\"):\n        \"\"\"\n        Exit config mode.\n\n        Like the Mellanox equipment, the TP-Link Jetstream does not\n        support a single command to completely exit the configuration mode.\n\n        Consequently, need to keep checking and sending \"exit\".\n        \"\"\"\n        output = \"\"\n        check_count = 12\n        while check_count >= 0:\n            if self.check_config_mode():\n                self.write_channel(self.normalize_cmd(exit_config))\n                output += self.read_until_pattern(pattern=pattern)\n            else:\n                break\n            check_count -= 1\n\n        if self.check_config_mode():\n            raise ValueError(\"Failed to exit configuration mode\")\n            log.debug(f\"exit_config_mode: {output}\")\n\n        return output\n\n    def check_config_mode(self, check_string=\"(config\", pattern=r\"#\"):\n        \"\"\"Check whether device is in configuration mode. Return a boolean.\"\"\"\n        return super().check_config_mode(check_string=check_string, pattern=pattern)\n\n    def set_base_prompt(\n        self, pri_prompt_terminator=\">\", alt_prompt_terminator=\"#\", delay_factor=1\n    ):\n        \"\"\"\n        Sets self.base_prompt\n\n        Used as delimiter for stripping of trailing prompt in output.\n\n        Should be set to something that is general and applies in multiple\n        contexts. For TP-Link this will be the router prompt with > or #\n        stripped off.\n\n        This will be set on logging in, but not when entering system-view\n        \"\"\"\n        return super().set_base_prompt(\n            pri_prompt_terminator=pri_prompt_terminator,\n            alt_prompt_terminator=alt_prompt_terminator,\n            delay_factor=delay_factor,\n        )\n\n\nclass TPLinkJetStreamSSH(TPLinkJetStreamBase):\n    def _override_check_dsa_parameters(parameters):\n        \"\"\"\n        Override check_dsa_parameters from cryptography's dsa.py\n\n        Without this the error below occurs:\n\n        ValueError: p must be exactly 1024, 2048, or 3072 bits long\n\n        Allows for shorter or longer parameters.p to be returned\n        from the server's host key. This is a HORRIBLE hack and a\n        security risk, please remove if possible!\n\n        By now, with firmware:\n\n        2.0.5 Build 20200109 Rel.36203(s)\n\n        It's still not possible to remove this hack.\n        \"\"\"\n        if parameters.q.bit_length() not in [160, 256]:\n            raise ValueError(\"q must be exactly 160 or 256 bits long\")\n\n        if not (1 < parameters.g < parameters.p):\n            raise ValueError(\"g, p don't satisfy 1 < g < p.\")\n\n    dsa._check_dsa_parameters = _override_check_dsa_parameters\n\n\nclass TPLinkJetStreamTelnet(TPLinkJetStreamBase):\n    def telnet_login(\n        self,\n        pri_prompt_terminator=\"#\",\n        alt_prompt_terminator=\">\",\n        username_pattern=r\"User:\",\n        pwd_pattern=r\"Password:\",\n        delay_factor=1,\n        max_loops=60,\n    ):\n        \"\"\"Telnet login: can be username/password or just password.\"\"\"\n        super().telnet_login(\n            pri_prompt_terminator=pri_prompt_terminator,\n            alt_prompt_terminator=alt_prompt_terminator,\n            username_pattern=username_pattern,\n            pwd_pattern=pwd_pattern,\n            delay_factor=delay_factor,\n            max_loops=max_loops,\n        )\n", "path": "netmiko/tplink/tplink_jetstream.py"}]}