From 56179e7b2766caa1588d30453cafe84bb5991d72 Mon Sep 17 00:00:00 2001 From: Thomas Ryan Date: Thu, 25 Jan 2024 11:21:54 -0500 Subject: [PATCH 1/2] Releasing v24.1 From 80ac39bf1984cc37c0d1cc6aacb21d6f8386641e Mon Sep 17 00:00:00 2001 From: Thomas Ryan Date: Thu, 25 Jan 2024 11:28:09 -0500 Subject: [PATCH 2/2] Releasing v24.1 --- docs/changelog/2024/january.rst | 68 ++++ docs/changelog/index.rst | 1 + docs/changelog_plugins/2024/january.rst | 66 ++++ docs/changelog_plugins/index.rst | 1 + src/unicon/plugins/__init__.py | 2 +- src/unicon/plugins/generic/patterns.py | 5 +- .../plugins/generic/service_implementation.py | 14 +- .../plugins/generic/service_statements.py | 2 +- src/unicon/plugins/generic/settings.py | 9 +- src/unicon/plugins/generic/statemachine.py | 7 +- src/unicon/plugins/generic/statements.py | 10 +- .../iosxe/cat9k/service_implementation.py | 14 +- src/unicon/plugins/iosxe/statemachine.py | 19 +- .../iosxr/moonshine/tests/config_test.py | 2 +- .../moonshine/tests/standalone_ping_test.py | 2 +- src/unicon/plugins/linux/patterns.py | 5 +- src/unicon/plugins/nxos/patterns.py | 2 +- src/unicon/plugins/nxos/service_patterns.py | 2 +- src/unicon/plugins/pid_tokens.csv | 321 +++++++++--------- src/unicon/plugins/sros/setting.py | 2 + .../mock_data/iosxe/iosxe_mock_data.yaml | 15 + .../mock_data/linux/linux_mock_data.yaml | 4 + .../mock_data/nxos/nxos_mock_data_n5k.yaml | 2 + .../nxos/nxos_repeat_poap_reload.yaml | 4 +- src/unicon/plugins/tests/test_plugin_iosxe.py | 78 ++++- src/unicon/plugins/tests/test_plugin_linux.py | 1 + src/unicon/plugins/utils.py | 4 + 27 files changed, 449 insertions(+), 213 deletions(-) create mode 100644 docs/changelog/2024/january.rst create mode 100644 docs/changelog_plugins/2024/january.rst diff --git a/docs/changelog/2024/january.rst b/docs/changelog/2024/january.rst new file mode 100644 index 00000000..813f8be1 --- /dev/null +++ b/docs/changelog/2024/january.rst @@ -0,0 +1,68 @@ +January 2024 +========== + +30 - Unicon v24.1 +------------------------ + + + +.. csv-table:: Module Versions + :header: "Modules", "Versions" + + ``unicon.plugins``, v24.1 + ``unicon``, v24.1 + +Install Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + bash$ pip install unicon.plugins + bash$ pip install unicon + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + bash$ pip install --upgrade unicon.plugins + bash$ pip install --upgrade unicon + +Features and Bug Fixes: +^^^^^^^^^^^^^^^^^^^^^^^ + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pty_backend + * Modified error handling logic to allow dialog to process statements on subprocess exit + +* utils + * Update ansi pattern to allow imports + +* statemachine + * Update hostname logic to handle hostnames with special characters + +* unicon + * Add CLI option to enable debug logs + + +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* service_implementation + * Modified Reload service + * Removed sendline after reload + +* iosxr + * Modified moonshine UTs + * Updated wrong import statements in standalone_ping_test.py and config_test.py UTs. + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index e812a82a..05bec9f8 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -4,6 +4,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2024/january 2023/november 2023/october 2023/september diff --git a/docs/changelog_plugins/2024/january.rst b/docs/changelog_plugins/2024/january.rst new file mode 100644 index 00000000..9765faba --- /dev/null +++ b/docs/changelog_plugins/2024/january.rst @@ -0,0 +1,66 @@ +January 2024 +========== + +30 - Unicon.Plugins v24.1 +------------------------ + + + +.. csv-table:: Module Versions + :header: "Modules", "Versions" + + ``unicon.plugins``, v24.1 + ``unicon``, v24.1 + +Install Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + bash$ pip install unicon.plugins + bash$ pip install unicon + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + bash$ pip install --upgrade unicon.plugins + bash$ pip install --upgrade unicon + +Features and Bug Fixes: +^^^^^^^^^^^^^^^^^^^^^^^ + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Add +-------------------------------------------------------------------------------- + +* generic + * Added more prompt support in connection statement list + + +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* iosxe + * Added unittests to test hostnames with special characters\ + * Update settings for reload API, change SYSLOG_WAIT to 10 seconds + * cat9k + * Update image_to_boot for HA device. (active and standby rp) + +* generic, iosxe + * Update config transition logic, increase wait time for prompt + +* generic + * Update response to setup dialog to "no" instead of "n" + +* linux + * Update linux hostname learning pattern to handle ANSI characters in prompt + + diff --git a/docs/changelog_plugins/index.rst b/docs/changelog_plugins/index.rst index e9457a76..b77a6578 100644 --- a/docs/changelog_plugins/index.rst +++ b/docs/changelog_plugins/index.rst @@ -4,6 +4,7 @@ Plugins Changelog .. toctree:: :maxdepth: 2 + 2024/january 2023/november 2023/october 2023/september diff --git a/src/unicon/plugins/__init__.py b/src/unicon/plugins/__init__.py index c53b2b64..ef8f7588 100644 --- a/src/unicon/plugins/__init__.py +++ b/src/unicon/plugins/__init__.py @@ -1,4 +1,4 @@ -__version__ = '23.11' +__version__ = '24.1' supported_chassis = [ 'single_rp', diff --git a/src/unicon/plugins/generic/patterns.py b/src/unicon/plugins/generic/patterns.py index d541a032..ca680b10 100644 --- a/src/unicon/plugins/generic/patterns.py +++ b/src/unicon/plugins/generic/patterns.py @@ -23,15 +23,12 @@ def __init__(self): """ initialises all generic patterns """ super().__init__() - # self.enable_prompt = r'.*%N#\s?$' self.default_hostname_pattern = r'WLC|RouterRP|Router|[Ss]witch|Controller|ios' self.enable_prompt = r'^(.*?)(Router|Router-stby|Router-sdby|RouterRP|RouterRP-standby|%N-standby|%N\(standby\)|%N-sdby|%N-stby|(S|s)witch|(S|s)witch\(standby\)|Controller|ios|-Slot[0-9]+|%N)(\(boot\))*#\s?$' - # self.disable_prompt = r'.*%N>\s?$' self.disable_prompt = r'^(.*?)(Router|Router-stby|Router-sdby|RouterRP|RouterRP-standby|%N-standby|%N-sdby|%N-stby|(S|s)witch|s(S|s)witch\(standby\)|Controller|ios|-Slot[0-9]+|%N)(\(boot\))*>\s?$' - # self.config_prompt = r'.*%N\(config.*\)#\s?$' self.config_prompt = r'^(.*)\(.*(con|cfg|ipsec-profile|ca-trustpoint|gkm-local-server)\S*\)#\s?$' self.rommon_prompt = r'^(.*?)(rommon[\s\d]*>|switch:)\s?$' # self.standby_enable_prompt = r'^(.*?)(RouterRP-standby|%N-standby|%N-sdby|%N\(standby\))#\s?$' @@ -71,7 +68,7 @@ def __init__(self): self.config_locked = r'Configuration (mode )?(is )?locked|Config mode cannot be entered' - self.config_start = r'Enter configuration commands, one per line\.\s+End with CNTL/Z\.\s*$' + self.config_start = r'\nEnter configuration commands, one per line\.\s+End with CNTL/Z\.\s*$' self.enable_secret = r'^.*?(Enter|Confirm) enable secret:\s*$' diff --git a/src/unicon/plugins/generic/service_implementation.py b/src/unicon/plugins/generic/service_implementation.py index 26e01adc..81fcfdb2 100644 --- a/src/unicon/plugins/generic/service_implementation.py +++ b/src/unicon/plugins/generic/service_implementation.py @@ -1094,10 +1094,12 @@ def call_service(self, post_reload_wait_time = None, *args, **kwargs): - con = self.connection timeout = timeout or self.timeout + syslog_wait = con.settings.SYSLOG_WAIT + con.settings.SYSLOG_WAIT = con.settings.RELOAD_SYSLOG_WAIT + if error_pattern is None: self.error_pattern = con.settings.ERROR_PATTERN else: @@ -1200,7 +1202,13 @@ def call_service(self, if (current_time - start_time) > timeout_time: con.log.info('Time out, trying to acces device..') break - con.sendline() + + # ! This line was added to resolve an issue with HA devices, but was + # ! found to cause further issues with other devices on reload + # TODO Need to find a better way to implement a fix for HA devices + # TODO that does not cause issues with other devices. Likely need to + # TODO modify the state machine and/or dialog processing. + # con.sendline() try: con.context = context con.connection_provider.connect() @@ -1212,6 +1220,8 @@ def call_service(self, con.log.exception('Connection to {} failed'.format(con.hostname)) self.result = False + con.settings.SYSLOG_WAIT = syslog_wait + self.log_buffer.seek(0) reload_output = self.log_buffer.read() # clear buffer diff --git a/src/unicon/plugins/generic/service_statements.py b/src/unicon/plugins/generic/service_statements.py index e7696e34..2e28d8ec 100644 --- a/src/unicon/plugins/generic/service_statements.py +++ b/src/unicon/plugins/generic/service_statements.py @@ -268,7 +268,7 @@ def config_session_locked_handler(context): continue_timer=False) setup_dialog = Statement(pattern=reload_patterns.setup_dialog, - action=send_response, args={'response': 'n'}, + action=send_response, args={'response': 'no'}, loop_continue=True, continue_timer=False) diff --git a/src/unicon/plugins/generic/settings.py b/src/unicon/plugins/generic/settings.py index 2154d28e..dbf98a94 100644 --- a/src/unicon/plugins/generic/settings.py +++ b/src/unicon/plugins/generic/settings.py @@ -70,11 +70,8 @@ def __init__(self): self.BOOT_FILESYSTEM = 'bootflash:' self.BOOT_FILE_REGEX = r'(\S+\.bin)' - # Wait for the config prompt to appear - # before checking for the config prompt. - # This may need to be adjusted if the RTT between - # the execution host and lab device is high. - self.CONFIG_TRANSITION_WAIT = 0.2 + # Time to wait for the config prompt to appear + self.CONFIG_TRANSITION_WAIT = 15 # If learn_hostname is requested but no hostname was actually learned, # substitute this default hostname when occurances of HOSTNAME_SUBST_PAT @@ -100,6 +97,8 @@ def __init__(self): # syslog message handling timers self.SYSLOG_WAIT = 1 + # syslog wait time for reload service + self.RELOAD_SYSLOG_WAIT = 10 # pattern to replace "more" string # command to continue for more_prompt_stmt diff --git a/src/unicon/plugins/generic/statemachine.py b/src/unicon/plugins/generic/statemachine.py index 269c8567..508f5de8 100644 --- a/src/unicon/plugins/generic/statemachine.py +++ b/src/unicon/plugins/generic/statemachine.py @@ -35,15 +35,17 @@ def config_service_prompt_handler(spawn, config_pattern): """ Check if we need to send the sevice config prompt command. """ if hasattr(spawn.settings, 'SERVICE_PROMPT_CONFIG_CMD') and spawn.settings.SERVICE_PROMPT_CONFIG_CMD: + spawn.log.debug('Waiting for config prompt') # if the config prompt is seen, return if re.search(config_pattern, spawn.buffer): return else: - # if no buffer changes for a few seconds, check again - if buffer_settled(spawn, spawn.settings.CONFIG_PROMPT_WAIT): + # if no buffer changes for (config timout) seconds, check again + if buffer_settled(spawn, spawn.settings.CONFIG_TRANSITION_WAIT): if re.search(config_pattern, spawn.buffer): return else: + spawn.log.debug('Config prompt not seen, enabling service prompt config') spawn.sendline(spawn.settings.SERVICE_PROMPT_CONFIG_CMD) @@ -66,7 +68,6 @@ def config_transition(statemachine, spawn, context): for attempt in range(max_attempts + 1): spawn.sendline(statemachine.config_command) - buffer_wait(spawn, spawn.settings.CONFIG_TRANSITION_WAIT) dialog.process(spawn, timeout=spawn.settings.CONFIG_TIMEOUT, context=context) statemachine.detect_state(spawn) diff --git a/src/unicon/plugins/generic/statements.py b/src/unicon/plugins/generic/statements.py index 18047a99..5b0e7a9e 100644 --- a/src/unicon/plugins/generic/statements.py +++ b/src/unicon/plugins/generic/statements.py @@ -806,13 +806,15 @@ def __init__(self): generic_statements.enter_your_selection_stmt ] -connection_statement_list = \ - authentication_statement_list + \ - initial_statement_list + \ - pre_connection_statement_list ############################################################ # Default pattern Statement ############################################################# default_statement_list = [generic_statements.more_prompt_stmt] + +connection_statement_list = \ + default_statement_list + \ + authentication_statement_list + \ + initial_statement_list + \ + pre_connection_statement_list \ No newline at end of file diff --git a/src/unicon/plugins/iosxe/cat9k/service_implementation.py b/src/unicon/plugins/iosxe/cat9k/service_implementation.py index c8768319..617a72e8 100644 --- a/src/unicon/plugins/iosxe/cat9k/service_implementation.py +++ b/src/unicon/plugins/iosxe/cat9k/service_implementation.py @@ -67,12 +67,22 @@ def pre_service(self, *args, **kwargs): if 'image_to_boot' in self.context: self.context['orig_image_to_boot'] = self.context['image_to_boot'] self.context["image_to_boot"] = kwargs["image_to_boot"] - self.connection.active.context = self.context - self.connection.standby.context = self.context + self.connection.active.context.update({ + "image_to_boot": self.context["image_to_boot"] + }) + self.connection.standby.context.update({ + "image_to_boot": self.context["image_to_boot"] + }) self.connection.log.info("'image_to_boot' specified with reload, transitioning to 'rommon' state") else: if 'image' in kwargs: self.context['image_to_boot'] = kwargs.get('image') + self.connection.active.context.update({ + "image_to_boot": self.context["image_to_boot"] + }) + self.connection.standby.context.update({ + "image_to_boot": self.context["image_to_boot"] + }) self.start_state = 'enable' super().pre_service(*args, **kwargs) diff --git a/src/unicon/plugins/iosxe/statemachine.py b/src/unicon/plugins/iosxe/statemachine.py index ee8d5ced..c2b515e5 100644 --- a/src/unicon/plugins/iosxe/statemachine.py +++ b/src/unicon/plugins/iosxe/statemachine.py @@ -4,7 +4,8 @@ import re from datetime import datetime -from unicon.plugins.generic.statemachine import GenericSingleRpStateMachine, config_transition +from unicon.plugins.generic.statemachine import (GenericSingleRpStateMachine, config_transition, + config_service_prompt_handler) from unicon.plugins.generic.statements import (connection_statement_list, default_statement_list, wait_and_enter) from unicon.plugins.generic.service_statements import reload_statement_list @@ -47,22 +48,6 @@ def send_break(statemachine, spawn, context): spawn.send('\x03') -def config_service_prompt_handler(spawn, config_pattern): - """ Check if we need to send the sevice config prompt command. - """ - if hasattr(spawn.settings, 'SERVICE_PROMPT_CONFIG_CMD') and spawn.settings.SERVICE_PROMPT_CONFIG_CMD: - # if the config prompt is seen, return - if re.search(config_pattern, spawn.buffer): - return - else: - # if no buffer changes for a few seconds, check again - if buffer_settled(spawn, spawn.settings.CONFIG_PROMPT_WAIT): - if re.search(config_pattern, spawn.buffer): - return - else: - spawn.sendline(spawn.settings.SERVICE_PROMPT_CONFIG_CMD) - - def enable_to_maintenance_transition(statemachine, spawn, context): dialog = Dialog([ diff --git a/src/unicon/plugins/iosxr/moonshine/tests/config_test.py b/src/unicon/plugins/iosxr/moonshine/tests/config_test.py index 6d0af588..484e745a 100755 --- a/src/unicon/plugins/iosxr/moonshine/tests/config_test.py +++ b/src/unicon/plugins/iosxr/moonshine/tests/config_test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 from pyats import aetest -from pyats.kleenex import BringUp +from pyats.bringup import BringUp import re, logging # NOTE: uut1 device must be Moonshine for this test to work. diff --git a/src/unicon/plugins/iosxr/moonshine/tests/standalone_ping_test.py b/src/unicon/plugins/iosxr/moonshine/tests/standalone_ping_test.py index 91f418fc..3cc8c4ad 100755 --- a/src/unicon/plugins/iosxr/moonshine/tests/standalone_ping_test.py +++ b/src/unicon/plugins/iosxr/moonshine/tests/standalone_ping_test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 from pyats import aetest -from pyats.kleenex import BringUp +from pyats.bringup import BringUp class common_setup(aetest.CommonSetup): @aetest.subsection diff --git a/src/unicon/plugins/linux/patterns.py b/src/unicon/plugins/linux/patterns.py index 6be51bb3..9fb3dcd0 100644 --- a/src/unicon/plugins/linux/patterns.py +++ b/src/unicon/plugins/linux/patterns.py @@ -1,6 +1,7 @@ - +from unicon.utils import ANSI_REGEX from unicon.plugins.generic.patterns import GenericPatterns + class LinuxPatterns(GenericPatterns): def __init__(self): super().__init__() @@ -11,7 +12,7 @@ def __init__(self): # The reason for using the learn_hostname pattern instead of the shell_prompt pattern # to learn the hostname, is that the regex in the router implementation matches \S # which is not exact enough for the known linux prompts. - self.learn_hostname = r'^.*?(?P[-\w]+)\s?([-\w\]/~:\.\d ]+)?([>\$~%#\]])\s*(\x1b\S+)?$' + self.learn_hostname = r'^.*?({a})?(?P[-\w]+)\s?([-\w\]/~:\.\d ]+)?([>\$~%#\]])\s*(\x1b\S+)?$'.format(a=ANSI_REGEX) # shell_prompt pattern will be used by the 'shell' state after lean_hostname matches # a known hostname pattern this pattern is set for the shell state at transition diff --git a/src/unicon/plugins/nxos/patterns.py b/src/unicon/plugins/nxos/patterns.py index bfe7e0e7..9de1725f 100644 --- a/src/unicon/plugins/nxos/patterns.py +++ b/src/unicon/plugins/nxos/patterns.py @@ -14,7 +14,7 @@ def __init__(self): self.config_prompt = r'^(?P.*?(?\s*$' - self.reboot = r'This command will reboot the system. \(y\/n\)\? \[n\]' + self.reboot = r'(.*?)This command will reboot the system. \(y\/n\)\? \[n\]' self.secure_password = r'^.*Do you want to enforce secure password standard \(yes\/no\) \[y\]\:' self.auto_provision = r'Abort( Power On)? Auto Provisioning and continue with normal setup \?\(yes\/no\)\[n\]\:' self.enable_vdc = r'Do you want to enable admin vdc\s?\(yes\/no\)\s?\[n\]\:' diff --git a/src/unicon/plugins/nxos/service_patterns.py b/src/unicon/plugins/nxos/service_patterns.py index 8ae7e7b3..b7352cbc 100644 --- a/src/unicon/plugins/nxos/service_patterns.py +++ b/src/unicon/plugins/nxos/service_patterns.py @@ -21,7 +21,7 @@ def __init__(self): class HaNxosReloadPatterns: # NXOS reload pattern def __init__(self): - self.reboot = r'This command will reboot the system. \(y\/n\)\? \[n\]' + self.reboot = r'(.*?)This command will reboot the system. \(y\/n\)\? \[n\]' self.secure_password = r'^.*Do you want to enforce secure password standard \(yes\/no\) \[y\]\:' self.auto_provision = r'Abort( Power On)? Auto Provisioning and continue with normal setup \?\(yes\/no\)\[n\]\:' self.enable_vdc = r'Do you want to enable admin vdc\s?\(yes\/no\)\s?\[n\]\:' diff --git a/src/unicon/plugins/pid_tokens.csv b/src/unicon/plugins/pid_tokens.csv index a83f6070..5b108e93 100644 --- a/src/unicon/plugins/pid_tokens.csv +++ b/src/unicon/plugins/pid_tokens.csv @@ -222,166 +222,166 @@ C8510-CHAS5,iosxe,cat8k,c8500, C8510CSR-SKIT-AC,iosxe,cat8k,c8500, C8540-CHAS13,iosxe,cat8k,c8500, C8540CSR-SKIT-AC,iosxe,cat8k,c8500, -C9105AXI-A,iosxe,cat9k,c9100ap, -C9105AXI-B,iosxe,cat9k,c9100ap, -C9105AXI-C,iosxe,cat9k,c9100ap, -C9105AXI-D,iosxe,cat9k,c9100ap, -C9105AXI-E,iosxe,cat9k,c9100ap, -C9105AXI-F,iosxe,cat9k,c9100ap, -C9105AXI-G,iosxe,cat9k,c9100ap, -C9105AXI-H,iosxe,cat9k,c9100ap, -C9105AXI-I,iosxe,cat9k,c9100ap, -C9105AXI-K,iosxe,cat9k,c9100ap, -C9105AXI-N,iosxe,cat9k,c9100ap, -C9105AXI-Q,iosxe,cat9k,c9100ap, -C9105AXI-R,iosxe,cat9k,c9100ap, -C9105AXI-S,iosxe,cat9k,c9100ap, -C9105AXI-T,iosxe,cat9k,c9100ap, -C9105AXI-Z,iosxe,cat9k,c9100ap, -C9105AXW-A,iosxe,cat9k,c9100ap, -C9105AXW-B,iosxe,cat9k,c9100ap, -C9105AXW-C,iosxe,cat9k,c9100ap, -C9105AXW-D,iosxe,cat9k,c9100ap, -C9105AXW-E,iosxe,cat9k,c9100ap, -C9105AXW-F,iosxe,cat9k,c9100ap, -C9105AXW-G,iosxe,cat9k,c9100ap, -C9105AXW-H,iosxe,cat9k,c9100ap, -C9105AXW-I,iosxe,cat9k,c9100ap, -C9105AXW-K,iosxe,cat9k,c9100ap, -C9105AXW-N,iosxe,cat9k,c9100ap, -C9105AXW-Q,iosxe,cat9k,c9100ap, -C9105AXW-R,iosxe,cat9k,c9100ap, -C9105AXW-S,iosxe,cat9k,c9100ap, -C9105AXW-T,iosxe,cat9k,c9100ap, -C9105AXW-Z,iosxe,cat9k,c9100ap, -C9115AXE-A,iosxe,cat9k,c9100ap, -C9115AXE-B,iosxe,cat9k,c9100ap, -C9115AXE-C,iosxe,cat9k,c9100ap, -C9115AXE-D,iosxe,cat9k,c9100ap, -C9115AXE-E,iosxe,cat9k,c9100ap, -C9115AXE-F,iosxe,cat9k,c9100ap, -C9115AXE-G,iosxe,cat9k,c9100ap, -C9115AXE-H,iosxe,cat9k,c9100ap, -C9115AXE-I,iosxe,cat9k,c9100ap, -C9115AXE-K,iosxe,cat9k,c9100ap, -C9115AXE-N,iosxe,cat9k,c9100ap, -C9115AXE-Q,iosxe,cat9k,c9100ap, -C9115AXE-R,iosxe,cat9k,c9100ap, -C9115AXE-S,iosxe,cat9k,c9100ap, -C9115AXE-T,iosxe,cat9k,c9100ap, -C9115AXE-Z,iosxe,cat9k,c9100ap, -C9115AXI-A,iosxe,cat9k,c9100ap, -C9115AXI-B,iosxe,cat9k,c9100ap, -C9115AXI-C,iosxe,cat9k,c9100ap, -C9115AXI-D,iosxe,cat9k,c9100ap, -C9115AXI-E,iosxe,cat9k,c9100ap, -C9115AXI-F,iosxe,cat9k,c9100ap, -C9115AXI-G,iosxe,cat9k,c9100ap, -C9115AXI-H,iosxe,cat9k,c9100ap, -C9115AXI-I,iosxe,cat9k,c9100ap, -C9115AXI-K,iosxe,cat9k,c9100ap, -C9115AXI-N,iosxe,cat9k,c9100ap, -C9115AXI-Q,iosxe,cat9k,c9100ap, -C9115AXI-R,iosxe,cat9k,c9100ap, -C9115AXI-S,iosxe,cat9k,c9100ap, -C9115AXI-T,iosxe,cat9k,c9100ap, -C9115AXI-Z,iosxe,cat9k,c9100ap, -C9117AXI-A,iosxe,cat9k,c9100ap, -C9117AXI-B,iosxe,cat9k,c9100ap, -C9117AXI-C,iosxe,cat9k,c9100ap, -C9117AXI-D,iosxe,cat9k,c9100ap, -C9117AXI-E,iosxe,cat9k,c9100ap, -C9117AXI-F,iosxe,cat9k,c9100ap, -C9117AXI-G,iosxe,cat9k,c9100ap, -C9117AXI-H,iosxe,cat9k,c9100ap, -C9117AXI-I,iosxe,cat9k,c9100ap, -C9117AXI-K,iosxe,cat9k,c9100ap, -C9117AXI-N,iosxe,cat9k,c9100ap, -C9117AXI-Q,iosxe,cat9k,c9100ap, -C9117AXI-R,iosxe,cat9k,c9100ap, -C9117AXI-S,iosxe,cat9k,c9100ap, -C9117AXI-T,iosxe,cat9k,c9100ap, -C9117AXI-Z,iosxe,cat9k,c9100ap, -C9120AXE-A,iosxe,cat9k,c9100ap, -C9120AXE-B,iosxe,cat9k,c9100ap, -C9120AXE-C,iosxe,cat9k,c9100ap, -C9120AXE-D,iosxe,cat9k,c9100ap, -C9120AXE-E,iosxe,cat9k,c9100ap, -C9120AXE-F,iosxe,cat9k,c9100ap, -C9120AXE-G,iosxe,cat9k,c9100ap, -C9120AXE-H,iosxe,cat9k,c9100ap, -C9120AXE-I,iosxe,cat9k,c9100ap, -C9120AXE-K,iosxe,cat9k,c9100ap, -C9120AXE-N,iosxe,cat9k,c9100ap, -C9120AXE-Q,iosxe,cat9k,c9100ap, -C9120AXE-R,iosxe,cat9k,c9100ap, -C9120AXE-S,iosxe,cat9k,c9100ap, -C9120AXE-T,iosxe,cat9k,c9100ap, -C9120AXE-Z,iosxe,cat9k,c9100ap, -C9120AXI-A,iosxe,cat9k,c9100ap, -C9120AXI-B,iosxe,cat9k,c9100ap, -C9120AXI-C,iosxe,cat9k,c9100ap, -C9120AXI-D,iosxe,cat9k,c9100ap, -C9120AXI-E,iosxe,cat9k,c9100ap, -C9120AXI-F,iosxe,cat9k,c9100ap, -C9120AXI-G,iosxe,cat9k,c9100ap, -C9120AXI-H,iosxe,cat9k,c9100ap, -C9120AXI-I,iosxe,cat9k,c9100ap, -C9120AXI-K,iosxe,cat9k,c9100ap, -C9120AXI-N,iosxe,cat9k,c9100ap, -C9120AXI-Q,iosxe,cat9k,c9100ap, -C9120AXI-R,iosxe,cat9k,c9100ap, -C9120AXI-S,iosxe,cat9k,c9100ap, -C9120AXI-T,iosxe,cat9k,c9100ap, -C9120AXI-Z,iosxe,cat9k,c9100ap, -C9120AXP-A,iosxe,cat9k,c9100ap, -C9120AXP-B,iosxe,cat9k,c9100ap, -C9120AXP-C,iosxe,cat9k,c9100ap, -C9120AXP-D,iosxe,cat9k,c9100ap, -C9120AXP-E,iosxe,cat9k,c9100ap, -C9120AXP-F,iosxe,cat9k,c9100ap, -C9120AXP-G,iosxe,cat9k,c9100ap, -C9120AXP-H,iosxe,cat9k,c9100ap, -C9120AXP-I,iosxe,cat9k,c9100ap, -C9120AXP-K,iosxe,cat9k,c9100ap, -C9120AXP-N,iosxe,cat9k,c9100ap, -C9120AXP-Q,iosxe,cat9k,c9100ap, -C9120AXP-R,iosxe,cat9k,c9100ap, -C9120AXP-S,iosxe,cat9k,c9100ap, -C9120AXP-T,iosxe,cat9k,c9100ap, -C9120AXP-Z,iosxe,cat9k,c9100ap, -C9130AXE-A,iosxe,cat9k,c9100ap, -C9130AXE-B,iosxe,cat9k,c9100ap, -C9130AXE-C,iosxe,cat9k,c9100ap, -C9130AXE-D,iosxe,cat9k,c9100ap, -C9130AXE-E,iosxe,cat9k,c9100ap, -C9130AXE-F,iosxe,cat9k,c9100ap, -C9130AXE-G,iosxe,cat9k,c9100ap, -C9130AXE-H,iosxe,cat9k,c9100ap, -C9130AXE-I,iosxe,cat9k,c9100ap, -C9130AXE-K,iosxe,cat9k,c9100ap, -C9130AXE-N,iosxe,cat9k,c9100ap, -C9130AXE-Q,iosxe,cat9k,c9100ap, -C9130AXE-R,iosxe,cat9k,c9100ap, -C9130AXE-S,iosxe,cat9k,c9100ap, -C9130AXE-T,iosxe,cat9k,c9100ap, -C9130AXE-Z,iosxe,cat9k,c9100ap, -C9130AXI-A,iosxe,cat9k,c9100ap, -C9130AXI-B,iosxe,cat9k,c9100ap, -C9130AXI-C,iosxe,cat9k,c9100ap, -C9130AXI-D,iosxe,cat9k,c9100ap, -C9130AXI-E,iosxe,cat9k,c9100ap, -C9130AXI-F,iosxe,cat9k,c9100ap, -C9130AXI-G,iosxe,cat9k,c9100ap, -C9130AXI-H,iosxe,cat9k,c9100ap, -C9130AXI-I,iosxe,cat9k,c9100ap, -C9130AXI-K,iosxe,cat9k,c9100ap, -C9130AXI-N,iosxe,cat9k,c9100ap, -C9130AXI-Q,iosxe,cat9k,c9100ap, -C9130AXI-R,iosxe,cat9k,c9100ap, -C9130AXI-S,iosxe,cat9k,c9100ap, -C9130AXI-T,iosxe,cat9k,c9100ap, -C9130AXI-Z,iosxe,cat9k,c9100ap, +C9105AXI-A,cheetah,ap,c9100ap, +C9105AXI-B,cheetah,ap,c9100ap, +C9105AXI-C,cheetah,ap,c9100ap, +C9105AXI-D,cheetah,ap,c9100ap, +C9105AXI-E,cheetah,ap,c9100ap, +C9105AXI-F,cheetah,ap,c9100ap, +C9105AXI-G,cheetah,ap,c9100ap, +C9105AXI-H,cheetah,ap,c9100ap, +C9105AXI-I,cheetah,ap,c9100ap, +C9105AXI-K,cheetah,ap,c9100ap, +C9105AXI-N,cheetah,ap,c9100ap, +C9105AXI-Q,cheetah,ap,c9100ap, +C9105AXI-R,cheetah,ap,c9100ap, +C9105AXI-S,cheetah,ap,c9100ap, +C9105AXI-T,cheetah,ap,c9100ap, +C9105AXI-Z,cheetah,ap,c9100ap, +C9105AXW-A,cheetah,ap,c9100ap, +C9105AXW-B,cheetah,ap,c9100ap, +C9105AXW-C,cheetah,ap,c9100ap, +C9105AXW-D,cheetah,ap,c9100ap, +C9105AXW-E,cheetah,ap,c9100ap, +C9105AXW-F,cheetah,ap,c9100ap, +C9105AXW-G,cheetah,ap,c9100ap, +C9105AXW-H,cheetah,ap,c9100ap, +C9105AXW-I,cheetah,ap,c9100ap, +C9105AXW-K,cheetah,ap,c9100ap, +C9105AXW-N,cheetah,ap,c9100ap, +C9105AXW-Q,cheetah,ap,c9100ap, +C9105AXW-R,cheetah,ap,c9100ap, +C9105AXW-S,cheetah,ap,c9100ap, +C9105AXW-T,cheetah,ap,c9100ap, +C9105AXW-Z,cheetah,ap,c9100ap, +C9115AXE-A,cheetah,ap,c9100ap, +C9115AXE-B,cheetah,ap,c9100ap, +C9115AXE-C,cheetah,ap,c9100ap, +C9115AXE-D,cheetah,ap,c9100ap, +C9115AXE-E,cheetah,ap,c9100ap, +C9115AXE-F,cheetah,ap,c9100ap, +C9115AXE-G,cheetah,ap,c9100ap, +C9115AXE-H,cheetah,ap,c9100ap, +C9115AXE-I,cheetah,ap,c9100ap, +C9115AXE-K,cheetah,ap,c9100ap, +C9115AXE-N,cheetah,ap,c9100ap, +C9115AXE-Q,cheetah,ap,c9100ap, +C9115AXE-R,cheetah,ap,c9100ap, +C9115AXE-S,cheetah,ap,c9100ap, +C9115AXE-T,cheetah,ap,c9100ap, +C9115AXE-Z,cheetah,ap,c9100ap, +C9115AXI-A,cheetah,ap,c9100ap, +C9115AXI-B,cheetah,ap,c9100ap, +C9115AXI-C,cheetah,ap,c9100ap, +C9115AXI-D,cheetah,ap,c9100ap, +C9115AXI-E,cheetah,ap,c9100ap, +C9115AXI-F,cheetah,ap,c9100ap, +C9115AXI-G,cheetah,ap,c9100ap, +C9115AXI-H,cheetah,ap,c9100ap, +C9115AXI-I,cheetah,ap,c9100ap, +C9115AXI-K,cheetah,ap,c9100ap, +C9115AXI-N,cheetah,ap,c9100ap, +C9115AXI-Q,cheetah,ap,c9100ap, +C9115AXI-R,cheetah,ap,c9100ap, +C9115AXI-S,cheetah,ap,c9100ap, +C9115AXI-T,cheetah,ap,c9100ap, +C9115AXI-Z,cheetah,ap,c9100ap, +C9117AXI-A,cheetah,ap,c9100ap, +C9117AXI-B,cheetah,ap,c9100ap, +C9117AXI-C,cheetah,ap,c9100ap, +C9117AXI-D,cheetah,ap,c9100ap, +C9117AXI-E,cheetah,ap,c9100ap, +C9117AXI-F,cheetah,ap,c9100ap, +C9117AXI-G,cheetah,ap,c9100ap, +C9117AXI-H,cheetah,ap,c9100ap, +C9117AXI-I,cheetah,ap,c9100ap, +C9117AXI-K,cheetah,ap,c9100ap, +C9117AXI-N,cheetah,ap,c9100ap, +C9117AXI-Q,cheetah,ap,c9100ap, +C9117AXI-R,cheetah,ap,c9100ap, +C9117AXI-S,cheetah,ap,c9100ap, +C9117AXI-T,cheetah,ap,c9100ap, +C9117AXI-Z,cheetah,ap,c9100ap, +C9120AXE-A,cheetah,ap,c9100ap, +C9120AXE-B,cheetah,ap,c9100ap, +C9120AXE-C,cheetah,ap,c9100ap, +C9120AXE-D,cheetah,ap,c9100ap, +C9120AXE-E,cheetah,ap,c9100ap, +C9120AXE-F,cheetah,ap,c9100ap, +C9120AXE-G,cheetah,ap,c9100ap, +C9120AXE-H,cheetah,ap,c9100ap, +C9120AXE-I,cheetah,ap,c9100ap, +C9120AXE-K,cheetah,ap,c9100ap, +C9120AXE-N,cheetah,ap,c9100ap, +C9120AXE-Q,cheetah,ap,c9100ap, +C9120AXE-R,cheetah,ap,c9100ap, +C9120AXE-S,cheetah,ap,c9100ap, +C9120AXE-T,cheetah,ap,c9100ap, +C9120AXE-Z,cheetah,ap,c9100ap, +C9120AXI-A,cheetah,ap,c9100ap, +C9120AXI-B,cheetah,ap,c9100ap, +C9120AXI-C,cheetah,ap,c9100ap, +C9120AXI-D,cheetah,ap,c9100ap, +C9120AXI-E,cheetah,ap,c9100ap, +C9120AXI-F,cheetah,ap,c9100ap, +C9120AXI-G,cheetah,ap,c9100ap, +C9120AXI-H,cheetah,ap,c9100ap, +C9120AXI-I,cheetah,ap,c9100ap, +C9120AXI-K,cheetah,ap,c9100ap, +C9120AXI-N,cheetah,ap,c9100ap, +C9120AXI-Q,cheetah,ap,c9100ap, +C9120AXI-R,cheetah,ap,c9100ap, +C9120AXI-S,cheetah,ap,c9100ap, +C9120AXI-T,cheetah,ap,c9100ap, +C9120AXI-Z,cheetah,ap,c9100ap, +C9120AXP-A,cheetah,ap,c9100ap, +C9120AXP-B,cheetah,ap,c9100ap, +C9120AXP-C,cheetah,ap,c9100ap, +C9120AXP-D,cheetah,ap,c9100ap, +C9120AXP-E,cheetah,ap,c9100ap, +C9120AXP-F,cheetah,ap,c9100ap, +C9120AXP-G,cheetah,ap,c9100ap, +C9120AXP-H,cheetah,ap,c9100ap, +C9120AXP-I,cheetah,ap,c9100ap, +C9120AXP-K,cheetah,ap,c9100ap, +C9120AXP-N,cheetah,ap,c9100ap, +C9120AXP-Q,cheetah,ap,c9100ap, +C9120AXP-R,cheetah,ap,c9100ap, +C9120AXP-S,cheetah,ap,c9100ap, +C9120AXP-T,cheetah,ap,c9100ap, +C9120AXP-Z,cheetah,ap,c9100ap, +C9130AXE-A,cheetah,ap,c9100ap, +C9130AXE-B,cheetah,ap,c9100ap, +C9130AXE-C,cheetah,ap,c9100ap, +C9130AXE-D,cheetah,ap,c9100ap, +C9130AXE-E,cheetah,ap,c9100ap, +C9130AXE-F,cheetah,ap,c9100ap, +C9130AXE-G,cheetah,ap,c9100ap, +C9130AXE-H,cheetah,ap,c9100ap, +C9130AXE-I,cheetah,ap,c9100ap, +C9130AXE-K,cheetah,ap,c9100ap, +C9130AXE-N,cheetah,ap,c9100ap, +C9130AXE-Q,cheetah,ap,c9100ap, +C9130AXE-R,cheetah,ap,c9100ap, +C9130AXE-S,cheetah,ap,c9100ap, +C9130AXE-T,cheetah,ap,c9100ap, +C9130AXE-Z,cheetah,ap,c9100ap, +C9130AXI-A,cheetah,ap,c9100ap, +C9130AXI-B,cheetah,ap,c9100ap, +C9130AXI-C,cheetah,ap,c9100ap, +C9130AXI-D,cheetah,ap,c9100ap, +C9130AXI-E,cheetah,ap,c9100ap, +C9130AXI-F,cheetah,ap,c9100ap, +C9130AXI-G,cheetah,ap,c9100ap, +C9130AXI-H,cheetah,ap,c9100ap, +C9130AXI-I,cheetah,ap,c9100ap, +C9130AXI-K,cheetah,ap,c9100ap, +C9130AXI-N,cheetah,ap,c9100ap, +C9130AXI-Q,cheetah,ap,c9100ap, +C9130AXI-R,cheetah,ap,c9100ap, +C9130AXI-S,cheetah,ap,c9100ap, +C9130AXI-T,cheetah,ap,c9100ap, +C9130AXI-Z,cheetah,ap,c9100ap, C9200-24P,iosxe,cat9k,c9200, C9200-24PB,iosxe,cat9k,c9200, C9200-24PXG,iosxe,cat9k,c9200, @@ -625,7 +625,6 @@ CISCO7609,ios,c7k,c7600, CISCO7609-S,ios,c7k,c7600, CISCO7613,ios,c7k,c7600, CISCO7613-S,ios,c7k,c7600, -CISCO9004,iosxe,cat9k,cat9k, CR-4430-B,iosxe,c4k,c4400, CR-4430-K9,iosxe,c4k,c4400, CR-4450-ICDN-K9,iosxe,c4k,c4400, diff --git a/src/unicon/plugins/sros/setting.py b/src/unicon/plugins/sros/setting.py index 9abe9a04..9f45c827 100644 --- a/src/unicon/plugins/sros/setting.py +++ b/src/unicon/plugins/sros/setting.py @@ -24,5 +24,7 @@ def __init__(self): self.CLASSIC_INIT_CONFIG_COMMANDS = [] self.DEFAULT_LEARNED_HOSTNAME = r'([^@# \t\n\r\f\v]+)' + self.LEARN_PATTERN = r'([^@# \t\n\r\f\v]+)' + self.ERROR_PATTERN.append("^Error: .*") self.CONFIGURE_ERROR_PATTERN.append("^Error: .*") diff --git a/src/unicon/plugins/tests/mock_data/iosxe/iosxe_mock_data.yaml b/src/unicon/plugins/tests/mock_data/iosxe/iosxe_mock_data.yaml index ff221273..eba25c85 100644 --- a/src/unicon/plugins/tests/mock_data/iosxe/iosxe_mock_data.yaml +++ b/src/unicon/plugins/tests/mock_data/iosxe/iosxe_mock_data.yaml @@ -406,6 +406,8 @@ general_maintenance_mode_stop2: general_config: prompt: "%N(conf)#" commands: &general_config_cmds + "hostname 163-67-30(118)": + new_state: special_hostname_config1 "!end indicator for bulk configure": "" "config-register 0x2102": "" "archive": "" @@ -926,6 +928,8 @@ reload_proceed_confirm: system_config_confirm: prompt: "Would you like to enter the initial configuration dialog? [yes/no]: " commands: + "no": + new_state: general_exec "n": new_state: general_exec "yes": @@ -1587,3 +1591,14 @@ general_enable_reload_to_rommon: <<: *gen_enable_cmds "reload": new_state: press_return + +special_hostname_config1: + prompt: "163-67-30(118)(config)#" + commands: + <<: *gen_enable_cmds + "end": + new_state: special_hostname_enable1 + +special_hostname_enable1: + prompt: "163-67-30(118)#" + commands: *gen_enable_cmds diff --git a/src/unicon/plugins/tests/mock_data/linux/linux_mock_data.yaml b/src/unicon/plugins/tests/mock_data/linux/linux_mock_data.yaml index c9d84b93..c163616b 100644 --- a/src/unicon/plugins/tests/mock_data/linux/linux_mock_data.yaml +++ b/src/unicon/plugins/tests/mock_data/linux/linux_mock_data.yaml @@ -685,3 +685,7 @@ prompt_recovery: "": new_state: exec + +ansi_prompt: + prompt: "\x1b[37mapc>" + commands: *cmds diff --git a/src/unicon/plugins/tests/mock_data/nxos/nxos_mock_data_n5k.yaml b/src/unicon/plugins/tests/mock_data/nxos/nxos_mock_data_n5k.yaml index e7cea604..d68b9997 100644 --- a/src/unicon/plugins/tests/mock_data/nxos/nxos_mock_data_n5k.yaml +++ b/src/unicon/plugins/tests/mock_data/nxos/nxos_mock_data_n5k.yaml @@ -115,6 +115,8 @@ n5k_basic_config_prompt: to skip the remaining dialogs. prompt: "Would you like to enter the basic configuration dialog (yes/no): " commands: + "no": + new_state: n5k_user_access_veri "n": new_state: n5k_user_access_veri diff --git a/src/unicon/plugins/tests/mock_data/nxos/nxos_repeat_poap_reload.yaml b/src/unicon/plugins/tests/mock_data/nxos/nxos_repeat_poap_reload.yaml index 139a866a..55bceca9 100644 --- a/src/unicon/plugins/tests/mock_data/nxos/nxos_repeat_poap_reload.yaml +++ b/src/unicon/plugins/tests/mock_data/nxos/nxos_repeat_poap_reload.yaml @@ -110,4 +110,6 @@ basic_config_prompt: prompt: "Would you like to enter the basic configuration dialog (yes/no):" commands: "n": - new_state: user_access_veri + new_state: user_access_veri + "no": + new_state: user_access_veri diff --git a/src/unicon/plugins/tests/test_plugin_iosxe.py b/src/unicon/plugins/tests/test_plugin_iosxe.py index 66c636b6..26ad80b3 100644 --- a/src/unicon/plugins/tests/test_plugin_iosxe.py +++ b/src/unicon/plugins/tests/test_plugin_iosxe.py @@ -1189,8 +1189,8 @@ def test_config_transition_setting(self): mit=True ) c.connect() - self.assertEqual(c.settings.CONFIG_TRANSITION_WAIT, 0.2) - self.assertEqual(c.spawn.settings.CONFIG_TRANSITION_WAIT, 0.2) + self.assertEqual(c.settings.CONFIG_TRANSITION_WAIT, 15) + self.assertEqual(c.spawn.settings.CONFIG_TRANSITION_WAIT, 15) c.configure() c.settings.CONFIG_TRANSITION_WAIT = 1 c.configure() @@ -1203,12 +1203,10 @@ def test_config_transition_learn_hostname(self): hostname='PE1', start=['mock_device_cli --os iosxe --state enable_slow_config --hostname PE1'], os='iosxe', - mit=True + mit=True, + learn_hostname=True ) c.connect() - # Force hostname learning to be enabled so default prompt pattern is used - # This was causing issues and should not fail with this test - c.state_machine.learn_hostname = True c.configure() @@ -1305,6 +1303,74 @@ def test_switchto_maintenance(self): c.disconnect() +class TestSpecialHostname(unittest.TestCase): + + def test_special_hostname1(self): + con = Connection( + hostname='PE1', + start=['mock_device_cli --os iosxe --state general_enable --hostname PE1'], + os='iosxe', + mit=True + ) + con.connect() + try: + con.configure('hostname 163-67-30(118)', timeout=3) + self.assertEqual(con.hostname, '163-67-30(118)') + self.assertEqual(con.state_machine.hostname, r'163\-67\-30\(118\)') + finally: + con.disconnect() + + def test_special_hostname2(self): + con = Connection( + hostname='PE1', + start=['mock_device_cli --os iosxe --state general_enable --hostname PE1'], + os='iosxe', + mit=True + ) + con.connect() + try: + con.hostname = '163-67-30(118)' + self.assertEqual(con.hostname, '163-67-30(118)') + self.assertEqual(con.state_machine.hostname, r'163\-67\-30\(118\)') + finally: + con.disconnect() + + def test_special_hostname_learn1(self): + con = Connection( + hostname='PE1', + start=['mock_device_cli --os iosxe --state general_password --hostname "163-67-30(118)"'], + credentials=dict(default=dict(username='cisco', password='cisco')), + os='generic', + mit=True, + learn_hostname=True, + learn_tokens=True, + connection_timeout=3, + debug=True + ) + try: + con.connect() + self.assertEqual(con.hostname, '163-67-30(118)') + self.assertEqual(con.state_machine.hostname, r'163\-67\-30\(118\)') + finally: + con.disconnect() + + def test_special_hostname_learn2(self): + con = Connection( + hostname='PE1', + start=['mock_device_cli --os iosxe --state general_password --hostname "cannot learn this "'], + credentials=dict(default=dict(username='cisco', password='cisco')), + os='iosxe', + mit=True, + debug=True, + learn_hostname=True, + connection_timeout=3 + ) + try: + with self.assertRaises(unicon.core.errors.ConnectionError): + con.connect() + finally: + con.disconnect() + if __name__ == "__main__": unittest.main() diff --git a/src/unicon/plugins/tests/test_plugin_linux.py b/src/unicon/plugins/tests/test_plugin_linux.py index dab03ca5..fa5632b2 100644 --- a/src/unicon/plugins/tests/test_plugin_linux.py +++ b/src/unicon/plugins/tests/test_plugin_linux.py @@ -318,6 +318,7 @@ def test_learn_hostname(self): 'exec18': LinuxSettings().DEFAULT_LEARNED_HOSTNAME, 'exec20': 'Linux', 'exec21': 'mock-server', + 'ansi_prompt': 'apc' } for state in states: diff --git a/src/unicon/plugins/utils.py b/src/unicon/plugins/utils.py index cb5eea96..06ee7398 100644 --- a/src/unicon/plugins/utils.py +++ b/src/unicon/plugins/utils.py @@ -491,6 +491,10 @@ def show_results(self): def learn_device_tokens(self, overwrite_testbed_tokens=False): + if not self.con.device: + self.con.log.debug('No device object, cannot learn tokens') + return + if overwrite_testbed_tokens: self.con.log.info('+++ Learning device tokens +++') else: