Skip to content

ENHANCEMENT PROPOSAL - Handle 3 new "initial" prompts in newer Cisco IOS versions #3717

@vv0bbLeS

Description

@vv0bbLeS

Hello all,

Apologies if this has been suggested/submitted elsewhere already - I tried to look before I posted this and couldn't find anything.

As we know, netmiko currently is able to handle classic "initial" Cisco prompts like would you like to enter the initial configuration dialogue and Press RETURN to get started just fine (these prompts are handled in the telnet_login function in the cisco_base_connection.py module).

However, on newer Cisco IOS versions, there are 3 additional prompts that come up on a new/blank Cisco device:

  • Enter enable secret
  • Confirm enable secret
  • Enter your selection

To see these prompts in context, here is the full console experience when connecting to a new/blank Cisco device that's running a newer Cisco IOS version:

Would you like to enter the initial configuration dialog? [yes/no]: no

  The enable secret is a password used to protect
  access to privileged EXEC and configuration modes.
  This password, after entered, becomes encrypted in
  the configuration.
  -------------------------------------------------
  secret should be of minimum 10 characters and maximum 32 characters with
  at least 1 upper case, 1 lower case, 1 digit and
  should not contain [cisco]
  -------------------------------------------------
  Enter enable secret: **********
  Confirm enable secret: **********

The following configuration command script was created:

enable secret 9 $9$.........
!
end


[0] Go to the IOS command prompt without saving this config.
[1] Return back to the setup without saving this config.
[2] Save this configuration to nvram and exit.

Enter your selection [2]: 0
% You can enter the setup, by typing setup at IOS command prompt


Press RETURN to get started!

To that effect, I've added code to the telnet_login function in the cisco_base_connection.py module to handle these 3 new prompts (see below).

  • I've ran the below code through black per the Contribution section of the netmiko README.MD , but I'm not sure how to actually get this code submitted for review to potentially be included in netmiko.
  • I looked at the netmiko Testing instructions but I wasn't sure that applied to this addition, as it's not necessarily a "new device" driver, just an enhancement to an already existing base function.
  • I have been using this updated function in my own netmiko install for several weeks now and it works great.
  • I tried to mark any code sections I added below with a !! NEW !! comment to distinguish them better from the established code.
  • NOTE: I arbitrarily placed the TEMP_ENABLE_PW variable in the telnet_login function itself, but of course it could be placed anywhere in the netmiko heirarchy and imported if that is more desirable.
