diff options
28 files changed, 7891 insertions, 717 deletions
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index a88f39f46b..6735c1a952 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -11304,8 +11304,13 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, vty, use_json, json_paths); - if (use_json && display) - json_object_object_add(json, "paths", json_paths); + if (use_json) { + if (display) + json_object_object_add(json, "paths", + json_paths); + else + json_object_free(json_paths); + } } else { if ((dest = bgp_node_match(rib, &match)) != NULL) { const struct prefix *dest_p = bgp_dest_get_prefix(dest); @@ -12784,6 +12789,7 @@ static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi, "No such neighbor or address family"); vty_out(vty, "%s\n", json_object_to_json_string(json)); json_object_free(json); + json_object_free(json_loop); } else vty_out(vty, "%% No such neighbor or address family\n"); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 6bcde16269..3dc2cfbd5c 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -3531,8 +3531,9 @@ static void bgp_route_map_process_peer(const char *rmap_name, PEER_FLAG_SOFT_RECONFIG)) { if (bgp_debug_update(peer, NULL, NULL, 1)) zlog_debug( - "Processing route_map %s update on peer %s (inbound, soft-reconfig)", - rmap_name, peer->host); + "Processing route_map %s(%s:%s) update on peer %s (inbound, soft-reconfig)", + rmap_name, afi2str(afi), + safi2str(safi), peer->host); bgp_soft_reconfig_in(peer, afi, safi); } else if (CHECK_FLAG(peer->cap, @@ -3541,8 +3542,9 @@ static void bgp_route_map_process_peer(const char *rmap_name, PEER_CAP_REFRESH_NEW_RCV)) { if (bgp_debug_update(peer, NULL, NULL, 1)) zlog_debug( - "Processing route_map %s update on peer %s (inbound, route-refresh)", - rmap_name, peer->host); + "Processing route_map %s(%s:%s) update on peer %s (inbound, route-refresh)", + rmap_name, afi2str(afi), + safi2str(safi), peer->host); bgp_route_refresh_send( peer, afi, safi, 0, 0, 0, BGP_ROUTE_REFRESH_NORMAL); @@ -3681,8 +3683,9 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug( - "Processing route_map %s update on table map", - rmap_name); + "Processing route_map %s(%s:%s) update on table map", + rmap_name, afi2str(afi), + safi2str(safi)); if (route_update) bgp_zebra_announce_table(bgp, afi, safi); } @@ -3709,8 +3712,9 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, if (bgp_debug_zebra(bn_p)) zlog_debug( - "Processing route_map %s update on static route %s", - rmap_name, + "Processing route_map %s(%s:%s) update on static route %s", + rmap_name, afi2str(afi), + safi2str(safi), inet_ntop(bn_p->family, &bn_p->u.prefix, buf, INET6_ADDRSTRLEN)); @@ -3760,8 +3764,9 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, if (bgp_debug_zebra(bn_p)) zlog_debug( - "Processing route_map %s update on aggregate-address route %s", - rmap_name, + "Processing route_map %s(%s:%s) update on aggregate-address route %s", + rmap_name, afi2str(afi), + safi2str(safi), inet_ntop(bn_p->family, &bn_p->u.prefix, buf, INET6_ADDRSTRLEN)); @@ -3796,8 +3801,9 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug( - "Processing route_map %s update on redistributed routes", - rmap_name); + "Processing route_map %s(%s:%s) update on redistributed routes", + rmap_name, afi2str(afi), + safi2str(safi)); bgp_redistribute_resend(bgp, afi, i, red->instance); @@ -3816,8 +3822,8 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug( - "Processing route_map %s update on advertise type5 route command", - rmap_name); + "Processing route_map %s(%s:%s) update on advertise type5 route command", + rmap_name, afi2str(afi), safi2str(safi)); if (route_update && advertise_type5_routes(bgp, afi)) { bgp_evpn_withdraw_type5_routes(bgp, afi, safi); diff --git a/doc/developer/topotests-markers.rst b/doc/developer/topotests-markers.rst new file mode 100644 index 0000000000..02ffe3f777 --- /dev/null +++ b/doc/developer/topotests-markers.rst @@ -0,0 +1,115 @@ +.. _topotests-markers: + +Markers +-------- + +To allow for automated selective testing on large scale continuous integration +systems, all tests must be marked with at least one of the following markers: + +* babeld +* bfdd +* bgpd +* eigrpd +* isisd +* ldpd +* nhrpd +* ospf6d +* ospfd +* pathd +* pbrd +* pimd +* ripd +* ripngd +* sharpd +* staticd +* vrrpd + +The markers corespond to the daemon subdirectories in FRR's source code and have +to be added to tests on a module level depending on which daemons are used +during the test. + +The goal is to have continuous integration systems scan code submissions, detect +changes to files in a daemons subdirectory and select only tests using that +daemon to run to shorten developers waiting times for test results and save test +infrastructure resources. + +Newly written modules and code changes on tests, which do not contain any or +incorrect markers will be rejected by reviewers. + + +Registering markers +^^^^^^^^^^^^^^^^^^^ +The Registration of new markers takes place in the file +``tests/topotests/pytest.ini`` and should be discussed with members of the TSC +beforehand. + +.. code:: python3 + + # tests/topotests/pytest.ini + [pytest] + ... + markers = + babeld: Tests that run against BABELD + bfdd: Tests that run against BFDD + ... + vrrpd: Tests that run against VRRPD + + +Adding markers to tests +^^^^^^^^^^^^^^^^^^^^^^^ +Markers are added to a test by placing a global variable in the test module. + +Adding a single marker: + +.. code:: python3 + + import pytest + + ... + + pytestmark = pytest.mark.bfdd + + ... + + def test_using_bfdd(): + + +Adding multiple markers: + +.. code:: python3 + + import pytest + + ... + + pytestmark = [ + pytest.mark.bgpd, + pytest.mark.ospfd, + pytest.mark.ospf6d + ] + + ... + + def test_using_bgpd_ospfd_ospf6d(): + + +Selecting marked modules fort testing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Selecting by a single marker: + +.. code:: bash + + pytest -v -m isisd + +Selecting by multiple markers: + +.. code:: bash + + pytest -v -m "isisd or ldpd or nhrpd" + + +Further Information +^^^^^^^^^^^^^^^^^^^ +The `online pytest documentation <https://docs.pytest.org/en/stable/example/markers.html>`_ +provides further information and usage examples for pytest markers. + diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index 688f73c991..3e8987f126 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -792,6 +792,8 @@ Requirements: conforms with this, run it without the :option:`-s` parameter. - Use `black <https://github.com/psf/black>`_ code formatter before creating a pull request. This ensures we have a unified code style. +- Mark test modules with pytest markers depending on the daemons used during the + tests (s. Markers) Tips: @@ -950,6 +952,8 @@ does what you need. If nothing is similar, then you may create a new topology, preferably, using the newest template (:file:`tests/topotests/example-test/test_template.py`). +.. include:: topotests-markers.rst + .. include:: topotests-snippets.rst License diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index d5b41b3d21..173daa9b22 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -435,10 +435,12 @@ Require policy on EBGP .. clicmd:: [no] bgp ebgp-requires-policy This command requires incoming and outgoing filters to be applied - for eBGP sessions. Without the incoming filter, no routes will be - accepted. Without the outgoing filter, no routes will be announced. + for eBGP sessions as part of RFC-8212 compliance. Without the incoming + filter, no routes will be accepted. Without the outgoing filter, no + routes will be announced. - This is enabled by default. + This is enabled by default for the traditional configuration and + turned off by default for datacenter configuration. When the incoming or outgoing filter is missing you will see "(Policy)" sign under ``show bgp summary``: @@ -457,6 +459,22 @@ Require policy on EBGP 192.168.0.2 4 65002 8 10 0 0 0 00:03:09 5 (Policy) fe80:1::2222 4 65002 9 11 0 0 0 00:03:09 (Policy) (Policy) + Additionally a `show bgp neighbor` command would indicate in the `For address family:` + block that: + + .. code-block:: frr + + exit1# show bgp neighbor + ... + For address family: IPv4 Unicast + Update group 1, subgroup 1 + Packet Queue length 0 + Inbound soft reconfiguration allowed + Community attribute sent to this neighbor(all) + Inbound updates discarded due to missing policy + Outbound updates discarded due to missing policy + 0 accepted prefixes + Reject routes with AS_SET or AS_CONFED_SET types ------------------------------------------------ diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 4f0ff90943..b00bdb3ab8 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -170,10 +170,34 @@ Showing OSPF6 information instance ID, simply type "show ipv6 ospf6 <cr>". JSON output can be obtained by appending 'json' to the end of command. -.. index:: show ipv6 ospf6 database -.. clicmd:: show ipv6 ospf6 database +.. index:: show ipv6 ospf6 database [<detail|dump|internal>] [json] +.. clicmd:: show ipv6 ospf6 database [<detail|dump|internal>] [json] - This command shows LSA database summary. You can specify the type of LSA. + This command shows LSAs present in the LSDB. There are three view options. + These options helps in viewing all the parameters of the LSAs. JSON output + can be obtained by appending 'json' to the end of command. JSON option is + not applicable with 'dump' option. + +.. index:: show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> [json] +.. clicmd:: show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> [json] + + These options filters out the LSA based on its type. The three views options + works here as well. JSON output can be obtained by appending 'json' to the + end of command. + +.. index:: show ipv6 ospf6 database adv-router A.B.C.D linkstate-id A.B.C.D [json] +.. clicmd:: show ipv6 ospf6 database adv-router A.B.C.D linkstate-id A.B.C.D [json] + + The LSAs additinally can also be filtered with the linkstate-id and + advertising-router fields. We can use the LSA type filter and views with + this command as well and visa-versa. JSON output can be obtained by + appending 'json' to the end of command. + +.. index:: show ipv6 ospf6 database self-originated [json] +.. clicmd:: show ipv6 ospf6 database self-originated [json] + + This command is used to filter the LSAs which are originated by the present + router. All the other filters are applicable here as well. .. index:: show ipv6 ospf6 interface [json] .. clicmd:: show ipv6 ospf6 interface [json] diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index 8cfdf2642c..eee98275ec 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -1296,7 +1296,9 @@ static char *ospf6_inter_area_prefix_lsa_get_prefix_str(struct ospf6_lsa *lsa, } static int ospf6_inter_area_prefix_lsa_show(struct vty *vty, - struct ospf6_lsa *lsa) + struct ospf6_lsa *lsa, + json_object *json_obj, + bool use_json) { struct ospf6_inter_prefix_lsa *prefix_lsa; char buf[INET6_ADDRSTRLEN]; @@ -1304,16 +1306,29 @@ static int ospf6_inter_area_prefix_lsa_show(struct vty *vty, prefix_lsa = (struct ospf6_inter_prefix_lsa *)OSPF6_LSA_HEADER_END( lsa->header); - vty_out(vty, " Metric: %lu\n", - (unsigned long)OSPF6_ABR_SUMMARY_METRIC(prefix_lsa)); + if (use_json) { + json_object_int_add( + json_obj, "metric", + (unsigned long)OSPF6_ABR_SUMMARY_METRIC(prefix_lsa)); + ospf6_prefix_options_printbuf(prefix_lsa->prefix.prefix_options, + buf, sizeof(buf)); + json_object_string_add(json_obj, "prefixOptions", buf); + json_object_string_add( + json_obj, "prefix", + ospf6_inter_area_prefix_lsa_get_prefix_str( + lsa, buf, sizeof(buf), 0)); + } else { + vty_out(vty, " Metric: %lu\n", + (unsigned long)OSPF6_ABR_SUMMARY_METRIC(prefix_lsa)); - ospf6_prefix_options_printbuf(prefix_lsa->prefix.prefix_options, buf, - sizeof(buf)); - vty_out(vty, " Prefix Options: %s\n", buf); + ospf6_prefix_options_printbuf(prefix_lsa->prefix.prefix_options, + buf, sizeof(buf)); + vty_out(vty, " Prefix Options: %s\n", buf); - vty_out(vty, " Prefix: %s\n", - ospf6_inter_area_prefix_lsa_get_prefix_str(lsa, buf, - sizeof(buf), 0)); + vty_out(vty, " Prefix: %s\n", + ospf6_inter_area_prefix_lsa_get_prefix_str( + lsa, buf, sizeof(buf), 0)); + } return 0; } @@ -1338,7 +1353,9 @@ static char *ospf6_inter_area_router_lsa_get_prefix_str(struct ospf6_lsa *lsa, } static int ospf6_inter_area_router_lsa_show(struct vty *vty, - struct ospf6_lsa *lsa) + struct ospf6_lsa *lsa, + json_object *json_obj, + bool use_json) { struct ospf6_inter_router_lsa *router_lsa; char buf[64]; @@ -1347,12 +1364,22 @@ static int ospf6_inter_area_router_lsa_show(struct vty *vty, lsa->header); ospf6_options_printbuf(router_lsa->options, buf, sizeof(buf)); - vty_out(vty, " Options: %s\n", buf); - vty_out(vty, " Metric: %lu\n", - (unsigned long)OSPF6_ABR_SUMMARY_METRIC(router_lsa)); + if (use_json) { + json_object_string_add(json_obj, "options", buf); + json_object_int_add( + json_obj, "metric", + (unsigned long)OSPF6_ABR_SUMMARY_METRIC(router_lsa)); + } else { + vty_out(vty, " Options: %s\n", buf); + vty_out(vty, " Metric: %lu\n", + (unsigned long)OSPF6_ABR_SUMMARY_METRIC(router_lsa)); + } inet_ntop(AF_INET, &router_lsa->router_id, buf, sizeof(buf)); - vty_out(vty, " Destination Router ID: %s\n", buf); + if (use_json) + json_object_string_add(json_obj, "destinationRouterId", buf); + else + vty_out(vty, " Destination Router ID: %s\n", buf); return 0; } diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 0e419cbff6..af99bc0c88 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -1890,7 +1890,8 @@ static char *ospf6_as_external_lsa_get_prefix_str(struct ospf6_lsa *lsa, return (buf); } -static int ospf6_as_external_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) +static int ospf6_as_external_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_obj, bool use_json) { struct ospf6_as_external_lsa *external; char buf[64]; @@ -1908,31 +1909,65 @@ static int ospf6_as_external_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T) ? 'T' : '-')); - vty_out(vty, " Bits: %s\n", buf); - vty_out(vty, " Metric: %5lu\n", - (unsigned long)OSPF6_ASBR_METRIC(external)); - - ospf6_prefix_options_printbuf(external->prefix.prefix_options, buf, - sizeof(buf)); - vty_out(vty, " Prefix Options: %s\n", buf); + if (use_json) { + json_object_string_add(json_obj, "bits", buf); + json_object_int_add(json_obj, "metric", + (unsigned long)OSPF6_ASBR_METRIC(external)); + ospf6_prefix_options_printbuf(external->prefix.prefix_options, + buf, sizeof(buf)); + json_object_string_add(json_obj, "prefixOptions", buf); + json_object_int_add( + json_obj, "referenceLsType", + ntohs(external->prefix.prefix_refer_lstype)); + json_object_string_add(json_obj, "prefix", + ospf6_as_external_lsa_get_prefix_str( + lsa, buf, sizeof(buf), 0)); + + /* Forwarding-Address */ + json_object_boolean_add( + json_obj, "forwardingAddressPresent", + CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)); + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) + json_object_string_add( + json_obj, "forwardingAddress", + ospf6_as_external_lsa_get_prefix_str( + lsa, buf, sizeof(buf), 1)); + + /* Tag */ + json_object_boolean_add( + json_obj, "tagPresent", + CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T)); + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T)) + json_object_int_add(json_obj, "tag", + ospf6_as_external_lsa_get_tag(lsa)); + } else { + vty_out(vty, " Bits: %s\n", buf); + vty_out(vty, " Metric: %5lu\n", + (unsigned long)OSPF6_ASBR_METRIC(external)); - vty_out(vty, " Referenced LSType: %d\n", - ntohs(external->prefix.prefix_refer_lstype)); + ospf6_prefix_options_printbuf(external->prefix.prefix_options, + buf, sizeof(buf)); + vty_out(vty, " Prefix Options: %s\n", buf); - vty_out(vty, " Prefix: %s\n", - ospf6_as_external_lsa_get_prefix_str(lsa, buf, sizeof(buf), 0)); + vty_out(vty, " Referenced LSType: %d\n", + ntohs(external->prefix.prefix_refer_lstype)); - /* Forwarding-Address */ - if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) { - vty_out(vty, " Forwarding-Address: %s\n", + vty_out(vty, " Prefix: %s\n", ospf6_as_external_lsa_get_prefix_str(lsa, buf, - sizeof(buf), 1)); - } + sizeof(buf), 0)); + + /* Forwarding-Address */ + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) { + vty_out(vty, " Forwarding-Address: %s\n", + ospf6_as_external_lsa_get_prefix_str( + lsa, buf, sizeof(buf), 1)); + } - /* Tag */ - if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T)) { - vty_out(vty, " Tag: %" ROUTE_TAG_PRI "\n", - ospf6_as_external_lsa_get_tag(lsa)); + /* Tag */ + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T)) { + vty_out(vty, " Tag: %" ROUTE_TAG_PRI "\n", + ospf6_as_external_lsa_get_tag(lsa)); + } } return 0; diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 17538c466a..01e4b31c4e 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -92,12 +92,15 @@ static char *ospf6_router_lsa_get_nbr_id(struct ospf6_lsa *lsa, char *buf, return buf; } -static int ospf6_router_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) +static int ospf6_router_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_obj, bool use_json) { char *start, *end, *current; char buf[32], name[32], bits[16], options[32]; struct ospf6_router_lsa *router_lsa; struct ospf6_router_lsdesc *lsdesc; + json_object *json_arr; + json_object *json_loop; router_lsa = (struct ospf6_router_lsa *)((char *)lsa->header @@ -105,7 +108,12 @@ static int ospf6_router_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) ospf6_capability_printbuf(router_lsa->bits, bits, sizeof(bits)); ospf6_options_printbuf(router_lsa->options, options, sizeof(options)); - vty_out(vty, " Bits: %s Options: %s\n", bits, options); + if (use_json) { + json_object_string_add(json_obj, "bits", bits); + json_object_string_add(json_obj, "options", options); + json_arr = json_object_new_array(); + } else + vty_out(vty, " Bits: %s Options: %s\n", bits, options); start = (char *)router_lsa + sizeof(struct ospf6_router_lsa); end = (char *)lsa->header + ntohs(lsa->header->length); @@ -126,18 +134,43 @@ static int ospf6_router_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) snprintf(name, sizeof(name), "Unknown (%#x)", lsdesc->type); - vty_out(vty, " Type: %s Metric: %d\n", name, - ntohs(lsdesc->metric)); - vty_out(vty, " Interface ID: %s\n", - inet_ntop(AF_INET, &lsdesc->interface_id, buf, - sizeof(buf))); - vty_out(vty, " Neighbor Interface ID: %s\n", - inet_ntop(AF_INET, &lsdesc->neighbor_interface_id, buf, - sizeof(buf))); - vty_out(vty, " Neighbor Router ID: %s\n", - inet_ntop(AF_INET, &lsdesc->neighbor_router_id, buf, - sizeof(buf))); + if (use_json) { + json_loop = json_object_new_object(); + json_object_string_add(json_loop, "type", name); + json_object_int_add(json_loop, "metric", + ntohs(lsdesc->metric)); + json_object_string_add(json_loop, "interfaceId", + inet_ntop(AF_INET, + &lsdesc->interface_id, + buf, sizeof(buf))); + json_object_string_add( + json_loop, "neighborInterfaceId", + inet_ntop(AF_INET, + &lsdesc->neighbor_interface_id, buf, + sizeof(buf))); + json_object_string_add( + json_loop, "neighborRouterId", + inet_ntop(AF_INET, &lsdesc->neighbor_router_id, + buf, sizeof(buf))); + json_object_array_add(json_arr, json_loop); + } else { + vty_out(vty, " Type: %s Metric: %d\n", name, + ntohs(lsdesc->metric)); + vty_out(vty, " Interface ID: %s\n", + inet_ntop(AF_INET, &lsdesc->interface_id, buf, + sizeof(buf))); + vty_out(vty, " Neighbor Interface ID: %s\n", + inet_ntop(AF_INET, + &lsdesc->neighbor_interface_id, buf, + sizeof(buf))); + vty_out(vty, " Neighbor Router ID: %s\n", + inet_ntop(AF_INET, &lsdesc->neighbor_router_id, + buf, sizeof(buf))); + } } + if (use_json) + json_object_object_add(json_obj, "lsaDescription", json_arr); + return 0; } @@ -421,29 +454,44 @@ static char *ospf6_network_lsa_get_ar_id(struct ospf6_lsa *lsa, char *buf, return (buf); } -static int ospf6_network_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) +static int ospf6_network_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_obj, bool use_json) { char *start, *end, *current; struct ospf6_network_lsa *network_lsa; struct ospf6_network_lsdesc *lsdesc; char buf[128], options[32]; + json_object *json_arr; network_lsa = (struct ospf6_network_lsa *)((caddr_t)lsa->header + sizeof(struct ospf6_lsa_header)); ospf6_options_printbuf(network_lsa->options, options, sizeof(options)); - vty_out(vty, " Options: %s\n", options); + if (use_json) + json_object_string_add(json_obj, "options", options); + else + vty_out(vty, " Options: %s\n", options); start = (char *)network_lsa + sizeof(struct ospf6_network_lsa); end = (char *)lsa->header + ntohs(lsa->header->length); + if (use_json) + json_arr = json_object_new_array(); + for (current = start; current + sizeof(struct ospf6_network_lsdesc) <= end; current += sizeof(struct ospf6_network_lsdesc)) { lsdesc = (struct ospf6_network_lsdesc *)current; inet_ntop(AF_INET, &lsdesc->router_id, buf, sizeof(buf)); - vty_out(vty, " Attached Router: %s\n", buf); + if (use_json) + json_object_array_add(json_arr, + json_object_new_string(buf)); + else + vty_out(vty, " Attached Router: %s\n", buf); } + if (use_json) + json_object_object_add(json_obj, "attachedRouter", json_arr); + return 0; } @@ -625,7 +673,8 @@ static char *ospf6_link_lsa_get_prefix_str(struct ospf6_lsa *lsa, char *buf, return NULL; } -static int ospf6_link_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) +static int ospf6_link_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_obj, bool use_json) { char *start, *end, *current; struct ospf6_link_lsa *link_lsa; @@ -634,6 +683,10 @@ static int ospf6_link_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) struct ospf6_prefix *prefix; const char *p, *mc, *la, *nu; struct in6_addr in6; + json_object *json_loop; + json_object *json_arr = NULL; + char str[15]; + char prefix_string[133]; link_lsa = (struct ospf6_link_lsa *)((caddr_t)lsa->header + sizeof(struct ospf6_lsa_header)); @@ -642,10 +695,18 @@ static int ospf6_link_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) inet_ntop(AF_INET6, &link_lsa->linklocal_addr, buf, sizeof(buf)); prefixnum = ntohl(link_lsa->prefix_num); - vty_out(vty, " Priority: %d Options: %s\n", link_lsa->priority, - options); - vty_out(vty, " LinkLocal Address: %s\n", buf); - vty_out(vty, " Number of Prefix: %d\n", prefixnum); + if (use_json) { + json_arr = json_object_new_array(); + json_object_int_add(json_obj, "priority", link_lsa->priority); + json_object_string_add(json_obj, "options", options); + json_object_string_add(json_obj, "linkLocalAddress", buf); + json_object_int_add(json_obj, "numberOfPrefix", prefixnum); + } else { + vty_out(vty, " Priority: %d Options: %s\n", + link_lsa->priority, options); + vty_out(vty, " LinkLocal Address: %s\n", buf); + vty_out(vty, " Number of Prefix: %d\n", prefixnum); + } start = (char *)link_lsa + sizeof(struct ospf6_link_lsa); end = (char *)lsa->header + ntohs(lsa->header->length); @@ -668,16 +729,31 @@ static int ospf6_link_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) nu = (CHECK_FLAG(prefix->prefix_options, OSPF6_PREFIX_OPTION_NU) ? "NU" : "--"); - vty_out(vty, " Prefix Options: %s|%s|%s|%s\n", p, mc, la, - nu); + if (use_json) { + json_loop = json_object_new_object(); + snprintf(str, sizeof(str), "%s|%s|%s|%s", p, mc, la, + nu); + json_object_string_add(json_loop, "prefixOption", str); + } else + vty_out(vty, " Prefix Options: %s|%s|%s|%s\n", p, + mc, la, nu); memset(&in6, 0, sizeof(in6)); memcpy(&in6, OSPF6_PREFIX_BODY(prefix), OSPF6_PREFIX_SPACE(prefix->prefix_length)); inet_ntop(AF_INET6, &in6, buf, sizeof(buf)); - vty_out(vty, " Prefix: %s/%d\n", buf, - prefix->prefix_length); + if (use_json) { + snprintf(prefix_string, sizeof(prefix_string), "%s/%d", + buf, prefix->prefix_length); + json_object_string_add(json_loop, "prefix", + prefix_string); + json_object_array_add(json_arr, json_loop); + } else + vty_out(vty, " Prefix: %s/%d\n", buf, + prefix->prefix_length); } + if (use_json) + json_object_object_add(json_obj, "prefix", json_arr); return 0; } @@ -828,7 +904,8 @@ static char *ospf6_intra_prefix_lsa_get_prefix_str(struct ospf6_lsa *lsa, return (buf); } -static int ospf6_intra_prefix_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) +static int ospf6_intra_prefix_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_obj, bool use_json) { char *start, *end, *current; struct ospf6_intra_prefix_lsa *intra_prefix_lsa; @@ -838,6 +915,10 @@ static int ospf6_intra_prefix_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) char id[16], adv_router[16]; const char *p, *mc, *la, *nu; struct in6_addr in6; + json_object *json_loop; + json_object *json_arr = NULL; + char str[15]; + char prefix_string[133]; intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *)((caddr_t)lsa->header @@ -845,13 +926,25 @@ static int ospf6_intra_prefix_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) prefixnum = ntohs(intra_prefix_lsa->prefix_num); - vty_out(vty, " Number of Prefix: %d\n", prefixnum); + if (use_json) { + json_arr = json_object_new_array(); + json_object_int_add(json_obj, "numberOfPrefix", prefixnum); + } else + vty_out(vty, " Number of Prefix: %d\n", prefixnum); inet_ntop(AF_INET, &intra_prefix_lsa->ref_id, id, sizeof(id)); inet_ntop(AF_INET, &intra_prefix_lsa->ref_adv_router, adv_router, sizeof(adv_router)); - vty_out(vty, " Reference: %s Id: %s Adv: %s\n", - ospf6_lstype_name(intra_prefix_lsa->ref_type), id, adv_router); + if (use_json) { + json_object_string_add( + json_obj, "reference", + ospf6_lstype_name(intra_prefix_lsa->ref_type)); + json_object_string_add(json_obj, "referenceId", id); + json_object_string_add(json_obj, "referenceAdv", adv_router); + } else + vty_out(vty, " Reference: %s Id: %s Adv: %s\n", + ospf6_lstype_name(intra_prefix_lsa->ref_type), id, + adv_router); start = (char *)intra_prefix_lsa + sizeof(struct ospf6_intra_prefix_lsa); @@ -875,16 +968,31 @@ static int ospf6_intra_prefix_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) nu = (CHECK_FLAG(prefix->prefix_options, OSPF6_PREFIX_OPTION_NU) ? "NU" : "--"); - vty_out(vty, " Prefix Options: %s|%s|%s|%s\n", p, mc, la, - nu); + if (use_json) { + json_loop = json_object_new_object(); + snprintf(str, sizeof(str), "%s|%s|%s|%s", p, mc, la, + nu); + json_object_string_add(json_loop, "prefixOption", str); + } else + vty_out(vty, " Prefix Options: %s|%s|%s|%s\n", p, + mc, la, nu); memset(&in6, 0, sizeof(in6)); memcpy(&in6, OSPF6_PREFIX_BODY(prefix), OSPF6_PREFIX_SPACE(prefix->prefix_length)); inet_ntop(AF_INET6, &in6, buf, sizeof(buf)); - vty_out(vty, " Prefix: %s/%d\n", buf, - prefix->prefix_length); + if (use_json) { + snprintf(prefix_string, sizeof(prefix_string), "%s/%d", + buf, prefix->prefix_length); + json_object_string_add(json_loop, "prefix", + prefix_string); + json_object_array_add(json_arr, json_loop); + } else + vty_out(vty, " Prefix: %s/%d\n", buf, + prefix->prefix_length); } + if (use_json) + json_object_object_add(json_obj, "prefix", json_arr); return 0; } diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index 29141ee7f8..f1b04c9bec 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -66,7 +66,8 @@ struct ospf6 *ospf6_get_by_lsdb(struct ospf6_lsa *lsa) return ospf6; } -static int ospf6_unknown_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) +static int ospf6_unknown_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_obj, bool use_json) { uint8_t *start, *end, *current; char byte[4]; @@ -74,18 +75,22 @@ static int ospf6_unknown_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) start = (uint8_t *)lsa->header + sizeof(struct ospf6_lsa_header); end = (uint8_t *)lsa->header + ntohs(lsa->header->length); - vty_out(vty, " Unknown contents:\n"); - for (current = start; current < end; current++) { - if ((current - start) % 16 == 0) - vty_out(vty, "\n "); - else if ((current - start) % 4 == 0) - vty_out(vty, " "); + if (use_json) + json_object_string_add(json_obj, "LsaType", "unknown"); + else { + vty_out(vty, " Unknown contents:\n"); + for (current = start; current < end; current++) { + if ((current - start) % 16 == 0) + vty_out(vty, "\n "); + else if ((current - start) % 4 == 0) + vty_out(vty, " "); + + snprintf(byte, sizeof(byte), "%02x", *current); + vty_out(vty, "%s", byte); + } - snprintf(byte, sizeof(byte), "%02x", *current); - vty_out(vty, "%s", byte); + vty_out(vty, "\n\n"); } - - vty_out(vty, "\n\n"); return 0; } @@ -392,13 +397,15 @@ void ospf6_lsa_show_summary_header(struct vty *vty) "AdvRouter", "Age", "SeqNum", "Payload"); } -void ospf6_lsa_show_summary(struct vty *vty, struct ospf6_lsa *lsa) +void ospf6_lsa_show_summary(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_array, bool use_json) { char adv_router[16], id[16]; int type; const struct ospf6_lsa_handler *handler; - char buf[64], tmpbuf[80]; + char buf[64]; int cnt = 0; + json_object *json_obj = NULL; assert(lsa); assert(lsa->header); @@ -409,34 +416,95 @@ void ospf6_lsa_show_summary(struct vty *vty, struct ospf6_lsa *lsa) type = ntohs(lsa->header->type); handler = ospf6_get_lsa_handler(lsa->header->type); + + if (use_json) + json_obj = json_object_new_object(); + if ((type == OSPF6_LSTYPE_INTER_PREFIX) || (type == OSPF6_LSTYPE_INTER_ROUTER) || (type == OSPF6_LSTYPE_AS_EXTERNAL)) { - vty_out(vty, "%-4s %-15s%-15s%4hu %8lx %30s\n", - ospf6_lstype_short_name(lsa->header->type), id, - adv_router, ospf6_lsa_age_current(lsa), - (unsigned long)ntohl(lsa->header->seqnum), - handler->lh_get_prefix_str(lsa, buf, sizeof(buf), 0)); + if (use_json) { + json_object_string_add( + json_obj, "type", + ospf6_lstype_short_name(lsa->header->type)); + json_object_string_add(json_obj, "lsId", id); + json_object_string_add(json_obj, "advRouter", + adv_router); + json_object_int_add(json_obj, "age", + ospf6_lsa_age_current(lsa)); + json_object_int_add( + json_obj, "seqNum", + (unsigned long)ntohl(lsa->header->seqnum)); + json_object_string_add( + json_obj, "payload", + handler->lh_get_prefix_str(lsa, buf, + sizeof(buf), 0)); + json_object_array_add(json_array, json_obj); + } else + vty_out(vty, "%-4s %-15s%-15s%4hu %8lx %30s\n", + ospf6_lstype_short_name(lsa->header->type), id, + adv_router, ospf6_lsa_age_current(lsa), + (unsigned long)ntohl(lsa->header->seqnum), + handler->lh_get_prefix_str(lsa, buf, + sizeof(buf), 0)); } else if (type != OSPF6_LSTYPE_UNKNOWN) { - snprintf(tmpbuf, sizeof(tmpbuf), "%-4s %-15s%-15s%4hu %8lx", - ospf6_lstype_short_name(lsa->header->type), id, - adv_router, ospf6_lsa_age_current(lsa), - (unsigned long)ntohl(lsa->header->seqnum)); - while (handler->lh_get_prefix_str(lsa, buf, sizeof(buf), cnt) != NULL) { - vty_out(vty, "%s %30s\n", tmpbuf, buf); + if (use_json) { + json_object_string_add( + json_obj, "type", + ospf6_lstype_short_name( + lsa->header->type)); + json_object_string_add(json_obj, "lsId", id); + json_object_string_add(json_obj, "advRouter", + adv_router); + json_object_int_add(json_obj, "age", + ospf6_lsa_age_current(lsa)); + json_object_int_add( + json_obj, "seqNum", + (unsigned long)ntohl( + lsa->header->seqnum)); + json_object_string_add(json_obj, "payload", + buf); + json_object_array_add(json_array, json_obj); + json_obj = json_object_new_object(); + } else + vty_out(vty, "%-4s %-15s%-15s%4hu %8lx %30s\n", + ospf6_lstype_short_name( + lsa->header->type), + id, adv_router, + ospf6_lsa_age_current(lsa), + (unsigned long)ntohl( + lsa->header->seqnum), + buf); cnt++; } + if (use_json) + json_object_free(json_obj); } else { - vty_out(vty, "%-4s %-15s%-15s%4hu %8lx\n", - ospf6_lstype_short_name(lsa->header->type), id, - adv_router, ospf6_lsa_age_current(lsa), - (unsigned long)ntohl(lsa->header->seqnum)); + if (use_json) { + json_object_string_add( + json_obj, "type", + ospf6_lstype_short_name(lsa->header->type)); + json_object_string_add(json_obj, "lsId", id); + json_object_string_add(json_obj, "advRouter", + adv_router); + json_object_int_add(json_obj, "age", + ospf6_lsa_age_current(lsa)); + json_object_int_add( + json_obj, "seqNum", + (unsigned long)ntohl(lsa->header->seqnum)); + json_object_array_add(json_array, json_obj); + } else + vty_out(vty, "%-4s %-15s%-15s%4hu %8lx\n", + ospf6_lstype_short_name(lsa->header->type), id, + adv_router, ospf6_lsa_age_current(lsa), + (unsigned long)ntohl(lsa->header->seqnum)); } } -void ospf6_lsa_show_dump(struct vty *vty, struct ospf6_lsa *lsa) +void ospf6_lsa_show_dump(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_array, bool use_json) { uint8_t *start, *end, *current; char byte[4]; @@ -444,6 +512,9 @@ void ospf6_lsa_show_dump(struct vty *vty, struct ospf6_lsa *lsa) start = (uint8_t *)lsa->header; end = (uint8_t *)lsa->header + ntohs(lsa->header->length); + if (use_json) + return; + vty_out(vty, "\n"); vty_out(vty, "%s:\n", lsa->name); @@ -458,12 +529,15 @@ void ospf6_lsa_show_dump(struct vty *vty, struct ospf6_lsa *lsa) } vty_out(vty, "\n\n"); + return; } -void ospf6_lsa_show_internal(struct vty *vty, struct ospf6_lsa *lsa) +void ospf6_lsa_show_internal(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_array, bool use_json) { char adv_router[64], id[64]; + json_object *json_obj; assert(lsa && lsa->header); @@ -471,30 +545,56 @@ void ospf6_lsa_show_internal(struct vty *vty, struct ospf6_lsa *lsa) inet_ntop(AF_INET, &lsa->header->adv_router, adv_router, sizeof(adv_router)); - vty_out(vty, "\n"); - vty_out(vty, "Age: %4hu Type: %s\n", ospf6_lsa_age_current(lsa), - ospf6_lstype_name(lsa->header->type)); - vty_out(vty, "Link State ID: %s\n", id); - vty_out(vty, "Advertising Router: %s\n", adv_router); - vty_out(vty, "LS Sequence Number: %#010lx\n", - (unsigned long)ntohl(lsa->header->seqnum)); - vty_out(vty, "CheckSum: %#06hx Length: %hu\n", - ntohs(lsa->header->checksum), ntohs(lsa->header->length)); - vty_out(vty, "Flag: %x \n", lsa->flag); - vty_out(vty, "Lock: %d \n", lsa->lock); - vty_out(vty, "ReTx Count: %d\n", lsa->retrans_count); - vty_out(vty, "Threads: Expire: 0x%p, Refresh: 0x%p \n", - (void *)lsa->expire, (void *)lsa->refresh); - vty_out(vty, "\n"); + if (use_json) { + json_obj = json_object_new_object(); + json_object_int_add(json_obj, "age", + ospf6_lsa_age_current(lsa)); + json_object_string_add(json_obj, "type", + ospf6_lstype_name(lsa->header->type)); + json_object_string_add(json_obj, "linkStateId", id); + json_object_string_add(json_obj, "advertisingRouter", + adv_router); + json_object_int_add(json_obj, "lsSequenceNumber", + (unsigned long)ntohl(lsa->header->seqnum)); + json_object_int_add(json_obj, "checksum", + ntohs(lsa->header->checksum)); + json_object_int_add(json_obj, "length", + ntohs(lsa->header->length)); + json_object_int_add(json_obj, "flag", lsa->flag); + json_object_int_add(json_obj, "lock", lsa->lock); + json_object_int_add(json_obj, "reTxCount", lsa->retrans_count); + + /* Threads Data not added */ + json_object_array_add(json_array, json_obj); + } else { + vty_out(vty, "\n"); + vty_out(vty, "Age: %4hu Type: %s\n", ospf6_lsa_age_current(lsa), + ospf6_lstype_name(lsa->header->type)); + vty_out(vty, "Link State ID: %s\n", id); + vty_out(vty, "Advertising Router: %s\n", adv_router); + vty_out(vty, "LS Sequence Number: %#010lx\n", + (unsigned long)ntohl(lsa->header->seqnum)); + vty_out(vty, "CheckSum: %#06hx Length: %hu\n", + ntohs(lsa->header->checksum), + ntohs(lsa->header->length)); + vty_out(vty, "Flag: %x \n", lsa->flag); + vty_out(vty, "Lock: %d \n", lsa->lock); + vty_out(vty, "ReTx Count: %d\n", lsa->retrans_count); + vty_out(vty, "Threads: Expire: 0x%p, Refresh: 0x%p \n", + (void *)lsa->expire, (void *)lsa->refresh); + vty_out(vty, "\n"); + } return; } -void ospf6_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) +void ospf6_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_array, bool use_json) { char adv_router[64], id[64]; const struct ospf6_lsa_handler *handler; struct timeval now, res; char duration[64]; + json_object *json_obj = NULL; assert(lsa && lsa->header); @@ -505,27 +605,47 @@ void ospf6_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) monotime(&now); timersub(&now, &lsa->installed, &res); timerstring(&res, duration, sizeof(duration)); - - vty_out(vty, "Age: %4hu Type: %s\n", ospf6_lsa_age_current(lsa), - ospf6_lstype_name(lsa->header->type)); - vty_out(vty, "Link State ID: %s\n", id); - vty_out(vty, "Advertising Router: %s\n", adv_router); - vty_out(vty, "LS Sequence Number: %#010lx\n", - (unsigned long)ntohl(lsa->header->seqnum)); - vty_out(vty, "CheckSum: %#06hx Length: %hu\n", - ntohs(lsa->header->checksum), ntohs(lsa->header->length)); - vty_out(vty, "Duration: %s\n", duration); + if (use_json) { + json_obj = json_object_new_object(); + json_object_int_add(json_obj, "age", + ospf6_lsa_age_current(lsa)); + json_object_string_add(json_obj, "type", + ospf6_lstype_name(lsa->header->type)); + json_object_string_add(json_obj, "advertisingRouter", + adv_router); + json_object_int_add(json_obj, "lsSequenceNumber", + (unsigned long)ntohl(lsa->header->seqnum)); + json_object_int_add(json_obj, "checkSum", + ntohs(lsa->header->checksum)); + json_object_int_add(json_obj, "length", + ntohs(lsa->header->length)); + json_object_string_add(json_obj, "duration", duration); + } else { + vty_out(vty, "Age: %4hu Type: %s\n", ospf6_lsa_age_current(lsa), + ospf6_lstype_name(lsa->header->type)); + vty_out(vty, "Link State ID: %s\n", id); + vty_out(vty, "Advertising Router: %s\n", adv_router); + vty_out(vty, "LS Sequence Number: %#010lx\n", + (unsigned long)ntohl(lsa->header->seqnum)); + vty_out(vty, "CheckSum: %#06hx Length: %hu\n", + ntohs(lsa->header->checksum), + ntohs(lsa->header->length)); + vty_out(vty, "Duration: %s\n", duration); + } handler = ospf6_get_lsa_handler(lsa->header->type); if (handler->lh_show != NULL) - handler->lh_show(vty, lsa); + handler->lh_show(vty, lsa, json_obj, use_json); else { assert(unknown_handler.lh_show != NULL); - unknown_handler.lh_show(vty, lsa); + unknown_handler.lh_show(vty, lsa, json_obj, use_json); } - vty_out(vty, "\n"); + if (use_json) + json_object_array_add(json_array, json_obj); + else + vty_out(vty, "\n"); } /* OSPFv3 LSA creation/deletion function */ diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index 814e276796..7fa9c5fe40 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -21,6 +21,7 @@ #ifndef OSPF6_LSA_H #define OSPF6_LSA_H #include "ospf6_top.h" +#include "lib/json.h" /* Debug option */ #define OSPF6_LSA_DEBUG 0x01 @@ -141,9 +142,10 @@ struct ospf6_lsa_handler { uint16_t lh_type; /* host byte order */ const char *lh_name; const char *lh_short_name; - int (*lh_show)(struct vty *, struct ospf6_lsa *); - char *(*lh_get_prefix_str)(struct ospf6_lsa *, char *buf, - int buflen, int pos); + int (*lh_show)(struct vty *, struct ospf6_lsa *, json_object *json_obj, + bool use_json); + char *(*lh_get_prefix_str)(struct ospf6_lsa *, char *buf, int buflen, + int pos); uint8_t lh_debug; }; @@ -206,10 +208,14 @@ extern char *ospf6_lsa_printbuf(struct ospf6_lsa *lsa, char *buf, int size); extern void ospf6_lsa_header_print_raw(struct ospf6_lsa_header *header); extern void ospf6_lsa_header_print(struct ospf6_lsa *lsa); extern void ospf6_lsa_show_summary_header(struct vty *vty); -extern void ospf6_lsa_show_summary(struct vty *vty, struct ospf6_lsa *lsa); -extern void ospf6_lsa_show_dump(struct vty *vty, struct ospf6_lsa *lsa); -extern void ospf6_lsa_show_internal(struct vty *vty, struct ospf6_lsa *lsa); -extern void ospf6_lsa_show(struct vty *vty, struct ospf6_lsa *lsa); +extern void ospf6_lsa_show_summary(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json, bool use_json); +extern void ospf6_lsa_show_dump(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json, bool use_json); +extern void ospf6_lsa_show_internal(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json, bool use_json); +extern void ospf6_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json, bool use_json); extern struct ospf6_lsa *ospf6_lsa_create(struct ospf6_lsa_header *header); extern struct ospf6_lsa * diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index c136c558cb..9636e1a230 100644 --- a/ospf6d/ospf6_lsdb.c +++ b/ospf6d/ospf6_lsdb.c @@ -346,55 +346,6 @@ int ospf6_lsdb_maxage_remover(struct ospf6_lsdb *lsdb) return (reschedule); } -void ospf6_lsdb_show(struct vty *vty, enum ospf_lsdb_show_level level, - uint16_t *type, uint32_t *id, uint32_t *adv_router, - struct ospf6_lsdb *lsdb) -{ - struct ospf6_lsa *lsa; - const struct route_node *end = NULL; - void (*showfunc)(struct vty *, struct ospf6_lsa *) = NULL; - - switch (level) { - case OSPF6_LSDB_SHOW_LEVEL_DETAIL: - showfunc = ospf6_lsa_show; - break; - case OSPF6_LSDB_SHOW_LEVEL_INTERNAL: - showfunc = ospf6_lsa_show_internal; - break; - case OSPF6_LSDB_SHOW_LEVEL_DUMP: - showfunc = ospf6_lsa_show_dump; - break; - case OSPF6_LSDB_SHOW_LEVEL_NORMAL: - default: - showfunc = ospf6_lsa_show_summary; - } - - if (type && id && adv_router) { - lsa = ospf6_lsdb_lookup(*type, *id, *adv_router, lsdb); - if (lsa) { - if (level == OSPF6_LSDB_SHOW_LEVEL_NORMAL) - ospf6_lsa_show(vty, lsa); - else - (*showfunc)(vty, lsa); - } - return; - } - - if (level == OSPF6_LSDB_SHOW_LEVEL_NORMAL) - ospf6_lsa_show_summary_header(vty); - - end = ospf6_lsdb_head(lsdb, !!type + !!(type && adv_router), - type ? *type : 0, adv_router ? *adv_router : 0, - &lsa); - while (lsa) { - if ((!adv_router || lsa->header->adv_router == *adv_router) - && (!id || lsa->header->id == *id)) - (*showfunc)(vty, lsa); - - lsa = ospf6_lsdb_next(end, lsa); - } -} - uint32_t ospf6_new_ls_id(uint16_t type, uint32_t adv_router, struct ospf6_lsdb *lsdb) { diff --git a/ospf6d/ospf6_lsdb.h b/ospf6d/ospf6_lsdb.h index 457e3dc4e4..7a62c46b02 100644 --- a/ospf6d/ospf6_lsdb.h +++ b/ospf6d/ospf6_lsdb.h @@ -92,7 +92,8 @@ enum ospf_lsdb_show_level { extern void ospf6_lsdb_show(struct vty *vty, enum ospf_lsdb_show_level level, uint16_t *type, uint32_t *id, uint32_t *adv_router, - struct ospf6_lsdb *lsdb); + struct ospf6_lsdb *lsdb, json_object *json, + bool use_json); extern uint32_t ospf6_new_ls_id(uint16_t type, uint32_t adv_router, struct ospf6_lsdb *lsdb); diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index 4b958e550f..02eb4baf8d 100644 --- a/ospf6d/ospf6d.c +++ b/ospf6d/ospf6d.c @@ -44,6 +44,7 @@ #include "ospf6_flood.h" #include "ospf6d.h" #include "ospf6_bfd.h" +#include "lib/json.h" struct route_node *route_prev(struct route_node *node) { @@ -154,53 +155,264 @@ static uint16_t parse_type_spec(int idx_lsa, int argc, struct cmd_token **argv) return type; } +void ospf6_lsdb_show(struct vty *vty, enum ospf_lsdb_show_level level, + uint16_t *type, uint32_t *id, uint32_t *adv_router, + struct ospf6_lsdb *lsdb, json_object *json_obj, + bool use_json) +{ + struct ospf6_lsa *lsa; + const struct route_node *end = NULL; + void (*showfunc)(struct vty *, struct ospf6_lsa *, json_object *, + bool) = NULL; + json_object *json_array = NULL; + + switch (level) { + case OSPF6_LSDB_SHOW_LEVEL_DETAIL: + showfunc = ospf6_lsa_show; + break; + case OSPF6_LSDB_SHOW_LEVEL_INTERNAL: + showfunc = ospf6_lsa_show_internal; + break; + case OSPF6_LSDB_SHOW_LEVEL_DUMP: + showfunc = ospf6_lsa_show_dump; + break; + case OSPF6_LSDB_SHOW_LEVEL_NORMAL: + default: + showfunc = ospf6_lsa_show_summary; + } + + if (use_json) + json_array = json_object_new_array(); + + if (type && id && adv_router) { + lsa = ospf6_lsdb_lookup(*type, *id, *adv_router, lsdb); + if (lsa) { + if (level == OSPF6_LSDB_SHOW_LEVEL_NORMAL) + ospf6_lsa_show(vty, lsa, json_array, use_json); + else + (*showfunc)(vty, lsa, json_array, use_json); + } + + if (use_json) + json_object_object_add(json_obj, "lsa", json_array); + return; + } + + if ((level == OSPF6_LSDB_SHOW_LEVEL_NORMAL) && !use_json) + ospf6_lsa_show_summary_header(vty); + + end = ospf6_lsdb_head(lsdb, !!type + !!(type && adv_router), + type ? *type : 0, adv_router ? *adv_router : 0, + &lsa); + while (lsa) { + if ((!adv_router || lsa->header->adv_router == *adv_router) + && (!id || lsa->header->id == *id)) + (*showfunc)(vty, lsa, json_array, use_json); + lsa = ospf6_lsdb_next(end, lsa); + } + + if (use_json) + json_object_object_add(json_obj, "lsa", json_array); +} + +static void ospf6_lsdb_show_wrapper(struct vty *vty, + enum ospf_lsdb_show_level level, + uint16_t *type, uint32_t *id, + uint32_t *adv_router, bool uj, + struct ospf6 *ospf6) +{ + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + json_object *json = NULL; + json_object *json_array = NULL; + json_object *json_obj = NULL; + + if (uj) { + json = json_object_new_object(); + json_array = json_object_new_array(); + } + for (ALL_LIST_ELEMENTS_RO(o->area_list, i, oa)) { + if (uj) { + json_obj = json_object_new_object(); + json_object_string_add(json_obj, "areaId", oa->name); + } else + vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); + ospf6_lsdb_show(vty, level, type, id, adv_router, oa->lsdb, + json_obj, uj); + if (uj) + json_object_array_add(json_array, json_obj); + } + if (uj) + json_object_object_add(json, "areaScopedLinkStateDb", + json_array); + + if (uj) + json_array = json_object_new_array(); + for (ALL_LIST_ELEMENTS_RO(o->area_list, i, oa)) { + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { + if (uj) { + json_obj = json_object_new_object(); + json_object_string_add(json_obj, "areaId", + oa->name); + json_object_string_add(json_obj, "interface", + oi->interface->name); + } else + vty_out(vty, IF_LSDB_TITLE_FORMAT, + oi->interface->name, oa->name); + ospf6_lsdb_show(vty, level, type, id, adv_router, + oi->lsdb, json_obj, uj); + if (uj) + json_object_array_add(json_array, json_obj); + } + } + if (uj) + json_object_object_add(json, "interfaceScopedLinkStateDb", + json_array); + if (uj) { + json_array = json_object_new_array(); + json_obj = json_object_new_object(); + } else + vty_out(vty, AS_LSDB_TITLE_FORMAT); + + ospf6_lsdb_show(vty, level, type, id, adv_router, o->lsdb, json_obj, + uj); + + if (uj) { + json_object_array_add(json_array, json_obj); + json_object_object_add(json, "asScopedLinkStateDb", json_array); + + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else + vty_out(vty, "\n"); +} + +static void ospf6_lsdb_type_show_wrapper(struct vty *vty, + enum ospf_lsdb_show_level level, + uint16_t *type, uint32_t *id, + uint32_t *adv_router, bool uj, + struct ospf6 *ospf6) +{ + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + json_object *json = NULL; + json_object *json_array = NULL; + json_object *json_obj = NULL; + + if (uj) { + json = json_object_new_object(); + json_array = json_object_new_array(); + } + + switch (OSPF6_LSA_SCOPE(*type)) { + case OSPF6_SCOPE_AREA: + for (ALL_LIST_ELEMENTS_RO(o->area_list, i, oa)) { + if (uj) { + json_obj = json_object_new_object(); + json_object_string_add(json_obj, "areaId", + oa->name); + } else + vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); + + ospf6_lsdb_show(vty, level, type, id, adv_router, + oa->lsdb, json_obj, uj); + if (uj) + json_object_array_add(json_array, json_obj); + } + if (uj) + json_object_object_add(json, "areaScopedLinkStateDb", + json_array); + break; + + case OSPF6_SCOPE_LINKLOCAL: + for (ALL_LIST_ELEMENTS_RO(o->area_list, i, oa)) { + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { + if (uj) { + json_obj = json_object_new_object(); + json_object_string_add( + json_obj, "areaId", oa->name); + json_object_string_add( + json_obj, "interface", + oi->interface->name); + } else + vty_out(vty, IF_LSDB_TITLE_FORMAT, + oi->interface->name, oa->name); + + ospf6_lsdb_show(vty, level, type, id, + adv_router, oi->lsdb, json_obj, + uj); + + if (uj) + json_object_array_add(json_array, + json_obj); + } + } + if (uj) + json_object_object_add( + json, "interfaceScopedLinkStateDb", json_array); + break; + + case OSPF6_SCOPE_AS: + if (uj) + json_obj = json_object_new_object(); + else + vty_out(vty, AS_LSDB_TITLE_FORMAT); + + ospf6_lsdb_show(vty, level, type, id, adv_router, o->lsdb, + json_obj, uj); + if (uj) { + json_object_array_add(json_array, json_obj); + json_object_object_add(json, "asScopedLinkStateDb", + json_array); + } + break; + + default: + assert(0); + break; + } + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else + vty_out(vty, "\n"); +} + DEFUN (show_ipv6_ospf6_database, show_ipv6_ospf6_database_cmd, - "show ipv6 ospf6 database [<detail|dump|internal>]", + "show ipv6 ospf6 database [<detail|dump|internal>] [json]", SHOW_STR IPV6_STR OSPF6_STR "Display Link state database\n" "Display details of LSAs\n" "Dump LSAs\n" - "Display LSA's internal information\n") + "Display LSA's internal information\n" + JSON_STR) { int idx_level = 4; int level; - struct listnode *i, *j; + bool uj = use_json(argc, argv); struct ospf6 *ospf6; - struct ospf6_area *oa; - struct ospf6_interface *oi; - ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); OSPF6_CMD_CHECK_RUNNING(ospf6); level = parse_show_level(idx_level, argc, argv); - - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); - ospf6_lsdb_show(vty, level, NULL, NULL, NULL, oa->lsdb); - } - - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { - vty_out(vty, IF_LSDB_TITLE_FORMAT, oi->interface->name, - oa->name); - ospf6_lsdb_show(vty, level, NULL, NULL, NULL, oi->lsdb); - } - } - - vty_out(vty, AS_LSDB_TITLE_FORMAT); - ospf6_lsdb_show(vty, level, NULL, NULL, NULL, ospf6->lsdb); - - vty_out(vty, "\n"); + ospf6_lsdb_show_wrapper(vty, level, NULL, NULL, NULL, uj, ospf6); return CMD_SUCCESS; } -DEFUN (show_ipv6_ospf6_database_type, - show_ipv6_ospf6_database_type_cmd, - "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> [<detail|dump|internal>]", +DEFUN (show_ipv6_ospf6_database_type, show_ipv6_ospf6_database_type_cmd, + "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> [<detail|dump|internal>] [json]", SHOW_STR IPV6_STR OSPF6_STR @@ -217,16 +429,14 @@ DEFUN (show_ipv6_ospf6_database_type, "Display details of LSAs\n" "Dump LSAs\n" "Display LSA's internal information\n" - ) + JSON_STR) { int idx_lsa = 4; int idx_level = 5; int level; - struct listnode *i, *j; - struct ospf6 *ospf6; - struct ospf6_area *oa; - struct ospf6_interface *oi; uint16_t type = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); @@ -235,43 +445,13 @@ DEFUN (show_ipv6_ospf6_database_type, type = parse_type_spec(idx_lsa, argc, argv); level = parse_show_level(idx_level, argc, argv); - switch (OSPF6_LSA_SCOPE(type)) { - case OSPF6_SCOPE_AREA: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); - ospf6_lsdb_show(vty, level, &type, NULL, NULL, - oa->lsdb); - } - break; - - case OSPF6_SCOPE_LINKLOCAL: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { - vty_out(vty, IF_LSDB_TITLE_FORMAT, - oi->interface->name, oa->name); - ospf6_lsdb_show(vty, level, &type, NULL, NULL, - oi->lsdb); - } - } - break; - - case OSPF6_SCOPE_AS: - vty_out(vty, AS_LSDB_TITLE_FORMAT); - ospf6_lsdb_show(vty, level, &type, NULL, NULL, ospf6->lsdb); - break; - - default: - assert(0); - break; - } - - vty_out(vty, "\n"); + ospf6_lsdb_type_show_wrapper(vty, level, &type, NULL, NULL, uj, ospf6); return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_database_id, show_ipv6_ospf6_database_id_cmd, - "show ipv6 ospf6 database <*|linkstate-id> A.B.C.D [<detail|dump|internal>]", + "show ipv6 ospf6 database <*|linkstate-id> A.B.C.D [<detail|dump|internal>] [json]", SHOW_STR IPV6_STR OSPF6_STR @@ -281,16 +461,15 @@ DEFUN (show_ipv6_ospf6_database_id, "Specify Link state ID as IPv4 address notation\n" "Display details of LSAs\n" "Dump LSAs\n" - "Display LSA's internal information\n") + "Display LSA's internal information\n" + JSON_STR) { int idx_ipv4 = 5; int idx_level = 6; int level; - struct listnode *i, *j; - struct ospf6 *ospf6; - struct ospf6_area *oa; - struct ospf6_interface *oi; uint32_t id = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); @@ -300,30 +479,14 @@ DEFUN (show_ipv6_ospf6_database_id, inet_pton(AF_INET, argv[idx_ipv4]->arg, &id); level = parse_show_level(idx_level, argc, argv); + ospf6_lsdb_show_wrapper(vty, level, NULL, &id, NULL, uj, ospf6); - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); - ospf6_lsdb_show(vty, level, NULL, &id, NULL, oa->lsdb); - } - - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { - vty_out(vty, IF_LSDB_TITLE_FORMAT, oi->interface->name, - oa->name); - ospf6_lsdb_show(vty, level, NULL, &id, NULL, oi->lsdb); - } - } - - vty_out(vty, AS_LSDB_TITLE_FORMAT); - ospf6_lsdb_show(vty, level, NULL, &id, NULL, ospf6->lsdb); - - vty_out(vty, "\n"); return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_database_router, show_ipv6_ospf6_database_router_cmd, - "show ipv6 ospf6 database <*|adv-router> * A.B.C.D <detail|dump|internal>", + "show ipv6 ospf6 database <*|adv-router> * A.B.C.D <detail|dump|internal> [json]", SHOW_STR IPV6_STR OSPF6_STR @@ -334,16 +497,15 @@ DEFUN (show_ipv6_ospf6_database_router, "Specify Advertising Router as IPv4 address notation\n" "Display details of LSAs\n" "Dump LSAs\n" - "Display LSA's internal information\n") + "Display LSA's internal information\n" + JSON_STR) { int idx_ipv4 = 6; int idx_level = 7; int level; - struct listnode *i, *j; - struct ospf6 *ospf6; - struct ospf6_area *oa; - struct ospf6_interface *oi; uint32_t adv_router = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); @@ -351,24 +513,7 @@ DEFUN (show_ipv6_ospf6_database_router, inet_pton(AF_INET, argv[idx_ipv4]->arg, &adv_router); level = parse_show_level(idx_level, argc, argv); - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); - ospf6_lsdb_show(vty, level, NULL, NULL, &adv_router, oa->lsdb); - } - - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { - vty_out(vty, IF_LSDB_TITLE_FORMAT, oi->interface->name, - oa->name); - ospf6_lsdb_show(vty, level, NULL, NULL, &adv_router, - oi->lsdb); - } - } - - vty_out(vty, AS_LSDB_TITLE_FORMAT); - ospf6_lsdb_show(vty, level, NULL, NULL, &adv_router, ospf6->lsdb); - - vty_out(vty, "\n"); + ospf6_lsdb_show_wrapper(vty, level, NULL, NULL, &adv_router, uj, ospf6); return CMD_SUCCESS; } @@ -408,7 +553,7 @@ DEFUN_HIDDEN (show_ipv6_ospf6_database_aggr_router, return CMD_SUCCESS; } ospf6_lsdb_show(vty, level, &type, NULL, NULL, - oa->temp_router_lsa_lsdb); + oa->temp_router_lsa_lsdb, NULL, false); /* Remove the temp cache */ ospf6_remove_temp_router_lsa(oa); } @@ -420,7 +565,7 @@ DEFUN_HIDDEN (show_ipv6_ospf6_database_aggr_router, DEFUN (show_ipv6_ospf6_database_type_id, show_ipv6_ospf6_database_type_id_cmd, - "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> linkstate-id A.B.C.D [<detail|dump|internal>]", + "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> linkstate-id A.B.C.D [<detail|dump|internal>] [json]", SHOW_STR IPV6_STR OSPF6_STR @@ -439,18 +584,16 @@ DEFUN (show_ipv6_ospf6_database_type_id, "Display details of LSAs\n" "Dump LSAs\n" "Display LSA's internal information\n" - ) + JSON_STR) { int idx_lsa = 4; int idx_ipv4 = 6; int idx_level = 7; int level; - struct listnode *i, *j; - struct ospf6 *ospf6; - struct ospf6_area *oa; - struct ospf6_interface *oi; uint16_t type = 0; uint32_t id = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); @@ -460,42 +603,13 @@ DEFUN (show_ipv6_ospf6_database_type_id, inet_pton(AF_INET, argv[idx_ipv4]->arg, &id); level = parse_show_level(idx_level, argc, argv); - switch (OSPF6_LSA_SCOPE(type)) { - case OSPF6_SCOPE_AREA: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); - ospf6_lsdb_show(vty, level, &type, &id, NULL, oa->lsdb); - } - break; - - case OSPF6_SCOPE_LINKLOCAL: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { - vty_out(vty, IF_LSDB_TITLE_FORMAT, - oi->interface->name, oa->name); - ospf6_lsdb_show(vty, level, &type, &id, NULL, - oi->lsdb); - } - } - break; - - case OSPF6_SCOPE_AS: - vty_out(vty, AS_LSDB_TITLE_FORMAT); - ospf6_lsdb_show(vty, level, &type, &id, NULL, ospf6->lsdb); - break; - - default: - assert(0); - break; - } - - vty_out(vty, "\n"); + ospf6_lsdb_type_show_wrapper(vty, level, &type, &id, NULL, uj, ospf6); return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_database_type_router, show_ipv6_ospf6_database_type_router_cmd, - "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> <*|adv-router> A.B.C.D [<detail|dump|internal>]", + "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> <*|adv-router> A.B.C.D [<detail|dump|internal>] [json]", SHOW_STR IPV6_STR OSPF6_STR @@ -515,18 +629,16 @@ DEFUN (show_ipv6_ospf6_database_type_router, "Display details of LSAs\n" "Dump LSAs\n" "Display LSA's internal information\n" - ) + JSON_STR) { int idx_lsa = 4; int idx_ipv4 = 6; int idx_level = 7; int level; - struct listnode *i, *j; - struct ospf6 *ospf6; - struct ospf6_area *oa; - struct ospf6_interface *oi; uint16_t type = 0; uint32_t adv_router = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); @@ -535,45 +647,15 @@ DEFUN (show_ipv6_ospf6_database_type_router, inet_pton(AF_INET, argv[idx_ipv4]->arg, &adv_router); level = parse_show_level(idx_level, argc, argv); - switch (OSPF6_LSA_SCOPE(type)) { - case OSPF6_SCOPE_AREA: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); - ospf6_lsdb_show(vty, level, &type, NULL, &adv_router, - oa->lsdb); - } - break; - - case OSPF6_SCOPE_LINKLOCAL: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { - vty_out(vty, IF_LSDB_TITLE_FORMAT, - oi->interface->name, oa->name); - ospf6_lsdb_show(vty, level, &type, NULL, - &adv_router, oi->lsdb); - } - } - break; - - case OSPF6_SCOPE_AS: - vty_out(vty, AS_LSDB_TITLE_FORMAT); - ospf6_lsdb_show(vty, level, &type, NULL, &adv_router, - ospf6->lsdb); - break; - - default: - assert(0); - break; - } - - vty_out(vty, "\n"); + ospf6_lsdb_type_show_wrapper(vty, level, &type, NULL, &adv_router, uj, + ospf6); return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_database_id_router, show_ipv6_ospf6_database_id_router_cmd, - "show ipv6 ospf6 database * A.B.C.D A.B.C.D [<detail|dump|internal>]", + "show ipv6 ospf6 database * A.B.C.D A.B.C.D [<detail|dump|internal>] [json]", SHOW_STR IPV6_STR OSPF6_STR @@ -584,18 +666,16 @@ DEFUN (show_ipv6_ospf6_database_id_router, "Display details of LSAs\n" "Dump LSAs\n" "Display LSA's internal information\n" - ) + JSON_STR) { int idx_ls_id = 5; int idx_adv_rtr = 6; int idx_level = 7; int level; - struct listnode *i, *j; - struct ospf6 *ospf6; - struct ospf6_area *oa; - struct ospf6_interface *oi; uint32_t id = 0; uint32_t adv_router = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); OSPF6_CMD_CHECK_RUNNING(ospf6); @@ -603,31 +683,14 @@ DEFUN (show_ipv6_ospf6_database_id_router, inet_pton(AF_INET, argv[idx_adv_rtr]->arg, &adv_router); level = parse_show_level(idx_level, argc, argv); - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); - ospf6_lsdb_show(vty, level, NULL, &id, &adv_router, oa->lsdb); - } - - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { - vty_out(vty, IF_LSDB_TITLE_FORMAT, oi->interface->name, - oa->name); - ospf6_lsdb_show(vty, level, NULL, &id, &adv_router, - oi->lsdb); - } - } - - vty_out(vty, AS_LSDB_TITLE_FORMAT); - ospf6_lsdb_show(vty, level, NULL, &id, &adv_router, ospf6->lsdb); - - vty_out(vty, "\n"); + ospf6_lsdb_show_wrapper(vty, level, NULL, &id, &adv_router, uj, ospf6); return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_database_adv_router_linkstate_id, show_ipv6_ospf6_database_adv_router_linkstate_id_cmd, - "show ipv6 ospf6 database adv-router A.B.C.D linkstate-id A.B.C.D [<detail|dump|internal>]", + "show ipv6 ospf6 database adv-router A.B.C.D linkstate-id A.B.C.D [<detail|dump|internal>] [json]", SHOW_STR IPV6_STR OSPF6_STR @@ -638,18 +701,17 @@ DEFUN (show_ipv6_ospf6_database_adv_router_linkstate_id, "Specify Link state ID as IPv4 address notation\n" "Display details of LSAs\n" "Dump LSAs\n" - "Display LSA's internal information\n") + "Display LSA's internal information\n" + JSON_STR) { int idx_adv_rtr = 5; int idx_ls_id = 7; int idx_level = 8; int level; - struct listnode *i, *j; - struct ospf6 *ospf6; - struct ospf6_area *oa; - struct ospf6_interface *oi; uint32_t id = 0; uint32_t adv_router = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); @@ -658,30 +720,13 @@ DEFUN (show_ipv6_ospf6_database_adv_router_linkstate_id, inet_pton(AF_INET, argv[idx_ls_id]->arg, &id); level = parse_show_level(idx_level, argc, argv); - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); - ospf6_lsdb_show(vty, level, NULL, &id, &adv_router, oa->lsdb); - } - - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { - vty_out(vty, IF_LSDB_TITLE_FORMAT, oi->interface->name, - oa->name); - ospf6_lsdb_show(vty, level, NULL, &id, &adv_router, - oi->lsdb); - } - } - - vty_out(vty, AS_LSDB_TITLE_FORMAT); - ospf6_lsdb_show(vty, level, NULL, &id, &adv_router, ospf6->lsdb); - - vty_out(vty, "\n"); + ospf6_lsdb_show_wrapper(vty, level, NULL, &id, &adv_router, uj, ospf6); return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_database_type_id_router, show_ipv6_ospf6_database_type_id_router_cmd, - "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> A.B.C.D A.B.C.D [<dump|internal>]", + "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> A.B.C.D A.B.C.D [<dump|internal>] [json]", SHOW_STR IPV6_STR OSPF6_STR @@ -698,20 +743,19 @@ DEFUN (show_ipv6_ospf6_database_type_id_router, "Specify Link state ID as IPv4 address notation\n" "Specify Advertising Router as IPv4 address notation\n" "Dump LSAs\n" - "Display LSA's internal information\n") + "Display LSA's internal information\n" + JSON_STR) { int idx_lsa = 4; int idx_ls_id = 5; int idx_adv_rtr = 6; int idx_level = 7; int level; - struct listnode *i, *j; - struct ospf6 *ospf6; - struct ospf6_area *oa; - struct ospf6_interface *oi; uint16_t type = 0; uint32_t id = 0; uint32_t adv_router = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); @@ -722,45 +766,15 @@ DEFUN (show_ipv6_ospf6_database_type_id_router, inet_pton(AF_INET, argv[idx_adv_rtr]->arg, &adv_router); level = parse_show_level(idx_level, argc, argv); - switch (OSPF6_LSA_SCOPE(type)) { - case OSPF6_SCOPE_AREA: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); - ospf6_lsdb_show(vty, level, &type, &id, &adv_router, - oa->lsdb); - } - break; - - case OSPF6_SCOPE_LINKLOCAL: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { - vty_out(vty, IF_LSDB_TITLE_FORMAT, - oi->interface->name, oa->name); - ospf6_lsdb_show(vty, level, &type, &id, - &adv_router, oi->lsdb); - } - } - break; - - case OSPF6_SCOPE_AS: - vty_out(vty, AS_LSDB_TITLE_FORMAT); - ospf6_lsdb_show(vty, level, &type, &id, &adv_router, - ospf6->lsdb); - break; - - default: - assert(0); - break; - } - - vty_out(vty, "\n"); + ospf6_lsdb_type_show_wrapper(vty, level, &type, &id, &adv_router, uj, + ospf6); return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_database_type_adv_router_linkstate_id, show_ipv6_ospf6_database_type_adv_router_linkstate_id_cmd, - "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> adv-router A.B.C.D linkstate-id A.B.C.D [<dump|internal>]", + "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> adv-router A.B.C.D linkstate-id A.B.C.D [<dump|internal>] [json]", SHOW_STR IPV6_STR OSPF6_STR @@ -779,20 +793,19 @@ DEFUN (show_ipv6_ospf6_database_type_adv_router_linkstate_id, "Search by Link state ID\n" "Specify Link state ID as IPv4 address notation\n" "Dump LSAs\n" - "Display LSA's internal information\n") + "Display LSA's internal information\n" + JSON_STR) { int idx_lsa = 4; int idx_adv_rtr = 6; int idx_ls_id = 8; int idx_level = 9; int level; - struct listnode *i, *j; - struct ospf6 *ospf6; - struct ospf6_area *oa; - struct ospf6_interface *oi; uint16_t type = 0; uint32_t id = 0; uint32_t adv_router = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); @@ -803,44 +816,14 @@ DEFUN (show_ipv6_ospf6_database_type_adv_router_linkstate_id, inet_pton(AF_INET, argv[idx_ls_id]->arg, &id); level = parse_show_level(idx_level, argc, argv); - switch (OSPF6_LSA_SCOPE(type)) { - case OSPF6_SCOPE_AREA: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); - ospf6_lsdb_show(vty, level, &type, &id, &adv_router, - oa->lsdb); - } - break; - - case OSPF6_SCOPE_LINKLOCAL: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { - vty_out(vty, IF_LSDB_TITLE_FORMAT, - oi->interface->name, oa->name); - ospf6_lsdb_show(vty, level, &type, &id, - &adv_router, oi->lsdb); - } - } - break; - - case OSPF6_SCOPE_AS: - vty_out(vty, AS_LSDB_TITLE_FORMAT); - ospf6_lsdb_show(vty, level, &type, &id, &adv_router, - ospf6->lsdb); - break; - - default: - assert(0); - break; - } - - vty_out(vty, "\n"); + ospf6_lsdb_type_show_wrapper(vty, level, &type, &id, &adv_router, uj, + ospf6); return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_database_self_originated, show_ipv6_ospf6_database_self_originated_cmd, - "show ipv6 ospf6 database self-originated [<detail|dump|internal>]", + "show ipv6 ospf6 database self-originated [<detail|dump|internal>] [json]", SHOW_STR IPV6_STR OSPF6_STR @@ -848,46 +831,28 @@ DEFUN (show_ipv6_ospf6_database_self_originated, "Display Self-originated LSAs\n" "Display details of LSAs\n" "Dump LSAs\n" - "Display LSA's internal information\n") + "Display LSA's internal information\n" + JSON_STR) { int idx_level = 5; int level; - struct listnode *i, *j; - struct ospf6 *ospf6; - struct ospf6_area *oa; - struct ospf6_interface *oi; uint32_t adv_router = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); OSPF6_CMD_CHECK_RUNNING(ospf6); level = parse_show_level(idx_level, argc, argv); adv_router = ospf6->router_id; - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); - ospf6_lsdb_show(vty, level, NULL, NULL, &adv_router, oa->lsdb); - } - - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { - vty_out(vty, IF_LSDB_TITLE_FORMAT, oi->interface->name, - oa->name); - ospf6_lsdb_show(vty, level, NULL, NULL, &adv_router, - oi->lsdb); - } - } - - vty_out(vty, AS_LSDB_TITLE_FORMAT); - ospf6_lsdb_show(vty, level, NULL, NULL, &adv_router, ospf6->lsdb); - - vty_out(vty, "\n"); + ospf6_lsdb_show_wrapper(vty, level, NULL, NULL, &adv_router, uj, ospf6); return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_database_type_self_originated, show_ipv6_ospf6_database_type_self_originated_cmd, - "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> self-originated [<detail|dump|internal>]", + "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> self-originated [<detail|dump|internal>] [json]", SHOW_STR IPV6_STR OSPF6_STR @@ -904,17 +869,16 @@ DEFUN (show_ipv6_ospf6_database_type_self_originated, "Display Self-originated LSAs\n" "Display details of LSAs\n" "Dump LSAs\n" - "Display LSA's internal information\n") + "Display LSA's internal information\n" + JSON_STR) { int idx_lsa = 4; int idx_level = 6; int level; - struct listnode *i, *j; - struct ospf6 *ospf6; - struct ospf6_area *oa; - struct ospf6_interface *oi; uint16_t type = 0; uint32_t adv_router = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); OSPF6_CMD_CHECK_RUNNING(ospf6); @@ -923,44 +887,14 @@ DEFUN (show_ipv6_ospf6_database_type_self_originated, adv_router = ospf6->router_id; - switch (OSPF6_LSA_SCOPE(type)) { - case OSPF6_SCOPE_AREA: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); - ospf6_lsdb_show(vty, level, &type, NULL, &adv_router, - oa->lsdb); - } - break; - - case OSPF6_SCOPE_LINKLOCAL: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { - vty_out(vty, IF_LSDB_TITLE_FORMAT, - oi->interface->name, oa->name); - ospf6_lsdb_show(vty, level, &type, NULL, - &adv_router, oi->lsdb); - } - } - break; - - case OSPF6_SCOPE_AS: - vty_out(vty, AS_LSDB_TITLE_FORMAT); - ospf6_lsdb_show(vty, level, &type, NULL, &adv_router, - ospf6->lsdb); - break; - - default: - assert(0); - break; - } - - vty_out(vty, "\n"); + ospf6_lsdb_type_show_wrapper(vty, level, &type, NULL, &adv_router, uj, + ospf6); return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_database_type_self_originated_linkstate_id, show_ipv6_ospf6_database_type_self_originated_linkstate_id_cmd, - "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> self-originated linkstate-id A.B.C.D [<detail|dump|internal>]", + "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> self-originated linkstate-id A.B.C.D [<detail|dump|internal>] [json]", SHOW_STR IPV6_STR OSPF6_STR @@ -979,19 +913,18 @@ DEFUN (show_ipv6_ospf6_database_type_self_originated_linkstate_id, "Specify Link state ID as IPv4 address notation\n" "Display details of LSAs\n" "Dump LSAs\n" - "Display LSA's internal information\n") + "Display LSA's internal information\n" + JSON_STR) { int idx_lsa = 4; int idx_ls_id = 7; int idx_level = 8; int level; - struct listnode *i, *j; - struct ospf6 *ospf6; - struct ospf6_area *oa; - struct ospf6_interface *oi; uint16_t type = 0; uint32_t adv_router = 0; uint32_t id = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); OSPF6_CMD_CHECK_RUNNING(ospf6); @@ -1000,44 +933,14 @@ DEFUN (show_ipv6_ospf6_database_type_self_originated_linkstate_id, level = parse_show_level(idx_level, argc, argv); adv_router = ospf6->router_id; - switch (OSPF6_LSA_SCOPE(type)) { - case OSPF6_SCOPE_AREA: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); - ospf6_lsdb_show(vty, level, &type, &id, &adv_router, - oa->lsdb); - } - break; - - case OSPF6_SCOPE_LINKLOCAL: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { - vty_out(vty, IF_LSDB_TITLE_FORMAT, - oi->interface->name, oa->name); - ospf6_lsdb_show(vty, level, &type, &id, - &adv_router, oi->lsdb); - } - } - break; - - case OSPF6_SCOPE_AS: - vty_out(vty, AS_LSDB_TITLE_FORMAT); - ospf6_lsdb_show(vty, level, &type, &id, &adv_router, - ospf6->lsdb); - break; - - default: - assert(0); - break; - } - - vty_out(vty, "\n"); + ospf6_lsdb_type_show_wrapper(vty, level, &type, &id, &adv_router, uj, + ospf6); return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_database_type_id_self_originated, show_ipv6_ospf6_database_type_id_self_originated_cmd, - "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> A.B.C.D self-originated [<detail|dump|internal>]", + "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> A.B.C.D self-originated [<detail|dump|internal>] [json]", SHOW_STR IPV6_STR OSPF6_STR @@ -1055,19 +958,18 @@ DEFUN (show_ipv6_ospf6_database_type_id_self_originated, "Display Self-originated LSAs\n" "Display details of LSAs\n" "Dump LSAs\n" - "Display LSA's internal information\n") + "Display LSA's internal information\n" + JSON_STR) { int idx_lsa = 4; int idx_ls_id = 5; int idx_level = 7; int level; - struct listnode *i, *j; - struct ospf6 *ospf6; - struct ospf6_area *oa; - struct ospf6_interface *oi; uint16_t type = 0; uint32_t adv_router = 0; uint32_t id = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); OSPF6_CMD_CHECK_RUNNING(ospf6); @@ -1076,38 +978,8 @@ DEFUN (show_ipv6_ospf6_database_type_id_self_originated, level = parse_show_level(idx_level, argc, argv); adv_router = ospf6->router_id; - switch (OSPF6_LSA_SCOPE(type)) { - case OSPF6_SCOPE_AREA: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); - ospf6_lsdb_show(vty, level, &type, &id, &adv_router, - oa->lsdb); - } - break; - - case OSPF6_SCOPE_LINKLOCAL: - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { - vty_out(vty, IF_LSDB_TITLE_FORMAT, - oi->interface->name, oa->name); - ospf6_lsdb_show(vty, level, &type, &id, - &adv_router, oi->lsdb); - } - } - break; - - case OSPF6_SCOPE_AS: - vty_out(vty, AS_LSDB_TITLE_FORMAT); - ospf6_lsdb_show(vty, level, &type, &id, &adv_router, - ospf6->lsdb); - break; - - default: - assert(0); - break; - } - - vty_out(vty, "\n"); + ospf6_lsdb_type_show_wrapper(vty, level, &type, &id, &adv_router, uj, + ospf6); return CMD_SUCCESS; } diff --git a/tests/topotests/example-test/test_template.py b/tests/topotests/example-test/test_template.py index 4305e0199f..973303b830 100644 --- a/tests/topotests/example-test/test_template.py +++ b/tests/topotests/example-test/test_template.py @@ -44,6 +44,18 @@ from lib.topolog import logger from mininet.topo import Topo +#TODO: select markers based on daemons used during test +# pytest module level markers +""" +pytestmark = pytest.mark.bfdd # single marker +pytestmark = [ + pytest.mark.bgpd, + pytest.mark.ospfd, + pytest.mark.ospf6d +] # multiple markers +""" + + class TemplateTopo(Topo): "Test topology builder" diff --git a/tests/topotests/example-topojson-test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py b/tests/topotests/example-topojson-test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py index f24f463b8a..cd48716905 100755 --- a/tests/topotests/example-topojson-test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py +++ b/tests/topotests/example-topojson-test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py @@ -53,6 +53,19 @@ from lib.topolog import logger from lib.bgp import verify_bgp_convergence from lib.topojson import build_topo_from_json, build_config_from_json + +#TODO: select markers based on daemons used during test +# pytest module level markers +""" +pytestmark = pytest.mark.bfdd # single marker +pytestmark = [ + pytest.mark.bgpd, + pytest.mark.ospfd, + pytest.mark.ospf6d +] # multiple markers +""" + + # Reading the data from JSON File for topology and configuration creation jsonFile = "{}/example_topojson_multiple_links.json".format(CWD) try: diff --git a/tests/topotests/example-topojson-test/test_topo_json_single_link/test_example_topojson.py b/tests/topotests/example-topojson-test/test_topo_json_single_link/test_example_topojson.py index 3ae3c9f4fe..0c72e30044 100755 --- a/tests/topotests/example-topojson-test/test_topo_json_single_link/test_example_topojson.py +++ b/tests/topotests/example-topojson-test/test_topo_json_single_link/test_example_topojson.py @@ -52,6 +52,19 @@ from lib.topolog import logger from lib.bgp import verify_bgp_convergence from lib.topojson import build_topo_from_json, build_config_from_json + +#TODO: select markers based on daemons used during test +# pytest module level markers +""" +pytestmark = pytest.mark.bfdd # single marker +pytestmark = [ + pytest.mark.bgpd, + pytest.mark.ospfd, + pytest.mark.ospf6d +] # multiple markers +""" + + # Reading the data from JSON File for topology and configuration creation jsonFile = "{}/example_topojson.json".format(CWD) diff --git a/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py b/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py index 06fa2f4626..d05ad6db21 100755 --- a/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py +++ b/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py @@ -54,6 +54,19 @@ from lib.topolog import logger from lib.bgp import verify_bgp_convergence from lib.topojson import build_topo_from_json, build_config_from_json + +#TODO: select markers based on daemons used during test +# pytest module level markers +""" +pytestmark = pytest.mark.bfdd # single marker +pytestmark = [ + pytest.mark.bgpd, + pytest.mark.ospfd, + pytest.mark.ospf6d +] # multiple markers +""" + + # Reading the data from JSON File for topology and configuration creation jsonFile = "{}/example_topojson.json".format(CWD) diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py index 6bb1326519..294f60bf68 100644 --- a/tests/topotests/lib/pim.py +++ b/tests/topotests/lib/pim.py @@ -539,7 +539,8 @@ def configure_pim_force_expire(tgen, topo, input_dict, build=False): ############################################# # Verification APIs ############################################# -def verify_pim_neighbors(tgen, topo, dut=None, iface=None): +@retry(attempts=6, wait=2, return_is_str=True) +def verify_pim_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None): """ Verify all PIM neighbors are up and running, config is verified using "show ip pim neighbor" cli @@ -550,10 +551,11 @@ def verify_pim_neighbors(tgen, topo, dut=None, iface=None): * `topo` : json file data * `dut` : dut info * `iface` : link for which PIM nbr need to check + * `nbr_ip` : neighbor ip of interface Usage ----- - result = verify_pim_neighbors(tgen, topo, dut, link) + result = verify_pim_neighbors(tgen, topo, dut, iface=ens192, nbr_ip=20.1.1.2) Returns ------- @@ -1530,8 +1532,8 @@ def verify_pim_interface_traffic(tgen, input_dict): return output_dict -@retry(attempts=31, wait=2, return_is_str=True) -def verify_pim_interface(tgen, topo, dut): +@retry(attempts=21, wait=2, return_is_str=True) +def verify_pim_interface(tgen, topo, dut, interface=None, interface_ip=None): """ Verify all PIM interface are up and running, config is verified using "show ip pim interface" cli @@ -1541,10 +1543,12 @@ def verify_pim_interface(tgen, topo, dut): * `tgen`: topogen object * `topo` : json file data * `dut` : device under test + * `interface` : interface name + * `interface_ip` : interface ip address Usage ----- - result = verify_pim_interfacetgen, topo, dut) + result = verify_pim_interfacetgen, topo, dut, interface=ens192, interface_ip=20.1.1.1) Returns ------- @@ -1560,56 +1564,55 @@ def verify_pim_interface(tgen, topo, dut): logger.info("[DUT: %s]: Verifying PIM interface status:", dut) rnode = tgen.routers()[dut] - show_ip_pim_interface_json = run_frr_cmd( - rnode, "show ip pim interface json", isjson=True - ) - - for destLink, data in topo["routers"][dut]["links"].items(): - if "type" in data and data["type"] == "loopback": - continue + show_ip_pim_interface_json = rnode.\ + vtysh_cmd("show ip pim interface json", isjson=True) + + logger.info("show_ip_pim_interface_json: \n %s", + show_ip_pim_interface_json) + + if interface_ip: + if interface in show_ip_pim_interface_json: + pim_intf_json = show_ip_pim_interface_json[interface] + if pim_intf_json["address"] != interface_ip: + errormsg = ("[DUT %s]: PIM interface " + "ip is not correct " + "[FAILED]!! Expected : %s, Found : %s" + %(dut, pim_intf_json["address"],interface_ip)) + return errormsg + else: + logger.info("[DUT %s]: PIM interface " + "ip is correct " + "[Passed]!! Expected : %s, Found : %s" + %(dut, pim_intf_json["address"],interface_ip)) + return True + else: + for destLink, data in topo["routers"][dut]["links"].items(): + if "type" in data and data["type"] == "loopback": + continue - if "pim" in data and data["pim"] == "enable": - pim_interface = data["interface"] - pim_intf_ip = data["ipv4"].split("/")[0] + if "pim" in data and data["pim"] == "enable": + pim_interface = data["interface"] + pim_intf_ip = data["ipv4"].split("/")[0] - if pim_interface in show_ip_pim_interface_json: - pim_intf_json = show_ip_pim_interface_json[pim_interface] + if pim_interface in show_ip_pim_interface_json: + pim_intf_json = show_ip_pim_interface_json\ + [pim_interface] # Verifying PIM interface - if ( - pim_intf_json["address"] != pim_intf_ip - and pim_intf_json["state"] != "up" - ): - errormsg = ( - "[DUT %s]: PIM interface: %s " - "PIM interface ip: %s, status check " - "[FAILED]!! Expected : %s, Found : %s" - % ( - dut, - pim_interface, - pim_intf_ip, - pim_interface, - pim_intf_json["state"], - ) - ) + if pim_intf_json["address"] != pim_intf_ip and \ + pim_intf_json["state"] != "up": + errormsg = ("[DUT %s]: PIM interface: %s " + "PIM interface ip: %s, status check " + "[FAILED]!! Expected : %s, Found : %s" + %(dut, pim_interface, pim_intf_ip, + pim_interface, pim_intf_json["state"])) return errormsg - logger.info( - "[DUT %s]: PIM interface: %s, " - "interface ip: %s, status: %s" - " [PASSED]!!", - dut, - pim_interface, - pim_intf_ip, - pim_intf_json["state"], - ) - else: - errormsg = ( - "[DUT %s]: PIM interface: %s " - "PIM interface ip: %s, is not present " - % (dut, pim_interface, pim_intf_ip,) - ) - return errormsg + logger.info("[DUT %s]: PIM interface: %s, " + "interface ip: %s, status: %s" + " [PASSED]!!", + dut, pim_interface, pim_intf_ip, + pim_intf_json["state"]) logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True @@ -1846,7 +1849,6 @@ def clear_ip_mroute_verify(tgen, dut): rnode = tgen.routers()[dut] - # sleep(60) logger.info("[DUT: %s]: IP mroutes uptime before clear", dut) mroute_json_1 = run_frr_cmd(rnode, "show ip mroute json", isjson=True) @@ -3387,3 +3389,62 @@ def verify_multicast_flag_state(tgen, dut, src_address, group_addresses, flag): logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True + + +@retry(attempts=21, wait=2, return_is_str=True) +def verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip): + """ + Verify all IGMP interface are up and running, config is verified + using "show ip igmp interface" cli + + Parameters + ---------- + * `tgen`: topogen object + * `topo` : json file data + * `dut` : device under test + * `igmp_iface` : interface name + * `interface_ip` : interface ip address + + Usage + ----- + result = verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router in tgen.routers(): + if router != dut: + continue + + logger.info("[DUT: %s]: Verifying PIM interface status:", + dut) + + rnode = tgen.routers()[dut] + show_ip_igmp_interface_json = \ + run_frr_cmd(rnode, "show ip igmp interface json", isjson=True) + + if igmp_iface in show_ip_igmp_interface_json: + igmp_intf_json = show_ip_igmp_interface_json[igmp_iface] + # Verifying igmp interface + if igmp_intf_json["address"] != interface_ip: + errormsg = ("[DUT %s]: igmp interface ip is not correct " + "[FAILED]!! Expected : %s, Found : %s" + %(dut, igmp_intf_json["address"], interface_ip)) + return errormsg + + logger.info("[DUT %s]: igmp interface: %s, " + "interface ip: %s" + " [PASSED]!!", + dut, igmp_iface, interface_ip) + else: + errormsg = ("[DUT %s]: igmp interface: %s " + "igmp interface ip: %s, is not present " + %(dut, igmp_iface, interface_ip)) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True diff --git a/tests/topotests/lib/snmptest.py b/tests/topotests/lib/snmptest.py index ba5835dcf7..910e901ade 100644 --- a/tests/topotests/lib/snmptest.py +++ b/tests/topotests/lib/snmptest.py @@ -80,11 +80,14 @@ class SnmpTester(object): def _parse_multiline(self, snmp_output): results = snmp_output.strip().split("\r\n") - out_dict = {} + out_dict = {} + out_list = [] for response in results: out_dict[self._get_snmp_oid(response)] = self._get_snmp_value(response) - return out_dict + out_list.append(self._get_snmp_value(response)) + + return out_dict, out_list def get(self, oid): cmd = "snmpget {0} {1}".format(self._snmp_config(), oid) @@ -114,7 +117,7 @@ class SnmpTester(object): return self.get_next(oid) == value def test_oid_walk(self, oid, values, oids=None): - results_dict = self.walk(oid) + results_dict, results_list = self.walk(oid) print("res {}".format(results_dict)) if oids is not None: index = 0 @@ -124,4 +127,4 @@ class SnmpTester(object): index += 1 return True - return results_dict.values() == values + return results_list == values diff --git a/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo3.json b/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo3.json new file mode 100644 index 0000000000..f582f4929d --- /dev/null +++ b/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo3.json @@ -0,0 +1,140 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, + "routers": { + "l1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "i1": {"ipv4": "auto", "pim": "enable"}, + "i6": {"ipv4": "auto", "pim": "enable"}, + "i7": {"ipv4": "auto", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "c1": {"ipv4": "auto", "pim": "enable"} + }, + "igmp": { + "interfaces": { + "l1-i1-eth1" :{ + "igmp":{ + "version": "2" + } + } + } + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.6.0/24", "10.0.9.0/24", "1.0.3.5/32"], + "next_hop": "10.0.12.2" + }, + { + "network": ["1.0.1.2/32", "1.0.3.5/32", "10.0.1.0/24", "1.0.2.2/32", "10.0.4.0/24"], + "next_hop": "10.0.2.1" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "l1": {"ipv4": "auto", "pim": "enable"}, + "f1": {"ipv4": "auto", "pim": "enable"}, + "i3": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["10.0.5.0/24", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24", "1.0.3.5/32"], + "next_hop": "10.0.7.1" + }, + { + "network": ["1.0.1.2/32", "10.0.8.0/24", "10.0.10.0/24", "10.0.4.0/24", "10.0.11.0/24", "10.0.1.0/24"], + "next_hop": "10.0.12.1" + }] + }, + "f1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "c2": {"ipv4": "auto", "pim": "enable"}, + "i2": {"ipv4": "auto", "pim": "enable"}, + "i8": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.11.0/24", "10.0.12.0/24"], + "next_hop": "10.0.7.2" + }, + { + "network": ["1.0.2.2/32", "10.0.1.0/24", "10.0.4.0/24", "1.0.1.2/32"], + "next_hop": "10.0.3.1" + }] + }, + "c1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "c2": {"ipv4": "auto", "pim": "enable"}, + "l1": {"ipv4": "auto", "pim": "enable"}, + "i4": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.6.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.11.0/24"], + "next_hop": "10.0.2.2" + }, + { + "network": ["10.0.5.0/24", "10.0.7.0/24", "1.0.3.5/32", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24", "10.0.4.0/24"], + "next_hop": "10.0.0.2" + }] + }, + "c2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "c1": {"ipv4": "auto", "pim": "enable"}, + "f1": {"ipv4": "auto", "pim": "enable"}, + "i5": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.6.0/24", "10.0.7.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.11.0/24"], + "next_hop": "10.0.3.2" + }, + { + "network": ["1.0.1.2/32", "10.0.4.0/24"], + "next_hop": "10.0.0.1" + }] + }, + "i1": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i2": { + "links": { + "f1": {"ipv4": "auto"} + } + }, + "i3": { + "links": { + "r2": {"ipv4": "auto"} + } + }, + "i4": { + "links": { + "c1": {"ipv4": "auto"} + } + }, + "i5": { + "links": { + "c2": {"ipv4": "auto"} + } + }, + "i6": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i7": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i8": { + "links": { + "f1": {"ipv4": "auto"} + } + } + } +} diff --git a/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo4.json b/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo4.json new file mode 100644 index 0000000000..4635dac7d2 --- /dev/null +++ b/tests/topotests/multicast-pim-sm-topo3/multicast_pim_sm_topo4.json @@ -0,0 +1,137 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, + "routers": { + "l1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "i1": {"ipv4": "auto", "pim": "enable"}, + "i6": {"ipv4": "auto", "pim": "enable"}, + "i7": {"ipv4": "auto", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "c1": {"ipv4": "auto", "pim": "enable"} + }, + "igmp": { + "interfaces": { + "l1-i1-eth1" :{ + "igmp":{ + "version": "2" + } + } + } + }, + "static_routes": [{ + "network": ["10.0.4.0/24", "10.0.3.1/24"], + "next_hop": "10.0.12.2" + }, + { + "network": ["10.0.1.2/24"], + "next_hop": "10.0.2.1" + }] + + }, + + "r2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "l1": {"ipv4": "auto", "pim": "enable"}, + "f1": {"ipv4": "auto", "pim": "enable"}, + "i3": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["10.0.4.0/24","10.0.3.1/24"], + "next_hop": "10.0.7.1" + }, + { + "network": ["1.0.4.11/32", "10.0.2.1/24", "10.0.1.2/24"], + "next_hop": "10.0.12.1" + }] + }, + "f1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r2": {"ipv4": "auto", "pim": "enable"}, + "c2": {"ipv4": "auto", "pim": "enable"}, + "i2": {"ipv4": "auto", "pim": "enable"}, + "i8": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["10.0.4.0/24","10.0.3.1/24"], + "next_hop": "10.0.3.1" + }, + { + "network": ["1.0.4.11/32", "10.0.2.1/24", "10.0.1.2/24"], + "next_hop": "10.0.7.2" + }] + }, + "c1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "c2": {"ipv4": "auto", "pim": "enable"}, + "l1": {"ipv4": "auto", "pim": "enable"}, + "i4": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [{ + "network": ["1.0.4.11/32","10.0.4.2/24", "10.0.3.1/24"], + "next_hop": "10.0.2.2" + }] + + + }, + "c2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "c1": {"ipv4": "auto", "pim": "enable"}, + "f1": {"ipv4": "auto", "pim": "enable"}, + "i5": {"ipv4": "auto", "pim": "enable"} + }, + "static_routes": [ + { + "network": ["1.0.4.11/32", "10.0.2.1/24", "10.0.1.2/24"], + "next_hop": "10.0.3.2" + }] + }, + "i1": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i2": { + "links": { + "f1": {"ipv4": "auto"} + } + }, + "i3": { + "links": { + "r2": {"ipv4": "auto"} + } + }, + "i4": { + "links": { + "c1": {"ipv4": "auto"} + } + }, + "i5": { + "links": { + "c2": {"ipv4": "auto"} + } + }, + "i6": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i7": { + "links": { + "l1": {"ipv4": "auto"} + } + }, + "i8": { + "links": { + "f1": {"ipv4": "auto"} + } + } + } +} diff --git a/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo3.py b/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo3.py new file mode 100755 index 0000000000..d31d7ace92 --- /dev/null +++ b/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo3.py @@ -0,0 +1,4607 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test multicast pim sm: + +Test steps +- Create topology (setup module) +- Bring up topology + +Following tests are covered: + +1. verify oil when join prune sent scenario_1 p0 +2. verify oil when join prune sent scenario_2 p0 +3. shut noshut source interface when upstream cleared from LHR p0( +4. shut noshut receiver interface when upstream cleared from LHR p0( +5. verify igmp clis p0 +6. verify igmp cli generate query once p0 +7. verify remove add igmp config to receiver interface p0 +8. verify remove add igmp commands when pim configured p0 +9. verify remove add pim commands when igmp configured p0 +10. pim dr priority p0 +11. pim hello timer p0 +12. Verify mroute after removing RP sending IGMP prune p2 +13. Verify prune is sent to LHR and FHR when PIM nbr went down +14. Verify mroute flag in LHR and FHR node +15. Verify IGMP prune processed correctly when same join received from IGMP and PIM +16. Verify multicast traffic flowing fine, when LHR connected to RP +17. Verify multicast traffic is flowing fine when FHR is connected to RP +""" + +import os +import re +import sys +import json +import time +import datetime +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + iperfSendIGMPJoin, + addKernelRoute, + reset_config_on_routers, + iperfSendTraffic, + kill_iperf, + shutdown_bringup_interface, + kill_router_daemons, + start_router, + start_router_daemons, + stop_router, + apply_raw_config, + add_interfaces_to_vlan, + tcpdump_capture_start, + tcpdump_capture_stop, + LOGDIR, + check_router_status, + required_linux_kernel_version, + topo_daemons, +) +from lib.pim import ( + create_pim_config, + create_igmp_config, + verify_igmp_groups, + verify_ip_mroutes, + clear_ip_mroute_verify, + clear_ip_mroute, + clear_ip_pim_interface_traffic, + verify_igmp_config, + verify_pim_neighbors, + verify_pim_config, + verify_pim_interface, + verify_upstream_iif, + verify_multicast_traffic, + verify_pim_rp_info, + get_refCount_for_mroute, + verify_multicast_flag_state, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/multicast_pim_sm_topo3.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +TOPOLOGY = """ + + i4-----c1-------------c2---i5 + | | + | | + i1-----l1------r2-----f1---i2 + | | | | + | | | | + i7 i6 i3 i8 + + Description: + i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send IGMP + join and traffic + l1 - LHR + f1 - FHR + r2 - FRR router + c1 - FRR router + c2 - FRR router +""" + +# Global variables +VLAN_1 = 2501 +GROUP_RANGE = "225.0.0.0/8" +IGMP_GROUP = "225.1.1.1/32" +IGMP_JOIN = "225.1.1.1" +VLAN_INTF_ADRESS_1 = "10.0.8.3/24" +GROUP_RANGE_1 = [ + "225.1.1.1/32", + "225.1.1.2/32", + "225.1.1.3/32", + "225.1.1.4/32", + "225.1.1.5/32", +] +IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"] +GROUP_RANGE_2 = [ + "226.1.1.1/32", + "226.1.1.2/32", + "226.1.1.3/32", + "226.1.1.4/32", + "226.1.1.5/32", +] +IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2", "226.1.1.3", "226.1.1.4", "226.1.1.5"] +GROUP_RANGE_3 = [ + "227.1.1.1/32", + "227.1.1.2/32", + "227.1.1.3/32", + "227.1.1.4/32", + "227.1.1.5/32", +] +IGMP_JOIN_RANGE_3 = ["227.1.1.1", "227.1.1.2", "227.1.1.3", "227.1.1.4", "227.1.1.5"] + +SAME_VLAN_IP_1 = {"ip": "10.1.1.1", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_2 = {"ip": "10.1.1.2", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_3 = {"ip": "10.1.1.3", "subnet": "255.255.255.0", "cidr": "24"} +SAME_VLAN_IP_4 = {"ip": "10.1.1.4", "subnet": "255.255.255.0", "cidr": "24"} +TCPDUMP_FILE = "{}/{}".format(LOGDIR, "v2query.txt") + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.19") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + logger.info("Master Topology: \n {}".format(TOPOLOGY)) + + logger.info("Running setup_module to create topology") + + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, iperf, iperf_intf, GROUP_RANGE, join=False, traffic=False +): + """ + API to do pre-configuration to send IGMP join and multicast + traffic + + parameters: + ----------- + * `tgen`: topogen object + * `topo`: input json data + * `tc_name`: caller test case name + * `iperf`: router running iperf + * `iperf_intf`: interface name router running iperf + * `GROUP_RANGE`: group range + * `join`: IGMP join, default False + * `traffic`: multicast traffic, default False + """ + + if join: + # Add route to kernal + result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + if traffic: + # Add route to kernal + result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + router_list = tgen.routers() + for router in router_list.keys(): + if router == iperf: + continue + + rnode = router_list[router] + rnode.run("echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter") + + for router in topo["routers"].keys(): + if "static_routes" in topo["routers"][router]: + static_routes = topo["routers"][router]["static_routes"] + for static_route in static_routes: + network = static_route["network"] + next_hop = static_route["next_hop"] + if type(network) is not list: + network = [network] + for net in network: + addKernelRoute(tgen, router, iperf_intf, net, next_hop) + return True + + +def verify_mroute_repopulated(uptime_before, uptime_after): + """ + API to compare uptime for mroutes + + Parameters + ---------- + * `uptime_before` : Uptime dictionary for any particular instance + * `uptime_after` : Uptime dictionary for any particular instance + """ + + for group in uptime_before.keys(): + for source in uptime_before[group].keys(): + if set(uptime_before[group]) != set(uptime_after[group]): + errormsg = ( + "mroute (%s, %s) has not come" + " up after mroute clear [FAILED!!]" % (source, group) + ) + return errormsg + + d1 = datetime.datetime.strptime(uptime_before[group][source], "%H:%M:%S") + d2 = datetime.datetime.strptime(uptime_after[group][source], "%H:%M:%S") + if d2 >= d1: + errormsg = "mroute (%s, %s) is not " "repopulated [FAILED!!]" % ( + source, + group, + ) + return errormsg + + logger.info("mroute (%s, %s) is " "repopulated [PASSED!!]", source, group) + + return True + + +def verify_state_incremented(state_before, state_after): + """ + API to compare interface traffic state incrementing + + Parameters + ---------- + * `state_before` : State dictionary for any particular instance + * `state_after` : State dictionary for any particular instance + """ + + for router, state_data in state_before.items(): + for state, value in state_data.items(): + if state_before[router][state] >= state_after[router][state]: + errormsg = ( + "[DUT: %s]: state %s value has not" + " incremented, Initial value: %s, " + "Current value: %s [FAILED!!]" + % ( + router, + state, + state_before[router][state], + state_after[router][state], + ) + ) + return errormsg + + logger.info( + "[DUT: %s]: State %s value is " + "incremented, Initial value: %s, Current value: %s" + " [PASSED!!]", + router, + state, + state_before[router][state], + state_after[router][state], + ) + + return True + + +def find_v2_query_msg_in_tcpdump(tgen, router, message, count, cap_file): + """ + Find v2 query messages in tcpdump file + + Parameters + ---------- + * `tgen` : Topology handler + * `router` : Device under test + * `cap_file` : tcp dump file name + + """ + + filepath = os.path.join(LOGDIR, tgen.modname, router, cap_file) + with open(filepath) as f: + if len(re.findall("{}".format(message), f.read())) < count: + errormsg = "[DUT: %s]: Verify Message: %s in tcpdump" " [FAILED!!]" % ( + router, + message, + ) + return errormsg + + logger.info( + "[DUT: %s]: Found message: %s in tcpdump " " count: %s [PASSED!!]", + router, + message, + count, + ) + return True + + +def find_tos_in_tcpdump(tgen, router, message, cap_file): + """ + Find v2 query messages in tcpdump file + + Parameters + ---------- + * `tgen` : Topology handler + * `router` : Device under test + * `cap_file` : tcp dump file name + + """ + + filepath = os.path.join(LOGDIR, tgen.modname, router, cap_file) + with open(filepath) as f: + + if len(re.findall(message, f.read())) < 1: + errormsg = "[DUT: %s]: Verify Message: %s in tcpdump" " [FAILED!!]" % ( + router, + message, + ) + return errormsg + + logger.info( + "[DUT: %s]: Found message: %s in tcpdump " "[PASSED!!]", router, message + ) + return True + + +def test_verify_oil_when_join_prune_sent_scenario_1_p1(request): + """ + TC_21_1: + Verify OIL detail updated in (S,G) and (*,G) mroute when IGMP + join/prune is sent + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (226.1.1.1-5)" + ) + step( + "Enable IGMP of FRR3 interface and send IGMP joins " + " from FRR3 node for group range (226.1.1.1-5)" + ) + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_dict = { + "f1": {"igmp": {"interfaces": {intf_f1_i8: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = { + "i1": topo["routers"]["i1"]["links"]["l1"]["interface"], + "i8": topo["routers"]["i8"]["links"]["f1"]["interface"], + } + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (226.1.1.1-5) in R2") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Configure one source on FRR3 for all the groups and send" " multicast traffic" + ) + + input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "r2", + "src_address": source_i2, + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "f1", + "src_address": "*", + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send the IGMP prune from ixia to (226.1.1.1-5) receiver on " "FRR1 node") + + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False) + + step( + "After receiving the IGMP prune from FRR1 , verify traffic " + "immediately stopped for this receiver 'show ip multicast'" + ) + + input_traffic = {"l1": {"traffic_sent": [intf_l1_i1]}} + result = verify_multicast_traffic(tgen, input_traffic, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + " Traffic is not stopped yet \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step( + "IGMP groups are remove from FRR1 node 'show ip igmp groups'" + " FRR3 IGMP still present" + ) + + dut = "l1" + result = verify_igmp_groups( + tgen, dut, intf_l1_i1, IGMP_JOIN_RANGE_1, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "IGMP groups are not deleted \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + dut = "f1" + result = verify_igmp_groups(tgen, dut, intf_f1_i8, IGMP_JOIN_RANGE_1) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "(*,G) and (S,G) OIL got removed immediately after receiving" + " prune 'show ip pim state' and 'show ip mroute' on FRR1 node," + " no impact on FRR3 receiver" + ) + + input_dict_l1 = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_l1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "mroutes are still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + for data in input_dict_l1: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "upstream entries are still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + input_dict_f1 = [ + { + "dut": "f1", + "src_address": "*", + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_f1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_f1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send the IGMP prune from ixia to (226.1.1.1-5) receiver on " " FRR3 node") + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + shutdown_bringup_interface(tgen, "f1", intf_f1_i8, False) + + step( + "After receiving the IGMP prune from FRR3s , verify traffic " + "immediately stopped for this receiver 'show ip multicast'" + ) + + input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}} + result = verify_multicast_traffic(tgen, input_traffic, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + " Traffic is not stopped yet \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step( + "IGMP groups are remove from FRR1 node 'show ip igmp groups'" + " FRR3 IGMP still present" + ) + + dut = "f1" + result = verify_igmp_groups( + tgen, dut, intf_f1_i8, IGMP_JOIN_RANGE_1, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "IGMP groups are not deleted \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step( + "(*,G) and (S,G) OIL got prune state (none) from all the nodes" + "FRR1, FRR3 verify using 'show ip mroute'" + ) + + input_dict_l1 = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_l1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "mroutes are still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + for data in input_dict_l1: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "upstream entries are still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + shutdown_bringup_interface(tgen, "f1", intf_f1_i8, True) + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True) + + for data in input_dict_l1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_oil_when_join_prune_sent_scenario_2_p1(request): + """ + TC_21_2: Verify OIL detail updated in (S,G) and (*,G) mroute when IGMP + join/prune is sent + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Removing FRR3 to simulate topo " "FHR(FRR1)---LHR(FRR2)") + + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"] + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"] + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_r2, False) + + step("Enable the PIM on all the interfaces of FRR1, FRR2") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (226.1.1.1-5)" + ) + step( + "Enable IGMP of FRR3 interface and send IGMP joins " + " from FRR3 node for group range (226.1.1.1-5)" + ) + + intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"] + input_dict = { + "r2": {"igmp": {"interfaces": {intf_r2_i3: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = { + "i1": topo["routers"]["i1"]["links"]["l1"]["interface"], + "i3": topo["routers"]["i3"]["links"]["r2"]["interface"], + } + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (226.1.1.1-5) in R2") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send the IGMP prune from ixia to (226.1.1.1-5) receiver on " "FRR3(r2) node") + + intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_i3, False) + + step( + "After sending IGMP prune from FRR3(r2) node verify (*,G) OIL " + "immediately removed for local receiver mroute should have " + " PIM protocol , IGMP should be removed verify using " + "'show ip mroute' no impact seen on FRR1(l1) (*,G)" + ) + + input_dict_r2 = [ + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + } + ] + + for data in input_dict_r2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "mroutes are still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + input_dict_l1_r2 = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_l1_r2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send the IGMP prune from ixia to (226.1.1.1-5) receiver on " "FRR1(l1) node") + + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False) + + step( + "After sending IGMP prune from FRR1 node verify (*,G) OIL" + "got removed immediately from FRR1 node" + ) + + input_dict_l1 = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + } + ] + + for data in input_dict_l1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "mroutes are still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step("After prune is sent verify upstream got removed in FRR1 node") + + for data in input_dict_l1: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "upstream entries are still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + write_test_footer(tc_name) + + +def test_shut_noshut_source_interface_when_upstream_cleared_from_LHR_p1(request): + """ + TC_26: Verify shut/no shut of source interface after upstream got cleared + from LHR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Enable the PIM on all the interfaces of FRR1, R2 and FRR3" " routers") + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)") + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP on R2 (loopback interface) for " "the group range 225.0.0.0/8") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to 225.1.1.1-225.1.1.10" " receiver") + + input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "'show ip mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + + source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "r2", + "src_address": source_i2, + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + step( + "'show ip pim upstream' and 'show ip pim upstream-rpf' showing" + " correct OIL and IIF on all the nodes" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the source interface from FRR3") + intf_f1_i2 = topo["routers"]["f1"]["links"]["i2"]["interface"] + shutdown_bringup_interface(tgen, "f1", intf_f1_i2, False) + + step( + "After shut of source interface verify (S,G) mroutes are cleared" + " from all the nodes" + ) + + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + result = verify_ip_mroutes( + tgen, "f1", source_i2, IGMP_JOIN_RANGE_1, intf_f1_i2, intf_f1_r2, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n mroutes are" + " still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behavior: {}".format(result)) + + step( + "After waiting for (S,G) timeout from FRR1 for same" + " source verify that (S,G) is flushed from FRR1 node" + " 'show ip pim upstream' 'show ip mroute' " + ) + + done_flag = False + for retry in range(1, 11): + result = verify_upstream_iif( + tgen, "l1", "Unknown", source_i2, IGMP_JOIN_RANGE_1, expected=False + ) + if result is not True: + done_flag = True + else: + continue + if done_flag: + logger.info("Expected Behavior: {}".format(result)) + break + + assert done_flag is True, ( + "Testcase {} : Failed Error: \n " + "mroutes are still present, after waiting for 10 mins".format(tc_name) + ) + + step("No shut the Source interface just after the upstream is expired" " from FRR1") + shutdown_bringup_interface(tgen, "f1", intf_f1_i2, True) + + step( + "After no shut of source interface , verify all the (S,G) is " + " populated again on 'show ip mroute' 'show ip pim upstream' " + " with proper OIL and IIF detail" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("shut and no shut the source interface immediately") + shutdown_bringup_interface(tgen, "f1", intf_f1_i2, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_i2, True) + + step( + "All the mroutes got updated with proper OIL after no shut of" + "interface verify using 'show ip mroute'" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_shut_noshut_receiver_interface_when_upstream_cleared_from_LHR_p1(request): + """ + TC_27: Verify shut/no shut of receiver interface after upstream got + cleared from LHR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Enable the PIM on all the interfaces of FRR1, R2 and FRR3" " routers") + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)") + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP on R2 (loopback interface) for " "the group range 225.0.0.0/8") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to 225.1.1.1-225.1.1.10" " receiver") + + input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "'show ip mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + + source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "r2", + "src_address": source_i2, + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "'show ip pim upstream' and 'show ip pim upstream-rpf' showing" + " correct OIL and IIF on all the nodes" + ) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the source interface FRR1") + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + intf_f1_i2 = topo["routers"]["f1"]["links"]["i2"]["interface"] + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False) + + step( + "After waiting for (S,G) timeout from FRR1 for same" + " source verify that (S,G) is flushed from FRR1 node" + " 'show ip pim upstream' 'show ip mroute' " + ) + + done_flag = False + for retry in range(1, 11): + result = verify_upstream_iif( + tgen, "l1", "Unknown", source_i2, IGMP_JOIN_RANGE_1, expected=False + ) + if result is not True: + done_flag = True + else: + continue + if done_flag: + logger.info("Expected Behavior: {}".format(result)) + break + + assert done_flag is True, ( + "Testcase {} : Failed Error: \n " + "mroutes are still present, after waiting for 10 mins".format(tc_name) + ) + + step("No shut the Source interface just after the upstream is expired" " from FRR1") + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True) + + step( + "After no shut of source interface , verify all the (S,G) is " + " populated again on 'show ip mroute' 'show ip pim upstream' " + " with proper OIL and IIF detail" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("shut and no shut the source interface immediately") + shutdown_bringup_interface(tgen, "f1", intf_f1_i2, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_i2, True) + + step( + "After no shut of receiver interface , verify all the (S,G) is " + "populated again on 'show ip mroute' 'show ip pim upstream' " + "with proper OIL and IIF detail" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_remove_add_igmp_config_to_receiver_interface_p0(request): + """ + TC_33: Verify removing and adding IGMP config from the receiver interface + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Enable PIM on all routers") + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)") + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure source on FRR3 and start the traffic for" " (225.1.1.1-225.1.1.10)") + + input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure source on FRR1 and start the traffic for" " (225.1.1.1-225.1.1.10)") + + input_src = {"i6": topo["routers"]["i6"]["links"]["l1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i6 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0] + source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i6, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from" + " receiver interface of FRR1" + ) + + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + input_dict_2 = { + "l1": { + "igmp": { + "interfaces": {intf_l1_i1: {"igmp": {"version": "2", "delete": True,}}} + } + } + } + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("IGMP join removed from FRR1 , verify using " "'show ip igmp groups json'") + + dut = "l1" + interface = topo["routers"]["l1"]["links"]["i1"]["interface"] + result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Groups are not" + " present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + intf_f1_i2 = topo["routers"]["f1"]["links"]["i2"]["interface"] + input_traffic = { + "l1": {"traffic_received": [intf_l1_r2], "traffic_sent": [intf_l1_i1]}, + "f1": {"traffic_sent": [intf_f1_r2], "traffic_received": [intf_f1_i2]}, + } + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Configure igmp 'ip igmp' and 'ip igmp version 2' from " + "receiver interface of FRR1" + ) + + input_dict_2 = { + "l1": {"igmp": {"interfaces": {intf_l1_i1: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "After adding IGMP on receiver interface verify (S,G) and (*,G)" + " entries got populated and traffic is resumed on FRR1 and FRR3 node" + ) + + step( + "Verify OIL/IIF and drJoinDesired using 'show ip mroute , and traffic" + " using show ip pim upstream and show ip multicast'" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from" + " receiver interface of FRR1" + ) + + input_dict_2 = { + "l1": { + "igmp": { + "interfaces": {intf_l1_i1: {"igmp": {"version": "2", "delete": True,}}} + } + } + } + + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("IGMP join removed from FRR1 , verify using " "'show ip igmp groups json'") + + dut = "l1" + interface = topo["routers"]["l1"]["links"]["i1"]["interface"] + result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Groups are not" + " present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Configure igmp 'ip igmp' and 'ip igmp version 2' from " + "receiver interface of FRR1" + ) + + input_dict_2 = { + "l1": {"igmp": {"interfaces": {intf_l1_i1: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "After adding IGMP on receiver interface verify (S,G) and (*,G)" + " entries got populated and traffic is resumed on FRR1 and FRR3 node" + ) + + step( + "Verify OIL/IIF and drJoinDesired using 'show ip mroute , and traffic" + " using show ip pim upstream and show ip multicast'" + ) + + input_dict_l1_f1 = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i6, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict_l1_f1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_l1_f1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Remove ip igmp and send igmp prune from FRR1 interface") + + input_dict_2 = { + "l1": { + "igmp": { + "interfaces": {intf_l1_i1: {"igmp": {"version": "2", "delete": True,}}} + } + } + } + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + step( + "Verification: After removing igmp 'no ip igmp' and " + " sending prune verify mroute and upstream got removed" + " from FRR1 verify using 'show ip mroute' and " + "'show ip pim upstream'" + ) + + dut = "l1" + iif = topo["routers"]["l1"]["links"]["i6"]["interface"] + oil = topo["routers"]["l1"]["links"]["i1"]["interface"] + source = source_i6 + result = verify_ip_mroutes( + tgen, dut, source, IGMP_JOIN_RANGE_1, iif, oil, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n routes are still" + " present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + write_test_footer(tc_name) + + +def test_verify_remove_add_igmp_commands_when_pim_configured_p0(request): + """ + TC_34: Verify removing and adding IGMP commands when PIM is already + configured + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Enable PIM on all routers") + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)") + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure source on FRR3 and start the traffic for" " (225.1.1.1-225.1.1.10)") + + input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure source on FRR1 and start the traffic for" " (225.1.1.1-225.1.1.10)") + + input_src = {"i6": topo["routers"]["i6"]["links"]["l1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i6 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0] + source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i6, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verification: After configuring IGMP related config , " + "verify config is present in the interface " + "'show ip igmp interface ensxx json'" + ) + + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + input_dict_1 = { + "l1": {"igmp": {"interfaces": {intf_l1_i1: {"igmp": {"version": "2"}}}}} + } + + result = verify_igmp_config(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from" + " receiver interface of FRR1" + ) + + input_dict_2 = { + "l1": { + "igmp": { + "interfaces": {intf_l1_i1: {"igmp": {"version": "2", "delete": True,}}} + } + } + } + + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Verification: After removing the config CLI got removed " + "'show ip igmp interface ensxx json'" + ) + + result = verify_igmp_config(tgen, input_dict_1, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n " + "IGMP interface is not removed \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure 'ip igmp last-member-query-count 10' on FRR1" " receiver interface") + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": {"igmp": {"query": {"last-member-query-count": 5}}} + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_igmp_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Remove 'ip igmp last-member-query-count 10' on FRR1" " receiver interface") + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": { + "query": {"last-member-query-count": "", "delete": True} + } + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": {"igmp": {"query": {"last-member-query-count": 2}}} + } + } + } + } + result = verify_igmp_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Configure 'ip igmp last-member-query-interval 20' on FRR1" + " receiver interface" + ) + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": {"query": {"last-member-query-interval": 20}} + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_igmp_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Remove 'ip igmp last-member-query-count 10' on FRR1" " receiver interface") + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": { + "query": {"last-member-query-interval": "", "delete": True} + } + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_3 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": { + "igmp": {"query": {"last-member-query-interval": 10}} + } + } + } + } + } + result = verify_igmp_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_remove_add_pim_commands_when_igmp_configured_p1(request): + """ + TC_35: Verify removing and adding PIM commands when IGMP is already + configured + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure 'ip pim' on receiver interface on FRR1") + step("Enable PIM on all routers") + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)") + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove 'no ip pim' on receiver interface on FRR1") + + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + input_dict_1 = {"l1": {"pim": {"disable": intf_l1_i1}}} + result = create_pim_config(tgen, topo, input_dict_1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure 'ip pim bsm' on receiver interface on FRR1") + + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "ip pim bsm"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove 'no ip pim bsm' on receiver interface on FRR1") + + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "no ip pim bsm"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure 'ip pim drpriority' on receiver interface on FRR1") + + raw_config = { + "l1": { + "raw_config": ["interface {}".format(intf_l1_i1), "ip pim drpriority 10"] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verification: After configuring PIM related config, " + "verify config is present in the interface " + "'show ip pim interface ensxx json'" + ) + + input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_i1: {"drPriority": 10}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove 'no ip pim drpriority' on receiver interface on FRR1") + + raw_config = { + "l1": { + "raw_config": ["interface {}".format(intf_l1_i1), "no ip pim drpriority 10"] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verification: After removing the config CLI got removed " + "'show ip pim interface ensxx json'" + ) + + input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_i1: {"drPriority": 1}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure 'ip pim hello' on receiver interface on FRR1") + + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "ip pim hello 50"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verification: After configuring PIM related config, " + "verify config is present in the interface " + "'show ip pim interface ensxx json'" + ) + + input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_i1: {"helloPeriod": 50}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove 'no ip pim hello' on receiver interface on FRR1") + + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "no ip pim hello"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verification: After removing the config CLI got removed " + "'show ip pim interface ensxx json'" + ) + + input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_i1: {"helloPeriod": 30}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure 'ip pim unicast-bsm' on receiver interface on FRR1") + + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "ip pim unicast-bsm"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove 'no ip pim hello' on receiver interface on FRR1") + + raw_config = { + "l1": { + "raw_config": ["interface {}".format(intf_l1_i1), "no ip pim unicast-bsm"] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_pim_dr_priority_p0(request): + """ + TC_36: Verify highest DR priority become the PIM DR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure 'ip pim' on receiver interface on FRR1") + step("Enable PIM on all routers") + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)") + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure 'ip pim drpriority 10' on receiver interface on FRR1(LHR)") + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + raw_config = { + "l1": { + "raw_config": ["interface {}".format(intf_l1_r2), "ip pim drpriority 10"] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "DR config is successful on FRR1 node , verify using " + " 'show ip pim interface json'" + ) + + input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_r2: {"drPriority": 10}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure 'ip pim drpriority 20' on receiver interface on FRR3(FHR)") + + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + raw_config = { + "f1": { + "raw_config": ["interface {}".format(intf_f1_r2), "ip pim drpriority 20"] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "DR config is successful on FRR3 node , verify using " + " 'show ip pim interface json'" + ) + + input_dict_dr = {"f1": {"pim": {"interfaces": {intf_f1_r2: {"drPriority": 20}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "PIM is enable on FRR1, FRR2 interface and neighbor is up, " + " verify using 'show ip pim interface'" + ) + + result = verify_pim_interface(tgen, topo, "l1") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_pim_interface(tgen, topo, "f1") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Highet IP become PIM DR , verify using " + "'show ip pim interface json' and 'show ip pim neighbor'" + ) + step("Highest priority become PIM DR") + + dr_address = topo["routers"]["l1"]["links"]["r2"]["ipv4"].split("/")[0] + input_dict_dr = { + "l1": {"pim": {"interfaces": {intf_l1_r2: {"drAddress": dr_address}}}} + } + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + dr_address = topo["routers"]["f1"]["links"]["r2"]["ipv4"].split("/")[0] + input_dict_dr = { + "f1": {"pim": {"interfaces": {intf_f1_r2: {"drAddress": dr_address}}}} + } + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove 'no ip pim drpriority' on receiver interface on FRR1") + + raw_config = { + "l1": { + "raw_config": ["interface {}".format(intf_l1_r2), "no ip pim drpriority 10"] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove 'no ip pim drpriority' on receiver interface on FRR3") + + raw_config = { + "f1": { + "raw_config": ["interface {}".format(intf_f1_r2), "no ip pim drpriority 20"] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "After removing drpriority , config got removed from both the " + "nodes and highest IP become PIM DR" + ) + + input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_r2: {"drPriority": 1}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + input_dict_dr = {"f1": {"pim": {"interfaces": {intf_f1_r2: {"drPriority": 1}}}}} + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + dr_address = topo["routers"]["r2"]["links"]["l1"]["ipv4"].split("/")[0] + input_dict_dr = { + "l1": {"pim": {"interfaces": {intf_l1_r2: {"drAddress": dr_address}}}} + } + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + dr_address = topo["routers"]["r2"]["links"]["f1"]["ipv4"].split("/")[0] + input_dict_dr = { + "f1": {"pim": {"interfaces": {intf_f1_r2: {"drAddress": dr_address}}}} + } + result = verify_pim_config(tgen, input_dict_dr) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_pim_hello_timer_p1(request): + """ + TC_37: Verify PIM hello is sent on configured timer + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure 'ip pim' on receiver interface on FRR1") + step("Enable PIM on all routers") + step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)") + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure PIM hello interval timer 100 on FRR1 node (FRR1-FRR2 link)") + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_r2), "ip pim hello 100"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "PIM hello interval is configured on interface verify using " + "'show ip pim interface'" + ) + + input_dict_hello = { + "l1": {"pim": {"interfaces": {intf_l1_r2: {"helloPeriod": 100}}}} + } + result = verify_pim_config(tgen, input_dict_hello) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Modify hello timer to 180 and then 50sec") + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_r2), "ip pim hello 180"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "PIM hello interval is configured on interface verify using " + "'show ip pim interface'" + ) + + input_dict_hello = { + "l1": {"pim": {"interfaces": {intf_l1_r2: {"helloPeriod": 180}}}} + } + result = verify_pim_config(tgen, input_dict_hello) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_r2), "ip pim hello 50"]} + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "PIM hello interval is configured on interface verify using " + "'show ip pim interface'" + ) + + input_dict_hello = { + "l1": {"pim": {"interfaces": {intf_l1_r2: {"helloPeriod": 50}}}} + } + result = verify_pim_config(tgen, input_dict_hello) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_mroute_after_removing_RP_sending_IGMP_prune_p2(request): + """ + TC_39 Verify mroute after removing the RP and sending IGMP prune + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Remove cisco connected link to simulate topo " + "LHR(FRR1(f1))----RP(cisco(f1)---FHR(FRR3(l1))" + ) + + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"] + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False) + + step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (225.1.1.1-5)" + ) + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_dict = { + "f1": {"igmp": {"interfaces": {intf_f1_i8: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i8": topo["routers"]["i8"]["links"]["f1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Send traffic from FHR to all the groups ( 225.1.1.1 to 225.1.1.5) and send" + " multicast traffic" + ) + + input_src = {"i6": topo["routers"]["i6"]["links"]["l1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i2 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0] + + input_dict_all = [ + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + }, + { + "dut": "f1", + "src_address": "*", + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove the RP config for both the range from all the nodes") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + "delete": True, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + input_dict_starg = [ + { + "dut": "f1", + "src_address": "*", + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + ] + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "mroute still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send prune from receiver-1 (using ctrl+c) on iperf interface") + kill_iperf(tgen) + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}} + traffic_before = verify_multicast_traffic( + tgen, input_traffic, return_traffic=True, expected=False + ) + assert isinstance(traffic_before, dict), ( + "Testcase {} : Failed \n traffic_before is not dictionary \n " + "Error: {}".format(tc_name, result) + ) + + step("IGMP groups are remove from FRR1 node 'show ip igmp groups'") + + dut = "f1" + result = verify_igmp_groups( + tgen, dut, intf_f1_i8, IGMP_JOIN_RANGE_1, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "IGMP groups still present still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step( + "After receiving the IGMP prune from FRR1 , verify traffic " + "immediately stopped for this receiver 'show ip multicast'" + ) + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}} + traffic_after = verify_multicast_traffic( + tgen, input_traffic, return_traffic=True, expected=False + ) + assert isinstance(traffic_after, dict), ( + "Testcase {} : Failed \n traffic_after is not dictionary \n " + "Error: {}".format(tc_name, result) + ) + + result = verify_state_incremented(traffic_before, traffic_after) + assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result) + logger.info("Expected Behaviour: {}".format(result)) + + step("Configure static RP for (225.1.1.1-5) as R2 loopback interface") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send IGMP joins again from LHR,check IGMP joins and starg received") + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send traffic from FHR and verify mroute upstream") + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i2 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0] + + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_prune_sent_to_LHR_and_FHR_when_PIMnbr_down_p2(request): + """ + TC_38 Verify prune is sent to LHR and FHR when PIM nbr went down + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Remove cisco connected link to simulate topo " + "LHR(FRR1(f1))----RP(cisco(f1)---FHR(FRR3(l1))" + ) + + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"] + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False) + + step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (225.1.1.1-5)" + ) + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_dict = { + "f1": {"igmp": {"interfaces": {intf_f1_i8: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i8": topo["routers"]["i8"]["links"]["f1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Send traffic from FHR to all the groups ( 225.1.1.1 to 225.1.1.5) and send" + " multicast traffic" + ) + + input_src = { + "i6": topo["routers"]["i6"]["links"]["l1"]["interface"], + "i2": topo["routers"]["i2"]["links"]["f1"]["interface"], + } + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i2 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0] + source_i1 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + + input_dict_all = [ + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + }, + { + "dut": "f1", + "src_address": "*", + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i1, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + step("Verify mcast traffic received") + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}} + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the link from LHR to RP from RP node") + + intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_f1, False) + + step("Verify RP info after Shut the link from LHR to RP from RP node") + dut = "f1" + rp_address = "1.0.5.17" + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_starg = [ + { + "dut": "f1", + "src_address": "*", + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + } + ] + + input_dict_sg_i2 = [ + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + ] + + input_dict_sg_i1 = [ + { + "dut": "f1", + "src_address": source_i1, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + } + ] + + input_dict_sg_i2_l1 = [ + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + } + ] + + step("Verify mroute after Shut the link from LHR to RP from RP node") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "mroute still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + for data in input_dict_sg_i1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify upstream after Shut the link from LHR to RP from RP node") + + for data in input_dict_starg: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "upstream still present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + for data in input_dict_sg_i1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("No shut the link from LHR to RP from RP node") + + intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_f1, True) + + step("Verify RP info after No shut the link from LHR to RP from RP node") + dut = "f1" + rp_address = "1.0.5.17" + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "RP iif is not updated \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step("Verify mroute after No shut the link from LHR to RP from RP node") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify upstrem after No shut the link from LHR to RP from RP node") + + for data in input_dict_starg: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify mcast traffic received after noshut LHR to RP from RP node") + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}} + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the link from FHR to RP from RP node") + + intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_l1, False) + + kill_iperf(tgen, dut="i2", action="remove_traffic") + + step("Verify RP info after Shut the link from FHR to RP from RP node") + dut = "l1" + rp_address = "1.0.5.17" + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify mroute after Shut the link from FHR to RP from RP node") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify upstream after Shut the link from FHR to RP from RP node") + + for data in input_dict_starg: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2_l1: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step(" No shut the link from FHR to RP from RP node") + + intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_l1, True) + + step("Verify RP info after Noshut the link from FHR to RP from RP node") + + dut = "l1" + rp_address = "1.0.5.17" + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "RP iif is not updated \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step("Verify mroute after Noshut the link from FHR to RP from RP node") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify mroute after Noshut the link from FHR to RP from RP node") + + for data in input_dict_starg: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify mcast traffic received after noshut FHR to RP from RP node") + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}} + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the link from FHR to RP from FHR node") + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_r2, False) + + step("Verify PIM Nbrs after Shut the link from FHR to RP from FHR node") + + kill_iperf(tgen, dut="i6", action="remove_traffic") + + step("Verify RP info after Shut the link from FHR to RP from FHR node") + dut = "l1" + rp_address = "1.0.5.17" + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify mroute after Shut the link from FHR to RP from FHR node") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify upstream after Shut the link from FHR to RP from FHR node") + for data in input_dict_starg: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2_l1: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step(" No shut the link from FHR to RP from FHR node") + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_r2, True) + + step("Verify RP info after No Shut the link from FHR to RP from FHR node") + dut = "l1" + rp_address = "1.0.5.17" + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "RP iif is not updated \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step("Verify mroute after No Shut the link from FHR to RP from FHR node") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify upstream after No Shut the link from FHR to RP from FHR node") + + for data in input_dict_starg: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg_i2: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify mcast traffic received after noshut FHR to RP from FHR node") + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}} + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_mroute_flags_p1(request): + """ + TC_47 Verify mroute flag in LHR and FHR node + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Remove cisco connected link to simulate topo " + "LHR(FRR1(f1))----RP(cisco(f1)---FHR(FRR3(l1))" + ) + + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"] + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False) + + step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (225.1.1.1-5)" + ) + + intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"] + input_dict = { + "f1": {"igmp": {"interfaces": {intf_f1_i8: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i8": topo["routers"]["i8"]["links"]["f1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Send traffic from FHR to all the groups ( 225.1.1.1 to 225.1.1.5) and send" + " multicast traffic" + ) + + input_src = { + "i6": topo["routers"]["i6"]["links"]["l1"]["interface"], + "i2": topo["routers"]["i2"]["links"]["f1"]["interface"], + } + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i2 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0] + source_i1 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] + + input_dict_all = [ + { + "dut": "l1", + "src_address": source_i2, + "iif": topo["routers"]["l1"]["links"]["i6"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + }, + { + "dut": "f1", + "src_address": "*", + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i1, + "iif": topo["routers"]["f1"]["links"]["i2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + { + "dut": "f1", + "src_address": source_i2, + "iif": topo["routers"]["f1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["f1"]["links"]["i8"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + dut = "f1" + step("verify flag for (*,G) on f1") + src_address = "*" + flag = "SC" + result = verify_multicast_flag_state( + tgen, dut, src_address, IGMP_JOIN_RANGE_1, flag + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify flag for (S,G) on f1 for Remote spurce ") + src_address = source_i2 + flag = "ST" + result = verify_multicast_flag_state( + tgen, dut, src_address, IGMP_JOIN_RANGE_1, flag + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_multicast_traffic_when_LHR_connected_to_RP_p1(request): + """ + TC_11: Verify multicast traffic flowing fine, when LHR connected to RP + Topology used: + FHR(FRR3(l1))---LHR(FRR1(r2)----RP(FRR2(f1)) + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Remove FRR3 to cisco connected link to simulate topo " + "FHR(FRR3(l1))---LHR(FRR1(r2)----RP(FRR2(f1))" + ) + + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"] + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False) + + step("Disable IGMP config from l1") + input_dict_2 = { + "l1": { + "igmp": { + "interfaces": { + "l1-i1-eth1": {"igmp": {"version": "2", "delete": True,}} + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Enable the PIM on all the interfaces of FRR1, R2 and FRR3" " routers") + step( + "Enable IGMP on FRR1(r2) interface and send IGMP join (226.1.1.1-5)" + " and (232.1.1.1-5)" + ) + + intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"] + input_dict = { + "r2": {"igmp": {"interfaces": {intf_r2_i3: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3 + + input_join = {"i3": topo["routers"]["i3"]["links"]["r2"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in (f1)") + + input_dict = { + "f1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["f1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": _GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to 225.1.1.1-225.1.1.10" " receiver") + + input_src = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "'show ip mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + + source_i1 = topo["routers"]["i1"]["links"]["l1"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": source_i1, + "iif": topo["routers"]["l1"]["links"]["i1"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + }, + { + "dut": "r2", + "src_address": source_i1, + "iif": topo["routers"]["r2"]["links"]["l1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + }, + ] + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Multicast traffic is flowing for all the groups verify" + "using 'show ip multicast'" + ) + + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"] + intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"] + intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"] + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + input_traffic = { + "l1": {"traffic_received": [intf_l1_i1]}, + "r2": {"traffic_received": [intf_r2_l1], "traffic_sent": [intf_r2_i3]}, + } + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut and No shut the receiver port") + + intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_i3, False) + + step( + "Verification: After Shut of receiver port, Verify (*,G) and " + "(S,G) got removed from LHR node (FRR1) using 'show ip mroute'" + ) + + input_dict_r2 = [ + { + "dut": "r2", + "src_address": "*", + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + }, + { + "dut": "r2", + "src_address": source_i1, + "iif": topo["routers"]["r2"]["links"]["l1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + }, + ] + + for data in input_dict_r2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n" + " Expected Behaviour: mroutes are cleared \n Error: {}".format( + tc_name, result + ) + ) + logger.info("Expected Behaviour: {}".format(result)) + + shutdown_bringup_interface(tgen, "r2", intf_r2_i3, True) + + step( + "Verification: After No shut of receiver port , Verify (*,G)" + " and (S,G) got populated on LHR node (FRR1) using " + "'show ip mroute' 'show ip pim upstream'" + ) + + for data in input_dict_r2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_r2: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Multicast traffic is resumed for all the groups verify " + "using 'show ip multicast'" + ) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut and No shut the source port") + + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False) + + step( + "Verification: After Shut of source port, Verify (*,G) and " + "(S,G) got removed from LHR node (FRR1) using 'show ip mroute'" + ) + + input_dict_l1 = [ + { + "dut": "l1", + "src_address": source_i1, + "iif": topo["routers"]["l1"]["links"]["i1"]["interface"], + "oil": topo["routers"]["l1"]["links"]["r2"]["interface"], + } + ] + + for data in input_dict_l1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n" + "mroutes are cleared \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True) + + step( + "Verification: After No shut of source port , Verify (*,G)" + " and (S,G) got populated on LHR node (FRR1) using " + "'show ip mroute' 'show ip pim upstream'" + ) + + for data in input_dict_l1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_l1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Multicast traffic is resumed for all the groups verify " + "using 'show ip multicast'" + ) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut and No shut of LHR to cisco port from LHR side") + + intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_f1, False) + + step( + "Verification: After Shut of source port, Verify (S,G) got " + "removed from LHR and FHR using 'show ip mroute'" + ) + + input_dict_r2_f1 = [ + { + "dut": "r2", + "src_address": "*", + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + }, + { + "dut": "f1", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict_r2_f1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n" + " mroutes are cleared \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + shutdown_bringup_interface(tgen, "r2", intf_r2_f1, True) + + step( + "Verification: After No shut of source port , Verify (*,G)" + " and (S,G) got populated on LHR node (FRR1) using " + "'show ip mroute' 'show ip pim upstream'" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Multicast traffic is resumed for all the groups verify " + "using 'show ip multicast'" + ) + + input_traffic_r2 = { + "r2": {"traffic_received": [intf_r2_l1], "traffic_sent": [intf_r2_i3]} + } + result = verify_multicast_traffic(tgen, input_traffic_r2) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut and no shut of FHR to LHR port from FHR side") + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_r2, False) + + step( + "Verification: After Shut of LHR to FHR port, Verify (S,G)" + "got removed from LHR 'show ip mroute'" + ) + + dut = "r2" + src_address = "*" + iif = topo["routers"]["r2"]["links"]["f1"]["interface"] + oil = topo["routers"]["r2"]["links"]["i3"]["interface"] + + result = verify_ip_mroutes(tgen, dut, src_address, _IGMP_JOIN_RANGE, iif, oil) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + src_address = source_i1 + iif = topo["routers"]["r2"]["links"]["l1"]["interface"] + oil = topo["routers"]["r2"]["links"]["i3"]["interface"] + + result = verify_ip_mroutes( + tgen, dut, src_address, _IGMP_JOIN_RANGE, iif, oil, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n" + " mroutes are cleared \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + shutdown_bringup_interface(tgen, "l1", intf_l1_r2, True) + + step( + "Verification: After No shut of source port , Verify (*,G)" + " and (S,G) got populated on LHR node (FRR1) using " + "'show ip mroute' 'show ip pim upstream'" + ) + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Multicast traffic is resumed for all the groups verify " + "using 'show ip multicast'" + ) + + result = verify_multicast_traffic(tgen, input_traffic_r2) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_multicast_traffic_when_FHR_connected_to_RP_p1(request): + """ + TC_12: Verify multicast traffic is flowing fine when FHR is connected to RP + Topology used: + LHR(FRR1)---FHR(FRR3)----RP(FRR2) + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + check_router_status(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Remove FRR3 to FRR2 connected link to simulate topo " + "FHR(FRR3)---LHR(FRR1)----RP(FFR2)" + ) + + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"] + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"] + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False) + shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False) + + step("Enable the PIM on all the interfaces of FRR1, R2 and FRR3" " routers") + step("Enable IGMP on FRR1(l1) interface and send IGMP join " " and (225.1.1.1-5)") + + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3 + + input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, _GROUP_RANGE, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, _IGMP_JOIN_RANGE, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP for (225.1.1.1-5) in (f1)") + + input_dict = { + "f1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["f1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": _GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3(r2) to 225.1.1.1-225.1.1.10" " receiver") + + input_src = {"i3": topo["routers"]["i3"]["links"]["r2"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, _GROUP_RANGE, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, _IGMP_JOIN_RANGE, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "'show ip mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + + source_i3 = topo["routers"]["i3"]["links"]["r2"]["ipv4"].split("/")[0] + input_dict_all = [ + { + "dut": "l1", + "src_address": "*", + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "l1", + "src_address": source_i3, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "r2", + "src_address": source_i3, + "iif": topo["routers"]["r2"]["links"]["i3"]["interface"], + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + ] + + for data in input_dict_all: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_all: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"] + intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"] + intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] + input_traffic = { + "l1": {"traffic_received": [intf_l1_r2], "traffic_sent": [intf_l1_i1]} + } + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the receiver(l1) port in 1 min interval") + + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False) + + step( + "Verification: After Shut of receiver port, Verify (*,G) and " + "(S,G) got removed from LHR node (FRR1) using 'show ip mroute'" + ) + + input_dict_l1 = [ + { + "dut": "l1", + "src_address": source_i3, + "iif": topo["routers"]["l1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["l1"]["links"]["i1"]["interface"], + } + ] + + for data in input_dict_l1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n" + " mroutes are cleared \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step("No shut the receiver(l1) port in 1 min interval") + + shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True) + + step( + "Verification: After No shut of receiver port , Verify (*,G)" + " and (S,G) got populated on LHR node (FRR1) using " + "'show ip mroute' 'show ip pim upstream'" + ) + + for data in input_dict_l1: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_l1: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the source(r2) port in 1 min interval") + + intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_i3, False) + + step( + "Verification: After Shut of source port, Verify (S,G) got " + "removed from FHR using 'show ip mroute'" + ) + + input_dict_r2 = [ + { + "dut": "r2", + "src_address": source_i3, + "iif": topo["routers"]["r2"]["links"]["i3"]["interface"], + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + } + ] + + for data in input_dict_r2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n" + " mroutes are cleared \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + step("No shut the source(r2) port in 1 min interval") + + shutdown_bringup_interface(tgen, "r2", intf_r2_i3, True) + + step( + "Verification: After No shut of source port , Verify (*,G)" + " and (S,G) got populated on LHR and FHR using " + "'show ip mroute' 'show ip pim upstream'" + ) + + for data in input_dict_r2: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_r2: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut FHR to RP port from FHR side") + + intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_f1, False) + + step( + "Verification: After Shut of FHR to cisco port, Verify (*,G) " + "got removed from FHR and cisco node using 'show ip mroute'" + ) + + input_dict_all_star = [ + { + "dut": "r2", + "src_address": "*", + "iif": topo["routers"]["r2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["r2"]["links"]["l1"]["interface"], + }, + { + "dut": "f1", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["f1"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict_all_star: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + _IGMP_JOIN_RANGE, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n" + " mroutes are cleared \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behaviour: {}".format(result)) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo4.py b/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo4.py new file mode 100755 index 0000000000..33c57f209a --- /dev/null +++ b/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo4.py @@ -0,0 +1,1120 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test multicast pim sm: + +Test steps +- Create topology (setup module) +- Bring up topology + +Following tests are covered: + +1. TC:48 Verify mroute after configuring black-hole route for RP and source +2. TC:49 Verify mroute when RP is reachable using default route +3. TC:50 Verify mroute when LHR,FHR,RP and transit routers reachable + using default routes +4. TC:52 Verify PIM nbr after changing interface ip +5. TC:53 Verify IGMP interface updated with correct detail after changing interface config +6. TC:54 Verify received and transmit hello stats are getting cleared after PIM nbr reset + + +""" + +import os +import re +import sys +import json +import time +import datetime +from time import sleep +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + iperfSendIGMPJoin, + addKernelRoute, + reset_config_on_routers, + iperfSendTraffic, + kill_iperf, + shutdown_bringup_interface, + start_router, + stop_router, + apply_raw_config, + create_static_routes, + required_linux_kernel_version, + topo_daemons, +) +from lib.pim import ( + create_pim_config, + create_igmp_config, + verify_igmp_groups, + verify_ip_mroutes, + clear_ip_pim_interface_traffic, + verify_igmp_config, + verify_pim_neighbors, + verify_pim_config, + verify_pim_interface, + verify_upstream_iif, + clear_ip_mroute, + verify_multicast_traffic, + verify_pim_rp_info, + verify_pim_interface_traffic, + verify_igmp_interface, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/multicast_pim_sm_topo4.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +TOPOLOGY = """ + + + i4-----c1-------------c2---i5 + | | + | | + i1-----l1------r2-----f1---i2 + | | | | + | | | | + i7 i6 i3 i8 + + Description: + i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send IGMP + join and traffic + l1 - LHR + f1 - FHR + r2 - FRR router + c1 - FRR router + c2 - FRR router +""" + +# Global variables + +GROUP_RANGE = "224.0.0.0/4" +IGMP_GROUP = "225.1.1.1/32" +IGMP_JOIN = "225.1.1.1" +GROUP_RANGE_1 = [ + "225.1.1.1/32", + "225.1.1.2/32", + "225.1.1.3/32", + "225.1.1.4/32", + "225.1.1.5/32", +] +IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"] +NEW_ADDRESS_1 = "192.168.20.1" +NEW_ADDRESS_2 = "192.168.20.2" +NEW_ADDRESS_1_SUBNET = "192.168.20.1/24" +NEW_ADDRESS_2_SUBNET = "192.168.20.2/24" + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.19") + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + logger.info("Master Topology: \n {}".format(TOPOLOGY)) + + logger.info("Running setup_module to create topology") + + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, iperf, iperf_intf, GROUP_RANGE, join=False, traffic=False +): + """ + API to do pre-configuration to send IGMP join and multicast + traffic + + parameters: + ----------- + * `tgen`: topogen object + * `topo`: input json data + * `tc_name`: caller test case name + * `iperf`: router running iperf + * `iperf_intf`: interface name router running iperf + * `GROUP_RANGE`: group range + * `join`: IGMP join, default False + * `traffic`: multicast traffic, default False + """ + + if join: + # Add route to kernal + result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + if traffic: + # Add route to kernal + result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + router_list = tgen.routers() + for router in router_list.keys(): + if router == iperf: + continue + + rnode = router_list[router] + rnode.run("echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter") + + for router in topo["routers"].keys(): + if "static_routes" in topo["routers"][router]: + static_routes = topo["routers"][router]["static_routes"] + for static_route in static_routes: + network = static_route["network"] + next_hop = static_route["next_hop"] + if type(network) is not list: + network = [network] + + return True + + +def verify_state_incremented(state_before, state_after): + """ + API to compare interface traffic state incrementing + + Parameters + ---------- + * `state_before` : State dictionary for any particular instance + * `state_after` : State dictionary for any particular instance + """ + + for router, state_data in state_before.items(): + for state, value in state_data.items(): + if state_before[router][state] >= state_after[router][state]: + errormsg = ( + "[DUT: %s]: state %s value has not" + " incremented, Initial value: %s, " + "Current value: %s [FAILED!!]" + % ( + router, + state, + state_before[router][state], + state_after[router][state], + ) + ) + return errormsg + + logger.info( + "[DUT: %s]: State %s value is " + "incremented, Initial value: %s, Current value: %s" + " [PASSED!!]", + router, + state, + state_before[router][state], + state_after[router][state], + ) + + return True + + +def test_mroute_when_RP_reachable_default_route_p2(request): + """ + TC_49 Verify mroute when and source RP is reachable using default route + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Remove c1-c2 connected link to simulate topo " + "c1(FHR)---l1(RP)----r2---f1-----c2(LHR)" + ) + + intf_c1_c2 = topo["routers"]["c1"]["links"]["c2"]["interface"] + intf_c2_c1 = topo["routers"]["c2"]["links"]["c1"]["interface"] + shutdown_bringup_interface(tgen, "c1", intf_c1_c2, False) + shutdown_bringup_interface(tgen, "c2", intf_c2_c1, False) + + step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (225.1.1.1-5)" + ) + + intf_c2_i5 = topo["routers"]["c2"]["links"]["i5"]["interface"] + input_dict = { + "c2": {"igmp": {"interfaces": {intf_c2_i5: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i5": topo["routers"]["i5"]["links"]["c2"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "l1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["l1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send traffic from C1 to all the groups ( 225.1.1.1 to 225.1.1.5)") + + input_src = {"i4": topo["routers"]["i4"]["links"]["c1"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i4 = topo["routers"]["i4"]["links"]["c1"]["ipv4"].split("/")[0] + + input_dict_starg = [ + { + "dut": "c2", + "src_address": "*", + "iif": topo["routers"]["c2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["c2"]["links"]["i5"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "c2", + "src_address": source_i4, + "iif": topo["routers"]["c2"]["links"]["f1"]["interface"], + "oil": topo["routers"]["c2"]["links"]["i5"]["interface"], + } + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Delete static routes on c2") + input_dict = { + "c2": { + "static_routes": [ + { + "network": ["1.0.4.11/32", "10.0.2.1/24", "10.0.1.2/24"], + "next_hop": "10.0.3.2", + "delete": True, + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify RP info unknown after removing static route from c2 ") + dut = "c2" + rp_address = topo["routers"]["l1"]["links"]["lo"]["ipv4"].split("/")[0] + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify mroute not present after Delete of static routes on c1") + + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Configure default routes on c2") + + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["ipv4"].split("/")[0] + + input_dict = { + "c2": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_f1_c2}]} + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("applying ip nht config on c2") + + raw_config = {"c2": {"raw_config": ["ip nht resolve-via-default"]}} + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify RP info is NOT unknown after removing static route from c2 ") + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify (s,g) populated after adding default route ") + + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify (*,g) populated after adding default route ") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_mroute_with_RP_default_route_all_nodes_p2(request): + """ + TC_50 Verify mroute when LHR,FHR,RP and transit routers reachable + using default routes + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Remove c1-c2 connected link to simulate topo " + "c1(LHR)---l1(RP)----r2---f1-----c2(FHR)" + ) + + intf_c1_c2 = topo["routers"]["c1"]["links"]["c2"]["interface"] + intf_c2_c1 = topo["routers"]["c2"]["links"]["c1"]["interface"] + shutdown_bringup_interface(tgen, "c1", intf_c1_c2, False) + shutdown_bringup_interface(tgen, "c2", intf_c2_c1, False) + + step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (225.1.1.1-5)" + ) + + intf_c1_i4 = topo["routers"]["c1"]["links"]["i4"]["interface"] + input_dict = { + "c1": {"igmp": {"interfaces": {intf_c1_i4: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i4": topo["routers"]["i4"]["links"]["c1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "l1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["l1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send traffic from C2 to all the groups ( 225.1.1.1 to 225.1.1.5)") + + input_src = {"i5": topo["routers"]["i5"]["links"]["c2"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i5 = topo["routers"]["i5"]["links"]["c2"]["ipv4"].split("/")[0] + + input_dict_starg = [ + { + "dut": "c1", + "src_address": "*", + "iif": topo["routers"]["c1"]["links"]["l1"]["interface"], + "oil": topo["routers"]["c1"]["links"]["i4"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "c1", + "src_address": source_i5, + "iif": topo["routers"]["c1"]["links"]["l1"]["interface"], + "oil": topo["routers"]["c1"]["links"]["i4"]["interface"], + } + ] + + step("Verify mroutes and iff upstream") + + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Delete static routes RP on all the nodes") + input_dict = { + "c2": { + "static_routes": [ + {"network": ["1.0.4.11/32"], "next_hop": "10.0.3.2", "delete": True} + ] + }, + "c1": { + "static_routes": [ + {"network": ["1.0.4.11/32"], "next_hop": "10.0.2.2", "delete": True} + ] + }, + "r2": { + "static_routes": [ + {"network": ["1.0.4.11/32"], "next_hop": "10.0.12.1", "delete": True} + ] + }, + "f1": { + "static_routes": [ + {"network": ["1.0.4.11/32"], "next_hop": "10.0.7.2", "delete": True} + ] + }, + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Verify RP info unknown after removing static route from c2 ") + dut = "c2" + rp_address = topo["routers"]["l1"]["links"]["lo"]["ipv4"].split("/")[0] + SOURCE = "Static" + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Configure default routes on all the nodes") + + intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["ipv4"].split("/")[0] + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["ipv4"].split("/")[0] + intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["ipv4"].split("/")[0] + intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["ipv4"].split("/")[0] + + input_dict = { + "c1": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_l1_c1}]}, + "c2": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_f1_c2}]}, + "r2": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_l1_r2}]}, + "f1": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_r2_f1}]}, + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("applying ip nht config on c2") + + raw_config = { + "c1": {"raw_config": ["ip nht resolve-via-default"]}, + "c2": {"raw_config": ["ip nht resolve-via-default"]}, + "r2": {"raw_config": ["ip nht resolve-via-default"]}, + "f1": {"raw_config": ["ip nht resolve-via-default"]}, + "l1": {"raw_config": ["ip nht resolve-via-default"]}, + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify RP info Not unknown after removing static route from c2 ") + dut = "c2" + step("Verify RP info is NOT unknown after removing static route from c2 ") + result = verify_pim_rp_info( + tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify (s,g) populated after adding default route ") + + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify (*,g) populated after adding default route ") + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_PIM_hello_tx_rx_p1(request): + """ + TC_54 Verify received and transmit hello stats + are getting cleared after PIM nbr reset + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + kill_iperf(tgen) + clear_ip_mroute(tgen) + reset_config_on_routers(tgen) + clear_ip_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Remove c1-c2 connected link to simulate topo " + "c1(LHR)---l1(RP)----r2---f1-----c2(FHR)" + ) + + intf_c1_c2 = topo["routers"]["c1"]["links"]["c2"]["interface"] + intf_c2_c1 = topo["routers"]["c2"]["links"]["c1"]["interface"] + shutdown_bringup_interface(tgen, "c1", intf_c1_c2, False) + shutdown_bringup_interface(tgen, "c2", intf_c2_c1, False) + + step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3") + step( + "Enable IGMP of FRR1 interface and send IGMP joins " + " from FRR1 node for group range (225.1.1.1-5)" + ) + + intf_c1_i4 = topo["routers"]["c1"]["links"]["i4"]["interface"] + input_dict = { + "c1": {"igmp": {"interfaces": {intf_c1_i4: {"igmp": {"version": "2"}}}}} + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_join = {"i4": topo["routers"]["i4"]["links"]["c1"]["interface"]} + + for recvr, recvr_intf in input_join.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, recvr, recvr_intf, GROUP_RANGE_1, join=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendIGMPJoin(tgen, recvr, IGMP_JOIN_RANGE_1, join_interval=1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "l1": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["l1"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send Mcast traffic from C2 to all the groups ( 225.1.1.1 to 225.1.1.5)") + + input_src = {"i5": topo["routers"]["i5"]["links"]["c2"]["interface"]} + + for src, src_intf in input_src.items(): + result = config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, src, src_intf, GROUP_RANGE_1, traffic=True + ) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = iperfSendTraffic(tgen, src, IGMP_JOIN_RANGE_1, 32, 2500) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i5 = topo["routers"]["i5"]["links"]["c2"]["ipv4"].split("/")[0] + + input_dict_starg = [ + { + "dut": "c1", + "src_address": "*", + "iif": topo["routers"]["c1"]["links"]["l1"]["interface"], + "oil": topo["routers"]["c1"]["links"]["i4"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "c1", + "src_address": source_i5, + "iif": topo["routers"]["c1"]["links"]["l1"]["interface"], + "oil": topo["routers"]["c1"]["links"]["i4"]["interface"], + } + ] + + step("(*,G) and (S,G) created on f1 and node verify using 'show ip mroute'") + for data in input_dict_sg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_starg: + result = verify_ip_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"] + intf_c1_l1 = topo["routers"]["c1"]["links"]["l1"]["interface"] + + step("verify before stats on C1") + state_dict = {"c1": {intf_c1_l1: ["helloTx", "helloRx"],}} + + c1_state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + c1_state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("Flap PIM nbr while doing interface c1-l1 interface shut from f1 side") + shutdown_bringup_interface(tgen, "c1", intf_c1_l1, False) + + step( + "After shut of local interface from c1 , verify rx/tx hello counters are cleared on c1 side" + "verify using 'show ip pim interface traffic'" + ) + shutdown_bringup_interface(tgen, "c1", intf_c1_l1, True) + + step("verify stats after on c1") + c1_state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + c1_state_after, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("verify stats not increamented on c1") + result = verify_state_incremented(c1_state_before, c1_state_after) + assert ( + result is not True + ), "Testcase{} : Failed Error: {}" "stats incremented".format(tc_name, result) + + step("verify before stats on l1") + l1_state_dict = {"l1": {intf_l1_c1: ["helloTx", "helloRx"],}} + + l1_state_before = verify_pim_interface_traffic(tgen, l1_state_dict) + assert isinstance( + l1_state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("Flap PIM nbr while doing interface r2-c1 shut from r2 side") + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False) + + step( + "After shut the interface from r2 side , verify r2 side rx and tx of hello" + "counters are resetted show ip pim interface traffic" + ) + shutdown_bringup_interface(tgen, "l1", intf_l1_c1, True) + + step("verify stats after on l1") + l1_state_after = verify_pim_interface_traffic(tgen, l1_state_dict) + assert isinstance( + l1_state_after, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("verify stats not increamented on l1") + result = verify_state_incremented(l1_state_before, l1_state_after) + assert ( + result is not True + ), "Testcase{} : Failed Error: {}" "stats incremented".format(tc_name, result) + + step("Reinit the dict") + c1_state_before = {} + l1_state_before = {} + c1_state_after = {} + l1_state_after = {} + + step("verify before stats on C1") + state_dict = {"c1": {intf_c1_l1: ["helloTx", "helloRx"],}} + + c1_state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + c1_state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("Flap c1-r2 pim nbr while changing ip address from c1 side") + c1_l1_ip_subnet = topo["routers"]["c1"]["links"]["l1"]["ipv4"] + + raw_config = { + "c1": { + "raw_config": [ + "interface {}".format(intf_c1_l1), + "no ip address {}".format(c1_l1_ip_subnet), + "ip address {}".format(NEW_ADDRESS_2_SUBNET), + ] + } + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify stats after on c1") + c1_state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + c1_state_after, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " + "Error: {}".format(tc_name, result) + + step("verify stats not increamented on c1") + result = verify_state_incremented(c1_state_before, c1_state_after) + assert ( + result is not True + ), "Testcase{} : Failed Error: {}" "stats incremented".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf_basic_functionality/ospf_chaos.json b/tests/topotests/ospf_basic_functionality/ospf_chaos.json new file mode 100644 index 0000000000..ed199f181b --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_chaos.json @@ -0,0 +1,166 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + }, + "redistribute": [{ + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "r1": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +} diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py new file mode 100644 index 0000000000..37b7528490 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py @@ -0,0 +1,576 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +from copy import deepcopy +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + step, + shutdown_bringup_interface, + topo_daemons, + verify_rib, + stop_router, start_router, + create_static_routes, + start_router_daemons, + kill_router_daemons +) + +from lib.ospf import ( + verify_ospf_neighbor, verify_ospf_rib, + create_router_ospf) + +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json +from ipaddress import IPv4Address + + + +# Global variables +topo = None + +NETWORK = { + "ipv4": ["11.0.20.1/32", "11.0.20.2/32", "11.0.20.3/32", "11.0.20.4/32", + "11.0.20.5/32"] +} +""" +Topology: + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + +TESTCASES = +1. Verify ospf functionality after restart ospfd. +2. Verify ospf functionality after restart FRR service. +3. Verify ospf functionality when staticd is restarted. + """ + +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_chaos.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +# ################################## +# Test cases start here. +# ################################## +def test_ospf_chaos_tc31_p1(request): + """Verify ospf functionality after restart ospfd.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + step( + "Create static routes(10.0.20.1/32) in R1 and redistribute " + "to OSPF using route map.") + + # Create Static routes + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK['ipv4'][0], + "no_of_ip": 5, + "next_hop": 'Null0', + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + ospf_red_r0 = { + "r0": { + "ospf": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify OSPF neighbors after base config is done.") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step("Verify that route is advertised to R1.") + dut = 'r1' + protocol = 'ospf' + nh = topo['routers']['r0']['links']['r1']['ipv4'].split('/')[0] + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Kill OSPFd daemon on R0.") + kill_router_daemons(tgen, "r0", ["ospfd"]) + + step("Verify OSPF neighbors are down after killing ospfd in R0") + dut = 'r0' + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, + expected=False) + assert ospf_covergence is not True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step("Verify that route advertised to R1 are deleted from RIB and FIB.") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Bring up OSPFd daemon on R0.") + start_router_daemons(tgen, "r0", ["ospfd"]) + + step("Verify OSPF neighbors are up after bringing back ospfd in R0") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step( + "All the neighbours are up and routes are installed before the" + " restart. Verify OSPF route table and ip route table.") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Kill OSPFd daemon on R1.") + kill_router_daemons(tgen, "r1", ["ospfd"]) + + step("Verify OSPF neighbors are down after killing ospfd in R1") + dut = 'r1' + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, + expected=False) + assert ospf_covergence is not True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step("Bring up OSPFd daemon on R1.") + start_router_daemons(tgen, "r1", ["ospfd"]) + + step("Verify OSPF neighbors are up after bringing back ospfd in R1") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step( + "All the neighbours are up and routes are installed before the" + " restart. Verify OSPF route table and ip route table.") + + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_chaos_tc32_p1(request): + """Verify ospf functionality after restart FRR service. """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + step( + "Create static routes(10.0.20.1/32) in R1 and redistribute " + "to OSPF using route map.") + + # Create Static routes + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK['ipv4'][0], + "no_of_ip": 5, + "next_hop": 'Null0', + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + ospf_red_r0 = { + "r0": { + "ospf": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify OSPF neighbors after base config is done.") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step("Verify that route is advertised to R1.") + dut = 'r1' + protocol = 'ospf' + + nh = topo['routers']['r0']['links']['r1']['ipv4'].split('/')[0] + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Restart frr on R0") + stop_router(tgen, 'r0') + start_router(tgen, 'r0') + + step("Verify OSPF neighbors are up after restarting R0") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step( + "All the neighbours are up and routes are installed before the" + " restart. Verify OSPF route table and ip route table.") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Restart frr on R1") + stop_router(tgen, 'r1') + start_router(tgen, 'r1') + + step("Verify OSPF neighbors are up after restarting R1") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step( + "All the neighbours are up and routes are installed before the" + " restart. Verify OSPF route table and ip route table.") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_chaos_tc34_p1(request): + """ + verify ospf functionality when staticd is restarted. + + Verify ospf functionalitywhen staticroutes are + redistributed & Staticd is restarted. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + step( + "Create static routes(10.0.20.1/32) in R1 and redistribute " + "to OSPF using route map.") + + # Create Static routes + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK['ipv4'][0], + "no_of_ip": 5, + "next_hop": 'Null0', + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + ospf_red_r0 = { + "r0": { + "ospf": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify OSPF neighbors after base config is done.") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step("Verify that route is advertised to R1.") + dut = 'r1' + protocol = 'ospf' + nh = topo['routers']['r0']['links']['r1']['ipv4'].split('/')[0] + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Kill staticd daemon on R0.") + kill_router_daemons(tgen, "r0", ["staticd"]) + + step("Verify that route advertised to R1 are deleted from RIB and FIB.") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Bring up staticd daemon on R0.") + start_router_daemons(tgen, "r0", ["staticd"]) + + step("Verify OSPF neighbors are up after bringing back ospfd in R0") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step( + "All the neighbours are up and routes are installed before the" + " restart. Verify OSPF route table and ip route table.") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Kill staticd daemon on R1.") + kill_router_daemons(tgen, "r1", ["staticd"]) + + step("Bring up staticd daemon on R1.") + start_router_daemons(tgen, "r1", ["staticd"]) + + step("Verify OSPF neighbors are up after bringing back ospfd in R1") + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step( + "All the neighbours are up and routes are installed before the" + " restart. Verify OSPF route table and ip route table.") + + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini index d1b18a57bb..0c45a09445 100644 --- a/tests/topotests/pytest.ini +++ b/tests/topotests/pytest.ini @@ -1,16 +1,29 @@ # Skip pytests example directory [pytest] norecursedirs = .git example-test example-topojson-test lib docker + +# Markers +# +# Please consult the documentation and discuss with TSC members before applying +# any changes to this list. markers = - babel: Tests that run against BABEL - bfd: Tests that run against BFDD - eigrp: Tests that run against EIGRPD - isis: Tests that run against ISISD - ldp: Tests that run against LDPD - ospf: Tests that run against OSPF( v2 and v3 ) - pbr: Tests that run against PBRD - pim: Tests that run against pim - rip: Tests that run against RIP, both v4 and v6 + babeld: Tests that run against BABELD + bfdd: Tests that run against BFDD + bgpd: Tests that run against BGPD + eigrpd: Tests that run against EIGRPD + isisd: Tests that run against ISISD + ldpd: Tests that run against LDPD + nhrpd: Tests that run against NHRPD + ospf6d: Tests that run against OSPF6D + ospfd: Tests that run against OSPFD + pathd: Tests that run against PATHD + pbrd: Tests that run against PBRD + pimd: Tests that run against PIMD + ripd: Tests that run against RIPD + ripngd: Tests that run against RIPNGD + sharpd: Tests that run against SHARPD + staticd: Tests that run against STATICD + vrrpd: Tests that run against VRRPD [topogen] # Default configuration values diff --git a/tests/topotests/simple-snmp-test/test_simple_snmp.py b/tests/topotests/simple-snmp-test/test_simple_snmp.py index 2b609ef14c..1e56252ea3 100755 --- a/tests/topotests/simple-snmp-test/test_simple_snmp.py +++ b/tests/topotests/simple-snmp-test/test_simple_snmp.py @@ -76,6 +76,11 @@ class TemplateTopo(Topo): def setup_module(mod): "Sets up the pytest environment" + + # skip tests is SNMP not installed + if not os.path.isfile("/usr/sbin/snmpd"): + error_msg = "SNMP not installed - skipping" + pytest.skip(error_msg) # This function initiates the topology build with Topogen... tgen = Topogen(TemplateTopo, mod.__name__) # ... and here it calls Mininet initialization functions. @@ -120,11 +125,13 @@ def test_r1_bgp_version(): "Wait for protocol convergence" tgen = get_topogen() - #tgen.mininet_cli() + # tgen.mininet_cli() r1 = tgen.net.get("r1") r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") assert r1_snmp.test_oid("bgpVersin", None) assert r1_snmp.test_oid("bgpVersion", "10") + assert r1_snmp.test_oid_walk("bgpVersion", ["10"]) + assert r1_snmp.test_oid_walk("bgpVersion", ["10"], ["0"]) if __name__ == "__main__": |
