Skip to content

Commit a71f21c

Browse files
committed
Releasing v25.4
1 parent e4fec41 commit a71f21c

24 files changed

+415
-14
lines changed

docs/changelog/2025/april.rst

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
April 2025
2+
==========
3+
4+
April 29 - Unicon v25.4
5+
------------------------
6+
7+
8+
9+
.. csv-table:: Module Versions
10+
:header: "Modules", "Versions"
11+
12+
``unicon.plugins``, v25.4
13+
``unicon``, v25.4
14+
15+
16+
17+
18+
Changelogs
19+
^^^^^^^^^^
20+
--------------------------------------------------------------------------------
21+
New
22+
--------------------------------------------------------------------------------
23+
24+
* connection
25+
* Added os learn version
26+
* Added ability to set learn_tokens and overwrite_testbed_tokens from a config file or environment variable
27+
* Environment Variables
28+
* UNICON_LEARN_TOKENS
29+
* UNICON_OVERWRITE_TESTBED_TOKENS
30+
* UNICON_LEARN_AND_OVERWRITE_TOKENS
31+
* Config File
32+
* [unicon]
33+
* learn_tokens
34+
* overwrite_testbed_tokens
35+
* learn_and_overwrite_tokens
36+
37+
* connection_provider
38+
* Modified update_os_version
39+
* Updated logic to execute 'show install summary' only on first connnection
40+
41+
42+
--------------------------------------------------------------------------------
43+
Fix
44+
--------------------------------------------------------------------------------
45+
46+
* linux
47+
* Modified linux connection provider
48+
* Wait for connection_timeout/2 on initial connection for the device to respond with some output
49+
50+
* generic
51+
* Add TRANSITION_WAIT setting to make transition wait time configurable
52+
53+
54+
--------------------------------------------------------------------------------
55+
Fix
56+
--------------------------------------------------------------------------------
57+
58+
* linux
59+
* Added support for prompt_line
60+
61+
* iosxe
62+
* IosXEPatterns
63+
* Updated the recovery-mode regex to match prompt
64+
65+
* iosxe_mock_data.yaml
66+
* Added 'show install summary' output in mock yaml
67+
68+
69+
--------------------------------------------------------------------------------
70+
New
71+
--------------------------------------------------------------------------------
72+
73+
* iosxe
74+
* add test for learn os
75+
76+

docs/changelog/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Changelog
44
.. toctree::
55
:maxdepth: 2
66

7+
2025/april
78
2025/march
89
2025/february
910
2025/january
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
April 2025
2+
==========
3+
4+
April 29 - Unicon.Plugins v25.4
5+
------------------------
6+
7+
8+
9+
.. csv-table:: Module Versions
10+
:header: "Modules", "Versions"
11+
12+
``unicon.plugins``, v25.4
13+
``unicon``, v25.4
14+
15+
16+
17+
18+
Changelogs
19+
^^^^^^^^^^
20+
--------------------------------------------------------------------------------
21+
Fix
22+
--------------------------------------------------------------------------------
23+
24+
* iosxr
25+
* Update admin host pattern
26+
* Update prompt commands to recover console
27+
28+
* generic
29+
* Updated output variable by passing count argument, To get get rid of messages like 'DeprecationWarning 'count' is passed as positional argument'
30+
* Update escape handler to support a list of prompt commands
31+
32+
33+
--------------------------------------------------------------------------------
34+
New
35+
--------------------------------------------------------------------------------
36+
37+
* iosxe
38+
* Cat9k
39+
* Support for HA ROMMON
40+
* Added support for no enable password being set. A UniconAuthenticationError will be raised if the enable password is not set and the user tries to enable the device.
41+
42+
* generic
43+
* Return output of HAReloadService to match with generic ReloadService
44+
45+

docs/changelog_plugins/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Plugins Changelog
44
.. toctree::
55
:maxdepth: 2
66

7+
2025/april
78
2025/march
89
2025/february
910
2025/january

