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

Multi-ASIC support for show ip route #1089

Closed
wants to merge 1 commit into from
Closed

Multi-ASIC support for show ip route #1089

wants to merge 1 commit into from

Conversation

gechiang
Copy link
Contributor

@gechiang gechiang commented Sep 2, 2020

Add Multi-ASIC support to handle "show ip route" on multi-ASIC devices

Depends on the following PRs

Signed-off-by: Gen-Hwa Chiang gechiang@microsoft.com

- What I did

Add support for multi ASIC CLI options for "show ip route"
2 new options have added

[-n, --namespace] to allow user to display the information for given namespaces (ASIC)
If this option is not present the information from all the namespaces will be displayed

[-d, --display] to allow user to display ip routes related with nexthop that are going through both internal and external interfaces
If this option is not present only ip routes with external interfaces as its nexthop will be display

On single ASIC platform, this options are not valid, so the behavior remains unchanged

- How I did it

  • Request the back-end handler (FRR/Zebra) via the specified BGP docker(s) to dump out the "show ip route" in "json" format
  • Depends on the -d option optionally filter out those route nexthops that are via internal (back-end) interfaces.
  • If after filtering there are no next hops any more, then skip that particular route from being displayed as well.
  • At the end of the filtering if user also specified "json" option, then just dump out in json format.
  • If user did not specify "json" option, then interpret the route output similar to how FRR/Zebra does and print out each route.

- How to verify it

Help menu

admin@sonic:~$ show ip route -h
Usage: show ip route [OPTIONS] [IPADDRESS] [vrf <vrf_name>] [...]

  Show IP (IPv4) routing table

Options:
  -d, --display [all|frontend]    Show internal interfaces  [default:
                                  frontend]
  -n, --namespace [asic0|asic1|asic2|asic3|asic4|asic5]
                                  Namespace name or all
  --verbose                       Enable verbose output
  -?, -h, --help                  Show this message and exit.
admin@sonic:~$

show ip route for all routes (include routes that uses internal interface as nexthop) from ASIC0 in multi ASIC device

admin@sonic:~$ show ip route -n asic0 -d all
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR, f - OpenFabric,
       > - selected route, * - FIB route, q - queued route, r - rejected route

C>*10.106.0.4/31 is directly connected, PortChannel1005, 1d00h03m
B>*10.10.192.48/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
C>*10.0.107.16/32 is directly connected, Loopback4096, 1d00h03m
B>*10.0.107.18/32 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                       via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.46/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.106.0.12/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                       via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.52/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.62/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.38/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
K>*0.0.0.0/0 [210/0] via 240.127.1.1, eth0, 1d00h03m
B>*10.10.192.36/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.54/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.0.107.21/32 [200/0] via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.0.107.8/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                      via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.106.0.8/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                      via 10.0.107.2, PortChannel4002, 1d00h03m
C>*10.0.107.0/31 is directly connected, PortChannel4001, 1d00h03m
B>*10.10.192.44/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.68/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.0.107.10/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                       via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.64/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
C>*172.16.132.64/32 is directly connected, Loopback0, 1d00h03m
B>*10.10.192.60/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.50/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.34/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.0.107.12/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                       via 10.0.107.2, PortChannel4002, 1d00h03m
C>*10.0.107.2/31 is directly connected, PortChannel4002, 1d00h03m
B>*10.0.107.17/32 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                       via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.0.107.4/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                      via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.42/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.0.107.19/32 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                       via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.0.107.14/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                       via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.66/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.70/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.32/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
C>*10.106.0.0/31 is directly connected, PortChannel1002, 1d00h03m
B>*10.0.107.6/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                      via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.56/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.40/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.10.192.58/31 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
  *                        via 10.0.107.2, PortChannel4002, 1d00h03m
B>*10.0.107.20/32 [200/0] via 10.0.107.0, PortChannel4001, 1d00h03m
admin@sonic:~$

