Skip to content

Commit 205d5b5

Browse files
authored
Merge pull request #115 from CiscoTestAutomation/release_25.7
Releasing v25.7
2 parents 3719657 + 9532f94 commit 205d5b5

23 files changed

+1025
-17
lines changed

.github/workflows/run_tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414

1515
strategy:
1616
matrix:
17-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
17+
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
1818
group: [1, 2, 3, 4, 5]
1919
steps:
2020
- uses: actions/checkout@v2

docs/changelog/2025/july.rst

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
July 2025
2+
==========
3+
4+
July 29 - Unicon v25.7
5+
------------------------
6+
7+
8+
9+
.. csv-table:: Module Versions
10+
:header: "Modules", "Versions"
11+
12+
``unicon.plugins``, v25.7
13+
``unicon``, v25.7
14+
15+
16+
17+
18+
Changelogs
19+
^^^^^^^^^^
20+
--------------------------------------------------------------------------------
21+
Fix
22+
--------------------------------------------------------------------------------
23+
24+
* bases/router
25+
* connection_provider
26+
* Updated logout service logic for single rp and multi rp connections
27+
28+
* router/connection
29+
* Initialized the UNICON_BACKEND_DECODE_ERROR_LIMIT to None for iosxr HA device connections
30+
31+
32+
--------------------------------------------------------------------------------
33+
New
34+
--------------------------------------------------------------------------------
35+
36+
* mock device
37+
* Add mock device for svl stack
38+
39+
* iosxe
40+
* Added cert-trustpool config pattern
41+
42+

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/july
78
2025/june
89
2025/may
910
2025/april
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
July 2025
2+
==========
3+
4+
July 29 - Unicon.Plugins v25.7
5+
------------------------
6+
7+
8+
9+
.. csv-table:: Module Versions
10+
:header: "Modules", "Versions"
11+
12+
``unicon.plugins``, v25.7
13+
``unicon``, v25.7
14+
15+
16+
17+
18+
Changelogs
19+
^^^^^^^^^^
20+
--------------------------------------------------------------------------------
21+
Fix
22+
--------------------------------------------------------------------------------
23+
24+
* iosxr
25+
* switchover
26+
* Fixed timeout handling by using explicit timeout parameter instead of self.timeout.
27+
* Update monitor service prompt pattern
28+
* Increase monitor stop timeout
29+
* Update execute() service to exit unsupported modes (e.g. monitor mode)
30+
31+
32+
--------------------------------------------------------------------------------
33+
New
34+
--------------------------------------------------------------------------------
35+
36+
* iosxe
37+
* Added support for 9500 and 9500x SVL switchover
38+
39+

docs/changelog_plugins/changelog_modify_generice_rommon_prompt_pattern_20241028185708.rst

Lines changed: 0 additions & 5 deletions
This file was deleted.

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/july
78
2025/june
89
2025/may
910
2025/april

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
'sphinx.ext.autodoc',
3737
'sphinx.ext.napoleon',
3838
'sphinx.ext.intersphinx',
39-
'sphinxcontrib.mockautodoc',
39+
# 'sphinxcontrib.mockautodoc',
4040
]
4141

4242
if os.environ.get('DEVNET', None) == 'true':

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.6"
1+
__version__ = "25.7"
22

33
supported_chassis = [
44
'single_rp',

src/unicon/plugins/iosxe/cat9k/c9500x/stackwise_virtual/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class IosXEC9500xStackwiseVirtualServiceList(StackIosXEServiceList):
1313
def __init__(self):
1414
super().__init__()
1515
self.reload = svc.SVLStackReload
16+
self.switchover = svc.SVLStackSwitchover
1617

1718

1819
class IosXEC9500xStackwiseVirtualRPConnection(IosXEStackRPConnection):

src/unicon/plugins/iosxe/cat9k/c9500x/stackwise_virtual/service_implementation.py

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
from unicon.plugins.iosxe.stack.utils import StackUtils
1212
from unicon.plugins.generic.statements import custom_auth_statements
1313
from unicon.plugins.iosxe.stack.service_statements import (switch_prompt,
14-
stack_reload_stmt_list)
14+
stack_reload_stmt_list,
15+
stack_switchover_stmt_list)
1516

1617
utils = StackUtils()
1718

