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

get_network_instances does not return expected results #1922

Closed
jlixfeld opened this issue May 15, 2023 · 6 comments · Fixed by #1923
Closed

get_network_instances does not return expected results #1922

jlixfeld opened this issue May 15, 2023 · 6 comments · Fixed by #1923
Assignees

Comments

@jlixfeld
Copy link

          @jlixfeld, can you relate what the return value of `get_network_instances` when ran against the device in question, and open a new issue if it's not as expected (I suspect `default` will have a `v6:no` "interface" in it

Originally posted by @bewing in #1919 (comment)

So a couple of things going on here, I think. get_network_instances() output differs from what is expected:

>>> from napalm import get_network_driver
>>> driver = get_network_driver('eos')
>>> device = driver("*", "*", "*")
>>> device.open()
>>> device.get_network_instances()
{'default': {'name': 'default', 'type': 'DEFAULT_INSTANCE', 'state': {'route_distinguisher': ''}, 'interfaces': {'interface': {}}}, 'management': {'name': 'management', 'type': 'L3VRF', 'state': {'route_distinguisher': ''}, 'interfaces': {'interface': {'Management1': {}}}}}
>>> device.cli(["show vrf"], encoding="json")
{'show vrf': {'vrfs': {'default': {'routeDistinguisher': '', 'vrfState': 'up', 'interfaces': ['Ethernet1', 'Ethernet2', 'Ethernet3', 'Ethernet4', 'Ethernet49/1', 'Ethernet5', 'Ethernet50/1', 'Ethernet52/1', 'Ethernet53/1', 'Ethernet54/1', 'Ethernet55/1', 'Ethernet56/1', 'Loopback0'], 'protocols': {'ipv4': {'routingState': 'up', 'protocolState': 'up', 'supported': True}, 'ipv6': {'routingState': 'down', 'protocolState': 'up', 'supported': True}}}, 'management': {'routeDistinguisher': '', 'vrfState': 'up', 'interfaces': ['Management1'], 'protocols': {'ipv4': {'routingState': 'up', 'protocolState': 'up', 'supported': True}, 'ipv6': {'routingState': 'down', 'protocolState': 'up', 'supported': True}}}}}}
>>> device.cli(["show ip interface"], encoding="json")
{'show ip interface': {'interfaces': {'Management1': {'directedBroadcastEnabled': False, 'interfaceAddress': {'secondaryIpsOrderedList': [], 'broadcastAddress': '255.255.255.255', 'virtualSecondaryIps': {}, 'dhcp': False, 'secondaryIps': {}, 'primaryIp': {'maskLen': 20, 'address': '172.28.137.81'}, 'virtualSecondaryIpsOrderedList': [], 'virtualIp': {'maskLen': 0, 'address': '0.0.0.0'}}, 'name': 'Management1', 'urpf': 'disable', 'interfaceStatus': 'connected', 'maxMssEgress': 0, 'maxMssIngress': 0, 'enabled': True, 'mtu': 1500, 'addresslessForwarding': 'isInvalid', 'interfaceAddressBrief': {'ipAddr': {'maskLen': 20, 'address': '172.28.137.81'}}, 'vrf': 'management', 'localProxyArp': False, 'injectHosts': False, 'proxyArp': False, 'ipv4Routable240': False, 'gratuitousArp': False, 'lineProtocolStatus': 'up', 'description': ''}, 'Ethernet50/1': {'directedBroadcastEnabled': False, 'interfaceAddress': {'secondaryIpsOrderedList': [], 'broadcastAddress': '255.255.255.255', 'virtualSecondaryIps': {}, 'dhcp': False, 'secondaryIps': {}, 'primaryIp': {'maskLen': 24, 'address': '100.1.9.9'}, 'virtualSecondaryIpsOrderedList': [], 'virtualIp': {'maskLen': 0, 'address': '0.0.0.0'}}, 'name': 'Ethernet50/1', 'urpf': 'disable', 'interfaceStatus': 'connected', 'maxMssEgress': 0, 'maxMssIngress': 0, 'enabled': True, 'mtu': 9202, 'addresslessForwarding': 'isInvalid', 'interfaceAddressBrief': {'ipAddr': {'maskLen': 24, 'address': '100.1.9.9'}}, 'vrf': 'default', 'localProxyArp': False, 'injectHosts': False, 'proxyArp': False, 'ipv4Routable240': False, 'gratuitousArp': False, 'lineProtocolStatus': 'up', 'description': ''}, 'Ethernet2': {'directedBroadcastEnabled': False, 'interfaceAddress': {'secondaryIpsOrderedList': [], 'broadcastAddress': '255.255.255.255', 'virtualSecondaryIps': {}, 'dhcp': False, 'secondaryIps': {}, 'primaryIp': {'maskLen': 24, 'address': '20.9.2.1'}, 'virtualSecondaryIpsOrderedList': [], 'virtualIp': {'maskLen': 0, 'address': '0.0.0.0'}}, 'name': 'Ethernet2', 'urpf': 'disable', 'interfaceStatus': 'connected', 'maxMssEgress': 0, 'maxMssIngress': 0, 'enabled': True, 'mtu': 9202, 'addresslessForwarding': 'isInvalid', 'interfaceAddressBrief': {'ipAddr': {'maskLen': 24, 'address': '20.9.2.1'}}, 'vrf': 'default', 'localProxyArp': False, 'injectHosts': False, 'proxyArp': False, 'ipv4Routable240': False, 'gratuitousArp': False, 'lineProtocolStatus': 'up', 'description': ''}, 'Ethernet3': {'directedBroadcastEnabled': False, 'interfaceAddress': {'secondaryIpsOrderedList': [], 'broadcastAddress': '255.255.255.255', 'virtualSecondaryIps': {}, 'dhcp': False, 'secondaryIps': {}, 'primaryIp': {'maskLen': 24, 'address': '20.9.3.1'}, 'virtualSecondaryIpsOrderedList': [], 'virtualIp': {'maskLen': 0, 'address': '0.0.0.0'}}, 'name': 'Ethernet3', 'urpf': 'disable', 'interfaceStatus': 'connected', 'maxMssEgress': 0, 'maxMssIngress': 0, 'enabled': True, 'mtu': 9202, 'addresslessForwarding': 'isInvalid', 'interfaceAddressBrief': {'ipAddr': {'maskLen': 24, 'address': '20.9.3.1'}}, 'vrf': 'default', 'localProxyArp': False, 'injectHosts': False, 'proxyArp': False, 'ipv4Routable240': False, 'gratuitousArp': False, 'lineProtocolStatus': 'up', 'description': ''}, 'Ethernet54/1': {'directedBroadcastEnabled': False, 'interfaceAddress': {'secondaryIpsOrderedList': [], 'broadcastAddress': '255.255.255.255', 'virtualSecondaryIps': {}, 'dhcp': False, 'secondaryIps': {}, 'primaryIp': {'maskLen': 24, 'address': '20.9.54.1'}, 'virtualSecondaryIpsOrderedList': [], 'virtualIp': {'maskLen': 0, 'address': '0.0.0.0'}}, 'name': 'Ethernet54/1', 'urpf': 'disable', 'interfaceStatus': 'connected', 'maxMssEgress': 0, 'maxMssIngress': 0, 'enabled': True, 'mtu': 9202, 'addresslessForwarding': 'isInvalid', 'interfaceAddressBrief': {'ipAddr': {'maskLen': 24, 'address': '20.9.54.1'}}, 'vrf': 'default', 'localProxyArp': False, 'injectHosts': False, 'proxyArp': False, 'ipv4Routable240': False, 'gratuitousArp': False, 'lineProtocolStatus': 'up', 'description': ''}, 'Ethernet1': {'directedBroadcastEnabled': False, 'interfaceAddress': {'secondaryIpsOrderedList': [], 'broadcastAddress': '255.255.255.255', 'virtualSecondaryIps': {}, 'dhcp': False, 'secondaryIps': {}, 'primaryIp': {'maskLen': 24, 'address': '100.9.54.9'}, 'virtualSecondaryIpsOrderedList': [], 'virtualIp': {'maskLen': 0, 'address': '0.0.0.0'}}, 'name': 'Ethernet1', 'urpf': 'disable', 'interfaceStatus': 'connected', 'maxMssEgress': 0, 'maxMssIngress': 0, 'enabled': True, 'mtu': 1500, 'addresslessForwarding': 'isInvalid', 'interfaceAddressBrief': {'ipAddr': {'maskLen': 24, 'address': '100.9.54.9'}}, 'vrf': 'default', 'localProxyArp': False, 'injectHosts': False, 'proxyArp': False, 'ipv4Routable240': False, 'gratuitousArp': False, 'lineProtocolStatus': 'up', 'description': ''}, 'Ethernet4': {'directedBroadcastEnabled': False, 'interfaceAddress': {'secondaryIpsOrderedList': [], 'broadcastAddress': '255.255.255.255', 'virtualSecondaryIps': {}, 'dhcp': False, 'secondaryIps': {}, 'primaryIp': {'maskLen': 24, 'address': '20.9.4.1'}, 'virtualSecondaryIpsOrderedList': [], 'virtualIp': {'maskLen': 0, 'address': '0.0.0.0'}}, 'name': 'Ethernet4', 'urpf': 'disable', 'interfaceStatus': 'connected', 'maxMssEgress': 0, 'maxMssIngress': 0, 'enabled': True, 'mtu': 9202, 'addresslessForwarding': 'isInvalid', 'interfaceAddressBrief': {'ipAddr': {'maskLen': 24, 'address': '20.9.4.1'}}, 'vrf': 'default', 'localProxyArp': False, 'injectHosts': False, 'proxyArp': False, 'ipv4Routable240': False, 'gratuitousArp': False, 'lineProtocolStatus': 'up', 'description': ''}, 'Ethernet5': {'directedBroadcastEnabled': False, 'interfaceAddress': {'secondaryIpsOrderedList': [], 'broadcastAddress': '255.255.255.255', 'virtualSecondaryIps': {}, 'dhcp': False, 'secondaryIps': {}, 'primaryIp': {'maskLen': 24, 'address': '20.9.5.1'}, 'virtualSecondaryIpsOrderedList': [], 'virtualIp': {'maskLen': 0, 'address': '0.0.0.0'}}, 'name': 'Ethernet5', 'urpf': 'disable', 'interfaceStatus': 'connected', 'maxMssEgress': 0, 'maxMssIngress': 0, 'enabled': True, 'mtu': 9202, 'addresslessForwarding': 'isInvalid', 'interfaceAddressBrief': {'ipAddr': {'maskLen': 24, 'address': '20.9.5.1'}}, 'vrf': 'default', 'localProxyArp': False, 'injectHosts': False, 'proxyArp': False, 'ipv4Routable240': False, 'gratuitousArp': False, 'lineProtocolStatus': 'up', 'description': ''}, 'Ethernet52/1': {'directedBroadcastEnabled': False, 'interfaceAddress': {'secondaryIpsOrderedList': [], 'broadcastAddress': '255.255.255.255', 'virtualSecondaryIps': {}, 'dhcp': False, 'secondaryIps': {}, 'primaryIp': {'maskLen': 24, 'address': '20.9.52.1'}, 'virtualSecondaryIpsOrderedList': [], 'virtualIp': {'maskLen': 0, 'address': '0.0.0.0'}}, 'name': 'Ethernet52/1', 'urpf': 'disable', 'interfaceStatus': 'connected', 'maxMssEgress': 0, 'maxMssIngress': 0, 'enabled': True, 'mtu': 9202, 'addresslessForwarding': 'isInvalid', 'interfaceAddressBrief': {'ipAddr': {'maskLen': 24, 'address': '20.9.52.1'}}, 'vrf': 'default', 'localProxyArp': False, 'injectHosts': False, 'proxyArp': False, 'ipv4Routable240': False, 'gratuitousArp': False, 'lineProtocolStatus': 'up', 'description': ''}, 'Ethernet55/1': {'directedBroadcastEnabled': False, 'interfaceAddress': {'secondaryIpsOrderedList': [], 'broadcastAddress': '255.255.255.255', 'virtualSecondaryIps': {}, 'dhcp': False, 'secondaryIps': {}, 'primaryIp': {'maskLen': 24, 'address': '20.9.55.1'}, 'virtualSecondaryIpsOrderedList': [], 'virtualIp': {'maskLen': 0, 'address': '0.0.0.0'}}, 'name': 'Ethernet55/1', 'urpf': 'disable', 'interfaceStatus': 'connected', 'maxMssEgress': 0, 'maxMssIngress': 0, 'enabled': True, 'mtu': 9202, 'addresslessForwarding': 'isInvalid', 'interfaceAddressBrief': {'ipAddr': {'maskLen': 24, 'address': '20.9.55.1'}}, 'vrf': 'default', 'localProxyArp': False, 'injectHosts': False, 'proxyArp': False, 'ipv4Routable240': False, 'gratuitousArp': False, 'lineProtocolStatus': 'up', 'description': ''}, 'Ethernet56/1': {'directedBroadcastEnabled': False, 'interfaceAddress': {'secondaryIpsOrderedList': [], 'broadcastAddress': '255.255.255.255', 'virtualSecondaryIps': {}, 'dhcp': False, 'secondaryIps': {}, 'primaryIp': {'maskLen': 24, 'address': '20.9.56.1'}, 'virtualSecondaryIpsOrderedList': [], 'virtualIp': {'maskLen': 0, 'address': '0.0.0.0'}}, 'name': 'Ethernet56/1', 'urpf': 'disable', 'interfaceStatus': 'connected', 'maxMssEgress': 0, 'maxMssIngress': 0, 'enabled': True, 'mtu': 9202, 'addresslessForwarding': 'isInvalid', 'interfaceAddressBrief': {'ipAddr': {'maskLen': 24, 'address': '20.9.56.1'}}, 'vrf': 'default', 'localProxyArp': False, 'injectHosts': False, 'proxyArp': False, 'ipv4Routable240': False, 'gratuitousArp': False, 'lineProtocolStatus': 'up', 'description': ''}, 'Ethernet53/1': {'directedBroadcastEnabled': False, 'interfaceAddress': {'secondaryIpsOrderedList': [], 'broadcastAddress': '255.255.255.255', 'virtualSecondaryIps': {}, 'dhcp': False, 'secondaryIps': {}, 'primaryIp': {'maskLen': 24, 'address': '20.9.53.1'}, 'virtualSecondaryIpsOrderedList': [], 'virtualIp': {'maskLen': 0, 'address': '0.0.0.0'}}, 'name': 'Ethernet53/1', 'urpf': 'disable', 'interfaceStatus': 'connected', 'maxMssEgress': 0, 'maxMssIngress': 0, 'enabled': True, 'mtu': 9202, 'addresslessForwarding': 'isInvalid', 'interfaceAddressBrief': {'ipAddr': {'maskLen': 24, 'address': '20.9.53.1'}}, 'vrf': 'default', 'localProxyArp': False, 'injectHosts': False, 'proxyArp': False, 'ipv4Routable240': False, 'gratuitousArp': False, 'lineProtocolStatus': 'up', 'description': ''}, 'Loopback0': {'directedBroadcastEnabled': False, 'interfaceAddress': {'secondaryIpsOrderedList': [], 'broadcastAddress': '255.255.255.255', 'virtualSecondaryIps': {}, 'dhcp': False, 'secondaryIps': {}, 'primaryIp': {'maskLen': 32, 'address': '10.0.9.1'}, 'virtualSecondaryIpsOrderedList': [], 'virtualIp': {'maskLen': 0, 'address': '0.0.0.0'}}, 'name': 'Loopback0', 'urpf': 'disable', 'interfaceStatus': 'connected', 'maxMssEgress': 0, 'maxMssIngress': 0, 'enabled': True, 'mtu': 65535, 'addresslessForwarding': 'isInvalid', 'interfaceAddressBrief': {'ipAddr': {'maskLen': 32, 'address': '10.0.9.1'}}, 'vrf': 'default', 'localProxyArp': False, 'injectHosts': False, 'proxyArp': False, 'ipv4Routable240': False, 'gratuitousArp': False, 'lineProtocolStatus': 'up', 'description': ''}, 'Ethernet49/1': {'directedBroadcastEnabled': False, 'interfaceAddress': {'secondaryIpsOrderedList': [], 'broadcastAddress': '255.255.255.255', 'virtualSecondaryIps': {}, 'dhcp': False, 'secondaryIps': {}, 'primaryIp': {'maskLen': 24, 'address': '100.2.9.9'}, 'virtualSecondaryIpsOrderedList': [], 'virtualIp': {'maskLen': 0, 'address': '0.0.0.0'}}, 'name': 'Ethernet49/1', 'urpf': 'disable', 'interfaceStatus': 'connected', 'maxMssEgress': 0, 'maxMssIngress': 0, 'enabled': True, 'mtu': 9202, 'addresslessForwarding': 'isInvalid', 'interfaceAddressBrief': {'ipAddr': {'maskLen': 24, 'address': '100.2.9.9'}}, 'vrf': 'default', 'localProxyArp': False, 'injectHosts': False, 'proxyArp': False, 'ipv4Routable240': False, 'gratuitousArp': False, 'lineProtocolStatus': 'up', 'description': ''}}}}
>>> 

