-
Notifications
You must be signed in to change notification settings - Fork 558
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
add canonical_interface #493
add canonical_interface #493
Conversation
Function to retun interface canonical name | ||
This puposely does not use regex, or first X characters, to ensure | ||
there is no chance for false positives. As an example, Po = PortChannel, and | ||
PO = POS. With either character or regex, that would produce a false positive. |
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.
regexes are case sensitive so po.*
and Po.*
are not going to cause any false positives. regexes has the advantage the code becomes simpler and more reliable as it should work if cisco in their next version decide to come up with yet another combination of short letters.
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 am usually regex heavy, but having been bit by this issue nearly a decade ago, I am cautious about it. As an example, there is Virtual-Access and Virtual-Template, the idea of getting this right the first time, without being explicit does not seem likely.
This should be helpful only, and like I said, knowing the pain it has caused me before, I try and do as much as possible to to avoid false positive's.
I have a question regarding this PR: Does cisco only hire sociopaths that hate people and deliberately make things horrible so they are harder to consume? |
napalm/base/interface_map.py
Outdated
], | ||
"VLAN_short": "Vl" | ||
}, | ||
"os_map": { |
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.
Is there any reason to break this down by platform? Why not just have a single mapping for all?
I am a bit confused by the implementation. I might be missing something but I am not sure why:
I think this could be extremely simplified by doing something like:
Unless I am missing something, of course. |
Under napalm-automation/hackathon-2017#5 I had the feeling we have agreed this would be a method defined under each driver. Of course I wouldn't have a problem having this helper, but this way we'd solve a couple of things:
|
|
|
In regards to:
I think I am missing something, how does that account for |
ffs why won't people stop buying cisco crap... Anyway, you can add extra k,v pairs to the reverse mapping. But what's the use case for the short naming anyway? Are you expecting people to do something like:
|
So thought it if I wanted to use neighbors data to generate a diagram, I would want to use short name in diagram. So some kind of toggle to force short name usage. |
Ok, makes sense. |
@dbarrosop Closed as in not gonna happen, hold off until reunification done, or need to update implementation?? |
no, sorry, I didn't close it. It was a side effect of deleting the branch. Reopining against develop |
My todo:
Outstanding questions:
Other than that, is everything good to go here? |
I think the method should provide a predefined dictionary that doesn't care about vendors ("po" is going to a port-channel regardless of the platform or is simply not have any meaning). Regarding non-regex approach, sure, if you can make it simpler than this #493 (comment) be my guest. |
Works as expected: |
napalm/ios/ios.py
Outdated
@@ -433,6 +435,13 @@ def commit_config(self): | |||
# Save config to startup (both replace and merge) | |||
output += self.device.send_command_expect("write mem") | |||
|
|||
def _canonical_int(self, interface): |
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 this one should be in the base driver.
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.
My thought process was
- Within the os, the only variable is interface
- Shorten from
napalm.base.helpers.canonical_interface(interface, "cisco_ios", True)
->self._canonical_int(interface)
napalm/base/helpers.py
Outdated
if not change: | ||
return interface | ||
|
||
if device_os == "cisco_nxos_ssh": |
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'd get rid of the device_os
tbh. I don't see much value in doing this check. What we could do instead is replace the device_os
with a fail_on_miss
bool that does something like:
try:
return reverse_mapping[long_int] + str(interface_number)
except KeyError:
if fail_on_miss:
raise
return long_int+str(interface_number)
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 am super cautious in making sure I know I am not messing up something here. I'm fine either way, and in reality this only get's turned on if implemented in said os driver, so maybe not that big of an issue.
I would like to keep the transposing of cisco_nxos_ssh
-> cisco_nxos
in case there needs to be a base_interfaces = base_interfaces.update(interface_map.get(device_os))
on the nxos.
napalm/base/helpers.py
Outdated
@@ -255,3 +256,47 @@ def as_number(as_number_val): | |||
return (int(big) << 16) + int(little) | |||
else: | |||
return int(as_number_str) | |||
|
|||
|
|||
def canonical_interface(interface, device_os, change, short=False): |
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 this should be two different methods; one for each direction. @mirceaulinic @ktbyers thoughts?
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'm ok either way. To clarify to M & K, one way is short to long (et -> Ethernet), the other is long to short (Ethernet -> eth ).
napalm/base/interface_map.py
Outdated
from __future__ import unicode_literals | ||
|
||
|
||
def _interface_map(): |
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 don't you just expose the variables directly so you can do something like:
from napalm.base import interface_map
...
short_name = interface_map.long_to_short[iface_name]
long_name = interface_map.short_to_long[short_name]
napalm/base/helpers.py
Outdated
int_map = _interface_map() | ||
base_interfaces = int_map['base_interfaces'] | ||
reverse_mapping = int_map['reverse_mapping'] | ||
if int_map.get(device_os): |
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 should come from the driver itself.
class IOSDriver(NetworkDriver):
# probably needs a better name but this is a class attribute
long_to_short = {"my_unique_iface_name_that_collides_with_something_else": "ohmy"}
...
def canonical_interface(interface, device, change, short=False):
...
base_interfaces = base_interfaces.update(device.long_to_short)
...
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.
Missed this before.
Per conversations with @itdependsnetworks I think Ken is going to move this to the parent class: def _canonical_int(self, interface): I will review after that is completed. |
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.
See feedback.
napalm/ios/ios.py
Outdated
@@ -128,6 +128,8 @@ def __init__(self, hostname, username, password, timeout=60, optional_args=None) | |||
|
|||
self.profile = ["ios"] | |||
|
|||
self.set_canonical_interface = optional_args.get('canonical_int', False) |
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.
How about self.use_canonical_int
? I don't like set_canonical_interface
as it makes me think we are doing a setter on an attribute.
napalm/base/helpers.py
Outdated
@@ -255,3 +256,41 @@ def as_number(as_number_val): | |||
return (int(big) << 16) + int(little) | |||
else: | |||
return int(as_number_str) | |||
|
|||
|
|||
def canonical_interface(interface, change, short=False, update_long_to_short=None): |
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 not make this be?
def canonical_interface(interface, change=True, short=False, update_long_to_short=None):
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.
Do we even need change
argument since we would always call this with True?
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.
Also you need to document the arguments here? Particularly short
and update_long_to_short
?
So update_long_to_short
is a dictionary of additional names someone could feed in going from abbreviated names to full names? I don't think that name makes sense.
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.
np with it being change=False
, but the idea was to not change it so drastically at first. @mirceaulinic (and I) had concerns about changing outcome so abruptly, perhaps next major version update, after we know it is good?
Documentation will be easier once everything has been agreed upon.
napalm/base/helpers.py
Outdated
@@ -19,6 +19,7 @@ | |||
import napalm.base.exceptions | |||
from napalm.base.utils.jinja_filters import CustomJinjaFilters | |||
from napalm.base.utils import py23_compat | |||
from napalm.base.interface_map import interface_map |
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.
We shouldn't use the name interface_map
since there is already an existing interface_map
in the ios.py code.
napalm/base/base.py
Outdated
@@ -1583,3 +1583,11 @@ def compliance_report(self, validation_file=None, validation_source=None): | |||
""" | |||
return validate.compliance_report(self, validation_file=validation_file, | |||
validation_source=validation_source) | |||
|
|||
def canonical_int(self, interface): |
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 this probably should be a private method, for end-user calls people can just directly call what is in helpers
.
napalm/base/interface_map.py
Outdated
from __future__ import unicode_literals | ||
|
||
|
||
interface_map = { |
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.
Shouldn't be named interface_map
due to existing name conflict.
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.
Also file name should not be interface_map.py
.
napalm/base/interface_map.py
Outdated
|
||
|
||
interface_map = { | ||
"base_interfaces": { |
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 a nested dictionary as opposed to two separate dictionaries? I think they should be two separate dictionaries.
napalm/base/helpers.py
Outdated
if base_interfaces.get(interface_type): | ||
long_int = base_interfaces.get(interface_type) | ||
# return short form if set | ||
if short is True: |
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.
Do we want to overload this method with returning the abbreviated name or have a second method? I think it is more logical to have these as two separate methods.
- Method one returns the canonical name.
- Method two returns the abbreviated name (from the canonical name).
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.
Was waiting on consensus on this
@mirceaulinic and @dbarrosop I think I have a +1 from Kirk. Let me know if anything should change, going to start implementing everywhere tomorrow my AM. I will probably work on a proposal PR for writing output files tonight. |
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 reviewed this extensively with @itdependsnetworks.
Added canonical_interface and updated some linter complaints when I first had everything in string_parsers.py.
Didn't want to go to crazy until I had consensus on naming and implementation.