@@ -247,3 +248,110 @@ def boot(con):
247248
if return_output:
248249
Result = namedtuple('Result', ['result', 'output'])
249250
self.result = Result(self.result, reload_cmd_output.match_output.replace(reload_cmd, '', 1))
251+
252+
253+
class SVLStackSwitchover(BaseService):
254+
""" Get Rp state
255+
256+
Service to get the redundancy state of the device rp.
257+
258+
Arguments:
259+
target: Service target, by default active
260+
261+
Returns:
262+
Expected return values are ACTIVE, STANDBY, MEMBER
263+
raise SubCommandFailure on failure.
264+
265+
Example:
266+
.. code-block:: python
267+
268+
rtr.get_rp_state()
269+
rtr.get_rp_state(target='standby')
270+
"""
271+
272+
def __init__(self, connection, context, **kwargs):
273+
super().__init__(connection, context, **kwargs)
274+
self.start_state = 'enable'
275+
self.end_state = 'enable'
276+
self.timeout = connection.settings.STACK_SWITCHOVER_TIMEOUT
277+
self.command = "redundancy force-switchover"
278+
self.dialog = Dialog(stack_switchover_stmt_list)
279+
self.__dict__.update(kwargs)
280+
281+
def call_service(self, command=None,
282+
reply=Dialog([]),
283+
timeout=None,
284+
*args, **kwargs):
285+
286+
switchover_cmd = command or self.command
287+
timeout = timeout or self.timeout
288+
conn = self.connection.active
289+
290+
expected_active_sw = self.connection.standby.member_id
291+
dialog = self.dialog
292+
293+
if reply:
294+
dialog = reply + self.dialog
295+
296+
# added connection dialog in case switchover ask for username/password
297+
connect_dialog = self.connection.connection_provider.get_connection_dialog()
298+
dialog += connect_dialog
299+
300+
conn.log.info('Processing on active rp %s-%s' % (conn.hostname, conn.alias))
301+
conn.sendline(switchover_cmd)
302+
try:
303+
# A loop has been implemented to handle the
304+
# "Press RETURN to get started" prompt twice. Based on extensive
305+
# testing during SVL reloads on 9500x devices, it was observed
306+
# that the device is not fully ready after the first prompt.
307+
# As a result, the logic accounts for this behavior by waiting for
308+
# the second occurrence of the message, which is assumed to be the
309+
# default behavior for these devices.
310+
for _ in range(2):
311+
match_object = dialog.process(conn.spawn, timeout=timeout,
312+
prompt_recovery=self.prompt_recovery,
313+
context=conn.context)
314+
except Exception as e:
315+
raise SubCommandFailure('Error during switchover ', e) from e
316+
317+
# try boot up original active rp with current active system
318+
# image, if it moved to rommon state.
319+
if 'state' in conn.context and conn.context.state == 'rommon':
320+
try:
321+
conn.state_machine.detect_state(conn.spawn, context=conn.context)
322+
conn.state_machine.go_to('enable', conn.spawn, timeout=timeout,
323+
prompt_recovery=self.prompt_recovery,
324+
context=conn.context, dialog=Dialog([switch_prompt]))
325+
except Exception as e:
326+
self.connection.log.warning('Fail to bring up original active rp from rommon state.', e)
327+
finally:
328+
conn.context.pop('state')
329+
330+
# To ensure the stack is ready to accept the login
331+
self.connection.log.info('Sleeping for %s secs.' % \
332+
self.connection.settings.POST_SWITCHOVER_SLEEP)
333+
sleep(self.connection.settings.POST_SWITCHOVER_SLEEP)
334+
335+
# check all members are ready
336+
conn.state_machine.detect_state(conn.spawn, context=conn.context)
337+
338+
interval = self.connection.settings.SWITCHOVER_POSTCHECK_INTERVAL
339+
if utils.is_all_member_ready(conn, timeout=timeout, interval=interval):
340+
self.connection.log.info('All members are ready.')
341+
else:
342+
self.connection.log.info('Timeout in %s secs. '
343+
'Not all members are in Ready state.' % timeout)
344+
self.result = False
345+
return
346+
347+
self.connection.log.info('Disconnecting and reconnecting')
348+
self.connection.disconnect()
349+
self.connection.connect()
350+
351+
self.connection.log.info('Verifying active and standby switch State.')
352+
if self.connection.active.member_id == expected_active_sw:
353+
self.connection.log.info('Switchover successful')
354+
self.result = True
355+
else:
356+
self.connection.log.info('Switchover failed')
357+
self.result = False

0 commit comments

Comments
 (0)