show ip route (exclude routes that uses internal interface as nexthop) from ASIC0 in multi ASIC device

admin@sonic:~$ show ip route -n asic0
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR, f - OpenFabric,
       > - selected route, * - FIB route, q - queued route, r - rejected route

C>*10.106.0.4/31 is directly connected, PortChannel1005, 1d00h03m
K>*0.0.0.0/0 [210/0] via 240.127.1.1, eth0, 1d00h04m
C>*10.106.0.0/31 is directly connected, PortChannel1002, 1d00h03m
C>*172.16.132.64/32 is directly connected, Loopback0, 1d00h04m
C>*10.0.107.16/32 is directly connected, Loopback4096, 1d00h04m
admin@sonic:~$

show ip route (exclude routes that uses internal interface as nexthop) from ASIC5 in multi ASIC device in json format

admin@sonic:~$ show ip route -n asic5 json
{
  "10.0.107.21/32": [
    {
      "distance": 0,
      "uptime": "1d00h06m",
      "destSelected": true,
      "protocol": "connected",
      "internalFlags": 8,
      "metric": 0,
      "selected": true,
      "installed": true,
      "internalNextHopNum": 1,
      "prefix": "10.0.107.21/32",
      "internalNextHopActiveNum": 1,
      "table": 254,
      "internalStatus": 16,
      "nexthops": [
        {
          "directlyConnected": true,
          "interfaceName": "Loopback4096",
          "interfaceIndex": 9,
          "fib": true,
          "flags": 3,
          "active": true
        }
      ]
    }
  ],
  "172.16.132.64/32": [
    {
      "distance": 0,
      "uptime": "1d00h06m",
      "destSelected": true,
      "protocol": "connected",
      "internalFlags": 8,
      "metric": 0,
      "selected": true,
      "installed": true,
      "internalNextHopNum": 1,
      "prefix": "172.16.132.64/32",
      "internalNextHopActiveNum": 1,
      "table": 254,
      "internalStatus": 16,
      "nexthops": [
        {
          "directlyConnected": true,
          "interfaceName": "Loopback0",
          "interfaceIndex": 8,
          "fib": true,
          "flags": 3,
          "active": true
        }
      ]
    }
  ],
  "10.0.107.14/31": [
    {
      "distance": 200,
      "uptime": "1d00h05m",
      "protocol": "bgp",
      "internalFlags": 5,
      "metric": 0,
      "internalStatus": 0,
      "internalNextHopNum": 1,
      "prefix": "10.0.107.14/31",
      "internalNextHopActiveNum": 0,
      "table": 254,
      "nexthops": [
        {
          "ip": "10.0.107.15",
          "flags": 0,
          "afi": "ipv4"
        }
      ]
    }
  ],
  "10.0.107.2/31": [
    {
      "distance": 200,
      "uptime": "1d00h06m",
      "protocol": "bgp",
      "internalFlags": 5,
      "metric": 0,
      "internalStatus": 0,
      "internalNextHopNum": 1,
      "prefix": "10.0.107.2/31",
      "internalNextHopActiveNum": 0,
      "table": 254,
      "nexthops": [
        {
          "ip": "10.0.107.3",
          "flags": 0,
          "afi": "ipv4"
        }
      ]
    }
  ],
  "10.0.107.10/31": [
    {
      "distance": 200,
      "uptime": "1d00h05m",
      "protocol": "bgp",
      "internalFlags": 5,
      "metric": 0,
      "internalStatus": 0,
      "internalNextHopNum": 1,
      "prefix": "10.0.107.10/31",
      "internalNextHopActiveNum": 0,
      "table": 254,
      "nexthops": [
        {
          "ip": "10.0.107.11",
          "flags": 0,
          "afi": "ipv4"
        }
      ]
    }
  ],
  "10.0.107.6/31": [
    {
      "distance": 200,
      "uptime": "1d00h06m",
      "protocol": "bgp",
      "internalFlags": 5,
      "metric": 0,
      "internalStatus": 0,
      "internalNextHopNum": 1,
      "prefix": "10.0.107.6/31",
      "internalNextHopActiveNum": 0,
      "table": 254,
      "nexthops": [
        {
          "ip": "10.0.107.7",
          "flags": 0,
          "afi": "ipv4"
        }
      ]
    }
  ],
  "0.0.0.0/0": [
    {
      "distance": 210,
      "uptime": "1d00h06m",
      "destSelected": true,
      "protocol": "kernel",
      "internalFlags": 8,
      "metric": 0,
      "selected": true,
      "installed": true,
      "internalNextHopNum": 1,
      "prefix": "0.0.0.0/0",
      "internalNextHopActiveNum": 1,
      "table": 254,
      "internalStatus": 16,
      "nexthops": [
        {
          "interfaceName": "eth0",
          "ip": "240.127.1.1",
          "interfaceIndex": 13,
          "fib": true,
          "flags": 3,
          "active": true,
          "afi": "ipv4"
        }
      ]
    }
  ]
}
admin@sonic:~$