docs/user_guide/connection.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,19 @@ To make use of this feature, you can choose from the following actions:
15441544
arguments:
15451545
learn_tokens: True
15461546
1547+
4. Use a pyats.conf file
1548+
1549+
.. code-block:: ini
1550+
1551+
[unicon]
1552+
learn_tokens = True
1553+
1554+
5. Use an environment variable
1555+
1556+
.. code-block:: bash
1557+
1558+
export UNICON_LEARN_TOKENS=1
1559+
15471560
By default, token discovery will not overwrite tokens that you have already defined in your testbed file.
15481561
It will only assign discovered tokens to the device object if the token does not yet exist or if the value is generic. For example: `platform: generic`.
15491562
@@ -1588,3 +1601,17 @@ This flag can be set in the same way as `learn_tokens`:
15881601
learn_tokens: True
15891602
overwrite_testbed_tokens: True
15901603
1604+
4. Use a pyats.conf file
1605+
1606+
.. code-block:: ini
1607+
1608+
[unicon]
1609+
learn_tokens = True
1610+
overwrite_testbed_tokens = True
1611+
1612+
5. Use an environment variable
1613+
1614+
.. code-block:: bash
1615+
1616+
export UNICON_LEARN_TOKENS=1
1617+
export UNICON_OVERWRITE_TESTBED_TOKENS=1

src/unicon/plugins/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = '25.3'
1+
__version__ = '25.4'
22

