scrapli / scrapli_cfg

Network device Configuration Management with scrapli
https://scrapli.github.io/scrapli_cfg/
MIT License
32 stars 4 forks source link

Arista EOS qos profile stuck when loading config snippet #51

Closed MarkRudenko closed 1 year ago

MarkRudenko commented 1 year ago

Describe the bug When configuring the following config on arista_eos execution stops:

qos profile CORE-EGRESS-QUEUING qos trust dscp qos dscp 24 tx-queue 5 bandwidth guaranteed percent 90 tx-queue 6 bandwidth guaranteed 100000

To Reproduce Steps to reproduce the behavior:

  1. Your script

{'auth_password': 'password', 'auth_strict_key': False, 'auth_username': 'user', 'host': 'eos_switch', 'timeout_socket': 20, 'timeout_transport': 20}

with EOSDriver(**device_sync) as conn: cfg_conn = ScrapliCfg(conn=conn) cfg_conn.prepare() cfg_conn.load_config(config=qos_conf)

  1. What you're connecting to (vendor, platform, version) Arista, arista_eos, 4.21.9M

  2. Anything else relevant Most probably the issue is in regular expression which looks for prompts of config sessions

Expected behavior Succesful configuration.

Stack Trace Copy of your stack trace here, please format it properly using triple back ticks (top left key on US keyboards!)