show ip route (exclude routes that uses internal interface as nexthop) from ASIC5 in multi ASIC device

admin@sonic:~$ show ip route -n asic5
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR, f - OpenFabric,
       > - selected route, * - FIB route, q - queued route, r - rejected route

C>*10.0.107.21/32 is directly connected, Loopback4096, 1d00h06m
C>*172.16.132.64/32 is directly connected, Loopback0, 1d00h06m
B 10.0.107.14/31 [200/0] via 10.0.107.15, inactive 1d00h06m
B 10.0.107.2/31 [200/0] via 10.0.107.3, inactive 1d00h06m
B 10.0.107.10/31 [200/0] via 10.0.107.11, inactive 1d00h06m
B 10.0.107.6/31 [200/0] via 10.0.107.7, inactive 1d00h06m
K>*0.0.0.0/0 [210/0] via 240.127.1.1, eth0, 1d00h07m
admin@sonic:~$

show ip route for all routes (exclude routes that uses internal interface as nexthop) from ALL ASICs in multi ASIC device

admin@sonic:~$ show ip route
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR, f - OpenFabric,
       > - selected route, * - FIB route, q - queued route, r - rejected route

C>*10.106.0.4/31 is directly connected, PortChannel1005, 1d00h04m
K>*0.0.0.0/0 [210/0] via 240.127.1.1, eth0, 1d00h05m
C>*10.106.0.0/31 is directly connected, PortChannel1002, 1d00h04m
C>*172.16.132.64/32 is directly connected, Loopback0, 1d00h04m
C>*10.0.107.16/32 is directly connected, Loopback4096, 1d00h04m
C>*10.106.0.8/31 is directly connected, PortChannel1008, 1d00h04m
K>*0.0.0.0/0 [210/0] via 240.127.1.1, eth0, 1d00h05m
C>*10.106.0.12/31 is directly connected, PortChannel1011, 1d00h04m
C>*172.16.132.64/32 is directly connected, Loopback0, 1d00h04m
C>*10.0.107.17/32 is directly connected, Loopback4096, 1d00h04m
C>*10.10.192.34/31 is directly connected, PortChannel1003, 1d00h04m
C>*10.10.192.48/31 is directly connected, PortChannel1013, 1d00h04m
C>*10.10.192.36/31 is directly connected, PortChannel1004, 1d00h04m
C>*10.10.192.32/31 is directly connected, PortChannel1001, 1d00h04m
C>*10.0.107.18/32 is directly connected, Loopback4096, 1d00h04m
C>*10.10.192.46/31 is directly connected, PortChannel1012, 1d00h04m
C>*10.10.192.44/31 is directly connected, PortChannel1010, 1d00h04m
C>*10.10.192.50/31 is directly connected, PortChannel1014, 1d00h04m
C>*10.10.192.38/31 is directly connected, PortChannel1006, 1d00h04m
K>*0.0.0.0/0 [210/0] via 240.127.1.1, eth0, 1d00h04m
C>*10.10.192.40/31 is directly connected, PortChannel1007, 1d00h04m
C>*10.10.192.42/31 is directly connected, PortChannel1009, 1d00h04m
C>*172.16.132.64/32 is directly connected, Loopback0, 1d00h04m
C>*10.10.192.54/31 is directly connected, PortChannel1016, 1d00h04m
C>*10.0.107.19/32 is directly connected, Loopback4096, 1d00h04m
C>*172.16.132.64/32 is directly connected, Loopback0, 1d00h04m
C>*10.10.192.62/31 is directly connected, PortChannel1020, 1d00h04m
C>*10.10.192.60/31 is directly connected, PortChannel1019, 1d00h04m
C>*10.10.192.70/31 is directly connected, PortChannel1024, 1d00h04m
C>*10.10.192.68/31 is directly connected, PortChannel1023, 1d00h04m
C>*10.10.192.52/31 is directly connected, PortChannel1015, 1d00h04m
K>*0.0.0.0/0 [210/0] via 240.127.1.1, eth0, 1d00h05m
C>*10.10.192.66/31 is directly connected, PortChannel1022, 1d00h04m
C>*10.10.192.58/31 is directly connected, PortChannel1018, 1d00h04m
C>*10.10.192.64/31 is directly connected, PortChannel1021, 1d00h04m
C>*10.10.192.56/31 is directly connected, PortChannel1017, 1d00h04m
C>*172.16.132.64/32 is directly connected, Loopback0, 1d00h04m
B 10.0.107.8/31 [200/0] via 10.0.107.9, inactive 1d00h04m
B 10.0.107.0/31 [200/0] via 10.0.107.1, inactive 1d00h04m
B 10.0.107.12/31 [200/0] via 10.0.107.13, inactive 1d00h04m
K>*0.0.0.0/0 [210/0] via 240.127.1.1, eth0, 1d00h05m
B 10.0.107.4/31 [200/0] via 10.0.107.5, inactive 1d00h04m
C>*10.0.107.20/32 is directly connected, Loopback4096, 1d00h04m
C>*10.0.107.21/32 is directly connected, Loopback4096, 1d00h04m
C>*172.16.132.64/32 is directly connected, Loopback0, 1d00h04m
B 10.0.107.14/31 [200/0] via 10.0.107.15, inactive 1d00h04m
B 10.0.107.2/31 [200/0] via 10.0.107.3, inactive 1d00h04m
B 10.0.107.10/31 [200/0] via 10.0.107.11, inactive 1d00h04m
B 10.0.107.6/31 [200/0] via 10.0.107.7, inactive 1d00h04m
K>*0.0.0.0/0 [210/0] via 240.127.1.1, eth0, 1d00h05m
admin@sonic:~$

