-
Notifications
You must be signed in to change notification settings - Fork 4
Retries and configure private #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Retries and configure private #1
Conversation
… (tmp until patch approved upstream)
| LOG.debug("Nothing to execute") | ||
| return | ||
|
|
||
| for count in range(num_retries): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need the multiple retries? I think I missed that bit of the conversation.
| error=e) | ||
| LOG.debug(output) | ||
| if not final_attempt: | ||
| LOG.error(e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think technically this this wants to be a warning log, would be an error log on the final error I think.
| with netmiko.ConnectHandler(**self.config) as net_connect: | ||
| net_connect.enable() | ||
| output = net_connect.send_config_set(config_commands=cmd_set) | ||
| output = net_connect.send_config_set( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be tempted to split this into two commits, just so we have the history of each change described in the commit message, for when we come back to this.
| ip='host') | ||
| connect_mock.send_config_set.assert_called_once_with( | ||
| config_commands=['spam ham aaaa']) | ||
| # FIXME: uncommented out once netmiko patch is accepted upstream: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I see any differences here? Which seems wrong I guess.
38ac4be to
c82344d
Compare
| output = net_connect._sanitize_output(output) | ||
| return output | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This entire method is lifted from netmiko, with the only alterations being the selfs being changed to net_connects, config_mode now accepting config_mode_cmd as an arg, and the appropriate extra args passed in at the top
JohnGarbutt
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would really like to try an explicit call to set the config mode, rather than changing the existing method.
Either way we need to make sure this doesn't break the other drivers, which is simpler if its just one extra method call.
| # config_mode_cmd='configure private', | ||
| # config_commands=cmd_set) | ||
| # FIXME: use the above, commented out version of | ||
| # send_config_set once netmiko patch is accepted upstream. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would try just adding this before the existing send_config_set(), I think that should be OK, because it only activates config mode if its not already enabled:
net_connect.config_mode(config_command='configure private')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
technically we would need to copy the save_configuration pattern and add something like start_config_transaction() that is a no op for all drivers except the juniper.
I would have much more hope of getting the above accepted upstream, if that does turn out to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to go this route, we would need a new driver method I think, the default could call net_connect.send_config_set and the juniper driver can override that method with the above changes.
Having said that, I still think my preference is trying an explicit start configuration call that only gets made by the juniper.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just adding net_connect.config_mode(config_command='configure private') would be the ideal solution, I agree, but that's the problem.
As it stands, the send_config_set method in netmiko has a line that reads: output = self.config_mode()
so this will override any value provided (such as 'configure private') and revert to the default for the driver. In the case of juniper, that's ''.
This is why I have had to hack the whole send_config_set method, above.
It's a good point about making this juniper specific though. That was an oversight, I'll fix that now.
| connect_mock = mock.MagicMock(SAVE_CONFIGURATION=None) | ||
| connect_mock.__enter__.return_value = connect_mock | ||
| nm_mock.return_value = connect_mock | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I guess we can drop this.
76825b0 to
7e1569b
Compare
7e1569b to
54f8727
Compare
|
I would too, but as |
9c6874c to
0143af6
Compare
markgoddard
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Darren, some thoughts inline.
| if exit_config_mode: | ||
| output += net_connect.exit_config_mode() | ||
| output = net_connect._sanitize_output(output) | ||
| return output |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this work:
@tenacity.retry(reraise=True, stop=tenacity.stop_after_attempt(3),
wait=tenacity.wait_fixed(14))
def send_config_set(self, net_connect, config_commands=None):
net_connect.config_mode(config_command='configure private')
net_connect.send_config_set(config_commands)
Once set to private config mode, netmiko should not change it to a different config mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a self.config_mode() call within send_config_set prior to any commands being sent, which presumable would reset the config_command back to it's defaults, which in the case of juniper, would be an empty string. Phil has suggested just monkey patching the config_mode, but it would still need to be called from a monkey-patched send_config_set so we may as well just override that, as I've done here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but it's a noop if the switch is already in config mode:
def config_mode(self, config_command='', pattern=''):
"""Enter into config_mode."""
output = ''
if not self.check_config_mode():
self.write_channel(self.normalize_cmd(config_command))
output = self.read_until_pattern(pattern=pattern)
if not self.check_config_mode():
raise ValueError("Failed to enter configuration mode.")
return output
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, the monkey patching I'm suggesting would take place entirely in netmiko_devices.juniper.Juniper and amounts to overriding the superclass _get_connection(). Darren has my example code now to take a look at.
| ) | ||
|
|
||
| @tenacity.retry(reraise=True, stop=tenacity.stop_after_attempt(3), | ||
| wait=tenacity.wait_fixed(14)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
14 seconds seems like quite a long delay. Would it be better to allow more retries and a shorter interval?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Darren and I talked about this. I think he used this time based on my statement as to the average commit duration. I agree with your approach of more retries at a shorter interval.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI, I think your retry could just be wrapped around the net_connect.commt() call in Juniper#save_configuration(), when the former raises a ValueError. You might want to check the error message, though, to only retry when it matches the expected error ('error: configuration database locked' or whatever it is).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds like a reasonable approach. If only they'd used a more specific exception we'd be more sure of what had gone wrong.
| ) | ||
|
|
||
| @tenacity.retry(reraise=True, stop=tenacity.stop_after_attempt(3), | ||
| wait=tenacity.wait_fixed(14)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what conditions will this actually perform a retry? I'm not sure there is much error checking at this level, which is in itself an issue. For instance, if a bogus command were to be entered, I'm not sure that netmiko would detect the failure. It should detect failure to commit changes.
|
No longer required - superseded by #2 |
Added "config_mode_cmd='configure private'" to the send_config_set command for use with juniper switches.
Unfortunatley, this has to include a temporary hack to allow the config mode to be set to "configure private", because there is an
output = self.config_mode()insend_config_set. This will override any external call to config_mode which is required to set it to 'configure private'. The real fix is needed upstream in the netmiko codebase, but until such time as that PR is accepted, this hack is necessary.