DEBUG:scrapli.driver:load core transport requested DEBUG:scrapli.driver:core transport 'system' loaded successfully DEBUG:scrapli.driver:generating combined network comms prompt pattern DEBUG:scrapli.driver:setting 'comms_prompt_pattern' value to '(^[\w.-@()/: ]{1,63}>\s?$)|(^[\w.-@()/: ]{1,63}#\s?$)|(^[\w.-@()/: ]{1,63}(config[\w.-@/:]{0,32})#\s?$)' INFO:scrapli.driver:opening connection to 'eos_switch' on port '22' DEBUG:scrapli.transport:opening transport connection to 'eos_switch' on port '22' DEBUG:scrapli.transport:created transport 'open_cmd': '['ssh', 'eos_switch', '-p', '22', '-o', 'ConnectTimeout=20', '-o', 'ServerAliveInterval=20', '-l', 'user', '-o', 'StrictHostKeyChecking=no', '-o', 'UserKnownHostsFile=/dev/null', '-F', '/dev/null']' DEBUG:scrapli.transport:transport connection to 'eos_switch' on port '22' opened successfully DEBUG:scrapli.channel:attempting in channel ssh authentication DEBUG:scrapli.channel:read: b"Warning: Permanently added 'eos_switch' (ED25519) to the list of known hosts.\n" DEBUG:scrapli.channel:read: b'' DEBUG:scrapli.channel:read: b"user@eos_switch's password: " DEBUG:scrapli.channel:write: REDACTED DEBUG:scrapli.channel:write: '\n' DEBUG:scrapli.channel:read: b'\n' DEBUG:scrapli.channel:read: b'Last login: Fri Sep 8 07:07:37 2023 from 10.254.241.33\n' DEBUG:scrapli.channel:read: b'\x1b[5n' DEBUG:scrapli.channel:read: b'eos_switch#' DEBUG:scrapli.channel:write: '\n' DEBUG:scrapli.channel:read: b'\n' DEBUG:scrapli.channel:read: b'eos_switch#' INFO:scrapli.driver:attempting to acquire 'privilege_exec' privilege level DEBUG:scrapli.driver:determined current privilege level is one of '['privilege_exec']' DEBUG:scrapli.driver:determined current privilege level is target privilege level, no action needed INFO:scrapli.channel:sending channel input: terminal length 0; strip_prompt: True; eager: False DEBUG:scrapli.channel:write: 'terminal length 0' DEBUG:scrapli.channel:read: b'terminal length 0' DEBUG:scrapli.channel:write: '\n' DEBUG:scrapli.channel:read: b'\n' DEBUG:scrapli.channel:read: b'Pagination disabled.\n' DEBUG:scrapli.channel:read: b'eos_switch#' INFO:scrapli.channel:sending channel input: terminal width 32767; strip_prompt: True; eager: False DEBUG:scrapli.channel:write: 'terminal width 32767' DEBUG:scrapli.channel:read: b'terminal width 32767' DEBUG:scrapli.channel:write: '\n' DEBUG:scrapli.channel:read: b'\n' DEBUG:scrapli.channel:read: b'Width set to 32767 columns.\n' DEBUG:scrapli.channel:read: b'eos_switch#' INFO:scrapli.driver:connection to 'eos_switch' on port '22' opened successfully DEBUG:scrapli_cfg:ScrapliCfg factory initialized INFO:scrapli_cfg.platform:preparing scrapli_cfg connection DEBUG:scrapli_cfg.platform:ignore_version is False, fetching device version INFO:scrapli_cfg.platform:get_version requested INFO:scrapli.channel:sending channel input: show version | i Software image version; strip_prompt: True; eager: False DEBUG:scrapli.channel:write: 'show version | i Software image version' DEBUG:scrapli.channel:read: b'show version | i Software image version' DEBUG:scrapli.channel:write: '\n' DEBUG:scrapli.channel:read: b'\n' DEBUG:scrapli.channel:read: b'Software image version: 4.21.9M\n' DEBUG:scrapli.channel:read: b'eos_switch#' INFO:scrapli_cfg.platform:load_config requested DEBUG:scrapli_cfg.platform:configuration session name will be 'scrapli_cfg_1694157649' DEBUG:scrapli.driver:generating combined network comms prompt pattern DEBUG:scrapli.driver:setting 'comms_promptpattern' value to '(^[\w.-@()/: ]{1,63}>\s?$)|(^[\w.-@()/: ]{1,63}#\s?$)|(^[\w.-@()/: ]{1,63}(config[\w.-@/:]{0,32})#\s?$)|(^[a-z0-9.-@()/: ]{1,63}(config-s-scrapl[a-z0-9.-@/:]{0,32})#\s?$)' DEBUG:scrapli.channel:write: '\n' DEBUG:scrapli.channel:read: b'\neos_switch#' INFO:scrapli.driver:attempting to acquire 'scrapli_cfg_1694157649' privilege level DEBUG:scrapli.driver:determined current privilege level is one of '['privilege_exec']' DEBUG:scrapli.driver:determined privilege escalation necessary INFO:scrapli.channel:sending channel input: configure session scrapli_cfg_1694157649; strip_prompt: True; eager: False DEBUG:scrapli.channel:write: 'configure session scrapli_cfg_1694157649' DEBUG:scrapli.channel:read: b'configure session' DEBUG:scrapli.channel:read: b' scrapli_c' DEBUG:scrapli.channel:read: b'fg_1694157649' DEBUG:scrapli.channel:write: '\n' DEBUG:scrapli.channel:read: b'\n' DEBUG:scrapli.channel:read: b'eos_switch(config-s-scrapl)#' DEBUG:scrapli.channel:write: '\n' DEBUG:scrapli.channel:read: b'\neos_switch(config-s-scrapl)#' INFO:scrapli.driver:attempting to acquire 'scrapli_cfg_1694157649' privilege level DEBUG:scrapli.driver:determined current privilege level is one of '['scrapli_cfg_1694157649']' DEBUG:scrapli.driver:determined current privilege level is target privilege level, no action needed INFO:scrapli.channel:sending channel input: qos profile CORE-EGRESS-QUEUING; strip_prompt: True; eager: False DEBUG:scrapli.channel:write: 'qos profile CORE-EGRESS-QUEUING' DEBUG:scrapli.channel:read: b'qos profile CORE-EGRESS-QUEUING' DEBUG:scrapli.channel:write: '\n' DEBUG:scrapli.channel:read: b'\n' DEBUG:scrapli.channel:read: b'eos_switch(config-s-scrapl-qos-profile-CORE-EGRESS-QUEUING)#' INFO:scrapli.channel:sending channel input: qos trust dscp; strip_prompt: True; eager: False DEBUG:scrapli.channel:write: 'qos trust dscp' DEBUG:scrapli.channel:read: b'qos trust dscp' DEBUG:scrapli.channel:write: '\n' DEBUG:scrapli.channel:read: b'\n' DEBUG:scrapli.channel:read: b'eos_switch(config-s-scrapl-qos-profile-CORE-EGRESS-QUEUING)#' INFO:scrapli.channel:sending channel input: qos dscp 24; strip_prompt: True; eager: False DEBUG:scrapli.channel:write: 'qos dscp 24' DEBUG:scrapli.channel:read: b'qos dscp 24' DEBUG:scrapli.channel:write: '\n' DEBUG:scrapli.channel:read: b'\neos_switch(config-s-scrapl-qos-profile-CORE-EGRESS-QUEUING)#' INFO:scrapli.channel:sending channel input: tx-queue 5; strip_prompt: True; eager: False DEBUG:scrapli.channel:write: 'tx-queue 5' DEBUG:scrapli.channel:read: b'tx-queue 5' DEBUG:scrapli.channel:write: '\n' DEBUG:scrapli.channel:read: b'\n' DEBUG:scrapli.channel:read: b'eos_switch(config-s-scrapl-qos-profile-CORE-EGRESS-QUEUING-txq-5)#' CRITICAL:scrapli.transport:operation timed out, closing connection DEBUG:scrapli.transport:closing transport connection to 'eos_switch' on port '22' CRITICAL:scrapli.transport:encountered EOF reading from transport; typically means the device closed the connection DEBUG:scrapli.transport:transport connection to 'eos_switch' on port '22' closed successfully INFO:scrapli.driver:closing connection to 'eos_switch' on port '22' DEBUG:scrapli.channel:write: '\n'