33
supported_chassis = [
44
'single_rp',

src/unicon/plugins/generic/patterns.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,5 @@ def __init__(self):
9090
self.new_password = r'^(Enter new password|Confirm password):\s*$'
9191

9292
self.enter_your_encryption_selection_2 = r'^.*?Enter your encryption selection( \[2])?:\s*$'
93+
94+
self.no_password_set = r'^.*% (No password set|Error in authentication.).*'

src/unicon/plugins/generic/service_implementation.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ def call_service(self, command=[], # noqa: C901
753753
output = self.extra_output_process(output)
754754
output = output.replace(command, "", 1)
755755
# only strip first newline and leave formatting intact
756-
output = re.sub(r"^\r?\r\n", "", output, 1)
756+
output = re.sub(r"^\r?\r\n", "", output, count=1)
757757
output = output.rstrip()
758758

759759
if command in command_output:
@@ -1083,9 +1083,6 @@ def __init__(self, connection, context, **kwargs):
10831083
self.timeout = connection.settings.RELOAD_TIMEOUT
10841084
self.dialog = Dialog(reload_statement_list + default_statement_list)
10851085
self.log_buffer = io.StringIO()
1086-
lb = UniconStreamHandler(self.log_buffer)
1087-
lb.setFormatter(logging.Formatter(fmt=UNICON_LOG_FORMAT))
1088-
self.connection.log.addHandler(lb)
10891086
self.__dict__.update(kwargs)
10901087

10911088
def call_service(self,
@@ -1124,6 +1121,10 @@ def call_service(self,
11241121
raise ValueError('append_error_pattern should be a list')
11251122
self.error_pattern += append_error_pattern
11261123

1124+
lb = UniconStreamHandler(self.log_buffer)
1125+
lb.setFormatter(logging.Formatter(fmt=UNICON_LOG_FORMAT))
1126+
self.connection.log.addHandler(lb)
1127+
11271128
# Clear log buffer
11281129
self.log_buffer.seek(0)
11291130
self.log_buffer.truncate()
@@ -1235,6 +1236,8 @@ def call_service(self,
12351236
# clear buffer
12361237
self.log_buffer.truncate()
12371238

1239+
self.connection.log.removeHandler(lb)
1240+
12381241
if return_output:
12391242
self.result = ReloadResult(self.result, reload_output)
12401243

@@ -2053,6 +2056,7 @@ def __init__(self, connection, context, **kwargs):
20532056
self.timeout = connection.settings.HA_RELOAD_TIMEOUT
20542057
self.dialog = Dialog(ha_reload_statement_list + default_statement_list)
20552058
self.command = 'reload'
2059+
self.log_buffer = io.StringIO()
20562060
self.__dict__.update(kwargs)
20572061

20582062
def call_service(self, # noqa: C901
@@ -2085,6 +2089,14 @@ def call_service(self, # noqa: C901
20852089
raise ValueError('append_error_pattern should be a list')
20862090
self.error_pattern += append_error_pattern
20872091

2092+
lb = UniconStreamHandler(self.log_buffer)
2093+
lb.setFormatter(logging.Formatter(fmt=UNICON_LOG_FORMAT))
2094+
self.connection.log.addHandler(lb)
2095+
2096+
# Clear log buffer
2097+
self.log_buffer.seek(0)
2098+
self.log_buffer.truncate()
2099+
20882100
if reply:
20892101
if dialog:
20902102
con.log.warning("**** Both 'reply' and 'dialog' were provided "
@@ -2232,6 +2244,13 @@ def call_service(self, # noqa: C901
22322244
counter += 1
22332245

22342246
con.log.info("+++ Reload Completed Successfully +++")
2247+
self.log_buffer.seek(0)
2248+
reload_output = self.log_buffer.read()
2249+
# clear buffer
2250+
self.log_buffer.truncate()
2251+
2252+
self.connection.log.removeHandler(lb)
2253+
22352254
self.result = True
22362255
if return_output:
22372256
self.result = ReloadResult(self.result, reload_output)

src/unicon/plugins/generic/settings.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ def __init__(self):
9999
# (wait time: 0.5, 1, 1.5, 2, 2.5, 3, 3.5 == total wait: 14.0s)
100100
self.ESCAPE_CHAR_PROMPT_WAIT_RETRIES = 7
101101

102+
# commands to get a prompt, default to "enter"
103+
self.ESCAPE_CHAR_PROMPT_COMMANDS = ['\r']
104+
102105
# syslog message handling timers
103106
self.SYSLOG_WAIT = 1
104107
# syslog wait time for reload service

src/unicon/plugins/generic/statements.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,21 @@ def escape_char_callback(spawn):
186186
# store current know buffer
187187
known_buffer = len(spawn.buffer.strip())
188188

189+
# list of commands to iterate through
190+
cmds = spawn.settings.ESCAPE_CHAR_PROMPT_COMMANDS
191+
iter_cmds = iter(cmds)
192+
189193
for retry_number in range(spawn.settings.ESCAPE_CHAR_PROMPT_WAIT_RETRIES):
190-
# hit enter
191-
spawn.sendline()
194+
195+
# iterate through the commands
196+
try:
197+
cmd = next(iter_cmds)
198+
except StopIteration:
199+
iter_cmds = iter(cmds)
200+
cmd = next(iter(iter_cmds))
201+
202+
# send command (typically "\r")
203+
spawn.send(cmd)
192204

193205
# incremental wait logic
194206
buffer_wait(spawn, spawn.settings.ESCAPE_CHAR_PROMPT_WAIT * (retry_number + 1))
@@ -453,6 +465,10 @@ def incorrect_login_handler(spawn, context, session):
453465
raise UniconAuthenticationError(
454466
'Login failure, either wrong username or password')
455467

468+
def no_password_handler(spawn, context, session):
469+
""" handles no password prompt
470+
"""
471+
raise UniconAuthenticationError('No password set on this device')
456472

457473
def sudo_password_handler(spawn, context, session):
458474
""" Password handler for sudo command
@@ -593,6 +609,12 @@ def __init__(self):
593609
loop_continue=True,
594610
continue_timer=False)
595611

612+
self.no_password_set_stmt = Statement(pattern=pat.no_password_set,
613+
action=no_password_handler,
614+
args=None,
615+
loop_continue=True,
616+
continue_timer=False)
617+
596618
self.disconnect_error_stmt = Statement(pattern=pat.disconnect_message,
597619
action=connection_failure_handler,
598620
args=None,
@@ -782,6 +804,7 @@ def __init__(self):
782804

783805
authentication_statement_list = [generic_statements.bad_password_stmt,
784806
generic_statements.login_incorrect,
807+
generic_statements.no_password_set_stmt,
785808
generic_statements.login_stmt,
786809
generic_statements.useraccess_stmt,
787810
generic_statements.new_password_stmt,

0 commit comments

Comments
 (0)