diff --git a/board/netconf/rootfs/lib/infix/cli-pretty b/board/netconf/rootfs/lib/infix/cli-pretty index 79d4fbe8..ed5aeefe 100755 --- a/board/netconf/rootfs/lib/infix/cli-pretty +++ b/board/netconf/rootfs/lib/infix/cli-pretty @@ -14,6 +14,12 @@ class Pad: state = 12 data = 41 +class PadRoute: + prefix = 30 + protocol = 10 + next_hop = 30 + metric = 10 + class Decore(): @staticmethod def decorate(sgr, txt, restore="0"): @@ -31,27 +37,53 @@ class Decore(): def green(txt): return Decore.decorate("32", txt, "39") +def get_json_data(default, indata, *args): + data = indata + for arg in args: + if arg in data: + data = data.get(arg) + else: + return default + + return data + +class Route: + def __init__(self,data): + self.data = data + self.prefix = data.get('ietf-ipv4-unicast-routing:destination-prefix', '') + self.protocol = data.get('source-protocol','')[14:] + self.metric = data.get('route-preference','') + + interface = get_json_data(None, self.data, 'next-hop', 'outgoing-interface') + address = get_json_data(None, self.data, 'next-hop', 'ietf-ipv4-unicast-routing:next-hop-address') + special = get_json_data(None, self.data, 'next-hop', 'special-next-hop') + if interface: + self.next_hop = interface + elif address: + self.next_hop = address + elif special: + self.next_hop = special + else: + self.next_hop = "unspecified" + + def print(self): + row = f"{self.prefix:<{PadRoute.prefix}}" + row += f"{self.next_hop:<{PadRoute.next_hop}}" + row += f"{self.metric:<{PadRoute.metric}}" + row += f"{self.protocol:<{PadRoute.protocol}}" + print(row) + class Iface: - def get_json_data(self, default, *args): - data = self.data - for arg in args: - if arg in data: - data = data.get(arg) - else: - return default - - return data - def __init__(self, data): self.data = data self.name = data.get('name', '') self.type = data.get('type', '') self.index = data.get('if-index', '') self.oper_status = data.get('oper-status', '') - self.autoneg = self.get_json_data('unknown', 'ieee802-ethernet-interface:ethernet', + self.autoneg = get_json_data('unknown', self.data, 'ieee802-ethernet-interface:ethernet', 'auto-negotiation', 'enable') - self.duplex = self.get_json_data('', 'ieee802-ethernet-interface:ethernet', 'duplex') - self.speed = self.get_json_data('', 'ieee802-ethernet-interface:ethernet', 'speed') + self.duplex = get_json_data('', self.data,'ieee802-ethernet-interface:ethernet','duplex') + self.speed = get_json_data('', self.data, 'ieee802-ethernet-interface:ethernet', 'speed') self.phys_address = data.get('phys-address', '') if data.get('statistics'): @@ -231,8 +263,8 @@ class Iface: print(f"{'in-octets':<{20}}: {self.in_octets}") print(f"{'out-octets':<{20}}: {self.out_octets}") - frame = self.get_json_data([], 'ieee802-ethernet-interface:ethernet', - 'statistics', 'frame') + frame = get_json_data([], self.data,'ieee802-ethernet-interface:ethernet', + 'statistics', 'frame') if frame: print(f"") for key, val in frame.items(): @@ -297,6 +329,25 @@ def ietf_interfaces(json, name): sys.exit(1) pr_interface_list(json) +def ietf_routing(json, protocol="ipv4"): + if not json.get("ietf-routing:routing"): + print(f"Error, top level \"ietf-routing:routing\" missing") + sys.exit(1) + routes = get_json_data({}, json, 'ietf-routing:routing','ribs', 'rib')[0] + routes = get_json_data(None, routes, "routes", "route") + + hdr = (f"{'PREFIX':<{PadRoute.prefix}}" + f"{'NEXT-HOP':<{PadRoute.next_hop}}" + f"{'METRIC':<{PadRoute.metric}}" + f"{'PROTOCOL':<{PadRoute.protocol}}") + + print(Decore.invert(hdr)) + + if routes: + for r in routes: + route = Route(r) + route.print() + def main(): try: json_data = json.load(sys.stdin) @@ -309,6 +360,8 @@ def main(): if args.module == "ietf-interfaces": sys.exit(ietf_interfaces(json_data, args.name)) + if args.module == "ietf-routing": + sys.exit(ietf_routing(json_data, args.name)) else: print(f"Error, unknown module {args.module}") sys.exit(1) diff --git a/board/netconf/rootfs/lib/infix/yanger b/board/netconf/rootfs/lib/infix/yanger index 2edec772..9aba3d56 100755 --- a/board/netconf/rootfs/lib/infix/yanger +++ b/board/netconf/rootfs/lib/infix/yanger @@ -116,6 +116,40 @@ def iface_is_dsa(iface_in): return False return True +def add_ipv4_route(routes): + cmd = ['ip', '-4', '-s', '-d', '-j', 'route'] + data = run_json_cmd(cmd) + out={} + out["route"] = [] + + for d in data: + new = {} + next_hop = {} + if(d['dst'] == "default"): + d['dst'] = "0.0.0.0/0" + if(d['dst'].find('/') == -1): + d['dst'] = d['dst']+"/32" + new['ietf-ipv4-unicast-routing:destination-prefix'] = d['dst'] + new['source-protocol'] = "infix-routing:"+d['protocol'] + if d.get("metric"): + new['route-preference'] = d['metric'] + + if d['type'] == "blackhole": + next_hop['special-next-hop'] = "blackhole" + if d['type'] == "unreachable": + next_hop['special-next-hop'] = "unreachable" + + if d['type'] == "unicast": + if(d.get("gateway")): + next_hop['ietf-ipv4-unicast-routing:next-hop-address'] = d['gateway'] + elif(d.get("dev")): + next_hop['outgoing-interface'] = d['dev'] + + new['next-hop'] = next_hop + + out['route'].append(new) + insert(routes, 'routes', out) + def add_ip_link(ifname, iface_out): cmd = ['ip', '-s', '-d', '-j', 'link', 'show', 'dev', ifname] @@ -125,7 +159,6 @@ def add_ip_link(ifname, iface_out): sys,exit(1) iface_in = data[0] - iface_out = yang_data['ietf-interfaces:interfaces']['interface'][0] if 'ifname' in iface_in: iface_out['name'] = iface_in['ifname'] @@ -323,25 +356,48 @@ def add_ethtool_std(ifname, iface_out): insert(iface_out, "ieee802-ethernet-interface:ethernet", "speed", str(num)) if __name__ == "__main__": - yang_data = { - "ietf-interfaces:interfaces": { - "interface": [{}] - } - } - - # For now, we handle each interface separately, as this is how it's - # currently implemented in sysrepo. I.e sysrepo will subscribe to - # each individual interface and query it for YANG data. - if len(sys.argv) != 2: - print(f"usage: yanger IFNAME", file=sys.stderr) + if len(sys.argv) < 2: + print(f"usage: yanger [params]", file=sys.stderr) sys.exit(1) - ifname = sys.argv[1] - iface_out = yang_data['ietf-interfaces:interfaces']['interface'][0] + model = sys.argv[1] + if(model == 'ietf-interfaces'): + # For now, we handle each interface separately, as this is how it's + # currently implemented in sysrepo. I.e sysrepo will subscribe to + # each individual interface and query it for YANG data. - add_ip_link(ifname, iface_out) - add_ip_addr(ifname, iface_out) - add_ethtool_groups(ifname, iface_out) - add_ethtool_std(ifname, iface_out) + if len(sys.argv) != 3: + print(f"usage: yanger ietf-interfaces IFNAME", file=sys.stderr) + sys.exit(1) + yang_data = { + "ietf-interfaces:interfaces": { + "interface": [{}] + } + } + + ifname = sys.argv[2] + iface_out = yang_data['ietf-interfaces:interfaces']['interface'][0] + + add_ip_link(ifname, iface_out) + add_ip_addr(ifname, iface_out) + add_ethtool_groups(ifname, iface_out) + add_ethtool_std(ifname, iface_out) + elif(model == 'ietf-routing'): + yang_data = { + "ietf-routing:routing": { + "ribs": { + "rib": [{ + "name": "ipv4", + "address-family": "ipv4", + }] + } + } + } + + ipv4routes = yang_data['ietf-routing:routing']['ribs']['rib'][0] + add_ipv4_route(ipv4routes); + else: + print(f"Unsupported model {model}", file=sys.stderr) + sys.exit(1) print(json.dumps(yang_data, indent=2)) diff --git a/external.mk b/external.mk index 1660fee1..28f1a2f3 100644 --- a/external.mk +++ b/external.mk @@ -17,3 +17,10 @@ run: run-menuconfig: $(BUILD_DIR)/buildroot-config/mconf CONFIG_="CONFIG_" BR2_CONFIG="$(BINARIES_DIR)/.config" \ $(BUILD_DIR)/buildroot-config/mconf $(BINARIES_DIR)/Config.in + +define FRR_POST_BUILD_HOOK + mkdir -p $(TARGET_DIR)/etc/iproute2/ + cp -r $(@D)/tools/etc/iproute2/rt_protos.d/ $(TARGET_DIR)/etc/iproute2/ +endef + +FRR_POST_BUILD_HOOKS += FRR_POST_BUILD_HOOK diff --git a/src/.clang-format b/src/.clang-format index 22ab0ced..e2ca61b0 100644 --- a/src/.clang-format +++ b/src/.clang-format @@ -711,6 +711,6 @@ UseTab: Always # Infix overrides # ColumnLimit: 0 -ForEachMacros: ['LYX_LIST_FOR_EACH', 'TAILQ_FOREACH', 'LY_LIST_FOR'] +ForEachMacros: ['LYX_LIST_FOR_EACH', 'TAILQ_FOREACH', 'LY_LIST_FOR', 'json_array_foreach'] UseTab: AlignWithSpaces ... diff --git a/src/confd/yang/infix-routing@2013-10-27.yang b/src/confd/yang/infix-routing@2013-10-27.yang index 64dd1b24..3e564d66 100644 --- a/src/confd/yang/infix-routing@2013-10-27.yang +++ b/src/confd/yang/infix-routing@2013-10-27.yang @@ -43,12 +43,39 @@ module infix-routing { } } + identity infix-source-protocol { + description "Infix naming of source protocols"; + } + identity kernel { + description "Kernel added route"; + base infix-source-protocol; + } + identity static { + description "Static added route"; + base infix-source-protocol; + } + identity ospf { + description "OSPF added route"; + base infix-source-protocol; + } + identity dhcp { + description "DHCP added route"; + base infix-source-protocol; + } deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ietf-r:name" { description "Limitation: Only one routing instance per protocol."; deviate replace { type infix-control-plane-name; } } + deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:routes/ietf-r:route/ietf-r:source-protocol" { + description "Use type names from iproute2"; + deviate replace { + type identityref { + base infix-source-protocol; + } + } + } /* Static routes */ deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ietf-r:static-routes/v4ur:ipv4/v4ur:route/v4ur:next-hop/v4ur:next-hop-options/v4ur:next-hop-list" { deviate not-supported; @@ -63,6 +90,9 @@ module infix-routing { deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:active-route" { deviate not-supported; } + deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:routes/ietf-r:route/ietf-r:next-hop/ietf-r:next-hop-options/ietf-r:next-hop-list" { + deviate not-supported; + } /* OSPF */ /* diff --git a/src/klish-plugin-infix/xml/infix.xml b/src/klish-plugin-infix/xml/infix.xml index e2002a79..8692d65a 100644 --- a/src/klish-plugin-infix/xml/infix.xml +++ b/src/klish-plugin-infix/xml/infix.xml @@ -230,6 +230,12 @@ + + + sysrepocfg -f json -X -d operational -m ietf-routing | \ + /lib/infix/cli-pretty "ietf-routing" + + diff --git a/src/statd/statd.c b/src/statd/statd.c index c2ec347b..4a2e3286 100644 --- a/src/statd/statd.c +++ b/src/statd/statd.c @@ -26,11 +26,12 @@ #include "shared.h" -#define SOCK_RMEM_SIZE 1000000 /* Arbitrary chosen, default = 212992 */ +#define SOCK_RMEM_SIZE 1000000 /* Arbitrary chosen, default = 212992 */ #define NL_BUF_SIZE 4096 /* Arbitrary chosen */ #define XPATH_MAX PATH_MAX #define XPATH_IFACE_BASE "/ietf-interfaces:interfaces" +#define XPATH_ROUTING_BASE "/ietf-routing:routing" TAILQ_HEAD(sub_head, sub); @@ -38,11 +39,12 @@ TAILQ_HEAD(sub_head, sub); * types, not only interfaces. */ struct sub { - char ifname[IFNAMSIZ]; + char name[IFNAMSIZ+3]; struct ev_io watcher; sr_subscription_ctx_t *sr_sub; - TAILQ_ENTRY(sub) entries; + TAILQ_ENTRY(sub) + entries; }; struct netlink { @@ -59,11 +61,11 @@ struct statd { static void set_sock_rcvbuf(int sd, int size) { - if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) { - perror("setsockopt"); - return; - } - DEBUG("Socket receive buffer size increased to: %d bytes", size); + if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) { + perror("setsockopt"); + return; + } + DEBUG("Socket receive buffer size increased to: %d bytes", size); } static int nl_sock_init(void) @@ -90,12 +92,12 @@ static int nl_sock_init(void) return sock; } -static struct sub *sub_find_iface(struct sub_head *subs, const char *ifname) +static struct sub *sub_find(struct sub_head *subs, const char *name) { struct sub *sub; TAILQ_FOREACH(sub, subs, entries) { - if (strcmp(sub->ifname, ifname) == 0) + if (strcmp(sub->name, name) == 0) return sub; } @@ -120,17 +122,26 @@ static json_t *json_get_ip_link(void) } static int ly_add_yanger_data(const struct ly_ctx *ctx, struct lyd_node **parent, - char *ifname) + char *model, const char *arg) { - char *args[] = { + char *yanger_args[4] = { "/lib/infix/yanger", - ifname, + model, + NULL, NULL }; FILE *stream; int err; int fd; + if (!model) { + ERROR("Missing yang model to use"); + return SR_ERR_SYS; + } + + if (!strcmp(model, "ietf-interfaces")) + yanger_args[2] = (char *)arg; + fd = memfd_create("my_temp_file", 0); if (fd == -1) { ERROR("Error, unable to create memfd"); @@ -145,7 +156,7 @@ static int ly_add_yanger_data(const struct ly_ctx *ctx, struct lyd_node **parent return SR_ERR_SYS; } - err = fsystemv(args, NULL, stream, NULL); + err = fsystemv(yanger_args, NULL, stream, NULL); if (err) { ERROR("Error, running yanger"); fclose(stream); @@ -162,7 +173,7 @@ static int ly_add_yanger_data(const struct ly_ctx *ctx, struct lyd_node **parent return SR_ERR_SYS; } - err = lyd_parse_data_fd(ctx, fd, LYD_JSON, LYD_PARSE_ONLY , 0, parent); + err = lyd_parse_data_fd(ctx, fd, LYD_JSON, LYD_PARSE_ONLY, 0, parent); if (err) ERROR("Error, parsing yanger data (%d)", err); @@ -179,13 +190,14 @@ static int sr_ifaces_cb(sr_session_ctx_t *session, uint32_t, const char *path, struct sub *sub = priv; const struct ly_ctx *ctx; sr_conn_ctx_t *con; + char *ifname; int err; DEBUG("Incoming query for xpath: %s", path); con = sr_session_get_connection(session); if (!con) { - ERROR("Error, getting connection"); + ERROR("Error, getting sr connection"); return SR_ERR_INTERNAL; } @@ -195,7 +207,39 @@ static int sr_ifaces_cb(sr_session_ctx_t *session, uint32_t, const char *path, return SR_ERR_INTERNAL; } - err = ly_add_yanger_data(ctx, parent, sub->ifname); + ifname = &sub->name[3]; + err = ly_add_yanger_data(ctx, parent, "ietf-interfaces", ifname); + if (err) + ERROR("Error adding yanger data"); + + sr_release_context(con); + + return err; +} + +static int sr_routes_cb(sr_session_ctx_t *session, uint32_t, const char *path, + const char *, const char *, uint32_t, + struct lyd_node **parent, __attribute__((unused)) void *priv) +{ + const struct ly_ctx *ctx; + sr_conn_ctx_t *con; + sr_error_t err; + + DEBUG("Incoming query for xpath: %s", path); + + con = sr_session_get_connection(session); + if (!con) { + ERROR("Error, getting sr connection"); + return SR_ERR_INTERNAL; + } + + ctx = sr_acquire_context(con); + if (!ctx) { + ERROR("Error, acquiring context"); + return SR_ERR_INTERNAL; + } + + err = ly_add_yanger_data(ctx, parent, "ietf-routing", NULL); if (err) ERROR("Error adding yanger data"); @@ -221,48 +265,36 @@ static void sr_event_cb(struct ev_loop *, struct ev_io *w, int) sr_subscription_process_events(sub->sr_sub, NULL, NULL); } -static int sub_to_iface(struct statd *statd, const char *ifname) +static int subscribe(struct statd *statd, char *model, char *xpath, const char *name, + int (*cb)(sr_session_ctx_t *session, uint32_t, const char *, const char *, + const char *, uint32_t, struct lyd_node **parent, void *priv)) { - char path[XPATH_MAX] = {}; struct sub *sub; int sr_ev_pipe; - int err; + sr_error_t err; - /** - * Skip internal interfaces (such as dsa0) - * - * NOTE: this is a good solution but it might be to slow if a lot of - * new interfaces pops up at the same time. We don't want to delay - * processing the netlink messages to much. - */ - if (ip_link_check_group(ifname, "internal") == 1) - return SR_ERR_OK; - - sub = sub_find_iface(&statd->subs, ifname); + sub = sub_find(&statd->subs, name); if (sub) { - DEBUG("Interface %s already subscribed", ifname); + DEBUG("%s already subscribed", name); return SR_ERR_OK; } - sub = malloc(sizeof(struct sub)); - if (!sub) - return SR_ERR_INTERNAL; - memset(sub, 0, sizeof(struct sub)); - snprintf(sub->ifname, sizeof(sub->ifname), "%s", ifname); - snprintf(path, sizeof(path), "%s/interface[name='%s']", - XPATH_IFACE_BASE, ifname); + if (strlen(name) > sizeof(sub->name)) { + ERROR("Subscriber name is to long"); + return SR_ERR_INTERNAL; + } + snprintf(sub->name, sizeof(sub->name), "%s", name); - DEBUG("Subscribe to events for \"%s\"", path); - err = sr_oper_get_subscribe(statd->sr_ses, "ietf-interfaces", - path, sr_ifaces_cb, sub, + DEBUG("Subscribe to events for \"%s\"", xpath); + err = sr_oper_get_subscribe(statd->sr_ses, model,xpath, cb, sub, SR_SUBSCR_DEFAULT | SR_SUBSCR_NO_THREAD | SR_SUBSCR_DONE_ONLY, &sub->sr_sub); if (err) { - ERROR("Error, subscribing to path \"%s\": %s", path, sr_strerror(err)); + ERROR("Error, subscribing to path \"%s\": %s", xpath, sr_strerror(err)); free(sub); - return SR_ERR_INTERNAL; + return err; } err = sr_get_event_pipe(sub->sr_sub, &sr_ev_pipe); @@ -270,7 +302,7 @@ static int sub_to_iface(struct statd *statd, const char *ifname) ERROR("Error, getting sysrepo event pipe: %s", sr_strerror(err)); sr_unsubscribe(sub->sr_sub); free(sub); - return SR_ERR_INTERNAL; + return err; } TAILQ_INSERT_TAIL(&statd->subs, sub, entries); @@ -282,31 +314,63 @@ static int sub_to_iface(struct statd *statd, const char *ifname) return SR_ERR_OK; } -static void unsub_to_ifaces(struct statd *statd) +static int sub_to_routes(struct statd *statd) +{ + return subscribe(statd, "ietf-routing", XPATH_ROUTING_BASE, "routes", sr_routes_cb); +} + +static int sub_to_iface(struct statd *statd, const char *ifname) +{ + char path[XPATH_MAX] = {}; + char name[IFNAMSIZ+3]; + snprintf(name,sizeof(name),"if-%s",ifname); + + /** + * Skip internal interfaces (such as dsa0) + * + * NOTE: this is a good solution but it might be to slow if a lot of + * new interfaces pops up at the same time. We don't want to delay + * processing the netlink messages to much. + */ + if (ip_link_check_group(ifname, "internal") == 1) + return SR_ERR_OK; + + snprintf(path, sizeof(path), "%s/interface[name='%s']", XPATH_IFACE_BASE, ifname); + return subscribe(statd, "ietf-interfaces", path, name, sr_ifaces_cb); +} + +static void unsub_to_all(struct statd *statd) { struct sub *sub; while (!TAILQ_EMPTY(&statd->subs)) { sub = TAILQ_FIRST(&statd->subs); - DEBUG("Unsubscribe from \"%s\" (all)", sub->ifname); + DEBUG("Unsubscribe from \"%s\" (all)", sub->name); sub_delete(statd->ev_loop, &statd->subs, sub); } } -static int unsub_to_iface(struct statd *statd, char *ifname) +static int unsub_to_name(struct statd *statd, char *name) { struct sub *sub; - sub = sub_find_iface(&statd->subs, ifname); + sub = sub_find(&statd->subs, name); if (!sub) { - ERROR("Error, can't find interface to delete (%s)", ifname); + ERROR("Error, can't find indentity to delete (%s)", name); return SR_ERR_INTERNAL; } - DEBUG("Unsubscribe from \"%s\"", sub->ifname); + DEBUG("Unsubscribe from \"%s\"", sub->name); sub_delete(statd->ev_loop, &statd->subs, sub); return SR_ERR_OK; } +static int unsub_to_iface(struct statd *statd, char *ifname) +{ + char name[IFNAMSIZ+3]; + snprintf(name,sizeof(name),"if-%s",ifname); + + return unsub_to_name(statd, name); +} static int nl_process_msg(struct nlmsghdr *nlh, struct statd *statd) { @@ -337,7 +401,7 @@ static int nl_process_msg(struct nlmsghdr *nlh, struct statd *statd) static void nl_event_cb(struct ev_loop *, struct ev_io *w, int) { - struct statd *statd = (struct statd *) w->data; + struct statd *statd = (struct statd *)w->data; char buf[NL_BUF_SIZE]; struct nlmsghdr *nlh; int err; @@ -379,13 +443,11 @@ static int sub_to_ifaces(struct statd *statd) json_array_foreach(j_root, i, j_iface) { json_t *j_ifname; int err; - j_ifname = json_object_get(j_iface, "ifname"); if (!json_is_string(j_ifname)) { ERROR("Got unexpected JSON type for 'ifname'"); continue; } - err = sub_to_iface(statd, json_string_value(j_ifname)); if (err) { ERROR("Unable to subscribe to %s", json_string_value(j_ifname)); @@ -447,6 +509,13 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } + err = sub_to_routes(&statd); + if (err) { + ERROR("Error register for IPv4 routes"); + sr_disconnect(sr_conn); + return EXIT_FAILURE; + } + ev_signal_init(&sigint_watcher, sigint_cb, SIGINT); sigint_watcher.data = &statd; ev_signal_start(statd.ev_loop, &sigint_watcher); @@ -464,7 +533,7 @@ int main(int argc, char *argv[]) /* We should never get here during normal operation */ INFO("Status daemon shutting down"); - unsub_to_ifaces(&statd); + unsub_to_all(&statd); sr_session_stop(statd.sr_ses); sr_disconnect(sr_conn); diff --git a/test/case/cli_pretty/all.yaml b/test/case/cli_pretty/all.yaml index e5074989..06b5e815 100644 --- a/test/case/cli_pretty/all.yaml +++ b/test/case/cli_pretty/all.yaml @@ -32,8 +32,13 @@ - "-n vlan1" - case: run.sh - name: "interface-birdge" + name: "interface-bridge" opts: - "json/bloated.json" - "ietf-interfaces" - "-n br0" +- case: run.sh + name: "routing" + opts: + - "json/bloated.json" + - "ietf-routing" diff --git a/test/case/cli_pretty/json/bloated.json b/test/case/cli_pretty/json/bloated.json index 2e580621..ecdc1c83 100644 --- a/test/case/cli_pretty/json/bloated.json +++ b/test/case/cli_pretty/json/bloated.json @@ -1,4 +1,53 @@ { + "ietf-routing:routing": { + "ribs": { + "rib": [ + { + "name": "ipv4", + "address-family": "ietf-routing:ipv4", + "routes": { + "route": [ + { + "next-hop": { + "outgoing-interface": "e0" + }, + "source-protocol": "infix-routing:kernel", + "ietf-ipv4-unicast-routing:destination-prefix": "1.1.1.0/24" + }, + { + "next-hop": { + "ietf-ipv4-unicast-routing:next-hop-address": "1.1.1.1" + }, + "source-protocol": "infix-routing:static", + "ietf-ipv4-unicast-routing:destination-prefix": "192.168.100.0/24" + }, + { + "next-hop": { + "special-next-hop": "blackhole" + }, + "source-protocol": "infix-routing:static", + "ietf-ipv4-unicast-routing:destination-prefix": "192.168.110.2/32" + }, + { + "next-hop": { + "special-next-hop": "blackhole" + }, + "source-protocol": "infix-routing:static", + "ietf-ipv4-unicast-routing:destination-prefix": "192.168.120.2/32" + }, + { + "next-hop": { + "special-next-hop": "unreachable" + }, + "source-protocol": "infix-routing:static", + "ietf-ipv4-unicast-routing:destination-prefix": "192.168.130.2/32" + } + ] + } + } + ] + } + }, "ietf-interfaces:interfaces": { "interface": [ { @@ -138,7 +187,7 @@ { "ip": "10.140.211.247", "prefix-length": 24, - "origin" : "static" + "origin": "static" } ] }, @@ -175,22 +224,22 @@ { "ip": "192.168.76.120", "prefix-length": 24, - "origin" : "static" + "origin": "static" }, { "ip": "192.168.151.147", "prefix-length": 24, - "origin" : "static" + "origin": "static" }, { "ip": "192.168.161.217", "prefix-length": 24, - "origin" : "dhcp" + "origin": "dhcp" }, { "ip": "192.168.55.37", "prefix-length": 24, - "origin" : "dhcp" + "origin": "dhcp" }, { "ip": "192.168.231.242", @@ -457,12 +506,12 @@ { "ip": "172.28.29.76", "prefix-length": 24, - "origin" : "static" + "origin": "static" }, { "ip": "172.16.187.13", "prefix-length": 24, - "origin" : "static" + "origin": "static" } ] },