ScrapliTimeout Traceback (most recent call last)

in 3 cfg_conn.prepare() ----> 4 cfg_conn.load_config(config=qos_conf) 5 /opt/homebrew/lib/python3.9/site-packages/scrapli_cfg/platform/core/arista_eos/sync_platform.py in load_config(self, config, replace, **kwargs) 180 --> 181 config_result = self.conn.send_config( 182 config=config, privilege_level=self.config_session_name /opt/homebrew/lib/python3.9/site-packages/scrapli/driver/network/sync_driver.py in send_config(self, config, strip_prompt, failed_when_contains, stop_on_failed, privilege_level, eager, timeout_ops) 574 # now that we have a list of configs, just use send_configs to actually execute them --> 575 multi_response = self.send_configs( 576 configs=split_config, /opt/homebrew/lib/python3.9/site-packages/scrapli/driver/network/sync_driver.py in send_configs(self, configs, strip_prompt, failed_when_contains, stop_on_failed, privilege_level, eager, timeout_ops) 515 --> 516 responses = super().send_commands( 517 commands=configs, /opt/homebrew/lib/python3.9/site-packages/scrapli/driver/generic/sync_driver.py in send_commands(self, commands, strip_prompt, failed_when_contains, stop_on_failed, eager, timeout_ops) 214 for command in commands[:-1]: --> 215 response = self._send_command( 216 command=command, /opt/homebrew/lib/python3.9/site-packages/scrapli/decorators.py in decorate(*args, **kwargs) 296 if timeout_ops_kwarg is None or timeout_ops_kwarg == driver_instance.timeout_ops: --> 297 result = wrapped_func(*args, **kwargs) 298 else: /opt/homebrew/lib/python3.9/site-packages/scrapli/driver/generic/sync_driver.py in _send_command(self, command, strip_prompt, failed_when_contains, eager, timeout_ops) 137 ) --> 138 raw_response, processed_response = self.channel.send_input( 139 channel_input=command, strip_prompt=strip_prompt, eager=eager /opt/homebrew/lib/python3.9/site-packages/scrapli/decorators.py in decorate(*args, **kwargs) 215 ): --> 216 return _multiprocessing_timeout( 217 transport=transport, /opt/homebrew/lib/python3.9/site-packages/scrapli/decorators.py in _multiprocessing_timeout(transport, logger, timeout, wrapped_func, args, kwargs) 112 ) --> 113 return future.result() 114 /opt/homebrew/Cellar/python@3.9/3.9.17_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/concurrent/futures/_base.py in result(self, timeout) 438 elif self._state == FINISHED: --> 439 return self.__get_result() 440 /opt/homebrew/Cellar/python@3.9/3.9.17_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/concurrent/futures/_base.py in __get_result(self) 390 try: --> 391 raise self._exception 392 finally: /opt/homebrew/Cellar/python@3.9/3.9.17_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/concurrent/futures/thread.py in run(self) 57 try: ---> 58 result = self.fn(*self.args, **self.kwargs) 59 except BaseException as exc: /opt/homebrew/lib/python3.9/site-packages/scrapli/channel/sync_channel.py in send_input(self, channel_input, strip_prompt, eager) 480 if not eager: --> 481 buf += self._read_until_prompt() 482 /opt/homebrew/lib/python3.9/site-packages/scrapli/channel/sync_channel.py in _read_until_prompt(self, buf) 136 while True: --> 137 read_buf.write(self.read()) 138 /opt/homebrew/lib/python3.9/site-packages/scrapli/channel/sync_channel.py in read(self) 68 """ ---> 69 buf = self.transport.read() 70 buf = buf.replace(b"\r", b"") /opt/homebrew/lib/python3.9/site-packages/scrapli/decorators.py in decorate(*args, **kwargs) 215 ): --> 216 return _multiprocessing_timeout( 217 transport=transport, /opt/homebrew/lib/python3.9/site-packages/scrapli/decorators.py in _multiprocessing_timeout(transport, logger, timeout, wrapped_func, args, kwargs) 107 if not future.done(): --> 108 return _handle_timeout( 109 transport=transport, /opt/homebrew/lib/python3.9/site-packages/scrapli/decorators.py in _handle_timeout(transport, logger, message) 133 transport.close() --> 134 raise ScrapliTimeout(message) 135 ScrapliTimeout: timed out reading from transport During handling of the above exception, another exception occurred: ScrapliConnectionNotOpened Traceback (most recent call last) in 2 cfg_conn = ScrapliCfg(conn=conn) 3 cfg_conn.prepare() ----> 4 cfg_conn.load_config(config=qos_conf) 5 /opt/homebrew/lib/python3.9/site-packages/scrapli/driver/base/sync_driver.py in __exit__(self, exception_type, exception_value, traceback) 64 65 """ ---> 66 self.close() 67 68 def open(self) -> None: /opt/homebrew/lib/python3.9/site-packages/scrapli/driver/base/sync_driver.py in close(self) 124 125 if self.on_close: --> 126 self.on_close(self) 127 128 self.transport.close() /opt/homebrew/lib/python3.9/site-packages/scrapli/driver/core/arista_eos/sync_driver.py in eos_on_close(conn) 42 43 """ ---> 44 conn.acquire_priv(desired_priv=conn.default_desired_privilege_level) 45 conn.channel.write(channel_input="exit") 46 conn.channel.send_return() /opt/homebrew/lib/python3.9/site-packages/scrapli/driver/network/sync_driver.py in acquire_priv(self, desired_priv) 154 155 while True: --> 156 current_prompt = self.channel.get_prompt() 157 privilege_action, target_priv = self._process_acquire_priv( 158 destination_priv=desired_priv, /opt/homebrew/lib/python3.9/site-packages/scrapli/decorators.py in decorate(*args, **kwargs) 214 or threading.current_thread() is not threading.main_thread() 215 ): --> 216 return _multiprocessing_timeout( 217 transport=transport, 218 logger=logger, /opt/homebrew/lib/python3.9/site-packages/scrapli/decorators.py in _multiprocessing_timeout(transport, logger, timeout, wrapped_func, args, kwargs) 111 message=_get_timeout_message(func_name=wrapped_func.__name__), 112 ) --> 113 return future.result() 114 115 /opt/homebrew/Cellar/python@3.9/3.9.17_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/concurrent/futures/_base.py in result(self, timeout) 437 raise CancelledError() 438 elif self._state == FINISHED: --> 439 return self.__get_result() 440 441 self._condition.wait(timeout) /opt/homebrew/Cellar/python@3.9/3.9.17_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/concurrent/futures/_base.py in __get_result(self) 389 if self._exception: 390 try: --> 391 raise self._exception 392 finally: 393 # Break a reference cycle with the exception in self._exception /opt/homebrew/Cellar/python@3.9/3.9.17_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/concurrent/futures/thread.py in run(self) 56 57 try: ---> 58 result = self.fn(*self.args, **self.kwargs) 59 except BaseException as exc: 60 self.future.set_exception(exc) /opt/homebrew/lib/python3.9/site-packages/scrapli/channel/sync_channel.py in get_prompt(self) 425 426 with self._channel_lock(): --> 427 self.send_return() 428 429 while True: /opt/homebrew/lib/python3.9/site-packages/scrapli/channel/base_channel.py in send_return(self) 350 351 """ --> 352 self.write(channel_input=self._base_channel_args.comms_return_char) 353 354 @staticmethod /opt/homebrew/lib/python3.9/site-packages/scrapli/channel/base_channel.py in write(self, channel_input, redacted) 334 self.logger.debug(f"write: {log_output}") 335 --> 336 self.transport.write(channel_input=channel_input.encode()) 337 338 def send_return(self) -> None: /opt/homebrew/lib/python3.9/site-packages/scrapli/transport/plugins/system/transport.py in write(self, channel_input) 169 def write(self, channel_input: bytes) -> None: 170 if not self.session: --> 171 raise ScrapliConnectionNotOpened 172 self.session.write(channel_input) ScrapliConnectionNotOpened: connection not opened, but attempting to call a method that requires an open connection, do you need to call 'open()'? **OS (please complete the following information):** MAC OS scrapli 2023.1.30 scrapli-cfg 2023.7.30
carlmontanari commented 1 year ago

yeah I think you're bang on, wanna try updating scrapli platform here (or you can pass custom privilege levels if you dont wanna mess w/ editing scrapli directly) to confirm bumping that {0,32} -> {0,63} on line 34 works for ya? if that looks good I can add some quick test and get that merged this weekend prolly.

MarkRudenko commented 1 year ago

I'm almost sure that it would help =) Please try changing regex. Thanks a lot!

In [1]: import re In [2]: re.search(r"^[\w.-@()/: ]{1,63}(config[\w.-@/:]{0,63})#\s?$", "test-dcs7010t-6(config-s-scrapl-qos-profile-CORE-EGRESS-QUEUING-txq-5)#") Out[2]: <re.Match object; span=(0, 71), match='test-dcs7010t-6(config-s-scrapl-qos-profile-CORE->

carlmontanari commented 1 year ago

sounds good -- will try to get that merged this weekend, will keep ya posted, thanks @MarkRudenko !

MarkRudenko commented 1 year ago

Hello Carl. I found that it also can't determine the privilege level when searching diff.


In [300]: conn = AsyncScrapli(**device)
     ...: conn.privilege_levels["configuration"].pattern = r"^[\w.\-@()/: ]{1,63}\(config[\w.\-@/:]{0,63}\)#\s?$"
     ...: await conn.open()
     ...: cfg_conn = AsyncScrapliCfg(conn=conn)
     ...: await cfg_conn.prepare()
     ...: loaded_cfg = await cfg_conn.load_config(qos_conf)

In [308]: await cfg_conn.diff_config()
INFO:scrapli_cfg.platform:diff_config requested
INFO:scrapli.channel:sending channel input: show session-config diffs; strip_prompt: True; eager: False
DEBUG:scrapli.channel:write: 'show session-config diffs'
DEBUG:scrapli.channel:read: b'show session-config diffs'
DEBUG:scrapli.channel:write: '\n'
DEBUG:scrapli.channel:read: b'\n'
DEBUG:scrapli.channel:read: b'\n'
DEBUG:scrapli.channel:read: b'eos_switch(config-s-scrapl-qos-profile-CORE-EGRESS-QUEUING-txq-6)#'
INFO:scrapli_cfg.platform:get_config for config source 'running' requested
DEBUG:scrapli.channel:write: '\n'
DEBUG:scrapli.channel:read: b'\n'
DEBUG:scrapli.channel:read: b'eos_switch(config-s-scrapl-qos-profile-CORE-EGRESS-QUEUING-txq-6)#'
INFO:scrapli.driver:attempting to acquire 'privilege_exec' privilege level
CRITICAL:scrapli.driver:could not determine privilege level from provided prompt: 'eos_switch(config-s-scrapl-qos-profile-CORE-EGRESS-QUEUING-txq-6)#'
---------------------------------------------------------------------------
ScrapliPrivilegeError                     Traceback (most recent call last)
<ipython-input-308-06b1126ae972> in <module>
----> 1 await cfg_conn.diff_config()

/opt/homebrew/lib/python3.9/site-packages/scrapli_cfg/platform/core/arista_eos/async_platform.py in diff_config(self, source)
    263             device_diff = diff_result.result
    264
--> 265             source_config_result = await self.get_config(source=source)
    266             source_config = source_config_result.result
    267

/opt/homebrew/lib/python3.9/site-packages/scrapli_cfg/platform/core/arista_eos/async_platform.py in get_config(self, source)
    126         response = self._pre_get_config(source=source)
    127
--> 128         config_result = await self.conn.send_command(
    129             command=self._get_config_command(source=source)
    130         )

/opt/homebrew/lib/python3.9/site-packages/scrapli/driver/network/async_driver.py in send_command(self, command, strip_prompt, failed_when_contains, timeout_ops)
    236
    237         """
--> 238         await self._acquire_appropriate_privilege_level()
    239
    240         if failed_when_contains is None:

/opt/homebrew/lib/python3.9/site-packages/scrapli/driver/network/async_driver.py in _acquire_appropriate_privilege_level(self, privilege_level)
    206
    207         if self._current_priv_level.name != resolved_privilege_level:
--> 208             await self.acquire_priv(desired_priv=resolved_privilege_level)
    209
    210     async def send_command(

/opt/homebrew/lib/python3.9/site-packages/scrapli/driver/network/async_driver.py in acquire_priv(self, desired_priv)
    155         while True:
    156             current_prompt = await self.channel.get_prompt()
--> 157             privilege_action, target_priv = self._process_acquire_priv(
    158                 destination_priv=desired_priv,
    159                 current_prompt=current_prompt,

/opt/homebrew/lib/python3.9/site-packages/scrapli/driver/network/base_driver.py in _process_acquire_priv(self, destination_priv, current_prompt)
    333
    334         # decide if we are already at the desired priv, then we don't need to do any thing!
--> 335         current_priv_patterns = self._determine_current_priv(current_prompt=current_prompt)
    336
    337         if self._current_priv_level.name in current_priv_patterns:

/opt/homebrew/lib/python3.9/site-packages/scrapli/driver/network/base_driver.py in _determine_current_priv(self, current_prompt)
    148             msg = f"could not determine privilege level from provided prompt: '{current_prompt}'"
    149             self.logger.critical(msg)
--> 150             raise ScrapliPrivilegeError(msg)
    151
    152         self.logger.debug(f"determined current privilege level is one of '{matching_priv_levels}'")

ScrapliPrivilegeError: could not determine privilege level from provided prompt: 'eos_switch(config-s-scrapl-qos-profile-CORE-EGRESS-QUEUING-txq-6)#'
MarkRudenko commented 1 year ago

It looks like that regex in EOSDriverBase should be changed also ({0,32} -> {0,63}):

rf"^[a-z0-9.\-@()/: ]{{1,63}}\(config\-s\-{sess_prompt}[a-z0-9_.\-@/:]{{0,32}}\)#\s?$"

Also, I noticed stupid EOS behavior. When you configure sessions in some cases the command

sh session-config diffs

won't show diff until you are at the top session.

The example:


test_eos(config-s-sess-2)#qos profile CORE-EGRESS-QUEUING
test_eos(config-s-sess-2-qos-profile-CORE-EGRESS-QUEUING)#qos trust dscp
test_eos(config-s-sess-2-qos-profile-CORE-EGRESS-QUEUING)#qos dscp 24
test_eos(config-s-sess-2-qos-profile-CORE-EGRESS-QUEUING)#tx-queue 5
test_eos(config-s-sess-2-qos-profile-CORE-EGRESS-QUEUING-txq-5)#bandwidth guaranteed percent 90
test_eos(config-s-sess-2-qos-profile-CORE-EGRESS-QUEUING-txq-5)#tx-queue 6
test_eos(config-s-sess-2-qos-profile-CORE-EGRESS-QUEUING-txq-6)# bandwidth guaranteed 100000
test_eos(config-s-sess-2-qos-profile-CORE-EGRESS-QUEUING-txq-6)#sh session-config diffs

test_eos(config-s-sess-2-qos-profile-CORE-EGRESS-QUEUING-txq-6)#ex
test_eos(config-s-sess-2-qos-profile-CORE-EGRESS-QUEUING)#sh session-config diffs

test_eos(config-s-sess-2-qos-profile-CORE-EGRESS-QUEUING)#ex
test_eos(config-s-sess-2)#sh session-config diffs
--- system:/running-config
+++ session:/sess-2349--957367488-0-session-config
@@ -27,6 +27,16 @@
 !
 ntp server vrf networkmanagement 10.144.2.253
 ntp server vrf networkmanagement 10.144.2.254
+!
+qos profile CORE-EGRESS-QUEUING
+   qos trust dscp
+   qos dscp 24
+   !
+   tx-queue 5
+      bandwidth guaranteed percent 90
+   !
+   tx-queue 6
+      bandwidth guaranteed 100000
 !

This is why diff will be empty. Unfortunately in EOS, there is no analog of the IOS XR root command, which would be useful here.

I made a workaround in my code by sending some exit commands until the current prompt equals to so-called target one:

In [489]: conn = AsyncScrapli(**device)
     ...: conn.privilege_levels["configuration"].pattern = r"^[\w.\-@()/: ]{1,63}\(config[\w.\-@/:]{0,63}\)#\s?$"
     ...: await conn.open()
     ...: cfg_conn = AsyncScrapliCfg(conn=conn)
     ...: await cfg_conn.prepare()
     ...: loaded_cfg = await cfg_conn.load_config(qos_conf)
     ...: session_name = cfg_conn.config_session_name
     ...: sess_prompt = re.escape(session_name[:6])
     ...: conn.privilege_levels[session_name].pattern = rf"^[a-z0-9.\-@()/: ]{{1,63}}\(config\-s\-{sess_prompt}[a-z0-9_.\-@/:]{{0,63}}\)#\s?$"
     ...: target_sessions_prompt = conn.host + f"(config-s-{sess_prompt})#"
     ...: for attempt in range(2):
     ...:     print(await conn.channel.get_prompt(), "<<<<<"*50)
     ...:     if conn.channel.get_prompt() != target_sessions_prompt:
     ...:         conn.transport.write(b"exit\n")
     ...: diff = await cfg_conn.diff_config()
     ...: await cfg_conn.abort_config()

It works somehow but maybe you will invent more elegant solution =)

carlmontanari commented 1 year ago

It looks like that regex in EOSDriverBase should be changed also ({0,32} -> {0,63}):

good catch, will get that updated too!

This is why diff will be empty. Unfortunately in EOS, there is no analog of the IOS XR root command, which would be useful here.

ahhh... I see what you mean. I looked quickly and there doesnt seem to be a "exit to root" (or something similar) option, but I am no eos expert so maybe that exists?

we could for sure make it work, but I wonder if its worth the effort? could you just include exit exit (or however many are needed) in the config you load? is that an option? not ideal for sure but seems like the simplest way to deal.

anyway, if you were interested in adding something like what you've got to scrapli-cfg im open to it, but I probably wont work that up myself.

regarding the prompt stuff, ive just pushed a commit to scrapli repo that has the changes here, you can see that in this commit -- so until the next release you can install from the repo!

I'll go ahead and close this out now but holler if that didn't fix things for some reason!

thanks for the help @MarkRudenko !

MarkRudenko commented 1 year ago

I think you are totally right it's much easier to send some additional exits. But yes scraply is a great lib thank you for your effort.