def telnet_login(
    self,
    pri_prompt_terminator: str = r"\#\s*$",
    alt_prompt_terminator: str = r">\s*$",
    username_pattern: str = r"(?:user:|username|login|user name)",
    pwd_pattern: str = r"assword",
    delay_factor: float = 1.0,
    max_loops: int = 20,
) -> str:
    """Telnet login. Can be username/password or just password."""

    # !! NEW !!
    TEMP_ENABLE_PW = "Abcde12345"

    delay_factor = self.select_delay_factor(delay_factor)

    if delay_factor < 1:
        if not self._legacy_mode and self.fast_cli:
            delay_factor = 1
    time.sleep(1 * delay_factor)

    output = ""
    return_msg = ""
    outer_loops = 3
    inner_loops = int(max_loops / outer_loops)
    i = 1
    for _ in range(outer_loops):
        while i <= inner_loops:
            try:
                output = self.read_channel()
                return_msg += output

                # Search for username pattern / send username
                if re.search(username_pattern, output, flags=re.I):
                    # Sometimes username/password must be terminated with "\r" and not "\r\n"
                    self.write_channel(self.username + "\r")
                    time.sleep(1 * delay_factor)
                    output = self.read_channel()
                    return_msg += output

                # Search for password pattern / send password
                if re.search(pwd_pattern, output, flags=re.I):

                    # Sometimes username/password must be terminated with "\r" and not "\r\n"
                    assert isinstance(self.password, str)

                    self.write_channel(self.password + "\r")

                    time.sleep(0.5 * delay_factor)
                    output = self.read_channel()
                    return_msg += output

                    if re.search(
                        pri_prompt_terminator, output, flags=re.M
                    ) or re.search(alt_prompt_terminator, output, flags=re.M):
                        return return_msg

                # Support direct telnet through terminal server
                if re.search(r"initial configuration dialog\? \[yes/no\]: ", output):
                    self.write_channel("no" + self.TELNET_RETURN)
                    time.sleep(0.5 * delay_factor)
                    count = 0
                    while count < 15:
                        output = self.read_channel()
                        return_msg += output

                        if re.search(r"ress RETURN to get started", output):
                            output = ""
                            break

                        # !! NEW !! Else, if the switch is prompting us for an Enable secret, break out of
                        # this while loop so we can proceed.
                        elif re.search(r"Enter enable secret:", output, re.IGNORECASE):
                            break
                        time.sleep(2 * delay_factor)
                        count += 1

                # !! NEW !! - New Cisco switches that prompt for initial enable password
                if re.search(r"Enter enable secret:", output, re.IGNORECASE):
                    # Note that we only want to use a RETURN here, not TELNET_RETURN,
                    # because after this password prompt there is another prompt that
                    # asks us if we want to save this temp password or disacrd it, with
                    # the default selection being to save the temp password.
                    #
                    # If we were to use the TELNET_RETURN, the combination of /n and /r
                    # in the TELNET_RETURN in a serial connection can cause two return's
                    # to be sent, which can cause us to either enter a blank value at a
                    # subsequent prompt (e.g. "Confirm enable secret") , or to accidentally
                    # accept the default selection at a prompt (e.g. the "do you want to save
                    # this temp enable password" prompt, which we don't want to save our temp
                    # enable password).
                    self.write_channel(TEMP_ENABLE_PW + self.RETURN)
                    time.sleep(0.5 * delay_factor)
                    count = 0
                    while count < 15:
                        output = self.read_channel()
                        return_msg += output
                        if re.search(r"Confirm enable secret:", output, re.IGNORECASE):
                            break
                        time.sleep(2 * delay_factor)
                        count += 1

                # !! NEW !! - New Cisco switches that ask you to confirm the initial enable password
                if re.search(r"Confirm enable secret:", output, re.IGNORECASE):
                    # again using RETURN and not TELNET_RETURN per the above explanation.
                    self.write_channel(TEMP_ENABLE_PW + self.RETURN)
                    time.sleep(0.5 * delay_factor)
                    count = 0
                    while count < 15:
                        output = self.read_channel()
                        return_msg += output
                        if re.search(r"Enter your selection", output, re.IGNORECASE):
                            break
                        time.sleep(2 * delay_factor)
                        count += 1

                # !! NEW !! - New Cisco switches that ask if you want to save the enable secret you just entered
                if re.search(r"Enter your selection", output, re.IGNORECASE):

                    # We don't want to save this config so we choose the "0" option
                    # again using RETURN and not TELNET_RETURN per the above explanation.
                    self.write_channel("0" + self.RETURN)
                    time.sleep(0.5 * delay_factor)
                    count = 0
                    while count < 15:
                        output = self.read_channel()
                        return_msg += output
                        if re.search(r"ress RETURN to get started", output):
                            output = ""
                            break
                        time.sleep(2 * delay_factor)
                        count += 1

                # Check for device with no password configured
                if re.search(r"assword required, but none set", output):
                    assert self.remote_conn is not None
                    self.remote_conn.close()
                    msg = "Login failed - Password required, but none set: {}".format(
                        self.host
                    )
                    raise NetmikoAuthenticationException(msg)

                # Check if proper data received
                if re.search(pri_prompt_terminator, output, flags=re.M) or re.search(
                    alt_prompt_terminator, output, flags=re.M
                ):
                    return return_msg

                i += 1

            except EOFError:
                assert self.remote_conn is not None
                self.remote_conn.close()
                msg = f"Login failed: {self.host}"
                raise NetmikoAuthenticationException(msg)

        # Try sending an <enter> to restart the login process
        self.write_channel(self.TELNET_RETURN)
        time.sleep(0.5 * delay_factor)
        i = 1

    # Last try to see if we already logged in
    self.write_channel(self.TELNET_RETURN)
    time.sleep(0.5 * delay_factor)
    output = self.read_channel()
    return_msg += output
    if re.search(pri_prompt_terminator, output, flags=re.M) or re.search(
        alt_prompt_terminator, output, flags=re.M
    ):
        return return_msg
    assert self.remote_conn is not None
    self.remote_conn.close()
    msg = f"Login failed: {self.host}"
    raise NetmikoAuthenticationException(msg)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions