-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
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 theContribution
section of the netmikoREADME.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 thetelnet_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)