- Previous command output (if the output of a command-line utility has changed)

- New command output (if the output of a command-line utility has changed)

** Please Note **
The test case changes will be submitted with a separate PR later so that we can take advantage of some other multi-ASIC CLI PR that have already created the test modules that can be reused without too much duplication of work.

Note 2
This is the same PR that I raised previously "#1060 (review)" which I closed due to messed up my last update commits thus recreating with all the update made based on previous comments in this new PR.

@gechiang gechiang requested review from jleveque, arlakshm, prsunny, smaheshm, rlhui and pavel-shirshov and removed request for jleveque and prsunny September 2, 2020 06:05
@@ -7,18 +7,21 @@
import re
import subprocess
import sys
import commands
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imports are generally listed in alphabetical order.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Will move it based on alphabetical order

Comment on lines +805 to +807
mpls_label_code = { 0:"IPv4 Explicit Null", 1:"Router Alert", 2:"IPv6 Explicit Null", 3:"implicit-null", 4:"Reserved (4)",
5:"Reserved (5)", 6:"Reserved (6)", 7:"Entropy Label Indicator", 8:"Reserved (8)", 9:"Reserved (9)", 10:"Reserved (10)",
11:"Reserved (11)", 12:"Reserved (12)", 13:"Generic Associated Channel", 14:"OAM Alert", 15:"Extension"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's try to maintain 79 chars in a line if possible.

Copy link
Contributor

@jleveque jleveque Sep 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is debatable now that we all have wider screens. I'm OK with making one exception to the PEP8 standard and extending the max line length to around 100 chars. Using a tool like autopep8 --max-line-length 100 can be helpful. We can discuss this more offline.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, it's not a hardcore requirement. With 80 chars I get more vertical splits in editor :).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I will reduce the length shorter to not exceed more than 100 chars.
The code can become harder to read if need to strictly follow this 79 chars limitation.

Comment on lines +808 to +884
NH_F_IS_RECURSIVE = 2
NH_F_IS_DUPLICATE = 5
for route, info in route_info.items():
for i in range(0, len(info)):
str_2_print = ""
str_2_print += proto_code[info[i]['protocol']]
if "instance" in info[i]:
str_2_print += "[" + str(info[i]['instance']) + "]"
if "selected" in info[i]:
str_2_print += ">"
else:
str_2_print += " "
for j in range(0, len(info[i]['nexthops'])):
if j != 0:
str_2_print = " "
if "queued" in info[i]:
str_2_print += "q"
elif "failed" in info[i]:
str_2_print += "r"
elif "installed" in info[i]:
if info[i]['nexthops'][j]['flags'] & (1 << NH_F_IS_RECURSIVE) or info[i]['nexthops'][j]['flags'] & (1 << NH_F_IS_DUPLICATE):
str_2_print += " "
else:
str_2_print += "*"
# on 1st nexhop print the prefix and distance/metric if appropriate.
# on all subsequent nexthop replace the prefix and distance/metric by empty spaces only.
if j == 0:
str_2_print += info[i]['prefix']
if info[i]['protocol'] != "connected":
str_2_print += " [" + str(info[i]['distance']) + "/" + str(info[i]['metric']) + "]"
elif info[i]['distance'] > 0 or info[i]['metric'] > 0:
str_2_print += " [" + str(info[i]['distance']) + "/" + str(info[i]['metric']) + "]"
str_length = len(str_2_print)
else:
str_2_print += " "*(str_length - 3)
if "ip" in info[i]['nexthops'][j]:
str_2_print += " via " + info[i]['nexthops'][j]['ip'] + ","
if "interfaceName" in info[i]['nexthops'][j]:
str_2_print += " " + info[i]['nexthops'][j]['interfaceName'] + ","
elif "directlyConnected" in info[i]['nexthops'][j]:
str_2_print += " is directly connected,"
if "interfaceName" in info[i]['nexthops'][j]:
str_2_print += " " + info[i]['nexthops'][j]['interfaceName'] + ","
elif "unreachable" in info[i]['nexthops'][j]:
if "reject" in info[i]['nexthops'][j]:
str_2_print += " (ICMP unreachable)"
elif "admin-prohibited" in info[i]['nexthops'][j]:
str_2_print += " (ICMP admin-prohibited)"
elif "blackhole" in info[i]['nexthops'][j]:
str_2_print += " (blackhole)"
if "vrf" in info[i]['nexthops'][j]:
str_2_print += "(vrf " + info[i]['nexthops'][j]['vrf'] + ", " + info[i]['nexthops'][j]['interfaceName'] + ","
if "active" not in info[i]['nexthops'][j]:
str_2_print += " inactive"
if "onLink" in info[i]['nexthops'][j]:
str_2_print += " onlink"
if "recursive" in info[i]['nexthops'][j]:
str_2_print += " (recursive)"
if "source" in info[i]['nexthops'][j]:
str_2_print += ", src " + info[i]['nexthops'][j]['source']
if "labels" in info[i]['nexthops'][j]:
# MPLS labels are stored as an array (list) in json if present. Need to print through each one in list
str_2_print += ", label "
for k in range(0, len(info[i]['nexthops'][j]['labels'])):
# MPLS labels that has value 15 or lower has special interpretation
if info[i]['nexthops'][j]['labels'][k] > 15:
label_string = str(info[i]['nexthops'][j]['labels'][k])
else:
label_string = mpls_label_code[info[i]['nexthops'][j]['labels'][k]]
if k == 0:
str_2_print += label_string
else:
str_2_print += "/" + label_string
# add uptime at the end of the string
str_2_print += " " + info[i]['uptime']
# print out this string
print str_2_print
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This too long, lets split it up. There are few types of next hops. Each type can have it's own render function that returns a formatted string. Also these functions can be in utils.py or you can also create a route.py.
show/main.py is the main CLI, this can just call an API that returns a formatted string.

Wondering for formatting if we can use python string formats.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Let me split up at section where it is looking at the same field but with different possibilities.
Unfortunately this logic here is not something sharable to other purpose so will not be moving this to other files...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you use 'tabulate' package python will do the formatting for you.

# print out this string
print str_2_print

def print_show_ip_route_hdr():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can also be moved into route.py

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you are saying now.. basically you want me to move all the helper routines to route.py.
To minimize the impact for now I will do this refactoring on next phase of the changes to move all helper routines to route.py
for this PR, let me keep it here in main.py for now. I will have to work on the mock test cases next which will do this refactoring at that time as well.

filter_back_end = False
else:
filter_back_end = True
if str(namespace) == "None":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be simplified to:

filter_back_end = False if display == "all" else True

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Still in the old "c" programming world... :)
Agreed.

filter_back_end = False
else:
filter_back_end = True
if str(namespace) == "None":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not:

if namespace == None:

Copy link
Contributor

@jleveque jleveque Sep 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even better:

if not namepsace:

However, this will also catch the empty string (''), which is used as the default namespace. If you expect the default namespace here, then go with @smaheshm's approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Forgot to remove it after a bug fix during unit testing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jleveque Yes. the user may not specify any namespace and in that case it will result to empty string.

@click.option('--verbose', is_flag=True, help="Enable verbose output")
def route(args, verbose):
def route(args, namespace, display, verbose):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you also split this up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Will split up to improve code readability

Comment on lines 937 to +942
for arg in args:
cmd += " " + str(arg)

cmd += '"'

run_command(cmd, display_cmd=verbose)
arg_strg += " " + str(arg)
if str(arg) == "json":
found_json = 1
if not found_json:
arg_strg += " json"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's add 'json' option as 'click' option. Mush easier.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original design of this handler before my changes purposely left these and many other options as variable arguments. This is done this way so that there is less impact when there are new options added or removed from the Zebra implementation. So in the same spirit we should not be adding this as a 'click' option.

route_info = json.loads(output)
if filter_back_end:
# clean up the dictionary to remove all the nexthops that are back-end interface
for route, info in route_info.items():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check_backend_nexthop can be a separate API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

return
if output[0] == "%":
# remove the "json" keyword that was added by this handler so it wont confuse the user
error_msg = output[:-4]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if "json" keywork wasn't specified.

This can be removed if 'json' is passed as click option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See previous comment response in regard to this "optional" argument. which we intent to keep the same way as before.

if not hdr_printed:
print_show_ip_route_hdr()
hdr_printed = True
print_ip_routes(filtered_route)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all ASICs have the same route table, with some routes with different next hops.

It looks like the routing table is being printed for each ASIC independently. We should use prefix as key and merge next hops, and print a prefix only once.

@gechiang
Copy link
Contributor Author

gechiang commented Nov 4, 2020

Many changes was made to support "merging" and IPV6.
I will close out this PR and open a new one for review instead...

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

Successfully merging this pull request may close these issues.

3 participants