I don't know for sure, but I suspect newer versions of EOS have changed the formatting of the show vrfs such that textfsm_extractor() templates aren't catching on.

However, newer versions of EOS support json encoding for show vrf now, which could make these differences easier to deal with (and is the output I included above, not the text version).

napalm/napalm/eos/eos.py

Lines 2121 to 2185 in cb4845c

def _show_vrf(self):
commands = ["show vrf"]
# This command has no JSON yet
raw_output = self._run_commands(commands, encoding="text")[0].get("output", "")
output = napalm.base.helpers.textfsm_extractor(self, "vrf", raw_output)
return output
def _get_vrfs(self):
output = self._show_vrf()
vrfs = [str(vrf["name"]) for vrf in output]
vrfs.append("default")
return vrfs
def get_network_instances(self, name=""):
"""get_network_instances implementation for EOS."""
output = self._show_vrf()
vrfs = {}
all_vrf_interfaces = {}
for vrf in output:
if (
vrf.get("route_distinguisher", "") == "<not set>"
or vrf.get("route_distinguisher", "") == "None"
):
vrf["route_distinguisher"] = ""
else:
vrf["route_distinguisher"] = str(vrf["route_distinguisher"])
interfaces = {}
for interface_raw in vrf.get("interfaces", []):
interface = interface_raw.split(",")
for line in interface:
if line.strip() != "":
interfaces[str(line.strip())] = {}
all_vrf_interfaces[str(line.strip())] = {}
vrfs[str(vrf["name"])] = {
"name": str(vrf["name"]),
"type": "L3VRF",
"state": {"route_distinguisher": vrf["route_distinguisher"]},
"interfaces": {"interface": interfaces},
}
all_interfaces = self.get_interfaces_ip().keys()
vrfs["default"] = {
"name": "default",
"type": "DEFAULT_INSTANCE",
"state": {"route_distinguisher": ""},
"interfaces": {
"interface": {
k: {} for k in all_interfaces if k not in all_vrf_interfaces.keys()
}
},
}
if name:
if name in vrfs:
return {str(name): vrfs[name]}
return {}
else:
return vrfs

