diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-update-loc-rib-step1.json b/tests/topotests/bgp_bmp/bmp1/bmp-update-loc-rib-step1.json new file mode 100644 index 0000000000..27f1fe6e27 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-update-loc-rib-step1.json @@ -0,0 +1,34 @@ +{ + "loc-rib": { + "update": { + "172.31.0.15/32": { + "as_path": "65501 65502", + "bgp_nexthop": "192.168.0.2", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "is_filtered": false, + "origin": "IGP", + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib" + }, + "2001::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2001::1111/128", + "is_filtered": false, + "nxhp_ip": "192:168::2", + "origin": "IGP", + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-update-loc-rib-step2.json b/tests/topotests/bgp_bmp/bmp1/bmp-update-loc-rib-step2.json new file mode 100644 index 0000000000..0aa6bd4fea --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-update-loc-rib-step2.json @@ -0,0 +1,43 @@ +{ + "loc-rib": { + "update": { + "2001::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2001::1111/128", + "is_filtered": false, + "label": 105, + "nxhp_ip": "192:168::2", + "nxhp_rd1": "0:0", + "nxhp_rd2": "0:0", + "origin": "IGP", + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "rd": "555:2", + "safi": 128 + }, + "172.31.0.15/32": { + "afi": 1, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "is_filtered": false, + "label": 102, + "nxhp_ip": "192.168.0.2", + "nxhp_rd": "0:0", + "origin": "IGP", + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "rd": "444:2", + "safi": 128 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-update-post-policy-step1.json b/tests/topotests/bgp_bmp/bmp1/bmp-update-post-policy-step1.json new file mode 100644 index 0000000000..d30ef9fd32 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-update-post-policy-step1.json @@ -0,0 +1,36 @@ +{ + "post-policy": { + "update": { + "172.31.0.15/32": { + "as_path": "65501 65502", + "bgp_nexthop": "192.168.0.2", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "post-policy" + }, + "2001::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "nxhp_ip": "192:168::2", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "post-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-update-post-policy-step2.json b/tests/topotests/bgp_bmp/bmp1/bmp-update-post-policy-step2.json new file mode 100644 index 0000000000..9bf2d57769 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-update-post-policy-step2.json @@ -0,0 +1,45 @@ +{ + "post-policy": { + "update": { + "172.31.0.15/32": { + "afi": 1, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "label": 102, + "nxhp_ip": "192.168.0.2", + "nxhp_rd": "0:0", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "post-policy", + "rd": "444:2", + "safi": 128 + }, + "2001::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "label": 105, + "nxhp_ip": "192:168::2", + "nxhp_rd1": "0:0", + "nxhp_rd2": "0:0", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "post-policy", + "rd": "555:2", + "safi": 128 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-update-pre-policy-step1.json b/tests/topotests/bgp_bmp/bmp1/bmp-update-pre-policy-step1.json new file mode 100644 index 0000000000..fafe6f7c21 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-update-pre-policy-step1.json @@ -0,0 +1,36 @@ +{ + "pre-policy": { + "update": { + "172.31.0.15/32": { + "as_path": "65501 65502", + "bgp_nexthop": "192.168.0.2", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "pre-policy" + }, + "2001::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "nxhp_ip": "192:168::2", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "pre-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-update-pre-policy-step2.json b/tests/topotests/bgp_bmp/bmp1/bmp-update-pre-policy-step2.json new file mode 100644 index 0000000000..95acb5903a --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-update-pre-policy-step2.json @@ -0,0 +1,45 @@ +{ + "pre-policy": { + "update": { + "172.31.0.15/32": { + "afi": 1, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "label": 102, + "nxhp_ip": "192.168.0.2", + "nxhp_rd": "0:0", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "pre-policy", + "rd": "444:2", + "safi": 128 + }, + "2001::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "label": 105, + "nxhp_ip": "192:168::2", + "nxhp_rd1": "0:0", + "nxhp_rd2": "0:0", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "pre-policy", + "rd": "555:2", + "safi": 128 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-loc-rib-step1.json b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-loc-rib-step1.json new file mode 100644 index 0000000000..3891fbb15e --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-loc-rib-step1.json @@ -0,0 +1,28 @@ +{ + "loc-rib": { + "withdraw": { + "172.31.0.15/32": { + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "is_filtered": false, + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib" + }, + "2001::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2001::1111/128", + "is_filtered": false, + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-loc-rib-step2.json b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-loc-rib-step2.json new file mode 100644 index 0000000000..1e5040ba60 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-loc-rib-step2.json @@ -0,0 +1,34 @@ +{ + "loc-rib": { + "withdraw": { + "172.31.0.15/32": { + "afi": 1, + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "is_filtered": false, + "label": 0, + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "rd": "444:2", + "safi": 128 + }, + "2001::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2001::1111/128", + "is_filtered": false, + "label": 0, + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "rd": "555:2", + "safi": 128 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-post-policy-step1.json b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-post-policy-step1.json new file mode 100644 index 0000000000..3224b525e2 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-post-policy-step1.json @@ -0,0 +1,30 @@ +{ + "post-policy": { + "withdraw": { + "172.31.0.15/32": { + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "post-policy" + }, + "2001::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "post-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-post-policy-step2.json b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-post-policy-step2.json new file mode 100644 index 0000000000..9eb221d4d0 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-post-policy-step2.json @@ -0,0 +1,36 @@ +{ + "post-policy": { + "withdraw": { + "2001::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "label": 0, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "post-policy", + "rd": "555:2", + "safi": 128 + }, + "172.31.0.15/32": { + "afi": 1, + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "label": 0, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "post-policy", + "rd": "444:2", + "safi": 128 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-pre-policy-step1.json b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-pre-policy-step1.json new file mode 100644 index 0000000000..8add68c022 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-pre-policy-step1.json @@ -0,0 +1,30 @@ +{ + "pre-policy": { + "withdraw": { + "172.31.0.15/32": { + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "pre-policy" + }, + "2001::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "pre-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-pre-policy-step2.json b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-pre-policy-step2.json new file mode 100644 index 0000000000..eea7501b22 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-pre-policy-step2.json @@ -0,0 +1,36 @@ +{ + "pre-policy": { + "withdraw": { + "2001::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "label": 0, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "pre-policy", + "rd": "555:2", + "safi": 128 + }, + "172.31.0.15/32": { + "afi": 1, + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "label": 0, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "pre-policy", + "rd": "444:2", + "safi": 128 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/r1/bgpd.conf b/tests/topotests/bgp_bmp/r1/bgpd.conf index 24505de4a8..485c217096 100644 --- a/tests/topotests/bgp_bmp/r1/bgpd.conf +++ b/tests/topotests/bgp_bmp/r1/bgpd.conf @@ -7,6 +7,18 @@ router bgp 65501 ! bmp targets bmp1 bmp connect 192.0.2.10 port 1789 min-retry 100 max-retry 10000 + bmp monitor ipv4 unicast pre-policy + bmp monitor ipv6 unicast pre-policy + bmp monitor ipv4 vpn pre-policy + bmp monitor ipv6 vpn pre-policy + bmp monitor ipv4 unicast post-policy + bmp monitor ipv6 unicast post-policy + bmp monitor ipv4 vpn post-policy + bmp monitor ipv6 vpn post-policy + bmp monitor ipv4 unicast loc-rib + bmp monitor ipv6 unicast loc-rib + bmp monitor ipv4 vpn loc-rib + bmp monitor ipv6 vpn loc-rib exit ! address-family ipv4 vpn @@ -28,7 +40,7 @@ router bgp 65501 neighbor 192:168::2 soft-reconfiguration inbound exit-address-family ! -router bgp 65502 vrf vrf1 +router bgp 65501 vrf vrf1 bgp router_id 192.168.0.1 bgp log-neighbor-changes address-family ipv4 unicast diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-update-step1.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-update-step1.json new file mode 100644 index 0000000000..038c87ca9d --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-update-step1.json @@ -0,0 +1,21 @@ +{ + "routes": { + "172.31.0.15/32": [ + { + "bestpath": true, + "pathFrom": "external", + "path": "65502", + "origin": "IGP", + "nexthops": [ + { + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} + diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-update-step2.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-update-step2.json new file mode 100644 index 0000000000..275f7f30e9 --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-update-step2.json @@ -0,0 +1,25 @@ +{ + "routes": { + "routeDistinguishers": { + "444:2": { + "172.31.0.15/32": [ + { + "bestpath": true, + "pathFrom": "external", + "path": "65502", + "origin": "IGP", + "nexthops": [ + { + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + } + } +} + diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-withdraw-step1.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-withdraw-step1.json new file mode 100644 index 0000000000..22a6c605c1 --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-withdraw-step1.json @@ -0,0 +1,6 @@ +{ + "routes": { + "2001::1111/128": null + } +} + diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-withdraw-step2.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-withdraw-step2.json new file mode 100644 index 0000000000..0f930ee71f --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-withdraw-step2.json @@ -0,0 +1,10 @@ +{ + "routes": { + "routeDistinguishers": { + "444:2": { + "2001::1111/128": null + } + } + } +} + diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-update-step1.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-update-step1.json new file mode 100644 index 0000000000..ed5cea68ce --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-update-step1.json @@ -0,0 +1,27 @@ +{ + "routes": { + "2001::1111/128": [ + { + "bestpath": true, + "pathFrom": "external", + "path": "65502", + "origin": "IGP", + "nexthops": [ + { + "ip": "192:168::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ] + } +} + diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-update-step2.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-update-step2.json new file mode 100644 index 0000000000..c4c0c16284 --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-update-step2.json @@ -0,0 +1,25 @@ +{ + "routes": { + "routeDistinguishers": { + "555:2": { + "2001::1111/128": [ + { + "bestpath": true, + "pathFrom": "external", + "path": "65502", + "origin": "IGP", + "nexthops": [ + { + "ip": "192:168::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} + diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-withdraw-step1.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-withdraw-step1.json new file mode 100644 index 0000000000..22a6c605c1 --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-withdraw-step1.json @@ -0,0 +1,6 @@ +{ + "routes": { + "2001::1111/128": null + } +} + diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-withdraw-step2.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-withdraw-step2.json new file mode 100644 index 0000000000..7b652afca6 --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-withdraw-step2.json @@ -0,0 +1,10 @@ +{ + "routes": { + "routeDistinguishers": { + "555:2": { + "2001::1111/128": null + } + } + } +} + diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp.py b/tests/topotests/bgp_bmp/test_bgp_bmp.py index 1cf6a0e7f8..658ad2b99a 100644 --- a/tests/topotests/bgp_bmp/test_bgp_bmp.py +++ b/tests/topotests/bgp_bmp/test_bgp_bmp.py @@ -50,6 +50,9 @@ PRE_POLICY = "pre-policy" POST_POLICY = "post-policy" LOC_RIB = "loc-rib" +UPDATE_EXPECTED_JSON = False +DEBUG_PCAP = False + def build_topo(tgen): tgen.add_router("r1") @@ -67,6 +70,12 @@ def setup_module(mod): tgen = Topogen(build_topo, mod.__name__) tgen.start_topology() + if DEBUG_PCAP: + tgen.gears["r1"].run("rm /tmp/bmp.pcap") + tgen.gears["r1"].run( + "tcpdump -nni r1-eth0 -s 0 -w /tmp/bmp.pcap &", stdout=None + ) + for rname, router in tgen.routers().items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) @@ -122,42 +131,181 @@ def get_bmp_messages(): return messages -def check_for_prefixes(expected_prefixes, bmp_log_type, policy, labels=None): +def update_seq(): + global SEQ + + messages = get_bmp_messages() + + if len(messages): + SEQ = messages[-1]["seq"] + + +def update_expected_files(bmp_actual, expected_prefixes, bmp_log_type, policy, step): + tgen = get_topogen() + + with open(f"/tmp/bmp-{bmp_log_type}-{policy}-step{step}.json", "w") as json_file: + json.dump(bmp_actual, json_file, indent=4) + + if step == 2: # vpn + rd = "444:2" + out = tgen.gears["r1"].vtysh_cmd("show bgp ipv4 vpn json", isjson=True) + filtered_out = { + "routes": { + "routeDistinguishers": { + rd: { + prefix: route_info + for prefix, route_info in out["routes"] + .get("routeDistinguishers", {}) + .get(rd, {}) + .items() + if prefix in expected_prefixes + } + } + } + } + if bmp_log_type == "withdraw": + for pfx in expected_prefixes: + if "::" in pfx: + continue + filtered_out["routes"]["routeDistinguishers"][rd][pfx] = None + + # ls /tmp/show*json | while read file; do egrep -v 'prefix|network|metric|ocPrf|version|weight|peerId|vrf|Version|valid|Reason|fe80' $file >$(basename $file); echo >> $(basename $file); done + with open( + f"/tmp/show-bgp-ipv4-{bmp_log_type}-step{step}.json", "w" + ) as json_file: + json.dump(filtered_out, json_file, indent=4) + + rd = "555:2" + out = tgen.gears["r1"].vtysh_cmd("show bgp ipv6 vpn json", isjson=True) + filtered_out = { + "routes": { + "routeDistinguishers": { + rd: { + prefix: route_info + for prefix, route_info in out["routes"] + .get("routeDistinguishers", {}) + .get(rd, {}) + .items() + if prefix in expected_prefixes + } + } + } + } + if bmp_log_type == "withdraw": + for pfx in expected_prefixes: + if "::" not in pfx: + continue + filtered_out["routes"]["routeDistinguishers"][rd][pfx] = None + with open( + f"/tmp/show-bgp-ipv6-{bmp_log_type}-step{step}.json", "w" + ) as json_file: + json.dump(filtered_out, json_file, indent=4) + + return + + out = tgen.gears["r1"].vtysh_cmd("show bgp ipv4 json", isjson=True) + filtered_out = { + "routes": { + prefix: route_info + for prefix, route_info in out["routes"].items() + if prefix in expected_prefixes + } + } + if bmp_log_type == "withdraw": + for pfx in expected_prefixes: + if "::" in pfx: + continue + filtered_out["routes"][pfx] = None + + # ls /tmp/show*json | while read file; do egrep -v 'prefix|network|metric|ocPrf|version|weight|peerId|vrf|Version|valid|Reason|fe80' $file >$(basename $file); echo >> $(basename $file); done + with open(f"/tmp/show-bgp-ipv4-{bmp_log_type}-step{step}.json", "w") as json_file: + json.dump(filtered_out, json_file, indent=4) + + out = tgen.gears["r1"].vtysh_cmd("show bgp ipv6 json", isjson=True) + filtered_out = { + "routes": { + prefix: route_info + for prefix, route_info in out["routes"].items() + if prefix in expected_prefixes + } + } + if bmp_log_type == "withdraw": + for pfx in expected_prefixes: + if "::" not in pfx: + continue + filtered_out["routes"][pfx] = None + with open(f"/tmp/show-bgp-ipv6-{bmp_log_type}-step{step}.json", "w") as json_file: + json.dump(filtered_out, json_file, indent=4) + + +def check_for_prefixes(expected_prefixes, bmp_log_type, policy, step): """ Check for the presence of the given prefixes in the BMP server logs with the given message type and the set policy. + """ global SEQ + # we care only about the new messages messages = [ m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ ] - # get the list of pairs (prefix, policy, seq) for the given message type - prefixes = [ - m["ip_prefix"] - for m in messages - if "ip_prefix" in m.keys() - and "bmp_log_type" in m.keys() - and m["bmp_log_type"] == bmp_log_type - and m["policy"] == policy - and ( - labels is None - or ( - m["ip_prefix"] in labels.keys() and m["label"] == labels[m["ip_prefix"]] - ) - ) - ] + # create empty initial files + # for step in $(seq 2); do + # for i in "update" "withdraw"; do + # for j in "pre-policy" "post-policy" "loc-rib"; do + # echo '{"null": {}}'> bmp-$i-$j-step$step.json + # done + # done + # done - # check for prefixes - for ep in expected_prefixes: - if ep not in prefixes: - msg = "The prefix {} is not present in the {} log messages." - logger.debug(msg.format(ep, bmp_log_type)) - return False + ref_file = f"{CWD}/bmp1/bmp-{bmp_log_type}-{policy}-step{step}.json" + expected = json.loads(open(ref_file).read()) - SEQ = messages[-1]["seq"] - return True + # Build actual json from logs + actual = {} + for m in messages: + if ( + "bmp_log_type" in m.keys() + and "ip_prefix" in m.keys() + and m["ip_prefix"] in expected_prefixes + and m["bmp_log_type"] == bmp_log_type + and m["policy"] == policy + ): + policy_dict = actual.setdefault(m["policy"], {}) + bmp_log_type_dict = policy_dict.setdefault(m["bmp_log_type"], {}) + + # Add or update the ip_prefix dictionary with filtered key-value pairs + bmp_log_type_dict[m["ip_prefix"]] = { + k: v + for k, v in sorted(m.items()) + # filter out variable keys + if k not in ["timestamp", "seq", "nxhp_link-local"] + and ( + # When policy is loc-rib, the peer-distinguisher is 0:0 + # for the default VRF or the RD if any or the 0:. + # 0: is used to distinguished. RFC7854 says: "If the + # peer is a "Local Instance Peer", it is set to a unique, + # locally defined value." The value is not tested because it + # is variable. + k != "peer_distinguisher" + or policy != LOC_RIB + or v == "0:0" + or not v.startswith("0:") + ) + } + + # build expected JSON files + if ( + UPDATE_EXPECTED_JSON + and actual + and set(actual.get(policy, {}).get(bmp_log_type, {}).keys()) + == set(expected_prefixes) + ): + update_expected_files(actual, expected_prefixes, bmp_log_type, policy, step) + + return topotest.json_cmp(actual, expected, exact=True) def check_for_peer_message(expected_peers, bmp_log_type): @@ -188,22 +336,6 @@ def check_for_peer_message(expected_peers, bmp_log_type): return True -def set_bmp_policy(tgen, node, asn, target, safi, policy, vrf=None): - """ - Configure the bmp policy. - """ - vrf = " vrf {}" if vrf else "" - cmd = [ - "con t\n", - "router bgp {}{}\n".format(asn, vrf), - "bmp targets {}\n".format(target), - "bmp monitor ipv4 {} {}\n".format(safi, policy), - "bmp monitor ipv6 {} {}\n".format(safi, policy), - "end\n", - ] - tgen.gears[node].vtysh_cmd("".join(cmd)) - - def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True): """ Configure the bgp prefixes. @@ -223,67 +355,49 @@ def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True): tgen.gears[node].vtysh_cmd("".join(cmd)) -def unicast_prefixes(policy): +def _test_prefixes(policy, vrf=None, step=0): """ Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes. Check if the previous actions are logged in the BMP server with the right message type and the right policy. """ tgen = get_topogen() - set_bmp_policy(tgen, "r1", 65501, "bmp1", "unicast", policy) - prefixes = ["172.31.0.15/32", "2111::1111/128"] - # add prefixes - configure_prefixes(tgen, "r2", 65502, "unicast", prefixes) + safi = "vpn" if vrf else "unicast" - logger.info("checking for updated prefixes") - # check - test_func = partial(check_for_prefixes, prefixes, "update", policy) - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) - assert success, "Checking the updated prefixes has been failed !." + prefixes = ["172.31.0.15/32", "2001::1111/128"] - # withdraw prefixes - configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, update=False) - logger.info("checking for withdrawed prefxies") - # check - test_func = partial(check_for_prefixes, prefixes, "withdraw", policy) - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) - assert success, "Checking the withdrawed prefixes has been failed !." + for type in ("update", "withdraw"): + update_seq() + configure_prefixes( + tgen, "r2", 65502, "unicast", prefixes, vrf=vrf, update=(type == "update") + ) -def vpn_prefixes(policy): - """ - Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes. - Check if the previous actions are logged in the BMP server with the right - message type and the right policy. - """ - tgen = get_topogen() - set_bmp_policy(tgen, "r1", 65501, "bmp1", "vpn", policy) + logger.info(f"checking for prefixes {type}") - prefixes = ["172.31.10.1/32", "2001::2222/128"] + for ipver in [4, 6]: + if UPDATE_EXPECTED_JSON: + continue + ref_file = "{}/r1/show-bgp-ipv{}-{}-step{}.json".format( + CWD, ipver, type, step + ) + expected = json.loads(open(ref_file).read()) - # "label vpn export" value in r2/bgpd.conf - labels = { - "172.31.10.1/32": 102, - "2001::2222/128": 105, - } + test_func = partial( + topotest.router_json_cmp, + tgen.gears["r1"], + f"show bgp ipv{ipver} {safi} json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = f"r1: BGP IPv{ipver} convergence failed" + assert res is None, assertmsg - # add prefixes - configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, vrf="vrf1") - - logger.info("checking for updated prefixes") - # check - test_func = partial(check_for_prefixes, prefixes, "update", policy, labels=labels) - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) - assert success, "Checking the updated prefixes has been failed !." - - # withdraw prefixes - configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, vrf="vrf1", update=False) - logger.info("checking for withdrawed prefixes") - # check - test_func = partial(check_for_prefixes, prefixes, "withdraw", policy) - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) - assert success, "Checking the withdrawed prefixes has been failed !." + # check + test_func = partial(check_for_prefixes, prefixes, type, policy, step) + success, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert success, "Checking the updated prefixes has failed ! %s" % res def test_bmp_server_logging(): @@ -300,7 +414,7 @@ def test_bmp_server_logging(): return False return True - success, _ = topotest.run_and_expect(check_for_log_file, True, wait=0.5) + success, _ = topotest.run_and_expect(check_for_log_file, True, count=30, wait=1) assert success, "The BMP server is not logging" @@ -314,7 +428,7 @@ def test_peer_up(): logger.info("checking for BMP peers up messages") test_func = partial(check_for_peer_message, peers, "peer up") - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) assert success, "Checking the updated prefixes has been failed !." @@ -323,21 +437,21 @@ def test_bmp_bgp_unicast(): Add/withdraw bgp unicast prefixes and check the bmp logs. """ logger.info("*** Unicast prefixes pre-policy logging ***") - unicast_prefixes(PRE_POLICY) + _test_prefixes(PRE_POLICY, step=1) logger.info("*** Unicast prefixes post-policy logging ***") - unicast_prefixes(POST_POLICY) + _test_prefixes(POST_POLICY, step=1) logger.info("*** Unicast prefixes loc-rib logging ***") - unicast_prefixes(LOC_RIB) + _test_prefixes(LOC_RIB, step=1) def test_bmp_bgp_vpn(): # check for the prefixes in the BMP server logging file logger.info("***** VPN prefixes pre-policy logging *****") - vpn_prefixes(PRE_POLICY) + _test_prefixes(PRE_POLICY, vrf="vrf1", step=2) logger.info("***** VPN prefixes post-policy logging *****") - vpn_prefixes(POST_POLICY) + _test_prefixes(POST_POLICY, vrf="vrf1", step=2) logger.info("***** VPN prefixes loc-rib logging *****") - vpn_prefixes(LOC_RIB) + _test_prefixes(LOC_RIB, vrf="vrf1", step=2) def test_peer_down(): @@ -353,7 +467,7 @@ def test_peer_down(): logger.info("checking for BMP peers down messages") test_func = partial(check_for_peer_message, peers, "peer down") - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) assert success, "Checking the updated prefixes has been failed !." diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-loc-rib-step1.json b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-loc-rib-step1.json new file mode 100644 index 0000000000..ba31bf1d5d --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-loc-rib-step1.json @@ -0,0 +1,32 @@ +{ + "loc-rib": { + "update": { + "172.31.0.15/32": { + "as_path": "65501 65502", + "bgp_nexthop": "192.168.0.2", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "is_filtered": false, + "origin": "IGP", + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_type": "loc-rib instance", + "policy": "loc-rib" + }, + "2111::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2111::1111/128", + "is_filtered": false, + "nxhp_ip": "192:168::2", + "origin": "IGP", + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-post-policy-step1.json b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-post-policy-step1.json new file mode 100644 index 0000000000..d5d9d65182 --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-post-policy-step1.json @@ -0,0 +1,36 @@ +{ + "post-policy": { + "update": { + "172.31.0.15/32": { + "as_path": "65501 65502", + "bgp_nexthop": "192.168.0.2", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "post-policy" + }, + "2111::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2111::1111/128", + "ipv6": true, + "nxhp_ip": "192:168::2", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "post-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-pre-policy-step1.json b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-pre-policy-step1.json new file mode 100644 index 0000000000..e11badc040 --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-pre-policy-step1.json @@ -0,0 +1,36 @@ +{ + "pre-policy": { + "update": { + "172.31.0.15/32": { + "as_path": "65501 65502", + "bgp_nexthop": "192.168.0.2", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "pre-policy" + }, + "2111::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2111::1111/128", + "ipv6": true, + "nxhp_ip": "192:168::2", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "pre-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-loc-rib-step1.json b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-loc-rib-step1.json new file mode 100644 index 0000000000..37ddc09ff8 --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-loc-rib-step1.json @@ -0,0 +1,26 @@ +{ + "loc-rib": { + "withdraw": { + "172.31.0.15/32": { + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "is_filtered": false, + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_type": "loc-rib instance", + "policy": "loc-rib" + }, + "2111::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2111::1111/128", + "is_filtered": false, + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-post-policy-step1.json b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-post-policy-step1.json new file mode 100644 index 0000000000..de84307a4e --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-post-policy-step1.json @@ -0,0 +1,30 @@ +{ + "post-policy": { + "withdraw": { + "172.31.0.15/32": { + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "post-policy" + }, + "2111::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2111::1111/128", + "ipv6": true, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "post-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-pre-policy-step1.json b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-pre-policy-step1.json new file mode 100644 index 0000000000..1c34498b7a --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-pre-policy-step1.json @@ -0,0 +1,30 @@ +{ + "pre-policy": { + "withdraw": { + "172.31.0.15/32": { + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "pre-policy" + }, + "2111::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2111::1111/128", + "ipv6": true, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "pre-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp_vrf/r1/bgpd.conf b/tests/topotests/bgp_bmp_vrf/r1/bgpd.conf index 994cdbf68e..961e20498b 100644 --- a/tests/topotests/bgp_bmp_vrf/r1/bgpd.conf +++ b/tests/topotests/bgp_bmp_vrf/r1/bgpd.conf @@ -7,6 +7,12 @@ router bgp 65501 vrf vrf1 ! bmp targets bmp1 bmp connect 192.0.2.10 port 1789 min-retry 100 max-retry 10000 + bmp monitor ipv4 unicast pre-policy + bmp monitor ipv6 unicast pre-policy + bmp monitor ipv4 unicast post-policy + bmp monitor ipv6 unicast post-policy + bmp monitor ipv4 unicast loc-rib + bmp monitor ipv6 unicast loc-rib exit ! diff --git a/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-update-step1.json b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-update-step1.json new file mode 100644 index 0000000000..038c87ca9d --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-update-step1.json @@ -0,0 +1,21 @@ +{ + "routes": { + "172.31.0.15/32": [ + { + "bestpath": true, + "pathFrom": "external", + "path": "65502", + "origin": "IGP", + "nexthops": [ + { + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} + diff --git a/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-withdraw-step1.json b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-withdraw-step1.json new file mode 100644 index 0000000000..6a77813776 --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-withdraw-step1.json @@ -0,0 +1,6 @@ +{ + "routes": { + "172.31.0.15/32": null + } +} + diff --git a/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-update-step1.json b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-update-step1.json new file mode 100644 index 0000000000..db34220149 --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-update-step1.json @@ -0,0 +1,27 @@ +{ + "routes": { + "2111::1111/128": [ + { + "bestpath": true, + "pathFrom": "external", + "path": "65502", + "origin": "IGP", + "nexthops": [ + { + "ip": "192:168::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ] + } +} + diff --git a/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-withdraw-step1.json b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-withdraw-step1.json new file mode 100644 index 0000000000..93f4a75e8c --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-withdraw-step1.json @@ -0,0 +1,6 @@ +{ + "routes": { + "2111::1111/128": null + } +} + diff --git a/tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py b/tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py index b683920d2e..d31328bdb6 100644 --- a/tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py +++ b/tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py @@ -51,6 +51,9 @@ PRE_POLICY = "pre-policy" POST_POLICY = "post-policy" LOC_RIB = "loc-rib" +UPDATE_EXPECTED_JSON = False +DEBUG_PCAP = False + def build_topo(tgen): tgen.add_router("r1") @@ -76,6 +79,12 @@ ip link set r1-eth1 master vrf1 """ ) + if DEBUG_PCAP: + tgen.gears["r1"].run("rm /tmp/bmp_vrf.pcap") + tgen.gears["r1"].run( + "tcpdump -nni r1-eth0 -s 0 -w /tmp/bmp_vrf.pcap &", stdout=None + ) + for rname, router in tgen.routers().items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) @@ -131,42 +140,125 @@ def get_bmp_messages(): return messages -def check_for_prefixes(expected_prefixes, bmp_log_type, policy, labels=None): +def update_seq(): + global SEQ + + messages = get_bmp_messages() + + if len(messages): + SEQ = messages[-1]["seq"] + + +def update_expected_files(bmp_actual, expected_prefixes, bmp_log_type, policy, step): + tgen = get_topogen() + + with open(f"/tmp/bmp-{bmp_log_type}-{policy}-step{step}.json", "w") as json_file: + json.dump(bmp_actual, json_file, indent=4) + + out = tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv4 json", isjson=True) + filtered_out = { + "routes": { + prefix: route_info + for prefix, route_info in out["routes"].items() + if prefix in expected_prefixes + } + } + if bmp_log_type == "withdraw": + for pfx in expected_prefixes: + if "::" in pfx: + continue + filtered_out["routes"][pfx] = None + + # ls /tmp/show*json | while read file; do egrep -v 'prefix|network|metric|ocPrf|version|weight|peerId|vrf|Version|valid|Reason|fe80' $file >$(basename $file); echo >> $(basename $file); done + with open(f"/tmp/show-bgp-ipv4-{bmp_log_type}-step{step}.json", "w") as json_file: + json.dump(filtered_out, json_file, indent=4) + + out = tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv6 json", isjson=True) + filtered_out = { + "routes": { + prefix: route_info + for prefix, route_info in out["routes"].items() + if prefix in expected_prefixes + } + } + if bmp_log_type == "withdraw": + for pfx in expected_prefixes: + if "::" not in pfx: + continue + filtered_out["routes"][pfx] = None + + with open(f"/tmp/show-bgp-ipv6-{bmp_log_type}-step{step}.json", "w") as json_file: + json.dump(filtered_out, json_file, indent=4) + + +def check_for_prefixes(expected_prefixes, bmp_log_type, policy, step): """ Check for the presence of the given prefixes in the BMP server logs with the given message type and the set policy. + """ global SEQ + # we care only about the new messages messages = [ m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ ] - # get the list of pairs (prefix, policy, seq) for the given message type - prefixes = [ - m["ip_prefix"] - for m in messages - if "ip_prefix" in m.keys() - and "bmp_log_type" in m.keys() - and m["bmp_log_type"] == bmp_log_type - and m["policy"] == policy - and ( - labels is None - or ( - m["ip_prefix"] in labels.keys() and m["label"] == labels[m["ip_prefix"]] - ) - ) - ] + # create empty initial files + # for step in $(seq 1); do + # for i in "update" "withdraw"; do + # for j in "pre-policy" "post-policy" "loc-rib"; do + # echo '{"null": {}}'> bmp-$i-$j-step$step.json + # done + # done + # done - # check for prefixes - for ep in expected_prefixes: - if ep not in prefixes: - msg = "The prefix {} is not present in the {} log messages." - logger.debug(msg.format(ep, bmp_log_type)) - return False + ref_file = f"{CWD}/bmp1/bmp-{bmp_log_type}-{policy}-step{step}.json" + expected = json.loads(open(ref_file).read()) - SEQ = messages[-1]["seq"] - return True + # Build actual json from logs + actual = {} + for m in messages: + if ( + "bmp_log_type" in m.keys() + and "ip_prefix" in m.keys() + and m["ip_prefix"] in expected_prefixes + and m["bmp_log_type"] == bmp_log_type + and m["policy"] == policy + ): + policy_dict = actual.setdefault(m["policy"], {}) + bmp_log_type_dict = policy_dict.setdefault(m["bmp_log_type"], {}) + + # Add or update the ip_prefix dictionary with filtered key-value pairs + bmp_log_type_dict[m["ip_prefix"]] = { + k: v + for k, v in sorted(m.items()) + # filter out variable keys + if k not in ["timestamp", "seq", "nxhp_link-local"] + and ( + # When policy is loc-rib, the peer-distinguisher is 0:0 + # for the default VRF or the RD if any or the 0:. + # 0: is used to distinguished. RFC7854 says: "If the + # peer is a "Local Instance Peer", it is set to a unique, + # locally defined value." The value is not tested because it + # is variable. + k != "peer_distinguisher" + or policy != LOC_RIB + or v == "0:0" + or not v.startswith("0:") + ) + } + + # build expected JSON files + if ( + UPDATE_EXPECTED_JSON + and actual + and set(actual.get(policy, {}).get(bmp_log_type, {}).keys()) + == set(expected_prefixes) + ): + update_expected_files(actual, expected_prefixes, bmp_log_type, policy, step) + + return topotest.json_cmp(actual, expected, exact=True) def check_for_peer_message(expected_peers, bmp_log_type): @@ -197,22 +289,6 @@ def check_for_peer_message(expected_peers, bmp_log_type): return True -def set_bmp_policy(tgen, node, asn, target, safi, policy, vrf=None): - """ - Configure the bmp policy. - """ - vrf = " vrf {}".format(vrf) if vrf else "" - cmd = [ - "con t\n", - "router bgp {}{}\n".format(asn, vrf), - "bmp targets {}\n".format(target), - "bmp monitor ipv4 {} {}\n".format(safi, policy), - "bmp monitor ipv6 {} {}\n".format(safi, policy), - "end\n", - ] - tgen.gears[node].vtysh_cmd("".join(cmd)) - - def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True): """ Configure the bgp prefixes. @@ -232,32 +308,48 @@ def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True): tgen.gears[node].vtysh_cmd("".join(cmd)) -def unicast_prefixes(policy): +def _test_prefixes(policy, step=1): """ Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes. Check if the previous actions are logged in the BMP server with the right message type and the right policy. """ tgen = get_topogen() - set_bmp_policy(tgen, "r1", 65501, "bmp1", "unicast", policy, vrf="vrf1") prefixes = ["172.31.0.15/32", "2111::1111/128"] - # add prefixes - configure_prefixes(tgen, "r2", 65502, "unicast", prefixes) - logger.info("checking for updated prefixes") - # check - test_func = partial(check_for_prefixes, prefixes, "update", policy) - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) - assert success, "Checking the updated prefixes has been failed !." + for type in ("update", "withdraw"): + update_seq() - # withdraw prefixes - configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, update=False) - logger.info("checking for withdrawed prefxies") - # check - test_func = partial(check_for_prefixes, prefixes, "withdraw", policy) - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) - assert success, "Checking the withdrawed prefixes has been failed !." + # add prefixes + configure_prefixes( + tgen, "r2", 65502, "unicast", prefixes, update=(type == "update") + ) + + logger.info(f"checking for prefixes {type}") + + for ipver in [4, 6]: + if UPDATE_EXPECTED_JSON: + continue + ref_file = "{}/r1/show-bgp-ipv{}-{}-step{}.json".format( + CWD, ipver, type, step + ) + expected = json.loads(open(ref_file).read()) + + test_func = partial( + topotest.router_json_cmp, + tgen.gears["r1"], + f"show bgp vrf vrf1 ipv{ipver} json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = f"r1: BGP IPv{ipver} convergence failed" + assert res is None, assertmsg + + # check + test_func = partial(check_for_prefixes, prefixes, type, policy, step) + success, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert success, "Checking the updated prefixes has been failed ! %s" % res def test_bmp_server_logging(): @@ -274,7 +366,7 @@ def test_bmp_server_logging(): return False return True - success, _ = topotest.run_and_expect(check_for_log_file, True, wait=0.5) + success, _ = topotest.run_and_expect(check_for_log_file, True, count=30, wait=1) assert success, "The BMP server is not logging" @@ -288,7 +380,7 @@ def test_peer_up(): logger.info("checking for BMP peers up messages") test_func = partial(check_for_peer_message, peers, "peer up") - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) assert success, "Checking the updated prefixes has been failed !." @@ -297,11 +389,11 @@ def test_bmp_bgp_unicast(): Add/withdraw bgp unicast prefixes and check the bmp logs. """ logger.info("*** Unicast prefixes pre-policy logging ***") - unicast_prefixes(PRE_POLICY) + _test_prefixes(PRE_POLICY) logger.info("*** Unicast prefixes post-policy logging ***") - unicast_prefixes(POST_POLICY) + _test_prefixes(POST_POLICY) logger.info("*** Unicast prefixes loc-rib logging ***") - unicast_prefixes(LOC_RIB) + _test_prefixes(LOC_RIB) def test_peer_down(): @@ -317,7 +409,7 @@ def test_peer_down(): logger.info("checking for BMP peers down messages") test_func = partial(check_for_peer_message, peers, "peer down") - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) assert success, "Checking the updated prefixes has been failed !."