Repository: Juniper/py-junos-eznc

Issue:
Pyez telnet not working as expected in certain JUNOS enhanced devices
Pyez telnet not working as expected in certain JUNOS enhanced devices. We are seeing login prompt 2 times.

#################### 0 ####################
INFO:jnpr.junos.tty:TTY: connecting to TTY:x.x.x.x ...
DEBUG:jnpr.junos.tty_telnet:Write:

INFO:jnpr.junos.tty:TTY: logging in......
DEBUG:jnpr.junos.tty_telnet:Got: b'\r\nLinux 4.8.28-WR2.2.1_standard-gf600f29 (re0) (pts/4)\r\n\r\ns0r_RE0 login: \r\n\r\nPassword: '
DEBUG:jnpr.junos.tty_telnet:Write: xxxx
DEBUG:jnpr.junos.tty_telnet:Got: b'\r\n\r\nLogin incorrect\r\ns0r_RE0 login: '
DEBUG:jnpr.junos.tty_telnet:Write: xxxx
DEBUG:jnpr.junos.tty_telnet:Got: b'Password: '
DEBUG:jnpr.junos.tty_telnet:Write: xxxx
DEBUG:jnpr.junos.tty_telnet:Got: b'\r\nLast login: Mon May 10 08:48:01 PDT 2021 from xyz.net on pts/4\r\n--- JUNOS 21.3-202105042357.0-EVO Linux (none) 4.8.28-WR2.2.1_standard-gf600f29 #'
INFO:jnpr.junos.tty:TTY: OK.....starting NETCONF
DEBUG:jnpr.junos.tty_telnet:Write: xml-mode netconf need-trailer
DEBUG:jnpr.junos.tty_netconf:Received:
b'<!-- user test, class local_class -->\n<hello 

Discussion:

Patch:
diff --git a/lib/jnpr/junos/transport/tty.py b/lib/jnpr/junos/transport/tty.py
index 204206a4..2b418051 100644
--- a/lib/jnpr/junos/transport/tty.py
+++ b/lib/jnpr/junos/transport/tty.py
@@ -217,14 +217,19 @@ class Terminal(object):
                 # assume we're in a hung state, i.e. we don't see
                 # a login prompt for whatever reason
                 self.state = self._ST_TTY_NOLOGIN
-                if self.console_has_banner:
-                    # if console connection has a banner or warning,
-                    # use this hack
-                    sleep(5)
-                    self.write("\n")
-                else:
-                    # @@@ this is still a hack - used by default
-                    self.write("<close-session/>")
+                # For console based telnet connection a new-line is required.
+                # Code modified to check with a newline for telnet based connections.
+                # Keeping below code as a comment for future enhancement.
+                # if self.console_has_banner:
+                # # if console connection has a banner or warning,
+                # # use this hack
+                #     sleep(5)
+                #     self.write("\n")
+                sleep(5)
+                self.write("\n")
+            else:
+                # @@@ this is still a hack - used by default
+                self.write("<close-session/>")
 
         def _ev_shell():
             if self.state == self._ST_INIT:
diff --git a/lib/jnpr/junos/transport/tty_telnet.py b/lib/jnpr/junos/transport/tty_telnet.py
index 30cef24c..057676c5 100644
--- a/lib/jnpr/junos/transport/tty_telnet.py
+++ b/lib/jnpr/junos/transport/tty_telnet.py
@@ -67,7 +67,9 @@ class Telnet(Terminal):
                 sleep(self.RETRY_BACKOFF)
         else:
             raise RuntimeError("open_fail: port not ready")
-        self.write("\n")
+        # the below line was added for console based telnet connection as it needs newline to work.
+        # it causes issue in EVO devices. Keeping it as a comment for future enhancement.
+        # self.write("\n")
 
     def _tty_close(self):
         self._tn.close()


Test patch:
diff --git a/tests/unit/transport/test_tty.py b/tests/unit/transport/test_tty.py
index b8361e0e..88129c07 100644
--- a/tests/unit/transport/test_tty.py
+++ b/tests/unit/transport/test_tty.py
@@ -39,6 +39,10 @@ class TestTTY(unittest.TestCase):
         self.terminal.read_prompt.return_value = (None, "testing")
         self.terminal.write = MagicMock()
         self.assertRaises(RuntimeError, self.terminal._login_state_machine)
+        self.terminal.write.assert_called_with("\n")
+        self.terminal.read_prompt.return_value = (None, "testing")
+        self.terminal.write = MagicMock()
+        self.assertRaises(RuntimeError, self.terminal._login_state_machine)
         self.terminal.write.assert_called_with("<close-session/>")
 
     @patch("jnpr.junos.transport.tty.sleep")