@bewing
Copy link
Member

bewing commented May 15, 2023

Per docs, we still support back to 4.15F, which does not have the JSON version of "show vrf". We'd have to have a deeper discussion to break that backwards compatibility / promise, and most likely target a major release for it.

It might make more sense just to treat the VRF output as a variable fixed-width table, and parse the width of each column by looking at the dasher line, rather than relying on regexes.

@bewing
Copy link
Member

bewing commented May 15, 2023

I have a working fixed-width parser, but it's breaking on https://github.com/napalm-automation/napalm/blob/develop/test/eos/mocked_data/test_get_network_instances/issue-509/show_vrf.text
because the text doesn't line up. Trying to figure out if I collected this output erroneously, or if older versions of EOS couldn't output proper fixed-width tables when line lengths got long

@bewing
Copy link
Member

bewing commented May 15, 2023

Wow, the output is even weirder in newer cEOS deploys:

r1#show version
Arista cEOSLab
Hardware version:
Serial number: CFF252FBD6B21E43DE19BCA5A9A7FCC1
Hardware MAC address: 001c.73af.ea6e
System MAC address: 001c.73af.ea6e

Software image version: 4.28.3M-28837868.4283M (engineering build)
Architecture: x86_64
Internal build version: 4.28.3M-28837868.4283M
Internal build ID: e950541d-fc75-4ced-a669-6fd2007c17b7
Image format version: 1.0
Image optimization: None

cEOS tools version: 1.1
Kernel version: 5.15.0-56-generic

Uptime: 2 minutes
Total memory: 231066228 kB
Free memory: 222771148 kB

r1#show vrf
Maximum number of VRFs allowed: 1024
   VRF            Protocols       State         Interfaces
-------------- --------------- ---------------- ------------------
   default        IPv4            routing       Et1, Et2, Lo0, Ma0
   default        IPv6            no routing    Ma0
   foo            IPv4            no routing
   foo            IPv6            no routing
   instance       IPv4            no routing
   instance       IPv6            no routing

@jlixfeld
Copy link
Author

Gross. Is it worth approaching the use of a text parser vs. the json parser similar to how the cli selector is deterministic based on the software version running on the box?

@bewing
Copy link
Member

bewing commented May 16, 2023

Ugh. I'd prefer not to use a try/except block, but it does appear to be the only way. RD has been removed from the VRF text output in newer versions (it's considered part of BGP, not global), so I'd need to either parse the raw bgp config (nothx) or use show bgp instance vrf all to collect the RD assigned to the first configured AF (hacky)

@jlixfeld
Copy link
Author

At least show bgp instance vrf all supports json 🤓

bewing added a commit that referenced this issue May 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants