Repository: napalm-automation/napalm

Issue:
`get_network_instances` does not return expected results
              @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 https://github.com/napalm-automation/napalm/issues/1919#issuecomment-1548098025_
            
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).

https://github.com/napalm-automation/napalm/blob/cb4845c40017a3e8e3c2b4e57838de2fd31a1348/napalm/eos/eos.py#L2121-L2185
vrf `default` in vrf list twice
### Description of Issue/Question

*Note*: Please check https://guides.github.com/features/mastering-markdown/
to see how to properly format your request.

### Did you follow the steps from https://github.com/napalm-automation/napalm#faq
(Place an ``x`` between the square brackets where applicable)

- [] Yes
- [] No


### Setup

### napalm version
(Paste verbatim output from `pip freeze | grep napalm` between quotes below)

```
root ➜ /workspaces/python $ pip freeze | grep napalm
napalm==4.0.0
root ➜ /workspaces/python $ 
```

### Network operating system version
(Paste verbatim output from `show version` - or equivalent - between quotes below)

```
Arista DCS-7280SR3-48YC8-F
Hardware version: 11.02
Serial number: JPE21101297
Hardware MAC address: 948e.d351.f8aa
System MAC address: 948e.d351.f8aa

Software image version: 4.25.5M
Architecture: i686
Internal build version: 4.25.5M-23663359.4255M
Internal build ID: b866497b-0134-4bb1-b262-a6aedd8cf7c6

Uptime: 1 weeks, 2 days, 2 hours and 27 minutes
Total memory: 8147232 kB
Free memory: 4597344 kB

```

### Steps to Reproduce the Issue

```
driver = get_network_driver('eos')
device = driver("arista", "admin", "admin")
device.open()
device.get_route_to("10.0.9.1")
```
### Error Traceback
(Paste the complete traceback of the exception between quotes below)

```

```

Output for VRF `default` is returned twice by `get_route_to()`:
```
{'10.0.9.1/32': [{'current_active': True,
   'last_active': True,
   'age': 0,
   'next_hop': '100.2.9.9',
   'protocol': 'ISISLevel2',
   'outgoing_interface': 'Ethernet1/1',
   'preference': 115,
   'inactive_reason': '',
   'routing_table': 'default',
   'selected_next_hop': True,
   'protocol_attributes': {}},
  {'current_active': True,
   'last_active': True,
   'age': 0,
   'next_hop': '100.2.9.9',
   'protocol': 'ISISLevel2',
   'outgoing_interface': 'Ethernet1/1',
   'preference': 115,
   'inactive_reason': '',
   'routing_table': 'default',
   'selected_next_hop': True,
   'protocol_attributes': {}}]}

```


The function to get the route -
https://github.com/napalm-automation/napalm/blob/cb4845c40017a3e8e3c2b4e57838de2fd31a1348/napalm/eos/eos.py#L1450

... calls `_show_vrf` -

https://github.com/napalm-automation/napalm/blob/cb4845c40017a3e8e3c2b4e57838de2fd31a1348/napalm/eos/eos.py#L2121-L2138

which, regardless whether or not `default` is returned by the system, added to the list of VRFs.


Discussion:
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.
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
I am slightly confused here -- Do you have a vrf literally named "default" on your router?  `default` in this output means "no VRF", IE the default routing table.  Do newer versions of EOS explicitly list the default VRF in the "show vrf" output?
I can't speak to anything < `4.25.5M`, but on that version, yes `default` is in there by... default:

```
cdv459-9.10:10:40#show vrf
Maximum number of vrfs allowed: 1023
   VRF              RD              Protocols       State            Interfaces
---------------- --------------- --------------- ------------------- ---------------------------
   default          <not set>       ipv4,ipv6       v4:routing,      Ethernet1, Ethernet2,
                                                    v6:no routing    Ethernet3, Ethernet4,
                                                                     Ethernet49/1, Ethernet5,
                                                                     Ethernet50/1, Ethernet52/1,
                                                                     Ethernet53/1, Ethernet54/1,
                                                                     Ethernet55/1, Ethernet56/1,
                                                                     Loopback0
   management       <not set>       ipv4,ipv6       v4:routing,      Management1
                                                    v6:no routing


cdv459-9.10:10:46#
```

```
cdv459-9.10:14:57#show run | i vrf instance
vrf instance management
cdv459-9.10:15:02#
```
Ack, playing with the above `show vrf`, there might be a bug in the TextFSM template as well. :/
@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

Resolving patch:
diff --git a/napalm/base/utils/string_parsers.py b/napalm/base/utils/string_parsers.py
--- a/napalm/base/utils/string_parsers.py
+++ b/napalm/base/utils/string_parsers.py
@@ -1,6 +1,7 @@
 """ Common methods to normalize a string """
 import re
-from typing import Union, List, Iterable, Dict, Optional
+import struct
+from typing import Union, List, Iterable, Dict, Optional, Tuple
 
 
 def convert(text: str) -> Union[str, int]:
@@ -133,3 +134,14 @@ def convert_uptime_string_seconds(uptime: str) -> int:
         raise Exception("Unrecognized uptime string:{}".format(uptime))
 
     return uptime_seconds
+
+
+def parse_fixed_width(text: str, *fields: int) -> List[Tuple[str, ...]]:
+    len = sum(fields)
+    fmtstring = " ".join(f"{fw}s" for fw in fields)
+    unpack = struct.Struct(fmtstring).unpack_from
+
+    def parse(line: str) -> Tuple[str, ...]:
+        return tuple([str(s.decode()) for s in unpack(line.ljust(len).encode())])
+
+    return [parse(s) for s in text.splitlines()]
diff --git a/napalm/eos/eos.py b/napalm/eos/eos.py
--- a/napalm/eos/eos.py
+++ b/napalm/eos/eos.py
@@ -2130,15 +2130,59 @@ def get_config(self, retrieve="all", full=False, sanitized=False):
         else:
             raise Exception("Wrong retrieve filter: {}".format(retrieve))
 
-    def _show_vrf(self):
+    def _show_vrf_json(self):
+        commands = ["show vrf"]
+
+        vrfs = self._run_commands(commands)[0]["vrfs"]
+        return [
+            {
+                "name": k,
+                "interfaces": [i for i in v["interfaces"]],
+                "route_distinguisher": v["routeDistinguisher"],
+            }
+            for k, v in vrfs.items()
+        ]
+
+    def _show_vrf_text(self):
         commands = ["show vrf"]
 
-        # This command has no JSON yet
+        # This command has no JSON in EOS < 4.23
         raw_output = self._run_commands(commands, encoding="text")[0].get("output", "")
 
-        output = napalm.base.helpers.textfsm_extractor(self, "vrf", raw_output)
+        width_line = raw_output.splitlines()[2]  # Line with dashes
+        fields = width_line.split(" ")
+        widths = [len(f) + 1 for f in fields]
+        widths[-1] -= 1
+
+        parsed_lines = string_parsers.parse_fixed_width(raw_output, *widths)
+
+        vrfs = []
+        vrf = {}
+        current_vrf = None
+        for line in parsed_lines[3:]:
+            line = [t.strip() for t in line]
+            if line[0]:
+                if current_vrf:
+                    vrfs.append(vrf)
+                current_vrf = line[0]
+                vrf = {
+                    "name": current_vrf,
+                    "interfaces": list(),
+                }
+            if line[1]:
+                vrf["route_distinguisher"] = line[1]
+            if line[4]:
+                vrf["interfaces"].extend([t.strip() for t in line[4].split(",") if t])
+        if current_vrf:
+            vrfs.append(vrf)
+
+        return vrfs
 
-        return output
+    def _show_vrf(self):
+        if self.cli_version == 2:
+            return self._show_vrf_json()
+        else:
+            return self._show_vrf_text()
 
     def _get_vrfs(self):
         output = self._show_vrf()
@@ -2171,23 +2215,26 @@ def get_network_instances(self, name=""):
                         interfaces[str(line.strip())] = {}
                         all_vrf_interfaces[str(line.strip())] = {}
 
-            vrfs[str(vrf["name"])] = {
-                "name": str(vrf["name"]),
-                "type": "L3VRF",
+            vrfs[vrf["name"]] = {
+                "name": vrf["name"],
+                "type": "DEFAULT_INSTANCE" if vrf["name"] == "default" else "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 "default" not in vrfs:
+            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:


Test patch:
diff --git a/test/base/test_helpers.py b/test/base/test_helpers.py
--- a/test/base/test_helpers.py
+++ b/test/base/test_helpers.py
@@ -57,7 +57,10 @@
 from napalm.base.netmiko_helpers import netmiko_args
 import napalm.base.exceptions
 from napalm.base.base import NetworkDriver
-from napalm.base.utils.string_parsers import convert_uptime_string_seconds
+from napalm.base.utils.string_parsers import (
+    convert_uptime_string_seconds,
+    parse_fixed_width,
+)
 
 
 class TestBaseHelpers(unittest.TestCase):
@@ -672,6 +675,37 @@ def test_ttp_parse(self):
         )
         self.assertEqual(result, _EXPECTED_RESULT)
 
+    def test_parse_fixed_width(self):
+        _TEST_STRING = """   VRF              RD              Protocols       State            Interfaces
+---------------- --------------- --------------- ------------------- ---------------------------
+123456789012345671234567890123456123456789012345612345678901234567890123456789012345678901234567
+"""  # noqa: E501
+        _EXPECTED_RESULT = [
+            (
+                "   VRF           ",
+                "   RD           ",
+                "   Protocols    ",
+                "   State            ",
+                "Interfaces                 ",
+            ),
+            (
+                "---------------- ",
+                "--------------- ",
+                "--------------- ",
+                "------------------- ",
+                "---------------------------",
+            ),
+            (
+                "12345678901234567",
+                "1234567890123456",
+                "1234567890123456",
+                "12345678901234567890",
+                "123456789012345678901234567",
+            ),
+        ]
+        result = parse_fixed_width(_TEST_STRING, 17, 16, 16, 20, 27)
+        self.assertEqual(result, _EXPECTED_RESULT)
+
 
 class FakeNetworkDriver(NetworkDriver):
     def __init__(self):
diff --git a/test/eos/conftest.py b/test/eos/conftest.py
--- a/test/eos/conftest.py
+++ b/test/eos/conftest.py
@@ -37,6 +37,19 @@ def __init__(self, hostname, username, password, timeout=60, optional_args=None)
 
         self.patched_attrs = ["device"]
         self.device = FakeEOSDevice()
+        self._cli_version = 1
+
+    @property
+    def cli_version(self):
+        try:
+            full_path = self.device.find_file("cli_version.txt")
+        except IOError:
+            return self._cli_version
+        return int(self.device.read_txt_file(full_path))
+
+    @cli_version.setter
+    def cli_version(self, value):
+        self._cli_version = value
 
 
 class FakeEOSDevice(BaseTestDouble):
diff --git a/test/eos/mocked_data/test_get_network_instances/issue-1922/expected_result.json b/test/eos/mocked_data/test_get_network_instances/issue-1922/expected_result.json
new file mode 100644
--- /dev/null
+++ b/test/eos/mocked_data/test_get_network_instances/issue-1922/expected_result.json
@@ -0,0 +1,30 @@
+{
+  "management": {
+    "name": "management",
+    "type": "L3VRF",
+    "state": { "route_distinguisher": "" },
+    "interfaces": { "interface": { "Management1": {} } }
+  },
+  "default": {
+    "interfaces": {
+      "interface": {
+        "Ethernet1": {},
+        "Ethernet2": {},
+        "Ethernet3": {},
+        "Ethernet4": {},
+        "Ethernet49/1": {},
+        "Ethernet5": {},
+        "Ethernet50/1": {},
+        "Ethernet52/1": {},
+        "Ethernet53/1": {},
+        "Ethernet54/1": {},
+        "Ethernet55/1": {},
+        "Ethernet56/1": {},
+        "Loopback0": {}
+      }
+    },
+    "state": { "route_distinguisher": "" },
+    "type": "DEFAULT_INSTANCE",
+    "name": "default"
+  }
+}
diff --git a/test/eos/mocked_data/test_get_network_instances/issue-1922/show_vrf.text b/test/eos/mocked_data/test_get_network_instances/issue-1922/show_vrf.text
new file mode 100644
--- /dev/null
+++ b/test/eos/mocked_data/test_get_network_instances/issue-1922/show_vrf.text
@@ -0,0 +1,12 @@
+Maximum number of vrfs allowed: 1023
+   VRF              RD              Protocols       State            Interfaces
+---------------- --------------- --------------- ------------------- ---------------------------
+   default          <not set>       ipv4,ipv6       v4:routing,      Ethernet1, Ethernet2,
+                                                    v6:no routing    Ethernet3, Ethernet4,
+                                                                     Ethernet49/1, Ethernet5,
+                                                                     Ethernet50/1, Ethernet52/1,
+                                                                     Ethernet53/1, Ethernet54/1,
+                                                                     Ethernet55/1, Ethernet56/1,
+                                                                     Loopback0
+   management       <not set>       ipv4,ipv6       v4:routing,      Management1
+                                                    v6:no routing
diff --git a/test/eos/mocked_data/test_get_network_instances/issue-509/show_vrf.text b/test/eos/mocked_data/test_get_network_instances/issue-509/show_vrf.text
--- a/test/eos/mocked_data/test_get_network_instances/issue-509/show_vrf.text
+++ b/test/eos/mocked_data/test_get_network_instances/issue-509/show_vrf.text
@@ -1,11 +1,11 @@
 Maximum number of vrfs allowed: 14
- Vrf     RD           Protocols    State               Interfaces
-------- ------------ ------------ ------------------- -------------------
-VRFA0   201:201   ipv4        v4:routing; multicast,  Vlan2, Vlan102
-                               v6:no routing
+ Vrf     RD        Protocols    State                   Interfaces
+------- --------- ------------ ------------------------ -------------------
+ VRFA0   201:201   ipv4         v4:routing; multicast,  Vlan2, Vlan102
+                                v6:no routing
 
-VRFA1   203:203   ipv4        v4:routing; multicast,  Vlan3, Vlan103
-                               v6:no routing
+ VRFA1   203:203   ipv4         v4:routing; multicast,  Vlan3, Vlan103
+                                v6:no routing
 
-VRFA2   205:205   ipv4        v4:routing; multicast,  Ethernet1, Vlan100
-                               v6:no routing
+ VRFA2   205:205   ipv4         v4:routing; multicast,  Ethernet1, Vlan100
+                                v6:no routing
diff --git a/test/eos/mocked_data/test_get_network_instances/vrf/cli_version.txt b/test/eos/mocked_data/test_get_network_instances/vrf/cli_version.txt
new file mode 100644
--- /dev/null
+++ b/test/eos/mocked_data/test_get_network_instances/vrf/cli_version.txt
@@ -0,0 +1 @@
+2
diff --git a/test/eos/mocked_data/test_get_network_instances/vrf/expected_result.json b/test/eos/mocked_data/test_get_network_instances/vrf/expected_result.json
new file mode 100644
--- /dev/null
+++ b/test/eos/mocked_data/test_get_network_instances/vrf/expected_result.json
@@ -0,0 +1,31 @@
+{
+  "default": {
+    "name": "default",
+    "type": "DEFAULT_INSTANCE",
+    "state": { "route_distinguisher": "" },
+    "interfaces": {
+      "interface": {
+        "Ethernet1": {},
+        "Ethernet2": {},
+        "Loopback0": {},
+        "Management0": {}
+      }
+    }
+  },
+  "foo": {
+    "name": "foo",
+    "type": "L3VRF",
+    "state": { "route_distinguisher": "0:1" },
+    "interfaces": {
+      "interface": {}
+    }
+  },
+  "bar": {
+    "name": "bar",
+    "type": "L3VRF",
+    "state": { "route_distinguisher": "" },
+    "interfaces": {
+      "interface": {}
+    }
+  }
+}
diff --git a/test/eos/mocked_data/test_get_network_instances/vrf/show_vrf.json b/test/eos/mocked_data/test_get_network_instances/vrf/show_vrf.json
new file mode 100644
--- /dev/null
+++ b/test/eos/mocked_data/test_get_network_instances/vrf/show_vrf.json
@@ -0,0 +1,61 @@
+{
+  "vrfs": {
+    "foo": {
+      "routeDistinguisher": "0:1",
+      "protocols": {
+        "ipv4": {
+          "supported": true,
+          "protocolState": "up",
+          "routingState": "up"
+        },
+        "ipv6": {
+          "supported": true,
+          "protocolState": "up",
+          "routingState": "down"
+        }
+      },
+      "vrfState": "up",
+      "interfacesV4": [],
+      "interfacesV6": [],
+      "interfaces": []
+    },
+    "bar": {
+      "routeDistinguisher": "",
+      "protocols": {
+        "ipv4": {
+          "supported": true,
+          "protocolState": "up",
+          "routingState": "down"
+        },
+        "ipv6": {
+          "supported": true,
+          "protocolState": "up",
+          "routingState": "down"
+        }
+      },
+      "vrfState": "up",
+      "interfacesV4": [],
+      "interfacesV6": [],
+      "interfaces": []
+    },
+    "default": {
+      "routeDistinguisher": "",
+      "protocols": {
+        "ipv4": {
+          "supported": true,
+          "protocolState": "up",
+          "routingState": "up"
+        },
+        "ipv6": {
+          "supported": true,
+          "protocolState": "up",
+          "routingState": "down"
+        }
+      },
+      "vrfState": "up",
+      "interfacesV4": ["Ethernet1", "Ethernet2", "Loopback0", "Management0"],
+      "interfacesV6": ["Management0"],
+      "interfaces": ["Ethernet1", "Ethernet2", "Loopback0", "Management0"]
+    }
+  }
+}