diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 66b2f09a11d..6ab972f2897 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -63,6 +63,8 @@ jobs: sudo rm -rf /usr/share/dotnet sudo rm -rf /usr/local/lib/android sudo rm -rf /opt/hostedtoolcache/CodeQL + sudo mv "${HOME}/.cache" /mnt/cache + ln -s /mnt/cache "${HOME}/.cache" - name: Checkout code uses: actions/checkout@v3 - name: Cache diff --git a/.github/workflows/protobufs.yml b/.github/workflows/protobufs.yml index 9adfc76c18d..e645507fc8e 100644 --- a/.github/workflows/protobufs.yml +++ b/.github/workflows/protobufs.yml @@ -33,56 +33,16 @@ jobs: - name: Lint protobufs run: | go install github.com/googleapis/api-linter/cmd/api-linter@latest - # Set directory to hold symlink - readonly PROTOBUF_IMPORT_DIR='protobuf-import' - mkdir -p "${PROTOBUF_IMPORT_DIR}" - # Remove any existing symlinks & empty directories - find "${PROTOBUF_IMPORT_DIR}" -type l -delete - find "${PROTOBUF_IMPORT_DIR}" -type d -empty -delete - # Download the required dependencies - go mod download - # Copy all of the proto files into the right directory. - fp_proto_dir="${PROTOBUF_IMPORT_DIR}/github.com/openconfig/featureprofiles" - mkdir -p "${fp_proto_dir}" - cp --parents `find -name \*.proto` "${fp_proto_dir}" - # Get ondatra modules we use and create required directory structure - go list -f "${PROTOBUF_IMPORT_DIR}/{{ .Path }}" -m github.com/openconfig/ondatra | xargs -L1 dirname | sort | uniq | xargs mkdir -p - # Create symlink - go list -f "{{ .Dir }} "${PROTOBUF_IMPORT_DIR}"/{{ .Path }}" -m github.com/openconfig/ondatra | xargs -L1 -- ln -s - cd "${fp_proto_dir}" - find . -name \*.proto -exec api-linter -I"${OLDPWD}"/"${PROTOBUF_IMPORT_DIR}" --disable-rule all --enable-rule core {} \+ - - name: Compile topology binding textprotos + make protoimports + cd protobuf-import + find github.com/openconfig/featureprofiles/ -name \*.proto -exec api-linter --disable-rule all --enable-rule core {} \+ + - name: Validate textprotos run: | - fail=0 - # Set directory to hold symlink - readonly PROTOBUF_IMPORT_DIR='protobuf-import' - mkdir -p "${PROTOBUF_IMPORT_DIR}" - # Remove any existing symlinks & empty directories - find "${PROTOBUF_IMPORT_DIR}" -type l -delete - find "${PROTOBUF_IMPORT_DIR}" -type d -empty -delete - # Download the required dependencies - go mod download - # Get ondatra modules we use and create required directory structure - go list -f "${PROTOBUF_IMPORT_DIR}/{{ .Path }}" -m github.com/openconfig/ondatra | xargs -L1 dirname | sort | uniq | xargs mkdir -p - # Create symlink - go list -f "{{ .Dir }} \"${PROTOBUF_IMPORT_DIR}\"/{{ .Path }}" -m github.com/openconfig/ondatra | xargs -L1 -- ln -s - for i in `find topologies/ -type f -name "*.binding"`; do - if ! output=$(protoc -I="${PROTOBUF_IMPORT_DIR}" --proto_path=topologies/proto --encode=openconfig.testing.Binding topologies/proto/binding.proto < $i 2>&1 >/dev/null); then - fail=1 - echo -e "Compile $i failed:\n$output\n" - fi - done - if [ "$fail" == "1" ]; then exit 1; fi - - name: Compile feature profile textprotos - run: | - fail=0 - for i in `find feature/ -type f -name "feature.textproto"`; do - if ! output=$(protoc --encode=openconfig.profiles.FeatureProfile proto/feature.proto < $i 2>&1 >/dev/null); then - fail=1 - echo -e "Compile $i failed:\n$output\n" - fi + go install github.com/bstoll/textproto-validator@latest + make protoimports + for i in `find . -name \*.textproto`; do + textproto-validator -I ./protobuf-import $i done - if [ "$fail" == "1" ]; then exit 1; fi validate_oc_paths: name: Validate OpenConfig Paths diff --git a/.github/workflows/readme_oc_path_and_rpc.yml b/.github/workflows/readme_oc_path_and_rpc.yml new file mode 100644 index 00000000000..1110244659f --- /dev/null +++ b/.github/workflows/readme_oc_path_and_rpc.yml @@ -0,0 +1,36 @@ +name: README OpenConfig Path and RPC Coverage + +on: + push: + branches: [ main ] + pull_request: + schedule: + - cron: "49 0 * * *" + +jobs: + integration-test: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v3 + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: stable + cache: false + + - name: Validate Validation Script + run: | + cd tools/validate_readme_spec + ./validate_readme_spec_test.sh + + - name: Validate Template README + run: | + go install ./tools/validate_readme_spec + validate_readme_spec -alsologtostderr doc/test-requirements-template.md + + - name: Validate Test READMEs + run: | + go install ./tools/validate_readme_spec + # TODO: Remove -feature-dir argument after all READMEs have converted to the new format. + validate_readme_spec -alsologtostderr -feature-dir feature/bgp/policybase/otg_tests/import_export_multi diff --git a/Makefile b/Makefile index 45756fbd5f5..ffe2ca29f5e 100644 --- a/Makefile +++ b/Makefile @@ -11,10 +11,16 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + +ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) +GO_PROTOS:=proto/feature_go_proto/feature.pb.go proto/metadata_go_proto/metadata.pb.go proto/ocpaths_go_proto/ocpaths.pb.go proto/ocrpcs_go_proto/ocrpcs.pb.go proto/nosimage_go_proto/nosimage.pb.go topologies/proto/binding/binding.pb.go + +.PHONY: all clean protos validate_paths protoimports +all: openconfig_public protos validate_paths + openconfig_public: tools/clone_oc_public.sh openconfig_public -.PHONY: validate_paths validate_paths: openconfig_public proto/feature_go_proto/feature.pb.go go run -v tools/validate_paths.go \ -alsologtostderr \ @@ -23,12 +29,9 @@ validate_paths: openconfig_public proto/feature_go_proto/feature.pb.go --yang_skip_roots=$(CURDIR)/openconfig_public/release/models/wifi \ --feature_files=${FEATURE_FILES} -proto/feature_go_proto/feature.pb.go: proto/feature.proto - mkdir -p proto/feature_go_proto - protoc --proto_path=proto --go_out=./ --go_opt=Mfeature.proto=proto/feature_go_proto feature.proto +protos: $(GO_PROTOS) -proto/metadata_go_proto/metadata.pb.go: proto/metadata.proto - mkdir -p proto/metadata_go_proto +protoimports: # Set directory to hold symlink mkdir -p protobuf-import # Remove any existing symlinks & empty directories @@ -36,10 +39,21 @@ proto/metadata_go_proto/metadata.pb.go: proto/metadata.proto find protobuf-import -type d -empty -delete # Download the required dependencies go mod download - # Get ondatra modules we use and create required directory structure + # Get ondatra & kne modules we use and create required directory structure go list -f 'protobuf-import/{{ .Path }}' -m github.com/openconfig/ondatra | xargs -L1 dirname | sort | uniq | xargs mkdir -p - # Create symlink + go list -f 'protobuf-import/{{ .Path }}' -m github.com/openconfig/kne | xargs -L1 dirname | sort | uniq | xargs mkdir -p + # Create symlinks go list -f '{{ .Dir }} protobuf-import/{{ .Path }}' -m github.com/openconfig/ondatra | xargs -L1 -- ln -s + go list -f '{{ .Dir }} protobuf-import/{{ .Path }}' -m github.com/openconfig/kne | xargs -L1 -- ln -s + ln -s $(ROOT_DIR) protobuf-import/github.com/openconfig/featureprofiles + +proto/feature_go_proto/feature.pb.go: proto/feature.proto + mkdir -p proto/feature_go_proto + protoc --proto_path=proto --go_out=./ --go_opt=Mfeature.proto=proto/feature_go_proto feature.proto + goimports -w proto/feature_go_proto/feature.pb.go + +proto/metadata_go_proto/metadata.pb.go: proto/metadata.proto protoimports + mkdir -p proto/metadata_go_proto protoc -I='protobuf-import' --proto_path=proto --go_out=./ --go_opt=Mmetadata.proto=proto/metadata_go_proto metadata.proto goimports -w proto/metadata_go_proto/metadata.pb.go @@ -53,7 +67,16 @@ proto/ocrpcs_go_proto/ocrpcs.pb.go: proto/ocrpcs.proto protoc --proto_path=proto --go_out=./ --go_opt=Mocrpcs.proto=proto/ocrpcs_go_proto ocrpcs.proto goimports -w proto/ocrpcs_go_proto/ocrpcs.pb.go -proto/nosimage_go_proto/nosimage.pb.go: proto/nosimage.proto +proto/nosimage_go_proto/nosimage.pb.go: proto/nosimage.proto protoimports mkdir -p proto/nosimage_go_proto - protoc -I="${GOPATH}/src" --proto_path=proto --go_out=./proto/nosimage_go_proto --go_opt=paths=source_relative --go_opt=Mnosimage.proto=proto/nosimage_go_proto --go_opt=Mgithub.com/openconfig/featureprofiles/proto/ocpaths.proto=github.com/openconfig/featureprofiles/proto/ocpaths_go_proto --go_opt=Mgithub.com/openconfig/featureprofiles/proto/ocrpcs.proto=github.com/openconfig/featureprofiles/proto/ocrpcs_go_proto nosimage.proto + protoc -I='protobuf-import' --proto_path=proto --go_out=./proto/nosimage_go_proto --go_opt=paths=source_relative --go_opt=Mnosimage.proto=proto/nosimage_go_proto --go_opt=Mgithub.com/openconfig/featureprofiles/proto/ocpaths.proto=github.com/openconfig/featureprofiles/proto/ocpaths_go_proto --go_opt=Mgithub.com/openconfig/featureprofiles/proto/ocrpcs.proto=github.com/openconfig/featureprofiles/proto/ocrpcs_go_proto nosimage.proto goimports -w proto/nosimage_go_proto/nosimage.pb.go + +topologies/proto/binding/binding.pb.go: topologies/proto/binding.proto protoimports + mkdir -p topologies/proto/binding + protoc -I='protobuf-import' --proto_path=topologies/proto --go_out=. --go_opt=Mbinding.proto=topologies/proto/binding binding.proto + goimports -w topologies/proto/binding/binding.pb.go + +clean: + rm -f $(GO_PROTOS) + rm -rf protobuf-import openconfig_public diff --git a/doc/test-requirements-template.md b/doc/test-requirements-template.md index b42c08871ef..5b0f61e89e5 100644 --- a/doc/test-requirements-template.md +++ b/doc/test-requirements-template.md @@ -4,7 +4,6 @@ about: Use this template to document the requirements for a new test to be imple title: '' labels: enhancement assignees: '' - --- # Instructions for this template @@ -28,40 +27,39 @@ Write a few sentences or paragraphs describing the purpose and scope of the test ## Procedure +* Test environment setup + * Description of procedure to configure ATE and DUT with pre-requisites making it possible to cover the intended paths and RPC's. + * TestID-x.y.z - Name of subtest * Step 1 * Step 2 - * Validation and pass fail criteria + * Validation and pass/fail criteria * TestID-x.y.z - Name of subtest * Step 1 * Step 2 - * Validation and pass fail criteria - -## Config Parameter Coverage - -Add list of OpenConfig 'config' paths used in this test, if any. - -## Telemetry Parameter Coverage - -Add list of OpenConfig 'state' paths used in this test, if any. - -## Protocol/RPC Parameter Coverage - -Add list of OpenConfig RPC's (gNMI, gNOI, gNSI, gRIBI) used in the list, if any. - -For example: - -* gNMI - * Set - * Subscribe -* gNOI - * System - * KillProcess - * Healthz - * Get - * Check - * Artifact + * Validation and pass/fail criteria + +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +```yaml +paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + gNMI.Subscribe: + on_change: true +``` ## Required DUT platform diff --git a/feature/acl/feature.textproto b/feature/acl/feature.textproto index d29f33b888a..a85969cdf78 100644 --- a/feature/acl/feature.textproto +++ b/feature/acl/feature.textproto @@ -11,6 +11,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "acl" diff --git a/feature/aft/aft_summary/otg_tests/route_summary_counters_test/metadata.textproto b/feature/aft/aft_summary/otg_tests/route_summary_counters_test/metadata.textproto index 7decf64f6e7..1031dcd2e0c 100644 --- a/feature/aft/aft_summary/otg_tests/route_summary_counters_test/metadata.textproto +++ b/feature/aft/aft_summary/otg_tests/route_summary_counters_test/metadata.textproto @@ -1,4 +1,4 @@ -# proto-file: third_party/openconfig/featureprofiles/proto/metadata.proto +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto # proto-message: Metadata uuid: "ef34466c-37da-4133-8d03-40ebe2a5168a" diff --git a/feature/aft/aft_summary/otg_tests/route_summary_counters_test/route_summary_counters_test.go b/feature/aft/aft_summary/otg_tests/route_summary_counters_test/route_summary_counters_test.go index ee6193d047e..6d8a28e7880 100644 --- a/feature/aft/aft_summary/otg_tests/route_summary_counters_test/route_summary_counters_test.go +++ b/feature/aft/aft_summary/otg_tests/route_summary_counters_test/route_summary_counters_test.go @@ -339,7 +339,7 @@ func (d *dutData) Configure(t *testing.T, dut *ondatra.DUTDevice) { if !d.ipv4 { aftType = oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST } - bgpOC := cfgplugins.BuildBGPOCConfig(t, dut, d.routerID, aftType, d.neighborConfig) + bgpOC := cfgplugins.BuildBGPOCConfig(t, dut, d.routerID, []oc.E_BgpTypes_AFI_SAFI_TYPE{aftType}, d.neighborConfig) for _, a := range []attrs.Attributes{dutPort1, dutPort2} { ocName := dut.Port(t, a.Name).Name() diff --git a/feature/aft/feature.textproto b/feature/aft/feature.textproto index f85d65be8d0..b8ec741be44 100644 --- a/feature/aft/feature.textproto +++ b/feature/aft/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "aft" diff --git a/feature/bgp/addpath/feature.textproto b/feature/bgp/addpath/feature.textproto index 8500d916f14..36293f0cccd 100644 --- a/feature/bgp/addpath/feature.textproto +++ b/feature/bgp/addpath/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "bgp_addpath" diff --git a/feature/bgp/aspath/feature.textproto b/feature/bgp/aspath/feature.textproto index 1faad483376..e82b142eb54 100644 --- a/feature/bgp/aspath/feature.textproto +++ b/feature/bgp/aspath/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "bgp_aspath" diff --git a/feature/bgp/bestpath/feature.textproto b/feature/bgp/bestpath/feature.textproto index 05e2c3ed899..48d03763a8f 100644 --- a/feature/bgp/bestpath/feature.textproto +++ b/feature/bgp/bestpath/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "bgp_bestpath" diff --git a/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/bgp_isis_redistribution_test.go b/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/bgp_isis_redistribution_test.go index b732a371bf0..c4430ea1534 100644 --- a/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/bgp_isis_redistribution_test.go +++ b/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/bgp_isis_redistribution_test.go @@ -39,8 +39,8 @@ const ( bgpName = "BGP" maskLenExact = "exact" dummyAS = uint32(64655) - dutAS = uint32(65656) - ateAS = uint32(65657) + dutAS = uint32(64656) + ateAS = uint32(64657) v4Route = "203.10.113.0" v4TrafficStart = "203.10.113.1" v4DummyRoute = "192.51.100.0" @@ -59,6 +59,8 @@ const ( v6PrefixSet = "prefix-set-v6" v6FlowName = "flow-v6" v6CommunitySet = "community-set-v6" + peerGrpNamev4 = "BGP-PEER-GROUP-V4" + peerGrpNamev6 = "BGP-PEER-GROUP-V6" ) var ( @@ -214,13 +216,19 @@ func setupEBGPAndAdvertise(t *testing.T, ts *isissession.TestSession) { g.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) g.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + pgv4 := bgp.GetOrCreatePeerGroup(peerGrpNamev4) + pgv4.PeerGroupName = ygot.String(peerGrpNamev4) + pgv6 := bgp.GetOrCreatePeerGroup(peerGrpNamev6) + pgv6.PeerGroupName = ygot.String(peerGrpNamev6) nV4 := bgp.GetOrCreateNeighbor(isissession.ATETrafficAttrs.IPv4) nV4.SetPeerAs(ateAS) nV4.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) + nV4.PeerGroup = ygot.String(peerGrpNamev4) nV6 := bgp.GetOrCreateNeighbor(isissession.ATETrafficAttrs.IPv6) nV6.SetPeerAs(ateAS) nV6.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + nV6.PeerGroup = ygot.String(peerGrpNamev6) gnmi.Update(t, ts.DUT, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(ts.DUT)).Config(), dni) // setup eBGP on ATE port2 diff --git a/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/metadata.textproto b/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/metadata.textproto index e8a6d9696ba..a87078e24c0 100644 --- a/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/metadata.textproto +++ b/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/metadata.textproto @@ -24,3 +24,11 @@ platform_exceptions: { bgp_conditions_match_community_set_unsupported: true } } +platform_exceptions: { + platform: { + vendor: JUNIPER + } + deviations: { + isis_level_enabled: true + } +} diff --git a/feature/bgp/dynamicneighbors/feature.textproto b/feature/bgp/dynamicneighbors/feature.textproto index 9d374d8eaa6..10b2877f662 100644 --- a/feature/bgp/dynamicneighbors/feature.textproto +++ b/feature/bgp/dynamicneighbors/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "bgp_dynamicneighbors" diff --git a/feature/bgp/feature.textproto b/feature/bgp/feature.textproto index 943c9169ad1..fcc9e02b447 100644 --- a/feature/bgp/feature.textproto +++ b/feature/bgp/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "bgp" diff --git a/feature/bgp/gracefulrestart/ate_tests/bgp_graceful_restart_test/bgp_graceful_restart_test.go b/feature/bgp/gracefulrestart/ate_tests/bgp_graceful_restart_test/bgp_graceful_restart_test.go index 545290eba11..207b9ffe4be 100644 --- a/feature/bgp/gracefulrestart/ate_tests/bgp_graceful_restart_test/bgp_graceful_restart_test.go +++ b/feature/bgp/gracefulrestart/ate_tests/bgp_graceful_restart_test/bgp_graceful_restart_test.go @@ -479,11 +479,13 @@ func configACLNative(t testing.TB, d *ondatra.DUTDevice, name string) { "drop": map[string]any{}, }, "match": map[string]any{ - "destination-ip": map[string]any{ - "prefix": ateDstCIDR, - }, - "source-ip": map[string]any{ - "prefix": aclNullPrefix, + "ipv4": map[string]any{ + "destination-ip": map[string]any{ + "prefix": ateDstCIDR, + }, + "source-ip": map[string]any{ + "prefix": aclNullPrefix, + }, }, }, }, @@ -499,11 +501,13 @@ func configACLNative(t testing.TB, d *ondatra.DUTDevice, name string) { "drop": map[string]any{}, }, "match": map[string]any{ - "source-ip": map[string]any{ - "prefix": ateDstCIDR, - }, - "destination-ip": map[string]any{ - "prefix": aclNullPrefix, + "ipv4": map[string]any{ + "source-ip": map[string]any{ + "prefix": ateDstCIDR, + }, + "destination-ip": map[string]any{ + "prefix": aclNullPrefix, + }, }, }, }, @@ -519,11 +523,13 @@ func configACLNative(t testing.TB, d *ondatra.DUTDevice, name string) { "accept": map[string]any{}, }, "match": map[string]any{ - "source-ip": map[string]any{ - "prefix": aclNullPrefix, - }, - "destination-ip": map[string]any{ - "prefix": aclNullPrefix, + "ipv4": map[string]any{ + "source-ip": map[string]any{ + "prefix": aclNullPrefix, + }, + "destination-ip": map[string]any{ + "prefix": aclNullPrefix, + }, }, }, }, @@ -534,14 +540,14 @@ func configACLNative(t testing.TB, d *ondatra.DUTDevice, name string) { } gpbSetRequest := &gpb.SetRequest{ Prefix: &gpb.Path{ - Origin: "srl", + Origin: "native", }, Update: []*gpb.Update{ { Path: &gpb.Path{ Elem: []*gpb.PathElem{ {Name: "acl"}, - {Name: "ipv4-filter", Key: map[string]string{"name": name}}, + {Name: "acl-filter", Key: map[string]string{"name": name, "type": "ipv4"}}, {Name: "entry", Key: map[string]string{"sequence-id": "10"}}, }, }, @@ -555,7 +561,7 @@ func configACLNative(t testing.TB, d *ondatra.DUTDevice, name string) { Path: &gpb.Path{ Elem: []*gpb.PathElem{ {Name: "acl"}, - {Name: "ipv4-filter", Key: map[string]string{"name": name}}, + {Name: "acl-filter", Key: map[string]string{"name": name, "type": "ipv4"}}, {Name: "entry", Key: map[string]string{"sequence-id": "20"}}, }, }, @@ -569,7 +575,7 @@ func configACLNative(t testing.TB, d *ondatra.DUTDevice, name string) { Path: &gpb.Path{ Elem: []*gpb.PathElem{ {Name: "acl"}, - {Name: "ipv4-filter", Key: map[string]string{"name": name}}, + {Name: "acl-filter", Key: map[string]string{"name": name, "type": "ipv4"}}, {Name: "entry", Key: map[string]string{"sequence-id": "30"}}, }, }, @@ -598,20 +604,20 @@ func configAdmitAllACLNative(t testing.TB, d *ondatra.DUTDevice, name string) { case ondatra.NOKIA: gpbDelRequest := &gpb.SetRequest{ Prefix: &gpb.Path{ - Origin: "srl", + Origin: "native", }, Delete: []*gpb.Path{ { Elem: []*gpb.PathElem{ {Name: "acl"}, - {Name: "ipv4-filter", Key: map[string]string{"name": name}}, + {Name: "acl-filter", Key: map[string]string{"name": name, "type": "ipv4"}}, {Name: "entry", Key: map[string]string{"sequence-id": "10"}}, }, }, { Elem: []*gpb.PathElem{ {Name: "acl"}, - {Name: "ipv4-filter", Key: map[string]string{"name": name}}, + {Name: "acl-filter", Key: map[string]string{"name": name, "type": "ipv4"}}, {Name: "entry", Key: map[string]string{"sequence-id": "20"}}, }, }, @@ -635,8 +641,9 @@ func configACLInterfaceNative(t *testing.T, d *ondatra.DUTDevice, ifName string) case ondatra.NOKIA: var interfaceAclVal = []any{ map[string]any{ - "ipv4-filter": []any{ - aclName, + "acl-filter": map[string]any{ + "name": aclName, + "type": "ipv4", }, }, } @@ -646,15 +653,14 @@ func configACLInterfaceNative(t *testing.T, d *ondatra.DUTDevice, ifName string) } gpbSetRequest := &gpb.SetRequest{ Prefix: &gpb.Path{ - Origin: "srl", + Origin: "native", }, Update: []*gpb.Update{ { Path: &gpb.Path{ Elem: []*gpb.PathElem{ - {Name: "interface", Key: map[string]string{"name": ifName}}, - {Name: "subinterface", Key: map[string]string{"index": "0"}}, {Name: "acl"}, + {Name: "interface", Key: map[string]string{"interface-id": ifName + ".0"}}, {Name: "input"}, }, }, diff --git a/feature/bgp/gracefulrestart/feature.textproto b/feature/bgp/gracefulrestart/feature.textproto index 8de12815c8c..4e28255f0f0 100644 --- a/feature/bgp/gracefulrestart/feature.textproto +++ b/feature/bgp/gracefulrestart/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "bgp_gracefulrestart" diff --git a/feature/bgp/multihop/feature.textproto b/feature/bgp/multihop/feature.textproto index 1a685ee3ca2..d5a4431e9e8 100644 --- a/feature/bgp/multihop/feature.textproto +++ b/feature/bgp/multihop/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "bgp_multihop" diff --git a/feature/bgp/multipath/feature.textproto b/feature/bgp/multipath/feature.textproto index 088fa111aa5..af19a786e95 100644 --- a/feature/bgp/multipath/feature.textproto +++ b/feature/bgp/multipath/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "bgp_multipath" diff --git a/feature/bgp/multipath/otg_tests/bgp_multipath_ecmp_test/bgp_multipath_ecmp_test.go b/feature/bgp/multipath/otg_tests/bgp_multipath_ecmp_test/bgp_multipath_ecmp_test.go index df68aa2f1e0..9af13da3fdd 100644 --- a/feature/bgp/multipath/otg_tests/bgp_multipath_ecmp_test/bgp_multipath_ecmp_test.go +++ b/feature/bgp/multipath/otg_tests/bgp_multipath_ecmp_test/bgp_multipath_ecmp_test.go @@ -167,8 +167,8 @@ func TestBGPSetup(t *testing.T) { for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { - bs := cfgplugins.NewBGPSession(t, cfgplugins.PortCount4) - bs.WithEBGP(t, oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, true, !tc.enableMultiAS) + bs := cfgplugins.NewBGPSession(t, cfgplugins.PortCount4, nil) + bs.WithEBGP(t, []oc.E_BgpTypes_AFI_SAFI_TYPE{oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST}, []string{"port2", "port3", "port4"}, true, !tc.enableMultiAS) dni := deviations.DefaultNetworkInstance(bs.DUT) bgp := bs.DUTConf.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").GetOrCreateBgp() gEBGP := bgp.GetOrCreateGlobal().GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateUseMultiplePaths().GetOrCreateEbgp() diff --git a/feature/bgp/multipath/otg_tests/bgp_multipath_wecmp_lbw_community_test/bgp_multipath_wecmp_test.go b/feature/bgp/multipath/otg_tests/bgp_multipath_wecmp_lbw_community_test/bgp_multipath_wecmp_test.go index 5a8a21640a7..a83268751d1 100644 --- a/feature/bgp/multipath/otg_tests/bgp_multipath_wecmp_lbw_community_test/bgp_multipath_wecmp_test.go +++ b/feature/bgp/multipath/otg_tests/bgp_multipath_wecmp_lbw_community_test/bgp_multipath_wecmp_test.go @@ -141,8 +141,8 @@ func verifyECMPLoadBalance(t *testing.T, ate *ondatra.ATEDevice, pc int, expecte } func TestBGPSetup(t *testing.T) { - bs := cfgplugins.NewBGPSession(t, cfgplugins.PortCount4) - bs.WithEBGP(t, oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, true, false) + bs := cfgplugins.NewBGPSession(t, cfgplugins.PortCount4, nil) + bs.WithEBGP(t, []oc.E_BgpTypes_AFI_SAFI_TYPE{oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST}, []string{"port3", "port4"}, true, false) dni := deviations.DefaultNetworkInstance(bs.DUT) bgp := bs.DUTConf.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").GetOrCreateBgp() gEBGP := bgp.GetOrCreateGlobal().GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateUseMultiplePaths().GetOrCreateEbgp() diff --git a/feature/bgp/policybase/feature.textproto b/feature/bgp/policybase/feature.textproto index d8863bca301..645d0930e23 100644 --- a/feature/bgp/policybase/feature.textproto +++ b/feature/bgp/policybase/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "bgp_policybase" diff --git a/feature/bgp/policybase/otg_tests/actions_MED_LocPref_prepend_flow_control/README.md b/feature/bgp/policybase/otg_tests/actions_med_localpref_prepend_flow_control/README.md similarity index 53% rename from feature/bgp/policybase/otg_tests/actions_MED_LocPref_prepend_flow_control/README.md rename to feature/bgp/policybase/otg_tests/actions_med_localpref_prepend_flow_control/README.md index 2efc3269fb3..d0cb2d10659 100644 --- a/feature/bgp/policybase/otg_tests/actions_MED_LocPref_prepend_flow_control/README.md +++ b/feature/bgp/policybase/otg_tests/actions_med_localpref_prepend_flow_control/README.md @@ -42,47 +42,6 @@ For each section of configuration below, prepare a gnmi.SetBatch with all the c * Local Preference = 50 ### RT-1.32.1 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] -#### IPv4, IPv6 iBGP set MED ---- -##### Configure a route-policy to set MED -* Configure an route-policy definition with the name ```med-policy``` - * /routing-policy/policy-definitions/policy-definition/config/name -* For routing-policy ```med-policy``` configure a statement with the name ```match-statement-1``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name -* For routing-policy ```med-policy``` statement ```match-statement-1``` set policy-result as ```ACCEPT_ROUTE``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result -* For routing-policy ```med-policy``` statement ```match-statement-1``` set MED as ```100``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/config/set-med -##### Configure bgp import and export policy for the DUT IPv4 and IPv6 BGP neighbors on ATE Port-1 -* Set default import and export policy to `REJECT_ROUTE`. (Note: even though this is the OC default, the DUT should still accept this configuration) - - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Add `policy-definition["med-policy"]` to import-policy and export-policy leaf-lists. - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy -##### Configure default policies for the DUT IPv4 and IPv6 BGP neighbors on ATE Port-2 -* Set default import and export policy to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -##### Verification -* Verify that policies are successfully applied to the DUT BGP neighbor on ATE Port-1 and default policies are set to ```REJECT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Verify that there are no policies applied to the DUT BGP neighbor on ATE Port-2 and default policies are set to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -#### Validate test results -* Validate that the ATE receives the prefix ```ipv4-network-1``` from DUT neighbor on ATE Port-2 and it has MED == 100 -* Validate that the ATE receives the prefix ```ipv6-network-1``` from DUT neighbor on ATE Port-2 and it has MED == 100 -* Validate that the ATE receives the prefix ```ipv4-network-2``` from DUT neighbor on ATE Port-1 and it has MED == 100 -* Validate that the ATE receives the prefix ```ipv6-network-2``` from DUT neighbor on ATE Port-1 and it has MED == 100 - -### RT-1.32.2 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] #### IPv4, IPv6 eBGP set MED --- ##### Configure a route-policy to set MED @@ -125,51 +84,7 @@ For each section of configuration below, prepare a gnmi.SetBatch with all the c * Validate that the ATE receives the prefix ```ipv4-network-2``` from DUT neighbor on ATE Port-1 and it has MED == 100 * Validate that the ATE receives the prefix ```ipv6-network-2``` from DUT neighbor on ATE Port-1 and it has MED == 100 -### RT-1.32.3 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] -#### IPv4, IPv6 iBGP increase MED ---- -##### Configure a route-policy to increase MED -* Configure an route-policy definition with the name ```med-policy``` - * /routing-policy/policy-definitions/policy-definition/config/name -* For routing-policy ```med-policy``` configure a statement with the name ```match-statement-1``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name -* For routing-policy ```med-policy``` statement ```match-statement-1``` set policy-result as ```ACCEPT_ROUTE``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result -* For routing-policy ```med-policy``` statement ```match-statement-1``` set MED as ```+100``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/config/set-med -##### Configure bgp import and export policy for the DUT IPv4 and IPv6 BGP neighbors on ATE Port-1 -* Set default import and export policy to ```REJECT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Add `policy-definition["med-policy"]` to import-policy and export-policy leaf-lists. -* ``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy -##### Configure default policies and remove any import, export policy for the DUT IPv4 and IPv6 BGP neighbors on ATE Port-2 -* Set default import and export policy to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Remove policy as import and export as a chain/list ```[med-policy]``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy -##### Verification -* Verify that policies are successfully applied to the DUT BGP neighbor on ATE Port-1 and default policies are set to ```REJECT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Verify that there is no policies applied to the DUT BGP neighbor on ATE Port-2 and default policies are set to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -#### Validate test results -* Validate that the ATE receives the prefix ```ipv4-network-1``` from DUT neighbor on ATE Port-1 and it has MED == 150 -* Validate that the ATE receives the prefix ```ipv6-network-1``` from DUT neighbor on ATE Port-1 and it has MED == 150 -* Validate that the ATE receives the prefix ```ipv4-network-2``` from DUT neighbor on ATE Port-2 and it has MED == 150 -* Validate that the ATE receives the prefix ```ipv6-network-2``` from DUT neighbor on ATE Port-2 and it has MED == 150 - -### RT-1.32.4 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] +### RT-1.32.2 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] #### IPv4, IPv6 eBGP increase MED --- ##### Configure a route-policy to increase MED @@ -212,7 +127,7 @@ For each section of configuration below, prepare a gnmi.SetBatch with all the c * Validate that the ATE receives the prefix ```ipv4-network-2``` from DUT neighbor on ATE Port-1 and it has MED == 150 * Validate that the ATE receives the prefix ```ipv6-network-2``` from DUT neighbor on ATE Port-1 and it has MED == 150 -### RT-1.32.5 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] +### RT-1.32.3 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] #### IPv4, IPv6 iBGP set Local Preference --- ##### Configure a route-policy to set Local Preference @@ -255,24 +170,29 @@ For each section of configuration below, prepare a gnmi.SetBatch with all the c * Validate that the ATE receives the prefix ```ipv4-network-2``` from DUT neighbor on ATE Port-1 and it has LocPref == 100 * Validate that the ATE receives the prefix ```ipv6-network-2``` from DUT neighbor on ATE Port-1 and it has LocPref == 100 - -### RT-1.32.6 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] -#### IPv4, IPv6 eBGP set Local Preference +### RT-1.32.4 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] +#### IPv4, IPv6 eBGP NEXT-STATEMENT --- -##### Configure a route-policy to set Local Preference -* Configure an route-policy definition with the name ```lp-policy``` +##### Configure a route-policy set MED, LocalPreferemce is separate statements +* Configure an route-policy definition with the name ```flow-control-policy``` * /routing-policy/policy-definitions/policy-definition/config/name -* For routing-policy ```lp-policy``` configure a statement with the name ```match-statement-1``` +* For routing-policy ```flow-control-policy``` configure a statement with the name ```match-statement-1``` * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name -* For routing-policy ```lp-policy``` statement ```match-statement-1``` set policy-result as ```ACCEPT_ROUTE``` +* For routing-policy ```flow-control-policy``` statement ```match-statement-1``` set policy-result as ```NEXT-STATEMENT``` * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result -* For routing-policy ```lp-policy``` statement ```match-statement-1``` set Local Preference as ```100``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/config/set-local-pref +* For routing-policy ```flow-control-policy``` statement ```match-statement-1``` set MED to 70 + * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/config/set-med +* For routing-policy ```flow-control-policy``` configure a statement with the name ```match-statement-2``` + * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name +* For routing-policy ```flow-control-policy``` statement ```match-statement-2``` set policy-result as ```ACCEPT-ROUTE``` + * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result +* For routing-policy ```flow-control-policy``` statement ```match-statement-2``` prepend as-path with local ASN ```10``` times + * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-as-path-prepend/config/repeat-n ##### Configure bgp import and export policy for the DUT IPv4 and IPv6 BGP neighbors on ATE Port-2 * Set default import and export policy to ```REJECT_ROUTE``` * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Add `policy-definition["lp-policy"]` to import-policy and export-policy leaf-lists. +* Apply as import and export only policy - ```[flow-control-policy]``` * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy ##### Configure default policies and remove any import, export policy for the DUT IPv4 and IPv6 BGP neighbors on ATE Port-1 @@ -294,55 +214,12 @@ For each section of configuration below, prepare a gnmi.SetBatch with all the c * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy #### Validate test results -* Validate that the ATE receives the prefix ```ipv4-network-1``` from DUT neighbor on ATE Port-2 and it has LocPref == 100 -* Validate that the ATE receives the prefix ```ipv6-network-1``` from DUT neighbor on ATE Port-2 and it has LocPref == 100 -* Validate that the ATE receives the prefix ```ipv4-network-2``` from DUT neighbor on ATE Port-1 and it has LocPref == 100 -* Validate that the ATE receives the prefix ```ipv6-network-2``` from DUT neighbor on ATE Port-1 and it has LocPref == 100 - -### RT-1.32.7 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] -#### IPv4, IPv6 iBGP prepend 10 x local ASN ---- -##### Configure a route-policy to prepend 10 -* Configure an route-policy definition with the name ```prepend-policy``` - * /routing-policy/policy-definitions/policy-definition/config/name -* For routing-policy ```prepend-policy``` configure a statement with the name ```match-statement-1``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name -* For routing-policy ```prepend-policy``` statement ```match-statement-1``` set policy-result as ```ACCEPT_ROUTE``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result -* For routing-policy ```prepend-policy``` statement ```match-statement-1``` prepend as-path with local ASN ```10``` times - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-as-path-prepend/config/repeat-n -##### Configure bgp import and export policy for the DUT IPv4 and IPv6 BGP neighbors on ATE Port-1 -* Set default import and export policy to ```REJECT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Apply as import and export only policy - ```[prepend-policy]``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy -##### Configure default policies and remove any import, export policy for the DUT IPv4 and IPv6 BGP neighbors on ATE Port-2 -* Set default import and export policy to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Remove all import and export policies - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy -##### Verification -* Verify that policies are successfully applied to the DUT BGP neighbor on ATE Port-1 and default policies are set to ```REJECT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Verify that there is no policies applied to the DUT BGP neighbor on ATE Port-2 and default policies are set to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -#### Validate test results -* Validate that the ATE receives the prefix ```ipv4-network-1``` from DUT neighbor on ATE Port-2 and it has 11 ASN on as-path. All equal to DUT's ASN. -* Validate that the ATE receives the prefix ```ipv6-network-1``` from DUT neighbor on ATE Port-2 and it has 11 ASN on as-path. All equal to DUT's ASN. -* Validate that the ATE receives the prefix ```ipv4-network-2``` from DUT neighbor on ATE Port-1 and it has 11 ASN on as-path. First equial to ATE port-2 ASN and other equal to DUT's ASN. -* Validate that the ATE receives the prefix ```ipv6-network-2``` from DUT neighbor on ATE Port-1 and it has 11 ASN on as-path. First equial to ATE port-2 ASN and other equal to DUT's ASN. +* Validate that the ATE receives the prefix ```ipv4-network-1``` from DUT neighbor on ATE Port-2 and it has MED == 70 and it has 11 ASN on as-path. All equal to DUT's ASN. +* Validate that the ATE receives the prefix ```ipv6-network-1``` from DUT neighbor on ATE Port-2 and it has MED == 70 and it has 11 ASN on as-path. All equal to DUT's ASN. +* Validate that the ATE receives the prefix ```ipv4-network-2``` from DUT neighbor on ATE Port-1 and it has MED == 70 and it has 11 ASN on as-path. All equal to DUT's ASN. +* Validate that the ATE receives the prefix ```ipv6-network-2``` from DUT neighbor on ATE Port-1 and it has MED == 70 and it has 11 ASN on as-path. All equal to DUT's ASN. -### RT-1.32.8 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] +### RT-1.32.5 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] #### IPv4, IPv6 eBGP prepend 10 x local ASN --- ##### Configure a route-policy to prepend 10 @@ -385,51 +262,7 @@ For each section of configuration below, prepare a gnmi.SetBatch with all the c * Validate that the ATE receives the prefix ```ipv4-network-2``` from DUT neighbor on ATE Port-1 and it has 11 ASN on as-path. First equial to ATE port-2 ASN and other equal to DUT's ASN. * Validate that the ATE receives the prefix ```ipv6-network-2``` from DUT neighbor on ATE Port-1 and it has 11 ASN on as-path. First equial to ATE port-2 ASN and other equal to DUT's ASN. -### RT-1.32.9 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] -#### IPv4, IPv6 iBGP prepend 10 x ASN ---- -##### Configure a route-policy to prepend 10 -* Configure an route-policy definition with the name ```prepend-policy``` - * /routing-policy/policy-definitions/policy-definition/config/name -* For routing-policy ```prepend-policy``` configure a statement with the name ```match-statement-1``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name -* For routing-policy ```prepend-policy``` statement ```match-statement-1``` set policy-result as ```ACCEPT_ROUTE``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result -* For routing-policy ```prepend-policy``` statement ```match-statement-1``` prepend as-path with ```23456``` ASN ```10``` times - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-as-path-prepend/config/repeat-n - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-as-path-prepend/config/asn -##### Configure bgp import and export policy for the DUT IPv4 and IPv6 BGP neighbors on ATE Port-1 -* Set default import and export policy to ```REJECT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Apply as import and export only policy - ```[prepend-policy]``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy -##### Configure default policies and remove any import, export policy for the DUT IPv4 and IPv6 BGP neighbors on ATE Port-2 -* Set default import and export policy to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Remove all import and export policies - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy -##### Verification -* Verify that policies are successfully applied to the DUT BGP neighbor on ATE Port-1 and default policies are set to ```REJECT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Verify that there is no policies applied to the DUT BGP neighbor on ATE Port-2 and default policies are set to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -#### Validate test results -* Validate that the ATE receives the prefix ```ipv4-network-1``` from DUT neighbor on ATE Port-2 and it has 11 ASN on as-path. First 10 equal to ```23456``` and last equal to DUT's ASN. -* Validate that the ATE receives the prefix ```ipv6-network-1``` from DUT neighbor on ATE Port-2 and it has 11 ASN on as-path. First 10 equal to ```23456``` and last equal to DUT's ASN. -* Validate that the ATE receives the prefix ```ipv4-network-2``` from DUT neighbor on ATE Port-1 and it has 11 ASN on as-path. First equal to ATE port-2 ASN and other 10 equal to ```23456``` ASN. -* Validate that the ATE receives the prefix ```ipv6-network-2``` from DUT neighbor on ATE Port-1 and it has 11 ASN on as-path. First equal to ATE port-2 ASN and other 10 equal to ```23456``` ASN. - -### RT-1.32.10 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] +### RT-1.32.6 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] #### IPv4, IPv6 eBGP prepend 10 x ASN --- ##### Configure a route-policy to prepend 10 @@ -473,104 +306,6 @@ For each section of configuration below, prepare a gnmi.SetBatch with all the c * Validate that the ATE receives the prefix ```ipv4-network-2``` from DUT neighbor on ATE Port-1 and it has 11 ASN on as-path. First equal to ATE port-2 ASN and other 10 equal to ```23456``` ASN. * Validate that the ATE receives the prefix ```ipv6-network-2``` from DUT neighbor on ATE Port-1 and it has 11 ASN on as-path. First equal to ATE port-2 ASN and other 10 equal to ```23456``` ASN. -### RT-1.32.11 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] -#### IPv4, IPv6 iBGP NEXT-STATEMENT ---- -##### Configure a route-policy set MED, LocalPreferemce is separate statements -* Configure an route-policy definition with the name ```flow-control-policy``` - * /routing-policy/policy-definitions/policy-definition/config/name -* For routing-policy ```flow-control-policy``` configure a statement with the name ```match-statement-1``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name -* For routing-policy ```flow-control-policy``` statement ```match-statement-1``` set policy-result as ```NEXT-STATEMENT``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result -* For routing-policy ```flow-control-policy``` statement ```match-statement-1``` set MED to 70 - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/config/set-med -* For routing-policy ```flow-control-policy``` configure a statement with the name ```match-statement-2``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name -* For routing-policy ```flow-control-policy``` statement ```match-statement-2``` set policy-result as ```ACCEPT-ROUTE``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result -* For routing-policy ```flow-control-policy``` statement ```match-statement-2``` set LocalPreference to 70 - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/config/set-local-pref -##### Configure bgp import and export policy for the DUT IPv4 and IPv6 BGP neighbors on ATE Port-1 -* Set default import and export policy to ```REJECT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Apply as import and export only policy - ```[flow-control-policy]``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy -##### Configure default policies and remove any import, export policy for the DUT IPv4 and IPv6 BGP neighbors on ATE Port-2 -* Set default import and export policy to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Remove all import and export policies - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy -##### Verification -* Verify that policies are successfully applied to the DUT BGP neighbor on ATE Port-1 and default policies are set to ```REJECT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Verify that there is no policies applied to the DUT BGP neighbor on ATE Port-2 and default policies are set to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -#### Validate test results -* Validate that the ATE receives the prefix ```ipv4-network-1``` from DUT neighbor on ATE Port-2 and it has MED == 70. -* Validate that the ATE receives the prefix ```ipv6-network-1``` from DUT neighbor on ATE Port-2 and it has MED == 70. -* Validate that the ATE receives the prefix ```ipv4-network-2``` from DUT neighbor on ATE Port-1 and it has MED == 70 and Local Preference == 70. -* Validate that the ATE receives the prefix ```ipv6-network-2``` from DUT neighbor on ATE Port-1 and it has MED == 70 and Local Preference == 70. - -### RT-1.32.12 [TODO: https://github.com/openconfig/featureprofiles/issues/2615] -#### IPv4, IPv6 eBGP NEXT-STATEMENT ---- -##### Configure a route-policy set MED, LocalPreferemce is separate statements -* Configure an route-policy definition with the name ```flow-control-policy``` - * /routing-policy/policy-definitions/policy-definition/config/name -* For routing-policy ```flow-control-policy``` configure a statement with the name ```match-statement-1``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name -* For routing-policy ```flow-control-policy``` statement ```match-statement-1``` set policy-result as ```NEXT-STATEMENT``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result -* For routing-policy ```flow-control-policy``` statement ```match-statement-1``` set MED to 70 - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/config/set-med -* For routing-policy ```flow-control-policy``` configure a statement with the name ```match-statement-2``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name -* For routing-policy ```flow-control-policy``` statement ```match-statement-2``` set policy-result as ```ACCEPT-ROUTE``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result -* For routing-policy ```flow-control-policy``` statement ```match-statement-2``` set LocalPreference to 70 - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/config/set-local-pref -##### Configure bgp import and export policy for the DUT IPv4 and IPv6 BGP neighbors on ATE Port-2 -* Set default import and export policy to ```REJECT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Apply as import and export only policy - ```[flow-control-policy]``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy -##### Configure default policies and remove any import, export policy for the DUT IPv4 and IPv6 BGP neighbors on ATE Port-1 -* Set default import and export policy to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Remove all import and export policies - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy -##### Verification -* Verify that policies are successfully applied to the DUT BGP neighbor on ATE Port-2 and default policies are set to ```REJECT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -* Verify that there is no policies applied to the DUT BGP neighbor on ATE Port-1 and default policies are set to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy -#### Validate test results -* Validate that the ATE receives the prefix ```ipv4-network-1``` from DUT neighbor on ATE Port-2 and it has MED == 70. -* Validate that the ATE receives the prefix ```ipv6-network-1``` from DUT neighbor on ATE Port-2 and it has MED == 70. -* Validate that the ATE receives the prefix ```ipv4-network-2``` from DUT neighbor on ATE Port-1 and it has MED == 70 and Local Preference == 70. -* Validate that the ATE receives the prefix ```ipv6-network-2``` from DUT neighbor on ATE Port-1 and it has MED == 70 and Local Preference == 70. - ## Config parameter coverage * /network-instances/network-instance/protocols/protocol/bgp/global/afi-safis/afi-safi/config/ @@ -603,3 +338,4 @@ For each section of configuration below, prepare a gnmi.SetBatch with all the c ## Required DUT platform * FFF + diff --git a/feature/bgp/policybase/otg_tests/actions_MED_LocPref_prepend_flow_control/actions_MED_LocPref_prepend_flow_control_test.go b/feature/bgp/policybase/otg_tests/actions_med_localpref_prepend_flow_control/actions_MED_LocPref_prepend_flow_control_test.go similarity index 79% rename from feature/bgp/policybase/otg_tests/actions_MED_LocPref_prepend_flow_control/actions_MED_LocPref_prepend_flow_control_test.go rename to feature/bgp/policybase/otg_tests/actions_med_localpref_prepend_flow_control/actions_MED_LocPref_prepend_flow_control_test.go index facfd899135..2ab76c4fb6c 100644 --- a/feature/bgp/policybase/otg_tests/actions_MED_LocPref_prepend_flow_control/actions_MED_LocPref_prepend_flow_control_test.go +++ b/feature/bgp/policybase/otg_tests/actions_med_localpref_prepend_flow_control/actions_MED_LocPref_prepend_flow_control_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package actions_MED_LocPref_prepend_flow_control_test +package actions_med_localpref_prepend_flow_control_test import ( "strconv" @@ -67,6 +67,7 @@ const ( nxtLocalPref = 70 setNxtPolicy = "flow-control-policy" matchStatement2 = "match-statement-2" + asnRepeatN = 10 ) var ( @@ -249,12 +250,15 @@ func configureASLocalPrefMEDPolicy(t *testing.T, dut *ondatra.DUTDevice, policyT t.Fatal(err) } actions2 := stmt2.GetOrCreateActions() - actions2.GetOrCreateBgpActions().SetLocalPref = ygot.Uint32(uint32(metric)) + asPrepend := actions2.GetOrCreateBgpActions().GetOrCreateSetAsPathPrepend() + asPrepend.Asn = ygot.Uint32(ASN) + asPrepend.RepeatN = ygot.Uint8(asnRepeatN) actions2.PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE default: rp = nil } gnmi.BatchReplace(batchConfig, gnmi.OC().RoutingPolicy().Config(), rp) + batchConfig.Set(t, dut) } // configureBGPDefaultImportExportPolicy configures default import/export policies @@ -371,15 +375,19 @@ func configureOTG(t *testing.T, otg *otg.OTG) gosnappi.Config { iDut1Bgp := iDut1Dev.Bgp().SetRouterId(iDut1Ipv4.Address()) iDut1Bgp4Peer := iDut1Bgp.Ipv4Interfaces().Add().SetIpv4Name(iDut1Ipv4.Name()).Peers().Add().SetName(atePort1.Name + ".BGP4.peer") iDut1Bgp4Peer.SetPeerAddress(iDut1Ipv4.Gateway()).SetAsNumber(dutAS).SetAsType(gosnappi.BgpV4PeerAsType.IBGP) + iDut1Bgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) iDut1Bgp6Peer := iDut1Bgp.Ipv6Interfaces().Add().SetIpv6Name(iDut1Ipv6.Name()).Peers().Add().SetName(atePort1.Name + ".BGP6.peer") iDut1Bgp6Peer.SetPeerAddress(iDut1Ipv6.Gateway()).SetAsNumber(dutAS).SetAsType(gosnappi.BgpV6PeerAsType.IBGP) + iDut1Bgp6Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) // eBGP v4 and v6 sessions on port2 iDut2Bgp := iDut2Dev.Bgp().SetRouterId(iDut2Ipv4.Address()) iDut2Bgp4Peer := iDut2Bgp.Ipv4Interfaces().Add().SetIpv4Name(iDut2Ipv4.Name()).Peers().Add().SetName(atePort2.Name + ".BGP4.peer") iDut2Bgp4Peer.SetPeerAddress(iDut2Ipv4.Gateway()).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + iDut2Bgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) iDut2Bgp6Peer := iDut2Bgp.Ipv6Interfaces().Add().SetIpv6Name(iDut2Ipv6.Name()).Peers().Add().SetName(atePort2.Name + ".BGP6.peer") iDut2Bgp6Peer.SetPeerAddress(iDut2Ipv6.Gateway()).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + iDut2Bgp6Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) // iBGP V4 routes from Port1 and set MED, Local Preference. bgpNeti1Bgp4PeerRoutes := iDut1Bgp4Peer.V4Routes().Add().SetName(atePort1.Name + ".BGP4.Route") @@ -388,8 +396,8 @@ func configureOTG(t *testing.T, otg *otg.OTG) gosnappi.Config { SetNextHopMode(gosnappi.BgpV4RouteRangeNextHopMode.MANUAL) bgpNeti1Bgp4PeerRoutes.Addresses().Add(). SetAddress(advertisedRoutesv4Net1).SetPrefix(advertisedRoutesv4PrefixLen) - bgpNeti1Bgp4PeerRoutes.Advanced().SetMultiExitDiscriminator(50) - bgpNeti1Bgp4PeerRoutes.Advanced().SetLocalPreference(50) + bgpNeti1Bgp4PeerRoutes.Advanced().SetIncludeMultiExitDiscriminator(true).SetMultiExitDiscriminator(50) + bgpNeti1Bgp4PeerRoutes.Advanced().SetIncludeLocalPreference(true).SetLocalPreference(50) // iBGP V6 routes from Port1 and set MED, Local Preference. bgpNeti1Bgp6PeerRoutes := iDut1Bgp6Peer.V6Routes().Add().SetName(atePort1.Name + ".BGP6.Route") @@ -398,8 +406,8 @@ func configureOTG(t *testing.T, otg *otg.OTG) gosnappi.Config { SetNextHopMode(gosnappi.BgpV6RouteRangeNextHopMode.MANUAL) bgpNeti1Bgp6PeerRoutes.Addresses().Add(). SetAddress(advertisedRoutesv6Net1).SetPrefix(advertisedRoutesv6PrefixLen) - bgpNeti1Bgp6PeerRoutes.Advanced().SetMultiExitDiscriminator(50) - bgpNeti1Bgp6PeerRoutes.Advanced().SetLocalPreference(50) + bgpNeti1Bgp6PeerRoutes.Advanced().SetIncludeMultiExitDiscriminator(true).SetMultiExitDiscriminator(50) + bgpNeti1Bgp6PeerRoutes.Advanced().SetIncludeLocalPreference(true).SetLocalPreference(50) // eBGP V4 routes from Port2 and set MED, Local Preference. bgpNeti2Bgp4PeerRoutes := iDut2Bgp4Peer.V4Routes().Add().SetName(atePort2.Name + ".BGP4.Route") @@ -408,8 +416,8 @@ func configureOTG(t *testing.T, otg *otg.OTG) gosnappi.Config { SetNextHopMode(gosnappi.BgpV4RouteRangeNextHopMode.MANUAL) bgpNeti2Bgp4PeerRoutes.Addresses().Add(). SetAddress(advertisedRoutesv4Net2).SetPrefix(advertisedRoutesv4PrefixLen) - bgpNeti2Bgp4PeerRoutes.Advanced().SetMultiExitDiscriminator(50) - bgpNeti2Bgp4PeerRoutes.Advanced().SetLocalPreference(50) + bgpNeti2Bgp4PeerRoutes.Advanced().SetIncludeMultiExitDiscriminator(true).SetMultiExitDiscriminator(50) + bgpNeti2Bgp4PeerRoutes.Advanced().SetIncludeLocalPreference(true).SetLocalPreference(50) // eBGP V6 routes from Port2 and set MED, Local Preference. bgpNeti2Bgp6PeerRoutes := iDut2Bgp6Peer.V6Routes().Add().SetName(atePort2.Name + ".BGP6.Route") @@ -418,8 +426,8 @@ func configureOTG(t *testing.T, otg *otg.OTG) gosnappi.Config { SetNextHopMode(gosnappi.BgpV6RouteRangeNextHopMode.MANUAL) bgpNeti2Bgp6PeerRoutes.Addresses().Add(). SetAddress(advertisedRoutesv6Net2).SetPrefix(advertisedRoutesv6PrefixLen) - bgpNeti2Bgp6PeerRoutes.Advanced().SetMultiExitDiscriminator(50) - bgpNeti2Bgp6PeerRoutes.Advanced().SetLocalPreference(50) + bgpNeti2Bgp6PeerRoutes.Advanced().SetIncludeMultiExitDiscriminator(true).SetMultiExitDiscriminator(50) + bgpNeti2Bgp6PeerRoutes.Advanced().SetIncludeLocalPreference(true).SetLocalPreference(50) t.Logf("Pushing config to ATE and starting protocols...") otg.PushConfig(t, config) @@ -428,19 +436,19 @@ func configureOTG(t *testing.T, otg *otg.OTG) gosnappi.Config { } // validateOTGBgpPrefixV4AndASLocalPrefMED verifies that the IPv4 prefix is received on OTG. -func validateOTGBgpPrefixV4AndASLocalPrefMED(t *testing.T, otg *otg.OTG, config gosnappi.Config, peerName, ipAddr string, prefixLen uint32, pathAttr string, metric uint32) { - t.Helper() +func validateOTGBgpPrefixV4AndASLocalPrefMED(t *testing.T, otg *otg.OTG, dut *ondatra.DUTDevice, config gosnappi.Config, peerName, ipAddr string, prefixLen uint32, pathAttr string, metric uint32) { + // t.Helper() _, ok := gnmi.WatchAll(t, otg, gnmi.OTG().BgpPeer(peerName).UnicastIpv4PrefixAny().State(), - time.Minute, + 30*time.Second, func(v *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv4Prefix]) bool { _, present := v.Val() return present }).Await(t) var foundPrefix = false if ok { - bgpPrefixes := gnmi.GetAll(t, otg, gnmi.OTG().BgpPeer(peerName).UnicastIpv4PrefixAny().State()) + bgpPrefixes := gnmi.GetAll[*otgtelemetry.BgpPeer_UnicastIpv4Prefix](t, otg, gnmi.OTG().BgpPeer(peerName).UnicastIpv4PrefixAny().State()) for _, bgpPrefix := range bgpPrefixes { if bgpPrefix.Address != nil && bgpPrefix.GetAddress() == ipAddr && bgpPrefix.PrefixLength != nil && bgpPrefix.GetPrefixLength() == prefixLen { @@ -454,14 +462,10 @@ func validateOTGBgpPrefixV4AndASLocalPrefMED(t *testing.T, otg *otg.OTG, config t.Logf("For Prefix %v, got MED %d want MED %d", bgpPrefix.GetAddress(), bgpPrefix.GetMultiExitDiscriminator(), metric) } case setLocalPrefPolicy: - if bgpPrefix.GetLocalPreference() != metric { - t.Errorf("For Prefix %v, got Local Preference %d want Local Preference %d", bgpPrefix.GetAddress(), bgpPrefix.GetLocalPreference(), metric) - } else { - t.Logf("For Prefix %v, got Local Preference %d want Local Preference %d", bgpPrefix.GetAddress(), bgpPrefix.GetLocalPreference(), metric) - } + validateImportRoutingPolicy(t, dut, ipAddr, metric) case setPrependPolicy: - if len(bgpPrefix.AsPath) != int(metric) { - t.Errorf("For Prefix %v, got AS Path Prepend %d want AS Path Prepend %d", bgpPrefix.GetAddress(), len(bgpPrefix.AsPath), int(metric)) + if len(bgpPrefix.AsPath[0].GetAsNumbers()) != int(metric) { + t.Errorf("For Prefix %v, got AS Path Prepend %d want AS Path Prepend %d", bgpPrefix.GetAddress(), len(bgpPrefix.AsPath[0].GetAsNumbers()), int(metric)) } else { t.Logf("For Prefix %v, got AS Path Prepend %d want AS Path Prepend %d", bgpPrefix.GetAddress(), len(bgpPrefix.AsPath), int(metric)) } @@ -471,12 +475,10 @@ func validateOTGBgpPrefixV4AndASLocalPrefMED(t *testing.T, otg *otg.OTG, config } else { t.Logf("For Prefix %v, got MED %d want MED %d", bgpPrefix.GetAddress(), bgpPrefix.GetMultiExitDiscriminator(), metric) } - if !strings.Contains(peerName, atePort2.Name) { - if bgpPrefix.GetLocalPreference() != metric { - t.Errorf("For Prefix %v, got Local Preference %d want Local Preference %d", bgpPrefix.GetAddress(), bgpPrefix.GetLocalPreference(), metric) - } else { - t.Logf("For Prefix %v, got Local Preference %d want Local Preference %d", bgpPrefix.GetAddress(), bgpPrefix.GetLocalPreference(), metric) - } + if len(bgpPrefix.AsPath[0].GetAsNumbers()) != asnRepeatN+1 { + t.Errorf("For Prefix %v, got AS Path Prepend %d want AS Path Prepend %d", bgpPrefix.GetAddress(), len(bgpPrefix.AsPath[0].GetAsNumbers()), asnRepeatN+1) + } else { + t.Logf("For Prefix %v, got AS Path Prepend %d want AS Path Prepend %d", bgpPrefix.GetAddress(), len(bgpPrefix.AsPath), asnRepeatN+1) } default: t.Errorf("Incorrect BGP Path Attribute. Expected MED, Local Pref or AS Path Prepend!!!!") @@ -491,19 +493,19 @@ func validateOTGBgpPrefixV4AndASLocalPrefMED(t *testing.T, otg *otg.OTG, config } // validateOTGBgpPrefixV6AndASLocalPrefMED verifies that the IPv6 prefix is received on OTG. -func validateOTGBgpPrefixV6AndASLocalPrefMED(t *testing.T, otg *otg.OTG, config gosnappi.Config, peerName, ipAddr string, prefixLen uint32, pathAttr string, metric uint32) { - t.Helper() +func validateOTGBgpPrefixV6AndASLocalPrefMED(t *testing.T, otg *otg.OTG, dut *ondatra.DUTDevice, config gosnappi.Config, peerName, ipAddr string, prefixLen uint32, pathAttr string, metric uint32) { + // t.Helper() _, ok := gnmi.WatchAll(t, otg, gnmi.OTG().BgpPeer(peerName).UnicastIpv6PrefixAny().State(), - time.Minute, + 30*time.Second, func(v *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv6Prefix]) bool { _, present := v.Val() return present }).Await(t) var foundPrefix = false if ok { - bgpPrefixes := gnmi.GetAll(t, otg, gnmi.OTG().BgpPeer(peerName).UnicastIpv6PrefixAny().State()) + bgpPrefixes := gnmi.GetAll[*otgtelemetry.BgpPeer_UnicastIpv6Prefix](t, otg, gnmi.OTG().BgpPeer(peerName).UnicastIpv6PrefixAny().State()) for _, bgpPrefix := range bgpPrefixes { if bgpPrefix.Address != nil && bgpPrefix.GetAddress() == ipAddr && bgpPrefix.PrefixLength != nil && bgpPrefix.GetPrefixLength() == prefixLen { @@ -517,14 +519,10 @@ func validateOTGBgpPrefixV6AndASLocalPrefMED(t *testing.T, otg *otg.OTG, config t.Logf("For Prefix %v, got MED %d want MED %d", bgpPrefix.GetAddress(), bgpPrefix.GetMultiExitDiscriminator(), metric) } case setLocalPrefPolicy: - if bgpPrefix.GetLocalPreference() != metric { - t.Errorf("For Prefix %v, got Local Preference %d want Local Preference %d", bgpPrefix.GetAddress(), bgpPrefix.GetLocalPreference(), metric) - } else { - t.Logf("For Prefix %v, got Local Preference %d want Local Preference %d", bgpPrefix.GetAddress(), bgpPrefix.GetLocalPreference(), metric) - } + validateImportRoutingPolicyV6(t, dut, ipAddr, metric) case setPrependPolicy: - if len(bgpPrefix.AsPath) != int(metric) { - t.Errorf("For Prefix %v, got AS Path Prepend %d want AS Path Prepend %d", bgpPrefix.GetAddress(), len(bgpPrefix.AsPath), int(metric)) + if len(bgpPrefix.AsPath[0].GetAsNumbers()) != int(metric) { + t.Errorf("For Prefix %v, got AS Path Prepend %d want AS Path Prepend %d", bgpPrefix.GetAddress(), len(bgpPrefix.AsPath[0].GetAsNumbers()), int(metric)) } else { t.Logf("For Prefix %v, got AS Path Prepend %d want AS Path Prepend %d", bgpPrefix.GetAddress(), len(bgpPrefix.AsPath), int(metric)) } @@ -534,12 +532,10 @@ func validateOTGBgpPrefixV6AndASLocalPrefMED(t *testing.T, otg *otg.OTG, config } else { t.Logf("For Prefix %v, got MED %d want MED %d", bgpPrefix.GetAddress(), bgpPrefix.GetMultiExitDiscriminator(), metric) } - if !strings.Contains(peerName, atePort2.Name) { - if bgpPrefix.GetLocalPreference() != metric { - t.Errorf("For Prefix %v, got Local Preference %d want Local Preference %d", bgpPrefix.GetAddress(), bgpPrefix.GetLocalPreference(), metric) - } else { - t.Logf("For Prefix %v, got Local Preference %d want Local Preference %d", bgpPrefix.GetAddress(), bgpPrefix.GetLocalPreference(), metric) - } + if len(bgpPrefix.AsPath[0].GetAsNumbers()) != asnRepeatN+1 { + t.Errorf("For Prefix %v, got AS Path Prepend %d want AS Path Prepend %d", bgpPrefix.GetAddress(), len(bgpPrefix.AsPath[0].GetAsNumbers()), asnRepeatN+1) + } else { + t.Logf("For Prefix %v, got AS Path Prepend %d want AS Path Prepend %d", bgpPrefix.GetAddress(), len(bgpPrefix.AsPath), asnRepeatN+1) } default: t.Errorf("Incorrect Routing Policy. Expected MED, Local Pref or AS Path Prepend!!!!") @@ -600,26 +596,6 @@ func TestBGPPolicy(t *testing.T) { deleteNbrv4, deleteNbrv6 string polNbrv4, polNbrv6 string }{{ - desc: "Configure iBGP set MED Import Export Policy", - rpPolicy: setMEDPolicy, - policyTypePort1: setMEDPolicy, - policyValue: "100", - policyStatement: matchStatement1, - defPolicyPort1: defRejectRoute, - defPolicyPort2: defAcceptRoute, - policyTypePort2: "", - port1v4Prefix: advertisedRoutesv4Net2, - port1v6Prefix: advertisedRoutesv6Net2, - port2v4Prefix: advertisedRoutesv4Net1, - port2v6Prefix: advertisedRoutesv6Net1, - metricValue: 100, - polNbrv4: atePort1.IPv4, - polNbrv6: atePort1.IPv6, - isDeletePolicy: false, - deleteNbrv4: "", - deleteNbrv6: "", - asn: dutAS, - }, { desc: "Configure eBGP set MED Import Export Policy", rpPolicy: setMEDPolicy, policyTypePort1: "", @@ -639,26 +615,6 @@ func TestBGPPolicy(t *testing.T) { deleteNbrv4: atePort1.IPv4, deleteNbrv6: atePort1.IPv6, asn: dutAS, - }, { - desc: "Configure iBGP increase MED Import Export Policy", - rpPolicy: setMEDPolicy, - policyTypePort1: setMEDPolicy, - policyValue: "+100", - policyStatement: matchStatement1, - defPolicyPort1: defRejectRoute, - defPolicyPort2: defAcceptRoute, - policyTypePort2: "", - port1v4Prefix: advertisedRoutesv4Net1, - port1v6Prefix: advertisedRoutesv6Net1, - port2v4Prefix: advertisedRoutesv4Net2, - port2v6Prefix: advertisedRoutesv6Net2, - metricValue: 150, - polNbrv4: atePort1.IPv4, - polNbrv6: atePort1.IPv6, - isDeletePolicy: true, - deleteNbrv4: atePort2.IPv4, - deleteNbrv6: atePort2.IPv6, - asn: dutAS, }, { desc: "Configure eBGP increase MED Import Export Policy", rpPolicy: setMEDPolicy, @@ -700,171 +656,138 @@ func TestBGPPolicy(t *testing.T) { deleteNbrv6: atePort2.IPv6, asn: dutAS, }, { - desc: "Configure eBGP set Local Preference Import Export Policy", - rpPolicy: setLocalPrefPolicy, + desc: "Configure eBGP set NEXT-STATEMENT Import Export Policy", + rpPolicy: setNxtPolicy, policyTypePort1: "", - policyValue: "100", + policyValue: "70", policyStatement: matchStatement1, defPolicyPort1: defAcceptRoute, defPolicyPort2: defRejectRoute, - policyTypePort2: setLocalPrefPolicy, + policyTypePort2: setNxtPolicy, port1v4Prefix: advertisedRoutesv4Net2, port1v6Prefix: advertisedRoutesv6Net2, port2v4Prefix: advertisedRoutesv4Net1, port2v6Prefix: advertisedRoutesv6Net1, - metricValue: 100, + metricValue: 70, polNbrv4: atePort2.IPv4, polNbrv6: atePort2.IPv6, isDeletePolicy: true, deleteNbrv4: atePort1.IPv4, deleteNbrv6: atePort1.IPv6, asn: dutAS, - }, { - desc: "Configure iBGP prepend 10 x local ASN Import Export Policy", - rpPolicy: setPrependPolicy, - policyTypePort1: setPrependPolicy, - policyValue: "10", - policyStatement: matchStatement1, - defPolicyPort1: defRejectRoute, - defPolicyPort2: defAcceptRoute, - policyTypePort2: "", - port1v4Prefix: advertisedRoutesv4Net2, - port1v6Prefix: advertisedRoutesv6Net2, - port2v4Prefix: advertisedRoutesv4Net1, - port2v6Prefix: advertisedRoutesv6Net1, - metricValue: 11, - polNbrv4: atePort1.IPv4, - polNbrv6: atePort1.IPv6, - isDeletePolicy: true, - deleteNbrv4: atePort2.IPv4, - deleteNbrv6: atePort2.IPv6, - asn: dutAS, }, { desc: "Configure eBGP prepend 10 x local ASN Import Export Policy", rpPolicy: setPrependPolicy, policyTypePort1: "", policyValue: "10", policyStatement: matchStatement1, - defPolicyPort1: defRejectRoute, - defPolicyPort2: defAcceptRoute, + defPolicyPort1: defAcceptRoute, + defPolicyPort2: defRejectRoute, policyTypePort2: setPrependPolicy, port1v4Prefix: advertisedRoutesv4Net2, port1v6Prefix: advertisedRoutesv6Net2, port2v4Prefix: advertisedRoutesv4Net1, port2v6Prefix: advertisedRoutesv6Net1, - metricValue: 11, + metricValue: asnRepeatN + 1, polNbrv4: atePort2.IPv4, polNbrv6: atePort2.IPv6, isDeletePolicy: true, deleteNbrv4: atePort1.IPv4, deleteNbrv6: atePort1.IPv6, asn: dutAS, - }, { - desc: "Configure iBGP prepend 10 x ASN Import Export Policy", - rpPolicy: setPrependPolicy, - policyTypePort1: setPrependPolicy, - policyValue: "10", - policyStatement: matchStatement1, - defPolicyPort1: defRejectRoute, - defPolicyPort2: defAcceptRoute, - policyTypePort2: "", - port1v4Prefix: advertisedRoutesv4Net2, - port1v6Prefix: advertisedRoutesv6Net2, - port2v4Prefix: advertisedRoutesv4Net1, - port2v6Prefix: advertisedRoutesv6Net1, - metricValue: 11, - polNbrv4: atePort1.IPv4, - polNbrv6: atePort1.IPv6, - isDeletePolicy: true, - deleteNbrv4: atePort2.IPv4, - deleteNbrv6: atePort2.IPv6, - asn: 23456, }, { desc: "Configure eBGP prepend 10 x ASN Import Export Policy", rpPolicy: setPrependPolicy, policyTypePort1: "", policyValue: "10", policyStatement: matchStatement1, - defPolicyPort1: defRejectRoute, - defPolicyPort2: defAcceptRoute, - policyTypePort2: setPrependPolicy, - port1v4Prefix: advertisedRoutesv4Net2, - port1v6Prefix: advertisedRoutesv6Net2, - port2v4Prefix: advertisedRoutesv4Net1, - port2v6Prefix: advertisedRoutesv6Net1, - metricValue: 11, - polNbrv4: atePort2.IPv4, - polNbrv6: atePort2.IPv6, - isDeletePolicy: true, - deleteNbrv4: atePort1.IPv4, - deleteNbrv6: atePort1.IPv6, - asn: 23456, - }, { - desc: "Configure iBGP set NEXT-STATEMENT Import Export Policy", - rpPolicy: setNxtPolicy, - policyTypePort1: setNxtPolicy, - policyValue: "70", - policyStatement: matchStatement1, - defPolicyPort1: defRejectRoute, - defPolicyPort2: defAcceptRoute, - policyTypePort2: "", - port1v4Prefix: advertisedRoutesv4Net2, - port1v6Prefix: advertisedRoutesv6Net2, - port2v4Prefix: advertisedRoutesv4Net1, - port2v6Prefix: advertisedRoutesv6Net1, - metricValue: 70, - polNbrv4: atePort1.IPv4, - polNbrv6: atePort1.IPv6, - isDeletePolicy: false, - deleteNbrv4: atePort2.IPv4, - deleteNbrv6: atePort2.IPv6, - asn: dutAS, - }, { - desc: "Configure eBGP set NEXT-STATEMENT Import Export Policy", - rpPolicy: setNxtPolicy, - policyTypePort1: "", - policyValue: "70", - policyStatement: matchStatement1, defPolicyPort1: defAcceptRoute, defPolicyPort2: defRejectRoute, - policyTypePort2: setNxtPolicy, + policyTypePort2: setPrependPolicy, port1v4Prefix: advertisedRoutesv4Net2, port1v6Prefix: advertisedRoutesv6Net2, port2v4Prefix: advertisedRoutesv4Net1, port2v6Prefix: advertisedRoutesv6Net1, - metricValue: 70, + metricValue: asnRepeatN + 1, polNbrv4: atePort2.IPv4, polNbrv6: atePort2.IPv6, isDeletePolicy: true, deleteNbrv4: atePort1.IPv4, deleteNbrv6: atePort1.IPv6, - asn: dutAS, + asn: 23456, }} for _, tc := range cases { t.Run(tc.desc, func(t *testing.T) { - + // Delete BGP import export policy + if tc.isDeletePolicy { + deleteBGPImportExportPolicy(t, dut, tc.deleteNbrv4, tc.deleteNbrv6) + } // Configure Routing Policy on the DUT. configureASLocalPrefMEDPolicy(t, dut, tc.rpPolicy, tc.policyValue, tc.policyStatement, tc.asn) // Configure BGP default import export policy on Port1 configureBGPDefaultImportExportPolicy(t, dut, atePort1.IPv4, atePort1.IPv6, tc.defPolicyPort1) - // Configure BGP default import export policy on Port1 + // Configure BGP default import export policy on Port2 configureBGPDefaultImportExportPolicy(t, dut, atePort2.IPv4, atePort2.IPv6, tc.defPolicyPort2) // Configure BGP import export policy configureBGPImportExportPolicy(t, dut, tc.polNbrv4, tc.polNbrv6, tc.rpPolicy) - // Delete BGP import export policy - if tc.isDeletePolicy { - deleteBGPImportExportPolicy(t, dut, tc.deleteNbrv4, tc.deleteNbrv6) - } + // Verify BGP policy verifyBgpPolicyTelemetry(t, dut, atePort1.IPv4, tc.defPolicyPort1, tc.policyTypePort1, true) verifyBgpPolicyTelemetry(t, dut, atePort1.IPv6, tc.defPolicyPort1, tc.policyTypePort1, false) verifyBgpPolicyTelemetry(t, dut, atePort2.IPv4, tc.defPolicyPort2, tc.policyTypePort2, true) verifyBgpPolicyTelemetry(t, dut, atePort2.IPv6, tc.defPolicyPort2, tc.policyTypePort2, false) + // Validate Prefixes - validateOTGBgpPrefixV4AndASLocalPrefMED(t, otg, otgConfig, atePort1.Name+".BGP4.Route", tc.port1v4Prefix, advertisedRoutesv4PrefixLen, tc.rpPolicy, tc.metricValue) - validateOTGBgpPrefixV6AndASLocalPrefMED(t, otg, otgConfig, atePort1.Name+".BGP6.Route", tc.port1v6Prefix, advertisedRoutesv6PrefixLen, tc.rpPolicy, tc.metricValue) - validateOTGBgpPrefixV4AndASLocalPrefMED(t, otg, otgConfig, atePort2.Name+".BGP4.Route", tc.port2v4Prefix, advertisedRoutesv4PrefixLen, tc.rpPolicy, tc.metricValue) - validateOTGBgpPrefixV6AndASLocalPrefMED(t, otg, otgConfig, atePort2.Name+".BGP6.Route", tc.port2v6Prefix, advertisedRoutesv6PrefixLen, tc.rpPolicy, tc.metricValue) + validateOTGBgpPrefixV4AndASLocalPrefMED(t, otg, dut, otgConfig, atePort1.Name+".BGP4.peer", tc.port1v4Prefix, advertisedRoutesv4PrefixLen, tc.rpPolicy, tc.metricValue) + validateOTGBgpPrefixV6AndASLocalPrefMED(t, otg, dut, otgConfig, atePort1.Name+".BGP6.peer", tc.port1v6Prefix, advertisedRoutesv6PrefixLen, tc.rpPolicy, tc.metricValue) + validateOTGBgpPrefixV4AndASLocalPrefMED(t, otg, dut, otgConfig, atePort2.Name+".BGP4.peer", tc.port2v4Prefix, advertisedRoutesv4PrefixLen, tc.rpPolicy, tc.metricValue) + validateOTGBgpPrefixV6AndASLocalPrefMED(t, otg, dut, otgConfig, atePort2.Name+".BGP6.peer", tc.port2v6Prefix, advertisedRoutesv6PrefixLen, tc.rpPolicy, tc.metricValue) }) } } + +func validateImportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, prefix string, metricValue uint32) { + dni := deviations.DefaultNetworkInstance(dut) + bgpRIBPath := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Rib() + locRib := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AfiSafi_Ipv4Unicast_LocRib](t, dut, bgpRIBPath.AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Ipv4Unicast().LocRib().State()) + found := false + for k, lr := range locRib.Route { + prefixAddr := strings.Split(lr.GetPrefix(), "/") + if prefixAddr[0] == prefix { + found = true + t.Logf("Found Route(prefix %s, origin: %v, pathid: %d) => %s", k.Prefix, k.Origin, k.PathId, lr.GetPrefix()) + attrSet := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet](t, dut, bgpRIBPath.AttrSet(lr.GetAttrIndex()).State()) + if attrSet == nil || attrSet.GetLocalPref() != metricValue { + t.Errorf("No local pref found for prefix %s", prefix) + } + break + } + } + + if !found { + t.Errorf("No Route found for prefix %s", prefix) + } +} + +func validateImportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice, prefix string, metricValue uint32) { + dni := deviations.DefaultNetworkInstance(dut) + bgpRIBPath := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Rib() + locRib := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AfiSafi_Ipv6Unicast_LocRib](t, dut, bgpRIBPath.AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Ipv6Unicast().LocRib().State()) + found := false + for k, lr := range locRib.Route { + prefixAddr := strings.Split(lr.GetPrefix(), "/") + if prefixAddr[0] == prefix { + found = true + t.Logf("Found Route(prefix %s, origin: %v, pathid: %d) => %s", k.Prefix, k.Origin, k.PathId, lr.GetPrefix()) + attrSet := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet](t, dut, bgpRIBPath.AttrSet(lr.GetAttrIndex()).State()) + if attrSet == nil || attrSet.GetLocalPref() != metricValue { + t.Errorf("No local pref found for prefix %s", prefix) + } + break + } + } + + if !found { + t.Errorf("No Route found for prefix %s", prefix) + } +} diff --git a/feature/bgp/policybase/otg_tests/actions_MED_LocPref_prepend_flow_control/metadata.textproto b/feature/bgp/policybase/otg_tests/actions_med_localpref_prepend_flow_control/metadata.textproto similarity index 99% rename from feature/bgp/policybase/otg_tests/actions_MED_LocPref_prepend_flow_control/metadata.textproto rename to feature/bgp/policybase/otg_tests/actions_med_localpref_prepend_flow_control/metadata.textproto index 87b9598040b..0ad0280524f 100644 --- a/feature/bgp/policybase/otg_tests/actions_MED_LocPref_prepend_flow_control/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/actions_med_localpref_prepend_flow_control/metadata.textproto @@ -36,3 +36,4 @@ platform_exceptions: { default_network_instance: "default" } } + diff --git a/feature/bgp/policybase/otg_tests/aspath_and_community_test/aspath_and_community_test.go b/feature/bgp/policybase/otg_tests/aspath_and_community_test/aspath_and_community_test.go index e08a901bdaf..4394d2c4248 100644 --- a/feature/bgp/policybase/otg_tests/aspath_and_community_test/aspath_and_community_test.go +++ b/feature/bgp/policybase/otg_tests/aspath_and_community_test/aspath_and_community_test.go @@ -15,7 +15,6 @@ package aspath_and_community_test import ( - "sort" "testing" "time" @@ -139,8 +138,6 @@ func configureOTG(t *testing.T, bs *cfgplugins.BGPSession, prefixesV4 [][]string } devices := bs.ATETop.Devices().Items() - byName := func(i, j int) bool { return devices[i].Name() < devices[j].Name() } - sort.Slice(devices, byName) ipv4 := devices[1].Ethernets().Items()[0].Ipv4Addresses().Items()[0] bgp4Peer := devices[1].Bgp().Ipv4Interfaces().Items()[0].Peers().Items()[0] @@ -243,9 +240,8 @@ func verifyTraffic(t *testing.T, ate *ondatra.ATEDevice, ports int, testResults func TestCommunitySet(t *testing.T) { testResults := [5]bool{true, true, true, false, false} - bs := cfgplugins.NewBGPSession(t, cfgplugins.PortCount2) - bs.WithEBGP(t, oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, true, true) - bs.WithEBGP(t, oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST, true, true) + bs := cfgplugins.NewBGPSession(t, cfgplugins.PortCount2, nil) + bs.WithEBGP(t, []oc.E_BgpTypes_AFI_SAFI_TYPE{oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST}, []string{"port2"}, true, true) configureOTG(t, bs, prefixesV4, prefixesV6) bs.PushAndStart(t) diff --git a/feature/bgp/policybase/otg_tests/aspath_test/aspath_test.go b/feature/bgp/policybase/otg_tests/aspath_test/aspath_test.go index b5b05f93d94..e9af47dd567 100644 --- a/feature/bgp/policybase/otg_tests/aspath_test/aspath_test.go +++ b/feature/bgp/policybase/otg_tests/aspath_test/aspath_test.go @@ -15,7 +15,6 @@ package aspath_test import ( - "sort" "testing" "time" @@ -91,8 +90,6 @@ func configureImportBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, func configureOTG(t *testing.T, bs *cfgplugins.BGPSession, prefixesV4 [][]string, prefixesV6 [][]string, aspathMembers [][]uint32) { devices := bs.ATETop.Devices().Items() - byName := func(i, j int) bool { return devices[i].Name() < devices[j].Name() } - sort.Slice(devices, byName) ipv4 := devices[1].Ethernets().Items()[0].Ipv4Addresses().Items()[0] bgp4Peer := devices[1].Bgp().Ipv4Interfaces().Items()[0].Peers().Items()[0] @@ -186,10 +183,9 @@ type testCase struct { testResults [6]bool } -func TestCommunitySet(t *testing.T) { - bs := cfgplugins.NewBGPSession(t, cfgplugins.PortCount2) - bs.WithEBGP(t, oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, true, true) - bs.WithEBGP(t, oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST, true, true) +func TestAsPathSet(t *testing.T) { + bs := cfgplugins.NewBGPSession(t, cfgplugins.PortCount2, nil) + bs.WithEBGP(t, []oc.E_BgpTypes_AFI_SAFI_TYPE{oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST}, []string{"port2"}, true, true) var aspathMembers = [][]uint32{ {100, 200, 300}, diff --git a/feature/bgp/policybase/otg_tests/chained_policies_test/README.md b/feature/bgp/policybase/otg_tests/chained_policies_test/README.md index 0fcba8d6e16..eab34543721 100644 --- a/feature/bgp/policybase/otg_tests/chained_policies_test/README.md +++ b/feature/bgp/policybase/otg_tests/chained_policies_test/README.md @@ -98,8 +98,6 @@ For each section of configuration below, prepare a gnmi.SetBatch with all the c * /routing-policy/policy-definitions/policy-definition/config/name * For routing-policy ```asp-policy-v4``` configure a statement with the name ```asp-statement-v4``` * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name -* For routing-policy ```asp-policy-v4``` statement ```asp-statement-v4``` set policy-result as ```ACCEPT_ROUTE``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result ##### Configure BGP actions to prepend AS * For routing-policy ```asp-policy-v4``` statement ```asp-statement-v4``` set AS-PATH prepend to the ASN of the DUT * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-as-path-prepend/config/asn @@ -195,8 +193,6 @@ For each section of configuration below, prepare a gnmi.SetBatch with all the c * /routing-policy/policy-definitions/policy-definition/config/name * For routing-policy ```asp-policy-v6``` configure a statement with the name ```asp-statement-v6``` * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name -* For routing-policy ```asp-policy-v6``` statement ```asp-statement-v6``` set policy-result as ```ACCEPT_ROUTE``` - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result ##### Configure BGP actions to prepend AS * For routing-policy ```asp-policy-v6``` statement ```asp-statement-v6``` set AS-PATH prepend to the ASN of the DUT * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-as-path-prepend/config/asn diff --git a/feature/bgp/policybase/otg_tests/chained_policies_test/chained_policies_test.go b/feature/bgp/policybase/otg_tests/chained_policies_test/chained_policies_test.go index 847c8e3807f..a1d1d75183c 100644 --- a/feature/bgp/policybase/otg_tests/chained_policies_test/chained_policies_test.go +++ b/feature/bgp/policybase/otg_tests/chained_policies_test/chained_policies_test.go @@ -43,13 +43,14 @@ const ( v42Route = "198.51.100.0" v42TrafficStart = "198.51.100.1" v4RoutePrefix = uint32(24) - v61Route = "2001:db8:128:128::0" + v61Route = "2001:db8:128:128::" v61TrafficStart = "2001:db8:128:128::1" - v62Route = "2001:db8:128:129::0" + v62Route = "2001:db8:128:129::" v62TrafficStart = "2001:db8:128:129::1" v6RoutePrefix = uint32(64) dutAS = uint32(65656) - ateAS = uint32(65657) + ateAS1 = uint32(65657) + ateAS2 = uint32(65658) bgpName = "BGP" maskLenExact = "exact" localPref = 200 @@ -74,6 +75,8 @@ const ( v6ASPStatement = "asp-statement-v6" v6MedPolicy = "med-policy-v6" v6MedStatement = "med-statement-v6" + peerGrpNamev4 = "BGP-PEER-GROUP-V4" + peerGrpNamev6 = "BGP-PEER-GROUP-V6" ) var ( @@ -145,7 +148,7 @@ type testData struct { type testCase struct { name string desc string - applyPolicy func(t *testing.T, dut *ondatra.DUTDevice) + applyPolicy func(t *testing.T, dut *ondatra.DUTDevice, operation string) validate func(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) ipv4 bool flowConfig flowConfig @@ -218,8 +221,10 @@ func TestBGPChainedPolicies(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { t.Logf("Description: %s", tc.desc) - tc.applyPolicy(t, dut) + tc.applyPolicy(t, dut, "set") + defer tc.applyPolicy(t, dut, "delete") tc.validate(t, dut, ate) + if tc.ipv4 { createFlow(t, td, tc.flowConfig) checkTraffic(t, td, v4Flow) @@ -231,7 +236,8 @@ func TestBGPChainedPolicies(t *testing.T) { } } -func configureImportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { +func configureImportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, operation string) { + batch := &gnmi.SetBatch{} root := &oc.Root{} rp := root.GetOrCreateRoutingPolicy() pdef1 := rp.GetOrCreatePolicyDefinition(v4PrefixPolicy) @@ -257,14 +263,23 @@ func configureImportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { } stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) stmt2.GetOrCreateActions().GetOrCreateBgpActions().SetSetLocalPref(localPref) - gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + if operation == "set" { + gnmi.BatchReplace(batch, gnmi.OC().RoutingPolicy().Config(), rp) + } else if operation == "delete" { + gnmi.BatchDelete(batch, gnmi.OC().RoutingPolicy().Config()) + } dni := deviations.DefaultNetworkInstance(dut) path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(atePort1.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() policy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(atePort1.IPv4).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateApplyPolicy() policy.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) policy.SetImportPolicy([]string{v4PrefixPolicy, v4LPPolicy}) - gnmi.Replace(t, dut, path.Config(), policy) + if operation == "set" { + gnmi.BatchReplace(batch, path.Config(), policy) + } else if operation == "delete" { + gnmi.BatchDelete(batch, path.Config()) + } + batch.Set(t, dut) } func validateImportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { @@ -276,27 +291,30 @@ func validateImportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, ate *onda t.Errorf("ImportPolicy = %v, want %v", importPolicies, []string{v4PrefixPolicy, v4LPPolicy}) } - bgpRIBPath := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Rib() - locRib := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AfiSafi_Ipv4Unicast_LocRib](t, dut, bgpRIBPath.AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Ipv4Unicast().LocRib().State()) - found := false - for k, lr := range locRib.Route { - if lr.GetPrefix() == advertisedIPv41.address { - found = true - t.Logf("Found Route(prefix %s, origin: %v, pathid: %d) => %s", k.Prefix, k.Origin, k.PathId, lr.GetPrefix()) - attrSet := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet](t, dut, bgpRIBPath.AttrSet(lr.GetAttrIndex()).State()) - if attrSet == nil || attrSet.GetLocalPref() != localPref { - t.Errorf("No local pref found for prefix %s", advertisedIPv41.address) + if !deviations.BGPRibOcPathUnsupported(dut) { + bgpRIBPath := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Rib() + locRib := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AfiSafi_Ipv4Unicast_LocRib](t, dut, bgpRIBPath.AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Ipv4Unicast().LocRib().State()) + found := false + for k, lr := range locRib.Route { + if lr.GetPrefix() == advertisedIPv41.address { + found = true + t.Logf("Found Route(prefix %s, origin: %v, pathid: %d) => %s", k.Prefix, k.Origin, k.PathId, lr.GetPrefix()) + attrSet := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet](t, dut, bgpRIBPath.AttrSet(lr.GetAttrIndex()).State()) + if attrSet == nil || attrSet.GetLocalPref() != localPref { + t.Errorf("No local pref found for prefix %s", advertisedIPv41.address) + } + break } - break } - } - if !found { - t.Errorf("No Route found for prefix %s", advertisedIPv41.address) + if !found { + t.Errorf("No Route found for prefix %s", advertisedIPv41.address) + } } } -func configureExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { +func configureExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, operation string) { + batch := &gnmi.SetBatch{} root := &oc.Root{} rp := root.GetOrCreateRoutingPolicy() pdef1 := rp.GetOrCreatePolicyDefinition(v4ASPPolicy) @@ -304,7 +322,6 @@ func configureExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { if err != nil { t.Fatalf("AppendNewStatement(%s) failed: %v", v4ASPStatement, err) } - stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetAsPathPrepend().SetAsn(dutAS) pdef2 := rp.GetOrCreatePolicyDefinition(v4MedPolicy) @@ -314,14 +331,23 @@ func configureExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { } stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) stmt2.GetOrCreateActions().GetOrCreateBgpActions().SetSetMed(oc.UnionUint32(med)) - gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + if operation == "set" { + gnmi.BatchReplace(batch, gnmi.OC().RoutingPolicy().Config(), rp) + } else if operation == "delete" { + gnmi.BatchDelete(batch, gnmi.OC().RoutingPolicy().Config()) + } dni := deviations.DefaultNetworkInstance(dut) path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(atePort1.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() policy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(atePort1.IPv4).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateApplyPolicy() policy.SetDefaultExportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) policy.SetExportPolicy([]string{v4ASPPolicy, v4MedPolicy}) - gnmi.Replace(t, dut, path.Config(), policy) + if operation == "set" { + gnmi.BatchReplace(batch, path.Config(), policy) + } else if operation == "delete" { + gnmi.BatchDelete(batch, path.Config()) + } + batch.Set(t, dut) } func validateExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { @@ -333,7 +359,7 @@ func validateExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, ate *onda t.Errorf("ExportPolicy = %v, want %v", exportPolicies, []string{v4PrefixPolicy, v4LPPolicy}) } - bgpPrefixes := gnmi.GetAll[*otgtelemetry.BgpPeer_UnicastIpv4Prefix](t, ate.OTG(), gnmi.OTG().BgpPeer("v4-bgpNet-dev2").UnicastIpv4PrefixAny().State()) + bgpPrefixes := gnmi.GetAll[*otgtelemetry.BgpPeer_UnicastIpv4Prefix](t, ate.OTG(), gnmi.OTG().BgpPeer("atePort1.BGP4.peer").UnicastIpv4PrefixAny().State()) found := false for _, bgpPrefix := range bgpPrefixes { if bgpPrefix.Address != nil && bgpPrefix.GetAddress() == v42Route && @@ -364,7 +390,8 @@ func validateExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, ate *onda } } -func configureImportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice) { +func configureImportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice, operation string) { + batch := &gnmi.SetBatch{} root := &oc.Root{} rp := root.GetOrCreateRoutingPolicy() pdef1 := rp.GetOrCreatePolicyDefinition(v6PrefixPolicy) @@ -390,14 +417,23 @@ func configureImportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice) { } stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) stmt2.GetOrCreateActions().GetOrCreateBgpActions().SetSetLocalPref(localPref) - gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + if operation == "set" { + gnmi.BatchReplace(batch, gnmi.OC().RoutingPolicy().Config(), rp) + } else if operation == "delete" { + gnmi.BatchDelete(batch, gnmi.OC().RoutingPolicy().Config()) + } dni := deviations.DefaultNetworkInstance(dut) path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(atePort1.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy() policy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(atePort1.IPv6).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateApplyPolicy() policy.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) policy.SetImportPolicy([]string{v6PrefixPolicy, v6LPPolicy}) - gnmi.Replace(t, dut, path.Config(), policy) + if operation == "set" { + gnmi.BatchReplace(batch, path.Config(), policy) + } else if operation == "delete" { + gnmi.BatchDelete(batch, path.Config()) + } + batch.Set(t, dut) } func validateImportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { @@ -408,28 +444,30 @@ func validateImportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice, ate *on if len(importPolicies) != 2 { t.Errorf("ImportPolicy = %v, want %v", importPolicies, []string{v6PrefixPolicy, v6LPPolicy}) } - - bgpRIBPath := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Rib() - locRib := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AfiSafi_Ipv6Unicast_LocRib](t, dut, bgpRIBPath.AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Ipv6Unicast().LocRib().State()) - found := false - for k, lr := range locRib.Route { - if lr.GetPrefix() == advertisedIPv61.address { - found = true - t.Logf("Found Route(prefix %s, origin: %v, pathid: %d) => %s", k.Prefix, k.Origin, k.PathId, lr.GetPrefix()) - attrSet := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet](t, dut, bgpRIBPath.AttrSet(lr.GetAttrIndex()).State()) - if attrSet == nil || attrSet.GetLocalPref() != localPref { - t.Errorf("No local pref found for prefix %s", advertisedIPv61.address) + if !deviations.BGPRibOcPathUnsupported(dut) { + bgpRIBPath := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Rib() + locRib := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AfiSafi_Ipv6Unicast_LocRib](t, dut, bgpRIBPath.AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Ipv6Unicast().LocRib().State()) + found := false + for k, lr := range locRib.Route { + if lr.GetPrefix() == advertisedIPv61.address { + found = true + t.Logf("Found Route(prefix %s, origin: %v, pathid: %d) => %s", k.Prefix, k.Origin, k.PathId, lr.GetPrefix()) + attrSet := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet](t, dut, bgpRIBPath.AttrSet(lr.GetAttrIndex()).State()) + if attrSet == nil || attrSet.GetLocalPref() != localPref { + t.Errorf("No local pref found for prefix %s", advertisedIPv61.address) + } + break } - break } - } - if !found { - t.Errorf("No Route found for prefix %s", advertisedIPv61.address) + if !found { + t.Errorf("No Route found for prefix %s", advertisedIPv61.address) + } } } -func configureExportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice) { +func configureExportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice, operation string) { + batch := &gnmi.SetBatch{} root := &oc.Root{} rp := root.GetOrCreateRoutingPolicy() pdef1 := rp.GetOrCreatePolicyDefinition(v6ASPPolicy) @@ -437,7 +475,6 @@ func configureExportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice) { if err != nil { t.Fatalf("AppendNewStatement(%s) failed: %v", v6ASPStatement, err) } - stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetAsPathPrepend().SetAsn(dutAS) pdef2 := rp.GetOrCreatePolicyDefinition(v6MedPolicy) @@ -447,14 +484,23 @@ func configureExportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice) { } stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) stmt2.GetOrCreateActions().GetOrCreateBgpActions().SetSetMed(oc.UnionUint32(med)) - gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + if operation == "set" { + gnmi.BatchReplace(batch, gnmi.OC().RoutingPolicy().Config(), rp) + } else if operation == "delete" { + gnmi.BatchDelete(batch, gnmi.OC().RoutingPolicy().Config()) + } dni := deviations.DefaultNetworkInstance(dut) path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(atePort1.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy() policy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(atePort1.IPv6).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateApplyPolicy() policy.SetDefaultExportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) policy.SetExportPolicy([]string{v6ASPPolicy, v6MedPolicy}) - gnmi.Replace(t, dut, path.Config(), policy) + if operation == "set" { + gnmi.BatchReplace(batch, path.Config(), policy) + } else if operation == "delete" { + gnmi.BatchDelete(batch, path.Config()) + } + batch.Set(t, dut) } func validateExportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { @@ -466,7 +512,7 @@ func validateExportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice, ate *on t.Errorf("ExportPolicy = %v, want %v", exportPolicies, []string{v6PrefixPolicy, v6LPPolicy}) } - bgpPrefixes := gnmi.GetAll[*otgtelemetry.BgpPeer_UnicastIpv6Prefix](t, ate.OTG(), gnmi.OTG().BgpPeer("v6-bgpNet-dev2").UnicastIpv6PrefixAny().State()) + bgpPrefixes := gnmi.GetAll[*otgtelemetry.BgpPeer_UnicastIpv6Prefix](t, ate.OTG(), gnmi.OTG().BgpPeer("atePort1.BGP6.peer").UnicastIpv6PrefixAny().State()) found := false for _, bgpPrefix := range bgpPrefixes { if bgpPrefix.Address != nil && bgpPrefix.GetAddress() == v62Route && @@ -578,30 +624,40 @@ func (td *testData) advertiseRoutesWithEBGP(t *testing.T) { g.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) g.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + pgv4 := bgp.GetOrCreatePeerGroup(peerGrpNamev4) + pgv4.PeerGroupName = ygot.String(peerGrpNamev4) + pgv6 := bgp.GetOrCreatePeerGroup(peerGrpNamev6) + pgv6.PeerGroupName = ygot.String(peerGrpNamev6) nV41 := bgp.GetOrCreateNeighbor(atePort1.IPv4) - nV41.SetPeerAs(ateAS) + nV41.SetPeerAs(ateAS1) nV41.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) + nV41.PeerGroup = ygot.String(peerGrpNamev4) nV42 := bgp.GetOrCreateNeighbor(atePort2.IPv4) - nV42.SetPeerAs(ateAS) + nV42.SetPeerAs(ateAS2) nV42.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) + nV42.PeerGroup = ygot.String(peerGrpNamev4) nV61 := bgp.GetOrCreateNeighbor(atePort1.IPv6) - nV61.SetPeerAs(ateAS) + nV61.SetPeerAs(ateAS1) nV61.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + nV61.PeerGroup = ygot.String(peerGrpNamev6) nV62 := bgp.GetOrCreateNeighbor(atePort2.IPv6) - nV62.SetPeerAs(ateAS) + nV62.SetPeerAs(ateAS2) nV62.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + nV62.PeerGroup = ygot.String(peerGrpNamev6) gnmi.Update(t, td.dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(td.dut)).Config(), ni) // configure eBGP on OTG port1 ipv41 := td.otgP1.Ethernets().Items()[0].Ipv4Addresses().Items()[0] dev1BGP := td.otgP1.Bgp().SetRouterId(atePort1.IPv4) bgp4Peer1 := dev1BGP.Ipv4Interfaces().Add().SetIpv4Name(ipv41.Name()).Peers().Add().SetName(td.otgP1.Name() + ".BGP4.peer") - bgp4Peer1.SetPeerAddress(dutPort1.IPv4).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + bgp4Peer1.SetPeerAddress(dutPort1.IPv4).SetAsNumber(ateAS1).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + bgp4Peer1.LearnedInformationFilter().SetUnicastIpv4Prefix(true) ipv61 := td.otgP1.Ethernets().Items()[0].Ipv6Addresses().Items()[0] bgp6Peer1 := dev1BGP.Ipv6Interfaces().Add().SetIpv6Name(ipv61.Name()).Peers().Add().SetName(td.otgP1.Name() + ".BGP6.peer") - bgp6Peer1.SetPeerAddress(dutPort1.IPv6).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + bgp6Peer1.SetPeerAddress(dutPort1.IPv6).SetAsNumber(ateAS1).SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + bgp6Peer1.LearnedInformationFilter().SetUnicastIpv6Prefix(true) // configure emulated network on ATE port1 netv41 := bgp4Peer1.V4Routes().Add().SetName("v4-bgpNet-dev1") @@ -613,11 +669,13 @@ func (td *testData) advertiseRoutesWithEBGP(t *testing.T) { ipv42 := td.otgP2.Ethernets().Items()[0].Ipv4Addresses().Items()[0] dev2BGP := td.otgP2.Bgp().SetRouterId(atePort2.IPv4) bgp4Peer2 := dev2BGP.Ipv4Interfaces().Add().SetIpv4Name(ipv42.Name()).Peers().Add().SetName(td.otgP2.Name() + ".BGP4.peer") - bgp4Peer2.SetPeerAddress(dutPort2.IPv4).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + bgp4Peer2.SetPeerAddress(dutPort2.IPv4).SetAsNumber(ateAS2).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + bgp4Peer2.LearnedInformationFilter().SetUnicastIpv4Prefix(true) ipv62 := td.otgP2.Ethernets().Items()[0].Ipv6Addresses().Items()[0] bgp6Peer2 := dev2BGP.Ipv6Interfaces().Add().SetIpv6Name(ipv62.Name()).Peers().Add().SetName(td.otgP2.Name() + ".BGP6.peer") - bgp6Peer2.SetPeerAddress(dutPort2.IPv6).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + bgp6Peer2.SetPeerAddress(dutPort2.IPv6).SetAsNumber(ateAS2).SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + bgp6Peer2.LearnedInformationFilter().SetUnicastIpv6Prefix(true) // configure emulated network on ATE port2 netv42 := bgp4Peer2.V4Routes().Add().SetName("v4-bgpNet-dev2") diff --git a/feature/bgp/policybase/otg_tests/chained_policies_test/metadata.textproto b/feature/bgp/policybase/otg_tests/chained_policies_test/metadata.textproto index 1210b24610d..11bbfd9320d 100644 --- a/feature/bgp/policybase/otg_tests/chained_policies_test/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/chained_policies_test/metadata.textproto @@ -17,3 +17,11 @@ platform_exceptions: { skip_set_rp_match_set_options: true } } +platform_exceptions: { + platform: { + vendor: JUNIPER + } + deviations: { + bgp_rib_oc_path_unsupported: true + } +} diff --git a/feature/bgp/policybase/otg_tests/comm_match_action_test/bgp_comm_match_action_test.go b/feature/bgp/policybase/otg_tests/comm_match_action_test/bgp_comm_match_action_test.go new file mode 100644 index 00000000000..c7b1ba2c0ed --- /dev/null +++ b/feature/bgp/policybase/otg_tests/comm_match_action_test/bgp_comm_match_action_test.go @@ -0,0 +1,770 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bgp_comm_match_action_test + +import ( + "fmt" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/otgutils" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + otgtelemetry "github.com/openconfig/ondatra/gnmi/otg" + otg "github.com/openconfig/ondatra/otg" + "github.com/openconfig/ygnmi/ygnmi" + "github.com/openconfig/ygot/ygot" +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +const ( + trafficDuration = 1 * time.Minute + tolerancePct = 2 + peerGrpName = "BGP-PEER-GROUP" + dutAS = 65501 + ateAS = 65502 + plenIPv4 = 30 + plenIPv6 = 126 + acceptPolicy = "PERMIT-ALL" +) + +var ( + dutPort1 = attrs.Attributes{ + Desc: "DUT to ATE Port1", + IPv4: "192.0.2.1", + IPv6: "2001:db8::192:0:2:1", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + atePort1 = attrs.Attributes{ + Name: "atePort1", + IPv4: "192.0.2.2", + IPv6: "2001:db8::192:0:2:2", + MAC: "02:00:01:01:01:01", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + dutPort2 = attrs.Attributes{ + Desc: "DUT to ATE Port2", + IPv4: "192.0.2.5", + IPv6: "2001:db8::192:0:2:5", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + atePort2 = attrs.Attributes{ + Name: "atePort2", + IPv4: "192.0.2.6", + IPv6: "2001:db8::192:0:2:6", + MAC: "02:00:02:01:01:01", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + + ebgp1NbrV4 = &bgpNeighbor{ + nbrAddr: atePort1.IPv4, + isV4: true, + afiSafi: oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, + as: ateAS} + ebgp1NbrV6 = &bgpNeighbor{ + nbrAddr: atePort1.IPv6, + isV4: false, + afiSafi: oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST, + as: ateAS} + ebgp2NbrV4 = &bgpNeighbor{ + nbrAddr: atePort2.IPv4, + isV4: true, + afiSafi: oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, + as: ateAS} + ebgp2NbrV6 = &bgpNeighbor{ + nbrAddr: atePort2.IPv6, + isV4: false, + afiSafi: oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST, + as: ateAS} + ebgpNbrs = []*bgpNeighbor{ebgp1NbrV4, ebgp2NbrV4, ebgp1NbrV6, ebgp2NbrV6} + + routes = map[string]*route{ + "prefix-set-1": { + prefixesV4: []string{"198.51.100.0", "198.51.100.4"}, + prefixesV6: []string{"2048:db1:64:64::", "2048:db1:64:64::4"}, + communityMembers: nil, + }, + "prefix-set-2": { + prefixesV4: []string{"198.51.100.8", "198.51.100.12"}, + prefixesV6: []string{"2048:db1:64:64::8", "2048:db1:64:64::c"}, + communityMembers: [][]int{{5, 5}, {6, 6}}, + }, + } + + communitySets = []communitySet{ + { + name: "match_std_comms", + members: []string{"5:5"}, + }, + { + name: "add_std_comms", + members: []string{"10:10", "20:20", "30:30"}, + }, + } +) + +type route struct { + prefixesV4 []string + prefixesV6 []string + communityMembers [][]int +} + +type communitySet struct { + name string + members []string +} + +type bgpNeighbor struct { + as uint32 + nbrAddr string + isV4 bool + afiSafi oc.E_BgpTypes_AFI_SAFI_TYPE +} + +func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + dc := gnmi.OC() + i1 := dutPort1.NewOCInterface(dut.Port(t, "port1").Name(), dut) + gnmi.Replace(t, dut, dc.Interface(i1.GetName()).Config(), i1) + + i2 := dutPort2.NewOCInterface(dut.Port(t, "port2").Name(), dut) + gnmi.Replace(t, dut, dc.Interface(i2.GetName()).Config(), i2) + fptest.ConfigureDefaultNetworkInstance(t, dut) + + if deviations.ExplicitPortSpeed(dut) { + fptest.SetPortSpeed(t, dut.Port(t, "port1")) + fptest.SetPortSpeed(t, dut.Port(t, "port2")) + } + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + fptest.AssignToNetworkInstance(t, dut, dut.Port(t, "port1").Name(), deviations.DefaultNetworkInstance(dut), 0) + fptest.AssignToNetworkInstance(t, dut, dut.Port(t, "port2").Name(), deviations.DefaultNetworkInstance(dut), 0) + } + + // Configure PERMIT_ALL Policy + d := &oc.Root{} + rp := d.GetOrCreateRoutingPolicy() + pdef := rp.GetOrCreatePolicyDefinition(acceptPolicy) + stmt, _ := pdef.AppendNewStatement("10") + stmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + + // Configure Community Sets on DUT + for _, communitySet := range communitySets { + configureCommunitySet(t, dut, communitySet) + } +} + +func bgpCreateNbr(localAs, peerAs uint32, dut *ondatra.DUTDevice) *oc.NetworkInstance_Protocol { + + // Configure BGP on DUT + dutOcRoot := &oc.Root{} + ni1 := dutOcRoot.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + niProto := ni1.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + bgp := niProto.GetOrCreateBgp() + + global := bgp.GetOrCreateGlobal() + global.RouterId = ygot.String(dutPort1.IPv4) + global.As = ygot.Uint32(localAs) + global.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) + global.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + + pg := bgp.GetOrCreatePeerGroup(peerGrpName) + pg.PeerAs = ygot.Uint32(ateAS) + pg.PeerGroupName = ygot.String(peerGrpName) + as4 := pg.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + as4.Enabled = ygot.Bool(true) + as6 := pg.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + as6.Enabled = ygot.Bool(true) + + for _, nbr := range ebgpNbrs { + bgpNbr := bgp.GetOrCreateNeighbor(nbr.nbrAddr) + bgpNbr.PeerGroup = ygot.String(peerGrpName) + bgpNbr.PeerAs = ygot.Uint32(nbr.as) + bgpNbr.Enabled = ygot.Bool(true) + + if nbr.isV4 == true { + af4 := bgpNbr.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + af4.Enabled = ygot.Bool(true) + if nbr.nbrAddr == atePort2.IPv4 { + af4.GetOrCreateApplyPolicy().ExportPolicy = []string{acceptPolicy} + } + af6 := bgpNbr.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + af6.Enabled = ygot.Bool(false) + } else { + af4 := bgpNbr.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + af4.Enabled = ygot.Bool(false) + af6 := bgpNbr.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + af6.Enabled = ygot.Bool(true) + if nbr.nbrAddr == atePort2.IPv6 { + af6.GetOrCreateApplyPolicy().ExportPolicy = []string{acceptPolicy} + } + } + } + return niProto +} + +func verifyBgpState(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + + t.Logf("Waiting for BGP neighbor to establish...") + for _, nbr := range ebgpNbrs { + nbrPath := bgpPath.Neighbor(nbr.nbrAddr) + var status *ygnmi.Value[oc.E_Bgp_Neighbor_SessionState] + status, ok := gnmi.Watch(t, dut, nbrPath.SessionState().State(), time.Minute, func(val *ygnmi.Value[oc.E_Bgp_Neighbor_SessionState]) bool { + state, ok := val.Val() + return ok && state == oc.Bgp_Neighbor_SessionState_ESTABLISHED + }).Await(t) + if !ok { + fptest.LogQuery(t, "BGP reported state", nbrPath.State(), gnmi.Get(t, dut, nbrPath.State())) + t.Fatal("No BGP neighbor formed") + } + state, _ := status.Val() + t.Logf("BGP adjacency for %s: %v", nbr.nbrAddr, state) + if want := oc.Bgp_Neighbor_SessionState_ESTABLISHED; state != want { + t.Errorf("BGP peer %s status got %d, want %d", nbr.nbrAddr, state, want) + } + } +} + +func configureCommunitySet(t *testing.T, dut *ondatra.DUTDevice, communitySet communitySet) { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + commSet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(communitySet.name) + var commMemberUnion []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union + for _, commMember := range communitySet.members { + commMemberUnion = append(commMemberUnion, oc.UnionString(commMember)) + } + commSet.SetCommunityMember(commMemberUnion) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) +} + +func configureRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, policyName string, nbr *bgpNeighbor, pgName string) { + d := &oc.Root{} + rp := d.GetOrCreateRoutingPolicy() + batchConfig := &gnmi.SetBatch{} + + var pdef *oc.RoutingPolicy_PolicyDefinition + + switch policyName { + case "add_std_comms": + pdef = rp.GetOrCreatePolicyDefinition(policyName) + stmt1, _ := pdef.AppendNewStatement("add_std_comms") + sc := stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetCommunity() + sc.GetOrCreateReference().SetCommunitySetRef("add_std_comms") + sc.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) + if !deviations.BgpActionsSetCommunityMethodUnsupported(dut) { + sc.SetMethod(oc.SetCommunity_Method_REFERENCE) + } + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_NEXT_STATEMENT) + stmt2, _ := pdef.AppendNewStatement("accept_all_routes") + stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + case "match_and_add_comms": + pdef = rp.GetOrCreatePolicyDefinition(policyName) + stmt1, _ := pdef.AppendNewStatement("match_and_add_std_comms") + if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { + stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet("match_std_comms") + ds := rp.GetOrCreateDefinedSets() + cs := ds.GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet("match_std_comms") + cs.SetMatchSetOptions(oc.BgpPolicy_MatchSetOptionsType_ANY) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().DefinedSets().Config(), ds) + } else { + cs := stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet() + cs.SetCommunitySet("match_std_comms") + cs.SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsType_ANY) + } + sc := stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetCommunity() + sc.GetOrCreateReference().SetCommunitySetRef("add_std_comms") + sc.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) + if !deviations.BgpActionsSetCommunityMethodUnsupported(dut) { + sc.SetMethod(oc.SetCommunity_Method_REFERENCE) + } + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_NEXT_STATEMENT) + stmt2, _ := pdef.AppendNewStatement("accept_all_routes") + stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + } + + if pdef != nil { + gnmi.BatchReplace(batchConfig, gnmi.OC().RoutingPolicy().PolicyDefinition(policyName).Config(), pdef) + } + + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + if nbr != nil { + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(nbr.nbrAddr).AfiSafi(nbr.afiSafi).ApplyPolicy().ImportPolicy().Config(), []string{policyName}) + } + if pgName != "" { + gnmi.BatchReplace(batchConfig, bgpPath.PeerGroup(pgName).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy().ImportPolicy().Config(), []string{policyName}) + gnmi.BatchReplace(batchConfig, bgpPath.PeerGroup(pgName).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy().ImportPolicy().Config(), []string{policyName}) + gnmi.BatchDelete(batchConfig, bgpPath.Neighbor(ebgp1NbrV4.nbrAddr).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy().ImportPolicy().Config()) + gnmi.BatchDelete(batchConfig, bgpPath.Neighbor(ebgp1NbrV6.nbrAddr).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy().ImportPolicy().Config()) + } + + batchConfig.Set(t, dut) +} + +func configureOTG(t *testing.T, otg *otg.OTG) gosnappi.Config { + t.Helper() + config := gosnappi.NewConfig() + port1 := config.Ports().Add().SetName("port1") + port2 := config.Ports().Add().SetName("port2") + + // Port1 Configuration. + iDut1Dev := config.Devices().Add().SetName(atePort1.Name) + iDut1Eth := iDut1Dev.Ethernets().Add().SetName(atePort1.Name + ".Eth").SetMac(atePort1.MAC) + iDut1Eth.Connection().SetPortName(port1.Name()) + iDut1Ipv4 := iDut1Eth.Ipv4Addresses().Add().SetName(atePort1.Name + ".IPv4") + iDut1Ipv4.SetAddress(atePort1.IPv4).SetGateway(dutPort1.IPv4).SetPrefix(uint32(atePort1.IPv4Len)) + iDut1Ipv6 := iDut1Eth.Ipv6Addresses().Add().SetName(atePort1.Name + ".IPv6") + iDut1Ipv6.SetAddress(atePort1.IPv6).SetGateway(dutPort1.IPv6).SetPrefix(uint32(atePort1.IPv6Len)) + + // Port2 Configuration. + iDut2Dev := config.Devices().Add().SetName(atePort2.Name) + iDut2Eth := iDut2Dev.Ethernets().Add().SetName(atePort2.Name + ".Eth").SetMac(atePort2.MAC) + iDut2Eth.Connection().SetPortName(port2.Name()) + iDut2Ipv4 := iDut2Eth.Ipv4Addresses().Add().SetName(atePort2.Name + ".IPv4") + iDut2Ipv4.SetAddress(atePort2.IPv4).SetGateway(dutPort2.IPv4).SetPrefix(uint32(atePort2.IPv4Len)) + iDut2Ipv6 := iDut2Eth.Ipv6Addresses().Add().SetName(atePort2.Name + ".IPv6") + iDut2Ipv6.SetAddress(atePort2.IPv6).SetGateway(dutPort2.IPv6).SetPrefix(uint32(atePort2.IPv6Len)) + + // eBGP v4 session on Port1. + iDut1Bgp := iDut1Dev.Bgp().SetRouterId(iDut1Ipv4.Address()) + iDut1Bgp4Peer := iDut1Bgp.Ipv4Interfaces().Add().SetIpv4Name(iDut1Ipv4.Name()).Peers().Add().SetName(atePort1.Name + ".BGP4.peer") + iDut1Bgp4Peer.SetPeerAddress(iDut1Ipv4.Gateway()).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + iDut1Bgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true) + // eBGP v6 session on Port1. + iDut1Bgp6Peer := iDut1Bgp.Ipv6Interfaces().Add().SetIpv6Name(iDut1Ipv6.Name()).Peers().Add().SetName(atePort1.Name + ".BGP6.peer") + iDut1Bgp6Peer.SetPeerAddress(iDut1Ipv6.Gateway()).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + iDut1Bgp6Peer.LearnedInformationFilter().SetUnicastIpv6Prefix(true) + + // eBGP v4 session on Port2. + iDut2Bgp := iDut2Dev.Bgp().SetRouterId(iDut2Ipv4.Address()) + iDut2Bgp4Peer := iDut2Bgp.Ipv4Interfaces().Add().SetIpv4Name(iDut2Ipv4.Name()).Peers().Add().SetName(atePort2.Name + ".BGP4.peer") + iDut2Bgp4Peer.SetPeerAddress(iDut2Ipv4.Gateway()).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + iDut2Bgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true) + // eBGP v6 session on Port2. + iDut2Bgp6Peer := iDut2Bgp.Ipv6Interfaces().Add().SetIpv6Name(iDut2Ipv6.Name()).Peers().Add().SetName(atePort2.Name + ".BGP6.peer") + iDut2Bgp6Peer.SetPeerAddress(iDut2Ipv6.Gateway()).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + iDut2Bgp6Peer.LearnedInformationFilter().SetUnicastIpv6Prefix(true) + + for key, sendRoute := range routes { + // eBGP V4 routes from Port1. + bgpNeti1Bgp4PeerRoutes := iDut1Bgp4Peer.V4Routes().Add().SetName(atePort1.Name + ".BGP4.Route." + key) + bgpNeti1Bgp4PeerRoutes.SetNextHopIpv4Address(iDut1Ipv4.Address()). + SetNextHopAddressType(gosnappi.BgpV4RouteRangeNextHopAddressType.IPV4). + SetNextHopMode(gosnappi.BgpV4RouteRangeNextHopMode.MANUAL) + + for _, prefixV4 := range sendRoute.prefixesV4 { + bgpNeti1Bgp4PeerRoutes.Addresses().Add().SetAddress(prefixV4).SetPrefix(plenIPv4) + } + + // eBGP V6 routes from Port1. + bgpNeti1Bgp6PeerRoutes := iDut1Bgp6Peer.V6Routes().Add().SetName(atePort1.Name + ".BGP6.Route." + key) + bgpNeti1Bgp6PeerRoutes.SetNextHopIpv6Address(iDut1Ipv6.Address()). + SetNextHopAddressType(gosnappi.BgpV6RouteRangeNextHopAddressType.IPV6). + SetNextHopMode(gosnappi.BgpV6RouteRangeNextHopMode.MANUAL) + + for _, prefixV6 := range sendRoute.prefixesV6 { + bgpNeti1Bgp6PeerRoutes.Addresses().Add().SetAddress(prefixV6).SetPrefix(plenIPv6) + } + + if sendRoute.communityMembers != nil { + for _, community := range sendRoute.communityMembers { + commV4 := bgpNeti1Bgp4PeerRoutes.Communities().Add() + commV4.SetType(gosnappi.BgpCommunityType.MANUAL_AS_NUMBER) + commV4.SetAsNumber(uint32(community[0])) + commV4.SetAsCustom(uint32(community[1])) + + commV6 := bgpNeti1Bgp6PeerRoutes.Communities().Add() + commV6.SetType(gosnappi.BgpCommunityType.MANUAL_AS_NUMBER) + commV6.SetAsNumber(uint32(community[0])) + commV6.SetAsCustom(uint32(community[1])) + } + } + } + + // ATE Traffic Configuration. + t.Logf("TestBGP:start ate Traffic config") + + var dstBgp4PeerRoutes, dst4Prefixes []string + for _, routeV4 := range iDut1Bgp4Peer.V4Routes().Items() { + dstBgp4PeerRoutes = append(dstBgp4PeerRoutes, routeV4.Name()) + for _, prefix := range routeV4.Addresses().Items() { + dst4Prefixes = append(dst4Prefixes, prefix.Address()) + } + } + flowipv4 := config.Flows().Add().SetName("bgpv4RoutesFlow") + flowipv4.Metrics().SetEnable(true) + flowipv4.TxRx().Device(). + SetTxNames([]string{iDut2Ipv4.Name()}). + SetRxNames(dstBgp4PeerRoutes) + flowipv4.Size().SetFixed(512) + flowipv4.Duration().FixedPackets().SetPackets(1000) + e1 := flowipv4.Packet().Add().Ethernet() + e1.Src().SetValue(iDut2Eth.Mac()) + v4 := flowipv4.Packet().Add().Ipv4() + v4.Src().SetValue(iDut2Ipv4.Address()) + v4.Dst().SetValues(dst4Prefixes) + + var dstBgp6PeerRoutes, dst6Prefixes []string + for _, routeV6 := range iDut1Bgp6Peer.V6Routes().Items() { + dstBgp6PeerRoutes = append(dstBgp6PeerRoutes, routeV6.Name()) + for _, prefix := range routeV6.Addresses().Items() { + dst6Prefixes = append(dst6Prefixes, prefix.Address()) + } + } + flowipv6 := config.Flows().Add().SetName("bgpv6RoutesFlow") + flowipv6.Metrics().SetEnable(true) + flowipv6.TxRx().Device(). + SetTxNames([]string{iDut2Ipv6.Name()}). + SetRxNames(dstBgp6PeerRoutes) + flowipv6.Size().SetFixed(512) + flowipv6.Duration().FixedPackets().SetPackets(1000) + e2 := flowipv6.Packet().Add().Ethernet() + e2.Src().SetValue(iDut2Eth.Mac()) + v6 := flowipv6.Packet().Add().Ipv6() + v6.Src().SetValue(iDut2Ipv6.Address()) + v6.Dst().SetValues(dst6Prefixes) + + otg.PushConfig(t, config) + otg.StartProtocols(t) + return config +} + +func sendTraffic(t *testing.T, otg *otg.OTG) { + t.Logf("Starting traffic") + otg.StartTraffic(t) + time.Sleep(trafficDuration) + t.Logf("Stop traffic") + otg.StopTraffic(t) +} + +func verifyTraffic(t *testing.T, ate *ondatra.ATEDevice, conf gosnappi.Config) { + otg := ate.OTG() + otgutils.LogFlowMetrics(t, otg, conf) + for _, flow := range conf.Flows().Items() { + recvMetric := gnmi.Get(t, otg, gnmi.OTG().Flow(flow.Name()).State()) + txPackets := float32(recvMetric.GetCounters().GetOutPkts()) + rxPackets := float32(recvMetric.GetCounters().GetInPkts()) + if txPackets == 0 { + t.Fatalf("TxPkts = 0, want > 0") + } + lostPackets := txPackets - rxPackets + lossPct := lostPackets * 100 / txPackets + if lossPct > tolerancePct { + t.Errorf("Traffic Loss Pct for Flow %s: got %v, want max %v pct failure", flow.Name(), lossPct, tolerancePct) + } else { + t.Logf("Traffic Test Passed! for flow %s", flow.Name()) + } + } +} + +func validateATEIPv4PrefixCommunitySet(t *testing.T, ate *ondatra.ATEDevice, bgpPeerName, subnet string, wantCommunitySet []string) { + otg := ate.OTG() + var gotCommunitySet []string + peerPath := gnmi.OTG().BgpPeer(bgpPeerName) + + _, ok := gnmi.Watch(t, + otg, + peerPath.UnicastIpv4Prefix(subnet, plenIPv4, otgtelemetry.UnicastIpv4Prefix_Origin_IGP, 0).State(), + time.Minute, + func(v *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv4Prefix]) bool { + prefix, ok := v.Val() + if ok { + gotCommunitySet = nil + for _, community := range prefix.Community { + gotCommunityNumber := community.GetCustomAsNumber() + gotCommunityValue := community.GetCustomAsValue() + gotCommunitySet = append(gotCommunitySet, fmt.Sprint(gotCommunityNumber)+":"+fmt.Sprint(gotCommunityValue)) + } + if cmp.Equal(gotCommunitySet, wantCommunitySet, cmpopts.SortSlices(func(a, b string) bool { return a < b })) { + t.Logf("ATE: Prefix %v learned with community %v", prefix.GetAddress(), gotCommunitySet) + return true + } + prefix.Community = nil + } + return false + }).Await(t) + + if !ok { + fptest.LogQuery(t, "ATE BGP Peer reported state", peerPath.State(), gnmi.Get(t, otg, peerPath.State())) + t.Errorf("ATE: Prefix %v got communities %v, want communities %v", subnet, gotCommunitySet, wantCommunitySet) + } +} + +func validateDutIPv4PrefixCommunitySet(t *testing.T, dut *ondatra.DUTDevice, bgpNbr *bgpNeighbor, subnet string, wantCommunitySet []string) { + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + statePath := bgpPath.Rib().AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Ipv4Unicast() + state := gnmi.Get(t, dut, statePath.State()) + + if communityIndex := state.GetNeighbor(bgpNbr.nbrAddr).GetAdjRibInPost().GetRoute(subnet, 0).GetCommunityIndex(); communityIndex != 0 { + t.Logf("DUT: Prefix %v learned with CommunityIndex: %v", subnet, communityIndex) + } else { + fptest.LogQuery(t, "Node BGP", statePath.State(), state) + t.Logf("DUT: Could not find AdjRibInPost Community for Prefix %v", subnet) + } + //TODO Validate Community for ipv4 prefixes on DUT +} + +func validateATEIPv6PrefixCommunitySet(t *testing.T, ate *ondatra.ATEDevice, bgpPeerName, subnet string, wantCommunitySet []string) { + otg := ate.OTG() + var gotCommunitySet []string + peerPath := gnmi.OTG().BgpPeer(bgpPeerName) + + _, ok := gnmi.Watch(t, + otg, + peerPath.UnicastIpv6Prefix(subnet, plenIPv6, otgtelemetry.UnicastIpv6Prefix_Origin_IGP, 0).State(), + time.Minute, + func(v *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv6Prefix]) bool { + prefix, ok := v.Val() + if ok { + for _, community := range prefix.Community { + gotCommunityNumber := community.GetCustomAsNumber() + gotCommunityValue := community.GetCustomAsValue() + gotCommunitySet = append(gotCommunitySet, fmt.Sprint(gotCommunityNumber)+":"+fmt.Sprint(gotCommunityValue)) + } + if cmp.Equal(gotCommunitySet, wantCommunitySet, cmpopts.SortSlices(func(a, b string) bool { return a < b })) { + t.Logf("ATE: Prefix %v learned with community %v", prefix.GetAddress(), gotCommunitySet) + return true + } + prefix.Community = nil + gotCommunitySet = nil + } + return false + }).Await(t) + + if !ok { + fptest.LogQuery(t, "ATE BGP Peer reported state", peerPath.State(), gnmi.Get(t, otg, peerPath.State())) + t.Errorf("ATE: Prefix %v got communities %v, want communities %v", subnet, gotCommunitySet, wantCommunitySet) + } +} + +func validateDutIPv6PrefixCommunitySet(t *testing.T, dut *ondatra.DUTDevice, bgpNbr *bgpNeighbor, subnet string, wantCommunitySet []string) { + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + statePath := bgpPath.Rib().AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Ipv6Unicast() + state := gnmi.Get(t, dut, statePath.State()) + + if communityIndex := state.GetNeighbor(bgpNbr.nbrAddr).GetAdjRibInPost().GetRoute(subnet, 0).GetCommunityIndex(); communityIndex != 0 { + t.Logf("DUT: Prefix %v learned with CommunityIndex: %v", subnet, communityIndex) + } else { + fptest.LogQuery(t, "Node BGP", statePath.State(), state) + t.Logf("DUT: Could not find AdjRibInPost Community for Prefix %v", subnet) + } + //TODO Validate Community for ipv6 prefixes on DUT +} + +type TestResults struct { + prefixSetName string + wantCommunitySet []string +} + +type testCase struct { + desc string + nbr *bgpNeighbor + peerGrp string + policyName string + testResults []TestResults +} + +// TestBGPCommMatchAction is to test community match actions at BGP neighbor & peer group levels. +func TestBGPCommMatchAction(t *testing.T) { + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + otg := ate.OTG() + + configureDUT(t, dut) + + dutConfPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + gnmi.Delete(t, dut, dutConfPath.Config()) + dutConf := bgpCreateNbr(dutAS, ateAS, dut) + gnmi.Replace(t, dut, dutConfPath.Config(), dutConf) + + otgConfig := configureOTG(t, otg) + verifyBgpState(t, dut) + + t.Run("RT-7.8.1", func(t *testing.T) { + testCases := []testCase{ + { + desc: "Validate Initial Config", + peerGrp: "", + policyName: acceptPolicy, + testResults: []TestResults{ + { + prefixSetName: "prefix-set-1", + wantCommunitySet: nil, + }, + { + prefixSetName: "prefix-set-2", + wantCommunitySet: []string{"5:5", "6:6"}, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + configureRoutingPolicy(t, dut, tc.policyName, ebgp1NbrV4, tc.peerGrp) + configureRoutingPolicy(t, dut, tc.policyName, ebgp1NbrV6, tc.peerGrp) + for _, testResult := range tc.testResults { + for _, prefix := range routes[testResult.prefixSetName].prefixesV4 { + validateATEIPv4PrefixCommunitySet(t, ate, atePort2.Name+".BGP4.peer", prefix, testResult.wantCommunitySet) + validateDutIPv4PrefixCommunitySet(t, dut, ebgp1NbrV4, prefix, nil) + } + for _, prefix := range routes[testResult.prefixSetName].prefixesV6 { + validateATEIPv6PrefixCommunitySet(t, ate, atePort2.Name+".BGP6.peer", prefix, testResult.wantCommunitySet) + validateDutIPv6PrefixCommunitySet(t, dut, ebgp1NbrV6, prefix, nil) + } + } + // Starting ATE Traffic and verify Traffic Flows + sendTraffic(t, otg) + verifyTraffic(t, ate, otgConfig) + }) + } + }) + + t.Run("RT-7.8.2", func(t *testing.T) { + testCases := []testCase{ + { + desc: "neighborV4-match_and_add_comms", + nbr: ebgp1NbrV4, + peerGrp: "", + policyName: "match_and_add_comms", + testResults: []TestResults{ + { + prefixSetName: "prefix-set-1", + wantCommunitySet: nil, + }, + { + prefixSetName: "prefix-set-2", + wantCommunitySet: []string{"5:5", "6:6", "10:10", "20:20", "30:30"}, + }, + }, + }, + { + desc: "neighborV6-match_and_add_comms", + nbr: ebgp1NbrV6, + peerGrp: "", + policyName: "match_and_add_comms", + testResults: []TestResults{ + { + prefixSetName: "prefix-set-1", + wantCommunitySet: nil, + }, + { + prefixSetName: "prefix-set-2", + wantCommunitySet: []string{"5:5", "6:6", "10:10", "20:20", "30:30"}, + }, + }, + }, + { + desc: "PeerGrp-match_and_add_comms", + nbr: nil, + peerGrp: peerGrpName, + policyName: "match_and_add_comms", + testResults: []TestResults{ + { + prefixSetName: "prefix-set-1", + wantCommunitySet: nil, + }, + { + prefixSetName: "prefix-set-2", + wantCommunitySet: []string{"5:5", "6:6", "10:10", "20:20", "30:30"}, + }, + }, + }, + { + desc: "neighborV4-add_std_comms", + nbr: ebgp1NbrV4, + peerGrp: "", + policyName: "add_std_comms", + testResults: []TestResults{ + { + prefixSetName: "prefix-set-1", + wantCommunitySet: []string{"10:10", "20:20", "30:30"}, + }, + { + prefixSetName: "prefix-set-2", + wantCommunitySet: []string{"5:5", "6:6", "10:10", "20:20", "30:30"}, + }, + }, + }, + { + desc: "neighborV6-add_std_comms", + nbr: ebgp1NbrV6, + peerGrp: "", + policyName: "add_std_comms", + testResults: []TestResults{ + { + prefixSetName: "prefix-set-1", + wantCommunitySet: []string{"10:10", "20:20", "30:30"}, + }, + { + prefixSetName: "prefix-set-2", + wantCommunitySet: []string{"5:5", "6:6", "10:10", "20:20", "30:30"}, + }, + }, + }, + { + desc: "PeerGrp-add_std_comms", + nbr: nil, + peerGrp: peerGrpName, + policyName: "add_std_comms", + testResults: []TestResults{ + { + prefixSetName: "prefix-set-1", + wantCommunitySet: []string{"10:10", "20:20", "30:30"}, + }, + { + prefixSetName: "prefix-set-2", + wantCommunitySet: []string{"5:5", "6:6", "10:10", "20:20", "30:30"}, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + configureRoutingPolicy(t, dut, tc.policyName, tc.nbr, tc.peerGrp) + for _, testResult := range tc.testResults { + for _, prefix := range routes[testResult.prefixSetName].prefixesV4 { + if (tc.nbr == nil) || (tc.nbr != nil && tc.nbr.isV4 == true) { + validateATEIPv4PrefixCommunitySet(t, ate, atePort2.Name+".BGP4.peer", prefix, testResult.wantCommunitySet) + validateDutIPv4PrefixCommunitySet(t, dut, ebgp1NbrV4, prefix, nil) + } + } + for _, prefix := range routes[testResult.prefixSetName].prefixesV6 { + if (tc.nbr == nil) || (tc.nbr != nil && tc.nbr.isV4 != true) { + validateATEIPv6PrefixCommunitySet(t, ate, atePort2.Name+".BGP6.peer", prefix, testResult.wantCommunitySet) + validateDutIPv6PrefixCommunitySet(t, dut, ebgp1NbrV6, prefix, nil) + } + } + } + }) + } + }) +} diff --git a/feature/bgp/policybase/otg_tests/comm_match_action_test/metadata.textproto b/feature/bgp/policybase/otg_tests/comm_match_action_test/metadata.textproto index 624d867dfa7..c560af7dabd 100644 --- a/feature/bgp/policybase/otg_tests/comm_match_action_test/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/comm_match_action_test/metadata.textproto @@ -1,7 +1,22 @@ # proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto # proto-message: Metadata -plan_id: "RT-7.8" -description: "BGP Policy Match Standard Community and Add Community Import/Export Policy" -testbed: TESTBED_DUT_ATE_2LINKS -tags: [TAGS_AGGREGATION, TAGS_TRANSIT, TAGS_DATACENTER_EDGE] +uuid: "384964cf-2e53-4ee5-b8a5-eb99f4345cc1" +plan_id: "RT-7.8" +description: "BGP Policy Match Standard Community and Add Community Import/Export Policy" +testbed: TESTBED_DUT_ATE_2LINKS +platform_exceptions: { + platform: { + vendor: NOKIA + } + deviations: { + explicit_port_speed: true + explicit_interface_in_default_vrf: true + interface_enabled: true + bgp_conditions_match_community_set_unsupported: true + bgp_actions_set_community_method_unsupported: true + } +} +tags: TAGS_AGGREGATION +tags: TAGS_TRANSIT +tags: TAGS_DATACENTER_EDGE diff --git a/feature/bgp/policybase/otg_tests/community_test/README.md b/feature/bgp/policybase/otg_tests/community_test/README.md index e9aa94f7037..d3c2d2a496e 100644 --- a/feature/bgp/policybase/otg_tests/community_test/README.md +++ b/feature/bgp/policybase/otg_tests/community_test/README.md @@ -2,7 +2,7 @@ ## Summary -BGP policy configuration for AS Paths and Community Sets +BGP policy configuration for Community Sets ## Subtests @@ -38,16 +38,16 @@ BGP policy configuration for AS Paths and Community Sets * conditions/bgp-conditions/match-community-set/config/match-set-options = ANY * actions/config/policy-result = ACCEPT_ROUTE * statement[name='accept_all_3_comms']/ - * conditions/bgp-conditions/match-as-path-set/config/as-path-set = 'all_3_comms' - * conditions/bgp-conditions/match-as-path-set/config/match-set-options = ALL + * conditions/bgp-conditions/match-community-set/config/community-set = 'all_3_comms' + * conditions/bgp-conditions/match-community-set/config/match-set-options = ALL * actions/config/policy-result = ACCEPT_ROUTE * statement[name='accept_no_3_comms']/ - * conditions/bgp-conditions/match-as-path-set/config/as-path-set = 'no_3_comms' - * conditions/bgp-conditions/match-as-path-set/config/match-set-options = INVERT + * conditions/bgp-conditions/match-community-set/config/community-set = 'no_3_comms' + * conditions/bgp-conditions/match-community-set/config/match-set-options = INVERT * actions/config/policy-result = ACCEPT_ROUTE * statement[name='accept_any_my_regex_comms']/ - * conditions/bgp-conditions/match-as-path-set/config/as-path-set = 'all_3_comms' - * conditions/bgp-conditions/match-as-path-set/config/match-set-options = ANY + * conditions/bgp-conditions/match-community-set/config/community-set = 'all_3_comms' + * conditions/bgp-conditions/match-community-set/config/match-set-options = ANY * actions/config/policy-result = ACCEPT_ROUTE * Send traffic from ATE port-2 to all prefix-sets. @@ -63,8 +63,6 @@ BGP policy configuration for AS Paths and Community Sets | prefix-set-3 | reject | reject | accept | accept | | prefix-set-4 | reject | reject | accept | reject | -* TODO: add coverage for link-bandwidth community in separate test. - ## Config Parameter Coverage ### Policy definition @@ -77,7 +75,9 @@ BGP policy configuration for AS Paths and Community Sets * /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/config/community-set-name * /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/config/community-member * /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/config/match-set-options -* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/config/community-set +* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/match-community-set/config/community-set +* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/match-community-set/config/match-set-options +* /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/ import-policy diff --git a/feature/bgp/policybase/otg_tests/community_test/community_test.go b/feature/bgp/policybase/otg_tests/community_test/community_test.go index 940e69e8ba6..1086a26b0f7 100644 --- a/feature/bgp/policybase/otg_tests/community_test/community_test.go +++ b/feature/bgp/policybase/otg_tests/community_test/community_test.go @@ -15,7 +15,7 @@ package community_test import ( - "sort" + "strconv" "testing" "time" @@ -46,9 +46,9 @@ var prefixesV4 = [][]string{ var prefixesV6 = [][]string{ {"2048:db1:64:64::0", "2048:db1:64:64::4"}, - {"2048:db1:64:64::8", "2048:db1:64:64::12"}, - {"2048:db1:64:64::16", "2048:db1:64:64::20"}, - {"2048:db1:64:64::24", "2048:db1:64:64::28"}, + {"2048:db1:64:64::8", "2048:db1:64:64::c"}, + {"2048:db1:64:64::10", "2048:db1:64:64::14"}, + {"2048:db1:64:64::18", "2048:db1:64:64::1c"}, } func TestMain(m *testing.M) { @@ -66,11 +66,14 @@ func configureImportBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(communitySetName) + + cs := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} for _, commMatch := range communityMatch { if commMatch != "" { - communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(commMatch)}) + cs = append(cs, oc.UnionString(commMatch)) } } + communitySet.SetCommunityMember(cs) communitySet.SetMatchSetOptions(matchSetOptions) if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { @@ -84,42 +87,40 @@ func configureImportBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, dni := deviations.DefaultNetworkInstance(dut) pathV6 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(ipv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy() policyV6 := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(ipv6).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateApplyPolicy() - policyV6.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + // policyV6.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) policyV6.SetImportPolicy([]string{"routePolicy"}) gnmi.Replace(t, dut, pathV6.Config(), policyV6) pathV4 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(ipv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() policyV4 := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(ipv4).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateApplyPolicy() - policyV4.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + // policyV4.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) policyV4.SetImportPolicy([]string{"routePolicy"}) gnmi.Replace(t, dut, pathV4.Config(), policyV4) } func configureOTG(t *testing.T, bs *cfgplugins.BGPSession, prefixesV4 [][]string, prefixesV6 [][]string, communityMembers [][][]int) { devices := bs.ATETop.Devices().Items() - byName := func(i, j int) bool { return devices[i].Name() < devices[j].Name() } - sort.Slice(devices, byName) ipv4 := devices[1].Ethernets().Items()[0].Ipv4Addresses().Items()[0] bgp4Peer := devices[1].Bgp().Ipv4Interfaces().Items()[0].Peers().Items()[0] - bgp4PeerRoute := bgp4Peer.V4Routes().Add() - bgp4PeerRoute.SetName(bs.ATEPorts[1].Name + ".BGP4.peer.dut") - bgp4PeerRoute.SetNextHopIpv4Address(ipv4.Address()) - ipv6 := devices[1].Ethernets().Items()[0].Ipv6Addresses().Items()[0] bgp6Peer := devices[1].Bgp().Ipv6Interfaces().Items()[0].Peers().Items()[0] - bgp6PeerRoute := bgp6Peer.V6Routes().Add() - bgp6PeerRoute.SetName(bs.ATEPorts[1].Name + ".BGP6.peer.dut") - bgp6PeerRoute.SetNextHopIpv6Address(ipv6.Address()) - for index, prefixes := range prefixesV4 { + bgp4PeerRoute := bgp4Peer.V4Routes().Add() + bgp4PeerRoute.SetName(bs.ATEPorts[1].Name + ".BGP4.peer.dut." + strconv.Itoa(index)) + bgp4PeerRoute.SetNextHopIpv4Address(ipv4.Address()) + route4Address1 := bgp4PeerRoute.Addresses().Add().SetAddress(prefixes[0]) route4Address1.SetPrefix(prefixV4Len) route4Address2 := bgp4PeerRoute.Addresses().Add().SetAddress(prefixes[1]) route4Address2.SetPrefix(prefixV4Len) + bgp6PeerRoute := bgp6Peer.V6Routes().Add() + bgp6PeerRoute.SetName(bs.ATEPorts[1].Name + ".BGP6.peer.dut." + strconv.Itoa(index)) + bgp6PeerRoute.SetNextHopIpv6Address(ipv6.Address()) + route6Address1 := bgp6PeerRoute.Addresses().Add().SetAddress(prefixesV6[index][0]) route6Address1.SetPrefix(prefixV6Len) route6Address2 := bgp6PeerRoute.Addresses().Add().SetAddress(prefixesV6[index][1]) @@ -141,24 +142,19 @@ func configureOTG(t *testing.T, bs *cfgplugins.BGPSession, prefixesV4 [][]string } } -func configureFlow(bs *cfgplugins.BGPSession, prefixPair []string, prefixType string) { - bs.ATETop.Flows().Clear() +func configureFlow(t *testing.T, bs *cfgplugins.BGPSession, prefixPair []string, prefixType string, index int) { - var rxNames []string - for i := 1; i < len(bs.ATEPorts); i++ { - rxNames = append(rxNames, bs.ATEPorts[i].Name+".BGP4.peer.dut") - } - flow := bs.ATETop.Flows().Add().SetName("flow") + flow := bs.ATETop.Flows().Add().SetName("flow" + prefixType + strconv.Itoa(index)) flow.Metrics().SetEnable(true) if prefixType == "ipv4" { flow.TxRx().Device(). - SetTxNames([]string{bs.ATEPorts[1].Name + ".IPv4"}). - SetRxNames(rxNames) + SetTxNames([]string{bs.ATEPorts[0].Name + ".IPv4"}). + SetRxNames([]string{bs.ATEPorts[1].Name + ".BGP4.peer.dut." + strconv.Itoa(index)}) } else { flow.TxRx().Device(). - SetTxNames([]string{bs.ATEPorts[1].Name + ".IPv6"}). - SetRxNames(rxNames) + SetTxNames([]string{bs.ATEPorts[0].Name + ".IPv6"}). + SetRxNames([]string{bs.ATEPorts[1].Name + ".BGP6.peer.dut." + strconv.Itoa(index)}) } flow.Duration().FixedPackets().SetPackets(totalPackets) @@ -170,18 +166,19 @@ func configureFlow(bs *cfgplugins.BGPSession, prefixPair []string, prefixType st if prefixType == "ipv4" { v4 := flow.Packet().Add().Ipv4() - v4.Src().SetValue(bs.ATEPorts[1].IPv4) + v4.Src().SetValue(bs.ATEPorts[0].IPv4) v4.Dst().SetValues(prefixPair) } else { v6 := flow.Packet().Add().Ipv6() - v6.Src().SetValue(bs.ATEPorts[1].IPv6) + v6.Src().SetValue(bs.ATEPorts[0].IPv6) v6.Dst().SetValues(prefixPair) } } -func verifyTraffic(t *testing.T, ate *ondatra.ATEDevice, ports int, testResults bool) { - framesTx := gnmi.Get[uint64](t, ate.OTG(), gnmi.OTG().Port(ate.Port(t, "port1").ID()).Counters().OutFrames().State()) - framesRx := gnmi.Get[uint64](t, ate.OTG(), gnmi.OTG().Port(ate.Port(t, "port2").ID()).Counters().InFrames().State()) +func verifyTraffic(t *testing.T, ate *ondatra.ATEDevice, prefixType string, testResults bool, index int) { + recvMetric := gnmi.Get(t, ate.OTG(), gnmi.OTG().Flow("flow"+prefixType+strconv.Itoa(index)).State()) + framesTx := recvMetric.GetCounters().GetOutPkts() + framesRx := recvMetric.GetCounters().GetInPkts() if framesTx == 0 { t.Error("No traffic was generated and frames transmitted were 0") @@ -201,9 +198,8 @@ type testCase struct { } func TestCommunitySet(t *testing.T) { - bs := cfgplugins.NewBGPSession(t, cfgplugins.PortCount2) - bs.WithEBGP(t, oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, true, true) - bs.WithEBGP(t, oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST, true, true) + bs := cfgplugins.NewBGPSession(t, cfgplugins.PortCount2, nil) + bs.WithEBGP(t, []oc.E_BgpTypes_AFI_SAFI_TYPE{oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST}, []string{"port2"}, true, true) var communityMembers = [][][]int{ { @@ -213,10 +209,10 @@ func TestCommunitySet(t *testing.T) { {100, 1}, {101, 1}, {200, 2}, }, { - {109, 1}, + {107, 1}, {108, 1}, {109, 1}, }, { - {400, 1}, + {400, 1}, {500, 1}, {600, 1}, }, } @@ -265,25 +261,26 @@ func TestCommunitySet(t *testing.T) { t.Run(tc.desc, func(t *testing.T) { configureImportBGPPolicy(t, bs.DUT, ipv4, ipv6, tc.communitySetName, tc.communityMatch, tc.matchSetOptions) - sleepTime := time.Duration(totalPackets/trafficPps) + 5 + sleepTime := time.Duration(totalPackets/trafficPps) + 2 + bs.ATETop.Flows().Clear() for index, prefixPairV4 := range prefixesV4 { + configureFlow(t, bs, prefixPairV4, "ipv4", index) + configureFlow(t, bs, prefixesV6[index], "ipv6", index) + } + bs.PushAndStartATE(t) - t.Logf("Running traffic test for IPv4 prefixes: [%s, %s]. Expected Result: [%t]", prefixPairV4[0], prefixPairV4[1], tc.testResults[index]) - configureFlow(bs, prefixPairV4, "ipv4") - bs.ATE.OTG().StartTraffic(t) - time.Sleep(sleepTime * time.Second) - bs.ATE.OTG().StopTraffic(t) - otgutils.LogFlowMetrics(t, bs.ATE.OTG(), bs.ATETop) - verifyTraffic(t, bs.ATE, int(cfgplugins.PortCount2), tc.testResults[index]) - - t.Logf("Running traffic test for IPv6 prefixes: [%s, %s]. Expected Result: [%t]", prefixesV6[index][0], prefixesV6[index][1], tc.testResults[index]) - configureFlow(bs, prefixesV6[index], "ipv6") - bs.ATE.OTG().StartTraffic(t) - time.Sleep(sleepTime * time.Second) - bs.ATE.OTG().StopTraffic(t) - otgutils.LogFlowMetrics(t, bs.ATE.OTG(), bs.ATETop) - verifyTraffic(t, bs.ATE, int(cfgplugins.PortCount2), tc.testResults[index]) + t.Logf("Starting traffic for IPv4 and v6") + bs.ATE.OTG().StartTraffic(t) + time.Sleep(sleepTime * time.Second) + bs.ATE.OTG().StopTraffic(t) + otgutils.LogFlowMetrics(t, bs.ATE.OTG(), bs.ATETop) + + for index, prefixPairV4 := range prefixesV4 { + t.Logf("Validating traffic test for IPv4 prefixes: [%s, %s]. Expected Result: [%t]", prefixPairV4[0], prefixPairV4[1], tc.testResults[index]) + verifyTraffic(t, bs.ATE, "ipv4", tc.testResults[index], index) + t.Logf("Validating traffic test for IPv6 prefixes: [%s, %s]. Expected Result: [%t]", prefixesV6[index][0], prefixesV6[index][1], tc.testResults[index]) + verifyTraffic(t, bs.ATE, "ipv6", tc.testResults[index], index) } }) } diff --git a/feature/bgp/policybase/otg_tests/default_policies_test/bgp_default_policies_test.go b/feature/bgp/policybase/otg_tests/default_policies_test/bgp_default_policies_test.go index 74ae5be5c6f..e0465b1344a 100644 --- a/feature/bgp/policybase/otg_tests/default_policies_test/bgp_default_policies_test.go +++ b/feature/bgp/policybase/otg_tests/default_policies_test/bgp_default_policies_test.go @@ -352,10 +352,13 @@ func configureOTG(t *testing.T, otg *otg.OTG) { SetNextHopMode(gosnappi.BgpV4RouteRangeNextHopMode.MANUAL) bgpNeti1Bgp4PeerRoutes.Addresses().Add(). SetAddress(ipv4Prefix1).SetPrefix(32) + bgpNeti1Bgp4PeerRoutes.AddPath().SetPathId(1) bgpNeti1Bgp4PeerRoutes.Addresses().Add(). SetAddress(ipv4Prefix2).SetPrefix(32) + bgpNeti1Bgp4PeerRoutes.AddPath().SetPathId(1) bgpNeti1Bgp4PeerRoutes.Addresses().Add(). SetAddress(ipv4Prefix3).SetPrefix(32) + bgpNeti1Bgp4PeerRoutes.AddPath().SetPathId(1) // eBGP V6 routes from Port1. bgpNeti1Bgp6PeerRoutes := iDut1Bgp6Peer.V6Routes().Add().SetName(atePort1.Name + ".BGP6.Route") @@ -364,10 +367,13 @@ func configureOTG(t *testing.T, otg *otg.OTG) { SetNextHopMode(gosnappi.BgpV6RouteRangeNextHopMode.MANUAL) bgpNeti1Bgp6PeerRoutes.Addresses().Add(). SetAddress(ipv6Prefix1).SetPrefix(128) + bgpNeti1Bgp6PeerRoutes.AddPath().SetPathId(1) bgpNeti1Bgp6PeerRoutes.Addresses().Add(). SetAddress(ipv6Prefix2).SetPrefix(128) + bgpNeti1Bgp6PeerRoutes.AddPath().SetPathId(1) bgpNeti1Bgp6PeerRoutes.Addresses().Add(). SetAddress(ipv6Prefix3).SetPrefix(128) + bgpNeti1Bgp6PeerRoutes.AddPath().SetPathId(1) // iBGP V4 routes from Port2. bgpNeti2Bgp4PeerRoutes := iDut2Bgp4Peer.V4Routes().Add().SetName(atePort2.Name + ".BGP4.Route") @@ -376,10 +382,13 @@ func configureOTG(t *testing.T, otg *otg.OTG) { SetNextHopMode(gosnappi.BgpV4RouteRangeNextHopMode.MANUAL) bgpNeti2Bgp4PeerRoutes.Addresses().Add(). SetAddress(ipv4Prefix4).SetPrefix(32) + bgpNeti2Bgp4PeerRoutes.AddPath().SetPathId(1) bgpNeti2Bgp4PeerRoutes.Addresses().Add(). SetAddress(ipv4Prefix5).SetPrefix(32) + bgpNeti2Bgp4PeerRoutes.AddPath().SetPathId(1) bgpNeti2Bgp4PeerRoutes.Addresses().Add(). SetAddress(ipv4Prefix6).SetPrefix(32) + bgpNeti2Bgp4PeerRoutes.AddPath().SetPathId(1) // iBGP V6 routes from Port2. bgpNeti2Bgp6PeerRoutes := iDut2Bgp6Peer.V6Routes().Add().SetName(atePort2.Name + ".BGP6.Route") @@ -388,10 +397,13 @@ func configureOTG(t *testing.T, otg *otg.OTG) { SetNextHopMode(gosnappi.BgpV6RouteRangeNextHopMode.MANUAL) bgpNeti2Bgp6PeerRoutes.Addresses().Add(). SetAddress(ipv6Prefix4).SetPrefix(128) + bgpNeti2Bgp6PeerRoutes.AddPath().SetPathId(1) bgpNeti2Bgp6PeerRoutes.Addresses().Add(). SetAddress(ipv6Prefix5).SetPrefix(128) + bgpNeti2Bgp6PeerRoutes.AddPath().SetPathId(1) bgpNeti2Bgp6PeerRoutes.Addresses().Add(). SetAddress(ipv6Prefix6).SetPrefix(128) + bgpNeti2Bgp6PeerRoutes.AddPath().SetPathId(1) t.Logf("Pushing config to OTG and starting protocols...") otg.PushConfig(t, config) @@ -468,6 +480,7 @@ func configureISIS(t *testing.T, dut *ondatra.DUTDevice, intfName []string, dutA globalISIS.LevelCapability = oc.Isis_LevelType_LEVEL_2 globalISIS.Net = []string{fmt.Sprintf("%v.%v.00", dutAreaAddress, dutSysID)} globalISIS.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST).Enabled = ygot.Bool(true) + globalISIS.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV6, oc.IsisTypes_SAFI_TYPE_UNICAST).Enabled = ygot.Bool(true) if deviations.ISISInstanceEnabledRequired(dut) { globalISIS.Instance = ygot.String(isisInstance) } @@ -486,6 +499,12 @@ func configureISIS(t *testing.T, dut *ondatra.DUTDevice, intfName []string, dutA isisIntfLevelAfi := isisIntfLevel.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST) isisIntfLevelAfi.Metric = ygot.Uint32(200) isisIntfLevelAfi.Enabled = ygot.Bool(true) + if deviations.ISISInterfaceAfiUnsupported(dut) { + isisIntf.Af = nil + } + if deviations.MissingIsisInterfaceAfiSafiEnable(dut) { + isisIntfLevelAfi.Enabled = nil + } } gnmi.Replace(t, dut, dutConfIsisPath.Config(), prot) } @@ -798,6 +817,7 @@ func TestBGPDefaultPolicies(t *testing.T) { t.Run("Verify BGP session telemetry", func(t *testing.T) { verifyBgpTelemetry(t, dut) }) + t.Run("Verify BGP capabilities", func(t *testing.T) { verifyBGPCapabilities(t, dut) }) diff --git a/feature/bgp/policybase/otg_tests/default_policies_test/metadata.textproto b/feature/bgp/policybase/otg_tests/default_policies_test/metadata.textproto index 7e05dc47964..a2f6c21c4cd 100644 --- a/feature/bgp/policybase/otg_tests/default_policies_test/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/default_policies_test/metadata.textproto @@ -21,6 +21,9 @@ platform_exceptions: { deviations: { interface_enabled: true default_network_instance: "default" + missing_isis_interface_afi_safi_enable: true + isis_interface_afi_unsupported: true + isis_instance_enabled_required: true } } platform_exceptions: { diff --git a/feature/bgp/policybase/otg_tests/extcomm_action_test/README.md b/feature/bgp/policybase/otg_tests/extcomm_action_test/README.md deleted file mode 100644 index 92a022c2544..00000000000 --- a/feature/bgp/policybase/otg_tests/extcomm_action_test/README.md +++ /dev/null @@ -1,364 +0,0 @@ -# RT-7.9: BGP Policy - Import/Export Policy Action Using Extended Communities - -## Summary - -Configure bgp policy to import/export routes by matching on extended communities. - -Matches should be performed against a subset of extended community types - -* <2b AS>:<4b value> per RFC4360 section 3.1 -* <4b AS>:<2b value> per RFC5668 section 2. -* link-bandwidth:<2 byte asn>: - per draft-ietf-idr-link-bandwidth-07 -* TODO: Additional match types can be added here. Currently these match types - cover the use cases needed for RT-7.9 - -## Testbed type - -* https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_2.testbed - -* Testbed configuration - Setup BGP sessions and prefixes - * Generate config for 2 DUT and ATE ports where - * DUT port 1 to ATE port 1. - * DUT port 2 to ATE port 2. - * Configure ATE port 1 with a BGP session to DUT port 1 - * Advertise ipv4 and ipv6 prefixes to DUT port 1 using the following communities: - * prefix-set-1 with 2 routes with communities `[100:95000, 200000:2, 300000:300000]` - * prefix-set-2 with 2 routes with communities `[100000:1, 101000:1, 200000:1]` - * prefix-set-3 with 2 routes with communities `[109000:1]` - * prefix-set-4 with 2 routes with communities `[400000:1]` - * prefix-set-5 with 2 routes with communities `[400000:1, link-bandwidth:100:1500000` - * prefix-set-6 with 2 routes with communities `[400000:1, link-bandwidth:100:1M` - -## Subtests - -* RT-7.9.1 - Validate bgp sessions and traffic - * For IPv4 and IPv6 prefixes: - * Observe received prefixes at ATE port-2. - * Generate traffic from ATE port-2 to ATE port-1. - * Validate that traffic can be received on ATE port-1 for all installed - routes. - -* RT-7.9.2 - Validate ext-community-sets and routing-policy using OC - release 2.x or earlier - * Configure the following community sets - (prefix: `routing-policy/defined-sets/bgp-defined-sets/ext-community-sets/ext-community-set`) - on the DUT. - * Create a community-set named 'any_my_3_ext_comms' with members as follows: - * community-member = [ "100:95000", "200000:2", "300000:300000" ] - * match-set-options = ANY - * Create a community-set named 'all_3_ext_comms' with members as follows: - * community-member = [ "100:95000", "200000:2", "300000:300000" ] - * match-set-options = ALL - * Create a community-set named 'no_3_ext_comms' with members as follows: - * community-member = [ "100000:99", "200000:2", "300000:300000" ] - * match-set-options = INVERT - * Create a community-set named 'any_my_regex_ext_comms' with members as follows: - * community-member = [ "10[0-9]000:1" ] - * match-set-options = ANY - * Create a community-set named 'any_ext_comms' with members as follows: - * community-member = [ "^.*$" ] - * match-set-options = ANY - - * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` - named 'any_3_comms' with the following `statements` - * statement[name='any_3_comms']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'any_my_3_ext_comms' - * actions/config/policy-result = ACCEPT_ROUTE - * statement[name='reject_all']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'any_ext_comms' - * actions/config/policy-result = REJECT_ROUTE - - * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` - named 'all_3_comms' with the following `statements` - * statement[name='all_3_comms']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'all_3_ext_comms' - * actions/config/policy-result = ACCEPT_ROUTE - * statement[name='reject_all']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'any_ext_comms' - * actions/config/policy-result = REJECT_ROUTE - - * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` - named 'no_3_comms' with the following `statements` - * statement[name='no_3_comms']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'no_3_ext_comms' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = INVERT - * actions/config/policy-result = ACCEPT_ROUTE - * statement[name='reject_all']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'any_ext_comms' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = ANY - * actions/config/policy-result = REJECT_ROUTE - - * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` - named 'any_my_regex_comms' with the following `statements` - * statement[name='any_my_regex_comms']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'any_my_regex_ext_comms' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = ANY - * actions/config/policy-result = ACCEPT_ROUTE - * statement[name='reject_all']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'any_ext_comms' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = ANY - * actions/config/policy-result = REJECT_ROUTE - - * For each policy-definition created - * Use gnmi Set REPLACE option for: - * `/routing-policy/policy-definitions` to configure the policy - * Use `/network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/apply-policy/config/import-policy` - to configure the policy on the DUT to the ATE port 1 bgp neighbor - * Send traffic from ATE port-2 to all prefix-sets-1,2,3,4. - * Verify traffic is received on ATE port 1 for accepted prefixes. - * Verify traffic is not received on ATE port 1 for rejected prefixes. - * Stop traffic - - * Expected matches for each policy - | | any_my_3_comms | all_3_comms | no_3_comms | any_my_regex_comms | - | ------------ | -------------- | ----------- | ---------- | ------------------ | - | prefix-set-1 | accept | accept | reject | accept | - | prefix-set-2 | accept | reject | reject | accept | - | prefix-set-3 | reject | reject | accept | accept | - | prefix-set-4 | reject | reject | accept | reject | - -* RT-7.9.3 - Validate ext-community-sets and routing-policy using OC release 3.x - * Note, this is the same at RT-7.9.2, but with a change in the location of the - `match-set-options` leaf which moved to - `/routing-policy/policy-definitions/policy-definition/policy-definition/bgp-conditions/match-ext-community-set/config/match-set-options` - * Configure the following community sets - (prefix: `routing-policy/defined-sets/bgp-defined-sets/ext-community-sets/ext-community-set`) - on the DUT. - * Create a community-set named 'any_my_3_ext_comms' with members as follows: - * community-member = [ "100:95000", "200000:2", "300000:300000" ] - * Create a community-set named 'all_3_ext_comms' with members as follows: - * community-member = [ "100:95000", "200000:2", "300000:300000" ] - * Create a community-set named 'no_3_ext_comms' with members as follows: - * community-member = [ "100000:99", "200000:2", "300000:300000" ] - * Create a community-set named 'any_my_regex_ext_comms' with members as follows: - * community-member = [ "10[0-9]000:1" ] - * Create a community-set named 'any_ext_comms' with members as follows: - * community-member = [ "^.*$" ] - - * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` - named 'any_3_comms' with the following `statements` - * statement[name='any_3_comms']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'any_my_3_ext_comms' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = ANY - * actions/config/policy-result = ACCEPT_ROUTE - * statement[name='reject_all']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'any_ext_comms' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = ANY - * actions/config/policy-result = REJECT_ROUTE - - * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` - named 'all_3_comms' with the following `statements` - * statement[name='all_3_comms']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'all_3_ext_comms' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = ALL - * actions/config/policy-result = ACCEPT_ROUTE - * statement[name='reject_all']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'any_ext_comms' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = ANY - * actions/config/policy-result = REJECT_ROUTE - - * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` - named 'no_3_comms' with the following `statements` - * statement[name='no_3_comms']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'no_3_ext_comms' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = INVERT - * actions/config/policy-result = ACCEPT_ROUTE - * statement[name='reject_all']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'any_ext_comms' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = ANY - * actions/config/policy-result = REJECT_ROUTE - - * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` - named 'any_my_regex_comms' with the following `statements` - * statement[name='any_my_regex_comms']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'any_my_regex_ext_comms' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = ANY - * actions/config/policy-result = ACCEPT_ROUTE - * statement[name='reject_all']/ - * conditions/bgp-conditions/match-ext-community-set/config/ext-community-set = 'any_ext_comms' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = ANY - * actions/config/policy-result = REJECT_ROUTE - - * For each policy-definition - * Use gnmi Set REPLACE option for: - * `/routing-policy/policy-definitions` to configure the policy - * Use `/network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/apply-policy/config/import-policy` - to configure the policy on the DUT to the ATE port 1 bgp neighbor - * Send traffic from ATE port-2 to all prefix-sets-1,2,3,4. - * Verify traffic is received on ATE port 1 for accepted prefixes. - * Verify traffic is not received on ATE port 1 for rejected prefixes. - * Stop traffic - - * Expected matches for each policy - | | any_my_3_comms | all_3_comms | no_3_comms | any_my_regex_comms | - | ------------ | -------------- | ----------- | ---------- | ------------------ | - | prefix-set-1 | accept | accept | reject | accept | - | prefix-set-2 | accept | reject | reject | accept | - | prefix-set-3 | reject | reject | accept | accept | - | prefix-set-4 | reject | reject | accept | reject | - -* RT-7.9.4 - Validate link-bandwidth ext-community-sets and matching policy - using OC model revision 2.x - * Add prefixes with link-bandwidth community to ATE port 1 to advertise ipv4 - and ipv6 prefixes to DUT port 1 using the following communities: - * prefix-set-5 with 2 routes with communities `[ link-bandwidth:500000:0, ]` - * prefix-set-6 with 2 routes with communities `[ link-bandwidth:600000:1M, ]` - - * Configure the following community sets - (prefix: `routing-policy/defined-sets/bgp-defined-sets/ext-community-sets/ext-community-set`) - on the DUT. - * Create an ext-community-set named 'linkbw_ext_comms_0' with: - * ext-community-member = [ "^link-bandwidth:.*:0$" ] - * match-set-options = ANY - * Create an ext-community-set named 'linkbw_ext_comms_1M' with members as follows: - * ext-community-member = [ "^link-bandwidth:.*:1M$" ] - * config/match-set-options = ANY - * Create an ext-community-set named 'linkbw_ext_comms_2G' with members as follows: - * ext-community-member = [ "^link-bandwidth:.*:2G$" ] - * match-set-options = ANY - * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` - named 'zero-bandwidth-reject' with the following `statements` - * statement[name='zero-bandwidth-reject']/ - * conditions/bgp-conditions/match-ext-community-set/config/community-set = 'linkbw_ext_comms_0' - * actions/config/policy-result = REJECT_ROUTE - * statement[name='accept_all']/ - * actions/config/policy-result = ACCEPT_ROUTE - * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` - named '1-megabit-match' with the following `statements` - * statement[name='1-megabit-match']/ - * conditions/bgp-conditions/match-ext-community-set/config/community-set = 'linkbw_ext_comms_1M' - * actions/config/policy-result = ACCEPT_ROUTE - * statement[name='reject_all']/ - * actions/config/policy-result = REJECT_ROUTE - - * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` - named 'link-bandwidth-match' with the following `statements` - * statement[name='2-gigabit-match']/ - * conditions/bgp-conditions/match-ext-community-set/config/community-set = 'linkbw_ext_comms_2G' - * actions/config/policy-result = ACCEPT_ROUTE - * statement[name='reject_all']/ - * actions/config/policy-result = REJECT_ROUTE - - * For each policy-definition - * Use gnmi Set REPLACE option for: - * `/routing-policy/policy-definitions` to configure the policy - * Use `/network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/apply-policy/config/import-policy` - to configure the policy on the DUT to the ATE port 1 bgp neighbor - * Send traffic from ATE port-2 to all prefix-sets-5,6. - * Verify traffic is received on ATE port 1 for accepted prefixes. - * Verify traffic is not received on ATE port 1 for rejected prefixes. - * Stop traffic - - * Expected matches for each policy - | | zero-bandwidth-reject | 1-megabit | 2-gigabit-match | - | ------------ | --------------------- | --------- | --------------- | - | prefix-set-5 | reject | reject | reject | - | prefix-set-6 | accept | accept | reject | - -* RT-7.9.5 - Validate ext-community-sets and matching policy using OC - release 3.x - * Note, this is the same at RT-7.9.4, but with a change in the location of the - `match-set-options` leaf which moved to - `/routing-policy/policy-definitions/policy-definition/policy-definition/bgp-conditions/match-ext-community-set/config/match-set-options` - * Add prefixes with link-bandwidth community to ATE port 1 to advertise ipv4 - and ipv6 prefixes to DUT port 1 using the following communities: - * prefix-set-5 with 2 routes with communities `[ link-bandwidth:500000:0, ]` - * prefix-set-6 with 2 routes with communities `[ link-bandwidth:600000:1M, ]` - - * Configure the following community sets - (prefix: `routing-policy/defined-sets/bgp-defined-sets/ext-community-sets/ext-community-set`) - on the DUT. - * Create a community-set named 'linkbw_ext_comms_0' with members as follows: - * `community-member = [ "^link-bandwidth:.*:0$" ]` - * Create a community-set named 'linkbw_ext_comms_1M' with members as follows: - * `community-member = [ "^link-bandwidth:.*:1M$" ]` - * Create a community-set named 'linkbw_ext_comms_2G' with members as follows: - * `community-member = [ "^link-bandwidth:.*:2G$" ]` - - * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` - named 'zero-bandwidth-reject' with the following `statements` - * statement[name='zero-bandwidth-reject']/ - * conditions/bgp-conditions/match-ext-community-set/config/community-set = 'linkbw_ext_comms_0' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = ANY - * actions/config/policy-result = REJECT_ROUTE - * statement[name='accept_all']/ - * actions/config/policy-result = ACCEPT_ROUTE - * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` - named '1-megabit-match' with the following `statements` - * statement[name='1-megabit-match']/ - * conditions/bgp-conditions/match-ext-community-set/config/community-set = 'linkbw_ext_comms_1M' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = ANY - * actions/config/policy-result = ACCEPT_ROUTE - * statement[name='reject_all']/ - * actions/config/policy-result = REJECT_ROUTE - - * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` - named 'link-bandwidth-match' with the following `statements` - * statement[name='2-gigabit-match']/ - * conditions/bgp-conditions/match-ext-community-set/config/community-set = 'linkbw_ext_comms_2G' - * conditions/bgp-conditions/match-ext-community-set/config/match-set-options = ANY - * actions/config/policy-result = ACCEPT_ROUTE - * statement[name='reject_all']/ - * actions/config/policy-result = REJECT_ROUTE - - * For each policy-definition - * Use gnmi Set REPLACE option for: - * `/routing-policy/policy-definitions` to configure the policy - * Use `/network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/apply-policy/config/import-policy` - to configure the policy on the DUT to the ATE port 1 bgp neighbor - * Send traffic from ATE port-2 to all prefix-sets-5,6. - * Verify traffic is received on ATE port 1 for accepted prefixes. - * Verify traffic is not received on ATE port 1 for rejected prefixes. - * Stop traffic - - * Expected matches for each policy - | | zero-bandwidth-reject | 1-megabit | 2-gigabit-match | - | ------------ | --------------------- | --------- | --------------- | - | prefix-set-5 | reject | reject | reject | - | prefix-set-6 | accept | accept | reject | - -## Config Parameter Coverage - -### Policy definition - -* /routing-policy/policy-definitions/policy-definition/config/name -* /routing-policy/policy-definitions/policy-definition/statements/statement/config/name - -### Policy for community-set configuration - -* /routing-policy/defined-sets/bgp-defined-sets/ext-community-sets/ext-community-set/config/ext-community-set-name -* /routing-policy/defined-sets/bgp-defined-sets/ext-community-sets/ext-community-set/config/community-member - -### Policy for community-set match configuration - -* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/config/community-set -* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/match-ext-community-set/config/match-set-options -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy - -## Telemetry Parameter Coverage - -### Policy definition state - -* /routing-policy/policy-definitions/policy-definition/state/name -* /routing-policy/policy-definitions/policy-definition/statements/statement/state/name - -### Policy for community-set match state - -* /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/state/community-set-name -* /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/state/community-member -* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/match-ext-community-set/state/match-set-options -* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/state/community-set - -### Paths to verify policy state - -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy - -### Paths to verify prefixes sent and received - -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/sent -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/received-pre-policy -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/received -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/installed diff --git a/feature/bgp/policybase/otg_tests/extcomm_action_test/metadata.textproto b/feature/bgp/policybase/otg_tests/extcomm_action_test/metadata.textproto deleted file mode 100644 index 7317700d4f6..00000000000 --- a/feature/bgp/policybase/otg_tests/extcomm_action_test/metadata.textproto +++ /dev/null @@ -1,7 +0,0 @@ -# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto -# proto-message: Metadata - -plan_id: "RT-7.9" -description: "BGP Policy - Import/Export Policy Action Using Communities" -testbed: TESTBED_DUT_ATE_2LINKS -tags: [TAGS_AGGREGATION, TAGS_TRANSIT, TAGS_DATACENTER_EDGE] diff --git a/feature/bgp/policybase/otg_tests/import_export_multi/README.md b/feature/bgp/policybase/otg_tests/import_export_multi/README.md new file mode 100644 index 00000000000..6e8fca19c0c --- /dev/null +++ b/feature/bgp/policybase/otg_tests/import_export_multi/README.md @@ -0,0 +1,245 @@ +# RT-7.11: BGP Policy - Import/Export Policy Action Using Multiple Criteria + +## Summary + +The purpose of this test is to verify a combination of bgp conditions using +matching and policy nesting as well as and actions in a single BGP import +policy. Additional combinations may be added in the future as additonal +subtests. + +## Testbed type + +* [2 port ATE to DUT](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_2.testbed) + +## Testbed common configuration + +This configuration initializes the testbed with configurations that are a +pre-requisite for the test. This configuration should not be part of the test +functions. + +* Testbed configuration - Setup eBGP sessions and prefixes + * Generate config for 2 DUT and ATE ports where + * DUT port 1 connects to ATE port 1. + * DUT port 2 connects to ATE port 2. + * Configure ATE port 1 with an external type BGP session to DUT port 1 + * DUT ASN 65000 + * ATE port 1 ASN 65100 + * ATE port 2 ASN 65200 + * Advertise ipv4 and ipv6 prefixes from ATE port 1 to DUT port 1 using + the following communities: + * prefix-set-1 with 2 ipv4 and 2 ipv6 routes with communities [ "10:1" ] + * prefix-set-2 with 2 ipv4 and 2 ipv6 routes with communities [ "20:1" ] + * prefix-set-3 with 2 ipv4 and 2 ipv6 routes with communities [ "30:1" ] + * prefix-set-4 with 2 ipv4 and 2 ipv6 routes with communities [ "20:2", "30:3" ] + * prefix-set-5 with 2 ipv4 and 2 ipv6 routes with communities [ "40:1" ] + * prefix-set-6 with 2 ipv4 and 2 ipv6 routes with communities [ "50:1" ] + * Configure accept_all policy + * Create policy-definitions/policy-definition/config/name = "accept_all" + * statements/statement/config/name = "accept" + * actions/config/policy-result = "ACCEPT_ROUTE" + * apply as an export and import policy on the DUT + eBGP session to ATE port 1 and port 2. + +* Configure the following community sets on the DUT: + * /routing-policy/defined-sets/bgp-defined-sets/ext-community-sets/ext-community-set/config + * name = "reject_communities" + * community-member = [ "10:1" ] + * name = "accept_communities" + * community-member = [ "20:1" ] + * name = "regex_community" + * community-member = [ "^30:.*$" ] + * name = "add_communities" + * community-member = [ "40:1", "40:2" ] + * name "my_community" + * community-member = [ "50:1" ] + * name = "add_comm_60" + * community-member = [ "60:1" ] + * name = "add_comm_70" + * community-member = [ "70:1" ] + +* Create an as-path-set on the DUT as follows + * /routing-policy/defined-sets/bgp-defined-sets/as-path-sets/as-path-set/config/ + * as-path-set-name = "my_aspath" + * as-path-set-member = "65100" + +* Validate bgp sessions and traffic + * For IPv4 and IPv6 prefixes: + * Observe received prefixes at ATE port-2. + * Generate traffic from ATE port-2 to ATE port-1. + * Validate + * Traffic can be received on ATE port-1 for all installed routes. + * Communities on ATE Port 2 are equal to those sent by ATE Port1 + * as-path shall be "65100 65000" + * Local-Preference should be not present + * MED should be not present + +## Procedure + +### RT-7.11.1 - Create a bgp policy containing the following conditions and actions + +* Summary of this policy + * Reject route matching any communities in a community-set. + * Reject route matching another policy (nested) and not matching a community-set. + * Add a community-set if missing that same community-set. + * Add two communities and set localpref if matching a community and prefix-set. + * Set MED if matching an aspath + +* Define a policy that will be called from another policy + * policy-definitions/policy-definition/config/name: "match_community_regex" + * statements/statement/config/name: "match_community_regex" + * conditions/bgp-conditions/match-community-set/config/ + * community-set: "regex-community" + * match-set-options: "ANY" + * actions/config/policy-result = "NEXT_STATEMENT" + +* Create policy-definitions/policy-definition/config/name = "multi_policy" + * statements/statement/config/name = "reject_route_community" + * conditions/bgp-conditions/match-community-set/config + * community-set = "reject_communities" + * match-set-options = "ANY" + * actions/config/policy-result = "REJECT_ROUTE" + + * statements/statement/config/name = "if_30:.*_and_not_20:1_nested_reject" + * conditions/config/call-policy = "match_community_regex" + * conditions/bgp-conditions/match-community-set/config/ + * community-set = "accept_communities" + * match-set-options = "INVERT" + * actions/config/policy-result = "REJECT_ROUTE" + + * statements/statement/config/name = "add_communities_if_missing" + * conditions/bgp-conditions/match-community-set/config/ + * community-set-refs = "add-communities" + * match-set-options: "INVERT" + * actions/bgp-actions/set-community/reference/config/ + * community-set-refs = "add-communities" + * method = "REFERENCE" + * option = "ADD" + * actions/config/policy-result = "NEXT_STATEMENT" + + * statements/statement/config/name: "match_comm_and_prefix_add_2_community_sets" + * conditions/bgp-conditions/match-community-set/config + * community-set = "my_community" + * match-set-options = "ANY" + * conditions/match-prefix-set/config + * prefix-set = "prefix-set-5" + * match-set-options = "ANY" + * actions/bgp-actions/set-community/config + * method = "REFERENCE" + * option = "ADD" + * community-set-refs = "add_comm_60", "add_comm_70" + * actions/bgp-actions/config/set-local-pref = 5 + * actions/config/policy-result = "NEXT_STATEMENT" + + * statements/statement/config/name: "match_aspath_set_med" + * conditions/bgp-conditions/match-as-path-set/config/ + * as-path-set = "my_aspath" + * match-set-options = "ANY" + * actions/bgp-actions/config/ + * set-med = 100 + * actions/config/policy-result = "ACCEPT_ROUTE" + +* Use gnmi Set REPLACE option to configure the policies above on the DUT at this subtree level: + * `/routing-policy/policy-definitions` + +#### RT-7.11.2 Attach multi_policy as import policy + +* Use gnmi Set REPLACE option to apply the policy on the DUT bgp neighbor to the ATE port 1. + * at this subtree level: /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy + * Set the value `config/import-policy` = "multi-policy" + +#### RT-7.11.3 Verify import_multi_policy expected attributes are present + +* Verify expected attributes are present in ATE. + +> NOTE: (At the time of writing, the APIs necesary to do this validation are not yet available via the OTG API. A feature enhancement has been submitted.) + +#### RT-7.11.4 Configure export_multi_policy + +This replace method should guarantee that the previous step's import-policy is removed. + +* Use gnmi Set REPLACE option to apply the policy on the DUT bgp neighbor to the ATE port 1. + * at this subtree level: /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy + * Set the value `config/export-policy` = "multi-policy" + +#### RT-7.11.5 Verify export_multi_policy expected attributes are present + +* Verify expected attributes are present in ATE. + +#### Multi_policy results observed on ATE port 2 for both import and export cases + + | | Received | Communities | as-path | lpref | med | Notes | + | ------------ | -------- | --------------------------------- | ----------- | ----- | --- | --------------------------------------------------------- | + | prefix-set-1 | False | n/a | n/a | n/a | n/a | rejected by statement reject_route_community | + | prefix-set-2 | True | [ "20:1", "40:1", "40:2" ] | 65000 65100 | n/a | 100 | accepted | + | prefix-set-3 | False | n/a | n/a | n/a | n/a | rejected by statement if_30:.*_and_not_20:1_nested_reject | + | prefix-set-4 | False | n/a | n/a | n/a | n/a | rejected by statement if_30:.*_and_not_20:1_nested_reject | + | prefix-set-5 | True | [ "40:1","40:2", "60:1", "70:1" ] | 65000 65100 | 5 | 100 | accepted and match_comm_and_prefix_add_2_community_sets | + | prefix-set-6 | True | [ "10:1", "40:1", "40:2" ] | 65000 65100 | n/a | 100 | accepted | + +## OpenConfig Path and RPC Coverage + +The below yaml defines the OC paths intended to be covered by this test. OC paths used for test setup are not listed here. + +```yaml +paths: + ## Config Paths ## + # Policy definition + /routing-policy/policy-definitions/policy-definition/config/name: + /routing-policy/policy-definitions/policy-definition/statements/statement/config/name: + + # Policy for community-set configuration + /routing-policy/defined-sets/bgp-defined-sets/ext-community-sets/ext-community-set/config/ext-community-set-name: + /routing-policy/defined-sets/bgp-defined-sets/ext-community-sets/ext-community-set/config/ext-community-member: + + # Policy for match configuration + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/match-community-set/config/community-set: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/match-community-set/config/match-set-options: + + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/match-as-path-set/config/as-path-set: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/match-as-path-set/config/match-set-options: + + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/prefix-set: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/match-set-options: + + # Policy for bgp actions + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-community/config/method: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-community/config/options: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-community/reference/config/community-set-ref: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-community/reference/config/community-set-refs: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/config/set-local-pref: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/config/set-med: + + # Policy for bgp attachment + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy: + + ## State Paths ## + # Policy definition state + /routing-policy/policy-definitions/policy-definition/state/name: + /routing-policy/policy-definitions/policy-definition/statements/statement/state/name: + + # Policy for community-set match state + /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/state/community-set-name: + /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/state/community-member: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/match-ext-community-set/state/match-set-options: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/state/community-set: + + # Paths to verify policy state + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy: + + # Paths to verify prefixes sent and received + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/sent: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/received-pre-policy: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/received: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/installed: + +rpcs: + gnmi: + gNMI.Subscribe: + gNMI.Set: +``` + +## Minimum DUT Required + +vRX - Virtual Router Device diff --git a/feature/bgp/policybase/otg_tests/import_export_multi/import_export_multi.go b/feature/bgp/policybase/otg_tests/import_export_multi/import_export_multi.go new file mode 100644 index 00000000000..c5132e21189 --- /dev/null +++ b/feature/bgp/policybase/otg_tests/import_export_multi/import_export_multi.go @@ -0,0 +1,535 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package import_export_test covers RT-7.11: BGP Policy - Import/Export Policy Action Using Multiple Criteria +package import_export_test + +import ( + "strconv" + "testing" + "time" + + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/cfgplugins" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/otgutils" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" +) + +const ( + prefixV4Len = 30 + prefixV6Len = 126 + trafficPps = 100 + totalPackets = 1200 + bgpName = "BGP" + medValue = 100 + localPref = 5 + parentPolicy = "multiPolicy" + callPolicy = "match_community_regex" + rejectStatement = "reject_route_community" + nestedRejectStatement = "if_30_and_not_20_nested_reject" + callPolicyStatement = "match_community_regex" + addMissingCommunitiesStatement = "add_communities_if_missing" + matchCommPrefixAddCommuStatement = "match_comm_and_prefix_add_2_community_sets" + matchAspathSetMedStatement = "match_aspath_set_med" + rejectCommunitySet = "reject_communities" + nestedRejectCommunitySet = "accept_communities" + regexCommunitySet = "regex-community" + addCommunitiesSetRefs = "add-communities" + myCommunitySet = "my_community" + prefixSetName = "prefix-set-5" + myAsPathName = "my_aspath" + bgpActionMethod = oc.SetCommunity_Method_REFERENCE + bgpSetCommunityOptionType = oc.BgpPolicy_BgpSetCommunityOptionType_ADD + prefixSetNameSetOptions = oc.RoutingPolicy_MatchSetOptionsRestrictedType_ANY + matchAny = oc.BgpPolicy_MatchSetOptionsType_ANY + matchInvert = oc.BgpPolicy_MatchSetOptionsType_INVERT + rejectResult = oc.RoutingPolicy_PolicyResultType_REJECT_ROUTE + nextstatementResult = oc.RoutingPolicy_PolicyResultType_NEXT_STATEMENT +) + +var prefixesV4 = [][]string{ + {"198.51.100.0", "198.51.100.4"}, + {"198.51.100.8", "198.51.100.12"}, + {"198.51.100.16", "198.51.100.20"}, + {"198.51.100.24", "198.51.100.28"}, + {"198.51.100.32", "198.51.100.36"}, + {"198.51.100.40", "198.51.100.44"}, +} + +var prefixesV6 = [][]string{ + {"2048:db1:64:64::0", "2048:db1:64:64::4"}, + {"2048:db1:64:64::8", "2048:db1:64:64::c"}, + {"2048:db1:64:64::10", "2048:db1:64:64::14"}, + {"2048:db1:64:64::18", "2048:db1:64:64::1c"}, + {"2048:db1:64:64::20", "2048:db1:64:64::24"}, + {"2048:db1:64:64::28", "2048:db1:64:64::2c"}, +} + +var communityMembers = [][][]int{ + { + {10, 1}, {11, 1}, + }, + { + {20, 1}, {21, 1}, + }, + { + {30, 1}, {31, 1}, + }, + { + {20, 2}, {30, 3}, + }, + { + {40, 1}, {41, 1}, + }, + { + {50, 1}, {51, 1}, + }, +} + +// TestMain triggers the test run +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +func configureImportExportAcceptAllBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, ipv6 string) { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + pdef1 := rp.GetOrCreatePolicyDefinition("routePolicy") + stmt1, err := pdef1.AppendNewStatement("routePolicyStatement") + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", "routePolicyStatement", err) + } + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + + dni := deviations.DefaultNetworkInstance(dut) + pathV6 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(ipv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy() + policyV6 := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(ipv6).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateApplyPolicy() + policyV6.SetImportPolicy([]string{"routePolicy"}) + policyV6.SetExportPolicy([]string{"routePolicy"}) + gnmi.Replace(t, dut, pathV6.Config(), policyV6) + + pathV4 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(ipv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() + policyV4 := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(ipv4).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateApplyPolicy() + policyV4.SetImportPolicy([]string{"routePolicy"}) + policyV4.SetExportPolicy([]string{"routePolicy"}) + gnmi.Replace(t, dut, pathV4.Config(), policyV4) + +} + +func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, ipv6 string) { + rejectCommunities := []string{"10:1"} + acceptCommunities := []string{"20:1"} + regexCommunities := []string{"(^|\\s)30:[0-9]+($|\\s)"} + addCommunitiesRefs := []string{"40:1", "40:2"} + addCommunitiesSetRefsAction := []string{"add-communities"} + setCommunitySetRefs := []string{"add_comm_60", "add_comm_70"} + myCommunitySets := []string{"50:1"} + + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + + // Configure the policy match_community_regex which will be called from multi_policy + + pdef2 := rp.GetOrCreatePolicyDefinition(callPolicy) + + // Configure match_community_regex:STATEMENT1:match_community_regex statement + + pd2stmt1, err := pdef2.AppendNewStatement(callPolicyStatement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", callPolicyStatement, err) + } + + // Configure regex_community:["(^|\\s)30:[0-9]+($|\\s)"] to match_community_regex statement + communitySetRegex := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(regexCommunitySet) + + pd2cs1 := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} + for _, commMatchPd2Cs1 := range regexCommunities { + if commMatchPd2Cs1 != "" { + pd2cs1 = append(pd2cs1, oc.UnionString(commMatchPd2Cs1)) + } + } + communitySetRegex.SetCommunityMember(pd2cs1) + communitySetRegex.SetMatchSetOptions(matchAny) + + if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { + pd2stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(regexCommunitySet) + } else { + pd2stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(regexCommunitySet) + } + + pd2stmt1.GetOrCreateActions().SetPolicyResult(nextstatementResult) + + // Configure the parent policy multi_policy. + + pdef1 := rp.GetOrCreatePolicyDefinition(parentPolicy) + + // Configure multi_policy:STATEMENT1: reject_route_community + stmt1, err := pdef1.AppendNewStatement(rejectStatement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", rejectStatement, err) + } + + // Configure reject_communities:[10:1] to reject_route_community statement + communitySetReject := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(rejectCommunitySet) + + cs1 := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} + for _, commMatch1 := range rejectCommunities { + if commMatch1 != "" { + cs1 = append(cs1, oc.UnionString(commMatch1)) + } + } + communitySetReject.SetCommunityMember(cs1) + communitySetReject.SetMatchSetOptions(matchAny) + + if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { + stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(rejectCommunitySet) + } else { + stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(rejectCommunitySet) + } + + stmt1.GetOrCreateActions().SetPolicyResult(rejectResult) + + // Configure multi_policy:STATEMENT2:if_30:.*_and_not_20:1_nested_reject + + stmt2, err := pdef1.AppendNewStatement(nestedRejectStatement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", nestedRejectStatement, err) + } + + // Call child policy match_community_regex from parent policy multi_policy + + statPath := rp.GetOrCreatePolicyDefinition(parentPolicy).GetStatement(nestedRejectStatement) + statPath.GetOrCreateConditions().SetCallPolicy(callPolicy) + + // Configure accept_communities:[20:1] to if_30_and_not_20_nested_reject statement + communitySetNestedReject := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(nestedRejectCommunitySet) + + cs2 := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} + for _, commMatch2 := range acceptCommunities { + if commMatch2 != "" { + cs2 = append(cs2, oc.UnionString(commMatch2)) + } + } + communitySetNestedReject.SetCommunityMember(cs2) + communitySetNestedReject.SetMatchSetOptions(matchInvert) + + if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { + stmt2.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(nestedRejectCommunitySet) + } else { + stmt2.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(nestedRejectCommunitySet) + } + + stmt2.GetOrCreateActions().SetPolicyResult(rejectResult) + + // Configure multi_policy:STATEMENT3: add_communities_if_missing statement + stmt3, err := pdef1.AppendNewStatement(addMissingCommunitiesStatement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", addMissingCommunitiesStatement, err) + } + + // Configure add-communities: [ "40:1", "40:2" ] to add_communities_if_missing statement + + communitySetRefsAddCommunities := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(addCommunitiesSetRefs) + + cs3 := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} + for _, commMatch4 := range addCommunitiesRefs { + if commMatch4 != "" { + cs3 = append(cs3, oc.UnionString(commMatch4)) + } + } + communitySetRefsAddCommunities.SetCommunityMember(cs3) + communitySetRefsAddCommunities.SetMatchSetOptions(matchInvert) + + if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { + stmt3.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(addCommunitiesSetRefs) + } else { + if deviations.BgpCommunitySetRefsUnsupported(dut) { + t.Logf("TODO: community-set-refs not supported b/316833803") + stmt3.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(addCommunitiesSetRefs) + } + } + + if deviations.BgpCommunitySetRefsUnsupported(dut) { + t.Logf("TODO: community-set-refs not supported b/316833803") + } else { + stmt3.GetOrCreateActions().GetOrCreateBgpActions().GetSetCommunity().GetOrCreateReference().SetCommunitySetRefs(addCommunitiesSetRefsAction) + stmt3.GetOrCreateActions().GetOrCreateBgpActions().GetSetCommunity().SetMethod(bgpActionMethod) + stmt3.GetOrCreateActions().GetOrCreateBgpActions().GetSetCommunity().SetOptions(bgpSetCommunityOptionType) + } + + stmt3.GetOrCreateActions().SetPolicyResult(nextstatementResult) + + // Configure multi_policy:STATEMENT4: match_comm_and_prefix_add_2_community_sets statement + + stmt4, err := pdef1.AppendNewStatement(matchCommPrefixAddCommuStatement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", matchCommPrefixAddCommuStatement, err) + } + + // Configure my_community: [ "50:1" ] to match_comm_and_prefix_add_2_community_sets statement + communitySetMatchCommPrefixAddCommu := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(myCommunitySet) + + cs4 := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} + for _, commMatch5 := range myCommunitySets { + if commMatch5 != "" { + cs4 = append(cs4, oc.UnionString(commMatch5)) + } + } + communitySetMatchCommPrefixAddCommu.SetCommunityMember(cs4) + communitySetMatchCommPrefixAddCommu.SetMatchSetOptions(matchAny) + + if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { + stmt4.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(myCommunitySet) + } else { + stmt4.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(myCommunitySet) + } + + // configure match-prefix-set: prefix-set-5 to match_comm_and_prefix_add_2_community_sets statement + stmt4.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetPrefixSet(prefixSetName) + stmt4.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetMatchSetOptions(prefixSetNameSetOptions) + + pset := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet(prefixSetName) + pset.GetOrCreatePrefix(prefixesV4[4][0]+"/29", "29..30") + if !deviations.SkipPrefixSetMode(dut) { + pset.SetMode(oc.PrefixSet_Mode_IPV4) + } + + psetV6 := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet(prefixSetName + "_V6") + psetV6.GetOrCreatePrefix(prefixesV6[4][0]+"/125", "125..126") + if !deviations.SkipPrefixSetMode(dut) { + psetV6.SetMode(oc.PrefixSet_Mode_IPV6) + } + + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(prefixSetName).Config(), pset) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(prefixSetName+"_V6").Config(), psetV6) + + if deviations.BgpCommunitySetRefsUnsupported(dut) { + t.Logf("TODO: community-set-refs not supported b/316833803") + } else { + // TODO: Add bgp-actions: community-set-refs to match_comm_and_prefix_add_2_community_sets statement + stmt4.GetOrCreateActions().GetOrCreateBgpActions().GetSetCommunity().GetOrCreateReference().SetCommunitySetRefs(setCommunitySetRefs) + stmt4.GetOrCreateActions().GetOrCreateBgpActions().GetSetCommunity().SetMethod(oc.SetCommunity_Method_REFERENCE) + stmt4.GetOrCreateActions().GetOrCreateBgpActions().GetSetCommunity().SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) + } + // set-local-pref = 5 + stmt4.GetOrCreateActions().GetOrCreateBgpActions().SetSetLocalPref(localPref) + + stmt4.GetOrCreateActions().SetPolicyResult(nextstatementResult) + + // Configure multi_policy:STATEMENT5: match_aspath_set_med statement + stmt5, err := pdef1.AppendNewStatement(matchAspathSetMedStatement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", matchAspathSetMedStatement, err) + } + + // TODO create as-path-set on the DUT, match-as-path-set not support. + // Configure set-med 100 + stmt5.GetOrCreateActions().GetOrCreateBgpActions().SetMed = oc.UnionUint32(medValue) + + stmt5.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + + // Configure the parent BGP import and export policy. + dni := deviations.DefaultNetworkInstance(dut) + pathV6 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(ipv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy() + policyV6 := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(ipv6).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateApplyPolicy() + policyV6.SetImportPolicy([]string{parentPolicy}) + policyV6.SetExportPolicy([]string{parentPolicy}) + policyV6.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + policyV6.SetDefaultExportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + gnmi.Replace(t, dut, pathV6.Config(), policyV6) + + pathV4 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(ipv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() + policyV4 := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(ipv4).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateApplyPolicy() + policyV4.SetImportPolicy([]string{parentPolicy}) + policyV4.SetExportPolicy([]string{parentPolicy}) + policyV4.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + policyV4.SetDefaultExportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + gnmi.Replace(t, dut, pathV4.Config(), policyV4) +} + +func configureOTG(t *testing.T, bs *cfgplugins.BGPSession, prefixesV4 [][]string, prefixesV6 [][]string, communityMembers [][][]int) { + t.Logf("configure OTG") + devices := bs.ATETop.Devices().Items() + + ipv4 := devices[1].Ethernets().Items()[0].Ipv4Addresses().Items()[0] + bgp4Peer := devices[1].Bgp().Ipv4Interfaces().Items()[0].Peers().Items()[0] + + ipv6 := devices[1].Ethernets().Items()[0].Ipv6Addresses().Items()[0] + bgp6Peer := devices[1].Bgp().Ipv6Interfaces().Items()[0].Peers().Items()[0] + + for index, prefixes := range prefixesV4 { + bgp4PeerRoute := bgp4Peer.V4Routes().Add() + bgp4PeerRoute.SetName(bs.ATEPorts[1].Name + ".BGP4.peer.dut." + strconv.Itoa(index)) + bgp4PeerRoute.SetNextHopIpv4Address(ipv4.Address()) + + route4Address1 := bgp4PeerRoute.Addresses().Add().SetAddress(prefixes[0]) + route4Address1.SetPrefix(prefixV4Len) + route4Address2 := bgp4PeerRoute.Addresses().Add().SetAddress(prefixes[1]) + route4Address2.SetPrefix(prefixV4Len) + + bgp6PeerRoute := bgp6Peer.V6Routes().Add() + bgp6PeerRoute.SetName(bs.ATEPorts[1].Name + ".BGP6.peer.dut." + strconv.Itoa(index)) + bgp6PeerRoute.SetNextHopIpv6Address(ipv6.Address()) + + route6Address1 := bgp6PeerRoute.Addresses().Add().SetAddress(prefixesV6[index][0]) + route6Address1.SetPrefix(prefixV6Len) + route6Address2 := bgp6PeerRoute.Addresses().Add().SetAddress(prefixesV6[index][1]) + route6Address2.SetPrefix(prefixV6Len) + + for _, commu := range communityMembers[index] { + if commu[0] != 0 { + commv4 := bgp4PeerRoute.Communities().Add() + commv4.SetType(gosnappi.BgpCommunityType.MANUAL_AS_NUMBER) + commv4.SetAsNumber(uint32(commu[0])) + commv4.SetAsCustom(uint32(commu[1])) + + commv6 := bgp6PeerRoute.Communities().Add() + commv6.SetType(gosnappi.BgpCommunityType.MANUAL_AS_NUMBER) + commv6.SetAsNumber(uint32(commu[0])) + commv6.SetAsCustom(uint32(commu[1])) + } + } + } +} + +func configureFlowV4(t *testing.T, bs *cfgplugins.BGPSession) { + t.Logf("configure V4 Flow on traffic generator") + for index, prefixPairV4 := range prefixesV4 { + flow := bs.ATETop.Flows().Add().SetName("flow" + "ipv4" + strconv.Itoa(index)) + flow.Metrics().SetEnable(true) + + flow.TxRx().Device(). + SetTxNames([]string{bs.ATEPorts[0].Name + ".IPv4"}). + SetRxNames([]string{bs.ATEPorts[1].Name + ".BGP4.peer.dut." + strconv.Itoa(index)}) + + flow.Duration().FixedPackets().SetPackets(totalPackets) + flow.Size().SetFixed(1500) + flow.Rate().SetPps(trafficPps) + + e := flow.Packet().Add().Ethernet() + e.Src().SetValue(bs.ATEPorts[1].MAC) + + v4 := flow.Packet().Add().Ipv4() + v4.Src().SetValue(bs.ATEPorts[0].IPv4) + v4.Dst().SetValues(prefixPairV4) + } +} + +func configureFlowV6(t *testing.T, bs *cfgplugins.BGPSession) { + t.Logf("configure V6 Flow on traffic generator") + for index, prefixPairV6 := range prefixesV6 { + flow := bs.ATETop.Flows().Add().SetName("flow" + "ipv6" + strconv.Itoa(index)) + flow.Metrics().SetEnable(true) + + flow.TxRx().Device(). + SetTxNames([]string{bs.ATEPorts[0].Name + ".IPv6"}). + SetRxNames([]string{bs.ATEPorts[1].Name + ".BGP6.peer.dut." + strconv.Itoa(index)}) + + flow.Duration().FixedPackets().SetPackets(totalPackets) + flow.Size().SetFixed(1500) + flow.Rate().SetPps(trafficPps) + + e := flow.Packet().Add().Ethernet() + e.Src().SetValue(bs.ATEPorts[1].MAC) + + v6 := flow.Packet().Add().Ipv6() + v6.Src().SetValue(bs.ATEPorts[0].IPv6) + v6.Dst().SetValues(prefixPairV6) + } +} + +func verifyTrafficV4AndV6(t *testing.T, bs *cfgplugins.BGPSession, testResults [6]bool) { + + sleepTime := time.Duration(totalPackets/trafficPps) + 2 + bs.ATE.OTG().StartTraffic(t) + time.Sleep(time.Second * sleepTime) + bs.ATE.OTG().StopTraffic(t) + + otgutils.LogFlowMetrics(t, bs.ATE.OTG(), bs.ATETop) + otgutils.LogPortMetrics(t, bs.ATE.OTG(), bs.ATETop) + + for index, prefixPairV4 := range prefixesV4 { + t.Logf("Running traffic test for IPv4 prefixes: [%s, %s]. Expected Result: [%t]", prefixPairV4[0], prefixPairV4[1], testResults[index]) + t.Logf("Running traffic test for IPv6 prefixes: [%s, %s]. Expected Result: [%t]", prefixesV6[index][0], prefixesV6[index][1], testResults[index]) + + t.Log("Checking flow telemetry for v4...") + recvMetric := gnmi.Get(t, bs.ATE.OTG(), gnmi.OTG().Flow("flow"+"ipv4"+strconv.Itoa(index)).State()) + txPackets := recvMetric.GetCounters().GetOutPkts() + rxPackets := recvMetric.GetCounters().GetInPkts() + lostPackets := txPackets - rxPackets + lossPct := lostPackets * 100 / txPackets + + t.Log("Checking flow telemetry for v6...") + recvMetric6 := gnmi.Get(t, bs.ATE.OTG(), gnmi.OTG().Flow("flow"+"ipv6"+strconv.Itoa(index)).State()) + txPackets6 := recvMetric6.GetCounters().GetOutPkts() + rxPackets6 := recvMetric6.GetCounters().GetInPkts() + lostPackets6 := txPackets6 - rxPackets6 + lossPct6 := lostPackets6 * 100 / txPackets6 + + if txPackets != rxPackets && testResults[index] { + t.Errorf("FAIL- got %v%% packet loss for %s flow and prefixes: [%s, %s]; want < 0%% traffic loss", lossPct, "flow"+"ipv4"+strconv.Itoa(index), prefixPairV4[0], prefixPairV4[1]) + } else if rxPackets != 0 && !testResults[index] { + t.Errorf("FAIL- got %v%% packet loss for %s flow and prefixes: [%s, %s]; want >100%% traffic loss", lossPct, "flow"+"ipv4"+strconv.Itoa(index), prefixPairV4[0], prefixPairV4[1]) + } else if txPackets6 != rxPackets6 && testResults[index] { + t.Errorf("FAIL- got %v%% packet loss for %s flow and prefixes: [%s, %s]; want < 0%% traffic loss", lossPct6, "flow"+"ipv6"+strconv.Itoa(index), prefixesV6[index][0], prefixesV6[index][1]) + } else if rxPackets6 != 0 && !testResults[index] { + t.Errorf("FAIL- got %v%% packet loss for %s flow and prefixes: [%s, %s]; want >100%% traffic loss", lossPct6, "flow"+"ipv6"+strconv.Itoa(index), prefixesV6[index][0], prefixesV6[index][1]) + } else { + t.Logf("Traffic validation successful for Prefixes: [%s, %s]. Result: [%t] PacketsTx: %d PacketsRx: %d", prefixesV6[index][0], prefixesV6[index][1], testResults[index], txPackets6, rxPackets6) + } + + } +} + +// TestImportExportMultifacetMatchActionsBGPPolicy covers RT-7.11 +func TestImportExportMultifacetMatchActionsBGPPolicy(t *testing.T) { + bs := cfgplugins.NewBGPSession(t, cfgplugins.PortCount2, nil) + bs.WithEBGP(t, []oc.E_BgpTypes_AFI_SAFI_TYPE{oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST}, []string{ + "port1", "port2"}, true, false) + + configureOTG(t, bs, prefixesV4, prefixesV6, communityMembers) + bs.PushAndStart(t) + + t.Log("Verify DUT BGP sessions up") + cfgplugins.VerifyDUTBGPEstablished(t, bs.DUT) + t.Log("Verify OTG BGP sessions up") + cfgplugins.VerifyOTGBGPEstablished(t, bs.ATE) + + ipv4 := bs.ATETop.Devices().Items()[1].Ethernets().Items()[0].Ipv4Addresses().Items()[0].Address() + ipv6 := bs.ATETop.Devices().Items()[1].Ethernets().Items()[0].Ipv6Addresses().Items()[0].Address() + + t.Logf("Verify Import Export Accept all bgp policy") + configureImportExportAcceptAllBGPPolicy(t, bs.DUT, ipv4, ipv6) + + configureFlowV4(t, bs) + configureFlowV6(t, bs) + + bs.PushAndStartATE(t) + + testResults := [6]bool{true, true, true, true, true, true} + verifyTrafficV4AndV6(t, bs, testResults) + + configureImportExportMultifacetMatchActionsBGPPolicy(t, bs.DUT, ipv4, ipv6) + + testResults1 := [6]bool{false, true, false, false, true, true} + verifyTrafficV4AndV6(t, bs, testResults1) +} diff --git a/feature/bgp/policybase/otg_tests/import_export_multi/metadata.textproto b/feature/bgp/policybase/otg_tests/import_export_multi/metadata.textproto new file mode 100644 index 00000000000..82c296ae6d0 --- /dev/null +++ b/feature/bgp/policybase/otg_tests/import_export_multi/metadata.textproto @@ -0,0 +1,24 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +plan_id: "RT-7.11" +description: "BGP Policy - Import/Export Policy Action Using Multiple Criteria" +uuid: "520f6013-0188-45a3-b5be-ce13c55ce7cd" + +testbed: TESTBED_DUT_ATE_2LINKS +tags: [TAGS_AGGREGATION, TAGS_DATACENTER_EDGE] +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + omit_l2_mtu: true + interface_enabled: true + default_network_instance: "default" + missing_value_for_defaults: true + skip_set_rp_match_set_options: true + bgp_community_set_refs_unsupported: true + bgp_conditions_match_community_set_unsupported: true + skip_prefix_set_mode: true + } +} diff --git a/feature/experimental/bgp/ate_tests/bgp_remove_private_as/metadata.textproto b/feature/bgp/policybase/otg_tests/nested_policies/metadata.textproto similarity index 53% rename from feature/experimental/bgp/ate_tests/bgp_remove_private_as/metadata.textproto rename to feature/bgp/policybase/otg_tests/nested_policies/metadata.textproto index 4a298ab13bd..e6691f8f821 100644 --- a/feature/experimental/bgp/ate_tests/bgp_remove_private_as/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/nested_policies/metadata.textproto @@ -1,24 +1,20 @@ # proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto # proto-message: Metadata - -uuid: "2b4372c2-8827-4624-837c-f44b1a54edf7" -plan_id: "RT-1.11" -description: "BGP remove private AS" +uuid: "aa9b83cb-fd87-4098-ab3d-db05c66fc3c0" +plan_id: "RT-1.30" +description: "BGP nested import/export policy attachment" testbed: TESTBED_DUT_ATE_2LINKS +tags: TAGS_DATACENTER_EDGE platform_exceptions: { platform: { vendor: ARISTA } deviations: { + omit_l2_mtu: true interface_enabled: true default_network_instance: "default" + missing_value_for_defaults: true + skip_set_rp_match_set_options: true } } -platform_exceptions: { - platform: { - vendor: NOKIA - } - deviations: { - interface_enabled: true - } -} + diff --git a/feature/bgp/policybase/otg_tests/nested_policies/nested_policies_test.go b/feature/bgp/policybase/otg_tests/nested_policies/nested_policies_test.go new file mode 100644 index 00000000000..d787c209d6e --- /dev/null +++ b/feature/bgp/policybase/otg_tests/nested_policies/nested_policies_test.go @@ -0,0 +1,718 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nested_policies_test + +import ( + "fmt" + "net" + "strings" + "testing" + "time" + + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/otgutils" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + otgtelemetry "github.com/openconfig/ondatra/gnmi/otg" + "github.com/openconfig/ygnmi/ygnmi" + "github.com/openconfig/ygot/ygot" +) + +const ( + ipv4PrefixLen = 30 + ipv6PrefixLen = 126 + v41Route = "203.0.113.0" + v41TrafficStart = "203.0.113.1" + v42Route = "198.51.100.0" + v42TrafficStart = "198.51.100.1" + v4RoutePrefix = uint32(24) + v61Route = "2001:db8:128:128::" + v61TrafficStart = "2001:db8:128:128::1" + v62Route = "2001:db8:128:129::" + v62TrafficStart = "2001:db8:128:129::1" + v6RoutePrefix = uint32(64) + dutAS = uint32(65656) + ateAS1 = uint32(65657) + ateAS2 = uint32(65658) + bgpName = "BGP" + maskLenExact = "exact" + localPref = 200 + med = 1000 + v4Flow = "flow-v4" + v4PrefixPolicy = "prefix-policy-v4" + v4PrefixStatement = "prefix-statement-v4" + v4PrefixSet = "prefix-set-v4" + v4LPPolicy = "lp-policy-v4" + v4LPStatement = "lp-statement-v4" + v4ASPPolicy = "asp-policy-v4" + v4ASPStatement = "asp-statement-v4" + v4MedPolicy = "med-policy-v4" + v4MedStatement = "med-statement-v4" + v6Flow = "flow-v6" + v6PrefixPolicy = "prefix-policy-v6" + v6PrefixStatement = "prefix-statement-v6" + v6PrefixSet = "prefix-set-v6" + v6LPPolicy = "lp-policy-v6" + v6LPStatement = "lp-statement-v6" + v6ASPPolicy = "asp-policy-v6" + v6ASPStatement = "asp-statement-v6" + v6MedPolicy = "med-policy-v6" + v6MedStatement = "med-statement-v6" + peerGrpNamev4 = "BGP-PEER-GROUP-V4" + peerGrpNamev6 = "BGP-PEER-GROUP-V6" +) + +var ( + dutPort1 = attrs.Attributes{ + Desc: "dutPort1", + IPv4: "192.0.2.1", + IPv4Len: ipv4PrefixLen, + IPv6: "2001:db8::192:0:2:1", + IPv6Len: ipv6PrefixLen, + } + + atePort1 = attrs.Attributes{ + Name: "atePort1", + MAC: "02:00:01:01:01:01", + IPv4: "192.0.2.2", + IPv4Len: ipv4PrefixLen, + IPv6: "2001:db8::192:0:2:2", + IPv6Len: ipv6PrefixLen, + } + + dutPort2 = attrs.Attributes{ + Desc: "dutPort2", + IPv4: "192.0.2.5", + IPv4Len: ipv4PrefixLen, + IPv6: "2001:db8::192:0:2:5", + IPv6Len: ipv6PrefixLen, + } + + atePort2 = attrs.Attributes{ + Name: "atePort2", + MAC: "02:00:01:01:01:02", + IPv4: "192.0.2.6", + IPv4Len: ipv4PrefixLen, + IPv6: "2001:db8::192:0:2:6", + IPv6Len: ipv6PrefixLen, + } + + advertisedIPv41 = Prefix{address: v41Route, prefix: v4RoutePrefix} + advertisedIPv42 = Prefix{address: v42Route, prefix: v4RoutePrefix} + advertisedIPv61 = Prefix{address: v61Route, prefix: v6RoutePrefix} + advertisedIPv62 = Prefix{address: v62Route, prefix: v6RoutePrefix} +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +type Prefix struct { + address string + prefix uint32 +} + +func (ip *Prefix) cidr(t *testing.T) string { + _, net, err := net.ParseCIDR(fmt.Sprintf("%s/%d", ip.address, ip.prefix)) + if err != nil { + t.Fatal(err) + } + return net.String() +} + +type testData struct { + dut *ondatra.DUTDevice + ate *ondatra.ATEDevice + top gosnappi.Config + otgP1 gosnappi.Device + otgP2 gosnappi.Device +} + +type testCase struct { + name string + desc string + applyPolicy func(t *testing.T, dut *ondatra.DUTDevice) + validate func(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) + ipv4 bool + flowConfig flowConfig +} + +type flowConfig struct { + src attrs.Attributes + dstNw string + dstIP string +} + +func TestBGPNestedPolicies(t *testing.T) { + dut := ondatra.DUT(t, "dut") + configureDUT(t, dut) + + ate := ondatra.ATE(t, "ate") + top := gosnappi.NewConfig() + devs := configureOTG(t, ate, top) + td := testData{ + dut: dut, + ate: ate, + top: top, + otgP1: devs[0], + otgP2: devs[1], + } + td.advertiseRoutesWithEBGP(t) + ate.OTG().PushConfig(t, top) + ate.OTG().StartProtocols(t) + defer ate.OTG().StopProtocols(t) + otgutils.WaitForARP(t, ate.OTG(), top, "IPv4") + otgutils.WaitForARP(t, ate.OTG(), top, "IPv6") + td.verifyDUTBGPEstablished(t) + td.verifyOTGBGPEstablished(t) + + testCases := []testCase{ + { + name: "IPv4BGPNestedmportPolicy", + desc: "IPv4 BGP Nested import policy test", + applyPolicy: configureImportRoutingPolicy, + validate: validateImportRoutingPolicy, + ipv4: true, + flowConfig: flowConfig{src: atePort2, dstNw: "v4-bgpNet-dev1", dstIP: v41TrafficStart}, + }, + { + name: "IPv4BGPNestedExportPolicy", + desc: "IPv4 BGP Nested export policy test", + applyPolicy: configureExportRoutingPolicy, + validate: validateExportRoutingPolicy, + ipv4: true, + flowConfig: flowConfig{src: atePort1, dstNw: "v4-bgpNet-dev2", dstIP: v42TrafficStart}, + }, + { + name: "IPv6BGPNestedImportPolicy", + desc: "IPv6 BGP Nested import policy test", + applyPolicy: configureImportRoutingPolicyV6, + validate: validateImportRoutingPolicyV6, + ipv4: false, + flowConfig: flowConfig{src: atePort2, dstNw: "v6-bgpNet-dev1", dstIP: v61TrafficStart}, + }, + { + name: "IPv6BGPNestedExportPolicy", + desc: "IPv6 BGP Nested export policy test", + applyPolicy: configureExportRoutingPolicyV6, + validate: validateExportRoutingPolicyV6, + ipv4: false, + flowConfig: flowConfig{src: atePort1, dstNw: "v6-bgpNet-dev2", dstIP: v62TrafficStart}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Logf("Description: %s", tc.desc) + tc.applyPolicy(t, dut) + tc.validate(t, dut, ate) + if tc.ipv4 { + createFlow(t, td, tc.flowConfig) + checkTraffic(t, td, v4Flow) + } else { + createFlowV6(t, td, tc.flowConfig) + checkTraffic(t, td, v6Flow) + } + }) + } +} + +// configureImportRoutingPolicy configures the dut for IPv4 BGP nested import policy test. +func configureImportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + + // Configure a route-policy to set the local preference. + pdef1 := rp.GetOrCreatePolicyDefinition(v4LPPolicy) + stmt1, err := pdef1.AppendNewStatement(v4LPStatement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", v4LPStatement, err) + } + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + stmt1.GetOrCreateActions().GetOrCreateBgpActions().SetSetLocalPref(localPref) + + // Configure a route-policy to match the prefix. + pdef2 := rp.GetOrCreatePolicyDefinition(v4PrefixPolicy) + stmt2, err := pdef2.AppendNewStatement(v4PrefixStatement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", v4PrefixStatement, err) + } + stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + t.Logf("Configuring nested policy") + // Configure a prefix-set for route filtering/matching. + prefixSet := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet(v4PrefixSet) + prefixSet.SetMode(oc.PrefixSet_Mode_IPV4) + prefixSet.GetOrCreatePrefix(advertisedIPv41.cidr(t), maskLenExact) + + if !deviations.SkipSetRpMatchSetOptions(dut) { + stmt2.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsRestrictedType_ANY) + } + stmt2.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetPrefixSet(v4PrefixSet) + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + + // Configure the nested policy. + dni := deviations.DefaultNetworkInstance(dut) + rpPolicy := root.GetOrCreateRoutingPolicy() + statPath := rpPolicy.GetOrCreatePolicyDefinition(v4LPPolicy).GetStatement(v4LPStatement).GetOrCreateConditions() + statPath.SetCallPolicy(v4PrefixPolicy) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rpPolicy) + + // Configure the parent BGP import policy. + path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(atePort1.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() + policy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(atePort1.IPv4).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateApplyPolicy() + policy.SetImportPolicy([]string{v4LPPolicy}) + gnmi.Update(t, dut, path.Config(), policy) +} + +func validateImportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { + dni := deviations.DefaultNetworkInstance(dut) + bgpRIBPath := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Rib() + locRib := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AfiSafi_Ipv4Unicast_LocRib](t, dut, bgpRIBPath.AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Ipv4Unicast().LocRib().State()) + found := false + for k, lr := range locRib.Route { + prefixAddr := strings.Split(lr.GetPrefix(), "/") + if prefixAddr[0] == advertisedIPv41.address { + found = true + t.Logf("Found Route(prefix %s, origin: %v, pathid: %d) => %s", k.Prefix, k.Origin, k.PathId, lr.GetPrefix()) + attrSet := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet](t, dut, bgpRIBPath.AttrSet(lr.GetAttrIndex()).State()) + if attrSet == nil || attrSet.GetLocalPref() != localPref { + t.Errorf("No local pref found for prefix %s", advertisedIPv41.address) + } + break + } + } + + if !found { + t.Errorf("No Route found for prefix %s", advertisedIPv41.address) + } +} + +// configureExportRoutingPolicy configures the dut for IPv4 BGP nested export policy test. +func configureExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + t.Logf("Configuring export routing policy") + pdef1 := rp.GetOrCreatePolicyDefinition(v4ASPPolicy) + stmt1, err := pdef1.AppendNewStatement(v4ASPStatement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", v4ASPStatement, err) + } + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetAsPathPrepend().SetAsn(dutAS) + + pdef2 := rp.GetOrCreatePolicyDefinition(v4MedPolicy) + stmt2, err := pdef2.AppendNewStatement(v4MedStatement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", v4MedStatement, err) + } + stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + stmt2.GetOrCreateActions().GetOrCreateBgpActions().SetSetMed(oc.UnionUint32(med)) + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + + // Configure the nested policy. + dni := deviations.DefaultNetworkInstance(dut) + rpPolicy := root.GetOrCreateRoutingPolicy() + statPath := rpPolicy.GetOrCreatePolicyDefinition(v4ASPPolicy).GetStatement(v4ASPStatement).GetOrCreateConditions() + statPath.SetCallPolicy(v4MedPolicy) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rpPolicy) + time.Sleep(time.Second * 60) + + // Configure the parent BGP import policy. + path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(atePort1.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() + policy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(atePort1.IPv4).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateApplyPolicy() + policy.SetExportPolicy([]string{v4ASPPolicy}) + gnmi.Update(t, dut, path.Config(), policy) + time.Sleep(time.Second * 60) +} + +func validateExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { + bgpPrefixes := gnmi.GetAll[*otgtelemetry.BgpPeer_UnicastIpv4Prefix](t, ate.OTG(), gnmi.OTG().BgpPeer("atePort1.BGP4.peer").UnicastIpv4PrefixAny().State()) + found := false + for _, bgpPrefix := range bgpPrefixes { + if bgpPrefix.Address != nil && bgpPrefix.GetAddress() == v42Route && + bgpPrefix.PrefixLength != nil && bgpPrefix.GetPrefixLength() == v4RoutePrefix { + found = true + t.Logf("Prefix recevied on OTG is correct, got prefix %v, want prefix %v", bgpPrefix, v42Route) + t.Logf("Prefix MED %d", bgpPrefix.GetMultiExitDiscriminator()) + if bgpPrefix.GetMultiExitDiscriminator() != med { + t.Errorf("For Prefix %v, got MED %d want MED %d", bgpPrefix.GetAddress(), bgpPrefix.GetMultiExitDiscriminator(), med) + } + asPaths := bgpPrefix.AsPath + for _, ap := range asPaths { + count := 0 + for _, an := range ap.AsNumbers { + if an == dutAS { + count++ + } + } + if count == 2 { + t.Logf("ASP for prefix %v is correct, got ASP %v", bgpPrefix.GetAddress(), ap.AsNumbers) + } + } + break + } + } + + if !found { + t.Errorf("No Route found for prefix %s", v42Route) + } +} + +// configureImportRoutingPolicyV6 configures the dut for IPv6 BGP nested import policy test. +func configureImportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice) { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + t.Logf("Configuring import routing policy") + pdef1 := rp.GetOrCreatePolicyDefinition(v6LPPolicy) + stmt1, err := pdef1.AppendNewStatement(v6LPStatement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", v6LPStatement, err) + } + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + stmt1.GetOrCreateActions().GetOrCreateBgpActions().SetSetLocalPref(localPref) + + pdef2 := rp.GetOrCreatePolicyDefinition(v6PrefixPolicy) + stmt2, err := pdef2.AppendNewStatement(v6PrefixStatement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", v6PrefixStatement, err) + } + stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + + prefixSet := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet(v6PrefixSet) + prefixSet.SetMode(oc.PrefixSet_Mode_IPV6) + prefixSet.GetOrCreatePrefix(advertisedIPv61.cidr(t), maskLenExact) + + if !deviations.SkipSetRpMatchSetOptions(dut) { + stmt2.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsRestrictedType_ANY) + } + stmt2.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetPrefixSet(v6PrefixSet) + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + + // Configure the nested policy. + dni := deviations.DefaultNetworkInstance(dut) + rpPolicy := root.GetOrCreateRoutingPolicy() + statPath := rpPolicy.GetOrCreatePolicyDefinition(v6LPPolicy).GetStatement(v6LPStatement).GetOrCreateConditions() + statPath.SetCallPolicy(v6PrefixPolicy) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rpPolicy) + + // Configure the parent BGP import policy. + path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(atePort1.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy() + policy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(atePort1.IPv6).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateApplyPolicy() + policy.SetImportPolicy([]string{v6LPPolicy}) + gnmi.Update(t, dut, path.Config(), policy) +} + +func validateImportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { + dni := deviations.DefaultNetworkInstance(dut) + bgpRIBPath := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Rib() + locRib := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AfiSafi_Ipv6Unicast_LocRib](t, dut, bgpRIBPath.AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Ipv6Unicast().LocRib().State()) + found := false + for k, lr := range locRib.Route { + prefixAddr := strings.Split(lr.GetPrefix(), "/") + t.Logf("lr.GetPrefix() -> %s, prefixAddr[0] -> %s, advertisedIPv61.address = %s", lr.GetPrefix(), prefixAddr[0], advertisedIPv61.address) + if prefixAddr[0] == advertisedIPv61.address { + found = true + t.Logf("Found Route(prefix %s, origin: %v, pathid: %d) => %s", k.Prefix, k.Origin, k.PathId, lr.GetPrefix()) + attrSet := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet](t, dut, bgpRIBPath.AttrSet(lr.GetAttrIndex()).State()) + if attrSet == nil || attrSet.GetLocalPref() != localPref { + t.Errorf("No local pref found for prefix %s", advertisedIPv61.address) + } + break + } + } + if !found { + t.Errorf("No Route found for prefix %s", advertisedIPv61.address) + } +} + +// configureExportRoutingPolicyV6 configures the dut for IPv6 BGP nested export policy test. +func configureExportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice) { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + t.Logf("Configuring export routing policy") + pdef1 := rp.GetOrCreatePolicyDefinition(v6ASPPolicy) + stmt1, err := pdef1.AppendNewStatement(v6ASPStatement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", v6ASPStatement, err) + } + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetAsPathPrepend().SetAsn(dutAS) + + pdef2 := rp.GetOrCreatePolicyDefinition(v6MedPolicy) + stmt2, err := pdef2.AppendNewStatement(v6MedStatement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", v6MedStatement, err) + } + stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + stmt2.GetOrCreateActions().GetOrCreateBgpActions().SetSetMed(oc.UnionUint32(med)) + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + + // Configure the nested policy. + dni := deviations.DefaultNetworkInstance(dut) + rpPolicy := root.GetOrCreateRoutingPolicy() + statPath := rpPolicy.GetOrCreatePolicyDefinition(v6ASPPolicy).GetStatement(v6ASPStatement).GetOrCreateConditions() + statPath.SetCallPolicy(v6MedPolicy) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rpPolicy) + time.Sleep(time.Second * 60) + + // Configure the parent BGP export policy. + path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(atePort1.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy() + policy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(atePort1.IPv6).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateApplyPolicy() + policy.SetExportPolicy([]string{v6ASPPolicy}) + gnmi.Update(t, dut, path.Config(), policy) + time.Sleep(time.Second * 60) +} + +func validateExportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { + bgpPrefixes := gnmi.GetAll[*otgtelemetry.BgpPeer_UnicastIpv6Prefix](t, ate.OTG(), gnmi.OTG().BgpPeer("atePort1.BGP6.peer").UnicastIpv6PrefixAny().State()) + + found := false + for _, bgpPrefix := range bgpPrefixes { + if bgpPrefix.Address != nil && bgpPrefix.GetAddress() == v62Route && + bgpPrefix.PrefixLength != nil && bgpPrefix.GetPrefixLength() == v6RoutePrefix { + found = true + t.Logf("Prefix recevied on OTG is correct, got prefix %v, want prefix %v", bgpPrefix, v62Route) + if bgpPrefix.GetMultiExitDiscriminator() != med { + t.Errorf("For Prefix %v, got MED %d want MED %d", bgpPrefix.GetAddress(), bgpPrefix.GetMultiExitDiscriminator(), med) + } + asPaths := bgpPrefix.AsPath + for _, ap := range asPaths { + count := 0 + for _, an := range ap.AsNumbers { + if an == dutAS { + count++ + } + } + if count == 2 { + t.Logf("ASP for prefix %v is correct, got ASP %v", bgpPrefix.GetAddress(), ap.AsNumbers) + } + } + break + } + } + + if !found { + t.Errorf("No Route found for prefix %s", v62Route) + } +} + +func createFlow(t *testing.T, td testData, fc flowConfig) { + td.top.Flows().Clear() + + t.Log("Configuring v4 traffic flow") + v4Flow := td.top.Flows().Add().SetName(v4Flow) + v4Flow.Metrics().SetEnable(true) + v4Flow.TxRx().Device(). + SetTxNames([]string{fc.src.Name + ".IPv4"}). + SetRxNames([]string{fc.dstNw}) + v4Flow.Size().SetFixed(512) + v4Flow.Rate().SetPps(100) + v4Flow.Duration().Continuous() + e1 := v4Flow.Packet().Add().Ethernet() + e1.Src().SetValue(fc.src.MAC) + v4 := v4Flow.Packet().Add().Ipv4() + v4.Src().SetValue(fc.src.IPv4) + v4.Dst().Increment().SetStart(fc.dstIP).SetCount(1) + + td.ate.OTG().PushConfig(t, td.top) + td.ate.OTG().StartProtocols(t) + otgutils.WaitForARP(t, td.ate.OTG(), td.top, "IPv4") +} + +func createFlowV6(t *testing.T, td testData, fc flowConfig) { + td.top.Flows().Clear() + + t.Log("Configuring v6 traffic flow") + v6Flow := td.top.Flows().Add().SetName(v6Flow) + v6Flow.Metrics().SetEnable(true) + v6Flow.TxRx().Device(). + SetTxNames([]string{fc.src.Name + ".IPv6"}). + SetRxNames([]string{fc.dstNw}) + v6Flow.Size().SetFixed(512) + v6Flow.Rate().SetPps(100) + v6Flow.Duration().Continuous() + e1 := v6Flow.Packet().Add().Ethernet() + e1.Src().SetValue(fc.src.MAC) + v6 := v6Flow.Packet().Add().Ipv6() + v6.Src().SetValue(fc.src.IPv6) + v6.Dst().Increment().SetStart(fc.dstIP).SetCount(1) + + td.ate.OTG().PushConfig(t, td.top) + td.ate.OTG().StartProtocols(t) + otgutils.WaitForARP(t, td.ate.OTG(), td.top, "IPv6") +} + +func checkTraffic(t *testing.T, td testData, flowName string) { + td.ate.OTG().StartTraffic(t) + time.Sleep(time.Second * 30) + td.ate.OTG().StopTraffic(t) + + otgutils.LogFlowMetrics(t, td.ate.OTG(), td.top) + otgutils.LogPortMetrics(t, td.ate.OTG(), td.top) + + t.Log("Checking flow telemetry...") + recvMetric := gnmi.Get(t, td.ate.OTG(), gnmi.OTG().Flow(flowName).State()) + txPackets := recvMetric.GetCounters().GetOutPkts() + rxPackets := recvMetric.GetCounters().GetInPkts() + lostPackets := txPackets - rxPackets + lossPct := lostPackets * 100 / txPackets + + if lossPct > 1 { + t.Errorf("FAIL- got %v%% packet loss for %s ; want < 1%%", lossPct, flowName) + } +} + +func (td *testData) advertiseRoutesWithEBGP(t *testing.T) { + t.Helper() + + root := &oc.Root{} + ni := root.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(td.dut)) + bgpP := ni.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName) + bgpP.SetEnabled(true) + bgp := bgpP.GetOrCreateBgp() + + g := bgp.GetOrCreateGlobal() + g.SetAs(dutAS) + g.SetRouterId(dutPort1.IPv4) + g.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) + g.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + + pgv4 := bgp.GetOrCreatePeerGroup(peerGrpNamev4) + pgv4.PeerGroupName = ygot.String(peerGrpNamev4) + pgv6 := bgp.GetOrCreatePeerGroup(peerGrpNamev6) + pgv6.PeerGroupName = ygot.String(peerGrpNamev6) + nV41 := bgp.GetOrCreateNeighbor(atePort1.IPv4) + nV41.SetPeerAs(ateAS1) + nV41.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) + nV41.PeerGroup = ygot.String(peerGrpNamev4) + nV42 := bgp.GetOrCreateNeighbor(atePort2.IPv4) + nV42.SetPeerAs(ateAS2) + nV42.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) + nV42.PeerGroup = ygot.String(peerGrpNamev4) + + nV61 := bgp.GetOrCreateNeighbor(atePort1.IPv6) + nV61.SetPeerAs(ateAS1) + nV61.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + nV61.PeerGroup = ygot.String(peerGrpNamev6) + nV62 := bgp.GetOrCreateNeighbor(atePort2.IPv6) + nV62.SetPeerAs(ateAS2) + nV62.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + nV62.PeerGroup = ygot.String(peerGrpNamev6) + gnmi.Update(t, td.dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(td.dut)).Config(), ni) + + // Configure eBGP on OTG port1. + ipv41 := td.otgP1.Ethernets().Items()[0].Ipv4Addresses().Items()[0] + dev1BGP := td.otgP1.Bgp().SetRouterId(atePort1.IPv4) + bgp4Peer1 := dev1BGP.Ipv4Interfaces().Add().SetIpv4Name(ipv41.Name()).Peers().Add().SetName(td.otgP1.Name() + ".BGP4.peer") + bgp4Peer1.SetPeerAddress(dutPort1.IPv4).SetAsNumber(ateAS1).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + bgp4Peer1.LearnedInformationFilter().SetUnicastIpv4Prefix(true) + + ipv61 := td.otgP1.Ethernets().Items()[0].Ipv6Addresses().Items()[0] + bgp6Peer1 := dev1BGP.Ipv6Interfaces().Add().SetIpv6Name(ipv61.Name()).Peers().Add().SetName(td.otgP1.Name() + ".BGP6.peer") + bgp6Peer1.SetPeerAddress(dutPort1.IPv6).SetAsNumber(ateAS1).SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + bgp6Peer1.LearnedInformationFilter().SetUnicastIpv6Prefix(true) + + // Configure emulated network on ATE port1. + netv41 := bgp4Peer1.V4Routes().Add().SetName("v4-bgpNet-dev1") + netv41.Addresses().Add().SetAddress(advertisedIPv41.address).SetPrefix(advertisedIPv41.prefix) + netv61 := bgp6Peer1.V6Routes().Add().SetName("v6-bgpNet-dev1") + netv61.Addresses().Add().SetAddress(advertisedIPv61.address).SetPrefix(advertisedIPv61.prefix) + + // Configure eBGP on OTG port2. + ipv42 := td.otgP2.Ethernets().Items()[0].Ipv4Addresses().Items()[0] + dev2BGP := td.otgP2.Bgp().SetRouterId(atePort2.IPv4) + bgp4Peer2 := dev2BGP.Ipv4Interfaces().Add().SetIpv4Name(ipv42.Name()).Peers().Add().SetName(td.otgP2.Name() + ".BGP4.peer") + bgp4Peer2.SetPeerAddress(dutPort2.IPv4).SetAsNumber(ateAS2).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + bgp4Peer2.LearnedInformationFilter().SetUnicastIpv4Prefix(true) + + ipv62 := td.otgP2.Ethernets().Items()[0].Ipv6Addresses().Items()[0] + bgp6Peer2 := dev2BGP.Ipv6Interfaces().Add().SetIpv6Name(ipv62.Name()).Peers().Add().SetName(td.otgP2.Name() + ".BGP6.peer") + bgp6Peer2.SetPeerAddress(dutPort2.IPv6).SetAsNumber(ateAS2).SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + bgp6Peer2.LearnedInformationFilter().SetUnicastIpv6Prefix(true) + + // Configure emulated network on ATE port2. + netv42 := bgp4Peer2.V4Routes().Add().SetName("v4-bgpNet-dev2") + netv42.Addresses().Add().SetAddress(advertisedIPv42.address).SetPrefix(advertisedIPv42.prefix) + netv62 := bgp6Peer2.V6Routes().Add().SetName("v6-bgpNet-dev2") + netv62.Addresses().Add().SetAddress(advertisedIPv62.address).SetPrefix(advertisedIPv62.prefix) +} + +// verifyDUTBGPEstablished verifies on dut for BGP peer establishment. +func (td *testData) verifyDUTBGPEstablished(t *testing.T) { + sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(td.dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().NeighborAny().SessionState().State() + watch := gnmi.WatchAll(t, td.dut, sp, 2*time.Minute, func(val *ygnmi.Value[oc.E_Bgp_Neighbor_SessionState]) bool { + state, ok := val.Val() + if !ok || state != oc.Bgp_Neighbor_SessionState_ESTABLISHED { + return false + } + return true + }) + if val, ok := watch.Await(t); !ok { + t.Fatalf("BGP sessions not established: got %v", val) + } + t.Log("DUT BGP sessions established") +} + +// VerifyOTGBGPEstablished verifies on OTG for BGP peer establishment. +func (td *testData) verifyOTGBGPEstablished(t *testing.T) { + sp := gnmi.OTG().BgpPeerAny().SessionState().State() + watch := gnmi.WatchAll(t, td.ate.OTG(), sp, 2*time.Minute, func(val *ygnmi.Value[otgtelemetry.E_BgpPeer_SessionState]) bool { + state, ok := val.Val() + if !ok || state != otgtelemetry.BgpPeer_SessionState_ESTABLISHED { + return false + } + return true + }) + if val, ok := watch.Await(t); !ok { + t.Fatalf("BGP sessions not established: got %v", val) + } + t.Log("OTG BGP sessions established") +} + +func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + p1 := dut.Port(t, "port1") + p2 := dut.Port(t, "port2") + b := &gnmi.SetBatch{} + gnmi.BatchReplace(b, gnmi.OC().Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), dut)) + gnmi.BatchReplace(b, gnmi.OC().Interface(p2.Name()).Config(), dutPort2.NewOCInterface(p2.Name(), dut)) + b.Set(t, dut) + + if deviations.ExplicitPortSpeed(dut) { + fptest.SetPortSpeed(t, p1) + fptest.SetPortSpeed(t, p2) + } + + fptest.ConfigureDefaultNetworkInstance(t, dut) + + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + fptest.AssignToNetworkInstance(t, dut, p1.Name(), deviations.DefaultNetworkInstance(dut), 0) + fptest.AssignToNetworkInstance(t, dut, p2.Name(), deviations.DefaultNetworkInstance(dut), 0) + } +} + +func configureOTG(t *testing.T, ate *ondatra.ATEDevice, top gosnappi.Config) []gosnappi.Device { + t.Helper() + p1 := ate.Port(t, "port1") + p2 := ate.Port(t, "port2") + + d1 := atePort1.AddToOTG(top, p1, &dutPort1) + d2 := atePort2.AddToOTG(top, p2, &dutPort2) + return []gosnappi.Device{d1, d2} +} diff --git a/feature/bgp/policybase/otg_tests/prefix_set_test/bgp_prefix_set_test.go b/feature/bgp/policybase/otg_tests/prefix_set_test/bgp_prefix_set_test.go new file mode 100644 index 00000000000..1b0d1c21391 --- /dev/null +++ b/feature/bgp/policybase/otg_tests/prefix_set_test/bgp_prefix_set_test.go @@ -0,0 +1,497 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bgp_prefix_set_test + +import ( + "testing" + "time" + + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ondatra/otg" + "github.com/openconfig/ygnmi/ygnmi" + "github.com/openconfig/ygot/ygot" +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +const ( + peerGrpName = "BGP-PEER-GROUP" + dutAS = 65501 + ateAS = 65502 + plenIPv4 = 30 + plenIPv6 = 126 + v4Prefixes = true + acceptPolicy = "PERMIT-ALL" + rejectPolicy = "REJECT-ALL" + bgpImportIPv4 = "IPv4-IMPORT" + bgpImportIPv6 = "IPv6-IMPORT" + bgpExportIPv4 = "IPv4-ExPORT" + bgpExportIPv6 = "IPv6-ExPORT" +) + +var ( + dutPort1 = attrs.Attributes{ + Desc: "DUT to ATE Port1", + IPv4: "192.0.2.1", + IPv6: "2001:db8::192:0:2:1", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + atePort1 = attrs.Attributes{ + Name: "atePort1", + IPv4: "192.0.2.2", + IPv6: "2001:db8::192:0:2:2", + MAC: "02:00:01:01:01:01", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + dutPort2 = attrs.Attributes{ + Desc: "DUT to ATE Port2", + IPv4: "192.0.2.5", + IPv6: "2001:db8::192:0:2:5", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + atePort2 = attrs.Attributes{ + Name: "atePort2", + IPv4: "192.0.2.6", + IPv6: "2001:db8::192:0:2:6", + MAC: "02:00:02:01:01:01", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + + ebgp1NbrV4 = &bgpNeighbor{ + nbrAddr: atePort1.IPv4, + isV4: true, + afiSafi: oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, + as: ateAS} + ebgp1NbrV6 = &bgpNeighbor{ + nbrAddr: atePort1.IPv6, + isV4: false, + afiSafi: oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST, + as: ateAS} + ebgp2NbrV4 = &bgpNeighbor{ + nbrAddr: atePort2.IPv4, + isV4: true, + afiSafi: oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, + as: ateAS} + ebgp2NbrV6 = &bgpNeighbor{ + nbrAddr: atePort2.IPv6, + isV4: false, + afiSafi: oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST, + as: ateAS} + ebgpNbrs = []*bgpNeighbor{ebgp1NbrV4, ebgp1NbrV6, ebgp2NbrV4, ebgp2NbrV6} + + route1 = &route{prefix: "10.23.15.1", maskLen: 32, isV4: true} + route2 = &route{prefix: "10.23.15.2", maskLen: 16, isV4: true} + route3 = &route{prefix: "10.23.15.60", maskLen: 26, isV4: true} + route4 = &route{prefix: "10.23.15.70", maskLen: 26, isV4: true} + route5 = &route{prefix: "20.23.15.2", maskLen: 16, isV4: true} + + route6 = &route{prefix: "2001:4860:f804::1", maskLen: 48, isV4: false} + route7 = &route{prefix: "2001:4860:f804::2", maskLen: 128, isV4: false} + route8 = &route{prefix: "2001:4860:f804:1111::1", maskLen: 64, isV4: false} + route9 = &route{prefix: "2001:4860:f804::10", maskLen: 70, isV4: false} + route10 = &route{prefix: "2001:5555:f804::1", maskLen: 48, isV4: false} + + routes = []*route{route1, route2, route3, route4, route5, route6, route7, route8, route9, route10} + + prefixSet1V4 = &prefixSetPolicy{ + name: "IPv4-prefix-set-1", + ipPrefix: "10.23.15.0/26", + maskLenRange: "exact", + statement: "10", + actionAccept: false, + isV4: true} + prefixSet2V4 = &prefixSetPolicy{ + name: "IPv4-prefix-set-2", + ipPrefix: "10.23.0.0/16", + maskLenRange: "16..32", + statement: "20", + actionAccept: true, + isV4: true} + prefixSet1V6 = &prefixSetPolicy{ + name: "IPv6-prefix-set-1", + ipPrefix: "2001:4860:f804::/48", + maskLenRange: "exact", + statement: "10", + actionAccept: true, + isV4: false} + prefixSet2V6 = &prefixSetPolicy{ + name: "IPv6-prefix-set-2", + ipPrefix: "::/0", + maskLenRange: "65..128", + statement: "20", + actionAccept: false, + isV4: false} +) + +type route struct { + prefix string + maskLen uint32 + isV4 bool +} + +type prefixSetPolicy struct { + name string + ipPrefix string + maskLenRange string + statement string + actionAccept bool + isV4 bool +} + +type bgpNeighbor struct { + as uint32 + nbrAddr string + isV4 bool + afiSafi oc.E_BgpTypes_AFI_SAFI_TYPE +} + +func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + dc := gnmi.OC() + i1 := dutPort1.NewOCInterface(dut.Port(t, "port1").Name(), dut) + gnmi.Replace(t, dut, dc.Interface(i1.GetName()).Config(), i1) + + i2 := dutPort2.NewOCInterface(dut.Port(t, "port2").Name(), dut) + gnmi.Replace(t, dut, dc.Interface(i2.GetName()).Config(), i2) + fptest.ConfigureDefaultNetworkInstance(t, dut) + + if deviations.ExplicitPortSpeed(dut) { + fptest.SetPortSpeed(t, dut.Port(t, "port1")) + fptest.SetPortSpeed(t, dut.Port(t, "port2")) + } + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + fptest.AssignToNetworkInstance(t, dut, dut.Port(t, "port1").Name(), deviations.DefaultNetworkInstance(dut), 0) + fptest.AssignToNetworkInstance(t, dut, dut.Port(t, "port2").Name(), deviations.DefaultNetworkInstance(dut), 0) + } +} + +func configurePrefixSet(t *testing.T, dut *ondatra.DUTDevice, prefixSet []*prefixSetPolicy) { + d := &oc.Root{} + rp := d.GetOrCreateRoutingPolicy() + for _, ps := range prefixSet { + pset := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet(ps.name) + pset.GetOrCreatePrefix(ps.ipPrefix, ps.maskLenRange) + if !deviations.SkipPrefixSetMode(dut) { + if ps.isV4 { + pset.SetMode(oc.PrefixSet_Mode_IPV4) + } else { + pset.SetMode(oc.PrefixSet_Mode_IPV6) + } + } + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(ps.name).Config(), pset) + } +} + +func applyPrefixSetPolicy(t *testing.T, dut *ondatra.DUTDevice, prefixSet []*prefixSetPolicy, policyName string, bgpNbr bgpNeighbor, importPolicy bool) { + d := &oc.Root{} + rp := d.GetOrCreateRoutingPolicy() + + // Associate prefix-set with routing-policy + pdef := rp.GetOrCreatePolicyDefinition(policyName) + for _, pSet := range prefixSet { + stmt, err := pdef.AppendNewStatement(pSet.statement) + if err != nil { + t.Fatal(err) + } + ps := stmt.GetOrCreateConditions().GetOrCreateMatchPrefixSet() + ps.SetPrefixSet(pSet.name) + if !deviations.SkipSetRpMatchSetOptions(dut) { + ps.SetMatchSetOptions(oc.E_RoutingPolicy_MatchSetOptionsRestrictedType(oc.RoutingPolicy_MatchSetOptionsType_ANY)) + } + stmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE + if !pSet.actionAccept { + stmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_REJECT_ROUTE + } + } + + batchConfig := &gnmi.SetBatch{} + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), rp) + + // Apply routing-policy with BGP neighbor + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + if importPolicy { + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(bgpNbr.nbrAddr).AfiSafi(bgpNbr.afiSafi).ApplyPolicy().ImportPolicy().Config(), []string{policyName}) + } else { + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(bgpNbr.nbrAddr).AfiSafi(bgpNbr.afiSafi).ApplyPolicy().ExportPolicy().Config(), []string{policyName}) + } + batchConfig.Set(t, dut) +} + +func bgpCreateNbr(localAs, peerAs uint32, dut *ondatra.DUTDevice) *oc.NetworkInstance_Protocol { + + // Configure BGP on DUT + dutOcRoot := &oc.Root{} + ni1 := dutOcRoot.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + niProto := ni1.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + bgp := niProto.GetOrCreateBgp() + + global := bgp.GetOrCreateGlobal() + global.RouterId = ygot.String(dutPort1.IPv4) + global.As = ygot.Uint32(localAs) + global.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) + global.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + + pg := bgp.GetOrCreatePeerGroup(peerGrpName) + pg.PeerAs = ygot.Uint32(ateAS) + pg.PeerGroupName = ygot.String(peerGrpName) + + for _, nbr := range ebgpNbrs { + bgpNbr := bgp.GetOrCreateNeighbor(nbr.nbrAddr) + bgpNbr.PeerGroup = ygot.String(peerGrpName) + bgpNbr.PeerAs = ygot.Uint32(nbr.as) + bgpNbr.Enabled = ygot.Bool(true) + + if nbr.isV4 == true { + af4 := bgpNbr.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + af4.Enabled = ygot.Bool(true) + af6 := bgpNbr.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + af6.Enabled = ygot.Bool(false) + } else { + af4 := bgpNbr.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + af4.Enabled = ygot.Bool(false) + af6 := bgpNbr.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + af6.Enabled = ygot.Bool(true) + } + } + return niProto +} + +func verifyBgpState(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + + t.Logf("Waiting for BGP neighbor to establish...") + for _, nbr := range ebgpNbrs { + nbrPath := bgpPath.Neighbor(nbr.nbrAddr) + var status *ygnmi.Value[oc.E_Bgp_Neighbor_SessionState] + status, ok := gnmi.Watch(t, dut, nbrPath.SessionState().State(), time.Minute, func(val *ygnmi.Value[oc.E_Bgp_Neighbor_SessionState]) bool { + state, ok := val.Val() + return ok && state == oc.Bgp_Neighbor_SessionState_ESTABLISHED + }).Await(t) + if !ok { + fptest.LogQuery(t, "BGP reported state", nbrPath.State(), gnmi.Get(t, dut, nbrPath.State())) + t.Fatal("No BGP neighbor formed") + } + state, _ := status.Val() + if want := oc.Bgp_Neighbor_SessionState_ESTABLISHED; state != want { + t.Errorf("BGP peer %s status got %d, want %d", nbr.nbrAddr, state, want) + } + } +} + +func configureOTG(t *testing.T, otg *otg.OTG) { + t.Helper() + config := gosnappi.NewConfig() + port1 := config.Ports().Add().SetName("port1") + port2 := config.Ports().Add().SetName("port2") + + // Port1 Configuration. Sets the ATE port + iDut1Dev := config.Devices().Add().SetName(atePort1.Name) + iDut1Eth := iDut1Dev.Ethernets().Add().SetName(atePort1.Name + ".Eth").SetMac(atePort1.MAC) + iDut1Eth.Connection().SetPortName(port1.Name()) + iDut1Ipv4 := iDut1Eth.Ipv4Addresses().Add().SetName(atePort1.Name + ".IPv4") + iDut1Ipv4.SetAddress(atePort1.IPv4).SetGateway(dutPort1.IPv4).SetPrefix(uint32(atePort1.IPv4Len)) + iDut1Ipv6 := iDut1Eth.Ipv6Addresses().Add().SetName(atePort1.Name + ".IPv6") + iDut1Ipv6.SetAddress(atePort1.IPv6).SetGateway(dutPort1.IPv6).SetPrefix(uint32(atePort1.IPv6Len)) + + // Port2 Configuration. + iDut2Dev := config.Devices().Add().SetName(atePort2.Name) + iDut2Eth := iDut2Dev.Ethernets().Add().SetName(atePort2.Name + ".Eth").SetMac(atePort2.MAC) + iDut2Eth.Connection().SetPortName(port2.Name()) + iDut2Ipv4 := iDut2Eth.Ipv4Addresses().Add().SetName(atePort2.Name + ".IPv4") + iDut2Ipv4.SetAddress(atePort2.IPv4).SetGateway(dutPort2.IPv4).SetPrefix(uint32(atePort2.IPv4Len)) + iDut2Ipv6 := iDut2Eth.Ipv6Addresses().Add().SetName(atePort2.Name + ".IPv6") + iDut2Ipv6.SetAddress(atePort2.IPv6).SetGateway(dutPort2.IPv6).SetPrefix(uint32(atePort2.IPv6Len)) + + // eBGP v4 seesion on Port1. + iDut1Bgp := iDut1Dev.Bgp().SetRouterId(iDut1Ipv4.Address()) + iDut1Bgp4Peer := iDut1Bgp.Ipv4Interfaces().Add().SetIpv4Name(iDut1Ipv4.Name()).Peers().Add().SetName(atePort1.Name + ".BGP4.peer") + iDut1Bgp4Peer.SetPeerAddress(iDut1Ipv4.Gateway()).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + iDut1Bgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true) + // eBGP v6 seesion on Port1. + iDut1Bgp6Peer := iDut1Bgp.Ipv6Interfaces().Add().SetIpv6Name(iDut1Ipv6.Name()).Peers().Add().SetName(atePort1.Name + ".BGP6.peer") + iDut1Bgp6Peer.SetPeerAddress(iDut1Ipv6.Gateway()).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + iDut1Bgp6Peer.LearnedInformationFilter().SetUnicastIpv6Prefix(true) + + // eBGP v4 seesion on Port2. + iDut2Bgp := iDut2Dev.Bgp().SetRouterId(iDut2Ipv4.Address()) + iDut2Bgp4Peer := iDut2Bgp.Ipv4Interfaces().Add().SetIpv4Name(iDut2Ipv4.Name()).Peers().Add().SetName(atePort2.Name + ".BGP4.peer") + iDut2Bgp4Peer.SetPeerAddress(iDut2Ipv4.Gateway()).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + iDut2Bgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true) + // eBGP v6 seesion on Port2. + iDut2Bgp6Peer := iDut2Bgp.Ipv6Interfaces().Add().SetIpv6Name(iDut2Ipv6.Name()).Peers().Add().SetName(atePort2.Name + ".BGP6.peer") + iDut2Bgp6Peer.SetPeerAddress(iDut2Ipv6.Gateway()).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + iDut2Bgp6Peer.LearnedInformationFilter().SetUnicastIpv6Prefix(true) + + // eBGP V4 routes from Port1. + bgpNeti1Bgp4PeerRoutes := iDut1Bgp4Peer.V4Routes().Add().SetName(atePort1.Name + ".BGP4.Route") + bgpNeti1Bgp4PeerRoutes.SetNextHopIpv4Address(iDut1Ipv4.Address()). + SetNextHopAddressType(gosnappi.BgpV4RouteRangeNextHopAddressType.IPV4). + SetNextHopMode(gosnappi.BgpV4RouteRangeNextHopMode.MANUAL) + + // eBGP V6 routes from Port1. + bgpNeti1Bgp6PeerRoutes := iDut1Bgp6Peer.V6Routes().Add().SetName(atePort1.Name + ".BGP6.Route") + bgpNeti1Bgp6PeerRoutes.SetNextHopIpv6Address(iDut1Ipv6.Address()). + SetNextHopAddressType(gosnappi.BgpV6RouteRangeNextHopAddressType.IPV6). + SetNextHopMode(gosnappi.BgpV6RouteRangeNextHopMode.MANUAL) + + for _, sendRoute := range routes { + if sendRoute.isV4 { + bgpNeti1Bgp4PeerRoutes.Addresses().Add(). + SetAddress(sendRoute.prefix).SetPrefix(sendRoute.maskLen) + } + if !sendRoute.isV4 { + bgpNeti1Bgp6PeerRoutes.Addresses().Add(). + SetAddress(sendRoute.prefix).SetPrefix(sendRoute.maskLen) + } + } + + otg.PushConfig(t, config) + otg.StartProtocols(t) +} + +func validatePrefixCount(t *testing.T, dut *ondatra.DUTDevice, nbr bgpNeighbor, wantInstalled, wantRx, wantSent uint32) { + t.Helper() + + statePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + t.Logf("Validating prefix count for peer %v", nbr.nbrAddr) + prefixPath := statePath.Neighbor(nbr.nbrAddr).AfiSafi(nbr.afiSafi).Prefixes() + + // Waiting for Installed count to get updated after session comes up or policy is applied + gotInstalled, ok := gnmi.Watch(t, dut, prefixPath.Installed().State(), 20*time.Second, func(val *ygnmi.Value[uint32]) bool { // increased wait time to 20s from 10s + gotInstalled, _ := val.Val() + t.Logf("Prefix that are installed %v and want %v", gotInstalled, wantInstalled) + return gotInstalled == wantInstalled + }).Await(t) + if !ok { + t.Errorf("Installed prefixes mismatch: got %v, want %v", gotInstalled, wantInstalled) + } + + if !deviations.MissingPrePolicyReceivedRoutes(dut) { + // Waiting for Received count to get updated after session comes up or policy is applied + gotRx, ok := gnmi.Watch(t, dut, prefixPath.ReceivedPrePolicy().State(), 10*time.Second, func(val *ygnmi.Value[uint32]) bool { + gotRx, _ := val.Val() + t.Logf("Prefix that are received %v and want %v", gotRx, wantRx) + return gotRx == wantRx + }).Await(t) + if !ok { + t.Errorf("Received prefixes mismatch: got %v, want %v", gotRx, wantRx) + } + } + + // Waiting for Sent count to get updated after session comes up or policy is applied + gotSent, ok := gnmi.Watch(t, dut, prefixPath.Sent().State(), 10*time.Second, func(val *ygnmi.Value[uint32]) bool { + t.Logf("Prefix that are sent %v", prefixPath.Sent().State()) + gotSent, _ := val.Val() + t.Logf("Prefix that are sent %v and want %v", gotSent, wantSent) + return gotSent == wantSent + }).Await(t) + if !ok { + t.Errorf("Sent prefixes mismatch: got %v, want %v", gotSent, wantSent) + } +} + +// testPrefixSet is to validate prefix-set policies +func testPrefixSet(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + importPolicy := true + + // Configuring all 4 reqruired prefix-sets + t.Run("Configure prefix-set", func(t *testing.T) { + configurePrefixSet(t, dut, []*prefixSetPolicy{prefixSet1V4, prefixSet2V4, prefixSet1V6, prefixSet2V6}) + }) + + // Associating prefix-set with the required routing-policy and applying to BGP neighbors on ATE-port-1 + t.Run("Validate acceptance based on prefix-set policy - import policy on neighbor", func(t *testing.T) { + applyPrefixSetPolicy(t, dut, []*prefixSetPolicy{prefixSet1V4, prefixSet2V4}, bgpImportIPv4, *ebgp1NbrV4, importPolicy) + applyPrefixSetPolicy(t, dut, []*prefixSetPolicy{prefixSet1V6, prefixSet2V6}, bgpImportIPv6, *ebgp1NbrV6, importPolicy) + if !deviations.DefaultImportExportPolicy(dut) { + t.Logf("Validate for neighbour %v", ebgp1NbrV4) + validatePrefixCount(t, dut, *ebgp1NbrV4, 3, 5, 0) + validatePrefixCount(t, dut, *ebgp1NbrV6, 1, 5, 0) + validatePrefixCount(t, dut, *ebgp2NbrV4, 0, 0, 3) + validatePrefixCount(t, dut, *ebgp2NbrV6, 0, 0, 1) + } else { + t.Logf("Validate for neighbour %v", ebgp1NbrV4) + validatePrefixCount(t, dut, *ebgp1NbrV4, 3, 5, 0) + // only route6 is expected to accepted based on prefix-set + validatePrefixCount(t, dut, *ebgp1NbrV6, 1, 5, 0) + validatePrefixCount(t, dut, *ebgp2NbrV4, 0, 0, 0) + validatePrefixCount(t, dut, *ebgp2NbrV6, 0, 0, 0) + } + }) + + // Associating prefix-set with the required routing-policy and applying to BGP neighbors on ATE-port-2 + t.Run("Validate advertise based on prefix-set policy - export policy on neighbor", func(t *testing.T) { + applyPrefixSetPolicy(t, dut, []*prefixSetPolicy{prefixSet2V4}, bgpExportIPv4, *ebgp2NbrV4, !importPolicy) + applyPrefixSetPolicy(t, dut, []*prefixSetPolicy{prefixSet1V6}, bgpExportIPv6, *ebgp2NbrV6, !importPolicy) + + // route1, route2, route4 expected to be advertised based on prefix-set + validatePrefixCount(t, dut, *ebgp2NbrV4, 0, 0, 3) + // only route6 is expected to be advertised based on prefix-set + validatePrefixCount(t, dut, *ebgp2NbrV6, 0, 0, 1) + }) +} + +// TestBGPPrefixSet is to test prefix-set at the BGP neighbor levels. +func TestBGPPrefixSet(t *testing.T) { + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + + // Bring up 4 eBGP neighbors between DUT and ATE + t.Run("Establish BGP sessions", func(t *testing.T) { + configureDUT(t, dut) + + dutConfPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + gnmi.Delete(t, dut, dutConfPath.Config()) + dutConf := bgpCreateNbr(dutAS, ateAS, dut) + gnmi.Replace(t, dut, dutConfPath.Config(), dutConf) + + otg := ate.OTG() + configureOTG(t, otg) + verifyBgpState(t, dut) + }) + + if !deviations.DefaultImportExportPolicy(dut) { + t.Run("Validate initial prefix count", func(t *testing.T) { + validatePrefixCount(t, dut, *ebgp1NbrV4, 5, 5, 0) + validatePrefixCount(t, dut, *ebgp1NbrV6, 5, 5, 0) + validatePrefixCount(t, dut, *ebgp2NbrV4, 0, 0, 5) + validatePrefixCount(t, dut, *ebgp2NbrV6, 0, 0, 5) + }) + } else { + t.Run("Validate initial prefix count", func(t *testing.T) { + validatePrefixCount(t, dut, *ebgp1NbrV4, 0, 1, 0) + validatePrefixCount(t, dut, *ebgp1NbrV6, 0, 5, 0) + validatePrefixCount(t, dut, *ebgp2NbrV4, 0, 0, 0) + validatePrefixCount(t, dut, *ebgp2NbrV6, 0, 0, 0) + }) + + } + + testPrefixSet(t, dut) +} diff --git a/feature/bgp/policybase/otg_tests/prefix_set_test/metadata.textproto b/feature/bgp/policybase/otg_tests/prefix_set_test/metadata.textproto index 10354b4d109..6b2bf873c36 100644 --- a/feature/bgp/policybase/otg_tests/prefix_set_test/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/prefix_set_test/metadata.textproto @@ -1,7 +1,36 @@ # proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto # proto-message: Metadata +uuid: "3a55a01a-2a2d-404e-b397-a840192d8d67" plan_id: "RT-1.33" description: "BGP Policy with prefix-set matching" testbed: TESTBED_DUT_ATE_2LINKS -tags: [TAGS_AGGREGATION, TAGS_TRANSIT, TAGS_DATACENTER_EDGE] +platform_exceptions: { + platform: { + vendor: NOKIA + } + deviations: { + explicit_port_speed: true + explicit_interface_in_default_vrf: true + interface_enabled: true + skip_set_rp_match_set_options: true + skip_prefix_set_mode: true + } +} +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + omit_l2_mtu: true + interface_enabled: true + default_network_instance: "default" + missing_value_for_defaults: true + skip_set_rp_match_set_options: true + default_import_export_policy: false + + } +} +tags: TAGS_AGGREGATION +tags: TAGS_TRANSIT +tags: TAGS_DATACENTER_EDGE diff --git a/feature/bgp/prefixlimit/feature.textproto b/feature/bgp/prefixlimit/feature.textproto index 23aec8e81c3..0ecbb09d46a 100644 --- a/feature/bgp/prefixlimit/feature.textproto +++ b/feature/bgp/prefixlimit/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "bgp_prefixlimit" diff --git a/feature/bgp/routereflector/feature.textproto b/feature/bgp/routereflector/feature.textproto index 7bdb66535ea..a1b04315f79 100644 --- a/feature/bgp/routereflector/feature.textproto +++ b/feature/bgp/routereflector/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "bgp_routereflector" diff --git a/feature/bgp/static_route_bgp_redistribution/README.md b/feature/bgp/static_route_bgp_redistribution/otg_tests/static_route_bgp_redistribution_test/README.md similarity index 100% rename from feature/bgp/static_route_bgp_redistribution/README.md rename to feature/bgp/static_route_bgp_redistribution/otg_tests/static_route_bgp_redistribution_test/README.md diff --git a/feature/bgp/static_route_bgp_redistribution/otg_tests/static_route_bgp_redistribution_test/metadata.textproto b/feature/bgp/static_route_bgp_redistribution/otg_tests/static_route_bgp_redistribution_test/metadata.textproto new file mode 100644 index 00000000000..b128bfe709d --- /dev/null +++ b/feature/bgp/static_route_bgp_redistribution/otg_tests/static_route_bgp_redistribution_test/metadata.textproto @@ -0,0 +1,24 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "eb7e7ab2-5f58-4039-b862-13ad55459074" +plan_id: "RT-1.27" +description: "Static route to BGP redistribution" +testbed: TESTBED_DUT_ATE_4LINKS +platform_exceptions: { + platform: { + vendor: NOKIA + } + deviations: { + explicit_port_speed: true + explicit_interface_in_default_vrf: true + aggregate_atomic_update: true + static_protocol_name: "static" + interface_enabled: true + skip_set_rp_match_set_options: true + skip_prefix_set_mode: true + table_connections_unsupported: true + use_vendor_native_tag_set_config: true + skip_bgp_send_community_type: true + } +} diff --git a/feature/bgp/static_route_bgp_redistribution/otg_tests/static_route_bgp_redistribution_test/static_route_bgp_redistribution_test.go b/feature/bgp/static_route_bgp_redistribution/otg_tests/static_route_bgp_redistribution_test/static_route_bgp_redistribution_test.go new file mode 100644 index 00000000000..846fd69d32d --- /dev/null +++ b/feature/bgp/static_route_bgp_redistribution/otg_tests/static_route_bgp_redistribution_test/static_route_bgp_redistribution_test.go @@ -0,0 +1,1967 @@ +// Copyright 2023 Nokia, Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This code is a Contribution to OpenConfig Feature Profiles project ("Work") +// made under the Google Software Grant and Corporate Contributor License +// Agreement ("CLA") and governed by the Apache License 2.0. No other rights +// or licenses in or to any of Nokia's intellectual property are granted for +// any other purpose. This code is provided on an "as is" basis without +// any warranties of any kind. +// +// SPDX-License-Identifier: Apache-2.0 + +package static_route_bgp_redistribution_test + +import ( + "context" + "encoding/json" + "fmt" + "reflect" + "testing" + "time" + + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/otgutils" + gpb "github.com/openconfig/gnmi/proto/gnmi" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + otgtelemetry "github.com/openconfig/ondatra/gnmi/otg" + "github.com/openconfig/ondatra/otg" + "github.com/openconfig/ygnmi/ygnmi" + "github.com/openconfig/ygot/ygot" +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +const ( + ipv4PrefixLen = 30 + ipv6PrefixLen = 126 + subInterfaceIndex = 0 + mtu = 1500 + peerGroupName = "PEER-GROUP" + dutAsn = 64512 + atePeer1Asn = 64511 + atePeer2Asn = 64512 + acceptRoute = true + metricPropagate = true + policyResultNext = true + isV4 = true + shouldBePresent = true + replace = true + redistributeStaticPolicyNameV4 = "route-policy-v4" + redistributeStaticPolicyNameV6 = "route-policy-v6" + policyStatementNameV4 = "statement-v4" + policyStatementNameV6 = "statement-v6" + trafficDuration = 30 * time.Second + tolerancePct = 2 + medZero = 0 + medNonZero = 1000 + medIPv4 = 104 + medIPv6 = 106 + localPreference = 100 +) + +var ( + dutPort1 = &attrs.Attributes{ + Name: "dutPort1", + MAC: "00:12:01:01:01:01", + IPv4: "192.168.1.1", + IPv6: "2001:db8::1", + IPv4Len: ipv4PrefixLen, + IPv6Len: ipv6PrefixLen, + MTU: mtu, + } + + dutPort2 = &attrs.Attributes{ + Name: "dutPort2", + MAC: "00:12:02:01:01:01", + IPv4: "192.168.1.5", + IPv6: "2001:db8::5", + IPv4Len: ipv4PrefixLen, + IPv6Len: ipv6PrefixLen, + MTU: mtu, + } + + dutPort3 = &attrs.Attributes{ + Name: "dutPort3", + MAC: "00:12:03:01:01:01", + IPv4: "192.168.1.9", + IPv6: "2001:db8::9", + IPv4Len: ipv4PrefixLen, + IPv6Len: ipv6PrefixLen, + MTU: mtu, + } + + atePort1 = &attrs.Attributes{ + Name: "atePort1", + MAC: "02:00:01:01:01:01", + IPv4: "192.168.1.2", + IPv6: "2001:db8::2", + IPv4Len: ipv4PrefixLen, + IPv6Len: ipv6PrefixLen, + MTU: mtu, + } + + atePort2 = &attrs.Attributes{ + Name: "atePort2", + MAC: "02:00:02:01:01:01", + IPv4: "192.168.1.6", + IPv6: "2001:db8::6", + IPv4Len: ipv4PrefixLen, + IPv6Len: ipv6PrefixLen, + MTU: mtu, + } + + atePort3 = &attrs.Attributes{ + Name: "atePort3", + MAC: "02:00:03:01:01:01", + IPv4: "192.168.1.10", + IPv6: "2001:db8::a", + IPv4Len: ipv4PrefixLen, + IPv6Len: ipv6PrefixLen, + MTU: mtu, + } + + dutPorts = map[string]*attrs.Attributes{ + "port1": dutPort1, + "port2": dutPort2, + "port3": dutPort3, + } +) + +func configureDUTPort( + t *testing.T, + dut *ondatra.DUTDevice, + port *ondatra.Port, + portAttrs *attrs.Attributes, +) { + t.Helper() + + gnmi.Replace( + t, + dut, + gnmi.OC().Interface(port.Name()).Config(), + portAttrs.NewOCInterface(port.Name(), dut), + ) + + if deviations.ExplicitPortSpeed(dut) { + fptest.SetPortSpeed(t, port) + } + + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + fptest.AssignToNetworkInstance(t, dut, port.Name(), deviations.DefaultNetworkInstance(dut), subInterfaceIndex) + } +} + +func configureDUTStatic( + t *testing.T, + dut *ondatra.DUTDevice, +) { + t.Helper() + + staticPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(dut)) + gnmi.Delete(t, dut, staticPath.Config()) + + dutOcRoot := &oc.Root{} + networkInstance := dutOcRoot.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + networkInstanceProtocolStatic := networkInstance.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, "static") + networkInstanceProtocolStatic.SetEnabled(true) + + ipv4StaticRoute := networkInstanceProtocolStatic.GetOrCreateStatic("192.168.10.0/24") + // TODO - we dont support, guessing table connection related? + if !deviations.UseVendorNativeTagSetConfig(dut) { + ipv4StaticRoute.SetSetTag(oc.UnionString("40")) + } else { + configureStaticRouteTagSet(t, dut) + attachTagSetToStaticRoute(t, dut, "192.168.10.0/24", "tag-static-v4") + } + + ipv4StaticRouteNextHop := ipv4StaticRoute.GetOrCreateNextHop("0") + ipv4StaticRouteNextHop.SetMetric(104) + ipv4StaticRouteNextHop.SetNextHop(oc.UnionString("192.168.1.6")) + + ipv6StaticRoute := networkInstanceProtocolStatic.GetOrCreateStatic("2024:db8:128:128::/64") + if !deviations.UseVendorNativeTagSetConfig(dut) { + ipv6StaticRoute.SetSetTag(oc.UnionString("60")) + } else { + attachTagSetToStaticRoute(t, dut, "2024:db8:128:128::/64", "tag-static-v6") + } + + ipv6StaticRouteNextHop := ipv6StaticRoute.GetOrCreateNextHop("0") + ipv6StaticRouteNextHop.SetMetric(106) + ipv6StaticRouteNextHop.SetNextHop(oc.UnionString("2001:DB8::6")) + + gnmi.Replace(t, dut, staticPath.Config(), networkInstanceProtocolStatic) +} + +func configureDUTRoutingPolicy( + t *testing.T, + dut *ondatra.DUTDevice, +) { + t.Helper() + + policyPath := gnmi.OC().RoutingPolicy().PolicyDefinition("import-dut-port2-connected-subnet") + + dutOcRoot := &oc.Root{} + connectedPolicy := dutOcRoot.GetOrCreateRoutingPolicy() + connectedPolicyDefinition := connectedPolicy.GetOrCreatePolicyDefinition("import-dut-port2-connected-subnet") + + // v4 + v4PrefixSet := connectedPolicy.GetOrCreateDefinedSets().GetOrCreatePrefixSet("fp-ipv4-prefix") + v4PrefixSet.GetOrCreatePrefix("192.168.1.4/30", "30..32") + + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet("fp-ipv4-prefix").Config(), v4PrefixSet) + + ipv4PrefixPolicyStatement, err := connectedPolicyDefinition.AppendNewStatement("fp-ipv4-prefix") + if err != nil { + t.Fatalf("failed creating new policy statement, err: %s", err) + } + + ipv4PrefixPolicyStatementAction := ipv4PrefixPolicyStatement.GetOrCreateActions() + ipv4PrefixPolicyStatementAction.SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + ipv4PrefixPolicyStatementConditionsPrefixes := ipv4PrefixPolicyStatement.GetOrCreateConditions().GetOrCreateMatchPrefixSet() + ipv4PrefixPolicyStatementConditionsPrefixes.SetPrefixSet("fp-ipv4-prefix") + + // v6 + v6PrefixSet := connectedPolicy.GetOrCreateDefinedSets().GetOrCreatePrefixSet("fp-ipv6-prefix") + v6PrefixSet.GetOrCreatePrefix("2001:db8::4/126", "126..128") + + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet("fp-ipv6-prefix").Config(), v6PrefixSet) + + ipv6PrefixPolicyStatement, err := connectedPolicyDefinition.AppendNewStatement("fp-ipv6-prefix") + if err != nil { + t.Fatalf("failed creating new policy statement, err: %s", err) + } + + ipv6PrefixPolicyStatementAction := ipv6PrefixPolicyStatement.GetOrCreateActions() + ipv6PrefixPolicyStatementAction.SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + ipv6PrefixPolicyStatementConditionsPrefixes := ipv6PrefixPolicyStatement.GetOrCreateConditions().GetOrCreateMatchPrefixSet() + ipv6PrefixPolicyStatementConditionsPrefixes.SetPrefixSet("fp-ipv6-prefix") + + gnmi.Replace(t, dut, policyPath.Config(), connectedPolicyDefinition) +} + +func configureDUTBGP( + t *testing.T, + dut *ondatra.DUTDevice, +) { + t.Helper() + + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + + dutOcRoot := &oc.Root{} + networkInstance := dutOcRoot.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + networkInstanceProtocolBgp := networkInstance.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + bgp := networkInstanceProtocolBgp.GetOrCreateBgp() + + bgpGlobal := bgp.GetOrCreateGlobal() + bgpGlobal.RouterId = ygot.String(dutPort1.IPv4) + bgpGlobal.As = ygot.Uint32(dutAsn) + + bgpGlobalIPv4AF := bgpGlobal.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + bgpGlobalIPv4AF.SetEnabled(true) + + bgpGlobalIPv6AF := bgpGlobal.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + bgpGlobalIPv6AF.SetEnabled(true) + + if !deviations.SkipBgpSendCommunityType(dut) { + bgpGlobalIPv6AF.SetSendCommunityType([]oc.E_Bgp_CommunityType{oc.Bgp_CommunityType_STANDARD}) + bgpGlobalIPv4AF.SetSendCommunityType([]oc.E_Bgp_CommunityType{oc.Bgp_CommunityType_STANDARD}) + } + + bgpPeerGroup := bgp.GetOrCreatePeerGroup(peerGroupName) + bgpPeerGroup.SetPeerAs(dutAsn) + + // dutPort1 -> atePort1 peer (ebgp session) + ateEBGPNeighborOne := bgp.GetOrCreateNeighbor(atePort1.IPv4) + ateEBGPNeighborOne.PeerGroup = ygot.String(peerGroupName) + ateEBGPNeighborOne.PeerAs = ygot.Uint32(atePeer1Asn) + ateEBGPNeighborOne.Enabled = ygot.Bool(true) + + ateEBGPNeighborIPv4AF := ateEBGPNeighborOne.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + ateEBGPNeighborIPv4AF.SetEnabled(true) + ateEBGPNeighborIPv4AFPolicy := ateEBGPNeighborIPv4AF.GetOrCreateApplyPolicy() + ateEBGPNeighborIPv4AFPolicy.SetImportPolicy([]string{"import-dut-port2-connected-subnet"}) + ateEBGPNeighborIPv4AFPolicy.SetExportPolicy([]string{"import-dut-port2-connected-subnet"}) + + ateEBGPNeighborTwo := bgp.GetOrCreateNeighbor(atePort1.IPv6) + ateEBGPNeighborTwo.PeerGroup = ygot.String(peerGroupName) + ateEBGPNeighborTwo.PeerAs = ygot.Uint32(atePeer1Asn) + ateEBGPNeighborTwo.Enabled = ygot.Bool(true) + ateEBGPNeighborIPv6AF := ateEBGPNeighborTwo.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + ateEBGPNeighborIPv6AF.SetEnabled(true) + ateEBGPNeighborIPv6AFPolicy := ateEBGPNeighborIPv6AF.GetOrCreateApplyPolicy() + ateEBGPNeighborIPv6AFPolicy.SetImportPolicy([]string{"import-dut-port2-connected-subnet"}) + ateEBGPNeighborIPv6AFPolicy.SetExportPolicy([]string{"import-dut-port2-connected-subnet"}) + + // dutPort3 -> atePort3 peer (ibgp session) + ateIBGPNeighborThree := bgp.GetOrCreateNeighbor(atePort3.IPv4) + ateIBGPNeighborThree.PeerGroup = ygot.String(peerGroupName) + ateIBGPNeighborThree.PeerAs = ygot.Uint32(atePeer2Asn) + ateIBGPNeighborThree.Enabled = ygot.Bool(true) + + ateIBGPNeighborThreeIPv4AF := ateIBGPNeighborThree.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + ateIBGPNeighborThreeIPv4AF.SetEnabled(true) + + ateIBGPNeighborThreeIPv6AF := ateIBGPNeighborThree.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + ateIBGPNeighborThreeIPv6AF.SetEnabled(true) + + ateIBGPNeighborFour := bgp.GetOrCreateNeighbor(atePort3.IPv6) + ateIBGPNeighborFour.PeerGroup = ygot.String(peerGroupName) + ateIBGPNeighborFour.PeerAs = ygot.Uint32(atePeer2Asn) + ateIBGPNeighborFour.Enabled = ygot.Bool(true) + + ateIBGPNeighborFourIPv4AF := ateIBGPNeighborFour.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + ateIBGPNeighborFourIPv4AF.SetEnabled(true) + + ateIBGPNeighborFourIPv6AF := ateIBGPNeighborFour.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + ateIBGPNeighborFourIPv6AF.SetEnabled(true) + + gnmi.Replace(t, dut, bgpPath.Config(), networkInstanceProtocolBgp) +} + +func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { + for portName, portAttrs := range dutPorts { + port := dut.Port(t, portName) + configureDUTPort(t, dut, port, portAttrs) + } + + configureDUTRoutingPolicy(t, dut) + configureDUTStatic(t, dut) + configureDUTBGP(t, dut) +} + +func awaitBGPEstablished(t *testing.T, dut *ondatra.DUTDevice, neighbors []string) { + for _, neighbor := range neighbors { + gnmi.Await(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)). + Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP"). + Bgp(). + Neighbor(neighbor). + SessionState().State(), time.Second*120, oc.Bgp_Neighbor_SessionState_ESTABLISHED) + } +} + +func configureOTG(t *testing.T) gosnappi.Config { + t.Helper() + + otgConfig := gosnappi.NewConfig() + + port1 := otgConfig.Ports().Add().SetName("port1") + port2 := otgConfig.Ports().Add().SetName("port2") + port3 := otgConfig.Ports().Add().SetName("port3") + + // Port3 Configuration. + iDut3Dev := otgConfig.Devices().Add().SetName(atePort3.Name) + iDut3Eth := iDut3Dev.Ethernets().Add().SetName(atePort3.Name + ".Eth").SetMac(atePort3.MAC) + iDut3Eth.Connection().SetPortName(port3.Name()) + iDut3Ipv4 := iDut3Eth.Ipv4Addresses().Add().SetName(atePort3.Name + ".IPv4") + iDut3Ipv4.SetAddress(atePort3.IPv4).SetGateway(dutPort3.IPv4).SetPrefix(uint32(atePort3.IPv4Len)) + iDut3Ipv6 := iDut3Eth.Ipv6Addresses().Add().SetName(atePort3.Name + ".IPv6") + iDut3Ipv6.SetAddress(atePort3.IPv6).SetGateway(dutPort3.IPv6).SetPrefix(uint32(atePort3.IPv6Len)) + + // Port1 Configuration. + iDut1Dev := otgConfig.Devices().Add().SetName(atePort1.Name) + iDut1Eth := iDut1Dev.Ethernets().Add().SetName(atePort1.Name + ".Eth").SetMac(atePort1.MAC) + iDut1Eth.Connection().SetPortName(port1.Name()) + iDut1Ipv4 := iDut1Eth.Ipv4Addresses().Add().SetName(atePort1.Name + ".IPv4") + iDut1Ipv4.SetAddress(atePort1.IPv4).SetGateway(dutPort1.IPv4).SetPrefix(uint32(atePort1.IPv4Len)) + iDut1Ipv6 := iDut1Eth.Ipv6Addresses().Add().SetName(atePort1.Name + ".IPv6") + iDut1Ipv6.SetAddress(atePort1.IPv6).SetGateway(dutPort1.IPv6).SetPrefix(uint32(atePort1.IPv6Len)) + + // Port2 Configuration. + iDut2Dev := otgConfig.Devices().Add().SetName(atePort2.Name) + iDut2Eth := iDut2Dev.Ethernets().Add().SetName(atePort2.Name + ".Eth").SetMac(atePort2.MAC) + iDut2Eth.Connection().SetPortName(port2.Name()) + iDut2Ipv4 := iDut2Eth.Ipv4Addresses().Add().SetName(atePort2.Name + ".IPv4") + iDut2Ipv4.SetAddress(atePort2.IPv4).SetGateway(dutPort2.IPv4).SetPrefix(uint32(atePort2.IPv4Len)) + iDut2Ipv6 := iDut2Eth.Ipv6Addresses().Add().SetName(atePort2.Name + ".IPv6") + iDut2Ipv6.SetAddress(atePort2.IPv6).SetGateway(dutPort2.IPv6).SetPrefix(uint32(atePort2.IPv6Len)) + + // eBGP v4 session on Port1. + iDut1Bgp := iDut1Dev.Bgp().SetRouterId(iDut1Ipv4.Address()) + iDut1Bgp4Peer := iDut1Bgp.Ipv4Interfaces().Add().SetIpv4Name(iDut1Ipv4.Name()).Peers().Add().SetName(atePort1.Name + ".BGP4.peer") + iDut1Bgp4Peer.SetPeerAddress(iDut1Ipv4.Gateway()).SetAsNumber(atePeer1Asn).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + iDut1Bgp4Peer.Capability().SetIpv4UnicastAddPath(true) + iDut1Bgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true) + // eBGP v6 session on Port1. + iDut1Bgp6Peer := iDut1Bgp.Ipv6Interfaces().Add().SetIpv6Name(iDut1Ipv6.Name()).Peers().Add().SetName(atePort1.Name + ".BGP6.peer") + iDut1Bgp6Peer.SetPeerAddress(iDut1Ipv6.Gateway()).SetAsNumber(atePeer1Asn).SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + iDut1Bgp6Peer.Capability().SetIpv6UnicastAddPath(true) + iDut1Bgp6Peer.LearnedInformationFilter().SetUnicastIpv6Prefix(true) + + // iBGP v4 session on Port3. + iDut3Bgp := iDut3Dev.Bgp().SetRouterId(iDut3Ipv4.Address()) + iDut3Bgp4Peer := iDut3Bgp.Ipv4Interfaces().Add().SetIpv4Name(iDut3Ipv4.Name()).Peers().Add().SetName(atePort3.Name + ".BGP4.peer") + iDut3Bgp4Peer.SetPeerAddress(iDut3Ipv4.Gateway()).SetAsNumber(atePeer2Asn).SetAsType(gosnappi.BgpV4PeerAsType.IBGP) + iDut3Bgp4Peer.Capability().SetIpv4UnicastAddPath(true) + iDut3Bgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true) + // iBGP v6 session on Port3. + iDut3Bgp6Peer := iDut3Bgp.Ipv6Interfaces().Add().SetIpv6Name(iDut3Ipv6.Name()).Peers().Add().SetName(atePort3.Name + ".BGP6.peer") + iDut3Bgp6Peer.SetPeerAddress(iDut3Ipv6.Gateway()).SetAsNumber(atePeer2Asn).SetAsType(gosnappi.BgpV6PeerAsType.IBGP) + iDut3Bgp6Peer.Capability().SetIpv6UnicastAddPath(true) + iDut3Bgp6Peer.LearnedInformationFilter().SetUnicastIpv6Prefix(true) + + return otgConfig +} + +// Configure OTG traffic-flow +func configureTrafficFlow(t *testing.T, otgConfig gosnappi.Config, isV4 bool, name, flowSrcEndPoint, flowDstEndPoint, srcMac, srcIp, dstIp string) gosnappi.Config { + t.Helper() + + // ATE Traffic Configuration. + t.Logf("TestBGP:start ate Traffic config: %v", name) + + otgConfig.Flows().Clear() + + flow := otgConfig.Flows().Add().SetName(name) + flow.Metrics().SetEnable(true) + flow.TxRx().Device(). + SetTxNames([]string{flowSrcEndPoint}). + SetRxNames([]string{flowDstEndPoint}) + flow.Size().SetFixed(1500) + flow.Duration().FixedPackets().SetPackets(1000) + e := flow.Packet().Add().Ethernet() + e.Src().SetValue(srcMac) + if isV4 { + v4 := flow.Packet().Add().Ipv4() + v4.Src().SetValue(srcIp) + v4.Dst().SetValue(dstIp) + } else { + v6 := flow.Packet().Add().Ipv6() + v6.Src().SetValue(srcIp) + v6.Dst().SetValue(dstIp) + } + + return otgConfig +} + +// Sending traffic over configured flow for fixed duration +func sendTraffic(t *testing.T, otg *otg.OTG) { + t.Logf("Starting traffic") + otg.StartTraffic(t) + time.Sleep(trafficDuration) + t.Logf("Stop traffic") + otg.StopTraffic(t) +} + +// Validate traffic flow +func verifyTraffic(t *testing.T, ate *ondatra.ATEDevice, conf gosnappi.Config) { + otg := ate.OTG() + otgutils.LogFlowMetrics(t, otg, conf) + for _, flow := range conf.Flows().Items() { + recvMetric := gnmi.Get(t, otg, gnmi.OTG().Flow(flow.Name()).State()) + txPackets := float32(recvMetric.GetCounters().GetOutPkts()) + rxPackets := float32(recvMetric.GetCounters().GetInPkts()) + if txPackets == 0 { + t.Fatalf("TxPkts = 0, want > 0") + } + lostPackets := txPackets - rxPackets + lossPct := lostPackets * 100 / txPackets + if lossPct > tolerancePct { + t.Errorf("Traffic Loss Pct for Flow %s: got %v, want max %v pct failure", flow.Name(), lossPct, tolerancePct) + } else { + t.Logf("Traffic Test Passed! for flow %s", flow.Name()) + } + } +} + +// Configure table-connection with source as static-route and destination as bgp +func configureTableConnection(t *testing.T, dut *ondatra.DUTDevice, isV4, mPropagation bool, importPolicy string, defaultImport oc.E_RoutingPolicy_DefaultPolicyType) { + + t.Helper() + + niPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)) + dutOcRoot := &oc.Root{} + networkInstance := dutOcRoot.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + addressFamily := oc.Types_ADDRESS_FAMILY_IPV4 + if !isV4 { + addressFamily = oc.Types_ADDRESS_FAMILY_IPV6 + } + + tc := networkInstance.GetOrCreateTableConnection( + oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, + oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, + addressFamily, + ) + + tc.SetDefaultImportPolicy(defaultImport) + if importPolicy != "" { + tc.SetImportPolicy([]string{importPolicy}) + } + tc.SetDisableMetricPropagation(!mPropagation) + gnmi.Update(t, dut, niPath.Config(), networkInstance) +} + +// Populate routing-policy to redistribute static-route +func redistributeStaticRoute(t *testing.T, isV4 bool, mPropagation, policyResultNext bool, routingPolicy *oc.RoutingPolicy) *oc.RoutingPolicy { + + redistributeStaticPolicyName := redistributeStaticPolicyNameV4 + policyStatementName := "redistribute-static" + if !isV4 { + redistributeStaticPolicyName = redistributeStaticPolicyNameV6 + } + + apolicy := routingPolicy.GetOrCreatePolicyDefinition(redistributeStaticPolicyName) + astmt, err := apolicy.AppendNewStatement(policyStatementName) + if err != nil { + t.Fatalf("failed creating new policy statement, err: %s", err) + } + astmt.GetOrCreateConditions().SetInstallProtocolEq(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC) + astmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE + if policyResultNext { + astmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_NEXT_STATEMENT + } + astmt.GetOrCreateActions().GetOrCreateBgpActions().SetSetRouteOrigin(oc.E_BgpPolicy_BgpOriginAttrType(oc.BgpPolicy_BgpOriginAttrType_IGP)) + astmt.GetOrCreateActions().GetOrCreateBgpActions().SetSetMed(oc.UnionUint32(medZero)) + if mPropagation { + astmt.GetOrCreateActions().GetOrCreateBgpActions().SetSetMed(oc.E_BgpActions_SetMed(oc.BgpActions_SetMed_IGP)) + } + + return routingPolicy +} + +// Configure routing-policy to redistribute static-route +func configureStaticRedistributionPolicy(t *testing.T, dut *ondatra.DUTDevice, isV4, acceptRoute, mPropagation bool) { + + t.Helper() + + dutOcRoot := &oc.Root{} + rp := dutOcRoot.GetOrCreateRoutingPolicy() + rp = redistributeStaticRoute(t, isV4, mPropagation, !policyResultNext, rp) + + redistributeStaticPolicyName := redistributeStaticPolicyNameV4 + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort1.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy().ExportPolicy() + if !isV4 { + redistributeStaticPolicyName = redistributeStaticPolicyNameV6 + bgpPath = gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort1.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy().ExportPolicy() + } + + astmt := rp.GetPolicyDefinition(redistributeStaticPolicyName).GetStatement("redistribute-static") + astmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_REJECT_ROUTE + if acceptRoute { + astmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE + } + + rpConfPath := gnmi.OC().RoutingPolicy() + gnmi.Replace(t, dut, rpConfPath.PolicyDefinition(redistributeStaticPolicyName).Config(), rp.GetOrCreatePolicyDefinition(redistributeStaticPolicyName)) + gnmi.Replace(t, dut, bgpPath.Config(), []string{redistributeStaticPolicyName}) +} + +// Validate configurations for table-connections and routing-policy +func validateRedistributeStatic(t *testing.T, dut *ondatra.DUTDevice, acceptRoute, isV4, mPropagation bool) { + + redistributeStaticPolicyName := redistributeStaticPolicyNameV4 + policyStatementName := "redistribute-static" + af := oc.Types_ADDRESS_FAMILY_IPV4 + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort1.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy().ExportPolicy().State() + if !isV4 { + redistributeStaticPolicyName = redistributeStaticPolicyNameV6 + af = oc.Types_ADDRESS_FAMILY_IPV6 + bgpPath = gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort1.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy().ExportPolicy().State() + } + + if !deviations.TableConnectionsUnsupported(dut) { + tcState := gnmi.Get(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).TableConnection( + oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, + oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, + af).State()) + + if tcState.GetSrcProtocol() != oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC { + t.Fatal("source protocol not static for table connection but should be") + } + + if tcState.GetDstProtocol() != oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP { + t.Fatal("destination protocol not bgp for table connection but should be") + } + + if tcState.GetAddressFamily() != af { + t.Fatal("address family not as expected or table connection but should be") + } + + if !mPropagation { + if tcState.GetDisableMetricPropagation() { + t.Fatal("Metric propagation disabled for table connection, expected enabled") + } + } else { + if !tcState.GetDisableMetricPropagation() { + t.Fatal("Metric propagation is enabled for table connection, expected disabled") + } + } + + importPolicies := tcState.GetImportPolicy() + found := false + for _, iPolicy := range importPolicies { + if iPolicy == redistributeStaticPolicyName { + found = true + } + } + + if !found { + t.Fatal("Expected import policy is not configured") + } + + if acceptRoute { + if tcState.GetDefaultImportPolicy() != oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE { + t.Fatal("default import policy is not accept route but it should be") + } + } else { + if tcState.GetDefaultImportPolicy() != oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE { + t.Fatal("default import policy is not reject route but it should be") + } + } + } else { + var foundPDef oc.RoutingPolicy_PolicyDefinition + policyDef := gnmi.GetAll(t, dut, gnmi.OC().RoutingPolicy().PolicyDefinitionAny().State()) + for _, pDef := range policyDef { + if pDef.GetName() == redistributeStaticPolicyName { + foundPDef = *pDef + } + } + + if foundPDef.GetName() != redistributeStaticPolicyName { + t.Fatal("Expected import policy is not configured") + } + + if foundPDef.GetStatement(policyStatementName).GetOrCreateConditions().GetInstallProtocolEq() != oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC { + t.Fatal("Source protocol not static for redistribution policy.Expected static protocol") + } + + if mPropagation { + if foundPDef.GetStatement(policyStatementName).GetActions().GetBgpActions().GetSetMed() != oc.E_BgpActions_SetMed(oc.BgpActions_SetMed_IGP) { + t.Fatal("Expected metric propagation is not configured") + } + } + + found := false + bgpPolicy := gnmi.Get(t, dut, bgpPath) + for _, exPol := range bgpPolicy { + if exPol == redistributeStaticPolicyName { + found = true + t.Logf("bgp associated with routing-policy: %v", exPol) + } + } + if !found { + t.Fatal("BGP not associated with expected policy") + } + } +} + +// Validate prefix-set routing-policy configurations +func validatePrefixSetRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, isV4 bool) { + + redistributeStaticPolicyName := redistributeStaticPolicyNameV4 + policyStatementName := policyStatementNameV4 + prefixSetName := "prefix-set-v4" + prefixSetMode := oc.PrefixSet_Mode_IPV4 + prefixAddress := "192.168.10.0/24" + prefixMaskLen := "exact" + if !isV4 { + redistributeStaticPolicyName = redistributeStaticPolicyNameV6 + policyStatementName = policyStatementNameV6 + prefixSetName = "prefix-set-v6" + prefixSetMode = oc.PrefixSet_Mode_IPV6 + prefixAddress = "2024:db8:128:128::/64" + } + + var foundPDef oc.RoutingPolicy_PolicyDefinition + policyDef := gnmi.GetAll(t, dut, gnmi.OC().RoutingPolicy().PolicyDefinitionAny().State()) + for _, pDef := range policyDef { + if pDef.GetName() == redistributeStaticPolicyName { + foundPDef = *pDef + } + } + + if foundPDef.GetName() != redistributeStaticPolicyName { + t.Fatal("Expected import policy is not configured") + } + + if foundPDef.GetStatement(policyStatementName).GetActions().GetPolicyResult() != oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE { + t.Fatalf("Routing-policy result unexpectd for statement %v. It is not set to ACCEPT_ROUTE.", policyStatementName) + } + + if foundPDef.GetStatement(policyStatementName).GetConditions().GetMatchPrefixSet().GetPrefixSet() != prefixSetName { + t.Fatal("Routing-policy not associated with expected prefix-set") + } + + if !deviations.SkipSetRpMatchSetOptions(dut) { + if foundPDef.GetStatement(policyStatementName).GetConditions().GetMatchPrefixSet().GetMatchSetOptions() != oc.RoutingPolicy_MatchSetOptionsRestrictedType_ANY { + t.Fatal("Routing-policy prefix-set match-set-option not set to ANY") + } + } + + var foundPSet oc.RoutingPolicy_DefinedSets_PrefixSet + prefixSet := gnmi.GetAll(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSetAny().State()) + for _, pSet := range prefixSet { + if pSet.GetName() == prefixSetName { + foundPSet = *pSet + } + } + + if foundPSet.GetName() != prefixSetName { + t.Fatal("Expected prefix-set is not configured") + } + + if !deviations.SkipPrefixSetMode(dut) { + if foundPSet.GetMode() != prefixSetMode { + t.Fatal("Expected prefix-set mode is not configured") + } + } + + if foundPSet.GetPrefix(prefixAddress, prefixMaskLen).GetIpPrefix() != prefixAddress { + t.Fatal("Expected prefix not configured in prefix-set") + } +} + +// 1.27.1 setup function +func redistributeIPv4Static(t *testing.T, dut *ondatra.DUTDevice) { + if deviations.TableConnectionsUnsupported(dut) { + configureStaticRedistributionPolicy(t, dut, isV4, acceptRoute, !metricPropagate) + } else { + configureTableConnection(t, dut, isV4, !metricPropagate, "", oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE) + } +} + +// 1.27.1 validation function +func validateRedistributeIPv4Static(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { + validateRedistributeStatic(t, dut, acceptRoute, isV4, !metricPropagate) + validateLearnedIPv4Prefix(t, ate, atePort1.Name+".BGP4.peer", "192.168.10.0", medZero, shouldBePresent) +} + +// 1.27.2 setup function +func redistributeIPv4StaticWithMetricPropagation(t *testing.T, dut *ondatra.DUTDevice) { + if deviations.TableConnectionsUnsupported(dut) { + configureStaticRedistributionPolicy(t, dut, isV4, acceptRoute, metricPropagate) + } else { + configureTableConnection(t, dut, isV4, metricPropagate, "", oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE) + } +} + +// 1.27.2 validation function +func validateRedistributeIPv4StaticWithMetricPropagation(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { + validateRedistributeStatic(t, dut, acceptRoute, isV4, metricPropagate) + validateLearnedIPv4Prefix(t, ate, atePort1.Name+".BGP4.peer", "192.168.10.0", medIPv4, shouldBePresent) +} + +// 1.27.3 setup function +func redistributeIPv6Static(t *testing.T, dut *ondatra.DUTDevice) { + if deviations.TableConnectionsUnsupported(dut) { + configureStaticRedistributionPolicy(t, dut, !isV4, acceptRoute, !metricPropagate) + } else { + configureTableConnection(t, dut, !isV4, !metricPropagate, "", oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE) + } +} + +// 1.27.3 validation function +func validateRedistributeIPv6Static(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { + validateRedistributeStatic(t, dut, acceptRoute, !isV4, !metricPropagate) + validateLearnedIPv6Prefix(t, ate, atePort1.Name+".BGP6.peer", "2024:db8:128:128::", medZero, shouldBePresent) +} + +// 1.27.4 setup function +func redistributeIPv6StaticWithMetricPropagation(t *testing.T, dut *ondatra.DUTDevice) { + if deviations.TableConnectionsUnsupported(dut) { + configureStaticRedistributionPolicy(t, dut, !isV4, acceptRoute, metricPropagate) + } else { + configureTableConnection(t, dut, !isV4, metricPropagate, "", oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE) + } +} + +// 1.27.4 validation function +func validateRedistributeIPv6StaticWithMetricPropagation(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { + validateRedistributeStatic(t, dut, acceptRoute, !isV4, metricPropagate) + validateLearnedIPv6Prefix(t, ate, atePort1.Name+".BGP6.peer", "2024:db8:128:128::", medIPv6, shouldBePresent) +} + +// 1.27.5 setup function +func redistributeIPv4IPv6StaticDefaultRejectPolicy(t *testing.T, dut *ondatra.DUTDevice) { + if deviations.TableConnectionsUnsupported(dut) { + configureStaticRedistributionPolicy(t, dut, isV4, !acceptRoute, !metricPropagate) + configureStaticRedistributionPolicy(t, dut, !isV4, !acceptRoute, !metricPropagate) + } else { + configureTableConnection(t, dut, isV4, !metricPropagate, "", oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + configureTableConnection(t, dut, !isV4, !metricPropagate, "", oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } +} + +// 1.27.5 validation function +func validateRedistributeIPv4IPv6DefaultRejectPolicy(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { + validateRedistributeStatic(t, dut, !acceptRoute, isV4, !metricPropagate) + validateRedistributeStatic(t, dut, !acceptRoute, !isV4, !metricPropagate) + validateLearnedIPv4Prefix(t, ate, atePort1.Name+".BGP4.peer", "192.168.10.0", medZero, !shouldBePresent) + validateLearnedIPv6Prefix(t, ate, atePort1.Name+".BGP6.peer", "2024:db8:128:128::", medZero, !shouldBePresent) +} + +// 1.27.6 setup function +func redistributeIPv4PrefixRoutePolicy(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { + policyPath := gnmi.OC().RoutingPolicy().PolicyDefinition(redistributeStaticPolicyNameV4) + + otgConfig := configureOTG(t) + otgConfig = configureTrafficFlow(t, otgConfig, isV4, "StaticRoutesV4Flow", atePort1.Name+".IPv4", atePort2.Name+".IPv4", atePort1.MAC, atePort1.IPv4, "192.168.10.0") + ate.OTG().PushConfig(t, otgConfig) + ate.OTG().StartProtocols(t) + + dutOcRoot := &oc.Root{} + redistributePolicy := dutOcRoot.GetOrCreateRoutingPolicy() + if deviations.TableConnectionsUnsupported(dut) { + redistributePolicy = redistributeStaticRoute(t, isV4, metricPropagate, policyResultNext, redistributePolicy) + } + + redistributePolicyDefinition := redistributePolicy.GetOrCreatePolicyDefinition(redistributeStaticPolicyNameV4) + + v4PrefixSet := redistributePolicy.GetOrCreateDefinedSets().GetOrCreatePrefixSet("prefix-set-v4") + v4PrefixSet.GetOrCreatePrefix("192.168.10.0/24", "exact") + if !deviations.SkipPrefixSetMode(dut) { + v4PrefixSet.SetMode(oc.PrefixSet_Mode_IPV4) + } + + v4PrefixSet.GetOrCreatePrefix("192.168.20.0/24", "exact") + if !deviations.SkipPrefixSetMode(dut) { + v4PrefixSet.SetMode(oc.PrefixSet_Mode_IPV4) + } + + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet("prefix-set-v4").Config(), v4PrefixSet) + + ipv4PrefixPolicyStatement, err := redistributePolicyDefinition.AppendNewStatement(policyStatementNameV4) + if err != nil { + t.Fatalf("failed creating new policy statement, err: %s", err) + } + + ipv4PrefixPolicyStatementAction := ipv4PrefixPolicyStatement.GetOrCreateActions() + ipv4PrefixPolicyStatementAction.SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + + ipv4PrefixPolicyStatementConditionsPrefixes := ipv4PrefixPolicyStatement.GetOrCreateConditions().GetOrCreateMatchPrefixSet() + ipv4PrefixPolicyStatementConditionsPrefixes.SetPrefixSet("prefix-set-v4") + if !deviations.SkipSetRpMatchSetOptions(dut) { + ipv4PrefixPolicyStatementConditionsPrefixes.SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsRestrictedType_ANY) + } + + gnmi.Replace(t, dut, policyPath.Config(), redistributePolicyDefinition) + + if deviations.TableConnectionsUnsupported(dut) { + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort1.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy().ExportPolicy() + gnmi.Replace(t, dut, bgpPath.Config(), []string{redistributeStaticPolicyNameV4}) + } else { + configureTableConnection(t, dut, isV4, metricPropagate, redistributeStaticPolicyNameV4, oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE) + } + + sendTraffic(t, ate.OTG()) + verifyTraffic(t, ate, otgConfig) +} + +// 1.27.6 validation function +func validateRedistributeIPv4PrefixRoutePolicy(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { + validateRedistributeStatic(t, dut, acceptRoute, isV4, metricPropagate) + validatePrefixSetRoutingPolicy(t, dut, isV4) + validateLearnedIPv4Prefix(t, ate, atePort1.Name+".BGP4.peer", "192.168.10.0", medIPv4, shouldBePresent) +} + +// 1.27.7 and 1.27.16 setup function +func redistributeStaticRoutePolicyWithASN(t *testing.T, dut *ondatra.DUTDevice, isV4 bool) { + + redistributeStaticPolicyName := redistributeStaticPolicyNameV4 + policyStatementName := policyStatementNameV4 + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort1.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy().ExportPolicy() + + if !isV4 { + redistributeStaticPolicyName = redistributeStaticPolicyNameV6 + policyStatementName = policyStatementNameV6 + bgpPath = gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort1.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy().ExportPolicy() + } + + policyPath := gnmi.OC().RoutingPolicy().PolicyDefinition(redistributeStaticPolicyName) + + dutOcRoot := &oc.Root{} + redistributePolicy := dutOcRoot.GetOrCreateRoutingPolicy() + + if deviations.TableConnectionsUnsupported(dut) { + redistributePolicy = redistributeStaticRoute(t, isV4, metricPropagate, policyResultNext, redistributePolicy) + } + + redistributePolicyDefinition := redistributePolicy.GetOrCreatePolicyDefinition(redistributeStaticPolicyName) + policyStatement, err := redistributePolicyDefinition.AppendNewStatement(policyStatementName) + if err != nil { + t.Fatalf("failed creating new policy statement, err: %s", err) + } + + policyStatementAction := policyStatement.GetOrCreateActions() + policyStatementAction.SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + policyStatementAction.GetOrCreateBgpActions().GetOrCreateSetAsPathPrepend().Asn = ygot.Uint32(64512) + if isV4 { + policyStatementAction.GetOrCreateBgpActions().GetOrCreateSetAsPathPrepend().Asn = ygot.Uint32(65499) + policyStatementAction.GetOrCreateBgpActions().GetOrCreateSetAsPathPrepend().SetRepeatN(3) + } + + gnmi.Replace(t, dut, policyPath.Config(), redistributePolicyDefinition) + + if deviations.TableConnectionsUnsupported(dut) { + gnmi.Replace(t, dut, bgpPath.Config(), []string{redistributeStaticPolicyName}) + } else { + configureTableConnection(t, dut, isV4, metricPropagate, redistributeStaticPolicyName, oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE) + } +} + +// 1.27.8 and 1.27.17 setup function +func redistributeStaticRoutePolicyWithMED(t *testing.T, dut *ondatra.DUTDevice, isV4 bool, medValue uint32) { + redistributeStaticPolicyName := redistributeStaticPolicyNameV4 + policyStatementName := policyStatementNameV4 + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort1.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy().ExportPolicy() + + if !isV4 { + redistributeStaticPolicyName = redistributeStaticPolicyNameV6 + policyStatementName = policyStatementNameV6 + bgpPath = gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort1.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy().ExportPolicy() + } + + policyPath := gnmi.OC().RoutingPolicy().PolicyDefinition(redistributeStaticPolicyName) + + dutOcRoot := &oc.Root{} + redistributePolicy := dutOcRoot.GetOrCreateRoutingPolicy() + + if deviations.TableConnectionsUnsupported(dut) { + redistributePolicy = redistributeStaticRoute(t, isV4, metricPropagate, policyResultNext, redistributePolicy) + } + + redistributePolicyDefinition := redistributePolicy.GetOrCreatePolicyDefinition(redistributeStaticPolicyName) + policyStatement, err := redistributePolicyDefinition.AppendNewStatement(policyStatementName) + if err != nil { + t.Fatalf("failed creating new policy statement, err: %s", err) + } + + policyStatementAction := policyStatement.GetOrCreateActions() + policyStatementAction.SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + policyStatement.GetOrCreateActions().GetOrCreateBgpActions().SetSetMed(oc.UnionUint32(medValue)) + + gnmi.Replace(t, dut, policyPath.Config(), redistributePolicyDefinition) + + if deviations.TableConnectionsUnsupported(dut) { + gnmi.Replace(t, dut, bgpPath.Config(), []string{redistributeStaticPolicyName}) + } else { + configureTableConnection(t, dut, isV4, metricPropagate, redistributeStaticPolicyName, oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE) + } +} + +// 1.27.9 and 1.27.18 setup function +func redistributeStaticRoutePolicyWithLocalPreference(t *testing.T, dut *ondatra.DUTDevice, isV4 bool, localPreferenceValue uint32) { + + redistributeStaticPolicyName := redistributeStaticPolicyNameV4 + policyStatementName := policyStatementNameV4 + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort3.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy().ExportPolicy() + + if !isV4 { + redistributeStaticPolicyName = redistributeStaticPolicyNameV6 + policyStatementName = policyStatementNameV6 + bgpPath = gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort3.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy().ExportPolicy() + } + + policyPath := gnmi.OC().RoutingPolicy().PolicyDefinition(redistributeStaticPolicyName) + + dutOcRoot := &oc.Root{} + redistributePolicy := dutOcRoot.GetOrCreateRoutingPolicy() + + if deviations.TableConnectionsUnsupported(dut) { + redistributePolicy = redistributeStaticRoute(t, isV4, metricPropagate, policyResultNext, redistributePolicy) + } + + redistributePolicyDefinition := redistributePolicy.GetOrCreatePolicyDefinition(redistributeStaticPolicyName) + policyStatement, err := redistributePolicyDefinition.AppendNewStatement(policyStatementName) + if err != nil { + t.Fatalf("failed creating new policy statement, err: %s", err) + } + + policyStatementAction := policyStatement.GetOrCreateActions() + policyStatementAction.SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + policyStatement.GetOrCreateActions().GetOrCreateBgpActions().SetLocalPref = ygot.Uint32(localPreferenceValue) + + gnmi.Replace(t, dut, policyPath.Config(), redistributePolicyDefinition) + + if deviations.TableConnectionsUnsupported(dut) { + gnmi.Replace(t, dut, bgpPath.Config(), []string{redistributeStaticPolicyName}) + } else { + configureTableConnection(t, dut, isV4, metricPropagate, redistributeStaticPolicyName, oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE) + } +} + +// 1.27.10 and 1.27.19 setup function +func redistributeStaticRoutePolicyWithCommunitySet(t *testing.T, dut *ondatra.DUTDevice, isV4 bool) { + + redistributeStaticPolicyName := redistributeStaticPolicyNameV4 + policyStatementName := policyStatementNameV4 + communitySetName := "community-set-v4" + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort3.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy().ExportPolicy() + + if !isV4 { + redistributeStaticPolicyName = redistributeStaticPolicyNameV6 + policyStatementName = policyStatementNameV6 + communitySetName = "community-set-v6" + bgpPath = gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort3.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy().ExportPolicy() + } + + policyPath := gnmi.OC().RoutingPolicy().PolicyDefinition(redistributeStaticPolicyName) + communityPath := gnmi.OC().RoutingPolicy().DefinedSets().BgpDefinedSets().CommunitySet(communitySetName) + + dutOcRoot := &oc.Root{} + redistributePolicy := dutOcRoot.GetOrCreateRoutingPolicy() + if deviations.TableConnectionsUnsupported(dut) { + redistributePolicy = redistributeStaticRoute(t, isV4, metricPropagate, policyResultNext, redistributePolicy) + } + redistributePolicyDefinition := redistributePolicy.GetOrCreatePolicyDefinition(redistributeStaticPolicyName) + + communitySet := dutOcRoot.GetOrCreateRoutingPolicy() + communitySetPolicyDefinition := communitySet.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(communitySetName) + communitySetPolicyDefinition.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString("64512:100")}) + + gnmi.Replace(t, dut, policyPath.Config(), redistributePolicyDefinition) + gnmi.Replace(t, dut, communityPath.Config(), communitySetPolicyDefinition) + + policyStatement, err := redistributePolicyDefinition.AppendNewStatement(policyStatementName) + if err != nil { + t.Fatalf("failed creating new policy statement, err: %s", err) + } + + policyStatementAction := policyStatement.GetOrCreateActions() + policyStatementAction.SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + policyStatementAction.GetOrCreateBgpActions().GetOrCreateSetCommunity().SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) + policyStatementAction.GetOrCreateBgpActions().GetOrCreateSetCommunity().GetOrCreateReference().SetCommunitySetRef(communitySetName) + + gnmi.Replace(t, dut, policyPath.Config(), redistributePolicyDefinition) + + if deviations.TableConnectionsUnsupported(dut) { + gnmi.Replace(t, dut, bgpPath.Config(), []string{redistributeStaticPolicyName}) + } else { + configureTableConnection(t, dut, isV4, metricPropagate, redistributeStaticPolicyName, oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE) + } +} + +// 1.27.12, 1.27.13, 1.27.20 and 1.27.21 setup function +func redistributeStaticRoutePolicyWithTagSet(t *testing.T, dut *ondatra.DUTDevice, isV4 bool, tagSetValue uint32) { + + redistributeStaticPolicyName := redistributeStaticPolicyNameV4 + tagSetName := "tag-set-v4" + policyStatementName := policyStatementNameV4 + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort1.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy().ExportPolicy() + if !isV4 { + redistributeStaticPolicyName = redistributeStaticPolicyNameV6 + tagSetName = "tag-set-v6" + policyStatementName = policyStatementNameV6 + bgpPath = gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort1.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy().ExportPolicy() + } + + policyPath := gnmi.OC().RoutingPolicy().PolicyDefinition(redistributeStaticPolicyName) + tagSetPath := gnmi.OC().RoutingPolicy().DefinedSets().TagSet(tagSetName) + + dutOcRoot := &oc.Root{} + redistributePolicy := dutOcRoot.GetOrCreateRoutingPolicy() + redistributePolicyDefinition := redistributePolicy.GetOrCreatePolicyDefinition(redistributeStaticPolicyName) + + if deviations.TableConnectionsUnsupported(dut) { + redistributeStaticRoute(t, isV4, !metricPropagate, policyResultNext, redistributePolicy) + gnmi.Replace(t, dut, policyPath.Config(), redistributePolicyDefinition) + configureRoutingPolicyTagSet(t, dut, isV4, tagSetValue) + gnmi.Replace(t, dut, bgpPath.Config(), []string{redistributeStaticPolicyName}) + } else { + tagSet := dutOcRoot.GetOrCreateRoutingPolicy() + tagSetPolicyDefinition := tagSet.GetOrCreateDefinedSets().GetOrCreateTagSet(tagSetName) + tagSetPolicyDefinition.SetTagValue([]oc.RoutingPolicy_DefinedSets_TagSet_TagValue_Union{oc.UnionString(fmt.Sprintf("%v", tagSetValue))}) + gnmi.Replace(t, dut, tagSetPath.Config(), tagSetPolicyDefinition) + + policyStatement, err := redistributePolicyDefinition.AppendNewStatement(policyStatementName) + if err != nil { + t.Fatalf("failed creating new policy statement, err: %s", err) + } + + policyStatementCondition := policyStatement.GetOrCreateConditions() + if !deviations.SkipSetRpMatchSetOptions(dut) { + policyStatementCondition.GetOrCreateMatchTagSet().SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsRestrictedType_ANY) + } + policyStatementCondition.GetOrCreateMatchTagSet().SetTagSet(tagSetName) + policyStatementAction := policyStatement.GetOrCreateActions() + policyStatementAction.SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + gnmi.Replace(t, dut, policyPath.Config(), redistributePolicyDefinition) + + configureTableConnection(t, dut, isV4, !metricPropagate, redistributeStaticPolicyName, oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE) + } +} + +// 1.27.14 and 1.27.22 setup function +func redistributeNullNextHopStaticRoute(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, isV4 bool) { + + redistributeStaticPolicyName := redistributeStaticPolicyNameV4 + tagSetName := "tag-set-v4" + tagValue := "40" + policyStatementName := policyStatementNameV4 + ipRoute := "192.168.20.0/24" + routeNextHop := "192.168.1.9" + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort1.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy().ExportPolicy() + if !isV4 { + redistributeStaticPolicyName = redistributeStaticPolicyNameV6 + tagSetName = "tag-set-v6" + tagValue = "60" + policyStatementName = policyStatementNameV6 + ipRoute = "2024:db8:64:64::/64" + routeNextHop = "2001:DB8::9" + bgpPath = gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort1.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy().ExportPolicy() + } + + policyPath := gnmi.OC().RoutingPolicy().PolicyDefinition(redistributeStaticPolicyName) + staticPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, "static") + + otgConfig := configureOTG(t) + if isV4 { + otgConfig = configureTrafficFlow(t, otgConfig, isV4, "StaticDropRoutesV4Flow", atePort3.Name+".IPv4", atePort2.Name+".IPv4", atePort3.MAC, atePort3.IPv4, "192.168.20.0") + } else { + otgConfig = configureTrafficFlow(t, otgConfig, isV4, "StaticDropRoutesV6Flow", atePort3.Name+".IPv6", atePort2.Name+".IPv6", atePort3.MAC, atePort3.IPv6, "2024:db8:64:64::") + } + ate.OTG().PushConfig(t, otgConfig) + ate.OTG().StartProtocols(t) + + dutOcRoot := &oc.Root{} + networkInstance := dutOcRoot.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + networkInstanceProtocolStatic := networkInstance.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, "static") + networkInstanceProtocolStatic.SetEnabled(true) + ipStaticRoute := networkInstanceProtocolStatic.GetOrCreateStatic(ipRoute) + if !deviations.UseVendorNativeTagSetConfig(dut) { + ipStaticRoute.SetSetTag(oc.UnionString(tagValue)) + } else { + configureStaticRouteTagSet(t, dut) + attachTagSetToStaticRoute(t, dut, ipRoute, tagSetName) + } + ipStaticRouteNextHop := ipStaticRoute.GetOrCreateNextHop("0") + ipStaticRouteNextHop.SetNextHop(oc.LocalRouting_LOCAL_DEFINED_NEXT_HOP_DROP) + gnmi.Update(t, dut, staticPath.Config(), networkInstanceProtocolStatic) + + redistributePolicy := dutOcRoot.GetOrCreateRoutingPolicy() + redistributePolicyDefinition := redistributePolicy.GetOrCreatePolicyDefinition(redistributeStaticPolicyName) + + if deviations.TableConnectionsUnsupported(dut) { + redistributeStaticRoute(t, isV4, !metricPropagate, policyResultNext, redistributePolicy) + + statement, err := redistributePolicyDefinition.AppendNewStatement(policyStatementName) + if err != nil { + t.Fatalf("failed creating new policy statement, err: %s", err) + } + statement.GetOrCreateActions().GetOrCreateBgpActions().SetSetNextHop(oc.UnionString(routeNextHop)) + statement.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + + gnmi.Replace(t, dut, policyPath.Config(), redistributePolicyDefinition) + gnmi.Replace(t, dut, bgpPath.Config(), []string{redistributeStaticPolicyName}) + } else { + ipPrefixPolicyStatement, err := redistributePolicyDefinition.AppendNewStatement(policyStatementName) + if err != nil { + t.Fatalf("failed creating new policy statement, err: %s", err) + } + + ipPrefixPolicyStatementAction := ipPrefixPolicyStatement.GetOrCreateActions() + ipPrefixPolicyStatementAction.GetOrCreateBgpActions().SetSetNextHop(oc.UnionString(routeNextHop)) + ipPrefixPolicyStatementAction.SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + gnmi.Replace(t, dut, policyPath.Config(), redistributePolicyDefinition) + + configureTableConnection(t, dut, isV4, !metricPropagate, redistributeStaticPolicyName, oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE) + } + + // Sending traffic to network via dut having static-route to drop it. + // Traffic must be dropped by the dut irrespective of the bgp advertised-route + // having updated next-hop, considering existing static-route is preferred over bgp. + // Commenting traffic validation for now + /* + sendTraffic(t, ate.OTG()) + verifyTraffic(t, ate, otgConfig) + */ +} + +// 1.27.15 setup function +func redistributeIPv6StaticRoutePolicy(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { + policyPath := gnmi.OC().RoutingPolicy().PolicyDefinition(redistributeStaticPolicyNameV6) + + otgConfig := configureOTG(t) + otgConfig = configureTrafficFlow(t, otgConfig, !isV4, "StaticRoutesV6Flow", atePort1.Name+".IPv6", atePort2.Name+".IPv6", atePort1.MAC, atePort1.IPv6, "2024:db8:128:128::") + ate.OTG().PushConfig(t, otgConfig) + ate.OTG().StartProtocols(t) + + dutOcRoot := &oc.Root{} + redistributePolicy := dutOcRoot.GetOrCreateRoutingPolicy() + if deviations.TableConnectionsUnsupported(dut) { + redistributePolicy = redistributeStaticRoute(t, !isV4, metricPropagate, policyResultNext, redistributePolicy) + } + redistributePolicyDefinition := redistributePolicy.GetOrCreatePolicyDefinition(redistributeStaticPolicyNameV6) + + v6PrefixSet := redistributePolicy.GetOrCreateDefinedSets().GetOrCreatePrefixSet("prefix-set-v6") + v6PrefixSet.GetOrCreatePrefix("2024:db8:128:128::/64", "exact") + if !deviations.SkipPrefixSetMode(dut) { + v6PrefixSet.SetMode(oc.PrefixSet_Mode_IPV6) + } + + v6PrefixSet.GetOrCreatePrefix("2024:db8:64:64::/64", "exact") + if !deviations.SkipPrefixSetMode(dut) { + v6PrefixSet.SetMode(oc.PrefixSet_Mode_IPV6) + } + + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet("prefix-set-v6").Config(), v6PrefixSet) + + ipv6PrefixPolicyStatement, err := redistributePolicyDefinition.AppendNewStatement(policyStatementNameV6) + if err != nil { + t.Fatalf("failed creating new policy statement, err: %s", err) + } + + ipv6PrefixPolicyStatementAction := ipv6PrefixPolicyStatement.GetOrCreateActions() + ipv6PrefixPolicyStatementAction.SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + + ipv6PrefixPolicyStatementConditionsPrefixes := ipv6PrefixPolicyStatement.GetOrCreateConditions().GetOrCreateMatchPrefixSet() + ipv6PrefixPolicyStatementConditionsPrefixes.SetPrefixSet("prefix-set-v6") + if !deviations.SkipSetRpMatchSetOptions(dut) { + ipv6PrefixPolicyStatementConditionsPrefixes.SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsRestrictedType_ANY) + } + + gnmi.Replace(t, dut, policyPath.Config(), redistributePolicyDefinition) + + if deviations.TableConnectionsUnsupported(dut) { + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(atePort1.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy().ExportPolicy() + gnmi.Replace(t, dut, bgpPath.Config(), []string{redistributeStaticPolicyNameV6}) + } else { + configureTableConnection(t, dut, !isV4, metricPropagate, redistributeStaticPolicyNameV6, oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE) + } + + sendTraffic(t, ate.OTG()) + verifyTraffic(t, ate, otgConfig) +} + +// 1.27.15 validation function +func validateRedistributeIPv6RoutePolicy(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { + validateRedistributeStatic(t, dut, acceptRoute, !isV4, metricPropagate) + validatePrefixSetRoutingPolicy(t, dut, !isV4) + validateLearnedIPv6Prefix(t, ate, atePort1.Name+".BGP6.peer", "2024:db8:128:128::", medIPv6, shouldBePresent) +} + +// 1.27.7 and 1.27.16 validation function +func validatePrefixASN(t *testing.T, ate *ondatra.ATEDevice, isV4 bool, bgpPeerName, subnet string, wantASPath []uint32) { + + foundPrefix := false + + if isV4 { + prefixPath := gnmi.OTG().BgpPeer(bgpPeerName).UnicastIpv4PrefixAny() + prefix, ok := gnmi.WatchAll(t, ate.OTG(), prefixPath.State(), 10*time.Second, func(val *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv4Prefix]) bool { + prefix, _ := val.Val() + if prefix.GetAddress() == subnet { + foundPrefix = true + gotASPath := prefix.AsPath[len(prefix.AsPath)-1].GetAsNumbers() + t.Logf("Prefix %v learned with ASN : %v", prefix.GetAddress(), gotASPath) + return reflect.DeepEqual(gotASPath, wantASPath) + } + return false + }).Await(t) + if !ok { + pfx, _ := prefix.Val() + t.Fatalf("Prefix not updated with required as-path. Got %v, want %v", pfx.AsPath[len(pfx.AsPath)-1].GetAsNumbers(), wantASPath) + } + } else { + prefixPath := gnmi.OTG().BgpPeer(bgpPeerName).UnicastIpv6PrefixAny() + prefix, ok := gnmi.WatchAll(t, ate.OTG(), prefixPath.State(), 10*time.Second, func(val *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv6Prefix]) bool { + prefix, _ := val.Val() + if prefix.GetAddress() == subnet { + foundPrefix = true + gotASPath := prefix.AsPath[len(prefix.AsPath)-1].GetAsNumbers() + t.Logf("Prefix %v learned with ASN : %v", prefix.GetAddress(), gotASPath) + return reflect.DeepEqual(gotASPath, wantASPath) + } + return false + }).Await(t) + if !ok { + pfx, _ := prefix.Val() + t.Fatalf("Prefix not updated with required as-path. Got %v, want %v", pfx.AsPath[len(pfx.AsPath)-1].GetAsNumbers(), wantASPath) + } + } + if !foundPrefix { + t.Fatalf("Prefix %v not present in OTG", subnet) + } +} + +// 1.27.9 and 1.27.18 validation function +func validatePrefixLocalPreference(t *testing.T, ate *ondatra.ATEDevice, isV4 bool, bgpPeerName, subnet string, wantLocalPreference uint32) { + + foundPrefix := false + if isV4 { + prefixPath := gnmi.OTG().BgpPeer(bgpPeerName).UnicastIpv4PrefixAny() + prefix, ok := gnmi.WatchAll(t, ate.OTG(), prefixPath.State(), 10*time.Second, func(val *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv4Prefix]) bool { + prefix, _ := val.Val() + if prefix.GetAddress() == subnet { + foundPrefix = true + gotLocalPreference := prefix.GetLocalPreference() + t.Logf("Prefix %v learned with localPreference : %v", prefix.GetAddress(), gotLocalPreference) + return gotLocalPreference == wantLocalPreference + } + return false + }).Await(t) + if !ok { + pfx, _ := prefix.Val() + t.Fatalf("Prefix not updated with the local-preference. Got %v, want %v", pfx.GetLocalPreference(), wantLocalPreference) + } + } else { + prefixPath := gnmi.OTG().BgpPeer(bgpPeerName).UnicastIpv6PrefixAny() + prefix, ok := gnmi.WatchAll(t, ate.OTG(), prefixPath.State(), 10*time.Second, func(val *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv6Prefix]) bool { + prefix, _ := val.Val() + if prefix.GetAddress() == subnet { + foundPrefix = true + gotLocalPreference := prefix.GetLocalPreference() + t.Logf("Prefix %v learned with localPreference : %v", prefix.GetAddress(), gotLocalPreference) + return gotLocalPreference == wantLocalPreference + } + return false + }).Await(t) + if !ok { + pfx, _ := prefix.Val() + t.Fatalf("Prefix not updated with the local-preference. Got %v, want %v", pfx.GetLocalPreference(), wantLocalPreference) + } + } + if !foundPrefix { + t.Fatalf("Prefix %v not present in OTG", subnet) + } +} + +// 1.27.10 and 1.27.19 validation function +func validatePrefixCommunitySet(t *testing.T, ate *ondatra.ATEDevice, isV4 bool, bgpPeerName, subnet, wantCommunitySet string) { + + foundPrefix := false + if isV4 { + prefixPath := gnmi.OTG().BgpPeer(bgpPeerName).UnicastIpv4PrefixAny() + prefix, ok := gnmi.WatchAll(t, ate.OTG(), prefixPath.State(), 10*time.Second, func(val *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv4Prefix]) bool { + prefix, _ := val.Val() + if prefix.GetAddress() == subnet { + foundPrefix = true + var gotCommunitySet string + for _, community := range prefix.Community { + gotCommunityNumber := community.GetCustomAsNumber() + gotCommunityValue := community.GetCustomAsValue() + gotCommunitySet = fmt.Sprint(gotCommunityNumber) + ":" + fmt.Sprint(gotCommunityValue) + } + t.Logf("Prefix %v learned with CommunitySet : %v", prefix.GetAddress(), gotCommunitySet) + return gotCommunitySet == wantCommunitySet + } + return false + }).Await(t) + if !ok { + pfx, _ := prefix.Val() + var gotCS string + for _, community := range pfx.Community { + gotCN := community.GetCustomAsNumber() + gotCV := community.GetCustomAsValue() + gotCS = fmt.Sprint(gotCN) + ":" + fmt.Sprint(gotCV) + } + t.Fatalf("Prefix not updated with the community-set. Got %v, want %v", gotCS, wantCommunitySet) + } + } else { + prefixPath := gnmi.OTG().BgpPeer(bgpPeerName).UnicastIpv6PrefixAny() + prefix, ok := gnmi.WatchAll(t, ate.OTG(), prefixPath.State(), 10*time.Second, func(val *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv6Prefix]) bool { + prefix, _ := val.Val() + if prefix.GetAddress() == subnet { + foundPrefix = true + var gotCommunitySet string + for _, community := range prefix.Community { + gotCommunityNumber := community.GetCustomAsNumber() + gotCommunityValue := community.GetCustomAsValue() + gotCommunitySet = fmt.Sprint(gotCommunityNumber) + ":" + fmt.Sprint(gotCommunityValue) + } + t.Logf("Prefix %v learned with CommunitySet : %v", prefix.GetAddress(), gotCommunitySet) + return gotCommunitySet == wantCommunitySet + } + return false + }).Await(t) + if !ok { + pfx, _ := prefix.Val() + var gotCS string + for _, community := range pfx.Community { + gotCN := community.GetCustomAsNumber() + gotCV := community.GetCustomAsValue() + gotCS = fmt.Sprint(gotCN) + ":" + fmt.Sprint(gotCV) + } + t.Fatalf("Prefix not updated with the community-set. Got %v, want %v", gotCS, wantCommunitySet) + } + } + + if !foundPrefix { + t.Fatalf("Prefix %v not present in OTG", subnet) + } +} + +// 1.27.12, 1.27.13, 1.27.20 and 1.27.21 validation function +func validateRedistributeRouteWithTagSet(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, isV4, shouldBePresent bool) { + + redistributeStaticPolicyName := redistributeStaticPolicyNameV4 + af := oc.Types_ADDRESS_FAMILY_IPV4 + tagSet := "tag-set-v4" + policyStatementName := policyStatementNameV4 + if !isV4 { + redistributeStaticPolicyName = redistributeStaticPolicyNameV6 + af = oc.Types_ADDRESS_FAMILY_IPV6 + tagSet = "tag-set-v6" + policyStatementName = policyStatementNameV6 + } + + if !deviations.TableConnectionsUnsupported(dut) { + tcState := gnmi.Get(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).TableConnection( + oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, + oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, + af).State()) + + importPolicies := tcState.GetImportPolicy() + found := false + for _, iPolicy := range importPolicies { + if iPolicy == redistributeStaticPolicyName { + found = true + } + } + + if !found { + t.Fatal("expected import policy is not configured") + } + } + + var foundPDef oc.RoutingPolicy_PolicyDefinition + policyDef := gnmi.GetAll(t, dut, gnmi.OC().RoutingPolicy().PolicyDefinitionAny().State()) + for _, pDef := range policyDef { + if pDef.GetName() == redistributeStaticPolicyName { + foundPDef = *pDef + } + } + + if foundPDef.GetName() != redistributeStaticPolicyName { + t.Fatal("Expected import policy is not configured") + } + if !deviations.UseVendorNativeTagSetConfig(dut) { + if foundPDef.GetStatement(policyStatementName).GetConditions().GetOrCreateMatchTagSet().GetTagSet() != tagSet { + t.Fatal("Expected tag-set is not configured") + } + if foundPDef.GetStatement(policyStatementName).GetConditions().GetOrCreateMatchTagSet().GetMatchSetOptions() != oc.RoutingPolicy_MatchSetOptionsRestrictedType_ANY { + t.Fatal("Expected match-set-option for tag-set is not configured") + } + } + + if isV4 { + validateLearnedIPv4Prefix(t, ate, atePort1.Name+".BGP4.peer", "192.168.10.0", medZero, shouldBePresent) + } else { + validateLearnedIPv6Prefix(t, ate, atePort1.Name+".BGP6.peer", "2024:db8:128:128::", medZero, shouldBePresent) + } +} + +// 1.27.14 and 1.27.22 validation function +func validateRedistributeNullNextHopStaticRoute(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, isV4 bool) { + + redistributeStaticPolicyName := redistributeStaticPolicyNameV4 + policyStatementName := policyStatementNameV4 + addressFamily := oc.Types_ADDRESS_FAMILY_IPV4 + nextHop := "192.168.1.9" + if !isV4 { + redistributeStaticPolicyName = redistributeStaticPolicyNameV6 + policyStatementName = policyStatementNameV6 + addressFamily = oc.Types_ADDRESS_FAMILY_IPV6 + nextHop = "2001:db8::9" + } + + if !deviations.TableConnectionsUnsupported(dut) { + tcState := gnmi.Get(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).TableConnection( + oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, + oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, + addressFamily).State()) + + importPolicies := tcState.GetImportPolicy() + found := false + for _, iPolicy := range importPolicies { + if iPolicy == redistributeStaticPolicyName { + found = true + } + } + + if !found { + t.Fatal("expected import policy is not configured") + } + } + + var foundPDef oc.RoutingPolicy_PolicyDefinition + policyDef := gnmi.GetAll(t, dut, gnmi.OC().RoutingPolicy().PolicyDefinitionAny().State()) + for _, pDef := range policyDef { + if pDef.GetName() == redistributeStaticPolicyName { + foundPDef = *pDef + } + } + + if foundPDef.GetName() != redistributeStaticPolicyName { + t.Fatal("Expected import policy is not configured") + } + + if foundPDef.GetStatement(policyStatementName).GetActions().GetBgpActions().GetSetNextHop() != oc.UnionString(nextHop) { + t.Fatal("Expected next-hop is not configured") + } + + if isV4 { + validateLearnedIPv4Prefix(t, ate, atePort1.Name+".BGP4.peer", "192.168.20.0", medZero, shouldBePresent) + } else { + validateLearnedIPv6Prefix(t, ate, atePort1.Name+".BGP6.peer", "2024:db8:64:64::", medZero, shouldBePresent) + } +} + +// Used by multiple IPv4 test validations for route presence and MED value +func validateLearnedIPv4Prefix(t *testing.T, ate *ondatra.ATEDevice, bgpPeerName, subnet string, expectedMED uint32, shouldBePresent bool) { + var learnedRedistributedPrefix *otgtelemetry.BgpPeer_UnicastIpv4Prefix + time.Sleep(5 * time.Second) + + _, ok := gnmi.WatchAll(t, + ate.OTG(), + gnmi.OTG().BgpPeer(bgpPeerName).UnicastIpv4PrefixAny().State(), + 30*time.Second, + func(v *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv4Prefix]) bool { + prefix, present := v.Val() + if present { + return prefix.GetAddress() == subnet + } + return false + }).Await(t) + + found := false + if ok { + prefixes := gnmi.GetAll(t, ate.OTG(), gnmi.OTG().BgpPeer(bgpPeerName).UnicastIpv4PrefixAny().State()) + for _, prefix := range prefixes { + if prefix.GetAddress() == subnet { + learnedRedistributedPrefix = prefix + found = true + t.Logf("Prefix learned in otg : %v with next-hop %v and MED %v", prefix.GetAddress(), prefix.GetNextHopIpv4Address(), prefix.GetMultiExitDiscriminator()) + if !shouldBePresent { + t.Fatal("Redistributed IPv4 prefix present in otg but should not be") + } + } + } + } + + if shouldBePresent && !ok && !found { + t.Fatal("Did not see redistributed IPv4 prefix in otg in time") + } + + actualMED := learnedRedistributedPrefix.GetMultiExitDiscriminator() + if actualMED != expectedMED { + t.Fatalf("ate learned redistributed prefix with med set to %d, expected %d", actualMED, expectedMED) + } +} + +// Used by multiple IPv6 test validations for route presence and MED value +func validateLearnedIPv6Prefix(t *testing.T, ate *ondatra.ATEDevice, bgpPeerName, subnet string, expectedMED uint32, shouldBePresent bool) { + + var learnedRedistributedPrefix *otgtelemetry.BgpPeer_UnicastIpv6Prefix + time.Sleep(5 * time.Second) + + _, ok := gnmi.WatchAll(t, + ate.OTG(), + gnmi.OTG().BgpPeer(bgpPeerName).UnicastIpv6PrefixAny().State(), + 30*time.Second, + func(v *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv6Prefix]) bool { + prefix, present := v.Val() + if present { + return prefix.GetAddress() == subnet + } + return false + }).Await(t) + + found := false + if ok { + prefixes := gnmi.GetAll(t, ate.OTG(), gnmi.OTG().BgpPeer(bgpPeerName).UnicastIpv6PrefixAny().State()) + for _, prefix := range prefixes { + if prefix.GetAddress() == subnet { + learnedRedistributedPrefix = prefix + found = true + t.Logf("Prefix learned in otg : %v with next-hop %v and MED %v", prefix.GetAddress(), prefix.GetNextHopIpv6Address(), prefix.GetMultiExitDiscriminator()) + if !shouldBePresent { + t.Fatal("Redistributed IPv6 prefix present in otg but should not be") + } + } + } + } + + if shouldBePresent && !ok && !found { + t.Fatal("Did not see redistributed IPv6 prefix in otg in time") + } + + actualMED := learnedRedistributedPrefix.GetMultiExitDiscriminator() + if actualMED != expectedMED { + t.Fatalf("ate learned redistributed prefix %v with med set to %d, expected %d", subnet, actualMED, expectedMED) + } +} + +func TestBGPStaticRouteRedistribution(t *testing.T) { + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + otg := ate.OTG() + + configureDUT(t, dut) + otgConfig := configureOTG(t) + otg.PushConfig(t, otgConfig) + otg.StartProtocols(t) + + awaitBGPEstablished(t, dut, []string{atePort1.IPv4, atePort3.IPv4}) + + type testCase struct { + name string + setup func() + validate func() + } + + testCases := []testCase{ + // 1.27.1 + { + name: "1.27.1 redistribute-ipv4-static-routes-with-metric-propagation-disabled", + setup: func() { redistributeIPv4Static(t, dut) }, + validate: func() { validateRedistributeIPv4Static(t, dut, ate) }, + }, + // 1.27.2 + { + name: "1.27.2 redistribute-ipv4-static-routes-with-metric-propagation-enabled", + setup: func() { redistributeIPv4StaticWithMetricPropagation(t, dut) }, + validate: func() { validateRedistributeIPv4StaticWithMetricPropagation(t, dut, ate) }, + }, + // 1.27.3 + { + name: "1.27.3 redistribute-ipv6-static-routes-with-metric-propagation-disabled", + setup: func() { redistributeIPv6Static(t, dut) }, + validate: func() { validateRedistributeIPv6Static(t, dut, ate) }, + }, + // 1.27.4 + { + name: "1.27.4 redistribute-ipv6-static-routes-with-metric-propagation-enabled", + setup: func() { redistributeIPv6StaticWithMetricPropagation(t, dut) }, + validate: func() { validateRedistributeIPv6StaticWithMetricPropagation(t, dut, ate) }, + }, + // 1.27.5 + { + name: "1.27.5 redistribute-ipv4-ipv6-default-reject-policy", + setup: func() { redistributeIPv4IPv6StaticDefaultRejectPolicy(t, dut) }, + validate: func() { validateRedistributeIPv4IPv6DefaultRejectPolicy(t, dut, ate) }, + }, + // 1.27.6 + { + name: "1.27.6 redistribute-ipv4-prefix-route-policy", + setup: func() { redistributeIPv4PrefixRoutePolicy(t, dut, ate) }, + validate: func() { validateRedistributeIPv4PrefixRoutePolicy(t, dut, ate) }, + }, + // 1.27.7 + { + name: "1.27.7 redistribute-ipv4-route-policy-as-prepend", + setup: func() { redistributeStaticRoutePolicyWithASN(t, dut, isV4) }, + validate: func() { + validatePrefixASN(t, ate, isV4, atePort1.Name+".BGP4.peer", "192.168.10.0", []uint32{64512, 65499, 65499, 65499}) + }, + }, + // 1.27.8 + { + name: "1.27.8 redistribute-ipv4-route-policy-med", + setup: func() { redistributeStaticRoutePolicyWithMED(t, dut, isV4, medNonZero) }, + validate: func() { + validateLearnedIPv4Prefix(t, ate, atePort1.Name+".BGP4.peer", "192.168.10.0", medNonZero, shouldBePresent) + }, + }, + // 1.27.9 + { + name: "1.27.9 redistribute-ipv4-route-policy-local-preference", + setup: func() { redistributeStaticRoutePolicyWithLocalPreference(t, dut, isV4, localPreference) }, + validate: func() { + validatePrefixLocalPreference(t, ate, isV4, atePort3.Name+".BGP4.peer", "192.168.10.0", localPreference) + }, + }, + // 1.27.10 + { + name: "1.27.10 redistribute-ipv4-route-policy-community-set", + setup: func() { redistributeStaticRoutePolicyWithCommunitySet(t, dut, isV4) }, + validate: func() { + validatePrefixCommunitySet(t, ate, isV4, atePort3.Name+".BGP4.peer", "192.168.10.0", "64512:100") + }, + }, + // 1.27.12 + { + name: "1.27.12 redistribute-ipv4-route-policy-unmatched-tag", + setup: func() { redistributeStaticRoutePolicyWithTagSet(t, dut, isV4, 100) }, + validate: func() { validateRedistributeRouteWithTagSet(t, dut, ate, isV4, !shouldBePresent) }, + }, + // 1.27.13 + { + name: "1.27.13 redistribute-ipv4-route-policy-matched-set", + setup: func() { redistributeStaticRoutePolicyWithTagSet(t, dut, isV4, 40) }, + validate: func() { validateRedistributeRouteWithTagSet(t, dut, ate, isV4, shouldBePresent) }, + }, + // 1.27.14 + { + name: "1.27.14 redistribute-ipv4-route-policy-nexthop", + setup: func() { redistributeNullNextHopStaticRoute(t, dut, ate, isV4) }, + validate: func() { validateRedistributeNullNextHopStaticRoute(t, dut, ate, isV4) }, + }, + // 1.27.15 + { + name: "1.27.15 redistribute-ipv6-route-policy", + setup: func() { redistributeIPv6StaticRoutePolicy(t, dut, ate) }, + validate: func() { validateRedistributeIPv6RoutePolicy(t, dut, ate) }, + }, + // 1.27.16 + { + name: "1.27.16 redistribute-ipv6-route-policy-as-prepend", + setup: func() { redistributeStaticRoutePolicyWithASN(t, dut, !isV4) }, + validate: func() { + validatePrefixASN(t, ate, !isV4, atePort1.Name+".BGP6.peer", "2024:db8:128:128::", []uint32{64512, 64512}) + }, + }, + // 1.27.17 + { + name: "1.27.17 redistribute-ipv6-route-policy-med", + setup: func() { redistributeStaticRoutePolicyWithMED(t, dut, !isV4, medNonZero) }, + validate: func() { + validateLearnedIPv6Prefix(t, ate, atePort1.Name+".BGP6.peer", "2024:db8:128:128::", medNonZero, shouldBePresent) + }, + }, + // 1.27.18 + { + name: "1.27.18 redistribute-ipv6-route-policy-local-preference", + setup: func() { redistributeStaticRoutePolicyWithLocalPreference(t, dut, !isV4, localPreference) }, + validate: func() { + validatePrefixLocalPreference(t, ate, !isV4, atePort3.Name+".BGP6.peer", "2024:db8:128:128::", localPreference) + }, + }, + // 1.27.19 + { + name: "1.27.19 redistribute-ipv6-route-policy-community-set", + setup: func() { redistributeStaticRoutePolicyWithCommunitySet(t, dut, !isV4) }, + validate: func() { + validatePrefixCommunitySet(t, ate, !isV4, atePort3.Name+".BGP6.peer", "2024:db8:128:128::", "64512:100") + }, + }, + // 1.27.20 + { + name: "1.27.20 redistribute-ipv6-route-policy-unmatched-tag", + setup: func() { redistributeStaticRoutePolicyWithTagSet(t, dut, !isV4, 100) }, + validate: func() { validateRedistributeRouteWithTagSet(t, dut, ate, !isV4, !shouldBePresent) }, + }, + // 1.27.21 + { + name: "1.27.21 redistribute-ipv6-route-policy-matched-set", + setup: func() { redistributeStaticRoutePolicyWithTagSet(t, dut, !isV4, 60) }, + validate: func() { validateRedistributeRouteWithTagSet(t, dut, ate, !isV4, shouldBePresent) }, + }, + // 1.27.22 + { + name: "1.27.22 redistribute-ipv6-route-policy-nexthop", + setup: func() { redistributeNullNextHopStaticRoute(t, dut, ate, !isV4) }, + validate: func() { validateRedistributeNullNextHopStaticRoute(t, dut, ate, !isV4) }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + tc.setup() + tc.validate() + }) + } +} + +// Function using native-yang to attach tag-set to static-route +func attachTagSetToStaticRoute(t *testing.T, dut *ondatra.DUTDevice, prefix, tagPolicy string) { + + tagValue, err := json.Marshal(tagPolicy) + if err != nil { + t.Fatalf("Error with json Marshal: %v", err) + } + + gpbSetRequest := &gpb.SetRequest{ + Prefix: &gpb.Path{ + Origin: "native", + }, + Update: []*gpb.Update{ + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{ + {Name: "network-instance", Key: map[string]string{"name": "DEFAULT"}}, + {Name: "static-routes"}, + {Name: "route", Key: map[string]string{"prefix": prefix}}, + {Name: "tag-set"}, + }, + }, + Val: &gpb.TypedValue{ + Value: &gpb.TypedValue_JsonIetfVal{ + JsonIetfVal: tagValue, + }, + }, + }, + }, + } + + gnmiClient := dut.RawAPIs().GNMI(t) + if _, err := gnmiClient.Set(context.Background(), gpbSetRequest); err != nil { + t.Fatalf("Unexpected error updating SRL static-route tag-set: %v", err) + } + +} + +// Function using native-yang to configure tag-set used by static-route +func configureStaticRouteTagSet(t *testing.T, dut *ondatra.DUTDevice) { + + var routingPolicyTagSetValueV4 = []any{ + map[string]any{ + "tag-value": []any{ + 40, + }, + }, + } + tagValueV4, err := json.Marshal(routingPolicyTagSetValueV4) + if err != nil { + t.Fatalf("Error with json Marshal: %v", err) + } + var routingPolicyTagSetValueV6 = []any{ + map[string]any{ + "tag-value": []any{ + 60, + }, + }, + } + tagValueV6, err := json.Marshal(routingPolicyTagSetValueV6) + if err != nil { + t.Fatalf("Error with json Marshal: %v", err) + } + + gpbSetRequest := &gpb.SetRequest{ + Prefix: &gpb.Path{ + Origin: "native", + }, + Update: []*gpb.Update{ + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{ + {Name: "routing-policy"}, + {Name: "tag-set", Key: map[string]string{"name": "tag-static-v4"}}, + }, + }, + Val: &gpb.TypedValue{ + Value: &gpb.TypedValue_JsonIetfVal{ + JsonIetfVal: tagValueV4, + }, + }, + }, + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{ + {Name: "routing-policy"}, + {Name: "tag-set", Key: map[string]string{"name": "tag-static-v6"}}, + }, + }, + Val: &gpb.TypedValue{ + Value: &gpb.TypedValue_JsonIetfVal{ + JsonIetfVal: tagValueV6, + }, + }, + }, + }, + } + + gnmiClient := dut.RawAPIs().GNMI(t) + if _, err := gnmiClient.Set(context.Background(), gpbSetRequest); err != nil { + t.Fatalf("Unexpected error updating SRL routing-policy tag-set for static-route: %v", err) + } +} + +// Function using native-yang to configure tag-set with routing-policy +func configureRoutingPolicyTagSet(t *testing.T, dut *ondatra.DUTDevice, isV4 bool, tValue uint32) { + + tagSetName := "tag-set-v4" + redistributeStaticPolicyName := redistributeStaticPolicyNameV4 + policyStatementName := policyStatementNameV4 + if !isV4 { + tagSetName = "tag-set-v6" + redistributeStaticPolicyName = redistributeStaticPolicyNameV6 + policyStatementName = policyStatementNameV6 + } + + var routingPolicyTagSet = []any{ + map[string]any{ + "match": map[string]any{ + "internal-tags": map[string]any{ + "tag-set": []string{tagSetName}, + }, + }, + "action": map[string]any{ + "policy-result": "accept", + }, + }, + } + tagSetStatement, err := json.Marshal(routingPolicyTagSet) + if err != nil { + t.Fatalf("Error with json Marshal: %v", err) + } + var routingPolicyTagSetValue = []any{ + map[string]any{ + "tag-value": []any{ + tValue, + }, + }, + } + tagValue, err := json.Marshal(routingPolicyTagSetValue) + if err != nil { + t.Fatalf("Error with json Marshal: %v", err) + } + + gpbTagSetReplace := &gpb.SetRequest{ + Prefix: &gpb.Path{ + Origin: "native", + }, + Replace: []*gpb.Update{ + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{ + {Name: "routing-policy"}, + {Name: "tag-set", Key: map[string]string{"name": tagSetName}}, + }, + }, + Val: &gpb.TypedValue{ + Value: &gpb.TypedValue_JsonIetfVal{ + JsonIetfVal: tagValue, + }, + }, + }, + }, + } + + gpbPolicyUpdate := &gpb.SetRequest{ + Prefix: &gpb.Path{ + Origin: "native", + }, + Update: []*gpb.Update{ + { + Path: &gpb.Path{ + Elem: []*gpb.PathElem{ + {Name: "routing-policy"}, + {Name: "policy", Key: map[string]string{"name": redistributeStaticPolicyName}}, + {Name: "statement", Key: map[string]string{"name": policyStatementName}}, + }, + }, + Val: &gpb.TypedValue{ + Value: &gpb.TypedValue_JsonIetfVal{ + JsonIetfVal: tagSetStatement, + }, + }, + }, + }, + } + + gnmiClient := dut.RawAPIs().GNMI(t) + if _, err := gnmiClient.Set(context.Background(), gpbTagSetReplace); err != nil { + t.Fatalf("Unexpected error updating SRL routing-policy tag-set: %v", err) + } + if _, err := gnmiClient.Set(context.Background(), gpbPolicyUpdate); err != nil { + t.Fatalf("Unexpected error updating SRL routing-policy tag-set: %v", err) + } +} diff --git a/feature/experimental/backup_nhg/otg_tests/backup_nhg_test/backup_nhg_test.go b/feature/experimental/backup_nhg/otg_tests/backup_nhg_test/backup_nhg_test.go index 5903ebf83d9..e80bd9718f5 100644 --- a/feature/experimental/backup_nhg/otg_tests/backup_nhg_test/backup_nhg_test.go +++ b/feature/experimental/backup_nhg/otg_tests/backup_nhg_test/backup_nhg_test.go @@ -397,12 +397,22 @@ func (a *testArgs) validateAftTelemetry(t *testing.T, vrfName, prefix, ipAddress t.Fatalf("Could not find prefix %s in telemetry AFT", prefix+"/"+mask) } aftPfx, _ := aftPfxVal.Val() - - aftNHG := gnmi.Get(t, a.dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(a.dut)).Afts().NextHopGroup(aftPfx.GetNextHopGroup()).State()) - if got := len(aftNHG.NextHop); got != 1 { - t.Fatalf("Prefix %s next-hop entry count: got %d, want 1", prefix+"/"+mask, got) + _, found = gnmi.Watch(t, a.dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(a.dut)).Afts().NextHopGroup(aftPfx.GetNextHopGroup()).State(), + 2*time.Minute, func(val *ygnmi.Value[*oc.NetworkInstance_Afts_NextHopGroup]) bool { + value, present := val.Val() + return present && len(value.NextHop) == 1 + }).Await(t) + if !found { + t.Fatalf("nexthop entry count mismatch for prefix %s", prefix+"/"+mask) } + /* + aftNHG := gnmi.Get(t, a.dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(a.dut)).Afts().NextHopGroup(aftPfx.GetNextHopGroup()).State()) + if got := len(aftNHG.NextHop); got != 1 { + t.Fatalf("Prefix %s next-hop entry count: got %d, want 1", prefix+"/"+mask, got) + } + */ + aftNHG := gnmi.Get(t, a.dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(a.dut)).Afts().NextHopGroup(aftPfx.GetNextHopGroup()).State()) for k := range aftNHG.NextHop { aftnh := gnmi.Get(t, a.dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(a.dut)).Afts().NextHop(k).State()) // Handle the cases where the device returns the indirect NH or the recursively resolved NH. diff --git a/feature/experimental/basic_entries_installed_in_gribi/gribi_ip4_entry/feature.textproto b/feature/experimental/basic_entries_installed_in_gribi/gribi_ip4_entry/feature.textproto index ed5c0b71946..14e9565fdd7 100644 --- a/feature/experimental/basic_entries_installed_in_gribi/gribi_ip4_entry/feature.textproto +++ b/feature/experimental/basic_entries_installed_in_gribi/gribi_ip4_entry/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "experimental_basic_entries_installed_in_gribi_gribi_ip4_entry" diff --git a/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn/README.md b/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn/README.md deleted file mode 100644 index 724e759f578..00000000000 --- a/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# RT-1.19: BGP 2-Byte and 4-Byte ASN support - -## Summary - -BGP 2-Byte and 4-Byte ASN support - -## Procedure - -* Establish BGP sessions as follows and verify all the sessions are established - * ATE (2-byte) - DUT (4-byte) - eBGP IPv4 with ASN < 65535 on DUT side - * ATE (2-byte) - DUT (4-byte) - eBGP IPv6 with ASN < 65535 on DUT side - * ATE (4-byte) - DUT (4-byte) - eBGP IPv4 - * ATE (4-byte) - DUT (4-byte) - eBGP IPv6 - * ATE (2-byte) - DUT (4-byte) - iBGP IPv4 with ASN < 65535 on DUT side - * ATE (4-byte) - DUT (4-byte) - iBGP IPv6 with ASN < 65535 on DUT side - * ATE (4-byte) - DUT (4-byte) - iBGP IPv4 - * ATE (4-byte) - DUT (4-byte) - iBGP IPv6 - -## Config Parameter Coverage - -* /global/config/as -* /neighbors/neighbor/config/peer-as -* /neighbors/neighbor/config/local-as - -## Telemetry Parameter Coverage - -* /global/config/as -* /neighbors/neighbor/config/peer-as -* /neighbors/neighbor/config/local-as diff --git a/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn/bgp_2byte_4byte_asn_test.go b/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn/bgp_2byte_4byte_asn_test.go deleted file mode 100644 index af656255725..00000000000 --- a/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn/bgp_2byte_4byte_asn_test.go +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2023 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bgp_2byte_4byte_asn_test - -import ( - "testing" - "time" - - "github.com/openconfig/featureprofiles/internal/attrs" - "github.com/openconfig/featureprofiles/internal/deviations" - "github.com/openconfig/ondatra/gnmi/oc" - "github.com/openconfig/ygot/ygot" - - "github.com/openconfig/featureprofiles/internal/fptest" - "github.com/openconfig/ondatra" - "github.com/openconfig/ondatra/gnmi" -) - -const ( - connInternal = "INTERNAL" - connExternal = "EXTERNAL" -) - -var ( - dutSrc = attrs.Attributes{ - Desc: "DUT to ATE source", - IPv4: "192.0.2.1", - IPv6: "2001:db8::192:0:2:1", - IPv4Len: 30, - IPv6Len: 126, - } - ateSrc = attrs.Attributes{ - Name: "ateSrc", - IPv4: "192.0.2.2", - IPv6: "2001:db8::192:0:2:2", - IPv4Len: 30, - IPv6Len: 126, - } -) - -type bgpNbr struct { - globalAS, localAS, peerAS uint32 - peerIP string - isV4 bool -} - -func TestMain(m *testing.M) { - fptest.RunTests(m) -} - -func TestBgpSession(t *testing.T) { - t.Log("Configure DUT interface") - dut := ondatra.DUT(t, "dut") - dc := gnmi.OC() - i1 := dutSrc.NewOCInterface(dut.Port(t, "port1").Name(), dut) - gnmi.Replace(t, dut, dc.Interface(i1.GetName()).Config(), i1) - if deviations.ExplicitInterfaceInDefaultVRF(dut) { - fptest.AssignToNetworkInstance(t, dut, i1.GetName(), deviations.DefaultNetworkInstance(dut), 0) - } - - t.Log("Configure Network Instance") - dutConfNIPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)) - gnmi.Replace(t, dut, dutConfNIPath.Type().Config(), oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_DEFAULT_INSTANCE) - - dutConfPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") - statePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() - - cases := []struct { - name string - nbr *bgpNbr - dutConf *oc.NetworkInstance_Protocol - ateConf *ondatra.ATETopology - }{ - { - name: "Establish eBGP connection between ATE (2-byte) - DUT (4-byte < 65535) for ipv4 peers", - nbr: &bgpNbr{globalAS: 300, localAS: 100, peerIP: ateSrc.IPv4, peerAS: 200, isV4: true}, - dutConf: createBgpNeighbor(&bgpNbr{globalAS: 300, localAS: 100, peerIP: ateSrc.IPv4, peerAS: 200, isV4: true}, dut), - ateConf: configureATE(t, &bgpNbr{globalAS: 300, localAS: 200, peerIP: dutSrc.IPv4, peerAS: 100, isV4: true}, connExternal), - }, { - name: "Establish eBGP connection between ATE (2-byte) - DUT (4-byte < 65535) for ipv6 peers", - nbr: &bgpNbr{globalAS: 300, localAS: 100, peerIP: ateSrc.IPv6, peerAS: 200, isV4: false}, - dutConf: createBgpNeighbor(&bgpNbr{globalAS: 300, localAS: 100, peerIP: ateSrc.IPv6, peerAS: 200, isV4: false}, dut), - ateConf: configureATE(t, &bgpNbr{globalAS: 300, localAS: 200, peerIP: dutSrc.IPv6, peerAS: 100, isV4: false}, connExternal), - }, { - name: "Establish eBGP connection between ATE (4-byte) - DUT (4-byte) for ipv4 peers", - nbr: &bgpNbr{globalAS: 300, localAS: 70000, peerIP: ateSrc.IPv4, peerAS: 80000, isV4: true}, - dutConf: createBgpNeighbor(&bgpNbr{globalAS: 300, localAS: 70000, peerIP: ateSrc.IPv4, peerAS: 80000, isV4: true}, dut), - ateConf: configureATE(t, &bgpNbr{globalAS: 300, localAS: 80000, peerIP: dutSrc.IPv4, peerAS: 70000, isV4: true}, connExternal), - }, { - name: "Establish eBGP connection between ATE (4-byte) - DUT (4-byte) for ipv6 peers", - nbr: &bgpNbr{globalAS: 300, localAS: 70000, peerIP: ateSrc.IPv6, peerAS: 80000, isV4: true}, - dutConf: createBgpNeighbor(&bgpNbr{globalAS: 300, localAS: 70000, peerIP: ateSrc.IPv6, peerAS: 80000, isV4: false}, dut), - ateConf: configureATE(t, &bgpNbr{globalAS: 300, localAS: 80000, peerIP: dutSrc.IPv6, peerAS: 70000, isV4: false}, connExternal), - }, { - name: "Establish iBGP connection between ATE (2-byte) - DUT (4-byte < 65535) for ipv4 peers", - nbr: &bgpNbr{globalAS: 300, localAS: 200, peerIP: ateSrc.IPv4, peerAS: 200, isV4: true}, - dutConf: createBgpNeighbor(&bgpNbr{globalAS: 300, localAS: 200, peerIP: ateSrc.IPv4, peerAS: 200, isV4: true}, dut), - ateConf: configureATE(t, &bgpNbr{globalAS: 300, localAS: 200, peerIP: dutSrc.IPv4, peerAS: 200, isV4: true}, connInternal), - }, { - name: "Establish iBGP connection between ATE (4-byte) - DUT (4-byte < 65535) for ipv6 peers", - nbr: &bgpNbr{globalAS: 300, localAS: 200, peerIP: ateSrc.IPv6, peerAS: 200, isV4: false}, - dutConf: createBgpNeighbor(&bgpNbr{globalAS: 300, localAS: 200, peerIP: ateSrc.IPv6, peerAS: 200, isV4: false}, dut), - ateConf: configureATE(t, &bgpNbr{globalAS: 300, localAS: 200, peerIP: dutSrc.IPv6, peerAS: 200, isV4: false}, connInternal), - }, { - name: "Establish iBGP connection between ATE (4-byte) - DUT (4-byte) for ipv4 peers", - nbr: &bgpNbr{globalAS: 300, localAS: 80000, peerIP: ateSrc.IPv4, peerAS: 80000, isV4: true}, - dutConf: createBgpNeighbor(&bgpNbr{globalAS: 300, localAS: 80000, peerIP: ateSrc.IPv4, peerAS: 80000, isV4: true}, dut), - ateConf: configureATE(t, &bgpNbr{globalAS: 300, localAS: 80000, peerIP: dutSrc.IPv4, peerAS: 80000, isV4: true}, connInternal), - }, { - name: "Establish iBGP connection between ATE (4-byte) - DUT (4-byte) for ipv6 peers", - nbr: &bgpNbr{globalAS: 300, localAS: 80000, peerIP: ateSrc.IPv6, peerAS: 80000, isV4: false}, - dutConf: createBgpNeighbor(&bgpNbr{globalAS: 300, localAS: 80000, peerIP: ateSrc.IPv6, peerAS: 80000, isV4: false}, dut), - ateConf: configureATE(t, &bgpNbr{globalAS: 300, localAS: 80000, peerIP: dutSrc.IPv6, peerAS: 80000, isV4: false}, connInternal), - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - t.Log("Clear BGP Configs on DUT") - bgpClearConfig(t, dut) - - t.Log("Configure BGP on DUT") - gnmi.Replace(t, dut, dutConfPath.Config(), tc.dutConf) - - fptest.LogQuery(t, "DUT BGP Config ", dutConfPath.Config(), gnmi.Get(t, dut, dutConfPath.Config())) - t.Log("Configure BGP on ATE") - tc.ateConf.Push(t) - tc.ateConf.StartProtocols(t) - - t.Log("Verify BGP session state : ESTABLISHED") - nbrPath := statePath.Neighbor(tc.nbr.peerIP) - gnmi.Await(t, dut, nbrPath.SessionState().State(), time.Second*60, oc.Bgp_Neighbor_SessionState_ESTABLISHED) - - t.Log("Verify BGP AS numbers") - verifyPeer(t, tc.nbr, dut) - - t.Log("Clear BGP Configs on ATE") - tc.ateConf.StopProtocols(t) - }) - } -} - -// bgpClearConfig removes all BGP configuration from the DUT. -func bgpClearConfig(t *testing.T, dut *ondatra.DUTDevice) { - t.Helper() - resetBatch := &gnmi.SetBatch{} - gnmi.BatchDelete(resetBatch, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Config()) - - if deviations.NetworkInstanceTableDeletionRequired(dut) { - tablePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).TableAny() - for _, table := range gnmi.LookupAll(t, dut, tablePath.Config()) { - if val, ok := table.Val(); ok { - if val.GetProtocol() == oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP { - gnmi.BatchDelete(resetBatch, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Table(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, val.GetAddressFamily()).Config()) - } - } - } - } - resetBatch.Set(t, dut) -} - -func verifyPeer(t *testing.T, nbr *bgpNbr, dut *ondatra.DUTDevice) { - t.Helper() - statePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() - nbrPath := statePath.Neighbor(nbr.peerIP) - glblPath := statePath.Global() - - // Check BGP peerAS from telemetry. - peerAS := gnmi.Get(t, dut, nbrPath.State()).GetPeerAs() - if peerAS != nbr.peerAS { - t.Errorf("BGP peerAs: got %v, want %v", peerAS, nbr.peerAS) - } - - // Check BGP localAS from telemetry. - localAS := gnmi.Get(t, dut, nbrPath.State()).GetLocalAs() - if localAS != nbr.localAS { - t.Errorf("BGP localAS: got %v, want %v", localAS, nbr.localAS) - } - - // Check BGP globalAS from telemetry. - globalAS := gnmi.Get(t, dut, glblPath.State()).GetAs() - if globalAS != nbr.globalAS { - t.Errorf("BGP globalAS: got %v, want %v", globalAS, nbr.globalAS) - } -} - -func configureATE(t *testing.T, ateParams *bgpNbr, connectionType string) *ondatra.ATETopology { - t.Helper() - t.Log("Configure ATE interface") - ate := ondatra.ATE(t, "ate") - port1 := ate.Port(t, "port1") - topo := ate.Topology().New() - - iDut1 := topo.AddInterface(ateSrc.Name).WithPort(port1) - iDut1.IPv4().WithAddress(ateSrc.IPv4CIDR()).WithDefaultGateway(dutSrc.IPv4) - iDut1.IPv6().WithAddress(ateSrc.IPv6CIDR()).WithDefaultGateway(dutSrc.IPv6) - - bgpDut1 := iDut1.BGP() - - peer := bgpDut1.AddPeer().WithPeerAddress(ateParams.peerIP).WithLocalASN(ateParams.localAS) - if connectionType == connInternal { - peer.WithTypeInternal() - } else { - peer.WithTypeExternal() - } - return topo -} - -func createBgpNeighbor(nbr *bgpNbr, dut *ondatra.DUTDevice) *oc.NetworkInstance_Protocol { - d := &oc.Root{} - ni1 := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) - niProto := ni1.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") - bgp := niProto.GetOrCreateBgp() - - global := bgp.GetOrCreateGlobal() - global.As = ygot.Uint32(nbr.globalAS) - global.RouterId = ygot.String(dutSrc.IPv4) - global.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) - - pg := bgp.GetOrCreatePeerGroup("ATE") - pg.PeerAs = ygot.Uint32(nbr.peerAS) - pg.PeerGroupName = ygot.String("ATE") - - neighbor := bgp.GetOrCreateNeighbor(nbr.peerIP) - neighbor.PeerAs = ygot.Uint32(nbr.peerAS) - neighbor.Enabled = ygot.Bool(true) - neighbor.PeerGroup = ygot.String("ATE") - neighbor.LocalAs = ygot.Uint32(nbr.localAS) - neighbor.GetOrCreateTimers().RestartTime = ygot.Uint16(75) - - if nbr.isV4 { - afisafi := neighbor.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) - afisafi.Enabled = ygot.Bool(true) - neighbor.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(false) - } else { - afisafi6 := neighbor.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) - afisafi6.Enabled = ygot.Bool(true) - neighbor.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(false) - } - return niProto -} diff --git a/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn/metadata.textproto b/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn/metadata.textproto deleted file mode 100644 index 18a90886ebd..00000000000 --- a/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn/metadata.textproto +++ /dev/null @@ -1,29 +0,0 @@ -# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto -# proto-message: Metadata - -uuid: "b960ced4-2f59-49bf-b078-e53f169fac06" -plan_id: "RT-1.19" -description: "BGP 2-Byte and 4-Byte ASN support" -testbed: TESTBED_DUT_ATE_2LINKS -platform_exceptions: { - platform: { - vendor: ARISTA - } - deviations: { - route_policy_under_afi_unsupported: true - omit_l2_mtu: true - network_instance_table_deletion_required: true - interface_enabled: true - default_network_instance: "default" - } -} -platform_exceptions: { - platform: { - vendor: NOKIA - } - deviations: { - interface_enabled: true - explicit_interface_in_default_vrf: true - } -} -tags: TAGS_AGGREGATION diff --git a/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn_policy_test/README.md b/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn_policy_test/README.md deleted file mode 100644 index 51d125b5eb9..00000000000 --- a/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn_policy_test/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# RT-1.24: BGP 2-Byte and 4-Byte ASN support with policy - -## Summary - -BGP 2-Byte and 4-Byte ASN support with policy - -## Procedure - -* Establish BGP sessions as follows and verify all the sessions are established: - * ATE (2-byte) - DUT (4-byte) - eBGP IPv4 with ASN < 65535 on DUT side - * ATE (2-byte) - DUT (4-byte) - eBGP IPv6 with ASN < 65535 on DUT side - * ATE (4-byte) - DUT (4-byte) - eBGP IPv4 - * ATE (4-byte) - DUT (4-byte) - eBGP IPv6 - * ATE (2-byte) - DUT (4-byte) - iBGP IPv4 with ASN < 65535 on DUT side - * ATE (4-byte) - DUT (4-byte) - iBGP IPv6 with ASN < 65535 on DUT side - * ATE (4-byte) - DUT (4-byte) - iBGP IPv4 - * ATE (4-byte) - DUT (4-byte) - iBGP IPv6 - -* Configure below policies and verify prefix count: - * Policy to reject prefix with prefix filter - * Policy to reject prefix with community filter - * Policy to reject prefix with regex filter to match as-path - -## Config Parameter Coverage - -* /global/config/as -* /neighbors/neighbor/config/peer-as -* /neighbors/neighbor/config/local-as - -## Telemetry Parameter Coverage - -* /global/config/as -* /neighbors/neighbor/config/peer-as -* /neighbors/neighbor/config/local-as diff --git a/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn_policy_test/bgp_2byte_4byte_asn_policy_test.go b/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn_policy_test/bgp_2byte_4byte_asn_policy_test.go deleted file mode 100644 index 7ea95500983..00000000000 --- a/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn_policy_test/bgp_2byte_4byte_asn_policy_test.go +++ /dev/null @@ -1,499 +0,0 @@ -// Copyright 2023 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bgp_2byte_4byte_asn_with_policy_test - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/openconfig/featureprofiles/internal/attrs" - "github.com/openconfig/featureprofiles/internal/deviations" - "github.com/openconfig/featureprofiles/internal/fptest" - gpb "github.com/openconfig/gnmi/proto/gnmi" - "github.com/openconfig/ondatra" - "github.com/openconfig/ondatra/gnmi" - "github.com/openconfig/ondatra/gnmi/oc" - "github.com/openconfig/ygot/ygot" -) - -const ( - connInternal = "INTERNAL" - connExternal = "EXTERNAL" - rejectPrefix = "REJECT-PREFIX" - communitySet = "COMM-SET" - rejectCommunity = "REJECT-COMMUNITY" - rejectAspath = "REJECT-AS-PATH" - aclStatement1 = "10" - aclStatement2 = "20" - aclStatement3 = "50" - aclStatement4 = "60" - prefixSubnetRangeV4 = "30..32" - prefixSubnetRangeV6 = "126..128" - globalAsNumber = 999 -) - -var prefixV4 = []string{"198.51.100.0/30", "198.51.100.4/30", "198.51.100.8/30"} -var prefixV6 = []string{"2001:DB8:1::0/126", "2001:DB8:1::4/126", "2001:DB8:1::8/126"} -var community = []string{"200:1"} - -var ( - dutSrc = attrs.Attributes{ - Desc: "DUT to ATE source", - IPv4: "192.0.2.1", - IPv6: "2001:db8::192:0:2:1", - IPv4Len: 30, - IPv6Len: 126, - } - ateSrc = attrs.Attributes{ - Name: "ateSrc", - IPv4: "192.0.2.2", - IPv6: "2001:db8::192:0:2:2", - IPv4Len: 30, - IPv6Len: 126, - } -) - -type bgpNbr struct { - localAS, peerAS uint32 - peerIP string - isV4 bool -} - -func TestMain(m *testing.M) { - fptest.RunTests(m) -} -func TestBgpSession(t *testing.T) { - t.Log("Configure DUT interface") - dut := ondatra.DUT(t, "dut") - dc := gnmi.OC() - i1 := dutSrc.NewOCInterface(dut.Port(t, "port1").Name(), dut) - gnmi.Replace(t, dut, dc.Interface(i1.GetName()).Config(), i1) - - t.Log("Configure Network Instance") - dutConfNIPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)) - gnmi.Replace(t, dut, dutConfNIPath.Type().Config(), oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_DEFAULT_INSTANCE) - - dutConfPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") - statePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() - - cases := []struct { - name string - nbr *bgpNbr - dutConf *oc.NetworkInstance_Protocol - ateConf *ondatra.ATETopology - }{ - { - name: "Establish eBGP connection between ATE (2-byte) - DUT (4-byte < 65535) for ipv4 peers", - nbr: &bgpNbr{localAS: 100, peerIP: ateSrc.IPv4, peerAS: 200, isV4: true}, - dutConf: createBgpNeighbor(&bgpNbr{localAS: 100, peerIP: ateSrc.IPv4, peerAS: 200, isV4: true}, dut), - ateConf: configureATE(t, &bgpNbr{localAS: 200, peerIP: dutSrc.IPv4, peerAS: 100, isV4: true}, connExternal, prefixV4), - }, { - name: "Establish eBGP connection between ATE (2-byte) - DUT (4-byte < 65535) for ipv6 peers", - nbr: &bgpNbr{localAS: 100, peerIP: ateSrc.IPv6, peerAS: 200, isV4: false}, - dutConf: createBgpNeighbor(&bgpNbr{localAS: 100, peerIP: ateSrc.IPv6, peerAS: 200, isV4: false}, dut), - ateConf: configureATE(t, &bgpNbr{localAS: 200, peerIP: dutSrc.IPv6, peerAS: 100, isV4: false}, connExternal, prefixV6), - }, { - name: "Establish eBGP connection between ATE (4-byte) - DUT (4-byte) for ipv4 peers", - nbr: &bgpNbr{localAS: 70000, peerIP: ateSrc.IPv4, peerAS: 80000, isV4: true}, - dutConf: createBgpNeighbor(&bgpNbr{localAS: 70000, peerIP: ateSrc.IPv4, peerAS: 80000, isV4: true}, dut), - ateConf: configureATE(t, &bgpNbr{localAS: 80000, peerIP: dutSrc.IPv4, peerAS: 70000, isV4: true}, connExternal, prefixV4), - }, { - name: "Establish eBGP connection between ATE (4-byte) - DUT (4-byte) for ipv6 peers", - nbr: &bgpNbr{localAS: 70000, peerIP: ateSrc.IPv6, peerAS: 80000, isV4: false}, - dutConf: createBgpNeighbor(&bgpNbr{localAS: 70000, peerIP: ateSrc.IPv6, peerAS: 80000, isV4: false}, dut), - ateConf: configureATE(t, &bgpNbr{localAS: 80000, peerIP: dutSrc.IPv6, peerAS: 70000, isV4: false}, connExternal, prefixV6), - }, { - name: "Establish iBGP connection between ATE (2-byte) - DUT (4-byte < 65535) for ipv4 peers", - nbr: &bgpNbr{localAS: 200, peerIP: ateSrc.IPv4, peerAS: 200, isV4: true}, - dutConf: createBgpNeighbor(&bgpNbr{localAS: 200, peerIP: ateSrc.IPv4, peerAS: 200, isV4: true}, dut), - ateConf: configureATE(t, &bgpNbr{localAS: 200, peerIP: dutSrc.IPv4, peerAS: 200, isV4: true}, connInternal, prefixV4), - }, { - name: "Establish iBGP connection between ATE (4-byte) - DUT (4-byte < 65535) for ipv6 peers", - nbr: &bgpNbr{localAS: 200, peerIP: ateSrc.IPv6, peerAS: 200, isV4: false}, - dutConf: createBgpNeighbor(&bgpNbr{localAS: 200, peerIP: ateSrc.IPv6, peerAS: 200, isV4: false}, dut), - ateConf: configureATE(t, &bgpNbr{localAS: 200, peerIP: dutSrc.IPv6, peerAS: 200, isV4: false}, connInternal, prefixV6), - }, { - name: "Establish iBGP connection between ATE (4-byte) - DUT (4-byte) for ipv4 peers", - nbr: &bgpNbr{localAS: 80000, peerIP: ateSrc.IPv4, peerAS: 80000, isV4: true}, - dutConf: createBgpNeighbor(&bgpNbr{localAS: 80000, peerIP: ateSrc.IPv4, peerAS: 80000, isV4: true}, dut), - ateConf: configureATE(t, &bgpNbr{localAS: 80000, peerIP: dutSrc.IPv4, peerAS: 80000, isV4: true}, connInternal, prefixV4), - }, { - name: "Establish iBGP connection between ATE (4-byte) - DUT (4-byte) for ipv6 peers", - nbr: &bgpNbr{localAS: 80000, peerIP: ateSrc.IPv6, peerAS: 80000, isV4: false}, - dutConf: createBgpNeighbor(&bgpNbr{localAS: 80000, peerIP: ateSrc.IPv6, peerAS: 80000, isV4: false}, dut), - ateConf: configureATE(t, &bgpNbr{localAS: 80000, peerIP: dutSrc.IPv6, peerAS: 80000, isV4: false}, connInternal, prefixV6), - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - t.Log("Clear BGP Configs on DUT") - bgpClearConfig(t, dut) - - configureRegexPolicy(t, dut) - - d := &oc.Root{} - rpl := configureBGPPolicy(t, d, tc.nbr.isV4) - gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rpl) - - t.Log("Configure BGP on DUT") - gnmi.Replace(t, dut, dutConfPath.Config(), tc.dutConf) - - fptest.LogQuery(t, "DUT BGP Config ", dutConfPath.Config(), gnmi.Get(t, dut, dutConfPath.Config())) - t.Log("Configure BGP on ATE") - tc.ateConf.Push(t) - tc.ateConf.StartProtocols(t) - - t.Log("Verify BGP session state : ESTABLISHED") - nbrPath := statePath.Neighbor(tc.nbr.peerIP) - gnmi.Await(t, dut, nbrPath.SessionState().State(), time.Second*60, oc.Bgp_Neighbor_SessionState_ESTABLISHED) - - t.Log("Verify BGP AS numbers and prefix count") - verifyPeer(t, tc.nbr, dut) - - t.Log("Apply BGP policy for rejecting prefix") - pol := applyBgpPolicy(rejectPrefix, dut, tc.nbr.isV4) - gnmi.Update(t, dut, dutConfPath.Config(), pol) - verifyPrefixesTelemetry(t, dut, 2, tc.nbr.isV4) - deleteBgpPolicy(t, dut, tc.nbr.isV4) - verifyPrefixesTelemetry(t, dut, 3, tc.nbr.isV4) - - t.Log("Apply BGP policy for rejecting prefix with community filter") - pol = applyBgpPolicy(rejectCommunity, dut, tc.nbr.isV4) - gnmi.Update(t, dut, dutConfPath.Config(), pol) - verifyPrefixesTelemetry(t, dut, 2, tc.nbr.isV4) - deleteBgpPolicy(t, dut, tc.nbr.isV4) - verifyPrefixesTelemetry(t, dut, 3, tc.nbr.isV4) - - t.Log("Apply BGP policy for rejecting prefix with as-path regex filter") - pol = applyBgpPolicy(rejectAspath, dut, tc.nbr.isV4) - gnmi.Update(t, dut, dutConfPath.Config(), pol) - verifyPrefixesTelemetry(t, dut, 2, tc.nbr.isV4) - - t.Log("Clear BGP Configs on ATE") - tc.ateConf.StopProtocols(t) - }) - } -} - -// Build config with Origin set to cli and Ascii encoded config. -func buildCliConfigRequest(config string) (*gpb.SetRequest, error) { - gpbSetRequest := &gpb.SetRequest{ - Update: []*gpb.Update{{ - Path: &gpb.Path{ - Origin: "cli", - Elem: []*gpb.PathElem{}, - }, - Val: &gpb.TypedValue{ - Value: &gpb.TypedValue_AsciiVal{ - AsciiVal: config, - }, - }, - }}, - } - return gpbSetRequest, nil -} - -// juniperCLI returns Juniper CLI config statement. -func juniperCLI() string { - return fmt.Sprintf(` - policy-options { - policy-statement %s { - term term1 { - from as-path match-as-path; - then reject; - } - term term2 { - then accept; - } - } - as-path match-as-path ".* 4400 3300"; - }`, rejectAspath) -} - -// configureRegexPolicy is used to configure vendor specific config statement. -func configureRegexPolicy(t *testing.T, dut *ondatra.DUTDevice) { - t.Helper() - var config string - gnmiClient := dut.RawAPIs().GNMI(t) - - switch dut.Vendor() { - case ondatra.JUNIPER: - config = juniperCLI() - t.Logf("Push the CLI config:%s", dut.Vendor()) - } - - gpbSetRequest, err := buildCliConfigRequest(config) - if err != nil { - t.Fatalf("Cannot build a gNMI SetRequest: %v", err) - } - - if _, err = gnmiClient.Set(context.Background(), gpbSetRequest); err != nil { - t.Fatalf("gnmiClient.Set() with unexpected error: %v", err) - } -} - -// configureBGPPolicy configures a BGP routing policy to accept or reject routes based on prefix match conditions -// Additonally, it also configures policy to match prefix based on community and regex for as path -func configureBGPPolicy(t *testing.T, d *oc.Root, isV4 bool) *oc.RoutingPolicy { - t.Helper() - rp := d.GetOrCreateRoutingPolicy() - pset := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet(rejectPrefix) - - if isV4 { - pset.GetOrCreatePrefix(prefixV4[2], prefixSubnetRangeV4) - } else { - pset.GetOrCreatePrefix(prefixV6[2], prefixSubnetRangeV6) - } - pdef := rp.GetOrCreatePolicyDefinition(rejectPrefix) - - stmt10, err := pdef.AppendNewStatement(aclStatement1) - if err != nil { - t.Errorf("Error while creating new statement %v", err) - } - stmt10.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_REJECT_ROUTE - stmt10.GetOrCreateConditions().GetOrCreateMatchPrefixSet().PrefixSet = ygot.String(rejectPrefix) - - stmt20, err := pdef.AppendNewStatement(aclStatement2) - if err != nil { - t.Errorf("Error while creating new statement %v", err) - } - stmt20.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE - - commSet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(communitySet) - commSet.CommunitySetName = ygot.String(communitySet) - var communityMembers []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union - for _, member := range community { - communityMember, _ := commSet.To_RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(member) - communityMembers = append(communityMembers, communityMember) - } - commSet.SetCommunityMember(communityMembers) - pdefComm := rp.GetOrCreatePolicyDefinition(rejectCommunity) - - stmt50, err := pdefComm.AppendNewStatement(aclStatement3) - if err != nil { - t.Errorf("Error while creating new statement %v", err) - } - stmt50.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_REJECT_ROUTE - stmt50.GetOrCreateConditions().GetOrCreateBgpConditions().CommunitySet = ygot.String(communitySet) - - stmt60, err := pdefComm.AppendNewStatement(aclStatement4) - if err != nil { - t.Errorf("Error while creating new statement %v", err) - } - stmt60.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE - - return rp -} - -func verifyPrefixesTelemetry(t *testing.T, dut *ondatra.DUTDevice, wantInstalled uint32, isV4 bool) { - t.Helper() - t.Logf("Verify BGP prefix count") - if isV4 { - verifyPrefixesTelemetryV4(t, dut, wantInstalled) - } else { - verifyPrefixesTelemetryV6(t, dut, wantInstalled) - } -} - -// verifyPrefixesTelemetry confirms that the dut shows the correct numbers of installed, sent and -// received IPv4 prefixes -func verifyPrefixesTelemetryV4(t *testing.T, dut *ondatra.DUTDevice, wantInstalled uint32) { - t.Helper() - statePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() - prefixesv4 := statePath.Neighbor(ateSrc.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Prefixes() - - if gotInstalled := gnmi.Get(t, dut, prefixesv4.Installed().State()); gotInstalled != wantInstalled { - t.Errorf("Installed prefixes mismatch: got %v, want %v", gotInstalled, wantInstalled) - } -} - -// verifyPrefixesTelemetryV6 confirms that the dut shows the correct numbers of installed, sent and -// received IPv6 prefixes -func verifyPrefixesTelemetryV6(t *testing.T, dut *ondatra.DUTDevice, wantInstalledv6 uint32) { - statePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() - prefixesv6 := statePath.Neighbor(ateSrc.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Prefixes() - - if gotInstalledv6 := gnmi.Get(t, dut, prefixesv6.Installed().State()); gotInstalledv6 != wantInstalledv6 { - t.Errorf("IPV6 Installed prefixes mismatch: got %v, want %v", gotInstalledv6, wantInstalledv6) - } -} - -// bgpClearConfig removes all BGP configuration from the DUT. -func bgpClearConfig(t *testing.T, dut *ondatra.DUTDevice) { - resetBatch := &gnmi.SetBatch{} - gnmi.BatchDelete(resetBatch, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Config()) - - if deviations.NetworkInstanceTableDeletionRequired(dut) { - tablePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).TableAny() - for _, table := range gnmi.LookupAll(t, dut, tablePath.Config()) { - if val, ok := table.Val(); ok { - if val.GetProtocol() == oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP { - gnmi.BatchDelete(resetBatch, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Table(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, val.GetAddressFamily()).Config()) - } - } - } - } - resetBatch.Set(t, dut) -} - -func verifyPeer(t *testing.T, nbr *bgpNbr, dut *ondatra.DUTDevice) { - statePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() - nbrPath := statePath.Neighbor(nbr.peerIP) - glblPath := statePath.Global() - - // Check BGP peerAS from telemetry. - peerAS := gnmi.Get(t, dut, nbrPath.State()).GetPeerAs() - if peerAS != nbr.peerAS { - t.Errorf("BGP peerAs: got %v, want %v", peerAS, nbr.peerAS) - } - - // Check BGP localAS from telemetry. - localAS := gnmi.Get(t, dut, nbrPath.State()).GetLocalAs() - if localAS != nbr.localAS { - t.Errorf("BGP localAS: got %v, want %v", localAS, nbr.localAS) - } - - // Check BGP globalAS from telemetry. - globalAS := gnmi.Get(t, dut, glblPath.State()).GetAs() - if globalAS != globalAsNumber { - t.Errorf("BGP globalAS: got %v, want %v", globalAS, nbr.localAS) - } - - verifyPrefixesTelemetry(t, dut, 3, nbr.isV4) -} - -func configureATE(t *testing.T, ateParams *bgpNbr, connectionType string, prefixes []string) *ondatra.ATETopology { - t.Helper() - t.Log("Configure ATE interface") - ate := ondatra.ATE(t, "ate") - port1 := ate.Port(t, "port1") - topo := ate.Topology().New() - - iDut1 := topo.AddInterface(ateSrc.Name).WithPort(port1) - iDut1.IPv4().WithAddress(ateSrc.IPv4CIDR()).WithDefaultGateway(dutSrc.IPv4) - iDut1.IPv6().WithAddress(ateSrc.IPv6CIDR()).WithDefaultGateway(dutSrc.IPv6) - - bgpDut1 := iDut1.BGP() - peer := bgpDut1.AddPeer().WithPeerAddress(ateParams.peerIP).WithLocalASN(ateParams.localAS) - - if connectionType == connInternal { - peer.WithTypeInternal() - } else { - peer.WithTypeExternal() - } - - network1 := iDut1.AddNetwork("bgpNeti1") - network2 := iDut1.AddNetwork("bgpNeti2") - network3 := iDut1.AddNetwork("bgpNeti3") - - if ateParams.isV4 { - network1.IPv4().WithAddress(prefixes[0]).WithCount(1) - network1.BGP().WithNextHopAddress(ateSrc.IPv4).AddASPathSegment(55000, 4400, 3300) - - network2.IPv4().WithAddress(prefixes[1]).WithCount(1) - network2.BGP().WithNextHopAddress(ateSrc.IPv4).AddASPathSegment(55000, 7700) - network2.BGP().Communities().WithPrivateCommunities("200:1") - - network3.IPv4().WithAddress(prefixes[2]).WithCount(1) - network3.BGP().WithNextHopAddress(ateSrc.IPv4) - } else { - network1.IPv6().WithAddress(prefixes[0]).WithCount(1) - network1.BGP().WithNextHopAddress(ateSrc.IPv6).AddASPathSegment(55000, 4400, 3300) - - network2.IPv6().WithAddress(prefixes[1]).WithCount(1) - network2.BGP().WithNextHopAddress(ateSrc.IPv6).AddASPathSegment(55000, 7700) - network2.BGP().Communities().WithPrivateCommunities("200:1") - - network3.IPv6().WithAddress(prefixes[2]).WithCount(1) - network3.BGP().WithNextHopAddress(ateSrc.IPv6) - } - return topo -} - -func applyBgpPolicy(policyName string, dut *ondatra.DUTDevice, isV4 bool) *oc.NetworkInstance_Protocol { - d := &oc.Root{} - ni1 := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) - niProto := ni1.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") - bgp := niProto.GetOrCreateBgp() - - pg := bgp.GetOrCreatePeerGroup("ATE") - pg.PeerGroupName = ygot.String("ATE") - - if deviations.RoutePolicyUnderAFIUnsupported(dut) { - //policy under peer group - pg.GetOrCreateApplyPolicy().ImportPolicy = []string{policyName} - return niProto - } - - aftType := oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST - if isV4 { - aftType = oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST - } - - afisafi := pg.GetOrCreateAfiSafi(aftType) - afisafi.Enabled = ygot.Bool(true) - rpl := afisafi.GetOrCreateApplyPolicy() - rpl.SetImportPolicy([]string{policyName}) - - return niProto -} - -func deleteBgpPolicy(t *testing.T, dut *ondatra.DUTDevice, isV4 bool) { - t.Helper() - t.Logf("Delete BGP policy on DUT") - aftType := oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST - if isV4 { - aftType = oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST - } - - policyConfPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().PeerGroup("ATE").AfiSafi(aftType).ApplyPolicy().Config() - gnmi.Delete(t, dut, policyConfPath) -} - -func createBgpNeighbor(nbr *bgpNbr, dut *ondatra.DUTDevice) *oc.NetworkInstance_Protocol { - d := &oc.Root{} - ni1 := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) - niProto := ni1.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") - bgp := niProto.GetOrCreateBgp() - - global := bgp.GetOrCreateGlobal() - global.As = ygot.Uint32(globalAsNumber) - global.RouterId = ygot.String(dutSrc.IPv4) - - pg := bgp.GetOrCreatePeerGroup("ATE") - pg.PeerAs = ygot.Uint32(nbr.peerAS) - pg.PeerGroupName = ygot.String("ATE") - - neighbor := bgp.GetOrCreateNeighbor(nbr.peerIP) - neighbor.PeerAs = ygot.Uint32(nbr.peerAS) - neighbor.LocalAs = ygot.Uint32(nbr.localAS) - neighbor.Enabled = ygot.Bool(true) - neighbor.PeerGroup = ygot.String("ATE") - neighbor.GetOrCreateTimers().RestartTime = ygot.Uint16(75) - - if nbr.isV4 { - afisafi := neighbor.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) - afisafi.Enabled = ygot.Bool(true) - neighbor.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(false) - } else { - afisafi6 := neighbor.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) - afisafi6.Enabled = ygot.Bool(true) - neighbor.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(false) - } - return niProto -} diff --git a/feature/experimental/bgp/ate_tests/bgp_remove_private_as/README.md b/feature/experimental/bgp/ate_tests/bgp_remove_private_as/README.md deleted file mode 100644 index 3cf8f73d886..00000000000 --- a/feature/experimental/bgp/ate_tests/bgp_remove_private_as/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# RT-1.11: BGP remove private AS  - -## Summary - -BGP remove private AS - -## Procedure - -* Establish BGP sessions as follows between ATE and DUT. - * ATE emulates two eBGP neighbors peering with the DUT using public AS numbers. - * DUT Port1 (AS 500) ---eBGP--- ATE Port1 (AS 100) - * DUT Port2 (AS 500) ---eBGP--- ATE Port2 (AS 200) - * Inject routes with AS_PATH modified to have private AS number 65501 from eBGP neighbor #1 - (ATE Port1). - * Validate received routes on ATE Port2 should have AS Path "500 100 65501". - * Configure "remove private AS" with type PRIVATE_AS_REMOVE_ALL on DUT. - * Validate that private AS numbers are stripped before advertisement to the eBGP peer ATE Port2. - * AS path for received routes on ATE Port2 should be "500 100". - * TODO: different patterns of private AS should be tested. - * AS Path SEQ - 65501, 65507, 65554 - * AS Path SEQ - 65501, 600 - * AS Path SEQ - 800, 65501, 600 - ## TODO : https://github.com/openconfig/featureprofiles/issues/1659 - ## SET mode is not working in ATE. - * AS Path SET - 800, 65505, 600 - -## Config Parameter coverage - -* /network-instances/network-instance/protocols/protocol/bgp/peer-groups/peer-group/config/remove-private-as - -## Telemetry Parameter coverage - -* /network-instances/network-instance/protocols/protocol/bgp/rib/attr-sets/attr-set/as4-path/as4-segment/state - -## Protocol/RPC Parameter coverage - -N/A - -## Minimum DUT platform requirement - -N/A \ No newline at end of file diff --git a/feature/experimental/bgp/ate_tests/bgp_remove_private_as/bgp_remove_private_as_test.go b/feature/experimental/bgp/ate_tests/bgp_remove_private_as/bgp_remove_private_as_test.go deleted file mode 100644 index 0d785ad59c7..00000000000 --- a/feature/experimental/bgp/ate_tests/bgp_remove_private_as/bgp_remove_private_as_test.go +++ /dev/null @@ -1,394 +0,0 @@ -// Copyright 2023 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bgp_remove_private_as_test - -import ( - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/openconfig/featureprofiles/internal/attrs" - "github.com/openconfig/featureprofiles/internal/deviations" - "github.com/openconfig/featureprofiles/internal/fptest" - "github.com/openconfig/ondatra" - "github.com/openconfig/ondatra/gnmi" - "github.com/openconfig/ondatra/gnmi/oc" - "github.com/openconfig/ygnmi/ygnmi" - "github.com/openconfig/ygot/ygot" -) - -func TestMain(m *testing.M) { - fptest.RunTests(m) -} - -// The testbed consists of ate:port1 -> dut:port1 and -// dut:port2 -> ate:port2. The first pair is called the "source" -// pair, and the second the "destination" pair. -// -// Source: ate:port1 -> dut:port1 subnet 192.0.2.0/30 -// Destination: dut:port2 -> ate:port2 subnet 192.0.2.4/30 -// -// Note that the first (.0, .3) and last (.4, .7) IPv4 addresses are -// reserved from the subnet for broadcast, so a /30 leaves exactly 2 -// usable addresses. This does not apply to IPv6 which allows /127 -// for point to point links, but we use /126 so the numbering is -// consistent with IPv4. - -const ( - trafficDuration = 1 * time.Minute - ipv4SrcTraffic = "192.0.2.2" - advertisedRoutesv4CIDR = "203.0.113.1/32" - peerGrpName1 = "BGP-PEER-GROUP1" - peerGrpName2 = "BGP-PEER-GROUP2" - policyName = "ALLOW" - routeCount = 254 - dutAS = 500 - ateAS1 = 100 - ateAS2 = 200 - plenIPv4 = 30 - plenIPv6 = 126 - removeASPath = true -) - -var ( - dutSrc = attrs.Attributes{ - Desc: "DUT to ATE source", - IPv4: "192.0.2.1", - IPv6: "2001:db8::192:0:2:1", - IPv4Len: plenIPv4, - IPv6Len: plenIPv6, - } - ateSrc = attrs.Attributes{ - Name: "ateSrc", - IPv4: "192.0.2.2", - IPv6: "2001:db8::192:0:2:2", - IPv4Len: plenIPv4, - IPv6Len: plenIPv6, - } - dutDst = attrs.Attributes{ - Desc: "DUT to ATE destination", - IPv4: "192.0.2.5", - IPv6: "2001:db8::192:0:2:5", - IPv4Len: plenIPv4, - IPv6Len: plenIPv6, - } - ateDst = attrs.Attributes{ - Name: "atedst", - IPv4: "192.0.2.6", - IPv6: "2001:db8::192:0:2:6", - IPv4Len: plenIPv4, - IPv6Len: plenIPv6, - } -) - -// configureDUT configures all the interfaces on the DUT. -func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { - dc := gnmi.OC() - i1 := dutSrc.NewOCInterface(dut.Port(t, "port1").Name(), dut) - gnmi.Replace(t, dut, dc.Interface(i1.GetName()).Config(), i1) - - i2 := dutDst.NewOCInterface(dut.Port(t, "port2").Name(), dut) - gnmi.Replace(t, dut, dc.Interface(i2.GetName()).Config(), i2) -} - -// verifyPortsUp asserts that each port on the device is operating. -func verifyPortsUp(t *testing.T, dev *ondatra.Device) { - t.Helper() - for _, p := range dev.Ports() { - status := gnmi.Get(t, dev, gnmi.OC().Interface(p.Name()).OperStatus().State()) - if want := oc.Interface_OperStatus_UP; status != want { - t.Errorf("%s Status: got %v, want %v", p, status, want) - } - } -} - -// bgpCreateNbr creates a BGP object with neighbors pointing to ateSrc and ateDst. -func bgpCreateNbr(localAs, peerAs uint32, dut *ondatra.DUTDevice) *oc.NetworkInstance_Protocol { - nbr1v4 := &bgpNeighbor{as: ateAS1, neighborip: ateSrc.IPv4, isV4: true, peerGrp: peerGrpName1} - nbr2v4 := &bgpNeighbor{as: ateAS2, neighborip: ateDst.IPv4, isV4: true, peerGrp: peerGrpName2} - nbrs := []*bgpNeighbor{nbr1v4, nbr2v4} - - d := &oc.Root{} - ni1 := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) - niProto := ni1.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") - bgp := niProto.GetOrCreateBgp() - global := bgp.GetOrCreateGlobal() - global.RouterId = ygot.String(dutDst.IPv4) - global.As = ygot.Uint32(localAs) - global.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) - global.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) - - // Note: we have to define the peer group even if we aren't setting any policy because it's - // invalid OC for the neighbor to be part of a peer group that doesn't exist. - pg1 := bgp.GetOrCreatePeerGroup(peerGrpName1) - pg1.PeerAs = ygot.Uint32(ateAS1) - pg1.PeerGroupName = ygot.String(peerGrpName1) - - pg2 := bgp.GetOrCreatePeerGroup(peerGrpName2) - pg2.PeerAs = ygot.Uint32(ateAS2) - pg2.PeerGroupName = ygot.String(peerGrpName2) - - if deviations.RoutePolicyUnderAFIUnsupported(dut) { - rpl := pg1.GetOrCreateApplyPolicy() - rpl.ImportPolicy = []string{policyName} - rpl.ExportPolicy = []string{policyName} - - rp2 := pg2.GetOrCreateApplyPolicy() - rp2.ImportPolicy = []string{policyName} - rp2.ExportPolicy = []string{policyName} - } else { - pgaf := pg1.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) - pgaf.Enabled = ygot.Bool(true) - rpl := pgaf.GetOrCreateApplyPolicy() - rpl.ImportPolicy = []string{policyName} - rpl.ExportPolicy = []string{policyName} - - pgaf2 := pg2.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) - pgaf2.Enabled = ygot.Bool(true) - rp2 := pgaf2.GetOrCreateApplyPolicy() - rp2.ImportPolicy = []string{policyName} - rp2.ExportPolicy = []string{policyName} - } - - for _, nbr := range nbrs { - nv4 := bgp.GetOrCreateNeighbor(nbr.neighborip) - nv4.PeerGroup = ygot.String(nbr.peerGrp) - nv4.PeerAs = ygot.Uint32(nbr.as) - nv4.Enabled = ygot.Bool(true) - af4 := nv4.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) - af4.Enabled = ygot.Bool(true) - af6 := nv4.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) - af6.Enabled = ygot.Bool(false) - } - return niProto -} - -// verifyBGPTelemetry checks that the dut has an established BGP session with reasonable settings. -func verifyBGPTelemetry(t *testing.T, dut *ondatra.DUTDevice) { - var nbrIP = []string{ateSrc.IPv4, ateDst.IPv4} - t.Logf("Verifying BGP state.") - statePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() - for _, nbr := range nbrIP { - var status *ygnmi.Value[oc.E_Bgp_Neighbor_SessionState] - nbrPath := statePath.Neighbor(nbr) - t.Logf("Waiting for BGP neighbor to establish...") - status, ok := gnmi.Watch(t, dut, nbrPath.SessionState().State(), time.Minute, func(val *ygnmi.Value[oc.E_Bgp_Neighbor_SessionState]) bool { - state, ok := val.Val() - return ok && state == oc.Bgp_Neighbor_SessionState_ESTABLISHED - }).Await(t) - if !ok { - fptest.LogQuery(t, "BGP reported state", nbrPath.State(), gnmi.Get(t, dut, nbrPath.State())) - t.Fatal("No BGP neighbor formed") - } - state, _ := status.Val() - t.Logf("BGP adjacency for %s: %s", nbr, state) - if want := oc.Bgp_Neighbor_SessionState_ESTABLISHED; state != want { - t.Errorf("BGP peer %s status got %v, want %d", nbr, status, want) - } - } -} - -// verifyPrefixesTelemetry confirms that the dut shows the correct numbers of installed, -// sent and received IPv4 prefixes. -func verifyPrefixesTelemetry(t *testing.T, dut *ondatra.DUTDevice, nbr string, wantInstalled, wantSent uint32) { - statePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() - prefixesv4 := statePath.Neighbor(nbr).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Prefixes() - if gotInstalled := gnmi.Get(t, dut, prefixesv4.Installed().State()); gotInstalled != wantInstalled { - t.Errorf("Installed prefixes mismatch: got %v, want %v", gotInstalled, wantInstalled) - } - if gotSent := gnmi.Get(t, dut, prefixesv4.Sent().State()); gotSent != wantSent { - t.Errorf("Sent prefixes mismatch: got %v, want %v", gotSent, wantSent) - } -} - -// configureATE configures the interfaces and BGP protocols on an ATE, including -// advertising some(faked) networks over BGP. -func configureATE(t *testing.T, ate *ondatra.ATEDevice, asSeg []uint32, asSEQMode bool) *ondatra.ATETopology { - port1 := ate.Port(t, "port1") - topo := ate.Topology().New() - iDut1 := topo.AddInterface(ateSrc.Name).WithPort(port1) - iDut1.IPv4().WithAddress(ateSrc.IPv4CIDR()).WithDefaultGateway(dutSrc.IPv4) - - port2 := ate.Port(t, "port2") - iDut2 := topo.AddInterface(ateDst.Name).WithPort(port2) - iDut2.IPv4().WithAddress(ateDst.IPv4CIDR()).WithDefaultGateway(dutDst.IPv4) - - // Setup ATE BGP route v4 advertisement. - bgpDut1 := iDut1.BGP() - bgpDut1.AddPeer().WithPeerAddress(dutSrc.IPv4).WithLocalASN(ateAS1). - WithTypeExternal() - - bgpDut2 := iDut2.BGP() - bgpDut2.AddPeer().WithPeerAddress(dutDst.IPv4).WithLocalASN(ateAS2). - WithTypeExternal() - - bgpNeti1 := iDut1.AddNetwork("bgpNeti1") - bgpNeti1.IPv4().WithAddress(advertisedRoutesv4CIDR).WithCount(routeCount) - bgpNeti1.BGP().WithNextHopAddress(ateSrc.IPv4) - - if asSEQMode { - bgpNeti1.BGP().AddASPathSegment(asSeg...).WithTypeSEQ() - } else { - // TODO : SET mode is not working - // https://github.com/openconfig/featureprofiles/issues/1659 - bgpNeti1.BGP().AddASPathSegment(asSeg...).WithTypeSET() - } - - t.Logf("Pushing config to ATE and starting protocols...") - topo.Push(t) - topo.StartProtocols(t) - - return topo -} - -type bgpNeighbor struct { - as uint32 - neighborip string - isV4 bool - peerGrp string -} - -// verifyBGPAsPath is to Validate AS Path attribute using bgp rib telemetry on ATE. -func verifyBGPAsPath(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, asSeg []uint32, removeASPath bool) { - at := gnmi.OC() - rib := at.NetworkInstance(ateDst.Name).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "0").Bgp().Rib() - prefixPath := rib.AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Ipv4Unicast(). - NeighborAny().AdjRibInPre().RouteAny().WithPathId(0).Prefix() - - gnmi.WatchAll(t, ate, prefixPath.State(), time.Minute, func(v *ygnmi.Value[string]) bool { - _, present := v.Val() - return present - }).Await(t) - - var wantASSeg = []uint32{dutAS, ateAS1} - - if removeASPath { - for _, as := range asSeg { - if as < 64512 { - wantASSeg = append(wantASSeg, as) - } - } - } else { - wantASSeg = append(wantASSeg, asSeg...) - } - - gotASSeg, ok := gnmi.WatchAll(t, ate, rib.AttrSetAny().AsSegmentMap().State(), 1*time.Minute, func(v *ygnmi.Value[map[uint32]*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet_AsSegment]) bool { - val, present := v.Val() - if present { - for _, as := range val { - if cmp.Equal(as.Member, wantASSeg) { - return true - } - } - } - return false - }).Await(t) - if !ok { - t.Errorf("Obtained AS path on ATE is not as expected, gotASSeg %v, wantASSeg %v", gotASSeg, wantASSeg) - } -} - -// configreRoutePolicy adds route-policy config -func configureRoutePolicy(t *testing.T, dut *ondatra.DUTDevice, name string, pr oc.E_RoutingPolicy_PolicyResultType) { - d := &oc.Root{} - rp := d.GetOrCreateRoutingPolicy() - pd := rp.GetOrCreatePolicyDefinition(name) - st, err := pd.AppendNewStatement("id-1") - if err != nil { - t.Fatal(err) - } - stc := st.GetOrCreateConditions() - stc.InstallProtocolEq = oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP - st.GetOrCreateActions().PolicyResult = pr - gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) -} - -// TestRemovePrivateAS is to Validate that private AS numbers are stripped -// before advertisement to the eBGP neighbor. -func TestRemovePrivateAS(t *testing.T) { - t.Logf("Start DUT config load.") - dut := ondatra.DUT(t, "dut") - - t.Run("Configure DUT interfaces", func(t *testing.T) { - t.Logf("Start DUT interface Config.") - configureDUT(t, dut) - }) - - t.Run("Configure DEFAULT network instance.", func(t *testing.T) { - t.Log("Configure Network Instance type to DEFAULT.") - dutConfNIPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)) - gnmi.Replace(t, dut, dutConfNIPath.Type().Config(), oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_DEFAULT_INSTANCE) - }) - - dutConfPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") - t.Run("Configure BGP Neighbors.", func(t *testing.T) { - t.Logf("Start DUT BGP Config.") - gnmi.Delete(t, dut, dutConfPath.Config()) - configureRoutePolicy(t, dut, policyName, oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) - dutConf := bgpCreateNbr(dutAS, ateAS1, dut) - gnmi.Replace(t, dut, dutConfPath.Config(), dutConf) - fptest.LogQuery(t, "DUT BGP Config", dutConfPath.Config(), gnmi.Get(t, dut, dutConfPath.Config())) - }) - - cases := []struct { - desc string - asSeg []uint32 - asSEQMode bool - }{{ - desc: "AS Path SEQ - 65501, 65507, 65534", - asSeg: []uint32{65501, 65507, 65534}, - asSEQMode: true, - }, { - desc: "AS Path SEQ - 65501, 600", - asSeg: []uint32{65501, 600}, - asSEQMode: true, - }, { - desc: "AS Path SEQ - 800, 65501, 600", - asSeg: []uint32{800, 65501, 600}, - asSEQMode: true, - }} - - for _, tc := range cases { - t.Run(tc.desc, func(t *testing.T) { - t.Logf("Start ATE Config.") - ate := ondatra.ATE(t, "ate") - topo := configureATE(t, ate, tc.asSeg, tc.asSEQMode) - - t.Log("Verifying port status.") - verifyPortsUp(t, dut.Device) - - t.Log("Check BGP parameters.") - verifyBGPTelemetry(t, dut) - - t.Log("Verify BGP prefix telemetry.") - verifyPrefixesTelemetry(t, dut, ateSrc.IPv4, routeCount, 0) - verifyPrefixesTelemetry(t, dut, ateDst.IPv4, 0, routeCount) - - t.Log("Verify AS Path list received at ate Port2 including private AS number.") - verifyBGPAsPath(t, dut, ate, tc.asSeg, !removeASPath) - - t.Log("Configure remove private AS on DUT.") - gnmi.Update(t, dut, dutConfPath.Bgp().PeerGroup(peerGrpName2).RemovePrivateAs().Config(), oc.Bgp_RemovePrivateAsOption_PRIVATE_AS_REMOVE_ALL) - - t.Log("Private AS numbers should be stripped off while advertising BGP routes into public AS.") - verifyBGPAsPath(t, dut, ate, tc.asSeg, removeASPath) - - topo.StopProtocols(t) - - t.Log("Remove remove-private-AS on DUT.") - gnmi.Delete(t, dut, dutConfPath.Bgp().PeerGroup(peerGrpName2).RemovePrivateAs().Config()) - }) - } -} diff --git a/feature/bgp/policybase/otg_tests/link_bandwidth_test/README.md b/feature/experimental/bgp/otg_tests/link_bandwidth_test/README.md similarity index 88% rename from feature/bgp/policybase/otg_tests/link_bandwidth_test/README.md rename to feature/experimental/bgp/otg_tests/link_bandwidth_test/README.md index 813e1e65b40..22ad233b580 100644 --- a/feature/bgp/policybase/otg_tests/link_bandwidth_test/README.md +++ b/feature/experimental/bgp/otg_tests/link_bandwidth_test/README.md @@ -1,9 +1,9 @@ -# RT-7.5: BGP Policy - Set Link Bandwidth Community +# RT-7.5: BGP Policy - Match and Set Link Bandwidth Community ## Summary -Configure bgp policy to add statically configured BGP link bandwidth -communities to routes based on a prefix match. +Configure bgp policy to match, add and delete statically configured BGP link +bandwidth communities to routes based on a prefix match. ## Testbed type @@ -13,8 +13,8 @@ communities to routes based on a prefix match. * Testbed configuration - Setup external BGP sessions and prefixes. * Generate config for 2 DUT and ATE ports where: - * DUT port 1 to ATE port 1. - * DUT port 2 to ATE port 2. + * DUT port 1 to ATE port 1 EBGP session. + * DUT port 2 to ATE port 2 IBGP session. * Configure dummy accept policies and attach it to both sessions on DUT. * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` named 'allow_all' with the following `statements` @@ -27,7 +27,8 @@ communities to routes based on a prefix match. * prefix-set-1 with 2 ipv4 and 2 ipv6 routes without communities. * prefix-set-2 with 2 ipv4 and 2 ipv6 routes with communities `[ "100:100" ]`. * prefix-set-3 with 2 ipv4 and 2 ipv6 routes with extended communities `[ "link-bandwidth:100:0" ]`. - + * Configure Send community knob to IBGP neigbour to advertise the communities to IBGP peer + * use `/network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/config/send-community`. * RT-7.5.1 - Validate bgp sessions and traffic * For IPv4 and IPv6 prefixes: * Observe received prefixes at ATE port-2. @@ -44,7 +45,7 @@ communities to routes based on a prefix match. * ext-community-member = [ "link-bandwidth:100:1M" ] * Create an ext-community-set named 'linkbw_2G' with members as follows: * ext-community-member = [ "link-bandwidth:100:2G" ] - * Create an community-set named 'regex_match_as100' with members as follows: + * Create an community-set named 'regex_match_comm100' with members as follows: * community-member = [ "^100:.*$" ] * Create an ext-community-set named 'linkbw_any' with members as follows: * ext-community-member = [ "^link-bandwidth:.*:.*$" ] @@ -64,7 +65,7 @@ communities to routes based on a prefix match. * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` named **'not_match_100_set_linkbw_1M'** with the following `statements` * statement[name='1-megabit-match']/ - * conditions/bgp-conditions/match-community-set/config/community-set = 'regex_match_as100' + * conditions/bgp-conditions/match-community-set/config/community-set = 'regex_match_comm100' * conditions/bgp-conditions/match-community-set/config/match-set-options = INVERT * actions/bgp-actions/set-ext-community/reference/config/ext-community-set-refs = 'linkbw_1M' * actions/config/policy-result = NEXT_STATEMENT @@ -74,7 +75,7 @@ communities to routes based on a prefix match. * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` named 'match_100_set_linkbw_2G' with the following `statements` * statement[name='2-gigabit-match']/ - * conditions/bgp-conditions/match-community-set/config/community-set = 'regex_match_as100' + * conditions/bgp-conditions/match-community-set/config/community-set = 'regex_match_comm100' * conditions/bgp-conditions/match-community-set/config/match-set-options = ANY * actions/bgp-actions/set-ext-community/reference/config/ext-community-set-refs = 'linkbw_2G' * actions/config/policy-result = NEXT_STATEMENT @@ -115,7 +116,7 @@ communities to routes based on a prefix match. * `/network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv6-unicast/neighbors/neighbor/adj-rib-in-post/routes/route/state/ext-community-index` * Expected community values for each policy - | | zero_linkbw | not_match_100_set_linkbw_1M | + | | set_linkbw_0 | not_match_100_set_linkbw_1M | | ------------ | -------------------------------------- | --------------------------- | | prefix-set-1 | [ "link-bandwidth:100:0" ] | [none] | | prefix-set-2 | [ "100:100", "link-bandwidth:100:0" ] | [ "100:100" ] | @@ -144,6 +145,10 @@ communities to routes based on a prefix match. ## Config Parameter Coverage +## Configuration to enable advertise communities to bgp peer + +* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/config/send-community + ### Policy for community-set configuration * /routing-policy/defined-sets/bgp-defined-sets/ext-community-sets/ext-community-set/config/ext-community-set-name @@ -153,11 +158,16 @@ communities to routes based on a prefix match. * /routing-policy/policy-definitions/policy-definition/config/name * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name +* /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result +* /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-ext-community/config/options +* /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-community/config/method * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-ext-community/reference/config/ext-community-set-refs +* /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/config/set-local-pref + ### Policy for community-set match configuration -* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/config/community-set +* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/match-ext-community-set/config/community-set * /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/match-ext-community-set/config/match-set-options ### Policy attachment point configuration diff --git a/feature/experimental/bgp/otg_tests/link_bandwidth_test/link_bandwidth_test.go b/feature/experimental/bgp/otg_tests/link_bandwidth_test/link_bandwidth_test.go new file mode 100644 index 00000000000..a45aaefada9 --- /dev/null +++ b/feature/experimental/bgp/otg_tests/link_bandwidth_test/link_bandwidth_test.go @@ -0,0 +1,829 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package link_bandwidth_test + +import ( + "testing" + "time" + + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/otgutils" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + otgtelemetry "github.com/openconfig/ondatra/gnmi/otg" + "github.com/openconfig/ygnmi/ygnmi" + "github.com/openconfig/ygot/ygot" +) + +const ( + ipv4PrefixLen = 30 + ipv6PrefixLen = 126 + v41Route = "203.0.113.0" + v41TrafficStart = "203.0.113.1" + v42Route = "203.0.114.0" + v42TrafficStart = "203.0.114.1" + v43Route = "203.0.115.0" + v43TrafficStart = "203.0.115.1" + v4RoutePrefix = uint32(24) + v61Route = "2001:db8:128:128::0" + v61TrafficStart = "2001:db8:128:128::1" + v62Route = "2001:db8:128:129::0" + v62TrafficStart = "2001:db8:128:129::1" + v63Route = "2001:db8:128:130::0" + v63TrafficStart = "2001:db8:128:130::1" + v6RoutePrefix = uint32(64) + dutAS = uint32(65656) + ateAS = uint32(65657) + bgpName = "BGP" + maskLenExact = "exact" + localPref = 200 + v4Flow = "flow-v4" + v6Flow = "flow-v6" +) + +var ( + dutPort1 = attrs.Attributes{ + Desc: "dutPort1", + IPv4: "192.0.2.1", + IPv4Len: ipv4PrefixLen, + IPv6: "2001:db8::192:0:2:1", + IPv6Len: ipv6PrefixLen, + } + + atePort1 = attrs.Attributes{ + Name: "atePort1", + MAC: "02:00:01:01:01:01", + IPv4: "192.0.2.2", + IPv4Len: ipv4PrefixLen, + IPv6: "2001:db8::192:0:2:2", + IPv6Len: ipv6PrefixLen, + } + + dutPort2 = attrs.Attributes{ + Desc: "dutPort2", + IPv4: "192.0.2.5", + IPv4Len: ipv4PrefixLen, + IPv6: "2001:db8::192:0:2:5", + IPv6Len: ipv6PrefixLen, + } + + atePort2 = attrs.Attributes{ + Name: "atePort2", + MAC: "02:00:01:01:01:02", + IPv4: "192.0.2.6", + IPv4Len: ipv4PrefixLen, + IPv6: "2001:db8::192:0:2:6", + IPv6Len: ipv6PrefixLen, + } + + advertisedIPv41 = ipAddr{address: v41Route, prefix: v4RoutePrefix} + advertisedIPv42 = ipAddr{address: v42Route, prefix: v4RoutePrefix} + advertisedIPv43 = ipAddr{address: v43Route, prefix: v4RoutePrefix} + advertisedIPv61 = ipAddr{address: v61Route, prefix: v6RoutePrefix} + advertisedIPv62 = ipAddr{address: v62Route, prefix: v6RoutePrefix} + advertisedIPv63 = ipAddr{address: v63Route, prefix: v6RoutePrefix} + extCommunitySet = map[string]string{ + "linkbw_0": "link-bandwidth:100:0", + "linkbw_1M": "link-bandwidth:100:1M", + "inkbw_2G": "link-bandwidth:100:2G", + "regex_match_comm100": "^100:.*$", + "linkbw_any": "^link-bandwidth:.*:.$", + "linkbw_any_0": "^link-bandwidth:.*:0$", + } +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +type ipAddr struct { + address string + prefix uint32 +} + +type testData struct { + dut *ondatra.DUTDevice + ate *ondatra.ATEDevice + top gosnappi.Config + otgP1 gosnappi.Device + otgP2 gosnappi.Device +} + +type extCommunity struct { + prefixSet1Comm string + prefixSet2Comm string + prefixSet3Comm string +} + +type flowConfig struct { + src attrs.Attributes + dstNw string + dstIP string +} + +func TestBGPLinkBandwidth(t *testing.T) { + dut := ondatra.DUT(t, "dut") + configureDUT(t, dut) + ate := ondatra.ATE(t, "ate") + top := gosnappi.NewConfig() + devs := configureOTG(t, ate, top) + td := testData{ + dut: dut, + ate: ate, + top: top, + otgP1: devs[0], + otgP2: devs[1], + } + type testCase struct { + name string + policyName string + applyPolicy func(t *testing.T, dut *ondatra.DUTDevice, policyName string) + validate func(t *testing.T, dut *ondatra.DUTDevice, policyName string) + routeCommunity extCommunity + localPerf bool + validateRouteCommunityV4 func(t *testing.T, td testData, ec extCommunity, localPerf bool) + validateRouteCommunityV6 func(t *testing.T, td testData, ec extCommunity, localPerf bool) + } + baseSetupConfigAndVerification(t, td) + configureExtCommunityRoutingPolicy(t, dut) + testCases := []testCase{ + { + name: "Policy set link bandwidth 0", + policyName: "set_linkbw_0", + applyPolicy: applyPolicyDut, + validate: validatPolicyDut, + routeCommunity: extCommunity{prefixSet1Comm: "link-bandwidth:100:0", prefixSet2Comm: "link-bandwidth:100:0", prefixSet3Comm: "link-bandwidth:100:0"}, + localPerf: false, + validateRouteCommunityV4: validateRouteCommunityV4, + validateRouteCommunityV6: validateRouteCommunityV6, + }, + { + name: "Policy set not_match_100_set_linkbw_1M", + policyName: "not_match_100_set_linkbw_1M", + applyPolicy: applyPolicyDut, + validate: validatPolicyDut, + routeCommunity: extCommunity{prefixSet1Comm: "none", prefixSet2Comm: "100:100", prefixSet3Comm: "link-bandwidth:100:0"}, + localPerf: false, + validateRouteCommunityV4: validateRouteCommunityV4, + validateRouteCommunityV6: validateRouteCommunityV6, + }, + { + name: "Policy set match_100_set_linkbw_2G", + policyName: "match_100_set_linkbw_2G", + applyPolicy: applyPolicyDut, + validate: validatPolicyDut, + routeCommunity: extCommunity{prefixSet1Comm: "none", prefixSet2Comm: "link-bandwidth:100:2000000000", prefixSet3Comm: "link-bandwidth:100:0"}, + localPerf: false, + validateRouteCommunityV4: validateRouteCommunityV4, + validateRouteCommunityV6: validateRouteCommunityV6, + }, + { + name: "Policy set del_linkbw", + policyName: "del_linkbw", + applyPolicy: applyPolicyDut, + validate: validatPolicyDut, + routeCommunity: extCommunity{prefixSet1Comm: "none", prefixSet2Comm: "100:100", prefixSet3Comm: "none"}, + localPerf: false, + validateRouteCommunityV4: validateRouteCommunityV4, + validateRouteCommunityV6: validateRouteCommunityV6, + }, + { + name: "Policy set rm_any_zero_bw_set_LocPref_5", + policyName: "match_linkbw_0_remove_and_set_localpref_5", + applyPolicy: applyPolicyDut, + validate: validatPolicyDut, + routeCommunity: extCommunity{prefixSet1Comm: "none", prefixSet2Comm: "100:100", prefixSet3Comm: "none"}, + localPerf: true, + validateRouteCommunityV4: validateRouteCommunityV4, + validateRouteCommunityV6: validateRouteCommunityV6, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Logf("Description: %s", tc.name) + tc.applyPolicy(t, dut, tc.policyName) + tc.validate(t, dut, tc.policyName) + tc.validateRouteCommunityV4(t, td, tc.routeCommunity, tc.localPerf) + tc.validateRouteCommunityV6(t, td, tc.routeCommunity, tc.localPerf) + }) + } +} + +func applyPolicyDut(t *testing.T, dut *ondatra.DUTDevice, policyName string) { + // Apply ipv4 policy to bgp neighbour. + root := &oc.Root{} + dni := deviations.DefaultNetworkInstance(dut) + path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName). + Bgp().Neighbor(atePort1.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() + + policy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName). + GetOrCreateBgp().GetOrCreateNeighbor(atePort1.IPv4).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateApplyPolicy() + policy.SetImportPolicy([]string{policyName}) + gnmi.Replace(t, dut, path.Config(), policy) + + // Apply ipv6 policy to bgp neighbour. + path = gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(atePort1.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy() + policy = root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(atePort1.IPv6).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateApplyPolicy() + policy.SetImportPolicy([]string{policyName}) + gnmi.Replace(t, dut, path.Config(), policy) +} + +func validatPolicyDut(t *testing.T, dut *ondatra.DUTDevice, policyName string) { + dni := deviations.DefaultNetworkInstance(dut) + path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(atePort1.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() + policy := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Neighbor_AfiSafi_ApplyPolicy](t, dut, path.State()) + importPolicies := policy.GetImportPolicy() + if len(importPolicies) != 1 { + t.Fatalf("ImportPolicy Ipv4 got= %v, want %v", importPolicies, []string{policyName}) + } +} + +func validateRouteCommunityV4(t *testing.T, td testData, ec extCommunity, localPerf bool) { + prefixes := map[string]string{ + v41Route: ec.prefixSet1Comm, + v42Route: ec.prefixSet2Comm, + v43Route: ec.prefixSet3Comm, + } + for prefix, community := range prefixes { + validateRouteCommunityV4Prefix(t, td, community, prefix, localPerf) + } +} + +func validateRouteCommunityV4Prefix(t *testing.T, td testData, community, v4Prefix string, localPerf bool) { + _, ok := gnmi.WatchAll(t, + td.ate.OTG(), + gnmi.OTG().BgpPeer(td.otgP2.Name()+".BGP4.peer").UnicastIpv4PrefixAny().State(), + time.Minute, + func(v *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv4Prefix]) bool { + _, present := v.Val() + return present + }).Await(t) + if ok { + bgpPrefixes := gnmi.GetAll(t, td.ate.OTG(), gnmi.OTG().BgpPeer(td.otgP2.Name()+".BGP4.peer").UnicastIpv4PrefixAny().State()) + t.Logf("bgp prefix:%v", bgpPrefixes) + for _, bgpPrefix := range bgpPrefixes { + if bgpPrefix.GetAddress() == v4Prefix { + t.Logf("Prefix recevied on OTG is correct, got Address %s, want prefix %v", bgpPrefix.GetAddress(), v4Prefix) + switch community { + case "none": + if len(bgpPrefix.Community) != 0 { + t.Fatalf("community is not empty it should be none") + } + case "100:100": + for _, gotCommunity := range bgpPrefix.Community { + t.Logf("community AS:%d val: %d", gotCommunity.GetCustomAsNumber(), gotCommunity.GetCustomAsValue()) + if gotCommunity.GetCustomAsNumber() != 100 && gotCommunity.GetCustomAsValue() != 100 { + t.Fatalf("community is not 100:100 got AS number:%d AS value:%d", gotCommunity.GetCustomAsNumber(), gotCommunity.GetCustomAsValue()) + } + } + default: + // TODO Verification as OTG not supported for Extended community/ + t.Logf("TODO: https://github.com/open-traffic-generator/snappi/issues/220 Verification as OTG not supported for Extended community") + if deviations.BgpExtendedCommunityIndexUnsupported(td.dut) { + verifyExtCommunityIndexV4(t, td, v4Prefix) + } + } + } + } + } +} + +func validateRouteCommunityV6(t *testing.T, td testData, ec extCommunity, localPerf bool) { + prefixes := map[string]string{ + v61Route: ec.prefixSet1Comm, + v62Route: ec.prefixSet2Comm, + v63Route: ec.prefixSet3Comm, + } + for prefix, community := range prefixes { + validateRouteCommunityV6Prefix(t, td, community, prefix, localPerf) + } +} + +func validateRouteCommunityV6Prefix(t *testing.T, td testData, community, v6Prefix string, localPerf bool) { + + // This function to verify received route communities on ATE ports. + _, ok := gnmi.WatchAll(t, + td.ate.OTG(), + gnmi.OTG().BgpPeer(td.otgP2.Name()+".BGP6.peer").UnicastIpv6PrefixAny().State(), + time.Minute, + func(v *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv6Prefix]) bool { + _, present := v.Val() + return present + }).Await(t) + if ok { + bgpPrefixes := gnmi.GetAll(t, td.ate.OTG(), gnmi.OTG().BgpPeer(td.otgP2.Name()+".BGP6.peer").UnicastIpv6PrefixAny().State()) + for _, bgpPrefix := range bgpPrefixes { + if bgpPrefix.GetAddress() == v6Prefix { + t.Logf("Prefix recevied on OTG is correct, got prefix:%v , want prefix %v", bgpPrefix, v6Prefix) + switch community { + case "none": + if len(bgpPrefix.Community) != 0 { + t.Fatalf("community is not empty it should be none") + } + case "100:100": + for _, gotCommunity := range bgpPrefix.Community { + t.Logf("community AS:%d val: %d", gotCommunity.GetCustomAsNumber(), gotCommunity.GetCustomAsValue()) + if gotCommunity.GetCustomAsNumber() != 100 && gotCommunity.GetCustomAsValue() != 100 { + t.Fatalf("community is not 100:100 got AS number:%d AS value:%d", gotCommunity.GetCustomAsNumber(), gotCommunity.GetCustomAsValue()) + } + } + default: + + // TODO Verification as OTG not supported for Extended community. + t.Logf("TODO: https://github.com/open-traffic-generator/snappi/issues/220 Verification as OTG not supported for Extended community") + if deviations.BgpExtendedCommunityIndexUnsupported(td.dut) { + verifyExtCommunityIndexV6(t, td, v6Prefix) + } + } + } + } + } +} + +func configureImportRoutingPolicyAllowAll(t *testing.T, dut *ondatra.DUTDevice) { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + pdef1 := rp.GetOrCreatePolicyDefinition("allow-all") + stmt1, err := pdef1.AppendNewStatement("allow-all") + if err != nil { + t.Fatalf("AppendNewStatement failed: %v", err) + } + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + + // Apply ipv4 policy to bgp neighbour. + dni := deviations.DefaultNetworkInstance(dut) + path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(atePort1.IPv4). + AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() + + policy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName). + GetOrCreateBgp().GetOrCreateNeighbor(atePort1.IPv4).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateApplyPolicy() + policy.SetImportPolicy([]string{"allow-all"}) + gnmi.Replace(t, dut, path.Config(), policy) + + // Apply ipv6 policy to bgp neighbour. + path = gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp(). + Neighbor(atePort1.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy() + + policy = root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName). + GetOrCreateBgp().GetOrCreateNeighbor(atePort1.IPv6).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateApplyPolicy() + policy.SetImportPolicy([]string{"allow-all"}) + gnmi.Replace(t, dut, path.Config(), policy) +} + +func validateImportRoutingPolicyAllowAll(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { + dni := deviations.DefaultNetworkInstance(dut) + path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp(). + Neighbor(atePort1.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() + + policy := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Neighbor_AfiSafi_ApplyPolicy](t, dut, path.State()) + importPolicies := policy.GetImportPolicy() + if len(importPolicies) != 1 { + t.Fatalf("ImportPolicy Ipv4 = %v, want %v", importPolicies, []string{"allow-all"}) + } + bgpRIBPath := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Rib() + locRib := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AfiSafi_Ipv4Unicast_LocRib](t, dut, bgpRIBPath. + AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Ipv4Unicast().LocRib().State()) + + found := 0 + expected := map[string]bool{ + advertisedIPv41.address: true, + advertisedIPv42.address: true, + advertisedIPv43.address: true, + } + for route, prefix := range locRib.Route { + if expected[prefix.GetPrefix()] { + found++ + t.Logf("Found Route(prefix %s, origin: %v, pathid: %d) => %s", route.Prefix, route.Origin, route.PathId, prefix.GetPrefix()) + } + } + if found != len(expected) { + t.Errorf("Not all V4 routes found. expected:%d got:%d", len(expected), found) + } + + // Verify ipv6 policy. + pathV6 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(atePort1.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy() + policyV6 := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Neighbor_AfiSafi_ApplyPolicy](t, dut, pathV6.State()) + importPolicies = policyV6.GetImportPolicy() + if len(importPolicies) != 1 { + t.Errorf("ImportPolicy Ipv6 got= %v, want= %v", importPolicies, []string{"allow-all"}) + } + bgpRIBPathV6 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Rib() + locRibv6 := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AfiSafi_Ipv6Unicast_LocRib](t, dut, bgpRIBPathV6.AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Ipv6Unicast().LocRib().State()) + found = 0 + expectedV6 := map[string]bool{ + advertisedIPv61.address: true, + advertisedIPv62.address: true, + advertisedIPv63.address: true, + } + for route, prefix := range locRibv6.Route { + if expectedV6[prefix.GetPrefix()] { + found++ + t.Logf("Found Route(prefix %s, origin: %v, pathid: %d) => %s", route.Prefix, route.Origin, route.PathId, prefix.GetPrefix()) + } + } + if found != len(expectedV6) { + t.Errorf("Not all v6 Routes found expected:%d got:%d", len(expectedV6), found) + } +} + +func configureExtCommunityRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { + root := &oc.Root{} + for name, community := range extCommunitySet { + rp := root.GetOrCreateRoutingPolicy() + pdef := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets() + stmt, err := pdef.NewExtCommunitySet(name) + if err != nil { + t.Fatalf("NewExtCommunitySet failed: %v", err) + } + stmt.SetExtCommunityMember([]string{community}) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + } + + // Configure routing policy link bandwidth zero. + rpSetLinkBwZero := root.GetOrCreateRoutingPolicy() + pdef1 := rpSetLinkBwZero.GetOrCreatePolicyDefinition("set_linkbw_0") + pdef1Stmt1, err := pdef1.AppendNewStatement("zero_linkbw") + if err != nil { + t.Fatalf("AppendNewStatement zero_linkbw failed: %v", err) + } + ref := pdef1Stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetExtCommunity() + ref.GetOrCreateReference().SetExtCommunitySetRef("linkbw_0") + ref.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) + ref.SetMethod(oc.SetCommunity_Method_REFERENCE) + pdef1Stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_NEXT_STATEMENT) + pdef1Stmt2, err := pdef1.AppendNewStatement("accept_all_routes") + if err != nil { + t.Fatalf("AppendNewStatement accept_all_routes failed: %v", err) + } + pdef1Stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rpSetLinkBwZero) + + // Configure routing Policy not_match_100_set_linkbw_1M. + rpNotMatch := root.GetOrCreateRoutingPolicy() + pdef2 := rpNotMatch.GetOrCreatePolicyDefinition("not_match_100_set_linkbw_1M") + pdef2Stmt1, err := pdef2.AppendNewStatement("1-megabit-match") + if err != nil { + t.Fatalf("AppendNewStatement 1-megabit-match failed: %v", err) + } + ref = pdef2Stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetExtCommunity() + ref.GetOrCreateReference().SetExtCommunitySetRef("linkbw_1M") + ref.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) + ref1 := pdef2Stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetMatchExtCommunitySet() + ref1.SetExtCommunitySet("regex_match_comm100") + ref1.SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsType_INVERT) + pdef2Stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_NEXT_STATEMENT) + pdef2Stmt2, err := pdef2.AppendNewStatement("accept_all_routes") + if err != nil { + t.Fatalf("AppendNewStatement accept_all_routes failed: %v", err) + } + pdef2Stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rpNotMatch) + + // Configure routing policy match_100_set_linkbw_2G. + rpMatch := root.GetOrCreateRoutingPolicy() + pdef3 := rpMatch.GetOrCreatePolicyDefinition("match_100_set_linkbw_2G") + pdef3Stmt1, err := pdef3.AppendNewStatement("2-gigabit-match") + if err != nil { + t.Fatalf("AppendNewStatement match_100_set_linkbw_2G failed: %v", err) + } + ref = pdef3Stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetExtCommunity() + ref.GetOrCreateReference().SetExtCommunitySetRef("linkbw_2G") + ref.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) + ref1 = pdef3Stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetMatchExtCommunitySet() + ref1.SetExtCommunitySet("regex_match_comm100") + ref1.SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsType_ANY) + pdef3Stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_NEXT_STATEMENT) + pdef3Stmt2, err := pdef3.AppendNewStatement("accept_all_routes") + if err != nil { + t.Fatalf("AppendNewStatement accept_all_routes failed: %v", err) + } + pdef3Stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rpMatch) + + // Configure routing policy del_linkbw. + rpDelLinkbw := root.GetOrCreateRoutingPolicy() + pdef4 := rpDelLinkbw.GetOrCreatePolicyDefinition("del_linkbw") + pdef4Stmt1, err := pdef4.AppendNewStatement("del_linkbw") + if err != nil { + t.Fatalf("AppendNewStatement del_linkbw failed: %v", err) + } + ref = pdef4Stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetExtCommunity() + ref.GetOrCreateReference().SetExtCommunitySetRef("linkbw_any") + ref.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_REMOVE) + ref.SetMethod(oc.SetCommunity_Method_REFERENCE) + pdef4Stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_NEXT_STATEMENT) + pdef4Stmt2, err := pdef4.AppendNewStatement("accept_all_routes") + if err != nil { + t.Fatalf("AppendNewStatement accept_all_routes failed: %v", err) + } + pdef4Stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rpDelLinkbw) + + // Configure routing policy match_linkbw_0_remove_and_set_localpref_5. + rpMatchLB0Perf5 := root.GetOrCreateRoutingPolicy() + pdef5 := rpMatchLB0Perf5.GetOrCreatePolicyDefinition("match_linkbw_0_remove_and_set_localpref_5") + pdef5Stmt1, err := pdef5.AppendNewStatement("match_and_remove_linkbw_any_0") + if err != nil { + t.Fatalf("AppendNewStatement match_and_remove_linkbw_any_0 failed: %v", err) + } + ref = pdef5Stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetExtCommunity() + ref.GetOrCreateReference().SetExtCommunitySetRef("linkbw_any_0") + ref.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_REMOVE) + ref.SetMethod(oc.SetCommunity_Method_REFERENCE) + ref1 = pdef5Stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetMatchExtCommunitySet() + ref1.SetExtCommunitySet("linkbw_any_0") + pdef5Stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_NEXT_STATEMENT) + pdef5Stmt1.GetOrCreateActions().GetOrCreateBgpActions().SetLocalPref = ygot.Uint32(5) + pdef5Stmt2, err := pdef5.AppendNewStatement("accept_all_routes") + if err != nil { + t.Fatalf("AppendNewStatement accept_all_routes failed: %v", err) + } + pdef5Stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rpMatchLB0Perf5) +} + +func createFlow(t *testing.T, td testData, fc flowConfig) { + td.top.Flows().Clear() + v4Flow := td.top.Flows().Add().SetName(v4Flow) + v4Flow.Metrics().SetEnable(true) + v4Flow.TxRx().Device(). + SetTxNames([]string{fc.src.Name + ".IPv4"}). + SetRxNames([]string{fc.dstNw}) + v4Flow.Size().SetFixed(512) + v4Flow.Rate().SetPps(100) + v4Flow.Duration().Continuous() + e1 := v4Flow.Packet().Add().Ethernet() + e1.Src().SetValue(fc.src.MAC) + v4 := v4Flow.Packet().Add().Ipv4() + v4.Src().SetValue(fc.src.IPv4) + v4.Dst().Increment().SetStart(fc.dstIP).SetCount(1) + + td.ate.OTG().PushConfig(t, td.top) + td.ate.OTG().StartProtocols(t) + otgutils.WaitForARP(t, td.ate.OTG(), td.top, "IPv4") +} + +func createFlowV6(t *testing.T, td testData, fc flowConfig) { + td.top.Flows().Clear() + v6Flow := td.top.Flows().Add().SetName(v6Flow) + v6Flow.Metrics().SetEnable(true) + v6Flow.TxRx().Device(). + SetTxNames([]string{fc.src.Name + ".IPv6"}). + SetRxNames([]string{fc.dstNw}) + v6Flow.Size().SetFixed(512) + v6Flow.Rate().SetPps(100) + v6Flow.Duration().Continuous() + e1 := v6Flow.Packet().Add().Ethernet() + e1.Src().SetValue(fc.src.MAC) + v6 := v6Flow.Packet().Add().Ipv6() + v6.Src().SetValue(fc.src.IPv6) + v6.Dst().Increment().SetStart(fc.dstIP).SetCount(1) + td.ate.OTG().PushConfig(t, td.top) + td.ate.OTG().StartProtocols(t) + otgutils.WaitForARP(t, td.ate.OTG(), td.top, "IPv6") +} + +func checkTraffic(t *testing.T, td testData, flowName string) { + td.ate.OTG().StartTraffic(t) + time.Sleep(time.Second * 30) + td.ate.OTG().StopTraffic(t) + otgutils.LogFlowMetrics(t, td.ate.OTG(), td.top) + otgutils.LogPortMetrics(t, td.ate.OTG(), td.top) + recvMetric := gnmi.Get(t, td.ate.OTG(), gnmi.OTG().Flow(flowName).State()) + txPackets := recvMetric.GetCounters().GetOutPkts() + rxPackets := recvMetric.GetCounters().GetInPkts() + lostPackets := txPackets - rxPackets + lossPct := lostPackets * 100 / txPackets + + if lossPct > 1 { + t.Errorf("FAIL in checkTraffic - Got %v%% packet loss for %s ; expected < 1%%", lossPct, flowName) + } +} + +func (td *testData) advertiseRoutesWithEBGP(t *testing.T) { + t.Helper() + + root := &oc.Root{} + ni := root.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(td.dut)) + bgpP := ni.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName) + bgpP.SetEnabled(true) + bgp := bgpP.GetOrCreateBgp() + + g := bgp.GetOrCreateGlobal() + g.SetAs(dutAS) + g.SetRouterId(dutPort1.IPv4) + g.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) + g.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + + nV41 := bgp.GetOrCreateNeighbor(atePort1.IPv4) + nV41.SetPeerAs(ateAS) + nV41.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) + nV42 := bgp.GetOrCreateNeighbor(atePort2.IPv4) + nV42.SetPeerAs(dutAS) + nV42.SetSendCommunity(oc.Bgp_CommunityType_BOTH) + nV42.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) + nV61 := bgp.GetOrCreateNeighbor(atePort1.IPv6) + nV61.SetPeerAs(ateAS) + nV61.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + nV62 := bgp.GetOrCreateNeighbor(atePort2.IPv6) + nV62.SetPeerAs(dutAS) + nV62.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + gnmi.Update(t, td.dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(td.dut)).Config(), ni) + + // Configure eBGP on OTG port1. + ipv41 := td.otgP1.Ethernets().Items()[0].Ipv4Addresses().Items()[0] + dev1BGP := td.otgP1.Bgp().SetRouterId(atePort1.IPv4) + bgp4Peer1 := dev1BGP.Ipv4Interfaces().Add().SetIpv4Name(ipv41.Name()).Peers().Add().SetName(td.otgP1.Name() + ".BGP4.peer") + bgp4Peer1.SetPeerAddress(dutPort1.IPv4).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + ipv61 := td.otgP1.Ethernets().Items()[0].Ipv6Addresses().Items()[0] + bgp6Peer1 := dev1BGP.Ipv6Interfaces().Add().SetIpv6Name(ipv61.Name()).Peers().Add().SetName(td.otgP1.Name() + ".BGP6.peer") + bgp6Peer1.SetPeerAddress(dutPort1.IPv6).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + + // Configure emulated network on ATE port1. + netv41 := bgp4Peer1.V4Routes().Add().SetName("v4-bgpNet-dev1") + netv41.Addresses().Add().SetAddress(advertisedIPv41.address).SetPrefix(advertisedIPv41.prefix) + netv61 := bgp6Peer1.V6Routes().Add().SetName("v6-bgpNet-dev1") + netv61.Addresses().Add().SetAddress(advertisedIPv61.address).SetPrefix(advertisedIPv61.prefix) + + // Configure routes with BGP community. + netv42 := bgp4Peer1.V4Routes().Add().SetName("v4-bgpNet-dev2") + netv42.Addresses().Add().SetAddress(advertisedIPv42.address).SetPrefix(advertisedIPv42.prefix) + commv4 := netv42.Communities().Add() + commv4.SetType(gosnappi.BgpCommunityType.MANUAL_AS_NUMBER) + commv4.SetAsNumber(100) + commv4.SetAsCustom(100) + netv62 := bgp6Peer1.V6Routes().Add().SetName("v6-bgpNet-dev2") + netv62.Addresses().Add().SetAddress(advertisedIPv62.address).SetPrefix(advertisedIPv62.prefix) + commv6 := netv62.Communities().Add() + commv6.SetType(gosnappi.BgpCommunityType.MANUAL_AS_NUMBER) + commv6.SetAsNumber(100) + commv6.SetAsCustom(100) + + // Configure routes with Link bandwidth community. + netv43 := bgp4Peer1.V4Routes().Add().SetName("v4-bgpNet-dev3") + netv43.Addresses().Add().SetAddress(advertisedIPv43.address).SetPrefix(advertisedIPv43.prefix) + extcommv4 := netv43.ExtendedCommunities().Add().NonTransitive2OctetAsType().LinkBandwidthSubtype() + extcommv4.SetGlobal2ByteAs(100) + extcommv4.SetBandwidth(0) + netv63 := bgp6Peer1.V6Routes().Add().SetName("v6-bgpNet-dev3") + netv63.Addresses().Add().SetAddress(advertisedIPv63.address).SetPrefix(advertisedIPv63.prefix) + extcommv6 := netv63.ExtendedCommunities().Add().NonTransitive2OctetAsType().LinkBandwidthSubtype() + extcommv6.SetGlobal2ByteAs(100) + extcommv6.SetBandwidth(0) + + // Configure iBGP on OTG port2. + ipv42 := td.otgP2.Ethernets().Items()[0].Ipv4Addresses().Items()[0] + dev2BGP := td.otgP2.Bgp().SetRouterId(atePort2.IPv4) + bgp4Peer2 := dev2BGP.Ipv4Interfaces().Add().SetIpv4Name(ipv42.Name()).Peers().Add().SetName(td.otgP2.Name() + ".BGP4.peer") + bgp4Peer2.SetPeerAddress(dutPort2.IPv4).SetAsNumber(dutAS).SetAsType(gosnappi.BgpV4PeerAsType.IBGP) + bgp4Peer2.Capability().SetIpv4UnicastAddPath(true).SetIpv6UnicastAddPath(true) + bgp4Peer2.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) + ipv62 := td.otgP2.Ethernets().Items()[0].Ipv6Addresses().Items()[0] + bgp6Peer2 := dev2BGP.Ipv6Interfaces().Add().SetIpv6Name(ipv62.Name()).Peers().Add().SetName(td.otgP2.Name() + ".BGP6.peer") + bgp6Peer2.SetPeerAddress(dutPort2.IPv6).SetAsNumber(dutAS).SetAsType(gosnappi.BgpV6PeerAsType.IBGP) + bgp6Peer2.Capability().SetIpv4UnicastAddPath(true).SetIpv6UnicastAddPath(true).SetExtendedNextHopEncoding(true) + bgp6Peer2.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) +} + +func (td *testData) verifyDUTBGPEstablished(t *testing.T) { + sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(td.dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().NeighborAny().SessionState().State() + watch := gnmi.WatchAll(t, td.dut, sp, 2*time.Minute, func(val *ygnmi.Value[oc.E_Bgp_Neighbor_SessionState]) bool { + state, ok := val.Val() + if !ok || state != oc.Bgp_Neighbor_SessionState_ESTABLISHED { + return false + } + return true + }) + if val, ok := watch.Await(t); !ok { + t.Fatalf("BGP sessions not established in verifyDUTBGPEstablished : got %v", val) + } +} + +// verifyOTGBGPEstablished verifies on OTG BGP peer establishment. +func (td *testData) verifyOTGBGPEstablished(t *testing.T) { + sp := gnmi.OTG().BgpPeerAny().SessionState().State() + watch := gnmi.WatchAll(t, td.ate.OTG(), sp, 2*time.Minute, func(val *ygnmi.Value[otgtelemetry.E_BgpPeer_SessionState]) bool { + state, ok := val.Val() + if !ok || state != otgtelemetry.BgpPeer_SessionState_ESTABLISHED { + return false + } + return true + }) + state, ok := watch.Await(t) + if !ok { + t.Fatalf("BGP sessions not established : verifyOTGBGPEstablished (%v)", state) + } +} + +func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + p1 := dut.Port(t, "port1") + p2 := dut.Port(t, "port2") + b := &gnmi.SetBatch{} + gnmi.BatchReplace(b, gnmi.OC().Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), dut)) + gnmi.BatchReplace(b, gnmi.OC().Interface(p2.Name()).Config(), dutPort2.NewOCInterface(p2.Name(), dut)) + b.Set(t, dut) + if deviations.ExplicitPortSpeed(dut) { + fptest.SetPortSpeed(t, p1) + fptest.SetPortSpeed(t, p2) + } + + fptest.ConfigureDefaultNetworkInstance(t, dut) + + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + fptest.AssignToNetworkInstance(t, dut, p1.Name(), deviations.DefaultNetworkInstance(dut), 0) + fptest.AssignToNetworkInstance(t, dut, p2.Name(), deviations.DefaultNetworkInstance(dut), 0) + } +} + +func configureOTG(t *testing.T, ate *ondatra.ATEDevice, top gosnappi.Config) []gosnappi.Device { + t.Helper() + p1 := ate.Port(t, "port1") + p2 := ate.Port(t, "port2") + + d1 := atePort1.AddToOTG(top, p1, &dutPort1) + d2 := atePort2.AddToOTG(top, p2, &dutPort2) + return []gosnappi.Device{d1, d2} +} + +// TODO to move base setup config in helper. +func baseSetupConfigAndVerification(t *testing.T, td testData) { + td.advertiseRoutesWithEBGP(t) + td.ate.OTG().PushConfig(t, td.top) + td.ate.OTG().StartProtocols(t) + otgutils.WaitForARP(t, td.ate.OTG(), td.top, "IPv4") + otgutils.WaitForARP(t, td.ate.OTG(), td.top, "IPv6") + td.verifyDUTBGPEstablished(t) + td.verifyOTGBGPEstablished(t) + configureImportRoutingPolicyAllowAll(t, td.dut) + validateImportRoutingPolicyAllowAll(t, td.dut, td.ate) + createFlow(t, td, flowConfig{src: atePort2, dstNw: "v4-bgpNet-dev1", dstIP: v41TrafficStart}) + checkTraffic(t, td, v4Flow) + createFlowV6(t, td, flowConfig{src: atePort2, dstNw: "v6-bgpNet-dev1", dstIP: v61TrafficStart}) + checkTraffic(t, td, v6Flow) + createFlow(t, td, flowConfig{src: atePort2, dstNw: "v4-bgpNet-dev2", dstIP: v42TrafficStart}) + checkTraffic(t, td, v4Flow) + createFlowV6(t, td, flowConfig{src: atePort2, dstNw: "v6-bgpNet-dev2", dstIP: v62TrafficStart}) + checkTraffic(t, td, v6Flow) + createFlow(t, td, flowConfig{src: atePort2, dstNw: "v4-bgpNet-dev3", dstIP: v43TrafficStart}) + checkTraffic(t, td, v4Flow) + createFlowV6(t, td, flowConfig{src: atePort2, dstNw: "v6-bgpNet-dev3", dstIP: v63TrafficStart}) + checkTraffic(t, td, v6Flow) +} + +func verifyExtCommunityIndexV4(t *testing.T, td testData, v4Address string) { + dni := deviations.DefaultNetworkInstance(td.dut) + bgpRIBPath := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Rib() + locRib := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AfiSafi_Ipv4Unicast_LocRib](t, td.dut, bgpRIBPath.AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Ipv4Unicast().LocRib().State()) + t.Logf("RIB: %v", locRib) + for route, prefix := range locRib.Route { + if prefix.GetPrefix() != v4Address { + continue + } + t.Logf("Found Route(prefix %s, origin: %v, pathid: %d) => %s", route.Prefix, route.Origin, route.PathId, prefix.GetPrefix()) + if prefix.ExtCommunityIndex == nil { + t.Fatalf("No V4 community index found") + } + extCommunity := bgpRIBPath.ExtCommunity(prefix.GetExtCommunityIndex()).ExtCommunity().State() + if extCommunity == nil { + t.Fatalf("No V4 community found at given index: %v", prefix.GetExtCommunityIndex()) + } + } +} + +func verifyExtCommunityIndexV6(t *testing.T, td testData, v6Address string) { + dni := deviations.DefaultNetworkInstance(td.dut) + bgpRIBPathV6 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Rib() + locRibv6 := gnmi.Get[*oc.NetworkInstance_Protocol_Bgp_Rib_AfiSafi_Ipv6Unicast_LocRib](t, td.dut, bgpRIBPathV6.AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Ipv6Unicast().LocRib().State()) + t.Logf("RIB: %v", locRibv6) + for route, prefix := range locRibv6.Route { + if prefix.GetPrefix() != v6Address { + continue + } + t.Logf("Found Route(prefix %s, origin: %v, pathid: %d) => %s", route.Prefix, route.Origin, route.PathId, prefix.GetPrefix()) + if prefix.ExtCommunityIndex == nil { + t.Fatalf("No V6 community index found") + } + extCommunity := bgpRIBPathV6.ExtCommunity(prefix.GetExtCommunityIndex()).ExtCommunity().State() + if extCommunity == nil { + t.Fatalf("No V6 community found at given index: %v", prefix.GetExtCommunityIndex()) + } + } +} diff --git a/feature/experimental/bgp/otg_tests/link_bandwidth_test/metadata.textproto b/feature/experimental/bgp/otg_tests/link_bandwidth_test/metadata.textproto new file mode 100644 index 00000000000..5e5b04d3695 --- /dev/null +++ b/feature/experimental/bgp/otg_tests/link_bandwidth_test/metadata.textproto @@ -0,0 +1,25 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "a8344612-0db0-42a1-96cf-38846a7f1603" +plan_id: "RT-7.5" +description: "BGP Policy - Match and Set Link Bandwidth Community" +testbed: TESTBED_DUT_ATE_2LINKS +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + route_policy_under_afi_unsupported: true + omit_l2_mtu: true + missing_value_for_defaults: true + interface_enabled: true + default_network_instance: "default" + skip_set_rp_match_set_options: true + skip_setting_disable_metric_propagation: true + bgp_conditions_match_community_set_unsupported: true + bgp_extended_community_index_unsupported: true + } +} +tags: TAGS_AGGREGATION +tags: TAGS_DATACENTER_EDGE diff --git a/feature/experimental/gnmi_service/benchmarking_drained_configuration_convergence_time/feature.textproto b/feature/experimental/gnmi_service/benchmarking_drained_configuration_convergence_time/feature.textproto index 9893a032bf4..fc6bb224159 100644 --- a/feature/experimental/gnmi_service/benchmarking_drained_configuration_convergence_time/feature.textproto +++ b/feature/experimental/gnmi_service/benchmarking_drained_configuration_convergence_time/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "experimental_gnmi_service_benchmarking_drained_configuration_convergence_time" diff --git a/feature/experimental/gnmi_service/telemetry_port_speed/feature.textproto b/feature/experimental/gnmi_service/telemetry_port_speed/feature.textproto index 29b1af29b18..f7a6d7df109 100644 --- a/feature/experimental/gnmi_service/telemetry_port_speed/feature.textproto +++ b/feature/experimental/gnmi_service/telemetry_port_speed/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "experimental_gnmi_service_telemetry_port_speed" diff --git a/feature/experimental/gribi/otg_tests/backup_nhg_action/metadata.textproto b/feature/experimental/gribi/otg_tests/backup_nhg_action/metadata.textproto index 84a121a892a..81bfb47ea92 100644 --- a/feature/experimental/gribi/otg_tests/backup_nhg_action/metadata.textproto +++ b/feature/experimental/gribi/otg_tests/backup_nhg_action/metadata.textproto @@ -11,6 +11,7 @@ platform_exceptions: { } deviations: { ipv4_missing_enabled: true + interface_ref_interface_id_format: true } } platform_exceptions: { diff --git a/feature/experimental/gribi/otg_tests/backup_nhg_action_pbf/metadata.textproto b/feature/experimental/gribi/otg_tests/backup_nhg_action_pbf/metadata.textproto index 5e9b261eb3d..f7a83ce9600 100644 --- a/feature/experimental/gribi/otg_tests/backup_nhg_action_pbf/metadata.textproto +++ b/feature/experimental/gribi/otg_tests/backup_nhg_action_pbf/metadata.textproto @@ -1,4 +1,4 @@ -# proto-file: third_party/openconfig/featureprofiles/proto/metadata.proto +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto # proto-message: Metadata uuid: "e4ec3769-86eb-41c8-8600-f8835cdcf0a6" @@ -11,6 +11,9 @@ platform_exceptions: { } deviations: { ipv4_missing_enabled: true + interface_ref_interface_id_format: true + pf_require_match_default_rule: true + pf_require_sequential_order_pbr_rules: true } } platform_exceptions: { diff --git a/feature/experimental/gribi/otg_tests/dut_daemon_failure/dut_daemon_failure_test.go b/feature/experimental/gribi/otg_tests/dut_daemon_failure/dut_daemon_failure_test.go index 1207b3b0346..a07108c8c39 100644 --- a/feature/experimental/gribi/otg_tests/dut_daemon_failure/dut_daemon_failure_test.go +++ b/feature/experimental/gribi/otg_tests/dut_daemon_failure/dut_daemon_failure_test.go @@ -93,7 +93,7 @@ var ( ondatra.ARISTA: "Gribi", ondatra.CISCO: "emsd", ondatra.JUNIPER: "rpd", - ondatra.NOKIA: "sr_gribi_server", + ondatra.NOKIA: "sr_grpc_server", } ) diff --git a/feature/experimental/gribi/otg_tests/vrf_policy_driven_te/metadata.textproto b/feature/experimental/gribi/otg_tests/vrf_policy_driven_te/metadata.textproto index 74a4b27399c..72bd2337297 100644 --- a/feature/experimental/gribi/otg_tests/vrf_policy_driven_te/metadata.textproto +++ b/feature/experimental/gribi/otg_tests/vrf_policy_driven_te/metadata.textproto @@ -43,7 +43,9 @@ platform_exceptions: { gnoi_subcomponent_path: true deprecated_vlan_id: true interface_enabled: true + static_protocol_name: "STATIC" default_network_instance: "default" + gribi_mac_override_static_arp_static_route: true missing_isis_interface_afi_safi_enable: true isis_interface_afi_unsupported: true isis_instance_enabled_required: true diff --git a/feature/experimental/gribi/otg_tests/vrf_policy_driven_te/vrf_policy_driven_te_test.go b/feature/experimental/gribi/otg_tests/vrf_policy_driven_te/vrf_policy_driven_te_test.go index 2d5f1cff9e1..3553efafb3b 100644 --- a/feature/experimental/gribi/otg_tests/vrf_policy_driven_te/vrf_policy_driven_te_test.go +++ b/feature/experimental/gribi/otg_tests/vrf_policy_driven_te/vrf_policy_driven_te_test.go @@ -19,6 +19,7 @@ import ( "fmt" "log" "os" + "strconv" "testing" "time" @@ -71,6 +72,7 @@ const ( dscpEncapNoMatch = 30 ipv4OuterSrc111WithMask = "198.51.100.111/32" ipv4OuterSrc222WithMask = "198.51.100.222/32" + magicIp = "192.168.1.1" magicMac = "02:00:00:00:00:01" gribiIPv4EntryDefVRF1 = "192.0.2.101" gribiIPv4EntryDefVRF2 = "192.0.2.102" @@ -364,11 +366,6 @@ func configureVrfSelectionPolicyW(t *testing.T, dut *ondatra.DUTDevice) { gnmi.Replace(t, dut, dutPolFwdPath.Config(), niP) } -func deleteVrfSelectionPolicy(t *testing.T, dut *ondatra.DUTDevice) { - t.Helper() - gnmi.Delete(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).PolicyForwarding().Config()) -} - func configureVrfSelectionPolicyC(t *testing.T, dut *ondatra.DUTDevice) { t.Helper() d := &oc.Root{} @@ -471,6 +468,44 @@ func configureVrfSelectionPolicyC(t *testing.T, dut *ondatra.DUTDevice) { gnmi.Replace(t, dut, dutPolFwdPath.Config(), niP) } +// configStaticArp configures static arp entries +func configStaticArp(p string, ipv4addr string, macAddr string) *oc.Interface { + i := &oc.Interface{Name: ygot.String(p)} + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + s := i.GetOrCreateSubinterface(0) + s4 := s.GetOrCreateIpv4() + n4 := s4.GetOrCreateNeighbor(ipv4addr) + n4.LinkLayerAddress = ygot.String(macAddr) + return i +} + +func staticARPWithMagicUniversalIP(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + p2 := dut.Port(t, "port2") + p3 := dut.Port(t, "port3") + p4 := dut.Port(t, "port4") + p5 := dut.Port(t, "port5") + p6 := dut.Port(t, "port6") + p7 := dut.Port(t, "port7") + portList := []*ondatra.Port{p2, p3, p4, p5, p6, p7} + for idx, p := range portList { + s := &oc.NetworkInstance_Protocol_Static{ + Prefix: ygot.String(magicIp + "/32"), + NextHop: map[string]*oc.NetworkInstance_Protocol_Static_NextHop{ + strconv.Itoa(idx): { + Index: ygot.String(strconv.Itoa(idx)), + InterfaceRef: &oc.NetworkInstance_Protocol_Static_NextHop_InterfaceRef{ + Interface: ygot.String(p.Name()), + }, + }, + }, + } + sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(dut)) + gnmi.Update(t, dut, sp.Static(magicIp+"/32").Config(), s) + gnmi.Update(t, dut, gnmi.OC().Interface(p.Name()).Config(), configStaticArp(p.Name(), magicIp, magicMac)) + } +} + // configureNetworkInstance configures vrfs DECAP_TE_VRF,ENCAP_TE_VRF_A,ENCAP_TE_VRF_B, // TE_VRF_222, TE_VRF_111. func configNonDefaultNetworkInstance(t *testing.T, dut *ondatra.DUTDevice) { @@ -500,14 +535,17 @@ func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { portList := []*ondatra.Port{p1, p2, p3, p4, p5, p6, p7, p8} portNameList := []string{"port1", "port2", "port3", "port4", "port5", "port6", "port7", "port8"} - gnmi.Replace(t, dut, d.Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), dut)) - gnmi.Replace(t, dut, d.Interface(p2.Name()).Config(), dutPort2.NewOCInterface(p2.Name(), dut)) - gnmi.Replace(t, dut, d.Interface(p3.Name()).Config(), dutPort3.NewOCInterface(p3.Name(), dut)) - gnmi.Replace(t, dut, d.Interface(p4.Name()).Config(), dutPort4.NewOCInterface(p4.Name(), dut)) - gnmi.Replace(t, dut, d.Interface(p5.Name()).Config(), dutPort5.NewOCInterface(p5.Name(), dut)) - gnmi.Replace(t, dut, d.Interface(p6.Name()).Config(), dutPort6.NewOCInterface(p6.Name(), dut)) - gnmi.Replace(t, dut, d.Interface(p7.Name()).Config(), dutPort7.NewOCInterface(p7.Name(), dut)) - gnmi.Replace(t, dut, d.Interface(p8.Name()).Config(), dutPort8.NewOCInterface(p8.Name(), dut)) + for idx, a := range []attrs.Attributes{dutPort1, dutPort2, dutPort3, dutPort4, dutPort5, dutPort6, dutPort7, dutPort8} { + p := portList[idx] + intf := a.NewOCInterface(p.Name(), dut) + if p.PMD() == ondatra.PMD100GBASEFR { + e := intf.GetOrCreateEthernet() + e.AutoNegotiate = ygot.Bool(false) + e.DuplexMode = oc.Ethernet_DuplexMode_FULL + e.PortSpeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_100GB + } + gnmi.Replace(t, dut, d.Interface(p.Name()).Config(), intf) + } // Configure loopback interface. loopbackIntfName = netutil.LoopbackInterface(t, dut, 0) @@ -541,6 +579,9 @@ func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { fptest.SetPortSpeed(t, dut.Port(t, pName)) } } + if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) { + staticARPWithMagicUniversalIP(t, dut) + } } func configureISIS(t *testing.T, dut *ondatra.DUTDevice, intfList []string, dutAreaAddress, dutSysID string) { @@ -706,48 +747,99 @@ func verifyBgpTelemetry(t *testing.T, dut *ondatra.DUTDevice) { } } -func configGribiBaselineAFT(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, args *testArgs) { - t.Helper() - - // Programming AFT entries for prefixes in DEFAULT VRF +func programAftWithMagicIp(t *testing.T, dut *ondatra.DUTDevice, args *testArgs) { args.client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(11).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port2").Name()), + WithIndex(11).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port2").Name()). + WithIPAddress(magicIp), fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(12).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port3").Name()), + WithIndex(12).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port3").Name()). + WithIPAddress(magicIp), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(11).AddNextHop(11, 1).AddNextHop(12, 3), fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryDefVRF1+"/"+maskLen32).WithNextHopGroup(11), fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(13).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port4").Name()), + WithIndex(13).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port4").Name()). + WithIPAddress(magicIp), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(12).AddNextHop(13, 2), fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryDefVRF2+"/"+maskLen32).WithNextHopGroup(12), fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(14).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port5").Name()), + WithIndex(14).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port5").Name()). + WithIPAddress(magicIp), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(13).AddNextHop(14, 1), fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryDefVRF3+"/"+maskLen32).WithNextHopGroup(13), fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(15).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port6").Name()), + WithIndex(15).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port6").Name()). + WithIPAddress(magicIp), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(14).AddNextHop(15, 1), fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryDefVRF4+"/"+maskLen32).WithNextHopGroup(14), fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(16).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port7").Name()), + WithIndex(16).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port7").Name()). + WithIPAddress(magicIp), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(15).AddNextHop(16, 1), fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryDefVRF5+"/"+maskLen32).WithNextHopGroup(15), ) +} + +func configGribiBaselineAFT(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, args *testArgs) { + t.Helper() + + if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) { + programAftWithMagicIp(t, dut, args) + } else { + // Programming AFT entries for prefixes in DEFAULT VRF + args.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(11).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port2").Name()), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(12).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port3").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(11).AddNextHop(11, 1).AddNextHop(12, 3), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF1+"/"+maskLen32).WithNextHopGroup(11), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(13).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port4").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(12).AddNextHop(13, 2), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF2+"/"+maskLen32).WithNextHopGroup(12), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(14).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port5").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(13).AddNextHop(14, 1), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF3+"/"+maskLen32).WithNextHopGroup(13), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(15).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port6").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(14).AddNextHop(15, 1), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF4+"/"+maskLen32).WithNextHopGroup(14), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(16).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port7").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(15).AddNextHop(16, 1), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF5+"/"+maskLen32).WithNextHopGroup(15), + ) + } if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) } @@ -1072,6 +1164,21 @@ func configureOTG(t *testing.T, otg *otg.OTG, ate *ondatra.ATEDevice) gosnappi.C port7 := config.Ports().Add().SetName("port7") port8 := config.Ports().Add().SetName("port8") + pmd100GFRPorts := []string{} + for _, p := range config.Ports().Items() { + port := ate.Port(t, p.Name()) + if port.PMD() == ondatra.PMD100GBASEFR { + pmd100GFRPorts = append(pmd100GFRPorts, port.ID()) + } + } + // Disable FEC for 100G-FR ports because Novus does not support it. + if len(pmd100GFRPorts) > 0 { + l1Settings := config.Layer1().Add().SetName("L1").SetPortNames(pmd100GFRPorts) + l1Settings.SetAutoNegotiate(true).SetIeeeMediaDefaults(false).SetSpeed("speed_100_gbps") + autoNegotiate := l1Settings.AutoNegotiation() + autoNegotiate.SetRsFec(false) + } + iDut1Dev := config.Devices().Add().SetName(atePort1.Name) iDut1Eth := iDut1Dev.Ethernets().Add().SetName(atePort1.Name + ".Eth").SetMac(atePort1.MAC) iDut1Eth.Connection().SetPortName(port1.Name()) @@ -1963,7 +2070,6 @@ func TestGribiDecap(t *testing.T) { }) t.Log("Delete vrf selection policy W and Apply vrf selectioin policy C.") - deleteVrfSelectionPolicy(t, dut) configureVrfSelectionPolicyC(t, dut) t.Run("Test-4: Tunneled traffic with no decap", func(t *testing.T) { @@ -1971,7 +2077,6 @@ func TestGribiDecap(t *testing.T) { }) t.Log("Delete vrf selection policy C and Apply vrf selectioin policy W.") - deleteVrfSelectionPolicy(t, dut) configureVrfSelectionPolicyW(t, dut) t.Run("Test-5: Match on default term and send to default VRF", func(t *testing.T) { diff --git a/feature/experimental/hierarchical_gribi_entries/base_hierarchical_route_installation/feature.textproto b/feature/experimental/hierarchical_gribi_entries/base_hierarchical_route_installation/feature.textproto index 36675d5be9c..f4d10b21f31 100644 --- a/feature/experimental/hierarchical_gribi_entries/base_hierarchical_route_installation/feature.textproto +++ b/feature/experimental/hierarchical_gribi_entries/base_hierarchical_route_installation/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "experimental_hierarchical_gribi_entries_base_hierarchical_route_installation" diff --git a/feature/experimental/hierarchical_gribi_entries/traffic_balancing_according_to_weights/feature.textproto b/feature/experimental/hierarchical_gribi_entries/traffic_balancing_according_to_weights/feature.textproto index a18ae203662..2f8a1e24a06 100644 --- a/feature/experimental/hierarchical_gribi_entries/traffic_balancing_according_to_weights/feature.textproto +++ b/feature/experimental/hierarchical_gribi_entries/traffic_balancing_according_to_weights/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "experimental_hierarchical_gribi_entries_traffic_balancing_according_to_weights" diff --git a/feature/experimental/isis/otg_tests/isis_drain_test/isis_drain_test.go b/feature/experimental/isis/otg_tests/isis_drain_test/isis_drain_test.go index d2ec54af22e..13ba419a8da 100644 --- a/feature/experimental/isis/otg_tests/isis_drain_test/isis_drain_test.go +++ b/feature/experimental/isis/otg_tests/isis_drain_test/isis_drain_test.go @@ -252,7 +252,9 @@ func configureISISDUT(t *testing.T, dut *ondatra.DUTDevice, intfs []string) { isisLevel2 := isis.GetOrCreateLevel(2) isisLevel2.MetricStyle = oc.Isis_MetricStyle_WIDE_METRIC - + if deviations.ISISLevelEnabled(dut) { + isisLevel2.Enabled = ygot.Bool(true) + } for _, intfName := range intfs { isisIntf := isis.GetOrCreateInterface(intfName) isisIntf.GetOrCreateInterfaceRef().Interface = ygot.String(intfName) diff --git a/feature/experimental/isis/otg_tests/isis_drain_test/metadata.textproto b/feature/experimental/isis/otg_tests/isis_drain_test/metadata.textproto index 5f7e30cc941..e704ecde9cb 100644 --- a/feature/experimental/isis/otg_tests/isis_drain_test/metadata.textproto +++ b/feature/experimental/isis/otg_tests/isis_drain_test/metadata.textproto @@ -39,3 +39,11 @@ platform_exceptions: { isis_require_same_l1_metric_with_l2_metric: true } } +platform_exceptions: { + platform: { + vendor: JUNIPER + } + deviations: { + isis_level_enabled: true + } +} diff --git a/feature/experimental/lacp_and_base_interface/aggregate_interfaces/feature.textproto b/feature/experimental/lacp_and_base_interface/aggregate_interfaces/feature.textproto index a96add04cd9..b9f58a8ae68 100644 --- a/feature/experimental/lacp_and_base_interface/aggregate_interfaces/feature.textproto +++ b/feature/experimental/lacp_and_base_interface/aggregate_interfaces/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "experimental_lacp_and_base_interface_aggregate_interfaces" diff --git a/feature/experimental/policy/policy_base/feature.textproto b/feature/experimental/policy/policy_base/feature.textproto index e1e3a20a9e4..e0799b0980c 100644 --- a/feature/experimental/policy/policy_base/feature.textproto +++ b/feature/experimental/policy/policy_base/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "experimental_policy_policy_base" diff --git a/feature/experimental/policy/policy_vrf_selection/feature.textproto b/feature/experimental/policy/policy_vrf_selection/feature.textproto index 8a0ef73150f..a5262ef5612 100644 --- a/feature/experimental/policy/policy_vrf_selection/feature.textproto +++ b/feature/experimental/policy/policy_vrf_selection/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "experimental_policy_policy_vrf_selection" diff --git a/feature/experimental/route_redistribution/feature.textproto b/feature/experimental/route_redistribution/feature.textproto index b91b55c46c8..fa276b3ca6b 100644 --- a/feature/experimental/route_redistribution/feature.textproto +++ b/feature/experimental/route_redistribution/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "experimental_route_redistribution" diff --git a/feature/experimental/security/aaa/kne_tests/tls_authentication_over_grpc_test/tls_authentication_over_grpc_test.go b/feature/experimental/security/aaa/kne_tests/tls_authentication_over_grpc_test/tls_authentication_over_grpc_test.go index 82508df4950..8581b9ed14c 100644 --- a/feature/experimental/security/aaa/kne_tests/tls_authentication_over_grpc_test/tls_authentication_over_grpc_test.go +++ b/feature/experimental/security/aaa/kne_tests/tls_authentication_over_grpc_test/tls_authentication_over_grpc_test.go @@ -50,9 +50,8 @@ func keyboardInteraction(password string) ssh.KeyboardInteractiveChallenge { } } -func gnmiClient(ctx context.Context, dut *ondatra.DUTDevice, gnmiAddr string) (gpb.GNMIClient, error) { - conn, err := grpc.DialContext( - ctx, +func gnmiClient(dut *ondatra.DUTDevice, gnmiAddr string) (gpb.GNMIClient, error) { + conn, err := grpc.NewClient( gnmiAddr, grpc.WithTransportCredentials( credentials.NewTLS(&tls.Config{ @@ -60,7 +59,7 @@ func gnmiClient(ctx context.Context, dut *ondatra.DUTDevice, gnmiAddr string) (g })), ) if err != nil { - return nil, fmt.Errorf("grpc.DialContext => unexpected failure dialing GNMI (should not require auth): %w", err) + return nil, fmt.Errorf("grpc.NewClient => unexpected failure dialing GNMI (should not require auth): %w", err) } return gpb.NewGNMIClient(conn), nil } @@ -240,7 +239,7 @@ func TestAuthentication(t *testing.T) { context.Background(), "username", tc.user, "password", tc.pass) - gnmi, err := gnmiClient(ctx, dut, gnmiAddr) + gnmi, err := gnmiClient(dut, gnmiAddr) if err != nil { t.Fatal(err) } diff --git a/feature/experimental/system/gnmi/benchmarking/internal/setup/setup.go b/feature/experimental/system/gnmi/benchmarking/internal/setup/setup.go index cc2fa872227..1facbd3a15b 100644 --- a/feature/experimental/system/gnmi/benchmarking/internal/setup/setup.go +++ b/feature/experimental/system/gnmi/benchmarking/internal/setup/setup.go @@ -25,9 +25,11 @@ import ( "testing" "time" + "github.com/open-traffic-generator/snappi/gosnappi" "github.com/openconfig/featureprofiles/internal/attrs" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/otgutils" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" "github.com/openconfig/ondatra/gnmi/oc" @@ -40,7 +42,6 @@ const ( ISISInstance = "DEFAULT" // PeerGrpName is BGP peer group name. PeerGrpName = "BGP-PEER-GROUP" - // DUTAs is DUT AS. DUTAs = 64500 // ATEAs is ATE AS. @@ -329,6 +330,69 @@ func ConfigureATE(t *testing.T, ate *ondatra.ATEDevice) { topo.StartProtocols(t) } +// ConfigureOTG function is to configure otg ports with ipv4, bgp and isis peers. +func ConfigureOTG(t *testing.T, ate *ondatra.ATEDevice) { + otg := ate.OTG() + topo := gosnappi.NewConfig() + + for i, dp := range ate.Ports() { + + topo.Ports().Add().SetName(dp.ID()) + dev := topo.Devices().Add().SetName(dp.ID() + "dev") + eth := dev.Ethernets().Add().SetName(dp.ID() + ".Eth") + eth.Connection().SetPortName(dp.ID()) + mac := fmt.Sprintf("02:00:01:01:01:%02x", byte(i&0xff)) + + eth.SetMac(mac) + + ip := eth.Ipv4Addresses().Add().SetName(dev.Name() + ".IPv4") + ip.SetAddress(ATEIPList[dp.ID()].String()).SetGateway(DUTIPList[dp.ID()].String()).SetPrefix(uint32(plenIPv4)) + + // Add BGP on ATE + bgpDut1 := dev.Bgp().SetRouterId(ip.Address()) + bgpDut1Peer := bgpDut1.Ipv4Interfaces().Add().SetIpv4Name(ip.Name()).Peers().Add().SetName(dp.ID() + ".BGP4.peer") + if dp.ID() == "port1" { + bgpDut1Peer.SetPeerAddress(DUTIPList[dp.ID()].String()).SetAsNumber(ATEAs2).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + } else { + bgpDut1Peer.SetPeerAddress(DUTIPList[dp.ID()].String()).SetAsNumber(ATEAs).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + } + bgpDut1Peer.Capability().SetIpv4Unicast(true) + bgpDut1Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true) + + // Add ISIS on ATE + devIsis := dev.Isis().SetSystemId(strconv.FormatInt(int64(i), 16)).SetName("devIsis" + dp.Name()) + devIsis.Basic().SetHostname(devIsis.Name()).SetLearnedLspFilter(true) + devIsis.Advanced().SetAreaAddresses([]string{"490002"}) + devIsisInt := devIsis.Interfaces().Add(). + SetEthName(eth.Name()). + SetName("devIsisInt"). + SetNetworkType(gosnappi.IsisInterfaceNetworkType.POINT_TO_POINT). + SetLevelType(gosnappi.IsisInterfaceLevelType.LEVEL_2) + devIsisInt.Authentication().SetAuthType("md5") + devIsisInt.Authentication().SetMd5(authPassword) + devIsisInt.Advanced().SetAutoAdjustMtu(true).SetAutoAdjustArea(true).SetAutoAdjustSupportedProtocols(true) + + if dp.ID() == "port1" { + // Add BGP routes and ISIS routes , ate port1 is ingress port. + dstBgp4PeerRoutes := bgpDut1Peer.V4Routes().Add().SetName("bgpNeti1") + dstBgp4PeerRoutes.SetNextHopIpv4Address(ip.Address()). + SetNextHopAddressType(gosnappi.BgpV4RouteRangeNextHopAddressType.IPV4). + SetNextHopMode(gosnappi.BgpV4RouteRangeNextHopMode.MANUAL) + dstBgp4PeerRoutes.Addresses().Add(). + SetAddress(AdvertiseBGPRoutesv4).SetPrefix(32).SetCount(RouteCount) + devIsisRoutes := devIsis.V4Routes().Add().SetName("isisnet1").SetLinkMetric(20) + devIsisRoutes.Addresses().Add(). + SetAddress(advertiseISISRoutesv4).SetPrefix(32).SetCount(RouteCount).SetStep(1) + } + } + + t.Log("Pushing config to ATE...") + otg.PushConfig(t, topo) + t.Log("Starting protocols to ATE...") + otg.StartProtocols(t) + otgutils.WaitForARP(t, otg, topo, "IPv4") +} + // VerifyISISTelemetry function to used verify ISIS telemetry on DUT // using OC isis telemetry path. func VerifyISISTelemetry(t *testing.T, dut *ondatra.DUTDevice) { diff --git a/feature/experimental/system/gnmi/benchmarking/ate_tests/drained_configuration_convergence_time/README.md b/feature/experimental/system/gnmi/benchmarking/otg_tests/drained_configuration_convergence_time/README.md similarity index 100% rename from feature/experimental/system/gnmi/benchmarking/ate_tests/drained_configuration_convergence_time/README.md rename to feature/experimental/system/gnmi/benchmarking/otg_tests/drained_configuration_convergence_time/README.md diff --git a/feature/experimental/system/gnmi/benchmarking/ate_tests/drained_configuration_convergence_time/drained_configuration_convergence_time_bgp_test.go b/feature/experimental/system/gnmi/benchmarking/otg_tests/drained_configuration_convergence_time/drained_configuration_convergence_time_bgp_test.go similarity index 86% rename from feature/experimental/system/gnmi/benchmarking/ate_tests/drained_configuration_convergence_time/drained_configuration_convergence_time_bgp_test.go rename to feature/experimental/system/gnmi/benchmarking/otg_tests/drained_configuration_convergence_time/drained_configuration_convergence_time_bgp_test.go index 925e9149c55..3b12bc1d5e5 100644 --- a/feature/experimental/system/gnmi/benchmarking/ate_tests/drained_configuration_convergence_time/drained_configuration_convergence_time_bgp_test.go +++ b/feature/experimental/system/gnmi/benchmarking/otg_tests/drained_configuration_convergence_time/drained_configuration_convergence_time_bgp_test.go @@ -17,7 +17,6 @@ package drained_configuration_convergence_time_test import ( - "net" "testing" "time" @@ -28,6 +27,7 @@ import ( "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" "github.com/openconfig/ondatra/gnmi/oc" + otgtelemetry "github.com/openconfig/ondatra/gnmi/otg" "github.com/openconfig/ygnmi/ygnmi" "github.com/openconfig/ygot/ygot" ) @@ -178,7 +178,6 @@ func verifyBGPAsPath(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevic gnmi.Replace(t, dut, dutPolicyConfPath.Config(), []string{setASpathPrependPolicy}) } t.Run("BGP-AS-PATH Verification", func(t *testing.T) { - at := gnmi.OC() for _, ap := range ate.Ports() { if ap.ID() == "port1" { // port1 is ingress, skip verification on ingress port. @@ -188,26 +187,17 @@ func verifyBGPAsPath(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevic // Validate if all prefixes are received by ATE. isConverged(t, dut, ate, ap) - rib := at.NetworkInstance(ap.Name()).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "0").Bgp().Rib() - prefixPath := rib.AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Ipv4Unicast(). - NeighborAny().AdjRibInPre().RouteAny().WithPathId(0).Prefix() + prefixPath := gnmi.OTG().BgpPeer(ap.ID() + ".BGP4.peer").UnicastIpv4PrefixAny() - gnmi.WatchAll(t, ate, prefixPath.State(), time.Minute, func(v *ygnmi.Value[string]) bool { + gnmi.WatchAll(t, ate.OTG(), prefixPath.Address().State(), time.Minute, func(v *ygnmi.Value[string]) bool { _, present := v.Val() return present }).Await(t) singlepath := []uint32{setup.DUTAs, setup.DUTAs, setup.DUTAs, setup.DUTAs, setup.ATEAs2} - _, ok := gnmi.WatchAll(t, ate, rib.AttrSetAny().AsSegmentMap().State(), 5*time.Minute, func(v *ygnmi.Value[map[uint32]*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet_AsSegment]) bool { + _, ok := gnmi.WatchAll(t, ate.OTG(), prefixPath.AsPathAny().State(), 5*time.Minute, func(v *ygnmi.Value[*otgtelemetry.BgpPeer_UnicastIpv4Prefix_AsPath]) bool { val, present := v.Val() - if present { - for _, as := range val { - if cmp.Equal(as.Member, singlepath) { - return true - } - } - } - return false + return present && cmp.Diff(val.AsNumbers, singlepath) == "" }).Await(t) if !ok { t.Errorf("Obtained AS path on ATE is not as expected") @@ -223,12 +213,6 @@ func verifyBGPAsPath(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevic // verifyBGPSetMED is to Validate MED attribute using bgp rib telemetry on ATE. func verifyBGPSetMED(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { - // Build wantSetMED to compare the diff. - var wantSetMED []uint32 - for i := 0; i < setup.RouteCount; i++ { - wantSetMED = append(wantSetMED, bgpMED) - } - // Start the timer. start := time.Now() if deviations.RoutePolicyUnderAFIUnsupported(dut) { @@ -244,7 +228,7 @@ func verifyBGPSetMED(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevic } t.Run("BGP-MED-Verification", func(t *testing.T) { - at := gnmi.OC() + for _, ap := range ate.Ports() { if ap.ID() == "port1" { continue @@ -252,30 +236,14 @@ func verifyBGPSetMED(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevic // Validate if all prefixes are received by ATE. isConverged(t, dut, ate, ap) - rib := at.NetworkInstance(ap.Name()).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "0").Bgp().Rib() - routeP := rib.AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Ipv4Unicast(). - NeighborAny().AdjRibInPre().RouteAny().WithPathId(0) - routes := gnmi.GetAll(t, ate, routeP.State()) - attrs := gnmi.GetAll(t, ate, rib.AttrSetAny().State()) - mask := net.IPv4Mask(255, 255, 255, 0) - masked := net.ParseIP(setup.AdvertiseBGPRoutesv4).Mask(mask) - var gotSetMED []uint32 - var pref []string - for _, route := range routes { - ip, _, _ := net.ParseCIDR(route.GetPrefix()) - pref = append(pref, route.GetPrefix()) - if ip.Mask(mask).Equal(masked) { - idx := route.GetAttrIndex() - if idx >= uint64(len(attrs)) { - t.Errorf("Invalid attr-index %d for prefix: %s", idx, route.GetPrefix()) - continue - } - gotSetMED = append(gotSetMED, attrs[idx].GetMed()) + + bgpPrefixes := gnmi.GetAll(t, ate.OTG(), gnmi.OTG().BgpPeer(ap.ID()+".BGP4.peer").UnicastIpv4PrefixAny().State()) + for _, prefix := range bgpPrefixes { + if prefix.GetMultiExitDiscriminator() != bgpMED { + t.Errorf("Received Prefix Med %d Expected Med %d for Prefix %v", prefix.GetMultiExitDiscriminator(), bgpMED, prefix.GetAddress()) } } - if diff := cmp.Diff(wantSetMED, gotSetMED); diff != "" { - t.Errorf("obtained MED on ATE is not as expected, got %v, want %v, Prefixes %v", gotSetMED, wantSetMED, pref) - } + } }) // End the timer and calculate time taken to apply setMED. @@ -303,7 +271,7 @@ func TestEstablish(t *testing.T) { t.Log("Configure ATE with Interfaces, BGP, ISIS configs.") ate := ondatra.ATE(t, "ate") - setup.ConfigureATE(t, ate) + setup.ConfigureOTG(t, ate) t.Log("Verify BGP Session state , should be in ESTABLISHED State.") setup.VerifyBgpTelemetry(t, dut) diff --git a/feature/experimental/system/gnmi/benchmarking/ate_tests/drained_configuration_convergence_time/drained_configuration_convergence_time_isis_test.go b/feature/experimental/system/gnmi/benchmarking/otg_tests/drained_configuration_convergence_time/drained_configuration_convergence_time_isis_test.go similarity index 73% rename from feature/experimental/system/gnmi/benchmarking/ate_tests/drained_configuration_convergence_time/drained_configuration_convergence_time_isis_test.go rename to feature/experimental/system/gnmi/benchmarking/otg_tests/drained_configuration_convergence_time/drained_configuration_convergence_time_isis_test.go index e14f163c63f..e8bf2d5cfd2 100644 --- a/feature/experimental/system/gnmi/benchmarking/ate_tests/drained_configuration_convergence_time/drained_configuration_convergence_time_isis_test.go +++ b/feature/experimental/system/gnmi/benchmarking/otg_tests/drained_configuration_convergence_time/drained_configuration_convergence_time_isis_test.go @@ -25,6 +25,7 @@ import ( "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" "github.com/openconfig/ondatra/gnmi/oc" + otgtelemetry "github.com/openconfig/ondatra/gnmi/otg" "github.com/openconfig/ygnmi/ygnmi" ) @@ -65,27 +66,25 @@ func setISISMetric(t *testing.T, dut *ondatra.DUTDevice) { func verifyISISMetric(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { t.Run("ISIS Metric verification", func(t *testing.T) { - at := gnmi.OC() for _, ap := range ate.Ports() { if ap.ID() == "port1" { // Port1 is ingress, skip verification on ingress port continue } - const want = oc.Interface_OperStatus_UP - - if got := gnmi.Get(t, ate, at.Interface(ap.Name()).OperStatus().State()); got != want { - t.Errorf("%s oper-status got %v, want %v", ap, got, want) - } - is := at.NetworkInstance(ap.Name()).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, "0").Isis() - lsps := is.LevelAny().LspAny() - - _, ok := gnmi.WatchAll(t, ate, lsps.Tlv(oc.IsisLsdbTypes_ISIS_TLV_TYPE_EXTENDED_IPV4_REACHABILITY).ExtendedIpv4Reachability().PrefixAny().Metric().State(), 5*time.Minute, func(v *ygnmi.Value[uint32]) bool { - val, present := v.Val() - return present && val == setup.ISISMetric + got, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis"+ap.Name()).LinkStateDatabase().LspsAny().Tlvs().ExtendedIpv4Reachability().PrefixAny().Metric().State(), time.Minute, func(v *ygnmi.Value[uint32]) bool { + metric, present := v.Val() + if present { + if metric == setup.ISISMetric { + return true + } + } + return false }).Await(t) + + metricInReceivedLsp, _ := got.Val() if !ok { - t.Errorf("Obtained Metric on ATE is not as expected") + t.Fatalf("Metric not matched. Expected %d got %d ", setup.ISISMetric, metricInReceivedLsp) } } }) @@ -96,28 +95,28 @@ func verifyISISMetric(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevi func verifyISISOverloadBit(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { t.Run("ISIS Overload bit verification", func(t *testing.T) { - at := gnmi.OC() for _, ap := range ate.Ports() { if ap.ID() == "port1" { // port1 is ingress, skip verification on ingress port continue } - const want = oc.Interface_OperStatus_UP + otg := ate.OTG() + _, ok := gnmi.WatchAll(t, otg, gnmi.OTG().IsisRouter("devIsis"+ap.Name()).LinkStateDatabase().LspsAny().Flags().State(), time.Minute, func(v *ygnmi.Value[[]otgtelemetry.E_Lsps_Flags]) bool { + flags, present := v.Val() + if present { + for _, flag := range flags { + if flag == otgtelemetry.Lsps_Flags_OVERLOAD { + return true + } + } + } + return false + }).Await(t) - if got := gnmi.Get(t, ate, at.Interface(ap.Name()).OperStatus().State()); got != want { - t.Errorf("%s oper-status got %v, want %v", ap, got, want) + if !ok { + t.Fatalf("OverLoad Bit not seen on learned lsp on ATE") } - // TODO: SetBit retrieval is not working in ATE. - // Ref: https://github.com/openconfig/featureprofiles/issues/1176 - // Below code will be uncommented once above issue is resolved. - - // is := at.NetworkInstance(ap.Name()).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, "0").Isis() - // lsps := is.LevelAny().LspAny() - // gotIsisSetBit := gnmi.GetAll(t, ate, lsps.Tlv(oc.IsisLsdbTypes_ISIS_TLV_TYPE_EXTENDED_IPV4_REACHABILITY).ExtendedIpv4Reachability().PrefixAny().SBit().State()) - // if diff := cmp.Diff(setup.ISISSetBitList, gotIsisSetBit); diff != "" { - // t.Errorf("obtained setBit on ATE is not as expected, got %v, want %v", gotIsisSetBit, setup.ISISSetBitList) - // } } }) } diff --git a/feature/experimental/system/gnmi/benchmarking/ate_tests/drained_configuration_convergence_time/metadata.textproto b/feature/experimental/system/gnmi/benchmarking/otg_tests/drained_configuration_convergence_time/metadata.textproto similarity index 100% rename from feature/experimental/system/gnmi/benchmarking/ate_tests/drained_configuration_convergence_time/metadata.textproto rename to feature/experimental/system/gnmi/benchmarking/otg_tests/drained_configuration_convergence_time/metadata.textproto diff --git a/feature/experimental/telemetry_only/feature.textproto b/feature/experimental/telemetry_only/feature.textproto index b8f75e48085..829203c9478 100644 --- a/feature/experimental/telemetry_only/feature.textproto +++ b/feature/experimental/telemetry_only/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "experimental_telemetry_only" diff --git a/feature/gnmi/otg_tests/telemetry_basic_check_test/metadata.textproto b/feature/gnmi/otg_tests/telemetry_basic_check_test/metadata.textproto index e122fbd26b8..7e0b5875853 100644 --- a/feature/gnmi/otg_tests/telemetry_basic_check_test/metadata.textproto +++ b/feature/gnmi/otg_tests/telemetry_basic_check_test/metadata.textproto @@ -67,3 +67,4 @@ platform_exceptions: { os_component_parent_is_chassis: true } } +path_presence_test: true diff --git a/feature/gnmi/subscribe/tests/gnmi_sample_mode_test/gnmi_sample_mode_test.go b/feature/gnmi/subscribe/tests/gnmi_sample_mode_test/gnmi_sample_mode_test.go index 3882ee875b0..1b31e0a73d4 100644 --- a/feature/gnmi/subscribe/tests/gnmi_sample_mode_test/gnmi_sample_mode_test.go +++ b/feature/gnmi/subscribe/tests/gnmi_sample_mode_test/gnmi_sample_mode_test.go @@ -35,7 +35,7 @@ func TestGNMISampleMode(t *testing.T) { p1Stream := samplestream.New(t, dut, gnmi.OC().Interface(p1.Name()).Description().State(), 10*time.Second) defer p1Stream.Close() - desc := p1Stream.Next(t) + desc := p1Stream.Next() if desc == nil { t.Errorf("Interface %q telemetry not received before config", p1.Name()) } else { @@ -48,7 +48,7 @@ func TestGNMISampleMode(t *testing.T) { gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), dut)) - desc = p1Stream.Next(t) + desc = p1Stream.Next() if desc == nil { t.Errorf("Interface %q telemetry not received after config", p1.Name()) } else { @@ -64,7 +64,7 @@ func TestGNMISampleMode(t *testing.T) { gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Description().Config(), "DUT Port 1 - Updated") - desc = p1Stream.Next(t) + desc = p1Stream.Next() if desc == nil { t.Errorf("Interface %q telemetry not received after description update", p1.Name()) } else { @@ -108,7 +108,7 @@ func TestNoInvalidValuesOnInterfaceFlap(t *testing.T) { time.Sleep(10 * time.Second) // wait 10 seconds for at-least 1 stream value. // Now validate description stream didn't return any invalid values. - vals := p1Stream.All(t) + vals := p1Stream.All() for idx, v := range vals { if v, ok := v.Val(); !ok { @@ -133,7 +133,7 @@ func TestISISProtocol(t *testing.T) { // Starting ISIS protocol takes some time to converge. So we may not receive ISIS data // in the first sample after configuration. - samples := niStream.Nexts(t, 5) + samples := niStream.Nexts(5) updated := false for idx, sample := range samples { diff --git a/feature/gnoi/factory_reset/feature.textproto b/feature/gnoi/factory_reset/feature.textproto index df72586373f..eaa711ccaa3 100644 --- a/feature/gnoi/factory_reset/feature.textproto +++ b/feature/gnoi/factory_reset/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "gnoi_factory_reset" diff --git a/feature/gnoi/file/feature.textproto b/feature/gnoi/file/feature.textproto index 151fc0b1102..fda97967dee 100644 --- a/feature/gnoi/file/feature.textproto +++ b/feature/gnoi/file/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "gnoi_file" diff --git a/feature/gnoi/healthz/feature.textproto b/feature/gnoi/healthz/feature.textproto index fdfdc06fe22..b7fcef86a4e 100644 --- a/feature/gnoi/healthz/feature.textproto +++ b/feature/gnoi/healthz/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "gnoi_healthz" diff --git a/feature/gnoi/os/feature.textproto b/feature/gnoi/os/feature.textproto index 6054dc78c1e..765f2044a91 100644 --- a/feature/gnoi/os/feature.textproto +++ b/feature/gnoi/os/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "gnoi_os" diff --git a/feature/gnoi/packet_link_qualification/feature.textproto b/feature/gnoi/packet_link_qualification/feature.textproto index 8d710ba0d81..e03f8b71b8f 100644 --- a/feature/gnoi/packet_link_qualification/feature.textproto +++ b/feature/gnoi/packet_link_qualification/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "gnoi_packet_link_qualification" diff --git a/feature/gnoi/packet_link_qualification/tests/packet_link_qualification_test/metadata.textproto b/feature/gnoi/packet_link_qualification/tests/packet_link_qualification_test/metadata.textproto index e0bbb248c81..550fef5802e 100644 --- a/feature/gnoi/packet_link_qualification/tests/packet_link_qualification_test/metadata.textproto +++ b/feature/gnoi/packet_link_qualification/tests/packet_link_qualification_test/metadata.textproto @@ -33,3 +33,14 @@ platform_exceptions: { explicit_port_speed: true } } +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + skip_plq_interface_oper_status_check: true + plq_reflector_stats_unsupported: true + plq_generator_capabilities_max_mtu: 512 + plq_generator_capabilities_max_pps: 40000000 + } +} diff --git a/feature/gnoi/packet_link_qualification/tests/packet_link_qualification_test/packet_link_qualification_test.go b/feature/gnoi/packet_link_qualification/tests/packet_link_qualification_test/packet_link_qualification_test.go index e3c600508a1..3da68cc1db0 100644 --- a/feature/gnoi/packet_link_qualification/tests/packet_link_qualification_test/packet_link_qualification_test.go +++ b/feature/gnoi/packet_link_qualification/tests/packet_link_qualification_test/packet_link_qualification_test.go @@ -44,6 +44,11 @@ func TestMain(m *testing.M) { // https://github.com/fullstorydev/grpcurl // +var ( + minRequiredGeneratorMTU = uint64(8184) + minRequiredGeneratorPPS = uint64(1e8) +) + func TestCapabilitiesResponse(t *testing.T) { dut1 := ondatra.DUT(t, "dut1") dut2 := ondatra.DUT(t, "dut2") @@ -60,6 +65,14 @@ func TestCapabilitiesResponse(t *testing.T) { t.Fatalf("Failed to handle gnoi LinkQualification().Capabilities(): %v", err) } + if deviations.PLQGeneratorCapabilitiesMaxMTU(dut1) != 0 { + minRequiredGeneratorMTU = uint64(deviations.PLQGeneratorCapabilitiesMaxMTU(dut1)) + } + + if deviations.PLQGeneratorCapabilitiesMaxPPS(dut1) != 0 { + minRequiredGeneratorPPS = deviations.PLQGeneratorCapabilitiesMaxPPS(dut1) + } + cases := []struct { desc string got uint64 @@ -91,7 +104,7 @@ func TestCapabilitiesResponse(t *testing.T) { }, { desc: "Generator MaxMtu", got: uint64(plqResp.GetGenerator().GetPacketGenerator().GetMaxMtu()), - min: uint64(8184), + min: minRequiredGeneratorMTU, }, { desc: "Generator MaxBps", got: uint64(plqResp.GetGenerator().GetPacketGenerator().GetMaxBps()), @@ -99,7 +112,7 @@ func TestCapabilitiesResponse(t *testing.T) { }, { desc: "Generator MaxPps", got: uint64(plqResp.GetGenerator().GetPacketGenerator().GetMaxPps()), - min: uint64(1e8), + min: minRequiredGeneratorPPS, }} for _, tc := range cases { @@ -231,6 +244,10 @@ func TestLinkQualification(t *testing.T) { } plqID := dut1.Name() + ":" + dp1.Name() + "<->" + dut2.Name() + ":" + dp2.Name() + + if deviations.PLQGeneratorCapabilitiesMaxMTU(dut1) != 0 { + minRequiredGeneratorMTU = uint64(deviations.PLQGeneratorCapabilitiesMaxMTU(dut1)) + } type LinkQualificationDuration struct { // time needed to complete preparation generatorsetupDuration time.Duration @@ -265,7 +282,7 @@ func TestLinkQualification(t *testing.T) { EndpointType: &plqpb.QualificationConfiguration_PacketGenerator{ PacketGenerator: &plqpb.PacketGeneratorConfiguration{ PacketRate: uint64(138888), - PacketSize: uint32(8184), + PacketSize: uint32(minRequiredGeneratorMTU), }, }, Timing: &plqpb.QualificationConfiguration_Rpc{ @@ -422,11 +439,16 @@ func TestLinkQualification(t *testing.T) { // The packet counters between Generator and Reflector mismatch tolerance level in percentage var tolerance float64 = 0.0001 - if ((math.Abs(float64(generatorPktsSent)-float64(reflectorPktsRxed)))/(float64(generatorPktsSent)+float64(reflectorPktsRxed)+tolerance))*200.00 > tolerance { - t.Errorf("The difference between packets received count at Reflector and packets sent count at Generator is greater than %0.4f percent: generatorPktsSent %v, reflectorPktsRxed %v", tolerance, generatorPktsSent, reflectorPktsRxed) - } - if ((math.Abs(float64(reflectorPktsSent)-float64(generatorPktsRxed)))/(float64(reflectorPktsSent)+float64(generatorPktsRxed)+tolerance))*200.00 > tolerance { - t.Errorf("The difference between packets received count at Generator and packets sent count at Reflector is greater than %0.4f percent: reflectorPktsSent %v, generatorPktsRxed %v", tolerance, reflectorPktsSent, generatorPktsRxed) + if deviations.PLQReflectorStatsUnsupported(dut1) { + if (math.Abs(float64(generatorPktsSent)-float64(generatorPktsRxed))/float64(generatorPktsSent))*100.00 > tolerance { + t.Errorf("The difference between packets sent count and packets received count at Generator is greater than %0.4f percent: generatorPktsSent %v, generatorPktsRxed %v", tolerance, generatorPktsSent, generatorPktsRxed) + } + } else { + if ((math.Abs(float64(generatorPktsSent)-float64(reflectorPktsRxed)))/(float64(generatorPktsSent)+float64(reflectorPktsRxed)+tolerance))*200.00 > tolerance { + t.Errorf("The difference between packets received count at Reflector and packets sent count at Generator is greater than %0.4f percent: generatorPktsSent %v, reflectorPktsRxed %v", tolerance, generatorPktsSent, reflectorPktsRxed) + } + if ((math.Abs(float64(reflectorPktsSent)-float64(generatorPktsRxed)))/(float64(reflectorPktsSent)+float64(generatorPktsRxed)+tolerance))*200.00 > tolerance { + t.Errorf("The difference between packets received count at Generator and packets sent count at Reflector is greater than %0.4f percent: reflectorPktsSent %v, generatorPktsRxed %v", tolerance, reflectorPktsSent, generatorPktsRxed) + } } - } diff --git a/feature/gnoi/system/feature.textproto b/feature/gnoi/system/feature.textproto index 6a9904718c6..32f544193ad 100644 --- a/feature/gnoi/system/feature.textproto +++ b/feature/gnoi/system/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "gnoi_system" diff --git a/feature/gribi/ate_tests/hierarchical_weight_resolution_pbf_test/metadata.textproto b/feature/gribi/ate_tests/hierarchical_weight_resolution_pbf_test/metadata.textproto index 147723391ea..f64bcea4013 100644 --- a/feature/gribi/ate_tests/hierarchical_weight_resolution_pbf_test/metadata.textproto +++ b/feature/gribi/ate_tests/hierarchical_weight_resolution_pbf_test/metadata.textproto @@ -13,7 +13,8 @@ platform_exceptions: { hierarchical_weight_resolution_tolerance: 1.5 ipv4_missing_enabled: true interface_ref_interface_id_format: true - + pf_require_match_default_rule: true + pf_require_sequential_order_pbr_rules: true } } platform_exceptions: { diff --git a/feature/gribi/ate_tests/hierarchical_weight_resolution_test/metadata.textproto b/feature/gribi/ate_tests/hierarchical_weight_resolution_test/metadata.textproto index f8ea374a730..63b7b4f20f6 100644 --- a/feature/gribi/ate_tests/hierarchical_weight_resolution_test/metadata.textproto +++ b/feature/gribi/ate_tests/hierarchical_weight_resolution_test/metadata.textproto @@ -13,7 +13,6 @@ platform_exceptions: { hierarchical_weight_resolution_tolerance: 1.5 ipv4_missing_enabled: true interface_ref_interface_id_format: true - } } platform_exceptions: { diff --git a/feature/gribi/feature.textproto b/feature/gribi/feature.textproto index 3fca4d03ce8..6245f883738 100644 --- a/feature/gribi/feature.textproto +++ b/feature/gribi/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "gribi" diff --git a/feature/gribi/otg_tests/backup_ngp_multiple_nh_pbf_test/metadata.textproto b/feature/gribi/otg_tests/backup_ngp_multiple_nh_pbf_test/metadata.textproto index 868d9b38bba..9c71cbd5fc6 100644 --- a/feature/gribi/otg_tests/backup_ngp_multiple_nh_pbf_test/metadata.textproto +++ b/feature/gribi/otg_tests/backup_ngp_multiple_nh_pbf_test/metadata.textproto @@ -11,6 +11,9 @@ platform_exceptions: { } deviations: { ipv4_missing_enabled: true + interface_ref_interface_id_format: true + pf_require_match_default_rule: true + pf_require_sequential_order_pbr_rules: true } } platform_exceptions: { diff --git a/feature/gribi/otg_tests/backup_nhg_multiple_nh_test/metadata.textproto b/feature/gribi/otg_tests/backup_nhg_multiple_nh_test/metadata.textproto index 43061474e5e..07ec0e6a61e 100644 --- a/feature/gribi/otg_tests/backup_nhg_multiple_nh_test/metadata.textproto +++ b/feature/gribi/otg_tests/backup_nhg_multiple_nh_test/metadata.textproto @@ -11,6 +11,7 @@ platform_exceptions: { } deviations: { ipv4_missing_enabled: true + interface_ref_interface_id_format: true } } platform_exceptions: { diff --git a/feature/gribi/otg_tests/base_hierarchical_nhg_update/base_hierarchical_nhg_update_test.go b/feature/gribi/otg_tests/base_hierarchical_nhg_update/base_hierarchical_nhg_update_test.go index bea9dc7941c..c705b00528b 100644 --- a/feature/gribi/otg_tests/base_hierarchical_nhg_update/base_hierarchical_nhg_update_test.go +++ b/feature/gribi/otg_tests/base_hierarchical_nhg_update/base_hierarchical_nhg_update_test.go @@ -224,16 +224,16 @@ func TestBaseHierarchicalNHGUpdate(t *testing.T) { desc: "Usecase for NHG update in hierarchical resolution scenario", fn: testBaseHierarchialNHG, }, - { - name: "testImplementDrain", - desc: "Usecase for Implementing Drain test", - fn: testImplementDrain, - }, { name: "testRecursiveIPv4EntrywithVRFSelectionPolW", desc: "Usecase for NHG update in hierarchical resolution scenario with VRF Selection Policy W", fn: testBaseHierarchialNHGwithVrfPolW, }, + { + name: "testImplementDrain", + desc: "Usecase for Implementing Drain test", + fn: testImplementDrain, + }, } // Configure the gRIBI client client := gribi.Client{ @@ -287,6 +287,8 @@ func testBaseHierarchialNHGwithVrfPolW(ctx context.Context, t *testing.T, args * ctx = context.WithValue(ctx, transitKey{}, true) testBaseHierarchialNHG(ctx, t, args) + //Delete Policy-forwarding PolicyW from the ingress interface + vrfpolicy.DeletePolicyForwarding(t, args.dut, "port1") } // TE3.7 - case 1: testBaseHierarchialNHG. diff --git a/feature/gribi/otg_tests/base_hierarchical_nhg_update/metadata.textproto b/feature/gribi/otg_tests/base_hierarchical_nhg_update/metadata.textproto index c08cca4e5e5..fca5d3f83ec 100644 --- a/feature/gribi/otg_tests/base_hierarchical_nhg_update/metadata.textproto +++ b/feature/gribi/otg_tests/base_hierarchical_nhg_update/metadata.textproto @@ -12,6 +12,9 @@ platform_exceptions: { deviations: { ipv4_missing_enabled: true gribi_mac_override_with_static_arp: true + interface_ref_interface_id_format: true + pf_require_match_default_rule: true + pf_require_sequential_order_pbr_rules: true } } platform_exceptions: { diff --git a/feature/gribi/otg_tests/base_hierarchical_route_installation_test/base_hierarchical_route_installation_test.go b/feature/gribi/otg_tests/base_hierarchical_route_installation_test/base_hierarchical_route_installation_test.go index 61865b765b9..6b7b4eb0003 100644 --- a/feature/gribi/otg_tests/base_hierarchical_route_installation_test/base_hierarchical_route_installation_test.go +++ b/feature/gribi/otg_tests/base_hierarchical_route_installation_test/base_hierarchical_route_installation_test.go @@ -175,11 +175,6 @@ func configNonDefaultNetworkInstance(t *testing.T, dut *ondatra.DUTDevice) { } } -func deleteVrfSelectionPolicy(t *testing.T, dut *ondatra.DUTDevice) { - t.Helper() - gnmi.Delete(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).PolicyForwarding().Config()) -} - type policyFwRule struct { SeqId uint32 protocol oc.UnionUint8 @@ -222,6 +217,12 @@ func configureVrfSelectionPolicyW(t *testing.T, dut *ondatra.DUTDevice) { pfRule12 := &policyFwRule{SeqId: 12, protocol: 41, sourceAddr: ipv4OuterSrc111WithMask, decapNi: niDecapTeVrf, postDecapNi: niDefault, decapFallbackNi: niTeVrf111} + if deviations.PfRequireSequentialOrderPbrRules(dut) { + pfRule10.SeqId = 910 + pfRule11.SeqId = 911 + pfRule12.SeqId = 912 + } + pfRuleList := []*policyFwRule{pfRule1, pfRule2, pfRule3, pfRule4, pfRule5, pfRule6, pfRule7, pfRule8, pfRule9, pfRule10, pfRule11, pfRule12} @@ -243,9 +244,21 @@ func configureVrfSelectionPolicyW(t *testing.T, dut *ondatra.DUTDevice) { pfRAction.PostDecapNetworkInstance = ygot.String(pfRule.postDecapNi) pfRAction.DecapFallbackNetworkInstance = ygot.String(pfRule.decapFallbackNi) } - pfR := niPf.GetOrCreateRule(13) - pfRAction := pfR.GetOrCreateAction() - pfRAction.NetworkInstance = ygot.String(niDefault) + + if deviations.PfRequireMatchDefaultRule(dut) { + pfR13 := niPf.GetOrCreateRule(913) + pfR13.GetOrCreateL2().SetEthertype(oc.PacketMatchTypes_ETHERTYPE_ETHERTYPE_IPV4) + pfRAction := pfR13.GetOrCreateAction() + pfRAction.NetworkInstance = ygot.String(niDefault) + pfR14 := niPf.GetOrCreateRule(914) + pfR14.GetOrCreateL2().SetEthertype(oc.PacketMatchTypes_ETHERTYPE_ETHERTYPE_IPV6) + pfRAction = pfR14.GetOrCreateAction() + pfRAction.NetworkInstance = ygot.String(niDefault) + } else { + pfR := niPf.GetOrCreateRule(13) + pfRAction := pfR.GetOrCreateAction() + pfRAction.NetworkInstance = ygot.String(niDefault) + } p1 := dut.Port(t, "port1") interfaceID := p1.Name() @@ -606,7 +619,6 @@ func testRecursiveIPv4EntrywithVrfPolW(t *testing.T, args *testArgs) { } t.Log("Delete existing vrf selection policy and Apply vrf selectioin policy W") configNonDefaultNetworkInstance(t, args.dut) - deleteVrfSelectionPolicy(t, args.dut) configureVrfSelectionPolicyW(t, args.dut) t.Logf("Adding IP %v with NHG %d NH %d with IP %v as NH via gRIBI", ateIndirectNH, nhgIndex2, nhIndex2, atePort2.IPv4) diff --git a/feature/gribi/otg_tests/base_hierarchical_route_installation_test/metadata.textproto b/feature/gribi/otg_tests/base_hierarchical_route_installation_test/metadata.textproto index 8058da4d567..ff82417d6fb 100644 --- a/feature/gribi/otg_tests/base_hierarchical_route_installation_test/metadata.textproto +++ b/feature/gribi/otg_tests/base_hierarchical_route_installation_test/metadata.textproto @@ -13,6 +13,8 @@ platform_exceptions: { ipv4_missing_enabled: true gribi_mac_override_with_static_arp: true interface_ref_interface_id_format: true + pf_require_match_default_rule: true + pf_require_sequential_order_pbr_rules: true } } platform_exceptions: { diff --git a/feature/gribi/otg_tests/basic_encap_test/basic_encap_test.go b/feature/gribi/otg_tests/basic_encap_test/basic_encap_test.go index 4ca9844fbc0..a9d33bea3e2 100644 --- a/feature/gribi/otg_tests/basic_encap_test/basic_encap_test.go +++ b/feature/gribi/otg_tests/basic_encap_test/basic_encap_test.go @@ -18,6 +18,7 @@ package basic_encap_test import ( "fmt" "log" + "math/rand" "os" "strconv" "strings" @@ -35,6 +36,7 @@ import ( "github.com/openconfig/featureprofiles/internal/fptest" "github.com/openconfig/featureprofiles/internal/gribi" "github.com/openconfig/featureprofiles/internal/otgutils" + "github.com/openconfig/gribigo/client" "github.com/openconfig/gribigo/fluent" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" @@ -75,6 +77,7 @@ const ( dscpEncapB1 = 20 dscpEncapB2 = 28 dscpEncapNoMatch = 30 + magicIp = "192.168.1.1" magicMac = "02:00:00:00:00:01" tunnelDstIP1 = "203.0.113.1" tunnelDstIP2 = "203.0.113.2" @@ -101,6 +104,7 @@ const ( // observing on IXIA OTG: Cannot start capture on more than one port belonging to the // same resource group or on more than one port behind the same front panel port in the chassis otgMutliPortCaptureSupported = false + seqIDBase = uint32(10) ) var ( @@ -585,6 +589,16 @@ func getPbrRules(dut *ondatra.DUTDevice, clusterFacing bool) []pbrRule { return pbrRules } +// seqIDOffset returns sequence ID offset added with seqIDBase (10), to avoid sequences +// like 1, 10, 11, 12,..., 2, 21, 22, ... while being sent by Ondatra to the DUT. +// It now generates sequences like 11, 12, 13, ..., 19, 20, 21,..., 99. +func seqIDOffset(dut *ondatra.DUTDevice, i uint32) uint32 { + if deviations.PfRequireSequentialOrderPbrRules(dut) { + return i + seqIDBase + } + return i +} + // configDefaultRoute configures a static route in DEFAULT network-instance. func configDefaultRoute(t *testing.T, dut *ondatra.DUTDevice, v4Prefix, v4NextHop, v6Prefix, v6NextHop string) { t.Logf("Configuring static route in DEFAULT network-instance") @@ -627,8 +641,7 @@ func getPbrPolicy(dut *ondatra.DUTDevice, name string, clusterFacing bool) *oc.N p.SetType(oc.Policy_Type_VRF_SELECTION_POLICY) for _, pRule := range getPbrRules(dut, clusterFacing) { - r := p.GetOrCreateRule(pRule.sequence) - l2 := r.GetOrCreateL2() + r := p.GetOrCreateRule(seqIDOffset(dut, pRule.sequence)) r4 := r.GetOrCreateIpv4() if pRule.dscpSet != nil { @@ -652,13 +665,14 @@ func getPbrPolicy(dut *ondatra.DUTDevice, name string, clusterFacing bool) *oc.N ra.PostDecapNetworkInstance = ygot.String(pRule.decapVrfSet[1]) ra.DecapFallbackNetworkInstance = ygot.String(pRule.decapVrfSet[2]) } - - if pRule.etherType != nil { - l2.SetEthertype(pRule.etherType) + if deviations.PfRequireMatchDefaultRule(dut) { + if pRule.etherType != nil { + r.GetOrCreateL2().Ethertype = pRule.etherType + } } + if pRule.encapVrf != "" { r.GetOrCreateAction().SetNetworkInstance(pRule.encapVrf) - } } return pf @@ -674,6 +688,33 @@ func configureBaseconfig(t *testing.T, dut *ondatra.DUTDevice) { gnmi.Replace(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).PolicyForwarding().Config(), pf) } +func staticARPWithMagicUniversalIP(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + sb := &gnmi.SetBatch{} + p2 := dut.Port(t, "port2") + p3 := dut.Port(t, "port3") + p4 := dut.Port(t, "port4") + p5 := dut.Port(t, "port5") + portList := []*ondatra.Port{p2, p3, p4, p5} + for idx, p := range portList { + s := &oc.NetworkInstance_Protocol_Static{ + Prefix: ygot.String(magicIp + "/32"), + NextHop: map[string]*oc.NetworkInstance_Protocol_Static_NextHop{ + strconv.Itoa(idx): { + Index: ygot.String(strconv.Itoa(idx)), + InterfaceRef: &oc.NetworkInstance_Protocol_Static_NextHop_InterfaceRef{ + Interface: ygot.String(p.Name()), + }, + }, + }, + } + sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(dut)) + gnmi.BatchUpdate(sb, sp.Static(magicIp+"/32").Config(), s) + gnmi.BatchUpdate(sb, gnmi.OC().Interface(p.Name()).Config(), configStaticArp(p.Name(), magicIp, magicMac)) + } + sb.Set(t, dut) +} + // programEntries pushes RIB entries on the DUT required for Encap functionality func programEntries(t *testing.T, dut *ondatra.DUTDevice, c *gribi.Client) { // push RIB entries @@ -682,28 +723,55 @@ func programEntries(t *testing.T, dut *ondatra.DUTDevice, c *gribi.Client) { c.AddNH(t, nh11ID, "MACwithIp", deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB, &gribi.NHOptions{Dest: otgPort3DummyIP.IPv4, Mac: magicMac}) c.AddNH(t, nh100ID, "MACwithIp", deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB, &gribi.NHOptions{Dest: otgPort4DummyIP.IPv4, Mac: magicMac}) c.AddNH(t, nh101ID, "MACwithIp", deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB, &gribi.NHOptions{Dest: otgPort5DummyIP.IPv4, Mac: magicMac}) - + c.AddNHG(t, nhg2ID, map[uint64]uint64{nh10ID: 1, nh11ID: 3}, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) + c.AddNHG(t, nhg3ID, map[uint64]uint64{nh100ID: 2, nh101ID: 3}, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) + } else if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) { + p2 := dut.Port(t, "port2") + p3 := dut.Port(t, "port3") + p4 := dut.Port(t, "port4") + p5 := dut.Port(t, "port5") + nh1, op1 := gribi.NHEntry(nh10ID, "MACwithInterface", deviations.DefaultNetworkInstance(dut), + fluent.InstalledInFIB, &gribi.NHOptions{Interface: p2.Name(), Mac: magicMac, Dest: magicIp}) + nh2, op2 := gribi.NHEntry(nh11ID, "MACwithInterface", deviations.DefaultNetworkInstance(dut), + fluent.InstalledInFIB, &gribi.NHOptions{Interface: p3.Name(), Mac: magicMac, Dest: magicIp}) + nh3, op3 := gribi.NHEntry(nh100ID, "MACwithInterface", deviations.DefaultNetworkInstance(dut), + fluent.InstalledInFIB, &gribi.NHOptions{Interface: p4.Name(), Mac: magicMac, Dest: magicIp}) + nh4, op4 := gribi.NHEntry(nh101ID, "MACwithInterface", deviations.DefaultNetworkInstance(dut), + fluent.InstalledInFIB, &gribi.NHOptions{Interface: p5.Name(), Mac: magicMac, Dest: magicIp}) + nhg1, op5 := gribi.NHGEntry(nhg2ID, map[uint64]uint64{nh10ID: 1, nh11ID: 3}, + deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) + nhg2, op6 := gribi.NHGEntry(nhg3ID, map[uint64]uint64{nh100ID: 2, nh101ID: 3}, + deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) + c.AddEntries(t, []fluent.GRIBIEntry{nh1, nh2, nh3, nh4, nhg1, nhg2}, + []*client.OpResult{op1, op2, op3, op4, op5, op6}) } else { c.AddNH(t, nh10ID, "MACwithInterface", deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB, &gribi.NHOptions{Interface: dut.Port(t, "port2").Name(), Mac: magicMac}) c.AddNH(t, nh11ID, "MACwithInterface", deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB, &gribi.NHOptions{Interface: dut.Port(t, "port3").Name(), Mac: magicMac}) c.AddNH(t, nh100ID, "MACwithInterface", deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB, &gribi.NHOptions{Interface: dut.Port(t, "port4").Name(), Mac: magicMac}) c.AddNH(t, nh101ID, "MACwithInterface", deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB, &gribi.NHOptions{Interface: dut.Port(t, "port5").Name(), Mac: magicMac}) + c.AddNHG(t, nhg2ID, map[uint64]uint64{nh10ID: 1, nh11ID: 3}, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) + c.AddNHG(t, nhg3ID, map[uint64]uint64{nh100ID: 2, nh101ID: 3}, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) } - c.AddNHG(t, nhg2ID, map[uint64]uint64{nh10ID: 1, nh11ID: 3}, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) c.AddIPv4(t, cidr(vipIP1, 32), nhg2ID, deviations.DefaultNetworkInstance(dut), deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) - c.AddNHG(t, nhg3ID, map[uint64]uint64{nh100ID: 2, nh101ID: 3}, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) c.AddIPv4(t, cidr(vipIP2, 32), nhg3ID, deviations.DefaultNetworkInstance(dut), deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) - c.AddNH(t, nh1ID, vipIP1, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) - c.AddNH(t, nh2ID, vipIP2, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) - c.AddNHG(t, nhg1ID, map[uint64]uint64{nh1ID: 1, nh2ID: 3}, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) + nh5, op7 := gribi.NHEntry(nh1ID, vipIP1, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) + nh6, op8 := gribi.NHEntry(nh2ID, vipIP2, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) + nhg3, op9 := gribi.NHGEntry(nhg1ID, map[uint64]uint64{nh1ID: 1, nh2ID: 3}, + deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) + c.AddEntries(t, []fluent.GRIBIEntry{nh5, nh6, nhg3}, []*client.OpResult{op7, op8, op9}) + c.AddIPv4(t, cidr(tunnelDstIP1, 32), nhg1ID, vrfTransit, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) c.AddIPv4(t, cidr(tunnelDstIP2, 32), nhg1ID, vrfTransit, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) - c.AddNH(t, nh201ID, "Encap", deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB, &gribi.NHOptions{Src: ipv4OuterSrc111, Dest: tunnelDstIP1, VrfName: vrfTransit}) - c.AddNH(t, nh202ID, "Encap", deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB, &gribi.NHOptions{Src: ipv4OuterSrc111, Dest: tunnelDstIP2, VrfName: vrfTransit}) - c.AddNHG(t, nhg10ID, map[uint64]uint64{nh201ID: 1, nh202ID: 3}, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) + nh7, op9 := gribi.NHEntry(nh201ID, "Encap", deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB, + &gribi.NHOptions{Src: ipv4OuterSrc111, Dest: tunnelDstIP1, VrfName: vrfTransit}) + nh8, op10 := gribi.NHEntry(nh202ID, "Encap", deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB, + &gribi.NHOptions{Src: ipv4OuterSrc111, Dest: tunnelDstIP2, VrfName: vrfTransit}) + nhg4, op11 := gribi.NHGEntry(nhg10ID, map[uint64]uint64{nh201ID: 1, nh202ID: 3}, + deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) + c.AddEntries(t, []fluent.GRIBIEntry{nh7, nh8, nhg4}, []*client.OpResult{op9, op10, op11}) c.AddIPv4(t, cidr(ipv4EntryPrefix, ipv4EntryPrefixLen), nhg10ID, vrfEncapA, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) c.AddIPv4(t, cidr(ipv4EntryPrefix, ipv4EntryPrefixLen), nhg10ID, vrfEncapB, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) c.AddIPv6(t, cidr(ipv6EntryPrefix, ipv6EntryPrefixLen), nhg10ID, vrfEncapA, deviations.DefaultNetworkInstance(dut), fluent.InstalledInFIB) @@ -717,13 +785,20 @@ func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { p3 := dut.Port(t, "port3") p4 := dut.Port(t, "port4") p5 := dut.Port(t, "port5") + portList := []*ondatra.Port{p1, p2, p3, p4, p5} // configure interfaces - gnmi.Replace(t, dut, d.Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), dut)) - gnmi.Replace(t, dut, d.Interface(p2.Name()).Config(), dutPort2.NewOCInterface(p2.Name(), dut)) - gnmi.Replace(t, dut, d.Interface(p3.Name()).Config(), dutPort3.NewOCInterface(p3.Name(), dut)) - gnmi.Replace(t, dut, d.Interface(p4.Name()).Config(), dutPort4.NewOCInterface(p4.Name(), dut)) - gnmi.Replace(t, dut, d.Interface(p5.Name()).Config(), dutPort5.NewOCInterface(p5.Name(), dut)) + for idx, a := range []attrs.Attributes{dutPort1, dutPort2, dutPort3, dutPort4, dutPort5} { + p := portList[idx] + intf := a.NewOCInterface(p.Name(), dut) + if p.PMD() == ondatra.PMD100GBASEFR && dut.Vendor() != ondatra.CISCO { + e := intf.GetOrCreateEthernet() + e.AutoNegotiate = ygot.Bool(false) + e.DuplexMode = oc.Ethernet_DuplexMode_FULL + e.PortSpeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_100GB + } + gnmi.Replace(t, dut, d.Interface(p.Name()).Config(), intf) + } // configure base PBF policies and network-instances configureBaseconfig(t, dut) @@ -732,6 +807,8 @@ func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { applyForwardingPolicy(t, dut, p1.Name()) if deviations.GRIBIMACOverrideWithStaticARP(dut) { staticARPWithSecondaryIP(t, dut) + } else if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) { + staticARPWithMagicUniversalIP(t, dut) } } @@ -768,6 +845,21 @@ func configureOTG(t *testing.T, ate *ondatra.ATEDevice) gosnappi.Config { otgPort4.AddToOTG(topo, p4, &dutPort4) otgPort5.AddToOTG(topo, p5, &dutPort5) + pmd100GFRPorts := []string{} + for _, p := range topo.Ports().Items() { + port := ate.Port(t, p.Name()) + if port.PMD() == ondatra.PMD100GBASEFR { + pmd100GFRPorts = append(pmd100GFRPorts, port.ID()) + } + } + // Disable FEC for 100G-FR ports because Novus does not support it. + if len(pmd100GFRPorts) > 0 { + l1Settings := topo.Layer1().Add().SetName("L1").SetPortNames(pmd100GFRPorts) + l1Settings.SetAutoNegotiate(true).SetIeeeMediaDefaults(false).SetSpeed("speed_100_gbps") + autoNegotiate := l1Settings.AutoNegotiation() + autoNegotiate.SetRsFec(false) + } + t.Logf("Pushing config to ATE and starting protocols...") otg.PushConfig(t, topo) t.Logf("starting protocols...") @@ -796,6 +888,15 @@ func clearCapture(t *testing.T, otg *otg.OTG, topo gosnappi.Config) { otg.PushConfig(t, topo) } +func randRange(max int, count int) []uint32 { + rand.New(rand.NewSource(time.Now().UnixNano())) + var result []uint32 + for len(result) < count { + result = append(result, uint32(rand.Intn(max))) + } + return result +} + // getFlow returns a flow of type ipv4, ipv4in4, ipv6in4 or ipv6 with dscp value passed in args. func (fa *flowAttr) getFlow(flowType string, name string, dscp uint32) gosnappi.Flow { flow := fa.topo.Flows().Add().SetName(name) @@ -811,9 +912,6 @@ func (fa *flowAttr) getFlow(flowType string, name string, dscp uint32) gosnappi. v4.Dst().SetValue(fa.dst) v4.TimeToLive().SetValue(ttl) v4.Priority().Dscp().Phb().SetValue(dscp) - udp := flow.Packet().Add().Udp() - udp.SrcPort().Increment().SetStart(50001).SetCount(1000) - udp.DstPort().Increment().SetStart(50001).SetCount(1000) // add inner ipv4 headers if flowType == "ipv4in4" { @@ -834,10 +932,10 @@ func (fa *flowAttr) getFlow(flowType string, name string, dscp uint32) gosnappi. v6.Dst().SetValue(fa.dst) v6.HopLimit().SetValue(ttl) v6.TrafficClass().SetValue(dscp << 2) - udp := flow.Packet().Add().Udp() - udp.SrcPort().Increment().SetStart(50001).SetCount(1000) - udp.DstPort().Increment().SetStart(50001).SetCount(1000) } + udp := flow.Packet().Add().Udp() + udp.SrcPort().SetValues(randRange(50001, 10000)) + udp.DstPort().SetValues(randRange(50001, 10000)) return flow } diff --git a/feature/gribi/otg_tests/basic_encap_test/metadata.textproto b/feature/gribi/otg_tests/basic_encap_test/metadata.textproto index 7f8aa1382c1..adf9ef8379d 100644 --- a/feature/gribi/otg_tests/basic_encap_test/metadata.textproto +++ b/feature/gribi/otg_tests/basic_encap_test/metadata.textproto @@ -4,7 +4,7 @@ uuid: "36cc79b5-3766-4cb4-b83b-1baea1464de8" plan_id: "TE-16.1" description: "basic encapsulation tests" -testbed: TESTBED_DUT_ATE_2LINKS +testbed: TESTBED_DUT_ATE_8LINKS platform_exceptions: { platform: { vendor: CISCO @@ -15,6 +15,20 @@ platform_exceptions: { interface_ref_interface_id_format: true ttl_copy_unsupported: true pf_require_match_default_rule: true + pf_require_sequential_order_pbr_rules: true + } +} +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + static_protocol_name: "STATIC" + gribi_mac_override_static_arp_static_route: true + interface_enabled: true + ttl_copy_unsupported: true + default_network_instance: "default" + omit_l2_mtu: true } } tags: TAGS_DATACENTER_EDGE diff --git a/feature/gribi/otg_tests/encap_frr/encap_frr_test.go b/feature/gribi/otg_tests/encap_frr/encap_frr_test.go index 089dcf58a11..46e80f2bef2 100644 --- a/feature/gribi/otg_tests/encap_frr/encap_frr_test.go +++ b/feature/gribi/otg_tests/encap_frr/encap_frr_test.go @@ -90,11 +90,11 @@ const ( niEncapTeVrfB = "ENCAP_TE_VRF_B" niTeVrf111 = "TE_VRF_111" niTeVrf222 = "TE_VRF_222" - niDefault = "DEFAULT" tolerancePct = 2 tolerance = 0.2 encapFlow = "encapFlow" correspondingHopLimit = 64 + magicIP = "192.168.1.1" magicMac = "02:00:00:00:00:01" gribiIPv4EntryDefVRF1 = "192.0.2.101" gribiIPv4EntryDefVRF2 = "192.0.2.102" @@ -125,6 +125,7 @@ const ( ateSrcNetCIDR = "198.51.100.0/24" ateSrcNetFirstIP = "198.51.100.1" ateSrcNetCount = 250 + ipOverIPProtocol = 4 checkEncap = true wantLoss = true @@ -213,7 +214,7 @@ type testArgs struct { } type policyFwRule struct { - SeqId uint32 + SeqID uint32 family string protocol oc.UnionUint8 dscpSet []uint8 @@ -267,6 +268,13 @@ func dutInterface(p *ondatra.Port, dut *ondatra.DUTDevice) *oc.Interface { i.Enabled = ygot.Bool(true) } + if p.PMD() == ondatra.PMD100GBASEFR { + e := i.GetOrCreateEthernet() + e.AutoNegotiate = ygot.Bool(false) + e.DuplexMode = oc.Ethernet_DuplexMode_FULL + e.PortSpeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_100GB + } + ipv4, ok := portsIPv4[id] if !ok { return nil @@ -342,42 +350,42 @@ func configureVrfSelectionPolicy(t *testing.T, dut *ondatra.DUTDevice) { d := &oc.Root{} dutPolFwdPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).PolicyForwarding() - pfRule1 := &policyFwRule{SeqId: 1, family: "ipv4", protocol: 4, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc222Addr + "/32", + pfRule1 := &policyFwRule{SeqID: 1, family: "ipv4", protocol: 4, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc222Addr + "/32", decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfA, decapFallbackNi: niTeVrf222} - pfRule2 := &policyFwRule{SeqId: 2, family: "ipv4", protocol: 41, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc222Addr + "/32", + pfRule2 := &policyFwRule{SeqID: 2, family: "ipv4", protocol: 41, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc222Addr + "/32", decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfA, decapFallbackNi: niTeVrf222} - pfRule3 := &policyFwRule{SeqId: 3, family: "ipv4", protocol: 4, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc111Addr + "/32", + pfRule3 := &policyFwRule{SeqID: 3, family: "ipv4", protocol: 4, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc111Addr + "/32", decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfA, decapFallbackNi: niTeVrf111} - pfRule4 := &policyFwRule{SeqId: 4, family: "ipv4", protocol: 41, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc111Addr + "/32", + pfRule4 := &policyFwRule{SeqID: 4, family: "ipv4", protocol: 41, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc111Addr + "/32", decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfA, decapFallbackNi: niTeVrf111} - pfRule5 := &policyFwRule{SeqId: 5, family: "ipv4", protocol: 4, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc222Addr + "/32", + pfRule5 := &policyFwRule{SeqID: 5, family: "ipv4", protocol: 4, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc222Addr + "/32", decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfB, decapFallbackNi: niTeVrf222} - pfRule6 := &policyFwRule{SeqId: 6, family: "ipv4", protocol: 41, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc222Addr + "/32", + pfRule6 := &policyFwRule{SeqID: 6, family: "ipv4", protocol: 41, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc222Addr + "/32", decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfB, decapFallbackNi: niTeVrf222} - pfRule7 := &policyFwRule{SeqId: 7, family: "ipv4", protocol: 4, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc111Addr + "/32", + pfRule7 := &policyFwRule{SeqID: 7, family: "ipv4", protocol: 4, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc111Addr + "/32", decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfB, decapFallbackNi: niTeVrf111} - pfRule8 := &policyFwRule{SeqId: 8, family: "ipv4", protocol: 41, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc111Addr + "/32", + pfRule8 := &policyFwRule{SeqID: 8, family: "ipv4", protocol: 41, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc111Addr + "/32", decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfB, decapFallbackNi: niTeVrf111} - pfRule9 := &policyFwRule{SeqId: 9, family: "ipv4", protocol: 4, sourceAddr: ipv4OuterSrc222Addr + "/32", - decapNi: niDecapTeVrf, postDecapNi: niDefault, decapFallbackNi: niTeVrf222} - pfRule10 := &policyFwRule{SeqId: 10, family: "ipv4", protocol: 41, sourceAddr: ipv4OuterSrc222Addr + "/32", - decapNi: niDecapTeVrf, postDecapNi: niDefault, decapFallbackNi: niTeVrf222} - pfRule11 := &policyFwRule{SeqId: 11, family: "ipv4", protocol: 4, sourceAddr: ipv4OuterSrc111Addr + "/32", - decapNi: niDecapTeVrf, postDecapNi: niDefault, decapFallbackNi: niTeVrf111} - pfRule12 := &policyFwRule{SeqId: 12, family: "ipv4", protocol: 41, sourceAddr: ipv4OuterSrc111Addr + "/32", - decapNi: niDecapTeVrf, postDecapNi: niDefault, decapFallbackNi: niTeVrf111} + pfRule9 := &policyFwRule{SeqID: 9, family: "ipv4", protocol: 4, sourceAddr: ipv4OuterSrc222Addr + "/32", + decapNi: niDecapTeVrf, postDecapNi: deviations.DefaultNetworkInstance(dut), decapFallbackNi: niTeVrf222} + pfRule10 := &policyFwRule{SeqID: 10, family: "ipv4", protocol: 41, sourceAddr: ipv4OuterSrc222Addr + "/32", + decapNi: niDecapTeVrf, postDecapNi: deviations.DefaultNetworkInstance(dut), decapFallbackNi: niTeVrf222} + pfRule11 := &policyFwRule{SeqID: 11, family: "ipv4", protocol: 4, sourceAddr: ipv4OuterSrc111Addr + "/32", + decapNi: niDecapTeVrf, postDecapNi: deviations.DefaultNetworkInstance(dut), decapFallbackNi: niTeVrf111} + pfRule12 := &policyFwRule{SeqID: 12, family: "ipv4", protocol: 41, sourceAddr: ipv4OuterSrc111Addr + "/32", + decapNi: niDecapTeVrf, postDecapNi: deviations.DefaultNetworkInstance(dut), decapFallbackNi: niTeVrf111} - pfRule13 := &policyFwRule{SeqId: 13, family: "ipv4", dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, + pfRule13 := &policyFwRule{SeqID: 13, family: "ipv4", dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, networkInstance: niEncapTeVrfA} - pfRule14 := &policyFwRule{SeqId: 14, family: "ipv6", dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, + pfRule14 := &policyFwRule{SeqID: 14, family: "ipv6", dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, networkInstance: niEncapTeVrfA} - pfRule15 := &policyFwRule{SeqId: 15, family: "ipv4", dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, + pfRule15 := &policyFwRule{SeqID: 15, family: "ipv4", dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, networkInstance: niEncapTeVrfB} - pfRule16 := &policyFwRule{SeqId: 16, family: "ipv6", dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, + pfRule16 := &policyFwRule{SeqID: 16, family: "ipv6", dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, networkInstance: niEncapTeVrfB} - pfRule17 := &policyFwRule{SeqId: 17, networkInstance: niDefault} + pfRule17 := &policyFwRule{SeqID: 17, networkInstance: deviations.DefaultNetworkInstance(dut)} pfRuleList := []*policyFwRule{pfRule1, pfRule2, pfRule3, pfRule4, pfRule5, pfRule6, pfRule7, pfRule8, pfRule9, pfRule10, pfRule11, pfRule12, pfRule13, pfRule14, @@ -389,7 +397,7 @@ func configureVrfSelectionPolicy(t *testing.T, dut *ondatra.DUTDevice) { niPf.SetType(oc.Policy_Type_VRF_SELECTION_POLICY) for _, pfRule := range pfRuleList { - pfR := niPf.GetOrCreateRule(pfRule.SeqId) + pfR := niPf.GetOrCreateRule(pfRule.SeqID) if pfRule.family == "ipv4" { pfRProtoIP := pfR.GetOrCreateIpv4() @@ -448,45 +456,122 @@ func configNonDefaultNetworkInstance(t *testing.T, dut *ondatra.DUTDevice) { } } +// configStaticArp configures static arp entries +func configStaticArp(p string, ipv4addr string, macAddr string) *oc.Interface { + i := &oc.Interface{Name: ygot.String(p)} + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + s := i.GetOrCreateSubinterface(0) + s4 := s.GetOrCreateIpv4() + n4 := s4.GetOrCreateNeighbor(ipv4addr) + n4.LinkLayerAddress = ygot.String(macAddr) + return i +} + +func staticARPWithMagicUniversalIP(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + sb := &gnmi.SetBatch{} + p2 := dut.Port(t, "port2") + p3 := dut.Port(t, "port3") + p4 := dut.Port(t, "port4") + p5 := dut.Port(t, "port5") + p6 := dut.Port(t, "port6") + p7 := dut.Port(t, "port7") + portList := []*ondatra.Port{p2, p3, p4, p5, p6, p7} + for idx, p := range portList { + s := &oc.NetworkInstance_Protocol_Static{ + Prefix: ygot.String(magicIP + "/32"), + NextHop: map[string]*oc.NetworkInstance_Protocol_Static_NextHop{ + strconv.Itoa(idx): { + Index: ygot.String(strconv.Itoa(idx)), + InterfaceRef: &oc.NetworkInstance_Protocol_Static_NextHop_InterfaceRef{ + Interface: ygot.String(p.Name()), + }, + }, + }, + } + sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(dut)) + gnmi.BatchUpdate(sb, sp.Static(magicIP+"/32").Config(), s) + gnmi.BatchUpdate(sb, gnmi.OC().Interface(p.Name()).Config(), configStaticArp(p.Name(), magicIP, magicMac)) + } + sb.Set(t, dut) +} + func configureGribiRoute(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, args *testArgs) { t.Helper() // Programming AFT entries for prefixes in DEFAULT VRF + if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) { + args.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(11).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port2").Name()).WithIPAddress(magicIP), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(12).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port3").Name()).WithIPAddress(magicIP), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(11).AddNextHop(11, 1).AddNextHop(12, 3), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(13).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port4").Name()).WithIPAddress(magicIP), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(12).AddNextHop(13, 2), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(14).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port5").Name()).WithIPAddress(magicIP), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(13).AddNextHop(14, 1), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(15).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port6").Name()).WithIPAddress(magicIP), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(14).AddNextHop(15, 1), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(16).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port7").Name()).WithIPAddress(magicIP), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(15).AddNextHop(16, 1), + ) + } else { + args.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(11).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port2").Name()), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(12).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port3").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(11).AddNextHop(11, 1).AddNextHop(12, 3), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(13).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port4").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(12).AddNextHop(13, 2), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(14).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port5").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(13).AddNextHop(14, 1), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(15).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port6").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(14).AddNextHop(15, 1), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(16).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port7").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(15).AddNextHop(16, 1), + ) + } + if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + args.client.Modify().AddEntry(t, - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(11).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port2").Name()), - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(12).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port3").Name()), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(11).AddNextHop(11, 1).AddNextHop(12, 3), fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryDefVRF1+"/"+maskLen32).WithNextHopGroup(11), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(13).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port4").Name()), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(12).AddNextHop(13, 2), fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryDefVRF2+"/"+maskLen32).WithNextHopGroup(12), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(14).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port5").Name()), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(13).AddNextHop(14, 1), fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryDefVRF3+"/"+maskLen32).WithNextHopGroup(13), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(15).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port6").Name()), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(14).AddNextHop(15, 1), fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryDefVRF4+"/"+maskLen32).WithNextHopGroup(14), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(16).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port7").Name()), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(15).AddNextHop(16, 1), fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryDefVRF5+"/"+maskLen32).WithNextHopGroup(15), ) @@ -523,14 +608,14 @@ func configureGribiRoute(ctx context.Context, t *testing.T, dut *ondatra.DUTDevi WithIndex(3).WithIPAddress(gribiIPv4EntryDefVRF3), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(2).AddNextHop(3, 1).WithBackupNHG(2000), - fluent.IPv4Entry().WithNetworkInstance(niTeVrf222). + fluent.IPv4Entry().WithNetworkInstance(niTeVrf222).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryVRF2221+"/"+maskLen32).WithNextHopGroup(2), fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(5).WithIPAddress(gribiIPv4EntryDefVRF5), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(4).AddNextHop(5, 1).WithBackupNHG(2000), - fluent.IPv4Entry().WithNetworkInstance(niTeVrf222). + fluent.IPv4Entry().WithNetworkInstance(niTeVrf222).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryVRF2222+"/"+maskLen32).WithNextHopGroup(4), ) if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { @@ -577,14 +662,14 @@ func configureGribiRoute(ctx context.Context, t *testing.T, dut *ondatra.DUTDevi WithIndex(2).WithIPAddress(gribiIPv4EntryDefVRF2), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(1).AddNextHop(1, 1).AddNextHop(2, 3).WithBackupNHG(1000), - fluent.IPv4Entry().WithNetworkInstance(niTeVrf111). + fluent.IPv4Entry().WithNetworkInstance(niTeVrf111).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryVRF1111+"/"+maskLen32).WithNextHopGroup(1), fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(4).WithIPAddress(gribiIPv4EntryDefVRF4), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(3).AddNextHop(4, 1).WithBackupNHG(1001), - fluent.IPv4Entry().WithNetworkInstance(niTeVrf111). + fluent.IPv4Entry().WithNetworkInstance(niTeVrf111).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryVRF1112+"/"+maskLen32).WithNextHopGroup(3), ) if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { @@ -615,7 +700,7 @@ func configureGribiRoute(ctx context.Context, t *testing.T, dut *ondatra.DUTDevi WithNextHopNetworkInstance(niTeVrf111), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(101).AddNextHop(101, 1).AddNextHop(102, 3).WithBackupNHG(18), - fluent.IPv4Entry().WithNetworkInstance(niEncapTeVrfA). + fluent.IPv4Entry().WithNetworkInstance(niEncapTeVrfA).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithPrefix(gribiIPv4EntryEncapVRF+"/"+maskLen24).WithNextHopGroup(101), ) if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { @@ -660,7 +745,12 @@ func configureISIS(t *testing.T, dut *ondatra.DUTDevice, intfName, dutAreaAddres isisIntfLevel.Enabled = ygot.Bool(true) isisIntfLevelAfi := isisIntfLevel.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST) isisIntfLevelAfi.Metric = ygot.Uint32(200) - isisIntfLevelAfi.Enabled = ygot.Bool(true) + if deviations.ISISInterfaceAfiUnsupported(dut) { + isisIntf.Af = nil + } + if deviations.MissingIsisInterfaceAfiSafiEnable(dut) { + isisIntfLevelAfi.Enabled = nil + } gnmi.Replace(t, dut, dutConfIsisPath.Config(), prot) } @@ -740,8 +830,11 @@ func verifyBgpTelemetry(t *testing.T, dut *ondatra.DUTDevice) { func configureOTG(t testing.TB, otg *otg.OTG, atePorts []*ondatra.Port) gosnappi.Config { t.Helper() config := gosnappi.NewConfig() - // ate := ondatra.ATE(t, "ate") + pmd100GFRPorts := []string{} for i, ap := range atePorts { + if ap.PMD() == ondatra.PMD100GBASEFR { + pmd100GFRPorts = append(pmd100GFRPorts, ap.ID()) + } // DUT and ATE ports are connected by the same names. dutid := fmt.Sprintf("dut:%s", ap.ID()) ateid := fmt.Sprintf("ate:%s", ap.ID()) @@ -800,6 +893,15 @@ func configureOTG(t testing.TB, otg *otg.OTG, atePorts []*ondatra.Port) gosnappi SetPortNames([]string{atePortNamelist[1], atePortNamelist[2], atePortNamelist[3], atePortNamelist[4], atePortNamelist[5], atePortNamelist[6], atePortNamelist[7]}). SetFormat(gosnappi.CaptureFormat.PCAP) + + // Disable FEC for 100G-FR ports because Novus does not support it. + if len(pmd100GFRPorts) > 0 { + l1Settings := config.Layer1().Add().SetName("L1").SetPortNames(pmd100GFRPorts) + l1Settings.SetAutoNegotiate(true).SetIeeeMediaDefaults(false).SetSpeed("speed_100_gbps") + autoNegotiate := l1Settings.AutoNegotiation() + autoNegotiate.SetRsFec(false) + } + otg.PushConfig(t, config) time.Sleep(30 * time.Second) otg.StartProtocols(t) @@ -1075,11 +1177,29 @@ func TestEncapFrr(t *testing.T) { t.Log("Configure Non-Default Network Instances") configNonDefaultNetworkInstance(t, dut) + if deviations.BackupNHGRequiresVrfWithDecap(dut) { + d := &oc.Root{} + ni := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + pf := ni.GetOrCreatePolicyForwarding() + fp1 := pf.GetOrCreatePolicy("match-ipip") + fp1.SetType(oc.Policy_Type_VRF_SELECTION_POLICY) + fp1.GetOrCreateRule(1).GetOrCreateIpv4().Protocol = oc.UnionUint8(ipOverIPProtocol) + fp1.GetOrCreateRule(1).GetOrCreateAction().NetworkInstance = ygot.String(deviations.DefaultNetworkInstance(dut)) + p1 := dut.Port(t, "port1") + intf := pf.GetOrCreateInterface(p1.Name()) + intf.ApplyVrfSelectionPolicy = ygot.String("match-ipip") + gnmi.Replace(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).PolicyForwarding().Config(), pf) + } + configureDUT(t, dut, dutPorts) t.Log("Apply vrf selection policy to DUT port-1") configureVrfSelectionPolicy(t, dut) + if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) { + staticARPWithMagicUniversalIP(t, dut) + } + t.Log("Install BGP route resolved by ISIS.") t.Log("Configure ISIS on DUT") configureISIS(t, dut, dut.Port(t, "port8").Name(), dutAreaAddress, dutSysID) @@ -1223,8 +1343,10 @@ func TestEncapFrr(t *testing.T) { if tc.encapUnviable == "primaryBackupRoutingSingle" { args.client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(1000).WithDecapsulateHeader(fluent.IPinIP). + WithIndex(1100).WithDecapsulateHeader(fluent.IPinIP). WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1000).AddNextHop(1100, 1), ) if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) @@ -1233,11 +1355,15 @@ func TestEncapFrr(t *testing.T) { if tc.encapUnviable == "primaryBackupRoutingAll" { args.client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(1000).WithDecapsulateHeader(fluent.IPinIP). + WithIndex(1100).WithDecapsulateHeader(fluent.IPinIP). WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1000).AddNextHop(1100, 1), fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(1001).WithDecapsulateHeader(fluent.IPinIP). + WithIndex(1101).WithDecapsulateHeader(fluent.IPinIP). WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1001).AddNextHop(1101, 1), ) if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) @@ -1250,7 +1376,7 @@ func TestEncapFrr(t *testing.T) { fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(1003).AddNextHop(1003, 1), fluent.IPv4Entry().WithNetworkInstance(niEncapTeVrfA). - WithPrefix("0.0.0.0/0").WithNextHopGroup(1003), + WithPrefix("0.0.0.0/0").WithNextHopGroup(1003).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)), ) if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) diff --git a/feature/gribi/otg_tests/encap_frr/metadata.textproto b/feature/gribi/otg_tests/encap_frr/metadata.textproto index 1d9a9ef004d..a0b9f7a1b75 100644 --- a/feature/gribi/otg_tests/encap_frr/metadata.textproto +++ b/feature/gribi/otg_tests/encap_frr/metadata.textproto @@ -39,5 +39,13 @@ platform_exceptions: { deviations: { interface_enabled: true default_network_instance: "default" + isis_instance_enabled_required: true + static_protocol_name: "STATIC" + gribi_mac_override_static_arp_static_route: true + ttl_copy_unsupported: true + omit_l2_mtu: true + backup_nhg_requires_vrf_with_decap: true + missing_isis_interface_afi_safi_enable: true + isis_interface_afi_unsupported: true } } diff --git a/feature/gribi/otg_tests/encap_frr_with_repair_vrf/README.md b/feature/gribi/otg_tests/encap_frr_with_repair_vrf/README.md new file mode 100644 index 00000000000..3298b85d563 --- /dev/null +++ b/feature/gribi/otg_tests/encap_frr_with_repair_vrf/README.md @@ -0,0 +1,617 @@ +# TE-16.3: encapsulation FRR scenarios + +## Summary + +Test FRR behaviors with encapsulation scenarios. + +## Topology + +- ATE port-1 <------> port-1 DUT +- DUT port-2 <------> port-2 ATE +- DUT port-3 <------> port-3 ATE +- DUT port-4 <------> port-4 ATE +- DUT port-5 <------> port-5 ATE +- DUT port-6 <------> port-6 ATE +- DUT port-7 <------> port-7 ATE +- DUT port-8 <------> port-8 ATE + +## Baseline setup + +* Apply the following vrf selection policy to DUT port-1 + +``` +# DSCP value that will be matched to ENCAP_TE_VRF_A +* dscp_encap_a_1 = 10 +* dscp_encap_a_2 = 18 + +# DSCP value that will be matched to ENCAP_TE_VRF_B +* dscp_encap_b_1 = 20 +* dscp_encap_b_2 = 28 + +# DSCP value that will NOT be matched to any VRF for encapsulation. +* dscp_encap_no_match = 30 + +# Magic source IP addresses used in VRF selection policy +* ipv4_outer_src_111 = 198.51.100.111 +* ipv4_outer_src_222 = 198.51.100.222 + +# Magic destination MAC address +* magic_mac = 02:00:00:00:00:01` +``` + +``` +network-instances { + network-instance { + name: DEFAULT + policy-forwarding { + policies { + policy { + policy-id: "vrf_selection_policy_c" + rules { + rule { + sequence-id: 1 + ipv4 { + protocol: 4 + dscp-set: [dscp_encap_a_1, dscp_encap_a_2] + source-address: "ipv4_outer_src_222" + } + action { + decap-network-instance: "DECAP_TE_VRF" + post-network-instance: "ENCAP_TE_VRF_A" + decap-fallback-network-instance: "TE_VRF_222" + } + } + rule { + sequence-id: 2 + ipv4 { + protocol: 41 + dscp-set: [dscp_encap_a_1, dscp_encap_a_2] + source-address: "ipv4_outer_src_222" + } + action { + decap-network-instance: "DECAP_TE_VRF" + post-network-instance: "ENCAP_TE_VRF_A" + decap-fallback-network-instance: "TE_VRF_222" + } + } + rule { + sequence-id: 3 + ipv4 { + protocol: 4 + dscp-set: [dscp_encap_a_1, dscp_encap_a_2] + source-address: "ipv4_outer_src_111" + } + action { + decap-network-instance: "DECAP_TE_VRF" + post-network-instance: "ENCAP_TE_VRF_A" + decap-fallback-network-instance: "TE_VRF_111" + } + } + rule { + sequence-id: 4 + ipv4 { + protocol: 41 + dscp-set: [dscp_encap_a_1, dscp_encap_a_2] + source-address: "ipv4_outer_src_111" + } + action { + decap-network-instance: "DECAP_TE_VRF" + post-network-instance: "ENCAP_TE_VRF_A" + decap-fallback-network-instance: "TE_VRF_111" + } + } + rule { + sequence-id: 5 + ipv4 { + protocol: 4 + dscp-set: [dscp_encap_b_1, dscp_encap_b_2] + source-address: "ipv4_outer_src_222" + } + action { + decap-network-instance: "DECAP_TE_VRF" + post-network-instance: "ENCAP_TE_VRF_B" + decap-fallback-network-instance: "TE_VRF_222" + } + } + rule { + sequence-id: 6 + ipv4 { + protocol: 41 + dscp-set: [dscp_encap_b_1, dscp_encap_b_2] + source-address: "ipv4_outer_src_222" + } + action { + decap-network-instance: "DECAP_TE_VRF" + post-network-instance: "ENCAP_TE_VRF_B" + decap-fallback-network-instance: "TE_VRF_222" + } + } + rule { + sequence-id: 7 + ipv4 { + protocol: 4 + dscp-set: [dscp_encap_b_1, dscp_encap_b_2] + source-address: "ipv4_outer_src_111" + } + action { + decap-network-instance: "DECAP_TE_VRF" + post-network-instance: "ENCAP_TE_VRF_B" + decap-fallback-network-instance: "TE_VRF_111" + } + } + rule { + sequence-id: 8 + ipv4 { + protocol: 41 + dscp-set: [dscp_encap_b_1, dscp_encap_b_2] + source-address: "ipv4_outer_src_111" + } + action { + decap-network-instance: "DECAP_TE_VRF" + post-network-instance: "ENCAP_TE_VRF_B" + decap-fallback-network-instance: "TE_VRF_111" + } + } + rule { + sequence-id: 9 + ipv4 { + protocol: 4 + source-address: "ipv4_outer_src_222" + } + action { + decap-network-instance: "DECAP_TE_VRF" + post-network-instance: "DEFAULT" + decap-fallback-network-instance: "TE_VRF_222" + } + } + rule { + sequence-id: 10 + ipv4 { + protocol: 41 + source-address: "ipv4_outer_src_222" + } + action { + decap-network-instance: "DECAP_TE_VRF" + post-network-instance: "DEFAULT" + decap-fallback-network-instance: "TE_VRF_222" + } + } + rule { + sequence-id: 11 + ipv4 { + protocol: 4 + source-address: "ipv4_outer_src_111" + } + action { + decap-network-instance: "DECAP_TE_VRF" + post-network-instance: "DEFAULT" + decap-fallback-network-instance: "TE_VRF_111" + } + } + rule { + sequence-id: 12 + ipv4 { + protocol: 41 + source-address: "ipv4_outer_src_111" + } + action { + decap-network-instance: "DECAP_TE_VRF" + post-network-instance: "DEFAULT" + decap-fallback-network-instance: "TE_VRF_111" + } + } + rule { + sequence-id: 13 + ipv4 { + dscp-set: [dscp_encap_a_1, dscp_encap_a_2] + } + action { + network-instance: "ENCAP_TE_VRF_A" + } + } + rule { + sequence-id: 14 + ipv6 { + dscp-set: [dscp_encap_a_1, dscp_encap_a_2] + } + action { + network-instance: "ENCAP_TE_VRF_A" + } + } + rule { + sequence-id: 15 + ipv4 { + dscp-set: [dscp_encap_b_1, dscp_encap_b_2] + } + action { + network-instance: "ENCAP_TE_VRF_B" + } + } + rule { + sequence-id: 16 + ipv6 { + dscp-set: [dscp_encap_b_1, dscp_encap_b_2] + } + action { + network-instance: "ENCAP_TE_VRF_B" + } + } + rule { + sequence-id: 17 + action { + network-instance: "DEFAULT" + } + } + } + } + } + } + } +} +``` + +* Using gRIBI, install the following gRIBI AFTs, and validate the specified + behavior. + +``` +IPv4Entry {138.0.11.0/24 (ENCAP_TE_VRF_A)} -> NHG#101 (DEFAULT VRF) -> { + {NH#101, DEFAULT VRF, weight:1}, + {NH#102, DEFAULT VRF, weight:3}, + backup_next_hop_group: 2001 // fallback to DEFAULT VRF +} + +NHG#2001 (DEFAULT VRF) { + {NH#2001, DEFAULT VRF} +} +NH#2001 -> { + network_instance: "DEFAULT" +} + +NH#101 -> { + encapsulate_header: OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_IPV4 + ip_in_ip { + dst_ip: "203.0.113.1" + src_ip: "ipv4_outer_src_111" + } + network_instance: "TE_VRF_111" +} +NH#102 -> { + encapsulate_header: OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_IPV4 + ip_in_ip { + dst_ip: "203.0.113.2" + src_ip: "ipv4_outer_src_111" + } + network_instance: "TE_VRF_111" +} + + +IPv4Entry {203.0.113.1/32 (TE_VRF_111)} -> NHG#1 (DEFAULT VRF) -> { + {NH#1, DEFAULT VRF, weight:1,ip_address=192.0.2.101}, + {NH#2, DEFAULT VRF, weight:3,ip_address=192.0.2.102}, + backup_next_hop_group: 3000 // Go to REPAIR VRF +} +IPv4Entry {192.0.2.101/32 (DEFAULT VRF)} -> NHG#11 (DEFAULT VRF) -> { + {NH#11, DEFAULT VRF, weight:1,mac_address:magic_mac, interface-ref:dut-port-2-interface}, + {NH#12, DEFAULT VRF, weight:3,mac_address:magic_mac, interface-ref:dut-port-3-interface}, +} +IPv4Entry {192.0.2.102/32 (DEFAUlT VRF)} -> NHG#12 (DEFAULT VRF) -> { + {NH#13, DEFAULT VRF, weight:2,mac_address:magic_mac, interface-ref:dut-port-4-interface}, +} + +NHG#3000 (DEFAULT VRF) { + {NH#3000, DEFAULT VRF} +} +NH#3000 -> { + network_instance: "REPAIR" +} + +IPv4Entry {203.0.113.1/32 (REPAIR)} -> NHG#1000 (DEFAULT VRF) -> { + {NH#1000, DEFAULT VRF} + backup_next_hop_group: 2000 // decap and fallback to DEFAULT VRF +} +NH#1000 -> { + decapsulate_header: OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_IPV4 + encapsulate_header: OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_IPV4 + ip_in_ip { + dst_ip: "203.0.113.100" + src_ip: "ipv4_outer_src_222" + } + network_instance: "TE_VRF_222" +} + +IPv4Entry {203.0.113.100/32 (TE_VRF_222)} -> NHG#2 (DEFAULT VRF) -> { + {NH#3, DEFAULT VRF, weight:1,ip_address=192.0.2.103}, + backup_next_hop_group: 2000 // decap and fallback to DEFAULT VRF +} +IPv4Entry {192.0.2.103/32 (DEFAULT VRF)} -> NHG#13 (DEFAULT VRF) -> { + {NH#14, DEFAULT VRF, weight:1,mac_address:magic_mac, interface-ref:dut-port-5-interface}, +} + +// 203.0.113.2 is the tunnel IP address. Note that the NHG#3 is different than NHG#1. + +IPv4Entry {203.0.113.2/32 (TE_VRF_111)} -> NHG#3 (DEFAULT VRF) -> { + {NH#4, DEFAULT VRF, weight:1,ip_address=192.0.2.104}, + backup_next_hop_group: 3000 // Go to REPAIR VRF +} + +IPv4Entry {192.0.2.104/32 (DEFAULT VRF)} -> NHG#14 (DEFAULT VRF) -> { + {NH#15, DEFAULT VRF, weight:1,mac_address:magic_mac, interface-ref:dut-port-6-interface}, +} + +IPv4Entry {203.0.113.2/32 (REPAIR)} -> NHG#1001 (DEFAULT VRF) -> { + {NH#1001, DEFAULT VRF} + backup_next_hop_group: 2000 // decap and fallback to DEFAULT VRF +} +NHG#1001 (DEFAULT VRF) { + {NH#1001, DEFAULT VRF} +} +NH#1001 -> { + decapsulate_header: OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_IPV4 + encapsulate_header: OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_IPV4 + ip_in_ip { + dst_ip: "203.0.113.101" + src_ip: "ipv4_outer_src_222" + } + network_instance: "TE_VRF_222" +} + +IPv4Entry {203.0.113.101/32 (TE_VRF_222)} -> NHG#4 (DEFAULT VRF) -> { + {NH#5, DEFAULT VRF, weight:1,ip_address=192.0.2.105}, + backup_next_hop_group: 2000 // decap and fallback to DEFAULT VRF +} +IPv4Entry {192.0.2.105/32 (DEFAULT VRF)} -> NHG#15 (DEFAULT VRF) -> { + {NH#16, DEFAULT VRF, weight:1,mac_address:magic_mac, interface-ref:dut-port-7-interface}, +} + +NHG#2000 (DEFAULT VRF) { + {NH#2000, DEFAULT VRF} +} +NH#2000 -> { + decapsulate_header: OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_IPV4 + network_instance: "DEFAULT" +} +``` + +* Install a BGP route resolved by ISIS in default VRF to route 138.0.11.8 + traffic out of DUT port-8. + +## Procedure + +At the start of each of the following scenarios, ensure: + +* All ports are up and baseline is reset as above. +* Send packets to DUT port-1. The outer v4 header has the destination + addresses 138.0.11.8. +* Validate that traffic is encapsulated to 203.0.113.1 and 203.0.113.2, and is + distributed per the hierarchical weights. + +Unless otherwise specified, all the tests below should use traffic with +`dscp_encap_a_1`. + +#### Test-1, primary encap unviable but backup encap viable for single tunnel + +Tests that if the primary NHG for an encap tunnel is unviable, then the traffic +for that tunnel is re-encaped into its specified backup tunnel. + +1. Shutdown DUT port-2, port-3, and port-4. +2. Validate that corresponding traffic that was encapped to 203.0.113.1 should + now be encapped with 203.0.113.100. + +#### Test-2, primary and backup encap unviable for single tunnel + +Tests that if the primary NHGs of both the encap tunnel and its backup tunnel +are unviable, then the traffic for that tunnel is not encapped. Instead, that +fraction of traffic should be forwarded according to the BGP/IS-IS routes in the +DEFAULT VRF. + +1. Shutdown DUT port-2, port-3, port-4 and port-5. +2. Validate that corresponding traffic (25% of the total traffic) that was + encapped to 203.0.113.1 are no longer encapped, and forwarded per BGP-ISIS + routes (in the default VRF) out of DUT port-8. + +#### Test-3, primary encap unviable with backup to routing for single tunnel + +Tests that if the primary NHGs of both the encap tunnel is unviable, and its +backup specifies fallback to routing, then the traffic for that tunnel is not +encapped. Instead, that fraction of traffic should be forwarded according to the +BGP/IS-IS routes in the DEFAULT VRF. + +1. Update `NHG#1000` to the following: + +``` +NHG#1000 (Default VRF) { + {NH#1000, DEFAULT VRF} +} +NH#1000 -> { + decapsulate_header: OPENCONFIGAFTTYPESDECAPSULATIONHEADERTYPE_IPV4 + network_instance: "DEFAULT" +} +``` + +1. Validate that all traffic is distributed per the hierarchical weights. +2. Shutdown DUT port-2, port-3, and port-4. +3. Validate that corresponding traffic (25% of the total traffic) that was + encapped to 203.0.113.1 are no longer encapped, and forwarded per BGP-ISIS + routes (in the default VRF) out of DUT port-8. + +#### Test-4, primary encap unviable but backup encap viable for all tunnels + +Tests that if the primary NHG for all encap tunnels are unviable, then the +traffic is re-encaped into the specified backup tunnels. This test ensures that +the device does not withdraw this IPv4Entry and sends this traffic to routing. + +1. Shutdown DUT port-2, port-3, port-4 and port-6. +2. Validate that traffic is encapsulated to 203.0.113.100 and 203.0.113.101 per + the weights. + +#### Test-5, primary and backup encap unviable for all tunnels + +Tests that if the primary NHGs of both the encap tunnel and its backup tunnel +are unviable for all tunnels in the encap NHG, then the traffic for that cluster +prefix is not encapped. Instead, that traffic should be forwarded according to +the BGP/IS-IS routes in the DEFAULT VRF. This stresses the double failure +handling, and ensures that the fallback to DEFAULT is activated through the +backup NHGs of the tunnels instead of withdrawing the IPv4Entry. + +1. Shutdown DUT port-2, port-3, port-4, port-5, port-6 and port-7. +2. Validate that all traffic is no longer encapsulated, and is all egressing + out of DUT port-8 per the BGP-ISIS routes in the default VRF. + +#### Test-6, primary encap unviable with backup to routing for all tunnels + +Tests that if the primary NHGs of both the encap tunnel is unviable, and its +backup specifies fallback to routing, for all tunnels in the encap NHG, then the +traffic for that cluster prefix is not encapped. Instead, that traffic should be +forwarded according to the BGP/IS-IS routes in the DEFAULT VRF. This stresses +the double failure handling, and ensures that the fallback to DEFAULT is +activated through the backup NHGs of the tunnels instead of withdrawing the +IPv4Entry. + +1. Update `NHG#1000` and `NHG#1001` to the following: + +``` +NHG#1000 (Default VRF) { {NH#1000, DEFAULT VRF} } + +NH#1000 -> { + decapsulate_header: OPENCONFIGAFTTYPESDECAPSULATIONHEADERTYPE_IPV4 + network_instance: "DEFAULT" +} + +NHG#1001 (Default VRF) { {NH#1001, DEFAULT VRF} } + +NH#1001 -> { + decapsulate_header: OPENCONFIGAFTTYPESDECAPSULATIONHEADERTYPE_IPV4 + network_instance: "DEFAULT" +} +``` + +1. Validate that all traffic is distributed per the hierarchical weights. +2. Shutdown DUT port-2, port-3, and port-4, and port-6. +3. Validate that all traffic is no longer encapsulated, and is all egressing + out of DUT port-8 per the BGP-ISIS routes in the default VRF. + +#### Test-7, no match in encap VRF + +Test that if there is no lookup match in the encap VRF, then the traffic should +be routed to the DEFAULT VRF for further lookup. + +1. In `ENCAP_TE_VRF_A`, Add an 0/0 static route pointing to the DEFAULT VRF + using gNMI. +2. Send traffic with destination address 20.0.0.1, which should produce no + match in `ENCAP_TE_VRF_A`. +3. Validate that the traffic is routed per the BGP-ISIS routes (in the DEFAULT + VR) out of DUT port-8. + +#### Test-8, no match in TE_VRF_222 + +Tests that if the re-encaps point to tunnels that do not exist, then the traffic +should be routed to the `DEFAULT` VRF for further lookup. + +1. Update `NHG#1000` and `NHG#1001` to the following: + +``` +NHG#1000 (Default VRF) { {NH#1000, DEFAULT VRF} } + +NH#1000 -> { + decapsulate_header: OPENCONFIGAFTTYPESDECAPSULATIONHEADERTYPE_IPV4 + encapsulate_header: OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_IPV4 + ip_in_ip { + dst_ip: "203.100.113.100" // This route does not exist in TE_VRF_222 + src_ip: "ipv4_outer_src_222" + } + network_instance: "TE_VRF_222" +} + +NHG#1001 (Default VRF) { {NH#1001, DEFAULT VRF} } + +NH#1001 -> { + decapsulate_header: OPENCONFIGAFTTYPESDECAPSULATIONHEADERTYPE_IPV4 + encapsulate_header: OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_IPV4 + ip_in_ip { + dst_ip: "203.100.113.101" // This route does not exist in TE_VRF_222 + src_ip: "ipv4_outer_src_222" + } + network_instance: "TE_VRF_222" +} +``` + +1. Validate that all traffic is distributed per the hierarchical weights. +2. Shutdown DUT port-2, port-3, and port-4, and port-6. +3. Validate that all traffic is no longer encapsulated, and is all egressing + out of DUT port-8 per the BGP-ISIS routes in the default VRF. + +#### Test-8, no match in TE_VRF_111 + +Tests that if the primary encaps point to tunnels that do not exist, then the +traffic should be routed to the `DEFAULT` VRF for further lookup. + +1. Validate that all traffic is distributed per the hierarchical weights. +2. Update NHG#101 to the following: + +``` +NHG#101 (DEFAULT VRF) -> { + {NH#101, DEFAULT VRF, weight:1}, + {NH#102, DEFAULT VRF, weight:3}, + backup_next_hop_group: 2001 // fallback to DEFAULT VRF +} + +NH#101 -> { + encapsulate_header: OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_IPV4 + ip_in_ip { + dst_ip: "203.100.113.1" // This route does not exist in TE_VRF_111 + src_ip: "ipv4_outer_src_111" + } + network_instance: "TE_VRF_111" +} +NH#102 -> { + encapsulate_header: OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_IPV4 + ip_in_ip { + dst_ip: "203.100.113.2" // This route does not exist in TE_VRF_111 + src_ip: "ipv4_outer_src_111" + } + network_instance: "TE_VRF_111" +} +``` + +1. Validate that all traffic is no longer encapsulated, and is all egressing + out of DUT port-8 per the BGP-ISIS routes in the default VRF. + +## Config Parameter Coverage + +* network-instances/network-instance/name +* network-instances/network-instance/policy-forwarding/policies/policy/policy-id +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/sequence-id +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv4/protocol +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv4/dscp-set +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv4/source-address +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv6/protocol +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv6/dscp-set +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv6/source-address +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/decap-network-instance +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/post-network-instance +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/decap-fallback-network-instance + +## Telemetry Parameter Coverage + +* network-instances/network-instance/name +* network-instances/network-instance/policy-forwarding/policies/policy/policy-id +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/sequence-id +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv4/protocol +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv4/dscp-set +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv4/source-address +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv6/protocol +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv6/dscp-set +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv6/source-address +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/decap-network-instance +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/post-network-instance +* network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/decap-fallback-network-instance + +## Protocol/RPC Parameter Coverage + +* gRIBI: + * Modify + * ModifyRequest + +## Required DUT platform + +- vRX diff --git a/feature/gribi/otg_tests/hierarchical_weight_resolution_pbf_test/README.md b/feature/gribi/otg_tests/hierarchical_weight_resolution_pbf_test/README.md new file mode 100644 index 00000000000..3dbc48f28fc --- /dev/null +++ b/feature/gribi/otg_tests/hierarchical_weight_resolution_pbf_test/README.md @@ -0,0 +1,149 @@ +# TE-3.31: Hierarchical weight resolution with PBF + +## Summary + +Ensures that next-hop weights (for WCMP) are honored hierarchically in gRIBI +recursive resolution and traffic is load-shared according to these weights. + +## Procedure + +Configure ATE and DUT: + +* Connect ATE port-1 to DUT port-1. ATE port-2 to DUT port-2. + +* Create a non-default VRF (VRF-1) that contains no interfaces. + +* On DUT port-2 and ATE port-2 create 18 L3 sub-interfaces each with a /30 + subnet as below: + + * On DUT port-2, create subinterfaces with indices 1 to 18 mapped to VLAN + IDs 1 to 18 and corressponding IPv4 addresses 192.0.2.5, 192.0.2.9, ..., + 192.0.2.73 respectively. + + * On ATE port-2, create subinterfaces with indices 1 to 18 mapped to VLAN + IDs 1 to 18 and corresponding IPv4 addresses 192.0.2.6, 192.0.2.10, ..., + 192.0.2.74 and default gateways as 192.0.2.5, 192.0.2.9, ..., 192.0.2.73 + respectively. + +* On DUT port-1 and ATE port-1 create a single L3 interface. + +* On DUT, create a policy-based forwarding rule to redirect all traffic + received from DUT port-1 into VRF-1 (based on src. IP match criteria). + +* Add an empty decap VRF, `DECAP_TE_VRF`. + +* Add 4 empty encap VRFs, `ENCAP_TE_VRF_A`, `ENCAP_TE_VRF_B`, `ENCAP_TE_VRF_C` + and `ENCAP_TE_VRF_D`. + +* Replace the existing VRF selection policy with `vrf_selection_policy_w` as + in + +Test case for basic hierarchical weight: + +* Establish gRIBI client connection with DUT with PERSISTENCE, make it become + leader and install the following Entries: + + * IPv4Entry 203.0.113.0/32 in VRF-1, pointing to NextHopGroup(NHG#1) in + default VRF, with two NextHops(NH#1, NH#2) in default VRF: + + * NH#1 with weight:1, pointing to 192.0.2.111 + + * NH#2 with weight:3, pointing to 192.0.2.222 + + * IPv4Entry 192.0.2.111/32 in default VRF, pointing to NextHopGroup(NHG#2) + in default VRF, with two NextHops(NH#10, NH#11) in default VRF: + + * NH#10 with weight:1, pointing to 192.0.2.10 + + * NH#11 with weight:3, pointing to 192.0.2.14 + + * IPv4Entry 192.0.2.222/32 in default VRF, pointing to NextHopGroup(NHG#3) + in default VRF, with two NextHops(NH#100, NH#101) in default VRF: + + * NH#100 with weight:3, pointing to 192.0.2.18 + + * NH#101 with weight:5, pointing to 192.0.2.22 + +* Validate with traffic: + + * NH10: (1/4) * (1/4) = 6.25% traffic received by ATE port-2 VLAN 1 + + * NH11: (1/4) * (3/4) = 18.75% traffic received by ATE port-2 VLAN 2 + + * NH100: (3/4) * (3/8) = 28.12% traffic received by ATE port-2 VLAN 3 + + * NH101: (3/4) * (5/8) = 46.87% traffic received by ATE port-2 VLAN 4 + + * A tolerance of 0.2% is allowed for each VLAN for now, since we only test + for 2 mins. + +Test case for hierarchical weight in boundary scenarios, with maximum expected +WCMP width of 16 nexthops: + +* Flush previous gRIBI Entries for all NIs and establish a new connection with + DUT with PERSISTENCE and install the following Entries: + + * IPv4Entry 203.0.113.0/32 in VRF-1, pointing to NextHopGroup(NHG#1) in + default VRF, with two NextHops(NH#1, NH#2) in default VRF: + + * NH#1 with weight:1, pointing to 192.0.2.111 + + * NH#2 with weight:31, pointing to 192.0.2.222 + + * IPv4Entry 192.0.2.111/32 in default VRF, pointing to NextHopGroup(NHG#2) + in default VRF, with two NextHops(NH#10, NH#11) in default VRF: + + * NH#10 with weight:3, pointing to 192.0.2.10 + + * NH#11 with weight:5, pointing to 192.0.2.14 + + * IPv4Entry 192.0.2.222/32 in default VRF, pointing to NextHopGroup(NHG#3) + in default VRF, with 16 NextHops(NH#100, NH#101, ..., NH#115), all with + weight: 16 except NHG#100 is of weight 1, in default VRF: + + * NH#100 with weight:1, pointing to 192.0.2.18 + + * NH#101 with weight:16, pointing to 192.0.2.22 + + * ... + + * NH#115 with weight:16, pointing to 192.0.2.79 + +* Validate with traffic: + + * NH10: (1/32) * (3/8) ~ 1.171% traffic received by ATE port-2 VLAN 1 + + * NH11: (1/32) * (5/8) ~ 1.953% traffic received by ATE port-2 VLAN 2 + + * NH100: (31/32) * (1/241) ~ 0.402% traffic received by ATE port-2 VLAN 3 + + * for each VLAN ID in 4...18: + + * NH: (31/32) * (16/241) ~ 6.432% traffic received by ATE port-2 VLAN + ID + + * A tolerance of 0.2% is allowed for each VLAN for now, since we only test + for 2 mins. + +## Config Parameter Coverage + +N/A + +## Telemetry Parameter Coverage + +TODO: +/network-instances/network-instance/afts/next-hop-groups/next-hop-group/next-hops/next-hop/state/weight + +## Protocol/RPC Parameter coverage + +* gRIBI: + * Modify() + * ModifyRequest: + * AFTOperation: + * next_hop_group + * NextHopGroupKey: id + * NextHopGroup: weight + +## Minimum DUT platform requirement + +vRX diff --git a/feature/gribi/otg_tests/hierarchical_weight_resolution_pbf_test/hierarchical_weight_resolution_pbf_test.go b/feature/gribi/otg_tests/hierarchical_weight_resolution_pbf_test/hierarchical_weight_resolution_pbf_test.go new file mode 100644 index 00000000000..2291b2aad96 --- /dev/null +++ b/feature/gribi/otg_tests/hierarchical_weight_resolution_pbf_test/hierarchical_weight_resolution_pbf_test.go @@ -0,0 +1,751 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package hierarchical_weight_resolution_test implements TE-3.3 of the Popgate vendor testplan +package hierarchical_weight_resolution_pbf_test + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "net" + "strconv" + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/gribi" + "github.com/openconfig/featureprofiles/internal/otgutils" + "github.com/openconfig/featureprofiles/internal/vrfpolicy" + "github.com/openconfig/gribigo/chk" + "github.com/openconfig/gribigo/constants" + "github.com/openconfig/gribigo/fluent" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ygot/ygot" +) + +type attributes struct { + attrs.Attributes + numSubIntf uint32 + ip func(vlan uint8) string + gateway func(vlan uint8) string +} + +type nhInfo struct { + index uint64 + weight uint64 +} + +const ( + ipv4EntryPrefix = "203.0.113.0/32" + ipv4FlowIP = "203.0.113.0" + innerSrcIPv4Start = "198.18.0.0" + innerDstIPv4Start = "198.19.0.0" + ipv4PrefixLen = 30 + ipv4FlowCount = 65000 + nhEntryIP1 = "192.0.2.111" + nhEntryIP2 = "192.0.2.222" + nonDefaultVRF = "TE_VRF_111" + policyName = "redirect-to-VRF1" + ipipProtocol = 4 + decapFlowSrc = "198.51.100.111" + dscpEncapA1 = 10 +) + +var ( + dutPort1 = attributes{ + Attributes: attrs.Attributes{ + Desc: "dutPort1", + Name: "port1", + IPv4: dutPort1IPv4(0), + IPv4Len: ipv4PrefixLen, + }, + numSubIntf: 0, + ip: dutPort1IPv4, + } + + atePort1 = attributes{ + Attributes: attrs.Attributes{ + Name: "port1", + MAC: "02:00:01:01:01:01", + IPv4: atePort1IPv4(0), + IPv4Len: ipv4PrefixLen, + }, + numSubIntf: 0, + ip: atePort1IPv4, + gateway: dutPort1IPv4, + } + + dutPort2 = attributes{ + Attributes: attrs.Attributes{ + Desc: "dutPort2", + Name: "port2", + IPv4: dutPort2IPv4(0), + IPv4Len: ipv4PrefixLen, + }, + numSubIntf: 18, + ip: dutPort2IPv4, + } + + atePort2 = attributes{ + Attributes: attrs.Attributes{ + Name: "port2", + MAC: "02:00:02:01:01:01", + IPv4: atePort2IPv4(0), + IPv4Len: ipv4PrefixLen, + }, + numSubIntf: 18, + ip: atePort2IPv4, + gateway: dutPort2IPv4, + } + + // nhgIPv4EntryMap maps NextHopGroups to the ipv4 entries pointing to that NextHopGroup. + nhgIPv4EntryMap = map[uint64]string{ + 1: ipv4EntryPrefix, + 2: cidr(nhEntryIP1, 32), + 3: cidr(nhEntryIP2, 32), + } + // 'tolerance' is the maximum difference that is allowed between the observed + // traffic distribution and the required traffic distribution. + tolerance = 0.2 +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +// dutPort1IPv4 returns ip address 192.0.2.1, for every vlanID. +func dutPort1IPv4(uint8) string { + return "192.0.2.1" +} + +// atePort1IPv4 returns ip address 192.0.2.2, for every vlanID +func atePort1IPv4(uint8) string { + return "192.0.2.2" +} + +// dutPort2IPv4 returns ip addresses starting 192.0.2.5, increasing by 4 +// for every vlanID. +func dutPort2IPv4(vlan uint8) string { + return fmt.Sprintf("192.0.2.%d", vlan*4+5) +} + +// atePort2IPv4 returns ip addresses starting 192.0.2.6, increasing by 4 +// for every vlanID. +func atePort2IPv4(vlan uint8) string { + return fmt.Sprintf("192.0.2.%d", vlan*4+6) +} + +// cidr taks as input the IPv4 address and the Mask and returns the IP string in +// CIDR notation. +func cidr(ipv4 string, ones int) string { + return ipv4 + "/" + strconv.Itoa(ones) +} + +// filterPacketReceived uses ATE:EgressTracking bucket counters to create a map +// with bucket-label as the Key and the percentage of packets-received for that +// bucket as the Value. +func filterPacketReceived(t *testing.T, flow string, ate *ondatra.ATEDevice) map[string]float64 { + t.Helper() + + // Check the egress packets + vlanTags := gnmi.GetAll(t, ate.OTG(), gnmi.OTG().Flow(flow).TaggedMetricAny().State()) + tags := gnmi.GetAll(t, ate.OTG(), gnmi.OTG().Flow(flow).TaggedMetricAny().TagsAny().State()) + t.Logf("There are a total of %v vlans", len(tags)) + + inPkts := map[string]uint64{} + for i, tag := range tags { + vlanHex := strings.Replace(tag.GetTagValue().GetValueAsHex(), "0x", "", -1) + vlanDec, _ := strconv.ParseUint(vlanHex, 16, 64) + inPkts[strconv.Itoa(int(vlanDec))] = vlanTags[i].GetCounters().GetInPkts() + } + inPct := map[string]float64{} + total := gnmi.Get(t, ate.OTG(), gnmi.OTG().Flow(flow).Counters().InPkts().State()) + for k, v := range inPkts { + inPct[k] = (float64(v) / float64(total)) * 100.0 + } + return inPct +} + +// configureGRIBIClient configures a new GRIBI client with PRESERVE and FIB_ACK. +func configureGRIBIClient(t *testing.T, dut *ondatra.DUTDevice) *fluent.GRIBIClient { + t.Helper() + gribic := dut.RawAPIs().GRIBI(t) + + // Configure the gRIBI client. + c := fluent.NewClient() + c.Connection(). + WithStub(gribic). + WithRedundancyMode(fluent.ElectedPrimaryClient). + WithInitialElectionID(1 /* low */, 0 /* hi */). + WithPersistence(). + WithFIBACK() + + return c +} + +// nextHopEntry configures a fluent.GRIBIEntry for a NextHopEntry. +func nextHopEntry(index uint64, networkInstance string, ipAddr string) fluent.GRIBIEntry { + return fluent.NextHopEntry(). + WithNetworkInstance(networkInstance). + WithIndex(index). + WithIPAddress(ipAddr) +} + +// nextHopGroupEntry configures a fluent.GRIBIEntry for a NextHopGroupEntry. +func nextHopGroupEntry(index uint64, networkInstance string, nhs []nhInfo) fluent.GRIBIEntry { + x := fluent.NextHopGroupEntry(). + WithNetworkInstance(networkInstance). + WithID(index) + for _, nh := range nhs { + x.AddNextHop(nh.index, nh.weight) + } + return x +} + +// ipv4Entry configures a fluent.GRIBIEntry for an IPv4Entry. +func ipv4Entry(prefix string, networkInstance string, nhgIndex uint64, nextHopGroupNetworkInstance string) fluent.GRIBIEntry { + return fluent.IPv4Entry(). + WithPrefix(prefix). + WithNetworkInstance(networkInstance). + WithNextHopGroup(nhgIndex). + WithNextHopGroupNetworkInstance(nextHopGroupNetworkInstance) +} + +// awaitTimeout calls a fluent client Await, adding a timeout to the context. +func awaitTimeout(ctx context.Context, c *fluent.GRIBIClient, t testing.TB, timeout time.Duration) error { + t.Helper() + subctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + return c.Await(subctx, t) +} + +// configSubinterfaceDUT configures the Sub Interfaces of an Interfaces, +// starting from Sub Interface 1. Each Subinterface is configured with a +// unique VlanID starting from 1 and an IP address. The starting IP Address +// for Subinterface(1) = dutPort.ip(1) = dutPort.ip + 4. +func (a *attributes) configSubinterfaceDUT(t *testing.T, intf *oc.Interface, dut *ondatra.DUTDevice) { + t.Helper() + if deviations.RequireRoutedSubinterface0(dut) { + s0 := intf.GetOrCreateSubinterface(0).GetOrCreateIpv4() + s0.Enabled = ygot.Bool(true) + } + for i := uint32(1); i <= a.numSubIntf; i++ { + ip := a.ip(uint8(i)) + + s := intf.GetOrCreateSubinterface(i) + if deviations.InterfaceEnabled(dut) { + s.Enabled = ygot.Bool(true) + } + if deviations.DeprecatedVlanID(dut) { + s.GetOrCreateVlan().VlanId = oc.UnionUint16(i) + } else { + s.GetOrCreateVlan().GetOrCreateMatch().GetOrCreateSingleTagged().VlanId = ygot.Uint16(uint16(i)) + } + s4 := s.GetOrCreateIpv4() + if deviations.InterfaceEnabled(dut) && !deviations.IPv4MissingEnabled(dut) { + s4.Enabled = ygot.Bool(true) + } + s4a := s4.GetOrCreateAddress(ip) + s4a.PrefixLength = ygot.Uint8(a.IPv4Len) + t.Logf("Adding DUT Subinterface with ID: %d, Vlan ID: %d and IPv4 address: %s", i, i, ip) + } +} + +// configInterfaceDUT configures the DUT interface with the provided IP Address. +// Sub Interfaces are also configured if numSubIntf > 0. +func (a *attributes) configInterfaceDUT(t *testing.T, d *ondatra.DUTDevice) { + t.Helper() + p := d.Port(t, a.Name) + i := &oc.Interface{Name: ygot.String(p.Name())} + + if a.numSubIntf > 0 { + i.Description = ygot.String(a.Desc) + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + if deviations.InterfaceEnabled(d) { + i.Enabled = ygot.Bool(true) + } + } else { + i = a.NewOCInterface(p.Name(), d) + } + + if deviations.ExplicitPortSpeed(d) { + i.GetOrCreateEthernet().PortSpeed = fptest.GetIfSpeed(t, p) + } + + a.configSubinterfaceDUT(t, i, d) + intfPath := gnmi.OC().Interface(p.Name()) + gnmi.Replace(t, d, intfPath.Config(), i) + fptest.LogQuery(t, "DUT", intfPath.Config(), gnmi.Get(t, d, intfPath.Config())) +} + +func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { + // configure NI. + configureNetworkInstance(t, dut) + + // Configure DUT ports. + dutPort1.configInterfaceDUT(t, dut) + dutPort2.configInterfaceDUT(t, dut) + + // assign subinterfaces to DEFAULT network instance if needed (deviation-based). + dutPort1.assignSubifsToDefaultNetworkInstance(t, dut) + dutPort2.assignSubifsToDefaultNetworkInstance(t, dut) + + // apply PBF to src interface. + dp1 := dut.Port(t, dutPort1.Name) + applyForwardingPolicy(t, dp1.Name()) +} + +// configureNetworkInstance creates and configures non-default and default NIs. +func configureNetworkInstance(t *testing.T, d *ondatra.DUTDevice) { + t.Helper() + + // configure non-default VRF + ni := &oc.NetworkInstance{ + Name: ygot.String(nonDefaultVRF), + Type: oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_L3VRF, + } + dni := gnmi.OC().NetworkInstance(nonDefaultVRF) + gnmi.Replace(t, d, dni.Config(), ni) + fptest.LogQuery(t, "NI", dni.Config(), gnmi.Get(t, d, dni.Config())) + + // configure PBF in DEFAULT vrf + defNIPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(d)) + fptest.ConfigureDefaultNetworkInstance(t, d) + gnmi.Replace(t, d, defNIPath.PolicyForwarding().Config(), configurePBF(d)) + +} + +// assignSubifsToDefaultNetworkInstance assign subinterfaces to the default network instance when ExplicitInterfaceInDefaultVRF is enabled. +func (a *attributes) assignSubifsToDefaultNetworkInstance(t *testing.T, d *ondatra.DUTDevice) { + p := d.Port(t, a.Name) + if deviations.ExplicitInterfaceInDefaultVRF(d) { + if a.numSubIntf == 0 { + fptest.AssignToNetworkInstance(t, d, p.Name(), deviations.DefaultNetworkInstance(d), 0) + } else { + for i := uint32(1); i <= a.numSubIntf; i++ { + fptest.AssignToNetworkInstance(t, d, p.Name(), deviations.DefaultNetworkInstance(d), i) + } + } + } +} + +// configurePBF returns a fully configured network-instance PF struct. +func configurePBF(dut *ondatra.DUTDevice) *oc.NetworkInstance_PolicyForwarding { + d := &oc.Root{} + ni := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + pf := ni.GetOrCreatePolicyForwarding() + vrfPolicy := pf.GetOrCreatePolicy(policyName) + vrfPolicy.SetType(oc.Policy_Type_VRF_SELECTION_POLICY) + vrfPolicy.GetOrCreateRule(1).GetOrCreateIpv4().Protocol = oc.UnionUint8(ipipProtocol) + vrfPolicy.GetOrCreateRule(1).GetOrCreateAction().NetworkInstance = ygot.String(nonDefaultVRF) + return pf +} + +// applyForwardingPolicy applies the forwarding policy on the interface. +func applyForwardingPolicy(t *testing.T, ingressPort string) { + t.Logf("Applying forwarding policy on interface %v ... ", ingressPort) + d := &oc.Root{} + dut := ondatra.DUT(t, "dut") + interfaceID := ingressPort + if deviations.InterfaceRefInterfaceIDFormat(dut) { + interfaceID = ingressPort + ".0" + } + pfPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).PolicyForwarding().Interface(interfaceID) + pfCfg := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)).GetOrCreatePolicyForwarding().GetOrCreateInterface(interfaceID) + pfCfg.ApplyVrfSelectionPolicy = ygot.String(policyName) + pfCfg.GetOrCreateInterfaceRef().Interface = ygot.String(ingressPort) + pfCfg.GetOrCreateInterfaceRef().Subinterface = ygot.Uint32(0) + if deviations.InterfaceRefConfigUnsupported(dut) { + pfCfg.InterfaceRef = nil + } + gnmi.Replace(t, dut, pfPath.Config(), pfCfg) +} + +// configureATE configures Ethernet + IPv4 on the ATE. If the number of +// Subinterfaces(numSubIntf) > 0, we then create additional sub-interfaces +// each with a unique VlanID starting from 1. The IPv4 addresses start with +// ATE:Port.IPv4 and then nextIP(ATE:Port.IPv4, 4) for each sub interface. +func (a *attributes) configureATE(t *testing.T, top gosnappi.Config, ate *ondatra.ATEDevice) { + t.Helper() + p := ate.Port(t, a.Name) + + // Configure source port on ATE : Port1. + + top.Ports().Add().SetName(p.ID()) + if a.numSubIntf == 0 { + ip := a.ip(0) + gateway := a.gateway(0) + dev := top.Devices().Add().SetName(a.Name) + eth := dev.Ethernets().Add().SetName(a.Name + ".Eth").SetMac(a.MAC) + eth.Connection().SetPortName(p.ID()) + ipObj := eth.Ipv4Addresses().Add().SetName(dev.Name() + ".IPv4") + ipObj.SetAddress(ip).SetGateway(gateway).SetPrefix(uint32(a.IPv4Len)) + t.Logf("Adding ATE Ipv4 address: %s with gateway: %s", cidr(ip, int(a.IPv4Len)), gateway) + } + // Configure destination port on ATE : Port2. + for i := uint32(1); i <= a.numSubIntf; i++ { + name := fmt.Sprintf(`dst%d`, i) + ip := a.ip(uint8(i)) + gateway := a.gateway(uint8(i)) + mac, err := incrementMAC(a.MAC, int(i)+1) + if err != nil { + t.Fatalf("Failed to generate mac address with error %s", err) + } + + dev := top.Devices().Add().SetName(name + ".Dev") + eth := dev.Ethernets().Add().SetName(name + ".Eth").SetMac(mac) + eth.Connection().SetPortName(p.ID()) + eth.Vlans().Add().SetName(name).SetId(uint32(i)) + eth.Ipv4Addresses().Add().SetName(name + ".IPv4").SetAddress(ip).SetGateway(gateway).SetPrefix(uint32(a.IPv4Len)) + + t.Logf("Adding ATE Ipv4 address: %s with gateway: %s and VlanID: %d", cidr(ip, 30), gateway, i) + } + // } +} + +// incrementMAC increments the MAC by i. Returns error if the mac cannot be parsed or overflows the mac address space +func incrementMAC(mac string, i int) (string, error) { + macAddr, err := net.ParseMAC(mac) + if err != nil { + return "", err + } + convMac := binary.BigEndian.Uint64(append([]byte{0, 0}, macAddr...)) + convMac = convMac + uint64(i) + buf := new(bytes.Buffer) + err = binary.Write(buf, binary.BigEndian, convMac) + if err != nil { + return "", err + } + newMac := net.HardwareAddr(buf.Bytes()[2:8]) + return newMac.String(), nil +} + +// testTraffic creates a traffic flow with ATE source & destination endpoints +// and configures a VlanID filter for output frames. The IPv4 header for the +// flow contains the ATE:Port1 address as source and the configured gRIBI- +// IndirectEntry as the destination. The function also takes as input a map of +// that is wanted and compares it to the actual +// traffic test result. +func testTraffic(t *testing.T, ate *ondatra.ATEDevice, top gosnappi.Config) map[string]float64 { + + dut := ondatra.DUT(t, "dut") + dstMac := gnmi.Get(t, dut, gnmi.OC().Interface(dut.Port(t, "port1").Name()).Ethernet().MacAddress().State()) + top.Flows().Clear().Items() + flowipv4 := top.Flows().Add().SetName("flow") + flowipv4.Metrics().SetEnable(true) + flowipv4.TxRx().Port().SetTxName(atePort1.Name).SetRxNames([]string{atePort2.Name}) + flowipv4.Size().SetFixed(100) + e1 := flowipv4.Packet().Add().Ethernet() + e1.Src().SetValue(atePort1.MAC) + e1.Dst().SetValue(dstMac) + v4 := flowipv4.Packet().Add().Ipv4() + v4.Src().SetValue(decapFlowSrc) + v4.Priority().Dscp().Phb().SetValue(dscpEncapA1) + v4.Dst().SetValue(ipv4FlowIP) + v4Inner := flowipv4.Packet().Add().Ipv4() + v4Inner.Src().Increment().SetStart(innerSrcIPv4Start).SetCount(ipv4FlowCount) + v4Inner.Dst().Increment().SetStart(innerDstIPv4Start).SetCount(ipv4FlowCount) + flowipv4.EgressPacket().Add().Ethernet() + vlan := flowipv4.EgressPacket().Add().Vlan() + vlanTag := vlan.Id().MetricTags().Add() + vlanTag.SetName("EgressVlanIdTrackingFlow") + ate.OTG().PushConfig(t, top) + ate.OTG().StartProtocols(t) + otgutils.WaitForARP(t, ate.OTG(), top, "IPv4") + + // Run traffic for 2 minutes. + ate.OTG().StartTraffic(t) + time.Sleep(1 * time.Minute) + ate.OTG().StopTraffic(t) + + otgutils.LogFlowMetrics(t, ate.OTG(), top) + + recvMetric := gnmi.Get(t, ate.OTG(), gnmi.OTG().Flow(flowipv4.Name()).State()) + txPkts := float32(recvMetric.GetCounters().GetOutPkts()) + rxPkts := float32(recvMetric.GetCounters().GetInPkts()) + lossPct := (txPkts - rxPkts) * 100 / txPkts + if txPkts == 0 { + t.Fatalf("TxPkts == 0, want > 0.") + } + if lossPct > 0 && recvMetric.GetCounters().GetOutPkts() > 0 { + t.Fatalf("Loss Pct for %s got %v, want 0", flowipv4.Name(), lossPct) + } + + // Compare traffic distribution with the wanted results. + results := filterPacketReceived(t, "flow", ate) + t.Logf("Filters: %v", results) + return results +} + +// aftNextHopWeights queries AFT telemetry using Get() and returns +// the weights. If not-found, an empty list is returned. +func aftNextHopWeights(t *testing.T, dut *ondatra.DUTDevice, nhg uint64, networkInstance string) []uint64 { + aft := gnmi.Get(t, dut, gnmi.OC().NetworkInstance(networkInstance).Afts().State()) + var nhgD *oc.NetworkInstance_Afts_NextHopGroup + for _, nhgData := range aft.NextHopGroup { + if nhgData.GetProgrammedId() == nhg { + nhgD = nhgData + break + } + } + + if nhgD == nil { + return []uint64{} + } + + got := []uint64{} + for _, nhD := range nhgD.NextHop { + got = append(got, nhD.GetWeight()) + } + + return got +} + +// testBasicHierarchicalWeight tests and validates traffic through 4 Vlans. +func testBasicHierarchicalWeight(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, + ate *ondatra.ATEDevice, top gosnappi.Config, gRIBI *fluent.GRIBIClient) { + defaultVRF := deviations.DefaultNetworkInstance(dut) + + // Set up NH#10, NH#11, NHG#2, IPv4Entry(192.0.2.111). + nh10 := nextHopEntry(10, defaultVRF, atePort2.ip(1)) + nh11 := nextHopEntry(11, defaultVRF, atePort2.ip(2)) + nhg2 := nextHopGroupEntry(2, defaultVRF, []nhInfo{{index: 10, weight: 1}, {index: 11, weight: 3}}) + ipEntry2 := ipv4Entry(nhgIPv4EntryMap[2], defaultVRF, 2, defaultVRF) + + gRIBI.Modify().AddEntry(t, nh10, nh11, nhg2, ipEntry2) + + // Set up NH#100, NH#101, NHG#3, IPv4Entry(192.0.2.222). + nh100 := nextHopEntry(100, defaultVRF, atePort2.ip(3)) + nh101 := nextHopEntry(101, defaultVRF, atePort2.ip(4)) + nhg3 := nextHopGroupEntry(3, defaultVRF, []nhInfo{{index: 100, weight: 3}, {index: 101, weight: 5}}) + ipEntry3 := ipv4Entry(nhgIPv4EntryMap[3], defaultVRF, 3, defaultVRF) + + gRIBI.Modify().AddEntry(t, nh100, nh101, nhg3, ipEntry3) + + // Set up NH#1, NH#2, NHG#1, IPv4Entry(198.18.196.1/22). + nh1 := nextHopEntry(1, defaultVRF, nhEntryIP1) + nh2 := nextHopEntry(2, defaultVRF, nhEntryIP2) + nhg1 := nextHopGroupEntry(1, defaultVRF, []nhInfo{{index: 1, weight: 1}, {index: 2, weight: 3}}) + ipEntry1 := ipv4Entry(nhgIPv4EntryMap[1], nonDefaultVRF, 1, defaultVRF) + + gRIBI.Modify().AddEntry(t, nh1, nh2, nhg1, ipEntry1) + + if err := awaitTimeout(ctx, gRIBI, t, time.Minute); err != nil { + t.Fatalf("Could not program entries via gRIBI, got err: %v", err) + } + + // Validate entries were installed in FIB. + for _, route := range nhgIPv4EntryMap { + chk.HasResult(t, gRIBI.Results(t), + fluent.OperationResult(). + WithIPv4Operation(route). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } + + // Test traffic flows correctly and + wantWeights := map[string]float64{ + "1": 6.25, + "2": 18.75, + "3": 28.12, + "4": 46.87, + } + t.Run("testTraffic", func(t *testing.T) { + got := testTraffic(t, ate, top) + if diff := cmp.Diff(wantWeights, got, cmpopts.EquateApprox(0, tolerance)); diff != "" { + t.Errorf("Packet distribution ratios -want,+got:\n%s", diff) + } + }) + + t.Run("validateAFTWeights", func(t *testing.T) { + for nhg, weights := range map[uint64][]uint64{ + 2: {1, 3}, + 3: {3, 5}, + } { + got := aftNextHopWeights(t, dut, nhg, defaultVRF) + ok := cmp.Equal(weights, got, cmpopts.SortSlices(func(a, b uint64) bool { return a < b })) + if !ok { + t.Errorf("Valid weights not present for NI: %s, NHG: %d, got: %v, want: %v", defaultVRF, nhg, got, weights) + } + } + }) + + // Flush gRIBI routes after test. + if err := gribi.FlushAll(gRIBI); err != nil { + t.Error(err) + } +} + +// testHierarchicalWeightBoundaryScenario tests and validates traffic through all 18 Vlans. +func testHierarchicalWeightBoundaryScenario(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, + ate *ondatra.ATEDevice, top gosnappi.Config, gRIBI *fluent.GRIBIClient) { + defaultVRF := deviations.DefaultNetworkInstance(dut) + + // Set up NH#10, NH#11, NHG#2, IPv4Entry(192.0.2.111). + nh10 := nextHopEntry(10, defaultVRF, atePort2.ip(1)) + nh11 := nextHopEntry(11, defaultVRF, atePort2.ip(2)) + nhg2 := nextHopGroupEntry(2, defaultVRF, []nhInfo{{index: 10, weight: 3}, {index: 11, weight: 5}}) + ipEntry2 := ipv4Entry(nhgIPv4EntryMap[2], defaultVRF, 2, defaultVRF) + + gRIBI.Modify().AddEntry(t, nh10, nh11, nhg2, ipEntry2) + + // Set up NH#100..NH#116, NHG#3, IPv4Entry(192.0.2.222). + nextHopWeights := []nhInfo{} + nhIdx := uint64(100) + gribiEntries := []fluent.GRIBIEntry{} + for i := 0; i < 16; i++ { + nh := nextHopEntry(nhIdx, defaultVRF, atePort2.ip(uint8(3+i))) + gribiEntries = append(gribiEntries, nh) + if i == 0 { + nextHopWeights = append(nextHopWeights, nhInfo{index: nhIdx, weight: 1}) + } else { + nextHopWeights = append(nextHopWeights, nhInfo{index: nhIdx, weight: 16}) + } + nhIdx++ + } + nhg3 := nextHopGroupEntry(3, defaultVRF, nextHopWeights) + ipEntry3 := ipv4Entry(nhgIPv4EntryMap[3], defaultVRF, 3, defaultVRF) + gribiEntries = append(gribiEntries, nhg3, ipEntry3) + + gRIBI.Modify().AddEntry(t, gribiEntries...) + + // Set up NH#1, NH#2, NHG#1, IPv4Entry(198.18.196.1/22). + nh1 := nextHopEntry(1, defaultVRF, nhEntryIP1) + nh2 := nextHopEntry(2, defaultVRF, nhEntryIP2) + nhg1 := nextHopGroupEntry(1, defaultVRF, []nhInfo{{index: 1, weight: 1}, {index: 2, weight: 31}}) + ipEntry1 := ipv4Entry(nhgIPv4EntryMap[1], nonDefaultVRF, 1, defaultVRF) + + gRIBI.Modify().AddEntry(t, nh1, nh2, nhg1, ipEntry1) + + if err := awaitTimeout(ctx, gRIBI, t, time.Minute); err != nil { + t.Fatalf("Could not program entries via gRIBI, got err: %v", err) + } + + // Validate entries were installed in FIB. + for _, route := range nhgIPv4EntryMap { + chk.HasResult(t, gRIBI.Results(t), + fluent.OperationResult(). + WithIPv4Operation(route). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } + + wantWeights := map[string]float64{ + "1": 1.171, + "2": 1.953, + "3": 0.402, + } + // 6.432 weight for vlans 4 to 18. + for i := 4; i <= 18; i++ { + wantWeights[strconv.Itoa(i)] = 6.432 + } + t.Run("testTraffic", func(t *testing.T) { + got := testTraffic(t, ate, top) + + if deviations.HierarchicalWeightResolutionTolerance(dut) != tolerance { + tolerance = deviations.HierarchicalWeightResolutionTolerance(dut) + } + if diff := cmp.Diff(wantWeights, got, cmpopts.EquateApprox(0, tolerance)); diff != "" { + t.Errorf("Packet distribution ratios -want,+got:\n%s", diff) + } + }) + + t.Run("validateAFTWeights", func(t *testing.T) { + for nhg, weights := range map[uint64][]uint64{ + 2: {3, 5}, + 3: {1, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, + } { + got := aftNextHopWeights(t, dut, nhg, defaultVRF) + ok := cmp.Equal(weights, got, cmpopts.SortSlices(func(a, b uint64) bool { return a < b })) + if !ok { + t.Errorf("Valid weights not present for NI: %s, NHG: %d, got: %v, want: %v", defaultVRF, nhg, got, weights) + } + } + }) + + // Flush gRIBI routes after test. + if err := gribi.FlushAll(gRIBI); err != nil { + t.Error(err) + } +} + +func TestHierarchicalWeightResolution(t *testing.T) { + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + ctx := context.Background() + + // Configure ATE ports and start Ethernet+IPv4. + top := gosnappi.NewConfig() + atePort1.configureATE(t, top, ate) + atePort2.configureATE(t, top, ate) + + ate.OTG().PushConfig(t, top) + + // configure DUT. + configureDUT(t, dut) + + ate.OTG().StartProtocols(t) + + // Configure gRIBI with FIB_ACK. + gRIBI := configureGRIBIClient(t, dut) + + gRIBI.Start(ctx, t) + defer gRIBI.Stop(t) + + defer func() { + // Flush all gRIBI routes after test. + if err := gribi.FlushAll(gRIBI); err != nil { + t.Error(err) + } + }() + + gRIBI.StartSending(ctx, t) + if err := awaitTimeout(ctx, gRIBI, t, time.Minute); err != nil { + t.Fatalf("Await got error during session negotiation for gRIBI: %v", err) + } + gribi.BecomeLeader(t, gRIBI) + + // Flush existing gRIBI routes before test. + if err := gribi.FlushAll(gRIBI); err != nil { + t.Fatal(err) + } + + t.Run("TestBasicHierarchicalWeightWithVrfPolW", func(t *testing.T) { + vrfpolicy.ConfigureVRFSelectionPolicyW(t, dut) + testBasicHierarchicalWeight(ctx, t, dut, ate, top, gRIBI) + }) + + t.Run("TestHierarchicalWeightBoundaryScenarioWithVrfPolW", func(t *testing.T) { + vrfpolicy.ConfigureVRFSelectionPolicyW(t, dut) + testHierarchicalWeightBoundaryScenario(ctx, t, dut, ate, top, gRIBI) + }) + + ate.OTG().StopProtocols(t) +} diff --git a/feature/gribi/otg_tests/hierarchical_weight_resolution_pbf_test/metadata.textproto b/feature/gribi/otg_tests/hierarchical_weight_resolution_pbf_test/metadata.textproto new file mode 100644 index 00000000000..f64bcea4013 --- /dev/null +++ b/feature/gribi/otg_tests/hierarchical_weight_resolution_pbf_test/metadata.textproto @@ -0,0 +1,51 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "bf5df0ee-79b7-460b-8146-3a1351fd56d7" +plan_id: "TE-3.31" +description: "Hierarchical weight resolution with PBF" +testbed: TESTBED_DUT_ATE_2LINKS +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + hierarchical_weight_resolution_tolerance: 1.5 + ipv4_missing_enabled: true + interface_ref_interface_id_format: true + pf_require_match_default_rule: true + pf_require_sequential_order_pbr_rules: true + } +} +platform_exceptions: { + platform: { + vendor: JUNIPER + } + deviations: { + hierarchical_weight_resolution_tolerance: 0.4 + explicit_interface_ref_definition: true + } +} +platform_exceptions: { + platform: { + vendor: NOKIA + } + deviations: { + explicit_interface_ref_definition: true + explicit_port_speed: true + explicit_interface_in_default_vrf: true + interface_enabled: true + } +} +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + omit_l2_mtu: true + deprecated_vlan_id: true + interface_enabled: true + default_network_instance: "default" + } +} +tags: TAGS_DATACENTER_EDGE diff --git a/feature/gribi/otg_tests/hierarchical_weight_resolution_test/README.md b/feature/gribi/otg_tests/hierarchical_weight_resolution_test/README.md new file mode 100644 index 00000000000..a7b35daed86 --- /dev/null +++ b/feature/gribi/otg_tests/hierarchical_weight_resolution_test/README.md @@ -0,0 +1,142 @@ +# TE-3.3: Hierarchical weight resolution + +## Summary + +Ensures that next-hop weights (for WCMP) are honored hierarchically in gRIBI +recursive resolution and traffic is load-shared according to these weights. + +## Procedure + +Configure ATE and DUT: + +* Connect ATE port-1 to DUT port-1. ATE port-2 to DUT port-2. + +* Create a non-default VRF (VRF-1) that contains no interfaces. + +* On DUT port-2 and ATE port-2 create 18 L3 sub-interfaces each with a /30 + subnet as below: + + * On DUT port-2, create subinterfaces with indices 1 to 18 mapped to VLAN + IDs 1 to 18 and corressponding IPv4 addresses 192.0.2.5, 192.0.2.9, ..., + 192.0.2.73 respectively. + + * On ATE port-2, create subinterfaces with indices 1 to 18 mapped to VLAN + IDs 1 to 18 and corresponding IPv4 addresses 192.0.2.6, 192.0.2.10, ..., + 192.0.2.74 and default gateways as 192.0.2.5, 192.0.2.9, ..., 192.0.2.73 + respectively. + +* On DUT port-1 and ATE port-1 create a single L3 interface. + +* On DUT, create a policy-based forwarding rule to redirect all traffic + received from DUT port-1 into VRF-1 (based on src. IP match criteria). + +Test case for basic hierarchical weight: + +* Establish gRIBI client connection with DUT with PERSISTENCE, make it become + leader and install the following Entries: + + * IPv4Entry 203.0.113.0/32 in VRF-1, pointing to NextHopGroup(NHG#1) in + default VRF, with two NextHops(NH#1, NH#2) in default VRF: + + * NH#1 with weight:1, pointing to 192.0.2.111 + + * NH#2 with weight:3, pointing to 192.0.2.222 + + * IPv4Entry 192.0.2.111/32 in default VRF, pointing to NextHopGroup(NHG#2) + in default VRF, with two NextHops(NH#10, NH#11) in default VRF: + + * NH#10 with weight:1, pointing to 192.0.2.10 + + * NH#11 with weight:3, pointing to 192.0.2.14 + + * IPv4Entry 192.0.2.222/32 in default VRF, pointing to NextHopGroup(NHG#3) + in default VRF, with two NextHops(NH#100, NH#101) in default VRF: + + * NH#100 with weight:3, pointing to 192.0.2.18 + + * NH#101 with weight:5, pointing to 192.0.2.22 + +* Validate with traffic: + + * NH10: (1/4) * (1/4) = 6.25% traffic received by ATE port-2 VLAN 1 + + * NH11: (1/4) * (3/4) = 18.75% traffic received by ATE port-2 VLAN 2 + + * NH100: (3/4) * (3/8) = 28.12% traffic received by ATE port-2 VLAN 3 + + * NH101: (3/4) * (5/8) = 46.87% traffic received by ATE port-2 VLAN 4 + + * A tolerance of 0.2% is allowed for each VLAN for now, since we only test + for 2 mins. + +Test case for hierarchical weight in boundary scenarios, with maximum expected +WCMP width of 16 nexthops: + +* Flush previous gRIBI Entries for all NIs and establish a new connection with + DUT with PERSISTENCE and install the following Entries: + + * IPv4Entry 203.0.113.0/32 in VRF-1, pointing to NextHopGroup(NHG#1) in + default VRF, with two NextHops(NH#1, NH#2) in default VRF: + + * NH#1 with weight:1, pointing to 192.0.2.111 + + * NH#2 with weight:31, pointing to 192.0.2.222 + + * IPv4Entry 192.0.2.111/32 in default VRF, pointing to NextHopGroup(NHG#2) + in default VRF, with two NextHops(NH#10, NH#11) in default VRF: + + * NH#10 with weight:3, pointing to 192.0.2.10 + + * NH#11 with weight:5, pointing to 192.0.2.14 + + * IPv4Entry 192.0.2.222/32 in default VRF, pointing to NextHopGroup(NHG#3) + in default VRF, with 16 NextHops(NH#100, NH#101, ..., NH#115), all with + weight: 16 except NHG#100 is of weight 1, in default VRF: + + * NH#100 with weight:1, pointing to 192.0.2.18 + + * NH#101 with weight:16, pointing to 192.0.2.22 + + * ... + + * NH#115 with weight:16, pointing to 192.0.2.79 + +* Validate with traffic: + + * NH10: (1/32) * (3/8) ~ 1.171% traffic received by ATE port-2 VLAN 1 + + * NH11: (1/32) * (5/8) ~ 1.953% traffic received by ATE port-2 VLAN 2 + + * NH100: (31/32) * (1/241) ~ 0.402% traffic received by ATE port-2 VLAN 3 + + * for each VLAN ID in 4...18: + + * NH: (31/32) * (16/241) ~ 6.432% traffic received by ATE port-2 VLAN + ID + + * A tolerance of 0.2% is allowed for each VLAN for now, since we only test + for 2 mins. + +## Config Parameter Coverage + +N/A + +## Telemetry Parameter Coverage + +TODO: +/network-instances/network-instance/afts/next-hop-groups/next-hop-group/next-hops/next-hop/state/weight + +## Protocol/RPC Parameter coverage + +* gRIBI: + * Modify() + * ModifyRequest: + * AFTOperation: + * next_hop_group + * NextHopGroupKey: id + * NextHopGroup: weight + +## Minimum DUT platform requirement + +vRX + diff --git a/feature/gribi/otg_tests/hierarchical_weight_resolution_test/hierarchical_weight_resolution_test.go b/feature/gribi/otg_tests/hierarchical_weight_resolution_test/hierarchical_weight_resolution_test.go new file mode 100644 index 00000000000..4d0bc5d8d34 --- /dev/null +++ b/feature/gribi/otg_tests/hierarchical_weight_resolution_test/hierarchical_weight_resolution_test.go @@ -0,0 +1,745 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package hierarchical_weight_resolution_test implements TE-3.3 of the Popgate vendor testplan +package hierarchical_weight_resolution_test + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "net" + "strconv" + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/gribi" + "github.com/openconfig/featureprofiles/internal/otgutils" + "github.com/openconfig/gribigo/chk" + "github.com/openconfig/gribigo/constants" + "github.com/openconfig/gribigo/fluent" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ygot/ygot" +) + +type attributes struct { + attrs.Attributes + numSubIntf uint32 + ip func(vlan uint8) string + gateway func(vlan uint8) string +} + +type nhInfo struct { + index uint64 + weight uint64 +} + +const ( + ipv4EntryPrefix = "203.0.113.0/32" + ipv4FlowIP = "203.0.113.0" + innerSrcIPv4Start = "198.18.0.0" + innerDstIPv4Start = "198.19.0.0" + ipv4PrefixLen = 30 + ipv4FlowCount = 65000 + nhEntryIP1 = "192.0.2.111" + nhEntryIP2 = "192.0.2.222" + nonDefaultVRF = "VRF-1" + policyName = "redirect-to-VRF1" + ipipProtocol = 4 +) + +var ( + dutPort1 = attributes{ + Attributes: attrs.Attributes{ + Desc: "dutPort1", + Name: "port1", + IPv4: dutPort1IPv4(0), + IPv4Len: ipv4PrefixLen, + }, + numSubIntf: 0, + ip: dutPort1IPv4, + } + + atePort1 = attributes{ + Attributes: attrs.Attributes{ + Name: "port1", + MAC: "02:00:01:01:01:01", + IPv4: atePort1IPv4(0), + IPv4Len: ipv4PrefixLen, + }, + numSubIntf: 0, + ip: atePort1IPv4, + gateway: dutPort1IPv4, + } + + dutPort2 = attributes{ + Attributes: attrs.Attributes{ + Desc: "dutPort2", + Name: "port2", + IPv4: dutPort2IPv4(0), + IPv4Len: ipv4PrefixLen, + }, + numSubIntf: 18, + ip: dutPort2IPv4, + } + + atePort2 = attributes{ + Attributes: attrs.Attributes{ + Name: "port2", + MAC: "02:00:02:01:01:01", + IPv4: atePort2IPv4(0), + IPv4Len: ipv4PrefixLen, + }, + numSubIntf: 18, + ip: atePort2IPv4, + gateway: dutPort2IPv4, + } + + // nhgIPv4EntryMap maps NextHopGroups to the ipv4 entries pointing to that NextHopGroup. + nhgIPv4EntryMap = map[uint64]string{ + 1: ipv4EntryPrefix, + 2: cidr(nhEntryIP1, 32), + 3: cidr(nhEntryIP2, 32), + } + // 'tolerance' is the maximum difference that is allowed between the observed + // traffic distribution and the required traffic distribution. + tolerance = 0.2 +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +// dutPort1IPv4 returns ip address 192.0.2.1, for every vlanID. +func dutPort1IPv4(uint8) string { + return "192.0.2.1" +} + +// atePort1IPv4 returns ip address 192.0.2.2, for every vlanID +func atePort1IPv4(uint8) string { + return "192.0.2.2" +} + +// dutPort2IPv4 returns ip addresses starting 192.0.2.5, increasing by 4 +// for every vlanID. +func dutPort2IPv4(vlan uint8) string { + return fmt.Sprintf("192.0.2.%d", vlan*4+5) +} + +// atePort2IPv4 returns ip addresses starting 192.0.2.6, increasing by 4 +// for every vlanID. +func atePort2IPv4(vlan uint8) string { + return fmt.Sprintf("192.0.2.%d", vlan*4+6) +} + +// cidr taks as input the IPv4 address and the Mask and returns the IP string in +// CIDR notation. +func cidr(ipv4 string, ones int) string { + return ipv4 + "/" + strconv.Itoa(ones) +} + +// filterPacketReceived uses ATE:EgressTracking bucket counters to create a map +// with bucket-label as the Key and the percentage of packets-received for that +// bucket as the Value. +func filterPacketReceived(t *testing.T, flow string, ate *ondatra.ATEDevice) map[string]float64 { + t.Helper() + + // Check the egress packets + vlanTags := gnmi.GetAll(t, ate.OTG(), gnmi.OTG().Flow(flow).TaggedMetricAny().State()) + tags := gnmi.GetAll(t, ate.OTG(), gnmi.OTG().Flow(flow).TaggedMetricAny().TagsAny().State()) + t.Logf("There are a total of %v vlans", len(tags)) + + inPkts := map[string]uint64{} + for i, tag := range tags { + vlanHex := strings.Replace(tag.GetTagValue().GetValueAsHex(), "0x", "", -1) + vlanDec, _ := strconv.ParseUint(vlanHex, 16, 64) + inPkts[strconv.Itoa(int(vlanDec))] = vlanTags[i].GetCounters().GetInPkts() + } + inPct := map[string]float64{} + total := gnmi.Get(t, ate.OTG(), gnmi.OTG().Flow(flow).Counters().InPkts().State()) + for k, v := range inPkts { + inPct[k] = (float64(v) / float64(total)) * 100.0 + } + return inPct +} + +// configureGRIBIClient configures a new GRIBI client with PRESERVE and FIB_ACK. +func configureGRIBIClient(t *testing.T, dut *ondatra.DUTDevice) *fluent.GRIBIClient { + t.Helper() + gribic := dut.RawAPIs().GRIBI(t) + + // Configure the gRIBI client. + c := fluent.NewClient() + c.Connection(). + WithStub(gribic). + WithRedundancyMode(fluent.ElectedPrimaryClient). + WithInitialElectionID(1 /* low */, 0 /* hi */). + WithPersistence(). + WithFIBACK() + + return c +} + +// nextHopEntry configures a fluent.GRIBIEntry for a NextHopEntry. +func nextHopEntry(index uint64, networkInstance string, ipAddr string) fluent.GRIBIEntry { + return fluent.NextHopEntry(). + WithNetworkInstance(networkInstance). + WithIndex(index). + WithIPAddress(ipAddr) +} + +// nextHopGroupEntry configures a fluent.GRIBIEntry for a NextHopGroupEntry. +func nextHopGroupEntry(index uint64, networkInstance string, nhs []nhInfo) fluent.GRIBIEntry { + x := fluent.NextHopGroupEntry(). + WithNetworkInstance(networkInstance). + WithID(index) + for _, nh := range nhs { + x.AddNextHop(nh.index, nh.weight) + } + return x +} + +// ipv4Entry configures a fluent.GRIBIEntry for an IPv4Entry. +func ipv4Entry(prefix string, networkInstance string, nhgIndex uint64, nextHopGroupNetworkInstance string) fluent.GRIBIEntry { + return fluent.IPv4Entry(). + WithPrefix(prefix). + WithNetworkInstance(networkInstance). + WithNextHopGroup(nhgIndex). + WithNextHopGroupNetworkInstance(nextHopGroupNetworkInstance) +} + +// awaitTimeout calls a fluent client Await, adding a timeout to the context. +func awaitTimeout(ctx context.Context, c *fluent.GRIBIClient, t testing.TB, timeout time.Duration) error { + t.Helper() + subctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + return c.Await(subctx, t) +} + +// configSubinterfaceDUT configures the Sub Interfaces of an Interfaces, +// starting from Sub Interface 1. Each Subinterface is configured with a +// unique VlanID starting from 1 and an IP address. The starting IP Address +// for Subinterface(1) = dutPort.ip(1) = dutPort.ip + 4. +func (a *attributes) configSubinterfaceDUT(t *testing.T, intf *oc.Interface, dut *ondatra.DUTDevice) { + t.Helper() + if deviations.RequireRoutedSubinterface0(dut) { + s0 := intf.GetOrCreateSubinterface(0).GetOrCreateIpv4() + s0.Enabled = ygot.Bool(true) + } + for i := uint32(1); i <= a.numSubIntf; i++ { + ip := a.ip(uint8(i)) + + s := intf.GetOrCreateSubinterface(i) + if deviations.InterfaceEnabled(dut) { + s.Enabled = ygot.Bool(true) + } + if deviations.DeprecatedVlanID(dut) { + s.GetOrCreateVlan().VlanId = oc.UnionUint16(i) + } else { + s.GetOrCreateVlan().GetOrCreateMatch().GetOrCreateSingleTagged().VlanId = ygot.Uint16(uint16(i)) + } + s4 := s.GetOrCreateIpv4() + if deviations.InterfaceEnabled(dut) && !deviations.IPv4MissingEnabled(dut) { + s4.Enabled = ygot.Bool(true) + } + s4a := s4.GetOrCreateAddress(ip) + s4a.PrefixLength = ygot.Uint8(a.IPv4Len) + t.Logf("Adding DUT Subinterface with ID: %d, Vlan ID: %d and IPv4 address: %s", i, i, ip) + } +} + +// configInterfaceDUT configures the DUT interface with the provided IP Address. +// Sub Interfaces are also configured if numSubIntf > 0. +func (a *attributes) configInterfaceDUT(t *testing.T, d *ondatra.DUTDevice) { + t.Helper() + p := d.Port(t, a.Name) + i := &oc.Interface{Name: ygot.String(p.Name())} + + if a.numSubIntf > 0 { + i.Description = ygot.String(a.Desc) + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + if deviations.InterfaceEnabled(d) { + i.Enabled = ygot.Bool(true) + } + } else { + i = a.NewOCInterface(p.Name(), d) + } + + if deviations.ExplicitPortSpeed(d) { + i.GetOrCreateEthernet().PortSpeed = fptest.GetIfSpeed(t, p) + } + + a.configSubinterfaceDUT(t, i, d) + intfPath := gnmi.OC().Interface(p.Name()) + gnmi.Replace(t, d, intfPath.Config(), i) + fptest.LogQuery(t, "DUT", intfPath.Config(), gnmi.Get(t, d, intfPath.Config())) +} + +func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { + // configure NI. + configureNetworkInstance(t, dut) + + // Configure DUT ports. + dutPort1.configInterfaceDUT(t, dut) + dutPort2.configInterfaceDUT(t, dut) + + // assign subinterfaces to DEFAULT network instance if needed (deviation-based). + dutPort1.assignSubifsToDefaultNetworkInstance(t, dut) + dutPort2.assignSubifsToDefaultNetworkInstance(t, dut) + + // apply PBF to src interface. + dp1 := dut.Port(t, dutPort1.Name) + applyForwardingPolicy(t, dp1.Name()) +} + +// configureNetworkInstance creates and configures non-default and default NIs. +func configureNetworkInstance(t *testing.T, d *ondatra.DUTDevice) { + t.Helper() + + // configure non-default VRF + ni := &oc.NetworkInstance{ + Name: ygot.String(nonDefaultVRF), + Type: oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_L3VRF, + } + dni := gnmi.OC().NetworkInstance(nonDefaultVRF) + gnmi.Replace(t, d, dni.Config(), ni) + fptest.LogQuery(t, "NI", dni.Config(), gnmi.Get(t, d, dni.Config())) + + // configure PBF in DEFAULT vrf + defNIPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(d)) + fptest.ConfigureDefaultNetworkInstance(t, d) + gnmi.Replace(t, d, defNIPath.PolicyForwarding().Config(), configurePBF(d)) + +} + +// assignSubifsToDefaultNetworkInstance assign subinterfaces to the default network instance when ExplicitInterfaceInDefaultVRF is enabled. +func (a *attributes) assignSubifsToDefaultNetworkInstance(t *testing.T, d *ondatra.DUTDevice) { + p := d.Port(t, a.Name) + if deviations.ExplicitInterfaceInDefaultVRF(d) { + if a.numSubIntf == 0 { + fptest.AssignToNetworkInstance(t, d, p.Name(), deviations.DefaultNetworkInstance(d), 0) + } else { + for i := uint32(1); i <= a.numSubIntf; i++ { + fptest.AssignToNetworkInstance(t, d, p.Name(), deviations.DefaultNetworkInstance(d), i) + } + } + } +} + +// configurePBF returns a fully configured network-instance PF struct. +func configurePBF(dut *ondatra.DUTDevice) *oc.NetworkInstance_PolicyForwarding { + d := &oc.Root{} + ni := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + pf := ni.GetOrCreatePolicyForwarding() + vrfPolicy := pf.GetOrCreatePolicy(policyName) + vrfPolicy.SetType(oc.Policy_Type_VRF_SELECTION_POLICY) + vrfPolicy.GetOrCreateRule(1).GetOrCreateIpv4().Protocol = oc.UnionUint8(ipipProtocol) + vrfPolicy.GetOrCreateRule(1).GetOrCreateAction().NetworkInstance = ygot.String(nonDefaultVRF) + return pf +} + +// applyForwardingPolicy applies the forwarding policy on the interface. +func applyForwardingPolicy(t *testing.T, ingressPort string) { + t.Logf("Applying forwarding policy on interface %v ... ", ingressPort) + d := &oc.Root{} + dut := ondatra.DUT(t, "dut") + interfaceID := ingressPort + if deviations.InterfaceRefInterfaceIDFormat(dut) { + interfaceID = ingressPort + ".0" + } + pfPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).PolicyForwarding().Interface(interfaceID) + pfCfg := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)).GetOrCreatePolicyForwarding().GetOrCreateInterface(interfaceID) + pfCfg.ApplyVrfSelectionPolicy = ygot.String(policyName) + pfCfg.GetOrCreateInterfaceRef().Interface = ygot.String(ingressPort) + pfCfg.GetOrCreateInterfaceRef().Subinterface = ygot.Uint32(0) + if deviations.InterfaceRefConfigUnsupported(dut) { + pfCfg.InterfaceRef = nil + } + gnmi.Replace(t, dut, pfPath.Config(), pfCfg) +} + +// configureATE configures Ethernet + IPv4 on the ATE. If the number of +// Subinterfaces(numSubIntf) > 0, we then create additional sub-interfaces +// each with a unique VlanID starting from 1. The IPv4 addresses start with +// ATE:Port.IPv4 and then nextIP(ATE:Port.IPv4, 4) for each sub interface. +func (a *attributes) configureATE(t *testing.T, top gosnappi.Config, ate *ondatra.ATEDevice) { + t.Helper() + p := ate.Port(t, a.Name) + + // Configure source port on ATE : Port1. + + top.Ports().Add().SetName(p.ID()) + if a.numSubIntf == 0 { + ip := a.ip(0) + gateway := a.gateway(0) + dev := top.Devices().Add().SetName(a.Name) + eth := dev.Ethernets().Add().SetName(a.Name + ".Eth").SetMac(a.MAC) + eth.Connection().SetPortName(p.ID()) + ipObj := eth.Ipv4Addresses().Add().SetName(dev.Name() + ".IPv4") + ipObj.SetAddress(ip).SetGateway(gateway).SetPrefix(uint32(a.IPv4Len)) + t.Logf("Adding ATE Ipv4 address: %s with gateway: %s", cidr(ip, int(a.IPv4Len)), gateway) + } + // Configure destination port on ATE : Port2. + for i := uint32(1); i <= a.numSubIntf; i++ { + name := fmt.Sprintf(`dst%d`, i) + ip := a.ip(uint8(i)) + gateway := a.gateway(uint8(i)) + mac, err := incrementMAC(a.MAC, int(i)+1) + if err != nil { + t.Fatalf("Failed to generate mac address with error %s", err) + } + + dev := top.Devices().Add().SetName(name + ".Dev") + eth := dev.Ethernets().Add().SetName(name + ".Eth").SetMac(mac) + eth.Connection().SetPortName(p.ID()) + eth.Vlans().Add().SetName(name).SetId(uint32(i)) + eth.Ipv4Addresses().Add().SetName(name + ".IPv4").SetAddress(ip).SetGateway(gateway).SetPrefix(uint32(a.IPv4Len)) + + t.Logf("Adding ATE Ipv4 address: %s with gateway: %s and VlanID: %d", cidr(ip, 30), gateway, i) + } + // } +} + +// incrementMAC increments the MAC by i. Returns error if the mac cannot be parsed or overflows the mac address space +func incrementMAC(mac string, i int) (string, error) { + macAddr, err := net.ParseMAC(mac) + if err != nil { + return "", err + } + convMac := binary.BigEndian.Uint64(append([]byte{0, 0}, macAddr...)) + convMac = convMac + uint64(i) + buf := new(bytes.Buffer) + err = binary.Write(buf, binary.BigEndian, convMac) + if err != nil { + return "", err + } + newMac := net.HardwareAddr(buf.Bytes()[2:8]) + return newMac.String(), nil +} + +// testTraffic creates a traffic flow with ATE source & destination endpoints +// and configures a VlanID filter for output frames. The IPv4 header for the +// flow contains the ATE:Port1 address as source and the configured gRIBI- +// IndirectEntry as the destination. The function also takes as input a map of +// that is wanted and compares it to the actual +// traffic test result. +func testTraffic(t *testing.T, ate *ondatra.ATEDevice, top gosnappi.Config) map[string]float64 { + + dut := ondatra.DUT(t, "dut") + dstMac := gnmi.Get(t, dut, gnmi.OC().Interface(dut.Port(t, "port1").Name()).Ethernet().MacAddress().State()) + top.Flows().Clear().Items() + flowipv4 := top.Flows().Add().SetName("flow") + flowipv4.Metrics().SetEnable(true) + flowipv4.TxRx().Port().SetTxName(atePort1.Name).SetRxNames([]string{atePort2.Name}) + flowipv4.Size().SetFixed(100) + e1 := flowipv4.Packet().Add().Ethernet() + e1.Src().SetValue(atePort1.MAC) + e1.Dst().SetValue(dstMac) + v4 := flowipv4.Packet().Add().Ipv4() + v4.Src().SetValue(atePort1.IPv4) + v4.Dst().SetValue(ipv4FlowIP) + v4Inner := flowipv4.Packet().Add().Ipv4() + v4Inner.Src().Increment().SetStart(innerSrcIPv4Start).SetCount(ipv4FlowCount) + v4Inner.Dst().Increment().SetStart(innerDstIPv4Start).SetCount(ipv4FlowCount) + flowipv4.EgressPacket().Add().Ethernet() + vlan := flowipv4.EgressPacket().Add().Vlan() + vlanTag := vlan.Id().MetricTags().Add() + vlanTag.SetName("EgressVlanIdTrackingFlow") + ate.OTG().PushConfig(t, top) + ate.OTG().StartProtocols(t) + otgutils.WaitForARP(t, ate.OTG(), top, "IPv4") + + // Run traffic for 2 minutes. + ate.OTG().StartTraffic(t) + time.Sleep(1 * time.Minute) + ate.OTG().StopTraffic(t) + + otgutils.LogFlowMetrics(t, ate.OTG(), top) + + recvMetric := gnmi.Get(t, ate.OTG(), gnmi.OTG().Flow(flowipv4.Name()).State()) + txPkts := float32(recvMetric.GetCounters().GetOutPkts()) + rxPkts := float32(recvMetric.GetCounters().GetInPkts()) + lossPct := (txPkts - rxPkts) * 100 / txPkts + if txPkts == 0 { + t.Fatalf("TxPkts == 0, want > 0.") + } + if lossPct > 0 && recvMetric.GetCounters().GetOutPkts() > 0 { + t.Fatalf("Loss Pct for %s got %v, want 0", flowipv4.Name(), lossPct) + } + + // Compare traffic distribution with the wanted results. + results := filterPacketReceived(t, "flow", ate) + t.Logf("Filters: %v", results) + return results +} + +// aftNextHopWeights queries AFT telemetry using Get() and returns +// the weights. If not-found, an empty list is returned. +func aftNextHopWeights(t *testing.T, dut *ondatra.DUTDevice, nhg uint64, networkInstance string) []uint64 { + aft := gnmi.Get(t, dut, gnmi.OC().NetworkInstance(networkInstance).Afts().State()) + var nhgD *oc.NetworkInstance_Afts_NextHopGroup + for _, nhgData := range aft.NextHopGroup { + if nhgData.GetProgrammedId() == nhg { + nhgD = nhgData + break + } + } + + if nhgD == nil { + return []uint64{} + } + + got := []uint64{} + for _, nhD := range nhgD.NextHop { + got = append(got, nhD.GetWeight()) + } + + return got +} + +// testBasicHierarchicalWeight tests and validates traffic through 4 Vlans. +func testBasicHierarchicalWeight(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, + ate *ondatra.ATEDevice, top gosnappi.Config, gRIBI *fluent.GRIBIClient) { + defaultVRF := deviations.DefaultNetworkInstance(dut) + + // Set up NH#10, NH#11, NHG#2, IPv4Entry(192.0.2.111). + nh10 := nextHopEntry(10, defaultVRF, atePort2.ip(1)) + nh11 := nextHopEntry(11, defaultVRF, atePort2.ip(2)) + nhg2 := nextHopGroupEntry(2, defaultVRF, []nhInfo{{index: 10, weight: 1}, {index: 11, weight: 3}}) + ipEntry2 := ipv4Entry(nhgIPv4EntryMap[2], defaultVRF, 2, defaultVRF) + + gRIBI.Modify().AddEntry(t, nh10, nh11, nhg2, ipEntry2) + + // Set up NH#100, NH#101, NHG#3, IPv4Entry(192.0.2.222). + nh100 := nextHopEntry(100, defaultVRF, atePort2.ip(3)) + nh101 := nextHopEntry(101, defaultVRF, atePort2.ip(4)) + nhg3 := nextHopGroupEntry(3, defaultVRF, []nhInfo{{index: 100, weight: 3}, {index: 101, weight: 5}}) + ipEntry3 := ipv4Entry(nhgIPv4EntryMap[3], defaultVRF, 3, defaultVRF) + + gRIBI.Modify().AddEntry(t, nh100, nh101, nhg3, ipEntry3) + + // Set up NH#1, NH#2, NHG#1, IPv4Entry(198.18.196.1/22). + nh1 := nextHopEntry(1, defaultVRF, nhEntryIP1) + nh2 := nextHopEntry(2, defaultVRF, nhEntryIP2) + nhg1 := nextHopGroupEntry(1, defaultVRF, []nhInfo{{index: 1, weight: 1}, {index: 2, weight: 3}}) + ipEntry1 := ipv4Entry(nhgIPv4EntryMap[1], nonDefaultVRF, 1, defaultVRF) + + gRIBI.Modify().AddEntry(t, nh1, nh2, nhg1, ipEntry1) + + if err := awaitTimeout(ctx, gRIBI, t, time.Minute); err != nil { + t.Fatalf("Could not program entries via gRIBI, got err: %v", err) + } + + // Validate entries were installed in FIB. + for _, route := range nhgIPv4EntryMap { + chk.HasResult(t, gRIBI.Results(t), + fluent.OperationResult(). + WithIPv4Operation(route). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } + + // Test traffic flows correctly and + wantWeights := map[string]float64{ + "1": 6.25, + "2": 18.75, + "3": 28.12, + "4": 46.87, + } + t.Run("testTraffic", func(t *testing.T) { + got := testTraffic(t, ate, top) + if diff := cmp.Diff(wantWeights, got, cmpopts.EquateApprox(0, tolerance)); diff != "" { + t.Errorf("Packet distribution ratios -want,+got:\n%s", diff) + } + }) + + t.Run("validateAFTWeights", func(t *testing.T) { + for nhg, weights := range map[uint64][]uint64{ + 2: {1, 3}, + 3: {3, 5}, + } { + got := aftNextHopWeights(t, dut, nhg, defaultVRF) + ok := cmp.Equal(weights, got, cmpopts.SortSlices(func(a, b uint64) bool { return a < b })) + if !ok { + t.Errorf("Valid weights not present for NI: %s, NHG: %d, got: %v, want: %v", defaultVRF, nhg, got, weights) + } + } + }) + + // Flush gRIBI routes after test. + if err := gribi.FlushAll(gRIBI); err != nil { + t.Error(err) + } +} + +// testHierarchicalWeightBoundaryScenario tests and validates traffic through all 18 Vlans. +func testHierarchicalWeightBoundaryScenario(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, + ate *ondatra.ATEDevice, top gosnappi.Config, gRIBI *fluent.GRIBIClient) { + defaultVRF := deviations.DefaultNetworkInstance(dut) + + // Set up NH#10, NH#11, NHG#2, IPv4Entry(192.0.2.111). + nh10 := nextHopEntry(10, defaultVRF, atePort2.ip(1)) + nh11 := nextHopEntry(11, defaultVRF, atePort2.ip(2)) + nhg2 := nextHopGroupEntry(2, defaultVRF, []nhInfo{{index: 10, weight: 3}, {index: 11, weight: 5}}) + ipEntry2 := ipv4Entry(nhgIPv4EntryMap[2], defaultVRF, 2, defaultVRF) + + gRIBI.Modify().AddEntry(t, nh10, nh11, nhg2, ipEntry2) + + // Set up NH#100..NH#116, NHG#3, IPv4Entry(192.0.2.222). + nextHopWeights := []nhInfo{} + nhIdx := uint64(100) + gribiEntries := []fluent.GRIBIEntry{} + for i := 0; i < 16; i++ { + nh := nextHopEntry(nhIdx, defaultVRF, atePort2.ip(uint8(3+i))) + gribiEntries = append(gribiEntries, nh) + if i == 0 { + nextHopWeights = append(nextHopWeights, nhInfo{index: nhIdx, weight: 1}) + } else { + nextHopWeights = append(nextHopWeights, nhInfo{index: nhIdx, weight: 16}) + } + nhIdx++ + } + nhg3 := nextHopGroupEntry(3, defaultVRF, nextHopWeights) + ipEntry3 := ipv4Entry(nhgIPv4EntryMap[3], defaultVRF, 3, defaultVRF) + gribiEntries = append(gribiEntries, nhg3, ipEntry3) + + gRIBI.Modify().AddEntry(t, gribiEntries...) + + // Set up NH#1, NH#2, NHG#1, IPv4Entry(198.18.196.1/22). + nh1 := nextHopEntry(1, defaultVRF, nhEntryIP1) + nh2 := nextHopEntry(2, defaultVRF, nhEntryIP2) + nhg1 := nextHopGroupEntry(1, defaultVRF, []nhInfo{{index: 1, weight: 1}, {index: 2, weight: 31}}) + ipEntry1 := ipv4Entry(nhgIPv4EntryMap[1], nonDefaultVRF, 1, defaultVRF) + + gRIBI.Modify().AddEntry(t, nh1, nh2, nhg1, ipEntry1) + + if err := awaitTimeout(ctx, gRIBI, t, time.Minute); err != nil { + t.Fatalf("Could not program entries via gRIBI, got err: %v", err) + } + + // Validate entries were installed in FIB. + for _, route := range nhgIPv4EntryMap { + chk.HasResult(t, gRIBI.Results(t), + fluent.OperationResult(). + WithIPv4Operation(route). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } + + wantWeights := map[string]float64{ + "1": 1.171, + "2": 1.953, + "3": 0.402, + } + // 6.432 weight for vlans 4 to 18. + for i := 4; i <= 18; i++ { + wantWeights[strconv.Itoa(i)] = 6.432 + } + t.Run("testTraffic", func(t *testing.T) { + got := testTraffic(t, ate, top) + + if deviations.HierarchicalWeightResolutionTolerance(dut) != tolerance { + tolerance = deviations.HierarchicalWeightResolutionTolerance(dut) + } + if diff := cmp.Diff(wantWeights, got, cmpopts.EquateApprox(0, tolerance)); diff != "" { + t.Errorf("Packet distribution ratios -want,+got:\n%s", diff) + } + }) + + t.Run("validateAFTWeights", func(t *testing.T) { + for nhg, weights := range map[uint64][]uint64{ + 2: {3, 5}, + 3: {1, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, + } { + got := aftNextHopWeights(t, dut, nhg, defaultVRF) + ok := cmp.Equal(weights, got, cmpopts.SortSlices(func(a, b uint64) bool { return a < b })) + if !ok { + t.Errorf("Valid weights not present for NI: %s, NHG: %d, got: %v, want: %v", defaultVRF, nhg, got, weights) + } + } + }) + + // Flush gRIBI routes after test. + if err := gribi.FlushAll(gRIBI); err != nil { + t.Error(err) + } +} + +func TestHierarchicalWeightResolution(t *testing.T) { + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + ctx := context.Background() + + // Configure ATE ports and start Ethernet+IPv4. + top := gosnappi.NewConfig() + atePort1.configureATE(t, top, ate) + atePort2.configureATE(t, top, ate) + + ate.OTG().PushConfig(t, top) + + // configure DUT. + configureDUT(t, dut) + + ate.OTG().StartProtocols(t) + + // Configure gRIBI with FIB_ACK. + gRIBI := configureGRIBIClient(t, dut) + + gRIBI.Start(ctx, t) + defer gRIBI.Stop(t) + + defer func() { + // Flush all gRIBI routes after test. + if err := gribi.FlushAll(gRIBI); err != nil { + t.Error(err) + } + }() + + gRIBI.StartSending(ctx, t) + if err := awaitTimeout(ctx, gRIBI, t, time.Minute); err != nil { + t.Fatalf("Await got error during session negotiation for gRIBI: %v", err) + } + gribi.BecomeLeader(t, gRIBI) + + // Flush existing gRIBI routes before test. + if err := gribi.FlushAll(gRIBI); err != nil { + t.Fatal(err) + } + + t.Run("TestBasicHierarchicalWeight", func(t *testing.T) { + testBasicHierarchicalWeight(ctx, t, dut, ate, top, gRIBI) + }) + + t.Run("TestHierarchicalWeightBoundaryScenario", func(t *testing.T) { + testHierarchicalWeightBoundaryScenario(ctx, t, dut, ate, top, gRIBI) + }) + + ate.OTG().StopProtocols(t) +} diff --git a/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn_policy_test/metadata.textproto b/feature/gribi/otg_tests/hierarchical_weight_resolution_test/metadata.textproto similarity index 55% rename from feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn_policy_test/metadata.textproto rename to feature/gribi/otg_tests/hierarchical_weight_resolution_test/metadata.textproto index bc335a7ac99..63b7b4f20f6 100644 --- a/feature/experimental/bgp/ate_tests/bgp_2byte_4byte_asn_policy_test/metadata.textproto +++ b/feature/gribi/otg_tests/hierarchical_weight_resolution_test/metadata.textproto @@ -1,16 +1,27 @@ # proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto # proto-message: Metadata -uuid: "a993eb24-40f8-4b86-bc6b-02c19f6a0d53" -plan_id: "RT-1.24" -description: "BGP 2-Byte and 4-Byte ASN support with policy" +uuid: "93695155-2898-47d4-9bbb-ac40611d3882" +plan_id: "TE-3.3" +description: "Hierarchical weight resolution" testbed: TESTBED_DUT_ATE_2LINKS platform_exceptions: { platform: { vendor: CISCO } deviations: { + hierarchical_weight_resolution_tolerance: 1.5 ipv4_missing_enabled: true + interface_ref_interface_id_format: true + } +} +platform_exceptions: { + platform: { + vendor: JUNIPER + } + deviations: { + hierarchical_weight_resolution_tolerance: 0.4 + explicit_interface_ref_definition: true } } platform_exceptions: { @@ -18,7 +29,7 @@ platform_exceptions: { vendor: NOKIA } deviations: { - use_vendor_native_acl_config: true + explicit_interface_ref_definition: true explicit_port_speed: true explicit_interface_in_default_vrf: true interface_enabled: true @@ -29,10 +40,10 @@ platform_exceptions: { vendor: ARISTA } deviations: { - route_policy_under_afi_unsupported: true omit_l2_mtu: true + deprecated_vlan_id: true interface_enabled: true default_network_instance: "default" } } -tags: TAGS_AGGREGATION +tags: TAGS_TRANSIT diff --git a/feature/gribi/otg_tests/ipv4_entry_with_aggregate_ports_test/ipv4_entry_with_aggregate_ports_test.go b/feature/gribi/otg_tests/ipv4_entry_with_aggregate_ports_test/ipv4_entry_with_aggregate_ports_test.go index bb81fb2e943..7e8bdaf7c50 100644 --- a/feature/gribi/otg_tests/ipv4_entry_with_aggregate_ports_test/ipv4_entry_with_aggregate_ports_test.go +++ b/feature/gribi/otg_tests/ipv4_entry_with_aggregate_ports_test/ipv4_entry_with_aggregate_ports_test.go @@ -407,14 +407,6 @@ func configureDUTBundle(t *testing.T, dut *ondatra.DUTDevice, aggPorts []*ondatr agg.GetOrCreateAggregation().LagType = oc.IfAggregate_AggregationType_STATIC gnmi.Replace(t, dut, gnmi.OC().Interface(aggID).Config(), agg) - // Static ARP configuration with neighbor IP as nh1IPAddr - if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) || deviations.GRIBIMACOverrideWithStaticARP(dut) { - ipv4 := agg.GetOrCreateSubinterface(0).GetOrCreateIpv4() - n4 := ipv4.GetOrCreateNeighbor(nh1IpAddr) - n4.LinkLayerAddress = ygot.String(staticDstMAC) - gnmi.Replace(t, dut, gnmi.OC().Interface(aggID).Config(), agg) - } - for _, port := range aggPorts { d := &oc.Root{} i := d.GetOrCreateInterface(port.Name()) @@ -426,6 +418,14 @@ func configureDUTBundle(t *testing.T, dut *ondatra.DUTDevice, aggPorts []*ondatr } gnmi.Replace(t, dut, gnmi.OC().Interface(port.Name()).Config(), i) } + + // Static ARP configuration with neighbor IP as nh1IPAddr + if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) || deviations.GRIBIMACOverrideWithStaticARP(dut) { + ipv4 := agg.GetOrCreateSubinterface(0).GetOrCreateIpv4() + n4 := ipv4.GetOrCreateNeighbor(nh1IpAddr) + n4.LinkLayerAddress = ygot.String(staticDstMAC) + gnmi.Replace(t, dut, gnmi.OC().Interface(aggID).Config(), agg) + } } // awaitTimeout calls a fluent client Await, adding a timeout to the context. diff --git a/feature/gribi/otg_tests/ipv4_entry_with_aggregate_ports_test/metadata.textproto b/feature/gribi/otg_tests/ipv4_entry_with_aggregate_ports_test/metadata.textproto index c84b014a0e7..896ce8a0e42 100644 --- a/feature/gribi/otg_tests/ipv4_entry_with_aggregate_ports_test/metadata.textproto +++ b/feature/gribi/otg_tests/ipv4_entry_with_aggregate_ports_test/metadata.textproto @@ -1,4 +1,4 @@ -# proto-file: third_party/openconfig/featureprofiles/proto/metadata.proto +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto # proto-message: Metadata uuid: "6cd082df-8344-4d75-b148-db572c387433" diff --git a/feature/gribi/otg_tests/static_lsp/README.md b/feature/gribi/otg_tests/static_lsp/README.md index 6df7598eb58..eced4b37bd8 100644 --- a/feature/gribi/otg_tests/static_lsp/README.md +++ b/feature/gribi/otg_tests/static_lsp/README.md @@ -1,4 +1,4 @@ -# TE-9.1: MPLS based forwarding Static LSP +# TE-9.2: MPLS based forwarding Static LSP ## Summary diff --git a/feature/gribi/otg_tests/weighted_balancing_test/setup_test.go b/feature/gribi/otg_tests/weighted_balancing_test/setup_test.go index 26cc43e6fda..7552b9339c9 100644 --- a/feature/gribi/otg_tests/weighted_balancing_test/setup_test.go +++ b/feature/gribi/otg_tests/weighted_balancing_test/setup_test.go @@ -187,6 +187,12 @@ func configureDUT(t testing.TB, dut *ondatra.DUTDevice) { dc := gnmi.OC() for _, dp := range dut.Ports() { if i := dutInterface(dp, dut); i != nil { + if dp.PMD() == ondatra.PMD100GBASEFR { + e := i.GetOrCreateEthernet() + e.AutoNegotiate = ygot.Bool(false) + e.DuplexMode = oc.Ethernet_DuplexMode_FULL + e.PortSpeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_100GB + } gnmi.Replace(t, dut, dc.Interface(dp.Name()).Config(), i) } else { t.Fatalf("No address found for port %v", dp) @@ -208,7 +214,11 @@ func configureDUT(t testing.TB, dut *ondatra.DUTDevice) { func configureATE(t testing.TB, ate *ondatra.ATEDevice) gosnappi.Config { t.Helper() config := gosnappi.NewConfig() + pmd100GFRPorts := []string{} for i, ap := range ate.Ports() { + if ap.PMD() == ondatra.PMD100GBASEFR { + pmd100GFRPorts = append(pmd100GFRPorts, ap.ID()) + } // DUT and ATE ports are connected by the same names. dutid := fmt.Sprintf("dut:%s", ap.ID()) ateid := fmt.Sprintf("ate:%s", ap.ID()) @@ -222,6 +232,13 @@ func configureATE(t testing.TB, ate *ondatra.ATEDevice) gosnappi.Config { SetAddress(portsIPv4[ateid]).SetGateway(portsIPv4[dutid]). SetPrefix(plen) } + // Disable FEC for 100G-FR ports because Novus does not support it. + if len(pmd100GFRPorts) > 0 { + l1Settings := config.Layer1().Add().SetName("L1").SetPortNames(pmd100GFRPorts) + l1Settings.SetAutoNegotiate(true).SetIeeeMediaDefaults(false).SetSpeed("speed_100_gbps") + autoNegotiate := l1Settings.AutoNegotiation() + autoNegotiate.SetRsFec(false) + } ate.OTG().PushConfig(t, config) return config } diff --git a/feature/interface/aggregate/feature.textproto b/feature/interface/aggregate/feature.textproto index 8be6a9fbbc5..5345b167a3f 100644 --- a/feature/interface/aggregate/feature.textproto +++ b/feature/interface/aggregate/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "interface_aggregate" diff --git a/feature/interface/holdtime/feature.textproto b/feature/interface/holdtime/feature.textproto index 881dc8f0047..26920b39624 100644 --- a/feature/interface/holdtime/feature.textproto +++ b/feature/interface/holdtime/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "interface_holdtime" diff --git a/feature/interface/holdtime/otg_tests/holdtime_test/holddown_timers_test.go b/feature/interface/holdtime/otg_tests/holdtime_test/holddown_timers_test.go new file mode 100644 index 00000000000..913505b0063 --- /dev/null +++ b/feature/interface/holdtime/otg_tests/holdtime_test/holddown_timers_test.go @@ -0,0 +1,616 @@ +package holddown_times_test + +import ( + "fmt" + "strconv" + "testing" + "time" + + "github.com/openconfig/ondatra/netutil" + + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ygot/ygot" +) + +const ( + ipv4PrefixLen = 30 + ipv6PrefixLen = 126 + lagName = "LAGRx" // OTG LAG NAME + upTimer = 5000 + downTimer = 300 + toleranceMS = 200 // Define the tolerance in milliseconds + +) + +var ( + aggID string + dutPort1Intf *ondatra.Port + ateSrc = attrs.Attributes{ + Name: "ateSrc", + MAC: "02:11:01:00:00:01", + IPv4: "192.0.2.1", + IPv6: "2001:db8::1", + IPv4Len: ipv4PrefixLen, + IPv6Len: ipv6PrefixLen, + } + + dutDst = attrs.Attributes{ + Desc: "DUT to ATE destination", + IPv4: "192.0.2.5", + IPv6: "2001:db8::5", + IPv4Len: ipv4PrefixLen, + IPv6Len: ipv6PrefixLen, + } + + ateDst = attrs.Attributes{ + Name: "ateDst", + MAC: "02:12:01:00:00:01", + IPv4: "192.0.2.6", + IPv6: "2001:db8::6", + IPv4Len: ipv4PrefixLen, + IPv6Len: ipv6PrefixLen, + } +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +func configureDUTBundle(t *testing.T, dut *ondatra.DUTDevice, aggPorts []*ondatra.Port, aggID string) { + t.Helper() + + agg := dutDst.NewOCInterface(aggID, dut) + agg.Type = oc.IETFInterfaces_InterfaceType_ieee8023adLag + agg.GetOrCreateAggregation().LagType = oc.IfAggregate_AggregationType_STATIC + gnmi.Replace(t, dut, gnmi.OC().Interface(aggID).Config(), agg) + + for _, port := range aggPorts { + d := &oc.Root{} + + i := d.GetOrCreateInterface(port.Name()) + i.GetOrCreateEthernet().AggregateId = ygot.String(aggID) + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + + if deviations.InterfaceEnabled(dut) { + i.Enabled = ygot.Bool(true) + } + gnmi.Replace(t, dut, gnmi.OC().Interface(port.Name()).Config(), i) + } +} + +func configureDUT(t *testing.T, dut *ondatra.DUTDevice, aggID string) { + t.Helper() + fptest.ConfigureDefaultNetworkInstance(t, dut) + dutAggPorts := []*ondatra.Port{ + dut.Port(t, "port1"), + } + configureDUTBundle(t, dut, dutAggPorts, aggID) + +} + +func configureOTG(t *testing.T, + ate *ondatra.ATEDevice, + aggID string) { + t.Helper() + + top := gosnappi.NewConfig() + + ateAggPorts := []*ondatra.Port{ + ate.Port(t, "port1"), + } + configureOTGBundle(t, top, ateAggPorts, aggID) + + t.Log(top.String()) + ate.OTG().PushConfig(t, top) + ate.OTG().StartProtocols(t) + + OTGInterfaceUP(t, ate) +} + +func OTGInterfaceUP(t *testing.T, + ate *ondatra.ATEDevice) { + + p1 := ondatra.ATE(t, "ate").Port(t, "port1") + portStateAction := gosnappi.NewControlState() + + // make sure interface is not down + portStateAction.Port().Link().SetPortNames([]string{p1.ID()}).SetState(gosnappi.StatePortLinkState.UP) + ate.OTG().SetControlState(t, portStateAction) +} + +func OTGInterfaceDOWN(t *testing.T, + ate *ondatra.ATEDevice, + dut *ondatra.DUTDevice) time.Time { + + p1 := ondatra.ATE(t, "ate").Port(t, "port1") + portStateAction := gosnappi.NewControlState() + timestamp := gnmi.Get(t, dut, gnmi.OC().System().CurrentDatetime().State()) + + timeObj, err := time.Parse(time.RFC3339Nano, timestamp) + if err != nil { + t.Errorf("Failed to parse time string: %v", timestamp) + return timeObj + } + + // make sure interface is not down + portStateAction.Port().Link().SetPortNames([]string{p1.ID()}).SetState(gosnappi.StatePortLinkState.DOWN) + ate.OTG().SetControlState(t, portStateAction) + + return timeObj +} + +func configureOTGBundle(t *testing.T, + + top gosnappi.Config, + aggPorts []*ondatra.Port, + aggID string) { + t.Helper() + agg := top.Lags().Add().SetName(lagName) + lagID, _ := strconv.Atoi(aggID) + agg.Protocol().Static().SetLagId(uint32(lagID)) + + for i, p := range aggPorts { + port := top.Ports().Add().SetName(p.ID()) + agg.Ports().Add().SetPortName(port.Name()).Ethernet().SetMac(ateSrc.MAC).SetName("LAGRx-" + strconv.Itoa(i)) + } + + dstDev := top.Devices().Add().SetName(agg.Name() + ".dev") + dstEth := dstDev.Ethernets().Add().SetName(lagName + ".Eth").SetMac(ateDst.MAC) + dstEth.Connection().SetLagName(agg.Name()) + dstEth.Ipv4Addresses().Add().SetName(lagName + ".IPv4").SetAddress(ateDst.IPv4).SetGateway(dutDst.IPv4).SetPrefix(uint32(ateDst.IPv4Len)) +} + +func displaySummaryTable(t *testing.T, + preActionTS, + postActionTS string, + actualDurationInMS, + minToleranceInMS, + maxToleranceInMS int64, + expectedOperStatus, + actualOperStatus string, + pass bool) { + + result := "FAIL" + if pass { + result = "PASS" + } + + // Prepare the strings for output. + expectedDurationStr := "300ms" // Assuming this is a constant value + + minToleranceStr := strconv.Itoa(int(minToleranceInMS)) + "ms" + maxToleranceStr := strconv.Itoa(int(maxToleranceInMS)) + "ms" + actualDurationStr := strconv.Itoa(int(actualDurationInMS)) + "ms" + + // Create a slice of metrics and corresponding values. + metrics := []string{"Pre-action TS", + "Post-action TS", + "Expected Duration", + "Actual Duration", + "Min Tolerance", + "Max Tolerance", + "Expected Oper Status", + "Actual Oper Status", + "Result"} + values := []string{preActionTS, + postActionTS, + expectedDurationStr, + actualDurationStr, + minToleranceStr, + maxToleranceStr, + expectedOperStatus, + actualOperStatus, + result} + + // Find the maximum width for the metrics to align the values. + maxMetricWidth := 0 + for _, metric := range metrics { + if len(metric) > maxMetricWidth { + maxMetricWidth = len(metric) + } + } + + // Create the vertical table. + table := "" + for i, metric := range metrics { + table += fmt.Sprintf("%-*s: %s\n", maxMetricWidth, metric, values[i]) + } + + t.Logf("\n%s", table) +} + +func flapOTGInterface(t *testing.T, + ate *ondatra.ATEDevice, + dut *ondatra.DUTDevice, + actionState string) (time.Time, time.Time, string, string) { + + // Shut down OTG Interface + p1 := ondatra.ATE(t, "ate").Port(t, "port1") + portStateAction := gosnappi.NewControlState() + + var otgStateChangeTsStr string + + // TC2 Step 1 Read timestamp of last oper-status change form DUT port-1 + preStateTSSTR := gnmi.Get(t, dut, gnmi.OC().Interface(aggID).LastChange().State()) + DutLastChangeTS1 := time.Unix(0, int64(preStateTSSTR)).UTC().Format(time.RFC3339Nano) + t.Logf("Step1. DutLastChangeTS1 is: %v", DutLastChangeTS1) + if actionState == "UP" { + portStateAction.Port().Link().SetPortNames([]string{p1.ID()}).SetState(gosnappi.StatePortLinkState.UP) + otgStateChangeTsStr = gnmi.Get(t, dut, gnmi.OC().System().CurrentDatetime().State()) + ate.OTG().SetControlState(t, portStateAction) + } else if actionState == "DOWN" { + // TC2 Step 2 Bring Down OTG Interface + t.Log("RT-5.5.2: Bring Down OTG Interface") + portStateAction.Port().Link().SetPortNames([]string{p1.ID()}).SetState(gosnappi.StatePortLinkState.DOWN) + otgStateChangeTsStr = gnmi.Get(t, dut, gnmi.OC().System().CurrentDatetime().State()) + ate.OTG().SetControlState(t, portStateAction) + + // TC2 Step 3 + t.Log("Step 3 sleeping 500ms") + time.Sleep(500 * time.Millisecond) + } + + // Step 4. Read timestamp of last oper-status change form DUT port-1 (DUT_LAST_CHANGE_TS) + postStateTSSTR := gnmi.Get(t, dut, gnmi.OC().Interface(aggID).LastChange().State()) + DutLastChangeTS2STR := time.Unix(0, int64(postStateTSSTR)).UTC().Format(time.RFC3339Nano) + DutLastChangeOper2 := gnmi.Get(t, dut, gnmi.OC().Interface(aggID).OperStatus().State()) + + var expectedStatus oc.E_Interface_OperStatus + if actionState == "UP" { + expectedStatus = oc.Interface_OperStatus_UP + } else if actionState == "DOWN" { + expectedStatus = oc.Interface_OperStatus_DOWN + } + + // Step 5. verify oper-status is DOWN + if DutLastChangeOper2 != expectedStatus { + t.Errorf("Interface %s status got %v, want %v", aggID, DutLastChangeTS2STR, expectedStatus.String()) + } else { + t.Logf("Interface %s status got %v, want %v", aggID, DutLastChangeTS2STR, expectedStatus.String()) + } + + // convert string type change to time.time + otgStateChangeTs, err := time.Parse(time.RFC3339Nano, otgStateChangeTsStr) + if err != nil { + t.Fatalf("failed to parse event timestamp: %v %v", err, otgStateChangeTs) + } + + DutLastChangeTS2, err := time.Parse(time.RFC3339Nano, DutLastChangeTS2STR) + if err != nil { + t.Fatalf("failed to parse event timestamp: %v %v", err, DutLastChangeTS2) + } + + // Step 6. verify oper-status last change time has changed + t.Log("Compare if pre and post timestamps are the same for the last change before and after shut event") + if DutLastChangeTS1 == DutLastChangeTS2STR { + t.Fatalf("Before Trigger Last Change was %v after trigger Last Change was %v", DutLastChangeTS1, DutLastChangeTS2STR) + } else { + t.Logf("Before Trigger Last Change was %v after trigger Last Change was %v", DutLastChangeTS1, DutLastChangeTS2STR) + } + + // convert to time objects + otgStateChangeTs = otgStateChangeTs.UTC() + DutLastChangeTS2 = DutLastChangeTS2.UTC() + + return otgStateChangeTs, DutLastChangeTS2, expectedStatus.String(), DutLastChangeOper2.String() + +} + +// verifyPortsUp asserts that each port on the device is operating. +func verifyPortsStatus(t *testing.T, dut *ondatra.DUTDevice, portState string, waitTime time.Duration) { + t.Helper() + + t.Logf("Checking Oper Status on %s", aggID) + + // Determine the expected status based on the portState argument. + var want oc.E_Interface_OperStatus + if portState == "UP" { + want = oc.Interface_OperStatus_UP + gnmi.Await(t, dut, + gnmi.OC().Interface(aggID).OperStatus().State(), + time.Second*waitTime, + oc.Interface_OperStatus_UP) + } else { + want = oc.Interface_OperStatus_DOWN + gnmi.Await(t, dut, + gnmi.OC().Interface(aggID).OperStatus().State(), + time.Second*waitTime, + oc.Interface_OperStatus_DOWN) + } + + status := gnmi.Get(t, dut, gnmi.OC().Interface(aggID).OperStatus().State()) + + // check the status and log the result. + if status != want { + t.Fatalf("Failed: %s Status: got %v, want %v", aggID, status, want) + } else { + t.Logf("Pass: %s Status: got %v, want %v", aggID, status, want) + } +} + +func TestHoldTimeConfig(t *testing.T) { + + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + + dutPort1Intf = dut.Port(t, "port1") + t.Run("ConfigureDUT Interfaces", func(t *testing.T) { + // Configure the DUT + aggID = netutil.NextAggregateInterface(t, dut) + t.Log(dutPort1Intf) + configureDUT(t, dut, aggID) + + }) + + t.Run("Configure Hold Timers on DUT", func(t *testing.T) { + // Construct the hold-time config object + holdTimeConfig := &oc.Interface_HoldTime{ + Up: ygot.Uint32(upTimer), + Down: ygot.Uint32(downTimer), + } + + intfPath := gnmi.OC().Interface(dutPort1Intf.Name()) + gnmi.Update(t, dut, intfPath.HoldTime().Config(), holdTimeConfig) + + }) + + t.Run("ConfigureOTG", func(t *testing.T) { + t.Logf("Configure ATE") + configureOTG(t, ate, aggID) + + }) + + t.Run(fmt.Sprintf("Verify Interface State for %s", aggID), func(t *testing.T) { + // Verify Port Status + t.Logf("Verifying port status for %s", aggID) + verifyPortsStatus(t, dut, "UP", 45) + }) + +} + +func TestTC1ValidateTimersConfig(t *testing.T) { + dut := ondatra.DUT(t, "dut") + + holdTimePath := gnmi.OC().Interface(dutPort1Intf.Name()).HoldTime().State() + + holdTimeState := gnmi.Get(t, dut, holdTimePath) + t.Log(holdTimeState) + if *holdTimeState.Up == upTimer && *holdTimeState.Down == downTimer { + t.Logf("Successfully configured times as up timer is %d and down timer"+ + " is %d", *holdTimeState.Up, *holdTimeState.Down) + } else { + t.Errorf("TC Failed: Configured up and down timers dont match what was configured "+ + "expected up %d got %d expected down %d got %d", upTimer, *holdTimeState.Up, + downTimer, *holdTimeState.Down) + } +} + +func TestTC2LongDown(t *testing.T) { + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + + var otgStateChangeTs, DutLastChangeTS2 time.Time + var expectedOper, actualOper string + + t.Run(fmt.Sprintf("Shut down OTG interface to cause remote fault on %s", aggID), func(t *testing.T) { + otgStateChangeTs, DutLastChangeTS2, expectedOper, actualOper = flapOTGInterface(t, ate, dut, "DOWN") + if expectedOper != actualOper { + t.Errorf("expectedOper and actualOper do not match: expected %s, got %s", expectedOper, actualOper) + } + }) + + duration := DutLastChangeTS2.Sub(otgStateChangeTs) + durationInMS := duration.Milliseconds() + + // Define the expected delay and tolerance + expectedDelayMS := 300 // Expected delay in milliseconds + minDuration := int64(expectedDelayMS - toleranceMS) + maxDuration := int64(expectedDelayMS + toleranceMS) + + // Check if the actual duration falls within the expected range + pass := durationInMS <= maxDuration + + t.Run(fmt.Sprintf("Calculate fault duration on %s", aggID), func(t *testing.T) { + t.Logf("Shutdown triggered at: %v", otgStateChangeTs) + t.Logf("Last change reported at: %v", DutLastChangeTS2) + t.Logf("Duration between shutdown triggered and last change reported: %v ms", durationInMS) + + if pass { + t.Logf("PASS: Duration is within the expected range; got %d ms", durationInMS) + } else { + t.Errorf("FAIL: Expected duration to be within %d ms to %d ms; got %d ms", minDuration, maxDuration, durationInMS) + } + }) + + t.Run("Bring back UP OTG Interface", func(t *testing.T) { + OTGInterfaceUP(t, ate) + t.Logf("Verifying port status for %s", aggID) + verifyPortsStatus(t, dut, "UP", 45) + }) + + t.Run("Verify test results", func(t *testing.T) { + displaySummaryTable(t, otgStateChangeTs.Format(time.RFC3339Nano), DutLastChangeTS2.Format(time.RFC3339Nano), + durationInMS, minDuration, maxDuration, expectedOper, actualOper, pass) + + }) + +} + +func TestTC3ShortUP(t *testing.T) { + + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + + t.Run("Start sending Ethernet Remote Fault on OTG", func(t *testing.T) { + + // shutting down OTG interface to emulate the RF + OTGInterfaceDOWN(t, ate, dut) + oper1 := gnmi.Get(t, dut, gnmi.OC().Interface(aggID).OperStatus().State()) + change1 := gnmi.Get(t, dut, gnmi.OC().Interface(aggID).LastChange().State()) + t.Log(oper1) + t.Log(change1) + + // bring port back up for 4 seconds below the 5000 ms hold up timer + OTGInterfaceUP(t, ate) + // shut the OTG interface back to down state + OTGInterfaceDOWN(t, ate, dut) + oper2 := gnmi.Get(t, dut, gnmi.OC().Interface(aggID).OperStatus().State()) + change2 := gnmi.Get(t, dut, gnmi.OC().Interface(aggID).LastChange().State()) + + // ensure the LAG interface is still down + verifyPortsStatus(t, dut, "DOWN", 4) + t.Log(oper2) + + change1Time := time.Unix(0, int64(change1)).UTC() + change2Time := time.Unix(0, int64(change2)).UTC() + + // Compare the times and ensure there is no change in the last change + if change1Time.Before(change2Time) || change1Time.After(change2Time) { + t.Errorf("Time 1 %v and Time 2 dont match %v", change1Time, change2Time) + } else if change1Time.Equal(change2Time) { + t.Logf("Time 1 %v and Time 2 the the same which is expected %v", change1Time, change2Time) + } + + // bring OTG port back up + OTGInterfaceUP(t, ate) + // verify interface is up for next test case + verifyPortsStatus(t, dut, "UP", 45) + + }) + +} + +func TestTC4SLongUP(t *testing.T) { + + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + + t.Run("Start sending Ethernet Remote Fault on OTG", func(t *testing.T) { + + // shutting down OTG interface to emulate the RF + OTGInterfaceDOWN(t, ate, dut) + time.Sleep(1 * time.Second) + change1 := gnmi.Get(t, dut, gnmi.OC().Interface(aggID).LastChange().State()) + t.Log(change1) + + // bring port back up for 4 seconds below the 5000 ms hold up timer + OTGInterfaceUP(t, ate) + // ensure the LAG interface is still down + verifyPortsStatus(t, dut, "UP", 30) + + // Collecting time stamp of interface up + change2 := gnmi.Get(t, dut, gnmi.OC().Interface(aggID).LastChange().State()) + + change1Time := time.Unix(0, int64(change1)).UTC() + change2Time := time.Unix(0, int64(change2)).UTC() + + // Calculate the difference in time + duration := change2Time.Sub(change1Time) + + // Convert the duration to milliseconds + durationInMS := duration.Milliseconds() + t.Logf("Duration interface %v ms", durationInMS) + + if durationInMS >= upTimer { + t.Logf("PASS: Expected interface up time delay of at least %v and got %v", upTimer, durationInMS) + } else { + t.Fatalf("FAIL: Expected interface up time delay of at least %v and got %v", upTimer, durationInMS) + } + + }) + +} + +func TestTC5ShortDOWN(t *testing.T) { + + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + + var time1 time.Time + var change1 *oc.Interface + + // Construct the hold-time config object + holdTimeConfig := &oc.Interface_HoldTime{ + Up: ygot.Uint32(upTimer), + Down: ygot.Uint32(2000), + } + + t.Run("Update hold timer configs down", func(t *testing.T) { + intfPath := gnmi.OC().Interface(dutPort1Intf.Name()) + gnmi.Update(t, dut, intfPath.HoldTime().Config(), holdTimeConfig) + + }) + + t.Run("Flap OTG Interfaces", func(t *testing.T) { + + t.Log("Verify Interface State before TC Start") + verifyPortsStatus(t, dut, "UP", 10) + // shutting down OTG interface to emulate the RF + t.Log("Shutdown OTG Interface") + change1 = gnmi.Get(t, dut, gnmi.OC().Interface(aggID).State()) + t.Logf("change1 last change is %v and status is %v", change1.LastChange, change1.AdminStatus) + + time1 = OTGInterfaceDOWN(t, ate, dut) + time.Sleep(200 * time.Millisecond) + t.Log("Bring OTG Interface Back UP") + OTGInterfaceUP(t, ate) + + }) + + t.Run("Verify Short Down Results", func(t *testing.T) { + + // Start building the log message + logMessage := "Interface Status Timeline\n" + + "----------------------------------------------------\n" + + "Event | Time | Oper Status\n" + + "----------------------------------------------------\n" + + "Last-change time 1 | %v | %v\n" + + "Trigger Start Time | %v | -\n" + + "Last-change Re-check | %v | %v\n" + + change2 := gnmi.Get(t, dut, gnmi.OC().Interface(aggID).State()) + + if *change2.LastChange == *change1.LastChange && change2.OperStatus == change1.OperStatus { + time2 := gnmi.Get(t, dut, gnmi.OC().System().CurrentDatetime().State()) + + // Dereference the value and convert to int64 before passing to time.Unix function + change2LastChangeTime := time.Unix(0, int64(*change2.LastChange)).UTC().Format(time.RFC3339Nano) + change1LastChangeTime := time.Unix(0, int64(*change1.LastChange)).UTC().Format(time.RFC3339Nano) + t1 := time1.UTC().Format(time.RFC3339Nano) + t2, err := time.Parse(time.RFC3339Nano, time2) + if err != nil { + t.Errorf("Failed to parse time string: %v", err) + return + } + + timeDiff := t2.Sub(time1).Milliseconds() + + logMessage += fmt.Sprintf("End Time | %v | -\n"+ + "-----------------------------------------------------\n"+ + "Total Elapsed Time: %vms\n", t2, timeDiff) + t.Logf(logMessage, change1LastChangeTime, change1.OperStatus, t1, change2LastChangeTime, change2.OperStatus) + + } else { + // Dereference the value and convert to int64 before passing to time.Unix function + change2LastChangeTime := time.Unix(0, int64(*change2.LastChange)).UTC().Format(time.RFC3339Nano) + change1LastChangeTime := time.Unix(0, int64(*change1.LastChange)).UTC().Format(time.RFC3339Nano) + t1 := time1.UTC().Format(time.RFC3339Nano) + + // Log failure message and the partially built log message without end time + t.Log("Failed due to an unexpected match such as last-change time or interface oper-status") + t.Fatalf(logMessage, change1LastChangeTime, change1.OperStatus, t1, change2LastChangeTime, change2.OperStatus) + } + }) + + t.Run("Verify port status UP", func(t *testing.T) { + t.Log("re-verify that the interface state is still up") + verifyPortsStatus(t, dut, "UP", 10) + + }) +} diff --git a/feature/interface/holdtime/otg_tests/holdtime_test/metadata.textproto b/feature/interface/holdtime/otg_tests/holdtime_test/metadata.textproto index 420c75186b1..6d2e250ac73 100644 --- a/feature/interface/holdtime/otg_tests/holdtime_test/metadata.textproto +++ b/feature/interface/holdtime/otg_tests/holdtime_test/metadata.textproto @@ -1,6 +1,15 @@ # proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto # proto-message: Metadata +uuid: "4ed6ff3f-b27e-4f46-93b2-8bcbc521d883" plan_id: "RT-5.5" -description: "Interface hold-times" +description: "Interface hold-time" testbed: TESTBED_DUT_ATE_2LINKS +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + ipv4_missing_enabled: true + } +} diff --git a/feature/interface/ip/feature.textproto b/feature/interface/ip/feature.textproto index fefb8ec57b8..b03ede8d819 100644 --- a/feature/interface/ip/feature.textproto +++ b/feature/interface/ip/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "interface_ip" diff --git a/feature/interface/ip/ipv6_slaac_link_local/otg_tests/ipv6_slaac_link_local_test/README.md b/feature/interface/ip/ipv6_slaac_link_local/otg_tests/ipv6_slaac_link_local_test/README.md index 04576be20c1..2009b5991b2 100644 --- a/feature/interface/ip/ipv6_slaac_link_local/otg_tests/ipv6_slaac_link_local_test/README.md +++ b/feature/interface/ip/ipv6_slaac_link_local/otg_tests/ipv6_slaac_link_local_test/README.md @@ -25,5 +25,5 @@ Enable IPv6 on interface level so ipv6 address of link-local scope is generated/ ## Protocol/RPC Parameter Coverage None -## Required DUT platform +## Minimum required DUT platform * FFF - fixed form factor diff --git a/feature/interface/singleton/feature.textproto b/feature/interface/singleton/feature.textproto index 35588043c7e..62fad1c9fc6 100644 --- a/feature/interface/singleton/feature.textproto +++ b/feature/interface/singleton/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "interface_singleton" diff --git a/feature/interface/staticarp/feature.textproto b/feature/interface/staticarp/feature.textproto index db7a4240b41..0dfd3be72c5 100644 --- a/feature/interface/staticarp/feature.textproto +++ b/feature/interface/staticarp/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "interface_staticarp" diff --git a/feature/isis/auth/feature.textproto b/feature/isis/auth/feature.textproto index 8f1c6148786..0300d1bc3ad 100644 --- a/feature/isis/auth/feature.textproto +++ b/feature/isis/auth/feature.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "isis_auth" diff --git a/feature/isis/feature.textproto b/feature/isis/feature.textproto index cef1e4f81f6..aaa62b80e57 100644 --- a/feature/isis/feature.textproto +++ b/feature/isis/feature.textproto @@ -1,3 +1,6 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile + id { name: "isis" version: 3 diff --git a/feature/isis/link_state_database/feature.textproto b/feature/isis/link_state_database/feature.textproto index 1cbee0d69ae..bb823bb97d6 100644 --- a/feature/isis/link_state_database/feature.textproto +++ b/feature/isis/link_state_database/feature.textproto @@ -1,3 +1,6 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile + id { name: "isis_link_state_database" version: 1 diff --git a/feature/isis/otg_tests/weighted_ecmp_test/README.md b/feature/isis/otg_tests/weighted_ecmp_test/README.md new file mode 100644 index 00000000000..e5755106280 --- /dev/null +++ b/feature/isis/otg_tests/weighted_ecmp_test/README.md @@ -0,0 +1,185 @@ +# RT-2.13: Weighted-ECMP for IS-IS + +## Summary + +This is to ensure that, + +* Implementations can be configured for weighted equal cost multipath (ECMP) + routing for IS-IS neighbors that are one hop away. + +* When WECMP is enabled, traffic destined to an IS-IS route represented by a + multipath set of next-hop interfaces will be unequally distributed across + the interfaces based on their bandwidth. + +## Testbed type + +[TESTBED_DUT_ATE_8LINKS](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_8.testbed) + +## Topolgy + +Each LAG bundle below is made up of 2x100G ports. + +```mermaid +graph LR; +A[ATE1:LAG1] <-- IBGP+IS-IS --> B[LAG1:DUT]; +C[DUT:LAG2] <-- IBGP+IS-IS --> D[LAG1:ATE2]; +E[DUT:LAG3] <-- IBGP+IS-IS --> F[LAG2:ATE2]; +G[DUT:LAG4] <-- IBGP+IS-IS --> H[LAG3:ATE2]; +``` + +## Procedure + +In the topology above, + +* Configure 1xLAG interface between ATE1<->DUT and 3xLAG interfaces between + DUT and ATE2. Each LAG interface is expected to be of 2x100Gbps + +* Configure IPv4 and IPv6 L2 adjacencies between DUT and ATE LAG bundles. + Therefore, DUT will have 1xIS-IS adjacency with ATE1 i.e. + DUT:LAG1<->ATE1:LAG1, and 3xIS-IS adjacencies with ATE2 i.e. + DUT:LAG2<->ATE2:LAG1, DUT:LAG3<->ATE2:LAG2 and DUT:LAG4<->ATE2:LAG3 + + * /network-instances/network-instance/protocols/protocol/isis/global/afi-safi + + * /network-instances/network-instance/protocols/protocol/isis/global/config/level-capability, + set to LEVEL_2 + + * /network-instances/network-instance/protocols/protocol/isis/levels/level/config/metric-style + set to WIDE_METRIC + +* Configure IPv4 and IPv6 IBGP peering between both ATEs and the DUT using + their loopback addresses for both IPv4 and IPv6 address families. + + * /network-instances/network-instance/protocols/protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/config + +* Attach a network with an IPv4 and an IPv6 prefix to ATE2 and have it + advertise these prefixes over its IBGP peering with the DUT. The DUT in turn + should advertise these prefixes over its IBGP peering with ATE1 + + * Please use `IPv4 prefix = 100.0.1.0/24` and `IPv6 prefix = + 2001:db8:64:64::/64` + +* Similarly, attach a different network to ATE1 with IPv4 and IPv6 prefixes + and advertise the same over its IBGP peering with the DUT. + + * Please use `IPv4 prefix = 100.0.2.0/24` and `IPv6 prefix = + 2001:db8:64:65::/64` + +* On the DUT, enable WECMP loadbalancing for multipath IS-IS routes and set + the load-balancing-weight to use LAG bandwidth. + + * /network-instances/network-instance/protocols/protocol/isis/global/config/weighted-ecmp + set to Enabled + + * /network-instances/network-instance/protocols/protocol/isis/interfaces/interface/weighted-ecmp/config/load-balancing-weight + set to Auto + +## RT-9.1: Equal distribution of traffic + +* Start 1024 flows from IPv4 addresses in 100.0.2.0/24 to 100.0.1.0/24 + +* Start 1024 flows from IPv6 addresses in 2001:db8:64:65::/64 to + 2001:db8:64:64::/64 + + +### Verification + +* Ensure that the DUT has learnt the routes for prefixes 100.0.1.0/24 and + 2001:db8:64:64::/64 over IBGP. Following paths + + * /network-instances/network-instance/afts/next-hops/next-hop/state/ip-address + +* Ensure that the DUT has learnt routes to the IPv4 and IPv6 loopback + addresses of ATE2. It is expected that these prefixes are reachable via 3 + different Next-Hop addresses corresponding to the LAG1, LAG2 and LAG3 + interfaces on ATE2. + +* It is expected that the IS-IS instance in DUT will equally distribute the + traffic received on DUT:LAG1 over the LAG bundles corresponding to + ATE2:LAG1, ATE2:LAG2 and ATE2:LAG3 when the 3 LAG bundles have the same + bandwidth available. + + * Traffic distribution between DUT:LAG2, DUT:LAG3 and DUT:LAG4 is expected + to be ~33% each of the total traffic received on DUT:LAG1. + + * Check for the following paths + + * /network-instances/network-instance/protocols/protocol/isis/global/state/weighted-ecmp, + should be true + + * /network-instances/network-instance/protocols/protocol/isis/interfaces/interface/weighted-ecmp/state/load-balancing-weight, + should be auto + + * /interfaces/interface/state/counters/out-pkts + + * /interfaces/interface/state/counters/in-pkts + +## RT-9.2: Unequal distribution of traffic + +* Stop traffic from RT-9.1 and introduce a failure by disabling one of the + member interfaces in ATE2:LAG1. + +* Restart 1024 flows from IPv4 addresses in 100.0.2.0/24 to 100.0.1.0/24 + +* Restart 1024 flows from IPv6 addresses in 2001:db8:64:65::/64 to + 2001:db8:64:64::/64 + + +### Verification + +* It is expected that the IS-IS instance in DUT will unequally distribute the + traffic received from ATE1:LAG1 over the LAG bundles corresponding to + ATE2:LAG1, ATE2:LAG2 and ATE3:LAG3. + + * Traffic on DUT:LAG2 is expected to be ~20% while traffic on DUT:LAG3 and + DUT:LAG4 is expected to be ~40% each of the total traffic received on + DUT:LAG1. If the traffic is not unequally shared between the DUT LAG + bundles towards ATE2 then this test is a failure. + + * Check for the following paths + + * /interfaces/interface/state/counters/out-pkts + + * /interfaces/interface/state/counters/in-pkts + +### Config paths + +* /network-instances/network-instance/protocols/protocol/bgp/peer-groups/peer-group/config +* /network-instances/network-instance/protocols/protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/config + +* /network-instances/network-instance/protocols/protocol/isis/global/afi-safi + +* /network-instances/network-instance/protocols/protocol/isis/global/config/level-capability + +* /network-instances/network-instance/protocols/protocol/isis/levels/level/config/metric-style + +* /network-instances/network-instance/protocols/protocol/isis/global/config/weighted-ecmp + +* /network-instances/network-instance/protocols/protocol/isis/interfaces/interface/weighted-ecmp/config/load-balancing-weight + +* /routing-policy/defined-sets/prefix-sets/prefix-set/ + +* /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/config/ip-prefix + +* /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/config/masklength-range/exact + +* /routing-policy/policy-definitions/policy-definition/config/name + +* /routing-policy/policy-definitions/policy-definition/statements/statement/config/name + +* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/prefix-set + +* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/match-set-options + +* /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result/ACCEPT_ROUTE + +* /network-instances/network-instance/protocols/protocol/bgp/neighbors/peer-group/afi-safis/afi-safi/apply-policy/config/import-policy + +* /network-instances/network-instance/protocols/protocol/bgp/neighbors/peer-group/afi-safis/afi-safi/apply-policy/config/export-policy + +### Telemetry Parameter Coverage + +* /network-instances/network-instance/protocols/protocol/isis/global/state/weighted-ecmp +* /network-instances/network-instance/protocols/protocol/isis/interfaces/interface/weighted-ecmp/state/load-balancing-weight +* /interfaces/interface/state/counters/out-pkts +* /interfaces/interface/state/counters/in-pkts diff --git a/feature/isis/otg_tests/weighted_ecmp_test/metadata.textproto b/feature/isis/otg_tests/weighted_ecmp_test/metadata.textproto new file mode 100644 index 00000000000..178c2e58a26 --- /dev/null +++ b/feature/isis/otg_tests/weighted_ecmp_test/metadata.textproto @@ -0,0 +1,24 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "2beaac46-9b7b-49c4-9bde-62ad530aa5c4" +plan_id: "RT-2.13" +description: "Weighted-ECMP for IS-IS" +testbed: TESTBED_DUT_ATE_8LINKS +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + interface_enabled: true + default_network_instance: "default" + omit_l2_mtu: true + isis_instance_enabled_required: true + isis_interface_afi_unsupported: true + missing_isis_interface_afi_safi_enable: true + isis_require_same_l1_metric_with_l2_metric: true + route_policy_under_afi_unsupported: true + static_protocol_name: "STATIC" + rib_wecmp: true + } +} diff --git a/feature/isis/otg_tests/weighted_ecmp_test/weighted_ecmp_test.go b/feature/isis/otg_tests/weighted_ecmp_test/weighted_ecmp_test.go new file mode 100644 index 00000000000..ad05d96cad3 --- /dev/null +++ b/feature/isis/otg_tests/weighted_ecmp_test/weighted_ecmp_test.go @@ -0,0 +1,663 @@ +package weighted_ecmp_test + +import ( + "fmt" + "testing" + "time" + + "math/rand" + + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/cfgplugins" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/otgutils" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ondatra/netutil" + "github.com/openconfig/ygot/ygot" +) + +const ( + ipv4PLen = 30 + ipv6PLen = 126 + isisInstance = "DEFAULT" + dutAreaAddress = "49.0001" + ateAreaAddress = "49" + dutSysID = "1920.0000.2001" + asn = 64501 + acceptRoutePolicy = "PERMIT-ALL" + trafficPPS = 50000 // Should be 5000000 + trafficv6PPS = 50000 // Should be 5000000 + srcTrafficV4 = "100.0.2.1" + srcTrafficV6 = "2001:db8:64:65::1" + dstTrafficV4 = "100.0.1.1" + dstTrafficV6 = "2001:db8:64:64::1" + v4Count = 254 + v6Count = 1000 // Should be 10000000 +) + +type aggPortData struct { + dutIPv4 string + ateIPv4 string + dutIPv6 string + ateIPv6 string + ateAggName string + ateAggMAC string + atePort1MAC string + atePort2MAC string + ateISISSysID string + ateLoopbackV4 string + ateLoopbackV6 string +} + +type ipAddr struct { + ip string + prefix uint32 +} + +var ( + agg1 = &aggPortData{ + dutIPv4: "192.0.2.1", + ateIPv4: "192.0.2.2", + dutIPv6: "2001:db8::1", + ateIPv6: "2001:db8::2", + ateAggName: "lag1", + ateAggMAC: "02:00:01:01:01:01", + atePort1MAC: "02:00:01:01:01:02", + atePort2MAC: "02:00:01:01:01:03", + ateISISSysID: "640000000002", + ateLoopbackV4: "192.0.2.17", + ateLoopbackV6: "2001:db8::17", + } + agg2 = &aggPortData{ + dutIPv4: "192.0.2.5", + ateIPv4: "192.0.2.6", + dutIPv6: "2001:db8::5", + ateIPv6: "2001:db8::6", + ateAggName: "lag2", + ateAggMAC: "02:00:01:01:01:04", + atePort1MAC: "02:00:01:01:01:05", + atePort2MAC: "02:00:01:01:01:06", + ateISISSysID: "640000000003", + ateLoopbackV4: "192.0.2.18", + ateLoopbackV6: "2001:db8::18", + } + agg3 = &aggPortData{ + dutIPv4: "192.0.2.9", + ateIPv4: "192.0.2.10", + dutIPv6: "2001:db8::11", + ateIPv6: "2001:db8::12", + ateAggName: "lag3", + ateAggMAC: "02:00:01:01:01:07", + atePort1MAC: "02:00:01:01:01:08", + atePort2MAC: "02:00:01:01:01:09", + ateISISSysID: "640000000004", + ateLoopbackV4: "192.0.2.18", + ateLoopbackV6: "2001:db8::18", + } + agg4 = &aggPortData{ + dutIPv4: "192.0.2.13", + ateIPv4: "192.0.2.14", + dutIPv6: "2001:db8::14", + ateIPv6: "2001:db8::15", + ateAggName: "lag4", + ateAggMAC: "02:00:01:01:01:10", + atePort1MAC: "02:00:01:01:01:11", + atePort2MAC: "02:00:01:01:01:12", + ateISISSysID: "640000000005", + ateLoopbackV4: "192.0.2.18", + ateLoopbackV6: "2001:db8::18", + } + + dutLoopback = attrs.Attributes{ + Desc: "Loopback ip", + IPv4: "192.0.2.21", + IPv6: "2001:db8::21", + IPv4Len: 32, + IPv6Len: 128, + } + ate1AdvV4 = &ipAddr{ip: "100.0.2.0", prefix: 24} + ate1AdvV6 = &ipAddr{ip: "2001:db8:64:65::0", prefix: 64} + ate2AdvV4 = &ipAddr{ip: "100.0.1.0", prefix: 24} + ate2AdvV6 = &ipAddr{ip: "2001:db8:64:64::0", prefix: 64} + + equalDistributionWeights = []uint64{33, 33, 33} + unequalDistributionWeights = []uint64{20, 40, 40} + + ecmpTolerance = uint64(1) +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +func TestWeightedECMPForISIS(t *testing.T) { + // ondatra.Debug().Breakpoint(t) + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + + aggIDs := configureDUT(t, dut) + + // Enable weighted ECMP and set LoadBalancing to Auto + if !deviations.RibWecmp(dut) { + b := &gnmi.SetBatch{} + // isisPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance) + isisPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance).Isis() + gnmi.BatchReplace(b, isisPath.Global().WeightedEcmp().Config(), true) + for _, aggID := range aggIDs { + gnmi.BatchReplace(b, isisPath.Interface(aggID).WeightedEcmp().Config(), &oc.NetworkInstance_Protocol_Isis_Interface_WeightedEcmp{ + LoadBalancingWeight: oc.NetworkInstance_Protocol_Isis_Interface_WeightedEcmp_LoadBalancingWeight_Union(oc.WeightedEcmp_LoadBalancingWeight_auto), + }) + } + b.Set(t, dut) + } + + top := configureATE(t, ate) + flows := configureFlows(t, top, ate1AdvV4, ate1AdvV6, ate2AdvV4, ate2AdvV6) + ate.OTG().PushConfig(t, top) + ate.OTG().StartProtocols(t) + for _, agg := range []*aggPortData{agg1, agg2} { + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + gnmi.Await(t, dut, bgpPath.Neighbor(agg.ateLoopbackV4).SessionState().State(), time.Minute, oc.Bgp_Neighbor_SessionState_ESTABLISHED) + gnmi.Await(t, dut, bgpPath.Neighbor(agg.ateLoopbackV6).SessionState().State(), time.Minute, oc.Bgp_Neighbor_SessionState_ESTABLISHED) + } + + startTraffic(t, ate, top) + t.Run("Equal_Distribution_Of_Traffic", func(t *testing.T) { + for _, flow := range flows { + loss := otgutils.GetFlowLossPct(t, ate.OTG(), flow.Name(), 20*time.Second) + if got, want := loss, 0.0; got != want { + t.Errorf("Flow %s loss: got %f, want %f", flow.Name(), got, want) + } + } + weights := trafficRXWeights(t, ate, []string{agg2.ateAggName, agg3.ateAggName, agg4.ateAggName}) + for idx, weight := range equalDistributionWeights { + if got, want := weights[idx], weight; got < want-ecmpTolerance || got > want+ecmpTolerance { + t.Errorf("ECMP Percentage for Aggregate Index: %d: got %d, want %d", idx+1, got, want) + } + } + }) + + // Disable ATE2:Port1 + if deviations.ATEPortLinkStateOperationsUnsupported(ate) { + p3 := dut.Port(t, "port3") + gnmi.Replace(t, dut, gnmi.OC().Interface(p3.Name()).Enabled().Config(), false) + t.Logf("Disable ATE2:Port1: %s, %s", p3.Name(), gnmi.OC().Interface(p3.Name()).OperStatus().State()) + } else { + p3 := ate.Port(t, "port3") // ATE:port3 is ATE2:port1 + psa := gosnappi.NewControlState() + psa.Port().Link().SetPortNames([]string{p3.ID()}).SetState(gosnappi.StatePortLinkState.DOWN) + ate.OTG().SetControlState(t, psa) + time.Sleep(10 * time.Second) + defer func() { + psa := gosnappi.NewControlState() + psa.Port().Link().SetPortNames([]string{p3.ID()}).SetState(gosnappi.StatePortLinkState.UP) + ate.OTG().SetControlState(t, psa) + }() + } + p3 := dut.Port(t, "port3") + gnmi.Await(t, dut, gnmi.OC().Interface(p3.Name()).OperStatus().State(), time.Minute*2, oc.Interface_OperStatus_DOWN) + top.Flows().Clear() + + startTraffic(t, ate, top) + + t.Run("Unequal_Distribution_Of_Traffic", func(t *testing.T) { + for _, flow := range flows { + loss := otgutils.GetFlowLossPct(t, ate.OTG(), flow.Name(), 20*time.Second) + if got, want := loss, 0.0; got != want { + t.Errorf("Flow %s loss: got %f, want %f", flow.Name(), got, want) + } + } + weights := trafficRXWeights(t, ate, []string{agg2.ateAggName, agg3.ateAggName, agg4.ateAggName}) + for idx, weight := range unequalDistributionWeights { + if got, want := weights[idx], weight; got < want-ecmpTolerance || got > want+ecmpTolerance { + t.Errorf("ECMP Percentage for Aggregate Index: %d: got %d, want %d", idx+1, got, want) + } + } + }) +} + +func trafficRXWeights(t *testing.T, ate *ondatra.ATEDevice, aggNames []string) []uint64 { + t.Helper() + var rxs []uint64 + for _, aggName := range aggNames { + metrics := gnmi.Get(t, ate.OTG(), gnmi.OTG().Lag(aggName).State()) + rxs = append(rxs, metrics.GetCounters().GetInFrames()) + } + var total uint64 + for _, rx := range rxs { + total += rx + } + for idx, rx := range rxs { + rxs[idx] = (rx * 100) / total + } + return rxs +} + +func startTraffic(t *testing.T, ate *ondatra.ATEDevice, top gosnappi.Config) { + t.Helper() + ate.OTG().StartTraffic(t) + time.Sleep(time.Minute) + ate.OTG().StopTraffic(t) + otgutils.LogFlowMetrics(t, ate.OTG(), top) + otgutils.LogLAGMetrics(t, ate.OTG(), top) +} + +func randRange(t *testing.T, start, end uint32, count int) []uint32 { + if count > int(end-start) { + t.Fatal("randRange: count greater than end-start.") + } + rand.New(rand.NewSource(time.Now().UnixNano())) + var result []uint32 + for len(result) < count { + diff := end - start + randomValue := rand.Int31n(int32(diff)) + int32(start) + result = append(result, uint32(randomValue)) + } + return result +} + +func configureFlows(t *testing.T, top gosnappi.Config, srcV4, srcV6, dstV4, dstV6 *ipAddr) []gosnappi.Flow { + t.Helper() + top.Flows().Clear() + fV4 := top.Flows().Add().SetName("flowV4") + fV4.Metrics().SetEnable(true) + fV4.TxRx().Device(). + SetTxNames([]string{agg1.ateAggName + ".IPv4"}). + SetRxNames([]string{agg2.ateAggName + ".IPv4", agg3.ateAggName + ".IPv4", agg4.ateAggName + ".IPv4"}) + fV4.Size().SetFixed(1500) + fV4.Rate().SetPps(trafficPPS) + eV4 := fV4.Packet().Add().Ethernet() + eV4.Src().SetValue(agg1.ateAggMAC) + v4 := fV4.Packet().Add().Ipv4() + v4.Src().Increment().SetStart(srcTrafficV4).SetCount(v4Count) + v4.Dst().Increment().SetStart(dstTrafficV4).SetCount(v4Count) + udp := fV4.Packet().Add().Udp() + udp.SrcPort().SetValues(randRange(t, 34525, 65535, 500)) + udp.DstPort().SetValues(randRange(t, 49152, 65535, 500)) + + fV6 := top.Flows().Add().SetName("flowV6") + fV6.Metrics().SetEnable(true) + fV6.TxRx().Device(). + SetTxNames([]string{agg1.ateAggName + ".IPv6"}). + SetRxNames([]string{agg2.ateAggName + ".IPv6", agg3.ateAggName + ".IPv6", agg4.ateAggName + ".IPv6"}) + fV6.Size().SetFixed(1500) + fV6.Rate().SetPps(trafficv6PPS) + eV6 := fV6.Packet().Add().Ethernet() + eV6.Src().SetValue(agg1.ateAggMAC) + + v6 := fV6.Packet().Add().Ipv6() + v6.Src().Increment().SetStart(srcTrafficV6).SetCount(v6Count) + v6.Dst().Increment().SetStart(dstTrafficV6).SetCount(v6Count) + udpv6 := fV6.Packet().Add().Udp() + udpv6.SrcPort().SetValues(randRange(t, 35521, 65535, 500)) + udpv6.DstPort().SetValues(randRange(t, 49152, 65535, 500)) + return []gosnappi.Flow{fV4, fV6} +} + +func configureATE(t *testing.T, ate *ondatra.ATEDevice) gosnappi.Config { + t.Helper() + top := gosnappi.NewConfig() + pmd100GFRPorts := []string{} + + for aggIdx, a := range []*aggPortData{agg1, agg2, agg3, agg4} { + p1 := ate.Port(t, fmt.Sprintf("port%d", (aggIdx*2)+1)) + p2 := ate.Port(t, fmt.Sprintf("port%d", (aggIdx*2)+2)) + top.Ports().Add().SetName(p1.ID()) + top.Ports().Add().SetName(p2.ID()) + if p1.PMD() == ondatra.PMD100GBASEFR { + pmd100GFRPorts = append(pmd100GFRPorts, p1.ID()) + } + if p2.PMD() == ondatra.PMD100GBASEFR { + pmd100GFRPorts = append(pmd100GFRPorts, p2.ID()) + } + + agg := top.Lags().Add().SetName(a.ateAggName) + agg.Protocol().Static().SetLagId(uint32(aggIdx + 1)) + + lagDev := top.Devices().Add().SetName(agg.Name() + ".Dev") + lagEth := lagDev.Ethernets().Add().SetName(agg.Name() + ".Eth").SetMac(a.ateAggMAC) + lagEth.Connection().SetLagName(agg.Name()) + lagEth.Ipv4Addresses().Add().SetName(agg.Name() + ".IPv4").SetAddress(a.ateIPv4).SetGateway(a.dutIPv4).SetPrefix(ipv4PLen) + lagEth.Ipv6Addresses().Add().SetName(agg.Name() + ".IPv6").SetAddress(a.ateIPv6).SetGateway(a.dutIPv6).SetPrefix(ipv6PLen) + lagDev.Ipv4Loopbacks().Add().SetName(agg.Name() + ".Loopback4").SetEthName(lagEth.Name()).SetAddress(a.ateLoopbackV4) + lagDev.Ipv6Loopbacks().Add().SetName(agg.Name() + ".Loopback6").SetEthName(lagEth.Name()).SetAddress(a.ateLoopbackV6) + + agg.Ports().Add().SetPortName(p1.ID()).Ethernet().SetMac(a.atePort1MAC).SetName(a.ateAggName + ".1") + agg.Ports().Add().SetPortName(p2.ID()).Ethernet().SetMac(a.atePort2MAC).SetName(a.ateAggName + ".2") + + configureOTGISIS(t, lagDev, a) + if aggIdx == 0 { + configureOTGBGP(t, lagDev, a, ate1AdvV4, ate1AdvV6) + } else { + configureOTGBGP(t, lagDev, a, ate2AdvV4, ate2AdvV6) + } + } + + // Disable FEC for 100G-FR ports because Novus does not support it. + if len(pmd100GFRPorts) > 0 { + l1Settings := top.Layer1().Add().SetName("L1").SetPortNames(pmd100GFRPorts) + l1Settings.SetAutoNegotiate(true).SetIeeeMediaDefaults(false).SetSpeed("speed_100_gbps") + autoNegotiate := l1Settings.AutoNegotiation() + autoNegotiate.SetRsFec(false) + } + return top +} + +func configureOTGBGP(t *testing.T, dev gosnappi.Device, agg *aggPortData, advV4, advV6 *ipAddr) { + t.Helper() + v4 := dev.Ipv4Loopbacks().Items()[0] + v6 := dev.Ipv6Loopbacks().Items()[0] + + iDutBgp := dev.Bgp().SetRouterId(agg.ateIPv4) + iDutBgp4Peer := iDutBgp.Ipv4Interfaces().Add().SetIpv4Name(v4.Name()).Peers().Add().SetName(agg.ateAggName + ".BGP4.peer") + iDutBgp4Peer.SetPeerAddress(dutLoopback.IPv4).SetAsNumber(asn).SetAsType(gosnappi.BgpV4PeerAsType.IBGP) + iDutBgp4Peer.Capability().SetIpv4UnicastAddPath(true).SetIpv6UnicastAddPath(false) + iDutBgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(false) + + iDutBgp6Peer := iDutBgp.Ipv6Interfaces().Add().SetIpv6Name(v6.Name()).Peers().Add().SetName(agg.ateAggName + ".BGP6.peer") + iDutBgp6Peer.SetPeerAddress(dutLoopback.IPv6).SetAsNumber(asn).SetAsType(gosnappi.BgpV6PeerAsType.IBGP) + iDutBgp6Peer.Capability().SetIpv4UnicastAddPath(false).SetIpv6UnicastAddPath(true) + iDutBgp6Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(false).SetUnicastIpv6Prefix(true) + + bgpNeti1Bgp4PeerRoutes := iDutBgp4Peer.V4Routes().Add().SetName(agg.ateAggName + ".BGP4.Route") + bgpNeti1Bgp4PeerRoutes.SetNextHopIpv4Address(agg.ateLoopbackV4). + SetNextHopAddressType(gosnappi.BgpV4RouteRangeNextHopAddressType.IPV4). + SetNextHopMode(gosnappi.BgpV4RouteRangeNextHopMode.MANUAL) + bgpNeti1Bgp4PeerRoutes.Addresses().Add().SetAddress(advV4.ip).SetPrefix(advV4.prefix).SetCount(1) + bgpNeti1Bgp4PeerRoutes.AddPath().SetPathId(1) + + bgpNeti1Bgp6PeerRoutes := iDutBgp6Peer.V6Routes().Add().SetName(agg.ateAggName + ".BGP6.Route") + bgpNeti1Bgp6PeerRoutes.Addresses().Add().SetAddress(advV6.ip).SetPrefix(advV6.prefix).SetCount(1) + bgpNeti1Bgp6PeerRoutes.AddPath().SetPathId(1) +} + +func configureOTGISIS(t *testing.T, dev gosnappi.Device, agg *aggPortData) { + t.Helper() + isis := dev.Isis().SetSystemId(agg.ateISISSysID).SetName(agg.ateAggName + ".ISIS") + isis.Basic().SetHostname(isis.Name()) + isis.Advanced().SetAreaAddresses([]string{ateAreaAddress}) + + isisInt := isis.Interfaces().Add(). + SetEthName(dev.Ethernets().Items()[0].Name()).SetName(agg.ateAggName + ".ISISInt"). + SetNetworkType(gosnappi.IsisInterfaceNetworkType.POINT_TO_POINT). + SetLevelType(gosnappi.IsisInterfaceLevelType.LEVEL_2).SetMetric(10) + isisInt.Advanced().SetAutoAdjustMtu(true).SetAutoAdjustArea(true).SetAutoAdjustSupportedProtocols(true) +} + +func configureDUT(t *testing.T, dut *ondatra.DUTDevice) []string { + t.Helper() + fptest.ConfigureDefaultNetworkInstance(t, dut) + + configureDUTLoopback(t, dut) + + var aggIDs []string + for aggIdx, a := range []*aggPortData{agg1, agg2, agg3, agg4} { + b := &gnmi.SetBatch{} + d := &oc.Root{} + + aggID := netutil.NextAggregateInterface(t, dut) + aggIDs = append(aggIDs, aggID) + + agg := d.GetOrCreateInterface(aggID) + agg.GetOrCreateAggregation().LagType = oc.IfAggregate_AggregationType_STATIC + agg.Type = oc.IETFInterfaces_InterfaceType_ieee8023adLag + agg.Description = ygot.String(a.ateAggName) + if deviations.InterfaceEnabled(dut) { + agg.Enabled = ygot.Bool(true) + } + s := agg.GetOrCreateSubinterface(0) + s4 := s.GetOrCreateIpv4() + if deviations.InterfaceEnabled(dut) { + s4.Enabled = ygot.Bool(true) + } + a4 := s4.GetOrCreateAddress(a.dutIPv4) + a4.PrefixLength = ygot.Uint8(ipv4PLen) + + s6 := s.GetOrCreateIpv6() + if deviations.InterfaceEnabled(dut) { + s6.Enabled = ygot.Bool(true) + } + a6 := s6.GetOrCreateAddress(a.dutIPv6) + a6.PrefixLength = ygot.Uint8(ipv6PLen) + + gnmi.BatchDelete(b, gnmi.OC().Interface(aggID).Aggregation().MinLinks().Config()) + gnmi.BatchReplace(b, gnmi.OC().Interface(aggID).Config(), agg) + + p1 := dut.Port(t, fmt.Sprintf("port%d", (aggIdx*2)+1)) + p2 := dut.Port(t, fmt.Sprintf("port%d", (aggIdx*2)+2)) + for _, port := range []*ondatra.Port{p1, p2} { + gnmi.BatchDelete(b, gnmi.OC().Interface(port.Name()).Ethernet().AggregateId().Config()) + + i := d.GetOrCreateInterface(port.Name()) + i.Description = ygot.String(fmt.Sprintf("LAG - Member -%s", port.Name())) + e := i.GetOrCreateEthernet() + e.AggregateId = ygot.String(aggID) + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + + if deviations.InterfaceEnabled(dut) { + i.Enabled = ygot.Bool(true) + } + if port.PMD() == ondatra.PMD100GBASEFR { + e.AutoNegotiate = ygot.Bool(false) + e.DuplexMode = oc.Ethernet_DuplexMode_FULL + e.PortSpeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_100GB + } + + gnmi.BatchReplace(b, gnmi.OC().Interface(port.Name()).Config(), i) + } + b.Set(t, dut) + } + // Wait for LAG interfaces to be UP + for _, aggID := range aggIDs { + gnmi.Await(t, dut, gnmi.OC().Interface(aggID).AdminStatus().State(), 30*time.Second, oc.Interface_AdminStatus_UP) + } + configureStaticRouteToATELoopbacks(t, dut) + configureRoutingPolicy(t, dut) + configureDUTISIS(t, dut, aggIDs) + configureDUTBGP(t, dut) + return aggIDs +} + +func configureRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + + d := &oc.Root{} + rp := d.GetOrCreateRoutingPolicy() + pdef := rp.GetOrCreatePolicyDefinition(acceptRoutePolicy) + stmt, _ := pdef.AppendNewStatement("20") + stmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().PolicyDefinition(acceptRoutePolicy).Config(), pdef) +} + +func configureDUTLoopback(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + lb := netutil.LoopbackInterface(t, dut, 0) + lo0 := gnmi.OC().Interface(lb).Subinterface(0) + ipv4Addrs := gnmi.LookupAll(t, dut, lo0.Ipv4().AddressAny().State()) + ipv6Addrs := gnmi.LookupAll(t, dut, lo0.Ipv6().AddressAny().State()) + foundV4 := false + for _, ip := range ipv4Addrs { + if v, ok := ip.Val(); ok { + foundV4 = true + dutLoopback.IPv4 = v.GetIp() + break + } + } + foundV6 := false + for _, ip := range ipv6Addrs { + if v, ok := ip.Val(); ok { + foundV6 = true + dutLoopback.IPv6 = v.GetIp() + break + } + } + if !foundV4 || !foundV6 { + lo1 := dutLoopback.NewOCInterface(lb, dut) + lo1.Type = oc.IETFInterfaces_InterfaceType_softwareLoopback + gnmi.Update(t, dut, gnmi.OC().Interface(lb).Config(), lo1) + } +} + +func configureStaticRouteToATELoopbacks(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + + sr4ATE1 := &cfgplugins.StaticRouteCfg{ + NetworkInstance: deviations.DefaultNetworkInstance(dut), + Prefix: agg1.ateLoopbackV4 + "/32", + NextHops: map[string]oc.NetworkInstance_Protocol_Static_NextHop_NextHop_Union{ + "0": oc.UnionString(agg1.ateIPv4), + }, + } + sr6ATE1 := &cfgplugins.StaticRouteCfg{ + NetworkInstance: deviations.DefaultNetworkInstance(dut), + Prefix: agg1.ateLoopbackV6 + "/128", + NextHops: map[string]oc.NetworkInstance_Protocol_Static_NextHop_NextHop_Union{ + "0": oc.UnionString(agg1.ateIPv6), + }, + } + sr4ATE2 := &cfgplugins.StaticRouteCfg{ + NetworkInstance: deviations.DefaultNetworkInstance(dut), + Prefix: agg2.ateLoopbackV4 + "/32", + NextHops: map[string]oc.NetworkInstance_Protocol_Static_NextHop_NextHop_Union{ + "0": oc.UnionString(agg2.ateIPv4), + "1": oc.UnionString(agg3.ateIPv4), + "2": oc.UnionString(agg4.ateIPv4), + }, + } + sr6ATE2 := &cfgplugins.StaticRouteCfg{ + NetworkInstance: deviations.DefaultNetworkInstance(dut), + Prefix: agg2.ateLoopbackV6 + "/128", + NextHops: map[string]oc.NetworkInstance_Protocol_Static_NextHop_NextHop_Union{ + "0": oc.UnionString(agg2.ateIPv6), + "1": oc.UnionString(agg3.ateIPv6), + "2": oc.UnionString(agg4.ateIPv6), + }, + } + b := &gnmi.SetBatch{} + for _, cfg := range []*cfgplugins.StaticRouteCfg{sr4ATE1, sr6ATE1, sr4ATE2, sr6ATE2} { + if _, err := cfgplugins.NewStaticRouteCfg(b, cfg, dut); err != nil { + t.Fatalf("Failed to configure static route to ATE Loopback: %v", err) + } + } + b.Set(t, dut) +} + +func configureDUTISIS(t *testing.T, dut *ondatra.DUTDevice, aggIDs []string) { + t.Helper() + d := &oc.Root{} + netInstance := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + prot := netInstance.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance) + prot.Enabled = ygot.Bool(true) + isis := prot.GetOrCreateIsis() + + globalISIS := isis.GetOrCreateGlobal() + if deviations.ISISInstanceEnabledRequired(dut) { + globalISIS.Instance = ygot.String(isisInstance) + } + globalISIS.LevelCapability = oc.Isis_LevelType_LEVEL_2 + globalISIS.Net = []string{fmt.Sprintf("%v.%v.00", dutAreaAddress, dutSysID)} + globalISIS.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST).Enabled = ygot.Bool(true) + globalISIS.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV6, oc.IsisTypes_SAFI_TYPE_UNICAST).Enabled = ygot.Bool(true) + + lspBit := globalISIS.GetOrCreateLspBit().GetOrCreateOverloadBit() + lspBit.SetBit = ygot.Bool(false) + + isisLevel2 := isis.GetOrCreateLevel(2) + isisLevel2.MetricStyle = oc.Isis_MetricStyle_WIDE_METRIC + + for _, aggID := range aggIDs { + isisIntf := isis.GetOrCreateInterface(aggID) + isisIntf.GetOrCreateInterfaceRef().Interface = ygot.String(aggID) + isisIntf.GetOrCreateInterfaceRef().Subinterface = ygot.Uint32(0) + if deviations.InterfaceRefConfigUnsupported(dut) { + isisIntf.InterfaceRef = nil + } + isisIntf.Enabled = ygot.Bool(true) + isisIntf.CircuitType = oc.Isis_CircuitType_POINT_TO_POINT + isisIntf.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST).Enabled = ygot.Bool(true) + isisIntf.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV6, oc.IsisTypes_SAFI_TYPE_UNICAST).Enabled = ygot.Bool(true) + if deviations.ISISInterfaceAfiUnsupported(dut) { + isisIntf.Af = nil + } + + isisIntfLevel := isisIntf.GetOrCreateLevel(2) + isisIntfLevel.Enabled = ygot.Bool(true) + + isisIntfLevelAfiv4 := isisIntfLevel.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST) + isisIntfLevelAfiv4.Metric = ygot.Uint32(10) + isisIntfLevelAfiv4.Enabled = ygot.Bool(true) + isisIntfLevelAfiv6 := isisIntfLevel.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV6, oc.IsisTypes_SAFI_TYPE_UNICAST) + isisIntfLevelAfiv6.Metric = ygot.Uint32(10) + isisIntfLevelAfiv6.Enabled = ygot.Bool(true) + if deviations.MissingIsisInterfaceAfiSafiEnable(dut) { + isisIntfLevelAfiv4.Enabled = nil + isisIntfLevelAfiv6.Enabled = nil + } + } + gnmi.Update(t, dut, gnmi.OC().Config(), d) +} + +func configureDUTBGP(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + + d := &oc.Root{} + ni := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + niProto := ni.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + bgp := niProto.GetOrCreateBgp() + + global := bgp.GetOrCreateGlobal() + global.RouterId = ygot.String(dutLoopback.IPv4) + global.As = ygot.Uint32(asn) + global.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) + global.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + pgName := "BGP-PEER-GROUP1" + pg := bgp.GetOrCreatePeerGroup(pgName) + pg.PeerAs = ygot.Uint32(asn) + if deviations.RoutePolicyUnderAFIUnsupported(dut) { + rpl := pg.GetOrCreateApplyPolicy() + rpl.SetExportPolicy([]string{acceptRoutePolicy}) + rpl.SetImportPolicy([]string{acceptRoutePolicy}) + } else { + af4 := pg.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + af4.Enabled = ygot.Bool(true) + rpl := af4.GetOrCreateApplyPolicy() + rpl.SetExportPolicy([]string{acceptRoutePolicy}) + rpl.SetImportPolicy([]string{acceptRoutePolicy}) + + af6 := pg.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + af6.Enabled = ygot.Bool(true) + rpl = af6.GetOrCreateApplyPolicy() + rpl.SetExportPolicy([]string{acceptRoutePolicy}) + rpl.SetImportPolicy([]string{acceptRoutePolicy}) + } + + for _, a := range []*aggPortData{agg1, agg2, agg3, agg4} { + bgpNbrV4 := bgp.GetOrCreateNeighbor(a.ateLoopbackV4) + bgpNbrV4.PeerGroup = ygot.String(pgName) + bgpNbrV4.PeerAs = ygot.Uint32(asn) + bgpNbrV4.Enabled = ygot.Bool(true) + bgpNbrV4T := bgpNbrV4.GetOrCreateTransport() + bgpNbrV4T.LocalAddress = ygot.String(dutLoopback.IPv4) + af4 := bgpNbrV4.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + af4.Enabled = ygot.Bool(true) + af6 := bgpNbrV4.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + af6.Enabled = ygot.Bool(false) + + bgpNbrV6 := bgp.GetOrCreateNeighbor(a.ateLoopbackV6) + bgpNbrV6.PeerGroup = ygot.String(pgName) + bgpNbrV6.PeerAs = ygot.Uint32(asn) + bgpNbrV6.Enabled = ygot.Bool(true) + bgpNbrV6T := bgpNbrV6.GetOrCreateTransport() + bgpNbrV6T.LocalAddress = ygot.String(dutLoopback.IPv6) + af4 = bgpNbrV6.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + af4.Enabled = ygot.Bool(false) + af6 = bgpNbrV6.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + af6.Enabled = ygot.Bool(true) + } + + gnmi.Replace(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Config(), niProto) +} diff --git a/feature/isis/weighted_ECMP/README.md b/feature/isis/weighted_ECMP/README.md deleted file mode 100644 index db975a0e1fd..00000000000 --- a/feature/isis/weighted_ECMP/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# RT-2.13: Weighted-ECMP for IS-IS - -## Summary -This is to ensure that, -* Implementations can be configured for weighted equal cost multipath (ECMP) routing for IS-IS neighbors that are one hop away. -* When WECMP is enabled, traffic destined to an IS-IS route represented by a multipath set of next-hop interfaces will be unequally distributed across the interfaces based on their bandwidth. - -## Testbed type -[TESTBED_DUT_ATE_8LINKS](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_8.testbed) - -## Topolgy -Each LAG bundle below is made up of 2x10G ports. -```mermaid -graph LR; -A[ATE1:LAG1] <-- IBGP+IS-IS --> B[LAG1:DUT]; -C[DUT:LAG2] <-- IBGP+IS-IS --> D[LAG1:ATE2]; -E[DUT:LAG3] <-- IBGP+IS-IS --> F[LAG2:ATE2]; -G[DUT:LAG4] <-- IBGP+IS-IS --> H[LAG3:ATE2]; -``` - -## Procedure -In the topology above, -* Configure 1xLAG interface between ATE1<->DUT and 3xLAG interfaces between DUT and ATE2. Each LAG interface is expected to be of 2x10Gbps -* Configure IPv4 and IPv6 L2 adjacencies between DUT and ATE LAG bundles. Therefore, DUT will have 1xIS-IS adjacency with ATE1 i.e. DUT:LAG1<->ATE1:LAG1, and 3xIS-IS adjacencies with ATE2 i.e. DUT:LAG2<->ATE2:LAG1, DUT:LAG3<->ATE2:LAG2 and DUT:LAG4<->ATE2:LAG3 - * /network-instances/network-instance/protocols/protocol/isis/global/afi-safi - * /network-instances/network-instance/protocols/protocol/isis/global/config/level-capability, set to LEVEL_2 - * /network-instances/network-instance/protocols/protocol/isis/levels/level/config/metric-style set to WIDE_METRIC -* Configure IPv4 and IPv6 IBGP peering between both ATEs and the DUT using their loopback addresses for both IPv4 and IPv6 address families. - * /network-instances/network-instance/protocols/protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/config -* Attach a network with an IPv4 and an IPv6 prefix to ATE2 and have it advertise these prefixes over its IBGP peering with the DUT. The DUT in turn should advertise these prefixes over its IBGP peering with ATE1 - * Please use `IPv4 prefix = 100.0.1.0/24` and `IPv6 prefix = 2001:db8:64:64::/64` -* Similarly, attach a different network to ATE1 with IPv4 and IPv6 prefixes and advertise the same over its IBGP peering with the DUT. - * Please use `IPv4 prefix = 100.0.2.0/24` and `IPv6 prefix = 2001:db8:64:65::/64` -* On the DUT, enable WECMP loadbalancing for multipath IS-IS routes and set the load-balancing-weight to use LAG bandwidth. - * /network-instances/network-instance/protocols/protocol/isis/global/config/weighted-ecmp set to Enabled - * /network-instances/network-instance/protocols/protocol/isis/interfaces/interface/weighted-ecmp/config/load-balancing-weight set to Auto - -## RT-9.1: Equal distribution of traffic -* Start 1024 flows from IPv4 addresses in 100.0.2.0/24 to 100.0.1.0/24 -* Start 1024 flows from IPv6 addresses in 2001:db8:64:65::/64 to 2001:db8:64:64::/64 -* Ensure that the total traffic of all flows combined is ~20Gbps -### Verification -* Ensure that the DUT has learnt the routes for prefixes 100.0.1.0/24 and 2001:db8:64:64::/64 over IBGP. Following paths - * /network-instances/network-instance/afts/next-hops/next-hop/state/ip-address -* Ensure that the DUT has learnt routes to the IPv4 and IPv6 loopback addresses of ATE2. It is expected that these prefixes are reachable via 3 different Next-Hop addresses corresponding to the LAG1, LAG2 and LAG3 interfaces on ATE2. -* It is expected that the IS-IS instance in DUT will equally distribute the traffic received on DUT:LAG1 over the LAG bundles corresponding to ATE2:LAG1, ATE2:LAG2 and ATE2:LAG3 when the 3 LAG bundles have the same bandwidth available. - * Traffic distribution between DUT:LAG2, DUT:LAG3 and DUT:LAG4 is expected to be ~33% each of the total traffic received on DUT:LAG1. - * Check for the following paths - * /network-instances/network-instance/protocols/protocol/isis/global/state/weighted-ecmp, should be true - * /network-instances/network-instance/protocols/protocol/isis/interfaces/interface/weighted-ecmp/state/load-balancing-weight, should be auto - * /interfaces/interface/state/counters/out-pkts - * /interfaces/interface/state/counters/in-pkts - -## RT-9.2: Unequal distribution of traffic -* Stop traffic from RT-9.1 and introduce a failure by disabling one of the member interfaces in ATE2:LAG1. -* Restart 1024 flows from IPv4 addresses in 100.0.2.0/24 to 100.0.1.0/24 -* Restart 1024 flows from IPv6 addresses in 2001:db8:64:65::/64 to 2001:db8:64:64::/64 -* Ensure that the total traffic of all flows combined is ~20Gbps -### Verification -* It is expected that the IS-IS instance in DUT will unequally distribute the traffic received from ATE1:LAG1 over the LAG bundles corresponding to ATE2:LAG1, ATE2:LAG2 and ATE3:LAG3. - * Traffic on DUT:LAG2 is expected to be ~20% while traffic on DUT:LAG3 and DUT:LAG4 is expected to be ~40% each of the total traffic received on DUT:LAG1. If the traffic is not unequally shared between the DUT LAG bundles towards ATE2 then this test is a failure. - * Check for the following paths - * /interfaces/interface/state/counters/out-pkts - * /interfaces/interface/state/counters/in-pkts - -### Config paths -* /network-instances/network-instance/protocols/protocol/bgp/peer-groups/peer-group/config -* /network-instances/network-instance/protocols/protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/config - -* /network-instances/network-instance/protocols/protocol/isis/global/afi-safi -* /network-instances/network-instance/protocols/protocol/isis/global/config/level-capability -* /network-instances/network-instance/protocols/protocol/isis/levels/level/config/metric-style -* /network-instances/network-instance/protocols/protocol/isis/global/config/weighted-ecmp -* /network-instances/network-instance/protocols/protocol/isis/interfaces/interface/weighted-ecmp/config/load-balancing-weight - -* /routing-policy/defined-sets/prefix-sets/prefix-set/ -* /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/config/ip-prefix -* /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/config/masklength-range/exact - -* /routing-policy/policy-definitions/policy-definition/config/name -* /routing-policy/policy-definitions/policy-definition/statements/statement/config/name -* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/prefix-set -* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/match-set-options -* /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result/ACCEPT_ROUTE - -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/peer-group/afi-safis/afi-safi/apply-policy/config/import-policy -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/peer-group/afi-safis/afi-safi/apply-policy/config/export-policy - -### Telemetry Parameter Coverage -* /network-instances/network-instance/protocols/protocol/isis/global/state/weighted-ecmp -* /network-instances/network-instance/protocols/protocol/isis/interfaces/interface/weighted-ecmp/state/load-balancing-weight -* /interfaces/interface/state/counters/out-pkts -* /interfaces/interface/state/counters/in-pkts diff --git a/feature/lldp/feature.textproto b/feature/lldp/feature.textproto index 4016a95d96f..50ba028ca87 100644 --- a/feature/lldp/feature.textproto +++ b/feature/lldp/feature.textproto @@ -1,3 +1,6 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile + id { name: "lldp" version: 1 diff --git a/feature/localaggregates/feature.textproto b/feature/localaggregates/feature.textproto index 445dadfead2..dc5c76ea7fa 100644 --- a/feature/localaggregates/feature.textproto +++ b/feature/localaggregates/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "localaggregates" diff --git a/feature/networkinstance/feature.textproto b/feature/networkinstance/feature.textproto index 98cef284592..10e9b7da93c 100644 --- a/feature/networkinstance/feature.textproto +++ b/feature/networkinstance/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "networkinstance" diff --git a/feature/p4rt/otg_tests/p4rt_daemon_failure_test/metadata.textproto b/feature/p4rt/otg_tests/p4rt_daemon_failure_test/metadata.textproto index 53b71895f77..d9063936e7a 100644 --- a/feature/p4rt/otg_tests/p4rt_daemon_failure_test/metadata.textproto +++ b/feature/p4rt/otg_tests/p4rt_daemon_failure_test/metadata.textproto @@ -1,4 +1,4 @@ -# proto-file: third_party/openconfig/featureprofiles/proto/metadata.proto +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto # proto-message: Metadata uuid: "78f99d29-c7db-4e6d-8284-614154efc0d1" diff --git a/feature/p4rt/otg_tests/p4rt_daemon_failure_test/p4rt_daemon_failure_test.go b/feature/p4rt/otg_tests/p4rt_daemon_failure_test/p4rt_daemon_failure_test.go index e9dd420e736..239ea5ba306 100644 --- a/feature/p4rt/otg_tests/p4rt_daemon_failure_test/p4rt_daemon_failure_test.go +++ b/feature/p4rt/otg_tests/p4rt_daemon_failure_test/p4rt_daemon_failure_test.go @@ -85,7 +85,7 @@ var ( ondatra.ARISTA: "P4Runtime", ondatra.CISCO: "emsd", ondatra.JUNIPER: "p4-switch", - ondatra.NOKIA: "sr_p4rt_server", + ondatra.NOKIA: "sr_grpc_server", } ) @@ -359,7 +359,7 @@ func TestP4RTDaemonFailure(t *testing.T) { ate.OTG().StopTraffic(t) // Skip check for CISCO devices that use the same process for P4RT & gNMI. - if dut.Vendor() != ondatra.CISCO { + if dut.Vendor() != ondatra.CISCO && dut.Vendor() != ondatra.NOKIA { // Verify interfaceID did not change since the last time we read it. changedID, notOk := watchID.Await(t) if notOk { diff --git a/feature/platform/controllercard/feature.textproto b/feature/platform/controllercard/feature.textproto index c406648800d..d8b1bda06cb 100644 --- a/feature/platform/controllercard/feature.textproto +++ b/feature/platform/controllercard/feature.textproto @@ -1,3 +1,6 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile + id { name: "platform_controllercard" version: 1 diff --git a/feature/platform/fabric/feature.textproto b/feature/platform/fabric/feature.textproto index b0ec826b2db..b86f5634e1f 100644 --- a/feature/platform/fabric/feature.textproto +++ b/feature/platform/fabric/feature.textproto @@ -1,3 +1,6 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile + id { name: "platform_fabric" version: 1 diff --git a/feature/platform/fabric/otg_tests/sampled_backplane_capacity_counters_test/metadata.textproto b/feature/platform/fabric/otg_tests/sampled_backplane_capacity_counters_test/metadata.textproto index 52f4163134e..7c618627449 100644 --- a/feature/platform/fabric/otg_tests/sampled_backplane_capacity_counters_test/metadata.textproto +++ b/feature/platform/fabric/otg_tests/sampled_backplane_capacity_counters_test/metadata.textproto @@ -1,4 +1,4 @@ -# proto-file: third_party/openconfig/featureprofiles/proto/metadata.proto +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto # proto-message: Metadata uuid: "ef804c64-84dd-432d-ab38-630e9c82b42d" diff --git a/feature/platform/fabric/otg_tests/sampled_backplane_capacity_counters_test/sampled_backplane_capacity_counters_test.go b/feature/platform/fabric/otg_tests/sampled_backplane_capacity_counters_test/sampled_backplane_capacity_counters_test.go index 4c1c707c0c6..3e7f39995df 100644 --- a/feature/platform/fabric/otg_tests/sampled_backplane_capacity_counters_test/sampled_backplane_capacity_counters_test.go +++ b/feature/platform/fabric/otg_tests/sampled_backplane_capacity_counters_test/sampled_backplane_capacity_counters_test.go @@ -129,7 +129,8 @@ func TestOnChangeBackplaneCapacityCounters(t *testing.T) { gnmi.Replace(t, dut, gnmi.OC().Component(f).Fabric().PowerAdminState().Config(), oc.Platform_ComponentPowerType_POWER_DISABLED) gnmi.Await(t, dut, gnmi.OC().Component(f).Fabric().PowerAdminState().State(), time.Minute, oc.Platform_ComponentPowerType_POWER_DISABLED) } - + t.Logf("Waiting for 90s after power disable...") + time.Sleep(90 * time.Second) ts2, tocs2, apct2 := getBackplaneCapacityCounters(t, dut, ics) for _, f := range fabrics[:fc] { @@ -145,7 +146,8 @@ func TestOnChangeBackplaneCapacityCounters(t *testing.T) { t.Errorf("Component %s oper-status after POWER_ENABLED, got: %v, want: %v", f, oper, oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE) } } - + t.Logf("Waiting for 90s after power enable...") + time.Sleep(90 * time.Second) ts3, tocs3, apct3 := getBackplaneCapacityCounters(t, dut, ics) for _, ic := range ics { @@ -184,7 +186,7 @@ func TestOnChangeBackplaneCapacityCounters(t *testing.T) { switch { case !ok1 || !ok2 || !ok3: t.Errorf("BackplaneFacingCapacity AvailablePct not present: ok1 %t, ok2 %t, ok3 %t", ok1, ok2, ok3) - case v1 <= v2 || v1 != v3: + case v1 != 0 && (v1 <= v2 || v1 != v3): t.Errorf("BackplaneFacingCapacity AvailablePct are not valid: v1 %d, v2 %d, v3 %d", v1, v2, v3) } }) diff --git a/feature/platform/tests/telemetry_inventory_test/telemetry_inventory_test.go b/feature/platform/tests/telemetry_inventory_test/telemetry_inventory_test.go index 7cb2a4c6a2e..0e6ef43e82f 100644 --- a/feature/platform/tests/telemetry_inventory_test/telemetry_inventory_test.go +++ b/feature/platform/tests/telemetry_inventory_test/telemetry_inventory_test.go @@ -745,8 +745,9 @@ func ValidateComponentState(t *testing.T, dut *ondatra.DUTDevice, cards []*oc.Co t.Errorf("Component %s Parent: Chassis component NOT found in the hierarchy tree of component", cName) break } - parentType := gnmi.Get(t, dut, gnmi.OC().Component(parent).Type().State()) - if parentType == componentType["Chassis"] { + pLoookup := gnmi.Lookup(t, dut, gnmi.OC().Component(parent).Type().State()) + parentType, present := pLoookup.Val() + if present && parentType == componentType["Chassis"] { t.Logf("Component %s Parent: Found chassis component in the hierarchy tree of component", cName) break } diff --git a/feature/platform/transceiver/zr_esnr_and_cd_test/README.MD b/feature/platform/transceiver/tests/zr_cd_test/README.md similarity index 63% rename from feature/platform/transceiver/zr_esnr_and_cd_test/README.MD rename to feature/platform/transceiver/tests/zr_cd_test/README.md index cbf4a6e25d7..1a9f6e127ab 100644 --- a/feature/platform/transceiver/zr_esnr_and_cd_test/README.MD +++ b/feature/platform/transceiver/tests/zr_cd_test/README.md @@ -1,14 +1,11 @@ -# TRANSCEIVER-1: Telemetry: 400ZR Electrical Signal to Noise Ratio(eSNR) and Chromatic Dispersion(CD) telemetry values streaming +# TRANSCEIVER-1: Telemetry: 400ZR Chromatic Dispersion(CD) telemetry values streaming ## Summary -Validate 400ZR optics module reports accurate eSNR and CD telemetry values. - -eSNR is defined as the electrical Signal to Noise ratio at the decision -sampling point in dB +Validate 400ZR optics module reports accurate CD telemetry values. Chromatic Dispersion is frequency dependent change in signal phase velocity due -to fiber measured in ps/nm +to fiber measured in ps/nm ## Procedure @@ -25,10 +22,6 @@ to fiber measured in ps/nm * With the ZR link is established as explained above, verify that the following ZR transceiver telemetry paths exist and are streamed for both the ZR optics - * /terminal-device/logical-channels/channel/otn/state/esnr/instant - * /terminal-device/logical-channels/channel/otn/state/esnr/avg - * /terminal-device/logical-channels/channel/otn/state/esnr/min - * /terminal-device/logical-channels/channel/otn/state/esnr/max * /platform/components/component/optical-channel/state/chromatic-dispersion/instant * /platform/components/component/optical-channel/state/chromatic-dispersion/avg * /platform/components/component/optical-channel/state/chromatic-dispersion/min @@ -38,7 +31,7 @@ to fiber measured in ps/nm stream any invalid string values like "nil" or "-inf" until valid values are available for streaming. -* eSNR and CD streamed values must always be of type Decimal64. +* CD streamed values must always be of type Decimal64. When link interfaces are in down state 0 must be reported as a valid value. @@ -47,34 +40,30 @@ to fiber measured in ps/nm communicated. -* Verify that the optics eSNR and CD is updated after the interface flaps. +* Verify that the optics CD is updated after the interface flaps. * Enable a pair of ZR interfaces on the DUT as explained above. - * Verify the ZR optics eSNR and CD telemetry values are in the normal range. + * Verify the ZR optics CD telemetry values are in the normal range. * Disable or shut down the interface on the DUT. * Verify with interfaces in down state both optics are streaming Decimal64 0 - value for both eSNR and CD. + value for CD. * Re-enable the interfaces on the DUT. - * Verify the ZR optics eSNR and CD telemetry values are updated to the + * Verify the ZR optics CD telemetry values are updated to the value in the normal range again. - * Typical expected value range for eSNR is 13.5 to - 18 dB +/-0.1 dB. * Typical CD expected value range is 0 to 2400 ps/nm. -* Verify that the optics eSNR and CD is updated after a fiber cut. +* Verify that the optics CD is updated after a fiber cut. * Enable a pair of ZR interfaces on the DUT as explained above. - * Verify the ZR optics eSNR and CD telemetry values are in the normal + * Verify the ZR optics CD telemetry values are in the normal range. * Simulate a fiber cut using the optical switch that sits in-between the DUT ports. * Verify with link in down state due to fiber cut both optics are streaming - Decimal64 0 value for both eSNR and CD. + Decimal64 0 value for CD. * Re-enable the optical switch connection to clear the fiber cut fault. - * Verify the ZR optics eSNR and CD telemetry values are updated to the value in the normal + * Verify the ZR optics CD telemetry values are updated to the value in the normal range again. - * Typical expected value range for eSNR is 13.5 to - 18 dB +/-0.1 dB. * Typical CD expected value range is 0 to 2400 ps/nm. ## Config Parameter coverage @@ -83,10 +72,6 @@ to fiber measured in ps/nm ## Telemetry Parameter coverage -* /terminal-device/logical-channels/channel/otn/state/esnr/instant -* /terminal-device/logical-channels/channel/otn/state/esnr/avg -* /terminal-device/logical-channels/channel/otn/state/esnr/min -* /terminal-device/logical-channels/channel/otn/state/esnr/max * /platform/components/component/optical-channel/state/chromatic-dispersion/instant * /platform/components/component/optical-channel/state/chromatic-dispersion/avg * /platform/components/component/optical-channel/state/chromatic-dispersion/min diff --git a/feature/platform/transceiver/tests/zr_cd_test/metadata.textproto b/feature/platform/transceiver/tests/zr_cd_test/metadata.textproto new file mode 100644 index 00000000000..5810d2322b9 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_cd_test/metadata.textproto @@ -0,0 +1,16 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata +uuid: "6c7ec460-10be-4c71-81f5-888f76ef241b" +plan_id: "TRANSCEIVER-1" +description: "Telemetry: 400ZR Chromatic Dispersion(CD) telemetry values streaming" +testbed: TESTBED_DUT_400ZR +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + interface_enabled: true + default_network_instance: "default" + missing_port_to_optical_channel_component_mapping: true + } +} diff --git a/feature/platform/transceiver/tests/zr_cd_test/zr_cd_test.go b/feature/platform/transceiver/tests/zr_cd_test/zr_cd_test.go new file mode 100644 index 00000000000..235db2506f2 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_cd_test/zr_cd_test.go @@ -0,0 +1,180 @@ +package zr_cd_test + +import ( + "fmt" + "testing" + "time" + + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/samplestream" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ygot/ygot" +) + +const ( + dp16QAM = 1 + samplingInterval = 10 * time.Second + minCDValue = 0 + maxCDValue = 2400 + inActiveCDValue = 0.0 + timeout = 10 * time.Minute + flapInterval = 30 * time.Second +) + +type portState int + +const ( + disabled portState = iota + enabled +) + +var ( + frequencies = []uint64{191400000, 196100000} + targetOutputPowers = []float64{-6, -10} +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +func interfaceConfig(t *testing.T, dut1 *ondatra.DUTDevice, dp *ondatra.Port, frequency uint64, targetOutputPower float64) { + d := &oc.Root{} + i := d.GetOrCreateInterface(dp.Name()) + i.Enabled = ygot.Bool(true) + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + gnmi.Replace(t, dut1, gnmi.OC().Interface(dp.Name()).Config(), i) + OCcomponent := opticalChannelComponentFromPort(t, dut1, dp) + gnmi.Replace(t, dut1, gnmi.OC().Component(OCcomponent).OpticalChannel().Config(), &oc.Component_OpticalChannel{ + TargetOutputPower: ygot.Float64(targetOutputPower), + Frequency: ygot.Uint64(frequency), + }) +} + +func verifyCDValue(t *testing.T, dut1 *ondatra.DUTDevice, pStream *samplestream.SampleStream[float64], sensorName string, status portState) float64 { + CDSample := pStream.Next() + if CDSample == nil { + t.Fatalf("CD telemetry %s was not streamed in the most recent subscription interval", sensorName) + } + CDVal, ok := CDSample.Val() + if !ok { + t.Fatalf("CD %q telemetry is not present", CDSample) + } + // Check CD return value of correct type + switch { + case status == disabled: + if CDVal != inActiveCDValue { + t.Fatalf("The inactive CD is %v, expected %v", CDVal, inActiveCDValue) + } + case status == enabled: + if CDVal < minCDValue && CDVal > maxCDValue { + t.Fatalf("The variable CD is %v, expected range (%v, %v)", CDVal, minCDValue, maxCDValue) + } + default: + t.Fatalf("Invalid status %v", status) + } + t.Logf("Device %v CD %s value : %v", dut1.Name(), sensorName, CDVal) + return CDVal +} + +func verifyAllCDValues(t *testing.T, dut1 *ondatra.DUTDevice, p1StreamInstant, p1StreamMax, p1StreamMin, p1StreamAvg *samplestream.SampleStream[float64], status portState) { + CDInstant := verifyCDValue(t, dut1, p1StreamInstant, "Instant", status) + CDMax := verifyCDValue(t, dut1, p1StreamMax, "Max", status) + CDMin := verifyCDValue(t, dut1, p1StreamMin, "Min", status) + CDAvg := verifyCDValue(t, dut1, p1StreamAvg, "Avg", status) + + if CDAvg >= CDMin && CDAvg <= CDMax { + t.Logf("The average is between the maximum and minimum values, Avg:%v Max:%v Min:%v", CDAvg, CDMax, CDMin) + } else { + t.Fatalf("The average is NOT between the maximum and minimum values, Avg:%v Max:%v Min:%v", CDAvg, CDMax, CDMin) + } + + if CDInstant >= CDMin && CDInstant <= CDMax { + t.Logf("The instant is between the maximum and minimum values, Instant:%v Max:%v Min:%v", CDInstant, CDMax, CDMin) + } else { + t.Fatalf("The instant is NOT between the maximum and minimum values, Instant:%v Max:%v Min:%v", CDInstant, CDMax, CDMin) + } + +} + +func TestCDValue(t *testing.T) { + dut1 := ondatra.DUT(t, "dut") + dp1 := dut1.Port(t, "port1") + dp2 := dut1.Port(t, "port2") + fptest.ConfigureDefaultNetworkInstance(t, dut1) + + // Derive transceiver names from ports. + tr1 := gnmi.Get(t, dut1, gnmi.OC().Interface(dp1.Name()).Transceiver().State()) + tr2 := gnmi.Get(t, dut1, gnmi.OC().Interface(dp2.Name()).Transceiver().State()) + component1 := gnmi.OC().Component(tr1) + + for _, frequency := range frequencies { + for _, targetOutputPower := range targetOutputPowers { + interfaceConfig(t, dut1, dp1, frequency, targetOutputPower) + interfaceConfig(t, dut1, dp2, frequency, targetOutputPower) + // Wait for channels to be up. + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + gnmi.Await(t, dut1, gnmi.OC().Interface(dp2.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + + p1StreamInstant := samplestream.New(t, dut1, component1.OpticalChannel().ChromaticDispersion().Instant().State(), samplingInterval) + p1StreamAvg := samplestream.New(t, dut1, component1.OpticalChannel().ChromaticDispersion().Avg().State(), samplingInterval) + p1StreamMin := samplestream.New(t, dut1, component1.OpticalChannel().ChromaticDispersion().Min().State(), samplingInterval) + p1StreamMax := samplestream.New(t, dut1, component1.OpticalChannel().ChromaticDispersion().Max().State(), samplingInterval) + + verifyAllCDValues(t, dut1, p1StreamInstant, p1StreamMax, p1StreamMin, p1StreamAvg, enabled) + + // Disable or shut down the interface on the DUT. + gnmi.Replace(t, dut1, gnmi.OC().Interface(dp1.Name()).Enabled().Config(), false) + gnmi.Replace(t, dut1, gnmi.OC().Interface(dp2.Name()).Enabled().Config(), false) + // Wait for channels to be down. + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_DOWN) + gnmi.Await(t, dut1, gnmi.OC().Interface(dp2.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_DOWN) + + verifyAllCDValues(t, dut1, p1StreamInstant, p1StreamMax, p1StreamMin, p1StreamAvg, disabled) + time.Sleep(flapInterval) + + // Re-enable interfaces. + gnmi.Replace(t, dut1, gnmi.OC().Component(tr1).Transceiver().Enabled().Config(), true) + gnmi.Replace(t, dut1, gnmi.OC().Component(tr2).Transceiver().Enabled().Config(), true) + // Wait for channels to be up. + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + gnmi.Await(t, dut1, gnmi.OC().Interface(dp2.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + + verifyAllCDValues(t, dut1, p1StreamInstant, p1StreamMax, p1StreamMin, p1StreamAvg, enabled) + + p1StreamMin.Close() + p1StreamMax.Close() + p1StreamAvg.Close() + p1StreamInstant.Close() + } + } +} + +func opticalChannelComponentFromPort(t *testing.T, dut *ondatra.DUTDevice, p *ondatra.Port) string { + t.Helper() + if deviations.MissingPortToOpticalChannelMapping(dut) { + switch dut.Vendor() { + case ondatra.ARISTA: + transceiverName := gnmi.Get(t, dut, gnmi.OC().Interface(p.Name()).Transceiver().State()) + return fmt.Sprintf("%s-Optical0", transceiverName) + default: + t.Fatal("Manual Optical channel name required when deviation missing_port_to_optical_channel_component_mapping applied.") + } + } + compName := gnmi.Get(t, dut, gnmi.OC().Interface(p.Name()).HardwarePort().State()) + for { + comp, ok := gnmi.Lookup(t, dut, gnmi.OC().Component(compName).State()).Val() + if !ok { + t.Fatalf("Recursive optical channel lookup failed for port: %s, component %s not found.", p.Name(), compName) + } + if comp.GetType() == oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_OPTICAL_CHANNEL { + return compName + } + if comp.GetParent() == "" { + t.Fatalf("Recursive optical channel lookup failed for port: %s, parent of component %s not found.", p.Name(), compName) + } + compName = comp.GetParent() + } +} diff --git a/feature/platform/transceiver/zr_fec_uncorrectable_frames_test/README.md b/feature/platform/transceiver/tests/zr_fec_uncorrectable_frames_test/README.md similarity index 100% rename from feature/platform/transceiver/zr_fec_uncorrectable_frames_test/README.md rename to feature/platform/transceiver/tests/zr_fec_uncorrectable_frames_test/README.md diff --git a/feature/platform/transceiver/tests/zr_fec_uncorrectable_frames_test/metadata.textproto b/feature/platform/transceiver/tests/zr_fec_uncorrectable_frames_test/metadata.textproto new file mode 100644 index 00000000000..5c9c9aea535 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_fec_uncorrectable_frames_test/metadata.textproto @@ -0,0 +1,7 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "6f9059c8-e49c-4ff6-8bd2-f474cd4fcfce" +plan_id: "TRANSCEIVER-10" +description: "Telemetry: 400ZR Optics FEC(Forward Error Correction) Uncorrectable Frames Streaming." +testbed: TESTBED_DUT_400ZR diff --git a/feature/platform/transceiver/tests/zr_fec_uncorrectable_frames_test/zr_fec_uncorrectable_frames_test.go b/feature/platform/transceiver/tests/zr_fec_uncorrectable_frames_test/zr_fec_uncorrectable_frames_test.go new file mode 100644 index 00000000000..2779d56a4d6 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_fec_uncorrectable_frames_test/zr_fec_uncorrectable_frames_test.go @@ -0,0 +1,91 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package zr_fec_uncorrectable_frames_test + +import ( + "fmt" + "reflect" + "testing" + "time" + + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/samplestream" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" +) + +const ( + sampleInterval = 10 * time.Second + targetOutputPowerdBm = -10 + targetFrequencyHz = 193100000 + intUpdateTime = 2 * time.Minute +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +func validateFecUncorrectableBlocks(t *testing.T, stream *samplestream.SampleStream[uint64]) { + fecStream := stream.Next() + if fecStream == nil { + t.Fatalf("Fec Uncorrectable Blocks was not streamed in the most recent subscription interval") + } + fec, ok := fecStream.Val() + if !ok { + t.Fatalf("Error capturing streaming Fec value") + } + if reflect.TypeOf(fec).Kind() != reflect.Int64 { + t.Fatalf("fec value is not type int64") + } + if fec != 0 { + t.Fatalf("Got FecUncorrectableBlocks got %d, want 0", fec) + } +} + +func TestZrUncorrectableFrames(t *testing.T) { + dut := ondatra.DUT(t, "dut") + + for _, port := range []string{"port1", "port2"} { + t.Run(fmt.Sprintf("Port:%s", port), func(t *testing.T) { + dp := dut.Port(t, "port1") + gnmi.Await(t, dut, gnmi.OC().Interface(dp.Name()).OperStatus().State(), intUpdateTime, oc.Interface_OperStatus_UP) + + // Derive transceiver names from ports. + tr := gnmi.Get(t, dut, gnmi.OC().Interface(dp.Name()).Transceiver().State()) + component := gnmi.OC().Component(tr) + + outputPower := gnmi.Get(t, dut, component.OpticalChannel().TargetOutputPower().State()) + if outputPower != targetOutputPowerdBm { + t.Fatalf("Output power does not match target output power, got: %v want :%v", outputPower, targetOutputPowerdBm) + } + + frequency := gnmi.Get(t, dut, component.OpticalChannel().Frequency().State()) + if frequency != targetFrequencyHz { + t.Fatalf("Frequency does not match target frequency, got: %v want :%v", frequency, targetFrequencyHz) + } + + streamFec := samplestream.New(t, dut, gnmi.OC().TerminalDevice().Channel(0).Otn().FecUncorrectableBlocks().State(), sampleInterval) + defer streamFec.Close() + validateFecUncorrectableBlocks(t, streamFec) + + // Toggle interface enabled + gnmi.Update(t, dut, gnmi.OC().Interface(dp.Name()).Enabled().Config(), bool(false)) + gnmi.Update(t, dut, gnmi.OC().Interface(dp.Name()).Enabled().Config(), bool(true)) + + validateFecUncorrectableBlocks(t, streamFec) + }) + } +} diff --git a/feature/platform/transceiver/zr_firmware_version_test/README.md b/feature/platform/transceiver/tests/zr_firmware_version_test/README.md similarity index 100% rename from feature/platform/transceiver/zr_firmware_version_test/README.md rename to feature/platform/transceiver/tests/zr_firmware_version_test/README.md diff --git a/feature/platform/transceiver/tests/zr_firmware_version_test/metadata.textproto b/feature/platform/transceiver/tests/zr_firmware_version_test/metadata.textproto new file mode 100644 index 00000000000..0e66e1c9c2e --- /dev/null +++ b/feature/platform/transceiver/tests/zr_firmware_version_test/metadata.textproto @@ -0,0 +1,17 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "0f3bb528-28f4-4284-8d2f-de105be09241" +plan_id: "TRANSCEIVER-3" +description: "Telemetry: 400ZR Optics firmware version streaming" +testbed: TESTBED_DUT_400ZR +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + interface_enabled: true + default_network_instance: "default" + missing_port_to_optical_channel_component_mapping: true + } +} \ No newline at end of file diff --git a/feature/platform/transceiver/tests/zr_firmware_version_test/zr_firmware_version_test.go b/feature/platform/transceiver/tests/zr_firmware_version_test/zr_firmware_version_test.go new file mode 100644 index 00000000000..d89ac708fe8 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_firmware_version_test/zr_firmware_version_test.go @@ -0,0 +1,148 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package zr_firmware_version_test + +import ( + "fmt" + "reflect" + "testing" + "time" + + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/samplestream" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ygot/ygot" +) + +const ( + dp16QAM = 1 + targetOutputPower = -10 + frequency = 193100000 +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +// Topology: dut:port1 <--> port2:dut + +func configInterface(t *testing.T, dut1 *ondatra.DUTDevice, dp *ondatra.Port, enable bool) { + d := &oc.Root{} + i := d.GetOrCreateInterface(dp.Name()) + i.Enabled = ygot.Bool(enable) + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + gnmi.Replace(t, dut1, gnmi.OC().Interface(dp.Name()).Config(), i) + component := opticalChannelComponentFromPort(t, dut1, dp) + gnmi.Replace(t, dut1, gnmi.OC().Component(component).OpticalChannel().Config(), &oc.Component_OpticalChannel{ + TargetOutputPower: ygot.Float64(targetOutputPower), + Frequency: ygot.Uint64(frequency), + }) +} +func verifyFirmwareVersionValue(t *testing.T, dut1 *ondatra.DUTDevice, pStream *samplestream.SampleStream[string]) { + firmwareVersionSample := pStream.Next() + if firmwareVersionSample == nil { + t.Fatalf("Firmware telemetry %v was not streamed in the most recent subscription interval", firmwareVersionSample) + } + firmwareVersionVal, ok := firmwareVersionSample.Val() + if !ok { + t.Fatalf("Firmware version %q telemetry is not present", firmwareVersionSample) + } + // Check firmware version return value of correct type + if reflect.TypeOf(firmwareVersionVal).Kind() != reflect.String { + t.Fatalf("Return value is not type string") + } +} + +func TestZRFirmwareVersionState(t *testing.T) { + dut1 := ondatra.DUT(t, "dut") + dp1 := dut1.Port(t, "port1") + dp2 := dut1.Port(t, "port2") + t.Logf("dut1: %v", dut1) + t.Logf("dut1 dp1 name: %v", dp1.Name()) + configInterface(t, dut1, dp1, true) + configInterface(t, dut1, dp2, true) + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), time.Minute*2, oc.Interface_OperStatus_UP) + transceiverName := gnmi.Get(t, dut1, gnmi.OC().Interface(dp1.Name()).Transceiver().State()) + // Check if TRANSCEIVER is of type 400ZR + if dp1.PMD() != ondatra.PMD400GBASEZR { + t.Fatalf("%s Transceiver is not 400ZR its of type: %v", transceiverName, dp1.PMD()) + } + component1 := gnmi.OC().Component(transceiverName) + + p1Stream := samplestream.New(t, dut1, component1.FirmwareVersion().State(), 10*time.Second) + + verifyFirmwareVersionValue(t, dut1, p1Stream) + + p1Stream.Close() +} +func TestZRFirmwareVersionStateInterfaceFlap(t *testing.T) { + dut1 := ondatra.DUT(t, "dut") + dp1 := dut1.Port(t, "port1") + dp2 := dut1.Port(t, "port2") + t.Logf("dut1: %v", dut1) + t.Logf("dut1 dp1 name: %v", dp1.Name()) + configInterface(t, dut1, dp1, true) + configInterface(t, dut1, dp2, true) + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), time.Minute, oc.Interface_OperStatus_UP) + transceiverName := gnmi.Get(t, dut1, gnmi.OC().Interface(dp1.Name()).Transceiver().State()) + // Check if TRANSCEIVER is of type 400ZR + if dp1.PMD() != ondatra.PMD400GBASEZR { + t.Fatalf("%s Transceiver is not 400ZR its of type: %v", transceiverName, dp1.PMD()) + } + // Disable interface + configInterface(t, dut1, dp1, false) + component1 := gnmi.OC().Component(transceiverName) + + p1Stream := samplestream.New(t, dut1, component1.FirmwareVersion().State(), 10*time.Second) + + // Wait 60 sec cooling off period + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), time.Minute, oc.Interface_OperStatus_DOWN) + verifyFirmwareVersionValue(t, dut1, p1Stream) + + // Enable interface + configInterface(t, dut1, dp1, true) + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), time.Minute, oc.Interface_OperStatus_UP) + verifyFirmwareVersionValue(t, dut1, p1Stream) +} + +func opticalChannelComponentFromPort(t *testing.T, dut *ondatra.DUTDevice, p *ondatra.Port) string { + t.Helper() + if deviations.MissingPortToOpticalChannelMapping(dut) { + switch dut.Vendor() { + case ondatra.ARISTA: + transceiverName := gnmi.Get(t, dut, gnmi.OC().Interface(p.Name()).Transceiver().State()) + return fmt.Sprintf("%s-Optical0", transceiverName) + default: + t.Fatal("Manual Optical channel name required when deviation missing_port_to_optical_channel_component_mapping applied.") + } + } + compName := gnmi.Get(t, dut, gnmi.OC().Interface(p.Name()).HardwarePort().State()) + for { + comp, ok := gnmi.Lookup(t, dut, gnmi.OC().Component(compName).State()).Val() + if !ok { + t.Fatalf("Recursive optical channel lookup failed for port: %s, component %s not found.", p.Name(), compName) + } + if comp.GetType() == oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_OPTICAL_CHANNEL { + return compName + } + if comp.GetParent() == "" { + t.Fatalf("Recursive optical channel lookup failed for port: %s, parent of component %s not found.", p.Name(), compName) + } + compName = comp.GetParent() + } +} diff --git a/feature/platform/transceiver/zr_input_output_power_test/README.MD b/feature/platform/transceiver/tests/zr_input_output_power_test/README.md similarity index 95% rename from feature/platform/transceiver/zr_input_output_power_test/README.MD rename to feature/platform/transceiver/tests/zr_input_output_power_test/README.md index 5cd87b500b5..0a6fbcdf506 100644 --- a/feature/platform/transceiver/zr_input_output_power_test/README.MD +++ b/feature/platform/transceiver/tests/zr_input_output_power_test/README.md @@ -53,8 +53,8 @@ power. are available for streaming. * RX Input and TX output power values must always be of type decimal64. - When link interfaces are in down state 0 must be reported as a valid - value. + When link interfaces are in down state RX Input power of -40 dbm must be + reported as a valid value. **Note:** For min, max, and avg values, 10 second sampling is preferred. If 10 seconds is not supported, the sampling interval used must be @@ -76,7 +76,7 @@ power. * Verify the ZR optics RX input and TX output power telemetry values are updated to the value in the normal range again. * Typical min/max value range for RX Signal Power -14 to 0 dbm. - * Typical min/max value range for TX Output Power -13 to -9 dbm. + * Typical min/max value range for TX Output Power -10 to -6 dbm. ## TRANSCEIVER-4.4 @@ -95,7 +95,7 @@ power. * Verify the ZR optics RX input and TX output power telemetry values are updated to the value in the normal range again. * Typical min/max value range for RX Signal Power -14 to 0 dbm. - * Typical min/max value range for TX Output Power -13 to -9 dbm. + * Typical min/max value range for TX Output Power -10 to -6 dbm. ## Config Parameter coverage diff --git a/feature/platform/transceiver/tests/zr_input_output_power_test/metadata.textproto b/feature/platform/transceiver/tests/zr_input_output_power_test/metadata.textproto new file mode 100644 index 00000000000..cfabe297f68 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_input_output_power_test/metadata.textproto @@ -0,0 +1,7 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "67be4256-6965-4a6d-bc68-322c878cbc73" +plan_id: "TRANSCEIVER-4" +description: "Telemetry: 400ZR RX input and TX output power telemetry values streaming." +testbed: TESTBED_DUT_ATE_2LINKS diff --git a/feature/platform/transceiver/tests/zr_input_output_power_test/zr_input_output_power_test.go b/feature/platform/transceiver/tests/zr_input_output_power_test/zr_input_output_power_test.go new file mode 100644 index 00000000000..0ed4461fcee --- /dev/null +++ b/feature/platform/transceiver/tests/zr_input_output_power_test/zr_input_output_power_test.go @@ -0,0 +1,413 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package zr_input_output_power_test + +import ( + "context" + "reflect" + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/samplestream" + gnps "github.com/openconfig/gnoi/system" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/testt" + "github.com/openconfig/ygot/ygot" +) + +const ( + opticalChannelTransceiverType = oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_OPTICAL_CHANNEL + maxRebootTime = 900 + maxCompWaitTime = 600 + samplingTime = 10000000000 // 10 Seconds. + targetOutputPower = -9 + interfaceFlapTimeOut = 30 // Seconds. + outputFreqLowerBound = 184500000 + outputFreqUpperBound = 196000000 + rxSignalPowerLowerBound = -14 + rxSignalPowerUpperBound = 0 + txOutputPowerLowerBound = -10 + txOutputPowerUpperBound = -6 +) + +type testData struct { + transceiverName string + dut *ondatra.DUTDevice + transceiverOpticalChannelName string + interfaceName string +} + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +// Removes any breakout configuration if present, and configures the DUT. +func configureDUT(t *testing.T, dut *ondatra.DUTDevice, transceiverName string) { + port := dut.Port(t, "port1") + // Remove any breakout configuration. + hardwareComponentName := gnmi.Get(t, dut, gnmi.OC().Interface(port.Name()).HardwarePort().State()) + gnmi.Delete(t, dut, gnmi.OC().Component(hardwareComponentName).Port().BreakoutMode().Config()) + + i1 := &oc.Interface{Name: ygot.String(port.Name())} + i1.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + if deviations.ExplicitPortSpeed(dut) { + i1.GetOrCreateEthernet().PortSpeed = fptest.GetIfSpeed(t, port) + } + + gnmi.Replace(t, dut, gnmi.OC().Interface(port.Name()).Config(), i1) + gnmi.Replace(t, dut, gnmi.OC().Component(transceiverName).Transceiver().Enabled().Config(), *ygot.Bool(true)) + t.Logf("Configured port %v", port.Name()) +} + +// Verifies valid Rx Signal power. +func verifyValidRxInputPower(t testing.TB, transceiverOpticalChanelInputPower *oc.Component_OpticalChannel_InputPower, isInterfaceDisabled bool, transceiverOpticalChannelName string, transceiverName string) { + t.Logf("Checking Transceiver = %v Optical Channel Input Power Statistics", transceiverOpticalChannelName) + t.Logf("Optical channel = %v , Instant Input Power = %v", transceiverOpticalChannelName, transceiverOpticalChanelInputPower.GetInstant()) + t.Logf("Optical channel = %v , Average Input Power = %v", transceiverOpticalChannelName, transceiverOpticalChanelInputPower.GetAvg()) + t.Logf("Optical channel = %v , Min Input Power = %v", transceiverOpticalChannelName, transceiverOpticalChanelInputPower.GetMin()) + t.Logf("Optical channel = %v , Max Input Power = %v", transceiverOpticalChannelName, transceiverOpticalChanelInputPower.GetMax()) + + if transceiverInputPowerInstantType := reflect.TypeOf(transceiverOpticalChanelInputPower.GetInstant()).Kind(); transceiverInputPowerInstantType != reflect.Float64 { + t.Fatalf("[Error]: Expected Optical Channel %v Instant InputPower data type = %v , Got =%v", transceiverOpticalChannelName, reflect.Float64, transceiverInputPowerInstantType) + } + if transceiverInputPowerAvgType := reflect.TypeOf(transceiverOpticalChanelInputPower.GetAvg()).Kind(); transceiverInputPowerAvgType != reflect.Float64 { + t.Fatalf("[Error]: Expected Optical Channel %v Avg InputPower data type = %v , Got =%v", transceiverOpticalChannelName, reflect.Float64, transceiverInputPowerAvgType) + } + if transceiverInputPowerMinType := reflect.TypeOf(transceiverOpticalChanelInputPower.GetMin()).Kind(); transceiverInputPowerMinType != reflect.Float64 { + t.Fatalf("[Error]: Expected Optical Channel %v Min InputPower data type = %v , Got =%v", transceiverOpticalChannelName, reflect.Float64, transceiverInputPowerMinType) + } + if transceiverInputPowerMaxType := reflect.TypeOf(transceiverOpticalChanelInputPower.GetMax()).Kind(); transceiverInputPowerMaxType != reflect.Float64 { + t.Fatalf("[Error]: Expected Optical Channel %v Max InputPower data type = %v , Got =%v", transceiverOpticalChannelName, reflect.Float64, transceiverInputPowerMaxType) + } + + if isInterfaceDisabled { + if transceiverOpticalChanelInputPower.GetInstant() > 0 { + t.Fatalf("[Error]: Expected Optical Channel %v Instant InputPower = %v ,Got =%v", transceiverOpticalChannelName, 0, transceiverOpticalChanelInputPower.GetInstant()) + } + if transceiverOpticalChanelInputPower.GetAvg() > 0 { + t.Fatalf("[Error]: Expected Optical Channel %v Avg InputPower = %v ,Got =%v", transceiverOpticalChannelName, 0, transceiverOpticalChanelInputPower.GetAvg()) + } + if transceiverOpticalChanelInputPower.GetMin() > 0 { + t.Fatalf("[Error]: Expected Optical Channel %v Min InputPower = %v ,Got =%v", transceiverOpticalChannelName, 0, transceiverOpticalChanelInputPower.GetMin()) + } + if transceiverOpticalChanelInputPower.GetMax() > 0 { + t.Fatalf("[Error]: Expected Optical Channel %v Max InputPower = %v ,Got =%v", transceiverOpticalChannelName, 0, transceiverOpticalChanelInputPower.GetMax()) + } + + } else if transceiverOpticalChanelInputPower.GetMin() < rxSignalPowerLowerBound || transceiverOpticalChanelInputPower.GetMax() > rxSignalPowerUpperBound { + t.Fatalf("Transciever %v RX Input power range Expected = %v to %v dbm, Got = %v to %v ", transceiverName, rxSignalPowerLowerBound, rxSignalPowerUpperBound, transceiverOpticalChanelInputPower.GetMin(), transceiverOpticalChanelInputPower.GetMax()) + } else if transceiverOpticalChanelInputPower.GetMin() > transceiverOpticalChanelInputPower.GetAvg() || transceiverOpticalChanelInputPower.GetAvg() > transceiverOpticalChanelInputPower.GetMax() || transceiverOpticalChanelInputPower.GetMin() > transceiverOpticalChanelInputPower.GetInstant() || transceiverOpticalChanelInputPower.GetInstant() > transceiverOpticalChanelInputPower.GetMax() { + t.Fatalf("Transciever %v RX Input power not following min <= avg/instant <= max. Got instant = %v ,min= %v , avg= %v , max =%v ", transceiverName, transceiverOpticalChanelInputPower.GetInstant(), transceiverOpticalChanelInputPower.GetMin(), transceiverOpticalChanelInputPower.GetAvg(), transceiverOpticalChanelInputPower.GetMax()) + } +} + +// Verifies valid Tx Output Power. +func verifyValidTxOutputPower(t testing.TB, transceiverOpticalChanelOutputPower *oc.Component_OpticalChannel_OutputPower, isInterfaceDisabled bool, transceiverOpticalChannelName string, transceiverName string) { + t.Logf("Checking Transceiver = %v Optical Channel Output Power Statistics", transceiverOpticalChannelName) + t.Logf("Optical channel = %v , Instant Output Power = %v", transceiverOpticalChannelName, transceiverOpticalChanelOutputPower.GetInstant()) + t.Logf("Optical channel = %v , Average Output Power = %v", transceiverOpticalChannelName, transceiverOpticalChanelOutputPower.GetAvg()) + t.Logf("Optical channel = %v , Min Output Power = %v", transceiverOpticalChannelName, transceiverOpticalChanelOutputPower.GetMin()) + t.Logf("Optical channel = %v , Max Output Power = %v", transceiverOpticalChannelName, transceiverOpticalChanelOutputPower.GetMax()) + if transceiverOutputPowerInstantType := reflect.TypeOf(transceiverOpticalChanelOutputPower.GetInstant()).Kind(); transceiverOutputPowerInstantType != reflect.Float64 { + t.Fatalf("[Error]: Expected Optical Channel %v Instant OutputPower data type = %v , Got =%v", transceiverOpticalChannelName, reflect.Float64, transceiverOutputPowerInstantType) + } + if transceiverOutputPowerAvgType := reflect.TypeOf(transceiverOpticalChanelOutputPower.GetAvg()).Kind(); transceiverOutputPowerAvgType != reflect.Float64 { + t.Fatalf("[Error]: Expected Optical Channel %v Avg OutputPower data type = %v , Got =%v", transceiverOpticalChannelName, reflect.Float64, transceiverOutputPowerAvgType) + } + if transceiverOutputPowerMinType := reflect.TypeOf(transceiverOpticalChanelOutputPower.GetMin()).Kind(); transceiverOutputPowerMinType != reflect.Float64 { + t.Fatalf("[Error]: Expected Optical Channel %v Min OutputPower data type = %v , Got =%v", transceiverOpticalChannelName, reflect.Float64, transceiverOutputPowerMinType) + } + if transceiverOutputPowerMaxType := reflect.TypeOf(transceiverOpticalChanelOutputPower.GetMax()).Kind(); transceiverOutputPowerMaxType != reflect.Float64 { + t.Fatalf("[Error]: Expected Optical Channel %v Max OutputPower data type = %v , Got =%v", transceiverOpticalChannelName, reflect.Float64, transceiverOutputPowerMaxType) + } + + if isInterfaceDisabled { + + if transceiverOpticalChanelOutputPower.GetInstant() != -40 { + t.Fatalf("[Error]: Expected Optical Channel %v Instant OutputPower = %v ,Got =%v", transceiverOpticalChannelName, -40, transceiverOpticalChanelOutputPower.GetInstant()) + } + if transceiverOpticalChanelOutputPower.GetAvg() != -40 { + t.Fatalf("[Error]: Expected Optical Channel %v Avg OutputPower = %v ,Got =%v", transceiverOpticalChannelName, -40, transceiverOpticalChanelOutputPower.GetAvg()) + } + if transceiverOpticalChanelOutputPower.GetMin() != -40 { + t.Fatalf("[Error]: Expected Optical Channel %v Min OutputPower = %v ,Got =%v", transceiverOpticalChannelName, -40, transceiverOpticalChanelOutputPower.GetMin()) + } + if transceiverOpticalChanelOutputPower.GetMax() != -40 { + t.Fatalf("[Error]: Expected Optical Channel %v Max OutputPower = %v ,Got =%v", transceiverOpticalChannelName, -40, transceiverOpticalChanelOutputPower.GetMax()) + } + + } else if transceiverOpticalChanelOutputPower.GetMin() < txOutputPowerLowerBound || transceiverOpticalChanelOutputPower.GetMax() > txOutputPowerUpperBound { + t.Fatalf("[Error]:Transciever %v TX Output power range Expected = %v to %v dbm, Got = %v to %v ", transceiverName, txOutputPowerLowerBound, txOutputPowerUpperBound, transceiverOpticalChanelOutputPower.GetMin(), transceiverOpticalChanelOutputPower.GetMax()) + } else if transceiverOpticalChanelOutputPower.GetMin() > transceiverOpticalChanelOutputPower.GetAvg() || transceiverOpticalChanelOutputPower.GetAvg() > transceiverOpticalChanelOutputPower.GetMax() || transceiverOpticalChanelOutputPower.GetMin() > transceiverOpticalChanelOutputPower.GetInstant() || transceiverOpticalChanelOutputPower.GetInstant() > transceiverOpticalChanelOutputPower.GetMax() { + t.Fatalf("Transciever %v TX Output power not following min <= avg/instant <= max . Got instant = %v ,min= %v , avg= %v , max =%v ", transceiverName, transceiverOpticalChanelOutputPower.GetInstant(), transceiverOpticalChanelOutputPower.GetMin(), transceiverOpticalChanelOutputPower.GetAvg(), transceiverOpticalChanelOutputPower.GetMax()) + } +} + +// Verifies whether inputPower , outputPower ,Frequency are as expected. +func verifyInputOutputPower(t *testing.T, tc *testData) { + transceiverComponentPath := gnmi.OC().Component(tc.transceiverName) + transceiverOpticalChannelPath := gnmi.OC().Component(tc.transceiverOpticalChannelName) + t.Logf("Checking if transceiver = %v is Enabled", tc.transceiverName) + + isTransceiverEnabled := gnmi.Get(t, tc.dut, transceiverComponentPath.Transceiver().Enabled().State()) + if isTransceiverEnabled != true { + t.Errorf("[Error]:Tranciever %v is not enabled ", tc.transceiverName) + } + t.Logf("Transceiver = %v is in Enabled state", tc.transceiverName) + + t.Logf("Checking Transceiver = %v Output Frequency ", tc.transceiverName) + outputFrequency := gnmi.Get(t, tc.dut, transceiverComponentPath.Transceiver().Channel(0).OutputFrequency().State()) + t.Logf("Transceiver = %v Output Frequency = %v ", tc.transceiverName, outputFrequency) + if reflect.TypeOf(outputFrequency).Kind() != reflect.Uint64 { + t.Errorf("[Error]: Expected output frequency data type =%v Got = %v", reflect.Uint64, reflect.TypeOf(outputFrequency)) + } + + if outputFrequency > outputFreqUpperBound || outputFrequency < outputFreqLowerBound { + t.Errorf("[Error]: Output Frequency is not a valid frequency %v =%v, Got = %v", outputFreqLowerBound, outputFreqUpperBound, outputFrequency) + } + t.Logf("Transceiver = %v Output Frequency is in the expected range", tc.transceiverName) + + opticalInputPowerStream := samplestream.New(t, tc.dut, transceiverOpticalChannelPath.OpticalChannel().InputPower().State(), 10*time.Second) + defer opticalInputPowerStream.Close() + transceiverOpticalOutputPowerStream := samplestream.New(t, tc.dut, transceiverOpticalChannelPath.OpticalChannel().OutputPower().State(), 10*time.Second) + defer transceiverOpticalOutputPowerStream.Close() + + transceiverOpticalChanelOutputPower, ok := transceiverOpticalOutputPowerStream.Next().Val() + if !ok { + t.Errorf("[Error]:Trasceiver = %v Output Power not received !", tc.transceiverOpticalChannelName) + } + + transceiverOpticalChanelInputPower, ok := opticalInputPowerStream.Next().Val() + if !ok { + t.Errorf("[Error]:Trasceiver = %v Power not received !", tc.transceiverOpticalChannelName) + } + + verifyValidTxOutputPower(t, transceiverOpticalChanelOutputPower, false, tc.transceiverOpticalChannelName, tc.transceiverName) + verifyValidRxInputPower(t, transceiverOpticalChanelInputPower, false, tc.transceiverOpticalChannelName, tc.transceiverName) + + rxTotalInputPowerSamplingTime := gnmi.Get(t, tc.dut, transceiverComponentPath.Transceiver().Channel(0).InputPower().Interval().State()) + rxTotalInputPowerStream := samplestream.New(t, tc.dut, transceiverComponentPath.Transceiver().Channel(0).InputPower().State(), time.Nanosecond*time.Duration(rxTotalInputPowerSamplingTime)) + defer rxTotalInputPowerStream.Close() + nexInputPower := rxTotalInputPowerStream.Next() + transceiverRxTotalInputPower, got := nexInputPower.Val() + + if transceiverRxTotalInputPower == nil || got == false { + t.Errorf("[Error]:Didn't recieve Rx total InputPower sample in 10 seconds for the transceiever = %v", tc.transceiverName) + } + + t.Logf("Transceiver = %v RX Total Instant Input Power = %v", tc.transceiverName, transceiverRxTotalInputPower.GetInstant()) + t.Logf("Transceiver = %v RX Total Average Input Power = %v", tc.transceiverName, transceiverRxTotalInputPower.GetAvg()) + t.Logf("Transceiver = %v RX Total Min Input Power = %v", tc.transceiverName, transceiverRxTotalInputPower.GetMin()) + t.Logf("Transceiver = %v RX Total channel Max Input Power = %v", tc.transceiverName, transceiverRxTotalInputPower.GetMax()) + + if transceiverOpticalChanelInputPower == nil || got == false { + t.Errorf("[Error]:Didn't recieve Optical channel InputPower sample in 10 seconds for the transceiever = %v", tc.transceiverName) + } + if transceiverOpticalChanelInputPower.GetMax() > transceiverRxTotalInputPower.GetMax() { + t.Errorf("[Error]:Transciever %v RX Signal Power = %v is more than Total RX Signal Power = %v ", tc.transceiverName, transceiverOpticalChanelInputPower.GetMax(), transceiverRxTotalInputPower.GetMax()) + } + +} + +// Reboots the device and verifies Rx InputPower, TxOutputPower data while components are booting. +func dutRebootRxInputTxOutputPowerCheck(t *testing.T, tc *testData) { + gnoiClient, err := tc.dut.RawAPIs().BindingDUT().DialGNOI(context.Background()) + if err != nil { + t.Fatalf("Failed to connect to gnoi server, err: %v", err) + } + rebootRequest := &gnps.RebootRequest{ + Method: gnps.RebootMethod_COLD, + Force: true, + } + preRebootCompStatus := gnmi.GetAll(t, tc.dut, gnmi.OC().ComponentAny().OperStatus().State()) + preRebootCompDebug := gnmi.GetAll(t, tc.dut, gnmi.OC().ComponentAny().State()) + preCompMatrix := []string{} + for _, preComp := range preRebootCompDebug { + if preComp.GetOperStatus() != oc.PlatformTypes_COMPONENT_OPER_STATUS_UNSET { + preCompMatrix = append(preCompMatrix, preComp.GetName()+":"+preComp.GetOperStatus().String()) + } + } + + bootTimeBeforeReboot := gnmi.Get(t, tc.dut, gnmi.OC().System().BootTime().State()) + t.Logf("DUT boot time before reboot: %v", bootTimeBeforeReboot) + var currentTime string + currentTime = gnmi.Get(t, tc.dut, gnmi.OC().System().CurrentDatetime().State()) + t.Logf("Time Before Reboot : %v", currentTime) + rebootResponse, err := gnoiClient.System().Reboot(context.Background(), rebootRequest) + t.Logf("Got Reboot response: %v, err: %v", rebootResponse, err) + if err != nil { + t.Fatalf("Failed to reboot chassis with unexpected err: %v", err) + } + for { + if errMsg := testt.CaptureFatal(t, func(t testing.TB) { + currentTime = gnmi.Get(t, tc.dut, gnmi.OC().System().CurrentDatetime().State()) + }); errMsg != nil { + t.Log("Reboot is started") + break + } + t.Log("Wait for reboot to be started") + time.Sleep(30 * time.Second) + } + startReboot := time.Now() + t.Logf("Waiting for DUT to boot up by polling the telemetry output.") + for { + if errMsg := testt.CaptureFatal(t, func(t testing.TB) { + currentTime = gnmi.Get(t, tc.dut, gnmi.OC().System().CurrentDatetime().State()) + }); errMsg == nil { + t.Logf("Device rebooted successfully with received time: %v", currentTime) + break + } + if uint64(time.Since(startReboot).Seconds()) > maxRebootTime { + t.Fatalf("Check boot time: got %v, want < %v", time.Since(startReboot), maxRebootTime) + } + } + time.Sleep(30 * time.Second) + startComp := time.Now() + for { + + postRebootCompStatus := gnmi.GetAll(t, tc.dut, gnmi.OC().ComponentAny().OperStatus().State()) + postRebootCompDebug := gnmi.GetAll(t, tc.dut, gnmi.OC().ComponentAny().State()) + postCompMatrix := []string{} + + if verErrMsg := testt.CaptureFatal(t, func(t testing.TB) { + interfaceEnabled := gnmi.Get(t, tc.dut, gnmi.OC().Interface(tc.interfaceName).Enabled().Config()) + transceiverOpticalChannelPath := gnmi.OC().Component(tc.transceiverOpticalChannelName) + transceiverOpticalChanelInputPower := gnmi.Get(t, tc.dut, transceiverOpticalChannelPath.OpticalChannel().InputPower().State()) + transceiverOpticalChanelOutputPower := gnmi.Get(t, tc.dut, transceiverOpticalChannelPath.OpticalChannel().OutputPower().State()) + verifyValidRxInputPower(t, transceiverOpticalChanelInputPower, !interfaceEnabled, tc.transceiverOpticalChannelName, tc.transceiverName) + verifyValidTxOutputPower(t, transceiverOpticalChanelOutputPower, !interfaceEnabled, tc.transceiverOpticalChannelName, tc.transceiverName) + }); strings.HasPrefix(*verErrMsg, "[Error]") { + t.Fatal(*verErrMsg) + } + + for _, postComp := range postRebootCompDebug { + if postComp.GetOperStatus() != oc.PlatformTypes_COMPONENT_OPER_STATUS_UNSET { + postCompMatrix = append(postCompMatrix, postComp.GetName()+":"+postComp.GetOperStatus().String()) + } + } + if len(preRebootCompStatus) == len(postRebootCompStatus) { + if rebootDiff := cmp.Diff(preCompMatrix, postCompMatrix); rebootDiff == "" { + time.Sleep(10 * time.Second) + break + } + } + if uint64(time.Since(startComp).Seconds()) > maxCompWaitTime { + t.Logf("DUT components status post reboot: %v", postRebootCompStatus) + if rebootDiff := cmp.Diff(preCompMatrix, postCompMatrix); rebootDiff != "" { + t.Logf("[DEBUG] Unexpected diff after reboot (-component missing from pre reboot, +component added from pre reboot): %v ", rebootDiff) + } + t.Fatalf("There's a difference in components obtained in pre reboot: %v and post reboot: %v.", len(preRebootCompStatus), len(postRebootCompStatus)) + break + } + time.Sleep(10 * time.Second) + } + +} + +// Verifies Rx InputPower, TxOutputPower data is streamed correctly after interface flaps. +func verifyRxInputTxOutputAfterFlap(t *testing.T, tc *testData) { + + interfacePath := gnmi.OC().Interface(tc.interfaceName) + + transceiverOpticalChannelPath := gnmi.OC().Component(tc.transceiverOpticalChannelName) + opticalInputPowerStream := samplestream.New(t, tc.dut, transceiverOpticalChannelPath.OpticalChannel().InputPower().State(), 10*time.Second) + defer opticalInputPowerStream.Close() + transceiverOpticalOutputPowerStream := samplestream.New(t, tc.dut, transceiverOpticalChannelPath.OpticalChannel().OutputPower().State(), 10*time.Second) + defer transceiverOpticalOutputPowerStream.Close() + + transceiverOpticalChanelInputPower, _ := opticalInputPowerStream.Next().Val() + transceiverOpticalChanelOutputPower, _ := transceiverOpticalOutputPowerStream.Next().Val() + + verifyValidRxInputPower(t, transceiverOpticalChanelInputPower, false, tc.transceiverOpticalChannelName, tc.transceiverName) + verifyValidTxOutputPower(t, transceiverOpticalChanelOutputPower, false, tc.transceiverOpticalChannelName, tc.transceiverName) + + t.Logf("Disbaling the interface = %v ", tc.interfaceName) + gnmi.Replace(t, tc.dut, interfacePath.Enabled().Config(), *ygot.Bool(false)) + gnmi.Await(t, tc.dut, interfacePath.Enabled().State(), time.Minute, *ygot.Bool(false)) + + t.Logf("Disabled the interface = %v", tc.interfaceName) + transceiverOpticalChanelInputPower, _ = opticalInputPowerStream.Nexts(5)[4].Val() + transceiverOpticalChanelOutputPower, _ = transceiverOpticalOutputPowerStream.Nexts(5)[4].Val() + verifyValidRxInputPower(t, transceiverOpticalChanelInputPower, true, tc.transceiverOpticalChannelName, tc.transceiverName) + verifyValidTxOutputPower(t, transceiverOpticalChanelOutputPower, true, tc.transceiverOpticalChannelName, tc.transceiverName) + + t.Logf("Re-enabling the interface = %v ", tc.interfaceName) + gnmi.Replace(t, tc.dut, interfacePath.Enabled().Config(), *ygot.Bool(true)) + gnmi.Await(t, tc.dut, interfacePath.Enabled().State(), time.Minute, *ygot.Bool(true)) + t.Logf("Re-enabled the interface = %v", tc.interfaceName) + + transceiverOpticalChanelInputPower, _ = opticalInputPowerStream.Nexts(5)[4].Val() + transceiverOpticalChanelOutputPower, _ = transceiverOpticalOutputPowerStream.Nexts(5)[4].Val() + verifyValidRxInputPower(t, transceiverOpticalChanelInputPower, false, tc.transceiverOpticalChannelName, tc.transceiverName) + verifyValidTxOutputPower(t, transceiverOpticalChanelOutputPower, false, tc.transceiverOpticalChannelName, tc.transceiverName) + +} + +func TestZrInputOutputPower(t *testing.T) { + dut1 := ondatra.DUT(t, "dut1") + dut2 := ondatra.DUT(t, "dut2") + + transceiver1Port := dut1.Port(t, "port1") + transceiver2Port := dut2.Port(t, "port1") + transceiver1Name := gnmi.Get(t, dut1, gnmi.OC().Interface(transceiver1Port.Name()).Transceiver().State()) + transceiver2Name := gnmi.Get(t, dut2, gnmi.OC().Interface(transceiver2Port.Name()).Transceiver().State()) + + configureDUT(t, dut1, transceiver1Name) + configureDUT(t, dut2, transceiver2Name) + + transceiver1OpticalChannelName := gnmi.Get(t, dut1, gnmi.OC().Component(transceiver1Name).Transceiver().Channel(0).AssociatedOpticalChannel().State()) + transceiver2OpticalChannelName := gnmi.Get(t, dut2, gnmi.OC().Component(transceiver2Name).Transceiver().Channel(0).AssociatedOpticalChannel().State()) + + testCases := []testData{ + { + transceiverName: transceiver1Name, + dut: dut1, + transceiverOpticalChannelName: transceiver1OpticalChannelName, + interfaceName: transceiver1Port.Name(), + }, + { + transceiverName: transceiver2Name, + dut: dut2, + transceiverOpticalChannelName: transceiver2OpticalChannelName, + interfaceName: transceiver2Port.Name(), + }, + } + + t.Run("RT-4.1: Testing Input, Output Power telemetry", func(t *testing.T) { + for _, testDataObj := range testCases { + verifyInputOutputPower(t, &testDataObj) + } + }) + + t.Run("RT-4.2: Testing Rx Input Power, Tx Output Power telemetry during DUT reboot", func(t *testing.T) { + for _, testDataObj := range testCases { + dutRebootRxInputTxOutputPowerCheck(t, &testDataObj) + } + }) + t.Run("RT-4.3: Interface flap Rx Input Power Tx Output Power telemetry test", func(t *testing.T) { + for _, testDataObj := range testCases { + verifyRxInputTxOutputAfterFlap(t, &testDataObj) + } + }) + + // Todo: ## TRANSCEIVER-4.4 . + +} diff --git a/feature/platform/transceiver/zr_inventory_test/README.md b/feature/platform/transceiver/tests/zr_inventory_test/README.md similarity index 96% rename from feature/platform/transceiver/zr_inventory_test/README.md rename to feature/platform/transceiver/tests/zr_inventory_test/README.md index bbb78e3bf09..d1cecb19411 100644 --- a/feature/platform/transceiver/zr_inventory_test/README.md +++ b/feature/platform/transceiver/tests/zr_inventory_test/README.md @@ -1,4 +1,4 @@ -# TRANSCEIVER-7: Telemetry: 400ZR module inventory information. +# TRANSCEIVER-7: Telemetry: 400ZR Optics inventory info streaming ## Summary @@ -30,7 +30,7 @@ Validate 400ZR modules report correct inventory information. streaming telemetry paths above. * Reset the optic by enabling and disabling the transceiver state through /components/component/transceiver/config/enabled. - * Wait atleast 20 seconds in between toggling transceiver state. + * Wait at least 20 seconds in between toggling transceiver state. * Verify the ZR optics still reports correct inventory information. * Telemetry subscription should be ON_CHANGE and streamed data should be of type String. diff --git a/feature/platform/transceiver/tests/zr_inventory_test/metadata.textproto b/feature/platform/transceiver/tests/zr_inventory_test/metadata.textproto new file mode 100644 index 00000000000..1a64604ac45 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_inventory_test/metadata.textproto @@ -0,0 +1,14 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata +uuid: "376d3561-4271-4efc-be63-3eec7b56b86d" +plan_id: "TRANSCEIVER-7" +description: "Telemetry: 400ZR Optics inventory info streaming" +testbed: TESTBED_DUT_400ZR +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + interface_enabled: true + } +} diff --git a/feature/platform/transceiver/tests/zr_inventory_test/zr_inventory_test.go b/feature/platform/transceiver/tests/zr_inventory_test/zr_inventory_test.go new file mode 100644 index 00000000000..9e09d65b9c1 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_inventory_test/zr_inventory_test.go @@ -0,0 +1,158 @@ +package zr_inventory_test + +import ( + "fmt" + "testing" + "time" + + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/samplestream" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ygot/ygot" +) + +const ( + dp16QAM = 1 + samplingInterval = 10 * time.Second + timeout = 5 * time.Minute + waitInterval = 30 * time.Second +) + +const ( + frequency = 193100000 + targetOutputPower = -10 +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +func configInterface(t *testing.T, dut1 *ondatra.DUTDevice, dp *ondatra.Port, frequency uint64, targetOutputPower float64) { + d := &oc.Root{} + i := d.GetOrCreateInterface(dp.Name()) + i.Enabled = ygot.Bool(true) + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + gnmi.Replace(t, dut1, gnmi.OC().Interface(dp.Name()).Config(), i) + c := opticalChannelComponentFromPort(t, dut1, dp) + gnmi.Replace(t, dut1, gnmi.OC().Component(c).OpticalChannel().Config(), &oc.Component_OpticalChannel{ + TargetOutputPower: ygot.Float64(targetOutputPower), + Frequency: ygot.Uint64(frequency), + }) +} + +func verifyAllInventoryValues(t *testing.T, pStreamsStr []*samplestream.SampleStream[string], pStreamsUnion []*samplestream.SampleStream[oc.Component_Type_Union]) { + for _, stream := range pStreamsStr { + inventoryStr := stream.Next() + if inventoryStr == nil { + t.Fatalf("Inventory telemetry %v was not streamed in the most recent subscription interval", stream) + } + inventoryVal, ok := inventoryStr.Val() + if !ok { + t.Fatalf("Inventory telemetry %q is not present or valid, expected ", inventoryStr) + } else { + t.Logf("Inventory telemetry %q is valid: %q", inventoryStr, inventoryVal) + } + } + + for _, stream := range pStreamsUnion { + inventoryUnion := stream.Next() + if inventoryUnion == nil { + t.Fatalf("Inventory telemetry %v was not streamed in the most recent subscription interval", stream) + } + inventoryVal, ok := inventoryUnion.Val() + if !ok { + t.Fatalf("Inventory telemetry %q is not present or valid, expected ", inventoryUnion) + } else { + t.Logf("Inventory telemetry %q is valid: %q", inventoryUnion, inventoryVal) + } + + } +} + +func TestInventory(t *testing.T) { + dut1 := ondatra.DUT(t, "dut") + dp1 := dut1.Port(t, "port1") + dp2 := dut1.Port(t, "port2") + fptest.ConfigureDefaultNetworkInstance(t, dut1) + + // Derive transceiver names from ports. + tr1 := gnmi.Get(t, dut1, gnmi.OC().Interface(dp1.Name()).Transceiver().State()) + tr2 := gnmi.Get(t, dut1, gnmi.OC().Interface(dp2.Name()).Transceiver().State()) + + if (dp1.PMD() != ondatra.PMD400GBASEZR) || (dp2.PMD() != ondatra.PMD400GBASEZR) { + t.Fatalf("Transceivers types (%v, %v): (%v, %v) are not 400ZR, expected %v", tr1, tr2, dp1.PMD(), dp2.PMD(), ondatra.PMD400GBASEZR) + } + component1 := gnmi.OC().Component(tr1) + + configInterface(t, dut1, dp1, frequency, targetOutputPower) + configInterface(t, dut1, dp2, frequency, targetOutputPower) + // Wait for channels to be up. + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + gnmi.Await(t, dut1, gnmi.OC().Interface(dp2.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + + var p1StreamsStr []*samplestream.SampleStream[string] + var p1StreamsUnion []*samplestream.SampleStream[oc.Component_Type_Union] + p1StreamsStr = append(p1StreamsStr, + samplestream.New(t, dut1, component1.SerialNo().State(), samplingInterval), + samplestream.New(t, dut1, component1.PartNo().State(), samplingInterval), + samplestream.New(t, dut1, component1.MfgName().State(), samplingInterval), + samplestream.New(t, dut1, component1.MfgDate().State(), samplingInterval), + samplestream.New(t, dut1, component1.HardwareVersion().State(), samplingInterval), + samplestream.New(t, dut1, component1.FirmwareVersion().State(), samplingInterval), + samplestream.New(t, dut1, component1.Description().State(), samplingInterval), + ) + p1StreamsUnion = append(p1StreamsUnion, samplestream.New(t, dut1, component1.Type().State(), samplingInterval)) + + verifyAllInventoryValues(t, p1StreamsStr, p1StreamsUnion) + + // Disable or shut down the interface on the DUT. + gnmi.Replace(t, dut1, gnmi.OC().Interface(dp1.Name()).Enabled().Config(), false) + gnmi.Replace(t, dut1, gnmi.OC().Interface(dp2.Name()).Enabled().Config(), false) + // Wait for channels to be down. + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_DOWN) + gnmi.Await(t, dut1, gnmi.OC().Interface(dp2.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_DOWN) + + t.Logf("Interfaces are down: %v, %v", dp1.Name(), dp2.Name()) + verifyAllInventoryValues(t, p1StreamsStr, p1StreamsUnion) + + time.Sleep(waitInterval) + // Re-enable interfaces. + gnmi.Replace(t, dut1, gnmi.OC().Interface(dp1.Name()).Enabled().Config(), true) + gnmi.Replace(t, dut1, gnmi.OC().Interface(dp2.Name()).Enabled().Config(), true) + // Wait for channels to be up. + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + gnmi.Await(t, dut1, gnmi.OC().Interface(dp2.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + + t.Logf("Interfaces are up: %v, %v", dp1.Name(), dp2.Name()) + verifyAllInventoryValues(t, p1StreamsStr, p1StreamsUnion) +} + +func opticalChannelComponentFromPort(t *testing.T, dut *ondatra.DUTDevice, p *ondatra.Port) string { + t.Helper() + if deviations.MissingPortToOpticalChannelMapping(dut) { + switch dut.Vendor() { + case ondatra.ARISTA: + transceiverName := gnmi.Get(t, dut, gnmi.OC().Interface(p.Name()).Transceiver().State()) + return fmt.Sprintf("%s-Optical0", transceiverName) + default: + t.Fatal("Manual Optical channel name required when deviation missing_port_to_optical_channel_component_mapping applied.") + } + } + compName := gnmi.Get(t, dut, gnmi.OC().Interface(p.Name()).HardwarePort().State()) + for { + comp, ok := gnmi.Lookup(t, dut, gnmi.OC().Component(compName).State()).Val() + if !ok { + t.Fatalf("Recursive optical channel lookup failed for port: %s, component %s not found.", p.Name(), compName) + } + if comp.GetType() == oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_OPTICAL_CHANNEL { + return compName + } + if comp.GetParent() == "" { + t.Fatalf("Recursive optical channel lookup failed for port: %s, parent of component %s not found.", p.Name(), compName) + } + compName = comp.GetParent() + } +} diff --git a/feature/platform/transceiver/zr_laser_bias_current_test/README.md b/feature/platform/transceiver/tests/zr_laser_bias_current_test/README.md similarity index 100% rename from feature/platform/transceiver/zr_laser_bias_current_test/README.md rename to feature/platform/transceiver/tests/zr_laser_bias_current_test/README.md diff --git a/feature/platform/transceiver/tests/zr_laser_bias_current_test/metadata.textproto b/feature/platform/transceiver/tests/zr_laser_bias_current_test/metadata.textproto new file mode 100644 index 00000000000..66bb18f7316 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_laser_bias_current_test/metadata.textproto @@ -0,0 +1,17 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "3559ab05-7b5b-40e5-86d8-63eb0e668c8a" +plan_id: "TRANSCEIVER-9" +description: "Telemetry: 400ZR TX laser bias current telemetry values streaming." +testbed: TESTBED_DUT_400ZR +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + interface_enabled: true + default_network_instance: "default" + missing_port_to_optical_channel_component_mapping: true + } +} diff --git a/feature/platform/transceiver/tests/zr_laser_bias_current_test/zr_laser_bias_current_test.go b/feature/platform/transceiver/tests/zr_laser_bias_current_test/zr_laser_bias_current_test.go new file mode 100644 index 00000000000..0044d117154 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_laser_bias_current_test/zr_laser_bias_current_test.go @@ -0,0 +1,218 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package zr_laser_bias_current_test + +import ( + "fmt" + "reflect" + "testing" + "time" + + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/samplestream" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ygot/ygot" +) + +const ( + dp16QAM = 1 + targetOutputPower = -10 + frequency = 193100000 +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +// Topology: +// dut:port1 <--> port2:dut +// + +func verifyLaserBiasCurrent(t *testing.T, pStream *samplestream.SampleStream[float64], sensorName string) float64 { + laserBias := pStream.Next() + if laserBias == nil { + t.Fatalf("laserBias telemetry %q was not streamed in the most recent subscription interval", sensorName) + } + laserBiasVal, ok := laserBias.Val() + if !ok { + t.Fatalf("LaserBias %q telemetry is not present", sensorName) + } + if reflect.TypeOf(laserBiasVal).Kind() != reflect.Float64 { + t.Errorf("Return value is not type float64") + } + if laserBiasVal <= 0 && laserBiasVal >= 131 { + t.Errorf("The laser bias value is not between 0 and 131") + } + t.Logf("laserBias value: %f", laserBiasVal) + return laserBiasVal +} + +func verifyLaserBiasCurrentAll(t *testing.T, pStreamInstant *samplestream.SampleStream[float64], pStreamAvg *samplestream.SampleStream[float64], pStreamMax *samplestream.SampleStream[float64], pStreamMin *samplestream.SampleStream[float64]) { + laserbiasInstant := verifyLaserBiasCurrent(t, pStreamInstant, "laserbiasInstant") + t.Logf("laserBias Instant value: %f", laserbiasInstant) + laserbiasMin := verifyLaserBiasCurrent(t, pStreamMin, "laserbiasMin") + t.Logf("laserBias Min value: %f", laserbiasMin) + laserbiasMax := verifyLaserBiasCurrent(t, pStreamMax, "laserbiasMax") + t.Logf("laserBias Max value: %f", laserbiasMax) + laserbiasAvg := verifyLaserBiasCurrent(t, pStreamAvg, "laserbiasAvg") + t.Logf("laserBias Avg value: %f", laserbiasAvg) + if laserbiasAvg >= laserbiasMin && laserbiasAvg <= laserbiasMax { + t.Logf("The average is between the maximum and minimum values") + } else { + t.Fatalf("The average is not between the maximum and minimum values Avg:%f Min:%f Max:%f", laserbiasAvg, laserbiasMin, laserbiasMax) + } +} +func interfaceConfig(t *testing.T, dut1 *ondatra.DUTDevice, dp *ondatra.Port) { + d := &oc.Root{} + i := d.GetOrCreateInterface(dp.Name()) + i.Enabled = ygot.Bool(true) + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + gnmi.Replace(t, dut1, gnmi.OC().Interface(dp.Name()).Config(), i) + OCcomponent := opticalChannelComponentFromPort(t, dut1, dp) + gnmi.Replace(t, dut1, gnmi.OC().Component(OCcomponent).OpticalChannel().Config(), &oc.Component_OpticalChannel{ + TargetOutputPower: ygot.Float64(targetOutputPower), + Frequency: ygot.Uint64(frequency), + }) +} +func TestZRLaserBiasCurrentState(t *testing.T) { + dut1 := ondatra.DUT(t, "dut") + dp1 := dut1.Port(t, "port1") + dp2 := dut1.Port(t, "port2") + t.Logf("dut1: %v", dut1) + t.Logf("dut1 dp1 name: %v", dp1.Name()) + interfaceConfig(t, dut1, dp1) + interfaceConfig(t, dut1, dp2) + intUpdateTime := 2 * time.Minute + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), intUpdateTime, oc.Interface_OperStatus_UP) + transceiverState := gnmi.Get(t, dut1, gnmi.OC().Interface(dp1.Name()).Transceiver().State()) + if dp1.PMD() != ondatra.PMD400GBASEZR { + t.Fatalf("%s Transceiver is not 400ZR its of type: %v", transceiverState, dp1.PMD()) + } + OCcomponent := opticalChannelComponentFromPort(t, dut1, dp1) + component1 := gnmi.OC().Component(OCcomponent) + p1StreamInstant := samplestream.New(t, dut1, component1.OpticalChannel().LaserBiasCurrent().Instant().State(), 10*time.Second) + p1StreamMin := samplestream.New(t, dut1, component1.OpticalChannel().LaserBiasCurrent().Min().State(), 10*time.Second) + p1StreamMax := samplestream.New(t, dut1, component1.OpticalChannel().LaserBiasCurrent().Max().State(), 10*time.Second) + p1StreamAvg := samplestream.New(t, dut1, component1.OpticalChannel().LaserBiasCurrent().Avg().State(), 10*time.Second) + defer p1StreamAvg.Close() + defer p1StreamMax.Close() + defer p1StreamMin.Close() + defer p1StreamInstant.Close() + verifyLaserBiasCurrentAll(t, p1StreamInstant, p1StreamAvg, p1StreamMax, p1StreamMin) +} +func TestZRLaserBiasCurrentStateInterface_Flap(t *testing.T) { + dut1 := ondatra.DUT(t, "dut") + dp1 := dut1.Port(t, "port1") + dp2 := dut1.Port(t, "port2") + t.Logf("dut1: %v", dut1) + t.Logf("dut1 dp1 name: %v", dp1.Name()) + interfaceConfig(t, dut1, dp1) + interfaceConfig(t, dut1, dp2) + intUpdateTime := 2 * time.Minute + // Check interface is up + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), intUpdateTime, oc.Interface_OperStatus_UP) + // Check if TRANSCEIVER is of type 400ZR + transceiverState := gnmi.Get(t, dut1, gnmi.OC().Interface(dp1.Name()).Transceiver().State()) + if dp1.PMD() != ondatra.PMD400GBASEZR { + t.Fatalf("%s Transceiver is not 400ZR its of type: %v", transceiverState, dp1.PMD()) + } + // Disable interface + d := &oc.Root{} + i := d.GetOrCreateInterface(dp1.Name()) + i.Enabled = ygot.Bool(false) + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + gnmi.Replace(t, dut1, gnmi.OC().Interface(dp1.Name()).Config(), i) + OCcomponent := opticalChannelComponentFromPort(t, dut1, dp1) + component1 := gnmi.OC().Component(OCcomponent) + p1StreamInstant := samplestream.New(t, dut1, component1.OpticalChannel().LaserBiasCurrent().Instant().State(), 10*time.Second) + p1StreamMin := samplestream.New(t, dut1, component1.OpticalChannel().LaserBiasCurrent().Min().State(), 10*time.Second) + p1StreamMax := samplestream.New(t, dut1, component1.OpticalChannel().LaserBiasCurrent().Max().State(), 10*time.Second) + p1StreamAvg := samplestream.New(t, dut1, component1.OpticalChannel().LaserBiasCurrent().Avg().State(), 10*time.Second) + defer p1StreamInstant.Close() + defer p1StreamMin.Close() + defer p1StreamMax.Close() + defer p1StreamAvg.Close() + verifyLaserBiasCurrentAll(t, p1StreamInstant, p1StreamAvg, p1StreamMax, p1StreamMin) + // Wait 120 sec cooling off period + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), intUpdateTime, oc.Interface_OperStatus_DOWN) + // Enable interface + verifyLaserBiasCurrentAll(t, p1StreamInstant, p1StreamAvg, p1StreamMax, p1StreamMin) + i.Enabled = ygot.Bool(true) + gnmi.Replace(t, dut1, gnmi.OC().Interface(dp1.Name()).Config(), i) + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), intUpdateTime, oc.Interface_OperStatus_UP) + verifyLaserBiasCurrentAll(t, p1StreamInstant, p1StreamAvg, p1StreamMax, p1StreamMin) +} +func TestZRLaserBiasCurrentStateTransceiverOnOff(t *testing.T) { + dut1 := ondatra.DUT(t, "dut") + dp1 := dut1.Port(t, "port1") + dp2 := dut1.Port(t, "port2") + t.Logf("dut1: %v", dut1) + t.Logf("dut1 dp1 name: %v", dp1.Name()) + interfaceConfig(t, dut1, dp1) + interfaceConfig(t, dut1, dp2) + intUpdateTime := 2 * time.Minute + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), intUpdateTime, oc.Interface_OperStatus_UP) + transceiverState := gnmi.Get(t, dut1, gnmi.OC().Interface(dp1.Name()).Transceiver().State()) + // Check if TRANSCEIVER is of type 400ZR + if dp1.PMD() != ondatra.PMD400GBASEZR { + t.Fatalf("%s Transceiver is not 400ZR its of type: %v", transceiverState, dp1.PMD()) + } + OCcomponent := opticalChannelComponentFromPort(t, dut1, dp1) + component1 := gnmi.OC().Component(OCcomponent) + p1StreamInstant := samplestream.New(t, dut1, component1.OpticalChannel().LaserBiasCurrent().Instant().State(), 10*time.Second) + p1StreamMin := samplestream.New(t, dut1, component1.OpticalChannel().LaserBiasCurrent().Min().State(), 10*time.Second) + p1StreamMax := samplestream.New(t, dut1, component1.OpticalChannel().LaserBiasCurrent().Max().State(), 10*time.Second) + p1StreamAvg := samplestream.New(t, dut1, component1.OpticalChannel().LaserBiasCurrent().Avg().State(), 10*time.Second) + defer p1StreamInstant.Close() + defer p1StreamMin.Close() + defer p1StreamMax.Close() + defer p1StreamAvg.Close() + // Disable interface transceiver power off + gnmi.Update(t, dut1, gnmi.OC().Component(dp1.Name()).Transceiver().Enabled().Config(), false) + verifyLaserBiasCurrentAll(t, p1StreamInstant, p1StreamAvg, p1StreamMax, p1StreamMin) + // Enable interface transceiver power on + gnmi.Update(t, dut1, gnmi.OC().Component(dp1.Name()).Transceiver().Enabled().Config(), true) + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), intUpdateTime, oc.Interface_OperStatus_UP) + verifyLaserBiasCurrentAll(t, p1StreamInstant, p1StreamAvg, p1StreamMax, p1StreamMin) +} +func opticalChannelComponentFromPort(t *testing.T, dut *ondatra.DUTDevice, p *ondatra.Port) string { + t.Helper() + if deviations.MissingPortToOpticalChannelMapping(dut) { + switch dut.Vendor() { + case ondatra.ARISTA: + transceiverState := gnmi.Get(t, dut, gnmi.OC().Interface(p.Name()).Transceiver().State()) + return fmt.Sprintf("%s-Optical0", transceiverState) + default: + t.Fatal("Manual Optical channel name required when deviation missing_port_to_optical_channel_component_mapping applied.") + } + } + compName := gnmi.Get(t, dut, gnmi.OC().Interface(p.Name()).HardwarePort().State()) + for { + comp, ok := gnmi.Lookup(t, dut, gnmi.OC().Component(compName).State()).Val() + if !ok { + t.Fatalf("Recursive optical channel lookup failed for port: %s, component %s not found.", p.Name(), compName) + } + if comp.GetType() == oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_OPTICAL_CHANNEL { + return compName + } + if comp.GetParent() == "" { + t.Fatalf("Recursive optical channel lookup failed for port: %s, parent of component %s not found.", p.Name(), compName) + } + compName = comp.GetParent() + } +} diff --git a/feature/platform/transceiver/zr_logical_channels_test/README.md b/feature/platform/transceiver/tests/zr_logical_channels_test/README.md similarity index 100% rename from feature/platform/transceiver/zr_logical_channels_test/README.md rename to feature/platform/transceiver/tests/zr_logical_channels_test/README.md diff --git a/feature/platform/transceiver/tests/zr_logical_channels_test/metadata.textproto b/feature/platform/transceiver/tests/zr_logical_channels_test/metadata.textproto new file mode 100644 index 00000000000..caa9410e80a --- /dev/null +++ b/feature/platform/transceiver/tests/zr_logical_channels_test/metadata.textproto @@ -0,0 +1,16 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata +uuid: "0bef25c1-0ea2-4b44-8a37-07de9f870cae" +plan_id: "TRANSCEIVER-11" +description: "Telemetry: 400ZR Optics logical channels provisioning and related telemetry." +testbed: TESTBED_DUT_400ZR +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + interface_enabled: true + default_network_instance: "default" + missing_port_to_optical_channel_component_mapping: true + } +} diff --git a/feature/platform/transceiver/tests/zr_logical_channels_test/zr_logical_channels_test.go b/feature/platform/transceiver/tests/zr_logical_channels_test/zr_logical_channels_test.go new file mode 100644 index 00000000000..5ca9be69189 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_logical_channels_test/zr_logical_channels_test.go @@ -0,0 +1,302 @@ +package zr_logical_channels_test + +import ( + "fmt" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/samplestream" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ygot/ygot" +) + +const ( + targetOutputPower = -9 + frequency = 193100000 + dp16QAM = 1 +) + +var ( + dutPort1 = attrs.Attributes{ + Desc: "dutPort1", + IPv4: "192.0.2.1", + IPv4Len: 30, + } + + dutPort2 = attrs.Attributes{ + Desc: "dutPort2", + IPv4: "192.0.2.5", + IPv4Len: 30, + } +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +func Test400ZRLogicalChannels(t *testing.T) { + dut := ondatra.DUT(t, "dut") + + p1 := dut.Port(t, "port1") + p2 := dut.Port(t, "port2") + + fptest.ConfigureDefaultNetworkInstance(t, dut) + + gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), dut)) + gnmi.Replace(t, dut, gnmi.OC().Interface(p2.Name()).Config(), dutPort2.NewOCInterface(p2.Name(), dut)) + + oc1 := opticalChannelFromPort(t, dut, p1) + oc2 := opticalChannelFromPort(t, dut, p2) + + configureLogicalChannels(t, dut, 40000, 40001, oc1) + configureLogicalChannels(t, dut, 40002, 40003, oc2) + + ethChan1 := samplestream.New(t, dut, gnmi.OC().TerminalDevice().Channel(40000).State(), 10*time.Second) + defer ethChan1.Close() + validateEthernetChannelTelemetry(t, 40000, 40001, ethChan1) + cohChan1 := samplestream.New(t, dut, gnmi.OC().TerminalDevice().Channel(40001).State(), 10*time.Second) + defer cohChan1.Close() + validateCoherentChannelTelemetry(t, 40001, oc1, cohChan1) + ethChan2 := samplestream.New(t, dut, gnmi.OC().TerminalDevice().Channel(40002).State(), 10*time.Second) + defer ethChan2.Close() + validateEthernetChannelTelemetry(t, 40002, 40003, ethChan2) + cohChan2 := samplestream.New(t, dut, gnmi.OC().TerminalDevice().Channel(40002).State(), 10*time.Second) + defer cohChan2.Close() + validateCoherentChannelTelemetry(t, 40003, oc2, cohChan2) +} + +func configureLogicalChannels(t *testing.T, dut *ondatra.DUTDevice, ethernetChIdx, coherentChIdx uint32, opticalChannel string) { + t.Helper() + b := &gnmi.SetBatch{} + + // Optical Channel and Tunable Parameters + gnmi.BatchReplace(b, gnmi.OC().Component(opticalChannel).OpticalChannel().Config(), &oc.Component_OpticalChannel{ + TargetOutputPower: ygot.Float64(targetOutputPower), + Frequency: ygot.Uint64(frequency), + OperationalMode: ygot.Uint16(dp16QAM), + }) + + // Ethernet Logical Channel + gnmi.BatchReplace(b, gnmi.OC().TerminalDevice().Channel(ethernetChIdx).Config(), &oc.TerminalDevice_Channel{ + LogicalChannelType: oc.TransportTypes_LOGICAL_ELEMENT_PROTOCOL_TYPE_PROT_ETHERNET, + AdminState: oc.TerminalDevice_AdminStateType_ENABLED, + Description: ygot.String("ETH Logical Channel"), + Index: ygot.Uint32(ethernetChIdx), + RateClass: oc.TransportTypes_TRIBUTARY_RATE_CLASS_TYPE_TRIB_RATE_400G, + TribProtocol: oc.TransportTypes_TRIBUTARY_PROTOCOL_TYPE_PROT_400GE, + Assignment: map[uint32]*oc.TerminalDevice_Channel_Assignment{ + 1: { + Index: ygot.Uint32(1), + LogicalChannel: ygot.Uint32(coherentChIdx), + Description: ygot.String("ETH to Coherent"), + Allocation: ygot.Float64(400), + AssignmentType: oc.Assignment_AssignmentType_LOGICAL_CHANNEL, + }, + }, + }) + + // Coherent Logical Channel + gnmi.BatchReplace(b, gnmi.OC().TerminalDevice().Channel(coherentChIdx).Config(), &oc.TerminalDevice_Channel{ + LogicalChannelType: oc.TransportTypes_LOGICAL_ELEMENT_PROTOCOL_TYPE_PROT_OTN, + AdminState: oc.TerminalDevice_AdminStateType_ENABLED, + Description: ygot.String("Coherent Logical Channel"), + Index: ygot.Uint32(coherentChIdx), + Assignment: map[uint32]*oc.TerminalDevice_Channel_Assignment{ + 1: { + Index: ygot.Uint32(1), + OpticalChannel: ygot.String(opticalChannel), + Description: ygot.String("Coherent to Optical"), + Allocation: ygot.Float64(400), + AssignmentType: oc.Assignment_AssignmentType_OPTICAL_CHANNEL, + }, + }, + }) + + b.Set(t, dut) +} + +func validateEthernetChannelTelemetry(t *testing.T, ethernetChIdx, coherentChIdx uint32, stream *samplestream.SampleStream[*oc.TerminalDevice_Channel]) { + val := stream.Next() // value received in the gnmi subscription within 10 seconds + if val == nil { + t.Fatalf("Ethernet Channel telemetry stream not received in last 10 seconds") + } + ec, ok := val.Val() + if !ok { + t.Fatalf("Ethernet Channel telemetry stream empty in last 10 seconds") + } + tcs := []struct { + desc string + got any + want any + }{ + { + desc: "Index", + got: ec.GetIndex(), + want: ethernetChIdx, + }, + { + desc: "Admin State", + got: ec.GetAdminState().String(), + want: oc.TerminalDevice_AdminStateType_ENABLED.String(), + }, + { + desc: "Description", + got: ec.GetDescription(), + want: "ETH Logical Channel", + }, + { + desc: "Logical Channel Type", + got: ec.GetLogicalChannelType().String(), + want: oc.TransportTypes_LOGICAL_ELEMENT_PROTOCOL_TYPE_PROT_ETHERNET.String(), + }, + { + desc: "Rate Class", + got: ec.GetRateClass().String(), + want: oc.TransportTypes_TRIBUTARY_RATE_CLASS_TYPE_TRIB_RATE_400G.String(), + }, + { + desc: "Trib Protocol", + got: ec.GetTribProtocol().String(), + want: oc.TransportTypes_TRIBUTARY_PROTOCOL_TYPE_PROT_400GE.String(), + }, + { + desc: "Assignment: Index", + got: ec.GetAssignment(1).GetIndex(), + want: uint32(1), + }, + { + desc: "Assignment: Logical Channel", + got: ec.GetAssignment(1).GetLogicalChannel(), + want: coherentChIdx, + }, + { + desc: "Assignment: Description", + got: ec.GetAssignment(1).GetDescription(), + want: "ETH to Coherent", + }, + { + desc: "Assignment: Allocation", + got: ec.GetAssignment(1).GetAllocation(), + want: float64(400), + }, + { + desc: "Assignment: Type", + got: ec.GetAssignment(1).GetAssignmentType().String(), + want: oc.Assignment_AssignmentType_LOGICAL_CHANNEL.String(), + }, + } + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + if diff := cmp.Diff(tc.got, tc.want); diff != "" { + t.Errorf("Ethernet Logical Channel: %s, diff (-got +want):\n%s", tc.desc, diff) + } + }) + } +} + +func validateCoherentChannelTelemetry(t *testing.T, coherentChIdx uint32, opticalChannel string, stream *samplestream.SampleStream[*oc.TerminalDevice_Channel]) { + val := stream.Next() // value received in the gnmi subscription within 10 seconds + if val == nil { + t.Fatalf("Coherent Channel telemetry stream not received in last 10 seconds") + } + cc, ok := val.Val() + if !ok { + t.Fatalf("Coherent Channel telemetry stream empty in last 10 seconds") + } + tcs := []struct { + desc string + got any + want any + }{ + { + desc: "Admin State", + got: cc.GetAdminState().String(), + want: oc.TerminalDevice_AdminStateType_ENABLED.String(), + }, + { + desc: "Description", + got: cc.GetDescription(), + want: "Coherent Logical Channel", + }, + { + desc: "Index", + got: cc.GetIndex(), + want: coherentChIdx, + }, + { + desc: "Logical Channel Type", + got: cc.GetLogicalChannelType().String(), + want: oc.TransportTypes_LOGICAL_ELEMENT_PROTOCOL_TYPE_PROT_OTN.String(), + }, + { + desc: "Assignment: Index", + got: cc.GetAssignment(1).GetIndex(), + want: uint32(1), + }, + { + desc: "Assignment: Optical Channel", + got: cc.GetAssignment(1).GetOpticalChannel(), + want: opticalChannel, + }, + { + desc: "Assignment: Description", + got: cc.GetAssignment(1).GetDescription(), + want: "Coherent to Optical", + }, + { + desc: "Assignment: Allocation", + got: cc.GetAssignment(1).GetAllocation(), + want: float64(400), + }, + { + desc: "Assignment: Type", + got: cc.GetAssignment(1).GetAssignmentType().String(), + want: oc.Assignment_AssignmentType_OPTICAL_CHANNEL.String(), + }, + } + + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + if diff := cmp.Diff(tc.got, tc.want); diff != "" { + t.Errorf("Coherent Logical Channel: %s, diff (-got +want):\n%s", tc.desc, diff) + } + }) + } +} + +// opticalChannelFromPort returns the connected optical channel component name for a given ondatra port. +func opticalChannelFromPort(t *testing.T, dut *ondatra.DUTDevice, p *ondatra.Port) string { + t.Helper() + + if deviations.MissingPortToOpticalChannelMapping(dut) { + switch dut.Vendor() { + case ondatra.ARISTA: + return fmt.Sprintf("%s-Optical0", p.Name()) + default: + t.Fatal("Manual Optical channel name required when deviation missing_port_to_optical_channel_component_mapping applied.") + } + } + compName := gnmi.Get(t, dut, gnmi.OC().Interface(p.Name()).HardwarePort().State()) + + for { + comp, ok := gnmi.Lookup(t, dut, gnmi.OC().Component(compName).State()).Val() + if !ok { + t.Fatalf("Recursive optical channel lookup failed for port: %s, component %s not found.", p.Name(), compName) + } + if comp.GetType() == oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_OPTICAL_CHANNEL { + return compName + } + + if comp.GetParent() == "" { + t.Fatalf("Recursive optical channel lookup failed for port: %s, parent of component %s not found.", p.Name(), compName) + } + + compName = comp.GetParent() + } +} diff --git a/feature/platform/transceiver/zr_low_power_mode_test/README.md b/feature/platform/transceiver/tests/zr_low_power_mode_test/README.md similarity index 100% rename from feature/platform/transceiver/zr_low_power_mode_test/README.md rename to feature/platform/transceiver/tests/zr_low_power_mode_test/README.md diff --git a/feature/platform/transceiver/tests/zr_low_power_mode_test/metadata.textproto b/feature/platform/transceiver/tests/zr_low_power_mode_test/metadata.textproto new file mode 100644 index 00000000000..8236fb587cf --- /dev/null +++ b/feature/platform/transceiver/tests/zr_low_power_mode_test/metadata.textproto @@ -0,0 +1,7 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "92fbb383-47b3-40e6-ab33-77be99e450b3" +plan_id: "TRANSCEIVER-13" +description: "Configuration: 400ZR Transceiver Low Power Mode Setting." +testbed: TESTBED_DUT_400ZR diff --git a/feature/platform/transceiver/tests/zr_low_power_mode_test/zr_low_power_mode_test.go b/feature/platform/transceiver/tests/zr_low_power_mode_test/zr_low_power_mode_test.go new file mode 100644 index 00000000000..4d655bdd815 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_low_power_mode_test/zr_low_power_mode_test.go @@ -0,0 +1,180 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package zr_low_power_mode_test + +import ( + "fmt" + "math" + "reflect" + "testing" + "time" + + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/samplestream" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" +) + +const ( + samplingInterval = 10 * time.Second + targetOutputPowerdBm = -10 + targetOutputPowerTolerancedBm = 1 + targetFrequencyHz = 193100000 + targetFrequencyToleranceHz = 100000 +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +// validateStreamOutput validates that the OC path is streamed in the most recent subscription interval. +func validateStreamOutput(t *testing.T, streams map[string]*samplestream.SampleStream[string]) { + for key, stream := range streams { + output := stream.Next() + if output == nil { + t.Fatalf("OC path for %s not streamed in the most recent subscription interval", key) + } + value, ok := output.Val() + if !ok { + t.Fatalf("Error capturing streaming value for %s", key) + } + if reflect.TypeOf(value).Kind() != reflect.String { + t.Fatalf("Return value is not type string for key :%s", key) + } + if value == "" { + t.Fatalf("OC path empty for %s", key) + } + t.Logf("Value for OC path %s: %s", key, value) + } +} + +// validateOutputPower validates that the output power is streamed in the most recent subscription interval. +func validateOutputPower(t *testing.T, streams map[string]*samplestream.SampleStream[float64]) { + for key, stream := range streams { + outputStream := stream.Next() + if outputStream == nil { + t.Fatalf("OC path for %s not streamed in the most recent subscription interval", key) + } + outputPower, ok := outputStream.Val() + if !ok { + t.Fatalf("Error capturing streaming value for %s", key) + } + // Check output power value is of correct type + if reflect.TypeOf(outputPower).Kind() != reflect.Float64 { + t.Fatalf("Return value is not type float64 for key :%s", key) + } + t.Logf("Output power for %s: %f", key, outputPower) + } +} + +func TestLowPowerMode(t *testing.T) { + dut := ondatra.DUT(t, "dut") + + for _, port := range []string{"port1", "port2"} { + t.Run(fmt.Sprintf("Port:%s", port), func(t *testing.T) { + dp := dut.Port(t, port) + + gnmi.Update(t, dut, gnmi.OC().Interface(dp.Name()).Enabled().Config(), bool(false)) + + // Derive transceiver names from ports. + tr := gnmi.Get(t, dut, gnmi.OC().Interface(dp.Name()).Transceiver().State()) + + // Stream all inventory information. + streamSerialNo := samplestream.New(t, dut, gnmi.OC().Component(tr).SerialNo().State(), samplingInterval) + defer streamSerialNo.Close() + streamPartNo := samplestream.New(t, dut, gnmi.OC().Component(tr).PartNo().State(), samplingInterval) + defer streamPartNo.Close() + streamType := samplestream.New(t, dut, gnmi.OC().Component(tr).Type().State(), samplingInterval) + defer streamType.Close() + streamDescription := samplestream.New(t, dut, gnmi.OC().Component(tr).Description().State(), samplingInterval) + defer streamDescription.Close() + streamMfgName := samplestream.New(t, dut, gnmi.OC().Component(tr).MfgName().State(), samplingInterval) + defer streamMfgName.Close() + streamMfgDate := samplestream.New(t, dut, gnmi.OC().Component(tr).MfgDate().State(), samplingInterval) + defer streamMfgDate.Close() + streamHwVersion := samplestream.New(t, dut, gnmi.OC().Component(tr).HardwareVersion().State(), samplingInterval) + defer streamHwVersion.Close() + streamFirmwareVersion := samplestream.New(t, dut, gnmi.OC().Component(tr).FirmwareVersion().State(), samplingInterval) + defer streamFirmwareVersion.Close() + + allStream := map[string]*samplestream.SampleStream[string]{ + "serialNo": streamSerialNo, + "partNo": streamPartNo, + "description": streamDescription, + "mfgName": streamMfgName, + "mfgDate": streamMfgDate, + "hwVersion": streamHwVersion, + "firmwareVersion": streamFirmwareVersion, + } + validateStreamOutput(t, allStream) + + opInst := samplestream.New(t, dut, gnmi.OC().Component(tr).OpticalChannel().OutputPower().Instant().State(), samplingInterval) + defer opInst.Close() + if opInstN := opInst.Next(); opInstN != nil { + if _, ok := opInstN.Val(); ok { + t.Fatalf("streaming /components/component/optical-channel/state/output-power/instant is not expected to be reported") + } + } + + opAvg := samplestream.New(t, dut, gnmi.OC().Component(tr).OpticalChannel().OutputPower().Avg().State(), samplingInterval) + defer opAvg.Close() + if opAvgN := opAvg.Next(); opAvgN != nil { + if _, ok := opAvgN.Val(); ok { + t.Fatalf("streaming /components/component/optical-channel/state/output-power/avg is not expected to be reported") + } + } + + opMin := samplestream.New(t, dut, gnmi.OC().Component(tr).OpticalChannel().OutputPower().Min().State(), samplingInterval) + defer opMin.Close() + if opMinN := opMin.Next(); opMinN != nil { + if _, ok := opMinN.Val(); ok { + t.Fatalf("streaming /components/component/optical-channel/state/output-power/min is not expected to be reported") + } + } + + opMax := samplestream.New(t, dut, gnmi.OC().Component(tr).OpticalChannel().OutputPower().Max().State(), samplingInterval) + defer opMax.Close() + if opMaxN := opMax.Next(); opMaxN != nil { + if _, ok := opMaxN.Val(); ok { + t.Fatalf("streaming /components/component/optical-channel/state/output-power/max is not expected to be reported") + } + } + + gnmi.Update(t, dut, gnmi.OC().Interface(dp.Name()).Enabled().Config(), bool(true)) + + powerStreamMap := map[string]*samplestream.SampleStream[float64]{ + "inst": opInst, + "avg": opAvg, + "min": opMin, + "max": opMax, + } + + validateOutputPower(t, powerStreamMap) + + // Derive transceiver names from ports. + component := gnmi.OC().Component(tr) + + outputPower := gnmi.Get(t, dut, component.OpticalChannel().TargetOutputPower().State()) + if math.Abs(float64(outputPower)-float64(targetOutputPowerdBm)) > targetOutputPowerTolerancedBm { + t.Fatalf("Output power is not within expected tolerance, got: %v want: %v tolerance: %v", outputPower, targetOutputPowerdBm, targetOutputPowerTolerancedBm) + } + + frequency := gnmi.Get(t, dut, component.OpticalChannel().Frequency().State()) + if math.Abs(float64(frequency)-float64(targetFrequencyHz)) > targetFrequencyToleranceHz { + t.Fatalf("Frequency is not within expected tolerance, got: %v want: %v tolerance: %v", frequency, targetFrequencyHz, targetFrequencyToleranceHz) + } + }) + } +} diff --git a/feature/platform/transceiver/tests/zr_pm_test/README.md b/feature/platform/transceiver/tests/zr_pm_test/README.md new file mode 100644 index 00000000000..fc3f96c7e05 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_pm_test/README.md @@ -0,0 +1,90 @@ +# TRANSCEIVER-6: Telemetry: 400ZR Optics performance metrics (pm) streaming. + +## Summary + +Validate 400ZR optics module reports performance metric (PM) data as defined in +module CMIS VDM(Versatile Diagnostics Monitor): +* eSNR is defined as the electrical Signal to Noise ratio at the decision sampling point in dB +* Q-value is the decibel (dB) value representing signal BER. +* pre-FEC BER bit error rate. + +## Procedure + +* Connect two ZR interfaces using a duplex LC fiber jumper such that TX + output power of one is the RX input power of the other module. + +* To establish a point to point ZR link ensure the following: + * Both transceivers state is enabled + * Both transceivers are set to a valid target TX output power + example -10 dBm. + * Both transceivers are tuned to a valid centre frequency + example 193.1 THz. + +* With the link ZR link established as explained above, verify that the + following ZR transceiver telemetry paths exist and are streamed for both + the ZR optics. + * /terminal-device/logical-channels/channel/otn/state/esnr/instant + * /terminal-device/logical-channels/channel/otn/state/esnr/avg + * /terminal-device/logical-channels/channel/otn/state/esnr/min + * /terminal-device/logical-channels/channel/otn/state/esnr/max + * /terminal-device/logical-channels/channel/otn/state/q-value/instant + * /terminal-device/logical-channels/channel/otn/state/q-value/avg + * /terminal-device/logical-channels/channel/otn/state/q-value/min + * /terminal-device/logical-channels/channel/otn/state/q-value/max + * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/instant + * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/avg + * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/min + * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/max + + +* For reported data check for validity min <= avg/instant <= max + +* When the modules or the devices are still in a boot stage, they must not + stream any invalid string values like "nil" or "-inf" until valid values + are available for streaming. + +* Q-value, eSNR and pre-Fec BER must always be of type decimal64. When link + interfaces are in down state 0.0 must be reported as a valid default value. + * Typical expected value range for eSNR is 13.5 to 18 dB +/-0.1 dB. + * Typical expected value for Pre-FEC BER should be less than 1.2E-2. + * Typical expected Q-value should be greater than 7 dB. + + +**Note:** For min, max, and avg values, 10 second sampling is preferred. If + 10 seconds is not supported, the sampling interval used must be + specified by adding a deviation to the test. + + +* Verify that the optics PM data is updated after the interface flaps. + + * Enable a pair of ZR interfaces on the DUT as explained above. + * Subscribe SAMPLE to the above PM leafs with a sample rate of 10 + seconds. + * Verify the ZR optics PMs are in the normal range. + * Use /components/component/transceiver/config/enabled to disable the + transceiver, wait 10 seconds and then re-enable the transceiver. + * Verify that the PM leafs report '0' during the reboot and no value + of nil or -inf is reported. + * Re-enable the interfaces on the DUT. + * Verify the ZR optics pre FEC PM is updated to the value in the normal + range again. + +## Config Parameter coverage + +* /components/component/transceiver/config/enabled + +## Telemetry Parameter coverage + + * /terminal-device/logical-channels/channel/otn/state/esnr/instant + * /terminal-device/logical-channels/channel/otn/state/esnr/avg + * /terminal-device/logical-channels/channel/otn/state/esnr/min + * /terminal-device/logical-channels/channel/otn/state/esnr/max + * /terminal-device/logical-channels/channel/otn/state/q-value/instant + * /terminal-device/logical-channels/channel/otn/state/q-value/avg + * /terminal-device/logical-channels/channel/otn/state/q-value/min + * /terminal-device/logical-channels/channel/otn/state/q-value/max + * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/instant + * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/avg + * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/min + * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/max +FFF - Fixed form factor \ No newline at end of file diff --git a/feature/platform/transceiver/tests/zr_pm_test/metadata.textproto b/feature/platform/transceiver/tests/zr_pm_test/metadata.textproto new file mode 100644 index 00000000000..91557f86ff6 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_pm_test/metadata.textproto @@ -0,0 +1,15 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "f5d5c225-8900-42fd-8771-8614895ae036" +plan_id: "TRANSCEIVER-6" +description: "Telemetry: 400ZR Optics performance metrics (pm) streaming." +testbed: TESTBED_DUT_400ZR +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + default_network_instance: "default" + } +} diff --git a/feature/platform/transceiver/tests/zr_pm_test/zr_pm_test.go b/feature/platform/transceiver/tests/zr_pm_test/zr_pm_test.go new file mode 100644 index 00000000000..f4d86199a35 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_pm_test/zr_pm_test.go @@ -0,0 +1,282 @@ +package zr_pm_test + +import ( + "testing" + "time" + + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/samplestream" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ygnmi/ygnmi" + "github.com/openconfig/ygot/ygot" +) + +const ( + dp16QAM = uint16(16) + samplingInterval = 10 * time.Second + minAllowedQValue = 7.0 + maxAllowedQValue = 14.0 + minAllowedPreFECBER = 1e-9 + maxAllowedPreFECBER = 1e-2 + minAllowedESNR = 10.0 + maxAllowedESNR = 25.0 + inactiveQValue = 0.0 + inactivePreFECBER = 0.0 + inactiveESNR = 0.0 + timeout = 10 * time.Minute + flapInterval = 30 * time.Second + otnIndexBase = uint32(4000) + ethernetIndexBase = uint32(40000) +) + +var ( + frequencies = []uint64{191400000, 196100000} + targetOpticalPowers = []float64{-6, -10} + + dutPorts = []attrs.Attributes{ + { + Desc: "dutPort1", + IPv4: "192.0.2.1", + IPv4Len: 30, + }, + { + Desc: "dutPort2", + IPv4: "192.0.2.5", + IPv4Len: 30, + }, + } +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +func TestPM(t *testing.T) { + dut := ondatra.DUT(t, "dut") + + fptest.ConfigureDefaultNetworkInstance(t, dut) + + var ( + trs = make(map[string]string) + ochs = make(map[string]string) + otnIndexes = make(map[string]uint32) + ethIndexes = make(map[string]uint32) + ) + + for i, p := range dut.Ports() { + // Check the port PMD is 400ZR. + if p.PMD() != ondatra.PMD400GBASEZR { + t.Fatalf("%s PMD is %v, not 400ZR", p.Name(), p.PMD()) + } + + // Configure interfaces. + gnmi.Replace(t, dut, gnmi.OC().Interface(p.Name()).Config(), dutPorts[i].NewOCInterface(p.Name(), dut)) + + // Get transceiver and optical channel. + trs[p.Name()] = gnmi.Get(t, dut, gnmi.OC().Interface(p.Name()).Transceiver().State()) + ochs[p.Name()] = gnmi.Get(t, dut, gnmi.OC().Component(trs[p.Name()]).Transceiver().Channel(0).AssociatedOpticalChannel().State()) + + // Assign OTN and ethernet indexes. + otnIndexes[p.Name()] = otnIndexBase + uint32(i) + ethIndexes[p.Name()] = ethernetIndexBase + uint32(i) + } + + for _, frequency := range frequencies { + for _, targetOpticalPower := range targetOpticalPowers { + // Configure OCH component and OTN and ETH logical channels. + for _, p := range dut.Ports() { + configOpticalChannel(t, dut, ochs[p.Name()], frequency, targetOpticalPower) + configOTNChannel(t, dut, ochs[p.Name()], otnIndexes[p.Name()]) + configETHChannel(t, dut, otnIndexes[p.Name()], ethIndexes[p.Name()]) + } + + // Create OTN channel sample steam for each port. + otnStreams := make(map[string]*samplestream.SampleStream[*oc.TerminalDevice_Channel]) + for portName, otnIndex := range otnIndexes { + otnStreams[portName] = samplestream.New(t, dut, gnmi.OC().TerminalDevice().Channel(otnIndex).State(), samplingInterval) + defer otnStreams[portName].Close() + } + + // Enable transceivers. + for _, tr := range trs { + gnmi.Replace(t, dut, gnmi.OC().Component(tr).Transceiver().Enabled().Config(), true) + } + + // Wait for streaming telemetry to report the channels as up. + for _, otnIndex := range otnIndexes { + gnmi.Await(t, dut, gnmi.OC().TerminalDevice().Channel(otnIndex).LinkState().State(), timeout, oc.Channel_LinkState_UP) + } + time.Sleep(samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. + + // Disable transceivers. + for _, tr := range trs { + gnmi.Replace(t, dut, gnmi.OC().Component(tr).Transceiver().Enabled().Config(), false) + } + + // Wait for streaming telemetry to report the channels as down. + for _, otnIndex := range otnIndexes { + gnmi.Await(t, dut, gnmi.OC().TerminalDevice().Channel(otnIndex).LinkState().State(), timeout, oc.Channel_LinkState_DOWN) + } + time.Sleep(samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. + + // Re-enable transceivers. + for _, tr := range trs { + gnmi.Replace(t, dut, gnmi.OC().Component(tr).Transceiver().Enabled().Config(), true) + } + + // Wait for streaming telemetry to report the channels as up. + for _, otnIndex := range otnIndexes { + gnmi.Await(t, dut, gnmi.OC().TerminalDevice().Channel(otnIndex).LinkState().State(), timeout, oc.Channel_LinkState_UP) + } + time.Sleep(samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. + + // Now validate OTN streams didn't return any invalid values. + for portName, stream := range otnStreams { + var linkStates []oc.E_Channel_LinkState + for _, val := range stream.All() { + linkStates = append(linkStates, validateStream(t, val, portName)) + } + validateLinkStateTransitions(t, linkStates, portName) + } + } + } +} + +// validateStream validates the stream data. +func validateStream(t *testing.T, data *ygnmi.Value[*oc.TerminalDevice_Channel], portName string) oc.E_Channel_LinkState { + if data == nil { + t.Errorf("Data not received for port %v.", portName) + return oc.Channel_LinkState_UNSET + } + v, ok := data.Val() + if !ok { + t.Errorf("Channel data is empty for port %v.", portName) + return oc.Channel_LinkState_UNSET + } + linkState := v.GetLinkState() + if linkState == oc.Channel_LinkState_UNSET { + t.Errorf("Link state data is empty for port %v", portName) + return oc.Channel_LinkState_UNSET + } + otn := v.GetOtn() + if otn == nil { + t.Errorf("OTN data is empty for port %v", portName) + return linkState + } + if b := otn.GetPreFecBer(); b == nil { + t.Errorf("PreFECBER data is empty for port %v", portName) + } else { + validatePMValue(t, portName, "PreFECBER", b.GetMin(), b.GetMax(), b.GetAvg(), b.GetInstant(), minAllowedPreFECBER, maxAllowedPreFECBER, inactivePreFECBER, linkState) + } + if e := otn.GetEsnr(); e == nil { + t.Errorf("ESNR data is empty for port %v", portName) + } else { + validatePMValue(t, portName, "esnr", e.GetMin(), e.GetMax(), e.GetAvg(), e.GetInstant(), minAllowedESNR, maxAllowedESNR, inactiveESNR, linkState) + } + if q := otn.GetQValue(); q == nil { + t.Errorf("QValue data is empty for port %v", portName) + } else { + validatePMValue(t, portName, "QValue", q.GetMin(), q.GetMax(), q.GetAvg(), q.GetInstant(), minAllowedQValue, maxAllowedQValue, inactiveQValue, linkState) + } + return linkState +} + +// validatePMValue validates the pm value. +func validatePMValue(t *testing.T, portName, pm string, instant, min, max, avg, minAllowed, maxAllowed, inactiveValue float64, linkState oc.E_Channel_LinkState) { + switch linkState { + case oc.Channel_LinkState_UP: + if instant < min || instant > max || avg < min || avg > max || min < minAllowed || max > maxAllowed { + t.Errorf("Invalid %v sample when %v is UP --> min : %v, max : %v, avg : %v, instant : %v", pm, portName, min, max, avg, instant) + return + } + case oc.Channel_LinkState_DOWN: + if instant != inactiveValue || avg != inactiveValue || min != inactiveValue || max != inactiveValue { + t.Errorf("Invalid %v sample when %v is DOWN --> min : %v, max : %v, avg : %v, instant : %v", pm, portName, min, max, avg, instant) + return + } + } + t.Logf("Valid %v sample when %v is %v --> min : %v, max : %v, avg : %v, instant : %v", pm, portName, linkState, min, max, avg, instant) +} + +// validateLinkStateTransitions validates the link state transitions. +func validateLinkStateTransitions(t *testing.T, linkStates []oc.E_Channel_LinkState, portName string) { + if len(linkStates) < 3 { + t.Errorf("Invalid %v link state transitions: want at least 3 samples, got %v", portName, len(linkStates)) + return + } + if linkStates[0] != oc.Channel_LinkState_DOWN { + t.Errorf("Invalid %v link state transitions: want DOWN for initial link state, got %v ", portName, linkStates[0]) + return + } + var transitionIndexes []int + for i := range linkStates { + if i == 0 { + continue + } + if linkStates[i-1] != linkStates[i] { + transitionIndexes = append(transitionIndexes, i) + } + } + if len(transitionIndexes) != 2 { + t.Errorf("Invalid %v link state transitions: want 2 transitions, got %v ", portName, len(transitionIndexes)) + return + } + if linkStates[transitionIndexes[0]] != oc.Channel_LinkState_UP { + t.Errorf("Invalid %v link state transitions: want DOWN-->UP, got %v-->%v", portName, linkStates[transitionIndexes[0]-1], linkStates[transitionIndexes[0]]) + return + } + if linkStates[transitionIndexes[1]] != oc.Channel_LinkState_DOWN { + t.Errorf("Invalid %v link state transitions: want UP-->DOWN, got %v-->%v", portName, linkStates[transitionIndexes[1]-1], linkStates[transitionIndexes[1]]) + } +} + +// configOpticalChannel configures the optical channel. +func configOpticalChannel(t *testing.T, dut *ondatra.DUTDevice, och string, frequency uint64, targetOpticalPower float64) { + gnmi.Replace(t, dut, gnmi.OC().Component(och).OpticalChannel().Config(), &oc.Component_OpticalChannel{ + Frequency: ygot.Uint64(frequency), + TargetOutputPower: ygot.Float64(targetOpticalPower), + }) +} + +// configOTNChannel configures the OTN channel. +func configOTNChannel(t *testing.T, dut *ondatra.DUTDevice, och string, otnIndex uint32) { + gnmi.Replace(t, dut, gnmi.OC().TerminalDevice().Channel(otnIndex).Config(), &oc.TerminalDevice_Channel{ + LogicalChannelType: oc.TransportTypes_LOGICAL_ELEMENT_PROTOCOL_TYPE_PROT_OTN, + AdminState: oc.TerminalDevice_AdminStateType_ENABLED, + Description: ygot.String("OTN Logical Channel"), + Index: ygot.Uint32(otnIndex), + Assignment: map[uint32]*oc.TerminalDevice_Channel_Assignment{ + 1: { + Index: ygot.Uint32(1), + OpticalChannel: ygot.String(och), + Description: ygot.String("OTN to Optical"), + Allocation: ygot.Float64(400), + AssignmentType: oc.Assignment_AssignmentType_OPTICAL_CHANNEL, + }, + }, + }) +} + +// configETHChannel configures the ETH channel. +func configETHChannel(t *testing.T, dut *ondatra.DUTDevice, otnIndex, ethIndex uint32) { + gnmi.Replace(t, dut, gnmi.OC().TerminalDevice().Channel(ethIndex).Config(), &oc.TerminalDevice_Channel{ + LogicalChannelType: oc.TransportTypes_LOGICAL_ELEMENT_PROTOCOL_TYPE_PROT_ETHERNET, + AdminState: oc.TerminalDevice_AdminStateType_ENABLED, + Description: ygot.String("ETH Logical Channel"), + Index: ygot.Uint32(ethIndex), + RateClass: oc.TransportTypes_TRIBUTARY_RATE_CLASS_TYPE_TRIB_RATE_400G, + TribProtocol: oc.TransportTypes_TRIBUTARY_PROTOCOL_TYPE_PROT_400GE, + Assignment: map[uint32]*oc.TerminalDevice_Channel_Assignment{ + 1: { + Index: ygot.Uint32(1), + LogicalChannel: ygot.Uint32(otnIndex), + Description: ygot.String("ETH to OTN"), + Allocation: ygot.Float64(400), + AssignmentType: oc.Assignment_AssignmentType_LOGICAL_CHANNEL, + }, + }, + }) +} diff --git a/feature/platform/transceiver/zr_supply_voltage_test/README.md b/feature/platform/transceiver/tests/zr_supply_voltage_test/README.md similarity index 100% rename from feature/platform/transceiver/zr_supply_voltage_test/README.md rename to feature/platform/transceiver/tests/zr_supply_voltage_test/README.md diff --git a/feature/platform/transceiver/tests/zr_supply_voltage_test/metadata.textproto b/feature/platform/transceiver/tests/zr_supply_voltage_test/metadata.textproto new file mode 100644 index 00000000000..c645ec9f682 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_supply_voltage_test/metadata.textproto @@ -0,0 +1,7 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "7e0251e4-8c5c-4dff-9683-8c21c817816c" +plan_id: "TRANSCEIVER-12" +description: "Telemetry: 400ZR Transceiver Supply Voltage streaming." +testbed: TESTBED_DUT_400ZR diff --git a/feature/platform/transceiver/tests/zr_supply_voltage_test/zr_supply_voltage_test.go b/feature/platform/transceiver/tests/zr_supply_voltage_test/zr_supply_voltage_test.go new file mode 100644 index 00000000000..39497170da3 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_supply_voltage_test/zr_supply_voltage_test.go @@ -0,0 +1,125 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package zr_supply_voltage_test + +import ( + "fmt" + "reflect" + "testing" + "time" + + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/samplestream" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" +) + +const ( + samplingInterval = 10 * time.Second + targetOutputPowerdBm = -10 + targetFrequencyHz = 193100000 + intUpdateTime = 2 * time.Minute +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +func verifyVoltageValue(t *testing.T, pStream *samplestream.SampleStream[float64], path string) float64 { + voltageSample := pStream.Next() + if voltageSample == nil { + t.Fatalf("Voltage telemetry %s was not streamed in the most recent subscription interval", path) + } + voltageVal, ok := voltageSample.Val() + if !ok { + t.Fatalf("Voltage %q telemetry is not present", voltageSample) + } + // Check voltage return value of correct type + if reflect.TypeOf(voltageVal).Kind() != reflect.Float64 { + t.Fatalf("Return value is not type float64") + } + t.Logf("Voltage sample value %s: %v", path, voltageVal) + return voltageVal +} + +func TestZrSupplyVoltage(t *testing.T) { + dut := ondatra.DUT(t, "dut") + + for _, port := range []string{"port1", "port2"} { + t.Run(fmt.Sprintf("Port:%s", port), func(t *testing.T) { + dp := dut.Port(t, port) + + gnmi.Await(t, dut, gnmi.OC().Interface(dp.Name()).OperStatus().State(), intUpdateTime, oc.Interface_OperStatus_UP) + + // Derive transceiver names from ports. + tr := gnmi.Get(t, dut, gnmi.OC().Interface(dp.Name()).Transceiver().State()) + component := gnmi.OC().Component(tr) + + outputPower := gnmi.Get(t, dut, component.OpticalChannel().TargetOutputPower().State()) + if outputPower != targetOutputPowerdBm { + t.Fatalf("Output power does not match target output power, got: %v want :%v", outputPower, targetOutputPowerdBm) + } + + frequency := gnmi.Get(t, dut, component.OpticalChannel().Frequency().State()) + if frequency != targetFrequencyHz { + t.Fatalf("Frequency does not match target frequency, got: %v want :%v", frequency, targetFrequencyHz) + } + + streamInst := samplestream.New(t, dut, component.Transceiver().SupplyVoltage().Instant().State(), samplingInterval) + defer streamInst.Close() + streamAvg := samplestream.New(t, dut, component.Transceiver().SupplyVoltage().Avg().State(), samplingInterval) + defer streamAvg.Close() + streamMin := samplestream.New(t, dut, component.Transceiver().SupplyVoltage().Min().State(), samplingInterval) + defer streamMin.Close() + streamMax := samplestream.New(t, dut, component.Transceiver().SupplyVoltage().Max().State(), samplingInterval) + defer streamMax.Close() + + volInst := verifyVoltageValue(t, streamInst, "Instant") + t.Logf("Port %s instant voltage: %v", dp.Name(), volInst) + volAvg := verifyVoltageValue(t, streamAvg, "Avg") + t.Logf("Port %s average voltage: %v", dp.Name(), volAvg) + volMin := verifyVoltageValue(t, streamMin, "Min") + t.Logf("Port %s minimum voltage: %v", dp.Name(), volMin) + volMax := verifyVoltageValue(t, streamMax, "Max") + t.Logf("Port %s maximum voltage: %v", dp.Name(), volMax) + + if volAvg >= volMin && volAvg <= volMax { + t.Logf("The average is between the maximum and minimum values") + } else { + t.Fatalf("The average is not between the maximum and minimum values, Avg:%v Max:%v Min:%v", volAvg, volMax, volMin) + } + + // Wait for the cooling off period + gnmi.Await(t, dut, gnmi.OC().Interface(dp.Name()).OperStatus().State(), intUpdateTime, oc.Interface_OperStatus_DOWN) + + volInstNew := verifyVoltageValue(t, streamInst, "Instant") + t.Logf("Port %s instant voltage after port down: %v", dp.Name(), volInstNew) + volAvgNew := verifyVoltageValue(t, streamAvg, "Avg") + t.Logf("Port %s average voltage after port down: %v", dp.Name(), volAvgNew) + volMinNew := verifyVoltageValue(t, streamMin, "Min") + t.Logf("Port %s minimum voltage after port down: %v", dp.Name(), volMinNew) + volMaxNew := verifyVoltageValue(t, streamMax, "Max") + t.Logf("Port %s maximum voltage after port down: %v", dp.Name(), volMaxNew) + + if volAvgNew >= volMinNew && volAvgNew <= volMaxNew { + t.Logf("The average voltage after port down is between the maximum and minimum values") + } else { + t.Fatalf("The average voltage after port down is not between the maximum and minimum values, Avg:%v Max:%v Min:%v", volAvgNew, volMaxNew, volMinNew) + } + }) + } + +} diff --git a/feature/platform/transceiver/otg_tests/zr_temperature_test/README.md b/feature/platform/transceiver/tests/zr_temperature_test/README.md similarity index 100% rename from feature/platform/transceiver/otg_tests/zr_temperature_test/README.md rename to feature/platform/transceiver/tests/zr_temperature_test/README.md diff --git a/feature/platform/transceiver/otg_tests/zr_temperature_test/metadata.textproto b/feature/platform/transceiver/tests/zr_temperature_test/metadata.textproto similarity index 100% rename from feature/platform/transceiver/otg_tests/zr_temperature_test/metadata.textproto rename to feature/platform/transceiver/tests/zr_temperature_test/metadata.textproto diff --git a/feature/platform/transceiver/otg_tests/zr_temperature_test/zr_temperature_test.go b/feature/platform/transceiver/tests/zr_temperature_test/zr_temperature_test.go similarity index 88% rename from feature/platform/transceiver/otg_tests/zr_temperature_test/zr_temperature_test.go rename to feature/platform/transceiver/tests/zr_temperature_test/zr_temperature_test.go index f9e85189ef8..a3850f1cb7b 100644 --- a/feature/platform/transceiver/otg_tests/zr_temperature_test/zr_temperature_test.go +++ b/feature/platform/transceiver/tests/zr_temperature_test/zr_temperature_test.go @@ -56,8 +56,8 @@ func interfaceConfig(t *testing.T, dut1 *ondatra.DUTDevice, dp *ondatra.Port) { Frequency: ygot.Uint64(frequency), }) } -func verifyTemperatureSensorValue(t *testing.T, dut1 *ondatra.DUTDevice, pStream *samplestream.SampleStream[float64], sensorName string) float64 { - temperatureSample := pStream.Next(t) +func verifyTemperatureSensorValue(t *testing.T, pStream *samplestream.SampleStream[float64], sensorName string) float64 { + temperatureSample := pStream.Next() if temperatureSample == nil { t.Fatalf("Temperature telemetry %s was not streamed in the most recent subscription interval", sensorName) } @@ -108,13 +108,13 @@ func TestZRTemperatureState(t *testing.T) { p1StreamAvg := samplestream.New(t, dut1, component1.Temperature().Avg().State(), 10*time.Second) p1StreamMin := samplestream.New(t, dut1, component1.Temperature().Min().State(), 10*time.Second) p1StreamMax := samplestream.New(t, dut1, component1.Temperature().Max().State(), 10*time.Second) - temperatureInstant := verifyTemperatureSensorValue(t, dut1, p1StreamInstant, "Instant") + temperatureInstant := verifyTemperatureSensorValue(t, p1StreamInstant, "Instant") t.Logf("Port1 dut1 %s Instant Temperature: %v", dp1.Name(), temperatureInstant) - temperatureMax := verifyTemperatureSensorValue(t, dut1, p1StreamMax, "Max") + temperatureMax := verifyTemperatureSensorValue(t, p1StreamMax, "Max") t.Logf("Port1 dut1 %s Max Temperature: %v", dp1.Name(), temperatureMax) - temperatureMin := verifyTemperatureSensorValue(t, dut1, p1StreamMin, "Min") + temperatureMin := verifyTemperatureSensorValue(t, p1StreamMin, "Min") t.Logf("Port1 dut1 %s Min Temperature: %v", dp1.Name(), temperatureMin) - temperatureAvg := verifyTemperatureSensorValue(t, dut1, p1StreamAvg, "Avg") + temperatureAvg := verifyTemperatureSensorValue(t, p1StreamAvg, "Avg") t.Logf("Port1 dut1 %s Avg Temperature: %v", dp1.Name(), temperatureAvg) if temperatureAvg >= temperatureMin && temperatureAvg <= temperatureMax { t.Logf("The average is between the maximum and minimum values") @@ -166,13 +166,13 @@ func TestZRTemperatureStateInterfaceFlap(t *testing.T) { p1StreamMax := samplestream.New(t, dut1, component1.Temperature().Max().State(), 10*time.Second) // Wait 120 sec cooling off period gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), intUpdateTime, oc.Interface_OperStatus_DOWN) - temperatureInstant := verifyTemperatureSensorValue(t, dut1, p1StreamInstant, "Instant") + temperatureInstant := verifyTemperatureSensorValue(t, p1StreamInstant, "Instant") t.Logf("Port1 dut1 %s Instant Temperature: %v", dp1.Name(), temperatureInstant) - temperatureMax := verifyTemperatureSensorValue(t, dut1, p1StreamMax, "Max") + temperatureMax := verifyTemperatureSensorValue(t, p1StreamMax, "Max") t.Logf("Port1 dut1 %s Max Temperature: %v", dp1.Name(), temperatureMax) - temperatureMin := verifyTemperatureSensorValue(t, dut1, p1StreamMin, "Min") + temperatureMin := verifyTemperatureSensorValue(t, p1StreamMin, "Min") t.Logf("Port1 dut1 %s Min Temperature: %v", dp1.Name(), temperatureMin) - temperatureAvg := verifyTemperatureSensorValue(t, dut1, p1StreamAvg, "Avg") + temperatureAvg := verifyTemperatureSensorValue(t, p1StreamAvg, "Avg") t.Logf("Port1 dut1 %s Avg Temperature: %v", dp1.Name(), temperatureAvg) i = d.GetOrCreateInterface(dp1.Name()) i.Enabled = ygot.Bool(true) @@ -180,13 +180,13 @@ func TestZRTemperatureStateInterfaceFlap(t *testing.T) { // Enable interface gnmi.Replace(t, dut1, gnmi.OC().Interface(dp1.Name()).Config(), i) gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), intUpdateTime, oc.Interface_OperStatus_UP) - temperatureInstant = verifyTemperatureSensorValue(t, dut1, p1StreamInstant, "Instant") + temperatureInstant = verifyTemperatureSensorValue(t, p1StreamInstant, "Instant") t.Logf("Port1 dut1 %s Instant Temperature: %v", dp1.Name(), temperatureInstant) - temperatureMax = verifyTemperatureSensorValue(t, dut1, p1StreamMax, "Max") + temperatureMax = verifyTemperatureSensorValue(t, p1StreamMax, "Max") t.Logf("Port1 dut1 %s Max Temperature: %v", dp1.Name(), temperatureMax) - temperatureMin = verifyTemperatureSensorValue(t, dut1, p1StreamMin, "Min") + temperatureMin = verifyTemperatureSensorValue(t, p1StreamMin, "Min") t.Logf("Port1 dut1 %s Min Temperature: %v", dp1.Name(), temperatureMin) - temperatureAvg = verifyTemperatureSensorValue(t, dut1, p1StreamAvg, "Avg") + temperatureAvg = verifyTemperatureSensorValue(t, p1StreamAvg, "Avg") t.Logf("Port1 dut1 %s Avg Temperature: %v", dp1.Name(), temperatureAvg) if temperatureAvg >= temperatureMin && temperatureAvg <= temperatureMax { t.Logf("The average is between the maximum and minimum values") diff --git a/feature/platform/transceiver/zr_tunable_parameters_test/README.md b/feature/platform/transceiver/tests/zr_tunable_parameters_test/README.md similarity index 100% rename from feature/platform/transceiver/zr_tunable_parameters_test/README.md rename to feature/platform/transceiver/tests/zr_tunable_parameters_test/README.md diff --git a/feature/platform/transceiver/tests/zr_tunable_parameters_test/metadata.textproto b/feature/platform/transceiver/tests/zr_tunable_parameters_test/metadata.textproto new file mode 100644 index 00000000000..5bd64161c63 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_tunable_parameters_test/metadata.textproto @@ -0,0 +1,16 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata +uuid: "47c623ec-69b3-4bca-ae07-012feff496b8" +plan_id: "TRANSCEIVER-5" +description: "Configuration: 400ZR channel frequency, output TX launch power and operational mode setting." +testbed: TESTBED_DUT_400ZR +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + interface_enabled: true + default_network_instance: "default" + missing_zr_optical_channel_tunable_parameters_telemetry: true + } +} diff --git a/feature/platform/transceiver/tests/zr_tunable_parameters_test/zr_tunable_parameters_test.go b/feature/platform/transceiver/tests/zr_tunable_parameters_test/zr_tunable_parameters_test.go new file mode 100644 index 00000000000..04c40b069f1 --- /dev/null +++ b/feature/platform/transceiver/tests/zr_tunable_parameters_test/zr_tunable_parameters_test.go @@ -0,0 +1,288 @@ +package zr_tunable_parameters_test + +import ( + "fmt" + "testing" + "time" + + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/samplestream" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ygot/ygot" +) + +const ( + samplingInterval = 10 * time.Second + frequencyTolerance = 1800 + dp16QAM = 1 +) + +var ( + dutPort1 = attrs.Attributes{ + Desc: "dutPort1", + IPv4: "192.0.2.1", + IPv4Len: 30, + } + dutPort2 = attrs.Attributes{ + Desc: "dutPort2", + IPv4: "192.0.2.5", + IPv4Len: 30, + } +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} +func Test400ZRTunableFrequency(t *testing.T) { + dut := ondatra.DUT(t, "dut") + p1 := dut.Port(t, "port1") + p2 := dut.Port(t, "port2") + fptest.ConfigureDefaultNetworkInstance(t, dut) + gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), dut)) + gnmi.Replace(t, dut, gnmi.OC().Interface(p2.Name()).Config(), dutPort2.NewOCInterface(p2.Name(), dut)) + oc1 := opticalChannelFromPort(t, dut, p1) + oc2 := opticalChannelFromPort(t, dut, p2) + streamOC1 := samplestream.New(t, dut, gnmi.OC().Component(oc1).State(), 10*time.Second) + defer streamOC1.Close() + streamOC2 := samplestream.New(t, dut, gnmi.OC().Component(oc2).State(), 10*time.Second) + defer streamOC2.Close() + tests := []struct { + description string + startFreq uint64 + endFreq uint64 + freqStep uint64 + targetOutputPower float64 + }{ + { + // Validate setting 400ZR optics module tunable laser center frequency + // across frequency range 196.100 - 191.400 THz for 100GHz grid. + description: "100GHz grid", + startFreq: 191400000, + endFreq: 196100000, + freqStep: 100000 * 4, + targetOutputPower: -13, + }, + { + // Validate setting 400ZR optics module tunable laser center frequency + // across frequency range 196.100 - 191.375 THz for 75GHz grid. + description: "75GHz grid", + startFreq: 191375000, + endFreq: 196100000, + freqStep: 75000 * 6, + targetOutputPower: -9, + }, + } + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + for freq := tc.startFreq; freq <= tc.endFreq; freq += tc.freqStep { + t.Run(fmt.Sprintf("Freq: %v", freq), func(t *testing.T) { + gnmi.Replace(t, dut, gnmi.OC().Component(oc1).OpticalChannel().Config(), &oc.Component_OpticalChannel{ + TargetOutputPower: ygot.Float64(tc.targetOutputPower), + Frequency: ygot.Uint64(freq), + OperationalMode: ygot.Uint16(dp16QAM), + }) + gnmi.Replace(t, dut, gnmi.OC().Component(oc2).OpticalChannel().Config(), &oc.Component_OpticalChannel{ + TargetOutputPower: ygot.Float64(tc.targetOutputPower), + Frequency: ygot.Uint64(freq), + OperationalMode: ygot.Uint16(dp16QAM), + }) + gnmi.Await(t, dut, gnmi.OC().Interface(p1.Name()).OperStatus().State(), time.Minute, oc.Interface_OperStatus_UP) + gnmi.Await(t, dut, gnmi.OC().Interface(p2.Name()).OperStatus().State(), time.Minute, oc.Interface_OperStatus_UP) + validateOpticsTelemetry(t, []*samplestream.SampleStream[*oc.Component]{streamOC1, streamOC2}, freq, tc.targetOutputPower) + }) + } + }) + } +} +func Test400ZRTunableOutputPower(t *testing.T) { + dut := ondatra.DUT(t, "dut") + p1 := dut.Port(t, "port1") + p2 := dut.Port(t, "port2") + fptest.ConfigureDefaultNetworkInstance(t, dut) + gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), dut)) + gnmi.Replace(t, dut, gnmi.OC().Interface(p2.Name()).Config(), dutPort2.NewOCInterface(p2.Name(), dut)) + oc1 := opticalChannelFromPort(t, dut, p1) + oc2 := opticalChannelFromPort(t, dut, p2) + streamOC1 := samplestream.New(t, dut, gnmi.OC().Component(oc1).State(), 10*time.Second) + defer streamOC1.Close() + streamOC2 := samplestream.New(t, dut, gnmi.OC().Component(oc2).State(), 10*time.Second) + defer streamOC2.Close() + tests := []struct { + description string + frequency uint64 + startTargetOutputPower float64 + endTargetOutputPower float64 + targetOutputPowerStep float64 + }{ + { + // Validate adjustable range of transmit output power across -13 to -9 dBm + // range in steps of 1dB. So the module’s output power will be set to -13, + // -12, -11, -10, -9 dBm in each step. + description: "adjustable range of transmit output power across -13 to -9 dBm range in steps of 1dB", + frequency: 193100000, + startTargetOutputPower: -13, + endTargetOutputPower: -9, + targetOutputPowerStep: 1, + }, + } + for _, tc := range tests { + for top := tc.startTargetOutputPower; top <= tc.endTargetOutputPower; top += tc.targetOutputPowerStep { + t.Run(fmt.Sprintf("Target Power: %v", top), func(t *testing.T) { + gnmi.Replace(t, dut, gnmi.OC().Component(oc1).OpticalChannel().Config(), &oc.Component_OpticalChannel{ + TargetOutputPower: ygot.Float64(top), + Frequency: ygot.Uint64(tc.frequency), + OperationalMode: ygot.Uint16(dp16QAM), + }) + gnmi.Replace(t, dut, gnmi.OC().Component(oc2).OpticalChannel().Config(), &oc.Component_OpticalChannel{ + TargetOutputPower: ygot.Float64(top), + Frequency: ygot.Uint64(tc.frequency), + OperationalMode: ygot.Uint16(dp16QAM), + }) + gnmi.Await(t, dut, gnmi.OC().Interface(p1.Name()).OperStatus().State(), time.Minute, oc.Interface_OperStatus_UP) + gnmi.Await(t, dut, gnmi.OC().Interface(p2.Name()).OperStatus().State(), time.Minute, oc.Interface_OperStatus_UP) + validateOpticsTelemetry(t, []*samplestream.SampleStream[*oc.Component]{streamOC1, streamOC2}, tc.frequency, top) + }) + } + } +} +func Test400ZRInterfaceFlap(t *testing.T) { + dut := ondatra.DUT(t, "dut") + p1 := dut.Port(t, "port1") + p2 := dut.Port(t, "port2") + fptest.ConfigureDefaultNetworkInstance(t, dut) + gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), dut)) + gnmi.Replace(t, dut, gnmi.OC().Interface(p2.Name()).Config(), dutPort2.NewOCInterface(p2.Name(), dut)) + oc1 := opticalChannelFromPort(t, dut, p1) + oc2 := opticalChannelFromPort(t, dut, p2) + streamOC1 := samplestream.New(t, dut, gnmi.OC().Component(oc1).State(), 10*time.Second) + defer streamOC1.Close() + streamOC2 := samplestream.New(t, dut, gnmi.OC().Component(oc2).State(), 10*time.Second) + defer streamOC2.Close() + targetPower := float64(-9) + frequency := uint64(193100000) + gnmi.Replace(t, dut, gnmi.OC().Component(oc1).OpticalChannel().Config(), &oc.Component_OpticalChannel{ + TargetOutputPower: ygot.Float64(targetPower), + Frequency: ygot.Uint64(frequency), + OperationalMode: ygot.Uint16(dp16QAM), + }) + gnmi.Replace(t, dut, gnmi.OC().Component(oc2).OpticalChannel().Config(), &oc.Component_OpticalChannel{ + TargetOutputPower: ygot.Float64(targetPower), + Frequency: ygot.Uint64(frequency), + OperationalMode: ygot.Uint16(dp16QAM), + }) + gnmi.Await(t, dut, gnmi.OC().Interface(p1.Name()).OperStatus().State(), time.Minute, oc.Interface_OperStatus_UP) + gnmi.Await(t, dut, gnmi.OC().Interface(p2.Name()).OperStatus().State(), time.Minute, oc.Interface_OperStatus_UP) + t.Run("Telemetry before flap", func(t *testing.T) { + validateOpticsTelemetry(t, []*samplestream.SampleStream[*oc.Component]{streamOC1, streamOC2}, frequency, targetPower) + }) + // Disable or shut down the interface on the DUT. + gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Enabled().Config(), false) + gnmi.Replace(t, dut, gnmi.OC().Interface(p2.Name()).Enabled().Config(), false) + // Verify with interfaces in down state both optics are streaming uint 0 + // value for frequency. + // Verify for the TX output power with interface in down state a decimal64 + // value of -40 dB is streamed. + t.Run("Telemetry during interface disabled", func(t *testing.T) { + validateOpticsTelemetry(t, []*samplestream.SampleStream[*oc.Component]{streamOC1, streamOC2}, 0, -40) + }) + // Re-enable the interfaces on the DUT. + gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Enabled().Config(), true) + gnmi.Replace(t, dut, gnmi.OC().Interface(p2.Name()).Enabled().Config(), true) + gnmi.Await(t, dut, gnmi.OC().Interface(p1.Name()).OperStatus().State(), time.Minute, oc.Interface_OperStatus_UP) + gnmi.Await(t, dut, gnmi.OC().Interface(p2.Name()).OperStatus().State(), time.Minute, oc.Interface_OperStatus_UP) + // Verify the ZR optics tune back to the correct frequency and TX output + // power as per the configuration and related telemetry values are updated + // to the value in the normal range again. + t.Run("Telemetry after flap", func(t *testing.T) { + validateOpticsTelemetry(t, []*samplestream.SampleStream[*oc.Component]{streamOC1, streamOC2}, frequency, targetPower) + }) +} +func validateOpticsTelemetry(t *testing.T, streams []*samplestream.SampleStream[*oc.Component], frequency uint64, outputPower float64) { + dut := ondatra.DUT(t, "dut") + var ocs []*oc.Component_OpticalChannel + for _, s := range streams { + val := s.Next() + if val == nil { + t.Fatal("Optical channel streaming telemetry not received") + } + v, ok := val.Val() + if !ok { + t.Fatal("Optical channel streaming telemetry empty") + } + ocs = append(ocs, v.GetOpticalChannel()) + } + + for _, oc := range ocs { + opm := oc.GetOperationalMode() + inst := oc.GetCarrierFrequencyOffset().GetInstant() + avg := oc.GetCarrierFrequencyOffset().GetAvg() + min := oc.GetCarrierFrequencyOffset().GetMin() + max := oc.GetCarrierFrequencyOffset().GetMax() + if got, want := opm, uint16(dp16QAM); got != want { + t.Errorf("Optical-Channel: operational-mode: got %v, want %v", got, want) + } + // Laser frequency offset should not be more than +/- 1.8 GHz max from the + // configured centre frequency. + if inst < -1*frequencyTolerance || inst > frequencyTolerance { + t.Errorf("Optical-Channel: carrier-frequency-offset not in tolerable range, got: %v, want: (+/-)%v", inst, frequencyTolerance) + } + if deviations.MissingZROpticalChannelTunableParametersTelemetry(dut) { + t.Log("Skipping Min/Max/Avg Tunable Parameters Telemetry validation. Deviation MissingZROpticalChannelTunableParametersTelemetry enabled.") + } else { + // For reported data check for validity: min <= avg/instant <= max + if min > inst { + t.Errorf("Optical-Channel: carrier-frequency-offset min: %v greater than carrier-frequency-offset instant: %v", min, inst) + } + if max < inst { + t.Errorf("Optical-Channel: carrier-frequency-offset max: %v less than carrier-frequency-offset instant: %v", max, inst) + } + if min > avg { + t.Errorf("Optical-Channel: carrier-frequency-offset min: %v greater than carrier-frequency-offset avg: %v", min, avg) + } + if max < avg { + t.Errorf("Optical-Channel: carrier-frequency-offset max: %v less than carrier-frequency-offset avg: %v", max, avg) + } + } + inst = oc.GetOutputPower().GetInstant() + avg = oc.GetOutputPower().GetAvg() + min = oc.GetOutputPower().GetMin() + max = oc.GetOutputPower().GetMax() + // When set to a specific target output power, transmit power control + // absolute accuracy should be within +/- 1 dBm of the target configured + // output power. + if inst < outputPower-1 || inst > outputPower+1 { + t.Errorf("Optical-Channel: output-power not in tolerable range, got: %v, want: %v", inst, outputPower) + } + if deviations.MissingZROpticalChannelTunableParametersTelemetry(dut) { + t.Log("Skipping Min/Max/Avg Tunable Parameters Telemetry validation. Deviation MissingZROpticalChannelTunableParametersTelemetry enabled.") + } else { + // For reported data check for validity: min <= avg/instant <= max + if min > inst { + t.Errorf("Optical-Channel: output-power min: %v greater than output-power instant: %v", min, inst) + } + if max < inst { + t.Errorf("Optical-Channel: output-power max: %v less than output-power instant: %v", max, inst) + } + if min > avg { + t.Errorf("Optical-Channel: output-power min: %v greater than output-power avg: %v", min, avg) + } + if max < avg { + t.Errorf("Optical-Channel: output-power max: %v less than output-power avg: %v", max, avg) + } + } + if got, want := oc.GetFrequency(), frequency; got != want { + t.Errorf("Optical-Channel: frequency: %v, want: %v", got, want) + } + } +} + +// opticalChannelFromPort returns the connected optical channel component name for a given ondatra port. +func opticalChannelFromPort(t *testing.T, dut *ondatra.DUTDevice, p *ondatra.Port) string { + t.Helper() + tr := gnmi.Get(t, dut, gnmi.OC().Interface(p.Name()).Transceiver().State()) + return gnmi.Get(t, dut, gnmi.OC().Component(tr).Transceiver().Channel(0).AssociatedOpticalChannel().State()) +} diff --git a/feature/platform/transceiver/zr_db_q_value_test/README.md b/feature/platform/transceiver/zr_db_q_value_test/README.md deleted file mode 100644 index b62da10dba8..00000000000 --- a/feature/platform/transceiver/zr_db_q_value_test/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# TRANSCEIVER-6: Telemetry: 400ZR Optics Q-value streaming. - -## Summary - -Validate 400ZR optics module reports Q-value performance data as defined in -module CMIS VDM(Versatile Diagnostics Monitor). -Q-value is the decibel (dB) value representing signal BER. - -## Procedure - -* Connect two ZR interfaces using a duplex LC fiber jumper such that TX - output power of one is the RX input power of the other module. - -* To establish a point to point ZR link ensure the following: - * Both transceivers state is enabled - * Both transceivers are set to a valid target TX output power - example -10 dBm. - * Both transceivers are tuned to a valid centre frequency - example 193.1 THz. - -* With the link ZR link established as explained above, verify that the - following ZR transceiver telemetry paths exist and are streamed for both - the ZR optics. - * /terminal-device/logical-channels/channel/otn/state/q-value/instant - * /terminal-device/logical-channels/channel/otn/state/q-value/avg - * /terminal-device/logical-channels/channel/otn/state/q-value/min - * /terminal-device/logical-channels/channel/otn/state/q-value/max - -* For reported data check for validity min <= avg/instant <= max - -* When the modules or the devices are still in a boot stage, they must not - stream any invalid string values like "nil" or "-inf" until valid values - are available for streaming. - -* Q-value must always be of type decimal64. When link interfaces are in down - state 0.0 must be reported as a valid default value. - - -**Note:** For min, max, and avg values, 10 second sampling is preferred. If - 10 seconds is not supported, the sampling interval used must be - specified by adding a deviation to the test. - - -* Verify that the optics Q-value is updated after the interface flaps. - - * Enable a pair of ZR interfaces on the DUT as explained above. - * Subscribe SAMPLE to the above q-value leafs with a sample rate of 10 - seconds. - * Verify the ZR optics Q-value PMs are in the normal range. - * Use /components/component/transceiver/config/enabled to disable the - transceiver, wait 20 seconds and then re-enable the transceiver. - * Verify that the q-value leafs report '0' during the reboot and no value - of nil or -inf is reported. - * Re-enable the interfaces on the DUT. - * Verify the ZR optics pre FEC PM is updated to the value in the normal - range again. Typical expected value should be greater than 7 dB. - -## Config Parameter coverage - -* /components/component/oc-transceiver:transceiver/oc-transceiver/config/enabled - -## Telemetry Parameter coverage - -* /terminal-device/logical-channels/channel/otn/state/q-value/instant -* /terminal-device/logical-channels/channel/otn/state/q-value/avg -* /terminal-device/logical-channels/channel/otn/state/q-value/min -* /terminal-device/logical-channels/channel/otn/state/q-value/max diff --git a/feature/platform/transceiver/zr_pre_fec_ber_test/README.md b/feature/platform/transceiver/zr_pre_fec_ber_test/README.md deleted file mode 100644 index 968b57ae70f..00000000000 --- a/feature/platform/transceiver/zr_pre_fec_ber_test/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# TRANSCEIVER-2: Telemetry: 400ZR Optics Pre-FEC(Forward Error Correction) BER(Bit Error Rate) - -## Summary - -Validate 400ZR optics module reports pre-FEC bit error rate performance data. - -## Procedure - -* Connect two ZR interfaces using a duplex LC fiber jumper such that TX - output power of one is the RX input power of the other module. -* To establish a point to point ZR link ensure the following: - * Both transceivers state is enabled - * Both transceivers are set to a valid target TX output power - example -10 dBm - * Both transceivers are tuned to a valid centre frequency - example 193.1 THz -* With the link ZR link established as explained above, verify that the - following ZR transceiver telemetry paths exist and are streamed for both - the ZR optics - * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/instant - * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/avg - * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/min - * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/max - -**Note:** For min, max, and avg values, 10 second sampling is preferred. If - 10 seconds is not supported, the sampling interval used must be - communicated. - - -* Verify that the optics pre-FEC BER is updated after the interface flaps. - - * Enable a pair of ZR interfaces on the DUT as explained above. - * Verify the ZR optics pre FEC BER PMs are in the normal range. - * Disable or shut down the interface on the DUT. - * Re-enable the interfaces on the DUT. - * Verify the ZR optics pre FEC PM is updated to the value in the normal - range again. Typical expected value should be less than 1.2E-2 - -## Config Parameter coverage - -* /components/component/oc-transceiver:transceiver/oc-transceiver/config/enabled - -## Telemetry Parameter coverage - - * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/instant - * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/avg - * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/min - * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/max diff --git a/feature/platform/transceiver/zr_temperature_test/README.md b/feature/platform/transceiver/zr_temperature_test/README.md deleted file mode 100644 index 57a4ab52bbb..00000000000 --- a/feature/platform/transceiver/zr_temperature_test/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# TRANSCEIVER-8: Telemetry: 400ZR Optics module temperature streaming. - -## Summary - -Validate 400ZR optics report module level internally measured temperature -in 1/256 degree Celsius increments as defined in the CMIS. - -Link to CMIS: -https://www.oiforum.com/wp-content/uploads/CMIS5p0_Third_Party_Spec.pdf - -## Procedure - -* Connect two ZR optics using a duplex LC fiber jumper such that TX - output power of one is the RX input power of the other module. -* To establish a point to point ZR link ensure the following: - * Both transceivers state is enabled. - * Both transceivers are set to a valid target TX output power - example -10 dBm. - * Both transceivers are tuned to a valid centre frequency - example 193.1 THz. -* With the ZR link established as explained above, verify that the - following ZR transceiver telemetry paths exist and are streamed for both - the ZR optics. - * /platform/components/component/state/temperature/instant - * /platform/components/component/state/temperature/min - * /platform/components/component/state/temperature/max - * /platform/components/component/state/temperature/avg -* For reported data check for validity min <= avg/instant <= max - -* If the modules or the devices are in a boot stage, they must not stream - any invalid string values like "nil" or "-inf". -* Reported temperature value must always be of type decimal64. - - -**Note:** For min, max, and avg values, 10 second sampling is preferred. If the - min, max average values or the 10 seconds sampling is not supported, - the sampling interval used must be specified and this must be - captured by adding a deviation to the test. - - -* Verify the module temperature is reported correctly with optics interface - in disabled state. - - * Use /interfaces/interface/config/enabled to disable the interfaces and - wait 120 seconds(cooling off period) before taking the temperature - reading again. - * Verify the module is able to stream the temperature data in this state. - * Verify the module reported temperature in this state is always less - than the module temperature captured during steady state operation with - interface state enabled. - * For reported data check for validity min <= avg/instant <= max - - * If the modules or the devices are in a boot stage, they must not stream - any invalid string values like "nil" or "-inf". - * Reported temperature value must always be of type decimal64. - -## Config Parameter coverage - -* /interfaces/interface/config/enabled - -## Telemetry Parameter coverage - -* /platform/components/component/state/temperature/instant -* /platform/components/component/state/temperature/min -* /platform/components/component/state/temperature/max -* /platform/components/component/state/temperature/avg \ No newline at end of file diff --git a/feature/qos/ecn/feature.textproto b/feature/qos/ecn/feature.textproto index a7840e719cb..b76984b3c87 100644 --- a/feature/qos/ecn/feature.textproto +++ b/feature/qos/ecn/feature.textproto @@ -1,3 +1,6 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile + id { name: "qos_ecn" version: 1 diff --git a/feature/qos/feature.textproto b/feature/qos/feature.textproto index 49b504206a7..4e439a3762d 100644 --- a/feature/qos/feature.textproto +++ b/feature/qos/feature.textproto @@ -1,3 +1,6 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile + id { name: "qos" version: 1 diff --git a/feature/qos/otg_tests/bursty_traffic_test/bursty_traffic_test.go b/feature/qos/otg_tests/bursty_traffic_test/bursty_traffic_test.go index ba98e083401..2e8e158c370 100644 --- a/feature/qos/otg_tests/bursty_traffic_test/bursty_traffic_test.go +++ b/feature/qos/otg_tests/bursty_traffic_test/bursty_traffic_test.go @@ -432,6 +432,7 @@ func TestBurstyTraffic(t *testing.T) { t.Logf("Running traffic 1 on DUT interfaces: %s => %s ", dp1.Name(), dp3.Name()) t.Logf("Running traffic 2 on DUT interfaces: %s => %s ", dp2.Name(), dp3.Name()) t.Logf("Sending traffic flows: \n%v\n\n", trafficFlows) + time.Sleep(30 * time.Second) ate.OTG().StartTraffic(t) time.Sleep(30 * time.Second) ate.OTG().StopTraffic(t) diff --git a/feature/qos/tests/qos_policy_config_test/qos_policy_config_test.go b/feature/qos/tests/qos_policy_config_test/qos_policy_config_test.go index b43b04be089..e035af3e562 100644 --- a/feature/qos/tests/qos_policy_config_test/qos_policy_config_test.go +++ b/feature/qos/tests/qos_policy_config_test/qos_policy_config_test.go @@ -601,8 +601,8 @@ func testECNConfig(t *testing.T) { ecnEnabled: true, dropEnabled: false, minThreshold: uint64(80000), - maxThreshold: math.MaxUint32, - maxDropProbabilityPercent: uint8(1), + maxThreshold: uint64(80001), + maxDropProbabilityPercent: uint8(100), weight: uint32(0), } @@ -620,9 +620,6 @@ func testECNConfig(t *testing.T) { t.Logf("qos ECN QueueManagementProfile config cases: %v", ecnConfig) gnmi.Replace(t, dut, gnmi.OC().Qos().Config(), q) - // TODO: Remove the following t.Skipf() after the config verification code has been tested. - t.Skipf("Skip the QoS config verification until it is tested against a DUT.") - // Verify the QueueManagementProfile is applied by checking the telemetry path state values. wredUniform := gnmi.OC().Qos().QueueManagementProfile("DropProfile").Wred().Uniform() if got, want := gnmi.Get(t, dut, wredUniform.EnableEcn().State()), ecnConfig.ecnEnabled; got != want { diff --git a/feature/security/gnsi/acctz/feature.textproto b/feature/security/gnsi/acctz/feature.textproto index 15e0ef02b2b..76c8f93c5c5 100644 --- a/feature/security/gnsi/acctz/feature.textproto +++ b/feature/security/gnsi/acctz/feature.textproto @@ -1,3 +1,6 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile + id { name: "security_gnsi_acctz" version: 0 diff --git a/feature/security/gnsi/authz/feature.textproto b/feature/security/gnsi/authz/feature.textproto index ee175c6ff74..183c61709fa 100644 --- a/feature/security/gnsi/authz/feature.textproto +++ b/feature/security/gnsi/authz/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "security_gnsi_authz" diff --git a/feature/security/gnsi/authz/tests/authz/authz1_4_test.go b/feature/security/gnsi/authz/tests/authz/authz1_4_test.go index 579a8c7710e..ec6f2008aa6 100644 --- a/feature/security/gnsi/authz/tests/authz/authz1_4_test.go +++ b/feature/security/gnsi/authz/tests/authz/authz1_4_test.go @@ -206,7 +206,7 @@ func TestAuthz1(t *testing.T) { // Pre-Test Section _, policyBefore := authz.Get(t, dut) t.Logf("Authz Policy of the Device %s before the Rotate Trigger is %s", dut.Name(), policyBefore.PrettyPrint(t)) - defer policyBefore.Rotate(t, dut, uint64(time.Now().UnixMilli()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) + defer policyBefore.Rotate(t, dut, uint64(time.Now().Unix()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) // Fetch the Desired Authorization Policy and Attach base Admin Policy Before Rotate newpolicy, ok := policyMap["policy-everyone-can-gnmi-not-gribi"] @@ -229,7 +229,7 @@ func TestAuthz1(t *testing.T) { // Pre-Test Section _, policyBefore := authz.Get(t, dut) t.Logf("Authz Policy of the Device %s before the Rotate Trigger is %s", dut.Name(), policyBefore.PrettyPrint(t)) - defer policyBefore.Rotate(t, dut, uint64(time.Now().UnixMilli()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) + defer policyBefore.Rotate(t, dut, uint64(time.Now().Unix()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) // Fetch the Desired Authorization Policy and Attach base Admin Policy Before Rotate newpolicy, ok := policyMap["policy-everyone-can-gribi-not-gnmi"] @@ -252,7 +252,7 @@ func TestAuthz1(t *testing.T) { dut := ondatra.DUT(t, "dut") _, policyBefore := authz.Get(t, dut) t.Logf("Authz Policy of the Device %s before the Rotate Trigger is %s", dut.Name(), policyBefore.PrettyPrint(t)) - defer policyBefore.Rotate(t, dut, uint64(time.Now().UnixMilli()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) + defer policyBefore.Rotate(t, dut, uint64(time.Now().Unix()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) // Fetch the Desired Authorization Policy and Attach base Admin Policy Before Rotate - 1 newpolicy, ok := policyMap["policy-gribi-get"] @@ -274,7 +274,7 @@ func TestAuthz1(t *testing.T) { } newpolicy.AddAllowRules("base", []string{*testInfraID}, []*gnxi.RPC{gnxi.RPCs.AllRPC}) // Rotate the policy. - newpolicy.Rotate(t, dut, uint64(time.Now().UnixMilli()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) + newpolicy.Rotate(t, dut, uint64(time.Now().Unix()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) // Verification of Policy for read-only to deny gRIBI Get and allow gNMI Get t.Run("Verification of Policy for read-only to deny gRIBI Get and allow gNMI Get", func(t *testing.T) { @@ -287,7 +287,7 @@ func TestAuthz1(t *testing.T) { // Pre-Test Section _, policyBefore := authz.Get(t, dut) t.Logf("Authz Policy of the Device %s before the Rotate Trigger is %s", dut.Name(), policyBefore.PrettyPrint(t)) - defer policyBefore.Rotate(t, dut, uint64(time.Now().UnixMilli()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) + defer policyBefore.Rotate(t, dut, uint64(time.Now().Unix()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) // Fetch the Desired Authorization Policy and Attach base Admin Policy Before Rotate newpolicy, ok := policyMap["policy-normal-1"] @@ -313,7 +313,7 @@ func TestAuthz2(t *testing.T) { // Pre-Test Section _, policyBefore := authz.Get(t, dut) t.Logf("Authz Policy of the Device %s before the Rotate Trigger is %s", dut.Name(), policyBefore.PrettyPrint(t)) - defer policyBefore.Rotate(t, dut, uint64(time.Now().UnixMilli()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) + defer policyBefore.Rotate(t, dut, uint64(time.Now().Unix()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) // Fetch the Desired Authorization Policy and Attach base Admin Policy Before Rotate newpolicy, ok := policyMap["policy-everyone-can-gnmi-not-gribi"] @@ -334,7 +334,7 @@ func TestAuthz2(t *testing.T) { autzRotateReq := &authzpb.RotateAuthzRequest_UploadRequest{ UploadRequest: &authzpb.UploadRequest{ Version: fmt.Sprintf("v0.%v", (time.Now().UnixNano())), - CreatedOn: uint64(time.Now().UnixMilli()), + CreatedOn: uint64(time.Now().Unix()), Policy: string(jsonPolicy), }, } @@ -348,9 +348,9 @@ func TestAuthz2(t *testing.T) { t.Fatalf("Error while receiving rotate request reply (client 1) %v", err) } // Rotate Request 2 - Before Finalizing the Request 1 - newpolicy, ok = policyMap["policy-everyone-can-gnmi-not-gribi"] + newpolicy, ok = policyMap["policy-everyone-can-gribi-not-gnmi"] if !ok { - t.Fatal("Policy policy-everyone-can-gnmi-not-gribi is not loaded from policy json file") + t.Fatal("Policy policy-everyone-can-gribi-not-gnmi is not loaded from policy json file") } newpolicy.AddAllowRules("base", []string{*testInfraID}, []*gnxi.RPC{gnxi.RPCs.AllRPC}) jsonPolicy, err = newpolicy.Marshal() @@ -365,7 +365,7 @@ func TestAuthz2(t *testing.T) { autzRotateReq = &authzpb.RotateAuthzRequest_UploadRequest{ UploadRequest: &authzpb.UploadRequest{ Version: fmt.Sprintf("v0.%v", (time.Now().UnixNano())), - CreatedOn: uint64(time.Now().UnixMilli()), + CreatedOn: uint64(time.Now().Unix()), Policy: string(jsonPolicy), }, } @@ -390,7 +390,7 @@ func TestAuthz2(t *testing.T) { // Pre-Test Section _, policyBefore := authz.Get(t, dut) t.Logf("Authz Policy of the Device %s before the Rotate Trigger is %s", dut.Name(), policyBefore.PrettyPrint(t)) - defer policyBefore.Rotate(t, dut, uint64(time.Now().UnixMilli()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) + defer policyBefore.Rotate(t, dut, uint64(time.Now().Unix()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) // Fetch the Desired Authorization Policy and Attach base Admin Policy Before Rotate newpolicy, ok := policyMap["policy-gribi-get"] @@ -399,7 +399,7 @@ func TestAuthz2(t *testing.T) { } newpolicy.AddAllowRules("base", []string{*testInfraID}, []*gnxi.RPC{gnxi.RPCs.AllRPC}) // Rotate the policy. - newpolicy.Rotate(t, dut, uint64(time.Now().UnixMilli()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) + newpolicy.Rotate(t, dut, uint64(time.Now().Unix()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) // Verification of Policy for read_only to allow gRIBI Get and to deny gNMI Get t.Run("Verification of Policy for read_only to allow gRIBI Get and to deny gNMI Get", func(t *testing.T) { @@ -425,7 +425,7 @@ func TestAuthz2(t *testing.T) { autzRotateReq := &authzpb.RotateAuthzRequest_UploadRequest{ UploadRequest: &authzpb.UploadRequest{ Version: fmt.Sprintf("v0.%v", (time.Now().UnixNano())), - CreatedOn: uint64(time.Now().UnixMilli()), + CreatedOn: uint64(time.Now().Unix()), Policy: string(jsonPolicy), }, } @@ -459,7 +459,7 @@ func TestAuthz2(t *testing.T) { // Pre-Test Section _, policyBefore := authz.Get(t, dut) t.Logf("Authz Policy of the Device %s before the Rotate Trigger is %s", dut.Name(), policyBefore.PrettyPrint(t)) - defer policyBefore.Rotate(t, dut, uint64(time.Now().UnixMilli()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) + defer policyBefore.Rotate(t, dut, uint64(time.Now().Unix()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) // Fetch the Desired Authorization Policy and Attach base Admin Policy Before Rotate newpolicy, ok := policyMap["policy-gribi-get"] @@ -468,7 +468,7 @@ func TestAuthz2(t *testing.T) { } newpolicy.AddAllowRules("base", []string{*testInfraID}, []*gnxi.RPC{gnxi.RPCs.AllRPC}) // Rotate the policy. - newpolicy.Rotate(t, dut, uint64(time.Now().UnixMilli()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) + newpolicy.Rotate(t, dut, uint64(time.Now().Unix()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) // Verification of Policy for read_only to allow gRIBI Get and to deny gNMI Get t.Run("Verification of Policy for read_only to allow gRIBI Get and to deny gNMI Get", func(t *testing.T) { @@ -495,7 +495,7 @@ func TestAuthz2(t *testing.T) { autzRotateReq := &authzpb.RotateAuthzRequest_UploadRequest{ UploadRequest: &authzpb.UploadRequest{ Version: fmt.Sprintf("v0.%v", (time.Now().UnixNano())), - CreatedOn: uint64(time.Now().UnixMilli()), + CreatedOn: uint64(time.Now().Unix()), Policy: string(jsonPolicy), }, } @@ -527,7 +527,7 @@ func TestAuthz2(t *testing.T) { // Pre-Test Section _, policyBefore := authz.Get(t, dut) t.Logf("Authz Policy of the Device %s before the Rotate Trigger is %s", dut.Name(), policyBefore.PrettyPrint(t)) - defer policyBefore.Rotate(t, dut, uint64(time.Now().UnixMilli()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) + defer policyBefore.Rotate(t, dut, uint64(time.Now().Unix()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) // Fetch the Desired Authorization Policy and Attach base Admin Policy Before Rotate newpolicy, ok := policyMap["policy-gribi-get"] @@ -537,7 +537,7 @@ func TestAuthz2(t *testing.T) { newpolicy.AddAllowRules("base", []string{*testInfraID}, []*gnxi.RPC{gnxi.RPCs.AllRPC}) // Rotate the policy. prevVersion := fmt.Sprintf("v0.%v", (time.Now().UnixNano())) - newpolicy.Rotate(t, dut, uint64(time.Now().UnixMilli()), prevVersion, false) + newpolicy.Rotate(t, dut, uint64(time.Now().Unix()), prevVersion, false) newpolicy, ok = policyMap["policy-gnmi-get"] if !ok { @@ -556,7 +556,7 @@ func TestAuthz2(t *testing.T) { autzRotateReq := &authzpb.RotateAuthzRequest_UploadRequest{ UploadRequest: &authzpb.UploadRequest{ Version: prevVersion, - CreatedOn: uint64(time.Now().UnixMilli()), + CreatedOn: uint64(time.Now().Unix()), Policy: string(jsonPolicy), }, } @@ -576,7 +576,7 @@ func TestAuthz2(t *testing.T) { }) t.Logf("Preforming Rotate with the same version with force overwrite\n") - newpolicy.Rotate(t, dut, uint64(time.Now().UnixMilli()), prevVersion, true) + newpolicy.Rotate(t, dut, uint64(time.Now().Unix()), prevVersion, true) // Verification of Policy for read_only to allow gRIBI Get and to deny gNMI Get t.Run("Verification of Policy for read_only to allow gRIBI Get and to deny gNMI Get after rotate wth force overwrite", func(t *testing.T) { authz.Verify(t, dut, spiffeCertReadOnly, gnxi.RPCs.GribiGet, &authz.ExceptDeny{}, &authz.HardVerify{}) @@ -593,7 +593,7 @@ func TestAuthz3(t *testing.T) { setUpBaseline(t, dut) _, policyBefore := authz.Get(t, dut) t.Logf("Authz Policy of the Device %s before the Rotate Trigger is %s", dut.Name(), policyBefore.PrettyPrint(t)) - defer policyBefore.Rotate(t, dut, uint64(time.Now().UnixMilli()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) + defer policyBefore.Rotate(t, dut, uint64(time.Now().Unix()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) // Fetch the Desired Authorization Policy object. newpolicy, ok := policyMap["policy-gribi-get"] @@ -603,7 +603,7 @@ func TestAuthz3(t *testing.T) { // Attach base Admin Policy // Rotate the policy. newpolicy.AddAllowRules("base", []string{*testInfraID}, []*gnxi.RPC{gnxi.RPCs.AllRPC}) - expCreatedOn := uint64(time.Now().UnixMilli()) + expCreatedOn := uint64(time.Now().Unix()) expVersion := fmt.Sprintf("v0.%v", (time.Now().UnixNano())) newpolicy.Rotate(t, dut, expCreatedOn, expVersion, false) t.Logf("New Rotated Authz Policy is %s", newpolicy.PrettyPrint(t)) @@ -635,9 +635,10 @@ func TestAuthz3(t *testing.T) { func TestAuthz4(t *testing.T) { // Pre-Test Section dut := ondatra.DUT(t, "dut") + setUpBaseline(t, dut) _, policyBefore := authz.Get(t, dut) t.Logf("Authz Policy of the Device %s before the Reboot Trigger is %s", dut.Name(), policyBefore.PrettyPrint(t)) - defer policyBefore.Rotate(t, dut, uint64(time.Now().UnixMilli()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) + defer policyBefore.Rotate(t, dut, uint64(time.Now().Unix()), fmt.Sprintf("v0.%v", (time.Now().UnixNano())), false) // Fetch the Desired Authorization Policy and Attach base Admin Policy Before Rotate newpolicy, ok := policyMap["policy-normal-1"] @@ -645,7 +646,7 @@ func TestAuthz4(t *testing.T) { t.Fatal("Policy policy-normal-1 is not loaded from policy json file") } newpolicy.AddAllowRules("base", []string{*testInfraID}, []*gnxi.RPC{gnxi.RPCs.AllRPC}) - expCreatedOn := uint64(time.Now().UnixMilli()) + expCreatedOn := uint64(time.Now().Unix()) expVersion := fmt.Sprintf("v0.%v", (time.Now().UnixNano())) t.Logf("New Authz Policy is %s", newpolicy.PrettyPrint(t)) newpolicy.Rotate(t, dut, expCreatedOn, expVersion, false) diff --git a/feature/security/gnsi/certz/feature.textproto b/feature/security/gnsi/certz/feature.textproto index 92288a1e337..e12f9bcbf7d 100644 --- a/feature/security/gnsi/certz/feature.textproto +++ b/feature/security/gnsi/certz/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "security_gnsi_certz" diff --git a/feature/security/gnsi/credentialz/feature.textproto b/feature/security/gnsi/credentialz/feature.textproto index 964ff8207a1..4bfd23a1c3f 100644 --- a/feature/security/gnsi/credentialz/feature.textproto +++ b/feature/security/gnsi/credentialz/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "security_gnsi_credentialz" diff --git a/feature/sflow/feature.textproto b/feature/sflow/feature.textproto index 2bf860d0b8f..e253000ce58 100644 --- a/feature/sflow/feature.textproto +++ b/feature/sflow/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "sflow" diff --git a/feature/sflow/otg_tests/sflow_base_test/metadata.textproto b/feature/sflow/otg_tests/sflow_base_test/metadata.textproto index 78c6dc422dd..d34f121c0cb 100644 --- a/feature/sflow/otg_tests/sflow_base_test/metadata.textproto +++ b/feature/sflow/otg_tests/sflow_base_test/metadata.textproto @@ -1,4 +1,4 @@ -# proto-file: third_party/openconfig/featureprofiles/proto/metadata.proto +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto # proto-message: Metadata uuid: "25b389ff-8526-46e3-acf2-016e86aff406" diff --git a/feature/sflow/otg_tests/sflow_base_test/sflow_base_test.go b/feature/sflow/otg_tests/sflow_base_test/sflow_base_test.go index 0d780bc4302..a305c3e1035 100644 --- a/feature/sflow/otg_tests/sflow_base_test/sflow_base_test.go +++ b/feature/sflow/otg_tests/sflow_base_test/sflow_base_test.go @@ -124,6 +124,7 @@ func TestSFlowTraffic(t *testing.T) { configureDUTBaseline(t, dut) srBatch := &gnmi.SetBatch{} + fptest.ConfigureDefaultNetworkInstance(t, dut) cfgplugins.NewStaticRouteCfg(srBatch, staticRoute, dut) srBatch.Set(t, dut) diff --git a/feature/staticroute/feature.textproto b/feature/staticroute/feature.textproto index ca5a09a3f02..9ee16d1323e 100644 --- a/feature/staticroute/feature.textproto +++ b/feature/staticroute/feature.textproto @@ -1,3 +1,6 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile + id { name: "staticroute" version: 1 diff --git a/feature/staticroute/otg_tests/basic_static_route_support_test/README.md b/feature/staticroute/otg_tests/basic_static_route_support_test/README.md index eecbe83c336..e64a0047f6b 100644 --- a/feature/staticroute/otg_tests/basic_static_route_support_test/README.md +++ b/feature/staticroute/otg_tests/basic_static_route_support_test/README.md @@ -57,7 +57,7 @@ #### Test to validate static route metric -* Configure metric of ipv4-route-b and ipv6-route-b to 1000 +* Configure metric of ipv4-route-b and ipv6-route-b to 100 * /network-instances/network-instance/protocols/protocol/static-routes/static/next-hops/next-hop/config/metric * Validate that the metric is set correctly * /network-instances/network-instance/protocols/protocol/static-routes/static/next-hops/next-hop/state/metric @@ -69,7 +69,7 @@ #### Test to validate static route preference -* Configure preference of ipv4-route-a and ipv6-route-a to 200 +* Configure preference of ipv4-route-a and ipv6-route-a to 50 * /network-instances/network-instance/protocols/protocol/static-routes/static/next-hops/next-hop/config/preference * Validate that the preference is set correctly * /network-instances/network-instance/protocols/protocol/static-routes/static/next-hops/next-hop/state/preference @@ -90,9 +90,9 @@ #### Test to validate IPv6 static route with IPv4 next-hop -* Remove metric of 1000 from ipv4-route-b and ipv6-route-b +* Remove metric of 100 from ipv4-route-b and ipv6-route-b * /network-instances/network-instance/protocols/protocol/static-routes/static/next-hops/next-hop/config/metric -* Remove preference of 200 from ipv4-route-a and ipv6-route-a +* Remove preference of 50 from ipv4-route-a and ipv6-route-a * /network-instances/network-instance/protocols/protocol/static-routes/static/next-hops/next-hop/config/preference * Change the IPv6 next-hop of the ipv6-route-a with the next hop set to the IPv4 address of ATE port-1 diff --git a/feature/staticroute/otg_tests/basic_static_route_support_test/basic_static_route_support_test.go b/feature/staticroute/otg_tests/basic_static_route_support_test/basic_static_route_support_test.go index 14259f34278..f801c2fbdc8 100644 --- a/feature/staticroute/otg_tests/basic_static_route_support_test/basic_static_route_support_test.go +++ b/feature/staticroute/otg_tests/basic_static_route_support_test/basic_static_route_support_test.go @@ -47,6 +47,8 @@ const ( ecmpTolerance = uint64(2) port1Tag = "0x101" port2Tag = "0x102" + dummyV6 = "2001:db8::192:0:2:d" + dummyMAC = "00:1A:11:00:0A:BC" ) var ( @@ -209,6 +211,9 @@ func TestBasicStaticRouteSupport(t *testing.T) { func TestDisableRecursiveNextHopResolution(t *testing.T) { dut := ondatra.DUT(t, "dut") + if deviations.UnsupportedStaticRouteNextHopRecurse(dut) { + t.Skip("Skipping Disable Recursive Next Hop Resolution Test. Deviation UnsupportedStaticRouteNextHopRecurse enabled.") + } configureDUT(t, dut) ate := ondatra.ATE(t, "ate") @@ -250,6 +255,7 @@ func TestDisableRecursiveNextHopResolution(t *testing.T) { if err := td.awaitISISAdjacency(t, dut.Port(t, "port2"), isisName); err != nil { t.Fatal(err) } + t.Run("RT-1.26.8: Disable Recursive Next Hop Resolution", func(t *testing.T) { td.testRecursiveNextHopResolution(t) td.testRecursiveNextHopResolutionDisabled(t) @@ -268,9 +274,11 @@ func (td *testData) testRecursiveNextHopResolution(t *testing.T) { "0": oc.UnionString(td.advertisedIPv4.address), }, } - if _, err := cfgplugins.NewStaticRouteCfg(b, sV4, td.dut); err != nil { + spV4, err := cfgplugins.NewStaticRouteCfg(b, sV4, td.dut) + if err != nil { t.Fatal(err) } + spV4.GetOrCreateNextHop("0").SetRecurse(true) // Configure one IPv6 static route i.e. ipv6-route on the DUT for destination // `ipv6-network 2001:db8:128:128::/64` with the next hop of `ipv6-loopback = // 2001:db8::64:64::1/128`. Remove all other existing next hops for the route. @@ -281,9 +289,11 @@ func (td *testData) testRecursiveNextHopResolution(t *testing.T) { "0": oc.UnionString(td.advertisedIPv6.address), }, } - if _, err := cfgplugins.NewStaticRouteCfg(b, sV6, td.dut); err != nil { + spV6, err := cfgplugins.NewStaticRouteCfg(b, sV6, td.dut) + if err != nil { t.Fatal(err) } + spV6.GetOrCreateNextHop("0").SetRecurse(true) b.Set(t, td.dut) @@ -308,8 +318,8 @@ func (td *testData) testRecursiveNextHopResolution(t *testing.T) { time.Sleep(trafficDuration) td.ate.OTG().StopTraffic(t) - lossV4 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v4Flow, 10*time.Second) - lossV6 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v6Flow, 10*time.Second) + lossV4 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v4Flow, 20*time.Second) + lossV6 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v6Flow, 20*time.Second) // Validate that traffic is received from DUT (doesn't matter which port) otgutils.LogFlowMetrics(t, td.ate.OTG(), td.top) @@ -349,8 +359,8 @@ func (td *testData) testRecursiveNextHopResolutionDisabled(t *testing.T) { time.Sleep(trafficDuration) td.ate.OTG().StopTraffic(t) - lossV4 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v4Flow, 10*time.Second) - lossV6 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v6Flow, 10*time.Second) + lossV4 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v4Flow, 20*time.Second) + lossV6 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v6Flow, 20*time.Second) // Validate that traffic is NOT received from DUT otgutils.LogFlowMetrics(t, td.ate.OTG(), td.top) @@ -363,7 +373,7 @@ func (td *testData) testRecursiveNextHopResolutionDisabled(t *testing.T) { }) } -func (td *testData) testStaticRouteECMP(t *testing.T) { +func (td *testData) configureStaticRouteToATEP1AndP2(t *testing.T) { b := &gnmi.SetBatch{} // Configure IPv4 static routes: // * Configure one IPv4 static route i.e. ipv4-route-a on the DUT for @@ -403,6 +413,19 @@ func (td *testData) testStaticRouteECMP(t *testing.T) { t.Fatalf("Failed to configure IPv6 static route: %v", err) } b.Set(t, td.dut) +} + +func (td *testData) deleteStaticRoutes(t *testing.T) { + b := &gnmi.SetBatch{} + sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(td.dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(td.dut)) + gnmi.BatchDelete(b, sp.Static(td.staticIPv4.cidr(t)).Config()) + gnmi.BatchDelete(b, sp.Static(td.staticIPv6.cidr(t)).Config()) + b.Set(t, td.dut) +} + +func (td *testData) testStaticRouteECMP(t *testing.T) { + td.configureStaticRouteToATEP1AndP2(t) + defer td.deleteStaticRoutes(t) t.Run("Telemetry", func(t *testing.T) { sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(td.dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(td.dut)) @@ -436,8 +459,8 @@ func (td *testData) testStaticRouteECMP(t *testing.T) { time.Sleep(trafficDuration) td.ate.OTG().StopTraffic(t) - lossV4 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v4Flow, 10*time.Second) - lossV6 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v6Flow, 10*time.Second) + lossV4 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v4Flow, 20*time.Second) + lossV6 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v6Flow, 20*time.Second) otgutils.LogFlowMetrics(t, td.ate.OTG(), td.top) if lossV4 > lossTolerance { @@ -492,17 +515,23 @@ func (td *testData) testStaticRouteECMP(t *testing.T) { } func (td *testData) testStaticRouteWithMetric(t *testing.T) { - const port2Metric = uint32(1000) + td.configureStaticRouteToATEP1AndP2(t) + defer td.deleteStaticRoutes(t) + + const port2Metric = uint32(100) sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(td.dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(td.dut)) - // Configure metric of ipv4-route-b and ipv6-route-b to 1000 + // Configure metric of ipv4-route-b and ipv6-route-b to 100 batch := &gnmi.SetBatch{} gnmi.BatchReplace(batch, sp.Static(td.staticIPv4.cidr(t)).NextHop("1").Metric().Config(), port2Metric) gnmi.BatchReplace(batch, sp.Static(td.staticIPv6.cidr(t)).NextHop("1").Metric().Config(), port2Metric) batch.Set(t, td.dut) t.Run("Telemetry", func(t *testing.T) { + if deviations.MissingStaticRouteNextHopMetricTelemetry(td.dut) { + t.Skip("Skipping Telemetry check for Metric, since deviation MissingStaticRouteNextHopMetricTelemetry is enabled.") + } gnmi.Await(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).Prefix().State(), 30*time.Second, td.staticIPv4.cidr(t)) gnmi.Await(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).Prefix().State(), 30*time.Second, td.staticIPv6.cidr(t)) // Validate that the metric is set correctly @@ -521,8 +550,8 @@ func (td *testData) testStaticRouteWithMetric(t *testing.T) { time.Sleep(trafficDuration) td.ate.OTG().StopTraffic(t) - lossV4 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v4Flow, 10*time.Second) - lossV6 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v6Flow, 10*time.Second) + lossV4 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v4Flow, 20*time.Second) + lossV6 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v6Flow, 20*time.Second) otgutils.LogFlowMetrics(t, td.ate.OTG(), td.top) if lossV4 > lossTolerance { @@ -533,7 +562,7 @@ func (td *testData) testStaticRouteWithMetric(t *testing.T) { } // Validate that traffic is received from DUT on port-1 and not on port-2 portCounters := egressTrackingCounters(t, td.ate, v4Flow) - _, rxV4 := otgutils.GetFlowStats(t, td.ate.OTG(), v4Flow, 10*time.Second) + _, rxV4 := otgutils.GetFlowStats(t, td.ate.OTG(), v4Flow, 20*time.Second) port1Counter, ok := portCounters[port1Tag] if !ok { t.Errorf("Port1 IPv4 egress tracking counter not found: %v", portCounters) @@ -543,7 +572,7 @@ func (td *testData) testStaticRouteWithMetric(t *testing.T) { } // Validate that traffic is received from DUT on port-1 and not on port-2 portCounters = egressTrackingCounters(t, td.ate, v6Flow) - _, rxV6 := otgutils.GetFlowStats(t, td.ate.OTG(), v6Flow, 10*time.Second) + _, rxV6 := otgutils.GetFlowStats(t, td.ate.OTG(), v6Flow, 20*time.Second) port1Counter, ok = portCounters[port1Tag] if !ok { t.Errorf("Port1 IPv6 egress tracking counter not found: %v", portCounters) @@ -555,23 +584,44 @@ func (td *testData) testStaticRouteWithMetric(t *testing.T) { } func (td *testData) testStaticRouteWithPreference(t *testing.T) { - const port1Preference = uint32(200) + td.configureStaticRouteToATEP1AndP2(t) + defer td.deleteStaticRoutes(t) + + const port1Preference = uint32(50) + const port2Metric = uint32(100) sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(td.dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(td.dut)) - // Configure preference of ipv4-route-a and ipv6-route-a to 200 + + // Configure metric of ipv4-route-b and ipv6-route-b to 100 batch := &gnmi.SetBatch{} - gnmi.BatchReplace(batch, sp.Static(td.staticIPv4.cidr(t)).NextHop("0").Preference().Config(), port1Preference) - gnmi.BatchReplace(batch, sp.Static(td.staticIPv6.cidr(t)).NextHop("0").Preference().Config(), port1Preference) + gnmi.BatchReplace(batch, sp.Static(td.staticIPv4.cidr(t)).NextHop("1").Metric().Config(), port2Metric) + gnmi.BatchReplace(batch, sp.Static(td.staticIPv6.cidr(t)).NextHop("1").Metric().Config(), port2Metric) + + // Configure preference of ipv4-route-a and ipv6-route-a to 50 + if deviations.SetMetricAsPreference(td.dut) { + // Lower metric indicate more favourable path. + // If we use Metric instead of Preference, we would need to have a port1Metric + // larger than port2Metric for traffic to pass through port 2 + port1Metric := port2Metric + port1Preference + gnmi.BatchReplace(batch, sp.Static(td.staticIPv4.cidr(t)).NextHop("0").Metric().Config(), port1Metric) + gnmi.BatchReplace(batch, sp.Static(td.staticIPv6.cidr(t)).NextHop("0").Metric().Config(), port1Metric) + } else { + gnmi.BatchReplace(batch, sp.Static(td.staticIPv4.cidr(t)).NextHop("0").Preference().Config(), port1Preference) + gnmi.BatchReplace(batch, sp.Static(td.staticIPv6.cidr(t)).NextHop("0").Preference().Config(), port1Preference) + } batch.Set(t, td.dut) t.Run("Telemetry", func(t *testing.T) { + if deviations.SetMetricAsPreference(td.dut) { + t.Skip("Skipping Preference telemetry check since deviation SetMetricAsPreference is enabled") + } gnmi.Await(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).Prefix().State(), 30*time.Second, td.staticIPv4.cidr(t)) gnmi.Await(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).Prefix().State(), 30*time.Second, td.staticIPv6.cidr(t)) // Validate that the preference is set correctly - if got, want := gnmi.Get(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).NextHop("1").Preference().State()), port1Preference; got != want { + if got, want := gnmi.Get(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).NextHop("0").Preference().State()), port1Preference; got != want { t.Errorf("IPv4 Static Route preference for NextHop 0, got: %d, want: %d", got, want) } - if got, want := gnmi.Get(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).NextHop("1").Preference().State()), port1Preference; got != want { + if got, want := gnmi.Get(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).NextHop("0").Preference().State()), port1Preference; got != want { t.Errorf("IPv6 Static Route preference for NextHop 0, got: %d, want: %d", got, want) } }) @@ -583,8 +633,8 @@ func (td *testData) testStaticRouteWithPreference(t *testing.T) { time.Sleep(trafficDuration) td.ate.OTG().StopTraffic(t) - lossV4 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v4Flow, 10*time.Second) - lossV6 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v6Flow, 10*time.Second) + lossV4 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v4Flow, 20*time.Second) + lossV6 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v6Flow, 20*time.Second) otgutils.LogFlowMetrics(t, td.ate.OTG(), td.top) if lossV4 > lossTolerance { @@ -595,7 +645,7 @@ func (td *testData) testStaticRouteWithPreference(t *testing.T) { } // Validate that traffic is now received from DUT on port-2 and not on port-1 portCounters := egressTrackingCounters(t, td.ate, v4Flow) - _, rxV4 := otgutils.GetFlowStats(t, td.ate.OTG(), v4Flow, 10*time.Second) + _, rxV4 := otgutils.GetFlowStats(t, td.ate.OTG(), v4Flow, 20*time.Second) port2Counter, ok := portCounters[port2Tag] if !ok { t.Errorf("Port2 IPv4 egress tracking counter not found: %v", portCounters) @@ -605,7 +655,7 @@ func (td *testData) testStaticRouteWithPreference(t *testing.T) { } // Validate that traffic is now received from DUT on port-2 and not on port-1 portCounters = egressTrackingCounters(t, td.ate, v6Flow) - _, rxV6 := otgutils.GetFlowStats(t, td.ate.OTG(), v6Flow, 10*time.Second) + _, rxV6 := otgutils.GetFlowStats(t, td.ate.OTG(), v6Flow, 20*time.Second) port2Counter, ok = portCounters[port2Tag] if !ok { t.Errorf("Port2 IPv6 egress tracking counter not found: %v", portCounters) @@ -618,6 +668,7 @@ func (td *testData) testStaticRouteWithPreference(t *testing.T) { func (td *testData) testStaticRouteSetTag(t *testing.T) { const tag = uint32(10) + b := &gnmi.SetBatch{} // Configure a tag of value 10 on ipv4 and ipv6 static routes v4Cfg := &cfgplugins.StaticRouteCfg{ @@ -650,6 +701,8 @@ func (td *testData) testStaticRouteSetTag(t *testing.T) { b.Set(t, td.dut) + defer td.deleteStaticRoutes(t) + // Validate the tag is set t.Run("Telemetry", func(t *testing.T) { sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(td.dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(td.dut)) @@ -665,31 +718,48 @@ func (td *testData) testStaticRouteSetTag(t *testing.T) { } func (td *testData) testIPv6StaticRouteWithIPv4NextHop(t *testing.T) { - b := &gnmi.SetBatch{} - // Remove metric of 1000 from ipv4-route-b and ipv6-route-b + // Remove metric of 100 from ipv4-route-b and ipv6-route-b // * /network-instances/network-instance/protocols/protocol/static-routes/static/next-hops/next-hop/config/metric - // Remove preference of 200 from ipv4-route-a and ipv6-route-a + // Remove preference of 50 from ipv4-route-a and ipv6-route-a // * /network-instances/network-instance/protocols/protocol/static-routes/static/next-hops/next-hop/config/preference // Change the IPv6 next-hop of the ipv6-route-a with the next hop set to the // IPv4 address of ATE port-1 // Change the IPv6 next-hop of the ipv6-route-b with the next hop set to the // IPv4 address of ATE port-2 - v6Cfg := &cfgplugins.StaticRouteCfg{ - NetworkInstance: deviations.DefaultNetworkInstance(td.dut), - Prefix: td.staticIPv6.cidr(t), - NextHops: map[string]oc.NetworkInstance_Protocol_Static_NextHop_NextHop_Union{ - "0": oc.UnionString(atePort1.IPv4), - "1": oc.UnionString(atePort2.IPv4), - }, + b := &gnmi.SetBatch{} + var v6Cfg *cfgplugins.StaticRouteCfg + if deviations.IPv6StaticRouteWithIPv4NextHopRequiresStaticARP(td.dut) { + staticARPWithMagicUniversalIP(t, td.dut) + v6Cfg = &cfgplugins.StaticRouteCfg{ + NetworkInstance: deviations.DefaultNetworkInstance(td.dut), + Prefix: td.staticIPv6.cidr(t), + NextHops: map[string]oc.NetworkInstance_Protocol_Static_NextHop_NextHop_Union{ + "0": oc.UnionString(dummyV6), + }, + } + } else { + v6Cfg = &cfgplugins.StaticRouteCfg{ + NetworkInstance: deviations.DefaultNetworkInstance(td.dut), + Prefix: td.staticIPv6.cidr(t), + NextHops: map[string]oc.NetworkInstance_Protocol_Static_NextHop_NextHop_Union{ + "0": oc.UnionString(atePort1.IPv4), + "1": oc.UnionString(atePort2.IPv4), + }, + } } if _, err := cfgplugins.NewStaticRouteCfg(b, v6Cfg, td.dut); err != nil { t.Fatalf("Failed to configure IPv6 static route: %v", err) } b.Set(t, td.dut) + defer td.deleteStaticRoutes(t) + // Validate both the routes i.e. ipv6-route-[a|b] are configured and the IPv4 // next-hop is reported correctly t.Run("Telemetry", func(t *testing.T) { + if deviations.IPv6StaticRouteWithIPv4NextHopRequiresStaticARP(td.dut) { + t.Skip("Telemetry not validated due to use of deviation: IPv6StaticRouteWithIPv4NextHopRequiresStaticARP.") + } sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(td.dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(td.dut)) gnmi.Await(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).Prefix().State(), 30*time.Second, td.staticIPv6.cidr(t)) gotStatic := gnmi.Get(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).State()) @@ -708,7 +778,7 @@ func (td *testData) testIPv6StaticRouteWithIPv4NextHop(t *testing.T) { time.Sleep(trafficDuration) td.ate.OTG().StopTraffic(t) - lossV6 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v6Flow, 10*time.Second) + lossV6 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v6Flow, 20*time.Second) otgutils.LogFlowMetrics(t, td.ate.OTG(), td.top) @@ -739,6 +809,44 @@ func (td *testData) testIPv6StaticRouteWithIPv4NextHop(t *testing.T) { }) } +func staticARPWithMagicUniversalIP(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + p1 := dut.Port(t, "port1") + p2 := dut.Port(t, "port2") + dummyIPCIDR := dummyV6 + "/128" + s2 := &oc.NetworkInstance_Protocol_Static{ + Prefix: ygot.String(dummyIPCIDR), + NextHop: map[string]*oc.NetworkInstance_Protocol_Static_NextHop{ + "0": { + Index: ygot.String("0"), + InterfaceRef: &oc.NetworkInstance_Protocol_Static_NextHop_InterfaceRef{ + Interface: ygot.String(p1.Name()), + }, + }, + "1": { + Index: ygot.String("1"), + InterfaceRef: &oc.NetworkInstance_Protocol_Static_NextHop_InterfaceRef{ + Interface: ygot.String(p2.Name()), + }, + }, + }, + } + sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(dut)) + static, ok := gnmi.LookupConfig(t, dut, sp.Config()).Val() + if !ok || static == nil { + static = &oc.NetworkInstance_Protocol{ + Identifier: oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, + Name: ygot.String(deviations.StaticProtocolName(dut)), + Static: map[string]*oc.NetworkInstance_Protocol_Static{ + dummyIPCIDR: s2, + }, + } + gnmi.Replace(t, dut, sp.Config(), static) + } else { + gnmi.Replace(t, dut, sp.Static(dummyIPCIDR).Config(), s2) + } +} + func (td *testData) testIPv4StaticRouteWithIPv6NextHop(t *testing.T) { b := &gnmi.SetBatch{} // Change the IPv4 next-hop of the ipv4-route-a with the next hop set to the @@ -758,6 +866,8 @@ func (td *testData) testIPv4StaticRouteWithIPv6NextHop(t *testing.T) { } b.Set(t, td.dut) + defer td.deleteStaticRoutes(t) + // Validate both the routes i.e. ipv4-route-[a|b] are configured and the IPv6 // next-hop is reported correctly t.Run("Telemetry", func(t *testing.T) { @@ -779,7 +889,7 @@ func (td *testData) testIPv4StaticRouteWithIPv6NextHop(t *testing.T) { time.Sleep(trafficDuration) td.ate.OTG().StopTraffic(t) - lossV4 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v4Flow, 10*time.Second) + lossV4 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v4Flow, 20*time.Second) otgutils.LogFlowMetrics(t, td.ate.OTG(), td.top) @@ -844,7 +954,12 @@ func (td *testData) testStaticRouteWithDropNextHop(t *testing.T) { } b.Set(t, td.dut) + defer td.deleteStaticRoutes(t) + t.Run("Telemetry", func(t *testing.T) { + if deviations.MissingStaticRouteDropNextHopTelemetry(td.dut) { + t.Skip("Skipping telemetry check for DROP next hop. Deviation MissingStaticRouteDropNextHopTelemetryenabled.") + } sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(td.dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(td.dut)) gnmi.Await(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).Prefix().State(), 30*time.Second, td.staticIPv4.cidr(t)) gnmi.Await(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).Prefix().State(), 30*time.Second, td.staticIPv6.cidr(t)) @@ -868,8 +983,8 @@ func (td *testData) testStaticRouteWithDropNextHop(t *testing.T) { time.Sleep(trafficDuration) td.ate.OTG().StopTraffic(t) - lossV4 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v4Flow, 10*time.Second) - lossV6 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v6Flow, 10*time.Second) + lossV4 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v4Flow, 20*time.Second) + lossV6 := otgutils.GetFlowLossPct(t, td.ate.OTG(), v6Flow, 20*time.Second) // Validate that traffic is dropped on DUT and not received on port-1 and // port-2 @@ -962,9 +1077,16 @@ func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { p2 := dut.Port(t, "port2") p3 := dut.Port(t, "port3") b := &gnmi.SetBatch{} - gnmi.BatchReplace(b, gnmi.OC().Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), dut)) - gnmi.BatchReplace(b, gnmi.OC().Interface(p2.Name()).Config(), dutPort2.NewOCInterface(p2.Name(), dut)) - gnmi.BatchReplace(b, gnmi.OC().Interface(p3.Name()).Config(), dutPort3.NewOCInterface(p3.Name(), dut)) + i1 := dutPort1.NewOCInterface(p1.Name(), dut) + i2 := dutPort2.NewOCInterface(p2.Name(), dut) + i3 := dutPort3.NewOCInterface(p3.Name(), dut) + if deviations.IPv6StaticRouteWithIPv4NextHopRequiresStaticARP(dut) { + i1.GetOrCreateSubinterface(0).GetOrCreateIpv6().GetOrCreateNeighbor(dummyV6).LinkLayerAddress = ygot.String(dummyMAC) + i2.GetOrCreateSubinterface(0).GetOrCreateIpv6().GetOrCreateNeighbor(dummyV6).LinkLayerAddress = ygot.String(dummyMAC) + } + gnmi.BatchReplace(b, gnmi.OC().Interface(p1.Name()).Config(), i1) + gnmi.BatchReplace(b, gnmi.OC().Interface(p2.Name()).Config(), i2) + gnmi.BatchReplace(b, gnmi.OC().Interface(p3.Name()).Config(), i3) b.Set(t, dut) if deviations.ExplicitPortSpeed(dut) { diff --git a/feature/staticroute/otg_tests/basic_static_route_support_test/metadata.textproto b/feature/staticroute/otg_tests/basic_static_route_support_test/metadata.textproto index e552d95cdc9..1fce39fd6f2 100644 --- a/feature/staticroute/otg_tests/basic_static_route_support_test/metadata.textproto +++ b/feature/staticroute/otg_tests/basic_static_route_support_test/metadata.textproto @@ -18,6 +18,11 @@ platform_exceptions: { isis_interface_afi_unsupported: true missing_isis_interface_afi_safi_enable: true isis_require_same_l1_metric_with_l2_metric: true + ipv6_static_route_with_ipv4_next_hop_requires_static_arp: true + set_metric_as_preference: true + missing_static_route_next_hop_metric_telemetry: true + unsupported_static_route_next_hop_recurse: true + missing_static_route_drop_next_hop_telemetry: true } } tags: TAGS_DATACENTER_EDGE diff --git a/feature/system/aaa/feature.textproto b/feature/system/aaa/feature.textproto index b9923cdff62..8dc185c3b3a 100644 --- a/feature/system/aaa/feature.textproto +++ b/feature/system/aaa/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "system_aaa" diff --git a/feature/system/attestz/feature.textproto b/feature/system/attestz/feature.textproto index 62a7aec8cd7..69517dc0029 100644 --- a/feature/system/attestz/feature.textproto +++ b/feature/system/attestz/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "system_attestz" diff --git a/feature/system/bootz/feature.textproto b/feature/system/bootz/feature.textproto index 5d47d980b31..e70994d5f07 100644 --- a/feature/system/bootz/feature.textproto +++ b/feature/system/bootz/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "system_bootz" diff --git a/feature/system/feature.textproto b/feature/system/feature.textproto index e7955038018..177499d7c8e 100644 --- a/feature/system/feature.textproto +++ b/feature/system/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "system" diff --git a/feature/system/gnmi/cliorigin/feature.textproto b/feature/system/gnmi/cliorigin/feature.textproto index 7ac0a36ac4a..1d84d99726b 100644 --- a/feature/system/gnmi/cliorigin/feature.textproto +++ b/feature/system/gnmi/cliorigin/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "system_gnmi_cliorigin" diff --git a/feature/system/gnmi/feature.textproto b/feature/system/gnmi/feature.textproto index ad8b231c431..b6bc47c2e4b 100644 --- a/feature/system/gnmi/feature.textproto +++ b/feature/system/gnmi/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "system_gnmi" diff --git a/feature/system/gnmi/get/feature.textproto b/feature/system/gnmi/get/feature.textproto index 740a884e128..55fd8121dd9 100644 --- a/feature/system/gnmi/get/feature.textproto +++ b/feature/system/gnmi/get/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "system_gnmi_get" diff --git a/feature/system/gnmi/metadata/feature.textproto b/feature/system/gnmi/metadata/feature.textproto index 5e3ce2a2739..e85b54ab1db 100644 --- a/feature/system/gnmi/metadata/feature.textproto +++ b/feature/system/gnmi/metadata/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "system_gnmi_metadata" diff --git a/feature/system/gnmi/metadata/tests/large_set_consistency_test/metadata.textproto b/feature/system/gnmi/metadata/tests/large_set_consistency_test/metadata.textproto index 80bdcf384d9..cae8c3c1a46 100644 --- a/feature/system/gnmi/metadata/tests/large_set_consistency_test/metadata.textproto +++ b/feature/system/gnmi/metadata/tests/large_set_consistency_test/metadata.textproto @@ -1,4 +1,4 @@ -# proto-file: third_party/openconfig/featureprofiles/proto/metadata.proto +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto # proto-message: Metadata uuid: "e9cdd09c-14b9-4e3c-b38c-584ef9611c3f" diff --git a/feature/system/gnmi/set/feature.textproto b/feature/system/gnmi/set/feature.textproto index a9b786b86df..30b70f2e858 100644 --- a/feature/system/gnmi/set/feature.textproto +++ b/feature/system/gnmi/set/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "system_gnmi_set" diff --git a/feature/system/gnmi/set/tests/gnmi_set_test/gnmi_set_test.go b/feature/system/gnmi/set/tests/gnmi_set_test/gnmi_set_test.go index 5f5d489e81a..8152a803887 100644 --- a/feature/system/gnmi/set/tests/gnmi_set_test/gnmi_set_test.go +++ b/feature/system/gnmi/set/tests/gnmi_set_test/gnmi_set_test.go @@ -15,12 +15,14 @@ package gnmi_set_test import ( + "context" "fmt" "regexp" "strconv" "strings" "sync" "testing" + "time" "flag" @@ -31,6 +33,7 @@ import ( "github.com/openconfig/ondatra/gnmi" "github.com/openconfig/ondatra/gnmi/oc" "github.com/openconfig/ondatra/netutil" + "github.com/openconfig/ygnmi/schemaless" "github.com/openconfig/ygnmi/ygnmi" "github.com/openconfig/ygot/ygot" "github.com/openconfig/ygot/ytypes" @@ -53,7 +56,11 @@ var ( pruneQoS = flag.Bool("prune_qos", true, "Prune QoS config.") // Experimental flags that will likely become a deviation. - cannotDeleteVRF = flag.Bool("cannot_delete_vrf", true, "Device cannot delete VRF.") // See "Note about cannotDeleteVRF" below. + cannotDeleteVRF = flag.Bool("cannot_delete_vrf", true, "Device cannot delete VRF.") // See "Note about cannotDeleteVRF" below. + cannotConfigurePortSpeed = flag.Bool("cannot_config_port_speed", false, "Some devices depending on the type of line card may not allow changing port speed, while still supporting the port speed leaf.") + + // Flags to ensure test passes without any dependency to the device config + baseOCConfigIsPresent = flag.Bool("base_oc_config_is_present", false, "No OC config is loaded on router, so Get config on the root returns no data.") ) var ( @@ -74,6 +81,29 @@ var ( } ) +// Options are optional parameters to pass when deleting configs from the collected running config used in removeStatementsBetweenWords +type Options struct { + interfaces []string +} + +// breakout struct parameters define the speed and number of physical channels +type breakout struct { + breakoutSpeed oc.E_IfEthernet_ETHERNET_SPEED + numPhysicalChannels *uint8 +} + +// showRunningConfig gets the running config from the router +func showRunningConfig(t testing.TB, dut *ondatra.DUTDevice) string { + if ondatra.DUT(t, "dut").Vendor() == ondatra.CISCO { + runningConfig, err := dut.RawAPIs().CLI(t).RunCommand(context.Background(), "show running-config") + if err != nil { + t.Fatalf("'show running-config' failed: %v", err) + } + return runningConfig.Output() + } + return "" +} + // Implementation Note // // Tests have three push variants: ItemOp, ContainerOp, and RootOp. @@ -108,8 +138,10 @@ func TestGetSet(t *testing.T) { // Configuring basic interface and network instance as some devices only populate OC after configuration. gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), dut)) gnmi.Replace(t, dut, gnmi.OC().Interface(p2.Name()).Config(), dutPort2.NewOCInterface(p2.Name(), dut)) - gnmi.Replace(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Type().Config(), - oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_DEFAULT_INSTANCE) + gnmi.Update(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Config(), &oc.NetworkInstance{ + Name: ygot.String(deviations.DefaultNetworkInstance(dut)), + Type: oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_DEFAULT_INSTANCE, + }) scope := defaultPushScope(dut) @@ -142,12 +174,12 @@ func TestDeleteInterface(t *testing.T) { op.push(t, dut, config, scope) t.Run("VerifyBeforeDelete", func(t *testing.T) { - v1 := gnmi.Lookup(t, dut, q1) - if got1, ok := v1.Val(); !ok || got1 != want1 { + v1, ok := gnmi.Await(t, dut, q1, 60*time.Second, want1).Val() + if !ok { t.Errorf("State got %v, want %v", v1, want1) } - v2 := gnmi.Lookup(t, dut, q2) - if got2, ok := v2.Val(); !ok || got2 != want2 { + v2, ok := gnmi.Await(t, dut, q2, 60*time.Second, want2).Val() + if !ok { t.Errorf("State got %v, want %v", v2, want2) } }) @@ -157,12 +189,8 @@ func TestDeleteInterface(t *testing.T) { config.DeleteInterface(p1.Name()) config.DeleteInterface(p2.Name()) - for _, iname := range scope.interfaces { - iface := config.GetInterface(iname) - if iface == nil { - config.Interface = nil - - } + if len(config.Interface) == 0 { + config.Interface = nil } op.push(t, dut, config, scope) @@ -208,6 +236,10 @@ func TestReuseIP(t *testing.T) { forEachPushOp(t, dut, func(t *testing.T, op pushOp, config *oc.Root) { t.Log("Initialize") + if deviations.SkipMacaddressCheck(dut) { + *setEthernetFromState = false + } + config.DeleteInterface(p1.Name()) config.DeleteInterface(agg1) configMember(config.GetOrCreateInterface(p1.Name()), agg1, dut) @@ -321,6 +353,10 @@ func TestDeleteNonDefaultVRF(t *testing.T) { config.DeleteInterface(p1.Name()) config.DeleteInterface(p2.Name()) + if deviations.ReorderCallsForVendorCompatibilty(dut) { + op.push(t, dut, config, scope) + } + ip1.ConfigOCInterface(config.GetOrCreateInterface(p1.Name()), dut) ip2.ConfigOCInterface(config.GetOrCreateInterface(p2.Name()), dut) @@ -328,8 +364,8 @@ func TestDeleteNonDefaultVRF(t *testing.T) { ni := config.GetOrCreateNetworkInstance(vrf) ni.Type = oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_L3VRF - id1 := attachInterface(ni, p1.Name(), 0) - id2 := attachInterface(ni, p2.Name(), 0) + id1 := attachInterface(dut, ni, p1.Name(), 0) + id2 := attachInterface(dut, ni, p2.Name(), 0) op.push(t, dut, config, scope) @@ -341,7 +377,10 @@ func TestDeleteNonDefaultVRF(t *testing.T) { }) t.Log("Cleanup") - + if deviations.ReorderCallsForVendorCompatibilty(dut) { + config.DeleteInterface(p1.Name()) + config.DeleteInterface(p2.Name()) + } config.DeleteNetworkInstance(vrf) op.push(t, dut, config, scope) @@ -369,6 +408,7 @@ func testMoveInterfaceBetweenVRF(t *testing.T, dut *ondatra.DUTDevice, firstVRF, p1 := dut.Port(t, "port1") p2 := dut.Port(t, "port2") + var id1, id2 string scope := &pushScope{ interfaces: []string{p1.Name(), p2.Name()}, @@ -387,11 +427,18 @@ func testMoveInterfaceBetweenVRF(t *testing.T, dut *ondatra.DUTDevice, firstVRF, config.DeleteNetworkInstance(firstVRF) ni := config.GetOrCreateNetworkInstance(firstVRF) ni.Type = oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_L3VRF + // add interface to firstVRF + if deviations.ReorderCallsForVendorCompatibilty(dut) { + id1 = attachInterface(dut, ni, p1.Name(), 0) + id2 = attachInterface(dut, ni, p2.Name(), 0) + } } - firstni := config.GetOrCreateNetworkInstance(firstVRF) - id1 := attachInterface(firstni, p1.Name(), 0) - id2 := attachInterface(firstni, p2.Name(), 0) + if !deviations.ReorderCallsForVendorCompatibilty(dut) { + firstni := config.GetOrCreateNetworkInstance(firstVRF) + id1 = attachInterface(dut, firstni, p1.Name(), 0) + id2 = attachInterface(dut, firstni, p2.Name(), 0) + } config.DeleteNetworkInstance(secondVRF) if *cannotDeleteVRF { @@ -404,9 +451,11 @@ func testMoveInterfaceBetweenVRF(t *testing.T, dut *ondatra.DUTDevice, firstVRF, t.Run("VerifyBeforeMove", func(t *testing.T) { verifyInterface(t, dut, p1.Name(), &ip1) verifyInterface(t, dut, p2.Name(), &ip2) - verifyAttachment(t, dut, firstVRF, id1, p1.Name()) - verifyAttachment(t, dut, firstVRF, id2, p2.Name()) - + // verify the added interface to first Non default VRF + if !deviations.ReorderCallsForVendorCompatibilty(dut) || firstVRF != defaultVRF { + verifyAttachment(t, dut, firstVRF, id1, p1.Name()) + verifyAttachment(t, dut, firstVRF, id2, p2.Name()) + } // We don't check /network-instances/network-instance/vlans/vlan/members because // these are for L2 switched ports, not L3 routed ports. }) @@ -416,18 +465,37 @@ func testMoveInterfaceBetweenVRF(t *testing.T, dut *ondatra.DUTDevice, firstVRF, if firstVRF != defaultVRF { // It is not necessary to explicitly remove the interface attachments since the VRF // is being deleted. + // delete interface before deleting NI + if deviations.ReorderCallsForVendorCompatibilty(dut) { + config.DeleteInterface(p1.Name()) + config.DeleteInterface(p2.Name()) + } config.DeleteNetworkInstance(firstVRF) } else { - // Remove just the interface attachments but keep the VRF. - firstni.DeleteInterface(id1) - firstni.DeleteInterface(id2) + // Delete interface from default NI before modifying the attachement + if deviations.ReorderCallsForVendorCompatibilty(dut) { + config.DeleteInterface(p1.Name()) + config.DeleteInterface(p2.Name()) + } else { + // Remove just the interface attachments but keep the VRF. + firstni := config.GetOrCreateNetworkInstance(firstVRF) + firstni.DeleteInterface(id1) + firstni.DeleteInterface(id2) + } } + op.push(t, dut, config, scope) secondni := config.GetOrCreateNetworkInstance(secondVRF) secondni.Type = oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_L3VRF - attachInterface(secondni, p1.Name(), 0) - attachInterface(secondni, p2.Name(), 0) - + if deviations.ReorderCallsForVendorCompatibilty(dut) { + id1 = attachInterface(dut, secondni, p1.Name(), 0) + id2 = attachInterface(dut, secondni, p2.Name(), 0) + ip1.ConfigOCInterface(config.GetOrCreateInterface(p1.Name()), dut) + ip2.ConfigOCInterface(config.GetOrCreateInterface(p2.Name()), dut) + } else { + attachInterface(dut, secondni, p1.Name(), 0) + attachInterface(dut, secondni, p2.Name(), 0) + } op.push(t, dut, config, scope) t.Run("VerifyAfterMove", func(t *testing.T) { @@ -438,7 +506,11 @@ func testMoveInterfaceBetweenVRF(t *testing.T, dut *ondatra.DUTDevice, firstVRF, }) t.Log("Cleanup") - + // delete interface before deleting NI + if deviations.ReorderCallsForVendorCompatibilty(dut) { + config.DeleteInterface(p1.Name()) + config.DeleteInterface(p2.Name()) + } config.DeleteNetworkInstance(secondVRF) op.push(t, dut, config, scope) @@ -447,6 +519,9 @@ func testMoveInterfaceBetweenVRF(t *testing.T, dut *ondatra.DUTDevice, firstVRF, func TestStaticProtocol(t *testing.T) { dut := ondatra.DUT(t, "dut") + if deviations.SkipContainerOp(dut) { + *skipContainerOp = true + } if deviations.StaticRouteNextHopInterfaceRefUnsupported(dut) { t.Skip() } @@ -487,8 +562,8 @@ func TestStaticProtocol(t *testing.T) { otherni := config.GetOrCreateNetworkInstance(otherVRF) otherni.Type = oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_L3VRF - id1 := attachInterface(otherni, p1.Name(), 0) - id2 := attachInterface(otherni, p2.Name(), 0) + id1 := attachInterface(dut, otherni, p1.Name(), 0) + id2 := attachInterface(dut, otherni, p2.Name(), 0) protocol := otherni.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, staticName) @@ -600,7 +675,11 @@ func TestStaticProtocol(t *testing.T) { }) t.Log("Cleanup") - + // delete interface before deleting NI + if deviations.ReorderCallsForVendorCompatibilty(dut) { + config.DeleteInterface(p1.Name()) + config.DeleteInterface(p2.Name()) + } config.DeleteNetworkInstance(otherVRF) config.DeleteNetworkInstance(unusedVRF) op.push(t, dut, config, scope) @@ -673,8 +752,8 @@ func configAggregate(i *oc.Interface, a *attrs.Attributes, dut *ondatra.DUTDevic func verifyMember(t testing.TB, p *ondatra.Port, aggID string) { t.Helper() q := gnmi.OC().Interface(p.Name()).Ethernet().AggregateId().State() - v := gnmi.Lookup(t, p.Device(), q) - if got, ok := v.Val(); !ok || got != aggID { + v, ok := gnmi.Await(t, p.Device(), q, 60*time.Second, aggID).Val() + if !ok { t.Errorf("State got %v, want %v", v, aggID) } } @@ -683,9 +762,9 @@ func verifyMember(t testing.TB, p *ondatra.Port, aggID string) { func verifyAggregate(t testing.TB, dev gnmi.DeviceOrOpts, aggID string, a *attrs.Attributes) { t.Helper() q := gnmi.OC().Interface(aggID).Aggregation().LagType().State() - v := gnmi.Lookup(t, dev, q) const want = oc.IfAggregate_AggregationType_STATIC - if got, ok := v.Val(); !ok || got != want { + v, ok := gnmi.Await(t, dev, q, 60*time.Second, want).Val() + if !ok { t.Errorf("State got %v, want %v", v, want) } verifyInterface(t, dev, aggID, a) @@ -695,8 +774,8 @@ func verifyAggregate(t testing.TB, dev gnmi.DeviceOrOpts, aggID string, a *attrs func verifyInterface(t testing.TB, dev gnmi.DeviceOrOpts, name string, a *attrs.Attributes) { t.Helper() q := gnmi.OC().Interface(name).Subinterface(0).Ipv4().Address(a.IPv4).PrefixLength().State() - v := gnmi.Lookup(t, dev, q) - if got, ok := v.Val(); !ok || got != a.IPv4Len { + v, ok := gnmi.Await(t, dev, q, 60*time.Second, a.IPv4Len).Val() + if !ok { t.Errorf("State got %v, want %v", v, a.IPv4Len) } else { t.Logf("Verified %v", v) @@ -704,12 +783,14 @@ func verifyInterface(t testing.TB, dev gnmi.DeviceOrOpts, name string, a *attrs. } // attachInterface attaches an interface name and subinterface sub to a network instance. -func attachInterface(ni *oc.NetworkInstance, name string, sub int) string { +func attachInterface(dut *ondatra.DUTDevice, ni *oc.NetworkInstance, name string, sub int) string { id := name // Possibly vendor specific? May have to use sub. niface := ni.GetOrCreateInterface(id) niface.Interface = ygot.String(name) niface.Subinterface = ygot.Uint32(uint32(sub)) - id = fmt.Sprintf("%s.%d", id, sub) + if deviations.InterfaceRefInterfaceIDFormat(dut) { + id = fmt.Sprintf("%s.%d", id, sub) + } return id } @@ -718,8 +799,8 @@ func attachInterface(ni *oc.NetworkInstance, name string, sub int) string { func verifyAttachment(t testing.TB, dev gnmi.DeviceOrOpts, vrf string, id string, name string) { t.Helper() q := gnmi.OC().NetworkInstance(vrf).Interface(id).Interface().State() - v := gnmi.Lookup(t, dev, q) - if got, ok := v.Val(); !ok || got != name { + v, ok := gnmi.Await(t, dev, q, 60*time.Second, name).Val() + if !ok { t.Errorf("State got %v, want %v", v, name) } else { t.Logf("Verified %v", v) @@ -789,6 +870,56 @@ func getDeviceConfig(t testing.TB, dev gnmi.DeviceOrOpts) *oc.Root { config := gnmi.Get[*oc.Root](t, dev, gnmi.OC().Config()) fptest.WriteQuery(t, "Untouched", gnmi.OC().Config(), config) + // load the base oc config from the device state when no oc config is loaded + if !*baseOCConfigIsPresent { + if ondatra.DUT(t, "dut").Vendor() == ondatra.CISCO { + intfsState := gnmi.GetAll(t, dev, gnmi.OC().InterfaceAny().State()) + for _, intf := range intfsState { + ygot.PruneConfigFalse(oc.SchemaTree["Interface"], intf) + config.DeleteInterface(intf.GetName()) + if intf.GetName() == "Loopback0" || intf.GetName() == "PTP0/RP1/CPU0/0" || intf.GetName() == "Null0" || intf.GetName() == "PTP0/RP0/CPU0/0" { + continue + } + intf.ForwardingViable = nil + intf.Mtu = nil + intf.HoldTime = nil + if intf.Subinterface != nil { + if intf.Subinterface[0].Ipv6 != nil { + intf.Subinterface[0].Ipv6.Autoconf = nil + } + } + config.AppendInterface(intf) + } + vrfsStates := gnmi.GetAll(t, dev, gnmi.OC().NetworkInstanceAny().State()) + for _, vrf := range vrfsStates { + // only needed for containerOp + if vrf.GetName() == "**iid" { + continue + } + if vrf.GetName() == "DEFAULT" { + config.NetworkInstance = nil + vrf.Interface = nil + for _, ni := range config.NetworkInstance { + ni.Mpls = nil + } + } + ygot.PruneConfigFalse(oc.SchemaTree["NetworkInstance"], vrf) + vrf.Table = nil + vrf.RouteLimit = nil + vrf.Mpls = nil + for _, intf := range vrf.Interface { + intf.AssociatedAddressFamilies = nil + } + for _, protocol := range vrf.Protocol { + for _, routes := range protocol.Static { + routes.Description = nil + } + } + config.AppendNetworkInstance(vrf) + } + } + } + if *pruneComponents { for cname, component := range config.Component { // Keep the port components in order to preserve the breakout-mode config. @@ -810,16 +941,39 @@ func getDeviceConfig(t testing.TB, dev gnmi.DeviceOrOpts) *oc.Root { // Ethernet config may not contain meaningful values if it wasn't explicitly // configured, so use its current state for the config, but prune non-config leaves. intf := gnmi.Get(t, dev, gnmi.OC().Interface(iname).State()) - breakout := config.GetComponent(intf.GetHardwarePort()).GetPort().GetBreakoutMode() e := intf.GetEthernet() - // Set port speed to unknown for non breakout interfaces - if breakout.GetGroup(1) == nil && e != nil { - e.SetPortSpeed(oc.IfEthernet_ETHERNET_SPEED_SPEED_UNKNOWN) + if len(intf.GetHardwarePort()) != 0 { + breakout := config.GetComponent(intf.GetHardwarePort()).GetPort().GetBreakoutMode() + e := intf.GetEthernet() + // Set port speed to unknown for non breakout interfaces + if breakout.GetGroup(1) == nil && e != nil { + e.SetPortSpeed(oc.IfEthernet_ETHERNET_SPEED_SPEED_UNKNOWN) + } } ygot.PruneConfigFalse(oc.SchemaTree["Interface_Ethernet"], e) if e.PortSpeed != 0 && e.PortSpeed != oc.IfEthernet_ETHERNET_SPEED_SPEED_UNKNOWN { iface.Ethernet = e } + // need to set mac address for mgmt interface to nil + if intf.GetName() == "MgmtEth0/RP0/CPU0/0" || intf.GetName() == "MgmtEth0/RP1/CPU0/0" && deviations.SkipMacaddressCheck(ondatra.DUT(t, "dut")) { + e.MacAddress = nil + } + // need to set mac address for bundle interface to nil + if iface.Ethernet.AggregateId != nil && deviations.SkipMacaddressCheck(ondatra.DUT(t, "dut")) { + iface.Ethernet.MacAddress = nil + continue + } + } + } + + if !*cannotConfigurePortSpeed { + for _, iface := range config.Interface { + if iface.GetEthernet() == nil { + continue + } + iface.GetEthernet().PortSpeed = oc.IfEthernet_ETHERNET_SPEED_UNSET + iface.GetEthernet().DuplexMode = oc.Ethernet_DuplexMode_UNSET + iface.GetEthernet().EnableFlowControl = nil } } @@ -887,7 +1041,14 @@ func (op rootOp) push(t testing.TB, dev gnmi.DeviceOrOpts, config *oc.Root, _ *p setEthernetFromBase(t, op.base, config) } fptest.WriteQuery(t, "RootOp", gnmi.OC().Config(), config) - gnmi.Replace(t, dev, gnmi.OC().Config(), config) + dut := ondatra.DUT(t, "dut") + if deviations.AddMissingBaseConfigViaCli(dut) { + if ondatra.DUT(t, "dut").Vendor() == ondatra.CISCO { + addMissingConfigForRootReplace(t, dev, config) + } + } else { + gnmi.Replace(t, dev, gnmi.OC().Config(), config) + } } // containerOp pushes config using replace of containers of lists directly under root in @@ -905,6 +1066,22 @@ func (op containerOp) push(t testing.TB, dev gnmi.DeviceOrOpts, config *oc.Root, fptest.WriteQuery(t, "ContainerOp", gnmi.OC().Config(), config) batch := &gnmi.SetBatch{} + if deviations.AddMissingBaseConfigViaCli(ondatra.DUT(t, "dut")) { + if ondatra.DUT(t, "dut").Vendor() == ondatra.CISCO { + supContainerConfig := addMissingConfigForContainerReplace(t, dev) + for port, data := range supContainerConfig { + gnmi.Update(t, ondatra.DUT(t, "dut"), gnmi.OC().Component(port).Config(), &oc.Component{ + Name: ygot.String(port), + }) + bmode := &oc.Component_Port_BreakoutMode{} + gp := bmode.GetOrCreateGroup(0) + gp.BreakoutSpeed = data.breakoutSpeed + gp.NumBreakouts = ygot.Uint8(*data.numPhysicalChannels + 1) + bmp := gnmi.OC().Component(port).Port().BreakoutMode() + gnmi.BatchReplace(batch, bmp.Config(), bmode) + } + } + } gnmi.BatchReplace(batch, interfacesQuery, &Interfaces{Interface: config.Interface}) gnmi.BatchReplace(batch, networkInstancesQuery, &NetworkInstances{NetworkInstance: config.NetworkInstance}) batch.Set(t, dev) @@ -1034,3 +1211,106 @@ type NetworkInstances struct { } func (*NetworkInstances) IsYANGGoStruct() {} + +func removeStatementsBetweenWords(inputStr, startWord, endWord string, opts ...*Options) string { + lines := strings.Split(inputStr, "\n") + result := []string{} + betweenWords := false + var start bool + for _, line := range lines { + if strings.HasPrefix(line, startWord) { + if len(opts) != 0 { + for _, opt := range opts { + for _, intf := range opt.interfaces { + if strings.Contains(line, intf) { + start = true + betweenWords = true + continue + } + } + } + } else { + start = true + betweenWords = true + continue + } + } + if strings.HasPrefix(line, endWord) { + betweenWords = false + if start == true { + start = false + continue + } + } + if !betweenWords { + result = append(result, line) + } + } + return strings.Join(result, "\n") +} + +func addMissingConfigForContainerReplace(t testing.TB, dev gnmi.DeviceOrOpts) map[string]breakout { + intfsState := gnmi.GetAll(t, dev, gnmi.OC().InterfaceAny().State()) + breakoutPortsMap := make(map[string]breakout) // which holds map of optic: {BreakoutSpeed:10, NumBreakouts:4} + port := make(map[string]uint8) + var trackspeed oc.E_IfEthernet_ETHERNET_SPEED + + for _, intf := range intfsState { + if intf.HardwarePort == nil || intf.PhysicalChannel == nil { + continue + } + hwp := strings.Split(intf.GetHardwarePort(), "Port")[1] + name := strings.Split(intf.GetName(), "GigE")[1] + channel := strconv.Itoa(int(intf.GetPhysicalChannel()[0])) + + if hwp+"/"+(channel) == name { + var speed oc.E_IfEthernet_ETHERNET_SPEED + + _, keyExists := breakoutPortsMap[intf.GetHardwarePort()] + if !keyExists && speed == oc.IfEthernet_ETHERNET_SPEED_UNSET { + if intf.GetEthernet().PortSpeed.String() == "SPEED_100GB" { + trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_100GB + } else if intf.GetEthernet().PortSpeed.String() == "SPEED_10GB" { + trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_10GB + } + } + + numChannels := make([]*uint8, len(intf.GetPhysicalChannel())) + truncated := uint8(intf.GetPhysicalChannel()[0]) + numChannels[0] = &truncated + + _, keyExists = port[intf.GetHardwarePort()] + if !keyExists { + breakoutPortsMap[intf.GetHardwarePort()] = breakout{numPhysicalChannels: numChannels[0], breakoutSpeed: trackspeed} + port[intf.GetHardwarePort()] = 0 + } + + if port[intf.GetHardwarePort()] < *numChannels[0] { + breakoutPortsMap[intf.GetHardwarePort()] = breakout{numPhysicalChannels: numChannels[0], breakoutSpeed: trackspeed} + port[intf.GetHardwarePort()] = *numChannels[0] + } + } + } + return breakoutPortsMap +} + +func addMissingConfigForRootReplace(t testing.TB, dev gnmi.DeviceOrOpts, config *oc.Root) { + batch := &gnmi.SetBatch{} + running := showRunningConfig(t, ondatra.DUT(t, "dut")) + //editing config while removing NI and interface since it will be part of another replace call + data := "hostname " + strings.Split(running, "hostname ")[1] + modifiedStr := strings.Replace(data, "\r\n", "\n", -1) + // remove interface config from the running configure + fileString := removeStatementsBetweenWords(modifiedStr, "interface ", "!", &Options{interfaces: []string{"HundredGigE", "FourHundredGigE", "TenGigE", "Bundle-Ether", "Loopback", "MgmtEth0", "FortyGigE", "PTP0/RP"}}) + // remove router static config from the running config + fileString = removeStatementsBetweenWords(fileString, "router static ", "!") + // need to explicitly remove configured NI "BLUE" since it is still present in running config and will overwrite config parameter which doesn't set it + fileString = removeStatementsBetweenWords(fileString, "vrf BLUE", "!") + cliPath, err := schemaless.NewConfig[string]("", "cli") + if err != nil { + t.Fatalf("Failed to create CLI ygnmi query: %v", err) + } + gnmi.BatchReplace(batch, cliPath, fileString) + gnmi.BatchReplace(batch, gnmi.OC().Config(), config) + batch.Set(t, dev) +} diff --git a/feature/system/gnmi/set/tests/gnmi_set_test/metadata.textproto b/feature/system/gnmi/set/tests/gnmi_set_test/metadata.textproto index d6df13be2ef..9fe05d0e4b2 100644 --- a/feature/system/gnmi/set/tests/gnmi_set_test/metadata.textproto +++ b/feature/system/gnmi/set/tests/gnmi_set_test/metadata.textproto @@ -10,7 +10,10 @@ platform_exceptions: { vendor: CISCO } deviations: { - ipv4_missing_enabled: true + skip_container_op: true + reorder_calls_for_vendor_compatibilty: true + add_missing_base_config_via_cli: true + skip_macaddress_check: true } } platform_exceptions: { diff --git a/feature/system/logging/remote_syslog/feature.textproto b/feature/system/logging/remote_syslog/feature.textproto index 0a9e873d314..6c14fbb34ba 100644 --- a/feature/system/logging/remote_syslog/feature.textproto +++ b/feature/system/logging/remote_syslog/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "system_logging_remote_syslog" diff --git a/feature/system/management/otg_tests/management_ha_test/README.md b/feature/system/management/otg_tests/management_ha_test/README.md new file mode 100644 index 00000000000..b5e1b4ce730 --- /dev/null +++ b/feature/system/management/otg_tests/management_ha_test/README.md @@ -0,0 +1,169 @@ +# MGT-1: Management HA solution test + +## Summary + +- Test management HA + +## Testbed type + +* https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_4.testbed + +## Procedure + +### Applying configuration + +For each section of configuration below, prepare a gnmi.SetBatch with all the configuration items appended to one SetBatch. Then apply the configuration to the DUT in one gnmi.Set +using the `replace` option + +### Initial Setup: + +* Connect DUT port-1, 2 and 3 to ATE port-1, 2 and 3 +* Create VRF "mgmt" on DUT + * /network-instances/network-instance[name=mgmt]/config/name = mgmt + * /network-instances/network-instance[name=mgmt]/config/route-distinguisher = 64512:100 +* Create an IPv6 networks ```ateNet``` attached to ATE port-1, 2 and 3 +* Create a loopback interface "lo1" on DUT and assign it an IPv6 address + * /interfaces/interface[name=lo1]/config/name = lo1 + * /interfaces/interface[name=lo1]/config/type = softwareLoopback + * /interfaces/interface[name=lo1]/subinterfaces/subinterface[index=0]/ipv6/addresses/address/config/ip + * /interfaces/interface[name=lo1]/subinterfaces/subinterface[index=0]/ipv6/addresses/address/config/prefix-length +* Configure the loopback interface to participate in the VRF "mgmt" + * /network-instances/network-instance[name=mgmt]/interfaces/interface[name=lo1]/config/interface = lo1 + +##### Configure linecard ports to ATE using BGP + +* Configure IPv6 addresses on DUT and ATE ports 1 and 2. Configure them to participate in the VRF "mgmt" + * /interfaces/interface/subinterfaces/subinterface/ipv6/addresses/address/config/ip + * /interfaces/interface/subinterfaces/subinterface/ipv6/addresses/address/config/prefix-length + * /network-instances/network-instance[name=mgmt]/interfaces/interface/config/interface +* Configure IPv6 eBGP between DUT Port-1 <--> ATE Port-1 and DUT Port-2 <--> ATE Port-2 in VRF "mgmt" + * /network-instances/network-instance[name=mgmt]/protocols/protocol[identifier=BGP, name=BGP]/global/config/as = 64512 + * /network-instances/network-instance[name=mgmt]/protocols/protocol[identifier=BGP, name=BGP]/global/config/router-id = + * /network-instances/network-instance[name=mgmt]/protocols/protocol[identifier=BGP, name=BGP]/neighbor/config/peer-as = 64511 +* Set default import and export policy to ```ACCEPT_ROUTE``` for the eBGP sessions + * /network-instances/network-instance[name=mgmt]/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy + * /network-instances/network-instance[name=mgmt]/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy +* Advertise a default route from ATE to DUT throught both the BGP sessions +* Redistribute the loopback interface from DUT to ATE through both the BGP sessions + ##### Configure redistribution + * Set address-family to ```IPV6``` + * /network-instances/network-instance/table-connections/table-connection/config/address-family + * Configure source protocol to ```CONNECTED``` + * /network-instances/network-instance/table-connections/table-connection/config/src-protocol + * Configure destination protocol to ```BGP``` + * /network-instances/network-instance/table-connections/table-connection/config/dst-protocol + * Configure default export policy to ```ACCEPT_ROUTE``` + * /network-instances/network-instance/table-connections/table-connection/config/default-export-policy + * Disable metric propogation by setting it to ```true``` + * /network-instances/network-instance/table-connections/table-connection/config/disable-metric-propagation + ##### Configure redistribution + * Configure an IPv6 route-policy definition with the name ```route-policy``` + * /routing-policy/policy-definitions/policy-definition/config/name + * For routing-policy ```route-policy``` configure a statement with the name ```statement``` + * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name + * For routing-policy ```route-policy``` statement ```statement``` set policy-result as ```ACCEPT_ROUTE``` + * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result + ##### Configure a prefix-set for route-filtering/matching + * Configure a prefix-set with the name ```prefix-set``` and mode ```IPV6``` + * /routing-policy/defined-sets/prefix-sets/prefix-set/config/name + * /routing-policy/defined-sets/prefix-sets/prefix-set/config/mode + * For prefix-set ```prefix-set``` set the ip-prefix to ```loopback0 IPv6 address/mask``` and masklength to ```exact``` + * /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/config/ip-prefix + * /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/config/masklength-range + ##### Attach the prefix-set to route-policy + * For routing-policy ```route-policy``` statement ```statement``` set match options to ```ANY``` + * /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/match-set-options + * For routing-policy ```route-policy``` statement ```statement``` set prefix set to ```prefix-set``` + * /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/prefix-set + ##### Attach the route-policy to the redistribution export-policy + * Apply routing policy ```route-policy``` for redistribution to BGP + * /network-instances/network-instance/table-connections/table-connection/config/export-policy + +##### Configure linecard port to ATE + +* Configure IPv6 addresses on DUT Port-3 and ATE Port-3 + * /interfaces/interface/subinterfaces/subinterface/ipv6/addresses/address/config/ip + * /interfaces/interface/subinterfaces/subinterface/ipv6/addresses/address/config/prefix-length +* Configure DUT Port-3 to participate in the VRF "mgmt" + * /network-instances/network-instance[name=mgmt]/interfaces/interface/config/interface +* Configure a static route on ATE device of Port-3 destined to the DUT loopback with next-hop address of DUT Port-3 with administrative-distance or preference of 220 +* Configure a IPv6 default static route on DUT in VRF "mgmt" pointing towards the IPv6 address of ATE Port-3 with Administrative Distance or Preference of 220 + * /network-instances/network-instance[name=mgmt]/protocols/protocol[identifier=STATIC, name=static]/static-routes/static/config/prefix = ::/0 + * /network-instances/network-instance[name=mgmt]/protocols/protocol[identifier=STATIC, name=static]/static-routes/static/next-hops/next-hop/index = 1 + * /network-instances/network-instance[name=mgmt]/protocols/protocol[identifier=STATIC, name=static]/static-routes/static/next-hops/next-hop[index=1]/config/next-hop = ATE Port-3 IP + * /network-instances/network-instance[name=mgmt]/protocols/protocol[identifier=STATIC, name=static]/static-routes/static/next-hops/next-hop/config/preference = 220 + +### MGT-1.1 [TODO: https://github.com/openconfig/featureprofiles/issues/2762] +#### Testing reachability to the DUT loopback with no failures in the network +--- + +* Generate ICMP echo (ping) sourced from the ```ateNet``` network destined towards the DUT loopback1 IPv6 address +* Validate ICMP echo-reply is received by the ATE on Port-1 or Port-2 + +### MGT-1.2 [TODO: https://github.com/openconfig/featureprofiles/issues/2762] +#### Testing BGP redundancy +--- + +* Shutdown BGP session on Port-1 +* Generate ICMP echo (ping) sourced from the ```ateNet``` network destined towards the DUT loopback1 IPv6 address +* Validate ICMP echo-reply is received by the ATE on Port-2 +* Bring up BGP session on Port-1 and shutdowm BGP on Port-2 +* Generate ICMP echo (ping) sourced from the ```ateNet``` network destined towards the DUT loopback1 IPv6 address +* Validate ICMP echo-reply is received by the ATE on Port-1 + +### MGT-1.3 [TODO: https://github.com/openconfig/featureprofiles/issues/2762] +#### Testing failover between BGP and Static route +--- + +* Shutdown BGP session on Port-1 and Port-2 +* Generate ICMP echo (ping) sourced from the ```ateNet``` network destined towards the DUT loopback1 IPv6 address +* Validate ICMP echo-reply is received by the ATE on Port-3 +* Bring up BGP session on Port-1 and Port-2 +* Generate ICMP echo (ping) sourced from the ```ateNet``` network destined towards the DUT loopback1 IPv6 address +* Validate ICMP echo-reply is received by the ATE on Port-1 or Port-2 + +## Config parameter coverage + +* /network-instances/network-instance/config/name +* /network-instances/network-instance/config/route-distinguisher +* /network-instances/network-instance/interfaces/interface/config/interface +* /interfaces/interface/config/name +* /interfaces/interface/config/type +* /interfaces/interface/subinterfaces/subinterface/ipv6/addresses/address/config/ip +* /interfaces/interface/subinterfaces/subinterface/ipv6/addresses/address/config/prefix-length +* /network-instances/network-instance/protocols/protocol/global/config/as +* /network-instances/network-instance/protocols/protocol/global/config/router-id +* /network-instances/network-instance/protocols/protocol/neighbor/config/peer-as +* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy +* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy +* /network-instances/network-instance/table-connections/table-connection/config/address-family +* /network-instances/network-instance/table-connections/table-connection/config/src-protocol +* /network-instances/network-instance/table-connections/table-connection/config/dst-protocol +* /network-instances/network-instance/table-connections/table-connection/config/default-export-policy +* /network-instances/network-instance/table-connections/table-connection/config/disable-metric-propagation +* /routing-policy/policy-definitions/policy-definition/config/name +* /routing-policy/policy-definitions/policy-definition/statements/statement/config/name +* /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result +* /routing-policy/defined-sets/prefix-sets/prefix-set/config/name +* /routing-policy/defined-sets/prefix-sets/prefix-set/config/mode +* /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/config/ip-prefix +* /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/config/masklength-range +* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/match-set-options +* /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/prefix-set +* /network-instances/network-instance/protocols/protocol/static-routes/static/config/prefix +* /network-instances/network-instance/protocols/protocol/static-routes/static/next-hops/next-hop/index +* /network-instances/network-instance/protocols/protocol/static-routes/static/next-hops/next-hop/config/next-hop +* /network-instances/network-instance/protocols/protocol/static-routes/static/next-hops/next-hop/config/preference + +## Telemetry parameter coverage + +* NA + +## Protocol/RPC Parameter Coverage + +* gNMI + * Set (replace) + +## Required DUT platform + +* FFF diff --git a/feature/system/management/otg_tests/management_ha_test/management_ha_test.go b/feature/system/management/otg_tests/management_ha_test/management_ha_test.go new file mode 100644 index 00000000000..d49da2d55ea --- /dev/null +++ b/feature/system/management/otg_tests/management_ha_test/management_ha_test.go @@ -0,0 +1,364 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package management_ha_test + +import ( + "fmt" + "math" + "sort" + "testing" + "time" + + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/cfgplugins" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/otgutils" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ondatra/netutil" + "github.com/openconfig/ygnmi/ygnmi" + "github.com/openconfig/ygot/ygot" +) + +const ( + prefixesStart = "2001:db8:1::1" + prefixP6Len = 128 + prefixesCount = 1 + pathID = 1 + defaultRoute = "0:0:0:0:0:0:0:0" + dutAS = 65501 +) + +var ( + dutlo0Attrs = attrs.Attributes{ + Desc: "Loopback ip", + IPv4: "203.0.113.1", + IPv6: "2001:db8::203:0:113:1", + IPv4Len: 32, + IPv6Len: 128, + } + + mgmtVRF = "mgmtvrf1" + bgpPorts = []string{"port1", "port2"} + + setNoPeerGroup = true + lossTolerance = float64(1) +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +func TestManagementHA1(t *testing.T) { + dut := ondatra.DUT(t, "dut") + p1 := dut.Port(t, "port1") + p2 := dut.Port(t, "port2") + p3 := dut.Port(t, "port3") + p4 := dut.Port(t, "port4") + loopbackIntfName := netutil.LoopbackInterface(t, dut, 1) + + addInterfacesToVRF(t, dut, mgmtVRF, []string{p1.Name(), p2.Name(), p3.Name(), p4.Name(), loopbackIntfName}) + + bs := cfgplugins.NewBGPSession(t, cfgplugins.PortCount4, &mgmtVRF) + bs.WithEBGP( + t, + []oc.E_BgpTypes_AFI_SAFI_TYPE{oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST}, + bgpPorts, + true, + true, + ) + + if setNoPeerGroup { + bs.DUTConf.GetOrCreateNetworkInstance(mgmtVRF).GetOrCreateProtocol(cfgplugins.PTBGP, "BGP").GetOrCreateBgp().PeerGroup = nil + neighbors := bs.DUTConf.GetOrCreateNetworkInstance(mgmtVRF).GetOrCreateProtocol(cfgplugins.PTBGP, "BGP").GetOrCreateBgp().Neighbor + for _, neighbor := range neighbors { + neighbor.PeerGroup = nil + } + } + + configureEmulatedNetworks(bs) + + bs.DUTConf.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)).GetOrCreateProtocol(cfgplugins.PTBGP, "BGP").GetOrCreateBgp().GetOrCreateGlobal().SetAs(dutAS) + bs.DUTConf.GetOrCreateNetworkInstance(mgmtVRF).SetRouteDistinguisher(fmt.Sprintf("%d:%d", dutAS, 100)) + + bs.PushAndStart(t) + if verfied := verifyDUTBGPEstablished(t, bs.DUT, mgmtVRF); verfied { + t.Log("DUT BGP sessions established") + } else { + t.Fatalf("BGP sessions not established") + } + cfgplugins.VerifyOTGBGPEstablished(t, bs.ATE) + + configureLoopbackOnDUT(t, bs.DUT) + advertiseDUTLoopbackToATE(t, bs.DUT) + configureStaticRoute(t, bs.DUT, bs.ATEPorts[2].IPv6) + configureImportExportBGPPolicy(t, bs) + + t.Run("traffic received by port1 or port2", func(t *testing.T) { + createFlowV6(t, bs) + otgutils.WaitForARP(t, bs.ATE.OTG(), bs.ATETop, "IPv6") + bs.ATE.OTG().StartTraffic(t) + time.Sleep(30 * time.Second) + bs.ATE.OTG().StopTraffic(t) + otgutils.LogFlowMetrics(t, bs.ATE.OTG(), bs.ATETop) + otgutils.LogPortMetrics(t, bs.ATE.OTG(), bs.ATETop) + lossV6 := otgutils.GetFlowLossPct(t, bs.ATE.OTG(), "v6Flow", 10*time.Second) + if lossV6 > lossTolerance { + t.Errorf("Loss percent for IPv6 Traffic: got: %f, want %f", lossV6, lossTolerance) + } + }) + + t.Run("traffic received by port2", func(t *testing.T) { + createFlowV6(t, bs) + gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Enabled().Config(), false) + gnmi.Await(t, dut, gnmi.OC().Interface(p1.Name()).AdminStatus().State(), 30*time.Second, oc.Interface_AdminStatus_DOWN) + bs.ATE.OTG().StartTraffic(t) + time.Sleep(30 * time.Second) + bs.ATE.OTG().StopTraffic(t) + otgutils.LogFlowMetrics(t, bs.ATE.OTG(), bs.ATETop) + otgutils.LogPortMetrics(t, bs.ATE.OTG(), bs.ATETop) + framesTx := gnmi.Get(t, bs.ATE.OTG(), gnmi.OTG().Port(bs.ATE.Port(t, "port4").ID()).Counters().OutFrames().State()) + framesRx := gnmi.Get(t, bs.ATE.OTG(), gnmi.OTG().Port(bs.ATE.Port(t, "port2").ID()).Counters().InFrames().State()) + if lossPct(float64(framesTx), float64(framesRx)) > lossTolerance { + t.Errorf("Frames sent/received: got: %d, want: %d", framesRx, framesTx) + } + }) + + t.Run("traffic received by port3", func(t *testing.T) { + createFlowV6(t, bs) + gnmi.Replace(t, dut, gnmi.OC().Interface(p2.Name()).Enabled().Config(), false) + gnmi.Await(t, dut, gnmi.OC().Interface(p2.Name()).AdminStatus().State(), 30*time.Second, oc.Interface_AdminStatus_DOWN) + bs.ATE.OTG().StartTraffic(t) + time.Sleep(30 * time.Second) + bs.ATE.OTG().StopTraffic(t) + otgutils.LogFlowMetrics(t, bs.ATE.OTG(), bs.ATETop) + otgutils.LogPortMetrics(t, bs.ATE.OTG(), bs.ATETop) + framesTx := gnmi.Get(t, bs.ATE.OTG(), gnmi.OTG().Port(bs.ATE.Port(t, "port4").ID()).Counters().OutFrames().State()) + framesRx := gnmi.Get(t, bs.ATE.OTG(), gnmi.OTG().Port(bs.ATE.Port(t, "port3").ID()).Counters().InFrames().State()) + if lossPct(float64(framesTx), float64(framesRx)) > lossTolerance { + t.Errorf("Frames sent/received: got: %d, want: %d", framesRx, framesTx) + } + }) + + t.Run("traffic received by port1", func(t *testing.T) { + createFlowV6(t, bs) + gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Enabled().Config(), true) + gnmi.Await(t, dut, gnmi.OC().Interface(p1.Name()).AdminStatus().State(), 30*time.Second, oc.Interface_AdminStatus_UP) + time.Sleep(30 * time.Second) + bs.ATE.OTG().StartTraffic(t) + time.Sleep(30 * time.Second) + bs.ATE.OTG().StopTraffic(t) + otgutils.LogFlowMetrics(t, bs.ATE.OTG(), bs.ATETop) + otgutils.LogPortMetrics(t, bs.ATE.OTG(), bs.ATETop) + framesTx := gnmi.Get(t, bs.ATE.OTG(), gnmi.OTG().Port(bs.ATE.Port(t, "port4").ID()).Counters().OutFrames().State()) + framesRx := gnmi.Get(t, bs.ATE.OTG(), gnmi.OTG().Port(bs.ATE.Port(t, "port1").ID()).Counters().InFrames().State()) + if lossPct(float64(framesTx), float64(framesRx)) > lossTolerance { + t.Errorf("Frames sent/received: got: %d, want: %d", framesRx, framesTx) + } + }) +} + +func createFlowV6(t *testing.T, bs *cfgplugins.BGPSession) { + bs.ATETop.Flows().Clear() + + t.Log("Configuring v6 traffic flow") + v6Flow := bs.ATETop.Flows().Add().SetName("v6Flow") + v6Flow.Metrics().SetEnable(true) + v6Flow.TxRx().Device(). + SetTxNames([]string{"port4.IPv6"}). + SetRxNames([]string{"port1.BGP4.peer.rr6", "port2.BGP4.peer.rr6", "port3.IPv6"}) + v6Flow.Size().SetFixed(512) + v6Flow.Rate().SetPps(100) + v6Flow.Duration().Continuous() + e1 := v6Flow.Packet().Add().Ethernet() + e1.Src().SetValues([]string{bs.ATEPorts[3].MAC}) + v6 := v6Flow.Packet().Add().Ipv6() + v6.Src().SetValue(bs.ATEPorts[3].IPv6) + v6.Dst().Increment().SetStart(prefixesStart).SetCount(1) + icmp1 := v6Flow.Packet().Add().Icmp() + icmp1.SetEcho(gosnappi.NewFlowIcmpEcho()) + + bs.ATE.OTG().PushConfig(t, bs.ATETop) + bs.ATE.OTG().StartProtocols(t) +} + +func configureStaticRoute(t *testing.T, dut *ondatra.DUTDevice, nextHopIP string) { + c := &oc.NetworkInstance_Protocol{ + Identifier: oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, + Name: ygot.String(deviations.StaticProtocolName(dut)), + } + s := c.GetOrCreateStatic(defaultRoute + "/0") + nh := s.GetOrCreateNextHop("0") + nh.NextHop = oc.UnionString(nextHopIP) + if deviations.SetMetricAsPreference(dut) { + nh.Metric = ygot.Uint32(220) + } else { + nh.Preference = ygot.Uint32(220) + } + sp := gnmi.OC().NetworkInstance(mgmtVRF).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(dut)) + gnmi.Update(t, dut, sp.Config(), c) + gnmi.Replace(t, dut, sp.Static(defaultRoute+"/0").Config(), s) +} + +func configureEmulatedNetworks(bs *cfgplugins.BGPSession) { + devices := bs.ATETop.Devices().Items() + byName := func(i, j int) bool { return devices[i].Name() < devices[j].Name() } + sort.Slice(devices, byName) + for i, otgPort := range bs.ATEPorts[:len(bgpPorts)] { + ipv6 := devices[i].Ethernets().Items()[0].Ipv6Addresses().Items()[0] + bgp6Peer := devices[i].Bgp().Ipv6Interfaces().Items()[0].Peers().Items()[0] + bgp6PeerRoute := bgp6Peer.V6Routes().Add() + bgp6PeerRoute.SetName(otgPort.Name + ".BGP4.peer.rr6") + bgp6PeerRoute.SetNextHopIpv6Address(ipv6.Address()) + bgp6PeerRoute.SetNextHopAddressType(gosnappi.BgpV6RouteRangeNextHopAddressType.IPV6) + bgp6PeerRoute.SetNextHopMode(gosnappi.BgpV6RouteRangeNextHopMode.MANUAL) + bgp6PeerRoute.AddPath().SetPathId(pathID) + + bgp6PeerRoute.Addresses().Add().SetAddress(prefixesStart).SetPrefix(prefixP6Len).SetCount(prefixesCount) + bgp6PeerRoute.Addresses().Add().SetAddress(defaultRoute).SetPrefix(0) + } +} + +func configureLoopbackOnDUT(t *testing.T, dut *ondatra.DUTDevice) { + loopbackIntfName := netutil.LoopbackInterface(t, dut, 1) + loop := dutlo0Attrs.NewOCInterface(loopbackIntfName, dut) + loop.Type = oc.IETFInterfaces_InterfaceType_softwareLoopback + gnmi.Update(t, dut, gnmi.OC().Interface(loopbackIntfName).Config(), loop) + t.Logf("Got DUT IPv6 loopback address: %v", dutlo0Attrs.IPv6) +} + +func addInterfacesToVRF(t *testing.T, dut *ondatra.DUTDevice, vrfname string, intfNames []string) { + root := &oc.Root{} + mgmtNI := root.GetOrCreateNetworkInstance(vrfname) + mgmtNI.Type = oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_L3VRF + for _, intfName := range intfNames { + vi := mgmtNI.GetOrCreateInterface(intfName) + vi.Interface = ygot.String(intfName) + vi.Subinterface = ygot.Uint32(0) + } + gnmi.Replace(t, dut, gnmi.OC().NetworkInstance(mgmtVRF).Config(), mgmtNI) + t.Logf("Added interface %v to VRF %s", intfNames, vrfname) +} + +func verifyDUTBGPEstablished(t *testing.T, dut *ondatra.DUTDevice, ni string) bool { + nSessionState := gnmi.OC().NetworkInstance(ni).Protocol(cfgplugins.PTBGP, "BGP").Bgp().NeighborAny().SessionState().State() + watch := gnmi.WatchAll(t, dut, nSessionState, 2*time.Minute, func(val *ygnmi.Value[oc.E_Bgp_Neighbor_SessionState]) bool { + state, ok := val.Val() + if !ok || state != oc.Bgp_Neighbor_SessionState_ESTABLISHED { + return false + } + return true + }) + if _, ok := watch.Await(t); !ok { + return false + } + return true +} + +func advertiseDUTLoopbackToATE(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + + batchSet := &gnmi.SetBatch{} + + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + pdef := rp.GetOrCreatePolicyDefinition("rp") + stmt, err := pdef.AppendNewStatement("rp-stmt") + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", "rp-stmt", err) + } + stmt.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + + prefixSet := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet("ps") + prefixSet.SetMode(oc.PrefixSet_Mode_IPV6) + prefixSet.GetOrCreatePrefix(dutlo0Attrs.IPv6CIDR(), "exact") + + if !deviations.SkipSetRpMatchSetOptions(dut) { + stmt.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsRestrictedType_ANY) + } + stmt.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetPrefixSet("ps") + gnmi.BatchUpdate(batchSet, gnmi.OC().RoutingPolicy().Config(), rp) + + tableConn := root.GetOrCreateNetworkInstance(mgmtVRF).GetOrCreateTableConnection(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_DIRECTLY_CONNECTED, oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, oc.Types_ADDRESS_FAMILY_IPV6) + if !deviations.SkipSettingDisableMetricPropagation(dut) { + tableConn.SetDisableMetricPropagation(false) + } + tableConn.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + tableConn.SetImportPolicy([]string{"rp"}) + gnmi.BatchUpdate(batchSet, gnmi.OC().NetworkInstance(mgmtVRF).TableConnection(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_DIRECTLY_CONNECTED, oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, oc.Types_ADDRESS_FAMILY_IPV6).Config(), tableConn) + + tableConn1 := root.GetOrCreateNetworkInstance(mgmtVRF).GetOrCreateTableConnection(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_DIRECTLY_CONNECTED, oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, oc.Types_ADDRESS_FAMILY_IPV4) + tableConn1.SetImportPolicy([]string{"rp"}) + gnmi.BatchUpdate(batchSet, gnmi.OC().NetworkInstance(mgmtVRF).TableConnection(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_DIRECTLY_CONNECTED, oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, oc.Types_ADDRESS_FAMILY_IPV4).Config(), tableConn1) + + batchSet.Set(t, dut) +} + +func configureImportExportBGPPolicy(t *testing.T, bs *cfgplugins.BGPSession) { + root := &oc.Root{} + batchSet := &gnmi.SetBatch{} + + rp := root.GetOrCreateRoutingPolicy() + pdef1 := rp.GetOrCreatePolicyDefinition("importRoutePolicy") + stmt1, err := pdef1.AppendNewStatement("routePolicyStatement1") + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", "routePolicyStatement1", err) + } + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + + prefixSet1 := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet("ps1") + prefixSet1.SetMode(oc.PrefixSet_Mode_IPV6) + prefixSet1.GetOrCreatePrefix(defaultRoute+"/0", "exact") + + if !deviations.SkipSetRpMatchSetOptions(bs.DUT) { + stmt1.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsRestrictedType_ANY) + } + stmt1.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetPrefixSet("ps1") + + pdef2 := rp.GetOrCreatePolicyDefinition("exportRoutePolicy") + stmt2, err := pdef2.AppendNewStatement("routePolicyStatement2") + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", "routePolicyStatement2", err) + } + stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + + prefixSet2 := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet("ps2") + prefixSet2.SetMode(oc.PrefixSet_Mode_IPV6) + prefixSet2.GetOrCreatePrefix(dutlo0Attrs.IPv6CIDR(), "exact") + + if !deviations.SkipSetRpMatchSetOptions(bs.DUT) { + stmt2.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsRestrictedType_ANY) + } + stmt2.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetPrefixSet("ps2") + + gnmi.BatchUpdate(batchSet, gnmi.OC().RoutingPolicy().Config(), rp) + + for _, neighbor := range []string{bs.ATEPorts[0].IPv6, bs.ATEPorts[1].IPv6} { + pathV6 := gnmi.OC().NetworkInstance(mgmtVRF).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp().Neighbor(neighbor).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy() + policyV6 := root.GetOrCreateNetworkInstance(mgmtVRF).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").GetOrCreateBgp().GetOrCreateNeighbor(neighbor).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateApplyPolicy() + policyV6.SetImportPolicy([]string{"importRoutePolicy"}) + policyV6.SetExportPolicy([]string{"exportRoutePolicy"}) + gnmi.BatchReplace(batchSet, pathV6.Config(), policyV6) + } + + batchSet.Set(t, bs.DUT) +} + +func lossPct(tx, rx float64) float64 { + return (math.Abs(tx-rx) * 100) / tx +} diff --git a/feature/system/management/otg_tests/management_ha_test/metadata.textproto b/feature/system/management/otg_tests/management_ha_test/metadata.textproto new file mode 100644 index 00000000000..fd2ab3d9568 --- /dev/null +++ b/feature/system/management/otg_tests/management_ha_test/metadata.textproto @@ -0,0 +1,24 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "b569d5de-0038-43f5-b329-bedce86eec3d" +plan_id: "MGT-1" +description: "Management HA solution test" +testbed: TESTBED_DUT_ATE_4LINKS +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + route_policy_under_afi_unsupported: true + omit_l2_mtu: true + static_protocol_name: "STATIC" + interface_enabled: true + default_network_instance: "default" + skip_set_rp_match_set_options: true + skip_setting_disable_metric_propagation: true + set_metric_as_preference: true + } +} +tags: TAGS_TRANSIT +tags: TAGS_DATACENTER_EDGE diff --git a/feature/system/ntp/feature.textproto b/feature/system/ntp/feature.textproto index a88d35bbf79..ad945baa9dd 100644 --- a/feature/system/ntp/feature.textproto +++ b/feature/system/ntp/feature.textproto @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# proto-file: github.com/openconfig/featureprofiles/proto/feature.proto +# proto-message: FeatureProfile id { name: "system_ntp" diff --git a/feature/system/tests/system_base_test/g_protocol_test.go b/feature/system/tests/system_base_test/g_protocol_test.go index baf1e745196..8e901db64e0 100644 --- a/feature/system/tests/system_base_test/g_protocol_test.go +++ b/feature/system/tests/system_base_test/g_protocol_test.go @@ -24,6 +24,8 @@ import ( "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/binding/introspect" "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" gpb "github.com/openconfig/gnmi/proto/gnmi" spb "github.com/openconfig/gnoi/system" @@ -56,7 +58,7 @@ func TestGNMIClient(t *testing.T) { dut := ondatra.DUT(t, "dut") conn := dialConn(t, dut, introspect.GNMI, 9339) c := gpb.NewGNMIClient(conn) - if _, err := c.Get(context.Background(), &gpb.GetRequest{}); err != nil { + if _, err := c.Get(context.Background(), &gpb.GetRequest{Encoding: gpb.Encoding_JSON_IETF, Path: []*gpb.Path{{Elem: []*gpb.PathElem{}}}}); err != nil { t.Fatalf("gnmi.Get failed: %v", err) } } @@ -76,9 +78,16 @@ func TestGNSIClient(t *testing.T) { dut := ondatra.DUT(t, "dut") conn := dialConn(t, dut, introspect.GNSI, 9339) c := authzpb.NewAuthzClient(conn) - if _, err := c.Get(context.Background(), &authzpb.GetRequest{}); err != nil { - t.Fatalf("gnsi.authz.Get failed: %v", err) + rsp, err := c.Get(context.Background(), &authzpb.GetRequest{}) + if err != nil { + statusError, _ := status.FromError(err) + if statusError.Code() == codes.FailedPrecondition { + t.Logf("Expected error FAILED_PRECONDITION seen for authz Get Request.") + } else { + t.Errorf("Unexpected error during authz Get Request.") + } } + t.Logf("gNSI authz get response is %s", rsp) } // TestGRIBIClient validates that the DUT listens on standard gRIBI Port. diff --git a/go.mod b/go.mod index 0c9db6be366..217b05894c9 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/openconfig/featureprofiles go 1.21 require ( - cloud.google.com/go/pubsub v1.34.0 + cloud.google.com/go/pubsub v1.36.1 cloud.google.com/go/storage v1.36.0 github.com/cisco-open/go-p4 v0.1.2 github.com/go-git/go-billy/v5 v5.5.0 @@ -15,11 +15,11 @@ require ( github.com/google/uuid v1.6.0 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/kr/pretty v0.3.1 - github.com/open-traffic-generator/snappi/gosnappi v1.1.0 + github.com/open-traffic-generator/snappi/gosnappi v1.3.0 github.com/openconfig/entity-naming v0.0.0-20230912181021-7ac806551a31 - github.com/openconfig/gnmi v0.10.0 + github.com/openconfig/gnmi v0.11.0 github.com/openconfig/gnoi v0.4.0 - github.com/openconfig/gnoigo v0.0.0-20240214160338-8660e1580468 + github.com/openconfig/gnoigo v0.0.0-20240320202954-ebd033e3542c github.com/openconfig/gnsi v1.4.0 github.com/openconfig/gocloser v0.0.0-20220310182203-c6c950ed3b0b github.com/openconfig/goyang v1.4.5 @@ -27,36 +27,39 @@ require ( github.com/openconfig/gribigo v0.0.0-20231213034307-d0abeba7f432 github.com/openconfig/kne v0.1.17 github.com/openconfig/models-ci v1.0.2-0.20231113233730-f0986391428e - github.com/openconfig/ondatra v0.5.4 + github.com/openconfig/ondatra v0.5.6 github.com/openconfig/replayer v0.0.0-20240110192655-4e9cf83d8d30 github.com/openconfig/testt v0.0.0-20220311054427-efbb1a32ec07 github.com/openconfig/ygnmi v0.11.1 github.com/openconfig/ygot v0.29.18 github.com/p4lang/p4runtime v1.4.0-rc.5.0.20220728214547-13f0d02a521e + github.com/pborman/uuid v1.2.1 github.com/protocolbuffers/txtpbfmt v0.0.0-20220608084003-fc78c767cd6a github.com/yoheimuta/go-protoparser/v4 v4.9.0 + github.com/yuin/goldmark v1.4.13 golang.org/x/crypto v0.21.0 - golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 + golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 golang.org/x/text v0.14.0 - google.golang.org/api v0.155.0 - google.golang.org/grpc v1.62.1 + google.golang.org/api v0.162.0 + google.golang.org/grpc v1.63.0 google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v2 v2.4.0 + gopkg.in/yaml.v3 v3.0.1 k8s.io/klog/v2 v2.100.1 ) require ( github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - golang.org/x/oauth2 v0.16.0 + golang.org/x/oauth2 v0.17.0 ) require ( bitbucket.org/creachadair/stringset v0.0.14 // indirect cloud.google.com/go v0.112.0 // indirect - cloud.google.com/go/compute v1.23.3 // indirect + cloud.google.com/go/compute v1.24.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v1.1.5 // indirect + cloud.google.com/go/iam v1.1.6 // indirect dario.cat/mergo v1.0.0 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/aristanetworks/arista-ceoslab-operator/v2 v2.0.2 // indirect @@ -65,7 +68,6 @@ require ( github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect - github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe // indirect github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa // indirect github.com/creack/pty v1.1.18 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect @@ -79,7 +81,7 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect @@ -116,8 +118,7 @@ require ( github.com/openconfig/grpctunnel v0.0.0-20220819142823-6f5422b8ca70 // indirect github.com/openconfig/lemming/operator v0.2.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect - github.com/pborman/uuid v1.2.1 // indirect - github.com/pelletier/go-toml/v2 v2.1.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.0 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect @@ -139,28 +140,27 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect - go.opentelemetry.io/otel v1.21.0 // indirect - go.opentelemetry.io/otel/metric v1.21.0 // indirect - go.opentelemetry.io/otel/trace v1.21.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect + go.opentelemetry.io/otel v1.22.0 // indirect + go.opentelemetry.io/otel/metric v1.22.0 // indirect + go.opentelemetry.io/otel/trace v1.22.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/mod v0.15.0 // indirect - golang.org/x/net v0.22.0 // indirect + golang.org/x/mod v0.16.0 // indirect + golang.org/x/net v0.23.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.18.0 // indirect + golang.org/x/tools v0.19.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.26.3 // indirect k8s.io/apimachinery v0.26.3 // indirect k8s.io/client-go v0.26.3 // indirect diff --git a/go.sum b/go.sum index ccba6217896..037ee5d349d 100644 --- a/go.sum +++ b/go.sum @@ -318,8 +318,9 @@ cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdi cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= cloud.google.com/go/compute v1.23.2/go.mod h1:JJ0atRC0J/oWYiiVBmsSsrRnh92DhZPG4hFDcR04Rns= -cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= +cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= +cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= @@ -626,8 +627,9 @@ cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+K cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= cloud.google.com/go/iam v1.1.4/go.mod h1:l/rg8l1AaA+VFMho/HYx2Vv6xinPSLMF8qfhRPIZ0L8= -cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= @@ -666,8 +668,9 @@ cloud.google.com/go/kms v1.15.0/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N cloud.google.com/go/kms v1.15.2/go.mod h1:3hopT4+7ooWRCjc2DxgnpESFxhIraaI2IpAVUEhbT/w= cloud.google.com/go/kms v1.15.3/go.mod h1:AJdXqHxS2GlPyduM99s9iGqi2nwbviBbhV/hdmt4iOQ= cloud.google.com/go/kms v1.15.4/go.mod h1:L3Sdj6QTHK8dfwK5D1JLsAyELsNMnd3tAIwGS4ltKpc= -cloud.google.com/go/kms v1.15.5 h1:pj1sRfut2eRbD9pFRjNnPNg/CzJPuQAzUujMIM1vVeM= cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= +cloud.google.com/go/kms v1.15.7 h1:7caV9K3yIxvlQPAcaFffhlT7d1qpxjB1wHBtjWa13SM= +cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI= cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= @@ -871,8 +874,9 @@ cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9 cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= cloud.google.com/go/pubsub v1.32.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsub v1.34.0 h1:ZtPbfwfi5rLaPeSvDC29fFoE20/tQvGrUS6kVJZJvkU= cloud.google.com/go/pubsub v1.34.0/go.mod h1:alj4l4rBg+N3YTFDDC+/YyFTs6JAjam2QfYsddcAW4c= +cloud.google.com/go/pubsub v1.36.1 h1:dfEPuGCHGbWUhaMCTHUFjfroILEkx55iUmKBZTP5f+Y= +cloud.google.com/go/pubsub v1.36.1/go.mod h1:iYjCa9EzWOoBiTdd4ps7QoMtMln5NwaZQpK1hbRfBDE= cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= @@ -1261,7 +1265,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= @@ -1364,8 +1367,9 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= @@ -1625,18 +1629,19 @@ github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/open-traffic-generator/keng-operator v0.3.15 h1:4qMC8MaCfV7TmmrfqI7rTusOChkHpiA8maih88aLaqY= github.com/open-traffic-generator/keng-operator v0.3.15/go.mod h1:+koaOnSyrJHdzxnaye+M6k+ZbszQlWI9u3tMxSpORNA= -github.com/open-traffic-generator/snappi/gosnappi v1.1.0 h1:xPWa7kUaGNmF0zihv4hsBDHNSqR/YXGc6fD6tj8sJv4= -github.com/open-traffic-generator/snappi/gosnappi v1.1.0/go.mod h1:CaE4nisXftNXdXWvTSqb4eiW2WMFIXkJsH5rqPoipcg= +github.com/open-traffic-generator/snappi/gosnappi v1.3.0 h1:6SFSuZLTuncLW1xMcBG5HEvVCWh9wVuxiYb71C3yj7s= +github.com/open-traffic-generator/snappi/gosnappi v1.3.0/go.mod h1:CaE4nisXftNXdXWvTSqb4eiW2WMFIXkJsH5rqPoipcg= github.com/openconfig/entity-naming v0.0.0-20230912181021-7ac806551a31 h1:K/9O+J20+liIof8WjquMydnebD0N1U9ItjhJYF6H4hg= github.com/openconfig/entity-naming v0.0.0-20230912181021-7ac806551a31/go.mod h1:ZRUrfwYYY+pLaOoWPad3p/8J4LLQcSqtXhBCkD2pXJc= github.com/openconfig/gnmi v0.0.0-20200414194230-1597cc0f2600/go.mod h1:M/EcuapNQgvzxo1DDXHK4tx3QpYM/uG4l591v33jG2A= github.com/openconfig/gnmi v0.0.0-20200508230933-d19cebf5e7be/go.mod h1:M/EcuapNQgvzxo1DDXHK4tx3QpYM/uG4l591v33jG2A= -github.com/openconfig/gnmi v0.10.0 h1:kQEZ/9ek3Vp2Y5IVuV2L/ba8/77TgjdXg505QXvYmg8= github.com/openconfig/gnmi v0.10.0/go.mod h1:Y9os75GmSkhHw2wX8sMsxfI7qRGAEcDh8NTa5a8vj6E= +github.com/openconfig/gnmi v0.11.0 h1:H7pLIb/o3xObu3+x0Fv9DCK7TH3FUh7mNwbYe+34hFw= +github.com/openconfig/gnmi v0.11.0/go.mod h1:9oJSQPPCpNvfMRj8e4ZoLVAw4wL8HyxXbiDlyuexCGU= github.com/openconfig/gnoi v0.4.0 h1:jbYXRMNmmvA8ZFv2FBLrYoxA1MFSui4tEui+8LAWyVc= github.com/openconfig/gnoi v0.4.0/go.mod h1:0d3/bgooXM/je6jI+fb7+9Iky8R8k6zSFG19xRYTRso= -github.com/openconfig/gnoigo v0.0.0-20240214160338-8660e1580468 h1:84ZrTGWMB73qVSX8uExxFNTHZKEmBJse1QXAdo/Eywc= -github.com/openconfig/gnoigo v0.0.0-20240214160338-8660e1580468/go.mod h1:iwgbKmQ8IU82mszJWXKpR0HSZiZ4DjeAnBZhYTp+l+4= +github.com/openconfig/gnoigo v0.0.0-20240320202954-ebd033e3542c h1:egPgBUBDn0XEtbz0CvE+Bh/I/3iTzwzMq5/rmtPJdQs= +github.com/openconfig/gnoigo v0.0.0-20240320202954-ebd033e3542c/go.mod h1:Se/HklUcFVcCGB66khgYouiesLRPoa4UL1ovvmE/68k= github.com/openconfig/gnsi v1.4.0 h1:E/OnAZh7jr3xpTdyawYXloVUg0cQbU3WQLtDGib/7L4= github.com/openconfig/gnsi v1.4.0/go.mod h1:jzPF4rVWPHhIG0F3t910Hh2VqqTbSfv18shbgE4AXhw= github.com/openconfig/gocloser v0.0.0-20220310182203-c6c950ed3b0b h1:NSYuxdlOWLldNpid1dThR6Dci96juXioUguMho6aliI= @@ -1661,8 +1666,8 @@ github.com/openconfig/lemming/operator v0.2.0 h1:dovZnR6lQkOHXcODli1NDOr/GVYrBY0 github.com/openconfig/lemming/operator v0.2.0/go.mod h1:LKgEXSR5VK2CAeh2uKijKAXFj42uQuwakrCHVPF0iII= github.com/openconfig/models-ci v1.0.2-0.20231113233730-f0986391428e h1:6N4jXpZa/SXYcNpJFjjZvenxO/xnTwuUCgCEinhNLfU= github.com/openconfig/models-ci v1.0.2-0.20231113233730-f0986391428e/go.mod h1:w38G/kObu95PbtwMYVp6SKhkHCegJFwL8B58Ns84g4s= -github.com/openconfig/ondatra v0.5.4 h1:ldyCk2HaJXIANFdGJHFdmLmb/amlYHLEDCliZYkkldA= -github.com/openconfig/ondatra v0.5.4/go.mod h1:QfGq//x6XIZcGmxFceyu1FYSgZk2nsLAc3NnYFyT51I= +github.com/openconfig/ondatra v0.5.6 h1:1NkkmyPG8To5+aoAO1bIGEstfHB4My/Szc13IM+Kup8= +github.com/openconfig/ondatra v0.5.6/go.mod h1:hFxrMsLsOmvOzHeUDDJZcypenZIo6lt6NrPOdDmnHHM= github.com/openconfig/replayer v0.0.0-20240110192655-4e9cf83d8d30 h1:KcHS08m7nFHq/D03ZfZKKNCSaS1jsuvdF3lCyDjPWJc= github.com/openconfig/replayer v0.0.0-20240110192655-4e9cf83d8d30/go.mod h1:VQ8FdPVaHwxKtamhcrwkPsvTeeoEgFYNK1xE8nHD0S8= github.com/openconfig/testt v0.0.0-20220311054427-efbb1a32ec07 h1:X631iD/B0ximGFb5P9LY5wHju4SiedxUhc5UZEo7VSw= @@ -1683,8 +1688,8 @@ github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3/go.mod h1:85jBQOZwp github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= -github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= +github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= @@ -1807,9 +1812,12 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.einride.tech/aip v0.66.0 h1:XfV+NQX6L7EOYK11yoHHFtndeaWh3KbD9/cN/6iWEt8= +go.einride.tech/aip v0.66.0/go.mod h1:qAhMsfT7plxBX+Oy7Huol6YUvZ0ZzdUz26yZsQwfl1M= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -1819,22 +1827,27 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= +go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= +go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= -go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= +go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -1894,8 +1907,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1941,8 +1954,8 @@ golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2014,8 +2027,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2050,8 +2063,9 @@ golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQ golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2288,8 +2302,8 @@ golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2376,8 +2390,9 @@ google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWL google.golang.org/api v0.139.0/go.mod h1:CVagp6Eekz9CjGZ718Z+sloknzkDJE7Vc1Ckj9+viBk= google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= google.golang.org/api v0.150.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg= -google.golang.org/api v0.155.0 h1:vBmGhCYs0djJttDNynWo44zosHlPvHmA0XiN2zP2DtA= google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk= +google.golang.org/api v0.162.0 h1:Vhs54HkaEpkMBdgGdOT2P6F0csGG/vxDS0hWHJzmmps= +google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -2547,8 +2562,9 @@ google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRx google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY= google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= -google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= @@ -2571,8 +2587,9 @@ google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3/go. google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c= google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= -google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c= google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= @@ -2600,8 +2617,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 h1:IR+hp6ypxjH24bkMfEJ0yHR21+gwPWdV+/IBrPQyn3k= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -2653,8 +2670,9 @@ google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSs google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= -google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8= +google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/internal/cfgplugins/bgp.go b/internal/cfgplugins/bgp.go index 23815ee23f6..0a72d34e13b 100644 --- a/internal/cfgplugins/bgp.go +++ b/internal/cfgplugins/bgp.go @@ -41,15 +41,15 @@ const ( RPLPermitAll = "PERMIT-ALL" // DutAS dut AS - DutAS = uint32(65656) + DutAS = uint32(65501) // AteAS1 for ATE port1 - AteAS1 = uint32(65536) + AteAS1 = uint32(65511) // AteAS2 for ATE port2 - AteAS2 = uint32(65537) + AteAS2 = uint32(65512) // AteAS3 for ATE port3 - AteAS3 = uint32(65538) + AteAS3 = uint32(65513) // AteAS4 for ATE port4 - AteAS4 = uint32(65539) + AteAS4 = uint32(65514) // BGPPeerGroup1 for ATE port1 BGPPeerGroup1 = "BGP-PEER-GROUP1" @@ -149,15 +149,16 @@ type BGPSession struct { DUTConf *oc.Root ATETop gosnappi.Config - DUTPorts []*attrs.Attributes - ATEPorts []*attrs.Attributes - aftType oc.E_BgpTypes_AFI_SAFI_TYPE + DUTPorts []*attrs.Attributes + ATEPorts []*attrs.Attributes + afiTypes []oc.E_BgpTypes_AFI_SAFI_TYPE + networkInstance string } // NewBGPSession creates a new BGPSession using the default global config, and // configures the interfaces on the dut and the ate based in given topology port count. // Only supports 2 and 4 port DUT-ATE topology -func NewBGPSession(t *testing.T, pc PortCount) *BGPSession { +func NewBGPSession(t *testing.T, pc PortCount, ni *string) *BGPSession { conf := &BGPSession{ DUT: ondatra.DUT(t, "dut"), DUTConf: &oc.Root{}, @@ -186,15 +187,25 @@ func NewBGPSession(t *testing.T, pc PortCount) *BGPSession { conf.ATEIntfs[i] = conf.ATEPorts[i].AddToOTG(conf.ATETop, conf.OndatraATEPorts[i], conf.DUTPorts[i]) } } + + if ni == nil { + fptest.ConfigureDefaultNetworkInstance(t, conf.DUT) + conf.networkInstance = deviations.DefaultNetworkInstance(conf.DUT) + } else { + conf.networkInstance = *ni + } + return conf } // WithEBGP adds eBGP specific config -func (bs *BGPSession) WithEBGP(t *testing.T, aftype oc.E_BgpTypes_AFI_SAFI_TYPE, isSamePG, isSameAS bool) *BGPSession { - if aftype != oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST && aftype != oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST { - t.Fatalf("Unsupported AFI type: %v", bs.aftType) +func (bs *BGPSession) WithEBGP(t *testing.T, afiTypes []oc.E_BgpTypes_AFI_SAFI_TYPE, bgpPorts []string, isSamePG, isSameAS bool) *BGPSession { + for _, afiType := range afiTypes { + if afiType != oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST && afiType != oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST { + t.Fatalf("Unsupported AFI type: %v", afiType) + } } - bs.aftType = aftype + bs.afiTypes = afiTypes asNumbers := []uint32{AteAS1, AteAS2, AteAS3, AteAS4} if isSameAS { @@ -205,34 +216,34 @@ func (bs *BGPSession) WithEBGP(t *testing.T, aftype oc.E_BgpTypes_AFI_SAFI_TYPE, byName := func(i, j int) bool { return devices[i].Name() < devices[j].Name() } sort.Slice(devices, byName) for i, otgPort := range bs.ATEPorts { + if !containsValue(bgpPorts, otgPort.Name) { + continue + } bgp := devices[i].Bgp().SetRouterId(otgPort.IPv4) - switch aftype { - case oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST: - ipv4 := devices[i].Ethernets().Items()[0].Ipv4Addresses().Items()[0] - bgp4Peer := bgp.Ipv4Interfaces().Add().SetIpv4Name(ipv4.Name()).Peers().Add().SetName(devices[i].Name() + ".BGP4.peer") - bgp4Peer.SetPeerAddress(ipv4.Gateway()) - bgp4Peer.SetAsNumber(uint32(asNumbers[i])) - bgp4Peer.SetAsType(gosnappi.BgpV4PeerAsType.EBGP) - bgp4Peer.Capability().SetIpv4UnicastAddPath(true).SetIpv6UnicastAddPath(true) - bgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) - case oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST: - ipv6 := devices[i].Ethernets().Items()[0].Ipv6Addresses().Items()[0] - bgp6Peer := bgp.Ipv6Interfaces().Add().SetIpv6Name(ipv6.Name()).Peers().Add().SetName(devices[i].Name() + ".BGP6.peer") - bgp6Peer.SetPeerAddress(ipv6.Gateway()) - bgp6Peer.SetAsNumber(uint32(asNumbers[i])) - bgp6Peer.SetAsType(gosnappi.BgpV6PeerAsType.EBGP) - bgp6Peer.Capability().SetIpv4UnicastAddPath(true).SetIpv6UnicastAddPath(true).SetExtendedNextHopEncoding(true) - bgp6Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) + for _, afiType := range afiTypes { + switch afiType { + case oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST: + ipv4 := devices[i].Ethernets().Items()[0].Ipv4Addresses().Items()[0] + bgp4Peer := bgp.Ipv4Interfaces().Add().SetIpv4Name(ipv4.Name()).Peers().Add().SetName(devices[i].Name() + ".BGP4.peer") + bgp4Peer.SetPeerAddress(ipv4.Gateway()) + bgp4Peer.SetAsNumber(uint32(asNumbers[i])) + bgp4Peer.SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + bgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true) + case oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST: + ipv6 := devices[i].Ethernets().Items()[0].Ipv6Addresses().Items()[0] + bgp6Peer := bgp.Ipv6Interfaces().Add().SetIpv6Name(ipv6.Name()).Peers().Add().SetName(devices[i].Name() + ".BGP6.peer") + bgp6Peer.SetPeerAddress(ipv6.Gateway()) + bgp6Peer.SetAsNumber(uint32(asNumbers[i])) + bgp6Peer.SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + bgp6Peer.LearnedInformationFilter().SetUnicastIpv6Prefix(true) + } } } - dni := bs.DUTConf.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(bs.DUT)) - dni.SetType(oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_DEFAULT_INSTANCE) - - niProtocol := dni.GetOrCreateProtocol(PTBGP, bgpName) - neighborConfig := BuildNeigborConfig(isSamePG, isSameAS, len(bs.DUTPorts)) - niProtocol.Bgp = BuildBGPOCConfig(t, bs.DUT, dutPort1.IPv4, aftype, neighborConfig) + niProtocol := bs.DUTConf.GetOrCreateNetworkInstance(bs.networkInstance).GetOrCreateProtocol(PTBGP, bgpName) + neighborConfig := bs.buildNeigborConfig(isSamePG, isSameAS, bgpPorts) + niProtocol.Bgp = BuildBGPOCConfig(t, bs.DUT, dutPort1.IPv4, afiTypes, neighborConfig) err := bs.configureRoutingPolicy() if err != nil { @@ -273,7 +284,7 @@ func (bs *BGPSession) PushDUT(t testing.TB) error { if deviations.ExplicitInterfaceInDefaultVRF(bs.DUT) { for i := 0; i < len(bs.DUTPorts); i++ { - fptest.AssignToNetworkInstance(t, bs.DUT, bs.OndatraDUTPorts[i].Name(), deviations.DefaultNetworkInstance(bs.DUT), 0) + fptest.AssignToNetworkInstance(t, bs.DUT, bs.OndatraDUTPorts[i].Name(), bs.networkInstance, 0) } } if deviations.ExplicitPortSpeed(bs.DUT) { @@ -291,11 +302,13 @@ func (bs *BGPSession) PushAndStartATE(t testing.TB) { otg.PushConfig(t, bs.ATETop) otg.StartProtocols(t) - switch bs.aftType { - case oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST: - otgutils.WaitForARP(t.(*testing.T), otg, bs.ATETop, "IPv4") - case oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST: - otgutils.WaitForARP(t.(*testing.T), otg, bs.ATETop, "IPv6") + for _, afiType := range bs.afiTypes { + switch afiType { + case oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST: + otgutils.WaitForARP(t.(*testing.T), otg, bs.ATETop, "IPv4") + case oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST: + otgutils.WaitForARP(t.(*testing.T), otg, bs.ATETop, "IPv6") + } } } @@ -334,74 +347,74 @@ func VerifyOTGBGPEstablished(t *testing.T, ate *ondatra.ATEDevice) { // NeighborConfig to hold neighbor specific config type NeighborConfig struct { + Name string IPv4Neighbor string IPv6Neighbor string PeerGroup string AS uint32 } -// BuildNeigborConfig builds neighbor config based on given flags -func BuildNeigborConfig(isSamePG, isSameAS bool, portCount int) []*NeighborConfig { - nc := []*NeighborConfig{ - { - IPv4Neighbor: atePort1.IPv4, - IPv6Neighbor: atePort1.IPv6, - PeerGroup: BGPPeerGroup1, - AS: AteAS1, - }, - { - IPv4Neighbor: atePort2.IPv4, - IPv6Neighbor: atePort2.IPv6, - PeerGroup: BGPPeerGroup2, - AS: AteAS2, - }, - } - - if portCount == int(PortCount4) { - nc = append( - nc, - &NeighborConfig{ - IPv4Neighbor: atePort3.IPv4, - IPv6Neighbor: atePort3.IPv6, - PeerGroup: BGPPeerGroup3, - AS: AteAS3, - }, - &NeighborConfig{ - IPv4Neighbor: atePort4.IPv4, - IPv6Neighbor: atePort4.IPv6, - PeerGroup: BGPPeerGroup4, - AS: AteAS4, - }, - ) +// buildNeigborConfig builds neighbor config based on given flags +func (bs *BGPSession) buildNeigborConfig(isSamePG, isSameAS bool, bgpPorts []string) []*NeighborConfig { + nc1 := &NeighborConfig{ + Name: "port1", + IPv4Neighbor: atePort1.IPv4, + IPv6Neighbor: atePort1.IPv6, + PeerGroup: BGPPeerGroup1, + AS: AteAS1, + } + nc2 := &NeighborConfig{ + Name: "port2", + IPv4Neighbor: atePort2.IPv4, + IPv6Neighbor: atePort2.IPv6, + PeerGroup: BGPPeerGroup2, + AS: AteAS2, + } + nc3 := &NeighborConfig{ + Name: "port3", + IPv4Neighbor: atePort3.IPv4, + IPv6Neighbor: atePort3.IPv6, + PeerGroup: BGPPeerGroup3, + AS: AteAS3, + } + nc4 := &NeighborConfig{ + Name: "port4", + IPv4Neighbor: atePort4.IPv4, + IPv6Neighbor: atePort4.IPv6, + PeerGroup: BGPPeerGroup4, + AS: AteAS4, + } + ncAll := []*NeighborConfig{nc1, nc2, nc3, nc4} + + validNC := []*NeighborConfig{} + for _, nc := range ncAll[:len(bs.DUTPorts)] { + if containsValue(bgpPorts, nc.Name) { + validNC = append(validNC, nc) + } } if isSamePG { - for _, n := range nc { - n.PeerGroup = BGPPeerGroup1 + for _, nc := range validNC { + nc.PeerGroup = BGPPeerGroup1 } } if isSameAS { - for _, n := range nc { - n.AS = AteAS1 + for _, nc := range validNC { + nc.AS = AteAS1 } } - return nc + return validNC } // BuildBGPOCConfig builds the BGP OC config applying global, neighbors and peer-group config -func BuildBGPOCConfig(t *testing.T, dut *ondatra.DUTDevice, routerID string, aftType oc.E_BgpTypes_AFI_SAFI_TYPE, neighborConfig []*NeighborConfig) *oc.NetworkInstance_Protocol_Bgp { - afiSafiGlobal := map[oc.E_BgpTypes_AFI_SAFI_TYPE]*oc.NetworkInstance_Protocol_Bgp_Global_AfiSafi{ - aftType: { - AfiSafiName: aftType, +func BuildBGPOCConfig(t *testing.T, dut *ondatra.DUTDevice, routerID string, afiTypes []oc.E_BgpTypes_AFI_SAFI_TYPE, neighborConfig []*NeighborConfig) *oc.NetworkInstance_Protocol_Bgp { + afiSafiGlobal := map[oc.E_BgpTypes_AFI_SAFI_TYPE]*oc.NetworkInstance_Protocol_Bgp_Global_AfiSafi{} + for _, afiType := range afiTypes { + afiSafiGlobal[afiType] = &oc.NetworkInstance_Protocol_Bgp_Global_AfiSafi{ + AfiSafiName: afiType, Enabled: ygot.Bool(true), - }, - } - afiSafiNeighbor := map[oc.E_BgpTypes_AFI_SAFI_TYPE]*oc.NetworkInstance_Protocol_Bgp_Neighbor_AfiSafi{ - aftType: { - AfiSafiName: aftType, - Enabled: ygot.Bool(true), - }, + } } global := &oc.NetworkInstance_Protocol_Bgp_Global{ @@ -414,23 +427,30 @@ func BuildBGPOCConfig(t *testing.T, dut *ondatra.DUTDevice, routerID string, aft peerGroups := make(map[string]*oc.NetworkInstance_Protocol_Bgp_PeerGroup) var neighbor string for _, nc := range neighborConfig { - switch aftType { - case oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST: - neighbor = nc.IPv4Neighbor - case oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST: - neighbor = nc.IPv6Neighbor - default: - t.Fatalf("Unsupported AFI type: %v", aftType) - } - - neighbors[neighbor] = &oc.NetworkInstance_Protocol_Bgp_Neighbor{ - PeerAs: ygot.Uint32(nc.AS), - PeerGroup: ygot.String(nc.PeerGroup), - NeighborAddress: ygot.String(neighbor), - AfiSafi: afiSafiNeighbor, + for _, afiType := range afiTypes { + switch afiType { + case oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST: + neighbor = nc.IPv4Neighbor + case oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST: + neighbor = nc.IPv6Neighbor + default: + t.Fatalf("Unsupported AFI type: %v", afiType) + } + + neighbors[neighbor] = &oc.NetworkInstance_Protocol_Bgp_Neighbor{ + PeerAs: ygot.Uint32(nc.AS), + PeerGroup: ygot.String(nc.PeerGroup), + NeighborAddress: ygot.String(neighbor), + AfiSafi: map[oc.E_BgpTypes_AFI_SAFI_TYPE]*oc.NetworkInstance_Protocol_Bgp_Neighbor_AfiSafi{ + afiType: { + AfiSafiName: afiType, + Enabled: ygot.Bool(true), + }, + }, + } + + peerGroups[nc.PeerGroup] = getPeerGroup(nc.PeerGroup, dut, afiType) } - - peerGroups[nc.PeerGroup] = getPeerGroup(nc.PeerGroup, dut, aftType) } return &oc.NetworkInstance_Protocol_Bgp{ @@ -441,7 +461,7 @@ func BuildBGPOCConfig(t *testing.T, dut *ondatra.DUTDevice, routerID string, aft } // getPeerGroup build peer-config -func getPeerGroup(pgn string, dut *ondatra.DUTDevice, aftype oc.E_BgpTypes_AFI_SAFI_TYPE) *oc.NetworkInstance_Protocol_Bgp_PeerGroup { +func getPeerGroup(pgn string, dut *ondatra.DUTDevice, afiType oc.E_BgpTypes_AFI_SAFI_TYPE) *oc.NetworkInstance_Protocol_Bgp_PeerGroup { bgp := &oc.NetworkInstance_Protocol_Bgp{} pg := bgp.GetOrCreatePeerGroup(pgn) @@ -454,10 +474,19 @@ func getPeerGroup(pgn string, dut *ondatra.DUTDevice, aftype oc.E_BgpTypes_AFI_S } // policy under peer group AFI - afisafi := pg.GetOrCreateAfiSafi(aftype) + afisafi := pg.GetOrCreateAfiSafi(afiType) afisafi.Enabled = ygot.Bool(true) rpl := afisafi.GetOrCreateApplyPolicy() rpl.SetExportPolicy([]string{RPLPermitAll}) rpl.SetImportPolicy([]string{RPLPermitAll}) return pg } + +func containsValue[T comparable](slice []T, value T) bool { + for _, v := range slice { + if v == value { + return true + } + } + return false +} diff --git a/internal/check/check_test.go b/internal/check/check_test.go index 529b0512abf..c021aaf7629 100644 --- a/internal/check/check_test.go +++ b/internal/check/check_test.go @@ -62,9 +62,9 @@ func newFakeGNMI(ctx context.Context) (*fakeGNMI, error) { if err != nil { return nil, err } - conn, err := grpc.DialContext(ctx, agent.Address(), grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, err := grpc.NewClient(agent.Address(), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { - return nil, fmt.Errorf("DialContext(%s): %w", agent.Address(), err) + return nil, fmt.Errorf("NewClient(%s): %w", agent.Address(), err) } client, err := ygnmi.NewClient(gpb.NewGNMIClient(conn)) diff --git a/internal/cntrsrv/cntrsrv.go b/internal/cntrsrv/cntrsrv.go index 74ce925e22a..bbbf4c2f47f 100644 --- a/internal/cntrsrv/cntrsrv.go +++ b/internal/cntrsrv/cntrsrv.go @@ -76,7 +76,7 @@ type rpcCredentials struct { *creds.UserPass } -func (r *rpcCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { +func (r *rpcCredentials) GetRequestMetadata(context.Context, ...string) (map[string]string, error) { return map[string]string{ "username": "admin", "password": "admin", @@ -89,8 +89,7 @@ func (r *rpcCredentials) RequireTransportSecurity() bool { // Dial connects to the remote gRPC CNTR server hosted at the address in the request proto. func (c *C) Dial(ctx context.Context, req *cpb.DialRequest) (*cpb.DialResponse, error) { - conn, err := grpc.DialContext(ctx, req.GetAddr(), - grpc.WithBlock(), + conn, err := grpc.NewClient(req.GetAddr(), grpc.WithPerRPCCredentials(&rpcCredentials{}), grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ InsecureSkipVerify: true, // NOLINT diff --git a/internal/cntrsrv/cntrsrv_test.go b/internal/cntrsrv/cntrsrv_test.go index 19a2f9274e2..4c358180e88 100644 --- a/internal/cntrsrv/cntrsrv_test.go +++ b/internal/cntrsrv/cntrsrv_test.go @@ -39,11 +39,8 @@ import ( spb "github.com/openconfig/gribi/v1/proto/service" ) -func newClient(ctx context.Context, t *testing.T, port uint) (cpb.CntrClient, func()) { - ctx, cancel := context.WithTimeout(ctx, 30*time.Second) - defer cancel() - - conn, err := grpc.DialContext(ctx, fmt.Sprintf("localhost:%d", port), grpc.WithBlock(), +func newClient(t *testing.T, port uint) (cpb.CntrClient, func()) { + conn, err := grpc.NewClient(fmt.Sprintf("localhost:%d", port), grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ InsecureSkipVerify: true, // NOLINT }))) @@ -98,7 +95,7 @@ type gRIBIServer struct { *spb.UnimplementedGRIBIServer } -func (g *gRIBIServer) Get(req *spb.GetRequest, stream spb.GRIBI_GetServer) error { +func (g *gRIBIServer) Get(_ *spb.GetRequest, stream spb.GRIBI_GetServer) error { if err := stream.Send(&spb.GetResponse{}); err != nil { return status.Errorf(codes.Internal, "can't send") } @@ -127,7 +124,7 @@ type gNMIServer struct { *gpb.UnimplementedGNMIServer } -func (g *gNMIServer) Capabilities(ctx context.Context, req *gpb.CapabilityRequest) (*gpb.CapabilityResponse, error) { +func (g *gNMIServer) Capabilities(context.Context, *gpb.CapabilityRequest) (*gpb.CapabilityResponse, error) { return &gpb.CapabilityResponse{ GNMIVersion: "demo", }, nil @@ -181,6 +178,9 @@ func TestDial(t *testing.T) { inServer: startServer, inServerPort: 60061, inReq: &cpb.DialRequest{ + Request: &cpb.DialRequest_Ping{ + Ping: &cpb.PingRequest{}, + }, Addr: "localhost:6666", }, wantErr: true, @@ -278,7 +278,7 @@ func TestDial(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - client, stopC := newClient(ctx, t, 60061) + client, stopC := newClient(t, 60061) defer stopC() got, err := client.Dial(ctx, tt.inReq) diff --git a/internal/deviations/deviations.go b/internal/deviations/deviations.go index 23880679ca0..67d5c0f392a 100644 --- a/internal/deviations/deviations.go +++ b/internal/deviations/deviations.go @@ -377,6 +377,11 @@ func ExplicitInterfaceInDefaultVRF(dut *ondatra.DUTDevice) bool { return lookupDUTDeviations(dut).GetExplicitInterfaceInDefaultVrf() } +// RibWecmp returns if device requires CLI knob to enable wecmp feature. +func RibWecmp(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetRibWecmp() +} + // InterfaceConfigVRFBeforeAddress returns if vrf should be configured before IP address when configuring interface. func InterfaceConfigVRFBeforeAddress(dut *ondatra.DUTDevice) bool { return lookupDUTDeviations(dut).GetInterfaceConfigVrfBeforeAddress() @@ -801,3 +806,133 @@ func PfRequireMatchDefaultRule(dut *ondatra.DUTDevice) bool { func MissingPortToOpticalChannelMapping(dut *ondatra.DUTDevice) bool { return lookupDUTDeviations(dut).GetMissingPortToOpticalChannelComponentMapping() } + +// SkipContainerOp returns true if gNMI container OP needs to be skipped. +// Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 +func SkipContainerOp(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetSkipContainerOp() +} + +// ReorderCallsForVendorCompatibilty returns true if call needs to be updated/added/deleted. +// Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 +func ReorderCallsForVendorCompatibilty(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetReorderCallsForVendorCompatibilty() +} + +// AddMissingBaseConfigViaCli returns true if missing base config needs to be added using CLI. +// Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 +func AddMissingBaseConfigViaCli(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetAddMissingBaseConfigViaCli() +} + +// SkipMacaddressCheck returns true if mac address for an interface via gNMI needs to be skipped. +// Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 +func SkipMacaddressCheck(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetSkipMacaddressCheck() +} + +// BGPRibOcPathUnsupported returns true if BGP RIB OC telemetry path is not supported. +func BGPRibOcPathUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetBgpRibOcPathUnsupported() +} + +// SkipPrefixSetMode return true if device needs to skip setting prefix-set mode while configuring prefix-set routing-policy +func SkipPrefixSetMode(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetSkipPrefixSetMode() +} + +// SetMetricAsPreference returns true for devices which set metric as +// preference for static next-hop +func SetMetricAsPreference(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetSetMetricAsPreference() +} + +// IPv6StaticRouteWithIPv4NextHopRequiresStaticARP returns true if devices don't support having an +// IPv6 static Route with an IPv4 address as next hop and requires configuring a static ARP entry. +// Arista: https://partnerissuetracker.corp.google.com/issues/316593298 +func IPv6StaticRouteWithIPv4NextHopRequiresStaticARP(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetIpv6StaticRouteWithIpv4NextHopRequiresStaticArp() +} + +// PfRequireSequentialOrderPbrRules returns true for device requires policy-forwarding rules to be in sequential order in the gNMI set-request. +func PfRequireSequentialOrderPbrRules(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetPfRequireSequentialOrderPbrRules() +} + +// MissingStaticRouteNextHopMetricTelemetry returns true for devices missing +// static route next-hop metric telemetry. +// Arista: https://partnerissuetracker.corp.google.com/issues/321010782 +func MissingStaticRouteNextHopMetricTelemetry(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetMissingStaticRouteNextHopMetricTelemetry() +} + +// UnsupportedStaticRouteNextHopRecurse returns true for devices that don't support recursive +// resolution of static route next hop. +// Arista: https://partnerissuetracker.corp.google.com/issues/314449182 +func UnsupportedStaticRouteNextHopRecurse(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetUnsupportedStaticRouteNextHopRecurse() +} + +// MissingStaticRouteDropNextHopTelemetry returns true for devices missing +// static route telemetry with DROP next hop. +// Arista: https://partnerissuetracker.corp.google.com/issues/330619816 +func MissingStaticRouteDropNextHopTelemetry(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetMissingStaticRouteDropNextHopTelemetry() +} + +// MissingZROpticalChannelTunableParametersTelemetry returns true for devices missing 400ZR +// optical-channel tunable parameters telemetry: min/max/avg. +// Arista: https://partnerissuetracker.corp.google.com/issues/319314781 +func MissingZROpticalChannelTunableParametersTelemetry(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetMissingZrOpticalChannelTunableParametersTelemetry() +} + +// PLQReflectorStatsUnsupported returns true for devices that does not support packet link qualification(PLQ) reflector packet sent/received stats. +func PLQReflectorStatsUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetPlqReflectorStatsUnsupported() +} + +// PLQGeneratorCapabilitiesMaxMTU returns supported max_mtu for devices that does not support packet link qualification(PLQ) Generator max_mtu to be atleast >= 8184. +func PLQGeneratorCapabilitiesMaxMTU(dut *ondatra.DUTDevice) uint32 { + return lookupDUTDeviations(dut).GetPlqGeneratorCapabilitiesMaxMtu() +} + +// PLQGeneratorCapabilitiesMaxPPS returns supported max_pps for devices that does not support packet link qualification(PLQ) Generator max_pps to be atleast >= 100000000. +func PLQGeneratorCapabilitiesMaxPPS(dut *ondatra.DUTDevice) uint64 { + return lookupDUTDeviations(dut).GetPlqGeneratorCapabilitiesMaxPps() +} + +// BgpExtendedCommunityIndexUnsupported return true if BGP extended community index is not supported. +func BgpExtendedCommunityIndexUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetBgpExtendedCommunityIndexUnsupported() +} + +// BgpCommunitySetRefsUnsupported return true if BGP community set refs is not supported. +func BgpCommunitySetRefsUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetBgpCommunitySetRefsUnsupported() +} + +// DefaultImportExportPolicy returns true when device does not have a default deny action in the absence of a route policy +func DefaultImportExportPolicy(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetDefaultImportExportPolicy() +} + +// TableConnectionsUnsupported returns true if Table Connections are unsupported. +func TableConnectionsUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetTableConnectionsUnsupported() +} + +// UseVendorNativeTagSetConfig returns whether a device requires native model to configure tag-set +func UseVendorNativeTagSetConfig(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetUseVendorNativeTagSetConfig() +} + +// SkipBgpSendCommunityType return true if device needs to skip setting BGP send-community-type +func SkipBgpSendCommunityType(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetSkipBgpSendCommunityType() +} + +// BgpActionsSetCommunityMethodUnsupported return true if BGP actions set-community method is unsupported +func BgpActionsSetCommunityMethodUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetBgpActionsSetCommunityMethodUnsupported() +} diff --git a/internal/otgutils/actions.go b/internal/otgutils/actions.go index af0feab727c..9ed8f136f98 100644 --- a/internal/otgutils/actions.go +++ b/internal/otgutils/actions.go @@ -37,9 +37,12 @@ func GetFlowStats(t testing.TB, otg *otg.OTG, flowName string, timeout time.Dura txPkts := gnmi.Get(t, otg, gnmi.OTG().Flow(flowName).Counters().OutPkts().State()) rxPkts, _ := gnmi.Watch(t, otg, gnmi.OTG().Flow(flowName).Counters().InPkts().State(), timeout, func(val *ygnmi.Value[uint64]) bool { - rxPackets, ok := val.Val() - return ok && rxPackets == txPkts + rxPackets, present := val.Val() + return present && rxPackets == txPkts }).Await(t) + if rxPkts == nil { + return txPkts, 0 + } rx, _ := rxPkts.Val() return txPkts, rx diff --git a/internal/samplestream/samplestream.go b/internal/samplestream/samplestream.go index da762a38135..ed8160c6286 100644 --- a/internal/samplestream/samplestream.go +++ b/internal/samplestream/samplestream.go @@ -19,11 +19,11 @@ const ( // SampleStream represents a gNMI Subscription with SAMPLE mode. type SampleStream[T any] struct { - dataMu sync.Mutex // Lock that protects the received data and the next channel. - nextCh chan *ygnmi.Value[T] // Channel that holds the next sample. - data []*ygnmi.Value[T] // Data received from gNMI call. - cancel context.CancelFunc // Cancels the subscription. - interval time.Duration // Configured interval for the SAMPLE mode stream. + dataMu sync.Mutex // Lock that protects the received data and the next channel. + lastVal *ygnmi.Value[T] // Holds the last received sample. + data []*ygnmi.Value[T] // Data received from gNMI call. + cancel context.CancelFunc // Cancels the subscription. + interval time.Duration // Configured interval for the SAMPLE mode stream. } // New creates a new SampleStream. @@ -42,10 +42,11 @@ func New[T any](t *testing.T, dut *ondatra.DUTDevice, q ygnmi.SingletonQuery[T], ygnmi.Watch(ctx, c, q, func(v *ygnmi.Value[T]) error { s.dataMu.Lock() defer s.dataMu.Unlock() - s.data = append(s.data, v) - if s.nextCh != nil { - s.nextCh <- v + if !v.IsPresent() { + return ygnmi.Continue } + s.data = append(s.data, v) + s.lastVal = v return ygnmi.Continue }, ygnmi.WithSubscriptionMode(gpb.SubscriptionMode_SAMPLE), ygnmi.WithSampleInterval(interval)) return s @@ -53,36 +54,24 @@ func New[T any](t *testing.T, dut *ondatra.DUTDevice, q ygnmi.SingletonQuery[T], // Next returns the next sample received within the sample interval. // If no sample is received within the interval, nil is returned. -func (s *SampleStream[T]) Next(t *testing.T) *ygnmi.Value[T] { - func() { - s.dataMu.Lock() - defer s.dataMu.Unlock() - s.nextCh = make(chan *ygnmi.Value[T], 1) - }() - defer func() { - s.dataMu.Lock() - defer s.dataMu.Unlock() - s.nextCh = nil - }() - select { - case v := <-s.nextCh: - return v - case <-time.After(s.interval + intervalTolerance): - return nil - } +func (s *SampleStream[T]) Next() *ygnmi.Value[T] { + time.Sleep(s.interval + intervalTolerance) + s.dataMu.Lock() + defer s.dataMu.Unlock() + return s.lastVal } // Nexts calls Next() count times and returns the slice of returned samples. -func (s *SampleStream[T]) Nexts(t *testing.T, count int) []*ygnmi.Value[T] { +func (s *SampleStream[T]) Nexts(count int) []*ygnmi.Value[T] { var nexts []*ygnmi.Value[T] for i := 0; i < count; i++ { - nexts = append(nexts, s.Next(t)) + nexts = append(nexts, s.Next()) } return nexts } // All returns the list of values that has been received thus far. -func (s *SampleStream[T]) All(t *testing.T) []*ygnmi.Value[T] { +func (s *SampleStream[T]) All() []*ygnmi.Value[T] { s.dataMu.Lock() defer s.dataMu.Unlock() return s.data diff --git a/internal/security/gnxi/rpcexec.go b/internal/security/gnxi/rpcexec.go index 9cf326f7fd5..f91274b8633 100644 --- a/internal/security/gnxi/rpcexec.go +++ b/internal/security/gnxi/rpcexec.go @@ -507,7 +507,7 @@ func GnsiAuthzRotate(ctx context.Context, dut *ondatra.DUTDevice, opts []grpc.Di } _, err = gnsiCStream.Recv() // invalid policy is expected since the empty policy is not allowed - if strings.Contains(err.Error(), "invalid policy") { + if strings.Contains(err.Error(), "invalid policy") || status.Code(err) == codes.InvalidArgument { return nil } return err diff --git a/internal/vrfpolicy/vrfpolicy.go b/internal/vrfpolicy/vrfpolicy.go index 2c35c500671..c4580303c8c 100644 --- a/internal/vrfpolicy/vrfpolicy.go +++ b/internal/vrfpolicy/vrfpolicy.go @@ -150,6 +150,12 @@ func BuildVRFSelectionPolicyW(t *testing.T, dut *ondatra.DUTDevice, niName strin pfRule7, pfRule8, pfRule9, pfRule10, pfRule11, pfRule12, } + if deviations.PfRequireSequentialOrderPbrRules(dut) { + pfRule10.seqID = 910 + pfRule11.seqID = 911 + pfRule12.seqID = 912 + } + ni := d.GetOrCreateNetworkInstance(niName) niP := ni.GetOrCreatePolicyForwarding() niPf := niP.GetOrCreatePolicy(vrfPolW) @@ -169,9 +175,20 @@ func BuildVRFSelectionPolicyW(t *testing.T, dut *ondatra.DUTDevice, niName strin pfRAction.DecapFallbackNetworkInstance = ygot.String(pfRule.action.decapFallbackNI) } - pfR := niPf.GetOrCreateRule(13) - pfRAction := pfR.GetOrCreateAction() - pfRAction.NetworkInstance = ygot.String(niDefault) + if deviations.PfRequireMatchDefaultRule(dut) { + pfR13 := niPf.GetOrCreateRule(913) + pfR13.GetOrCreateL2().SetEthertype(oc.PacketMatchTypes_ETHERTYPE_ETHERTYPE_IPV4) + pfRAction := pfR13.GetOrCreateAction() + pfRAction.NetworkInstance = ygot.String(niDefault) + pfR14 := niPf.GetOrCreateRule(914) + pfR14.GetOrCreateL2().SetEthertype(oc.PacketMatchTypes_ETHERTYPE_ETHERTYPE_IPV6) + pfRAction = pfR14.GetOrCreateAction() + pfRAction.NetworkInstance = ygot.String(niDefault) + } else { + pfR := niPf.GetOrCreateRule(13) + pfRAction := pfR.GetOrCreateAction() + pfRAction.NetworkInstance = ygot.String(niDefault) + } return niP } @@ -180,9 +197,6 @@ func BuildVRFSelectionPolicyW(t *testing.T, dut *ondatra.DUTDevice, niName strin func ConfigureVRFSelectionPolicyW(t *testing.T, dut *ondatra.DUTDevice) { t.Helper() - t.Log("Delete existing vrf selection policy and Apply vrf selectioin policy W") - gnmi.Delete(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).PolicyForwarding().Config()) - port1 := dut.Port(t, "port1") interfaceID := port1.Name() if deviations.InterfaceRefInterfaceIDFormat(dut) { @@ -202,3 +216,16 @@ func ConfigureVRFSelectionPolicyW(t *testing.T, dut *ondatra.DUTDevice) { } gnmi.Replace(t, dut, dutForwardingPath.Interface(interfaceID).Config(), interface1) } + +// DeletePolicyForwarding deletes policy configured under given interface. +func DeletePolicyForwarding(t *testing.T, dut *ondatra.DUTDevice, portID string) { + t.Helper() + p1 := dut.Port(t, portID) + ingressPort := p1.Name() + interfaceID := ingressPort + if deviations.InterfaceRefInterfaceIDFormat(dut) { + interfaceID = ingressPort + ".0" + } + pfpath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).PolicyForwarding().Interface(interfaceID) + gnmi.Delete(t, dut, pfpath.Config()) +} diff --git a/proto/metadata.proto b/proto/metadata.proto index 6cfed151bbc..5735220a367 100644 --- a/proto/metadata.proto +++ b/proto/metadata.proto @@ -427,6 +427,66 @@ message Metadata { // Devices missing component tree mapping from hardware port // to optical channel. bool missing_port_to_optical_channel_component_mapping = 150; + // Skip gNMI container OP tc. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + bool skip_container_op = 151; + // Reorder calls for vendor compatibility. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + bool reorder_calls_for_vendor_compatibilty = 152; + // Add missing base config using cli. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + bool add_missing_base_config_via_cli = 153; + // skip_macaddress_check returns true if mac address for an interface via gNMI needs to be skipped. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + bool skip_macaddress_check = 154; + // Devices are having native telemetry paths for BGP RIB verification. + // Juniper : b/306144372 + bool bgp_rib_oc_path_unsupported = 155; + // Skip setting prefix-set mode while configuring prefix-set routing-policy + bool skip_prefix_set_mode = 156; + // Devices set metric as preference for static next-hop + bool set_metric_as_preference = 157; + // Devices don't support having an IPv6 static Route with an IPv4 address + // as next hop and requires configuring a static ARP entry. + // Arista: https://partnerissuetracker.corp.google.com/issues/316593298 + bool ipv6_static_route_with_ipv4_next_hop_requires_static_arp = 158; + // Device requires policy-forwarding rules to be in sequential order in the gNMI set-request. + bool pf_require_sequential_order_pbr_rules = 159; + // Device telemetry missing next hop metric value. + // Arista: https://partnerissuetracker.corp.google.com/issues/321010782 + bool missing_static_route_next_hop_metric_telemetry = 160; + // Device does not support recursive resolution of static route next hop. + // Arista: https://partnerissuetracker.corp.google.com/issues/314449182 + bool unsupported_static_route_next_hop_recurse = 161; + // Device missing telemetry for static route that has DROP next hop. + // Arista: https://partnerissuetracker.corp.google.com/issues/330619816 + bool missing_static_route_drop_next_hop_telemetry = 162; + // Device missing 400ZR optical-channel tunable parameters telemetry: + // min/max/avg. + // Arista: https://partnerissuetracker.corp.google.com/issues/319314781 + bool missing_zr_optical_channel_tunable_parameters_telemetry = 163; + // Device that does not support packet link qualification reflector packet sent/received stats. + bool plq_reflector_stats_unsupported = 164; + // Device that does not support PLQ Generator max_mtu to be atleast >= 8184. + uint32 plq_generator_capabilities_max_mtu = 165; + // Device that does not support PLQ Generator max_pps to be atleast >= 100000000. + uint64 plq_generator_capabilities_max_pps = 166; + // Support for bgp extended community index + bool bgp_extended_community_index_unsupported = 167; + // Support for bgp community set refs + bool bgp_community_set_refs_unsupported = 168; + // Arista device needs CLI knob to enable WECMP feature + bool rib_wecmp = 169; + // Device not supporting table-connection need to set this true + bool table_connections_unsupported = 170; + // Configure tag-set using vendor native model + bool use_vendor_native_tag_set_config = 171; + // Skip setting send-community-type in bgp global config + bool skip_bgp_send_community_type = 172; + // Device does not have a default deny action in the absence of a route policy + bool default_import_export_policy = 173; + // Support for bgp actions set-community method + bool bgp_actions_set_community_method_unsupported = 174; // Reserved field numbers and identifiers. reserved 84, 9, 28, 20, 90, 97, 55, 89, 19; } @@ -451,5 +511,9 @@ message Metadata { // The `tags` used to identify the area(s) testcase applies to. An empty tag // is the default implying it applies to all areas. repeated Tags tags = 6; + + // Whether this test only checks paths for presence rather than semantic + // checks. + bool path_presence_test = 7; } diff --git a/proto/metadata_go_proto/metadata.pb.go b/proto/metadata_go_proto/metadata.pb.go index add2dc9e174..f0bd72fe372 100644 --- a/proto/metadata_go_proto/metadata.pb.go +++ b/proto/metadata_go_proto/metadata.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 +// protoc-gen-go v1.33.0 // protoc v3.21.12 // source: metadata.proto @@ -179,6 +179,9 @@ type Metadata struct { // The `tags` used to identify the area(s) testcase applies to. An empty tag // is the default implying it applies to all areas. Tags []Metadata_Tags `protobuf:"varint,6,rep,packed,name=tags,proto3,enum=openconfig.testing.Metadata_Tags" json:"tags,omitempty"` + // Whether this test only checks paths for presence rather than semantic + // checks. + PathPresenceTest bool `protobuf:"varint,7,opt,name=path_presence_test,json=pathPresenceTest,proto3" json:"path_presence_test,omitempty"` } func (x *Metadata) Reset() { @@ -255,6 +258,13 @@ func (x *Metadata) GetTags() []Metadata_Tags { return nil } +func (x *Metadata) GetPathPresenceTest() bool { + if x != nil { + return x.PathPresenceTest + } + return false +} + type Metadata_Platform struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -698,6 +708,66 @@ type Metadata_Deviations struct { // Devices missing component tree mapping from hardware port // to optical channel. MissingPortToOpticalChannelComponentMapping bool `protobuf:"varint,150,opt,name=missing_port_to_optical_channel_component_mapping,json=missingPortToOpticalChannelComponentMapping,proto3" json:"missing_port_to_optical_channel_component_mapping,omitempty"` + // Skip gNMI container OP tc. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + SkipContainerOp bool `protobuf:"varint,151,opt,name=skip_container_op,json=skipContainerOp,proto3" json:"skip_container_op,omitempty"` + // Reorder calls for vendor compatibility. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + ReorderCallsForVendorCompatibilty bool `protobuf:"varint,152,opt,name=reorder_calls_for_vendor_compatibilty,json=reorderCallsForVendorCompatibilty,proto3" json:"reorder_calls_for_vendor_compatibilty,omitempty"` + // Add missing base config using cli. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + AddMissingBaseConfigViaCli bool `protobuf:"varint,153,opt,name=add_missing_base_config_via_cli,json=addMissingBaseConfigViaCli,proto3" json:"add_missing_base_config_via_cli,omitempty"` + // skip_macaddress_check returns true if mac address for an interface via gNMI needs to be skipped. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + SkipMacaddressCheck bool `protobuf:"varint,154,opt,name=skip_macaddress_check,json=skipMacaddressCheck,proto3" json:"skip_macaddress_check,omitempty"` + // Devices are having native telemetry paths for BGP RIB verification. + // Juniper : b/306144372 + BgpRibOcPathUnsupported bool `protobuf:"varint,155,opt,name=bgp_rib_oc_path_unsupported,json=bgpRibOcPathUnsupported,proto3" json:"bgp_rib_oc_path_unsupported,omitempty"` + // Skip setting prefix-set mode while configuring prefix-set routing-policy + SkipPrefixSetMode bool `protobuf:"varint,156,opt,name=skip_prefix_set_mode,json=skipPrefixSetMode,proto3" json:"skip_prefix_set_mode,omitempty"` + // Devices set metric as preference for static next-hop + SetMetricAsPreference bool `protobuf:"varint,157,opt,name=set_metric_as_preference,json=setMetricAsPreference,proto3" json:"set_metric_as_preference,omitempty"` + // Devices don't support having an IPv6 static Route with an IPv4 address + // as next hop and requires configuring a static ARP entry. + // Arista: https://partnerissuetracker.corp.google.com/issues/316593298 + Ipv6StaticRouteWithIpv4NextHopRequiresStaticArp bool `protobuf:"varint,158,opt,name=ipv6_static_route_with_ipv4_next_hop_requires_static_arp,json=ipv6StaticRouteWithIpv4NextHopRequiresStaticArp,proto3" json:"ipv6_static_route_with_ipv4_next_hop_requires_static_arp,omitempty"` + // Device requires policy-forwarding rules to be in sequential order in the gNMI set-request. + PfRequireSequentialOrderPbrRules bool `protobuf:"varint,159,opt,name=pf_require_sequential_order_pbr_rules,json=pfRequireSequentialOrderPbrRules,proto3" json:"pf_require_sequential_order_pbr_rules,omitempty"` + // Device telemetry missing next hop metric value. + // Arista: https://partnerissuetracker.corp.google.com/issues/321010782 + MissingStaticRouteNextHopMetricTelemetry bool `protobuf:"varint,160,opt,name=missing_static_route_next_hop_metric_telemetry,json=missingStaticRouteNextHopMetricTelemetry,proto3" json:"missing_static_route_next_hop_metric_telemetry,omitempty"` + // Device does not support recursive resolution of static route next hop. + // Arista: https://partnerissuetracker.corp.google.com/issues/314449182 + UnsupportedStaticRouteNextHopRecurse bool `protobuf:"varint,161,opt,name=unsupported_static_route_next_hop_recurse,json=unsupportedStaticRouteNextHopRecurse,proto3" json:"unsupported_static_route_next_hop_recurse,omitempty"` + // Device missing telemetry for static route that has DROP next hop. + // Arista: https://partnerissuetracker.corp.google.com/issues/330619816 + MissingStaticRouteDropNextHopTelemetry bool `protobuf:"varint,162,opt,name=missing_static_route_drop_next_hop_telemetry,json=missingStaticRouteDropNextHopTelemetry,proto3" json:"missing_static_route_drop_next_hop_telemetry,omitempty"` + // Device missing 400ZR optical-channel tunable parameters telemetry: + // min/max/avg. + // Arista: https://partnerissuetracker.corp.google.com/issues/319314781 + MissingZrOpticalChannelTunableParametersTelemetry bool `protobuf:"varint,163,opt,name=missing_zr_optical_channel_tunable_parameters_telemetry,json=missingZrOpticalChannelTunableParametersTelemetry,proto3" json:"missing_zr_optical_channel_tunable_parameters_telemetry,omitempty"` + // Device that does not support packet link qualification reflector packet sent/received stats. + PlqReflectorStatsUnsupported bool `protobuf:"varint,164,opt,name=plq_reflector_stats_unsupported,json=plqReflectorStatsUnsupported,proto3" json:"plq_reflector_stats_unsupported,omitempty"` + // Device that does not support PLQ Generator max_mtu to be atleast >= 8184. + PlqGeneratorCapabilitiesMaxMtu uint32 `protobuf:"varint,165,opt,name=plq_generator_capabilities_max_mtu,json=plqGeneratorCapabilitiesMaxMtu,proto3" json:"plq_generator_capabilities_max_mtu,omitempty"` + // Device that does not support PLQ Generator max_pps to be atleast >= 100000000. + PlqGeneratorCapabilitiesMaxPps uint64 `protobuf:"varint,166,opt,name=plq_generator_capabilities_max_pps,json=plqGeneratorCapabilitiesMaxPps,proto3" json:"plq_generator_capabilities_max_pps,omitempty"` + // Support for bgp extended community index + BgpExtendedCommunityIndexUnsupported bool `protobuf:"varint,167,opt,name=bgp_extended_community_index_unsupported,json=bgpExtendedCommunityIndexUnsupported,proto3" json:"bgp_extended_community_index_unsupported,omitempty"` + // Support for bgp community set refs + BgpCommunitySetRefsUnsupported bool `protobuf:"varint,168,opt,name=bgp_community_set_refs_unsupported,json=bgpCommunitySetRefsUnsupported,proto3" json:"bgp_community_set_refs_unsupported,omitempty"` + // Arista device needs CLI knob to enable WECMP feature + RibWecmp bool `protobuf:"varint,169,opt,name=rib_wecmp,json=ribWecmp,proto3" json:"rib_wecmp,omitempty"` + // Device not supporting table-connection need to set this true + TableConnectionsUnsupported bool `protobuf:"varint,170,opt,name=table_connections_unsupported,json=tableConnectionsUnsupported,proto3" json:"table_connections_unsupported,omitempty"` + // Configure tag-set using vendor native model + UseVendorNativeTagSetConfig bool `protobuf:"varint,171,opt,name=use_vendor_native_tag_set_config,json=useVendorNativeTagSetConfig,proto3" json:"use_vendor_native_tag_set_config,omitempty"` + // Skip setting send-community-type in bgp global config + SkipBgpSendCommunityType bool `protobuf:"varint,172,opt,name=skip_bgp_send_community_type,json=skipBgpSendCommunityType,proto3" json:"skip_bgp_send_community_type,omitempty"` + // Device does not have a default deny action in the absence of a route policy + DefaultImportExportPolicy bool `protobuf:"varint,173,opt,name=default_import_export_policy,json=defaultImportExportPolicy,proto3" json:"default_import_export_policy,omitempty"` + // Support for bgp actions set-community method + BgpActionsSetCommunityMethodUnsupported bool `protobuf:"varint,174,opt,name=bgp_actions_set_community_method_unsupported,json=bgpActionsSetCommunityMethodUnsupported,proto3" json:"bgp_actions_set_community_method_unsupported,omitempty"` } func (x *Metadata_Deviations) Reset() { @@ -1670,6 +1740,174 @@ func (x *Metadata_Deviations) GetMissingPortToOpticalChannelComponentMapping() b return false } +func (x *Metadata_Deviations) GetSkipContainerOp() bool { + if x != nil { + return x.SkipContainerOp + } + return false +} + +func (x *Metadata_Deviations) GetReorderCallsForVendorCompatibilty() bool { + if x != nil { + return x.ReorderCallsForVendorCompatibilty + } + return false +} + +func (x *Metadata_Deviations) GetAddMissingBaseConfigViaCli() bool { + if x != nil { + return x.AddMissingBaseConfigViaCli + } + return false +} + +func (x *Metadata_Deviations) GetSkipMacaddressCheck() bool { + if x != nil { + return x.SkipMacaddressCheck + } + return false +} + +func (x *Metadata_Deviations) GetBgpRibOcPathUnsupported() bool { + if x != nil { + return x.BgpRibOcPathUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetSkipPrefixSetMode() bool { + if x != nil { + return x.SkipPrefixSetMode + } + return false +} + +func (x *Metadata_Deviations) GetSetMetricAsPreference() bool { + if x != nil { + return x.SetMetricAsPreference + } + return false +} + +func (x *Metadata_Deviations) GetIpv6StaticRouteWithIpv4NextHopRequiresStaticArp() bool { + if x != nil { + return x.Ipv6StaticRouteWithIpv4NextHopRequiresStaticArp + } + return false +} + +func (x *Metadata_Deviations) GetPfRequireSequentialOrderPbrRules() bool { + if x != nil { + return x.PfRequireSequentialOrderPbrRules + } + return false +} + +func (x *Metadata_Deviations) GetMissingStaticRouteNextHopMetricTelemetry() bool { + if x != nil { + return x.MissingStaticRouteNextHopMetricTelemetry + } + return false +} + +func (x *Metadata_Deviations) GetUnsupportedStaticRouteNextHopRecurse() bool { + if x != nil { + return x.UnsupportedStaticRouteNextHopRecurse + } + return false +} + +func (x *Metadata_Deviations) GetMissingStaticRouteDropNextHopTelemetry() bool { + if x != nil { + return x.MissingStaticRouteDropNextHopTelemetry + } + return false +} + +func (x *Metadata_Deviations) GetMissingZrOpticalChannelTunableParametersTelemetry() bool { + if x != nil { + return x.MissingZrOpticalChannelTunableParametersTelemetry + } + return false +} + +func (x *Metadata_Deviations) GetPlqReflectorStatsUnsupported() bool { + if x != nil { + return x.PlqReflectorStatsUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetPlqGeneratorCapabilitiesMaxMtu() uint32 { + if x != nil { + return x.PlqGeneratorCapabilitiesMaxMtu + } + return 0 +} + +func (x *Metadata_Deviations) GetPlqGeneratorCapabilitiesMaxPps() uint64 { + if x != nil { + return x.PlqGeneratorCapabilitiesMaxPps + } + return 0 +} + +func (x *Metadata_Deviations) GetBgpExtendedCommunityIndexUnsupported() bool { + if x != nil { + return x.BgpExtendedCommunityIndexUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetBgpCommunitySetRefsUnsupported() bool { + if x != nil { + return x.BgpCommunitySetRefsUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetRibWecmp() bool { + if x != nil { + return x.RibWecmp + } + return false +} + +func (x *Metadata_Deviations) GetTableConnectionsUnsupported() bool { + if x != nil { + return x.TableConnectionsUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetUseVendorNativeTagSetConfig() bool { + if x != nil { + return x.UseVendorNativeTagSetConfig + } + return false +} + +func (x *Metadata_Deviations) GetSkipBgpSendCommunityType() bool { + if x != nil { + return x.SkipBgpSendCommunityType + } + return false +} + +func (x *Metadata_Deviations) GetDefaultImportExportPolicy() bool { + if x != nil { + return x.DefaultImportExportPolicy + } + return false +} + +func (x *Metadata_Deviations) GetBgpActionsSetCommunityMethodUnsupported() bool { + if x != nil { + return x.BgpActionsSetCommunityMethodUnsupported + } + return false +} + type Metadata_PlatformExceptions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1733,7 +1971,7 @@ var file_metadata_proto_rawDesc = []byte{ 0x74, 0x69, 0x6e, 0x67, 0x1a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x72, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x62, 0x65, - 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfe, 0x53, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbf, 0x62, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x6e, 0x49, @@ -1752,660 +1990,777 @@ var file_metadata_proto_rawDesc = []byte{ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x1a, 0xb8, 0x01, 0x0a, - 0x08, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x2e, 0x0a, 0x06, 0x76, 0x65, 0x6e, - 0x64, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6f, 0x6e, 0x64, 0x61, - 0x74, 0x72, 0x61, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x56, 0x65, 0x6e, 0x64, 0x6f, - 0x72, 0x52, 0x06, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x61, 0x72, - 0x64, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x72, 0x65, 0x67, 0x65, - 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, - 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x34, 0x0a, 0x16, 0x73, - 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, - 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x73, 0x6f, 0x66, - 0x74, 0x77, 0x61, 0x72, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x67, 0x65, - 0x78, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x0e, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, - 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x1a, 0xff, 0x4b, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x70, 0x76, 0x34, 0x5f, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x70, 0x76, 0x34, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, - 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x39, 0x0a, 0x18, 0x74, 0x72, 0x61, 0x63, - 0x65, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x74, 0x72, 0x61, 0x63, - 0x65, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x1a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x5f, 0x6c, 0x34, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x75, 0x64, - 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x4c, 0x34, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x55, 0x64, 0x70, - 0x12, 0x3a, 0x0a, 0x19, 0x70, 0x72, 0x65, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x72, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x17, 0x70, 0x72, 0x65, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x57, 0x0a, 0x28, - 0x68, 0x69, 0x65, 0x72, 0x61, 0x72, 0x63, 0x68, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x77, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, - 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x25, - 0x68, 0x69, 0x65, 0x72, 0x61, 0x72, 0x63, 0x68, 0x69, 0x63, 0x61, 0x6c, 0x57, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6c, 0x65, - 0x72, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x1f, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x6d, 0x75, - 0x6c, 0x74, 0x69, 0x5f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x5f, 0x75, 0x6e, 0x73, - 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, - 0x69, 0x73, 0x69, 0x73, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, - 0x79, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x52, 0x0a, 0x26, - 0x69, 0x73, 0x69, 0x73, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x6c, - 0x65, 0x76, 0x65, 0x6c, 0x31, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, - 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x22, 0x69, 0x73, - 0x69, 0x73, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4c, 0x65, 0x76, 0x65, 0x6c, - 0x31, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, - 0x12, 0x41, 0x0a, 0x1d, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, - 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x69, 0x73, 0x69, 0x73, 0x53, 0x69, 0x6e, - 0x67, 0x6c, 0x65, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x1e, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x69, 0x73, 0x69, - 0x73, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x51, 0x0a, 0x26, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, - 0x63, 0x65, 0x5f, 0x61, 0x66, 0x69, 0x5f, 0x73, 0x61, 0x66, 0x69, 0x5f, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, - 0x67, 0x49, 0x73, 0x69, 0x73, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x41, 0x66, - 0x69, 0x53, 0x61, 0x66, 0x69, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x54, 0x0a, 0x27, 0x69, - 0x73, 0x69, 0x73, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x65, - 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x72, 0x65, - 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x69, 0x73, - 0x69, 0x73, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x64, 0x12, 0x58, 0x0a, 0x29, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, - 0x69, 0x74, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x0d, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x25, 0x69, 0x73, 0x69, 0x73, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, - 0x69, 0x74, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x49, 0x0a, 0x21, 0x69, - 0x73, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x75, 0x70, 0x70, - 0x72, 0x65, 0x73, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x69, 0x73, 0x69, 0x73, 0x52, 0x65, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x53, 0x75, 0x70, 0x70, 0x72, 0x65, 0x73, 0x73, 0x55, 0x6e, 0x73, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x69, 0x70, 0x5f, 0x6e, 0x65, 0x69, - 0x67, 0x68, 0x62, 0x6f, 0x72, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x0f, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x70, 0x4e, 0x65, 0x69, 0x67, 0x68, 0x62, 0x6f, 0x72, 0x4d, - 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x6f, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x6f, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x18, 0x10, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x12, 0x6f, 0x73, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, - 0x6f, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x12, 0x37, 0x0a, 0x18, 0x6f, 0x73, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x62, 0x79, - 0x5f, 0x72, 0x70, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x6f, 0x73, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6c, 0x6c, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x6e, 0x64, 0x62, 0x79, 0x52, 0x70, - 0x12, 0x50, 0x0a, 0x25, 0x6c, 0x6c, 0x64, 0x70, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, - 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, - 0x64, 0x65, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x21, 0x6c, 0x6c, 0x64, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x47, 0x6c, 0x6f, 0x62, - 0x61, 0x6c, 0x12, 0x55, 0x0a, 0x28, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x67, - 0x70, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x15, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x42, 0x67, 0x70, - 0x4c, 0x61, 0x73, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x47, 0x0a, 0x20, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x16, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x1d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, - 0x66, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, - 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, - 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x17, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x14, 0x73, 0x74, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x55, 0x6e, 0x73, - 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x3f, 0x0a, 0x1d, 0x69, 0x70, 0x76, 0x36, - 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x67, 0x72, 0x69, 0x62, - 0x69, 0x5f, 0x6e, 0x68, 0x5f, 0x64, 0x6d, 0x61, 0x63, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x18, 0x69, 0x70, 0x76, 0x36, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x47, 0x72, - 0x69, 0x62, 0x69, 0x4e, 0x68, 0x44, 0x6d, 0x61, 0x63, 0x12, 0x45, 0x0a, 0x1f, 0x65, 0x63, 0x6e, - 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x64, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x19, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x1c, 0x65, 0x63, 0x6e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x45, 0x0a, 0x1f, 0x69, 0x70, 0x76, 0x36, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, - 0x65, 0x64, 0x5f, 0x70, 0x6b, 0x74, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, - 0x74, 0x65, 0x64, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x69, 0x70, 0x76, 0x36, 0x44, - 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x65, 0x64, 0x50, 0x6b, 0x74, 0x73, 0x55, 0x6e, 0x73, 0x75, - 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x1e, 0x64, 0x72, 0x6f, 0x70, 0x5f, - 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x5f, 0x75, 0x6e, - 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x1b, 0x64, 0x72, 0x6f, 0x70, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x65, 0x61, 0x76, 0x65, - 0x73, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x1c, - 0x63, 0x6c, 0x69, 0x5f, 0x74, 0x61, 0x6b, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x65, 0x63, 0x65, 0x64, - 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x6f, 0x63, 0x18, 0x1d, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x18, 0x63, 0x6c, 0x69, 0x54, 0x61, 0x6b, 0x65, 0x73, 0x50, 0x72, 0x65, 0x63, - 0x65, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x4f, 0x63, 0x12, 0x3f, 0x0a, 0x1c, - 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, - 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x1e, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x19, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x49, 0x6e, 0x70, - 0x75, 0x74, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x3b, 0x0a, - 0x1a, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x5f, 0x63, 0x68, 0x69, 0x70, 0x5f, 0x69, 0x64, 0x5f, - 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x1f, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x17, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x43, 0x68, 0x69, 0x70, 0x49, 0x64, 0x55, - 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x51, 0x0a, 0x25, 0x62, 0x61, - 0x63, 0x6b, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x66, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x63, - 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, - 0x74, 0x65, 0x64, 0x18, 0x20, 0x20, 0x01, 0x28, 0x08, 0x52, 0x22, 0x62, 0x61, 0x63, 0x6b, 0x70, - 0x6c, 0x61, 0x6e, 0x65, 0x46, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, - 0x74, 0x79, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x49, 0x0a, - 0x21, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x65, 0x72, 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x18, 0x21, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, - 0x61, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x43, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x5a, 0x0a, 0x2b, 0x6e, 0x6f, 0x5f, 0x6d, - 0x69, 0x78, 0x5f, 0x6f, 0x66, 0x5f, 0x74, 0x61, 0x67, 0x67, 0x65, 0x64, 0x5f, 0x61, 0x6e, 0x64, - 0x5f, 0x75, 0x6e, 0x74, 0x61, 0x67, 0x67, 0x65, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x18, 0x22, 0x20, 0x01, 0x28, 0x08, 0x52, 0x25, 0x6e, - 0x6f, 0x4d, 0x69, 0x78, 0x4f, 0x66, 0x54, 0x61, 0x67, 0x67, 0x65, 0x64, 0x41, 0x6e, 0x64, 0x55, - 0x6e, 0x74, 0x61, 0x67, 0x67, 0x65, 0x64, 0x53, 0x75, 0x62, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, - 0x61, 0x63, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, - 0x5f, 0x70, 0x34, 0x72, 0x74, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, - 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x23, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x65, 0x78, 0x70, 0x6c, - 0x69, 0x63, 0x69, 0x74, 0x50, 0x34, 0x72, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6d, 0x70, - 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x3e, 0x0a, 0x1c, 0x75, 0x73, 0x65, 0x5f, 0x76, 0x65, 0x6e, - 0x64, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x61, 0x63, 0x6c, 0x5f, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x24, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x75, 0x73, 0x65, - 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x41, 0x63, 0x6c, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x77, 0x5f, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, - 0x25, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x73, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x49, 0x0a, 0x21, 0x65, - 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, - 0x65, 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x26, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x66, 0x44, 0x65, 0x66, 0x69, - 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x1d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x6e, 0x73, 0x75, - 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x27, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x73, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x55, - 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x50, 0x0a, 0x25, 0x65, 0x78, - 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x67, 0x72, 0x69, 0x62, 0x69, 0x5f, 0x75, 0x6e, 0x64, - 0x65, 0x72, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x65, 0x78, 0x70, 0x6c, 0x69, - 0x63, 0x69, 0x74, 0x47, 0x72, 0x69, 0x62, 0x69, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x4e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x13, - 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x70, - 0x65, 0x65, 0x64, 0x18, 0x29, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x65, 0x78, 0x70, 0x6c, 0x69, - 0x63, 0x69, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x65, 0x64, 0x12, 0x48, 0x0a, 0x21, - 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, - 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x72, - 0x66, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, - 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x49, 0x6e, 0x44, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x56, 0x72, 0x66, 0x12, 0x2c, 0x0a, 0x12, 0x71, 0x6f, 0x73, 0x5f, 0x64, 0x72, - 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x73, 0x18, 0x2b, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x10, 0x71, 0x6f, 0x73, 0x44, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x4f, 0x63, - 0x74, 0x65, 0x74, 0x73, 0x12, 0x4f, 0x0a, 0x24, 0x73, 0x75, 0x62, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x66, 0x61, 0x63, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x2c, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x21, 0x73, 0x75, 0x62, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, - 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x4d, 0x69, - 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x63, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x49, 0x0a, 0x22, 0x67, 0x72, - 0x69, 0x62, 0x69, 0x5f, 0x6d, 0x61, 0x63, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, - 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x61, 0x72, 0x70, - 0x18, 0x2e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x67, 0x72, 0x69, 0x62, 0x69, 0x4d, 0x61, 0x63, - 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x57, 0x69, 0x74, 0x68, 0x53, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x41, 0x72, 0x70, 0x12, 0x4a, 0x0a, 0x22, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x66, 0x69, 0x5f, - 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x2f, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x1e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x6e, - 0x64, 0x65, 0x72, 0x41, 0x66, 0x69, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, - 0x64, 0x12, 0x56, 0x0a, 0x28, 0x67, 0x6e, 0x6f, 0x69, 0x5f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, - 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x62, 0x6f, 0x6f, - 0x74, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x30, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x24, 0x67, 0x6e, 0x6f, 0x69, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x43, - 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x55, 0x6e, - 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x44, 0x0a, 0x1f, 0x6e, 0x74, 0x70, - 0x5f, 0x6e, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x72, 0x66, - 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x31, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x1b, 0x6e, 0x74, 0x70, 0x4e, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, - 0x74, 0x56, 0x72, 0x66, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, - 0x1e, 0x0a, 0x0b, 0x6f, 0x6d, 0x69, 0x74, 0x5f, 0x6c, 0x32, 0x5f, 0x6d, 0x74, 0x75, 0x18, 0x32, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6f, 0x6d, 0x69, 0x74, 0x4c, 0x32, 0x4d, 0x74, 0x75, 0x12, - 0x46, 0x0a, 0x20, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x5f, 0x63, 0x61, 0x72, 0x64, 0x5f, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x61, 0x64, - 0x6d, 0x69, 0x6e, 0x18, 0x33, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x73, 0x6b, 0x69, 0x70, 0x43, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x43, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x77, - 0x65, 0x72, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x61, 0x6e, 0x6e, 0x65, - 0x72, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x3c, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0f, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, - 0x65, 0x72, 0x12, 0x2e, 0x0a, 0x13, 0x62, 0x67, 0x70, 0x5f, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, - 0x6e, 0x63, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x11, 0x62, 0x67, 0x70, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x12, 0x4d, 0x0a, 0x24, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x71, 0x75, 0x61, 0x6c, 0x5f, - 0x77, 0x61, 0x69, 0x74, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x3e, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x1f, 0x6c, 0x69, 0x6e, 0x6b, 0x51, 0x75, 0x61, 0x6c, 0x57, 0x61, 0x69, 0x74, 0x41, 0x66, - 0x74, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x64, 0x12, 0x43, 0x0a, 0x1e, 0x67, 0x6e, 0x6f, 0x69, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x5f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x73, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, - 0x65, 0x6e, 0x74, 0x18, 0x3f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x67, 0x6e, 0x6f, 0x69, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x75, 0x62, 0x63, 0x6f, 0x6d, - 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x56, 0x0a, 0x28, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x64, 0x18, 0x40, 0x20, 0x01, 0x28, 0x08, 0x52, 0x24, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x33, - 0x0a, 0x16, 0x62, 0x67, 0x70, 0x5f, 0x6d, 0x64, 0x35, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x74, 0x18, 0x41, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, - 0x62, 0x67, 0x70, 0x4d, 0x64, 0x35, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x65, 0x74, 0x12, 0x4b, 0x0a, 0x23, 0x64, 0x65, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x64, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, - 0x64, 0x5f, 0x61, 0x73, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x73, 0x18, 0x42, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x1e, 0x64, 0x65, 0x71, 0x75, 0x65, 0x75, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, - 0x6f, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x41, 0x73, 0x44, 0x72, 0x6f, 0x70, 0x73, - 0x12, 0x2a, 0x0a, 0x11, 0x67, 0x72, 0x69, 0x62, 0x69, 0x5f, 0x72, 0x69, 0x62, 0x61, 0x63, 0x6b, - 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x43, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x67, 0x72, 0x69, - 0x62, 0x69, 0x52, 0x69, 0x62, 0x61, 0x63, 0x6b, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x36, 0x0a, 0x17, - 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, - 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x44, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x12, 0x3b, 0x0a, 0x1a, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, - 0x74, 0x73, 0x18, 0x45, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, - 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x46, 0x6f, 0x72, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, - 0x73, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x12, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x67, 0x6e, 0x6f, 0x69, 0x5f, 0x73, 0x75, 0x62, 0x63, - 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x47, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x14, 0x67, 0x6e, 0x6f, 0x69, 0x53, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x70, - 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x4c, 0x0a, 0x23, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, 0x72, - 0x66, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x48, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x72, 0x66, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x65, 0x70, 0x72, 0x65, - 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x76, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x49, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x10, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x56, - 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x58, 0x0a, 0x2a, 0x67, 0x72, 0x69, 0x62, 0x69, 0x5f, 0x6d, - 0x61, 0x63, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x5f, 0x61, 0x72, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x18, 0x4a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x24, 0x67, 0x72, 0x69, 0x62, 0x69, - 0x4d, 0x61, 0x63, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, - 0x63, 0x41, 0x72, 0x70, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, - 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x18, 0x4b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x66, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, - 0x71, 0x6f, 0x73, 0x5f, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x73, 0x18, 0x4c, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x09, 0x71, 0x6f, 0x73, 0x4f, 0x63, 0x74, 0x65, 0x74, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x63, - 0x70, 0x75, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6e, 0x63, 0x65, 0x73, - 0x74, 0x6f, 0x72, 0x18, 0x4d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x63, 0x70, 0x75, 0x4d, 0x69, - 0x73, 0x73, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x12, 0x41, 0x0a, - 0x1d, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x5f, - 0x73, 0x75, 0x62, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x30, 0x18, 0x4e, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x64, 0x53, 0x75, 0x62, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x30, - 0x12, 0x5f, 0x0a, 0x2d, 0x67, 0x6e, 0x6f, 0x69, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x6f, - 0x76, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, - 0x64, 0x18, 0x4f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x28, 0x67, 0x6e, 0x6f, 0x69, 0x53, 0x77, 0x69, - 0x74, 0x63, 0x68, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x4d, 0x69, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, - 0x64, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x50, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x16, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x4f, 0x0a, 0x24, 0x70, - 0x34, 0x72, 0x74, 0x5f, 0x75, 0x6e, 0x73, 0x65, 0x74, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x69, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x65, 0x64, 0x18, 0x51, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x70, 0x34, 0x72, 0x74, 0x55, - 0x6e, 0x73, 0x65, 0x74, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x50, 0x72, - 0x69, 0x6d, 0x61, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x3b, 0x0a, 0x1a, - 0x62, 0x6b, 0x75, 0x70, 0x5f, 0x61, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x72, 0x65, 0x73, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x52, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x17, 0x62, 0x6b, 0x75, 0x70, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x49, 0x0a, 0x22, 0x62, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x5f, 0x6e, 0x68, 0x67, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, - 0x5f, 0x76, 0x72, 0x66, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x64, 0x65, 0x63, 0x61, 0x70, 0x18, - 0x53, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4e, 0x68, 0x67, - 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x56, 0x72, 0x66, 0x57, 0x69, 0x74, 0x68, 0x44, - 0x65, 0x63, 0x61, 0x70, 0x12, 0x43, 0x0a, 0x1e, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x61, 0x66, 0x69, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x55, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x69, 0x73, - 0x69, 0x73, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x41, 0x66, 0x69, 0x55, 0x6e, - 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4c, 0x0a, 0x23, 0x70, 0x34, 0x72, - 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x65, - 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x18, 0x56, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x70, 0x34, 0x72, 0x74, 0x4d, 0x6f, 0x64, 0x69, - 0x66, 0x79, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x55, 0x6e, 0x73, 0x75, - 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x5e, 0x0a, 0x2d, 0x6f, 0x73, 0x5f, 0x63, 0x6f, - 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, - 0x73, 0x5f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x5f, 0x6f, 0x72, 0x5f, - 0x6c, 0x69, 0x6e, 0x65, 0x63, 0x61, 0x72, 0x64, 0x18, 0x57, 0x20, 0x01, 0x28, 0x08, 0x52, 0x27, - 0x6f, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x72, 0x65, 0x6e, - 0x74, 0x49, 0x73, 0x53, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x4f, 0x72, 0x4c, - 0x69, 0x6e, 0x65, 0x63, 0x61, 0x72, 0x64, 0x12, 0x42, 0x0a, 0x1e, 0x6f, 0x73, 0x5f, 0x63, 0x6f, - 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, - 0x73, 0x5f, 0x63, 0x68, 0x61, 0x73, 0x73, 0x69, 0x73, 0x18, 0x58, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x1a, 0x6f, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x49, 0x73, 0x43, 0x68, 0x61, 0x73, 0x73, 0x69, 0x73, 0x12, 0x57, 0x0a, 0x2a, 0x69, - 0x73, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x5f, 0x73, 0x61, 0x6d, 0x65, - 0x5f, 0x6c, 0x31, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, - 0x6c, 0x32, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x5b, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x23, 0x69, 0x73, 0x69, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x53, 0x61, 0x6d, 0x65, - 0x4c, 0x31, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x57, 0x69, 0x74, 0x68, 0x4c, 0x32, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x12, 0x57, 0x0a, 0x2a, 0x62, 0x67, 0x70, 0x5f, 0x73, 0x65, 0x74, 0x5f, - 0x6d, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x65, 0x71, 0x75, - 0x61, 0x6c, 0x5f, 0x6f, 0x73, 0x70, 0x66, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x18, 0x5c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x62, 0x67, 0x70, 0x53, 0x65, 0x74, - 0x4d, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x45, 0x71, 0x75, 0x61, 0x6c, - 0x4f, 0x73, 0x70, 0x66, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x4e, 0x0a, - 0x24, 0x70, 0x34, 0x72, 0x74, 0x5f, 0x67, 0x64, 0x70, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x73, 0x5f, 0x64, 0x6f, 0x74, 0x31, 0x71, 0x5f, 0x73, 0x75, 0x62, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x5d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x20, 0x70, 0x34, 0x72, - 0x74, 0x47, 0x64, 0x70, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x44, 0x6f, 0x74, 0x31, - 0x71, 0x53, 0x75, 0x62, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x59, 0x0a, - 0x2a, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, - 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x5e, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x25, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x6e, 0x73, - 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x65, 0x74, 0x5f, - 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x18, 0x5f, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0d, 0x73, 0x65, 0x74, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x55, 0x73, 0x65, 0x72, - 0x12, 0x73, 0x0a, 0x38, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x6c, 0x73, 0x70, 0x5f, 0x6c, 0x69, 0x66, - 0x65, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x6c, 0x73, 0x70, 0x5f, 0x72, 0x65, 0x66, 0x72, - 0x65, 0x73, 0x68, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x60, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x31, 0x69, 0x73, 0x69, 0x73, 0x4c, 0x73, 0x70, 0x4c, 0x69, 0x66, 0x65, 0x74, - 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x73, 0x4c, 0x73, 0x70, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x4f, 0x0a, 0x24, 0x6c, 0x69, 0x6e, 0x65, 0x63, 0x61, 0x72, - 0x64, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x62, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x21, 0x6c, 0x69, 0x6e, 0x65, 0x63, 0x61, 0x72, 0x64, 0x43, 0x70, 0x75, - 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x73, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x53, 0x0a, 0x26, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x73, - 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x18, 0x63, 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, - 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x5c, 0x0a, 0x2b, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x72, 0x64, 0x5f, 0x63, - 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, - 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x64, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x27, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x43, 0x61, 0x72, 0x64, - 0x43, 0x70, 0x75, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, - 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x45, 0x0a, 0x1f, 0x66, 0x61, 0x62, - 0x72, 0x69, 0x63, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, - 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x65, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x1c, 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x44, 0x72, 0x6f, 0x70, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x12, 0x55, 0x0a, 0x27, 0x6c, 0x69, 0x6e, 0x65, 0x63, 0x61, 0x72, 0x64, 0x5f, 0x6d, 0x65, 0x6d, - 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x66, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x24, 0x6c, 0x69, 0x6e, 0x65, 0x63, 0x61, 0x72, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, - 0x79, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x73, 0x75, - 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x20, 0x71, 0x6f, 0x73, 0x5f, 0x76, - 0x6f, 0x71, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, - 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x67, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x1c, 0x71, 0x6f, 0x73, 0x56, 0x6f, 0x71, 0x44, 0x72, 0x6f, 0x70, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, - 0x44, 0x0a, 0x1f, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x70, 0x76, 0x36, 0x5f, 0x66, 0x6c, 0x6f, 0x77, - 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, - 0x65, 0x64, 0x18, 0x68, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x61, 0x74, 0x65, 0x49, 0x70, 0x76, - 0x36, 0x46, 0x6c, 0x6f, 0x77, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, - 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x50, 0x0a, 0x25, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x72, 0x73, 0x5f, 0x63, 0x73, 0x6e, 0x70, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x69, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x69, 0x73, 0x69, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x73, - 0x43, 0x73, 0x6e, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x55, 0x6e, 0x73, 0x75, - 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x71, 0x0a, 0x37, 0x69, 0x73, 0x69, 0x73, 0x5f, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x66, 0x72, 0x6f, 0x6d, - 0x5f, 0x61, 0x72, 0x65, 0x61, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, - 0x65, 0x64, 0x18, 0x6a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x30, 0x69, 0x73, 0x69, 0x73, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x44, 0x72, 0x6f, 0x70, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x72, 0x65, 0x61, 0x73, 0x55, - 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x50, 0x0a, 0x25, 0x69, 0x73, - 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x5f, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, - 0x74, 0x65, 0x64, 0x18, 0x6b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x69, 0x73, 0x69, 0x73, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x50, 0x61, 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x73, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4c, 0x0a, 0x22, - 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x74, 0x68, 0x72, 0x65, - 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, - 0x65, 0x64, 0x18, 0x6c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, - 0x65, 0x69, 0x76, 0x65, 0x72, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x73, 0x55, - 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x20, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x6f, 0x70, 0x62, 0x61, 0x63, 0x6b, - 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x61, 0x77, 0x5f, 0x67, 0x6e, 0x6d, 0x69, 0x18, 0x6d, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4c, - 0x6f, 0x6f, 0x70, 0x62, 0x61, 0x63, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x61, 0x77, 0x47, 0x6e, - 0x6d, 0x69, 0x12, 0x40, 0x0a, 0x1d, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x6e, - 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x73, 0x73, 0x5f, 0x63, 0x68, - 0x65, 0x63, 0x6b, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x73, 0x6b, 0x69, 0x70, 0x54, - 0x63, 0x70, 0x4e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x73, 0x73, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x12, 0x4c, 0x0a, 0x23, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x6c, 0x73, 0x70, - 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6c, 0x65, 0x61, 0x66, 0x73, 0x5f, - 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x6f, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x1f, 0x69, 0x73, 0x69, 0x73, 0x4c, 0x73, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x4c, 0x65, 0x61, 0x66, 0x73, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, - 0x65, 0x64, 0x12, 0x31, 0x0a, 0x15, 0x71, 0x6f, 0x73, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, - 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x70, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x12, 0x71, 0x6f, 0x73, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x73, 0x49, 0x64, 0x12, 0x55, 0x0a, 0x28, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x66, 0x69, - 0x62, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, - 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x65, 0x63, - 0x6b, 0x18, 0x71, 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x73, 0x6b, 0x69, 0x70, 0x46, 0x69, 0x62, - 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x46, 0x6f, 0x72, - 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x50, 0x0a, 0x25, - 0x71, 0x6f, 0x73, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x72, 0x65, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x72, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x71, 0x6f, 0x73, - 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x66, - 0x0a, 0x31, 0x62, 0x67, 0x70, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x65, 0x78, 0x74, - 0x65, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x65, - 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, - 0x74, 0x65, 0x64, 0x18, 0x73, 0x20, 0x01, 0x28, 0x08, 0x52, 0x2b, 0x62, 0x67, 0x70, 0x47, 0x6c, - 0x6f, 0x62, 0x61, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x4e, 0x65, 0x78, 0x74, - 0x48, 0x6f, 0x70, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x55, 0x6e, 0x73, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x31, 0x0a, 0x15, 0x62, 0x67, 0x70, 0x5f, 0x6c, 0x6c, - 0x67, 0x72, 0x5f, 0x6f, 0x63, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x18, - 0x74, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x62, 0x67, 0x70, 0x4c, 0x6c, 0x67, 0x72, 0x4f, 0x63, - 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x1d, 0x74, 0x75, 0x6e, - 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x75, - 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x75, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x1a, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, - 0x68, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x1e, - 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x76, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x50, 0x61, 0x74, 0x68, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, - 0x64, 0x12, 0x51, 0x0a, 0x26, 0x65, 0x63, 0x6e, 0x5f, 0x73, 0x61, 0x6d, 0x65, 0x5f, 0x6d, 0x69, - 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x5f, - 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x77, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x21, 0x65, 0x63, 0x6e, 0x53, 0x61, 0x6d, 0x65, 0x4d, 0x69, 0x6e, 0x4d, 0x61, 0x78, - 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, - 0x72, 0x74, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x1d, 0x71, 0x6f, 0x73, 0x5f, 0x73, 0x63, 0x68, 0x65, - 0x64, 0x75, 0x6c, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x72, 0x65, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x78, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x71, 0x6f, 0x73, - 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x48, 0x0a, 0x21, 0x71, 0x6f, 0x73, 0x5f, 0x73, - 0x65, 0x74, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x79, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x1d, 0x71, 0x6f, 0x73, 0x53, 0x65, 0x74, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, - 0x64, 0x12, 0x42, 0x0a, 0x1e, 0x71, 0x6f, 0x73, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, - 0x74, 0x65, 0x64, 0x18, 0x7a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x71, 0x6f, 0x73, 0x47, 0x65, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, - 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x6c, 0x65, - 0x76, 0x65, 0x6c, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x7b, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x10, 0x69, 0x73, 0x69, 0x73, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x12, 0x48, 0x0a, 0x21, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, - 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x69, - 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x7c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x66, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x66, 0x61, 0x63, 0x65, 0x49, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x47, 0x0a, - 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6c, 0x6f, 0x6f, - 0x70, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, - 0x64, 0x18, 0x7d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4c, - 0x69, 0x6e, 0x6b, 0x4c, 0x6f, 0x6f, 0x70, 0x62, 0x61, 0x63, 0x6b, 0x55, 0x6e, 0x73, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4d, 0x0a, 0x24, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x70, - 0x6c, 0x71, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x70, 0x65, - 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x7e, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x73, 0x6b, 0x69, 0x70, 0x50, 0x6c, 0x71, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x4a, 0x0a, 0x22, 0x62, 0x67, 0x70, 0x5f, 0x65, 0x78, 0x70, - 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x18, 0x7f, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x1e, 0x62, 0x67, 0x70, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x50, 0x72, - 0x65, 0x66, 0x69, 0x78, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, - 0x64, 0x12, 0x58, 0x0a, 0x29, 0x62, 0x67, 0x70, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, - 0x5f, 0x6f, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x80, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x24, 0x62, 0x67, 0x70, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, - 0x67, 0x4f, 0x63, 0x4d, 0x61, 0x78, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x26, 0x73, - 0x6b, 0x69, 0x70, 0x5f, 0x62, 0x67, 0x70, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, - 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x61, 0x66, - 0x69, 0x73, 0x61, 0x66, 0x69, 0x18, 0x81, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x73, 0x6b, - 0x69, 0x70, 0x42, 0x67, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x57, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x41, 0x66, 0x69, 0x73, 0x61, 0x66, 0x69, 0x12, - 0x62, 0x0a, 0x2e, 0x6d, 0x69, 0x73, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x68, 0x61, - 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, - 0x74, 0x18, 0x82, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x29, 0x6d, 0x69, 0x73, 0x6d, 0x61, 0x74, - 0x63, 0x68, 0x65, 0x64, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x49, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, - 0x65, 0x6e, 0x74, 0x12, 0x68, 0x0a, 0x31, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x68, - 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x83, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x2c, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, - 0x79, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5d, 0x0a, - 0x2b, 0x67, 0x6e, 0x6f, 0x69, 0x5f, 0x73, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, - 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x84, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x27, 0x67, 0x6e, 0x6f, 0x69, 0x53, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x70, - 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x44, 0x0a, 0x1f, - 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x6e, 0x6f, 0x6e, 0x5f, 0x62, 0x67, 0x70, 0x5f, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, - 0x85, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x73, 0x6b, 0x69, 0x70, 0x4e, 0x6f, 0x6e, 0x42, - 0x67, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x65, - 0x63, 0x6b, 0x12, 0x55, 0x0a, 0x27, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, - 0x79, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x86, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x69, 0x73, 0x69, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x53, 0x74, 0x79, 0x6c, 0x65, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x55, 0x6e, - 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x63, 0x0a, 0x2f, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x68, - 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x66, - 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x87, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x29, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x4e, 0x65, 0x78, 0x74, 0x48, 0x6f, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, - 0x52, 0x65, 0x66, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x3a, - 0x0a, 0x19, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x6e, 0x65, - 0x78, 0x74, 0x68, 0x6f, 0x70, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x88, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x16, 0x73, 0x6b, 0x69, 0x70, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x4e, 0x65, - 0x78, 0x74, 0x68, 0x6f, 0x70, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x63, 0x74, 0x72, 0x6c, 0x5f, 0x66, 0x6c, - 0x61, 0x67, 0x18, 0x89, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x63, 0x74, 0x72, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x12, 0x5f, 0x0a, - 0x2c, 0x69, 0x70, 0x76, 0x36, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x76, - 0x65, 0x72, 0x74, 0x69, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x8a, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x28, 0x69, 0x70, 0x76, 0x36, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, - 0x41, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x5d, - 0x0a, 0x2b, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x65, - 0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, - 0x79, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x8b, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x27, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x4c, 0x69, 0x6d, 0x69, - 0x74, 0x45, 0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, - 0x72, 0x79, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x43, 0x0a, - 0x1e, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x61, 0x73, 0x18, - 0x8c, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x73, 0x6b, 0x69, 0x70, 0x53, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, - 0x41, 0x73, 0x12, 0x40, 0x0a, 0x1d, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x70, 0x62, 0x66, 0x5f, 0x77, - 0x69, 0x74, 0x68, 0x5f, 0x64, 0x65, 0x63, 0x61, 0x70, 0x5f, 0x65, 0x6e, 0x63, 0x61, 0x70, 0x5f, - 0x76, 0x72, 0x66, 0x18, 0x8d, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x73, 0x6b, 0x69, 0x70, - 0x50, 0x62, 0x66, 0x57, 0x69, 0x74, 0x68, 0x44, 0x65, 0x63, 0x61, 0x70, 0x45, 0x6e, 0x63, 0x61, - 0x70, 0x56, 0x72, 0x66, 0x12, 0x31, 0x0a, 0x14, 0x74, 0x74, 0x6c, 0x5f, 0x63, 0x6f, 0x70, 0x79, - 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x8e, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x12, 0x74, 0x74, 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x55, 0x6e, 0x73, 0x75, - 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4b, 0x0a, 0x22, 0x67, 0x72, 0x69, 0x62, 0x69, - 0x5f, 0x64, 0x65, 0x63, 0x61, 0x70, 0x5f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x70, 0x6c, 0x65, - 0x6e, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x8f, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x67, 0x72, 0x69, 0x62, 0x69, 0x44, 0x65, 0x63, 0x61, 0x70, - 0x4d, 0x69, 0x78, 0x65, 0x64, 0x50, 0x6c, 0x65, 0x6e, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, - 0x72, 0x74, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x69, 0x73, 0x69, - 0x73, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x90, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x10, 0x73, 0x6b, 0x69, 0x70, 0x49, 0x73, 0x69, 0x73, 0x53, 0x65, 0x74, 0x4c, - 0x65, 0x76, 0x65, 0x6c, 0x12, 0x44, 0x0a, 0x1f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x69, 0x73, 0x69, - 0x73, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x73, 0x74, 0x79, - 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x91, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, - 0x73, 0x6b, 0x69, 0x70, 0x49, 0x73, 0x69, 0x73, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x40, 0x0a, 0x1d, 0x73, 0x6b, - 0x69, 0x70, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x70, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, - 0x73, 0x65, 0x74, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x92, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x18, 0x73, 0x6b, 0x69, 0x70, 0x53, 0x65, 0x74, 0x52, 0x70, 0x4d, 0x61, 0x74, - 0x63, 0x68, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x55, 0x0a, 0x27, - 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x70, 0x72, 0x6f, 0x70, - 0x61, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x93, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, - 0x73, 0x6b, 0x69, 0x70, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x72, 0x6f, 0x70, 0x61, 0x67, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x62, 0x0a, 0x2e, 0x62, 0x67, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, - 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, - 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x94, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x29, 0x62, 0x67, - 0x70, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, 0x61, 0x74, 0x63, 0x68, - 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x53, 0x65, 0x74, 0x55, 0x6e, 0x73, 0x75, - 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x1d, 0x70, 0x66, 0x5f, 0x72, 0x65, - 0x71, 0x75, 0x69, 0x72, 0x65, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x64, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x95, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x19, 0x70, 0x66, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x44, - 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x67, 0x0a, 0x31, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x6f, 0x70, - 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x63, 0x6f, - 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, - 0x96, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x2b, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x50, - 0x6f, 0x72, 0x74, 0x54, 0x6f, 0x4f, 0x70, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x70, - 0x69, 0x6e, 0x67, 0x4a, 0x04, 0x08, 0x54, 0x10, 0x55, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, - 0x04, 0x08, 0x1c, 0x10, 0x1d, 0x4a, 0x04, 0x08, 0x14, 0x10, 0x15, 0x4a, 0x04, 0x08, 0x5a, 0x10, - 0x5b, 0x4a, 0x04, 0x08, 0x61, 0x10, 0x62, 0x4a, 0x04, 0x08, 0x37, 0x10, 0x38, 0x4a, 0x04, 0x08, - 0x59, 0x10, 0x5a, 0x4a, 0x04, 0x08, 0x13, 0x10, 0x14, 0x1a, 0xa0, 0x01, 0x0a, 0x12, 0x50, 0x6c, - 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x41, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, - 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, - 0x6f, 0x72, 0x6d, 0x12, 0x47, 0x0a, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xfa, 0x01, 0x0a, - 0x07, 0x54, 0x65, 0x73, 0x74, 0x62, 0x65, 0x64, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x45, 0x53, 0x54, - 0x42, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, - 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, - 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, - 0x54, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x34, 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x02, 0x12, 0x1a, - 0x0a, 0x16, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, - 0x45, 0x5f, 0x32, 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x03, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, - 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, 0x5f, 0x34, 0x4c, - 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x04, 0x12, 0x1e, 0x0a, 0x1a, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, - 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, 0x5f, 0x39, 0x4c, 0x49, 0x4e, 0x4b, 0x53, - 0x5f, 0x4c, 0x41, 0x47, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, - 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, 0x5f, 0x32, 0x4c, - 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x06, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, - 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, 0x5f, 0x38, 0x4c, 0x49, 0x4e, 0x4b, 0x53, - 0x10, 0x07, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, - 0x54, 0x5f, 0x34, 0x30, 0x30, 0x5a, 0x52, 0x10, 0x08, 0x22, 0x6d, 0x0a, 0x04, 0x54, 0x61, 0x67, - 0x73, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x41, 0x47, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x41, 0x47, 0x53, 0x5f, - 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x18, 0x0a, - 0x14, 0x54, 0x41, 0x47, 0x53, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x43, 0x45, 0x4e, 0x54, 0x45, 0x52, - 0x5f, 0x45, 0x44, 0x47, 0x45, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x41, 0x47, 0x53, 0x5f, - 0x45, 0x44, 0x47, 0x45, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x41, 0x47, 0x53, 0x5f, 0x54, - 0x52, 0x41, 0x4e, 0x53, 0x49, 0x54, 0x10, 0x04, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x12, + 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x65, + 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x70, 0x61, 0x74, 0x68, 0x50, 0x72, + 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x54, 0x65, 0x73, 0x74, 0x1a, 0xb8, 0x01, 0x0a, 0x08, 0x50, + 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x2e, 0x0a, 0x06, 0x76, 0x65, 0x6e, 0x64, 0x6f, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x72, + 0x61, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x52, + 0x06, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x61, 0x72, 0x64, 0x77, + 0x61, 0x72, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4d, + 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x6f, 0x66, + 0x74, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, + 0x67, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x73, 0x6f, 0x66, 0x74, 0x77, + 0x61, 0x72, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x67, 0x65, 0x78, 0x4a, + 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x0e, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x5f, + 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x1a, 0x92, 0x5a, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x70, 0x76, 0x34, 0x5f, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x12, 0x69, 0x70, 0x76, 0x34, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x45, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x39, 0x0a, 0x18, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x3b, 0x0a, 0x1a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, + 0x6c, 0x34, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x75, 0x64, 0x70, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x4c, 0x34, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x55, 0x64, 0x70, 0x12, 0x3a, + 0x0a, 0x19, 0x70, 0x72, 0x65, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x72, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x17, 0x70, 0x72, 0x65, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x57, 0x0a, 0x28, 0x68, 0x69, + 0x65, 0x72, 0x61, 0x72, 0x63, 0x68, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6c, + 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x25, 0x68, 0x69, + 0x65, 0x72, 0x61, 0x72, 0x63, 0x68, 0x69, 0x63, 0x61, 0x6c, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, + 0x6e, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x1f, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x6d, 0x75, 0x6c, 0x74, + 0x69, 0x5f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x69, 0x73, + 0x69, 0x73, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x55, + 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x52, 0x0a, 0x26, 0x69, 0x73, + 0x69, 0x73, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x6c, 0x65, 0x76, + 0x65, 0x6c, 0x31, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x22, 0x69, 0x73, 0x69, 0x73, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x31, 0x44, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x41, + 0x0a, 0x1d, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x74, 0x6f, + 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x69, 0x73, 0x69, 0x73, 0x53, 0x69, 0x6e, 0x67, 0x6c, + 0x65, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x12, 0x43, 0x0a, 0x1e, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x69, 0x73, 0x69, 0x73, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x51, 0x0a, 0x26, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, + 0x67, 0x5f, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x5f, 0x61, 0x66, 0x69, 0x5f, 0x73, 0x61, 0x66, 0x69, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, + 0x73, 0x69, 0x73, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x41, 0x66, 0x69, 0x53, + 0x61, 0x66, 0x69, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x54, 0x0a, 0x27, 0x69, 0x73, 0x69, + 0x73, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x69, 0x73, 0x69, 0x73, + 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, + 0x58, 0x0a, 0x29, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, + 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x25, 0x69, 0x73, 0x69, 0x73, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x49, 0x0a, 0x21, 0x69, 0x73, 0x69, + 0x73, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x72, 0x65, + 0x73, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x69, 0x73, 0x69, 0x73, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x53, 0x75, 0x70, 0x70, 0x72, 0x65, 0x73, 0x73, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x69, 0x70, 0x5f, 0x6e, 0x65, 0x69, 0x67, 0x68, + 0x62, 0x6f, 0x72, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x0f, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x11, 0x69, 0x70, 0x4e, 0x65, 0x69, 0x67, 0x68, 0x62, 0x6f, 0x72, 0x4d, 0x69, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x6f, 0x73, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x5f, 0x6e, 0x6f, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x12, 0x6f, 0x73, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x72, + 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x12, 0x37, 0x0a, 0x18, 0x6f, 0x73, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6c, 0x6c, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x62, 0x79, 0x5f, 0x72, + 0x70, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x6f, 0x73, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6c, 0x6c, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x6e, 0x64, 0x62, 0x79, 0x52, 0x70, 0x12, 0x50, + 0x0a, 0x25, 0x6c, 0x6c, 0x64, 0x70, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, + 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x6c, + 0x6c, 0x64, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x12, 0x55, 0x0a, 0x28, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x67, 0x70, 0x5f, + 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x15, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x23, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x42, 0x67, 0x70, 0x4c, 0x61, + 0x73, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x47, 0x0a, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, + 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x1d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x66, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x12, 0x34, 0x0a, 0x16, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x75, + 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x14, 0x73, 0x74, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x55, 0x6e, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x3f, 0x0a, 0x1d, 0x69, 0x70, 0x76, 0x36, 0x5f, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x67, 0x72, 0x69, 0x62, 0x69, 0x5f, + 0x6e, 0x68, 0x5f, 0x64, 0x6d, 0x61, 0x63, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x69, + 0x70, 0x76, 0x36, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x47, 0x72, 0x69, 0x62, + 0x69, 0x4e, 0x68, 0x44, 0x6d, 0x61, 0x63, 0x12, 0x45, 0x0a, 0x1f, 0x65, 0x63, 0x6e, 0x5f, 0x70, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x1c, 0x65, 0x63, 0x6e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x64, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, + 0x0a, 0x1f, 0x69, 0x70, 0x76, 0x36, 0x5f, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x65, 0x64, + 0x5f, 0x70, 0x6b, 0x74, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x69, 0x70, 0x76, 0x36, 0x44, 0x69, 0x73, + 0x63, 0x61, 0x72, 0x64, 0x65, 0x64, 0x50, 0x6b, 0x74, 0x73, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x1e, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x77, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x5f, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, + 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x64, + 0x72, 0x6f, 0x70, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x55, + 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x1c, 0x63, 0x6c, + 0x69, 0x5f, 0x74, 0x61, 0x6b, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x65, 0x63, 0x65, 0x64, 0x65, 0x6e, + 0x63, 0x65, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x6f, 0x63, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x18, 0x63, 0x6c, 0x69, 0x54, 0x61, 0x6b, 0x65, 0x73, 0x50, 0x72, 0x65, 0x63, 0x65, 0x64, + 0x65, 0x6e, 0x63, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x4f, 0x63, 0x12, 0x3f, 0x0a, 0x1c, 0x73, 0x63, + 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x77, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x19, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x3b, 0x0a, 0x1a, 0x73, + 0x77, 0x69, 0x74, 0x63, 0x68, 0x5f, 0x63, 0x68, 0x69, 0x70, 0x5f, 0x69, 0x64, 0x5f, 0x75, 0x6e, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x17, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x43, 0x68, 0x69, 0x70, 0x49, 0x64, 0x55, 0x6e, 0x73, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x51, 0x0a, 0x25, 0x62, 0x61, 0x63, 0x6b, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x66, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x61, 0x70, + 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x18, 0x20, 0x20, 0x01, 0x28, 0x08, 0x52, 0x22, 0x62, 0x61, 0x63, 0x6b, 0x70, 0x6c, 0x61, + 0x6e, 0x65, 0x46, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, + 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x49, 0x0a, 0x21, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, + 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x18, 0x21, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x5a, 0x0a, 0x2b, 0x6e, 0x6f, 0x5f, 0x6d, 0x69, 0x78, + 0x5f, 0x6f, 0x66, 0x5f, 0x74, 0x61, 0x67, 0x67, 0x65, 0x64, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x75, + 0x6e, 0x74, 0x61, 0x67, 0x67, 0x65, 0x64, 0x5f, 0x73, 0x75, 0x62, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x73, 0x18, 0x22, 0x20, 0x01, 0x28, 0x08, 0x52, 0x25, 0x6e, 0x6f, 0x4d, + 0x69, 0x78, 0x4f, 0x66, 0x54, 0x61, 0x67, 0x67, 0x65, 0x64, 0x41, 0x6e, 0x64, 0x55, 0x6e, 0x74, + 0x61, 0x67, 0x67, 0x65, 0x64, 0x53, 0x75, 0x62, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x70, + 0x34, 0x72, 0x74, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, + 0x6e, 0x74, 0x18, 0x23, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, + 0x69, 0x74, 0x50, 0x34, 0x72, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, + 0x65, 0x6e, 0x74, 0x12, 0x3e, 0x0a, 0x1c, 0x75, 0x73, 0x65, 0x5f, 0x76, 0x65, 0x6e, 0x64, 0x6f, + 0x72, 0x5f, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x61, 0x63, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x18, 0x24, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x75, 0x73, 0x65, 0x56, 0x65, + 0x6e, 0x64, 0x6f, 0x72, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x41, 0x63, 0x6c, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x77, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x25, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x14, 0x73, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x55, 0x6e, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x49, 0x0a, 0x21, 0x65, 0x78, 0x70, + 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, + 0x72, 0x65, 0x66, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x26, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x66, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x1d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, + 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x27, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x73, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x50, 0x0a, 0x25, 0x65, 0x78, 0x70, 0x6c, + 0x69, 0x63, 0x69, 0x74, 0x5f, 0x67, 0x72, 0x69, 0x62, 0x69, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x72, + 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, + 0x74, 0x47, 0x72, 0x69, 0x62, 0x69, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x78, + 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x65, + 0x64, 0x18, 0x29, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, + 0x74, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x65, 0x64, 0x12, 0x48, 0x0a, 0x21, 0x65, 0x78, + 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x5f, 0x69, 0x6e, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x72, 0x66, 0x18, + 0x2a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x49, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x56, 0x72, 0x66, 0x12, 0x2c, 0x0a, 0x12, 0x71, 0x6f, 0x73, 0x5f, 0x64, 0x72, 0x6f, 0x70, + 0x70, 0x65, 0x64, 0x5f, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x73, 0x18, 0x2b, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x10, 0x71, 0x6f, 0x73, 0x44, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x4f, 0x63, 0x74, 0x65, + 0x74, 0x73, 0x12, 0x4f, 0x0a, 0x24, 0x73, 0x75, 0x62, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x73, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x21, 0x73, 0x75, 0x62, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x50, 0x61, + 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x4d, 0x69, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x72, + 0x65, 0x74, 0x72, 0x79, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x49, 0x0a, 0x22, 0x67, 0x72, 0x69, 0x62, + 0x69, 0x5f, 0x6d, 0x61, 0x63, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x5f, 0x77, + 0x69, 0x74, 0x68, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x61, 0x72, 0x70, 0x18, 0x2e, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x67, 0x72, 0x69, 0x62, 0x69, 0x4d, 0x61, 0x63, 0x4f, 0x76, + 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x57, 0x69, 0x74, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x41, 0x72, 0x70, 0x12, 0x4a, 0x0a, 0x22, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x66, 0x69, 0x5f, 0x75, 0x6e, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x2f, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x1e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x6e, 0x64, 0x65, + 0x72, 0x41, 0x66, 0x69, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, + 0x56, 0x0a, 0x28, 0x67, 0x6e, 0x6f, 0x69, 0x5f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x5f, 0x63, + 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x5f, + 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x30, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x24, 0x67, 0x6e, 0x6f, 0x69, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x43, 0x6f, 0x6d, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x55, 0x6e, 0x73, 0x75, + 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x44, 0x0a, 0x1f, 0x6e, 0x74, 0x70, 0x5f, 0x6e, + 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x72, 0x66, 0x5f, 0x75, + 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x31, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x1b, 0x6e, 0x74, 0x70, 0x4e, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, + 0x72, 0x66, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x1e, 0x0a, + 0x0b, 0x6f, 0x6d, 0x69, 0x74, 0x5f, 0x6c, 0x32, 0x5f, 0x6d, 0x74, 0x75, 0x18, 0x32, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x09, 0x6f, 0x6d, 0x69, 0x74, 0x4c, 0x32, 0x4d, 0x74, 0x75, 0x12, 0x46, 0x0a, + 0x20, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x5f, 0x63, 0x61, 0x72, 0x64, 0x5f, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x18, 0x33, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x73, 0x6b, 0x69, 0x70, 0x43, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x43, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x77, 0x65, 0x72, + 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x5f, + 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, + 0x12, 0x2e, 0x0a, 0x13, 0x62, 0x67, 0x70, 0x5f, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, + 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x62, + 0x67, 0x70, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x4d, 0x0a, 0x24, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x71, 0x75, 0x61, 0x6c, 0x5f, 0x77, 0x61, + 0x69, 0x74, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f, + 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x3e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, + 0x6c, 0x69, 0x6e, 0x6b, 0x51, 0x75, 0x61, 0x6c, 0x57, 0x61, 0x69, 0x74, 0x41, 0x66, 0x74, 0x65, + 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, + 0x43, 0x0a, 0x1e, 0x67, 0x6e, 0x6f, 0x69, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x65, + 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x73, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, + 0x74, 0x18, 0x3f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x67, 0x6e, 0x6f, 0x69, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x70, 0x6f, + 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x56, 0x0a, 0x28, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x64, + 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, + 0x18, 0x40, 0x20, 0x01, 0x28, 0x08, 0x52, 0x24, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x33, 0x0a, 0x16, + 0x62, 0x67, 0x70, 0x5f, 0x6d, 0x64, 0x35, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, + 0x5f, 0x72, 0x65, 0x73, 0x65, 0x74, 0x18, 0x41, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x62, 0x67, + 0x70, 0x4d, 0x64, 0x35, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x65, + 0x74, 0x12, 0x4b, 0x0a, 0x23, 0x64, 0x65, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x64, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x5f, + 0x61, 0x73, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x73, 0x18, 0x42, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, + 0x64, 0x65, 0x71, 0x75, 0x65, 0x75, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x6f, 0x74, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x64, 0x41, 0x73, 0x44, 0x72, 0x6f, 0x70, 0x73, 0x12, 0x2a, + 0x0a, 0x11, 0x67, 0x72, 0x69, 0x62, 0x69, 0x5f, 0x72, 0x69, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x6f, + 0x6e, 0x6c, 0x79, 0x18, 0x43, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x67, 0x72, 0x69, 0x62, 0x69, + 0x52, 0x69, 0x62, 0x61, 0x63, 0x6b, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x36, 0x0a, 0x17, 0x61, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x44, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x61, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x12, 0x3b, 0x0a, 0x1a, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, + 0x18, 0x45, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x46, 0x6f, 0x72, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, + 0x30, 0x0a, 0x14, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x34, 0x0a, 0x16, 0x67, 0x6e, 0x6f, 0x69, 0x5f, 0x73, 0x75, 0x62, 0x63, 0x6f, 0x6d, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x47, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x14, 0x67, 0x6e, 0x6f, 0x69, 0x53, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, + 0x65, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x4c, 0x0a, 0x23, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, 0x72, 0x66, 0x5f, + 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x48, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x72, 0x66, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x76, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x49, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x10, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x56, 0x6c, 0x61, + 0x6e, 0x49, 0x64, 0x12, 0x58, 0x0a, 0x2a, 0x67, 0x72, 0x69, 0x62, 0x69, 0x5f, 0x6d, 0x61, 0x63, + 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x5f, 0x61, 0x72, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x18, 0x4a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x24, 0x67, 0x72, 0x69, 0x62, 0x69, 0x4d, 0x61, + 0x63, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, + 0x72, 0x70, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x2b, 0x0a, + 0x11, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x18, 0x4b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, + 0x61, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x6f, + 0x73, 0x5f, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x73, 0x18, 0x4c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x71, 0x6f, 0x73, 0x4f, 0x63, 0x74, 0x65, 0x74, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x70, 0x75, + 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, + 0x72, 0x18, 0x4d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x63, 0x70, 0x75, 0x4d, 0x69, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x12, 0x41, 0x0a, 0x1d, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x75, + 0x62, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x30, 0x18, 0x4e, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x1a, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x64, 0x53, 0x75, 0x62, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x30, 0x12, 0x5f, + 0x0a, 0x2d, 0x67, 0x6e, 0x6f, 0x69, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x6f, 0x76, 0x65, + 0x72, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, + 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x18, + 0x4f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x28, 0x67, 0x6e, 0x6f, 0x69, 0x53, 0x77, 0x69, 0x74, 0x63, + 0x68, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x4d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x12, + 0x38, 0x0a, 0x18, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x50, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x16, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x4f, 0x0a, 0x24, 0x70, 0x34, 0x72, + 0x74, 0x5f, 0x75, 0x6e, 0x73, 0x65, 0x74, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x69, + 0x64, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, + 0x64, 0x18, 0x51, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x70, 0x34, 0x72, 0x74, 0x55, 0x6e, 0x73, + 0x65, 0x74, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x50, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x3b, 0x0a, 0x1a, 0x62, 0x6b, + 0x75, 0x70, 0x5f, 0x61, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, + 0x65, 0x73, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x52, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, + 0x62, 0x6b, 0x75, 0x70, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x49, 0x0a, 0x22, 0x62, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x5f, 0x6e, 0x68, 0x67, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x76, + 0x72, 0x66, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x64, 0x65, 0x63, 0x61, 0x70, 0x18, 0x53, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x1d, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4e, 0x68, 0x67, 0x52, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x56, 0x72, 0x66, 0x57, 0x69, 0x74, 0x68, 0x44, 0x65, 0x63, + 0x61, 0x70, 0x12, 0x43, 0x0a, 0x1e, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x5f, 0x61, 0x66, 0x69, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x18, 0x55, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x69, 0x73, 0x69, 0x73, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x41, 0x66, 0x69, 0x55, 0x6e, 0x73, 0x75, + 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4c, 0x0a, 0x23, 0x70, 0x34, 0x72, 0x74, 0x5f, + 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x65, 0x6e, 0x74, + 0x72, 0x79, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x56, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x70, 0x34, 0x72, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x5e, 0x0a, 0x2d, 0x6f, 0x73, 0x5f, 0x63, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x73, 0x5f, + 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x5f, 0x6f, 0x72, 0x5f, 0x6c, 0x69, + 0x6e, 0x65, 0x63, 0x61, 0x72, 0x64, 0x18, 0x57, 0x20, 0x01, 0x28, 0x08, 0x52, 0x27, 0x6f, 0x73, + 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, + 0x73, 0x53, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x4f, 0x72, 0x4c, 0x69, 0x6e, + 0x65, 0x63, 0x61, 0x72, 0x64, 0x12, 0x42, 0x0a, 0x1e, 0x6f, 0x73, 0x5f, 0x63, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x73, 0x5f, + 0x63, 0x68, 0x61, 0x73, 0x73, 0x69, 0x73, 0x18, 0x58, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x6f, + 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x49, 0x73, 0x43, 0x68, 0x61, 0x73, 0x73, 0x69, 0x73, 0x12, 0x57, 0x0a, 0x2a, 0x69, 0x73, 0x69, + 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x5f, 0x73, 0x61, 0x6d, 0x65, 0x5f, 0x6c, + 0x31, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6c, 0x32, + 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x5b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x69, + 0x73, 0x69, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x53, 0x61, 0x6d, 0x65, 0x4c, 0x31, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x57, 0x69, 0x74, 0x68, 0x4c, 0x32, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x12, 0x57, 0x0a, 0x2a, 0x62, 0x67, 0x70, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x6d, 0x65, + 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x65, 0x71, 0x75, 0x61, 0x6c, + 0x5f, 0x6f, 0x73, 0x70, 0x66, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x18, 0x5c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x62, 0x67, 0x70, 0x53, 0x65, 0x74, 0x4d, 0x65, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x45, 0x71, 0x75, 0x61, 0x6c, 0x4f, 0x73, + 0x70, 0x66, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x4e, 0x0a, 0x24, 0x70, + 0x34, 0x72, 0x74, 0x5f, 0x67, 0x64, 0x70, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, + 0x5f, 0x64, 0x6f, 0x74, 0x31, 0x71, 0x5f, 0x73, 0x75, 0x62, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, + 0x61, 0x63, 0x65, 0x18, 0x5d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x20, 0x70, 0x34, 0x72, 0x74, 0x47, + 0x64, 0x70, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x44, 0x6f, 0x74, 0x31, 0x71, 0x53, + 0x75, 0x62, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x59, 0x0a, 0x2a, 0x61, + 0x74, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x75, 0x6e, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x5e, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x25, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x6e, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x65, 0x74, 0x5f, 0x6e, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x18, 0x5f, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0d, 0x73, 0x65, 0x74, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x73, + 0x0a, 0x38, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x6c, 0x73, 0x70, 0x5f, 0x6c, 0x69, 0x66, 0x65, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x6c, 0x73, 0x70, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, + 0x68, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x60, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x31, 0x69, 0x73, 0x69, 0x73, 0x4c, 0x73, 0x70, 0x4c, 0x69, 0x66, 0x65, 0x74, 0x69, 0x6d, + 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x73, 0x4c, 0x73, 0x70, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x76, 0x61, 0x6c, 0x12, 0x4f, 0x0a, 0x24, 0x6c, 0x69, 0x6e, 0x65, 0x63, 0x61, 0x72, 0x64, 0x5f, + 0x63, 0x70, 0x75, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x62, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x21, 0x6c, 0x69, 0x6e, 0x65, 0x63, 0x61, 0x72, 0x64, 0x43, 0x70, 0x75, 0x55, 0x74, + 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x12, 0x53, 0x0a, 0x26, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, + 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x63, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, + 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x55, 0x6e, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x5c, 0x0a, 0x2b, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x72, 0x64, 0x5f, 0x63, 0x70, 0x75, + 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x6e, 0x73, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x64, 0x20, 0x01, 0x28, 0x08, 0x52, 0x27, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x43, 0x61, 0x72, 0x64, 0x43, 0x70, + 0x75, 0x55, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x73, 0x75, + 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x45, 0x0a, 0x1f, 0x66, 0x61, 0x62, 0x72, 0x69, + 0x63, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x75, + 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x65, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x1c, 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x44, 0x72, 0x6f, 0x70, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x65, 0x72, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x55, + 0x0a, 0x27, 0x6c, 0x69, 0x6e, 0x65, 0x63, 0x61, 0x72, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x6e, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x66, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x24, 0x6c, 0x69, 0x6e, 0x65, 0x63, 0x61, 0x72, 0x64, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, + 0x74, 0x69, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x20, 0x71, 0x6f, 0x73, 0x5f, 0x76, 0x6f, 0x71, + 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x75, 0x6e, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x67, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x1c, 0x71, 0x6f, 0x73, 0x56, 0x6f, 0x71, 0x44, 0x72, 0x6f, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x65, 0x72, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x44, 0x0a, + 0x1f, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x70, 0x76, 0x36, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x6c, + 0x61, 0x62, 0x65, 0x6c, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x18, 0x68, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x61, 0x74, 0x65, 0x49, 0x70, 0x76, 0x36, 0x46, + 0x6c, 0x6f, 0x77, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, + 0x74, 0x65, 0x64, 0x12, 0x50, 0x0a, 0x25, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x72, 0x73, 0x5f, 0x63, 0x73, 0x6e, 0x70, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x69, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x21, 0x69, 0x73, 0x69, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x73, 0x43, 0x73, + 0x6e, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x71, 0x0a, 0x37, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x61, + 0x72, 0x65, 0x61, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x18, 0x6a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x30, 0x69, 0x73, 0x69, 0x73, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x65, 0x72, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x44, 0x72, 0x6f, 0x70, 0x46, 0x72, 0x6f, 0x6d, 0x41, 0x72, 0x65, 0x61, 0x73, 0x55, 0x6e, 0x73, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x50, 0x0a, 0x25, 0x69, 0x73, 0x69, 0x73, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x5f, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x18, 0x6b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x69, 0x73, 0x69, 0x73, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x50, 0x61, 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x55, + 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4c, 0x0a, 0x22, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, + 0x6f, 0x6c, 0x64, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x18, 0x6c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x72, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x73, 0x55, 0x6e, 0x73, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x20, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x6f, 0x70, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x6d, + 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x61, 0x77, 0x5f, 0x67, 0x6e, 0x6d, 0x69, 0x18, 0x6d, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x1c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4c, 0x6f, 0x6f, + 0x70, 0x62, 0x61, 0x63, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x61, 0x77, 0x47, 0x6e, 0x6d, 0x69, + 0x12, 0x40, 0x0a, 0x1d, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x74, 0x63, 0x70, 0x5f, 0x6e, 0x65, 0x67, + 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x73, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x73, 0x6b, 0x69, 0x70, 0x54, 0x63, 0x70, + 0x4e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x73, 0x73, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x12, 0x4c, 0x0a, 0x23, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x6c, 0x73, 0x70, 0x5f, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6c, 0x65, 0x61, 0x66, 0x73, 0x5f, 0x75, 0x6e, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x6f, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x1f, 0x69, 0x73, 0x69, 0x73, 0x4c, 0x73, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x4c, 0x65, 0x61, 0x66, 0x73, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x12, 0x31, 0x0a, 0x15, 0x71, 0x6f, 0x73, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x72, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x70, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x12, 0x71, 0x6f, 0x73, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x73, 0x49, 0x64, 0x12, 0x55, 0x0a, 0x28, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x66, 0x69, 0x62, 0x5f, + 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f, 0x66, + 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, + 0x71, 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x73, 0x6b, 0x69, 0x70, 0x46, 0x69, 0x62, 0x46, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x46, 0x6f, 0x72, 0x77, 0x61, + 0x72, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x50, 0x0a, 0x25, 0x71, 0x6f, + 0x73, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x18, 0x72, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x71, 0x6f, 0x73, 0x42, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x66, 0x0a, 0x31, + 0x62, 0x67, 0x70, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x64, 0x65, 0x64, 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x65, 0x6e, 0x63, + 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x18, 0x73, 0x20, 0x01, 0x28, 0x08, 0x52, 0x2b, 0x62, 0x67, 0x70, 0x47, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x4e, 0x65, 0x78, 0x74, 0x48, 0x6f, + 0x70, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x12, 0x31, 0x0a, 0x15, 0x62, 0x67, 0x70, 0x5f, 0x6c, 0x6c, 0x67, 0x72, + 0x5f, 0x6f, 0x63, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x18, 0x74, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x12, 0x62, 0x67, 0x70, 0x4c, 0x6c, 0x67, 0x72, 0x4f, 0x63, 0x55, 0x6e, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x1d, 0x74, 0x75, 0x6e, 0x6e, 0x65, + 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x75, 0x6e, 0x73, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x75, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, + 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x55, + 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x1e, 0x74, 0x75, + 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, + 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x76, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x1b, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x50, 0x61, 0x74, 0x68, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, + 0x51, 0x0a, 0x26, 0x65, 0x63, 0x6e, 0x5f, 0x73, 0x61, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, + 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x5f, 0x75, 0x6e, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x77, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x21, 0x65, 0x63, 0x6e, 0x53, 0x61, 0x6d, 0x65, 0x4d, 0x69, 0x6e, 0x4d, 0x61, 0x78, 0x54, 0x68, + 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x12, 0x41, 0x0a, 0x1d, 0x71, 0x6f, 0x73, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, + 0x6c, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x18, 0x78, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x71, 0x6f, 0x73, 0x53, 0x63, + 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x48, 0x0a, 0x21, 0x71, 0x6f, 0x73, 0x5f, 0x73, 0x65, 0x74, + 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x75, + 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x79, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x1d, 0x71, 0x6f, 0x73, 0x53, 0x65, 0x74, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, + 0x42, 0x0a, 0x1e, 0x71, 0x6f, 0x73, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x18, 0x7a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x71, 0x6f, 0x73, 0x47, 0x65, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, + 0x74, 0x65, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x6c, 0x65, 0x76, 0x65, + 0x6c, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x7b, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x10, 0x69, 0x73, 0x69, 0x73, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x12, 0x48, 0x0a, 0x21, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x72, + 0x65, 0x66, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x5f, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x7c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x66, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, + 0x61, 0x63, 0x65, 0x49, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x47, 0x0a, 0x20, 0x6d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6c, 0x6f, 0x6f, 0x70, 0x62, + 0x61, 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, + 0x7d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4c, 0x69, 0x6e, + 0x6b, 0x4c, 0x6f, 0x6f, 0x70, 0x62, 0x61, 0x63, 0x6b, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x12, 0x4d, 0x0a, 0x24, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x70, 0x6c, 0x71, + 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x7e, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x1f, 0x73, 0x6b, 0x69, 0x70, 0x50, 0x6c, 0x71, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x12, 0x4a, 0x0a, 0x22, 0x62, 0x67, 0x70, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, + 0x63, 0x69, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x18, 0x7f, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x1e, 0x62, 0x67, 0x70, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x50, 0x72, 0x65, 0x66, + 0x69, 0x78, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x12, + 0x58, 0x0a, 0x29, 0x62, 0x67, 0x70, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x6f, + 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x5f, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x80, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x24, 0x62, 0x67, 0x70, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x4f, + 0x63, 0x4d, 0x61, 0x78, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x26, 0x73, 0x6b, 0x69, + 0x70, 0x5f, 0x62, 0x67, 0x70, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x61, 0x66, 0x69, 0x73, + 0x61, 0x66, 0x69, 0x18, 0x81, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x73, 0x6b, 0x69, 0x70, + 0x42, 0x67, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x57, + 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x41, 0x66, 0x69, 0x73, 0x61, 0x66, 0x69, 0x12, 0x62, 0x0a, + 0x2e, 0x6d, 0x69, 0x73, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x68, 0x61, 0x72, 0x64, + 0x77, 0x61, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, + 0x82, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x29, 0x6d, 0x69, 0x73, 0x6d, 0x61, 0x74, 0x63, 0x68, + 0x65, 0x64, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x49, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, + 0x74, 0x12, 0x68, 0x0a, 0x31, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x68, 0x61, 0x72, + 0x64, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, + 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x83, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x2c, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x42, + 0x65, 0x66, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5d, 0x0a, 0x2b, 0x67, + 0x6e, 0x6f, 0x69, 0x5f, 0x73, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, + 0x5f, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x75, + 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x84, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x27, 0x67, 0x6e, 0x6f, 0x69, 0x53, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, + 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x44, 0x0a, 0x1f, 0x73, 0x6b, + 0x69, 0x70, 0x5f, 0x6e, 0x6f, 0x6e, 0x5f, 0x62, 0x67, 0x70, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x85, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x73, 0x6b, 0x69, 0x70, 0x4e, 0x6f, 0x6e, 0x42, 0x67, 0x70, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x12, 0x55, 0x0a, 0x27, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, + 0x73, 0x74, 0x79, 0x6c, 0x65, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, + 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x86, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x23, 0x69, 0x73, 0x69, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, 0x74, + 0x79, 0x6c, 0x65, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x55, 0x6e, 0x73, 0x75, + 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x63, 0x0a, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x68, 0x6f, 0x70, + 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x5f, 0x75, + 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x87, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x29, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x4e, 0x65, + 0x78, 0x74, 0x48, 0x6f, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, + 0x66, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x3a, 0x0a, 0x19, + 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x6e, 0x65, 0x78, 0x74, + 0x68, 0x6f, 0x70, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x88, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x16, 0x73, 0x6b, 0x69, 0x70, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x4e, 0x65, 0x78, 0x74, + 0x68, 0x6f, 0x70, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x63, 0x74, 0x72, 0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67, + 0x18, 0x89, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x46, + 0x6c, 0x6f, 0x77, 0x63, 0x74, 0x72, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x12, 0x5f, 0x0a, 0x2c, 0x69, + 0x70, 0x76, 0x36, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x76, 0x65, 0x72, + 0x74, 0x69, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, + 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x8a, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x28, 0x69, 0x70, 0x76, 0x36, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x41, 0x64, + 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x5d, 0x0a, 0x2b, + 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x65, 0x78, 0x63, + 0x65, 0x65, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, + 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x8b, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x27, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x45, + 0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, + 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x1e, 0x73, + 0x6b, 0x69, 0x70, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x61, 0x73, 0x18, 0x8c, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x73, 0x6b, 0x69, 0x70, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x41, 0x73, + 0x12, 0x40, 0x0a, 0x1d, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x70, 0x62, 0x66, 0x5f, 0x77, 0x69, 0x74, + 0x68, 0x5f, 0x64, 0x65, 0x63, 0x61, 0x70, 0x5f, 0x65, 0x6e, 0x63, 0x61, 0x70, 0x5f, 0x76, 0x72, + 0x66, 0x18, 0x8d, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x73, 0x6b, 0x69, 0x70, 0x50, 0x62, + 0x66, 0x57, 0x69, 0x74, 0x68, 0x44, 0x65, 0x63, 0x61, 0x70, 0x45, 0x6e, 0x63, 0x61, 0x70, 0x56, + 0x72, 0x66, 0x12, 0x31, 0x0a, 0x14, 0x74, 0x74, 0x6c, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x75, + 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x8e, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x12, 0x74, 0x74, 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4b, 0x0a, 0x22, 0x67, 0x72, 0x69, 0x62, 0x69, 0x5f, 0x64, + 0x65, 0x63, 0x61, 0x70, 0x5f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x70, 0x6c, 0x65, 0x6e, 0x5f, + 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x8f, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x1e, 0x67, 0x72, 0x69, 0x62, 0x69, 0x44, 0x65, 0x63, 0x61, 0x70, 0x4d, 0x69, + 0x78, 0x65, 0x64, 0x50, 0x6c, 0x65, 0x6e, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x69, 0x73, 0x69, 0x73, 0x5f, + 0x73, 0x65, 0x74, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x90, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x10, 0x73, 0x6b, 0x69, 0x70, 0x49, 0x73, 0x69, 0x73, 0x53, 0x65, 0x74, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x12, 0x44, 0x0a, 0x1f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x69, 0x73, 0x69, 0x73, 0x5f, + 0x73, 0x65, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x91, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x73, 0x6b, + 0x69, 0x70, 0x49, 0x73, 0x69, 0x73, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x53, + 0x74, 0x79, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x40, 0x0a, 0x1d, 0x73, 0x6b, 0x69, 0x70, + 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x70, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x65, + 0x74, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x92, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x18, 0x73, 0x6b, 0x69, 0x70, 0x53, 0x65, 0x74, 0x52, 0x70, 0x4d, 0x61, 0x74, 0x63, 0x68, + 0x53, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x55, 0x0a, 0x27, 0x73, 0x6b, + 0x69, 0x70, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x61, 0x67, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x93, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x73, 0x6b, + 0x69, 0x70, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x50, 0x72, 0x6f, 0x70, 0x61, 0x67, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x62, 0x0a, 0x2e, 0x62, 0x67, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, + 0x69, 0x74, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, + 0x74, 0x65, 0x64, 0x18, 0x94, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x29, 0x62, 0x67, 0x70, 0x43, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x43, 0x6f, + 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x53, 0x65, 0x74, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x1d, 0x70, 0x66, 0x5f, 0x72, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x95, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x70, + 0x66, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x67, 0x0a, 0x31, 0x6d, 0x69, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x6f, 0x70, 0x74, 0x69, + 0x63, 0x61, 0x6c, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x63, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x96, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x2b, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x72, + 0x74, 0x54, 0x6f, 0x4f, 0x70, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, + 0x67, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x5f, 0x6f, 0x70, 0x18, 0x97, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x73, + 0x6b, 0x69, 0x70, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4f, 0x70, 0x12, 0x51, + 0x0a, 0x25, 0x72, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x5f, + 0x66, 0x6f, 0x72, 0x5f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x74, 0x69, 0x62, 0x69, 0x6c, 0x74, 0x79, 0x18, 0x98, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, + 0x72, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x46, 0x6f, 0x72, 0x56, + 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, 0x74, + 0x79, 0x12, 0x44, 0x0a, 0x1f, 0x61, 0x64, 0x64, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, + 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, 0x69, 0x61, + 0x5f, 0x63, 0x6c, 0x69, 0x18, 0x99, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x61, 0x64, 0x64, + 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x56, 0x69, 0x61, 0x43, 0x6c, 0x69, 0x12, 0x33, 0x0a, 0x15, 0x73, 0x6b, 0x69, 0x70, 0x5f, + 0x6d, 0x61, 0x63, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x18, 0x9a, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x73, 0x6b, 0x69, 0x70, 0x4d, 0x61, 0x63, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x3d, 0x0a, 0x1b, + 0x62, 0x67, 0x70, 0x5f, 0x72, 0x69, 0x62, 0x5f, 0x6f, 0x63, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, + 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x9b, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x17, 0x62, 0x67, 0x70, 0x52, 0x69, 0x62, 0x4f, 0x63, 0x50, 0x61, 0x74, 0x68, + 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x73, + 0x6b, 0x69, 0x70, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x6d, + 0x6f, 0x64, 0x65, 0x18, 0x9c, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x6b, 0x69, 0x70, + 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x53, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x38, 0x0a, + 0x18, 0x73, 0x65, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x61, 0x73, 0x5f, 0x70, + 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x9d, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x15, 0x73, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x41, 0x73, 0x50, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x72, 0x0a, 0x38, 0x69, 0x70, 0x76, 0x36, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x77, 0x69, 0x74, + 0x68, 0x5f, 0x69, 0x70, 0x76, 0x34, 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, + 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, + 0x61, 0x72, 0x70, 0x18, 0x9e, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x2f, 0x69, 0x70, 0x76, 0x36, + 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x49, + 0x70, 0x76, 0x34, 0x4e, 0x65, 0x78, 0x74, 0x48, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x73, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x72, 0x70, 0x12, 0x50, 0x0a, 0x25, 0x70, + 0x66, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x72, 0x5f, 0x72, + 0x75, 0x6c, 0x65, 0x73, 0x18, 0x9f, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x20, 0x70, 0x66, 0x52, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x50, 0x62, 0x72, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x61, 0x0a, + 0x2e, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x18, + 0xa0, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x28, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x4e, 0x65, 0x78, 0x74, 0x48, 0x6f, + 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, + 0x12, 0x58, 0x0a, 0x29, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x65, 0x78, + 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x65, 0x18, 0xa1, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x24, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x4e, 0x65, 0x78, 0x74, + 0x48, 0x6f, 0x70, 0x52, 0x65, 0x63, 0x75, 0x72, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x2c, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x68, 0x6f, 0x70, + 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x18, 0xa2, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x26, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x44, 0x72, 0x6f, 0x70, 0x4e, 0x65, 0x78, 0x74, 0x48, 0x6f, 0x70, + 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x12, 0x73, 0x0a, 0x37, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x7a, 0x72, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, + 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x74, 0x75, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x74, 0x72, 0x79, 0x18, 0xa3, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x31, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x5a, 0x72, 0x4f, 0x70, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x54, 0x75, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x65, 0x74, 0x65, 0x72, 0x73, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x12, 0x46, + 0x0a, 0x1f, 0x70, 0x6c, 0x71, 0x5f, 0x72, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x18, 0xa4, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x70, 0x6c, 0x71, 0x52, 0x65, 0x66, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x55, 0x6e, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4b, 0x0a, 0x22, 0x70, 0x6c, 0x71, 0x5f, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x69, 0x65, 0x73, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6d, 0x74, 0x75, 0x18, 0xa5, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x1e, 0x70, 0x6c, 0x71, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x4d, 0x61, 0x78, + 0x4d, 0x74, 0x75, 0x12, 0x4b, 0x0a, 0x22, 0x70, 0x6c, 0x71, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, + 0x73, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x70, 0x73, 0x18, 0xa6, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x1e, 0x70, 0x6c, 0x71, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x43, 0x61, + 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x4d, 0x61, 0x78, 0x50, 0x70, 0x73, + 0x12, 0x57, 0x0a, 0x28, 0x62, 0x67, 0x70, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xa7, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x24, 0x62, 0x67, 0x70, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x55, 0x6e, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4b, 0x0a, 0x22, 0x62, 0x67, 0x70, + 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x72, + 0x65, 0x66, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, + 0xa8, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x62, 0x67, 0x70, 0x43, 0x6f, 0x6d, 0x6d, 0x75, + 0x6e, 0x69, 0x74, 0x79, 0x53, 0x65, 0x74, 0x52, 0x65, 0x66, 0x73, 0x55, 0x6e, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x69, 0x62, 0x5f, 0x77, 0x65, + 0x63, 0x6d, 0x70, 0x18, 0xa9, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x69, 0x62, 0x57, + 0x65, 0x63, 0x6d, 0x70, 0x12, 0x43, 0x0a, 0x1d, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xaa, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x6e, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x20, 0x75, 0x73, 0x65, + 0x5f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x74, + 0x61, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0xab, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x75, 0x73, 0x65, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x4e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x54, 0x61, 0x67, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x3f, 0x0a, 0x1c, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x62, 0x67, 0x70, 0x5f, 0x73, 0x65, + 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0xac, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x73, 0x6b, 0x69, 0x70, 0x42, 0x67, + 0x70, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x40, 0x0a, 0x1c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x69, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x18, 0xad, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x64, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x12, 0x5e, 0x0a, 0x2c, 0x62, 0x67, 0x70, 0x5f, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, + 0x79, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x18, 0xae, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x27, 0x62, 0x67, 0x70, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, + 0x69, 0x74, 0x79, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x4a, 0x04, 0x08, 0x54, 0x10, 0x55, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, + 0x4a, 0x04, 0x08, 0x1c, 0x10, 0x1d, 0x4a, 0x04, 0x08, 0x14, 0x10, 0x15, 0x4a, 0x04, 0x08, 0x5a, + 0x10, 0x5b, 0x4a, 0x04, 0x08, 0x61, 0x10, 0x62, 0x4a, 0x04, 0x08, 0x37, 0x10, 0x38, 0x4a, 0x04, + 0x08, 0x59, 0x10, 0x5a, 0x4a, 0x04, 0x08, 0x13, 0x10, 0x14, 0x1a, 0xa0, 0x01, 0x0a, 0x12, 0x50, + 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x41, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, + 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x47, 0x0a, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xfa, 0x01, + 0x0a, 0x07, 0x54, 0x65, 0x73, 0x74, 0x62, 0x65, 0x64, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x45, 0x53, + 0x54, 0x42, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, + 0x54, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, + 0x55, 0x54, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x34, 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x02, 0x12, + 0x1a, 0x0a, 0x16, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, + 0x54, 0x45, 0x5f, 0x32, 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x03, 0x12, 0x1a, 0x0a, 0x16, 0x54, + 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, 0x5f, 0x34, + 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x04, 0x12, 0x1e, 0x0a, 0x1a, 0x54, 0x45, 0x53, 0x54, 0x42, + 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, 0x5f, 0x39, 0x4c, 0x49, 0x4e, 0x4b, + 0x53, 0x5f, 0x4c, 0x41, 0x47, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, 0x54, 0x45, 0x53, 0x54, 0x42, + 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, 0x5f, 0x32, + 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x06, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, 0x53, 0x54, 0x42, + 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, 0x5f, 0x38, 0x4c, 0x49, 0x4e, 0x4b, + 0x53, 0x10, 0x07, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, + 0x55, 0x54, 0x5f, 0x34, 0x30, 0x30, 0x5a, 0x52, 0x10, 0x08, 0x22, 0x6d, 0x0a, 0x04, 0x54, 0x61, + 0x67, 0x73, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x41, 0x47, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, + 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x41, 0x47, 0x53, + 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x18, + 0x0a, 0x14, 0x54, 0x41, 0x47, 0x53, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x43, 0x45, 0x4e, 0x54, 0x45, + 0x52, 0x5f, 0x45, 0x44, 0x47, 0x45, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x41, 0x47, 0x53, + 0x5f, 0x45, 0x44, 0x47, 0x45, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x41, 0x47, 0x53, 0x5f, + 0x54, 0x52, 0x41, 0x4e, 0x53, 0x49, 0x54, 0x10, 0x04, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/testregistry.textproto b/testregistry.textproto index 95f0f5d539c..883d2bfcdba 100644 --- a/testregistry.textproto +++ b/testregistry.textproto @@ -487,7 +487,7 @@ test: { test: { id: "RT-1.27" description: "Static route to BGP redistribution" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/static-route_bgp_redistribution/README.md" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/static_route_bgp_redistribution/otg_tests/static_route_bgp_redistribution_test/README.md" exec: " " } test: { @@ -769,7 +769,7 @@ test: { test: { id: "RT-7.5" description: "BGP Policy - Set Link Bandwidth Community" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/policybase/otg_tests/link_bandwidth_test/README.md" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/experimental/bgp/otg_tests/link_bandwidth_test/README.md" } test: { id: "RT-7.6" @@ -790,9 +790,9 @@ test: { exec: " " } test: { - id: "RT-7.9" - description: "BGP Policy - Import/Export Policy Action Using Extended Communities" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/policybase/otg_tests/extcomm_action_test/README.md" + id: "RT-7.11" + description: "RT-7.11: BGP Policy - Import/Export Policy Action Using Multiple Criteria" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/policybase/otg_tests/import-export-multi/README.md" exec: " " } test: { @@ -1034,21 +1034,26 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/gribi/ate_tests/supervisor_failure_test/README.md" exec: " " } -test: { - id: "TE-9" - description: "MPLS based forwarding Static LSP" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/gribi/otg_tests/mpls_compliance/README.md" -} test: { id: "TE-9.1" description: "Base gRIBI MPLS Compliance" readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/gribi/otg_tests/static_lsp/README.md" } +test: { + id: "TE-9.2" + description: "MPLS based forwarding Static LSP" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/gribi/otg_tests/mpls_compliance/README.md" +} test: { id: "TE-10" description: "gRIBI MPLS Forwarding" readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/gribi/otg_tests/mpls_forwarding/README.md" } +test: { + id: "TE-16.2" + description: "gRIBI encapsulation FRR scenarios" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/gribi/otg_tests/encap_frr/README.md" +} test: { id: "TR-6.1" description: "system logging remote syslog" @@ -1331,14 +1336,8 @@ test: { } test: { id: "TRANSCEIVER-1" - description: "400ZR Electrical Signal to Noise Ratio(eSNR) and Chromatic Dispersion(CD) telemetry values streaming" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_esnr_and_cd_test/README.md" - exec: " " -} -test: { - id: "TRANSCEIVER-2" - description: "400ZR Optics Pre-FEC(Forward Error Correction) BER(Bit Error Rate)" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/ZR_pre-fec_ber_test/README.md" + description: "400ZR Chromatic Dispersion(CD) telemetry values streaming" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_cd_test/README.md" exec: " " } test: { @@ -1361,7 +1360,9 @@ test: { } test: { id: "TRANSCEIVER-6" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_db_q_value_test/README.md" + description: "400ZR Optics performance metrics (pm) streaming." + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_pm_test/README.md" + exec: " " } test: { id: "TRANSCEIVER-8" @@ -1399,3 +1400,9 @@ test: { readme: "https://github.com/openconfig/featureprofiles/feature/experimental/platform/tests/breakout_configuration/README.md" exec: " " } +test: { + id: "MGT-1" + description: "Management HA test" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/management/README.md" + exec: " " +} diff --git a/tools/addrundata/case.go b/tools/addrundata/case.go index 6be28a9404e..03fa2e8622d 100644 --- a/tools/addrundata/case.go +++ b/tools/addrundata/case.go @@ -11,6 +11,7 @@ import ( "github.com/google/uuid" mpb "github.com/openconfig/featureprofiles/proto/metadata_go_proto" + "github.com/openconfig/featureprofiles/tools/internal/fpciutil" "google.golang.org/protobuf/proto" ) @@ -23,8 +24,8 @@ type testcase struct { // read reads the markdown and existing rundata from the test directory. func (tc *testcase) read(testdir string) error { - if err := readFile(filepath.Join(testdir, "README.md"), tc.readMarkdown); err != nil { - return fmt.Errorf("could not parse README.md: %w", err) + if err := readFile(filepath.Join(testdir, fpciutil.READMEname), tc.readMarkdown); err != nil { + return fmt.Errorf("could not parse %s: %w", fpciutil.READMEname, err) } if err := readFile(filepath.Join(testdir, "metadata.textproto"), tc.readProto); err != nil && !os.IsNotExist(err) { return fmt.Errorf("could not parse metadata.textproto: %w", err) diff --git a/tools/addrundata/main.go b/tools/addrundata/main.go index 4322510e96c..dcc68dc748c 100644 --- a/tools/addrundata/main.go +++ b/tools/addrundata/main.go @@ -14,15 +14,13 @@ package main import ( - "errors" "fmt" "os" - "path/filepath" - "runtime" "flag" "github.com/golang/glog" + "github.com/openconfig/featureprofiles/tools/internal/fpciutil" ) var ( @@ -32,37 +30,13 @@ var ( mergejson = flag.String("mergejson", "", "Merge the JSON listing from this JSON file.") ) -func isDir(path string) bool { - info, err := os.Stat(path) - if err != nil { - return false - } - return info.IsDir() -} - -func featureDir() (string, error) { - _, path, _, ok := runtime.Caller(0) - if !ok { - return "", errors.New("could not detect caller") - } - newpath := filepath.Dir(path) - for newpath != "." && newpath != "/" { - featurepath := filepath.Join(newpath, "feature") - if isDir(featurepath) { - return featurepath, nil - } - newpath = filepath.Dir(newpath) - } - return "", fmt.Errorf("feature root not found from %s", path) -} - func main() { flag.Parse() featuredir := *dir if featuredir == "" { var err error - featuredir, err = featureDir() + featuredir, err = fpciutil.FeatureDir() if err != nil { glog.Exitf("Unable to locate feature root: %v", err) } diff --git a/tools/internal/fpciutil/filepathutil.go b/tools/internal/fpciutil/filepathutil.go new file mode 100644 index 00000000000..2abd4ae2062 --- /dev/null +++ b/tools/internal/fpciutil/filepathutil.go @@ -0,0 +1,54 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package fpciutil contains filepath related utilities for featureprofiles CI. +package fpciutil + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "runtime" +) + +const ( + // READMEname is the name of all test READMEs according to the contribution guide. + READMEname = "README.md" +) + +func isDir(path string) bool { + info, err := os.Stat(path) + if err != nil { + return false + } + return info.IsDir() +} + +// FeatureDir finds the path to the feature directory from CWD. +func FeatureDir() (string, error) { + _, path, _, ok := runtime.Caller(0) + if !ok { + return "", errors.New("could not detect caller") + } + newpath := filepath.Dir(path) + for newpath != "." && newpath != "/" { + featurepath := filepath.Join(newpath, "feature") + if isDir(featurepath) { + return featurepath, nil + } + newpath = filepath.Dir(newpath) + } + return "", fmt.Errorf("feature root not found from %s", path) +} diff --git a/tools/internal/mdocspec/md.go b/tools/internal/mdocspec/md.go new file mode 100644 index 00000000000..f1fc8601bae --- /dev/null +++ b/tools/internal/mdocspec/md.go @@ -0,0 +1,104 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mdocspec + +import ( + "io" + + "github.com/yuin/goldmark" + "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/extension" + "github.com/yuin/goldmark/renderer" +) + +const ( + // OCSpecHeading is the MarkDown heading that MUST precede the yaml + // section containing the OC path and RPC listing. + OCSpecHeading = "OpenConfig Path and RPC Coverage" +) + +type mdOCSpecs struct{} + +// MDOCSpecs is an extension that only renders the first yaml block from a +// functional test README that comes after the pre-established OC Spec heading +// `OCSpecHeading`. +var MDOCSpecs = &mdOCSpecs{} + +func (e *mdOCSpecs) Extend(m goldmark.Markdown) { + extension.GFM.Extend(m) + m.SetRenderer(&yamlRenderer{}) +} + +type yamlRenderer struct{} + +func (r *yamlRenderer) Render(w io.Writer, source []byte, n ast.Node) error { + return ast.Walk(n, func(n ast.Node, entering bool) (ast.WalkStatus, error) { + return renderYAML(w, source, n, entering) + }) +} + +func (r *yamlRenderer) AddOptions(...renderer.Option) {} + +func ocSpecHeading(source []byte, n ast.Node) (heading *ast.Heading, ok bool) { + if h, ok := n.(*ast.Heading); ok { + if h.Lines().Len() == 0 { + return nil, false + } + headingSegment := h.Lines().At(0) + if string(headingSegment.Value(source)) == OCSpecHeading { + return h, true + } + } + return nil, false +} + +func yamlCodeBlock(source []byte, n ast.Node) (block *ast.FencedCodeBlock, ok bool) { + if c, ok := n.(*ast.FencedCodeBlock); ok && c.Info != nil { + if lang := c.Info.Text(source); len(lang) > 0 && string(lang) == "yaml" { + return c, true + } + } + return nil, false +} + +func renderYAML(w io.Writer, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { + if !entering { + return ast.WalkContinue, nil + } + heading, ok := ocSpecHeading(source, n) + if !ok { + return ast.WalkContinue, nil + } + // Check if prior to the next heading of the same level, + // a yaml code block can be found. + for next := heading.NextSibling(); next != nil; next = next.NextSibling() { + if h, ok := next.(*ast.Heading); ok && h.Level <= heading.Level { + // End of heading reached. + return ast.WalkContinue, nil + } + if c, ok := yamlCodeBlock(source, next); ok { + l := c.Lines().Len() + for i := 0; i != l; i++ { + line := c.Lines().At(i) + if _, err := w.Write(line.Value(source)); err != nil { + return ast.WalkStop, err + } + } + // Stop after finding the first such YAML block. + return ast.WalkStop, nil + } + } + return ast.WalkContinue, nil +} diff --git a/tools/internal/mdocspec/md_test.go b/tools/internal/mdocspec/md_test.go new file mode 100644 index 00000000000..e7b6a997c88 --- /dev/null +++ b/tools/internal/mdocspec/md_test.go @@ -0,0 +1,634 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mdocspec + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/yuin/goldmark" +) + +func TestRenderer(t *testing.T) { + tests := []struct { + desc string + inSource []byte + want string + }{{ + desc: "basic", + inSource: []byte(`--- +name: New featureprofiles test requirement +about: Use this template to document the requirements for a new test to be implemented. +title: '' +labels: enhancement +assignees: '' +--- + +# Instructions for this template + +Below is the required template for writing test requirements. Good examples of test +requirements include: + +* [TE-3.7: Base Hierarchical NHG Update](/feature/gribi/otg_tests/base_hierarchical_nhg_update/README.md) +* [gNMI-1.13: Telemetry: Optics Power and Bias Current](https://github.com/openconfig/featureprofiles/blob/main/feature/platform/tests/optics_power_and_bias_current_test/README.md) +* [RT-5.1: Singleton Interface](https://github.com/openconfig/featureprofiles/blob/main/feature/interface/singleton/otg_tests/singleton_test/README.md) + +# TestID-x.y: Short name of test here + +## Summary + +Write a few sentences or paragraphs describing the purpose and scope of the test. + +## Testbed type + +* Specify the .testbed topology file from the [topologies](https://github.com/openconfig/featureprofiles/tree/main/topologies) folder to be used with this test + +## Procedure + +* Test environment setup + * Description of procedure to configure ATE and DUT with pre-requisites making it possible to cover the intended paths and RPC's. + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +` + "```" + `yaml +paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + gNMI.Subscribe: + on_change: true +` + "```" + ` + +## Required DUT platform + +* Specify the minimum DUT-type: + * MFF - A modular form factor device containing LINECARDs, FABRIC and redundant CONTROLLER_CARD components + * FFF - fixed form factor + * vRX - virtual router device +`), + want: `paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + gNMI.Subscribe: + on_change: true +`, + }, { + desc: "second-yaml-block-in-separate-heading", + inSource: []byte(`--- +name: New featureprofiles test requirement +about: Use this template to document the requirements for a new test to be implemented. +title: '' +labels: enhancement +assignees: '' +--- + +## Procedure + +* Test environment setup + * Description of procedure to configure ATE and DUT with pre-requisites making it possible to cover the intended paths and RPC's. + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +` + "```" + `yaml +paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + gNMI.Subscribe: + on_change: true +` + "```" + ` + +## Required DUT platform + +` + "```" + `yaml +paths: + # interface configuration + /a/b/c: + /d/e/f: + +rpcs: + fooi: + fooi.Set: + union_replace: true + fooi.Subscribe: + on_change: true +` + "```" + ` + +* Specify the minimum DUT-type: + * MFF - A modular form factor device containing LINECARDs, FABRIC and redundant CONTROLLER_CARD components + * FFF - fixed form factor + * vRX - virtual router device +`), + want: `paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + gNMI.Subscribe: + on_change: true +`, + }, { + desc: "two-yaml-blocks-same-heading", + inSource: []byte(`--- +name: New featureprofiles test requirement +about: Use this template to document the requirements for a new test to be implemented. +title: '' +labels: enhancement +assignees: '' +--- + +## Procedure + +* Test environment setup + * Description of procedure to configure ATE and DUT with pre-requisites making it possible to cover the intended paths and RPC's. + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +` + "```" + `yaml +paths: + # interface configuration + /a/b/c: + /d/e/f: + +rpcs: + fooi: + fooi.Set: + union_replace: true + fooi.Subscribe: + on_change: true +` + "```" + ` + +` + "```" + `yaml +paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + gNMI.Subscribe: + on_change: true +` + "```" + ` + +## Required DUT platform + +* Specify the minimum DUT-type: + * MFF - A modular form factor device containing LINECARDs, FABRIC and redundant CONTROLLER_CARD components + * FFF - fixed form factor + * vRX - virtual router device +`), + want: `paths: + # interface configuration + /a/b/c: + /d/e/f: + +rpcs: + fooi: + fooi.Set: + union_replace: true + fooi.Subscribe: + on_change: true +`, + }, { + desc: "yaml-block-after-next-heading-ignored", + inSource: []byte(`--- +name: New featureprofiles test requirement +about: Use this template to document the requirements for a new test to be implemented. +title: '' +labels: enhancement +assignees: '' +--- + +## Procedure + +* Test environment setup + * Description of procedure to configure ATE and DUT with pre-requisites making it possible to cover the intended paths and RPC's. + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +## Required DUT platform + +` + "```" + `yaml +paths: + # interface configuration + /a/b/c: + /d/e/f: + +rpcs: + fooi: + fooi.Set: + union_replace: true + fooi.Subscribe: + on_change: true +` + "```" + ` + +* Specify the minimum DUT-type: + * MFF - A modular form factor device containing LINECARDs, FABRIC and redundant CONTROLLER_CARD components + * FFF - fixed form factor + * vRX - virtual router device +`), + want: ``, + }, { + desc: "yaml-block-after-next-higher-heading-ignored", + inSource: []byte(`--- +name: New featureprofiles test requirement +about: Use this template to document the requirements for a new test to be implemented. +title: '' +labels: enhancement +assignees: '' +--- + +## Procedure + +* Test environment setup + * Description of procedure to configure ATE and DUT with pre-requisites making it possible to cover the intended paths and RPC's. + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +# Required DUT platform + +Some text + +` + "```" + `yaml +paths: + # interface configuration + /a/b/c: + /d/e/f: + +rpcs: + fooi: + fooi.Set: + union_replace: true + fooi.Subscribe: + on_change: true +` + "```" + ` + +* Specify the minimum DUT-type: + * MFF - A modular form factor device containing LINECARDs, FABRIC and redundant CONTROLLER_CARD components + * FFF - fixed form factor + * vRX - virtual router device +`), + want: ``, + }, { + desc: "yaml-block-after-next-lower-heading-accepted", + inSource: []byte(`--- +name: New featureprofiles test requirement +about: Use this template to document the requirements for a new test to be implemented. +title: '' +labels: enhancement +assignees: '' +--- + +## Procedure + +* Test environment setup + * Description of procedure to configure ATE and DUT with pre-requisites making it possible to cover the intended paths and RPC's. + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +### Required DUT platform + +Some text + +` + "```" + `yaml +paths: + # interface configuration + /a/b/c: + /d/e/f: + +rpcs: + fooi: + fooi.Set: + union_replace: true + fooi.Subscribe: + on_change: true +` + "```" + ` + +* Specify the minimum DUT-type: + * MFF - A modular form factor device containing LINECARDs, FABRIC and redundant CONTROLLER_CARD components + * FFF - fixed form factor + * vRX - virtual router device +`), + want: `paths: + # interface configuration + /a/b/c: + /d/e/f: + +rpcs: + fooi: + fooi.Set: + union_replace: true + fooi.Subscribe: + on_change: true +`, + }, { + desc: "two-blocks-same-heading-first-language-not-specified-and-ignored", + inSource: []byte(`--- +name: New featureprofiles test requirement +about: Use this template to document the requirements for a new test to be implemented. +title: '' +labels: enhancement +assignees: '' +--- + +## Procedure + +* Test environment setup + * Description of procedure to configure ATE and DUT with pre-requisites making it possible to cover the intended paths and RPC's. + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +` + "```" + ` +paths: + # interface configuration + /a/b/c: + /d/e/f: + +rpcs: + fooi: + fooi.Set: + union_replace: true + fooi.Subscribe: + on_change: true +` + "```" + ` + +` + "```" + `yaml +paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + gNMI.Subscribe: + on_change: true +` + "```" + ` + +## Required DUT platform + +* Specify the minimum DUT-type: + * MFF - A modular form factor device containing LINECARDs, FABRIC and redundant CONTROLLER_CARD components + * FFF - fixed form factor + * vRX - virtual router device +`), + want: `paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + gNMI.Subscribe: + on_change: true +`, + }, { + desc: "no-yaml-blocks", + inSource: []byte(`--- +name: New featureprofiles test requirement +about: Use this template to document the requirements for a new test to be implemented. +title: '' +labels: enhancement +assignees: '' +--- + +## Procedure + +* Test environment setup + * Description of procedure to configure ATE and DUT with pre-requisites making it possible to cover the intended paths and RPC's. + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +## Required DUT platform + +* Specify the minimum DUT-type: + * MFF - A modular form factor device containing LINECARDs, FABRIC and redundant CONTROLLER_CARD components + * FFF - fixed form factor + * vRX - virtual router device +`), + want: ``, + }, { + desc: "no-yaml-blocks-last-heading", + inSource: []byte(`--- +name: New featureprofiles test requirement +about: Use this template to document the requirements for a new test to be implemented. +title: '' +labels: enhancement +assignees: '' +--- + +## Procedure + +* Test environment setup + * Description of procedure to configure ATE and DUT with pre-requisites making it possible to cover the intended paths and RPC's. + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +* Specify the minimum DUT-type: + * MFF - A modular form factor device containing LINECARDs, FABRIC and redundant CONTROLLER_CARD components + * FFF - fixed form factor + * vRX - virtual router device +`), + want: ``, + }, { + desc: "yaml-block-empty", + inSource: []byte(`--- +name: New featureprofiles test requirement +about: Use this template to document the requirements for a new test to be implemented. +title: '' +labels: enhancement +assignees: '' +--- + +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +` + "```" + `yaml +` + "```" + ` +`), + want: ``, + }} + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + var buf strings.Builder + md := goldmark.New( + goldmark.WithExtensions(MDOCSpecs), + ) + if err := md.Convert(tt.inSource, &buf); err != nil { + t.Fatalf("MDOCSpecs.Convert: %v", err) + } + if diff := cmp.Diff(tt.want, buf.String()); diff != "" { + t.Errorf("MDOCSpecs.Convert (-want, +got):\n%s", diff) + } + }) + } +} diff --git a/tools/internal/mdocspec/ocspec.go b/tools/internal/mdocspec/ocspec.go new file mode 100644 index 00000000000..a40c3ff1fc0 --- /dev/null +++ b/tools/internal/mdocspec/ocspec.go @@ -0,0 +1,145 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package mdocspec parses yaml OC requirements from functional test READMEs. +package mdocspec + +import ( + "bytes" + "fmt" + "sort" + + "github.com/yuin/goldmark" + "golang.org/x/exp/maps" + "gopkg.in/yaml.v3" + + ppb "github.com/openconfig/featureprofiles/proto/ocpaths_go_proto" + rpb "github.com/openconfig/featureprofiles/proto/ocrpcs_go_proto" +) + +// ErrNotFound indicates the OpenConfig Path and RPC Coverage YAML block was +// not found or was invalid. +var ErrNotFound = fmt.Errorf(`did not detect valid yaml block under a heading titled %q, please see https://github.com/openconfig/featureprofiles/blob/main/doc/test-requirements-template.md for example`, OCSpecHeading) + +// Parse extracts sorted OpenConfig Path and RPC Coverage from a +// featureprofiles README. +// +// If such a coverage section is not found in the README, `ErrNotFound` will be +// returned. +// +// Expected markdown format: +// +// ## OpenConfig Path and RPC Coverage +// +// ```yaml +// paths: +// /interfaces/interface/config/description: +// /interfaces/interface/config/enabled: +// /components/component/state/name: +// platform_type: "CHASSIS" +// +// rpcs: +// gnmi: +// gNMI.Set: +// union_replace: true +// gNMI.Subscribe: +// on_change: true +// ``` +// +// The first yaml code block after a heading line named exactly as +// "OpenConfig Path and RPC Coverage" will be parsed. Any other code blocks are +// ignored. +func Parse(source []byte) (*ppb.OCPaths, *rpb.OCRPCs, error) { + var buf bytes.Buffer + md := goldmark.New( + goldmark.WithExtensions(MDOCSpecs), + ) + if err := md.Convert(source, &buf); err != nil { + return nil, nil, fmt.Errorf("MDOCSpec.Convert: %v", err) + } + if buf.Len() == 0 { + return nil, nil, ErrNotFound + } + + return parseYAML(buf.Bytes()) +} + +func parseYAML(source []byte) (*ppb.OCPaths, *rpb.OCRPCs, error) { + s := map[string]map[string]map[string]any{} + if err := yaml.Unmarshal(source, &s); err != nil { + return nil, nil, fmt.Errorf("mdocspec: error parsing YAML: %v", err) + } + + protoPaths := &ppb.OCPaths{} + + paths := s["paths"] + pathNames := maps.Keys(paths) + sort.Strings(pathNames) + for _, name := range pathNames { + var platformType string + for propertyName, property := range paths[name] { + switch propertyName { + case "platform_type": + p, ok := property.(string) + if !ok { + return nil, nil, fmt.Errorf("mdocspec: only string values expected for `platform_type` attribute, got (%T, %v)", property, property) + } + platformType = p + default: + return nil, nil, fmt.Errorf("mdocspec: only `platform_type` is expected as a valid attribute for paths, got %q", propertyName) + } + } + ocPath := &ppb.OCPath{ + Name: name, + } + if platformType != "" { + ocPath.OcpathConstraint = &ppb.OCPathConstraint{ + Constraint: &ppb.OCPathConstraint_PlatformType{ + PlatformType: platformType, + }, + } + } + protoPaths.Ocpaths = append(protoPaths.Ocpaths, ocPath) + } + + protoRPCs := &rpb.OCRPCs{ + OcProtocols: map[string]*rpb.OCProtocol{}, + } + + rpcs, ok := s["rpcs"] + if !ok { + return nil, nil, fmt.Errorf("mdocspec: YAML does not have mandatory top-level \"rpcs\" attribute") + } + rpcNames := maps.Keys(rpcs) + sort.Strings(rpcNames) + var hasMethod bool + for _, name := range rpcNames { + methods := maps.Keys(rpcs[name]) + if len(methods) > 0 { + hasMethod = true + } + sort.Strings(methods) + for i, method := range methods { + methods[i] = name + "." + method + } + protoRPCs.OcProtocols[name] = &rpb.OCProtocol{ + MethodName: methods, + } + } + if !hasMethod { + return nil, nil, fmt.Errorf("mdocspec: YAML does not have least one RPC method specified") + } + + return protoPaths, protoRPCs, nil +} diff --git a/tools/internal/mdocspec/ocspec_test.go b/tools/internal/mdocspec/ocspec_test.go new file mode 100644 index 00000000000..92ef91d06ef --- /dev/null +++ b/tools/internal/mdocspec/ocspec_test.go @@ -0,0 +1,532 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mdocspec + +import ( + "errors" + "testing" + + "github.com/google/go-cmp/cmp" + ppb "github.com/openconfig/featureprofiles/proto/ocpaths_go_proto" + rpb "github.com/openconfig/featureprofiles/proto/ocrpcs_go_proto" + "google.golang.org/protobuf/encoding/prototext" + "google.golang.org/protobuf/testing/protocmp" +) + +func mustOCPaths(t *testing.T, textproto string) *ppb.OCPaths { + ocPaths := &ppb.OCPaths{} + if err := prototext.Unmarshal([]byte(textproto), ocPaths); err != nil { + t.Fatal(err) + } + return ocPaths +} + +func mustOCRPCs(t *testing.T, textproto string) *rpb.OCRPCs { + ocRPCs := &rpb.OCRPCs{} + if err := prototext.Unmarshal([]byte(textproto), ocRPCs); err != nil { + t.Fatal(err) + } + return ocRPCs +} + +func TestParse(t *testing.T) { + tests := []struct { + desc string + inMD string + wantOCPaths *ppb.OCPaths + wantOCRPCs *rpb.OCRPCs + wantNotFoundErr bool + wantErr bool + }{{ + desc: "good", + inMD: `--- +name: New featureprofiles test requirement +about: Use this template to document the requirements for a new test to be implemented. +title: '' +labels: enhancement +assignees: '' +--- + +# Instructions for this template + +Below is the required template for writing test requirements. Good examples of test +requirements include: + +* [TE-3.7: Base Hierarchical NHG Update](/feature/gribi/otg_tests/base_hierarchical_nhg_update/README.md) +* [gNMI-1.13: Telemetry: Optics Power and Bias Current](https://github.com/openconfig/featureprofiles/blob/main/feature/platform/tests/optics_power_and_bias_current_test/README.md) +* [RT-5.1: Singleton Interface](https://github.com/openconfig/featureprofiles/blob/main/feature/interface/singleton/otg_tests/singleton_test/README.md) + +# TestID-x.y: Short name of test here + +## Summary + +Write a few sentences or paragraphs describing the purpose and scope of the test. + +## Testbed type + +* Specify the .testbed topology file from the [topologies](https://github.com/openconfig/featureprofiles/tree/main/topologies) folder to be used with this test + +## Procedure + +* Test environment setup + * Description of procedure to configure ATE and DUT with pre-requisites making it possible to cover the intended paths and RPC's. + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +* TestID-x.y.z - Name of subtest + * Step 1 + * Step 2 + * Validation and pass/fail criteria + +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +` + "```" + `yaml +paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + gNMI.Subscribe: + on_change: true + gnoi: + healthz.Healthz.Get: + healthz.Healthz.List: + healthz.Healthz.Acknowledge: + healthz.Healthz.Artifact: + healthz.Healthz.Check: + bgp.BGP.ClearBGPNeighbor: +` + "```" + ` + +## Required DUT platform + +* Specify the minimum DUT-type: + * MFF - A modular form factor device containing LINECARDs, FABRIC and redundant CONTROLLER_CARD components + * FFF - fixed form factor + * vRX - virtual router device +`, + wantOCPaths: mustOCPaths(t, ` +ocpaths: { + name: "/components/component/state/name" + ocpath_constraint: { + platform_type: "CHASSIS" + } +} +ocpaths: { + name: "/interfaces/interface/config/description" +} +ocpaths: { + name: "/interfaces/interface/config/enabled" +} +`), + wantOCRPCs: mustOCRPCs(t, ` +oc_protocols: { + key: "gnmi" + value: { + method_name: "gnmi.gNMI.Set" + method_name: "gnmi.gNMI.Subscribe" + } +} +oc_protocols: { + key: "gnoi" + value: { + method_name: "gnoi.bgp.BGP.ClearBGPNeighbor" + method_name: "gnoi.healthz.Healthz.Acknowledge" + method_name: "gnoi.healthz.Healthz.Artifact" + method_name: "gnoi.healthz.Healthz.Check" + method_name: "gnoi.healthz.Healthz.Get" + method_name: "gnoi.healthz.Healthz.List" + } +} +`), + }, { + desc: "empty", + inMD: ``, + wantNotFoundErr: true, + wantErr: true, + }, { + desc: "no-heading", + inMD: ` +` + "```" + `yaml +paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" + +` + "```" + ` + `, + wantNotFoundErr: true, + wantErr: true, + }, { + desc: "zero-rpcs", + inMD: `--- +name: New featureprofiles test requirement +--- + +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +` + "```" + `yaml +paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" +rpcs: +` + "```" + ` + +## Required DUT platform +`, + wantErr: true, + }, { + desc: "no-rpcs", + inMD: `--- +name: New featureprofiles test requirement +--- + +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +` + "```" + `yaml +paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" + +` + "```" + ` + +## Required DUT platform +`, + wantErr: true, + }, { + desc: "zero-paths-one-rpc", + inMD: ` +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +` + "```" + `yaml +paths: +rpcs: + gnoi: + healthz.Healthz.Get: +` + "```" + ` + +## Required DUT platform +`, + wantOCPaths: mustOCPaths(t, ``), + wantOCRPCs: mustOCRPCs(t, ` +oc_protocols: { + key: "gnoi" + value: { + method_name: "gnoi.healthz.Healthz.Get" + } +} +`), + }, { + desc: "no-paths-one-rpc", + inMD: ` +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +` + "```" + `yaml +rpcs: + gnoi: + healthz.Healthz.Get: +` + "```" + ` + +## Required DUT platform +`, + wantOCPaths: mustOCPaths(t, ``), + wantOCRPCs: mustOCRPCs(t, ` +oc_protocols: { + key: "gnoi" + value: { + method_name: "gnoi.healthz.Healthz.Get" + } +} +`), + }, { + desc: "zero-paths-one-rpc-zero-methods", + inMD: ` +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +` + "```" + `yaml +paths: +rpcs: + gnoi: +` + "```" + ` + +## Required DUT platform +`, + wantErr: true, + }, { + desc: "zero-paths-zero-rpcs", + inMD: ` +## OpenConfig Path and RPC Coverage + +This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here. + +` + "```" + `yaml +paths: +rpcs: +` + "```" + ` + +## Required DUT platform +`, + wantErr: true, + }} + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + gotOCPaths, gotOCRPCs, err := Parse([]byte(tt.inMD)) + if gotNotFoundErr := errors.Is(err, ErrNotFound); gotNotFoundErr != tt.wantNotFoundErr { + t.Fatalf("Parse gotNotFoundErr: %v, wantNotFoundErr: %v", gotNotFoundErr, tt.wantNotFoundErr) + } + if gotErr := err != nil; gotErr != tt.wantErr { + t.Fatalf("Parse gotErr: %v, wantErr: %v", err, tt.wantErr) + } + if diff := cmp.Diff(tt.wantOCPaths, gotOCPaths, protocmp.Transform()); diff != "" { + t.Errorf("Parse OCPaths (-want, +got):\n%s", diff) + } + if diff := cmp.Diff(tt.wantOCRPCs, gotOCRPCs, protocmp.Transform()); diff != "" { + t.Errorf("Parse OCRPCs (-want, +got):\n%s", diff) + } + }) + } +} + +func TestParseYAML(t *testing.T) { + tests := []struct { + desc string + inYAML string + wantOCPaths *ppb.OCPaths + wantOCRPCs *rpb.OCRPCs + wantErr bool + }{{ + desc: "good", + inYAML: `paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + gNMI.Subscribe: + on_change: true + gnoi: + healthz.Healthz.Get: + healthz.Healthz.List: + healthz.Healthz.Acknowledge: + healthz.Healthz.Artifact: + healthz.Healthz.Check: + bgp.BGP.ClearBGPNeighbor: +`, + wantOCPaths: mustOCPaths(t, ` +ocpaths: { + name: "/components/component/state/name" + ocpath_constraint: { + platform_type: "CHASSIS" + } +} +ocpaths: { + name: "/interfaces/interface/config/description" +} +ocpaths: { + name: "/interfaces/interface/config/enabled" +} +`), + wantOCRPCs: mustOCRPCs(t, ` +oc_protocols: { + key: "gnmi" + value: { + method_name: "gnmi.gNMI.Set" + method_name: "gnmi.gNMI.Subscribe" + } +} +oc_protocols: { + key: "gnoi" + value: { + method_name: "gnoi.bgp.BGP.ClearBGPNeighbor" + method_name: "gnoi.healthz.Healthz.Acknowledge" + method_name: "gnoi.healthz.Healthz.Artifact" + method_name: "gnoi.healthz.Healthz.Check" + method_name: "gnoi.healthz.Healthz.Get" + method_name: "gnoi.healthz.Healthz.List" + } +} +`), + }, { + desc: "missing-rpcs", + inYAML: `paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" +`, + wantErr: true, + }, { + desc: "missing-paths", + inYAML: `rpcs: + gnmi: + gNMI.Set: + union_replace: true + gNMI.Subscribe: + on_change: true + gnoi: + healthz.Healthz.Get: + healthz.Healthz.List: + healthz.Healthz.Acknowledge: + healthz.Healthz.Artifact: + healthz.Healthz.Check: + bgp.BGP.ClearBGPNeighbor: +`, + wantOCPaths: mustOCPaths(t, ``), + wantOCRPCs: mustOCRPCs(t, ` +oc_protocols: { + key: "gnmi" + value: { + method_name: "gnmi.gNMI.Set" + method_name: "gnmi.gNMI.Subscribe" + } +} +oc_protocols: { + key: "gnoi" + value: { + method_name: "gnoi.bgp.BGP.ClearBGPNeighbor" + method_name: "gnoi.healthz.Healthz.Acknowledge" + method_name: "gnoi.healthz.Healthz.Artifact" + method_name: "gnoi.healthz.Healthz.Check" + method_name: "gnoi.healthz.Healthz.Get" + method_name: "gnoi.healthz.Healthz.List" + } +} +`), + }, { + desc: "empty", + inYAML: ``, + wantErr: true, + }, { + desc: "extra-spaces", + inYAML: ` +paths: + # interface configuration + /interfaces/interface/config/description: + /interfaces/interface/config/enabled: + # name of chassis component + /components/component/state/name: + platform_type: "CHASSIS" + + + +rpcs: + + + gnmi: + gNMI.Set: + union_replace: true + gNMI.Subscribe: + on_change: true + gnoi: + healthz.Healthz.Get: + healthz.Healthz.List: + healthz.Healthz.Acknowledge: + healthz.Healthz.Artifact: + healthz.Healthz.Check: + bgp.BGP.ClearBGPNeighbor: + +`, + wantOCPaths: mustOCPaths(t, ` +ocpaths: { + name: "/components/component/state/name" + ocpath_constraint: { + platform_type: "CHASSIS" + } +} +ocpaths: { + name: "/interfaces/interface/config/description" +} +ocpaths: { + name: "/interfaces/interface/config/enabled" +} +`), + wantOCRPCs: mustOCRPCs(t, ` +oc_protocols: { + key: "gnmi" + value: { + method_name: "gnmi.gNMI.Set" + method_name: "gnmi.gNMI.Subscribe" + } +} +oc_protocols: { + key: "gnoi" + value: { + method_name: "gnoi.bgp.BGP.ClearBGPNeighbor" + method_name: "gnoi.healthz.Healthz.Acknowledge" + method_name: "gnoi.healthz.Healthz.Artifact" + method_name: "gnoi.healthz.Healthz.Check" + method_name: "gnoi.healthz.Healthz.Get" + method_name: "gnoi.healthz.Healthz.List" + } +} +`), + }} + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + gotOCPaths, gotOCRPCs, err := parseYAML([]byte(tt.inYAML)) + if gotErr := err != nil; gotErr != tt.wantErr { + t.Fatalf("parseYAML gotErr: %v, wantErr: %v", err, tt.wantErr) + } + if diff := cmp.Diff(tt.wantOCPaths, gotOCPaths, protocmp.Transform()); diff != "" { + t.Errorf("parseYAML OCPaths (-want, +got):\n%s", diff) + } + if diff := cmp.Diff(tt.wantOCRPCs, gotOCRPCs, protocmp.Transform()); diff != "" { + t.Errorf("parseYAML OCRPCs (-want, +got):\n%s", diff) + } + }) + } +} diff --git a/tools/internal/ocpaths/clone_public.go b/tools/internal/ocpaths/clone_public.go new file mode 100644 index 00000000000..4821cab0db0 --- /dev/null +++ b/tools/internal/ocpaths/clone_public.go @@ -0,0 +1,47 @@ +package ocpaths + +import ( + "fmt" + "io" + "os" + "os/exec" + "path/filepath" +) + +// ClonePublicRepo clones the openconfig/public repo at the given path. +// +// - branch is the branch to be downloaded (passed to git clone -b). If it is empty then the argument will be omitted. +// +// # Note +// +// - If the "public" folder already exists, then no additional downloads will +// be made. +// - A manual deletion of the downloadPath folder is required if no longer used. +func ClonePublicRepo(downloadPath, branch string) (string, error) { + if downloadPath == "" { + return "", fmt.Errorf("must provide download path") + } + publicPath := filepath.Join(downloadPath, "public") + + if _, err := os.Stat(publicPath); err == nil { // If NO error + return publicPath, nil + } + + args := []string{"clone", "--depth", "1", "https://github.com/openconfig/public.git", publicPath} + if branch != "" { + args = append(args, "-b", branch, "--single-branch") + } + cmd := exec.Command("git", args...) + stderr, err := cmd.StderrPipe() + if err != nil { + return "", err + } + if err := cmd.Start(); err != nil { + return "", fmt.Errorf("failed to clone public repo: %v, command failed to start: %q", err, cmd.String()) + } + stderrOutput, _ := io.ReadAll(stderr) + if err := cmd.Wait(); err != nil { + return "", fmt.Errorf("failed to clone public repo: %v, command failed during execution: %q\n%s", err, cmd.String(), stderrOutput) + } + return publicPath, nil +} diff --git a/tools/internal/ocrpcs/ocrpcs.go b/tools/internal/ocrpcs/ocrpcs.go index b948c9778a0..d460d60997c 100644 --- a/tools/internal/ocrpcs/ocrpcs.go +++ b/tools/internal/ocrpcs/ocrpcs.go @@ -15,12 +15,22 @@ import ( "github.com/openconfig/gnmi/errlist" ) +// cloneAPIRepo clones the openconfig/ repo at the given path. +// +// # Note +// +// * If the folder already exists, then no additional downloads will be made. +// * A manual deletion of the folder is required if no longer used. func cloneAPIRepo(downloadPath, api string) (string, error) { if downloadPath == "" { return "", fmt.Errorf("must provide download path") } repoPath := filepath.Join(downloadPath, api) + if _, err := os.Stat(repoPath); err == nil { // If NO error + return repoPath, nil + } + cmd := exec.Command("git", "clone", "--single-branch", "--depth", "1", fmt.Sprintf("https://github.com/openconfig/%s.git", api), repoPath) stderr, err := cmd.StderrPipe() if err != nil { diff --git a/tools/nosimage/validate/validate.go b/tools/nosimage/validate/validate.go index 614fdbe8694..e6fab17705c 100644 --- a/tools/nosimage/validate/validate.go +++ b/tools/nosimage/validate/validate.go @@ -18,10 +18,7 @@ package main import ( "flag" "fmt" - "io" "os" - "os/exec" - "path/filepath" "github.com/openconfig/featureprofiles/tools/internal/ocpaths" "github.com/openconfig/featureprofiles/tools/internal/ocrpcs" @@ -58,27 +55,6 @@ func init() { config = New(nil) } -func clonePublicRepo(downloadPath, branch string) (string, error) { - if downloadPath == "" { - return "", fmt.Errorf("must provide download path") - } - publicPath := filepath.Join(config.DownloadPath, "public") - - cmd := exec.Command("git", "clone", "-b", branch, "--single-branch", "--depth", "1", "https://github.com/openconfig/public.git", publicPath) - stderr, err := cmd.StderrPipe() - if err != nil { - return "", err - } - if err := cmd.Start(); err != nil { - return "", fmt.Errorf("failed to clone public repo: %v, command failed to start: %q", err, cmd.String()) - } - stderrOutput, _ := io.ReadAll(stderr) - if err := cmd.Wait(); err != nil { - return "", fmt.Errorf("failed to clone public repo: %v, command failed during execution: %q\n%s", err, cmd.String(), stderrOutput) - } - return publicPath, nil -} - func unmarshalFile(filePath string) (*npb.NOSImageProfile, error) { if filePath == "" { return nil, fmt.Errorf("must provide non-empty file path to read from") @@ -92,7 +68,6 @@ func unmarshalFile(filePath string) (*npb.NOSImageProfile, error) { return nil, err } return profile, nil - } func main() { @@ -107,7 +82,7 @@ func main() { if err := os.MkdirAll(config.DownloadPath, 0750); err != nil { fmt.Println(fmt.Errorf("cannot create download path directory: %v", config.DownloadPath)) } - publicPath, err := clonePublicRepo(config.DownloadPath, "v"+profile.Ocpaths.GetVersion()) + publicPath, err := ocpaths.ClonePublicRepo(config.DownloadPath, "v"+profile.Ocpaths.GetVersion()) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/tools/validate_readme_spec/readme_allowlist.go b/tools/validate_readme_spec/readme_allowlist.go new file mode 100644 index 00000000000..fb3aafbaa24 --- /dev/null +++ b/tools/validate_readme_spec/readme_allowlist.go @@ -0,0 +1,23 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +var ( + // nonTestREADMEs are exempt from coverage spec validation. + nonTestREADMEs = map[string]struct{}{ + "security/gnsi/certz/test_data/README.md": {}, + "experimental/p4rt/README.md": {}, + } +) diff --git a/tools/validate_readme_spec/testdata/invalid_all_empty.md b/tools/validate_readme_spec/testdata/invalid_all_empty.md new file mode 100644 index 00000000000..fafa429c9fc --- /dev/null +++ b/tools/validate_readme_spec/testdata/invalid_all_empty.md @@ -0,0 +1,6 @@ +## OpenConfig Path and RPC Coverage + +```yaml +paths: +rpcs: +``` diff --git a/tools/validate_readme_spec/testdata/invalid_empty_rpcs.md b/tools/validate_readme_spec/testdata/invalid_empty_rpcs.md new file mode 100644 index 00000000000..e52afb6b707 --- /dev/null +++ b/tools/validate_readme_spec/testdata/invalid_empty_rpcs.md @@ -0,0 +1,7 @@ +## OpenConfig Path and RPC Coverage + +```yaml +paths: + /interfaces/interface/config/name: +rpcs: +``` diff --git a/tools/validate_readme_spec/testdata/invalid_heading.md b/tools/validate_readme_spec/testdata/invalid_heading.md new file mode 100644 index 00000000000..bcaf33c72bb --- /dev/null +++ b/tools/validate_readme_spec/testdata/invalid_heading.md @@ -0,0 +1,6 @@ +## Hello world + +```yaml +paths: +rpcs: +``` diff --git a/tools/validate_readme_spec/testdata/invalid_path.md b/tools/validate_readme_spec/testdata/invalid_path.md new file mode 100644 index 00000000000..f5f7a93ed2f --- /dev/null +++ b/tools/validate_readme_spec/testdata/invalid_path.md @@ -0,0 +1,9 @@ +## OpenConfig Path and RPC Coverage + +```yaml +paths: + /interfaces/interface/config: +rpcs: + gnmi: + gNMI.Subscribe: +``` diff --git a/tools/validate_readme_spec/testdata/valid_empty_paths.md b/tools/validate_readme_spec/testdata/valid_empty_paths.md new file mode 100644 index 00000000000..3dfaf3ef72b --- /dev/null +++ b/tools/validate_readme_spec/testdata/valid_empty_paths.md @@ -0,0 +1,7 @@ +## OpenConfig Path and RPC Coverage + +```yaml +rpcs: + gnmi: + gNMI.Subscribe: +``` diff --git a/tools/validate_readme_spec/validate_readme_spec.go b/tools/validate_readme_spec/validate_readme_spec.go new file mode 100644 index 00000000000..dc9bb3561aa --- /dev/null +++ b/tools/validate_readme_spec/validate_readme_spec.go @@ -0,0 +1,158 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Command validate_readme_spec validates Paths and RPCs listed by MarkDown +// (READMEs) against the most recent repository states in +// github.com/openconfig. +// +// Note: For `rpcs`, only the RPC name and methods are validated. Any +// attributes defined below RPC methods (e.g. union_replace) are not validated. +package main + +import ( + "flag" + "fmt" + "io/fs" + "os" + "path/filepath" + "strings" + + log "github.com/golang/glog" + "github.com/openconfig/featureprofiles/tools/internal/fpciutil" + "github.com/openconfig/featureprofiles/tools/internal/mdocspec" + "github.com/openconfig/featureprofiles/tools/internal/ocpaths" + "github.com/openconfig/featureprofiles/tools/internal/ocrpcs" + "golang.org/x/exp/maps" +) + +// Config is the set of flags for this binary. +type Config struct { + DownloadPath string + FeatureDir string +} + +// New registers a flagset with the configuration needed by this binary. +func New(fs *flag.FlagSet) *Config { + c := &Config{} + + if fs == nil { + fs = flag.CommandLine + } + fs.StringVar(&c.DownloadPath, "download-path", "./tmp", "path into which to download OpenConfig GitHub repos for validation") + fs.StringVar(&c.FeatureDir, "feature-dir", "", "path to the feature directory of featureprofiles, for which all README.md files are validated for their coverage spec aside from the allow-list in readme_allowlist.go") + + return c +} + +var ( + config *Config +) + +func init() { + config = New(nil) +} + +func readmeFiles(featureDir string) ([]string, error) { + var files []string + err := filepath.WalkDir(featureDir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.Name() != fpciutil.READMEname { + return nil + } + relpath, err := filepath.Rel(filepath.Dir(featureDir), path) + if err != nil { + return fmt.Errorf("unexpected error: cannot take relative path of file %q against feature directory %q", path, featureDir) + } + if _, ok := nonTestREADMEs[relpath]; ok { + // Allowlist + return nil + } + + files = append(files, path) + return nil + }) + return files, err +} + +func main() { + flag.Parse() + + fileCount := flag.NArg() + var files []string + switch { + case fileCount != 0 && config.FeatureDir != "": + log.Exit("If -feature-dir flag is specified, README files must not be specified as positional arguments.") + case fileCount == 0 && config.FeatureDir == "": + var err error + config.FeatureDir, err = fpciutil.FeatureDir() + if err != nil { + log.Exitf("Unable to locate feature root: %v", err) + } + fallthrough + case config.FeatureDir != "": + var err error + files, err = readmeFiles(config.FeatureDir) + if err != nil { + log.Exitf("Error gathering README.md files for validation: %v", err) + } + case fileCount != 0: + files = flag.Args() + default: + log.Exit("Program internal error: input not handled.") + } + + if err := os.MkdirAll(config.DownloadPath, 0750); err != nil { + fmt.Println(fmt.Errorf("cannot create download path directory: %v", config.DownloadPath)) + } + publicPath, err := ocpaths.ClonePublicRepo(config.DownloadPath, "") + if err != nil { + log.Exit(err) + } + + erredFiles := map[string]struct{}{} + for _, file := range files { + log.Infof("Validating %q", file) + b, err := os.ReadFile(file) + if err != nil { + log.Exitf("Error reading file: %q", file) + } + ocPaths, ocRPCs, err := mdocspec.Parse(b) + if err != nil { + log.Errorf("file %v: %v", file, err) + erredFiles[file] = struct{}{} + continue + } + + paths, invalidPaths, err := ocpaths.ValidatePaths(ocPaths.GetOcpaths(), publicPath) + if err != nil { + log.Errorf("%q contains %d invalid OCPaths:\n%v", file, len(invalidPaths), err) + erredFiles[file] = struct{}{} + } else { + log.Infof("%q contains %d valid OCPaths\n", file, len(paths)) + } + + rpcValidCount, err := ocrpcs.ValidateRPCs(config.DownloadPath, ocRPCs.GetOcProtocols()) + if err != nil { + log.Errorf("%q contains invalid RPCs: %v", file, err) + erredFiles[file] = struct{}{} + } else { + log.Infof("%q contains %d valid OCRPCs\n", file, rpcValidCount) + } + } + if len(erredFiles) > 0 { + log.Exitf("The following files have errors:\n%v", strings.Join(maps.Keys(erredFiles), "\n")) + } +} diff --git a/tools/validate_readme_spec/validate_readme_spec_test.sh b/tools/validate_readme_spec/validate_readme_spec_test.sh new file mode 100755 index 00000000000..faea2862eb2 --- /dev/null +++ b/tools/validate_readme_spec/validate_readme_spec_test.sh @@ -0,0 +1,44 @@ +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#!/bin/bash + +go install ./ + +filename=invalid_all_empty.md +if validate_readme_spec -alsologtostderr testdata/"${filename}"; then + echo "Validation passed, but failure expected" + exit 1 +fi +filename=invalid_empty_rpcs.md +if validate_readme_spec -alsologtostderr testdata/"${filename}"; then + echo "Validation passed, but failure expected" + exit 1 +fi +filename=invalid_heading.md +if validate_readme_spec -alsologtostderr testdata/"${filename}"; then + echo "Validation passed, but failure expected" + exit 1 +fi +filename=invalid_path.md +if validate_readme_spec -alsologtostderr testdata/"${filename}"; then + echo "Validation passed, but failure expected" + exit 1 +fi +filename=valid_empty_paths.md +if ! validate_readme_spec -alsologtostderr testdata/"${filename}"; then + echo "Validation failed, but pass expected" + exit 1 +fi +echo "PASS" diff --git a/topologies/binding/binding.go b/topologies/binding/binding.go index 3cdbc8ef903..71f7c21c034 100644 --- a/topologies/binding/binding.go +++ b/topologies/binding/binding.go @@ -47,6 +47,7 @@ import ( var ( // To be stubbed out by unit tests. + //lint:ignore SA1019 DialContext allows for blocking on new connections. grpcDialContextFn = grpc.DialContext gosnappiNewAPIFn = gosnappi.NewApi ) diff --git a/topologies/kne/arista/ceos/dut.textproto b/topologies/kne/arista/ceos/dut.textproto index 89719208076..c493398cb19 100644 --- a/topologies/kne/arista/ceos/dut.textproto +++ b/topologies/kne/arista/ceos/dut.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "arista-ceos-dut" nodes: { name: "dut" diff --git a/topologies/kne/arista/ceos/dutate.textproto b/topologies/kne/arista/ceos/dutate.textproto index 4c1ea45c1bd..024f2a0f542 100644 --- a/topologies/kne/arista/ceos/dutate.textproto +++ b/topologies/kne/arista/ceos/dutate.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "arista-ceos-dutate" nodes: { name: "dut" diff --git a/topologies/kne/arista/ceos/dutate_lag.textproto b/topologies/kne/arista/ceos/dutate_lag.textproto index 1cb7bf05a11..46e0c31907e 100644 --- a/topologies/kne/arista/ceos/dutate_lag.textproto +++ b/topologies/kne/arista/ceos/dutate_lag.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "arista-ceos-dutate-lag" nodes: { name: "dut" diff --git a/topologies/kne/arista/ceos/dutdut.textproto b/topologies/kne/arista/ceos/dutdut.textproto index 234b1002f5a..b09d232f2ab 100644 --- a/topologies/kne/arista/ceos/dutdut.textproto +++ b/topologies/kne/arista/ceos/dutdut.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "arista-ceos-dutdut" nodes: { name: "dut1" diff --git a/topologies/kne/arista/ceos/dutdutate.textproto b/topologies/kne/arista/ceos/dutdutate.textproto index a39cde9d718..b864e64655f 100644 --- a/topologies/kne/arista/ceos/dutdutate.textproto +++ b/topologies/kne/arista/ceos/dutdutate.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "arista-ceos-dutdut" nodes: { name: "dut1" diff --git a/topologies/kne/arista/ceos/topology.textproto b/topologies/kne/arista/ceos/topology.textproto index dd99b31428a..455c4647557 100644 --- a/topologies/kne/arista/ceos/topology.textproto +++ b/topologies/kne/arista/ceos/topology.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "arista-ceos" nodes: { name: "dut1" diff --git a/topologies/kne/cisco/8000e/dut.textproto b/topologies/kne/cisco/8000e/dut.textproto index 387ae75508d..bbf34e15a6b 100644 --- a/topologies/kne/cisco/8000e/dut.textproto +++ b/topologies/kne/cisco/8000e/dut.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "cisco-8000e-dut" nodes: { name: "dut" diff --git a/topologies/kne/cisco/8000e/dutate.textproto b/topologies/kne/cisco/8000e/dutate.textproto index 1fcf0ace9c3..acddea86948 100644 --- a/topologies/kne/cisco/8000e/dutate.textproto +++ b/topologies/kne/cisco/8000e/dutate.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "cisco-8000e-dutate" nodes: { name: "dut" diff --git a/topologies/kne/cisco/8000e/dutate_lag.textproto b/topologies/kne/cisco/8000e/dutate_lag.textproto index b614136de25..2c15f33bd75 100644 --- a/topologies/kne/cisco/8000e/dutate_lag.textproto +++ b/topologies/kne/cisco/8000e/dutate_lag.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "cisco-8000e-dutate-lag" nodes: { name: "dut" diff --git a/topologies/kne/cisco/8000e/dutdut.textproto b/topologies/kne/cisco/8000e/dutdut.textproto index aefa13ecad5..919da1e25ea 100644 --- a/topologies/kne/cisco/8000e/dutdut.textproto +++ b/topologies/kne/cisco/8000e/dutdut.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "cisco-8000e-dutdut" nodes: { name: "dut1" diff --git a/topologies/kne/cisco/8000e/dutdutate.textproto b/topologies/kne/cisco/8000e/dutdutate.textproto index 35b040c836f..407d0cf2a9f 100644 --- a/topologies/kne/cisco/8000e/dutdutate.textproto +++ b/topologies/kne/cisco/8000e/dutdutate.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "cisco-8000e-dutdut" nodes: { name: "dut1" diff --git a/topologies/kne/cisco/8000e/topology.textproto b/topologies/kne/cisco/8000e/topology.textproto index e9564924dc8..37d3ef20aa1 100644 --- a/topologies/kne/cisco/8000e/topology.textproto +++ b/topologies/kne/cisco/8000e/topology.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "cisco-8000e" nodes: { name: "dut1" diff --git a/topologies/kne/cisco/xrd/dut.textproto b/topologies/kne/cisco/xrd/dut.textproto index 549ed5e792a..839b73c0b40 100644 --- a/topologies/kne/cisco/xrd/dut.textproto +++ b/topologies/kne/cisco/xrd/dut.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "cisco-xrd-dut" nodes: { name: "dut" diff --git a/topologies/kne/cisco/xrd/dutate.textproto b/topologies/kne/cisco/xrd/dutate.textproto index 3b85637a06f..dfa65bc7d14 100644 --- a/topologies/kne/cisco/xrd/dutate.textproto +++ b/topologies/kne/cisco/xrd/dutate.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "cisco-xrd-dutate" nodes: { name: "dut" diff --git a/topologies/kne/cisco/xrd/dutate_lag.textproto b/topologies/kne/cisco/xrd/dutate_lag.textproto index c7028277fe7..869650071c4 100644 --- a/topologies/kne/cisco/xrd/dutate_lag.textproto +++ b/topologies/kne/cisco/xrd/dutate_lag.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "cisco-xrd-dutate-lag" nodes: { name: "dut" diff --git a/topologies/kne/cisco/xrd/dutdut.textproto b/topologies/kne/cisco/xrd/dutdut.textproto index bdcbaf6d757..4f9e85c87bf 100644 --- a/topologies/kne/cisco/xrd/dutdut.textproto +++ b/topologies/kne/cisco/xrd/dutdut.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "cisco-xrd-dutdut" nodes: { name: "dut1" diff --git a/topologies/kne/cisco/xrd/dutdutate.textproto b/topologies/kne/cisco/xrd/dutdutate.textproto index b1a3593bfc8..9ec13b06ae0 100644 --- a/topologies/kne/cisco/xrd/dutdutate.textproto +++ b/topologies/kne/cisco/xrd/dutdutate.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "cisco-xrd-dutdut" nodes: { name: "dut1" diff --git a/topologies/kne/cisco/xrd/topology.textproto b/topologies/kne/cisco/xrd/topology.textproto index eeb90ec60b3..5995c6c31ec 100644 --- a/topologies/kne/cisco/xrd/topology.textproto +++ b/topologies/kne/cisco/xrd/topology.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "cisco-xrd" nodes: { name: "dut1" diff --git a/topologies/kne/juniper/ncptx/dut.textproto b/topologies/kne/juniper/ncptx/dut.textproto index 022ef7a0ac9..91270ded372 100644 --- a/topologies/kne/juniper/ncptx/dut.textproto +++ b/topologies/kne/juniper/ncptx/dut.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "juniper-ncptx-dut" nodes: { name: "dut" diff --git a/topologies/kne/juniper/ncptx/dutate.textproto b/topologies/kne/juniper/ncptx/dutate.textproto index 88b35a765df..44e80de3a7d 100644 --- a/topologies/kne/juniper/ncptx/dutate.textproto +++ b/topologies/kne/juniper/ncptx/dutate.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "juniper-ncptx-dutate" nodes: { name: "dut" diff --git a/topologies/kne/juniper/ncptx/dutate_lag.textproto b/topologies/kne/juniper/ncptx/dutate_lag.textproto index 75cffbe01c1..501445d4d68 100644 --- a/topologies/kne/juniper/ncptx/dutate_lag.textproto +++ b/topologies/kne/juniper/ncptx/dutate_lag.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "juniper-ncptx-dutate-lag" nodes: { name: "dut" diff --git a/topologies/kne/juniper/ncptx/dutdut.textproto b/topologies/kne/juniper/ncptx/dutdut.textproto index 18d95178b49..383dab806b1 100644 --- a/topologies/kne/juniper/ncptx/dutdut.textproto +++ b/topologies/kne/juniper/ncptx/dutdut.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "juniper-ncptx-dutdut" nodes: { name: "dut1" diff --git a/topologies/kne/juniper/ncptx/dutdutate.textproto b/topologies/kne/juniper/ncptx/dutdutate.textproto index 395b14120e5..902ded8e9a3 100644 --- a/topologies/kne/juniper/ncptx/dutdutate.textproto +++ b/topologies/kne/juniper/ncptx/dutdutate.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "juniper-ncptx-dutdutate" nodes: { name: "dut1" diff --git a/topologies/kne/juniper/ncptx/topology.textproto b/topologies/kne/juniper/ncptx/topology.textproto index 16f7f9c227c..1ce0868d482 100644 --- a/topologies/kne/juniper/ncptx/topology.textproto +++ b/topologies/kne/juniper/ncptx/topology.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "juniper-ncptx" nodes: { name: "dut1" diff --git a/topologies/kne/nokia/srlinux/config.cfg b/topologies/kne/nokia/srlinux/config.cfg index 7f734c0580d..28e1788311d 100644 --- a/topologies/kne/nokia/srlinux/config.cfg +++ b/topologies/kne/nokia/srlinux/config.cfg @@ -1,4 +1,3 @@ - acl { acl-filter cpm type ipv4 { entry 441 { @@ -89,22 +88,52 @@ system { lldp { admin-state enable } - gnmi-server { + grpc-server mgmt { + rate-limit 65535 + session-limit 65535 + yang-models openconfig admin-state enable - rate-limit 65000 - trace-options [ - request - response - common + default-tls-profile true + network-instance mgmt + port 9339 + services [ + gnmi + gnsi + gnoi ] + } + grpc-server mgmt-gribi { + admin-state enable + rate-limit 65535 + session-limit 65535 + default-tls-profile true + network-instance mgmt + port 9340 + services [ + gribi + ] + } + grpc-server mgmt-p4rt { + admin-state enable + rate-limit 65535 + session-limit 65535 + default-tls-profile true + network-instance mgmt + port 9559 + services [ + p4rt + ] + } + json-rpc-server { + admin-state enable network-instance mgmt { - admin-state enable - port 9339 - yang-models openconfig - tls-profile kne-profile - } - unix-socket { - admin-state enable + http { + admin-state enable + } + https { + admin-state enable + tls-profile kne-profile + } } } tls { @@ -135,26 +164,6 @@ ufJifhmpItpy3mkUCLEJ33ex2AA6 " } } - gribi-server { - admin-state enable - network-instance mgmt { - admin-state enable - port 9340 - tls-profile kne-profile - } - } - json-rpc-server { - admin-state enable - network-instance mgmt { - http { - admin-state enable - } - https { - admin-state enable - tls-profile kne-profile - } - } - } banner { login-banner "................................................................ : Welcome to Nokia SR Linux! : @@ -173,20 +182,6 @@ ufJifhmpItpy3mkUCLEJ33ex2AA6 ................................................................ " } - p4rt-server { - admin-state enable - network-instance mgmt { - admin-state enable - tls-profile kne-profile - } - } } network-instance DEFAULT { } -network-instance mgmt { - protocols { - gribi { - admin-state enable - } - } -} diff --git a/topologies/kne/nokia/srlinux/dut.textproto b/topologies/kne/nokia/srlinux/dut.textproto index e899c143eb6..4861b80218f 100644 --- a/topologies/kne/nokia/srlinux/dut.textproto +++ b/topologies/kne/nokia/srlinux/dut.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "nokia-srlinux-dut" nodes: { name: "dut" diff --git a/topologies/kne/nokia/srlinux/dutate.textproto b/topologies/kne/nokia/srlinux/dutate.textproto index 67a6019ddda..3fb4002cdd4 100644 --- a/topologies/kne/nokia/srlinux/dutate.textproto +++ b/topologies/kne/nokia/srlinux/dutate.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "nokia-srlinux-dutate" nodes: { name: "dut" diff --git a/topologies/kne/nokia/srlinux/dutate_lag.textproto b/topologies/kne/nokia/srlinux/dutate_lag.textproto index d2a249aa2b5..e7738b3f529 100644 --- a/topologies/kne/nokia/srlinux/dutate_lag.textproto +++ b/topologies/kne/nokia/srlinux/dutate_lag.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "nokia-srlinux-dutate-lag" nodes: { name: "dut" diff --git a/topologies/kne/nokia/srlinux/dutdut.textproto b/topologies/kne/nokia/srlinux/dutdut.textproto index a7efd8f1b14..809db2f577f 100644 --- a/topologies/kne/nokia/srlinux/dutdut.textproto +++ b/topologies/kne/nokia/srlinux/dutdut.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "nokia-srlinux-dutdut" nodes: { name: "dut1" diff --git a/topologies/kne/nokia/srlinux/dutdutate.textproto b/topologies/kne/nokia/srlinux/dutdutate.textproto index 01abc5af46f..95e29436d0e 100644 --- a/topologies/kne/nokia/srlinux/dutdutate.textproto +++ b/topologies/kne/nokia/srlinux/dutdutate.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "nokia-srlinux-dutdut" nodes: { name: "dut1" diff --git a/topologies/kne/nokia/srlinux/topology.textproto b/topologies/kne/nokia/srlinux/topology.textproto index 809a0f49445..52cf5569932 100644 --- a/topologies/kne/nokia/srlinux/topology.textproto +++ b/topologies/kne/nokia/srlinux/topology.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "nokia-srlinux" nodes: { name: "dut1" diff --git a/topologies/kne/openconfig/lemming/dut.textproto b/topologies/kne/openconfig/lemming/dut.textproto index dbbbfb029ac..fac5db0ef7c 100644 --- a/topologies/kne/openconfig/lemming/dut.textproto +++ b/topologies/kne/openconfig/lemming/dut.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "openconfig-lemming-dut" nodes: { name: "dut" diff --git a/topologies/kne/openconfig/lemming/dutate.textproto b/topologies/kne/openconfig/lemming/dutate.textproto index ab946444816..7c8e669516f 100644 --- a/topologies/kne/openconfig/lemming/dutate.textproto +++ b/topologies/kne/openconfig/lemming/dutate.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "openconfig-lemming-dutate" nodes: { name: "dut" diff --git a/topologies/kne/openconfig/lemming/dutate_lag.textproto b/topologies/kne/openconfig/lemming/dutate_lag.textproto index 72f6f440b53..829e68ddf27 100644 --- a/topologies/kne/openconfig/lemming/dutate_lag.textproto +++ b/topologies/kne/openconfig/lemming/dutate_lag.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "openconfig-lemming-dutate-lag" nodes: { name: "dut" diff --git a/topologies/kne/openconfig/lemming/dutdut.textproto b/topologies/kne/openconfig/lemming/dutdut.textproto index ce6470682da..3c9598202b2 100644 --- a/topologies/kne/openconfig/lemming/dutdut.textproto +++ b/topologies/kne/openconfig/lemming/dutdut.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "openconfig-lemming-dutdut" nodes: { name: "dut1" diff --git a/topologies/kne/openconfig/lemming/dutdutate.textproto b/topologies/kne/openconfig/lemming/dutdutate.textproto index c0b8c9423c5..6d3c7e204cd 100644 --- a/topologies/kne/openconfig/lemming/dutdutate.textproto +++ b/topologies/kne/openconfig/lemming/dutdutate.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "openconfig-lemming-dutdut" nodes: { name: "dut1" diff --git a/topologies/kne/openconfig/lemming/topology.textproto b/topologies/kne/openconfig/lemming/topology.textproto index fa137660acc..2a23223f525 100644 --- a/topologies/kne/openconfig/lemming/topology.textproto +++ b/topologies/kne/openconfig/lemming/topology.textproto @@ -1,3 +1,5 @@ +# proto-file: github.com/openconfig/kne/proto/topo.proto +# proto-message: Topology name: "openconfig-lemming" nodes: { name: "dut1" diff --git a/topologies/proto/binding.proto b/topologies/proto/binding.proto index 014175f46a7..2391eb9ab51 100644 --- a/topologies/proto/binding.proto +++ b/topologies/proto/binding.proto @@ -164,6 +164,6 @@ message Port { // Link between two ports. // Links are only relevant if dynamic solving is enabled. message Link { - string a = 1; // First port in the format ":". - string b = 2; // Second port in the format ":". + string a = 1; // First port in the format ":". + string b = 2; // Second port in the format ":". } diff --git a/topologies/proto/binding/binding.pb.go b/topologies/proto/binding/binding.pb.go index 553f789d413..dcbc653959c 100644 --- a/topologies/proto/binding/binding.pb.go +++ b/topologies/proto/binding/binding.pb.go @@ -21,11 +21,12 @@ package binding import ( + reflect "reflect" + sync "sync" + proto "github.com/openconfig/ondatra/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( @@ -615,8 +616,8 @@ type Link struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - A string `protobuf:"bytes,1,opt,name=a,proto3" json:"a,omitempty"` // First port in the format ":". - B string `protobuf:"bytes,2,opt,name=b,proto3" json:"b,omitempty"` // Second port in the format ":". + A string `protobuf:"bytes,1,opt,name=a,proto3" json:"a,omitempty"` // First port in the format ":". + B string `protobuf:"bytes,2,opt,name=b,proto3" json:"b,omitempty"` // Second port in the format ":". } func (x *Link) Reset() { diff --git a/topologies/proto/generate.sh b/topologies/proto/generate.sh deleted file mode 100755 index 5d99e9e0bec..00000000000 --- a/topologies/proto/generate.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# -# Copyright 2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This script is used to generate the Feature Profiles topology -# binding proto APIs. - -set -e - -# Set directory to hold symlink -mkdir -p protobuf-import -# Remove any existing symlinks & empty directories -find protobuf-import -type l -delete -find protobuf-import -type d -empty -delete -# Download the required dependencies -go mod download -# Get ondatra modules we use and create required directory structure -go list -f 'protobuf-import/{{ .Path }}' -m github.com/openconfig/ondatra | xargs -L1 dirname | sort | uniq | xargs mkdir -p -go list -f '{{ .Dir }} protobuf-import/{{ .Path }}' -m github.com/openconfig/ondatra | xargs -L1 -- ln -s - -cd "$( dirname "${BASH_SOURCE[0]}" )" - -protoc -I='../../protobuf-import' --proto_path=. --go_out=. --go_opt=module=github.com/openconfig/featureprofiles/topologies/proto *.proto