Skip to content
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

Idea for Hackathon - Normalize Interface #5

Open
itdependsnetworks opened this issue Oct 14, 2017 · 12 comments
Open

Idea for Hackathon - Normalize Interface #5

itdependsnetworks opened this issue Oct 14, 2017 · 12 comments

Comments

@itdependsnetworks
Copy link

Normalize Interface

I currently do this in post-processing, but would be nice to have an option to return an normalized interface. This is essentially what I do: https://github.com/itdependsnetworks/Scripts/blob/master/normalize_interface/normalize_interface.py

@itdependsnetworks
Copy link
Author

This came up from netbox (see above), I am trying to gauge the core teams willingness to allow?

@ktbyers @dbarrosop @mirceaulinic

@mirceaulinic
Copy link
Member

mirceaulinic commented Oct 18, 2017

Great topic, thanks for raising this, Ken.

I like the idea of having the interfaces names normalised (I guess that's one of the few things our getter does not present in a uniform shape).

I fear changing the output of the existing getters, get_interfaces and get_interfaces_ip - but not limited to, will be directly affected by this changes, and this comes with a cascade of problems:

  • Documentation.
  • Backwards compatibility.
  • Reliability, meaning: are we going to really cover all the possible interfaces? Can we really do this?

But, as I said, I like the idea, and here's what I would suggest:

  1. For the beginning introduce a public method in each driver, say normalise_interface_name, that given an interface name having the platform format, returns the NAPALM uniform format, e.g.:
>>> d = get_network_driver('junos')
>>> j = d(...)
>>> j.open()
...
>>> j.normalise_interface_name('xe-0/0/0')
TenGigEth0/0/0

(just a dummy return, of course, cause y'all ❤️ crisco)

This would be good for various reasons, for whenever the user needs that. And it would also work well to be invoked in Netbox, or anywhere else. If the user trusts this, they can anyway post-process the output from the NAPALM getters with this and get the output they need.

  1. We can eventually add a flag to the getters in subject, i.e., def get_interfaces(self, normalise_interfaces=False).

  2. In time, when we'll consider this helper as being stable enough, we can eventually reconsider if the normalise_interfaces flag should default to True. But, in my opinion, this is way too early right now.

@ktbyers
Copy link

ktbyers commented Oct 18, 2017

Yes, I like what @mirceaulinic proposed here....that sounds like a good solution to this problem.

I think we could also have issues on how do we lookup the canonical name from the shortened name since the shortened name could have a lot of different potential forms.

@itdependsnetworks
Copy link
Author

Agreed with the approach. I do just want to note, my solution is low impact since it defaults back to the original if there is no match. I use it internally with good results.

The real change (and impact ) is with new results when upgrading.

@jeremystretch
Copy link

Would it be sufficient simply to include the port description along with the ID? Here's what we see on a device with an IOS neighbor:

Neighbour Information:
Chassis type       : Mac address
Chassis ID         : 88:5a:92:4e:12:ff
Port type          : Interface name
Port ID            : Te1/49
Port description   : TenGigabitEthernet1/49
System name        : cisco-switch

Here's what NAPALM reports:

"xe-0/0/4": [
    {
        "hostname": "cisco-switch",
        "port": "Te1/49"
    }
]

Including the port description should provide the full name, at least for IOS. This would be enough for us over at NetBox, because we could consider a match on either string to indicate a valid connection.

(On the IOS switch I'm looking at, setting a local description on the interface does not appear to change what is reported via LLDP. Junos is less predictable, but it only ever uses one form of interface ID anyway.)

@itdependsnetworks
Copy link
Author

That would be specific to the getter, and my suggestion would be to open an issue against it to be discussed. This method would be across all getters.

I would argue that the issue is still there, in that local interface would still (presumably) be the shortname, depending on vendor implementation.

@dbarrosop
Copy link
Member

dbarrosop commented Oct 19, 2017

@jeremystretch yeah, if you want that info you probably should open an issue for discussion. Current get_lldp_neighbors is quite bare so I wouldn't mind discussing adding more info.

Regarding the whole idea, I like the idea but only partially. If we are to normalize interfaces, which I think we should, I would certainly not use a naming scheme that emulates how Cisco does it because I think it's terrible and basically why we are in this mess. I think basically that interface names should be named after $type$id and that's it. For example:

  • ethernet1/1, vlan23, atm321, etc. That also means the whole translation is extremely simpler because it's a matter of finding patterns rather than having to map every potential combination. For example, to know it's of ethernet type it would be as simple as:
regex = re.compile("(?P<name>(\w|-)*)(?P<id>\d+.*)")
match = regex.match(raw_name)

ifname = match.group('name').lower()
ifid = match.group('id').lower()

ethertypes_start_with = ['gi', 'fa', 'hu', 'xe', ....]
vlans_start_with = ['vl', 'blah', 'bleh', ...]
...
if 'eth' in ifname or ifname[0:2] in ethertypes_start_with:
    prefix = "ethernet"
elif 'vlan' in ifname or ifname[0:2] in vlans_start_with:
    prefix = "vlan"
...
return "{}.{}".format(prefix, ifid)

@dbarrosop
Copy link
Member

dbarrosop commented Oct 19, 2017

Then there is the matter on how to best deploy this and I agree with the plan @mirceaulinic presented but I'd suggest instead to use an optional_arg to enable it for the whole session so the user doesn't have to specify it in every single method.

@itdependsnetworks
Copy link
Author

@dbarrosop I would not suggest using regex here. What is the difference between a POS1 and Po1 as an example (POS vs Port-channel). tbh, this is based on: https://github.com/gitpan/Net-Telnet-Cisco-IOS/blob/374e304db5966fc6362f479dafcfdf88c91f0af7/lib/Net/Telnet/Cisco/IOS.pm#L619 which worked much better than my prior regex attempts.

My intention is to leave it as found in the configuration. I can understand normalizing et, vl, etc... As a value add, but I suspect most use it as I do to have one source of truth of interfaces. In addition, this is a valid cisco config (though both daughter cards never exist at same time)

interface GigabitEthernet1/1/1
!
interface GigabitEthernet1/1/2
!
interface GigabitEthernet1/1/3
!
interface GigabitEthernet1/1/4
!
interface TenGigabitEthernet1/1/1
!
interface TenGigabitEthernet1/1/2
!

@dbarrosop
Copy link
Member

dbarrosop commented Oct 20, 2017

What is the difference between a POS1 and Po1

I don't see the problem. In my example code you would have (ifname: pos, id: 1) and (ifname: po, id: 2) respectively so it works as expected:

if ifname == 'pos':
    # we got pos
elif ifname[0:2] == 'po':
    # we got a port-channel

My intention is to leave it as found in the configuration

So the scope is not to normalize but to expand to the longer form of the name?

@itdependsnetworks
Copy link
Author

@dbarrasop correct, should probably adjust the name

@dbarrosop
Copy link
Member

Ok, I have no opinion then :P

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants