diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index fa5fc248a8..3d19306a75 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -299,10 +299,13 @@ Route Map Set Command Set the route's weight. -.. index:: set metric METRIC -.. clicmd:: set metric METRIC +.. index:: [no] set metric <[+|-](1-4294967295)|rtt|+rtt|-rtt> +.. clicmd:: [no] set metric <[+|-](1-4294967295)|rtt|+rtt|-rtt> - Set the BGP attribute MED. + Set the BGP attribute MED to a specific value. Use `+`/`-` to add or subtract + the specified value to/from the MED. Use `rtt` to set the MED to the round + trip time or `+rtt`/`-rtt` to add/subtract the round trip time to/from the + MED. .. index:: set as-path prepend AS_PATH .. clicmd:: set as-path prepend AS_PATH diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 836be38113..339d025124 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -719,15 +719,13 @@ DEFPY_YANG( DEFPY_YANG( set_metric, set_metric_cmd, - "set metric <(0-4294967295)$metric|rtt$rtt|+rtt$artt|-rtt$srtt|+metric$ametric|-metric$smetric>", + "set metric <(-4294967295-4294967295)$metric|rtt$rtt|+rtt$artt|-rtt$srtt>", SET_STR "Metric value for destination routing protocol\n" - "Metric value\n" + "Metric value (use +/- for additions or subtractions)\n" "Assign round trip time\n" "Add round trip time\n" - "Subtract round trip time\n" - "Add metric\n" - "Subtract metric\n") + "Subtract round trip time\n") { const char *xpath = "./set-action[action='metric']"; char xpath_value[XPATH_MAXLEN]; @@ -746,17 +744,17 @@ DEFPY_YANG( snprintf(xpath_value, sizeof(xpath_value), "%s/subtract-round-trip-time", xpath); snprintf(value, sizeof(value), "true"); - } else if (ametric) { + } else if (metric_str && metric_str[0] == '+') { snprintf(xpath_value, sizeof(xpath_value), "%s/add-metric", xpath); - snprintf(value, sizeof(value), "true"); - } else if (smetric) { + snprintf(value, sizeof(value), "%s", ++metric_str); + } else if (metric_str && metric_str[0] == '-') { snprintf(xpath_value, sizeof(xpath_value), "%s/subtract-metric", xpath); - snprintf(value, sizeof(value), "true"); + snprintf(value, sizeof(value), "%s", ++metric_str); } else { snprintf(xpath_value, sizeof(xpath_value), "%s/value", xpath); - snprintf(value, sizeof(value), "%lu", metric); + snprintf(value, sizeof(value), "%s", metric_str); } nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value); @@ -765,7 +763,7 @@ DEFPY_YANG( DEFPY_YANG( no_set_metric, no_set_metric_cmd, - "no set metric [(0-4294967295)]", + "no set metric [OPTVAL]", NO_STR SET_STR "Metric value for destination routing protocol\n" @@ -831,9 +829,12 @@ void route_map_action_show(struct vty *vty, struct lyd_node *dnode, } else if (yang_dnode_get(dnode, "./subtract-round-trip-time")) { vty_out(vty, " set metric -rtt\n"); } else if (yang_dnode_get(dnode, "./add-metric")) { - vty_out(vty, " set metric +metric\n"); + vty_out(vty, " set metric +%s\n", + yang_dnode_get_string(dnode, "./add-metric")); } else if (yang_dnode_get(dnode, "./subtract-metric")) { - vty_out(vty, " set metric -metric\n"); + vty_out(vty, " set metric -%s\n", + yang_dnode_get_string(dnode, + "./subtract-metric")); } else { vty_out(vty, " set metric %s\n", yang_dnode_get_string(dnode, "./value")); diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c index 967f3fd4d4..597a6b1ecf 100644 --- a/lib/routemap_northbound.c +++ b/lib/routemap_northbound.c @@ -983,8 +983,19 @@ lib_route_map_entry_set_action_value_destroy(struct nb_cb_destroy_args *args) static int lib_route_map_entry_set_action_add_metric_modify(struct nb_cb_modify_args *args) { + char metric_str[16]; + + if (args->event == NB_EV_VALIDATE + && yang_dnode_get_uint32(args->dnode, NULL) == 0) { + snprintf(args->errmsg, args->errmsg_len, + "Can't add zero to metric"); + return NB_ERR_VALIDATION; + } + + snprintf(metric_str, sizeof(metric_str), "+%s", + yang_dnode_get_string(args->dnode, NULL)); return set_action_modify(args->event, args->dnode, args->resource, - "+metric"); + metric_str); } static int lib_route_map_entry_set_action_add_metric_destroy( @@ -999,8 +1010,19 @@ static int lib_route_map_entry_set_action_add_metric_destroy( static int lib_route_map_entry_set_action_subtract_metric_modify( struct nb_cb_modify_args *args) { + char metric_str[16]; + + if (args->event == NB_EV_VALIDATE + && yang_dnode_get_uint32(args->dnode, NULL) == 0) { + snprintf(args->errmsg, args->errmsg_len, + "Can't subtract zero from metric"); + return NB_ERR_VALIDATION; + } + + snprintf(metric_str, sizeof(metric_str), "-%s", + yang_dnode_get_string(args->dnode, NULL)); return set_action_modify(args->event, args->dnode, args->resource, - "-metric"); + metric_str); } static int lib_route_map_entry_set_action_subtract_metric_destroy( diff --git a/tests/topotests/bgp_features/r1/show_bgp.json b/tests/topotests/bgp_features/r1/show_bgp.json new file mode 100644 index 0000000000..97260b0d0a --- /dev/null +++ b/tests/topotests/bgp_features/r1/show_bgp.json @@ -0,0 +1,351 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "192.168.0.1", + "defaultLocPrf": 100, + "localAS": 65000, + "routes": { "0.0.0.0/0": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"0.0.0.0", + "prefixLen":0, + "network":"0.0.0.0\/0", + "weight":0, + "peerId":"192.168.101.2", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r4", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.0.0/24": [ + { + "pathFrom":"external", + "prefix":"192.168.0.0", + "prefixLen":24, + "network":"192.168.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.1.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.2.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.3.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.3.0", + "prefixLen":24, + "network":"192.168.3.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.3.0", + "prefixLen":24, + "network":"192.168.3.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.6.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.6.0", + "prefixLen":24, + "network":"192.168.6.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.7.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"internal", + "prefix":"192.168.7.0", + "prefixLen":24, + "network":"192.168.7.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.8.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.8.0", + "prefixLen":24, + "network":"192.168.8.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.8.0", + "prefixLen":24, + "network":"192.168.8.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.101.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.101.0", + "prefixLen":24, + "network":"192.168.101.0\/24", + "metric":0, + "weight":0, + "peerId":"192.168.101.2", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r4", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.102.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.102.0", + "prefixLen":24, + "network":"192.168.102.0\/24", + "metric":0, + "weight":0, + "peerId":"192.168.101.2", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r4", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.201.0/24": [ + { + "pathFrom":"internal", + "prefix":"192.168.201.0", + "prefixLen":24, + "network":"192.168.201.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"65200", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.202.0/24": [ + { + "pathFrom":"internal", + "prefix":"192.168.202.0", + "prefixLen":24, + "network":"192.168.202.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"65200", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_features/r1/show_bgp_metric_test.json b/tests/topotests/bgp_features/r1/show_bgp_metric_test.json new file mode 100644 index 0000000000..1720572bcb --- /dev/null +++ b/tests/topotests/bgp_features/r1/show_bgp_metric_test.json @@ -0,0 +1,57 @@ +{ + "routerId": "192.168.0.1", + "routes": { + "192.168.1.0/24": [ + { + "valid":true, + "prefix":"192.168.1.0", + "prefixLen":24, + "metric":11, + "nexthops":[ + { + "ip":"192.168.0.2", + "used":true + } + ] + }, + { + "valid":true, + "prefix":"192.168.1.0", + "prefixLen":24, + "metric":0, + "nexthops":[ + { + "ip":"0.0.0.0", + "used":true + } + ] + } +],"192.168.101.0/24": [ + { + "prefix":"192.168.101.0", + "prefixLen":24, + "metric":111, + "peerId":"192.168.101.2" + } +],"192.168.102.0/24": [ + { + "prefix":"192.168.102.0", + "prefixLen":24, + "metric":0, + "peerId":"192.168.101.2" + } +],"192.168.201.0/24": [ + { + "prefix":"192.168.201.0", + "prefixLen":24, + "metric":210, + "peerId":"192.168.0.2" + } +],"192.168.202.0/24": [ + { + "prefix":"192.168.202.0", + "prefixLen":24, + "metric":11, + "peerId":"192.168.0.2" + } +] } } diff --git a/tests/topotests/bgp_features/r2/show_bgp.json b/tests/topotests/bgp_features/r2/show_bgp.json new file mode 100644 index 0000000000..43ca57d8f7 --- /dev/null +++ b/tests/topotests/bgp_features/r2/show_bgp.json @@ -0,0 +1,350 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "192.168.0.2", + "defaultLocPrf": 100, + "localAS": 65000, + "routes": { "0.0.0.0/0": [ + { + "pathFrom":"internal", + "prefix":"0.0.0.0", + "prefixLen":0, + "network":"0.0.0.0\/0", + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.0.0/24": [ + { + "pathFrom":"external", + "prefix":"192.168.0.0", + "prefixLen":24, + "network":"192.168.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.1.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.2.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.3.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.3.0", + "prefixLen":24, + "network":"192.168.3.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.3.0", + "prefixLen":24, + "network":"192.168.3.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.6.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"internal", + "prefix":"192.168.6.0", + "prefixLen":24, + "network":"192.168.6.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.7.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.7.0", + "prefixLen":24, + "network":"192.168.7.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.8.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.8.0", + "prefixLen":24, + "network":"192.168.8.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.8.0", + "prefixLen":24, + "network":"192.168.8.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.101.0/24": [ + { + "pathFrom":"internal", + "prefix":"192.168.101.0", + "prefixLen":24, + "network":"192.168.101.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.102.0/24": [ + { + "pathFrom":"internal", + "prefix":"192.168.102.0", + "prefixLen":24, + "network":"192.168.102.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.201.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.201.0", + "prefixLen":24, + "network":"192.168.201.0\/24", + "metric":0, + "weight":0, + "peerId":"192.168.201.2", + "path":"65200", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.2", + "hostname":"r5", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.202.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.202.0", + "prefixLen":24, + "network":"192.168.202.0\/24", + "metric":0, + "weight":0, + "peerId":"192.168.201.2", + "path":"65200", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.2", + "hostname":"r5", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_features/r2/show_bgp_metric_test.json b/tests/topotests/bgp_features/r2/show_bgp_metric_test.json new file mode 100644 index 0000000000..960f13d204 --- /dev/null +++ b/tests/topotests/bgp_features/r2/show_bgp_metric_test.json @@ -0,0 +1,57 @@ +{ + "routerId": "192.168.0.2", + "routes": { + "192.168.2.0/24": [ + { + "valid":true, + "prefix":"192.168.2.0", + "prefixLen":24, + "metric":0, + "nexthops":[ + { + "ip":"192.168.0.1", + "used":true + } + ] + }, + { + "valid":true, + "prefix":"192.168.2.0", + "prefixLen":24, + "metric":0, + "nexthops":[ + { + "ip":"0.0.0.0", + "used":true + } + ] + } +],"192.168.101.0/24": [ + { + "prefix":"192.168.101.0", + "prefixLen":24, + "metric":101, + "peerId":"192.168.0.1" + } +],"192.168.102.0/24": [ + { + "prefix":"192.168.102.0", + "prefixLen":24, + "metric":0, + "peerId":"192.168.0.1" + } +],"192.168.201.0/24": [ + { + "prefix":"192.168.201.0", + "prefixLen":24, + "metric":222, + "peerId":"192.168.201.2" + } +],"192.168.202.0/24": [ + { + "prefix":"192.168.202.0", + "prefixLen":24, + "metric":0, + "peerId":"192.168.201.2" + } +] } } diff --git a/tests/topotests/bgp_features/r4/show_bgp_metric_test.json b/tests/topotests/bgp_features/r4/show_bgp_metric_test.json new file mode 100644 index 0000000000..2329498ca2 --- /dev/null +++ b/tests/topotests/bgp_features/r4/show_bgp_metric_test.json @@ -0,0 +1,27 @@ +{ + "routerId": "192.168.100.1", + "localAS": 65100, + "routes": { + "192.168.1.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":1011, + "weight":0, + "peerId":"192.168.101.1", + "path":"65000", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_features/r5/show_bgp_metric_test.json b/tests/topotests/bgp_features/r5/show_bgp_metric_test.json new file mode 100644 index 0000000000..e6608b46a8 --- /dev/null +++ b/tests/topotests/bgp_features/r5/show_bgp_metric_test.json @@ -0,0 +1,27 @@ +{ + "routerId": "192.168.200.1", + "localAS": 65200, + "routes": { + "192.168.2.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":2022, + "weight":0, + "peerId":"192.168.201.1", + "path":"65000", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.1", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py index 91613a0193..7c45ca8a29 100644 --- a/tests/topotests/bgp_features/test_bgp_features.py +++ b/tests/topotests/bgp_features/test_bgp_features.py @@ -261,6 +261,282 @@ def test_bgp_no_shutdown(): assert res is None, assertmsg +def test_bgp_metric_config(): + "Test BGP Changing metric values in route-maps" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring bgp route-maps on router r1 and r2 to update metric") + + # # Adding the following configuration to r1: + # router bgp 65000 + # address-family ipv4 unicast + # neighbor 192.168.0.2 route-map addmetric-in in + # neighbor 192.168.0.2 route-map addmetric-out out + # neighbor 192.168.101.2 route-map setmetric-in in + # neighbor 192.168.101.2 route-map setmetric-out out + # exit-address-family + # ! + # ip prefix-list net1 seq 10 permit 192.168.101.0/24 + # ip prefix-list net2 seq 20 permit 192.168.1.0/24 + # ! + # route-map setmetric-in permit 10 + # match ip address prefix-list net1 + # set metric 111 + # ! + # route-map setmetric-in permit 20 + # ! + # route-map setmetric-out permit 10 + # match ip address prefix-list net2 + # set metric 1011 + # ! + # route-map setmetric-out permit 20 + # ! + # route-map addmetric-in permit 10 + # set metric +11 + # ! + # route-map addmetric-out permit 10 + # set metric +12 + # ! + + tgen.net['r1'].cmd('vtysh -c "conf t" -c "router bgp 65000" '+ + '-c "address-family ipv4 unicast" '+ + '-c "neighbor 192.168.0.2 route-map addmetric-in in" '+ + '-c "neighbor 192.168.0.2 route-map addmetric-out out" '+ + '-c "neighbor 192.168.101.2 route-map setmetric-in in" '+ + '-c "neighbor 192.168.101.2 route-map setmetric-out out" ') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "ip prefix-list net1 seq 10 permit 192.168.101.0/24" '+ + '-c "ip prefix-list net2 seq 20 permit 192.168.1.0/24"') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "route-map setmetric-in permit 10" '+ + '-c "match ip address prefix-list net1" '+ + '-c "set metric 111" '+ + '-c "route-map setmetric-in permit 20"') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "route-map setmetric-out permit 10" '+ + '-c "match ip address prefix-list net2" '+ + '-c "set metric 1011" '+ + '-c "route-map setmetric-out permit 20"') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "route-map addmetric-in permit 10" '+ + '-c "set metric +11"') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "route-map addmetric-out permit 10" '+ + '-c "set metric +12"') + + # # Adding the following configuration to r2: + # router bgp 65000 + # address-family ipv4 unicast + # neighbor 192.168.0.1 route-map subtractmetric-in in + # neighbor 192.168.0.1 route-map subtractmetric-out out + # neighbor 192.168.201.2 route-map setmetric-in in + # neighbor 192.168.201.2 route-map setmetric-out out + # exit-address-family + # ! + # ip prefix-list net1 seq 10 permit 192.168.201.0/24 + # ip prefix-list net2 seq 20 permit 192.168.2.0/24 + # ! + # route-map setmetric-in permit 10 + # match ip address prefix-list net1 + # set metric 222 + # ! + # route-map setmetric-in permit 20 + # ! + # route-map setmetric-out permit 10 + # match ip address prefix-list net2 + # set metric 2022 + # ! + # route-map setmetric-out permit 20 + # ! + # route-map subtractmetric-in permit 10 + # set metric -22 + # ! + # route-map subtractmetric-out permit 10 + # set metric -23 + # ! + + tgen.net['r2'].cmd('vtysh -c "conf t" -c "router bgp 65000" '+ + '-c "address-family ipv4 unicast" '+ + '-c "neighbor 192.168.0.1 route-map subtractmetric-in in" '+ + '-c "neighbor 192.168.0.1 route-map subtractmetric-out out" '+ + '-c "neighbor 192.168.201.2 route-map setmetric-in in" ' + + '-c "neighbor 192.168.201.2 route-map setmetric-out out" ') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "ip prefix-list net1 seq 10 permit 192.168.201.0/24" '+ + '-c "ip prefix-list net2 seq 20 permit 192.168.2.0/24" ') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "route-map setmetric-in permit 10" '+ + '-c "match ip address prefix-list net1" '+ + '-c "set metric 222" '+ + '-c "route-map setmetric-in permit 20"') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "route-map setmetric-out permit 10" '+ + '-c "match ip address prefix-list net2" '+ + '-c "set metric 2022" '+ + '-c "route-map setmetric-out permit 20"') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "route-map subtractmetric-in permit 10" '+ + '-c "set metric -22"') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "route-map subtractmetric-out permit 10" '+ + '-c "set metric -23"') + + # Clear IN the bgp neighbors to make sure the route-maps are applied + tgen.net['r1'].cmd('vtysh -c "clear ip bgp 192.168.0.2 in" '+ + '-c "clear ip bgp 192.168.101.2 in"') + tgen.net['r2'].cmd('vtysh -c "clear ip bgp 192.168.0.1 in" '+ + '-c "clear ip bgp 192.168.201.2 in"') + + # tgen.mininet_cli() + + # Checking BGP config - should show the bgp metric settings in the route-maps + logger.info("Checking BGP configuration for correct 'set metric' values") + + setmetric111 = tgen.net['r1'].cmd('vtysh -c "show running" | grep "^ set metric 111"').rstrip() + assertmsg = "'set metric 111' configuration applied to R1, but not visible in configuration" + assert setmetric111 == ' set metric 111', assertmsg + + setmetric222 = tgen.net['r2'].cmd('vtysh -c "show running" | grep "^ set metric 222"').rstrip() + assertmsg = "'set metric 222' configuration applied to R2, but not visible in configuration" + assert setmetric222 == ' set metric 222', assertmsg + + +def test_bgp_metric_add_config(): + "Test BGP Changing metric values in route-maps" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking BGP configuration for correct 'set metric' ADD value") + + setmetricP11 = tgen.net['r1'].cmd('vtysh -c "show running" | grep "^ set metric +11"').rstrip() + assertmsg = "'set metric +11' configuration applied to R1, but not visible in configuration" + assert setmetricP11 == ' set metric +11', assertmsg + + +def test_bgp_metric_subtract_config(): + "Test BGP Changing metric values in route-maps" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking BGP configuration for correct 'set metric' SUBTRACT value") + + setmetricM22 = tgen.net['r2'].cmd('vtysh -c "show running" | grep "^ set metric -22"').rstrip() + assertmsg = "'set metric -22' configuration applied to R2, but not visible in configuration" + assert setmetricM22 == ' set metric -22', assertmsg + + +def test_bgp_set_metric(): + "Test setting metrics" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Test absolute metric") + + # Check BGP Summary on local and remote routers + for rtrNum in [1, 2, 4, 5]: + logger.info("Checking metrics of BGP router on r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/show_bgp_metric_test.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP metrics on router r{} wrong".format(rtrNum) + assert res is None, assertmsg + + +def test_bgp_remove_metric_rmaps(): + "Test removing route-maps with metric changes again" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Test absolute metric") + + # Remove metric route-maps and relevant comfiguration + + tgen.net['r1'].cmd('vtysh -c "conf t" -c "router bgp 65000" '+ + '-c "address-family ipv4 unicast" '+ + '-c "no neighbor 192.168.0.2 route-map addmetric-in in" '+ + '-c "no neighbor 192.168.0.2 route-map addmetric-out out" '+ + '-c "no neighbor 192.168.101.2 route-map setmetric-in in" '+ + '-c "no neighbor 192.168.101.2 route-map setmetric-out out" ') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "no ip prefix-list net1" '+ + '-c "no ip prefix-list net2"') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "no route-map setmetric-in" ') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "no route-map setmetric-out" ') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "no route-map addmetric-in" ') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "no route-map addmetric-out" ') + + tgen.net['r2'].cmd('vtysh -c "conf t" -c "router bgp 65000" '+ + '-c "address-family ipv4 unicast" '+ + '-c "no neighbor 192.168.0.1 route-map subtractmetric-in in" '+ + '-c "no neighbor 192.168.0.1 route-map subtractmetric-out out" '+ + '-c "no neighbor 192.168.201.2 route-map setmetric-in in" ' + + '-c "no neighbor 192.168.201.2 route-map setmetric-out out" ') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "no ip prefix-list net1" '+ + '-c "no ip prefix-list net2" ') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "no route-map setmetric-in" ') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "no route-map setmetric-out" ') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "no route-map addmetric-in" ') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "no route-map addmetric-out" ') + + # Clear IN the bgp neighbors to make sure the route-maps are applied + tgen.net['r1'].cmd('vtysh -c "clear ip bgp 192.168.0.2 in" '+ + '-c "clear ip bgp 192.168.101.2 in"') + tgen.net['r2'].cmd('vtysh -c "clear ip bgp 192.168.0.1 in" '+ + '-c "clear ip bgp 192.168.201.2 in"') + + # tgen.mininet_cli() + + # Check BGP Summary on local and remote routers + for rtrNum in [1, 2]: + logger.info("Checking metrics of BGP router on r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/show_bgp.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP routes on router r{} are wrong after removing metric route-maps".format(rtrNum) + assert res is None, assertmsg + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/yang/frr-route-map.yang b/yang/frr-route-map.yang index c9d35938cb..b22a96a740 100644 --- a/yang/frr-route-map.yang +++ b/yang/frr-route-map.yang @@ -373,15 +373,19 @@ module frr-route-map { case add-metric { leaf add-metric { - description "Add unit to metric."; - type boolean; + description "Add value to metric."; + type uint32 { + range "0..4294967295"; + } } } case subtract-metric { leaf subtract-metric { - description "Subtract unit from metric."; - type boolean; + description "Subtract value from metric."; + type uint32 { + range "0..4294967295"; + } } }