diff --git a/.github/workflows/nosimage.yml b/.github/workflows/nosimage.yml index 73038133b49..0be4a982ccd 100644 --- a/.github/workflows/nosimage.yml +++ b/.github/workflows/nosimage.yml @@ -21,21 +21,22 @@ jobs: - name: Generate Examples and Check No Diff run: | cd tools/nosimage - go run example/generate_example.go -file-path example/example_nosimageprofile.textproto - go run example/generate_example.go -file-path example/example_nosimageprofile_invalid.textproto -invalid + go generate ./example git diff --exit-code --ignore-all-space --ignore-blank-lines - name: Validate Good Example run: | cd tools/nosimage - go run validate/validate.go -file example/example_nosimageprofile.textproto; rm -rf tmp + go run validate/validate.go -file example/valid_example_nosimageprofile.textproto; rm -rf tmp - name: Validate Bad Example run: | cd tools/nosimage - if go run validate/validate.go -file example/example_nosimageprofile_invalid.textproto; then - echo "Validation passed, but failure expected" - exit 1 - fi + for file in example/invalid-*.textproto; do + if go run validate/validate.go -file "$file"; then + echo "Validation passed for $file, but failure expected" + exit 1 + fi + done rm -rf tmp diff --git a/.github/workflows/readme_oc_path_and_rpc.yml b/.github/workflows/readme_oc_path_and_rpc.yml index 1721090ed53..b757f475a66 100644 --- a/.github/workflows/readme_oc_path_and_rpc.yml +++ b/.github/workflows/readme_oc_path_and_rpc.yml @@ -36,8 +36,8 @@ jobs: go install ./tools/validate_readme_spec exemption_flags=( - --non-test-readme security/gnsi/certz/test_data/README.md - --non-test-readme experimental/p4rt/README.md + --non-test-readme feature/security/gnsi/certz/test_data/README.md + --non-test-readme feature/experimental/p4rt/README.md --non-test-readme feature/security/gnsi/acctz/README.md ) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3179307ab77..5875bebd432 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,10 +62,10 @@ The directory tree is organized as follows: * `cloudbuild/` contains google cloud build scripts for running virtual routers in containers on [KNE](https://github.com/openconfig/kne) * `feature/` contains definition and tests of feature profiles. -* `feature/experimental` contains new features and tests which are not yet - categorized or not confirmed to pass on any hardware platform or software - release. When the test is deemed more mature, it is moved to the `feature/` - directory. +* `feature/experimental` contains tests which have automation which is + not confirmed to pass on any hardware platform or software release. + When the test automation is passing against at least one DUT, + it is moved to the `feature/` directory. * `internal/` contains packages used by feature profile tests. * `proto/` contains protobuf files for feature profiles. * `tools/` contains code used for CI checks. @@ -96,7 +96,7 @@ allowed file types, please file an issue for discussion. ## Test Suite Organization Test suites should be placed in subdirectories formatted like -`feature//[/]//.go`. +`feature//[/]//.go`. For example: * `feature/interface/` is the collection of interface feature profiles. @@ -107,7 +107,8 @@ For example: * `feature/interface/singleton/feature.textproto` - defines the singleton interface feature profile in machine readable format. * `feature/interface/singleton/ate_tests/` contains the singleton interfaces - test suite using ATE traffic generation API. + test suite using ATE traffic generation API. Note, use of the ATE API is + deprecated and should not be used for any new test development. * `feature/interface/singleton/otg_tests/` contains the singleton interfaces test suite using OTG traffic generation API. * `feature/interface/singleton/kne_tests/` contains the singleton interfaces @@ -177,7 +178,7 @@ were discovered when implementing the code. ## Test Structure Generally, a Feature Profiles ONDATRA test has the following stages: configure -DUT, configure ATE, generate and verify traffic, verify telemetry. The +DUT, configure OTG, generate and verify traffic, verify telemetry. The configuration stages should be factored out to their own functions, and any subtests should be run under `t.Run` so the test output clearly reflects which parts of the test passed and which parts failed. @@ -189,13 +190,13 @@ occurred. ``` func TestFoo(t *testing.T) { configureDUT(t) // calls t.Fatal() on error. - configureATE(t) // calls t.Fatal() on error. + configureOTG(t) // calls t.Fatal() on error. t.Run("Traffic", func(t *testing.T) { ... }) t.Run("Telemetry", func(t *testing.T) { ... }) } ``` -In the above example, `configureDUT` and `configureATE` should not be subtests, +In the above example, `configureDUT` and `configureOTG` should not be subtests, otherwise they could be skipped when someone specifies a test filter. The "Traffic" and "Telemetry" subtests will both run even if there is a fatal condition during `t.Run()`. @@ -219,7 +220,7 @@ func TestTableDriven(t *testing.T) { t.Run(c.name, func(t *testing.T) { t.Log("Description: ", c.desc) configureDUT(t, /* parameterized by c */) - configureATE(t, /* parameterized by c */) + configureOTG(t, /* parameterized by c */) t.Run("Traffic", func(t *testing.T) { ... }) t.Run("Telemetry", func(t *testing.T) { ... }) }) @@ -432,8 +433,6 @@ To contribute a pull request: [GitHub Quickstart](https://docs.github.com/en/get-started/quickstart) guide. - * New contributions should be in the feature/experimental directory. - 1. When opening a pull request, use a descriptive title and detail. See [Pull Request Title](#pull-request-title) below. @@ -480,7 +479,7 @@ preferred format is: ``` * (M) internal/fptest/* - Add a helper for referencing a keychain from other modules. - * (M) feature/experimental/isis/ate_tests/base_adjacencies_test + * (M) feature/isis/otg_tests/base_adjacencies_test - Fix testing of hello-authentication to reference a specific keychain. - Fix authentication of *SNP packets, referencing a keychain diff --git a/Makefile b/Makefile index ffe2ca29f5e..79898629989 100644 --- a/Makefile +++ b/Makefile @@ -72,6 +72,11 @@ proto/nosimage_go_proto/nosimage.pb.go: proto/nosimage.proto protoimports 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 +proto/testregistry_go_proto/testregistry.pb.go: proto/testregistry.proto protoimports + mkdir -p proto/testregistry_go_proto + protoc -I='protobuf-import' --proto_path=proto --go_out=./proto/testregistry_go_proto --go_opt=paths=source_relative --go_opt=Mtestregistry.proto=proto/testregistry_go_proto testregistry.proto + goimports -w proto/testregistry_go_proto/testregistry.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 diff --git a/cloudbuild/virtual.yaml b/cloudbuild/virtual.yaml index 217fb51cabb..68f8f565378 100644 --- a/cloudbuild/virtual.yaml +++ b/cloudbuild/virtual.yaml @@ -5,7 +5,7 @@ steps: gcloud secrets versions access latest --secret=featureprofiles-ci-ssh > builder-key gcloud secrets versions access latest --secret=featureprofiles-ci-ssh-pub > builder-key.pub - id: fp-presubmit - name: gcr.io/${PROJECT_ID}/remote-builder + name: us-west1-docker.pkg.dev/${PROJECT_ID}/featureprofiles-ci/remote-builder waitFor: ["-"] env: - USERNAME=user 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 1031dcd2e0c..617ae227d00 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 @@ -30,6 +30,14 @@ platform_exceptions: { isis_single_topology_required: true } } +platform_exceptions: { + platform: { + vendor: JUNIPER + } + deviations: { + isis_level_enabled: true + } +} platform_exceptions: { platform: { vendor: ARISTA diff --git a/feature/bgp/admin_distance/otg_tests/admin_distance_test/README.md b/feature/bgp/admin_distance/otg_tests/admin_distance_test/README.md new file mode 100644 index 00000000000..464703ecf20 --- /dev/null +++ b/feature/bgp/admin_distance/otg_tests/admin_distance_test/README.md @@ -0,0 +1,92 @@ +# RT-1.34: BGP route-distance configuration + +## Summary + +BGP default-route-distance, external-route-distance and internal-route-distance (administrative distance) configuration. + +## 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 +* Configure IPv4/IPv6 addresses on the ports +* Create an IPv4 network i.e. ```ipv4-network-1 = 192.168.10.0/24``` attached to ATE port-1 and port-2 +* Create an IPv6 network i.e. ```ipv6-network-1 = 2024:db8:64:64::/64``` attached to ATE port-1 and port-2 +* Configure IPv4 and IPv6 IS-IS between DUT Port-1 and ATE Port-1 + * /network-instances/network-instance/protocols/protocol/isis/global/config + * Advertise ```ipv4-network-1 = 192.168.10.0/24``` and ```ipv6-network-1 = 2024:db8:64:64::/64``` from ATE to DUT over the IPv4 and IPv6 IS-IS session on port-1 + +### RT-1.34.1 [TODO:https://github.com/openconfig/featureprofiles/issues/3050] +#### Validate traffic with modified eBGP Route-Distance of 5 +* Configure IPv4 and IPv6 eBGP between DUT Port-2 and ATE Port-2 + * /network-instances/network-instance/protocols/protocol/bgp/global/config + * /network-instances/network-instance/protocols/protocol/bgp/global/afi-safis/afi-safi/config/ + * Advertise ```ipv4-network-1 = 192.168.10.0/24``` and ```ipv6-network-1 = 2024:db8:64:64::/64``` from ATE to DUT over the IPv4 and IPv6 eBGP session on port-2 +* Configure Route-Distance of eBGP session on port-2 to 5 + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/config/external-route-distance +* Validate using gNMI Subscribe with mode 'ONCE' that the correct Route-Distance value of 5 is reported: + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/state/external-route-distance +* Generate traffic from ATE port-3 towards ```ipv4-network-1 = 192.168.10.0/24``` and ```ipv6-network-1 = 2024:db8:64:64::/64``` +* Verify that the traffic is received on port-2 of the ATE + +### RT-1.34.2 [TODO:https://github.com/openconfig/featureprofiles/issues/3050] +#### Validate traffic with modified eBGP Route-Distance of 250 +* Configure Route-Distance of eBGP session on port-2 to 250 + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/config/external-route-distance +* Validate using gNMI Subscribe with mode 'ONCE' that the correct Route-Distance value of 250 is reported: + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/state/external-route-distance +* Generate traffic from ATE port-3 towards ```ipv4-network-1 = 192.168.10.0/24``` and ```ipv6-network-1 = 2024:db8:64:64::/64``` +* Verify that the traffic is received on port-1 of the ATE + +### RT-1.34.3 [TODO:https://github.com/openconfig/featureprofiles/issues/3050] +#### Validate traffic with modified iBGP Route-Distance of 5 +* Replace IPv4 and IPv6 eBGP with IPv4 and IPv6 iBGP between DUT Port-2 and ATE Port-2 + * /network-instances/network-instance/protocols/protocol/bgp/global/config + * /network-instances/network-instance/protocols/protocol/bgp/global/afi-safis/afi-safi/config/ + * Advertise ```ipv4-network-1 = 192.168.10.0/24``` and ```ipv6-network-1 = 2024:db8:64:64::/64``` from ATE to DUT over the IPv4 and IPv6 iBGP session on port-2 +* Configure Route-Distance of iBGP session on port-2 to 5 + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/config/internal-route-distance +* Validate using gNMI Subscribe with mode 'ONCE' that the correct Route-Distance value of 5 is reported: + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/state/internal-route-distance +* Generate traffic from ATE port-3 towards ```ipv4-network-1 = 192.168.10.0/24``` and ```ipv6-network-1 = 2024:db8:64:64::/64``` +* Validate that the traffic is received on port-2 of the ATE + +### RT-1.34.4 [TODO:https://github.com/openconfig/featureprofiles/issues/3050] +#### Validate traffic with modified iBGP Route-Distance of 250 +* Configure Route-Distance of iBGP session on port-2 to 250 + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/config/internal-route-distance +* Validate using gNMI Subscribe with mode 'ONCE' that the correct Route-Distance value of 250 is reported: + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/state/internal-route-distance +* Generate traffic from ATE port-3 towards ```ipv4-network-1 = 192.168.10.0/24``` and ```ipv6-network-1 = 2024:db8:64:64::/64``` +* Validate that the traffic is received on port-1 of the ATE + +## 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 + ### Route-Distance + /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/config/external-route-distance: + /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/config/internal-route-distance: + + ## State paths + ### Route-Distance + /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/state/internal-route-distance: + /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/state/external-route-distance: + +rpcs: + gnmi: + gNMI.Set: + gNMI.Subscribe: +``` diff --git a/feature/bgp/admin_distance/otg_tests/admin_distance_test/admin_distance_test.go b/feature/bgp/admin_distance/otg_tests/admin_distance_test/admin_distance_test.go new file mode 100644 index 00000000000..10bfe4f8f01 --- /dev/null +++ b/feature/bgp/admin_distance/otg_tests/admin_distance_test/admin_distance_test.go @@ -0,0 +1,332 @@ +// 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 admin_distance_test + +import ( + "math" + "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/isissession" + "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/otg" + "github.com/openconfig/ygot/ygot" +) + +const ( + prefixV4Len = uint32(24) + prefixV6Len = uint32(64) + v4Network = "192.168.10.0" + v6Network = "2024:db8:64:64::" + pathID = 1 + prefixesCount = 1 + bgpName = "BGP" + dutAS = uint32(64656) + ateAS = uint32(64657) + peerGrpNamev4 = "BGP-PEER-GROUP-V4" + peerGrpNamev6 = "BGP-PEER-GROUP-V6" + ateSysID = "640000000001" + ateAreaAddress = "49.0002" + lossTolerance = 1 +) + +var ( + dutPort3 = &attrs.Attributes{ + Desc: "DUT to ATE link", + IPv4: "192.0.2.9", + IPv6: "2001:db8::9", + IPv4Len: 30, + IPv6Len: 126, + } + + atePort3 = &attrs.Attributes{ + Name: "port3", + Desc: "ATE to DUT link", + MAC: "02:12:01:00:00:01", + IPv4: "192.0.2.10", + IPv6: "2001:db8::a", + IPv4Len: 30, + IPv6Len: 126, + } + + advertisedIPv4 ipAddr = ipAddr{address: v4Network, prefix: prefixV4Len} + advertisedIPv6 ipAddr = ipAddr{address: v6Network, prefix: prefixV6Len} +) + +type ipAddr struct { + address string + prefix uint32 +} + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +// isis on port1 and eBGP on port2 +func TestAdminDistance(t *testing.T) { + ts := isissession.MustNew(t).WithISIS() + configurePort3(t, ts) + advertisePrefixFromISISPort(t, ts) + t.Run("ISIS Setup", func(t *testing.T) { + ts.PushAndStart(t) + ts.MustAdjacency(t) + }) + + setupEBGPAndAdvertise(t, ts) + t.Run("BGP Setup", func(t *testing.T) { + t.Log("Verify DUT BGP sessions up") + cfgplugins.VerifyDUTBGPEstablished(t, ts.DUT) + + t.Log("Verify OTG BGP sessions up") + cfgplugins.VerifyOTGBGPEstablished(t, ts.ATE) + }) + + testCases := []struct { + desc string + rd uint8 + port string + bgp string + }{ + { + desc: "EBGP RD value 5", + rd: 5, + port: "port2", + bgp: "eBGP", + }, + { + desc: "EBGP RD value 250", + rd: 250, + port: "port1", + bgp: "eBGP", + }, + { + desc: "IBGP RD value 5", + rd: 5, + port: "port2", + bgp: "iBGP", + }, + { + desc: "IBGP RD value 250", + rd: 250, + port: "port1", + bgp: "iBGP", + }, + } + + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(ts.DUT)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp() + t.Run("Test Admin Distance", func(t *testing.T) { + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + if tc.bgp == "iBGP" { + changeProtocolToIBGP(t, ts) + gnmi.Update(t, ts.DUT, bgpPath.Global().DefaultRouteDistance().InternalRouteDistance().Config(), tc.rd) + gnmi.Await(t, ts.DUT, bgpPath.Global().DefaultRouteDistance().InternalRouteDistance().State(), 30*time.Second, tc.rd) + } else { + gnmi.Update(t, ts.DUT, bgpPath.Global().DefaultRouteDistance().ExternalRouteDistance().Config(), tc.rd) + gnmi.Await(t, ts.DUT, bgpPath.Global().DefaultRouteDistance().ExternalRouteDistance().State(), 30*time.Second, tc.rd) + } + + ts.ATETop.Flows().Clear() + createFlow(t, ts.ATETop, ts.ATE.OTG(), false) + createFlow(t, ts.ATETop, ts.ATE.OTG(), true) + ts.ATE.OTG().PushConfig(t, ts.ATETop) + ts.ATE.OTG().StartProtocols(t) + otgutils.WaitForARP(t, ts.ATE.OTG(), ts.ATETop, "IPv4") + otgutils.WaitForARP(t, ts.ATE.OTG(), ts.ATETop, "IPv6") + + ts.ATE.OTG().StartTraffic(t) + // added 30 seconds for sleep for traffic flow + time.Sleep(30 * time.Second) + ts.ATE.OTG().StopTraffic(t) + + otgutils.LogFlowMetrics(t, ts.ATE.OTG(), ts.ATETop) + otgutils.LogPortMetrics(t, ts.ATE.OTG(), ts.ATETop) + + txPkts := gnmi.Get[uint64](t, ts.ATE.OTG(), gnmi.OTG().Port(ts.ATE.Port(t, "port3").ID()).Counters().OutFrames().State()) + rxPkts := gnmi.Get[uint64](t, ts.ATE.OTG(), gnmi.OTG().Port(ts.ATE.Port(t, tc.port).ID()).Counters().InFrames().State()) + if got := (math.Abs(float64(txPkts)-float64(rxPkts)) * 100) / float64(txPkts); got > lossTolerance { + t.Errorf("Packet loss percentage for flow: got %v, want %v", got, lossTolerance) + } + }) + } + }) +} + +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) + } + st.GetOrCreateActions().PolicyResult = pr + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) +} + +func changeProtocolToIBGP(t *testing.T, ts *isissession.TestSession) { + root := &oc.Root{} + dni := root.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(ts.DUT)) + bgpP := dni.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName) + bgpP.GetOrCreateBgp().GetOrCreateNeighbor(isissession.ATETrafficAttrs.IPv4).SetPeerAs(dutAS) + bgpP.GetOrCreateBgp().GetOrCreateNeighbor(isissession.ATETrafficAttrs.IPv6).SetPeerAs(dutAS) + gnmi.Update(t, ts.DUT, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(ts.DUT)).Config(), dni) + + bgp4Peer := ts.ATEIntf2.Bgp().Ipv4Interfaces().Items()[0].Peers().Items()[0] + bgp4Peer.SetAsNumber(dutAS).SetAsType(gosnappi.BgpV4PeerAsType.IBGP) + bgp6Peer := ts.ATEIntf2.Bgp().Ipv6Interfaces().Items()[0].Peers().Items()[0] + bgp6Peer.SetAsNumber(dutAS).SetAsType(gosnappi.BgpV6PeerAsType.IBGP) +} + +func configurePort3(t *testing.T, ts *isissession.TestSession) { + t.Helper() + dc := gnmi.OC() + + dp3 := ts.DUT.Port(t, "port3") + i3 := dutPort3.ConfigOCInterface(ts.DUTConf.GetOrCreateInterface(dp3.Name()), ts.DUT) + gnmi.Replace(t, ts.DUT, dc.Interface(i3.GetName()).Config(), i3) + if deviations.ExplicitInterfaceInDefaultVRF(ts.DUT) { + fptest.AssignToNetworkInstance(t, ts.DUT, dp3.Name(), deviations.DefaultNetworkInstance(ts.DUT), 0) + } + if deviations.ExplicitPortSpeed(ts.DUT) { + fptest.SetPortSpeed(t, dp3) + } + ap3 := ts.ATE.Port(t, "port3") + atePort3.AddToOTG(ts.ATETop, ap3, dutPort3) +} + +func createFlow(t *testing.T, config gosnappi.Config, otg *otg.OTG, isV6 bool) { + t.Helper() + + flowName := "flowV4" + if isV6 { + flowName = "flowV6" + } + flow := config.Flows().Add().SetName(flowName) + flow.Metrics().SetEnable(true) + if isV6 { + flow.TxRx().Device(). + SetTxNames([]string{"port3.IPv6"}). + SetRxNames([]string{"port1.IPv6", "port2.IPv6"}) + } else { + flow.TxRx().Device(). + SetTxNames([]string{"port3.IPv4"}). + SetRxNames([]string{"port1.IPv4", "port2.IPv4"}) + } + flow.Size().SetFixed(512) + flow.Rate().SetPps(100) + flow.Duration().Continuous() + ethHeader := flow.Packet().Add().Ethernet() + ethHeader.Src().SetValue(atePort3.MAC) + if isV6 { + ipHeader := flow.Packet().Add().Ipv6() + ipHeader.Src().SetValue(atePort3.IPv6) + ipHeader.Dst().SetValue(advertisedIPv6.address) + } else { + ipHeader := flow.Packet().Add().Ipv4() + ipHeader.Src().SetValue(atePort3.IPv4) + ipHeader.Dst().SetValue(advertisedIPv4.address) + } +} + +// setupEBGPAndAdvertise setups eBGP on DUT port1 and ATE port1 +func setupEBGPAndAdvertise(t *testing.T, ts *isissession.TestSession) { + t.Helper() + + // setup eBGP on DUT port1 and iBGP on port2 + root := &oc.Root{} + dni := root.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(ts.DUT)) + dni.SetType(oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_DEFAULT_INSTANCE) + + bgpP := dni.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName) + bgpP.SetEnabled(true) + bgp := bgpP.GetOrCreateBgp() + + g := bgp.GetOrCreateGlobal() + g.SetAs(dutAS) + g.SetRouterId(isissession.DUTTrafficAttrs.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.PeerAs = ygot.Uint32(dutAS) + pgv4.PeerGroupName = ygot.String(peerGrpNamev4) + pgv6 := bgp.GetOrCreatePeerGroup(peerGrpNamev6) + pgv6.PeerAs = ygot.Uint32(dutAS) + 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) + + // Configure Import Allow-All policy + configureRoutePolicy(t, ts.DUT, "ALLOW", oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + pg1af4 := pgv4.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + pg1af4.Enabled = ygot.Bool(true) + + pg1rpl4 := pg1af4.GetOrCreateApplyPolicy() + pg1rpl4.SetImportPolicy([]string{"ALLOW"}) + + pg1af6 := pgv6.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + pg1af6.Enabled = ygot.Bool(true) + pg1rpl6 := pg1af6.GetOrCreateApplyPolicy() + pg1rpl6.SetImportPolicy([]string{"ALLOW"}) + + gnmi.Update(t, ts.DUT, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(ts.DUT)).Config(), dni) + + // setup eBGP on ATE port1 + devBGP := ts.ATEIntf2.Bgp().SetRouterId(isissession.ATETrafficAttrs.IPv4) + + ipv4 := ts.ATEIntf2.Ethernets().Items()[0].Ipv4Addresses().Items()[0] + bgp4Peer := devBGP.Ipv4Interfaces().Add().SetIpv4Name(ipv4.Name()).Peers().Add().SetName(ts.ATEIntf2.Name() + ".BGP4.peer") + bgp4Peer.SetPeerAddress(isissession.DUTTrafficAttrs.IPv4).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV4PeerAsType.EBGP) + + ipv6 := ts.ATEIntf2.Ethernets().Items()[0].Ipv6Addresses().Items()[0] + bgp6Peer := devBGP.Ipv6Interfaces().Add().SetIpv6Name(ipv6.Name()).Peers().Add().SetName(ts.ATEIntf2.Name() + ".BGP6.peer") + bgp6Peer.SetPeerAddress(isissession.DUTTrafficAttrs.IPv6).SetAsNumber(ateAS).SetAsType(gosnappi.BgpV6PeerAsType.EBGP) + + // configure emulated IPv4 and IPv6 networks + netv4 := bgp4Peer.V4Routes().Add().SetName("v4-bgpNet-dev") + netv4.Addresses().Add().SetAddress(advertisedIPv4.address).SetPrefix(advertisedIPv4.prefix).SetCount(uint32(prefixesCount)) + + netv6 := bgp6Peer.V6Routes().Add().SetName("v6-bgpNet-dev") + netv6.Addresses().Add().SetAddress(advertisedIPv6.address).SetPrefix(advertisedIPv6.prefix).SetCount(uint32(prefixesCount)) + + ts.ATE.OTG().PushConfig(t, ts.ATETop) + ts.ATE.OTG().StartProtocols(t) + otgutils.WaitForARP(t, ts.ATE.OTG(), ts.ATETop, "IPv4") + otgutils.WaitForARP(t, ts.ATE.OTG(), ts.ATETop, "IPv6") +} + +func advertisePrefixFromISISPort(t *testing.T, ts *isissession.TestSession) { + netv4 := ts.ATEIntf1.Isis().V4Routes().Add().SetName("netv4").SetLinkMetric(10).SetOriginType(gosnappi.IsisV4RouteRangeOriginType.EXTERNAL) + netv4.Addresses().Add().SetAddress(advertisedIPv4.address).SetPrefix(advertisedIPv4.prefix).SetCount(uint32(prefixesCount)) + + netv6 := ts.ATEIntf1.Isis().V6Routes().Add().SetName("netv6").SetLinkMetric(10).SetOriginType(gosnappi.IsisV6RouteRangeOriginType.EXTERNAL) + netv6.Addresses().Add().SetAddress(advertisedIPv6.address).SetPrefix(advertisedIPv6.prefix).SetCount(uint32(prefixesCount)) +} diff --git a/feature/bgp/admin_distance/otg_tests/admin_distance_test/metadata.textproto b/feature/bgp/admin_distance/otg_tests/admin_distance_test/metadata.textproto new file mode 100644 index 00000000000..42c8e91175e --- /dev/null +++ b/feature/bgp/admin_distance/otg_tests/admin_distance_test/metadata.textproto @@ -0,0 +1,50 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "00e08af7-9b22-4b0e-bf1b-c84e0af5a896" +plan_id: "RT-1.34" +description: "BGP route-distance configuration" +testbed: TESTBED_DUT_ATE_4LINKS +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + isis_instance_enabled_required: true + omit_l2_mtu: true + missing_value_for_defaults: true + interface_enabled: true + default_network_instance: "default" + isis_interface_afi_unsupported: true + skip_isis_set_level: true + skip_set_rp_match_set_options: true + skip_setting_disable_metric_propagation: true + skip_setting_statement_for_policy: true + } +} +platform_exceptions: { + platform: { + vendor: JUNIPER + } + deviations: { + bgp_rib_oc_path_unsupported: true + } +} +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + bgp_rib_oc_path_unsupported: true + } +} +platform_exceptions: { + platform: { + vendor: NOKIA + } + deviations: { + explicit_interface_in_default_vrf: true + interface_enabled: true + } +} + diff --git a/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/README.md b/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/README.md index 093bcf0895c..5a9fe642af5 100644 --- a/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/README.md +++ b/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/README.md @@ -355,3 +355,57 @@ ## Required DUT platform * FFF + +## 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 + /network-instances/network-instance/protocols/protocol/isis/global/config/level-capability: + /network-instances/network-instance/protocols/protocol/isis/levels/level/config/metric-style: + /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/isis-actions/config/set-level: + /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: + /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/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-community/reference/config/community-set-ref: + /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/disable-metric-propagation: + /network-instances/network-instance/table-connections/table-connection/config/import-policy: + + ## State paths + /routing-policy/policy-definitions/policy-definition/state/name: + /routing-policy/policy-definitions/policy-definition/statements/statement/state/name: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/state/policy-result: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/isis-actions/state/set-level: + /routing-policy/defined-sets/prefix-sets/prefix-set/state/name: + /routing-policy/defined-sets/prefix-sets/prefix-set/state/mode: + /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/state/ip-prefix: + /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/state/masklength-range: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/state/match-set-options: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/state/prefix-set: + /network-instances/network-instance/table-connections/table-connection/state/import-policy: + /network-instances/network-instance/table-connections/table-connection/state/address-family: + /network-instances/network-instance/table-connections/table-connection/state/src-protocol: + /network-instances/network-instance/table-connections/table-connection/state/dst-protocol: + /network-instances/network-instance/table-connections/table-connection/state/disable-metric-propagation: + /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: + +rpcs: + gnmi: + gNMI.Set: + gNMI.Subscribe: +``` 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 c4430ea1534..00ff8bae5f0 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 @@ -25,6 +25,7 @@ import ( "github.com/openconfig/featureprofiles/internal/cfgplugins" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/helpers" "github.com/openconfig/featureprofiles/internal/isissession" "github.com/openconfig/featureprofiles/internal/otgutils" "github.com/openconfig/ondatra" @@ -36,31 +37,37 @@ import ( ) const ( - bgpName = "BGP" - maskLenExact = "exact" - dummyAS = uint32(64655) - dutAS = uint32(64656) - ateAS = uint32(64657) - v4Route = "203.10.113.0" - v4TrafficStart = "203.10.113.1" - v4DummyRoute = "192.51.100.0" - v4RoutePrefix = uint32(24) - v6Route = "2001:db8:128:128:0:0:0:0" - v6TrafficStart = "2001:db8:128:128:0:0:0:1" - v6DummyRoute = "2001:db8:128:129:0:0:0:0" - v6RoutePrefix = uint32(64) - v4RoutePolicy = "route-policy-v4" - v4Statement = "statement-v4" - v4PrefixSet = "prefix-set-v4" - v4FlowName = "flow-v4" - v4CommunitySet = "community-set-v4" - v6RoutePolicy = "route-policy-v6" - v6Statement = "statement-v6" - v6PrefixSet = "prefix-set-v6" - v6FlowName = "flow-v6" - v6CommunitySet = "community-set-v6" - peerGrpNamev4 = "BGP-PEER-GROUP-V4" - peerGrpNamev6 = "BGP-PEER-GROUP-V6" + bgpName = "BGP" + maskLenExact = "exact" + dummyAS = uint32(64655) + dutAS = uint32(64656) + ateAS = uint32(64657) + v4Route = "203.10.113.0" + v4TrafficStart = "203.10.113.1" + v4DummyRoute = "192.51.100.0" + v4RoutePrefix = uint32(24) + v6Route = "2001:db8:128:128:0:0:0:0" + v6TrafficStart = "2001:db8:128:128:0:0:0:1" + v6DummyRoute = "2001:db8:128:129:0:0:0:0" + v6RoutePrefix = uint32(64) + v4RoutePolicy = "route-policy-v4" + v4Statement = "statement-v4" + v4PrefixSet = "prefix-set-v4" + v4FlowName = "flow-v4" + v4CommunitySet = "community-set-v4" + v6RoutePolicy = "route-policy-v6" + v6Statement = "statement-v6" + v6PrefixSet = "prefix-set-v6" + v6FlowName = "flow-v6" + v6CommunitySet = "community-set-v6" + peerGrpNamev4 = "BGP-PEER-GROUP-V4" + peerGrpNamev6 = "BGP-PEER-GROUP-V6" + allowAllPolicy = "ALLOWAll" + tablePolicyMatchCommunitySetTag = "TablePolicyMatchCommunitySetTag" + matchTagRedistributionPolicy = "MatchTagRedistributionPolicy" + nonMatchingCommunityVal = "64655:200" + matchingCommunityVal = "64657:100" + routeTagVal = 10000 ) var ( @@ -96,13 +103,13 @@ type testCase struct { ipv4 bool } -func TestBGPToBGPRedistribution(t *testing.T) { +func TestBGPToISISRedistribution(t *testing.T) { ts := isissession.MustNew(t).WithISIS() t.Run("ISIS Setup", func(t *testing.T) { ts.PushAndStart(t) ts.MustAdjacency(t) }) - + configureRoutePolicyAllow(t, ts.DUT, allowAllPolicy, oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) setupEBGPAndAdvertise(t, ts) t.Run("BGP Setup", func(t *testing.T) { t.Log("Verify DUT BGP sessions up") @@ -200,7 +207,7 @@ func TestBGPToBGPRedistribution(t *testing.T) { // setupEBGPAndAdvertise setups eBGP on DUT port1 and ATE port1 func setupEBGPAndAdvertise(t *testing.T, ts *isissession.TestSession) { t.Helper() - + dut := ondatra.DUT(t, "dut") // setup eBGP on DUT port2 root := &oc.Root{} dni := root.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(ts.DUT)) @@ -220,6 +227,26 @@ func setupEBGPAndAdvertise(t *testing.T, ts *isissession.TestSession) { pgv4.PeerGroupName = ygot.String(peerGrpNamev4) pgv6 := bgp.GetOrCreatePeerGroup(peerGrpNamev6) pgv6.PeerGroupName = ygot.String(peerGrpNamev6) + if deviations.RoutePolicyUnderAFIUnsupported(dut) { + rpl := pgv4.GetOrCreateApplyPolicy() + rpl.SetExportPolicy([]string{allowAllPolicy}) + rpl.SetImportPolicy([]string{allowAllPolicy}) + rplv6 := pgv6.GetOrCreateApplyPolicy() + rplv6.SetExportPolicy([]string{"ALLOW"}) + rplv6.SetImportPolicy([]string{"ALLOW"}) + } else { + pg1af4 := pgv4.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + pg1af4.Enabled = ygot.Bool(true) + pg1rpl4 := pg1af4.GetOrCreateApplyPolicy() + pg1rpl4.SetExportPolicy([]string{allowAllPolicy}) + pg1rpl4.SetImportPolicy([]string{allowAllPolicy}) + pg1af6 := pgv6.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + pg1af6.Enabled = ygot.Bool(true) + pg1rpl6 := pg1af6.GetOrCreateApplyPolicy() + pg1rpl6.SetExportPolicy([]string{allowAllPolicy}) + pg1rpl6.SetImportPolicy([]string{allowAllPolicy}) + } + nV4 := bgp.GetOrCreateNeighbor(isissession.ATETrafficAttrs.IPv4) nV4.SetPeerAs(ateAS) nV4.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) @@ -302,39 +329,49 @@ func matchingPrefixRoutePolicy(t *testing.T, dut *ondatra.DUTDevice) { } func nonMatchingCommunityRoutePolicy(t *testing.T, dut *ondatra.DUTDevice) { - root := &oc.Root{} - rp := root.GetOrCreateRoutingPolicy() - pdef := rp.GetOrCreatePolicyDefinition(v4RoutePolicy) - stmt, err := pdef.AppendNewStatement(v4Statement) - if err != nil { - t.Fatalf("AppendNewStatement(%s) failed: %v", v4Statement, err) - } - stmt.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) - if !deviations.SkipIsisSetLevel(dut) { - stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetLevel(2) - } - if !deviations.SkipIsisSetMetricStyleType(dut) { - stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetMetricStyleType(oc.IsisPolicy_MetricStyle_WIDE_METRIC) - } + if deviations.CommunityMatchWithRedistributionUnsupported(dut) { + configureBGPTablePolicyWithSetTag(t, v4PrefixSet, advertisedIPv4.cidr(t), v4CommunitySet, dummyAS, 200, true) + bgpISISRedistributionWithRouteTagPolicy(t, dut, oc.Types_ADDRESS_FAMILY_IPV4) + } else { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + pdef := rp.GetOrCreatePolicyDefinition(v4RoutePolicy) + stmt, err := pdef.AppendNewStatement(v4Statement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", v4Statement, err) + } + stmt.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + if !deviations.SkipIsisSetLevel(dut) { + stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetLevel(2) + } + if !deviations.SkipIsisSetMetricStyleType(dut) { + stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetMetricStyleType(oc.IsisPolicy_MetricStyle_WIDE_METRIC) + } - communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v4CommunitySet) - communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", dummyAS, 200))}) - communitySet.SetMatchSetOptions(oc.BgpPolicy_MatchSetOptionsType_ANY) + communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v4CommunitySet) + communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", dummyAS, 200))}) + communitySet.SetMatchSetOptions(oc.BgpPolicy_MatchSetOptionsType_ANY) - if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { - stmt.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(v4CommunitySet) - } else { - stmt.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(v4CommunitySet) + if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { + stmt.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(v4CommunitySet) + } else { + stmt.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(v4CommunitySet) + } + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) } - gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) } func matchingCommunityRoutePolicy(t *testing.T, dut *ondatra.DUTDevice) { - root := &oc.Root{} - rp := root.GetOrCreateRoutingPolicy() - communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v4CommunitySet) - communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", ateAS, 100))}) - gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().BgpDefinedSets().CommunitySet(v4CommunitySet).Config(), communitySet) + if deviations.CommunityMatchWithRedistributionUnsupported(dut) { + configureBGPTablePolicyWithSetTag(t, v4PrefixSet, advertisedIPv4.cidr(t), v4CommunitySet, ateAS, 100, true) + bgpISISRedistributionWithRouteTagPolicy(t, dut, oc.Types_ADDRESS_FAMILY_IPV4) + } else { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v4CommunitySet) + communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", ateAS, 100))}) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().BgpDefinedSets().CommunitySet(v4CommunitySet).Config(), communitySet) + } } func verifyNonMatchingPrefixTelemetry(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { @@ -422,9 +459,16 @@ func verifyNonMatchingCommunityTelemetry(t *testing.T, dut *ondatra.DUTDevice, a if commSet == nil { t.Errorf("Community set is nil, want non-nil") } - cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", dummyAS, 200), 16, 0) - if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { - t.Errorf("Community set member: %v, want: %d", commSetMember, cm) + if deviations.BgpCommunityMemberIsAString(dut) { + cm := nonMatchingCommunityVal + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionString(cm))) { + t.Errorf("Community set member: %v, want: %s", commSetMember, cm) + } + } else { + cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", dummyAS, 200), 16, 0) + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { + t.Errorf("Community set member: %v, want: %d", commSetMember, cm) + } } _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().ExtendedIpv4Reachability().Prefix(advertisedIPv4.address).State(), 30*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_ExtendedIpv4Reachability_Prefix]) bool { @@ -441,9 +485,16 @@ func verifyMatchingCommunityTelemetry(t *testing.T, dut *ondatra.DUTDevice, ate if commSet == nil { t.Errorf("Community set is nil, want non-nil") } - cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", ateAS, 100), 16, 0) - if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { - t.Errorf("Community set member: %v, want: %v", commSetMember, cm) + if deviations.BgpCommunityMemberIsAString(dut) { + cm := matchingCommunityVal + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionString(cm))) { + t.Errorf("Community set member: %v, want: %v", commSetMember, cm) + } + } else { + cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", ateAS, 100), 16, 0) + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { + t.Errorf("Community set member: %v, want: %v", commSetMember, cm) + } } _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().ExtendedIpv4Reachability().Prefix(advertisedIPv4.address).State(), 30*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_ExtendedIpv4Reachability_Prefix]) bool { @@ -494,39 +545,49 @@ func matchingPrefixRoutePolicyV6(t *testing.T, dut *ondatra.DUTDevice) { } func nonMatchingCommunityRoutePolicyV6(t *testing.T, dut *ondatra.DUTDevice) { - root := &oc.Root{} - rp := root.GetOrCreateRoutingPolicy() - pdef := rp.GetOrCreatePolicyDefinition(v6RoutePolicy) - stmt, err := pdef.AppendNewStatement(v6Statement) - if err != nil { - t.Fatalf("AppendNewStatement(%s) failed: %v", v6Statement, err) - } - stmt.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) - if !deviations.SkipIsisSetLevel(dut) { - stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetLevel(2) - } - if !deviations.SkipIsisSetMetricStyleType(dut) { - stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetMetricStyleType(oc.IsisPolicy_MetricStyle_WIDE_METRIC) - } + if deviations.CommunityMatchWithRedistributionUnsupported(dut) { + configureBGPTablePolicyWithSetTag(t, v6PrefixSet, advertisedIPv6.cidr(t), v6CommunitySet, dummyAS, 200, false) + bgpISISRedistributionWithRouteTagPolicy(t, dut, oc.Types_ADDRESS_FAMILY_IPV6) + } else { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + pdef := rp.GetOrCreatePolicyDefinition(v6RoutePolicy) + stmt, err := pdef.AppendNewStatement(v6Statement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", v6Statement, err) + } + stmt.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + if !deviations.SkipIsisSetLevel(dut) { + stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetLevel(2) + } + if !deviations.SkipIsisSetMetricStyleType(dut) { + stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetMetricStyleType(oc.IsisPolicy_MetricStyle_WIDE_METRIC) + } - communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v6CommunitySet) - communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", dummyAS, 200))}) - communitySet.SetMatchSetOptions(oc.BgpPolicy_MatchSetOptionsType_ANY) + communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v6CommunitySet) + communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", dummyAS, 200))}) + communitySet.SetMatchSetOptions(oc.BgpPolicy_MatchSetOptionsType_ANY) - if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { - stmt.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(v6CommunitySet) - } else { - stmt.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(v6CommunitySet) + if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { + stmt.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(v6CommunitySet) + } else { + stmt.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(v6CommunitySet) + } + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) } - gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) } func matchingCommunityRoutePolicyV6(t *testing.T, dut *ondatra.DUTDevice) { - root := &oc.Root{} - rp := root.GetOrCreateRoutingPolicy() - communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v6CommunitySet) - communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", ateAS, 100))}) - gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().BgpDefinedSets().CommunitySet(v6CommunitySet).Config(), communitySet) + if deviations.CommunityMatchWithRedistributionUnsupported(dut) { + configureBGPTablePolicyWithSetTag(t, v6PrefixSet, advertisedIPv6.cidr(t), v6CommunitySet, ateAS, 100, false) + bgpISISRedistributionWithRouteTagPolicy(t, dut, oc.Types_ADDRESS_FAMILY_IPV6) + } else { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v6CommunitySet) + communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", ateAS, 100))}) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().BgpDefinedSets().CommunitySet(v6CommunitySet).Config(), communitySet) + } } func verifyNonMatchingPrefixTelemetryV6(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { @@ -581,7 +642,7 @@ func verifyNonMatchingPrefixTelemetryV6(t *testing.T, dut *ondatra.DUTDevice, at t.Errorf("Import policy: %v, want: %s", importPolicy, []string{v6RoutePolicy}) } - _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 30*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { + _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 60*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { prefix, present := v.Val() return present && prefix.GetPrefix() == advertisedIPv6.address }).Await(t) @@ -600,7 +661,7 @@ func verifyMatchingPrefixTelemetryV6(t *testing.T, dut *ondatra.DUTDevice, ate * t.Errorf("Prefix is nil, want: %s", advertisedIPv6.cidr(t)) } - _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 30*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { + _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 60*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { prefix, present := v.Val() return present && prefix.GetPrefix() == advertisedIPv6.address }).Await(t) @@ -614,12 +675,19 @@ func verifyNonMatchingCommunityTelemetryV6(t *testing.T, dut *ondatra.DUTDevice, if commSet == nil { t.Errorf("Community set is nil, want non-nil") } - cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", dummyAS, 200), 16, 0) - if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { - t.Errorf("Community set member: %v, want: %d", commSetMember, cm) + if deviations.BgpCommunityMemberIsAString(dut) { + cm := nonMatchingCommunityVal + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionString(cm))) { + t.Errorf("Community set member: %v, want: %v", commSetMember, cm) + } + } else { + cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", dummyAS, 200), 16, 0) + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { + t.Errorf("Community set member: %v, want: %d", commSetMember, cm) + } } - _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 30*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { + _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 60*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { prefix, present := v.Val() return present && prefix.GetPrefix() == advertisedIPv6.address }).Await(t) @@ -633,12 +701,19 @@ func verifyMatchingCommunityTelemetryV6(t *testing.T, dut *ondatra.DUTDevice, at if commSet == nil { t.Errorf("Community set is nil, want non-nil") } - cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", ateAS, 100), 16, 0) - if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { - t.Errorf("Community set member: %v, want: %v", commSetMember, cm) + if deviations.BgpCommunityMemberIsAString(dut) { + cm := matchingCommunityVal + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionString(cm))) { + t.Errorf("Community set member: %v, want: %v", commSetMember, cm) + } + } else { + cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", ateAS, 100), 16, 0) + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { + t.Errorf("Community set member: %v, want: %v", commSetMember, cm) + } } - _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 30*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { + _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 60*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { prefix, present := v.Val() return present && prefix.GetPrefix() == advertisedIPv6.address }).Await(t) @@ -654,7 +729,9 @@ func bgpISISRedistribution(t *testing.T, dut *ondatra.DUTDevice) { if !deviations.SkipSettingDisableMetricPropagation(dut) { tableConn.SetDisableMetricPropagation(false) } - tableConn.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + if !deviations.DefaultRoutePolicyUnsupported(dut) { + tableConn.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } tableConn.SetImportPolicy([]string{v4RoutePolicy}) gnmi.Update(t, dut, gnmi.OC().NetworkInstance(dni).TableConnection(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, oc.Types_ADDRESS_FAMILY_IPV4).Config(), tableConn) } @@ -666,10 +743,86 @@ func bgpISISRedistributionV6(t *testing.T, dut *ondatra.DUTDevice) { if !deviations.SkipSettingDisableMetricPropagation(dut) { tableConn.SetDisableMetricPropagation(false) } - tableConn.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + if !deviations.DefaultRoutePolicyUnsupported(dut) { + tableConn.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } tableConn.SetImportPolicy([]string{v6RoutePolicy}) gnmi.Update(t, dut, gnmi.OC().NetworkInstance(dni).TableConnection(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, oc.Types_ADDRESS_FAMILY_IPV6).Config(), tableConn) } +func bgpISISRedistributionWithRouteTagPolicy(t *testing.T, dut *ondatra.DUTDevice, afi oc.E_Types_ADDRESS_FAMILY) { + dni := deviations.DefaultNetworkInstance(dut) + root := &oc.Root{} + tableConn := root.GetOrCreateNetworkInstance(dni).GetOrCreateTableConnection(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, afi) + if !deviations.SkipSettingDisableMetricPropagation(dut) { + tableConn.SetDisableMetricPropagation(false) + } + tableConn.SetImportPolicy([]string{matchTagRedistributionPolicy}) + gnmi.Update(t, dut, gnmi.OC().NetworkInstance(dni).TableConnection(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, afi).Config(), tableConn) +} + +func configureBGPTablePolicyWithSetTag(t *testing.T, prefixSetName, prefixSetAddress, communitySetName string, commAS, commValue uint32, v4Nbr bool) { + dut := ondatra.DUT(t, "dut") + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + //BGP Table-policy to match community & prefix and set the route-Tag + pdef1 := rp.GetOrCreatePolicyDefinition(tablePolicyMatchCommunitySetTag) + stmt1, err := pdef1.AppendNewStatement("SetTag") + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", "routePolicyStatement", err) + } + //Create prefix-set + prefixSet := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet(prefixSetName) + prefixSet.GetOrCreatePrefix(prefixSetAddress, maskLenExact) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(prefixSetName).Config(), prefixSet) + //Create community-set + communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(communitySetName) + communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", commAS, commValue))}) + communitySet.SetMatchSetOptions(oc.BgpPolicy_MatchSetOptionsType_ANY) + + stmt1.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetPrefixSet(prefixSetName) + stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(communitySetName) + stmt1.GetOrCreateActions().GetOrCreateSetTag().SetMode(oc.SetTag_Mode_INLINE) + stmt1.GetOrCreateActions().GetOrCreateSetTag().GetOrCreateInline().SetTag([]oc.RoutingPolicy_PolicyDefinition_Statement_Actions_SetTag_Inline_Tag_Union{oc.UnionUint32(routeTagVal)}) + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + + //Create tag-set with above route tag value + tagSet := rp.GetOrCreateDefinedSets().GetOrCreateTagSet("RouteTagForRedistribution") + tagSet.SetName("RouteTagForRedistribution") + tagSet.SetTagValue([]oc.RoutingPolicy_DefinedSets_TagSet_TagValue_Union{oc.UnionUint32(routeTagVal)}) + + //Route-policy to match tag and accept + pdef2 := rp.GetOrCreatePolicyDefinition("MatchTagRedistributionPolicy") + stmt2, err := pdef2.AppendNewStatement("matchTag") + stmt2.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetPrefixSet(prefixSetName) + stmt2.GetOrCreateConditions().GetOrCreateMatchTagSet().SetTagSet("RouteTagForRedistribution") + stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", "routePolicyStatement", err) + } + + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + var bgpTablePolicyCLI string + if v4Nbr { + bgpTablePolicyCLI = fmt.Sprintf("router bgp %v instance BGP address-family ipv4 unicast \n table-policy %v", dutAS, tablePolicyMatchCommunitySetTag) + helpers.GnmiCLIConfig(t, dut, bgpTablePolicyCLI) + } else { + bgpTablePolicyCLI = fmt.Sprintf("router bgp %v instance BGP address-family ipv6 unicast \n table-policy %v", dutAS, tablePolicyMatchCommunitySetTag) + helpers.GnmiCLIConfig(t, dut, bgpTablePolicyCLI) + } +} + +func configureRoutePolicyAllow(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) + } + st.GetOrCreateActions().PolicyResult = pr + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) +} func createFlow(t *testing.T, ts *isissession.TestSession) { ts.ATETop.Flows().Clear() @@ -693,6 +846,7 @@ func createFlow(t *testing.T, ts *isissession.TestSession) { ts.ATE.OTG().PushConfig(t, ts.ATETop) ts.ATE.OTG().StartProtocols(t) otgutils.WaitForARP(t, ts.ATE.OTG(), ts.ATETop, "IPv4") + cfgplugins.VerifyDUTBGPEstablished(t, ts.DUT) } func createFlowV6(t *testing.T, ts *isissession.TestSession) { @@ -717,6 +871,7 @@ func createFlowV6(t *testing.T, ts *isissession.TestSession) { ts.ATE.OTG().PushConfig(t, ts.ATETop) ts.ATE.OTG().StartProtocols(t) otgutils.WaitForARP(t, ts.ATE.OTG(), ts.ATETop, "IPv6") + cfgplugins.VerifyDUTBGPEstablished(t, ts.DUT) } func checkTraffic(t *testing.T, ts *isissession.TestSession, flowName string) { 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 a87078e24c0..f4552e8cdf0 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 @@ -32,3 +32,16 @@ platform_exceptions: { isis_level_enabled: true } } +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + skip_isis_set_level: true + skip_isis_set_metric_style_type: true + bgp_conditions_match_community_set_unsupported: true + bgp_community_member_is_a_string: true + community_match_with_redistribution_unsupported: true + default_route_policy_unsupported: true + } +} diff --git a/feature/bgp/gracefulrestart/ate_tests/bgp_graceful_restart_test/README.md b/feature/bgp/gracefulrestart/ate_tests/bgp_graceful_restart_test/README.md index a990301af8a..d84c42a4972 100644 --- a/feature/bgp/gracefulrestart/ate_tests/bgp_graceful_restart_test/README.md +++ b/feature/bgp/gracefulrestart/ate_tests/bgp_graceful_restart_test/README.md @@ -102,7 +102,6 @@ The origial RFC4724 had no coverage for Graceful restart process post send/recei * Start traffic from ATE Port1 towards ATE Port2. Confirm there is zero packet loss. Stop traffic. * Revert ATE configurtion blocking TCP connection to/from DUT over TCP-Port:179 so the EBGP peering between ATE:Port1 <> DUT:port1 is reestablished. Restart traffic and confirm that there is zero packet loss. * Restart the above procedure for the IBGP peering between DUT port-2 and ATE port-2 - ## Config Parameter Coverage For prefixes: @@ -140,3 +139,15 @@ BGP conifguration: * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/graceful-restart/state/received * /network-instances/network-instance/protocols/protocol/bgp/global/graceful-restart/state/restart-time * /network-instances/network-instance/protocols/protocol/bgp/global/graceful-restart/state/stale-routes-time + +## OpenConfig Path and RPC Coverage + +```yaml +rpcs: + gnmi: + gNMI.Set: + gNMI.Get: + gNMI.Subscribe: + gnoi: + system.System.KillProcess: +``` \ No newline at end of file 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 207b9ffe4be..8eacae295cf 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 @@ -15,15 +15,12 @@ package bgp_graceful_restart_test import ( - "context" - "encoding/json" "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" @@ -466,221 +463,6 @@ func configACLInterface(t *testing.T, iFace *oc.Acl_Interface, ifName string) *a return aclConf } -// Helper function to replicate configACL() configs in native model -// Define the values for each ACL entry and marshal for json encoding. -// Then craft a gNMI set Request to update the changes. -func configACLNative(t testing.TB, d *ondatra.DUTDevice, name string) { - t.Helper() - switch d.Vendor() { - case ondatra.NOKIA: - var aclEntry10Val = []any{ - map[string]any{ - "action": map[string]any{ - "drop": map[string]any{}, - }, - "match": map[string]any{ - "ipv4": map[string]any{ - "destination-ip": map[string]any{ - "prefix": ateDstCIDR, - }, - "source-ip": map[string]any{ - "prefix": aclNullPrefix, - }, - }, - }, - }, - } - entry10Update, err := json.Marshal(aclEntry10Val) - if err != nil { - t.Fatalf("Error with json Marshal: %v", err) - } - - var aclEntry20Val = []any{ - map[string]any{ - "action": map[string]any{ - "drop": map[string]any{}, - }, - "match": map[string]any{ - "ipv4": map[string]any{ - "source-ip": map[string]any{ - "prefix": ateDstCIDR, - }, - "destination-ip": map[string]any{ - "prefix": aclNullPrefix, - }, - }, - }, - }, - } - entry20Update, err := json.Marshal(aclEntry20Val) - if err != nil { - t.Fatalf("Error with json Marshal: %v", err) - } - - var aclEntry30Val = []any{ - map[string]any{ - "action": map[string]any{ - "accept": map[string]any{}, - }, - "match": map[string]any{ - "ipv4": map[string]any{ - "source-ip": map[string]any{ - "prefix": aclNullPrefix, - }, - "destination-ip": map[string]any{ - "prefix": aclNullPrefix, - }, - }, - }, - }, - } - entry30Update, err := json.Marshal(aclEntry30Val) - 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: "acl"}, - {Name: "acl-filter", Key: map[string]string{"name": name, "type": "ipv4"}}, - {Name: "entry", Key: map[string]string{"sequence-id": "10"}}, - }, - }, - Val: &gpb.TypedValue{ - Value: &gpb.TypedValue_JsonIetfVal{ - JsonIetfVal: entry10Update, - }, - }, - }, - { - Path: &gpb.Path{ - Elem: []*gpb.PathElem{ - {Name: "acl"}, - {Name: "acl-filter", Key: map[string]string{"name": name, "type": "ipv4"}}, - {Name: "entry", Key: map[string]string{"sequence-id": "20"}}, - }, - }, - Val: &gpb.TypedValue{ - Value: &gpb.TypedValue_JsonIetfVal{ - JsonIetfVal: entry20Update, - }, - }, - }, - { - Path: &gpb.Path{ - Elem: []*gpb.PathElem{ - {Name: "acl"}, - {Name: "acl-filter", Key: map[string]string{"name": name, "type": "ipv4"}}, - {Name: "entry", Key: map[string]string{"sequence-id": "30"}}, - }, - }, - Val: &gpb.TypedValue{ - Value: &gpb.TypedValue_JsonIetfVal{ - JsonIetfVal: entry30Update, - }, - }, - }, - }, - } - gnmiClient := d.RawAPIs().GNMI(t) - if _, err := gnmiClient.Set(context.Background(), gpbSetRequest); err != nil { - t.Fatalf("Unexpected error configuring SRL ACL: %v", err) - } - default: - t.Fatalf("Unsupported vendor %s for deviation 'UseVendorNativeACLConfiguration'", d.Vendor()) - } -} - -// Helper function to replicate AdmitAllACL() configs in native model, -// then craft a gNMI set Request to update the changes. -func configAdmitAllACLNative(t testing.TB, d *ondatra.DUTDevice, name string) { - t.Helper() - switch d.Vendor() { - case ondatra.NOKIA: - gpbDelRequest := &gpb.SetRequest{ - Prefix: &gpb.Path{ - Origin: "native", - }, - Delete: []*gpb.Path{ - { - Elem: []*gpb.PathElem{ - {Name: "acl"}, - {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: "acl-filter", Key: map[string]string{"name": name, "type": "ipv4"}}, - {Name: "entry", Key: map[string]string{"sequence-id": "20"}}, - }, - }, - }, - } - gnmiClient := d.RawAPIs().GNMI(t) - if _, err := gnmiClient.Set(context.Background(), gpbDelRequest); err != nil { - t.Fatalf("Unexpected error removing SRL ACL: %v", err) - } - default: - t.Fatalf("Unsupported vendor %s for deviation 'UseVendorNativeACLConfiguration'", d.Vendor()) - } -} - -// Helper function to replicate configACLInterface in native model. -// Set ACL at interface ingress, -// then craft a gNMI set Request to update the changes. -func configACLInterfaceNative(t *testing.T, d *ondatra.DUTDevice, ifName string) { - t.Helper() - switch d.Vendor() { - case ondatra.NOKIA: - var interfaceAclVal = []any{ - map[string]any{ - "acl-filter": map[string]any{ - "name": aclName, - "type": "ipv4", - }, - }, - } - interfaceAclUpdate, err := json.Marshal(interfaceAclVal) - 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: "acl"}, - {Name: "interface", Key: map[string]string{"interface-id": ifName + ".0"}}, - {Name: "input"}, - }, - }, - Val: &gpb.TypedValue{ - Value: &gpb.TypedValue_JsonIetfVal{ - JsonIetfVal: interfaceAclUpdate, - }, - }, - }, - }, - } - gnmiClient := d.RawAPIs().GNMI(t) - if _, err := gnmiClient.Set(context.Background(), gpbSetRequest); err != nil { - t.Fatalf("Unexpected error configuring interface ACL: %v", err) - } - default: - t.Fatalf("Unsupported vendor %s for deviation 'UseVendorNativeACLConfiguration'", d.Vendor()) - } -} - func TestTrafficWithGracefulRestartSpeaker(t *testing.T) { dut := ondatra.DUT(t, "dut") ate := ondatra.ATE(t, "ate") @@ -734,14 +516,9 @@ func TestTrafficWithGracefulRestartSpeaker(t *testing.T) { startTime := time.Now() t.Log("Trigger Graceful Restart on ATE") ate.Actions().NewBGPGracefulRestart().WithRestartTime(grRestartTime * time.Second).WithPeers(bgpPeer).Send(t) - if deviations.UseVendorNativeACLConfig(dut) { - configACLNative(t, dut, aclName) - configACLInterfaceNative(t, dut, ifName) - } else { - gnmi.Replace(t, dut, gnmi.OC().Acl().AclSet(aclName, oc.Acl_ACL_TYPE_ACL_IPV4).Config(), configACL(d, aclName)) - aclConf := configACLInterface(t, iFace, ifName) - gnmi.Replace(t, dut, aclConf.Config(), iFace) - } + gnmi.Replace(t, dut, gnmi.OC().Acl().AclSet(aclName, oc.Acl_ACL_TYPE_ACL_IPV4).Config(), configACL(d, aclName)) + aclConf := configACLInterface(t, iFace, ifName) + gnmi.Replace(t, dut, aclConf.Config(), iFace) replaceDuration := time.Since(startTime) time.Sleep(grTimer - stopDuration - replaceDuration) t.Log("Send Traffic while GR timer counting down. Traffic should pass as BGP GR is enabled!") @@ -776,14 +553,9 @@ func TestTrafficWithGracefulRestartSpeaker(t *testing.T) { t.Run("RemoveAclInterface", func(t *testing.T) { t.Log("Removing Acl on the interface to restore BGP GR. Traffic should now pass!") - if deviations.UseVendorNativeACLConfig(dut) { - configAdmitAllACLNative(t, dut, aclName) - configACLInterfaceNative(t, dut, ifName) - } else { - gnmi.Replace(t, dut, gnmi.OC().Acl().AclSet(aclName, oc.Acl_ACL_TYPE_ACL_IPV4).Config(), configAdmitAllACL(d, aclName)) - aclPath := configACLInterface(t, iFace, ifName) - gnmi.Replace(t, dut, aclPath.Config(), iFace) - } + gnmi.Replace(t, dut, gnmi.OC().Acl().AclSet(aclName, oc.Acl_ACL_TYPE_ACL_IPV4).Config(), configAdmitAllACL(d, aclName)) + aclPath := configACLInterface(t, iFace, ifName) + gnmi.Replace(t, dut, aclPath.Config(), iFace) }) t.Run("VerifyBGPEstablished", func(t *testing.T) { diff --git a/feature/bgp/gracefulrestart/ate_tests/bgp_graceful_restart_test/metadata.textproto b/feature/bgp/gracefulrestart/ate_tests/bgp_graceful_restart_test/metadata.textproto index 4407d7edefe..6bb6b0f824b 100644 --- a/feature/bgp/gracefulrestart/ate_tests/bgp_graceful_restart_test/metadata.textproto +++ b/feature/bgp/gracefulrestart/ate_tests/bgp_graceful_restart_test/metadata.textproto @@ -18,7 +18,6 @@ platform_exceptions: { vendor: NOKIA } deviations: { - use_vendor_native_acl_config: true explicit_port_speed: true explicit_interface_in_default_vrf: true interface_enabled: true diff --git a/feature/bgp/linkbandwidth_community/otg_tests/linkbandwidth_aggregation/README.md b/feature/bgp/linkbandwidth_community/otg_tests/linkbandwidth_aggregation/README.md new file mode 100644 index 00000000000..46466da8b60 --- /dev/null +++ b/feature/bgp/linkbandwidth_community/otg_tests/linkbandwidth_aggregation/README.md @@ -0,0 +1,80 @@ +# RT-7.6: BGP Link Bandwidth Community - Cumulative + +## Summary + +This test verifies Link-bandwidth (LBW) extended community cumulative feature by DUT. + +## Testbed type + +* [`featureprofiles/topologies/atedut_2.testbed`](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_2.testbed) + +## Procedure + +### Test environment setup + + ``` + | | + [ 64x eBGP ] --- [ ATE Port 1 ] ---- | DUT | ---- [ ATE Port 2 ] + | | + ``` + +#### Configuration + +* Configure DUT with 2 routed ports. +* Configure 64x eBGP peers on ATE Port 1 interface +* Configure 64x eBGP peers on DUT Port 1 interface in peering group UPSTREAM. +* Configure a single eBGP peer on ATE Port 2 interface. +* Configure a single eBGP peer on DUT Port 2 interface in peering group DOWNSTREAM. + + +### RT-7.6.1: Verify LBW cumulative to eBGP peer + +* Enable BGP LBW receive for peering group UPSTREAM. +* Enable BGP LBW send for peering group DOWNSTREAM. +* Enable Link Bandwidth Cumulative feature on DOWNSTREAM. + +**TODO:** [Cumulative Link Bandwidth feature](https://datatracker.ietf.org/doc/draft-ietf-bess-ebgp-dmz/) is not currently modeled in OC. Related PR: https://github.com/openconfig/public/pull/1131 + + +2. Advertise the same test prefix from ATE from all UPSTREAM peers with LBW community: + * 32 peers - 10Mbps + * 16 peers - 20Mbps + * 8 peers - 40Mbps + * 8 peers - 80Mbps + +3. Verify that DUT advertises the test route to DOWNSTREAM eBGP peer with cumulative bandwidth community of 1600Mbps. + +### RT-7.6.2: Verify LBW changes. +Using RT-7.6.1 set up conduct following changes: + +1) Disable 32 peers advertising 10Mpbs bandwidth community. +2) Verify that DUT advertises the test route to Upstream peer with cumulative bandwidth community of 1280Mbps. +3) Re-enable 32 peers advertising 10Mpbs bandwidth community. +4) Verify that DUT advertises the test route to Upstream peer with cumulative bandwidth community of 1600Mbps. + +### RT-7.6.3: Verify LBW cumulative to iBGP peer + +1. Reconfigure 64x peers in peering group UPSTREAM to iBGP +2. Reconfigure a single peer in peering group DOWNSTREAM to iBGP +3. Repeat test RT-7.6.1 for iBGP. + +## OpenConfig Path and RPC Coverage + +```yaml +paths: + /network-instances/network-instance/protocols/protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/use-multiple-paths/ebgp/link-bandwidth-ext-community/config/enabled: + /network-instances/network-instance/protocols/protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/use-multiple-paths/ibgp/link-bandwidth-ext-community/config/enabled: + # TODO: Add Cumulative LBW path. + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + replace: true + gNMI.Subscribe: + on_change: true +``` + +## Required DUT platform + +* FFF \ No newline at end of file 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 5833ced40b0..3ab2c1cc4fd 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 @@ -15,6 +15,7 @@ package bgp_multipath_wecmp_test import ( + "fmt" "sort" "strconv" "testing" @@ -24,6 +25,7 @@ import ( "github.com/openconfig/featureprofiles/internal/cfgplugins" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/helpers" "github.com/openconfig/featureprofiles/internal/otgutils" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" @@ -147,15 +149,34 @@ func TestBGPSetup(t *testing.T) { dni := deviations.DefaultNetworkInstance(bs.DUT) bgp := bs.DUTConf.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").GetOrCreateBgp() if deviations.MultipathUnsupportedNeighborOrAfisafi(bs.DUT) { + t.Logf("MultipathUnsupportedNeighborOrAfisafi is supported") bgp.GetOrCreatePeerGroup(cfgplugins.BGPPeerGroup1).GetOrCreateUseMultiplePaths().Enabled = ygot.Bool(true) bgp.GetOrCreatePeerGroup(cfgplugins.BGPPeerGroup1).GetOrCreateUseMultiplePaths().GetOrCreateEbgp().AllowMultipleAs = ygot.Bool(true) } - gEBGP := bgp.GetOrCreateGlobal().GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateUseMultiplePaths().GetOrCreateEbgp() - bgp.GetOrCreatePeerGroup(cfgplugins.BGPPeerGroup1).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateUseMultiplePaths().Enabled = ygot.Bool(true) - - if !deviations.SkipSettingAllowMultipleAS(bs.DUT) { - gEBGP.AllowMultipleAs = ygot.Bool(true) + if deviations.SkipAfiSafiPathForBgpMultipleAs(bs.DUT) { + var communitySetCLIConfig string + t.Log("AfiSafi Path For BgpMultipleAs is not supported") + gEBGP := bgp.GetOrCreateGlobal().GetOrCreateUseMultiplePaths().GetOrCreateEbgp() + gEBGPMP := bgp.GetOrCreateGlobal().GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateUseMultiplePaths().GetOrCreateEbgp() + gEBGPMP.MaximumPaths = ygot.Uint32(maxPaths) + if deviations.SkipSettingAllowMultipleAS(bs.DUT) { + gEBGP.AllowMultipleAs = ygot.Bool(false) + switch bs.DUT.Vendor() { + case ondatra.CISCO: + communitySetCLIConfig = fmt.Sprintf("router bgp %v instance BGP neighbor-group %v \n ebgp-recv-extcommunity-dmz \n ebgp-send-extcommunity-dmz\n", cfgplugins.DutAS, cfgplugins.BGPPeerGroup1) + default: + t.Fatalf("Unsupported vendor %s for deviation 'CommunityMemberRegexUnsupported'", bs.DUT.Vendor()) + } + helpers.GnmiCLIConfig(t, bs.DUT, communitySetCLIConfig) + } + } else { + t.Logf("AfiSafi Path For BgpMultipleAs is supported") + gEBGP := bgp.GetOrCreateGlobal().GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateUseMultiplePaths().GetOrCreateEbgp() + if !deviations.SkipSettingAllowMultipleAS(bs.DUT) { + gEBGP.AllowMultipleAs = ygot.Bool(true) + } } + bgp.GetOrCreatePeerGroup(cfgplugins.BGPPeerGroup1).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateUseMultiplePaths().Enabled = ygot.Bool(true) configureOTG(t, bs) bs.PushAndStart(t) @@ -172,6 +193,8 @@ func TestBGPSetup(t *testing.T) { hopGroup := gnmi.Get[*oc.NetworkInstance_Afts_NextHopGroup](t, bs.DUT, aftsPath.NextHopGroup(ipv4Entry.GetNextHopGroup()).State()) if got, want := len(hopGroup.NextHop), 2; got != want { t.Errorf("prefix: %s, found %d hops, want %d", ipv4Entry.GetPrefix(), got, want) + } else { + t.Logf("prefix: %s, found %d hops, want %d", ipv4Entry.GetPrefix(), got, want) } sleepTime := time.Duration(totalPackets/trafficPps) + 5 diff --git a/feature/bgp/multipath/otg_tests/bgp_multipath_wecmp_lbw_community_test/metadata.textproto b/feature/bgp/multipath/otg_tests/bgp_multipath_wecmp_lbw_community_test/metadata.textproto index 6d66dff7ed3..cba5b2008ac 100644 --- a/feature/bgp/multipath/otg_tests/bgp_multipath_wecmp_lbw_community_test/metadata.textproto +++ b/feature/bgp/multipath/otg_tests/bgp_multipath_wecmp_lbw_community_test/metadata.textproto @@ -11,6 +11,8 @@ platform_exceptions: { } deviations: { ipv4_missing_enabled: true + skip_setting_allow_multiple_as: true + skip_afi_safi_path_for_bgp_multiple_as: true } } platform_exceptions: { @@ -45,3 +47,4 @@ platform_exceptions: { } } tags: TAGS_DATACENTER_EDGE + diff --git a/feature/bgp/policybase/otg_tests/actions_med_localpref_prepend_flow_control/metadata.textproto b/feature/bgp/policybase/otg_tests/actions_med_localpref_prepend_flow_control/metadata.textproto index 04eec9a2434..24be17ffe8b 100644 --- a/feature/bgp/policybase/otg_tests/actions_med_localpref_prepend_flow_control/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/actions_med_localpref_prepend_flow_control/metadata.textproto @@ -5,7 +5,7 @@ uuid: "b4b2249f-9226-4555-9946-dc22cf6adae2" plan_id: "RT-1.32" description: "BGP policy actions - MED, LocPref, prepend, flow-control" testbed: TESTBED_DUT_ATE_2LINKS -tags: [TAGS_AGGREGATION, TAGS_DATACENTER_EDGE] +tags: [TAGS_DATACENTER_EDGE] platform_exceptions: { platform: { vendor: CISCO diff --git a/feature/bgp/policybase/otg_tests/aspath_and_community_test/README.md b/feature/bgp/policybase/otg_tests/aspath_and_community_test/README.md index 683d2dec7fe..8f434b0bcb4 100644 --- a/feature/bgp/policybase/otg_tests/aspath_and_community_test/README.md +++ b/feature/bgp/policybase/otg_tests/aspath_and_community_test/README.md @@ -126,3 +126,53 @@ import-policy * /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 + +### 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 match + /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/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: + + ## 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/defined-sets/bgp-defined-sets/community-sets/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.Set: + gNMI.Get: + gNMI.Subscribe: +``` + 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 4394d2c4248..de109463b72 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,6 +15,8 @@ package aspath_and_community_test import ( + "fmt" + "strconv" "testing" "time" @@ -22,6 +24,7 @@ import ( "github.com/openconfig/featureprofiles/internal/cfgplugins" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/helpers" "github.com/openconfig/featureprofiles/internal/otgutils" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" @@ -34,6 +37,8 @@ const ( trafficPps = 100 totalPackets = 1200 bgpName = "BGP" + ImpPolicy = "routing-policy-ASPATH-COMMUNITY" + RPLPermitAll = "PERMIT-ALL" ) var prefixesV4 = [][]string{ @@ -46,37 +51,68 @@ 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::32", "2048:db1:64:64::40"}, + {"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"}, } func TestMain(m *testing.M) { fptest.RunTests(m) } -func configureImportBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, ipv6 string) { - communitySetName := "path_and_community" - communityMatch := "10[0-9]:1" - commMatchSetOptions := oc.BgpPolicy_MatchSetOptionsType_ANY - aspathSetName := "any_my_3_comms" - aspathMatch := []string{"10[0-9]:1"} - aspMatchSetOptions := oc.RoutingPolicy_MatchSetOptionsType_ANY - +func configureImportBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, ipv6 string, aspathMatch []string, communityMatch string, aspathSetName string, communitySetName string, commMatchSetOptions oc.E_BgpPolicy_MatchSetOptionsType, aspMatchSetOptions oc.E_RoutingPolicy_MatchSetOptionsType) { root := &oc.Root{} rp := root.GetOrCreateRoutingPolicy() - pdef1 := rp.GetOrCreatePolicyDefinition("routePolicy") - stmt1, err := pdef1.AppendNewStatement("match_community") + pdef1 := rp.GetOrCreatePolicyDefinition(ImpPolicy) + stmt1, err := pdef1.AppendNewStatement("match_as_and_community") + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", "any_my_aspath", err) + } + if !deviations.SkipSettingStatementForPolicy(dut) { + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + } + + aspathSet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateAsPathSet(aspathSetName) + aspathSet.SetAsPathSetMember(aspathMatch) + stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchAsPathSet().SetAsPathSet(aspathSetName) + stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchAsPathSet().SetMatchSetOptions(aspMatchSetOptions) + pdAllow := rp.GetOrCreatePolicyDefinition(RPLPermitAll) + st, err := pdAllow.AppendNewStatement("id-1") if err != nil { - t.Fatalf("AppendNewStatement(%s) failed: %v", "match_community", err) + t.Fatalf("AppendNewStatement(%s) failed: %v", "routePolicyStatement", err) } + st.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) - stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + // stmt2, err := pdef1.AppendNewStatement("match_comms") + // if err != nil { + // t.Fatalf("AppendNewStatement(%s) failed: %v", "routePolicyStatement", err) + // } + if !deviations.SkipSettingStatementForPolicy(dut) { + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + } - communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(communitySetName) - communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(communityMatch)}) - communitySet.SetMatchSetOptions(commMatchSetOptions) + if !(deviations.CommunityMemberRegexUnsupported(dut) && communitySetName == "any_my_3_comms") { + communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(communitySetName) + cs := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} + if communityMatch != "" { + cs = append(cs, oc.UnionString(communityMatch)) + } + communitySet.SetCommunityMember(cs) + communitySet.SetMatchSetOptions(commMatchSetOptions) + } + + var communitySetCLIConfig string + if deviations.CommunityMemberRegexUnsupported(dut) && communitySetName == "any_my_3_comms" { + switch dut.Vendor() { + case ondatra.CISCO: + communitySetCLIConfig = fmt.Sprintf("community-set %v\n ios-regex '(10[0-9]:1)'\n end-set", communitySetName) + default: + t.Fatalf("Unsupported vendor %s for deviation 'CommunityMemberRegexUnsupported'", dut.Vendor()) + } + helpers.GnmiCLIConfig(t, dut, communitySetCLIConfig) + } if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(communitySetName) @@ -84,29 +120,27 @@ func configureImportBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(communitySetName) } - stmt2, err := pdef1.AppendNewStatement("match_as") - if err != nil { - t.Fatalf("AppendNewStatement(%s) failed: %v", "match_as", err) + if deviations.CommunityMemberRegexUnsupported(dut) && communitySetName == "any_my_3_comms" { + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + } else { + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) } - aspathSet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateAsPathSet(aspathSetName) - aspathSet.SetAsPathSetMember(aspathMatch) - stmt2.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchAsPathSet().SetAsPathSet(aspathSetName) - stmt2.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchAsPathSet().SetMatchSetOptions(aspMatchSetOptions) - - 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.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) - policyV6.SetImportPolicy([]string{"routePolicy"}) + if !deviations.DefaultRoutePolicyUnsupported(dut) { + policyV6.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } + policyV6.SetImportPolicy([]string{ImpPolicy}) 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.SetImportPolicy([]string{"routePolicy"}) + if !deviations.DefaultRoutePolicyUnsupported(dut) { + policyV4.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } + policyV4.SetImportPolicy([]string{ImpPolicy}) gnmi.Replace(t, dut, pathV4.Config(), policyV4) } @@ -142,18 +176,14 @@ func configureOTG(t *testing.T, bs *cfgplugins.BGPSession, prefixesV4 [][]string 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]) @@ -162,6 +192,10 @@ func configureOTG(t *testing.T, bs *cfgplugins.BGPSession, prefixesV4 [][]string asp4 := bgp4PeerRoute.AsPath().Segments().Add() asp4.SetAsNumbers(aspathMembers[index]) + 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]) @@ -186,24 +220,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) @@ -215,18 +244,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") @@ -253,26 +283,36 @@ func TestCommunitySet(t *testing.T) { 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() - - configureImportBGPPolicy(t, bs.DUT, ipv4, ipv6) + aspathMatch := []string{"(10[0-9]|200)"} + communityMatch := "(10[0-9]:1)" + communitySetName := "any_my_3_comms" + aspathSetName := "any_my_aspath" + commMatchSetOptions := oc.BgpPolicy_MatchSetOptionsType_ANY + aspMatchSetOptions := oc.RoutingPolicy_MatchSetOptionsType_ANY + configureImportBGPPolicy(t, bs.DUT, ipv4, ipv6, aspathMatch, communityMatch, aspathSetName, communitySetName, commMatchSetOptions, aspMatchSetOptions) sleepTime := time.Duration(totalPackets/trafficPps) + 5 for index, prefixPairV4 := range prefixesV4 { - + bs.ATETop.Flows().Clear() t.Logf("Running traffic test for IPv4 prefixes: [%s, %s]. Expected Result: [%t]", prefixPairV4[0], prefixPairV4[1], testResults[index]) - configureFlow(bs, prefixPairV4, "ipv4") + configureFlow(t, bs, prefixPairV4, "ipv4", index) + bs.PushAndStartATE(t) + time.Sleep(sleepTime * time.Second) 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), testResults[index]) + verifyTraffic(t, bs.ATE, "ipv4", testResults[index], index) + bs.ATETop.Flows().Clear() t.Logf("Running traffic test for IPv6 prefixes: [%s, %s]. Expected Result: [%t]", prefixesV6[index][0], prefixesV6[index][1], testResults[index]) - configureFlow(bs, prefixesV6[index], "ipv6") + configureFlow(t, bs, prefixesV6[index], "ipv6", index) + bs.PushAndStartATE(t) + time.Sleep(sleepTime * time.Second) 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), testResults[index]) + verifyTraffic(t, bs.ATE, "ipv6", testResults[index], index) } } diff --git a/feature/bgp/policybase/otg_tests/aspath_and_community_test/metadata.textproto b/feature/bgp/policybase/otg_tests/aspath_and_community_test/metadata.textproto index 9aa803a9f83..fc8bcf61ac6 100644 --- a/feature/bgp/policybase/otg_tests/aspath_and_community_test/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/aspath_and_community_test/metadata.textproto @@ -1,11 +1,12 @@ # proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto # proto-message: Metadata + uuid: "61f4c5b2-e188-4cc8-9b8b-c744660db2f0" plan_id: "RT-7.4" description: "BGP Policy AS Path Set and Community Set" testbed: TESTBED_DUT_ATE_2LINKS -tags: [TAGS_AGGREGATION, TAGS_TRANSIT, TAGS_DATACENTER_EDGE] +tags: [TAGS_TRANSIT, TAGS_DATACENTER_EDGE] platform_exceptions: { platform: { vendor: ARISTA @@ -21,3 +22,17 @@ platform_exceptions: { bgp_conditions_match_community_set_unsupported: true } } + +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + bgp_conditions_match_community_set_unsupported: true + skip_setting_statement_for_policy: true + default_import_export_policy_unsupported: true + community_member_regex_unsupported: true + default_route_policy_unsupported: true + } +} + diff --git a/feature/bgp/policybase/otg_tests/aspath_test/metadata.textproto b/feature/bgp/policybase/otg_tests/aspath_test/metadata.textproto index e994d73191b..722a02f9a02 100644 --- a/feature/bgp/policybase/otg_tests/aspath_test/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/aspath_test/metadata.textproto @@ -5,7 +5,7 @@ uuid: "f4387453-37e8-4c2b-99c3-24b13cc51fd1" plan_id: "RT-7.3" description: "BGP Policy AS Path Set" testbed: TESTBED_DUT_ATE_2LINKS -tags: [TAGS_AGGREGATION, TAGS_TRANSIT, TAGS_DATACENTER_EDGE] +tags: [TAGS_TRANSIT, TAGS_DATACENTER_EDGE] platform_exceptions: { platform: { vendor: ARISTA @@ -29,4 +29,4 @@ platform_exceptions: { community_member_regex_unsupported: true default_route_policy_unsupported: true } -} \ No newline at end of file +} 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 2f388556041..01cf548ea50 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 @@ -274,7 +274,7 @@ func configureImportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, operatio 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() - if !deviations.DefaultImportExportPolicy(dut) { + if !deviations.DefaultImportExportPolicyUnsupported(dut) { policy.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) } policy.SetImportPolicy([]string{v4PrefixPolicy, v4LPPolicy}) @@ -380,9 +380,19 @@ func configureExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, operatio 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() - if !deviations.DefaultImportExportPolicy(dut) { + if !deviations.DefaultImportExportPolicyUnsupported(dut) { policy.SetDefaultExportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) } + + // As Per RFC 8212, Routes contained in an Adj-RIB-In associated with an EBGP peer + // SHALL NOT be considered eligible in the Decision Process if no + // explicit Import Policy has been applied. + importPolPath := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(atePort2.IPv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() + eBGPPeerPolicy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(atePort2.IPv4).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateApplyPolicy() + if !deviations.DefaultImportExportPolicyUnsupported(dut) { + eBGPPeerPolicy.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE) + } + if deviations.FlattenPolicyWithMultipleStatements(dut) { policy.SetExportPolicy([]string{v4ASPPolicy}) } else { @@ -390,11 +400,14 @@ func configureExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, operatio } if deviations.SkipSettingStatementForPolicy(dut) { gnmi.Update(t, dut, path.Config(), policy) + gnmi.Update(t, dut, importPolPath.Config(), eBGPPeerPolicy) } else { if operation == "set" { gnmi.BatchReplace(batch, path.Config(), policy) + gnmi.BatchReplace(batch, importPolPath.Config(), eBGPPeerPolicy) } else if operation == "delete" { gnmi.BatchDelete(batch, path.Config()) + gnmi.BatchDelete(batch, importPolPath.Config()) } batch.Set(t, dut) } @@ -482,9 +495,10 @@ func configureImportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice, operat 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() - if !deviations.DefaultImportExportPolicy(dut) { + if !deviations.DefaultImportExportPolicyUnsupported(dut) { policy.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) } + policy.SetImportPolicy([]string{v6PrefixPolicy, v6LPPolicy}) if deviations.SkipSettingStatementForPolicy(dut) { gnmi.Update(t, dut, path.Config(), policy) @@ -586,9 +600,19 @@ func configureExportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice, operat 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() - if !deviations.DefaultImportExportPolicy(dut) { + if !deviations.DefaultImportExportPolicyUnsupported(dut) { policy.SetDefaultExportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) } + + // As Per RFC 8212, Routes contained in an Adj-RIB-In associated with an EBGP peer + // SHALL NOT be considered eligible in the Decision Process if no + // explicit Import Policy has been applied. + importPolPath := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(atePort2.IPv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy() + eBGPPeerPolicy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(atePort2.IPv6).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateApplyPolicy() + if !deviations.DefaultImportExportPolicyUnsupported(dut) { + eBGPPeerPolicy.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE) + } + if deviations.FlattenPolicyWithMultipleStatements(dut) { policy.SetExportPolicy([]string{v6ASPPolicy}) } else { @@ -596,11 +620,14 @@ func configureExportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice, operat } if deviations.SkipSettingStatementForPolicy(dut) { gnmi.Update(t, dut, path.Config(), policy) + gnmi.Update(t, dut, importPolPath.Config(), eBGPPeerPolicy) } else { if operation == "set" { gnmi.BatchReplace(batch, path.Config(), policy) + gnmi.BatchReplace(batch, importPolPath.Config(), eBGPPeerPolicy) } else if operation == "delete" { gnmi.BatchDelete(batch, path.Config()) + gnmi.BatchDelete(batch, importPolPath.Config()) } batch.Set(t, dut) } @@ -730,7 +757,7 @@ 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) - if deviations.DefaultImportExportPolicy(td.dut) { + if deviations.DefaultImportExportPolicyUnsupported(td.dut) { t.Logf("Configuring default route-policy for BGP on DUT") rp := root.GetOrCreateRoutingPolicy() pdef := rp.GetOrCreatePolicyDefinition("PERMIT-ALL") @@ -752,7 +779,7 @@ func (td *testData) advertiseRoutesWithEBGP(t *testing.T) { nV41.SetPeerAs(ateAS1) nV41.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) nV41.PeerGroup = ygot.String(peerGrpNamev4) - if deviations.DefaultImportExportPolicy(td.dut) { + if deviations.DefaultImportExportPolicyUnsupported(td.dut) { afisafiv41 := nV41.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) afisafiv41.GetOrCreateApplyPolicy().SetImportPolicy([]string{"PERMIT-ALL"}) afisafiv41.GetOrCreateApplyPolicy().SetExportPolicy([]string{"PERMIT-ALL"}) @@ -761,7 +788,7 @@ func (td *testData) advertiseRoutesWithEBGP(t *testing.T) { nV42.SetPeerAs(ateAS2) nV42.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) nV42.PeerGroup = ygot.String(peerGrpNamev4) - if deviations.DefaultImportExportPolicy(td.dut) { + if deviations.DefaultImportExportPolicyUnsupported(td.dut) { afisafiv42 := nV42.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) afisafiv42.GetOrCreateApplyPolicy().SetImportPolicy([]string{"PERMIT-ALL"}) afisafiv42.GetOrCreateApplyPolicy().SetExportPolicy([]string{"PERMIT-ALL"}) @@ -770,7 +797,7 @@ func (td *testData) advertiseRoutesWithEBGP(t *testing.T) { nV61.SetPeerAs(ateAS1) nV61.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) nV61.PeerGroup = ygot.String(peerGrpNamev6) - if deviations.DefaultImportExportPolicy(td.dut) { + if deviations.DefaultImportExportPolicyUnsupported(td.dut) { afisafiv61 := nV61.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) afisafiv61.GetOrCreateApplyPolicy().SetImportPolicy([]string{"PERMIT-ALL"}) afisafiv61.GetOrCreateApplyPolicy().SetExportPolicy([]string{"PERMIT-ALL"}) @@ -779,7 +806,7 @@ func (td *testData) advertiseRoutesWithEBGP(t *testing.T) { nV62.SetPeerAs(ateAS2) nV62.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) nV62.PeerGroup = ygot.String(peerGrpNamev6) - if deviations.DefaultImportExportPolicy(td.dut) { + if deviations.DefaultImportExportPolicyUnsupported(td.dut) { afisafiv62 := nV62.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) afisafiv62.GetOrCreateApplyPolicy().SetImportPolicy([]string{"PERMIT-ALL"}) afisafiv62.GetOrCreateApplyPolicy().SetExportPolicy([]string{"PERMIT-ALL"}) 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 2b3e7bedf9a..6dc207736c3 100644 --- a/feature/bgp/policybase/otg_tests/chained_policies_test/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/chained_policies_test/metadata.textproto @@ -15,7 +15,7 @@ platform_exceptions: { default_network_instance: "default" missing_value_for_defaults: true skip_set_rp_match_set_options: true - default_import_export_policy: false + default_import_export_policy_unsupported: false skip_setting_statement_for_policy: true } } @@ -33,7 +33,7 @@ platform_exceptions: { } deviations: { bgp_rib_oc_path_unsupported: true - default_import_export_policy: true + default_import_export_policy_unsupported: true skip_setting_statement_for_policy: true skip_checking_attribute_index: true flatten_policy_with_multiple_statements: 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 index c7b1ba2c0ed..c6f66a3165f 100644 --- 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 @@ -40,14 +40,16 @@ func TestMain(m *testing.M) { } const ( - trafficDuration = 1 * time.Minute - tolerancePct = 2 - peerGrpName = "BGP-PEER-GROUP" - dutAS = 65501 - ateAS = 65502 - plenIPv4 = 30 - plenIPv6 = 126 - acceptPolicy = "PERMIT-ALL" + trafficDuration = 1 * time.Minute + tolerancePct = 2 + peerGrpName = "BGP-PEER-GROUP" + dutAS = 65501 + ateAS = 65502 + plenIPv4 = 30 + plenIPv6 = 126 + acceptPolicy = "PERMIT-ALL" + matchStdCommunitySet = "match_std_comms" + addStdCommunitySet = "add_std_comms" ) var ( @@ -119,11 +121,11 @@ var ( communitySets = []communitySet{ { - name: "match_std_comms", + name: matchStdCommunitySet, members: []string{"5:5"}, }, { - name: "add_std_comms", + name: addStdCommunitySet, members: []string{"10:10", "20:20", "30:30"}, }, } @@ -197,6 +199,9 @@ func bgpCreateNbr(localAs, peerAs uint32, dut *ondatra.DUTDevice) *oc.NetworkIns pg := bgp.GetOrCreatePeerGroup(peerGrpName) pg.PeerAs = ygot.Uint32(ateAS) pg.PeerGroupName = ygot.String(peerGrpName) + if !deviations.SkipBgpSendCommunityType(dut) { + pg.SetSendCommunityType([]oc.E_Bgp_CommunityType{oc.Bgp_CommunityType_STANDARD}) + } 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) @@ -266,10 +271,10 @@ func configureCommunitySet(t *testing.T, dut *ondatra.DUTDevice, communitySet co } func configureRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, policyName string, nbr *bgpNeighbor, pgName string) { + addStdCommunitySetRefs := []string{addStdCommunitySet} d := &oc.Root{} rp := d.GetOrCreateRoutingPolicy() batchConfig := &gnmi.SetBatch{} - var pdef *oc.RoutingPolicy_PolicyDefinition switch policyName { @@ -277,7 +282,11 @@ func configureRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, policyName str pdef = rp.GetOrCreatePolicyDefinition(policyName) stmt1, _ := pdef.AppendNewStatement("add_std_comms") sc := stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetCommunity() - sc.GetOrCreateReference().SetCommunitySetRef("add_std_comms") + if deviations.BgpCommunitySetRefsUnsupported(dut) { + sc.GetOrCreateReference().SetCommunitySetRef(addStdCommunitySet) + } else { + sc.GetOrCreateReference().SetCommunitySetRefs(addStdCommunitySetRefs) + } sc.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) if !deviations.BgpActionsSetCommunityMethodUnsupported(dut) { sc.SetMethod(oc.SetCommunity_Method_REFERENCE) @@ -289,18 +298,22 @@ func configureRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, policyName str pdef = rp.GetOrCreatePolicyDefinition(policyName) stmt1, _ := pdef.AppendNewStatement("match_and_add_std_comms") if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { - stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet("match_std_comms") + stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(matchStdCommunitySet) ds := rp.GetOrCreateDefinedSets() - cs := ds.GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet("match_std_comms") + cs := ds.GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(matchStdCommunitySet) 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.SetCommunitySet(matchStdCommunitySet) cs.SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsType_ANY) } sc := stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetCommunity() - sc.GetOrCreateReference().SetCommunitySetRef("add_std_comms") + if deviations.BgpCommunitySetRefsUnsupported(dut) { + sc.GetOrCreateReference().SetCommunitySetRef(addStdCommunitySet) + } else { + sc.GetOrCreateReference().SetCommunitySetRefs(addStdCommunitySetRefs) + } sc.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) if !deviations.BgpActionsSetCommunityMethodUnsupported(dut) { sc.SetMethod(oc.SetCommunity_Method_REFERENCE) @@ -528,7 +541,7 @@ func validateDutIPv4PrefixCommunitySet(t *testing.T, dut *ondatra.DUTDevice, bgp 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 + // TODO Validate Community for ipv4 prefixes on DUT } func validateATEIPv6PrefixCommunitySet(t *testing.T, ate *ondatra.ATEDevice, bgpPeerName, subnet string, wantCommunitySet []string) { @@ -575,7 +588,7 @@ func validateDutIPv6PrefixCommunitySet(t *testing.T, dut *ondatra.DUTDevice, bgp 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 + // TODO Validate Community for ipv6 prefixes on DUT } type TestResults struct { 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 d2237552f41..b6d656dc70e 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 @@ -15,6 +15,8 @@ platform_exceptions: { interface_enabled: true bgp_conditions_match_community_set_unsupported: true bgp_actions_set_community_method_unsupported: true + skip_bgp_send_community_type: true + bgp_community_set_refs_unsupported: true } } platform_exceptions: { @@ -22,8 +24,10 @@ platform_exceptions: { vendor: ARISTA } deviations: { + omit_l2_mtu: true interface_enabled: true default_network_instance: "default" + bgp_conditions_match_community_set_unsupported: true } } tags: TAGS_AGGREGATION diff --git a/feature/bgp/policybase/otg_tests/community_test/metadata.textproto b/feature/bgp/policybase/otg_tests/community_test/metadata.textproto index 0b6e57c3804..e30e35b5e7a 100644 --- a/feature/bgp/policybase/otg_tests/community_test/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/community_test/metadata.textproto @@ -5,7 +5,7 @@ uuid: "4330effe-a20c-42fb-8372-80fb0901a325" plan_id: "RT-7.2" description: "BGP Policy Community Set" testbed: TESTBED_DUT_ATE_2LINKS -tags: [TAGS_AGGREGATION, TAGS_TRANSIT, TAGS_DATACENTER_EDGE] +tags: [TAGS_TRANSIT, TAGS_DATACENTER_EDGE] platform_exceptions: { platform: { vendor: ARISTA @@ -29,4 +29,15 @@ platform_exceptions: { bgp_conditions_match_community_set_unsupported: true community_member_regex_unsupported: true } -} \ No newline at end of file +} +platform_exceptions: { + platform: { + vendor: NOKIA + } + deviations: { + explicit_interface_in_default_vrf: true + interface_enabled: true + bgp_conditions_match_community_set_unsupported: true + } +} + diff --git a/feature/bgp/policybase/otg_tests/default_policies_test/README.md b/feature/bgp/policybase/otg_tests/default_policies_test/README.md index eb0d2ead415..f339dfefee3 100644 --- a/feature/bgp/policybase/otg_tests/default_policies_test/README.md +++ b/feature/bgp/policybase/otg_tests/default_policies_test/README.md @@ -45,7 +45,7 @@ B <-- IBGP+IS-IS --> C[Port2:OTG]; * IS-IS and static routes shouldn't be advertised to the EBGP and IBGP peers. ### RT-7.1.2 : Policy definition in policy chain is not satisfied and Default Policy has ACCEPT_ROUTE action - * Continue with the same configuration as RT-7.1 + * Continue with the same configuration as RT-7.1.1 * Replace the default-policy REJECT-ALL with default-policy ACCEPT-ALL which has action ACCEPT_ROUTE. * Ensure ACCEPT-ALL default-policy is applied to both IPv4-unicast and IPv6-unicast AFI-SAFI of both IBGP and EBGP peers * Following test expectations. If expectations not met, the test should fail. @@ -56,7 +56,7 @@ B <-- IBGP+IS-IS --> C[Port2:OTG]; * IS-IS and static routes shouldn't be advertised to the EBGP and IBGP peers. ### RT-7.1.3 : No policy attached either at the Peer-group or at the neighbor level and Default Policy has ACCEPT_ROUTE action - * Continue with the same configuration as RT-7.2. However, do not attach any non-default import/export policies to the peers at either the peer-group or neighbor levels. + * Continue with the same configuration as RT-7.1.2. However, do not attach any non-default import/export policies to the peers at either the peer-group or neighbor levels. * Ensure that the ACCEPT-ALL default-policy with default action of ACCEPT_ROUTE is appled to both IPv4-unicast and IPv6-unicast AFI-SAFI of both IBGP and EBGP peers * Following test expectations. If expectations not met, the test should fail. * DUT:Port1 should accept import of IPv4-prefix1, IPv4-prefix2, IPv4-prefix3, IPv6-prefix1, IPv6-prefix2 and IPv6-prefix3 @@ -66,7 +66,7 @@ B <-- IBGP+IS-IS --> C[Port2:OTG]; * IS-IS and static routes shouldn't be advertised to the EBGP and IBGP peers. ### RT-7.1.4 : No policy attached either at the Peer-group or at the neighbor level and Default Policy has REJECT_ROUTE action - * Continue with the same configuration as RT-7.3. Ensure no non-default import/export policies are applied to the peers at either the peer-group or neighbor levels. + * Continue with the same configuration as RT-7.1.3. Ensure no non-default import/export policies are applied to the peers at either the peer-group or neighbor levels. * Ensure that only the REJECT-ALL default-policy with default action of REJECT_ROUTE is appled to both IPv4-unicast and IPv6-unicast AFI-SAFI of both IBGP and EBGP peers * Following test expectations. If expectations not met, the test should fail. * DUT:Port1 should reject import of IPv4-prefix1, IPv4-prefix2, IPv4-prefix3, IPv6-prefix1, IPv6-prefix2 and IPv6-prefix3 @@ -77,7 +77,7 @@ B <-- IBGP+IS-IS --> C[Port2:OTG]; ### RT-7.1.5 : No policy, including the default-policy is attached either at the Peer-group or at the neighbor level for only IBGP peer #### TODO: RT-7.5 should be automated only after the expected behavior is confirmed in https://github.com/openconfig/public/issues/981 - * Continue with the same configuration as RT-7.4. However, do not attach any non-default OR default import/export policies to the IBGP peer at the peer-group or neighbor levels. This is true for both IPv4-unicast and IPv6-unicast AFI-SAFI. + * Continue with the same configuration as RT-7.1.4. However, do not attach any non-default OR default import/export policies to the IBGP peer at the peer-group or neighbor levels. This is true for both IPv4-unicast and IPv6-unicast AFI-SAFI. * Ensure that only the ACCEPT-ALL IMPORT/EXPORT default-policy with default action of ACCEPT_ROUTE is appled to the EBGP peer on both IPv4-unicast and IPv6-unicast AFI-SAFI * Following test expectations. If expectations not met, the test should fail. * DUT:Port1 should accept import of IPv4-prefix1, IPv4-prefix2, IPv4-prefix3, IPv6-prefix1, IPv6-prefix2 and IPv6-prefix3 @@ -88,7 +88,7 @@ B <-- IBGP+IS-IS --> C[Port2:OTG]; ### RT-7.1.6 : No policy, including the default-policy is attached either at the Peer-group or at the neighbor level for both EBGP and IBGP peers #### TODO: RT-7.1.6 should be automated only after the expected behavior is confirmed in https://github.com/openconfig/public/issues/981 - * Continue with the same configuration as RT-7.5. However, do not attach any non-default OR default import/export policies to the IBGP and EBGP peers at the peer-group or neighbor levels. This is true for both IPv4-unicast and IPv6-unicast AFI-SAFI. + * Continue with the same configuration as RT-7.1.5. However, do not attach any non-default OR default import/export policies to the IBGP and EBGP peers at the peer-group or neighbor levels. This is true for both IPv4-unicast and IPv6-unicast AFI-SAFI. * Following test expectations. If expectations not met, the test should fail. * DUT:Port1 should reject import of IPv4-prefix1, IPv4-prefix2, IPv4-prefix3, IPv6-prefix1, IPv6-prefix2 and IPv6-prefix3 * DUT:Port1 should reject export of IPv4-prefix4, IPv4-prefix5, IPv4-prefix6, IPv6-prefix4, IPv6-prefix5 and IPv6-prefix6 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 b2e084548b3..60178b91633 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 @@ -39,54 +39,54 @@ func TestMain(m *testing.M) { } const ( - peerGrpName1 = "BGP-PEER-GROUP1" - peerGrpName2 = "BGP-PEER-GROUP2" - peerGrpName3 = "BGP-PEER-GROUP3" - peerGrpName4 = "BGP-PEER-GROUP4" - dutAS = 65501 - ateAS = 65502 - plenIPv4 = 30 - plenIPv6 = 126 - dutAreaAddress = "49.0001" - dutSysID = "1920.0000.2001" - otgSysID2 = "640000000001" - isisInstance = "DEFAULT" - otgIsisPort2LoopV4 = "203.0.113.10" - otgIsisPort2LoopV6 = "2001:db8::203:0:113:10" - v4Prefixes = true - rejectAll = "REJECT-ALL" - rejectRoute = oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE - acceptRoute = oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE - ebgpImportIPv4 = "EBGP-IMPORT-IPV4" - ebgpImportIPv6 = "EBGP-IMPORT-IPV6" - ebgpExportIPv4 = "EBGP-EXPORT-IPV4" - ebgpExportIPv6 = "EBGP-EXPORT-IPV6" - ibgpImportIPv4 = "IBGP-IMPORT-IPV4" - ibgpImportIPv6 = "IBGP-IMPORT-IPV6" - ibgpExportIPv4 = "IBGP-EXPORT-IPV4" - ibgpExportIPv6 = "IBGP-EXPORT-IPV6" - maskLengthRange32 = "32..32" - maskLengthRange128 = "128..128" - maskLen32 = "32" - maskLen128 = "128" - ipv4Prefix1 = "198.51.100.1" - ipv4Prefix2 = "198.51.100.2" - ipv4Prefix3 = "198.51.100.3" - ipv4Prefix4 = "198.51.100.4" - ipv4Prefix5 = "198.51.100.5" - ipv4Prefix6 = "198.51.100.6" - ipv4Prefix7 = "198.51.100.7" - ipv4Prefix8 = "198.51.100.8" - ipv6Prefix1 = "2001:DB8:2::1" - ipv6Prefix2 = "2001:DB8:2::2" - ipv6Prefix3 = "2001:DB8:2::3" - ipv6Prefix4 = "2001:DB8:2::4" - ipv6Prefix5 = "2001:DB8:2::5" - ipv6Prefix6 = "2001:DB8:2::6" - ipv6Prefix7 = "2001:DB8:2::7" - ipv6Prefix8 = "2001:DB8:2::8" - maskLenExact = "exact" - replacePolicy = true + peerGrpName1 = "BGP-PEER-GROUP1" + peerGrpName2 = "BGP-PEER-GROUP2" + peerGrpName3 = "BGP-PEER-GROUP3" + peerGrpName4 = "BGP-PEER-GROUP4" + dutAS = 65501 + ateAS = 65502 + plenIPv4 = 30 + plenIPv6 = 126 + dutAreaAddress = "49.0001" + dutSysID = "1920.0000.2001" + otgSysID2 = "640000000001" + isisInstance = "DEFAULT" + otgIsisPort2LoopV4 = "203.0.113.10" + otgIsisPort2LoopV6 = "2001:db8::203:0:113:10" + v4Prefixes = true + rejectAll = "REJECT-ALL" + rejectRoute = oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE + acceptRoute = oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE + ebgpImportIPv4 = "EBGP-IMPORT-IPV4" + ebgpImportIPv6 = "EBGP-IMPORT-IPV6" + ebgpExportIPv4 = "EBGP-EXPORT-IPV4" + ebgpExportIPv6 = "EBGP-EXPORT-IPV6" + ibgpImportIPv4 = "IBGP-IMPORT-IPV4" + ibgpImportIPv6 = "IBGP-IMPORT-IPV6" + ibgpExportIPv4 = "IBGP-EXPORT-IPV4" + ibgpExportIPv6 = "IBGP-EXPORT-IPV6" + maskLengthRange32 = "32..32" + maskLengthRange128 = "128..128" + maskLen32 = "32" + maskLen128 = "128" + ipv4Prefix1 = "198.51.100.1" + ipv4Prefix2 = "198.51.100.2" + ipv4Prefix3 = "198.51.100.3" + ipv4Prefix4 = "198.51.100.4" + ipv4Prefix5 = "198.51.100.5" + ipv4Prefix6 = "198.51.100.6" + ipv4Prefix7 = "198.51.100.7" + ipv4Prefix8 = "198.51.100.8" + ipv6Prefix1 = "2001:DB8:2::1" + ipv6Prefix2 = "2001:DB8:2::2" + ipv6Prefix3 = "2001:DB8:2::3" + ipv6Prefix4 = "2001:DB8:2::4" + ipv6Prefix5 = "2001:DB8:2::5" + ipv6Prefix6 = "2001:DB8:2::6" + ipv6Prefix7 = "2001:DB8:2::7" + ipv6Prefix8 = "2001:DB8:2::8" + maskLenExact = "exact" + defaultStatementOnly = true ) var ( @@ -426,9 +426,9 @@ func configureOTG(t *testing.T, otg *otg.OTG) { t.Logf("Pushing config to OTG and starting protocols...") otg.PushConfig(t, config) - time.Sleep(30 * time.Second) + time.Sleep(40 * time.Second) otg.StartProtocols(t) - time.Sleep(30 * time.Second) + time.Sleep(40 * time.Second) } func verifyBGPCapabilities(t *testing.T, dut *ondatra.DUTDevice) { @@ -527,11 +527,15 @@ 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) + isisIntfLevelAfi6 := isisIntfLevel.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV6, oc.IsisTypes_SAFI_TYPE_UNICAST) + isisIntfLevelAfi6.Metric = ygot.Uint32(200) + isisIntfLevelAfi6.Enabled = ygot.Bool(true) if deviations.ISISInterfaceAfiUnsupported(dut) { isisIntf.Af = nil } if deviations.MissingIsisInterfaceAfiSafiEnable(dut) { isisIntfLevelAfi.Enabled = nil + isisIntfLevelAfi6.Enabled = nil } } gnmi.Replace(t, dut, dutConfIsisPath.Config(), prot) @@ -587,26 +591,77 @@ func deleteBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, nbrList []*bgpNbrList } } -func configureRoutingPolicyDefaultAction(t *testing.T, dut *ondatra.DUTDevice, action string, replace bool) { +func configurePrefixMatchAndDefaultStatement(t *testing.T, dut *ondatra.DUTDevice, prefixSet, prefixSubnetRange, maskLen string, ipPrefixSet []string, action string, defaultStatementOnly bool) *oc.RoutingPolicy { d := &oc.Root{} rp := d.GetOrCreateRoutingPolicy() - for _, policy := range []string{ebgpExportIPv4, ebgpExportIPv6, ibgpExportIPv4, ibgpExportIPv6, ebgpImportIPv4, ebgpImportIPv6, ibgpImportIPv4, ibgpImportIPv6} { - pdef := rp.GetOrCreatePolicyDefinition(policy) - stmt, err := pdef.AppendNewStatement("50") + if !defaultStatementOnly { + pset := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet(prefixSet) + for _, pref := range ipPrefixSet { + pset.GetOrCreatePrefix(pref+"/"+maskLen, prefixSubnetRange) + mode := oc.PrefixSet_Mode_IPV4 + if maskLen == maskLen128 { + mode = oc.PrefixSet_Mode_IPV6 + } + if !deviations.SkipPrefixSetMode(dut) { + pset.SetMode(mode) + } + } + } + + pdef := rp.GetOrCreatePolicyDefinition(prefixSet) + if !defaultStatementOnly { + stmt1, err := pdef.AppendNewStatement("10") if err != nil { t.Fatal(err) } - stmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE - if action == "reject" { - stmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_REJECT_ROUTE - } + stmt1.GetOrCreateConditions().GetOrCreateMatchPrefixSet().PrefixSet = ygot.String(prefixSet) + stmt1.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE } - if replace { - gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) - } else { - gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + stmt2, err := pdef.AppendNewStatement("50") + if err != nil { + t.Fatal(err) } - time.Sleep(5 * time.Second) + stmt2.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE + if action == "reject" { + stmt2.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_REJECT_ROUTE + } + + return rp +} + +func configureRoutingPolicyDefaultAction(t *testing.T, dut *ondatra.DUTDevice, action string, defaultStatementOnly bool) { + t.Helper() + batchConfig := &gnmi.SetBatch{} + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + + t.Logf("Delete prefix set policies") + deleteBGPPolicy(t, dut, []*bgpNbrList{ebgpNbrV4, ebgpNbrV6, ibgpNbrV4, ibgpNbrV6}) + gnmi.BatchDelete(batchConfig, gnmi.OC().RoutingPolicy().Config()) + batchConfig.Set(t, dut) + time.Sleep(20 * time.Second) + + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ebgpImportIPv4, maskLenExact, maskLen32, []string{ipv4Prefix1, ipv4Prefix2}, action, defaultStatementOnly)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ebgpImportIPv6, maskLenExact, maskLen128, []string{ipv6Prefix1, ipv6Prefix2}, action, defaultStatementOnly)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ebgpExportIPv4, maskLenExact, maskLen32, []string{ipv4Prefix4}, action, defaultStatementOnly)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ebgpExportIPv6, maskLenExact, maskLen128, []string{ipv6Prefix4}, action, defaultStatementOnly)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ibgpImportIPv4, maskLenExact, maskLen32, []string{ipv4Prefix4, ipv4Prefix5}, action, defaultStatementOnly)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ibgpImportIPv6, maskLenExact, maskLen128, []string{ipv6Prefix4, ipv6Prefix5}, action, defaultStatementOnly)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ibgpExportIPv4, maskLenExact, maskLen32, []string{ipv4Prefix1}, action, defaultStatementOnly)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ibgpExportIPv6, maskLenExact, maskLen128, []string{ipv6Prefix1}, action, defaultStatementOnly)) + + // Apply the above policies to the respective peering at the respective AFI-SAFI levels + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ebgpNbrV4.nbrAddr).AfiSafi(ebgpNbrV4.afiSafi).ApplyPolicy().ImportPolicy().Config(), []string{ebgpImportIPv4}) + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ebgpNbrV4.nbrAddr).AfiSafi(ebgpNbrV4.afiSafi).ApplyPolicy().ExportPolicy().Config(), []string{ebgpExportIPv4}) + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ebgpNbrV6.nbrAddr).AfiSafi(ebgpNbrV6.afiSafi).ApplyPolicy().ImportPolicy().Config(), []string{ebgpImportIPv6}) + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ebgpNbrV6.nbrAddr).AfiSafi(ebgpNbrV6.afiSafi).ApplyPolicy().ExportPolicy().Config(), []string{ebgpExportIPv6}) + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ibgpNbrV4.nbrAddr).AfiSafi(ibgpNbrV4.afiSafi).ApplyPolicy().ImportPolicy().Config(), []string{ibgpImportIPv4}) + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ibgpNbrV4.nbrAddr).AfiSafi(ibgpNbrV4.afiSafi).ApplyPolicy().ExportPolicy().Config(), []string{ibgpExportIPv4}) + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ibgpNbrV6.nbrAddr).AfiSafi(ibgpNbrV6.afiSafi).ApplyPolicy().ImportPolicy().Config(), []string{ibgpImportIPv6}) + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ibgpNbrV6.nbrAddr).AfiSafi(ibgpNbrV6.afiSafi).ApplyPolicy().ExportPolicy().Config(), []string{ibgpExportIPv6}) + + batchConfig.Set(t, dut) + + time.Sleep(20 * time.Second) } func testDefaultPolicyRejectRouteAction(t *testing.T, dut *ondatra.DUTDevice) { @@ -614,7 +669,7 @@ func testDefaultPolicyRejectRouteAction(t *testing.T, dut *ondatra.DUTDevice) { t.Run("Create and apply default-policy REJECT-ALL with action as REJECT_ROUTE", func(t *testing.T) { if deviations.BgpDefaultPolicyUnsupported(dut) { - configureRoutingPolicyDefaultAction(t, dut, "reject", !replacePolicy) + configureRoutingPolicyDefaultAction(t, dut, "reject", !defaultStatementOnly) } else { configureBGPDefaultPolicy(t, dut, rejectRoute) } @@ -638,7 +693,7 @@ func testDefaultPolicyAcceptRouteAction(t *testing.T, dut *ondatra.DUTDevice) { t.Helper() t.Run("Create and apply default-policy ACCEPT-ALL with action as ACCEPT_ROUTE", func(t *testing.T) { if deviations.BgpDefaultPolicyUnsupported(dut) { - configureRoutingPolicyDefaultAction(t, dut, "accept", !replacePolicy) + configureRoutingPolicyDefaultAction(t, dut, "accept", !defaultStatementOnly) } else { configureBGPDefaultPolicy(t, dut, acceptRoute) } @@ -663,7 +718,7 @@ func testDefaultPolicyAcceptRouteActionOnly(t *testing.T, dut *ondatra.DUTDevice t.Run("Create and apply default-policy ACCEPT-ALL with action as ACCEPT_ROUTE", func(t *testing.T) { if deviations.BgpDefaultPolicyUnsupported(dut) { - configureRoutingPolicyDefaultAction(t, dut, "accept", replacePolicy) + configureRoutingPolicyDefaultAction(t, dut, "accept", defaultStatementOnly) } else { configureBGPDefaultPolicy(t, dut, acceptRoute) t.Run("Delete prefix set policies", func(t *testing.T) { @@ -703,7 +758,7 @@ func testDefaultPolicyRejectRouteActionOnly(t *testing.T, dut *ondatra.DUTDevice t.Run("Create and apply default-policy REJECT-ALL with action as REJECT_ROUTE", func(t *testing.T) { if deviations.BgpDefaultPolicyUnsupported(dut) { - configureRoutingPolicyDefaultAction(t, dut, "reject", replacePolicy) + configureRoutingPolicyDefaultAction(t, dut, "reject", defaultStatementOnly) } else { configureBGPDefaultPolicy(t, dut, rejectRoute) } @@ -802,14 +857,14 @@ func verifyPostPolicyPrefixTelemetry(t *testing.T, dut *ondatra.DUTDevice, nbr * } if !deviations.MissingPrePolicyReceivedRoutes(dut) { - if gotRxPrePol, ok := gnmi.Watch(t, dut, afiSafiPath.Prefixes().ReceivedPrePolicy().State(), 10*time.Second, func(val *ygnmi.Value[uint32]) bool { + if gotRxPrePol, ok := gnmi.Watch(t, dut, afiSafiPath.Prefixes().ReceivedPrePolicy().State(), 20*time.Second, func(val *ygnmi.Value[uint32]) bool { gotRxPrePol, ok := val.Val() return ok && gotRxPrePol == nbr.wantRxPrePolicy }).Await(t); !ok { t.Errorf("Received pre policy prefixes mismatch: got %v, want %v", gotRxPrePol, nbr.wantRxPrePolicy) } } - if gotRx, ok := gnmi.Watch(t, dut, afiSafiPath.Prefixes().Received().State(), 10*time.Second, func(val *ygnmi.Value[uint32]) bool { + if gotRx, ok := gnmi.Watch(t, dut, afiSafiPath.Prefixes().Received().State(), 20*time.Second, func(val *ygnmi.Value[uint32]) bool { gotRx, ok := val.Val() return ok && gotRx == nbr.wantRx }).Await(t); !ok { @@ -907,7 +962,7 @@ func TestBGPDefaultPolicies(t *testing.T) { }) t.Run("Verify prefix telemetry on DUT for all iBGP and eBGP peers", func(t *testing.T) { - if deviations.DefaultImportExportPolicy(dut) { + if deviations.DefaultImportExportPolicyUnsupported(dut) { verifyPrefixesTelemetry(t, dut, atePort1.IPv4, 3, 3, 3, v4Prefixes) verifyPrefixesTelemetry(t, dut, otgIsisPort2LoopV4, 3, 3, 3, v4Prefixes) verifyPrefixesTelemetry(t, dut, atePort1.IPv6, 3, 3, 3, !v4Prefixes) 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 252c462d193..21a048a2b80 100644 --- a/feature/bgp/policybase/otg_tests/default_policies_test/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/default_policies_test/metadata.textproto @@ -12,7 +12,6 @@ platform_exceptions: { deviations: { isis_level_enabled: true skip_non_bgp_route_export_check: true - default_import_export_policy: true } } platform_exceptions: { @@ -25,7 +24,7 @@ platform_exceptions: { missing_isis_interface_afi_safi_enable: true isis_interface_afi_unsupported: true isis_instance_enabled_required: true - default_import_export_policy: true + default_import_export_policy_unsupported: true bgp_default_policy_unsupported: true } } @@ -41,5 +40,16 @@ platform_exceptions: { skip_non_bgp_route_export_check: true missing_isis_interface_afi_safi_enable: true bgp_default_policy_unsupported: true + skip_prefix_set_mode: true + } +} +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + missing_isis_interface_afi_safi_enable: true + bgp_default_policy_unsupported: true + prepolicy_received_routes: true } } diff --git a/feature/bgp/policybase/otg_tests/import_export_multi_test/import_export_multi_test.go b/feature/bgp/policybase/otg_tests/import_export_multi_test/import_export_multi_test.go index fcc29de6e15..0e20c80a60c 100644 --- a/feature/bgp/policybase/otg_tests/import_export_multi_test/import_export_multi_test.go +++ b/feature/bgp/policybase/otg_tests/import_export_multi_test/import_export_multi_test.go @@ -16,7 +16,9 @@ package import_export_multi_test import ( + "fmt" "strconv" + "strings" "testing" "time" @@ -24,10 +26,14 @@ import ( "github.com/openconfig/featureprofiles/internal/cfgplugins" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/helpers" "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" ) const ( @@ -35,9 +41,13 @@ const ( prefixV6Len = 126 trafficPps = 100 totalPackets = 1200 - bgpName = "BGP" - medValue = 100 localPref = 5 + medValue = 100 + bgpName = "BGP" + otglocalPref = "local-pref" + otgMED = "med" + otgASPath = "as-path" + otgCommunity = "community" parentPolicy = "multiPolicy" callPolicy = "match_community_regex" rejectStatement = "reject_route_community" @@ -94,19 +104,50 @@ var communityMembers = [][][]int{ {20, 2}, {30, 3}, }, { - {40, 1}, {41, 1}, + {40, 1}, {50, 1}, }, { {50, 1}, {51, 1}, }, } +var communityReceived [][][]int + +type bgpNbrList struct { + nbrAddr string + afiSafi oc.E_BgpTypes_AFI_SAFI_TYPE +} + // TestMain triggers the test run func TestMain(m *testing.M) { fptest.RunTests(m) } +func deleteBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, nbrList []*bgpNbrList) { + t.Helper() + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + for _, nbr := range nbrList { + nbrAfiSafiPath := bgpPath.Neighbor(nbr.nbrAddr).AfiSafi(nbr.afiSafi) + b := &gnmi.SetBatch{} + gnmi.BatchDelete(b, nbrAfiSafiPath.ApplyPolicy().ImportPolicy().Config()) + gnmi.BatchDelete(b, nbrAfiSafiPath.ApplyPolicy().ExportPolicy().Config()) + b.Set(t, dut) + } +} + func configureImportExportAcceptAllBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, ipv6 string) { + // Delete PERMIT-ALL policy applied to neighbor + deleteBGPPolicy(t, dut, []*bgpNbrList{ + { + nbrAddr: ipv4, + afiSafi: oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, + }, + { + nbrAddr: ipv6, + afiSafi: oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST, + }, + }) + root := &oc.Root{} rp := root.GetOrCreateRoutingPolicy() pdef1 := rp.GetOrCreatePolicyDefinition("routePolicy") @@ -132,7 +173,7 @@ func configureImportExportAcceptAllBGPPolicy(t *testing.T, dut *ondatra.DUTDevic gnmi.Replace(t, dut, pathV4.Config(), policyV4) } -func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, ipv6 string) { +func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, ipv6 string, ipv41 string, ipv61 string) { rejectCommunities := []string{"10:1"} acceptCommunities := []string{"20:1"} regexCommunities := []string{"^30:.*$"} @@ -159,16 +200,29 @@ func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ond } // Configure regex_community:["^30:.*$"] to match_community_regex statement - communitySetRegex := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(regexCommunitySet) + if !(deviations.CommunityMemberRegexUnsupported(dut)) { + 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) + } - pd2cs1 := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} - for _, commMatchPd2Cs1 := range regexCommunities { - if commMatchPd2Cs1 != "" { - pd2cs1 = append(pd2cs1, oc.UnionString(commMatchPd2Cs1)) + var communitySetCLIConfig string + if deviations.CommunityMemberRegexUnsupported(dut) { + switch dut.Vendor() { + case ondatra.CISCO: + communitySetCLIConfig = fmt.Sprintf("community-set %v\n ios-regex '(%v)'\n end-set", regexCommunitySet, regexCommunities[0]) + default: + t.Fatalf("Unsupported vendor %s for deviation 'CommunityMemberRegexUnsupported'", dut.Vendor()) } + helpers.GnmiCLIConfig(t, dut, communitySetCLIConfig) } - communitySetRegex.SetCommunityMember(pd2cs1) - communitySetRegex.SetMatchSetOptions(matchAny) if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { pd2stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(regexCommunitySet) @@ -176,7 +230,9 @@ func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ond pd2stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(regexCommunitySet) } - pd2stmt1.GetOrCreateActions().SetPolicyResult(nextstatementResult) + if !deviations.SkipSettingStatementForPolicy(dut) { + pd2stmt1.GetOrCreateActions().SetPolicyResult(nextstatementResult) + } // Configure the parent policy multi_policy. @@ -276,13 +332,8 @@ func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ond stmt3.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetCommunity().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) + if !deviations.SkipSettingStatementForPolicy(dut) { + stmt3.GetOrCreateActions().SetPolicyResult(nextstatementResult) } // Configure my_community: [ "50:1" ] to match_comm_and_prefix_add_2_community_sets statement @@ -297,15 +348,30 @@ func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ond communitySetMatchCommPrefixAddCommu.SetCommunityMember(cs4) communitySetMatchCommPrefixAddCommu.SetMatchSetOptions(matchAny) + // 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) + } + stmt6, err := pdef1.AppendNewStatement(matchCommPrefixAddCommuStatement + "_V6") + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", matchCommPrefixAddCommuStatement, err) + } + if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { stmt4.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(myCommunitySet) + stmt6.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(myCommunitySet) } else { stmt4.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(myCommunitySet) + stmt6.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) + stmt6.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetPrefixSet(prefixSetName + "_V6") + stmt6.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetMatchSetOptions(prefixSetNameSetOptions) pset := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet(prefixSetName) pset.GetOrCreatePrefix(prefixesV4[4][0]+"/29", "29..30") @@ -329,11 +395,19 @@ func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ond stmt4.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetCommunity().GetOrCreateReference().SetCommunitySetRefs(setCommunitySetRefs) stmt4.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetCommunity().SetMethod(oc.SetCommunity_Method_REFERENCE) stmt4.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetCommunity().SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) + + stmt6.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetCommunity().GetOrCreateReference().SetCommunitySetRefs(setCommunitySetRefs) + stmt6.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetCommunity().SetMethod(oc.SetCommunity_Method_REFERENCE) + stmt6.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetCommunity().SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) } // set-local-pref = 5 stmt4.GetOrCreateActions().GetOrCreateBgpActions().SetSetLocalPref(localPref) + stmt6.GetOrCreateActions().GetOrCreateBgpActions().SetSetLocalPref(localPref) - stmt4.GetOrCreateActions().SetPolicyResult(nextstatementResult) + if !deviations.SkipSettingStatementForPolicy(dut) { + stmt4.GetOrCreateActions().SetPolicyResult(nextstatementResult) + stmt6.GetOrCreateActions().SetPolicyResult(nextstatementResult) + } // Configure multi_policy:STATEMENT5: match_aspath_set_med statement stmt5, err := pdef1.AppendNewStatement(matchAspathSetMedStatement) @@ -347,7 +421,11 @@ func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ond stmt5.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) - gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + if deviations.CommunityMemberRegexUnsupported(dut) { + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + } else { + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + } // Configure the parent BGP import and export policy. dni := deviations.DefaultNetworkInstance(dut) @@ -355,17 +433,51 @@ func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ond 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) + if !deviations.DefaultRoutePolicyUnsupported(dut) { + policyV6.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + policyV6.SetDefaultExportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } gnmi.Replace(t, dut, pathV6.Config(), policyV6) + if !deviations.SkipBgpSendCommunityType(dut) { + n6 := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(ipv6) + n6.SetSendCommunityType([]oc.E_Bgp_CommunityType{oc.Bgp_CommunityType_BOTH}) + gnmi.Update(t, dut, gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(ipv6).Config(), n6) + } + 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) + if !deviations.DefaultRoutePolicyUnsupported(dut) { + policyV4.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + policyV4.SetDefaultExportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } gnmi.Replace(t, dut, pathV4.Config(), policyV4) + + if !deviations.SkipBgpSendCommunityType(dut) { + n4 := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(ipv4) + n4.SetSendCommunityType([]oc.E_Bgp_CommunityType{oc.Bgp_CommunityType_BOTH}) + gnmi.Update(t, dut, gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(ipv4).Config(), n4) + } + + pathV61 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(ipv61).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy() + policyV61 := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(ipv61).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateApplyPolicy() + policyV61.SetExportPolicy([]string{parentPolicy}) + if !deviations.DefaultRoutePolicyUnsupported(dut) { + policyV6.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + policyV6.SetDefaultExportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } + gnmi.Update(t, dut, pathV61.Config(), policyV61) + + pathV41 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(ipv41).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() + policyV41 := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(ipv41).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateApplyPolicy() + policyV41.SetExportPolicy([]string{parentPolicy}) + if !deviations.DefaultRoutePolicyUnsupported(dut) { + policyV4.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + policyV4.SetDefaultExportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } + gnmi.Update(t, dut, pathV41.Config(), policyV41) } func configureOTG(t *testing.T, bs *cfgplugins.BGPSession, prefixesV4 [][]string, prefixesV6 [][]string, communityMembers [][][]int) { @@ -502,8 +614,210 @@ func verifyTrafficV4AndV6(t *testing.T, bs *cfgplugins.BGPSession, testResults [ } } +func validateLocalPreferenceV4(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()) + if !deviations.SkipCheckingAttributeIndex(dut) { + 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 + } else { + attrSetList := gnmi.GetAll[*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet](t, dut, bgpRIBPath.AttrSetAny().State()) + foundLP := false + for _, attrSet := range attrSetList { + if attrSet.GetLocalPref() == metricValue { + foundLP = true + t.Logf("Found local pref %d for prefix %s", attrSet.GetLocalPref(), prefix) + break + } + } + if !foundLP { + t.Errorf("No local pref found for prefix %s", prefix) + } + } + } + } + + if !found { + t.Errorf("No Route found for prefix %s", prefix) + } +} + +func validateLocalPreferenceV6(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()) + if !deviations.SkipCheckingAttributeIndex(dut) { + 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 + } else { + attrSetList := gnmi.GetAll[*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet](t, dut, bgpRIBPath.AttrSetAny().State()) + foundLP := false + for _, attrSet := range attrSetList { + if attrSet.GetLocalPref() == metricValue { + foundLP = true + t.Logf("Found local pref %d for prefix %s", attrSet.GetLocalPref(), prefix) + break + } + } + if !foundLP { + t.Errorf("No local pref found for prefix %s", prefix) + } + } + } + } + + if !found { + t.Errorf("No Route found for prefix %s", prefix) + } +} + +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(), + 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[*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 { + foundPrefix = true + t.Logf("Prefix recevied on OTG is correct, got prefix %v, want prefix %v", bgpPrefix, ipAddr) + switch pathAttr { + case otgMED: + if bgpPrefix.GetMultiExitDiscriminator() != metric[0] { + t.Errorf("For Prefix %v, got MED %d want MED %d", bgpPrefix.GetAddress(), bgpPrefix.GetMultiExitDiscriminator(), metric) + } else { + t.Logf("For Prefix %v, got MED %d want MED %d", bgpPrefix.GetAddress(), bgpPrefix.GetMultiExitDiscriminator(), metric) + } + case otgASPath: + if len(bgpPrefix.AsPath[0].GetAsNumbers()) != len(metric) { + t.Logf("AS number: %v", bgpPrefix.AsPath[0].GetAsNumbers()) + t.Logf("Metric: %v", metric) + t.Errorf("For Prefix %v, got AS Path Prepend %d want AS Path Prepend %d", bgpPrefix.GetAddress(), len(bgpPrefix.AsPath[0].GetAsNumbers()), len(metric)) + } else { + for index, asPath := range bgpPrefix.AsPath[0].GetAsNumbers() { + if asPath == metric[index] { + t.Logf("Comparing if got AS Path %v, want AS Path %v, are equal", bgpPrefix.AsPath[0].GetAsNumbers()[index], metric[index]) + } else { + t.Errorf("For Prefix %v, got AS Path %d want AS Path %d", bgpPrefix.GetAddress(), bgpPrefix.AsPath[0].GetAsNumbers(), metric) + } + } + t.Logf("For Prefix %v, got AS Path %d want AS Path %d", bgpPrefix.GetAddress(), bgpPrefix.AsPath[0].GetAsNumbers(), metric) + } + case otglocalPref: + validateLocalPreferenceV6(t, dut, ipAddr, metric[0]) + case otgCommunity: + t.Logf("For Prefix %v, Community received on OTG: %v", bgpPrefix.GetAddress(), bgpPrefix.Community) + for _, gotCommunity := range bgpPrefix.Community { + // TODO: add check for community + t.Logf("community AS:%d val: %d", gotCommunity.GetCustomAsNumber(), gotCommunity.GetCustomAsValue()) + } + default: + t.Errorf("Incorrect Routing Policy. Expected MED, Local Pref or AS Path Prepend!!!!") + } + break + } + } + } + if !foundPrefix { + t.Errorf("Prefix %v not received on OTG", ipAddr) + } +} + +// validateOTGBgpPrefixV4AndASLocalPrefMED verifies that the IPv4 prefix is received on OTG. +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(), + 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[*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 { + foundPrefix = true + t.Logf("Prefix recevied on OTG is correct, got prefix %v, want prefix %v", bgpPrefix.Address, ipAddr) + switch pathAttr { + case otgMED: + if bgpPrefix.GetMultiExitDiscriminator() != metric[0] { + t.Errorf("For Prefix %v, got MED %d want MED %d", bgpPrefix.GetAddress(), bgpPrefix.GetMultiExitDiscriminator(), metric) + } else { + t.Logf("For Prefix %v, got MED %d want MED %d", bgpPrefix.GetAddress(), bgpPrefix.GetMultiExitDiscriminator(), metric) + } + case otgASPath: + if len(bgpPrefix.AsPath[0].GetAsNumbers()) != len(metric) { + t.Logf("AS number: %v", bgpPrefix.AsPath[0].GetAsNumbers()) + t.Logf("Metric: %v", metric) + t.Errorf("For Prefix %v, got AS Path Prepend %d want AS Path Prepend %d", bgpPrefix.GetAddress(), len(bgpPrefix.AsPath[0].GetAsNumbers()), len(metric)) + } else { + for index, asPath := range bgpPrefix.AsPath[0].GetAsNumbers() { + if asPath == metric[index] { + t.Logf("Comparing if got AS Path %v, want AS Path %v, are equal", bgpPrefix.AsPath[0].GetAsNumbers()[index], metric[index]) + } else { + t.Errorf("For Prefix %v, got AS Path %d want AS Path %d", bgpPrefix.GetAddress(), bgpPrefix.AsPath[0].GetAsNumbers(), metric) + } + } + t.Logf("For Prefix %v, got AS Path %d want AS Path %d are equal", bgpPrefix.GetAddress(), bgpPrefix.AsPath[0].GetAsNumbers(), metric) + } + case otglocalPref: + validateLocalPreferenceV4(t, dut, ipAddr, metric[0]) + case otgCommunity: + t.Logf("For Prefix %v, Community received on OTG: %v", bgpPrefix.GetAddress(), bgpPrefix.Community) + for _, gotCommunity := range bgpPrefix.Community { + // TODO: add check for community + t.Logf("community AS:%d val: %d", gotCommunity.GetCustomAsNumber(), gotCommunity.GetCustomAsValue()) + } + default: + t.Errorf("Incorrect BGP Path Attribute. Expected MED, Local Pref or AS Path Prepend!!!!") + } + break + } + } + } + if !foundPrefix { + t.Errorf("Prefix %v not received on OTG", ipAddr) + } +} + // TestImportExportMultifacetMatchActionsBGPPolicy covers RT-7.11 func TestImportExportMultifacetMatchActionsBGPPolicy(t *testing.T) { + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + otg := ate.OTG() + var otgConfig gosnappi.Config + 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) @@ -519,6 +833,9 @@ func TestImportExportMultifacetMatchActionsBGPPolicy(t *testing.T) { 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() + ipv41 := bs.ATETop.Devices().Items()[0].Ethernets().Items()[0].Ipv4Addresses().Items()[0].Address() + ipv61 := bs.ATETop.Devices().Items()[0].Ethernets().Items()[0].Ipv6Addresses().Items()[0].Address() + t.Logf("Verify Import Export Accept all bgp policy") configureImportExportAcceptAllBGPPolicy(t, bs.DUT, ipv4, ipv6) @@ -530,8 +847,59 @@ func TestImportExportMultifacetMatchActionsBGPPolicy(t *testing.T) { testResults := [6]bool{true, true, true, true, true, true} verifyTrafficV4AndV6(t, bs, testResults) - configureImportExportMultifacetMatchActionsBGPPolicy(t, bs.DUT, ipv4, ipv6) + configureImportExportMultifacetMatchActionsBGPPolicy(t, bs.DUT, ipv4, ipv6, ipv41, ipv61) + time.Sleep(time.Second * 120) testResults1 := [6]bool{false, true, false, false, true, true} verifyTrafficV4AndV6(t, bs, testResults1) + + testMedResults := [6]bool{false, true, false, false, true, true} + testASPathResults := [6]bool{false, true, false, false, true, true} + testLocalPrefResults := [6]bool{false, false, false, false, true, false} + testCommunityResults := [6]bool{false, true, false, false, true, true} + + medValue := []uint32{medValue} + asPathValue := []uint32{cfgplugins.DutAS, cfgplugins.AteAS2} + localPrefValue := []uint32{localPref} + communityResultValue := []uint32{} + + if deviations.BgpCommunitySetRefsUnsupported(dut) { + for index, cm := range communityMembers { + if testCommunityResults[index] { + communityReceived = append(communityReceived, cm) + } + } + } else { + communityReceived = [][][]int{ + append(communityMembers[1], []int{40, 1}, []int{40, 2}), + append(communityMembers[4], []int{40, 2}, []int{60, 1}, []int{70, 1}), + append(communityMembers[5], []int{40, 1}, []int{40, 2})} + } + + for index, prefix := range prefixesV4 { + if testMedResults[index] { + for idx, pref := range prefix { + validateOTGBgpPrefixV4AndASLocalPrefMED(t, otg, dut, otgConfig, bs.ATEPorts[0].Name+".BGP4.peer", pref, prefixV4Len, otgMED, medValue) + validateOTGBgpPrefixV6AndASLocalPrefMED(t, otg, dut, otgConfig, bs.ATEPorts[0].Name+".BGP6.peer", prefixesV6[index][idx], prefixV6Len, otgMED, medValue) + } + } + if testLocalPrefResults[index] { + for idx, pref := range prefix { + validateOTGBgpPrefixV4AndASLocalPrefMED(t, otg, dut, otgConfig, bs.ATEPorts[0].Name+".BGP4.peer", pref, prefixV4Len, otglocalPref, localPrefValue) + validateOTGBgpPrefixV6AndASLocalPrefMED(t, otg, dut, otgConfig, bs.ATEPorts[0].Name+".BGP6.peer", prefixesV6[index][idx], prefixV6Len, otglocalPref, localPrefValue) + } + } + if testASPathResults[index] { + for idx, pref := range prefix { + validateOTGBgpPrefixV4AndASLocalPrefMED(t, otg, dut, otgConfig, bs.ATEPorts[0].Name+".BGP4.peer", pref, prefixV4Len, otgASPath, asPathValue) + validateOTGBgpPrefixV6AndASLocalPrefMED(t, otg, dut, otgConfig, bs.ATEPorts[0].Name+".BGP6.peer", prefixesV6[index][idx], prefixV6Len, otgASPath, asPathValue) + } + } + if testCommunityResults[index] && !deviations.SkipBgpSendCommunityType(dut) { + for idx, pref := range prefix { + validateOTGBgpPrefixV4AndASLocalPrefMED(t, otg, dut, otgConfig, bs.ATEPorts[0].Name+".BGP4.peer", pref, prefixV4Len, otgCommunity, communityResultValue) + validateOTGBgpPrefixV6AndASLocalPrefMED(t, otg, dut, otgConfig, bs.ATEPorts[0].Name+".BGP6.peer", prefixesV6[index][idx], prefixV6Len, otgCommunity, communityResultValue) + } + } + } } diff --git a/feature/bgp/policybase/otg_tests/import_export_multi_test/metadata.textproto b/feature/bgp/policybase/otg_tests/import_export_multi_test/metadata.textproto index a1e6e2490a2..ec09b76414c 100644 --- a/feature/bgp/policybase/otg_tests/import_export_multi_test/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/import_export_multi_test/metadata.textproto @@ -6,7 +6,7 @@ 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] +tags: [TAGS_DATACENTER_EDGE] platform_exceptions: { platform: { vendor: ARISTA @@ -19,7 +19,21 @@ platform_exceptions: { 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 bgp_community_member_is_a_string: true + skip_bgp_send_community_type: true + } +} +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + bgp_conditions_match_community_set_unsupported: true + bgp_community_set_refs_unsupported: true + community_member_regex_unsupported: true + skip_setting_statement_for_policy: true + default_route_policy_unsupported: true + skip_checking_attribute_index: true + skip_bgp_send_community_type: true } } diff --git a/feature/bgp/policybase/otg_tests/nested_policies/metadata.textproto b/feature/bgp/policybase/otg_tests/nested_policies/metadata.textproto index 712cd851126..beed0cdd3fd 100644 --- a/feature/bgp/policybase/otg_tests/nested_policies/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/nested_policies/metadata.textproto @@ -15,6 +15,7 @@ platform_exceptions: { default_network_instance: "default" missing_value_for_defaults: true skip_set_rp_match_set_options: true + routing_policy_chaining_unsupported: true } } @@ -27,4 +28,12 @@ platform_exceptions: { skip_checking_attribute_index: true } } +platform_exceptions: { + platform: { + vendor: JUNIPER + } + deviations: { + bgp_rib_oc_path_unsupported: 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 index 662bb3f8957..34727c8d907 100644 --- a/feature/bgp/policybase/otg_tests/nested_policies/nested_policies_test.go +++ b/feature/bgp/policybase/otg_tests/nested_policies/nested_policies_test.go @@ -76,6 +76,8 @@ const ( v6MedStatement = "med-statement-v6" peerGrpNamev4 = "BGP-PEER-GROUP-V4" peerGrpNamev6 = "BGP-PEER-GROUP-V6" + permitAll = "PERMIT-ALL" + permitAllStmtName = "20" ) var ( @@ -162,10 +164,10 @@ type flowConfig struct { 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, @@ -239,6 +241,14 @@ func configureImportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { root := &oc.Root{} rp := root.GetOrCreateRoutingPolicy() + // Configure PERMIT-ALL policy + pdef := rp.GetOrCreatePolicyDefinition(permitAll) + stmt, err := pdef.AppendNewStatement(permitAllStmtName) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", permitAllStmtName, err) + } + stmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE + // Configure a route-policy to set the local preference. pdef1 := rp.GetOrCreatePolicyDefinition(v4LPPolicy) stmt1, err := pdef1.AppendNewStatement(v4LPStatement) @@ -271,56 +281,68 @@ func configureImportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { stmt2.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsRestrictedType_ANY) } stmt2.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetPrefixSet(v4PrefixSet) + statPath := rp.GetOrCreatePolicyDefinition(v4LPPolicy).GetStatement(v4LPStatement).GetOrCreateConditions() + statPath.SetCallPolicy(v4PrefixPolicy) gnmi.BatchReplace(batch, 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) + if deviations.RoutePolicyUnderAFIUnsupported(dut) { + //policy under peer group + path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().PeerGroup(peerGrpNamev4).ApplyPolicy() + policy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreatePeerGroup(peerGrpNamev4).GetOrCreateApplyPolicy() + policy.SetImportPolicy([]string{v4LPPolicy}) + gnmi.BatchReplace(batch, path.Config(), policy) + + } else { + 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}) + if !deviations.RoutingPolicyChainingUnsupported(dut) { + gnmi.BatchUpdate(batch, path.Config(), policy) + } else { + gnmi.BatchReplace(batch, path.Config(), policy) + } + } + batch.Set(t, dut) } 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()) - if !deviations.SkipCheckingAttributeIndex(dut) { - 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 - } else { - attrSetList := gnmi.GetAll[*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet](t, dut, bgpRIBPath.AttrSetAny().State()) - foundLP := false - for _, attrSet := range attrSetList { - if attrSet.GetLocalPref() == localPref { - foundLP = true - t.Logf("Found local pref %d for prefix %s", attrSet.GetLocalPref(), advertisedIPv41.address) - break + if !deviations.BGPRibOcPathUnsupported(dut) { + 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()) + if !deviations.SkipCheckingAttributeIndex(dut) { + 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 + } else { + attrSetList := gnmi.GetAll[*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet](t, dut, bgpRIBPath.AttrSetAny().State()) + foundLP := false + for _, attrSet := range attrSetList { + if attrSet.GetLocalPref() == localPref { + foundLP = true + t.Logf("Found local pref %d for prefix %s", attrSet.GetLocalPref(), advertisedIPv41.address) + break + } + } + if !foundLP { + t.Errorf("No local pref found for prefix %s", advertisedIPv41.address) } - } - if !foundLP { - t.Errorf("No local pref found for prefix %s", advertisedIPv41.address) } } } - } - if !found { - t.Errorf("No Route found for prefix %s", advertisedIPv41.address) + if !found { + t.Errorf("No Route found for prefix %s", advertisedIPv41.address) + } } } @@ -328,7 +350,16 @@ func validateImportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, ate *onda func configureExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { batch := &gnmi.SetBatch{} root := &oc.Root{} + gnmi.BatchDelete(batch, gnmi.OC().RoutingPolicy().Config()) rp := root.GetOrCreateRoutingPolicy() + + pdef := rp.GetOrCreatePolicyDefinition(permitAll) + stmt, err := pdef.AppendNewStatement(permitAllStmtName) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", permitAllStmtName, err) + } + stmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE + t.Logf("Configuring export routing policy") pdef1 := rp.GetOrCreatePolicyDefinition(v4ASPPolicy) stmt1, err := pdef1.AppendNewStatement(v4ASPStatement) @@ -350,22 +381,36 @@ func configureExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) } stmt2.GetOrCreateActions().GetOrCreateBgpActions().SetSetMed(oc.UnionUint32(med)) + statPath := rp.GetOrCreatePolicyDefinition(v4ASPPolicy).GetStatement(v4ASPStatement).GetOrCreateConditions() + statPath.SetCallPolicy(v4MedPolicy) gnmi.BatchReplace(batch, 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 * 120) + if deviations.RoutePolicyUnderAFIUnsupported(dut) { + //policy under peer group + path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().PeerGroup(peerGrpNamev4).ApplyPolicy() + gnmi.BatchDelete(batch, path.Config()) + policy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreatePeerGroup(peerGrpNamev4).GetOrCreateApplyPolicy() + policy.SetExportPolicy([]string{v4ASPPolicy}) + gnmi.BatchReplace(batch, path.Config(), policy) + } else { + 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() + gnmi.BatchDelete(batch, path.Config()) + 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}) + if !deviations.RoutingPolicyChainingUnsupported(dut) { + gnmi.BatchUpdate(batch, path.Config(), policy) + } else { + gnmi.BatchReplace(batch, path.Config(), policy) + } + gnmi.BatchReplace(batch, path.Config(), policy) + } + batch.Set(t, dut) + time.Sleep(time.Second * 60) } func validateExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { @@ -397,7 +442,6 @@ func validateExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, ate *onda break } } - if !found { t.Errorf("No Route found for prefix %s", v42Route) } @@ -407,7 +451,16 @@ func validateExportRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, ate *onda func configureImportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice) { batch := &gnmi.SetBatch{} root := &oc.Root{} + gnmi.BatchDelete(batch, gnmi.OC().RoutingPolicy().Config()) rp := root.GetOrCreateRoutingPolicy() + + pdef := rp.GetOrCreatePolicyDefinition(permitAll) + stmt, err := pdef.AppendNewStatement(permitAllStmtName) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", permitAllStmtName, err) + } + stmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE + t.Logf("Configuring import routing policy") pdef1 := rp.GetOrCreatePolicyDefinition(v6LPPolicy) stmt1, err := pdef1.AppendNewStatement(v6LPStatement) @@ -437,57 +490,76 @@ func configureImportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice) { stmt2.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsRestrictedType_ANY) } stmt2.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetPrefixSet(v6PrefixSet) + statPath := rp.GetOrCreatePolicyDefinition(v6LPPolicy).GetStatement(v6LPStatement).GetOrCreateConditions() + statPath.SetCallPolicy(v6PrefixPolicy) gnmi.BatchReplace(batch, 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) - + if deviations.RoutePolicyUnderAFIUnsupported(dut) { + //policy under peer group + pathV4 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().PeerGroup(peerGrpNamev4).ApplyPolicy() + gnmi.BatchDelete(batch, pathV4.Config()) + path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().PeerGroup(peerGrpNamev6).ApplyPolicy() + gnmi.BatchDelete(batch, path.Config()) + policy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreatePeerGroup(peerGrpNamev6).GetOrCreateApplyPolicy() + policy.SetImportPolicy([]string{v6LPPolicy}) + gnmi.BatchReplace(batch, path.Config(), policy) + } else { + pathV4 := 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() + gnmi.BatchDelete(batch, pathV4.Config()) + 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() + gnmi.BatchDelete(batch, path.Config()) + 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}) + if !deviations.RoutingPolicyChainingUnsupported(dut) { + gnmi.BatchUpdate(batch, path.Config(), policy) + } else { + gnmi.BatchReplace(batch, path.Config(), policy) + } + } + batch.Set(t, dut) } 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(), "/") - 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()) - if !deviations.SkipCheckingAttributeIndex(dut) { - 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 - } else { - attrSetList := gnmi.GetAll[*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet](t, dut, bgpRIBPath.AttrSetAny().State()) - foundLP := false - for _, attrSet := range attrSetList { - if attrSet.GetLocalPref() == localPref { - foundLP = true - t.Logf("Found local pref %d for prefix %s", attrSet.GetLocalPref(), advertisedIPv61.address) - break + if !deviations.BGPRibOcPathUnsupported(dut) { + 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()) + if !deviations.SkipCheckingAttributeIndex(dut) { + 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 + } else { + attrSetList := gnmi.GetAll[*oc.NetworkInstance_Protocol_Bgp_Rib_AttrSet](t, dut, bgpRIBPath.AttrSetAny().State()) + foundLP := false + for _, attrSet := range attrSetList { + if attrSet.GetLocalPref() == localPref { + foundLP = true + t.Logf("Found local pref %d for prefix %s", attrSet.GetLocalPref(), advertisedIPv61.address) + break + } + } + if !foundLP { + t.Errorf("No local pref found for prefix %s", advertisedIPv41.address) } - } - if !foundLP { - t.Errorf("No local pref found for prefix %s", advertisedIPv41.address) } } } - } - if !found { - t.Errorf("No Route found for prefix %s", advertisedIPv61.address) + if !found { + t.Errorf("No Route found for prefix %s", advertisedIPv61.address) + } } } @@ -495,7 +567,16 @@ func validateImportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice, ate *on func configureExportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice) { batch := &gnmi.SetBatch{} root := &oc.Root{} + gnmi.BatchDelete(batch, gnmi.OC().RoutingPolicy().Config()) rp := root.GetOrCreateRoutingPolicy() + + pdef := rp.GetOrCreatePolicyDefinition(permitAll) + stmt, err := pdef.AppendNewStatement(permitAllStmtName) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", permitAllStmtName, err) + } + stmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE + t.Logf("Configuring export routing policy") pdef1 := rp.GetOrCreatePolicyDefinition(v6ASPPolicy) stmt1, err := pdef1.AppendNewStatement(v6ASPStatement) @@ -516,21 +597,38 @@ func configureExportRoutingPolicyV6(t *testing.T, dut *ondatra.DUTDevice) { stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) } stmt2.GetOrCreateActions().GetOrCreateBgpActions().SetSetMed(oc.UnionUint32(med)) + statPath := rp.GetOrCreatePolicyDefinition(v6ASPPolicy).GetStatement(v6ASPStatement).GetOrCreateConditions() + statPath.SetCallPolicy(v6MedPolicy) gnmi.BatchReplace(batch, 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) + if deviations.RoutePolicyUnderAFIUnsupported(dut) { + //policy under peer group + pathV4 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().PeerGroup(peerGrpNamev4).ApplyPolicy() + gnmi.BatchDelete(batch, pathV4.Config()) + path := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().PeerGroup(peerGrpNamev6).ApplyPolicy() + gnmi.BatchDelete(batch, path.Config()) + policy := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreatePeerGroup(peerGrpNamev6).GetOrCreateApplyPolicy() + policy.SetExportPolicy([]string{v6LPPolicy}) + gnmi.BatchReplace(batch, path.Config(), policy) + } else { + pathV4 := 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() + gnmi.BatchDelete(batch, pathV4.Config()) + 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() + gnmi.BatchDelete(batch, path.Config()) + 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}) + if !deviations.RoutingPolicyChainingUnsupported(dut) { + gnmi.BatchUpdate(batch, path.Config(), policy) + } else { + gnmi.BatchReplace(batch, path.Config(), policy) + } + batch.Set(t, dut) + } time.Sleep(time.Second * 60) } @@ -635,6 +733,7 @@ func checkTraffic(t *testing.T, td testData, flowName string) { 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) @@ -649,10 +748,10 @@ func (td *testData) advertiseRoutesWithEBGP(t *testing.T) { t.Logf("Configuring route-policy for BGP on DUT") rp := root.GetOrCreateRoutingPolicy() - pdef := rp.GetOrCreatePolicyDefinition("PERMIT-ALL") - stmt, err := pdef.AppendNewStatement("20") + pdef := rp.GetOrCreatePolicyDefinition(permitAll) + stmt, err := pdef.AppendNewStatement(permitAllStmtName) if err != nil { - t.Fatalf("AppendNewStatement(%s) failed: %v", "20", err) + t.Fatalf("AppendNewStatement(%s) failed: %v", permitAllStmtName, err) } stmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE gnmi.Update(t, td.dut, gnmi.OC().RoutingPolicy().Config(), rp) @@ -666,32 +765,32 @@ func (td *testData) advertiseRoutesWithEBGP(t *testing.T) { nV41.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) nV41.PeerGroup = ygot.String(peerGrpNamev4) afisafiv41 := nV41.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) - afisafiv41.GetOrCreateApplyPolicy().SetImportPolicy([]string{"PERMIT-ALL"}) - afisafiv41.GetOrCreateApplyPolicy().SetExportPolicy([]string{"PERMIT-ALL"}) + afisafiv41.GetOrCreateApplyPolicy().SetImportPolicy([]string{permitAll}) + afisafiv41.GetOrCreateApplyPolicy().SetExportPolicy([]string{permitAll}) 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) afisafiv42 := nV42.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) - afisafiv42.GetOrCreateApplyPolicy().SetImportPolicy([]string{"PERMIT-ALL"}) - afisafiv42.GetOrCreateApplyPolicy().SetExportPolicy([]string{"PERMIT-ALL"}) + afisafiv42.GetOrCreateApplyPolicy().SetImportPolicy([]string{permitAll}) + afisafiv42.GetOrCreateApplyPolicy().SetExportPolicy([]string{permitAll}) 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) afisafiv61 := nV61.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) - afisafiv61.GetOrCreateApplyPolicy().SetImportPolicy([]string{"PERMIT-ALL"}) - afisafiv61.GetOrCreateApplyPolicy().SetExportPolicy([]string{"PERMIT-ALL"}) + afisafiv61.GetOrCreateApplyPolicy().SetImportPolicy([]string{permitAll}) + afisafiv61.GetOrCreateApplyPolicy().SetExportPolicy([]string{permitAll}) 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) afisafiv62 := nV62.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) - afisafiv62.GetOrCreateApplyPolicy().SetImportPolicy([]string{"PERMIT-ALL"}) - afisafiv62.GetOrCreateApplyPolicy().SetExportPolicy([]string{"PERMIT-ALL"}) + afisafiv62.GetOrCreateApplyPolicy().SetImportPolicy([]string{permitAll}) + afisafiv62.GetOrCreateApplyPolicy().SetExportPolicy([]string{permitAll}) gnmi.Update(t, td.dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(td.dut)).Config(), ni) @@ -772,10 +871,6 @@ func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { 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) - // 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, true) - // t.Logf("Wait for 2minutes for the OTG config to be created, now check in the DUT") - // time.Sleep(2 * time.Minute) if deviations.ExplicitPortSpeed(dut) { fptest.SetPortSpeed(t, p1) 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 index 0175455dec8..07a0db917a5 100644 --- 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 @@ -430,7 +430,7 @@ func testPrefixSet(t *testing.T, dut *ondatra.DUTDevice) { 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) { + if deviations.DefaultImportExportPolicyUnsupported(dut) { t.Logf("Validate for neighbour %v", ebgp1NbrV4) validatePrefixCount(t, dut, *ebgp1NbrV4, 3, 5, 0) validatePrefixCount(t, dut, *ebgp1NbrV6, 1, 5, 0) @@ -487,7 +487,7 @@ func TestBGPPrefixSet(t *testing.T) { verifyBgpState(t, dut) }) - if deviations.DefaultImportExportPolicy(dut) { + if deviations.DefaultImportExportPolicyUnsupported(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) 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 cc69dc8d104..159d68be6a0 100644 --- a/feature/bgp/policybase/otg_tests/prefix_set_test/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/prefix_set_test/metadata.textproto @@ -27,8 +27,7 @@ platform_exceptions: { default_network_instance: "default" missing_value_for_defaults: true skip_set_rp_match_set_options: true - default_import_export_policy: true - + default_import_export_policy_unsupported: true } } platform_exceptions: { diff --git a/feature/bgp/static_route_bgp_redistribution/otg_tests/static_route_bgp_redistribution_test/README.md b/feature/bgp/static_route_bgp_redistribution/otg_tests/static_route_bgp_redistribution_test/README.md index 4939440ceba..e663f722f25 100644 --- a/feature/bgp/static_route_bgp_redistribution/otg_tests/static_route_bgp_redistribution_test/README.md +++ b/feature/bgp/static_route_bgp_redistribution/otg_tests/static_route_bgp_redistribution_test/README.md @@ -77,97 +77,9 @@ * Set a tag on the ```ipv6-route``` to 60 * /network-instances/network-instance/protocols/protocol/static-routes/static/config/set-tag -### RT-1.27.1 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] -#### Redistribute IPv4 static routes to BGP with metric propogation diabled ---- -##### Configure redistribution -* Redistribute ```ipv4-route``` to BGP -* Set address-family to ```IPV4``` - * /network-instances/network-instance/table-connections/table-connection/config/address-family -* Configure source protocol to ```STATIC``` - * /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 import policy to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/table-connections/table-connection/config/default-import-policy -* Disable metric propogation by setting it to ```true``` - * /network-instances/network-instance/table-connections/table-connection/config/disable-metric-propagation -##### Verification -* Verify the address-family is set to ```IPV4``` - * /network-instances/network-instance/table-connections/table-connection/state/address-family -* Verify source protocol is set to ```STATIC``` - * /network-instances/network-instance/table-connections/table-connection/state/src-protocol -* Verify destination protocol is set to ```BGP``` - * /network-instances/network-instance/table-connections/table-connection/state/dst-protocol -* Verify default import policy is set to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/table-connections/table-connection/state/default-import-policy -* Verify disable metric propogation is set to ```true``` - * /network-instances/network-instance/table-connections/table-connection/state/disable-metric-propagation -##### Validate test results -* Validate that the ATE receives the redistributed static route ```ipv4-route``` with MED either having no value (missing) or ```0``` but not ```104``` - * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv4-unicast/loc-rib/routes/route/prefix - * /network-instances/network-instance/protocols/protocol/bgp/rib/attr-sets/attr-set/state/med - -### RT-1.27.2 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] -#### Redistribute IPv4 static routes to BGP with metric propogation enabled ---- -##### Configure static route metric to be copied to MED -* Enable metric propogation by setting disable-metric-propagation to ```false``` - * /network-instances/network-instance/table-connections/table-connection/config/disable-metric-propagation -##### Verification -* Verify disable metric propogation is now ```false``` - * /network-instances/network-instance/table-connections/table-connection/state/disable-metric-propagation -##### Validate test results -* Validate that the ATE receives the redistributed static route ```ipv4-route``` with MED of ```104``` - * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv4-unicast/loc-rib/routes/route/prefix - * /network-instances/network-instance/protocols/protocol/bgp/rib/attr-sets/attr-set/state/med -### RT-1.27.3 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] -#### Redistribute IPv6 static routes to BGP with metric propogation diabled ---- -##### Configure redistribution -* Set address-family to ```IPV6``` - * /network-instances/network-instance/table-connections/table-connection/config/address-family -* Configure source protocol to ```STATIC``` - * /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 import policy to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/table-connections/table-connection/config/default-import-policy -* Disable metric propogation by setting it to ```true``` - * /network-instances/network-instance/table-connections/table-connection/config/disable-metric-propagation -##### Verification -* Verify the address-family is set to ```IPV6``` - * /network-instances/network-instance/table-connections/table-connection/state/address-family -* Verify source protocol is set to ```STATIC``` - * /network-instances/network-instance/table-connections/table-connection/state/src-protocol -* Verify destination protocol is set to ```BGP``` - * /network-instances/network-instance/table-connections/table-connection/state/dst-protocol -* Verify default import policy is set to ```ACCEPT_ROUTE``` - * /network-instances/network-instance/table-connections/table-connection/state/default-import-policy -* Verify disable metric propogation is set to ```true``` - * /network-instances/network-instance/table-connections/table-connection/state/disable-metric-propagation -##### Validate test results -* Validate that the ATE receives the redistributed static route ```ipv6-route``` with MED either having no value (missing) or ```0``` but not ```106``` - * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv6-unicast/loc-rib/routes/route/prefix - * /network-instances/network-instance/protocols/protocol/bgp/rib/attr-sets/attr-set/state/med - -### RT-1.27.4 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] -#### Redistribute IPv6 static routes to BGP with metric propogation enabled ---- -##### Configure static route metric to be copied to MED -* Enable metric propogation by setting it to ```false``` - * /network-instances/network-instance/table-connections/table-connection/config/disable-metric-propagation -##### Verification -* Verify disable metric propogation is now ```false``` - * /network-instances/network-instance/table-connections/table-connection/state/disable-metric-propagation -##### Validate test results -* Validate that the ATE receives the redistributed static route ```ipv6-route``` with MED ```106``` - * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv6-unicast/loc-rib/routes/route/prefix - * /network-instances/network-instance/protocols/protocol/bgp/rib/attr-sets/attr-set/state/med - -### RT-1.27.5 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] -#### Redistribute IPv4 and IPv6 static routes to BGP with default-import-policy set to reject +### RT-1.27.1 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] +#### Redistribute IPv4 static routes to BGP with default-import-policy set to reject --- ##### Configure default policy to reject routes * Configure default import policy to ```REJECT_ROUTE``` @@ -176,11 +88,10 @@ * Verify default import policy is set to ```REJECT_ROUTE``` * /network-instances/network-instance/table-connections/table-connection/state/default-import-policy ##### Validate test results -* Validate that the ATE does not receives the redistributed static route ```ipv4-route``` and ```ipv6-route``` +* Validate that the ATE does not receives the redistributed static route ```ipv4-route``` * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv4-unicast/loc-rib/routes/route/prefix - * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv6-unicast/loc-rib/routes/route/prefix -### RT-1.27.6 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] +### RT-1.27.2 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] #### Redistribute IPv4 static routes to BGP matching a prefix using a route-policy --- ##### Configure a route-policy @@ -234,7 +145,35 @@ * Initiate traffic from ATE port-1 to the DUT and destined to ```ipv4-network``` i.e. ```192.168.10.0/24``` * Validate that the traffic is received on ATE port-2 -### RT-1.27.7 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] +### RT-1.27.3 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] +#### Redistribute IPv4 static routes to BGP with metric propogation diabled +--- +##### Configure redistribution +* Disable metric propogation by setting it to ```true``` + * /network-instances/network-instance/table-connections/table-connection/config/disable-metric-propagation +##### Verification +* Verify disable metric propogation is set to ```true``` + * /network-instances/network-instance/table-connections/table-connection/state/disable-metric-propagation +##### Validate test results +* Validate that the ATE receives the redistributed static route ```ipv4-route``` with MED either having no value (missing) or ```0``` but not ```104``` + * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv4-unicast/loc-rib/routes/route/prefix + * /network-instances/network-instance/protocols/protocol/bgp/rib/attr-sets/attr-set/state/med + +### RT-1.27.4 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] +#### Redistribute IPv4 static routes to BGP with metric propogation enabled +--- +##### Configure static route metric to be copied to MED +* Enable metric propogation by setting disable-metric-propagation to ```false``` + * /network-instances/network-instance/table-connections/table-connection/config/disable-metric-propagation +##### Verification +* Verify disable metric propogation is now ```false``` + * /network-instances/network-instance/table-connections/table-connection/state/disable-metric-propagation +##### Validate test results +* Validate that the ATE receives the redistributed static route ```ipv4-route``` with MED of ```104``` + * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv4-unicast/loc-rib/routes/route/prefix + * /network-instances/network-instance/protocols/protocol/bgp/rib/attr-sets/attr-set/state/med + +### RT-1.27.5 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] #### Redistribute IPv4 static routes to BGP with AS-PATH prepend --- ##### Configure BGP actions to prepend AS @@ -252,7 +191,7 @@ * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv4-unicast/loc-rib/routes/route/prefix * /network-instances/network-instance/protocols/protocol/bgp/rib/attr-sets/attr-set/as-path/as-segment/state/member -### RT-1.27.8 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] +### RT-1.27.6 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] #### Redistribute IPv4 static routes to BGP with MED set to ```1000``` --- ##### Configure BGP actions to set MED @@ -266,7 +205,7 @@ * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv4-unicast/loc-rib/routes/route/prefix * /network-instances/network-instance/protocols/protocol/bgp/rib/attr-sets/attr-set/state/med -### RT-1.27.9 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] +### RT-1.27.7 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] #### Redistribute IPv4 static routes to BGP with Local-Preference set to ```100``` --- ##### Configure BGP actions to set local-pref @@ -280,7 +219,7 @@ * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv4-unicast/loc-rib/routes/route/prefix * /network-instances/network-instance/protocols/protocol/bgp/rib/attr-sets/attr-set/state/local-pref -### RT-1.27.10 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] +### RT-1.27.8 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] #### Redistribute IPv4 static routes to BGP with community set to ```64512:100``` --- ##### Configure a community-set @@ -302,7 +241,7 @@ * /network-instances/network-instance/protocols/protocol/bgp/rib/communities/community/state/index * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv4-unicast/loc-rib/routes/route/state/community-index -### RT-1.27.12 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] +### RT-1.27.9 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] #### Redistribution of IPv4 static routes to BGP that does not match a conditional tag should not happen --- ##### Configure a tag-set with incorrect tag value to validate that the route is not redistributed @@ -328,7 +267,7 @@ * Verify that the ATE does not receives the redistributed static route ```ipv4-route``` * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv6-unicast/loc-rib/routes/route/prefix -### RT-1.27.13 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] +### RT-1.27.10 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] #### Redistribution of IPv4 static routes to BGP that matches a conditional tag should happen --- ##### Configure a tag-set with correct tag value to validate that the route is redistributed @@ -343,7 +282,7 @@ * Initiate traffic from ATE port-1 to the DUT and destined to ```ipv4-network``` i.e. ```192.168.10.0/24``` * Validate that the traffic is received on ATE port-2 -### RT-1.27.14 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] +### RT-1.27.11 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] #### Redistribute a NULL IPv4 static routes to BGP with a next-hop configured through route-policy --- ##### Configure a NULL static route @@ -365,7 +304,23 @@ * Initiate traffic from ATE port-3 to the DUT and destined to ```ipv4-drop-network``` i.e. ```192.168.20.0/24``` * Validate that the traffic is received on ATE port-2 -### RT-1.27.15 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] + + +### RT-1.27.12 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] +#### Redistribute IPv6 static routes to BGP with default-import-policy set to reject +--- +##### Configure default policy to reject routes +* Configure default import policy to ```REJECT_ROUTE``` + * /network-instances/network-instance/table-connections/table-connection/config/default-import-policy +##### Verification +* Verify default import policy is set to ```REJECT_ROUTE``` + * /network-instances/network-instance/table-connections/table-connection/state/default-import-policy +##### Validate test results +* Validate that the ATE does not receives the redistributed static route ```ipv4-route``` and ```ipv6-route``` + * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv4-unicast/loc-rib/routes/route/prefix + * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv6-unicast/loc-rib/routes/route/prefix + +### RT-1.27.13 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] #### Redistribute IPv6 static routes to BGP matching a prefix using a route-policy --- ##### Configure a route-policy @@ -419,6 +374,35 @@ * Initiate traffic from ATE port-1 to the DUT and destined to ```ipv6-network``` i.e. ```2024:db8:128:128::/64``` * Validate that the traffic is received on ATE port-2 + +### RT-1.27.14 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] +#### Redistribute IPv6 static routes to BGP with metric propogation diabled +--- +##### Disable metric propogation +* Disable metric propogation by setting it to ```true``` + * /network-instances/network-instance/table-connections/table-connection/config/disable-metric-propagation +##### Verification +* Verify disable metric propogation is set to ```true``` + * /network-instances/network-instance/table-connections/table-connection/state/disable-metric-propagation +##### Validate test results +* Validate that the ATE receives the redistributed static route ```ipv6-route``` with MED either having no value (missing) or ```0``` but not ```106``` + * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv6-unicast/loc-rib/routes/route/prefix + * /network-instances/network-instance/protocols/protocol/bgp/rib/attr-sets/attr-set/state/med + +### RT-1.27.15 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] +#### Redistribute IPv6 static routes to BGP with metric propogation enabled +--- +##### Configure static route metric to be copied to MED +* Enable metric propogation by setting it to ```false``` + * /network-instances/network-instance/table-connections/table-connection/config/disable-metric-propagation +##### Verification +* Verify disable metric propogation is now ```false``` + * /network-instances/network-instance/table-connections/table-connection/state/disable-metric-propagation +##### Validate test results +* Validate that the ATE receives the redistributed static route ```ipv6-route``` with MED ```106``` + * /network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv6-unicast/loc-rib/routes/route/prefix + * /network-instances/network-instance/protocols/protocol/bgp/rib/attr-sets/attr-set/state/med + ### RT-1.27.16 [TODO: https://github.com/openconfig/featureprofiles/issues/2568] #### Redistribute IPv6 static routes to BGP with AS-PATH prepend --- 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 index 89b2354afb0..a0da74fe48d 100644 --- 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 @@ -29,7 +29,11 @@ platform_exceptions: { deviations: { omit_l2_mtu: true default_network_instance: "default" + interface_enabled: true static_protocol_name: "STATIC" skip_bgp_send_community_type: true + skip_setting_disable_metric_propagation: true + same_policy_attached_to_all_afis: true + set_metric_as_preference: 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 index 49e72a7399b..2aa5bece36a 100644 --- 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 @@ -144,14 +144,15 @@ var ( "port2": dutPort2, "port3": dutPort3, } + + atePorts = map[string]*attrs.Attributes{ + "port1": atePort1, + "port2": atePort2, + "port3": atePort3, + } ) -func configureDUTPort( - t *testing.T, - dut *ondatra.DUTDevice, - port *ondatra.Port, - portAttrs *attrs.Attributes, -) { +func configureDUTPort(t *testing.T, dut *ondatra.DUTDevice, port *ondatra.Port, portAttrs *attrs.Attributes) { t.Helper() gnmi.Replace( @@ -170,10 +171,7 @@ func configureDUTPort( } } -func configureDUTStatic( - t *testing.T, - dut *ondatra.DUTDevice, -) { +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)) @@ -194,8 +192,12 @@ func configureDUTStatic( } ipv4StaticRouteNextHop := ipv4StaticRoute.GetOrCreateNextHop("0") - ipv4StaticRouteNextHop.SetMetric(104) - ipv4StaticRouteNextHop.SetNextHop(oc.UnionString("192.168.1.6")) + if deviations.SetMetricAsPreference(dut) { + ipv4StaticRouteNextHop.Metric = ygot.Uint32(104) + } else { + ipv4StaticRouteNextHop.Preference = ygot.Uint32(104) + } + ipv4StaticRouteNextHop.SetNextHop(oc.LocalRouting_LOCAL_DEFINED_NEXT_HOP_DROP) ipv6StaticRoute := networkInstanceProtocolStatic.GetOrCreateStatic("2024:db8:128:128::/64") if !deviations.UseVendorNativeTagSetConfig(dut) { @@ -204,71 +206,37 @@ func configureDUTStatic( 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")) + ipv6StaticRouteNextHop := ipv6StaticRoute.GetOrCreateNextHop("1") + if deviations.SetMetricAsPreference(dut) { + ipv6StaticRouteNextHop.Metric = ygot.Uint32(106) + } else { + ipv6StaticRouteNextHop.Preference = ygot.Uint32(106) + } + ipv6StaticRouteNextHop.SetNextHop(oc.LocalRouting_LOCAL_DEFINED_NEXT_HOP_DROP) gnmi.Replace(t, dut, staticPath.Config(), networkInstanceProtocolStatic) } -func configureDUTRoutingPolicy( - t *testing.T, - dut *ondatra.DUTDevice, -) { +func configureDUTBGP(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) + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") - ipv6PrefixPolicyStatement, err := connectedPolicyDefinition.AppendNewStatement("fp-ipv6-prefix") + // permit all policy + rp := dutOcRoot.GetOrCreateRoutingPolicy() + pdef := rp.GetOrCreatePolicyDefinition("permit-all") + stmt, err := pdef.AppendNewStatement("accept") if err != nil { t.Fatalf("failed creating new policy statement, err: %s", err) } + stmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) - 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{} + // setup BGP networkInstance := dutOcRoot.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) networkInstanceProtocolBgp := networkInstance.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + networkInstanceProtocolBgp.SetEnabled(true) bgp := networkInstanceProtocolBgp.GetOrCreateBgp() bgpGlobal := bgp.GetOrCreateGlobal() @@ -298,18 +266,19 @@ func configureDUTBGP( 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"}) + ateEBGPNeighborIPv4AFPolicy.SetImportPolicy([]string{"permit-all"}) + ateEBGPNeighborIPv4AFPolicy.SetExportPolicy([]string{"permit-all"}) 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"}) + ateEBGPNeighborIPv6AFPolicy.SetImportPolicy([]string{"permit-all"}) + ateEBGPNeighborIPv6AFPolicy.SetExportPolicy([]string{"permit-all"}) // dutPort3 -> atePort3 peer (ibgp session) ateIBGPNeighborThree := bgp.GetOrCreateNeighbor(atePort3.IPv4) @@ -319,20 +288,20 @@ func configureDUTBGP( 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) + ateIBGPNeighborThreeIPv4AFPolicy := ateIBGPNeighborThreeIPv4AF.GetOrCreateApplyPolicy() + ateIBGPNeighborThreeIPv4AFPolicy.SetImportPolicy([]string{"permit-all"}) + ateIBGPNeighborThreeIPv4AFPolicy.SetExportPolicy([]string{"permit-all"}) 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) + ateIBGPNeighborFourIPv6AFPolicy := ateIBGPNeighborFourIPv6AF.GetOrCreateApplyPolicy() + ateIBGPNeighborFourIPv6AFPolicy.SetImportPolicy([]string{"permit-all"}) + ateIBGPNeighborFourIPv6AFPolicy.SetExportPolicy([]string{"permit-all"}) gnmi.Replace(t, dut, bgpPath.Config(), networkInstanceProtocolBgp) } @@ -342,8 +311,8 @@ func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { port := dut.Port(t, portName) configureDUTPort(t, dut, port, portAttrs) } + fptest.ConfigureDefaultNetworkInstance(t, dut) - configureDUTRoutingPolicy(t, dut) configureDUTStatic(t, dut) configureDUTBGP(t, dut) } @@ -354,68 +323,46 @@ func awaitBGPEstablished(t *testing.T, dut *ondatra.DUTDevice, neighbors []strin Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP"). Bgp(). Neighbor(neighbor). - SessionState().State(), time.Second*120, oc.Bgp_Neighbor_SessionState_ESTABLISHED) + SessionState().State(), time.Second*240, oc.Bgp_Neighbor_SessionState_ESTABLISHED) } } -func configureOTG(t *testing.T) gosnappi.Config { +func configureOTG(t *testing.T, ate *ondatra.ATEDevice) 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)) + for portName, portAttrs := range atePorts { + port := ate.Port(t, portName) + portAttrs.AddToOTG(otgConfig, port, dutPorts[portName]) + } + + devices := otgConfig.Devices().Items() // eBGP v4 session on Port1. - iDut1Bgp := iDut1Dev.Bgp().SetRouterId(iDut1Ipv4.Address()) + bgp := devices[0].Bgp().SetRouterId(atePort1.IPv4) + iDut1Ipv4 := devices[0].Ethernets().Items()[0].Ipv4Addresses().Items()[0] + iDut1Bgp := 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. + iDut1Ipv6 := devices[0].Ethernets().Items()[0].Ipv6Addresses().Items()[0] 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()) + bgp = devices[2].Bgp().SetRouterId(atePort3.IPv4) + iDut3Ipv4 := devices[2].Ethernets().Items()[0].Ipv4Addresses().Items()[0] + iDut3Bgp := 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. + iDut3Ipv6 := devices[2].Ethernets().Items()[0].Ipv6Addresses().Items()[0] 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 @@ -484,7 +431,6 @@ func verifyTraffic(t *testing.T, ate *ondatra.ATEDevice, conf gosnappi.Config) { // 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)) @@ -495,18 +441,44 @@ func configureTableConnection(t *testing.T, dut *ondatra.DUTDevice, isV4, mPropa addressFamily = oc.Types_ADDRESS_FAMILY_IPV6 } + batchSet := &gnmi.SetBatch{} 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) + if !deviations.SkipSettingDisableMetricPropagation(dut) { + tc.SetDisableMetricPropagation(!mPropagation) + } + gnmi.BatchUpdate(batchSet, niPath.TableConnection(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, addressFamily).Config(), tc) + + if deviations.SamePolicyAttachedToAllAfis(dut) { + if addressFamily == oc.Types_ADDRESS_FAMILY_IPV4 { + addressFamily = oc.Types_ADDRESS_FAMILY_IPV6 + } else { + addressFamily = oc.Types_ADDRESS_FAMILY_IPV4 + } + + tc1 := networkInstance.GetOrCreateTableConnection( + oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, + oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, + addressFamily, + ) + + if importPolicy != "" { + tc1.SetImportPolicy([]string{importPolicy}) + } + if !deviations.SkipSettingDisableMetricPropagation(dut) { + tc1.SetDisableMetricPropagation(!mPropagation) + } + gnmi.BatchUpdate(batchSet, niPath.TableConnection(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, addressFamily).Config(), tc1) + } + + batchSet.Set(t, dut) } // Populate routing-policy to redistribute static-route @@ -539,7 +511,6 @@ func redistributeStaticRoute(t *testing.T, isV4 bool, mPropagation, policyResult // Configure routing-policy to redistribute static-route func configureStaticRedistributionPolicy(t *testing.T, dut *ondatra.DUTDevice, isV4, acceptRoute, mPropagation bool) { - t.Helper() dutOcRoot := &oc.Root{} @@ -595,35 +566,15 @@ func validateRedistributeStatic(t *testing.T, dut *ondatra.DUTDevice, acceptRout 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") + if !deviations.SkipSettingDisableMetricPropagation(dut) { + 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") + } } } } else { @@ -729,7 +680,7 @@ func validatePrefixSetRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice, isV4 b } } -// 1.27.1 setup function +// 1.27.3 setup function func redistributeIPv4Static(t *testing.T, dut *ondatra.DUTDevice) { if deviations.TableConnectionsUnsupported(dut) { configureStaticRedistributionPolicy(t, dut, isV4, acceptRoute, !metricPropagate) @@ -738,13 +689,13 @@ func redistributeIPv4Static(t *testing.T, dut *ondatra.DUTDevice) { } } -// 1.27.1 validation function +// 1.27.3 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 +// 1.27.4 setup function func redistributeIPv4StaticWithMetricPropagation(t *testing.T, dut *ondatra.DUTDevice) { if deviations.TableConnectionsUnsupported(dut) { configureStaticRedistributionPolicy(t, dut, isV4, acceptRoute, metricPropagate) @@ -753,13 +704,13 @@ func redistributeIPv4StaticWithMetricPropagation(t *testing.T, dut *ondatra.DUTD } } -// 1.27.2 validation function +// 1.27.4 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 +// 1.27.14 setup function func redistributeIPv6Static(t *testing.T, dut *ondatra.DUTDevice) { if deviations.TableConnectionsUnsupported(dut) { configureStaticRedistributionPolicy(t, dut, !isV4, acceptRoute, !metricPropagate) @@ -768,13 +719,13 @@ func redistributeIPv6Static(t *testing.T, dut *ondatra.DUTDevice) { } } -// 1.27.3 validation function +// 1.27.14 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 +// 1.27.15 setup function func redistributeIPv6StaticWithMetricPropagation(t *testing.T, dut *ondatra.DUTDevice) { if deviations.TableConnectionsUnsupported(dut) { configureStaticRedistributionPolicy(t, dut, !isV4, acceptRoute, metricPropagate) @@ -783,36 +734,47 @@ func redistributeIPv6StaticWithMetricPropagation(t *testing.T, dut *ondatra.DUTD } } -// 1.27.4 validation function +// 1.27.15 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) { +// 1.27.1 setup function +func redistributeIPv4StaticDefaultRejectPolicy(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) + } +} + +// 1.27.12 setup function +func redistributeIPv6StaticDefaultRejectPolicy(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_REJECT_ROUTE) } } -// 1.27.5 validation function -func validateRedistributeIPv4IPv6DefaultRejectPolicy(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { +// 1.27.1 validation function +func validateRedistributeIPv4DefaultRejectPolicy(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) +} + +// 1.27.12 validation function +func validateRedistributeIPv6DefaultRejectPolicy(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.6 setup function +// 1.27.2 setup function func redistributeIPv4PrefixRoutePolicy(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { policyPath := gnmi.OC().RoutingPolicy().PolicyDefinition(redistributeStaticPolicyNameV4) - otgConfig := configureOTG(t) + otgConfig := configureOTG(t, ate) 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) @@ -865,14 +827,14 @@ func redistributeIPv4PrefixRoutePolicy(t *testing.T, dut *ondatra.DUTDevice, ate verifyTraffic(t, ate, otgConfig) } -// 1.27.6 validation function +// 1.27.2 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 +// 1.27.5 and 1.27.16 setup function func redistributeStaticRoutePolicyWithASN(t *testing.T, dut *ondatra.DUTDevice, isV4 bool) { redistributeStaticPolicyName := redistributeStaticPolicyNameV4 @@ -917,7 +879,7 @@ func redistributeStaticRoutePolicyWithASN(t *testing.T, dut *ondatra.DUTDevice, } } -// 1.27.8 and 1.27.17 setup function +// 1.27.6 and 1.27.17 setup function func redistributeStaticRoutePolicyWithMED(t *testing.T, dut *ondatra.DUTDevice, isV4 bool, medValue uint32) { redistributeStaticPolicyName := redistributeStaticPolicyNameV4 policyStatementName := policyStatementNameV4 @@ -957,7 +919,7 @@ func redistributeStaticRoutePolicyWithMED(t *testing.T, dut *ondatra.DUTDevice, } } -// 1.27.9 and 1.27.18 setup function +// 1.27.7 and 1.27.18 setup function func redistributeStaticRoutePolicyWithLocalPreference(t *testing.T, dut *ondatra.DUTDevice, isV4 bool, localPreferenceValue uint32) { redistributeStaticPolicyName := redistributeStaticPolicyNameV4 @@ -998,7 +960,7 @@ func redistributeStaticRoutePolicyWithLocalPreference(t *testing.T, dut *ondatra } } -// 1.27.10 and 1.27.19 setup function +// 1.27.8 and 1.27.19 setup function func redistributeStaticRoutePolicyWithCommunitySet(t *testing.T, dut *ondatra.DUTDevice, isV4 bool) { redistributeStaticPolicyName := redistributeStaticPolicyNameV4 @@ -1049,7 +1011,7 @@ func redistributeStaticRoutePolicyWithCommunitySet(t *testing.T, dut *ondatra.DU } } -// 1.27.12, 1.27.13, 1.27.20 and 1.27.21 setup function +// 1.27.9, 1.27.10, 1.27.20 and 1.27.21 setup function func redistributeStaticRoutePolicyWithTagSet(t *testing.T, dut *ondatra.DUTDevice, isV4 bool, tagSetValue uint32) { redistributeStaticPolicyName := redistributeStaticPolicyNameV4 @@ -1099,7 +1061,7 @@ func redistributeStaticRoutePolicyWithTagSet(t *testing.T, dut *ondatra.DUTDevic } } -// 1.27.14 and 1.27.22 setup function +// 1.27.11 and 1.27.22 setup function func redistributeNullNextHopStaticRoute(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, isV4 bool) { redistributeStaticPolicyName := redistributeStaticPolicyNameV4 @@ -1122,7 +1084,7 @@ func redistributeNullNextHopStaticRoute(t *testing.T, dut *ondatra.DUTDevice, at policyPath := gnmi.OC().RoutingPolicy().PolicyDefinition(redistributeStaticPolicyName) staticPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(dut)) - otgConfig := configureOTG(t) + otgConfig := configureOTG(t, ate) if isV4 { otgConfig = configureTrafficFlow(t, otgConfig, isV4, "StaticDropRoutesV4Flow", atePort3.Name+".IPv4", atePort2.Name+".IPv4", atePort3.MAC, atePort3.IPv4, "192.168.20.0") } else { @@ -1185,11 +1147,11 @@ func redistributeNullNextHopStaticRoute(t *testing.T, dut *ondatra.DUTDevice, at */ } -// 1.27.15 setup function +// 1.27.13 setup function func redistributeIPv6StaticRoutePolicy(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { policyPath := gnmi.OC().RoutingPolicy().PolicyDefinition(redistributeStaticPolicyNameV6) - otgConfig := configureOTG(t) + otgConfig := configureOTG(t, ate) 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) @@ -1241,14 +1203,14 @@ func redistributeIPv6StaticRoutePolicy(t *testing.T, dut *ondatra.DUTDevice, ate verifyTraffic(t, ate, otgConfig) } -// 1.27.15 validation function +// 1.27.13 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 +// 1.27.5 and 1.27.16 validation function func validatePrefixASN(t *testing.T, ate *ondatra.ATEDevice, isV4 bool, bgpPeerName, subnet string, wantASPath []uint32) { foundPrefix := false @@ -1291,7 +1253,7 @@ func validatePrefixASN(t *testing.T, ate *ondatra.ATEDevice, isV4 bool, bgpPeerN } } -// 1.27.9 and 1.27.18 validation function +// 1.27.7 and 1.27.18 validation function func validatePrefixLocalPreference(t *testing.T, ate *ondatra.ATEDevice, isV4 bool, bgpPeerName, subnet string, wantLocalPreference uint32) { foundPrefix := false @@ -1333,7 +1295,7 @@ func validatePrefixLocalPreference(t *testing.T, ate *ondatra.ATEDevice, isV4 bo } } -// 1.27.10 and 1.27.19 validation function +// 1.27.8 and 1.27.19 validation function func validatePrefixCommunitySet(t *testing.T, ate *ondatra.ATEDevice, isV4 bool, bgpPeerName, subnet, wantCommunitySet string) { foundPrefix := false @@ -1398,7 +1360,7 @@ func validatePrefixCommunitySet(t *testing.T, ate *ondatra.ATEDevice, isV4 bool, } } -// 1.27.12, 1.27.13, 1.27.20 and 1.27.21 validation function +// 1.27.9, 1.27.10, 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 @@ -1458,7 +1420,7 @@ func validateRedistributeRouteWithTagSet(t *testing.T, dut *ondatra.DUTDevice, a } } -// 1.27.14 and 1.27.22 validation function +// 1.27.11 and 1.27.22 validation function func validateRedistributeNullNextHopStaticRoute(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, isV4 bool) { redistributeStaticPolicyName := redistributeStaticPolicyNameV4 @@ -1516,98 +1478,58 @@ func validateRedistributeNullNextHopStaticRoute(t *testing.T, dut *ondatra.DUTDe // 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) - + bgpPrefixes := gnmi.GetAll(t, ate.OTG(), gnmi.OTG().BgpPeer(bgpPeerName).UnicastIpv4PrefixAny().State()) 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") - } + for _, bgpPrefix := range bgpPrefixes { + if bgpPrefix.Address != nil && bgpPrefix.GetAddress() == subnet { + found = true + t.Logf("Prefix recevied on OTG is correct, got prefix %v, want prefix %v", bgpPrefix, subnet) + t.Logf("Prefix MED %d", bgpPrefix.GetMultiExitDiscriminator()) + if bgpPrefix.GetMultiExitDiscriminator() != expectedMED { + t.Errorf("For Prefix %v, got MED %d want MED %d", bgpPrefix.GetAddress(), bgpPrefix.GetMultiExitDiscriminator(), expectedMED) } + break } } - 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) + if !found { + t.Errorf("No Route found for prefix %s", subnet) } } // 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) - + bgpPrefixes := gnmi.GetAll[*otgtelemetry.BgpPeer_UnicastIpv6Prefix](t, ate.OTG(), gnmi.OTG().BgpPeer(bgpPeerName).UnicastIpv6PrefixAny().State()) 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") - } + for _, bgpPrefix := range bgpPrefixes { + if bgpPrefix.Address != nil && bgpPrefix.GetAddress() == subnet { + found = true + t.Logf("Prefix recevied on OTG is correct, got prefix %v, want prefix %v", bgpPrefix, subnet) + t.Logf("Prefix MED %d", bgpPrefix.GetMultiExitDiscriminator()) + if bgpPrefix.GetMultiExitDiscriminator() != expectedMED { + t.Errorf("For Prefix %v, got MED %d want MED %d", bgpPrefix.GetAddress(), bgpPrefix.GetMultiExitDiscriminator(), expectedMED) } + break } } - 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) + if !found { + t.Errorf("No Route found for prefix %s", subnet) } } 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) + otgConfig := configureOTG(t, ate) + ate.OTG().PushConfig(t, otgConfig) + ate.OTG().StartProtocols(t) awaitBGPEstablished(t, dut, []string{atePort1.IPv4, atePort3.IPv4}) @@ -1620,96 +1542,102 @@ func TestBGPStaticRouteRedistribution(t *testing.T) { 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) }, + name: "1.27.1 redistribute-ipv4-ipv6-default-reject-policy", + setup: func() { redistributeIPv4StaticDefaultRejectPolicy(t, dut) }, + validate: func() { validateRedistributeIPv4DefaultRejectPolicy(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) }, + name: "1.27.2 redistribute-ipv4-prefix-route-policy", + setup: func() { redistributeIPv4PrefixRoutePolicy(t, dut, ate) }, + validate: func() { validateRedistributeIPv4PrefixRoutePolicy(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) }, + name: "1.27.3 redistribute-ipv4-static-routes-with-metric-propagation-disabled", + setup: func() { redistributeIPv4Static(t, dut) }, + validate: func() { validateRedistributeIPv4Static(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) }, + name: "1.27.4 redistribute-ipv4-static-routes-with-metric-propagation-enabled", + setup: func() { redistributeIPv4StaticWithMetricPropagation(t, dut) }, + validate: func() { validateRedistributeIPv4StaticWithMetricPropagation(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", + name: "1.27.5 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 + // 1.27.6 { - name: "1.27.8 redistribute-ipv4-route-policy-med", + name: "1.27.6 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 + // 1.27.7 { - name: "1.27.9 redistribute-ipv4-route-policy-local-preference", + name: "1.27.7 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 + // 1.27.8 { - name: "1.27.10 redistribute-ipv4-route-policy-community-set", + name: "1.27.8 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 + // 1.27.9 { - name: "1.27.12 redistribute-ipv4-route-policy-unmatched-tag", + name: "1.27.9 redistribute-ipv4-route-policy-unmatched-tag", setup: func() { redistributeStaticRoutePolicyWithTagSet(t, dut, isV4, 100) }, validate: func() { validateRedistributeRouteWithTagSet(t, dut, ate, isV4, !shouldBePresent) }, }, - // 1.27.13 + // 1.27.10 { - name: "1.27.13 redistribute-ipv4-route-policy-matched-set", + name: "1.27.10 redistribute-ipv4-route-policy-matched-set", setup: func() { redistributeStaticRoutePolicyWithTagSet(t, dut, isV4, 40) }, validate: func() { validateRedistributeRouteWithTagSet(t, dut, ate, isV4, shouldBePresent) }, }, - // 1.27.14 + // 1.27.11 { - name: "1.27.14 redistribute-ipv4-route-policy-nexthop", + name: "1.27.11 redistribute-ipv4-route-policy-nexthop", setup: func() { redistributeNullNextHopStaticRoute(t, dut, ate, isV4) }, validate: func() { validateRedistributeNullNextHopStaticRoute(t, dut, ate, isV4) }, }, - // 1.27.15 + // 1.27.12 + { + name: "1.27.12 redistribute-ipv6-default-reject-policy", + setup: func() { redistributeIPv6StaticDefaultRejectPolicy(t, dut) }, + validate: func() { validateRedistributeIPv6DefaultRejectPolicy(t, dut, ate) }, + }, + // 1.27.13 { - name: "1.27.15 redistribute-ipv6-route-policy", + name: "1.27.13 redistribute-ipv6-route-policy", setup: func() { redistributeIPv6StaticRoutePolicy(t, dut, ate) }, validate: func() { validateRedistributeIPv6RoutePolicy(t, dut, ate) }, }, + // 1.27.14 + { + name: "1.27.14 redistribute-ipv6-static-routes-with-metric-propagation-disabled", + setup: func() { redistributeIPv6Static(t, dut) }, + validate: func() { validateRedistributeIPv6Static(t, dut, ate) }, + }, + // 1.27.15 + { + name: "1.27.15 redistribute-ipv6-static-routes-with-metric-propagation-enabled", + setup: func() { redistributeIPv6StaticWithMetricPropagation(t, dut) }, + validate: func() { validateRedistributeIPv6StaticWithMetricPropagation(t, dut, ate) }, + }, // 1.27.16 { name: "1.27.16 redistribute-ipv6-route-policy-as-prepend", diff --git a/feature/bgp/tests/local_bgp_test/README.md b/feature/bgp/tests/local_bgp_test/README.md index 3e30b2b711b..de4b31660f8 100644 --- a/feature/bgp/tests/local_bgp_test/README.md +++ b/feature/bgp/tests/local_bgp_test/README.md @@ -13,18 +13,28 @@ Enable an Accept-route all import-policy/export-policy for eBGP session under th This test is suitable for running in a KNE environment. -## Parameter Coverage - -* /network-instances/network-instance/protocols/protocol/bgp/global/config/as -* /network-instances/network-instance/protocols/protocol/bgp/global/config/router-id -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/config/auth-password -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/config/local-as -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/config/neighbor-address -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/config/peer-as -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/neighbor-address -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/state/messages/received/last-notification-error-code -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/state/session-state -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/state/supported-capabilities -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/timers/config/hold-time -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/timers/config/keepalive-interval +## OpenConfig Path and RPC Coverage + +```yaml +paths: + ## Parameter Coverage + + /network-instances/network-instance/protocols/protocol/bgp/global/config/as: + /network-instances/network-instance/protocols/protocol/bgp/global/config/router-id: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/config/auth-password: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/config/local-as: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/config/neighbor-address: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/config/peer-as: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/neighbor-address: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/state/messages/received/last-notification-error-code: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/state/session-state: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/state/supported-capabilities: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/timers/config/hold-time: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/timers/config/keepalive-interval: + +rpcs: + gnmi: + gNMI.Subscribe: + gNMI.Set: +``` diff --git a/feature/bgp/tests/local_bgp_test/local_bgp_test.go b/feature/bgp/tests/local_bgp_test/local_bgp_test.go index d582aeae44a..b3e7834d4d1 100644 --- a/feature/bgp/tests/local_bgp_test/local_bgp_test.go +++ b/feature/bgp/tests/local_bgp_test/local_bgp_test.go @@ -176,7 +176,7 @@ func TestEstablish(t *testing.T) { }, dut) gnmi.Replace(t, dut, dutConfPath.Config(), dutConf) gnmi.Replace(t, ate, ateConfPath.Config(), ateConf) - gnmi.Await(t, dut, nbrPath.SessionState().State(), time.Second*120, oc.Bgp_Neighbor_SessionState_ESTABLISHED) + gnmi.Await(t, dut, nbrPath.SessionState().State(), time.Second*180, oc.Bgp_Neighbor_SessionState_ESTABLISHED) wantState := dutConf.Bgp dutState := gnmi.Get(t, dut, statePath.State()) if deviations.MissingValueForDefaults(dut) { diff --git a/feature/bgp/timers/otg_tests/bgp_keepalive_and_holdtimer_configuration_test/README.md b/feature/bgp/timers/otg_tests/bgp_keepalive_and_holdtimer_configuration_test/README.md index 32acae86d73..43d4f97c60f 100644 --- a/feature/bgp/timers/otg_tests/bgp_keepalive_and_holdtimer_configuration_test/README.md +++ b/feature/bgp/timers/otg_tests/bgp_keepalive_and_holdtimer_configuration_test/README.md @@ -16,20 +16,28 @@ BGP Keepalive and HoldTimer Configuration Test * Verify that the sessions are established after soft reset. -## Config Parameter coverage - -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/timers/config/keepalive-interval -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/timers/config/hold-time - -## Telemetry Parameter coverage - -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/timers/state/keepalive-interval -* /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/timers/state/hold-time - -## Protocol/RPC Parameter coverage - -N/A - +## 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 ## + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/timers/config/keepalive-interval: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/timers/config/hold-time: + + ## State Paths ## + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/timers/state/keepalive-interval: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/timers/state/hold-time: + +rpcs: + gnmi: + gNMI.Get: + gNMI.Subscribe: + on_change: true + gNMI.Set: +``` + ## Minimum DUT platform requirement -vRX \ No newline at end of file +vRX diff --git a/feature/bgp/timers/otg_tests/bgp_keepalive_and_holdtimer_configuration_test/bgp_keepalive_and_holdtimer_configuration_test.go b/feature/bgp/timers/otg_tests/bgp_keepalive_and_holdtimer_configuration_test/bgp_keepalive_and_holdtimer_configuration_test.go index 75fd153d88a..4d5bf0acc90 100644 --- a/feature/bgp/timers/otg_tests/bgp_keepalive_and_holdtimer_configuration_test/bgp_keepalive_and_holdtimer_configuration_test.go +++ b/feature/bgp/timers/otg_tests/bgp_keepalive_and_holdtimer_configuration_test/bgp_keepalive_and_holdtimer_configuration_test.go @@ -259,8 +259,13 @@ func bgpCreateNbr(dut *ondatra.DUTDevice) *oc.NetworkInstance_Protocol { nv4.GetOrCreateTimers().RestartTime = ygot.Uint16(bgpGlobalAttrs.grRestartTime) afisafi := nv4.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) afisafi.Enabled = ygot.Bool(true) - prefixLimit := afisafi.GetOrCreateIpv4Unicast().GetOrCreatePrefixLimit() - prefixLimit.MaxPrefixes = ygot.Uint32(uint32(nbr.pfxLimit)) + if deviations.BGPExplicitPrefixLimitReceived(dut) { + prefixLimit := afisafi.GetOrCreateIpv4Unicast().GetOrCreatePrefixLimitReceived() + prefixLimit.MaxPrefixes = ygot.Uint32(uint32(nbr.pfxLimit)) + } else { + prefixLimit := afisafi.GetOrCreateIpv4Unicast().GetOrCreatePrefixLimit() + prefixLimit.MaxPrefixes = ygot.Uint32(uint32(nbr.pfxLimit)) + } if deviations.RoutePolicyUnderAFIUnsupported(dut) { rpl := pgv4.GetOrCreateApplyPolicy() rpl.ImportPolicy = []string{bgpGlobalAttrs.rplName} @@ -281,8 +286,13 @@ func bgpCreateNbr(dut *ondatra.DUTDevice) *oc.NetworkInstance_Protocol { nv6.GetOrCreateTimers().RestartTime = ygot.Uint16(bgpGlobalAttrs.grRestartTime) afisafi6 := nv6.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) afisafi6.Enabled = ygot.Bool(true) - prefixLimit6 := afisafi6.GetOrCreateIpv6Unicast().GetOrCreatePrefixLimit() - prefixLimit6.MaxPrefixes = ygot.Uint32(nbr.pfxLimit) + if deviations.BGPExplicitPrefixLimitReceived(dut) { + prefixLimit := afisafi6.GetOrCreateIpv6Unicast().GetOrCreatePrefixLimitReceived() + prefixLimit.MaxPrefixes = ygot.Uint32(uint32(nbr.pfxLimit)) + } else { + prefixLimit := afisafi6.GetOrCreateIpv6Unicast().GetOrCreatePrefixLimit() + prefixLimit.MaxPrefixes = ygot.Uint32(uint32(nbr.pfxLimit)) + } if deviations.RoutePolicyUnderAFIUnsupported(dut) { rpl := pgv6.GetOrCreateApplyPolicy() rpl.ImportPolicy = []string{bgpGlobalAttrs.rplName} diff --git a/feature/bgp/timers/otg_tests/bgp_keepalive_and_holdtimer_configuration_test/metadata.textproto b/feature/bgp/timers/otg_tests/bgp_keepalive_and_holdtimer_configuration_test/metadata.textproto index 8b25dcb8189..d9df992f174 100644 --- a/feature/bgp/timers/otg_tests/bgp_keepalive_and_holdtimer_configuration_test/metadata.textproto +++ b/feature/bgp/timers/otg_tests/bgp_keepalive_and_holdtimer_configuration_test/metadata.textproto @@ -21,6 +21,7 @@ platform_exceptions: { explicit_port_speed: true explicit_interface_in_default_vrf: true interface_enabled: true + bgp_explicit_prefix_limit_received: true } } platform_exceptions: { diff --git a/feature/container/containerz/README.md b/feature/container/containerz/README.md deleted file mode 100644 index 44f7dcef001..00000000000 --- a/feature/container/containerz/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# CNTR-1: Basic container lifecycle via `gnoi.Containerz`. - -## CNTR-1.1: Deploy and Start a Container - -Using the -[`gnoi.Containerz`](https://github.com/openconfig/gnoi/tree/main/containerz) API -(reference implementation to be available -[`openconfig/containerz`](https://github.com/openconfig/containerz), deploy a -container to the DUT. Using `gnoi.Containerz` start the container. - -The container should expose a simple health API. The test succeeds if is -possible to connect to the container via the gRPC API to determine its health. - -## CNTR-1.2: Retrieve a running container's logs. - -Using the container started as part of CNTR-1.1, retrieve the logs from the -container and ensure non-zero contents are returned when using -`gnoi.Containerz.Log`. - -## CNTR-1.3: List the running containers on a DUT - -Using the container started as part of CNTR-1.1, validate that the container is -included in the listed set of containers when calling `gnoi.Containerz.List`. - -## CNTR-1.4: Stop a container running on a DUT. - -Using the container started as part of CNTR-1.2, validate that the container can -be stopped, and is subsequently no longer listed in the `gnoi.Containerz.List` -API. - diff --git a/feature/container/containerz/tests/container_lifecycle/README.md b/feature/container/containerz/tests/container_lifecycle/README.md new file mode 100644 index 00000000000..768d43fee2b --- /dev/null +++ b/feature/container/containerz/tests/container_lifecycle/README.md @@ -0,0 +1,113 @@ +# CNTR-1: Basic container lifecycle via `gnoi.Containerz`. + +## Summary + +Verify the correct behaviour of `gNOI.Containerz` when operating containers. + +## Procedure + +This step only applies if the reference implementation of containerz is being +tested. + +Start by pulling the reference implementation: + +```shell +$ git clone git@github.com:openconfig/containerz.git +``` + +Then `cd` into the containerz directory and build containerz: + +```shell +$ cd containerz +$ go build . +``` + +Finally start containerz: + +```shell +$ ./containerz start +``` + +You should see the following output: + +```shell +$ ./containerz start +I0620 12:02:57.408496 3615908 janitor.go:33] janitor-starting +I0620 12:02:57.408547 3615908 janitor.go:36] janitor-started +I0620 12:02:57.408583 3615908 server.go:167] server-start +I0620 12:02:57.408595 3615908 server.go:170] Starting up on Containerz server, listening on: [::]:19999 +I0620 12:02:57.408608 3615908 server.go:171] server-ready +``` + +### Build Test Container + +The test container is available in the feature profile repository under +`internal/cntrsrv`. + +Start by entering in that directory and running the following commands: + +```shell +$ cd internal/cntrsrv +$ go mod vendor +$ CGO_ENABLED=0 go build . +$ docker build -f build/Dockerfile.local -t cntrsrv:latest . +``` + +At this point you will have a container image build for the test container. + +```shell +$ docker images +REPOSITORY TAG IMAGE ID CREATED SIZE +cntrsrv latest 8d786a6eebc8 3 minutes ago 21.4MB +``` + +Now export the container to a tarball. + +```shell +$ docker save -o /tmp/cntrsrv.tar cntrsrv:latest +$ docker rmi cntrsrv:latest +``` + +This is the tarball that will be used during tests. + +## CNTR-1.1: Deploy and Start a Container + +Using the +[`gnoi.Containerz`](https://github.com/openconfig/gnoi/tree/main/containerz) API +(reference implementation to be available +[`openconfig/containerz`](https://github.com/openconfig/containerz), deploy a +container to the DUT. Using `gnoi.Containerz` start the container. + +The container should expose a simple health API. The test succeeds if is +possible to connect to the container via the gRPC API to determine its health. + +## CNTR-1.2: Retrieve a running container's logs. + +Using the container started as part of CNTR-1.1, retrieve the logs from the +container and ensure non-zero contents are returned when using +`gnoi.Containerz.Log`. + +## CNTR-1.3: List the running containers on a DUT + +Using the container started as part of CNTR-1.1, validate that the container is +included in the listed set of containers when calling `gnoi.Containerz.List`. + +## CNTR-1.4: Stop a container running on a DUT. + +Using the container started as part of CNTR-1.2, validate that the container can +be stopped, and is subsequently no longer listed in the `gnoi.Containerz.List` +API. + +## OpenConfig Path and RPC Coverage + +The below yaml defines the RPCs intended to be covered by this test. + +```yaml +rpcs: + gnoi: + containerz.Containerz.Deploy: + containerz.Containerz.StartContainer: + containerz.Containerz.StopContainer: + containerz.Containerz.Log: + containerz.Containerz.ListContainer: +``` \ No newline at end of file diff --git a/feature/container/containerz/tests/container_lifecycle/containerz_test.go b/feature/container/containerz/tests/container_lifecycle/containerz_test.go new file mode 100644 index 00000000000..4d14062b228 --- /dev/null +++ b/feature/container/containerz/tests/container_lifecycle/containerz_test.go @@ -0,0 +1,66 @@ +package container_lifecycle_test + +import ( + "context" + "crypto/tls" + "flag" + "testing" + + "github.com/openconfig/containerz/client" + cpb "github.com/openconfig/featureprofiles/internal/cntrsrv/proto/cntr" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +var ( + containerTar = flag.String("container_tar", "/tmp/cntrsrv.tar", "The container tarball to deploy.") + containerzAddr = flag.String("containerz_addr", "localhost:19999", "containerz server address") +) + +// TestDeployAndStartContainer implements CNTR-1.1 validating that it is +// possible deploy and start a container via containerz. +func TestDeployAndStartContainer(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + cli, err := client.NewClient(ctx, *containerzAddr) + if err != nil { + t.Fatalf("unable to dial containerz: %v", err) + } + + progCh, err := cli.PushImage(ctx, "cntrsrv", "latest", *containerTar) + if err != nil { + t.Fatalf("unable to push image: %v", err) + } + + for prog := range progCh { + switch { + case prog.Error != nil: + t.Fatalf("failed to push image: %v", err) + case prog.Finished: + t.Logf("Pushed %s/%s\n", prog.Image, prog.Tag) + default: + t.Logf(" %d bytes pushed", prog.BytesReceived) + } + } + + ret, err := cli.StartContainer(ctx, "cntrsrv", "latest", "./cntrsrv", "test-instance", client.WithPorts([]string{"60061:60061"})) + if err != nil { + t.Fatalf("unable to start container: %v", err) + } + t.Logf("Started %s", ret) + + tlsc := credentials.NewTLS(&tls.Config{ + InsecureSkipVerify: true, // NOLINT + }) + conn, err := grpc.DialContext(ctx, "localhost:60061", grpc.WithTransportCredentials(tlsc), grpc.WithBlock()) + if err != nil { + t.Fatalf("Failed to dial cntrsrv, %v", err) + } + + cntrCli := cpb.NewCntrClient(conn) + if _, err = cntrCli.Ping(ctx, &cpb.PingRequest{}); err != nil { + t.Errorf("unable to reach cntrsrv: %v", err) + } + +} diff --git a/feature/container/containerz/tests/container_lifecycle/metadata.textproto b/feature/container/containerz/tests/container_lifecycle/metadata.textproto new file mode 100644 index 00000000000..7d84745fd46 --- /dev/null +++ b/feature/container/containerz/tests/container_lifecycle/metadata.textproto @@ -0,0 +1,7 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "a477c97d-c895-4af3-9c97-9327bf86d517" +plan_id: "CNTR-1" +description: "Basic container lifecycle via `gnoi.Containerz`." +testbed: TESTBED_DUT_ATE_2LINKS \ No newline at end of file diff --git a/feature/experimental/bgp/ate_tests/bgp_long_lived_graceful_restart/README.md b/feature/experimental/bgp/ate_tests/bgp_long_lived_graceful_restart/README.md index 5d9f48a4f5a..ba3a85b48d4 100644 --- a/feature/experimental/bgp/ate_tests/bgp_long_lived_graceful_restart/README.md +++ b/feature/experimental/bgp/ate_tests/bgp_long_lived_graceful_restart/README.md @@ -76,9 +76,15 @@ BGP Long-Lived Graceful Restart * /neighbors/neighbor/afi-safis/afi-safi/graceful-restart/state/received * /neighbors/neighbor/afi-safis/afi-safi/graceful-restart/state/advertised -## Protocol/RPC Parameter coverage +## OpenConfig Path and RPC Coverage -N/A +```yaml +rpcs: + gnmi: + gNMI.Set: + gNMI.Get: + gNMI.Subscribe: +``` ## Minimum DUT platform requirement diff --git a/feature/experimental/bgp/ate_tests/bgp_long_lived_graceful_restart/bgp_long_lived_graceful_restart_test.go b/feature/experimental/bgp/ate_tests/bgp_long_lived_graceful_restart/bgp_long_lived_graceful_restart_test.go index 341903baae9..9fc12263244 100644 --- a/feature/experimental/bgp/ate_tests/bgp_long_lived_graceful_restart/bgp_long_lived_graceful_restart_test.go +++ b/feature/experimental/bgp/ate_tests/bgp_long_lived_graceful_restart/bgp_long_lived_graceful_restart_test.go @@ -16,7 +16,6 @@ package bgp_long_lived_graceful_restart_test import ( "context" - "encoding/json" "fmt" "testing" "time" @@ -24,14 +23,12 @@ import ( "github.com/openconfig/featureprofiles/internal/attrs" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/gnoi" gpb "github.com/openconfig/gnmi/proto/gnmi" - gnps "github.com/openconfig/gnoi/system" - "github.com/openconfig/gnoigo/system" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" "github.com/openconfig/ondatra/gnmi/oc" "github.com/openconfig/ondatra/gnmi/oc/acl" - "github.com/openconfig/ondatra/gnoi" "github.com/openconfig/ondatra/ixnet" "github.com/openconfig/ygnmi/ygnmi" "github.com/openconfig/ygot/ygot" @@ -193,12 +190,6 @@ var ( IPv4Len: plenIPv4, IPv6Len: plenIPv6, } - routingDaemon = map[ondatra.Vendor]string{ - ondatra.JUNIPER: "rpd", - ondatra.ARISTA: "Bgp-main", - ondatra.CISCO: "emsd", - ondatra.NOKIA: "sr_bgp_mgr", - } ) func configureRoutePolicy(t *testing.T, dut *ondatra.DUTDevice, name string, pr oc.E_RoutingPolicy_PolicyResultType) { @@ -612,215 +603,6 @@ func configACLInterface(iFace *oc.Acl_Interface, ifName string) *acl.Acl_Interfa return aclConf } -// Helper function to replicate configACL() configs in native model. -// Define the values for each ACL entry and marshal for json encoding. -// Then craft a gNMI set Request to update the changes. -func configACLNative(t testing.TB, d *ondatra.DUTDevice, name string) { - t.Helper() - switch d.Vendor() { - case ondatra.NOKIA: - var aclEntry10Val = []any{ - map[string]any{ - "action": map[string]any{ - "drop": map[string]any{}, - }, - "match": map[string]any{ - "destination-ip": map[string]any{ - "prefix": ateDstCIDR, - }, - "source-ip": map[string]any{ - "prefix": aclNullPrefix, - }, - }, - }, - } - entry10Update, err := json.Marshal(aclEntry10Val) - if err != nil { - t.Fatalf("Error with json Marshal: %v", err) - } - - var aclEntry20Val = []any{ - map[string]any{ - "action": map[string]any{ - "drop": map[string]any{}, - }, - "match": map[string]any{ - "source-ip": map[string]any{ - "prefix": ateDstCIDR, - }, - "destination-ip": map[string]any{ - "prefix": aclNullPrefix, - }, - }, - }, - } - entry20Update, err := json.Marshal(aclEntry20Val) - if err != nil { - t.Fatalf("Error with json Marshal: %v", err) - } - - var aclEntry30Val = []any{ - map[string]any{ - "action": map[string]any{ - "accept": map[string]any{}, - }, - "match": map[string]any{ - "source-ip": map[string]any{ - "prefix": aclNullPrefix, - }, - "destination-ip": map[string]any{ - "prefix": aclNullPrefix, - }, - }, - }, - } - entry30Update, err := json.Marshal(aclEntry30Val) - if err != nil { - t.Fatalf("Error with json Marshal: %v", err) - } - gpbSetRequest := &gpb.SetRequest{ - Prefix: &gpb.Path{ - Origin: "srl", - }, - Update: []*gpb.Update{ - { - Path: &gpb.Path{ - Elem: []*gpb.PathElem{ - {Name: "acl"}, - {Name: "ipv4-filter", Key: map[string]string{"name": name}}, - {Name: "entry", Key: map[string]string{"sequence-id": "10"}}, - }, - }, - Val: &gpb.TypedValue{ - Value: &gpb.TypedValue_JsonIetfVal{ - JsonIetfVal: entry10Update, - }, - }, - }, - { - Path: &gpb.Path{ - Elem: []*gpb.PathElem{ - {Name: "acl"}, - {Name: "ipv4-filter", Key: map[string]string{"name": name}}, - {Name: "entry", Key: map[string]string{"sequence-id": "20"}}, - }, - }, - Val: &gpb.TypedValue{ - Value: &gpb.TypedValue_JsonIetfVal{ - JsonIetfVal: entry20Update, - }, - }, - }, - { - Path: &gpb.Path{ - Elem: []*gpb.PathElem{ - {Name: "acl"}, - {Name: "ipv4-filter", Key: map[string]string{"name": name}}, - {Name: "entry", Key: map[string]string{"sequence-id": "30"}}, - }, - }, - Val: &gpb.TypedValue{ - Value: &gpb.TypedValue_JsonIetfVal{ - JsonIetfVal: entry30Update, - }, - }, - }, - }, - } - gnmiClient := d.RawAPIs().GNMI(t) - if _, err := gnmiClient.Set(context.Background(), gpbSetRequest); err != nil { - t.Fatalf("Unexpected error configuring SRL ACL: %v", err) - } - default: - t.Fatalf("Unsupported vendor %s for deviation 'UseVendorNativeACLConfiguration'", d.Vendor()) - } -} - -// Helper function to replicate AdmitAllACL() configs in native model, -// then craft a gNMI set Request to update the changes. -func configAdmitAllACLNative(t testing.TB, d *ondatra.DUTDevice, name string) { - t.Helper() - switch d.Vendor() { - case ondatra.NOKIA: - gpbDelRequest := &gpb.SetRequest{ - Prefix: &gpb.Path{ - Origin: "srl", - }, - Delete: []*gpb.Path{ - { - Elem: []*gpb.PathElem{ - {Name: "acl"}, - {Name: "ipv4-filter", Key: map[string]string{"name": name}}, - {Name: "entry", Key: map[string]string{"sequence-id": "10"}}, - }, - }, - { - Elem: []*gpb.PathElem{ - {Name: "acl"}, - {Name: "ipv4-filter", Key: map[string]string{"name": name}}, - {Name: "entry", Key: map[string]string{"sequence-id": "20"}}, - }, - }, - }, - } - gnmiClient := d.RawAPIs().GNMI(t) - if _, err := gnmiClient.Set(context.Background(), gpbDelRequest); err != nil { - t.Fatalf("Unexpected error removing SRL ACL: %v", err) - } - default: - t.Fatalf("Unsupported vendor %s for deviation 'UseVendorNativeACLConfiguration'", d.Vendor()) - } -} - -// Helper function to replicate configACLInterface in native model. -// Set ACL at interface ingress, -// then craft a gNMI set Request to update the changes. -func configACLInterfaceNative(t *testing.T, d *ondatra.DUTDevice, ifName string) { - t.Helper() - switch d.Vendor() { - case ondatra.NOKIA: - var interfaceAclVal = []any{ - map[string]any{ - "ipv4-filter": []any{ - aclName, - }, - }, - } - interfaceAclUpdate, err := json.Marshal(interfaceAclVal) - if err != nil { - t.Fatalf("Error with json Marshal: %v", err) - } - gpbSetRequest := &gpb.SetRequest{ - Prefix: &gpb.Path{ - Origin: "srl", - }, - 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: "input"}, - }, - }, - Val: &gpb.TypedValue{ - Value: &gpb.TypedValue_JsonIetfVal{ - JsonIetfVal: interfaceAclUpdate, - }, - }, - }, - }, - } - gnmiClient := d.RawAPIs().GNMI(t) - if _, err := gnmiClient.Set(context.Background(), gpbSetRequest); err != nil { - t.Fatalf("Unexpected error configuring interface ACL: %v", err) - } - default: - t.Fatalf("Unsupported vendor %s for deviation 'UseVendorNativeACLConfiguration'", d.Vendor()) - } -} - func disableLLGRConf(dut *ondatra.DUTDevice, as int) string { switch dut.Vendor() { case ondatra.ARISTA: @@ -862,56 +644,6 @@ func removeNewPeers(t *testing.T, dut *ondatra.DUTDevice, nbrs []*bgpNeighbor) { fptest.LogQuery(t, "DUT BGP Config", dutConfPath.Config(), gnmi.Get(t, dut, dutConfPath.Config())) } -func restartRoutingProcess(t *testing.T, dut *ondatra.DUTDevice) { - t.Helper() - if _, ok := routingDaemon[dut.Vendor()]; !ok { - t.Fatalf("Please add support for vendor %v in var routingDaemon", dut.Vendor()) - } - t.Run("KillGRIBIDaemon", func(t *testing.T) { - // Find the PID of routing Daemon. - var pId uint64 - pName := routingDaemon[dut.Vendor()] - t.Run("FindroutingDaemonPid", func(t *testing.T) { - pId = findProcessByName(t, dut, pName) - if pId == 0 { - t.Fatalf("Couldn't find pid of routing daemon '%s'", pName) - } else { - t.Logf("Pid of routing daemon '%s' is '%d'", pName, pId) - } - }) - - // Kill routing daemon through gNOI Kill Request. - t.Run("ExecuteGnoiKill", func(t *testing.T) { - // TODO - pid type is uint64 in oc-system model, but uint32 in gNOI Kill Request proto. - // Until the models are brought in line, typecasting the uint64 to uint32. - gNOIKillProcess(t, dut, pName, uint32(pId)) - // Wait for a bit for routing daemon on the DUT to restart. - time.Sleep(30 * time.Second) - }) - }) -} - -// findProcessByName uses telemetry to find out the PID of a process -func findProcessByName(t *testing.T, dut *ondatra.DUTDevice, pName string) uint64 { - t.Helper() - pList := gnmi.GetAll(t, dut, gnmi.OC().System().ProcessAny().State()) - var pID uint64 - for _, proc := range pList { - if proc.GetName() == pName { - pID = proc.GetPid() - t.Logf("Pid of daemon '%s' is '%d'", pName, pID) - } - } - return pID -} - -// gNOIKillProcess kills a daemon on the DUT, given its name and pid. -func gNOIKillProcess(t *testing.T, dut *ondatra.DUTDevice, pName string, pID uint32) { - t.Helper() - killResponse := gnoi.Execute(t, dut, system.NewKillProcessOperation().Name(pName).PID(pID).Signal(gnps.KillProcessRequest_SIGNAL_TERM).Restart(true)) - t.Logf("Got kill process response: %v\n\n", killResponse) -} - // setBgpPolicy is used to configure routing policy on DUT. func setBgpPolicy(t *testing.T, dut *ondatra.DUTDevice, d *oc.Root) { t.Helper() @@ -1106,14 +838,9 @@ func TestTrafficWithGracefulRestartLLGR(t *testing.T) { startTime := time.Now() t.Log("Trigger graceful restart on ATE") ate.Actions().NewBGPGracefulRestart().WithRestartTime(grRestartTime * time.Second).WithPeers(bgpPeer).Send(t) - if deviations.UseVendorNativeACLConfig(dut) { - configACLNative(t, dut, aclName) - configACLInterfaceNative(t, dut, ifName) - } else { - gnmi.Replace(t, dut, gnmi.OC().Acl().AclSet(aclName, oc.Acl_ACL_TYPE_ACL_IPV4).Config(), configACL(d, aclName)) - aclConf := configACLInterface(iFace, ifName) - gnmi.Replace(t, dut, aclConf.Config(), iFace) - } + gnmi.Replace(t, dut, gnmi.OC().Acl().AclSet(aclName, oc.Acl_ACL_TYPE_ACL_IPV4).Config(), configACL(d, aclName)) + aclConf := configACLInterface(iFace, ifName) + gnmi.Replace(t, dut, aclConf.Config(), iFace) t.Run("Verify graceful restart telemetry", func(t *testing.T) { verifyGracefulRestart(t, dut) @@ -1158,7 +885,7 @@ func TestTrafficWithGracefulRestartLLGR(t *testing.T) { }) t.Run("Restart routing", func(t *testing.T) { - restartRoutingProcess(t, dut) + gnoi.KillProcess(t, dut, gnoi.ROUTING, gnoi.SigTerm, true, true) }) var bgpIxPeer []*ixnet.BGP @@ -1203,14 +930,9 @@ func TestTrafficWithGracefulRestartLLGR(t *testing.T) { t.Run("RemoveAclInterface", func(t *testing.T) { t.Log("Removing ACL on the interface to restore BGP GR. Traffic should now pass!") - if deviations.UseVendorNativeACLConfig(dut) { - configAdmitAllACLNative(t, dut, aclName) - configACLInterfaceNative(t, dut, ifName) - } else { - gnmi.Replace(t, dut, gnmi.OC().Acl().AclSet(aclName, oc.Acl_ACL_TYPE_ACL_IPV4).Config(), configAdmitAllACL(d, aclName)) - aclPath := configACLInterface(iFace, ifName) - gnmi.Replace(t, dut, aclPath.Config(), iFace) - } + gnmi.Replace(t, dut, gnmi.OC().Acl().AclSet(aclName, oc.Acl_ACL_TYPE_ACL_IPV4).Config(), configAdmitAllACL(d, aclName)) + aclPath := configACLInterface(iFace, ifName) + gnmi.Replace(t, dut, aclPath.Config(), iFace) }) t.Run("VerifyBGPEstablished", func(t *testing.T) { @@ -1292,14 +1014,9 @@ func TestTrafficWithGracefulRestart(t *testing.T) { startTime := time.Now() t.Log("Trigger graceful restart on ATE") ate.Actions().NewBGPGracefulRestart().WithRestartTime(grRestartTime * time.Second).WithPeers(bgpPeer).Send(t) - if deviations.UseVendorNativeACLConfig(dut) { - configACLNative(t, dut, aclName) - configACLInterfaceNative(t, dut, ifName) - } else { - gnmi.Replace(t, dut, gnmi.OC().Acl().AclSet(aclName, oc.Acl_ACL_TYPE_ACL_IPV4).Config(), configACL(d, aclName)) - aclConf := configACLInterface(iFace, ifName) - gnmi.Replace(t, dut, aclConf.Config(), iFace) - } + gnmi.Replace(t, dut, gnmi.OC().Acl().AclSet(aclName, oc.Acl_ACL_TYPE_ACL_IPV4).Config(), configACL(d, aclName)) + aclConf := configACLInterface(iFace, ifName) + gnmi.Replace(t, dut, aclConf.Config(), iFace) t.Run("Verify graceful restart telemetry", func(t *testing.T) { verifyGracefulRestart(t, dut) @@ -1336,14 +1053,9 @@ func TestTrafficWithGracefulRestart(t *testing.T) { t.Run("RemoveAclInterface", func(t *testing.T) { t.Log("Removing Acl on the interface to restore BGP GR. Traffic should now pass!") - if deviations.UseVendorNativeACLConfig(dut) { - configAdmitAllACLNative(t, dut, aclName) - configACLInterfaceNative(t, dut, ifName) - } else { - gnmi.Replace(t, dut, gnmi.OC().Acl().AclSet(aclName, oc.Acl_ACL_TYPE_ACL_IPV4).Config(), configAdmitAllACL(d, aclName)) - aclPath := configACLInterface(iFace, ifName) - gnmi.Replace(t, dut, aclPath.Config(), iFace) - } + gnmi.Replace(t, dut, gnmi.OC().Acl().AclSet(aclName, oc.Acl_ACL_TYPE_ACL_IPV4).Config(), configAdmitAllACL(d, aclName)) + aclPath := configACLInterface(iFace, ifName) + gnmi.Replace(t, dut, aclPath.Config(), iFace) }) t.Run("VerifyBGPEstablished", func(t *testing.T) { diff --git a/feature/experimental/bgp/ate_tests/bgp_long_lived_graceful_restart/metadata.textproto b/feature/experimental/bgp/ate_tests/bgp_long_lived_graceful_restart/metadata.textproto index f76012ba389..004e3e5a3e6 100644 --- a/feature/experimental/bgp/ate_tests/bgp_long_lived_graceful_restart/metadata.textproto +++ b/feature/experimental/bgp/ate_tests/bgp_long_lived_graceful_restart/metadata.textproto @@ -18,7 +18,6 @@ platform_exceptions: { vendor: NOKIA } deviations: { - use_vendor_native_acl_config: true explicit_port_speed: true explicit_interface_in_default_vrf: true interface_enabled: true diff --git a/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn/README.md b/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn/README.md index 724e759f578..b372590de53 100644 --- a/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn/README.md +++ b/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn/README.md @@ -16,14 +16,23 @@ BGP 2-Byte and 4-Byte ASN support * 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 +## OpenConfig Path and RPC Coverage +```yaml +paths: + ## Config Parameter Coverage + + /network-instances/network-instance/protocols/protocol/bgp/global/config/as: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/config/peer-as: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/config/local-as: + + ## Telemetry Parameter Coverage + + /network-instances/network-instance/protocols/protocol/bgp/global/state/as: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/state/peer-as: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/state/local-as: + +rpcs: + gnmi: + gNMI.Subscribe: + gNMI.Set: +``` diff --git a/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn/bgp_2byte_4byte_asn_test.go b/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn/bgp_2byte_4byte_asn_test.go index aa6222e648f..dcc80fbed56 100644 --- a/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn/bgp_2byte_4byte_asn_test.go +++ b/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn/bgp_2byte_4byte_asn_test.go @@ -149,7 +149,7 @@ func TestBgpSession(t *testing.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) + gnmi.Await(t, dut, nbrPath.SessionState().State(), time.Second*120, oc.Bgp_Neighbor_SessionState_ESTABLISHED) t.Log("Verify BGP AS numbers") verifyPeer(t, tc.nbr, dut) diff --git a/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn_policy_test/README.md b/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn_policy_test/README.md index 51d125b5eb9..2864d7d9409 100644 --- a/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn_policy_test/README.md +++ b/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn_policy_test/README.md @@ -32,3 +32,13 @@ BGP 2-Byte and 4-Byte ASN support with policy * /global/config/as * /neighbors/neighbor/config/peer-as * /neighbors/neighbor/config/local-as + +## OpenConfig Path and RPC Coverage + +```yaml +rpcs: + gnmi: + gNMI.Set: + gNMI.Get: + gNMI.Subscribe: +``` \ No newline at end of file diff --git a/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn_policy_test/metadata.textproto b/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn_policy_test/metadata.textproto index bc335a7ac99..eaae52b496a 100644 --- a/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn_policy_test/metadata.textproto +++ b/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn_policy_test/metadata.textproto @@ -18,7 +18,6 @@ platform_exceptions: { vendor: NOKIA } deviations: { - use_vendor_native_acl_config: true explicit_port_speed: true explicit_interface_in_default_vrf: true interface_enabled: true diff --git a/feature/experimental/bgp/otg_tests/link_bandwidth_test/README.md b/feature/experimental/bgp/otg_tests/link_bandwidth_test/README.md index 5705fd88f82..4fa01af7fec 100644 --- a/feature/experimental/bgp/otg_tests/link_bandwidth_test/README.md +++ b/feature/experimental/bgp/otg_tests/link_bandwidth_test/README.md @@ -26,7 +26,7 @@ bandwidth communities to routes based on a prefix match. * Advertise ipv4 and ipv6 prefixes to DUT port 1 using the following communities: * 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" ]`. + * prefix-set-3 with 2 ipv4 and 2 ipv6 routes with extended communities `[ "link-bandwidth:23456: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 @@ -39,19 +39,16 @@ bandwidth communities to routes based on a prefix match. * RT-7.5.2 - Validate adding and removing link-bandwidth ext-community-sets using OC model release 3.x * Configure the following extended community sets on the DUT: (prefix: `routing-policy/defined-sets/bgp-defined-sets/ext-community-sets/ext-community-set`) - * Create an ext-community-set named 'linkbw_0' with: - * ext-community-member = [ "link-bandwidth:100:0" ] * Create an ext-community-set named 'linkbw_1M' with members as follows: - * ext-community-member = [ "link-bandwidth:100:1M" ] + * ext-community-member = [ "link-bandwidth:23456:1M" ] * Create an ext-community-set named 'linkbw_2G' with members as follows: - * ext-community-member = [ "link-bandwidth:100:2G" ] + * ext-community-member = [ "link-bandwidth:23456:2G" ] * 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:.*:.*$" ] - * Create an ext-community-set named 'linkbw_any_0' with members as follows: - * ext-community-member = [ "^link-bandwidth:.*:.0" ] + * Create a `/routing-policy/policy-definitions/policy-definition/policy-definition` named **'not_match_100_set_linkbw_1M'** with the following `statements` @@ -92,6 +90,7 @@ bandwidth communities to routes based on a prefix match. * statement[name='accept_all_routes']/ * actions/config/policy-result = ACCEPT_ROUTE + * For each policy-definition created, run a subtest (RT-7.8.3.x-) to * Use gnmi Set REPLACE option for: @@ -112,36 +112,39 @@ bandwidth communities to routes based on a prefix match. * Verify expected communities are present in ATE. * Verify expected communities are present in DUT state. * Do not fail test if this path is not supported, only log results - * `/network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv4-unicast/neighbors/neighbor/adj-rib-in-post/routes/route/state/ext-community-index` - * `/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` + * `/network-instances/network-instance/protocols/protocol/bgp/rib/afi-safis/afi-safi/ipv4-unicast/neighbors/neighbor/adj-rib-in-post/routes/route/state/ext-community-index` + * `/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` + * Mark test as passing if Global Administartive valuee (ASN) of link-bakdwidth extended community **send by DUT** is either `23456` or ASN of DUT. * Expected community values for each policy | | 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" ] | - | prefix-set-3 | [ "link-bandwidth:100:0" ] | [ "link-bandwidth:100:0" ] | - - | | match_100_set_linkbw_2G | del_linkbw | rm_any_zero_bw_set_LocPref_5 | - | ------------ | ----------------------------------------------- | ------------- | ---------------------------- | - | prefix-set-1 | [ none ] | [none] | [none] | - | prefix-set-2 | [ "100:100", "link-bandwidth:100:2000000000" ] | [ "100:100" ] | [ "100:100" ] | - | prefix-set-3 | [ "link-bandwidth:100:0" ] | [ none ] | [ none ], localpref=5 | + | prefix-set-1 | *DEPRECATED* | [none] | + | prefix-set-2 | *DEPRECATED* | [ "100:100" ] | + | prefix-set-3 | *DEPRECATED* | [ "link-bandwidth:23456:0" ] | - * LocalPreference - The prefixes of "prefix-set-3" matching policy "rm_any_zero_bw_set_LocPref_5" should have Local Preference value 5.\ - All other prefixes, Local Preference should be none or 100 (standard default).\ - For all other policies, Local Preference should be none or 100 (standard default) + | | match_100_set_linkbw_2G | del_linkbw | rm_any_zero_bw_set_LocPref_5 | + | ------------ | ------------------------------------------------- | ------------- | ---------------------------- | + | prefix-set-1 | [ none ] | [none] | *DEPRECATED* | + | prefix-set-2 | [ "100:100", "link-bandwidth:23456:2000000000" ] | [ "100:100" ] | *DEPRECATED* | + | prefix-set-3 | [ "link-bandwidth:23456:0" ] | [ none ] | *DEPRECATED* | * Regarding prefix-set-3 and policy "nomatch_100_set_linkbw_2G" * prefix-set-3 is advertised to the DUT with community "link-bandwidth:100:0" set. * The DUT evaluates a match for "regex_nomatch_as100". This does not match because the regex pattern does not include the link-bandwidth community type. * Community linkbw_2G should be added. + + ## OpenConfig Path and RPC Coverage 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 index dddf7f24868..e240cd817d6 100644 --- 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 @@ -15,6 +15,7 @@ package link_bandwidth_test import ( + "fmt" "strconv" "strings" "testing" @@ -22,8 +23,10 @@ import ( "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/helpers" "github.com/openconfig/featureprofiles/internal/otgutils" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" @@ -56,8 +59,8 @@ const ( v63RouteOtg = "2001:db8:128:130::" v63TrafficStart = "2001:db8:128:130::1" v6RoutePrefix = uint32(64) - dutAS = uint32(65656) - ateAS = uint32(65657) + dutAS = uint32(32001) + ateAS = uint32(32002) bgpName = "BGP" maskLenExact = "exact" localPref = 200 @@ -107,13 +110,19 @@ var ( 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", - "linkbw_2G": "link-bandwidth:100:2G", - "linkbw_any": "^link-bandwidth:.*:.$", - "linkbw_any_0": "^link-bandwidth:.*:0$", + "linkbw_1M": "link-bandwidth:23456:1M", + "linkbw_2G": "link-bandwidth:23456:2G", + "linkbw_any": "^link-bandwidth:.*:.$", + } + + extCommunitySetCisco = map[string]string{ + "linkbw_1M": "23456:1000000", + "linkbw_2G": "23456:2000000000", + "linkbw_any": "^.*:.$", } + CommunitySet = map[string]string{ "regex_match_comm100": "^100:.*$", } @@ -168,28 +177,21 @@ func TestBGPLinkBandwidth(t *testing.T) { 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) + validateRouteCommunityV4 func(t *testing.T, td testData, ec extCommunity) + validateRouteCommunityV6 func(t *testing.T, td testData, ec extCommunity) } baseSetupConfigAndVerification(t, td) configureExtCommunityRoutingPolicy(t, dut) + if deviations.BgpExplicitExtendedCommunityEnable(dut) { + enableExtCommunityCLIConfig(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"}, + routeCommunity: extCommunity{prefixSet1Comm: "none", prefixSet2Comm: "100:100", prefixSet3Comm: "link-bandwidth:23456:0"}, localPerf: false, validateRouteCommunityV4: validateRouteCommunityV4, validateRouteCommunityV6: validateRouteCommunityV6, @@ -199,7 +201,7 @@ func TestBGPLinkBandwidth(t *testing.T) { policyName: "match_100_set_linkbw_2G", applyPolicy: applyPolicyDut, validate: validatPolicyDut, - routeCommunity: extCommunity{prefixSet1Comm: "none", prefixSet2Comm: "link-bandwidth:100:2000000000", prefixSet3Comm: "link-bandwidth:100:0"}, + routeCommunity: extCommunity{prefixSet1Comm: "none", prefixSet2Comm: "link-bandwidth:23456:2000000000", prefixSet3Comm: "link-bandwidth:23456:0"}, localPerf: false, validateRouteCommunityV4: validateRouteCommunityV4, validateRouteCommunityV6: validateRouteCommunityV6, @@ -214,28 +216,29 @@ func TestBGPLinkBandwidth(t *testing.T) { 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) + tc.validateRouteCommunityV4(t, td, tc.routeCommunity) + tc.validateRouteCommunityV6(t, td, tc.routeCommunity) }) } } +func enableExtCommunityCLIConfig(t *testing.T, dut *ondatra.DUTDevice) { + var extCommunityEnableCLIConfig string + switch dut.Vendor() { + case ondatra.CISCO: + extCommunityEnableCLIConfig = fmt.Sprintf("router bgp %v instance BGP neighbor-group %v \n ebgp-recv-extcommunity-dmz \n ebgp-send-extcommunity-dmz\n", dutAS, cfgplugins.BGPPeerGroup1) + default: + t.Fatalf("Unsupported vendor %s for deviation 'BgpExplicitExtendedCommunityEnable'", dut.Vendor()) + } + helpers.GnmiCLIConfig(t, dut, extCommunityEnableCLIConfig) +} + func applyPolicyDut(t *testing.T, dut *ondatra.DUTDevice, policyName string) { // Apply ipv4 policy to bgp neighbour. root := &oc.Root{} @@ -253,6 +256,17 @@ func applyPolicyDut(t *testing.T, dut *ondatra.DUTDevice, policyName string) { 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) + + ni := root.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + niProto := ni.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + bgp := niProto.GetOrCreateBgp() + bgp.GetOrCreatePeerGroup(cfgplugins.BGPPeerGroup1).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) + bgp.GetOrCreatePeerGroup(cfgplugins.BGPPeerGroup1).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + bgpNbrV4 := bgp.GetOrCreateNeighbor(atePort1.IPv4) + bgpNbrV4.PeerGroup = ygot.String(cfgplugins.BGPPeerGroup1) + bgpNbrV6 := bgp.GetOrCreateNeighbor(atePort1.IPv6) + bgpNbrV6.PeerGroup = ygot.String(cfgplugins.BGPPeerGroup1) + gnmi.Update(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Config(), niProto) } func validatPolicyDut(t *testing.T, dut *ondatra.DUTDevice, policyName string) { @@ -265,18 +279,18 @@ func validatPolicyDut(t *testing.T, dut *ondatra.DUTDevice, policyName string) { } } -func validateRouteCommunityV4(t *testing.T, td testData, ec extCommunity, localPerf bool) { +func validateRouteCommunityV4(t *testing.T, td testData, ec extCommunity) { prefixes := map[string]string{ v41Route: ec.prefixSet1Comm, v42Route: ec.prefixSet2Comm, v43Route: ec.prefixSet3Comm, } for prefix, community := range prefixes { - validateRouteCommunityV4Prefix(t, td, community, prefix, localPerf) + validateRouteCommunityV4Prefix(t, td, community, prefix) } } -func validateRouteCommunityV4Prefix(t *testing.T, td testData, community, v4Prefix string, localPerf bool) { +func validateRouteCommunityV4Prefix(t *testing.T, td testData, community, v4Prefix string) { _, ok := gnmi.WatchAll(t, td.ate.OTG(), gnmi.OTG().BgpPeer(td.otgP2.Name()+".BGP4.peer").UnicastIpv4PrefixAny().State(), @@ -296,9 +310,6 @@ func validateRouteCommunityV4Prefix(t *testing.T, td testData, community, v4Pref if len(bgpPrefix.Community) != 0 || len(bgpPrefix.ExtendedCommunity) != 0 { t.Errorf("community and ext communituy are not empty it should be none") } - if localPerf && bgpPrefix.GetLocalPreference() != localPerfCfg && bgpPrefix.GetAddress() == v43Route { - t.Errorf("local preference is not %d got %d", localPerfCfg, bgpPrefix.GetLocalPreference()) - } case "100:100": for _, gotCommunity := range bgpPrefix.Community { t.Logf("community AS:%d val: %d", gotCommunity.GetCustomAsNumber(), gotCommunity.GetCustomAsValue()) @@ -307,22 +318,30 @@ func validateRouteCommunityV4Prefix(t *testing.T, td testData, community, v4Pref } } default: + if len(bgpPrefix.ExtendedCommunity) == 0 { + t.Errorf("ERROR extended community is empty, expected %v", community) + return + } for _, ec := range bgpPrefix.ExtendedCommunity { lbSubType := ec.Structured.NonTransitive_2OctetAsType.LinkBandwidthSubtype listCommunity := strings.Split(community, ":") Bandwidth := listCommunity[2] + if lbSubType.GetGlobal_2ByteAs() != 23456 && lbSubType.GetGlobal_2ByteAs() != 32002 && lbSubType.GetGlobal_2ByteAs() != 32001 { + t.Errorf("ERROR AS number should be 23456 or %d got %d", ateAS, lbSubType.GetGlobal_2ByteAs()) + return + } if Bandwidth == "0" { - if lbSubType.GetGlobal_2ByteAs() != 100 || ygot.BinaryToFloat32(lbSubType.GetBandwidth()) != 0 { - t.Errorf("ERROR lb AS want 100, got :%d, Bandwidth want 0, got:=%v", lbSubType.GetGlobal_2ByteAs(), ygot.BinaryToFloat32(lbSubType.GetBandwidth())) + if ygot.BinaryToFloat32(lbSubType.GetBandwidth()) != 0 { + t.Errorf("ERROR lb Bandwidth want 0, got:=%v", ygot.BinaryToFloat32(lbSubType.GetBandwidth())) } } else { - if lbSubType.GetGlobal_2ByteAs() != 100 || ygot.BinaryToFloat32(lbSubType.GetBandwidth()) != 2000000000 { - t.Errorf("ERROR lb AS want 100, got :%d, Bandwidth want :2G, got=%v", lbSubType.GetGlobal_2ByteAs(), ygot.BinaryToFloat32(lbSubType.GetBandwidth())) + if ygot.BinaryToFloat32(lbSubType.GetBandwidth()) != 2.5e+08 && ygot.BinaryToFloat32(lbSubType.GetBandwidth()) != 2000000000 { + t.Errorf("ERROR lb Bandwidth want :2G, got=%v", ygot.BinaryToFloat32(lbSubType.GetBandwidth())) } } - } - if deviations.BgpExtendedCommunityIndexUnsupported(td.dut) { - verifyExtCommunityIndexV4(t, td, v4Prefix) + if !deviations.BgpExtendedCommunityIndexUnsupported(td.dut) { + verifyExtCommunityIndexV4(t, td, v4Prefix) + } } } } @@ -330,18 +349,18 @@ func validateRouteCommunityV4Prefix(t *testing.T, td testData, community, v4Pref } } -func validateRouteCommunityV6(t *testing.T, td testData, ec extCommunity, localPerf bool) { +func validateRouteCommunityV6(t *testing.T, td testData, ec extCommunity) { prefixes := map[string]string{ v61RouteOtg: ec.prefixSet1Comm, v62RouteOtg: ec.prefixSet2Comm, v63RouteOtg: ec.prefixSet3Comm, } for prefix, community := range prefixes { - validateRouteCommunityV6Prefix(t, td, community, prefix, localPerf) + validateRouteCommunityV6Prefix(t, td, community, prefix) } } -func validateRouteCommunityV6Prefix(t *testing.T, td testData, community, v6Prefix string, localPerf bool) { +func validateRouteCommunityV6Prefix(t *testing.T, td testData, community, v6Prefix string) { // This function to verify received route communities on ATE ports. _, ok := gnmi.WatchAll(t, @@ -362,9 +381,6 @@ func validateRouteCommunityV6Prefix(t *testing.T, td testData, community, v6Pref if len(bgpPrefix.Community) != 0 || len(bgpPrefix.ExtendedCommunity) != 0 { t.Errorf("community and ext community are not empty it should be none") } - if localPerf && bgpPrefix.GetLocalPreference() != localPerfCfg { - t.Errorf("local preference is not %d got %d", localPerfCfg, bgpPrefix.GetLocalPreference()) - } case "100:100": for _, gotCommunity := range bgpPrefix.Community { t.Logf("community AS:%d val: %d", gotCommunity.GetCustomAsNumber(), gotCommunity.GetCustomAsValue()) @@ -373,29 +389,36 @@ func validateRouteCommunityV6Prefix(t *testing.T, td testData, community, v6Pref } } default: + if len(bgpPrefix.ExtendedCommunity) == 0 { + t.Errorf("ERROR extended community is empty, expected %v", community) + return + } for _, ec := range bgpPrefix.ExtendedCommunity { lbSubType := ec.Structured.NonTransitive_2OctetAsType.LinkBandwidthSubtype listCommunity := strings.Split(community, ":") Bandwidth := listCommunity[2] + if lbSubType.GetGlobal_2ByteAs() != 23456 && lbSubType.GetGlobal_2ByteAs() != 32002 && lbSubType.GetGlobal_2ByteAs() != 32001 { + t.Errorf("ERROR AS number should be 23456 or %d got %d", ateAS, lbSubType.GetGlobal_2ByteAs()) + return + } if Bandwidth == "0" { - if lbSubType.GetGlobal_2ByteAs() != 100 || ygot.BinaryToFloat32(lbSubType.GetBandwidth()) != 0 { - t.Errorf("ERROR lb AS want 100, got :%d, Bandwidth want 0, got:=%v", lbSubType.GetGlobal_2ByteAs(), ygot.BinaryToFloat32(lbSubType.GetBandwidth())) + if ygot.BinaryToFloat32(lbSubType.GetBandwidth()) != 0 { + t.Errorf("ERROR lb Bandwidth want 0, got:=%v", ygot.BinaryToFloat32(lbSubType.GetBandwidth())) } } else { - if lbSubType.GetGlobal_2ByteAs() != 100 || ygot.BinaryToFloat32(lbSubType.GetBandwidth()) != 2000000000 { - t.Errorf("ERROR lb AS want 100, got :%d, Bandwidth want :2G, got=%v", lbSubType.GetGlobal_2ByteAs(), ygot.BinaryToFloat32(lbSubType.GetBandwidth())) + if ygot.BinaryToFloat32(lbSubType.GetBandwidth()) != 2.5e+08 && ygot.BinaryToFloat32(lbSubType.GetBandwidth()) != 2000000000 { + t.Errorf("ERROR lb Bandwidth want :2G, got=%v", ygot.BinaryToFloat32(lbSubType.GetBandwidth())) } } - } - if deviations.BgpExtendedCommunityIndexUnsupported(td.dut) { - verifyExtCommunityIndexV6(t, td, v6Prefix) + if !deviations.BgpExtendedCommunityIndexUnsupported(td.dut) { + verifyExtCommunityIndexV6(t, td, v6Prefix) + } } } } } } } - func configureImportRoutingPolicyAllowAll(t *testing.T, dut *ondatra.DUTDevice) { root := &oc.Root{} rp := root.GetOrCreateRoutingPolicy() @@ -484,47 +507,66 @@ func validateImportRoutingPolicyAllowAll(t *testing.T, dut *ondatra.DUTDevice, a 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) + var communitySetCLIConfig string + var extCommunitySetCLIConfig string + switch dut.Vendor() { + case ondatra.CISCO: + extCommunitySet = extCommunitySetCisco + default: + t.Logf("extCommunitySet = %v", extCommunitySet) + } + if !deviations.BgpExtendedCommunitySetUnsupported(dut) { + 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) } - stmt.SetExtCommunityMember([]string{community}) - gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) - } - for name, community := range CommunitySet { - rp := root.GetOrCreateRoutingPolicy() - pdef := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets() - stmt, err := pdef.NewCommunitySet(name) - if err != nil { - t.Fatalf("NewCommunitySet failed: %v", err) + } else { + switch dut.Vendor() { + case ondatra.CISCO: + for name, community := range extCommunitySet { + if name == "linkbw_any" && deviations.CommunityMemberRegexUnsupported(dut) { + communitySetCLIConfig = fmt.Sprintf("community-set %v \n dfa-regex '%v' \n end-set", name, community) + helpers.GnmiCLIConfig(t, dut, communitySetCLIConfig) + } else { + extCommunitySetCLIConfig = fmt.Sprintf("extcommunity-set bandwidth %v \n %v \n end-set", name, community) + helpers.GnmiCLIConfig(t, dut, extCommunitySetCLIConfig) + } + } + default: + t.Fatalf("Unsupported vendor %s for native command support for deviation 'BgpExtendedCommunitySetUnsupported'", dut.Vendor()) } - cs := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} - cs = append(cs, oc.UnionString(community)) - stmt.SetCommunityMember(cs) - 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().SetExtCommunitySetRefs([]string{"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) + if !(deviations.CommunityMemberRegexUnsupported(dut)) { + for name, community := range CommunitySet { + rp := root.GetOrCreateRoutingPolicy() + pdef := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets() + stmt, err := pdef.NewCommunitySet(name) + if err != nil { + t.Fatalf("NewCommunitySet failed: %v", err) + } + cs := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} + cs = append(cs, oc.UnionString(community)) + stmt.SetCommunityMember(cs) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + } + } else { + switch dut.Vendor() { + case ondatra.CISCO: + for name, community := range CommunitySet { + communitySetCLIConfig = fmt.Sprintf("community-set %v\n dfa-regex '%v' \n end-set", name, community) + helpers.GnmiCLIConfig(t, dut, communitySetCLIConfig) + } + default: + t.Fatalf("Unsupported vendor %s for native cmd support for deviation 'CommunityMemberRegexUnsupported'", dut.Vendor()) + } } - 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() @@ -533,22 +575,59 @@ func configureExtCommunityRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { if err != nil { t.Fatalf("AppendNewStatement 1-megabit-match failed: %v", err) } - ref = pdef2Stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetExtCommunity() - ref.GetOrCreateReference().SetExtCommunitySetRefs([]string{"linkbw_1M"}) - ref.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) - ref.SetMethod(oc.SetCommunity_Method_REFERENCE) - if !deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { + + if !deviations.BgpSetExtCommunitySetRefsUnsupported(dut) { + ref := pdef2Stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetExtCommunity() + ref.GetOrCreateReference().SetExtCommunitySetRefs([]string{"linkbw_1M"}) + ref.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) + ref.SetMethod(oc.SetCommunity_Method_REFERENCE) + } + if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { + switch dut.Vendor() { + case ondatra.ARISTA: + name1, community1 := "regex_match_comm100_deviation1", "^100:.*$" + rpDev1 := root.GetOrCreateRoutingPolicy() + pdefDev1 := rpDev1.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets() + stmtDev1, err := pdefDev1.NewCommunitySet(name1) + if err != nil { + t.Fatalf("NewCommunitySet failed: %v", err) + } + cs := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} + cs = append(cs, oc.UnionString(community1)) + stmtDev1.SetCommunityMember(cs) + stmtDev1.SetMatchSetOptions(oc.BgpPolicy_MatchSetOptionsType_INVERT) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rpDev1) + ref1 := pdef2Stmt1.GetOrCreateConditions().GetOrCreateBgpConditions() + ref1.SetCommunitySet("regex_match_comm100_deviation1") + } + } else { ref1 := pdef2Stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet() ref1.SetCommunitySet("regex_match_comm100") ref1.SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsType_INVERT) } - pdef2Stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_NEXT_STATEMENT) + if !deviations.SkipSettingStatementForPolicy(dut) { + 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) + + if !deviations.BgpSetExtCommunitySetRefsUnsupported(dut) { + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rpNotMatch) + } else { + switch dut.Vendor() { + case ondatra.CISCO: + var communityCLIConfig string + communityCLIConfig = fmt.Sprintf("community-set %v\n dfa-regex '%v', \n match invert \n end-set", "regex_match_comm100", CommunitySet["regex_match_comm100"]) + policySetCLIConfig := fmt.Sprintf("route-policy %v \n #statement-1 1-megabit-match \n if community is-empty then \n pass \n elseif community in %v then \n set extcommunity bandwidth %v \n endif \n pass \n #statement-2 accept_all_routes \n done \n end-policy", "not_match_100_set_linkbw_1M", "regex_match_comm100", "linkbw_1M") + helpers.GnmiCLIConfig(t, dut, communityCLIConfig) + helpers.GnmiCLIConfig(t, dut, policySetCLIConfig) + default: + t.Fatalf("Unsupported vendor %s for native cmd support for deviation 'BgpSetExtCommunitySetRefsUnsupported'", dut.Vendor()) + } + } // Configure routing policy match_100_set_linkbw_2G. rpMatch := root.GetOrCreateRoutingPolicy() @@ -557,22 +636,56 @@ func configureExtCommunityRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { if err != nil { t.Fatalf("AppendNewStatement match_100_set_linkbw_2G failed: %v", err) } - ref = pdef3Stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetExtCommunity() - ref.GetOrCreateReference().SetExtCommunitySetRefs([]string{"linkbw_2G"}) - ref.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) - ref.SetMethod(oc.SetCommunity_Method_REFERENCE) - if !deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { + if !deviations.BgpSetExtCommunitySetRefsUnsupported(dut) { + ref := pdef3Stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetExtCommunity() + ref.GetOrCreateReference().SetExtCommunitySetRefs([]string{"linkbw_2G"}) + ref.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_ADD) + ref.SetMethod(oc.SetCommunity_Method_REFERENCE) + } + if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { + switch dut.Vendor() { + case ondatra.ARISTA: + name2, community2 := "regex_match_comm100_deviation2", "^100:.*$" + rpDev2 := root.GetOrCreateRoutingPolicy() + pdefDev2 := rpDev2.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets() + stmtDev2, err := pdefDev2.NewCommunitySet(name2) + if err != nil { + t.Fatalf("NewCommunitySet failed: %v", err) + } + cs := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} + cs = append(cs, oc.UnionString(community2)) + stmtDev2.SetCommunityMember(cs) + stmtDev2.SetMatchSetOptions(oc.BgpPolicy_MatchSetOptionsType_ANY) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rpDev2) + ref1 := pdef3Stmt1.GetOrCreateConditions().GetOrCreateBgpConditions() + ref1.SetCommunitySet("regex_match_comm100_deviation2") + } + } else { ref1 := pdef3Stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetMatchCommunitySet() ref1.SetCommunitySet("regex_match_comm100") ref1.SetMatchSetOptions(oc.RoutingPolicy_MatchSetOptionsType_ANY) } - pdef3Stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_NEXT_STATEMENT) + if !deviations.SkipSettingStatementForPolicy(dut) { + 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) + if !deviations.BgpSetExtCommunitySetRefsUnsupported(dut) { + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rpMatch) + } else { + switch dut.Vendor() { + case ondatra.CISCO: + communitySetCLIConfig = fmt.Sprintf("community-set %v\n dfa-regex '%v', \n match any \n end-set", "regex_match_any_comm100", CommunitySet["regex_match_comm100"]) + helpers.GnmiCLIConfig(t, dut, communitySetCLIConfig) + communitySetCLIConfig = fmt.Sprintf("route-policy %v \n #statement-1 2-gigabit-match \n if community in %v then \n set extcommunity bandwidth %v \n endif \n pass \n #statement-2 accept_all_routes \n done \n end-policy", "match_100_set_linkbw_2G", "regex_match_any_comm100", "linkbw_2G") + helpers.GnmiCLIConfig(t, dut, communitySetCLIConfig) + default: + t.Fatalf("Unsupported vendor %s for native cmd support for deviation 'BgpSetExtCommunitySetRefsUnsupported' and 'BGPConditionsMatchCommunitySetUnsupported' and 'SkipSettingStatementForPolicy'", dut.Vendor()) + } + } // Configure routing policy del_linkbw. rpDelLinkbw := root.GetOrCreateRoutingPolicy() @@ -581,42 +694,32 @@ func configureExtCommunityRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { if err != nil { t.Fatalf("AppendNewStatement del_linkbw failed: %v", err) } - ref = pdef4Stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetExtCommunity() - ref.GetOrCreateReference().SetExtCommunitySetRefs([]string{"linkbw_any"}) - ref.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_REMOVE) - ref.SetMethod(oc.SetCommunity_Method_REFERENCE) - pdef4Stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_NEXT_STATEMENT) + if !deviations.BgpDeleteLinkBandwidthUnsupported(dut) { + ref := pdef4Stmt1.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetExtCommunity() + ref.GetOrCreateReference().SetExtCommunitySetRefs([]string{"linkbw_any"}) + ref.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_REMOVE) + ref.SetMethod(oc.SetCommunity_Method_REFERENCE) + } + if !deviations.SkipSettingStatementForPolicy(dut) { + 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().SetExtCommunitySetRefs([]string{"linkbw_any_0"}) - ref.SetOptions(oc.BgpPolicy_BgpSetCommunityOptionType_REMOVE) - ref.SetMethod(oc.SetCommunity_Method_REFERENCE) - if !deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { - 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) + if !deviations.BgpDeleteLinkBandwidthUnsupported(dut) { + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rpDelLinkbw) + } else { + var delLinkbwCLIConfig string + switch dut.Vendor() { + case ondatra.CISCO: + delLinkbwCLIConfig = fmt.Sprintf("route-policy %v\n delete extcommunity bandwidth all\n end-policy", "del_linkbw") + default: + t.Fatalf("Unsupported vendor %s for native cmd support for deviation 'BgpDeleteLinkBandwidthUnsupported'", dut.Vendor()) + } + helpers.GnmiCLIConfig(t, dut, delLinkbwCLIConfig) } - - 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) { @@ -697,14 +800,18 @@ func (td *testData) advertiseRoutesWithEBGP(t *testing.T) { 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) + if !deviations.SkipBgpSendCommunityType(td.dut) { + nV42.SetSendCommunityType([]oc.E_Bgp_CommunityType{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.SetSendCommunity(oc.Bgp_CommunityType_BOTH) + if !deviations.SkipBgpSendCommunityType(td.dut) { + nV62.SetSendCommunityType([]oc.E_Bgp_CommunityType{oc.Bgp_CommunityType_BOTH}) + } 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) @@ -741,12 +848,12 @@ func (td *testData) advertiseRoutesWithEBGP(t *testing.T) { 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.SetGlobal2ByteAs(23456) 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.SetGlobal2ByteAs(23456) extcommv6.SetBandwidth(0) // Configure iBGP on OTG port2. @@ -836,15 +943,11 @@ func baseSetupConfigAndVerification(t *testing.T, td testData) { 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-dev1", dstIP: v61TrafficStart}) + createFlowV6(t, td, flowConfig{src: atePort2, dstNw: "v6-bgpNet-dev2", dstIP: v62TrafficStart}) createFlowV6(t, td, flowConfig{src: atePort2, dstNw: "v6-bgpNet-dev3", dstIP: v63TrafficStart}) checkTraffic(t, td, v6Flow) } diff --git a/feature/experimental/bgp/otg_tests/link_bandwidth_test/metadata.textproto b/feature/experimental/bgp/otg_tests/link_bandwidth_test/metadata.textproto index 5e5b04d3695..5d2b671575b 100644 --- a/feature/experimental/bgp/otg_tests/link_bandwidth_test/metadata.textproto +++ b/feature/experimental/bgp/otg_tests/link_bandwidth_test/metadata.textproto @@ -21,5 +21,21 @@ platform_exceptions: { bgp_extended_community_index_unsupported: true } } +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + bgp_extended_community_set_unsupported: true + community_member_regex_unsupported: true + skip_setting_statement_for_policy: true + bgp_set_ext_community_set_refs_unsupported: true + bgp_delete_link_bandwidth_unsupported: true + skip_bgp_send_community_type: true + bgp_extended_community_index_unsupported: true + bgp_conditions_match_community_set_unsupported: true + bgp_explicit_extended_community_enable: true + } +} tags: TAGS_AGGREGATION tags: TAGS_DATACENTER_EDGE diff --git a/feature/experimental/gribi/otg_tests/backup_nhg_action_pbf/README.md b/feature/experimental/gribi/otg_tests/backup_nhg_action_pbf/README.md index a9e511a5fa8..b816261f0c6 100644 --- a/feature/experimental/gribi/otg_tests/backup_nhg_action_pbf/README.md +++ b/feature/experimental/gribi/otg_tests/backup_nhg_action_pbf/README.md @@ -37,7 +37,7 @@ Different test scenarios requires different setups. * Connect a gRIBI client to DUT with session parameters `{persistence=PRESERVE Redundancy=SINGLE_PRIMARY}` * gRIBI Flush the DUT. - * Inject the following gRIBI structure to the DTU: + * Inject the following gRIBI structure to the DUT: ```text VIP_1/32 {DEFAULT VRF} --> NHG#1 --> NH#1 {next-hop: ATEPort2IP} @@ -54,7 +54,7 @@ Different test scenarios requires different setups. * Connect a gRIBI client to DUT with session parameters `{persistence=PRESERVE Redundancy=SINGLE_PRIMARY}` * gRIBI Flush the DUT. - * Inject the following gRIBI structure to the DTU: + * Inject the following gRIBI structure to the DUT: ```text VIP_1/32 {DEFAULT VRF} --> NHG#1 --> NH#1 {next-hop: ATEPort2IP} @@ -99,6 +99,34 @@ Different test scenarios requires different setups. traffic with decapsulated traffic with destination IP as `InnerDstIP_1` at ATE port-4. +* Test#3 - (tunnel viability triggers decap): + + * Deploy Setup#2 as above and inject below gRIBI structure to the DUT: + + ```text + NHG#102 --> [NH#104 {decap-encap, src:OuterSrcIP_2, dst:OuterDstIP_FAILURE, network-instance: VRF-C}, backupNHG: NHG#103] + ``` + + * Send IPinIP traffic to `OuterDstIP_1`. Validate that ATE port-2 receives + the IPinIP traffic with outer IP as `OuterDstIP_1`. + + * Shutdown DUT port-2 interface, and validate that ATE port-4 receives the + traffic with decapsulated traffic with destination IP as `InnerDstIP_1` + at ATE port-4. + +* Test#4 - (resolution failure on new tunnels triggers decap in the backup NHG): + + * Deploy Setup#2 as above. + + * Remove `OuterDstIP_2/32` from `VRF-C`. + + * Send IPinIP traffic to `OuterDstIP_1`. Validate that ATE port-2 receives + the IPinIP traffic with outer IP as `OuterDstIP_1`. + + * Shutdown DUT port-2 interface, and validate that ATE port-4 receives the + traffic with decapsulated traffic with destination IP as `InnerDstIP_1` + at ATE port-4. + ## OpenConfig Path and RPC Coverage ```yaml rpcs: diff --git a/feature/experimental/gribi/otg_tests/backup_nhg_action_pbf/backup_nhg_action_pbf_test.go b/feature/experimental/gribi/otg_tests/backup_nhg_action_pbf/backup_nhg_action_pbf_test.go index 81885d754ca..acb2cd0e964 100644 --- a/feature/experimental/gribi/otg_tests/backup_nhg_action_pbf/backup_nhg_action_pbf_test.go +++ b/feature/experimental/gribi/otg_tests/backup_nhg_action_pbf/backup_nhg_action_pbf_test.go @@ -44,6 +44,7 @@ const ( outerSrcIP2 = "203.0.113.2" innerDstIP1 = "198.18.0.1" innerSrcIP1 = "198.18.0.255" + outerDstIPFAILURE = "204.0.113.1" vip1 = "198.18.1.1" vip2 = "198.18.1.2" vrfA = "TE_VRF_111" @@ -260,6 +261,7 @@ func TestBackupNHGAction(t *testing.T) { addStaticRoute(t, dut) ate.OTG().StartProtocols(t) + vrfpolicy.ConfigureVRFSelectionPolicy(t, dut, vrfpolicy.VRFPolicyW) test := []struct { name string @@ -267,14 +269,24 @@ func TestBackupNHGAction(t *testing.T) { fn func(ctx context.Context, t *testing.T, args *testArgs) }{ { - name: "testBackupDecapWithVrfPolW", - desc: "Usecase with 2 NHOP Groups - Backup Pointing to Decap with vrf policy W", - fn: testBackupDecapWithVrfPolW, + name: "testBackupDecap", + desc: "Usecase with 2 NHOP Groups - Backup Pointing to Decap", + fn: testBackupDecap, }, { - name: "testDecapEncapWithVrfPolW", - desc: "Usecase with 3 NHOP Groups - Redirect pointing to back up DecapEncap and its Backup Pointing to Decap with vrf policy W", - fn: testDecapEncapWithVrfPolW, + name: "testDecapEncap", + desc: "Usecase with 3 NHOP Groups - Redirect pointing to back up DecapEncap and its Backup Pointing to Decap", + fn: testDecapEncap, + }, + { + name: "testDecap", + desc: "Usecase with 3 NHOP Groups - DecapEncap pointing to fake destination IP that won't resolve and the backup is decap", + fn: testDecap, + }, + { + name: "testDecapBackupNHG", + desc: "Usecase with 3 NHOP Groups - Resolution failure on new tunnels triggers decap in the backup NHG", + fn: testDecapBackupNHG, }, } // Configure the gRIBI client @@ -309,6 +321,10 @@ func TestBackupNHGAction(t *testing.T) { // TE11.3 - case 1: next-hop viability triggers decap in backup NHG. func testBackupDecap(ctx context.Context, t *testing.T, args *testArgs) { + if deviations.SkipPbfWithDecapEncapVrf(args.dut) { + t.Skip("Skipping test as PBF with decap encap vrf is not supported") + } + t.Logf("Adding VIP %v/32 with NHG %d NH %d and atePort2 via gRIBI", vip1, nhg1ID, nh1ID) nh, nhOpResult := gribi.NHEntry(nh1ID, atePort2.IPv4, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) nhg, nhgOpResult := gribi.NHGEntry(nhg1ID, map[uint64]uint64{nh1ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) @@ -362,6 +378,9 @@ func testBackupDecap(ctx context.Context, t *testing.T, args *testArgs) { // TE11.3 - case 2: new tunnel viability triggers decap in the backup NHG. func testDecapEncap(ctx context.Context, t *testing.T, args *testArgs) { + if deviations.SkipPbfWithDecapEncapVrf(args.dut) { + t.Skip("Skipping test as PBF with decap encap vrf is not supported") + } t.Logf("Adding VIP1 %v/32 with NHG %d NH %d and atePort2 via gRIBI", vip1, nhg1ID, nh1ID) nh, nhOpResult := gribi.NHEntry(nh1ID, atePort2.IPv4, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) @@ -426,13 +445,13 @@ func testDecapEncap(ctx context.Context, t *testing.T, args *testArgs) { t.Logf("Create flows with dst %s", outerDstIP1) baseFlow := createFlow(t, args.ate, args.top, "BaseFlow", &atePort2) - encapFLow := createFlow(t, args.ate, args.top, "DecapEncapFlow", &atePort3) - decapFLow := createFlow(t, args.ate, args.top, "DecapFlow", &atePort4) - updateFlows(t, args.ate, []gosnappi.Flow{baseFlow, encapFLow, decapFLow}) + encapFlow := createFlow(t, args.ate, args.top, "DecapEncapFlow", &atePort3) + decapFlow := createFlow(t, args.ate, args.top, "DecapFlow", &atePort4) + updateFlows(t, args.ate, []gosnappi.Flow{baseFlow, encapFlow, decapFlow}) t.Run("ValidatePrimaryPath", func(t *testing.T) { t.Logf("Validate Primary path traffic recieved on port 2 and no traffic on other flows/ate ports") - validateTrafficFlows(t, args.ate, []gosnappi.Flow{baseFlow}, []gosnappi.Flow{encapFLow, decapFLow}, baseSrcFlowFilter, baseDstFlowFilter) + validateTrafficFlows(t, args.ate, []gosnappi.Flow{baseFlow}, []gosnappi.Flow{encapFlow, decapFlow}, baseSrcFlowFilter, baseDstFlowFilter) }) t.Log("Shutdown Port2") @@ -451,7 +470,7 @@ func testDecapEncap(ctx context.Context, t *testing.T, args *testArgs) { } t.Run("ValidateDecapEncapPath", func(t *testing.T) { t.Log("Validate traffic with encap header recieved on port 3 and no traffic on other flows/ate ports") - validateTrafficFlows(t, args.ate, []gosnappi.Flow{encapFLow}, []gosnappi.Flow{baseFlow, decapFLow}, encapSrcFlowFilter, encapDstFlowFilter) + validateTrafficFlows(t, args.ate, []gosnappi.Flow{encapFlow}, []gosnappi.Flow{baseFlow, decapFlow}, encapSrcFlowFilter, encapDstFlowFilter) }) t.Log("Shutdown Port3") @@ -471,24 +490,197 @@ func testDecapEncap(ctx context.Context, t *testing.T, args *testArgs) { } t.Run("ValidateDecapPath", func(t *testing.T) { t.Log("Validate traffic after decap is recieved on port4 and no traffic on other flows/ate ports") - validateTrafficFlows(t, args.ate, []gosnappi.Flow{decapFLow}, []gosnappi.Flow{baseFlow, encapFLow}, decapSrcFlowFliter, decapDstFlowFliter) + validateTrafficFlows(t, args.ate, []gosnappi.Flow{decapFlow}, []gosnappi.Flow{baseFlow, encapFlow}, decapSrcFlowFliter, decapDstFlowFliter) }) } -func testBackupDecapWithVrfPolW(ctx context.Context, t *testing.T, args *testArgs) { +// TE11.3 - case 3: tunnel viability triggers decap. +func testDecap(ctx context.Context, t *testing.T, args *testArgs) { if deviations.SkipPbfWithDecapEncapVrf(args.dut) { t.Skip("Skipping test as PBF with decap encap vrf is not supported") } - vrfpolicy.ConfigureVRFSelectionPolicy(t, args.dut, vrfpolicy.VRFPolicyW) - testBackupDecap(ctx, t, args) + + t.Logf("Adding VIP1 %v/32 with NHG %d NH %d and atePort2 via gRIBI", vip1, nhg1ID, nh1ID) + nh, nhOpResult := gribi.NHEntry(nh1ID, atePort2.IPv4, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + nhg, nhgOpResult := gribi.NHGEntry(nhg1ID, map[uint64]uint64{nh1ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, + []*client.OpResult{nhOpResult, nhgOpResult}) + args.client.AddIPv4(t, vip1+"/"+mask, nhg1ID, deviations.DefaultNetworkInstance(args.dut), deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + + t.Logf("Adding VIP2 %v/32 with NHG %d , NH %d and atePort3 via gRIBI", vip2, nhg2ID, nh2ID) + nh, nhOpResult = gribi.NHEntry(nh2ID, atePort3.IPv4, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + nhg, nhgOpResult = gribi.NHGEntry(nhg2ID, map[uint64]uint64{nh2ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, + []*client.OpResult{nhOpResult, nhgOpResult}) + args.client.AddIPv4(t, vip2+"/"+mask, nhg2ID, deviations.DefaultNetworkInstance(args.dut), deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + + t.Logf("Adding NHG %d with NH %d as redirect to vrfB via gRIBI", nhg100ID, nh100ID) + nh, nhOpResult = gribi.NHEntry(nh100ID, "VRFOnly", deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB, &gribi.NHOptions{VrfName: vrfB}) + nhg, nhgOpResult = gribi.NHGEntry(nhg100ID, map[uint64]uint64{nh100ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, + []*client.OpResult{nhOpResult, nhgOpResult}) + + t.Logf("Adding NHG %d NH %d with %v and backup NHG %d via gRIBI", nhg101ID, nh101ID, vip1, nhg100ID) + nh, nhOpResult = gribi.NHEntry(nh101ID, vip1, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + nhg, nhgOpResult = gribi.NHGEntry(nhg101ID, map[uint64]uint64{nh101ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB, &gribi.NHGOptions{BackupNHG: nhg100ID}) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, + []*client.OpResult{nhOpResult, nhgOpResult}) + + t.Logf("Adding an IPv4Entry for %s in VRF %s with primary VIP1, backup as VRF %s via gRIBI", outerDstIP1, vrfA, vrfB) + args.client.AddIPv4(t, outerDstIP1+"/"+mask, nhg101ID, vrfA, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + + t.Logf("Adding NHG %d with NH %d as decap and DEFAULT vrf lookup via gRIBI", nhg103ID, nh103ID) + nh, nhOpResult = gribi.NHEntry(nh103ID, "Decap", deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB, &gribi.NHOptions{VrfName: deviations.DefaultNetworkInstance(args.dut)}) + nhg, nhgOpResult = gribi.NHGEntry(nhg103ID, map[uint64]uint64{nh103ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, + []*client.OpResult{nhOpResult, nhgOpResult}) + + t.Logf("Adding NHG %d NH %d and NH as %v and backup NHG %d via gRIBI", nhg104ID, nh104ID, vip2, nhg103ID) + nh, nhOpResult = gribi.NHEntry(nh104ID, "DecapEncap", deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB, &gribi.NHOptions{Src: outerSrcIP2, Dest: outerDstIPFAILURE, VrfName: vrfC}) + nhg, nhgOpResult = gribi.NHGEntry(nhg104ID, map[uint64]uint64{nh104ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB, &gribi.NHGOptions{BackupNHG: nhg103ID}) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, + []*client.OpResult{nhOpResult, nhgOpResult}) + + t.Logf("Adding an IPv4Entry for %s in vrf %s with NHG %d via gRIBI", outerDstIP2, vrfC, nhg104ID) + args.client.AddIPv4(t, outerDstIP2+"/"+mask, nhg104ID, vrfC, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + + t.Logf("Adding NHG %d NH %d and NH as decap and encap with destination vrf as %v and backup NHG %d via gRIBI", nhg102ID, nh104ID, vrfC, nhg103ID) + nhg, nhgOpResult = gribi.NHGEntry(nhg102ID, map[uint64]uint64{nh104ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB, &gribi.NHGOptions{BackupNHG: nhg103ID}) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, []*client.OpResult{nhOpResult, nhgOpResult}) + + t.Logf("Adding an IPv4Entry for %s in vrf %s with decap and encap destiantion in %s via gRIBI", outerDstIP1, vrfB, vrfC) + args.client.AddIPv4(t, outerDstIP1+"/"+mask, nhg102ID, vrfB, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + + p2 := args.dut.Port(t, "port2") + t.Log("Capture port2 status if Up") + gnmi.Await(t, args.dut, gnmi.OC().Interface(p2.Name()).OperStatus().State(), 1*time.Minute, oc.Interface_OperStatus_UP) + operStatus := gnmi.Get(t, args.dut, gnmi.OC().Interface(p2.Name()).OperStatus().State()) + if want := oc.Interface_OperStatus_UP; operStatus != want { + t.Errorf("Get(DUT port2 oper status): got %v, want %v", operStatus, want) + } + + t.Logf("Create flows with dst %s", outerDstIP1) + baseFlow := createFlow(t, args.ate, args.top, "BaseFlow", &atePort2) + decapFlow := createFlow(t, args.ate, args.top, "DecapFlow", &atePort4) + updateFlows(t, args.ate, []gosnappi.Flow{baseFlow, decapFlow}) + + t.Run("ValidatePrimaryPath", func(t *testing.T) { + t.Logf("Validate Primary path traffic recieved on port 2 and no traffic on other flows/ate ports") + validateTrafficFlows(t, args.ate, []gosnappi.Flow{baseFlow}, []gosnappi.Flow{decapFlow}, baseSrcFlowFilter, baseDstFlowFilter) + }) + + t.Log("Shutdown Port2") + portStateAction := gosnappi.NewControlState() + linkState := portStateAction.Port().Link().SetPortNames([]string{"port2"}).SetState(gosnappi.StatePortLinkState.DOWN) + args.ate.OTG().SetControlState(t, portStateAction) + // Restore port state at end of test case. + linkState.SetState(gosnappi.StatePortLinkState.UP) + defer args.ate.OTG().SetControlState(t, portStateAction) + + t.Log("Capture port2 status if down") + gnmi.Await(t, args.dut, gnmi.OC().Interface(p2.Name()).OperStatus().State(), 1*time.Minute, oc.Interface_OperStatus_DOWN) + operStatusAfterShut := gnmi.Get(t, args.dut, gnmi.OC().Interface(p2.Name()).OperStatus().State()) + if want := oc.Interface_OperStatus_DOWN; operStatusAfterShut != want { + t.Errorf("Get(DUT port2 oper status): got %v, want %v", operStatus, want) + } + t.Run("ValidateDecapPath", func(t *testing.T) { + t.Log("Validate traffic after decap is recieved on port4 and no traffic on other flows/ate ports") + validateTrafficFlows(t, args.ate, []gosnappi.Flow{decapFlow}, []gosnappi.Flow{baseFlow}, decapSrcFlowFliter, decapDstFlowFliter) + }) } -func testDecapEncapWithVrfPolW(ctx context.Context, t *testing.T, args *testArgs) { +// TE11.3 - case 4: resolution failure on new tunnels triggers decap in the backup NHG. +func testDecapBackupNHG(ctx context.Context, t *testing.T, args *testArgs) { if deviations.SkipPbfWithDecapEncapVrf(args.dut) { t.Skip("Skipping test as PBF with decap encap vrf is not supported") } - vrfpolicy.ConfigureVRFSelectionPolicy(t, args.dut, vrfpolicy.VRFPolicyW) - testDecapEncap(ctx, t, args) + + t.Logf("Adding VIP1 %v/32 with NHG %d NH %d and atePort2 via gRIBI", vip1, nhg1ID, nh1ID) + nh, nhOpResult := gribi.NHEntry(nh1ID, atePort2.IPv4, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + nhg, nhgOpResult := gribi.NHGEntry(nhg1ID, map[uint64]uint64{nh1ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, + []*client.OpResult{nhOpResult, nhgOpResult}) + args.client.AddIPv4(t, vip1+"/"+mask, nhg1ID, deviations.DefaultNetworkInstance(args.dut), deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + + t.Logf("Adding VIP2 %v/32 with NHG %d , NH %d and atePort3 via gRIBI", vip2, nhg2ID, nh2ID) + nh, nhOpResult = gribi.NHEntry(nh2ID, atePort3.IPv4, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + nhg, nhgOpResult = gribi.NHGEntry(nhg2ID, map[uint64]uint64{nh2ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, + []*client.OpResult{nhOpResult, nhgOpResult}) + args.client.AddIPv4(t, vip2+"/"+mask, nhg2ID, deviations.DefaultNetworkInstance(args.dut), deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + + t.Logf("Adding NHG %d with NH %d as redirect to vrfB via gRIBI", nhg100ID, nh100ID) + nh, nhOpResult = gribi.NHEntry(nh100ID, "VRFOnly", deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB, &gribi.NHOptions{VrfName: vrfB}) + nhg, nhgOpResult = gribi.NHGEntry(nhg100ID, map[uint64]uint64{nh100ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, + []*client.OpResult{nhOpResult, nhgOpResult}) + + t.Logf("Adding NHG %d NH %d with %v and backup NHG %d via gRIBI", nhg101ID, nh101ID, vip1, nhg100ID) + nh, nhOpResult = gribi.NHEntry(nh101ID, vip1, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + nhg, nhgOpResult = gribi.NHGEntry(nhg101ID, map[uint64]uint64{nh101ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB, &gribi.NHGOptions{BackupNHG: nhg100ID}) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, + []*client.OpResult{nhOpResult, nhgOpResult}) + + t.Logf("Adding an IPv4Entry for %s in VRF %s with primary VIP1, backup as VRF %s via gRIBI", outerDstIP1, vrfA, vrfB) + args.client.AddIPv4(t, outerDstIP1+"/"+mask, nhg101ID, vrfA, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + + t.Logf("Adding NHG %d with NH %d as decap and DEFAULT vrf lookup via gRIBI", nhg103ID, nh103ID) + nh, nhOpResult = gribi.NHEntry(nh103ID, "Decap", deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB, &gribi.NHOptions{VrfName: deviations.DefaultNetworkInstance(args.dut)}) + nhg, nhgOpResult = gribi.NHGEntry(nhg103ID, map[uint64]uint64{nh103ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, + []*client.OpResult{nhOpResult, nhgOpResult}) + + t.Logf("Adding NHG %d NH %d and NH as %v and backup NHG %d via gRIBI", nhg104ID, nh104ID, vip2, nhg103ID) + nh, nhOpResult = gribi.NHEntry(nh104ID, vip2, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + nhg, nhgOpResult = gribi.NHGEntry(nhg104ID, map[uint64]uint64{nh104ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB, &gribi.NHGOptions{BackupNHG: nhg103ID}) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, + []*client.OpResult{nhOpResult, nhgOpResult}) + + t.Logf("Adding NHG %d NH %d and NH as decap and encap with destination vrf as %v and backup NHG %d via gRIBI", nhg102ID, nh102ID, vrfC, nhg103ID) + nh, nhOpResult = gribi.NHEntry(nh102ID, "DecapEncap", deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB, &gribi.NHOptions{Src: outerSrcIP2, Dest: outerDstIP2, VrfName: vrfC}) + nhg, nhgOpResult = gribi.NHGEntry(nhg102ID, map[uint64]uint64{nh102ID: 1}, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB, &gribi.NHGOptions{BackupNHG: nhg103ID}) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, + []*client.OpResult{nhOpResult, nhgOpResult}) + + t.Logf("Adding an IPv4Entry for %s in vrf %s with decap and encap destiantion in %s via gRIBI", outerDstIP1, vrfB, vrfC) + args.client.AddIPv4(t, outerDstIP1+"/"+mask, nhg102ID, vrfB, deviations.DefaultNetworkInstance(args.dut), fluent.InstalledInFIB) + + p2 := args.dut.Port(t, "port2") + t.Log("Capture port2 status if Up") + gnmi.Await(t, args.dut, gnmi.OC().Interface(p2.Name()).OperStatus().State(), 1*time.Minute, oc.Interface_OperStatus_UP) + operStatus := gnmi.Get(t, args.dut, gnmi.OC().Interface(p2.Name()).OperStatus().State()) + if want := oc.Interface_OperStatus_UP; operStatus != want { + t.Errorf("Get(DUT port2 oper status): got %v, want %v", operStatus, want) + } + + t.Logf("Create flows with dst %s", outerDstIP1) + baseFlow := createFlow(t, args.ate, args.top, "BaseFlow", &atePort2) + decapFlow := createFlow(t, args.ate, args.top, "DecapFlow", &atePort4) + updateFlows(t, args.ate, []gosnappi.Flow{baseFlow, decapFlow}) + + t.Run("ValidatePrimaryPath", func(t *testing.T) { + t.Logf("Validate Primary path traffic recieved on port 2 and no traffic on other flows/ate ports") + validateTrafficFlows(t, args.ate, []gosnappi.Flow{baseFlow}, []gosnappi.Flow{decapFlow}, baseSrcFlowFilter, baseDstFlowFilter) + }) + + t.Log("Shutdown Port2") + portStateAction := gosnappi.NewControlState() + linkState := portStateAction.Port().Link().SetPortNames([]string{"port2"}).SetState(gosnappi.StatePortLinkState.DOWN) + args.ate.OTG().SetControlState(t, portStateAction) + // Restore port state at end of test case. + linkState.SetState(gosnappi.StatePortLinkState.UP) + defer args.ate.OTG().SetControlState(t, portStateAction) + + t.Log("Capture port2 status if down") + gnmi.Await(t, args.dut, gnmi.OC().Interface(p2.Name()).OperStatus().State(), 1*time.Minute, oc.Interface_OperStatus_DOWN) + operStatusAfterShut := gnmi.Get(t, args.dut, gnmi.OC().Interface(p2.Name()).OperStatus().State()) + if want := oc.Interface_OperStatus_DOWN; operStatusAfterShut != want { + t.Errorf("Get(DUT port2 oper status): got %v, want %v", operStatus, want) + } + t.Run("ValidateDecapPath", func(t *testing.T) { + t.Log("Validate traffic after decap is recieved on port4 and no traffic on other flows/ate ports") + validateTrafficFlows(t, args.ate, []gosnappi.Flow{decapFlow}, []gosnappi.Flow{baseFlow}, decapSrcFlowFliter, decapDstFlowFliter) + }) } // createFlow returns a flow name from atePort1 to the dstPfx. diff --git a/feature/experimental/gribi/otg_tests/dut_daemon_failure/README.md b/feature/experimental/gribi/otg_tests/dut_daemon_failure/README.md index a27b42da6d7..6cd0a79e2a2 100644 --- a/feature/experimental/gribi/otg_tests/dut_daemon_failure/README.md +++ b/feature/experimental/gribi/otg_tests/dut_daemon_failure/README.md @@ -29,11 +29,14 @@ Ensure that gRIBI entries are persisted over daemon failure. * Issuing a gRIBI Get RPC results in 203.0.113.0/24 being returned. -## Protocol/RPC Parameter Coverage - -* gRIBI - * ModifyRequest - * GetRequest +## OpenConfig Path and RPC Coverage +```yaml +rpcs: + gribi: + gRIBI.Get: + gRIBI.Modify: + gRIBI.Flush: +``` ## Telemetry Parameter Coverage 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 a07108c8c39..1558e4252f7 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 @@ -23,16 +23,14 @@ import ( "github.com/openconfig/featureprofiles/internal/attrs" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/gnoi" "github.com/openconfig/featureprofiles/internal/gribi" "github.com/openconfig/featureprofiles/internal/otgutils" - gnps "github.com/openconfig/gnoi/system" - "github.com/openconfig/gnoigo/system" - grps "github.com/openconfig/gribi/v1/proto/service" + grpb "github.com/openconfig/gribi/v1/proto/service" "github.com/openconfig/gribigo/fluent" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" "github.com/openconfig/ondatra/gnmi/oc" - "github.com/openconfig/ondatra/gnoi" "github.com/openconfig/ygnmi/ygnmi" "github.com/openconfig/ygot/ygot" ) @@ -88,13 +86,6 @@ var ( IPv4: "192.0.2.6", IPv4Len: ipv4PrefixLen, } - - gRIBIDaemons = map[ondatra.Vendor]string{ - ondatra.ARISTA: "Gribi", - ondatra.CISCO: "emsd", - ondatra.JUNIPER: "rpd", - ondatra.NOKIA: "sr_grpc_server", - } ) // configInterfaceDUT configures the DUT interfaces. @@ -235,7 +226,7 @@ func verifyGRIBIGet(ctx context.Context, t *testing.T, clientA *gribi.Client, du entries := getResponse.GetEntry() var found bool for _, entry := range entries { - v := entry.Entry.(*grps.AFTEntry_Ipv4) + v := entry.Entry.(*grpb.AFTEntry_Ipv4) if prefix := v.Ipv4.GetPrefix(); prefix != "" { if prefix == ateDstNetCIDR { found = true @@ -249,36 +240,12 @@ func verifyGRIBIGet(ctx context.Context, t *testing.T, clientA *gribi.Client, du } } -// gNOIKillProcess kills a daemon on the DUT, given its name and pid. -func gNOIKillProcess(ctx context.Context, t *testing.T, args *testArgs, pName string, pID uint32) { - killResponse := gnoi.Execute(t, args.dut, system.NewKillProcessOperation().Name(pName).PID(pID).Signal(gnps.KillProcessRequest_SIGNAL_TERM).Restart(true)) - t.Logf("Got kill process response: %v\n\n", killResponse) -} - -// findProcessByName uses telemetry to find out the PID of a process -func findProcessByName(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, pName string) uint64 { - pList := gnmi.GetAll(t, dut, gnmi.OC().System().ProcessAny().State()) - var pID uint64 - for _, proc := range pList { - if proc.GetName() == pName { - pID = proc.GetPid() - t.Logf("Pid of daemon '%s' is '%d'", pName, pID) - } - } - return pID -} - func TestDUTDaemonFailure(t *testing.T) { start := time.Now() dut := ondatra.DUT(t, "dut") ctx := context.Background() - // Check if vendor specific gRIBI daemon name has been added to gRIBIDaemons var - if _, ok := gRIBIDaemons[dut.Vendor()]; !ok { - t.Fatalf("Please add support for vendor %v in var gRIBIDaemons", dut.Vendor()) - } - // Configure the DUT. t.Logf("Configure DUT") configureDUT(t, dut) @@ -343,30 +310,7 @@ func TestDUTDaemonFailure(t *testing.T) { }) t.Run("KillGRIBIDaemon", func(t *testing.T) { - - // Find the PID of gRIBI Daemon. - var pId uint64 - pName := gRIBIDaemons[dut.Vendor()] - t.Run("FindGRIBIDaemonPid", func(t *testing.T) { - - pId = findProcessByName(ctx, t, dut, pName) - if pId == 0 { - t.Fatalf("Couldn't find pid of gRIBI daemon '%s'", pName) - } else { - t.Logf("Pid of gRIBI daemon '%s' is '%d'", pName, pId) - } - }) - - // Kill gRIBI daemon through gNOI Kill Request. - t.Run("ExecuteGnoiKill", func(t *testing.T) { - // TODO - pid type is uint64 in oc-system model, but uint32 in gNOI Kill Request proto. - // Until the models are brought in line, typecasting the uint64 to uint32. - gNOIKillProcess(ctx, t, args, pName, uint32(pId)) - - // Wait for a bit for gRIBI daemon on the DUT to restart. - time.Sleep(30 * time.Second) - - }) + gnoi.KillProcess(t, dut, gnoi.GRIBI, gnoi.SigTerm, true, true) t.Logf("Time check: %s", time.Since(start)) diff --git a/feature/experimental/gribi/otg_tests/route_addition_during_failover_test/metadata.textproto b/feature/experimental/gribi/otg_tests/route_addition_during_failover_test/metadata.textproto index 66ccbf4fc07..f879dee9680 100644 --- a/feature/experimental/gribi/otg_tests/route_addition_during_failover_test/metadata.textproto +++ b/feature/experimental/gribi/otg_tests/route_addition_during_failover_test/metadata.textproto @@ -29,7 +29,6 @@ platform_exceptions: { } deviations: { gnoi_subcomponent_path: true - deprecated_vlan_id: true interface_enabled: true default_network_instance: "default" } diff --git a/feature/experimental/gribi/otg_tests/route_removal_during_failover_test/metadata.textproto b/feature/experimental/gribi/otg_tests/route_removal_during_failover_test/metadata.textproto index 596c16cd50d..6f5779483cf 100644 --- a/feature/experimental/gribi/otg_tests/route_removal_during_failover_test/metadata.textproto +++ b/feature/experimental/gribi/otg_tests/route_removal_during_failover_test/metadata.textproto @@ -38,7 +38,6 @@ platform_exceptions: { } deviations: { gnoi_subcomponent_path: true - deprecated_vlan_id: true interface_enabled: true default_network_instance: "default" } 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 08cdce07060..4c73a0c21cd 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 @@ -44,7 +44,6 @@ platform_exceptions: { } deviations: { gnoi_subcomponent_path: true - deprecated_vlan_id: true interface_enabled: true static_protocol_name: "STATIC" default_network_instance: "default" diff --git a/feature/experimental/isis/otg_tests/isis_interface_passive_test/README.md b/feature/experimental/isis/otg_tests/isis_interface_passive_test/README.md index ce29ac4d93c..cd87a3ba503 100644 --- a/feature/experimental/isis/otg_tests/isis_interface_passive_test/README.md +++ b/feature/experimental/isis/otg_tests/isis_interface_passive_test/README.md @@ -14,83 +14,78 @@ * Ensure that IS-IS adjacency is not coming up on the passive interface. * TODO-Verify the output of ST path displaying the interface as passive in ISIS database/adj table -## Config Parameter coverage +# OpenConfig Path and RPC Coverage +```yaml +paths: +# config +/network-instances/network-instance/protocols/protocol/isis/global/config/authentication-check: +/network-instances/network-instance/protocols/protocol/isis/global/config/net: +/network-instances/network-instance/protocols/protocol/isis/global/config/level-capability: +/network-instances/network-instance/protocols/protocol/isis/global/config/hello-padding: +/network-instances/network-instance/protocols/protocol/isis/global/afi-safi/af/config/enabled: +/network-instances/network-instance/protocols/protocol/isis/levels/level/config/level-number: +/network-instances/network-instance/protocols/protocol/isis/levels/level/config/enabled: +/network-instances/network-instance/protocols/protocol/isis/levels/level/authentication/config/enabled: +/network-instances/network-instance/protocols/protocol/isis/levels/level/authentication/config/auth-mode: +/network-instances/network-instance/protocols/protocol/isis/levels/level/authentication/config/auth-password: +/network-instances/network-instance/protocols/protocol/isis/levels/level/authentication/config/auth-type: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/config/interface-id: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/config/enabled: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/config/circuit-type: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/config/passive: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/timers/config/csnp-interval: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/timers/config/lsp-pacing-interval: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/config/level-number: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/config/passive: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/timers/config/hello-interval: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/timers/config/hello-multiplier: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/hello-authentication/config/auth-mode: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/hello-authentication/config/auth-password: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/hello-authentication/config/auth-type: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/hello-authentication/config/enabled: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/afi-safi/af/config/afi-name: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/afi-safi/af/config/safi-name: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/afi-safi/af/config/enabled: -* For prefix: +# isis telemetry +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/state/passive: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/state/passive: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/adjacency-state: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/neighbor-ipv4-address: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/neighbor-ipv6-address: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/system-id: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/area-address: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/dis-system-id: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/local-extended-circuit-id: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/multi-topology: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/neighbor-circuit-type: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/neighbor-extended-circuit-id: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/neighbor-snpa: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/nlpid: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/priority: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/restart-status: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/restart-support: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/adjacencies/adjacency/state/restart-suppress: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/afi-safi/af/state/afi-name: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/afi-safi/af/state/metric: +/network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/afi-safi/af/state/safi-name: +/network-instances/network-instance/protocols/protocol/isis/levels/level/system-level-counters/state/auth-fails: +/network-instances/network-instance/protocols/protocol/isis/levels/level/system-level-counters/state/auth-type-fails: +/network-instances/network-instance/protocols/protocol/isis/levels/level/system-level-counters/state/corrupted-lsps: +/network-instances/network-instance/protocols/protocol/isis/levels/level/system-level-counters/state/database-overloads: +/network-instances/network-instance/protocols/protocol/isis/levels/level/system-level-counters/state/exceed-max-seq-nums: +/network-instances/network-instance/protocols/protocol/isis/levels/level/system-level-counters/state/id-len-mismatch: +/network-instances/network-instance/protocols/protocol/isis/levels/level/system-level-counters/state/lsp-errors: +/network-instances/network-instance/protocols/protocol/isis/levels/level/system-level-counters/state/manual-address-drop-from-area : +/network-instances/network-instance/protocols/protocol/isis/levels/level/system-level-counters/state/max-area-address-mismatches: +/network-instances/network-instance/protocols/protocol/isis/levels/level/system-level-counters/state/own-lsp-purges: +/network-instances/network-instance/protocols/protocol/isis/levels/level/system-level-counters/state/part-changes : +/network-instances/network-instance/protocols/protocol/isis/levels/level/system-level-counters/state/seq-num-skips: +/network-instances/network-instance/protocols/protocol/isis/levels/level/system-level-counters/state/spf-runs: - * /network-instances/network-instance/protocols/protocol/isis/ - -* Parameters: - - * global/config/authentication-check - * global/config/net - * global/config/level-capability - * global/config/hello-padding - * global/afi-safi/af/config/enabled - * levels/level/config/level-number - * levels/level/config/enabled - * levels/level/authentication/config/enabled - * levels/level/authentication/config/auth-mode - * levels/level/authentication/config/auth-password - * levels/level/authentication/config/auth-type - * interfaces/interface/config/interface-id - * interfaces/interface/config/enabled - * interfaces/interface/config/circuit-type - * interfaces/interface/config/passive - * interfaces/interface/timers/config/csnp-interval - * interfaces/interface/timers/config/lsp-pacing-interval - * interfaces/interface/levels/level/config/level-number - * interfaces/interface/levels/level/config/passive - * interfaces/interface/levels/level/timers/config/hello-interval - * interfaces/interface/levels/level/timers/config/hello-multiplier - * interfaces/interface/levels/level/hello-authentication/config/auth-mode - * interfaces/interface/levels/level/hello-authentication/config/auth-password - * interfaces/interface/levels/level/hello-authentication/config/auth-type - * interfaces/interface/levels/level/hello-authentication/config/enabled - * interfaces/interface/afi-safi/af/config/afi-name - * interfaces/interface/afi-safi/af/config/safi-name - * interfaces/interface/afi-safi/af/config/enabled - -## Telemetry Parameter coverage - -* For prefix: - - * /network-instances/network-instance/protocols/protocol/isis/ - -* Parameters: - - * interfaces/interface/state/passive - * interfaces/interface/levels/level/state/passive - * interfaces/interface/levels/level/adjacencies/adjacency/state/adjacency-state - * interfaces/interface/levels/level/adjacencies/adjacency/state/neighbor-ipv4-address - * interfaces/interface/levels/level/adjacencies/adjacency/state/neighbor-ipv6-address - * interfaces/interface/levels/level/adjacencies/adjacency/state/system-id - * interfaces/interface/levels/level/adjacencies/adjacency/state/area-address - * interfaces/interface/levels/level/adjacencies/adjacency/state/dis-system-id - * interfaces/interface/levels/level/adjacencies/adjacency/state/local-extended-circuit-id - * interfaces/interface/levels/level/adjacencies/adjacency/state/multi-topology - * interfaces/interface/levels/level/adjacencies/adjacency/state/neighbor-circuit-type - * interfaces/interface/levels/level/adjacencies/adjacency/state/neighbor-extended-circuit-id - * interfaces/interface/levels/level/adjacencies/adjacency/state/neighbor-snpa - * interfaces/interface/levels/level/adjacencies/adjacency/state/nlpid - * interfaces/interface/levels/level/adjacencies/adjacency/state/priority - * interfaces/interface/levels/level/adjacencies/adjacency/state/restart-status - * interfaces/interface/levels/level/adjacencies/adjacency/state/restart-support - * interfaces/interface/levels/level/adjacencies/adjacency/state/restart-suppress - * interfaces/interface/levels/level/afi-safi/af/state/afi-name - * interfaces/interface/levels/level/afi-safi/af/state/metric - * interfaces/interface/levels/level/afi-safi/af/state/safi-name - * interfaces/interface/levels/level/afi-safi/af/state/metric - * levels/level/system-level-counters/state/auth-fails - * levels/level/system-level-counters/state/auth-type-fails - * levels/level/system-level-counters/state/corrupted-lsps - * levels/level/system-level-counters/state/database-overloads - * levels/level/system-level-counters/state/exceed-max-seq-nums - * levels/level/system-level-counters/state/id-len-mismatch - * levels/level/system-level-counters/state/lsp-errors - * levels/level/system-level-counters/state/manual-address-drop-from-area - * levels/level/system-level-counters/state/max-area-address-mismatches - * levels/level/system-level-counters/state/own-lsp-purges - * levels/level/system-level-counters/state/part-changes - * levels/level/system-level-counters/state/seq-num-skips - * levels/level/system-level-counters/state/spf-runs +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: +``` diff --git a/feature/experimental/isis/otg_tests/isis_interface_passive_test/isis_interface_passive_test.go b/feature/experimental/isis/otg_tests/isis_interface_passive_test/isis_interface_passive_test.go index 48200bfbf3b..725c6fab80a 100644 --- a/feature/experimental/isis/otg_tests/isis_interface_passive_test/isis_interface_passive_test.go +++ b/feature/experimental/isis/otg_tests/isis_interface_passive_test/isis_interface_passive_test.go @@ -58,6 +58,9 @@ func configureISIS(t *testing.T, ts *isissession.TestSession) { globalIsis.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV6, oc.IsisTypes_SAFI_TYPE_UNICAST).Enabled = ygot.Bool(true) globalIsis.LevelCapability = oc.Isis_LevelType_LEVEL_2 globalIsis.AuthenticationCheck = ygot.Bool(true) + if deviations.ISISGlobalAuthenticationNotRequired(ts.DUT) { + globalIsis.AuthenticationCheck = nil + } globalIsis.HelloPadding = oc.Isis_HelloPaddingType_ADAPTIVE // Level configs. @@ -69,6 +72,11 @@ func configureISIS(t *testing.T, ts *isissession.TestSession) { auth.AuthMode = oc.IsisTypes_AUTH_MODE_MD5 auth.AuthType = oc.KeychainTypes_AUTH_TYPE_SIMPLE_KEY auth.AuthPassword = ygot.String(password) + if deviations.ISISExplicitLevelAuthenticationConfig(ts.DUT) { + auth.DisableCsnp = ygot.Bool(false) + auth.DisableLsp = ygot.Bool(false) + auth.DisablePsnp = ygot.Bool(false) + } // Interface configs. intfName := ts.DUTPort1.Name() @@ -188,16 +196,20 @@ func TestIsisInterfacePassive(t *testing.T) { t.Errorf("FAIL- Expected area address not found, got %s, want %s", got, want) } // Checking dis system id. - if got := gnmi.Get(t, ts.DUT, adjPath.DisSystemId().State()); got != "0000.0000.0000" { - t.Errorf("FAIL- Expected dis system id not found, got %s, want %s", got, "0000.0000.0000") + if !deviations.MissingValueForDefaults(ts.DUT) { + if got := gnmi.Get(t, ts.DUT, adjPath.DisSystemId().State()); got != "0000.0000.0000" { + t.Errorf("FAIL- Expected dis system id not found, got %s, want %s", got, "0000.0000.0000") + } } // Checking isis local extended circuit id. if got := gnmi.Get(t, ts.DUT, adjPath.LocalExtendedCircuitId().State()); got == 0 { t.Errorf("FAIL- Expected local extended circuit id not found,expected non-zero value, got %d", got) } // Checking multitopology. - if got := gnmi.Get(t, ts.DUT, adjPath.MultiTopology().State()); got != false { - t.Errorf("FAIL- Expected value for multi topology not found, got %t, want %t", got, false) + if !deviations.MissingValueForDefaults(ts.DUT) { + if got := gnmi.Get(t, ts.DUT, adjPath.MultiTopology().State()); got != false { + t.Errorf("FAIL- Expected value for multi topology not found, got %t, want %t", got, false) + } } // Checking neighbor circuit type. if got := gnmi.Get(t, ts.DUT, adjPath.NeighborCircuitType().State()); got != oc.Isis_LevelType_LEVEL_2 { @@ -240,59 +252,81 @@ func TestIsisInterfacePassive(t *testing.T) { t.Errorf("FAIL- Restart support not present") } // Checking isis restart suppress. - if _, ok := gnmi.Lookup(t, ts.DUT, adjPath.RestartStatus().State()).Val(); !ok { - t.Errorf("FAIL- Restart suppress not present") + if !deviations.MissingValueForDefaults(ts.DUT) { + if _, ok := gnmi.Lookup(t, ts.DUT, adjPath.RestartStatus().State()).Val(); !ok { + t.Errorf("FAIL- Restart suppress not present") + } } }) t.Run("System level counter checks", func(t *testing.T) { // Checking authFail counters. - if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().AuthFails().State()); got != 0 { - t.Errorf("FAIL- Not expecting any authentication key failure, got %d, want %d", got, 0) + if !deviations.MissingValueForDefaults(ts.DUT) { + if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().AuthFails().State()); got != 0 { + t.Errorf("FAIL- Not expecting any authentication key failure, got %d, want %d", got, 0) + } } // Checking authTypeFail counters. - if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().AuthTypeFails().State()); got != 0 { - t.Errorf("FAIL- Not expecting any authentication type mismatches, got %d, want %d", got, 0) + if !deviations.MissingValueForDefaults(ts.DUT) { + if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().AuthTypeFails().State()); got != 0 { + t.Errorf("FAIL- Not expecting any authentication type mismatches, got %d, want %d", got, 0) + } } // Checking corrupted lsps counters. - if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().CorruptedLsps().State()); got != 0 { - t.Errorf("FAIL- Not expecting any corrupted lsps, got %d, want %d", got, 0) + if !deviations.MissingValueForDefaults(ts.DUT) { + if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().CorruptedLsps().State()); got != 0 { + t.Errorf("FAIL- Not expecting any corrupted lsps, got %d, want %d", got, 0) + } } // Checking database_overloads counters. - if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().DatabaseOverloads().State()); got != 0 { - t.Errorf("FAIL- Not expecting non zero database_overloads, got %d, want %d", got, 0) + if !deviations.MissingValueForDefaults(ts.DUT) { + if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().DatabaseOverloads().State()); got != 0 { + t.Errorf("FAIL- Not expecting non zero database_overloads, got %d, want %d", got, 0) + } } // Checking execeeded maximum seq number counters"). - if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().ExceedMaxSeqNums().State()); got != 0 { - t.Errorf("FAIL- Not expecting non zero max_seqnum counter, got %d, want %d", got, 0) + if !deviations.MissingValueForDefaults(ts.DUT) { + if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().ExceedMaxSeqNums().State()); got != 0 { + t.Errorf("FAIL- Not expecting non zero max_seqnum counter, got %d, want %d", got, 0) + } } // Checking IdLenMismatch counters. - if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().IdLenMismatch().State()); got != 0 { - t.Errorf("FAIL- Not expecting non zero IdLen_Mismatch counter, got %d, want %d", got, 0) + if !deviations.MissingValueForDefaults(ts.DUT) { + if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().IdLenMismatch().State()); got != 0 { + t.Errorf("FAIL- Not expecting non zero IdLen_Mismatch counter, got %d, want %d", got, 0) + } } // Checking LspErrors counters. - if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().LspErrors().State()); got != 0 { - t.Errorf("FAIL- Not expecting any lsp errors, got %d, want %d", got, 0) + if !deviations.MissingValueForDefaults(ts.DUT) { + if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().LspErrors().State()); got != 0 { + t.Errorf("FAIL- Not expecting any lsp errors, got %d, want %d", got, 0) + } } // Checking MaxAreaAddressMismatches counters. - if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().MaxAreaAddressMismatches().State()); got != 0 { - t.Errorf("FAIL- Not expecting non zero MaxAreaAddressMismatches counter, got %d, want %d", got, 0) + if !deviations.MissingValueForDefaults(ts.DUT) { + if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().MaxAreaAddressMismatches().State()); got != 0 { + t.Errorf("FAIL- Not expecting non zero MaxAreaAddressMismatches counter, got %d, want %d", got, 0) + } } // Checking OwnLspPurges counters. - if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().OwnLspPurges().State()); got != 0 { - t.Errorf("FAIL- Not expecting non zero OwnLspPurges counter, got %d, want %d", got, 0) + if !deviations.MissingValueForDefaults(ts.DUT) { + if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().OwnLspPurges().State()); got != 0 { + t.Errorf("FAIL- Not expecting non zero OwnLspPurges counter, got %d, want %d", got, 0) + } } // Checking SeqNumSkips counters. - if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().SeqNumSkips().State()); got != 0 { - t.Errorf("FAIL- Not expecting non zero SeqNumber skips, got %d, want %d", got, 0) + if !deviations.MissingValueForDefaults(ts.DUT) { + if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().SeqNumSkips().State()); got != 0 { + t.Errorf("FAIL- Not expecting non zero SeqNumber skips, got %d, want %d", got, 0) + } } // Checking ManualAddressDropFromAreas counters. - if !deviations.ISISCounterManualAddressDropFromAreasUnsupported(ts.DUT) { + if !(deviations.ISISCounterManualAddressDropFromAreasUnsupported(ts.DUT) || deviations.MissingValueForDefaults(ts.DUT)) { if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().ManualAddressDropFromAreas().State()); got != 0 { t.Errorf("FAIL- Not expecting non zero ManualAddressDropFromAreas counter, got %d, want %d", got, 0) } } // Checking PartChanges counters. - if !deviations.ISISCounterPartChangesUnsupported(ts.DUT) { + if !(deviations.ISISCounterPartChangesUnsupported(ts.DUT) || deviations.MissingValueForDefaults(ts.DUT)) { if got := gnmi.Get(t, ts.DUT, statePath.Level(2).SystemLevelCounters().PartChanges().State()); got != 0 { t.Errorf("FAIL- Not expecting partition changes, got %d, want %d", got, 0) } diff --git a/feature/experimental/isis/otg_tests/isis_interface_passive_test/metadata.textproto b/feature/experimental/isis/otg_tests/isis_interface_passive_test/metadata.textproto index f14063d5dc4..75bc2932d0e 100644 --- a/feature/experimental/isis/otg_tests/isis_interface_passive_test/metadata.textproto +++ b/feature/experimental/isis/otg_tests/isis_interface_passive_test/metadata.textproto @@ -10,6 +10,8 @@ platform_exceptions: { vendor: NOKIA } deviations: { + isis_global_authentication_not_required: true + isis_explicit_level_authentication_config: true isis_interface_level1_disable_required: true missing_isis_interface_afi_safi_enable: true explicit_port_speed: true diff --git a/feature/experimental/p4rt/README.md b/feature/experimental/p4rt/README.md index f673b15bac5..a2dc5701736 100644 --- a/feature/experimental/p4rt/README.md +++ b/feature/experimental/p4rt/README.md @@ -51,6 +51,10 @@ This document specifies the requirements for p4rt test implementation. implementation already exists in `p4rtutils` library: `p4rtutils.P4RTNodesByPort()`. -7. If P4RT Node Names cannot be resolved by walking the Components tree, use - deviation flag `--deviation_explicit_p4rt_node_component` and pass the node - names through args `--arg_p4rt_node_name_1`, `--arg_p4rt_node_name_2`. +## 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 + +``` diff --git a/feature/experimental/p4rt/otg_tests/google_discovery_protocol_packetout_lag_test/README.md b/feature/experimental/p4rt/otg_tests/google_discovery_protocol_packetout_lag_test/README.md new file mode 100644 index 00000000000..d96635af3e4 --- /dev/null +++ b/feature/experimental/p4rt/otg_tests/google_discovery_protocol_packetout_lag_test/README.md @@ -0,0 +1,57 @@ +# P4RT-3.21: Google Discovery Protocol: PacketOut with LAG + +## Summary + +Verify that GDP packets can be sent by the controller. + +## Testbed Type + +* https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_2.testbed + +## Procedure + +* Configure two lag bundles between ATE and DUT with one member port in each of the LAG. + A[ATE:LAG1] <---> B[LAG1:DUT]; + C[ATE:LAG2] <---> D[LAG2:DUT]; +* Enable the P4RT server on the device. +* Connect two P4RT clients in a master/secondary configuration. +* Configure the forwarding pipeline and install the P4RT table entry required for GDP. +* Send a GDP packet from the master with egress_singleton_port set to one of the connected interfaces. +* Verify that the GDP packet is received on the ATE port connected to the indicated interface. +* Repeat sending the packet in the same way but from the secondary connection. +* Verify that the packet is not received on the ATE. + +## OpenConfig Path and RPC Coverage + +The below yaml defines the OC paths intended to be covered by this test. + +```yaml +paths: + ## Config parameter coverage + /interfaces/interface/ethernet/config/port-speed: + /interfaces/interface/ethernet/config/duplex-mode: + /interfaces/interface/ethernet/config/aggregate-id: + /interfaces/interface/aggregation/config/lag-type: + /network-instances/network-instance/vlans/vlan/state/vlan-id: + + ## Telemetry parameter coverage + /lacp/interfaces/interface/members/member/state/counters/lacp-in-pkts: + /lacp/interfaces/interface/members/member/state/counters/lacp-out-pkts: + /lacp/interfaces/interface/members/member/state/counters/lacp-rx-errors: + /lacp/interfaces/interface/name: + /lacp/interfaces/interface/state/name: + /lacp/interfaces/interface/members/member/interface: + /lacp/interfaces/interface/members/member/state/interface: + + + ## Protocol/RPC Parameter Coverage +rpcs: + gnmi: + gNMI.Subscribe: + gNMI.Set: + gNMI.Get: +``` + +## Required DUT platform + +* FFF diff --git a/feature/experimental/p4rt/otg_tests/google_discovery_protocol_packetout_lag_test/google_discovery_protocol_packetout_lag_test.go b/feature/experimental/p4rt/otg_tests/google_discovery_protocol_packetout_lag_test/google_discovery_protocol_packetout_lag_test.go new file mode 100644 index 00000000000..4f26092b92d --- /dev/null +++ b/feature/experimental/p4rt/otg_tests/google_discovery_protocol_packetout_lag_test/google_discovery_protocol_packetout_lag_test.go @@ -0,0 +1,563 @@ +// 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 google_discovery_protocol_packetout_lag_test + +import ( + "context" + "errors" + "flag" + "fmt" + "net" + "sort" + "testing" + "time" + + "github.com/cisco-open/go-p4/p4rt_client" + "github.com/cisco-open/go-p4/utils" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/otgutils" + "github.com/openconfig/featureprofiles/internal/p4rtutils" + "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" + p4v1pb "github.com/p4lang/p4runtime/go/p4/v1" +) + +const ( + ipv4PLen = 30 + packetCount = 300 +) + +var ( + p4InfoFile = flag.String("p4info_file_location", "../../wbb.p4info.pb.txt", "Path to the p4info file.") + streamName = "p4rt" + gdpInLayers layers.EthernetType = 0x6007 + deviceID = uint64(1) + portID = uint32(10) + electionID = uint64(100) + vlanID = uint16(4000) + pktOutDstMAC = "02:F6:65:64:00:08" +) + +type aggPortData struct { + dutIPv4 string + ateIPv4 string + ateAggName string + ateAggMAC string + atePortMAC string + aggPortID uint32 + hasVlan bool +} + +var ( + agg1 = &aggPortData{ + dutIPv4: "192.0.2.1", + ateIPv4: "192.0.2.2", + ateAggName: "lag1", + ateAggMAC: "02:00:01:01:01:01", + atePortMAC: "02:00:01:01:01:02", + aggPortID: 10, + hasVlan: true, + } + agg2 = &aggPortData{ + dutIPv4: "192.0.2.5", + ateIPv4: "192.0.2.6", + ateAggName: "lag2", + ateAggMAC: "02:00:01:01:01:04", + atePortMAC: "02:00:01:01:01:05", + aggPortID: 11, + hasVlan: false, + } +) + +type PacketIO interface { + GetTableEntry(delete bool) []*p4rtutils.ACLWbbIngressTableEntryInfo + GetPacketOut(portID uint32) []*p4v1pb.PacketOut +} + +type testArgs struct { + ctx context.Context + leader *p4rt_client.P4RTClient + follower *p4rt_client.P4RTClient + dut *ondatra.DUTDevice + ate *ondatra.ATEDevice + top gosnappi.Config + packetIO PacketIO +} + +// programmTableEntry programs or deletes p4rt table entry based on delete flag. +func programmTableEntry(ctx context.Context, t *testing.T, client *p4rt_client.P4RTClient, packetIO PacketIO, delete bool) error { + t.Helper() + err := client.Write(&p4v1pb.WriteRequest{ + DeviceId: deviceID, + ElectionId: &p4v1pb.Uint128{High: uint64(0), Low: electionID}, + Updates: p4rtutils.ACLWbbIngressTableEntryGet( + packetIO.GetTableEntry(delete), + ), + Atomicity: p4v1pb.WriteRequest_CONTINUE_ON_ERROR, + }) + if err != nil { + return err + } + return nil +} + +// sendPackets sends out packets via PacketOut message in StreamChannel. +func sendPackets(t *testing.T, client *p4rt_client.P4RTClient, packets []*p4v1pb.PacketOut, packetCount int) { + count := packetCount / len(packets) + for _, packet := range packets { + for i := 0; i < count; i++ { + if err := client.StreamChannelSendMsg( + &streamName, &p4v1pb.StreamMessageRequest{ + Update: &p4v1pb.StreamMessageRequest_Packet{ + Packet: packet, + }, + }); err != nil { + t.Errorf("There is error seen in Packet Out. %v, %s", err, err) + } + } + } +} + +// testPacketOut sends out PacketOut with GDP payload on p4rt leader or +// follower client, then verify DUT interface statistics +func testPacketOut(ctx context.Context, t *testing.T, args *testArgs) { + leader := args.leader + follower := args.follower + + // Insert wbb acl entry on the DUT + if err := programmTableEntry(ctx, t, leader, args.packetIO, false); err != nil { + t.Fatalf("There is error when programming entry") + } + // Delete wbb acl entry on the device + defer programmTableEntry(ctx, t, leader, args.packetIO, true) + + packetOutTests := []struct { + desc string + client *p4rt_client.P4RTClient + expectPass bool + }{{ + desc: "PacketOut from Primary Controller", + client: leader, + expectPass: true, + }, { + desc: "PacketOut from Secondary Controller", + client: follower, + expectPass: false, + }} + + for _, test := range packetOutTests { + t.Run(test.desc, func(t *testing.T) { + // Check initial packet counters + port1 := sortPorts(args.ate.Ports())[0].ID() + counter0 := gnmi.Get(t, args.ate.OTG(), gnmi.OTG().Port(port1).Counters().InFrames().State()) + packets := args.packetIO.GetPacketOut(portID) + sendPackets(t, test.client, packets, packetCount) + + // Wait for ate stats to be populated + time.Sleep(4 * time.Minute) + otgutils.LogFlowMetrics(t, args.ate.OTG(), args.top) + otgutils.LogPortMetrics(t, args.ate.OTG(), args.top) + // Check packet counters after packet out + counter1 := gnmi.Get(t, args.ate.OTG(), gnmi.OTG().Port(port1).Counters().InFrames().State()) + // Verify InPkts stats to check P4RT stream + t.Logf("Received %v packets on ATE port %s", counter1-counter0, port1) + + if test.expectPass { + if counter1-counter0 < uint64(packetCount*0.95) { + t.Fatalf("Not all the packets are received.") + } + } else { + if counter1-counter0 > uint64(packetCount*0.10) { + t.Fatalf("Unexpected packets are received.") + } + } + }) + } +} + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +// sortPorts sorts the ports by the testbed port ID. +func sortPorts(ports []*ondatra.Port) []*ondatra.Port { + sort.Slice(ports, func(i, j int) bool { + idi, idj := ports[i].ID(), ports[j].ID() + li, lj := len(idi), len(idj) + if li == lj { + return idi < idj + } + return li < lj // "port2" < "port10" + }) + return ports +} + +// configureDUT configures agg1 and agg2 on the DUT. +func configureDUT(t *testing.T, dut *ondatra.DUTDevice) []string { + t.Helper() + fptest.ConfigureDefaultNetworkInstance(t, dut) + var aggIDs []string + for aggIdx, a := range []*aggPortData{agg1, agg2} { + 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) + + 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*1)+1)) + // p2 := dut.Port(t, fmt.Sprintf("port%d", (aggIdx*2)+2)) + for _, port := range []*ondatra.Port{p1} { + gnmi.BatchDelete(b, gnmi.OC().Interface(port.Name()).Ethernet().AggregateId().Config()) + i := d.GetOrCreateInterface(port.Name()) + // i := &oc.Interface{Name: ygot.String(p1.Name()), Id: ygot.Uint32(a.aggPortID)} + // i := d.Interface{Name: ygot.String(port.Name()), Id: ygot.Uint32(a.aggPortID)} + i.Id = ygot.Uint32(a.aggPortID) + 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 a.hasVlan && deviations.P4RTGdpRequiresDot1QSubinterface(dut) { + s1 := i.GetOrCreateSubinterface(1) + s1.GetOrCreateVlan().GetOrCreateMatch().GetOrCreateSingleTagged().SetVlanId(vlanID) + if deviations.NoMixOfTaggedAndUntaggedSubinterfaces(dut) { + s.GetOrCreateVlan().GetOrCreateMatch().GetOrCreateSingleTagged().SetVlanId(10) + i.GetOrCreateAggregation().GetOrCreateSwitchedVlan().SetNativeVlan(10) + } + } + if deviations.InterfaceEnabled(dut) { + i.Enabled = ygot.Bool(true) + } + gnmi.BatchReplace(b, gnmi.OC().Interface(port.Name()).Config(), i) + } + + b.Set(t, dut) + } + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + for _, aggID := range aggIDs { + fptest.AssignToNetworkInstance(t, dut, aggID, deviations.DefaultNetworkInstance(dut), 0) + } + } + // Wait for LAG interfaces to be UP + for _, aggID := range aggIDs { + gnmi.Await(t, dut, gnmi.OC().Interface(aggID).AdminStatus().State(), 60*time.Second, oc.Interface_AdminStatus_UP) + } + gnmi.Replace(t, dut, gnmi.OC().System().MacAddress().RoutingMac().Config(), pktOutDstMAC) + return aggIDs +} + +// configureATE configures agg1 and agg2 on the ATE. +func configureATE(t *testing.T, ate *ondatra.ATEDevice) gosnappi.Config { + t.Helper() + top := gosnappi.NewConfig() + + for aggIdx, a := range []*aggPortData{agg1, agg2} { + p1 := ate.Port(t, fmt.Sprintf("port%d", (aggIdx*1)+1)) + // p2 := ate.Port(t, fmt.Sprintf("port%d", (aggIdx*2)+2)) + top.Ports().Add().SetName(p1.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) + agg.Ports().Add().SetPortName(p1.ID()).Ethernet().SetMac(a.atePortMAC).SetName(a.ateAggName + ".1") + // agg.Ports().Add().SetPortName(p2.ID()).Ethernet().SetMac(a.atePort2MAC).SetName(a.ateAggName + ".2") + } + return top +} + +// configureDeviceIDs configures p4rt device-id on the DUT. +func configureDeviceID(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice) { + nodes := p4rtutils.P4RTNodesByPort(t, dut) + p4rtNode, ok := nodes["port1"] + if !ok { + t.Fatal("Couldn't find P4RT Node for port: port1") + } + t.Logf("Configuring P4RT Node: %s", p4rtNode) + c := oc.Component{} + c.Name = ygot.String(p4rtNode) + c.IntegratedCircuit = &oc.Component_IntegratedCircuit{} + c.IntegratedCircuit.NodeId = ygot.Uint64(deviceID) + gnmi.Replace(t, dut, gnmi.OC().Component(p4rtNode).Config(), &c) +} + +// setupP4RTClient sends client arbitration message for both leader and follower clients, +// then sends setforwordingpipelineconfig with leader client. +func setupP4RTClient(ctx context.Context, args *testArgs) error { + // Setup p4rt-client stream parameters + streamParameter := p4rt_client.P4RTStreamParameters{ + Name: streamName, + DeviceId: deviceID, + ElectionIdH: uint64(0), + ElectionIdL: electionID, + } + + // Send ClientArbitration message on both p4rt leader and follower clients. + clients := []*p4rt_client.P4RTClient{args.leader, args.follower} + for index, client := range clients { + if client != nil { + client.StreamChannelCreate(&streamParameter) + if err := client.StreamChannelSendMsg(&streamName, &p4v1pb.StreamMessageRequest{ + Update: &p4v1pb.StreamMessageRequest_Arbitration{ + Arbitration: &p4v1pb.MasterArbitrationUpdate{ + DeviceId: streamParameter.DeviceId, + ElectionId: &p4v1pb.Uint128{ + High: streamParameter.ElectionIdH, + Low: streamParameter.ElectionIdL - uint64(index), + }, + }, + }, + }); err != nil { + return fmt.Errorf("errors seen when sending ClientArbitration message: %v", err) + } + if _, _, arbErr := client.StreamChannelGetArbitrationResp(&streamName, 1); arbErr != nil { + if err := p4rtutils.StreamTermErr(client.StreamTermErr); err != nil { + return err + } + return fmt.Errorf("errors seen in ClientArbitration response: %v", arbErr) + } + } + } + + // Load p4info file. + p4Info, err := utils.P4InfoLoad(p4InfoFile) + if err != nil { + return errors.New("errors seen when loading p4info file") + } + + // Send SetForwardingPipelineConfig for p4rt leader client. + if err := args.leader.SetForwardingPipelineConfig(&p4v1pb.SetForwardingPipelineConfigRequest{ + DeviceId: deviceID, + ElectionId: &p4v1pb.Uint128{High: uint64(0), Low: electionID}, + Action: p4v1pb.SetForwardingPipelineConfigRequest_VERIFY_AND_COMMIT, + Config: &p4v1pb.ForwardingPipelineConfig{ + P4Info: p4Info, + Cookie: &p4v1pb.ForwardingPipelineConfig_Cookie{ + Cookie: 159, + }, + }, + }); err != nil { + return errors.New("errors seen when sending SetForwardingPipelineConfig") + } + return nil +} + +// getGDPParameter returns GDP related parameters for testPacketOut testcase. +func getGDPParameter(t *testing.T) PacketIO { + mac, err := net.ParseMAC(pktOutDstMAC) + if err != nil { + t.Fatalf("Could not parse MAC: %v", err) + } + return &GDPPacketIO{ + IngressPort: fmt.Sprint(portID), + DstMAC: mac, + } +} + +func TestPacketOut(t *testing.T) { + dut := ondatra.DUT(t, "dut") + ctx := context.Background() + + configureDUT(t, dut) + + // Configure the ATE + ate := ondatra.ATE(t, "ate") + top := configureATE(t, ate) + ate.OTG().PushConfig(t, top) + ate.OTG().StartProtocols(t) + + // Configure P4RT device-id and port-id on the DUT + configureDeviceID(ctx, t, dut) + + leader := p4rt_client.NewP4RTClient(&p4rt_client.P4RTClientParameters{}) + if err := leader.P4rtClientSet(dut.RawAPIs().P4RT(t)); err != nil { + t.Fatalf("Could not initialize p4rt client: %v", err) + } + + follower := p4rt_client.NewP4RTClient(&p4rt_client.P4RTClientParameters{}) + if err := follower.P4rtClientSet(dut.RawAPIs().P4RT(t)); err != nil { + t.Fatalf("Could not initialize p4rt client: %v", err) + } + + args := &testArgs{ + ctx: ctx, + leader: leader, + follower: follower, + dut: dut, + ate: ate, + top: top, + } + + if err := setupP4RTClient(ctx, args); err != nil { + t.Fatalf("Could not setup p4rt client: %v", err) + } + + args.packetIO = getGDPParameter(t) + testPacketOut(ctx, t, args) +} + +type GDPPacketIO struct { + PacketIO + IngressPort string + DstMAC net.HardwareAddr +} + +// packetGDPRequestGet generates PacketOut payload for GDP packets. +func packetGDPRequestGet(vlan bool) []byte { + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + pktEth := &layers.Ethernet{ + SrcMAC: net.HardwareAddr{0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA}, + // GDP MAC is 00:0A:DA:F0:F0:F0 + DstMAC: net.HardwareAddr{0x00, 0x0A, 0xDA, 0xF0, 0xF0, 0xF0}, + EthernetType: gdpInLayers, + } + + payload := []byte{} + payLoadLen := 64 + for i := 0; i < payLoadLen; i++ { + payload = append(payload, byte(i)) + } + if vlan { + pktEth.EthernetType = layers.EthernetTypeDot1Q + d1q := &layers.Dot1Q{ + VLANIdentifier: vlanID, + Type: gdpInLayers, + } + gopacket.SerializeLayers(buf, opts, + pktEth, d1q, gopacket.Payload(payload), + ) + } else { + gopacket.SerializeLayers(buf, opts, + pktEth, gopacket.Payload(payload), + ) + } + return buf.Bytes() +} + +func ipPacketToATEPort1(dstMAC net.HardwareAddr) []byte { + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + eth := &layers.Ethernet{ + SrcMAC: net.HardwareAddr{0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA}, + // GDP MAC is 00:0A:DA:F0:F0:F0 + DstMAC: dstMAC, + EthernetType: layers.EthernetTypeIPv4, + } + ip := &layers.IPv4{ + SrcIP: net.ParseIP(agg2.ateIPv4), + DstIP: net.ParseIP(agg1.ateIPv4), + TTL: 2, + Version: 4, + Protocol: layers.IPProtocolIPv4, + } + tcp := &layers.TCP{ + SrcPort: 10000, + DstPort: 20000, + Seq: 11050, + } + + // Required for checksum computation. + tcp.SetNetworkLayerForChecksum(ip) + + payload := []byte{} + payLoadLen := 64 + for i := 0; i < payLoadLen; i++ { + payload = append(payload, byte(i)) + } + gopacket.SerializeLayers(buf, opts, + eth, ip, tcp, gopacket.Payload(payload), + ) + return buf.Bytes() +} + +// GetTableEntry creates wbb acl entry related to GDP. +func (gdp *GDPPacketIO) GetTableEntry(delete bool) []*p4rtutils.ACLWbbIngressTableEntryInfo { + actionType := p4v1pb.Update_INSERT + if delete { + actionType = p4v1pb.Update_DELETE + } + return []*p4rtutils.ACLWbbIngressTableEntryInfo{{ + Type: actionType, + EtherType: 0x6007, + EtherTypeMask: 0xFFFF, + Priority: 1, + }} +} + +// GetPacketOut generates PacketOut message with payload as GDP. +func (gdp *GDPPacketIO) GetPacketOut(portID uint32) []*p4v1pb.PacketOut { + gdpWithVlan := &p4v1pb.PacketOut{ + Payload: packetGDPRequestGet(true), + Metadata: []*p4v1pb.PacketMetadata{ + { + MetadataId: uint32(1), // "egress_port" + Value: []byte(fmt.Sprint(portID)), + }, + }, + } + gdpWithoutVlan := &p4v1pb.PacketOut{ + Payload: packetGDPRequestGet(false), + Metadata: []*p4v1pb.PacketMetadata{ + { + MetadataId: uint32(1), // "egress_port" + Value: []byte(fmt.Sprint(portID)), + }, + }, + } + + nonGDP := &p4v1pb.PacketOut{ + Payload: ipPacketToATEPort1(gdp.DstMAC), + Metadata: []*p4v1pb.PacketMetadata{ + { + MetadataId: uint32(2), // "submit_to_ingress" + Value: []byte{1}, + }, + }, + } + return []*p4v1pb.PacketOut{gdpWithoutVlan, gdpWithVlan, nonGDP} +} diff --git a/feature/experimental/p4rt/otg_tests/google_discovery_protocol_packetout_lag_test/metadata.textproto b/feature/experimental/p4rt/otg_tests/google_discovery_protocol_packetout_lag_test/metadata.textproto new file mode 100644 index 00000000000..4a20b5c1048 --- /dev/null +++ b/feature/experimental/p4rt/otg_tests/google_discovery_protocol_packetout_lag_test/metadata.textproto @@ -0,0 +1,44 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "14861016-4ac8-43a8-8535-3f2c034e0e03" +plan_id: "P4RT-3.21" +description: "Google Discovery Protocol: PacketOut with LAG" +testbed: TESTBED_DUT_ATE_2LINKS +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + ipv4_missing_enabled: true + } +} +platform_exceptions: { + platform: { + vendor: JUNIPER + } + deviations: { + p4rt_gdp_requires_dot1q_subinterface: true + no_mix_of_tagged_and_untagged_subinterfaces: true + } +} +platform_exceptions: { + platform: { + vendor: NOKIA + } + deviations: { + explicit_port_speed: true + explicit_interface_in_default_vrf: true + interface_enabled: true + } +} +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + interface_enabled: true + default_network_instance: "default" + } +} + diff --git a/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/README.md b/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/README.md new file mode 100644 index 00000000000..d15347dad27 --- /dev/null +++ b/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/README.md @@ -0,0 +1,255 @@ +# P4RT-5.3: Traceroute: PacketIn With VRF Selection + +## Summary + +Test FRR behaviors with VRF selection 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 + +* Setup equivalent to [TE-17.1 vrf_policy_driven_te](https://github.com/openconfig/featureprofiles/blob/main/feature/experimental/gribi/otg_tests/vrf_policy_driven_te/README.md), including GRibi programming. + +* Install a BGP route resolved by ISIS in default VRF to route traffic out of DUT port-8 for 203.0.113.0. + +* Enable the P4RT server on the device. + +* Connect a P4RT client and configure the forwarding pipeline. Install P4RT table entries required for traceroute. + These are located in [p4rt_utils.go] (https://github.com/openconfig/featureprofiles/blob/main/internal/p4rtutils/p4rtutils.go) + p4rtutils.ACLWbbIngressTableEntryGet(packetIO.GetTableEntry(delete, isIPv4)) + + +## Procedure + +At the start of each of the following scenarios, ensure: + +* All ports are up and baseline is reset as above. + +Unless otherwise specified, all the tests below should use traffic with +`dscp_encap_a_1` referenced int he VRF selection policy. + +### Test-1 + +Tests that traceroute with TTL=1 for a packet that would match the VRF +selection policy for encap has target_egress_port set based on the +encap VRF. + +* Send packets to DUT port-1 with outer packet header TTL=1. The outer v4 header has the destination + addresses 138.0.11.8. verify that packets with TTL=1 are received by the client. + +* Verify that the punted packets have both ingress_port and target_egress_port metadata set. + +The distribution of packets should have target_egress_port set with port2 1.56% of +the time, port3 4.68%, port4 18.75% and port6 75%. + +### Test-2 + +Tests that traceroute with TTL=1 for a packet that would match the VRF +selection policy for default has target_egress_port set based on the +default VRF. + +* Send packets to DUT port-1 with packet TTL=1. The v4 header has the destination + address 203.0.113.0. Verify that packets with TTL=1 are received by the client. +* Verify that the packets have both ingress_port and target_egress_port metadata set. +target_egress_port should be dut port 8. + + +### Test-3 + +Tests that a packet punted due to TTL=1 for a packet that would +otherwise hit a transit VRF has target_egress_port set based on that +transit VRF. + +* Send 4in4 (IP protocol 4) and 6in4 (IP protocol 41) packets to DUT port-1 where + * The outer v4 header has the destination address 203.0.113.1. + * The outer v4 header has the source address ipv4_outer_src_111. + * The outer v4 header has DSCP value has `dscp_encap_no_match` and `dscp_encap_match` + * The outer v4 header has TTL=1 +* Verify that the punted packets have both ingress_port and + target_egress_port metadata set. target_egress_port should be set + to on DUT port-2, port-3, and port-4 per the heirarchical weight. + +### Test-4 (TBD) + +Tests that traceroute respects transit FRR. + +### Test-5 (TBD) + +Tests that traceroute respects transit FRR when the backup is also unviable. + +### Test-6 + +Tests that traceroute respects decap rules. + +1. Using gRIBI to install the following entries in the `DECAP_TE_VRF`: + + ``` + IPv4Entry {192.51.100.1/24 (DECAP_TE_VRF)} -> NHG#3001 (DEFAULT VRF) -> { + {NH#3001, DEFAULT VRF, weight:1} + } + NH#3001 -> { + decapsulate_header: OPENCONFIGAFTTYPESDECAPSULATIONHEADERTYPE_IPV4 + } + + ``` + +2. Apply vrf selection policy `vrf_selection_policy_w` to DUT port-1. + +3. Send the following 6in4 and 4in4 flows to DUT port-1: + + ``` + * inner_src: `ipv4_inner_src` + * inner_dst: `ipv4_inner_encap_match` + * dscp: `dscp_encap_no_match` + * outer_src: `ipv4_outer_src_111` + * outer_dst: `ipv4_outer_decap_match` + * dscp: `dscp_encap_no_match` + * proto: `4` + * outer TTL: `1` + + * inner_src: `ipv6_inner_src` + * inner_dst: `ipv6_inner_encap_match` + * dscp: `dscp_encap_no_match` + * outer_src: `ipv4_outer_src_111` + * outer_dst: `ipv4_outer_decap_match` + * dscp: `dscp_encap_no_match` + * proto: `41` + * outer TTL: `1` + ``` + +4. Verify that all punted packets: + * Have ingress_port and target_egress_port metadata set + * target_egress_port is set to DUT port-8 per the hierarchical weight. + +6. Change the subnet mask from /24 and repeat the test for the masks /32, /22, and /28 and verify again that the packets are punted correctly. + + +### Test-7 (TBD) + +Encap failure cases (TBD on confirmation) + +### Test-8 (TBD) + +Tests that traceroute for a packet with a route lookup miss has an unset target_egress_port. + +### Test-9, decap the encap + +1. Using gRIBI to install the following entries in the `DECAP_TE_VRF`: + + ``` + IPv4Entry {192.51.100.1/24 (DECAP_TE_VRF)} -> NHG#3001 (DEFAULT VRF) -> { + {NH#3001, DEFAULT VRF, weight:1} + } + NH#3001 -> { + decapsulate_header: OPENCONFIGAFTTYPESDECAPSULATIONHEADERTYPE_IPV4 + } + ``` + +2. Apply vrf selection policy `vrf_selection_policy_w` to DUT port-1. + +3. Send the following packets to DUT port-1: + + ``` + * inner_src: `ipv4_inner_src` + * inner_dst: `ipv4_inner_encap_match` + * dscp: `dscp_encap_a_1` + * outer_src: `ipv4_outer_src_222` + * outer_dst: `ipv4_outer_decap_match` + * dscp: `dscp_encap_a_1` + * proto: `4` + * outer TTL: '1' + ``` + + ``` + * inner_src: `ipv6_inner_src` + * inner_dst: `ipv6_inner_encap_match` + * dscp: `dscp_encap_a_1` + * outer_src: `ipv4_outer_src_111` + * outer_dst: `ipv4_outer_decap_match` + * dscp: `dscp_encap_a_1` + * proto: `41` + * outer TTL: `1` + ``` + +4. We should expect that all punted packets: + * Have ingress_port and target_egress_port metadata set + * target_egress_port is set to DUT port-2, port-3, port-4 and port-6 per the hierarchical weight. + +5. Send the following packets to DUT port -1 + + ``` + * inner_src: `ipv4_inner_src` + * inner_dst: `ipv4_inner_encap_match` + * dscp: `dscp_encap_b_1` + * outer_src: `ipv4_outer_src_111` + * outer_dst: `ipv4_outer_decap_match` + * dscp: `dscp_encap_b_1` + * proto: `4` + + * inner_src: `ipv6_inner_src` + * inner_dst: `ipv6_inner_encap_match` + * dscp: `dscp_encap_b_1` + * outer_src: `ipv4_outer_src_222` + * outer_dst: `ipv4_outer_decap_match` + * dscp: `dscp_encap_b_1` + * proto: `41` + ``` + +6. Verify that all punted packets: + * Have ingress_port and target_egress_port metadata set + * target_egress_port is set to DUT port-2, port-3, port-4 and port-6 per the hierarchical weight. + +## 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 + +## OpenConfig Path and RPC Coverage + +```yaml +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: + gribi: + gRIBI.Get: + gRIBI.Modify: + gRIBI.Flush: +``` + +## Config parameter coverage diff --git a/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/metadata.textproto b/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/metadata.textproto new file mode 100644 index 00000000000..b9ebd5de7e4 --- /dev/null +++ b/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/metadata.textproto @@ -0,0 +1,24 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "5a5491b5-9900-4a05-b289-43feb1ceb915" +plan_id: "P4RT-5.3" +description: "Traceroute: PacketIn With VRF Selection" +testbed: TESTBED_DUT_ATE_8LINKS +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + 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 + ate_port_link_state_operations_unsupported: true + } +} diff --git a/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/packetin_test.go b/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/packetin_test.go new file mode 100644 index 00000000000..d8e5c3e9739 --- /dev/null +++ b/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/packetin_test.go @@ -0,0 +1,275 @@ +// 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 to test P4RT with traceroute traffic of IPV4 and IPV6 with TTL/HopLimit as 0&1. +// go test -v . -testbed /root/ondatra/featureprofiles/topologies/atedut_2.testbed -binding /root/ondatra/featureprofiles/topologies/atedut_2.binding -outputs_dir logs + +package traceroute_packetin_with_vrf_selection_test + +import ( + "context" + "strings" + "testing" + "time" + + "github.com/cisco-open/go-p4/p4rt_client" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/p4rtutils" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ygnmi/ygnmi" + pb "github.com/p4lang/p4runtime/go/p4/v1" +) + +type PacketIO interface { + GetTableEntry(delete bool, isIPv4 bool) []*p4rtutils.ACLWbbIngressTableEntryInfo + GetPacketTemplate() *PacketIOPacket + GetTrafficFlow(ate *ondatra.ATEDevice, dstMac string, isIpv4 bool, + TTL uint8, frameSize uint32, frameRate uint64, dstIP string, flowValues *flowArgs) gosnappi.Flow + GetIngressPort() string +} + +type PacketIOPacket struct { + TTL *uint8 + SrcMAC, DstMAC *string + EthernetType *uint32 + HopLimit *uint8 +} + +// programmTableEntry programs or deletes p4rt table entry based on delete flag. +func programmTableEntry(client *p4rt_client.P4RTClient, packetIO PacketIO, delete bool, isIPv4 bool) error { + err := client.Write(&pb.WriteRequest{ + DeviceId: deviceID, + ElectionId: &pb.Uint128{High: uint64(0), Low: electionID}, + Updates: p4rtutils.ACLWbbIngressTableEntryGet( + packetIO.GetTableEntry(delete, isIPv4), + ), + Atomicity: pb.WriteRequest_CONTINUE_ON_ERROR, + }) + return err +} + +// decodePacket decodes L2 header in the packet and returns source and destination MAC and ethernet type. +func decodePacket(t *testing.T, packetData []byte) (string, string, layers.EthernetType) { + t.Helper() + packet := gopacket.NewPacket(packetData, layers.LayerTypeEthernet, gopacket.Default) + etherHeader := packet.Layer(layers.LayerTypeEthernet) + if etherHeader != nil { + header, decoded := etherHeader.(*layers.Ethernet) + if decoded { + return header.SrcMAC.String(), header.DstMAC.String(), header.EthernetType + } + } + return "", "", layers.EthernetType(0) +} + +// decodePacket decodes L2 header in the packet and returns TTL. packetData[14:0] to remove first 14 bytes of Ethernet header. +func decodePacket4(t *testing.T, packetData []byte) uint8 { + t.Helper() + packet := gopacket.NewPacket(packetData[14:], layers.LayerTypeIPv4, gopacket.Default) + if IPv4 := packet.Layer(layers.LayerTypeIPv4); IPv4 != nil { + ipv4, _ := IPv4.(*layers.IPv4) + IPv4 := ipv4.TTL + return IPv4 + } + return 7 +} + +// decodePacket decodes IPV6 L2 header in the packet and returns HopLimit. packetData[14:] to remove first 14 bytes of Ethernet header. +func decodePacket6(t *testing.T, packetData []byte) uint8 { + t.Helper() + packet := gopacket.NewPacket(packetData[14:], layers.LayerTypeIPv6, gopacket.Default) + if IPv6 := packet.Layer(layers.LayerTypeIPv6); IPv6 != nil { + ipv6, _ := IPv6.(*layers.IPv6) + IPv6 := ipv6.HopLimit + return IPv6 + } + return 7 +} + +// testTraffic sends traffic flow for duration seconds and returns the +// number of packets sent out. +func testTraffic(t *testing.T, top gosnappi.Config, ate *ondatra.ATEDevice, flows []gosnappi.Flow, srcEndPoint gosnappi.Port, duration int, cs gosnappi.ControlState) int { + t.Helper() + top.Flows().Clear() + for _, flow := range flows { + flow.TxRx().Port().SetTxName(srcEndPoint.Name()).SetRxName(srcEndPoint.Name()) + flow.Metrics().SetEnable(true) + top.Flows().Append(flow) + } + ate.OTG().PushConfig(t, top) + ate.OTG().StartProtocols(t) + time.Sleep(30 * time.Second) + ate.OTG().StartTraffic(t) + time.Sleep(time.Duration(duration) * time.Second) + ate.OTG().StopTraffic(t) + + cs.Port().Capture().SetState(gosnappi.StatePortCaptureState.STOP) + ate.OTG().SetControlState(t, cs) + + outPkts := gnmi.GetAll(t, ate.OTG(), gnmi.OTG().FlowAny().Counters().OutPkts().State()) + total := 0 + for _, count := range outPkts { + total += int(count) + } + return total +} + +// testPacketIn programs p4rt table entry and sends traffic related to Traceroute, +// then validates packetin message metadata and payload. +func testPacketIn(ctx context.Context, t *testing.T, args *testArgs, isIPv4 bool, cs gosnappi.ControlState, flowValues []*flowArgs, EgressPortMap map[string]bool) []float64 { + leader := args.leader + if isIPv4 { + // Insert p4rtutils acl entry on the DUT + if err := programmTableEntry(leader, args.packetIO, false, isIPv4); err != nil { + t.Fatalf("There is error when programming entry") + } + // Delete p4rtutils acl entry on the device + defer programmTableEntry(leader, args.packetIO, true, isIPv4) + } else { + // Insert p4rtutils acl entry on the DUT + if err := programmTableEntry(leader, args.packetIO, false, false); err != nil { + t.Fatalf("There is error when programming entry") + } + // Delete p4rtutils acl entry on the device + defer programmTableEntry(leader, args.packetIO, true, false) + } + streamChan := args.leader.StreamChannelGet(&streamName) + qSize := 12000 + streamChan.SetArbQSize(qSize) + qSizeRead := streamChan.GetArbQSize() + if qSize != qSizeRead { + t.Errorf("Stream '%s' expecting Arbitration qSize(%d) Got (%d)", + streamName, qSize, qSizeRead) + } + + streamChan.SetPacketQSize(qSize) + qSizeRead = streamChan.GetPacketQSize() + if qSize != qSizeRead { + t.Errorf("Stream '%s' expecting Packet qSize(%d) Got (%d)", + streamName, qSize, qSizeRead) + } + + // Send Traceroute traffic from ATE + srcEndPoint := ateInterface(t, args.top, "port1") + llAddress, found := gnmi.Watch(t, args.ate.OTG(), gnmi.OTG().Interface("atePort1"+".Eth").Ipv4Neighbor(portsIPv4["dut:port1"]).LinkLayerAddress().State(), time.Minute, func(val *ygnmi.Value[string]) bool { + return val.IsPresent() + }).Await(t) + if !found { + t.Fatalf("Could not get the LinkLayerAddress %s", llAddress) + } + dstMac, _ := llAddress.Val() + var flow []gosnappi.Flow + for _, flowValue := range flowValues { + flow = append(flow, args.packetIO.GetTrafficFlow(args.ate, dstMac, isIPv4, 1, 300, 50, ipv4InnerDst, flowValue)) + } + pktOut := testTraffic(t, args.top, args.ate, flow, srcEndPoint, 60, cs) + var countPkts = map[string]int{"11": 0, "12": 0, "13": 0, "14": 0, "15": 0, "16": 0, "17": 0} + + packetInTests := []struct { + desc string + client *p4rt_client.P4RTClient + wantPkts int + }{{ + desc: "PacketIn to Primary Controller", + client: leader, + wantPkts: pktOut, + }} + + t.Log("TTL/HopLimit 1") + for _, test := range packetInTests { + t.Run(test.desc, func(t *testing.T) { + // Extract packets from PacketIn message sent to p4rt client + _, packets, err := test.client.StreamChannelGetPackets(&streamName, uint64(test.wantPkts), 30*time.Second) + if err != nil { + t.Errorf("Unexpected error on fetchPackets: %v", err) + } + + if test.wantPkts == 0 { + return + } + + gotPkts := 0 + t.Logf("Start to decode packet and compare with expected packets.") + wantPacket := args.packetIO.GetPacketTemplate() + + for _, packet := range packets { + if packet != nil { + srcMAC, _, etherType := decodePacket(t, packet.Pkt.GetPayload()) + if etherType != layers.EthernetTypeIPv4 && etherType != layers.EthernetTypeIPv6 { + continue + } + if !strings.EqualFold(srcMAC, tracerouteSrcMAC) { + continue + } + if wantPacket.TTL != nil { + // TTL/HopLimit comparison for IPV4 & IPV6 + if isIPv4 { + captureTTL := decodePacket4(t, packet.Pkt.GetPayload()) + if captureTTL != TTL1 { + t.Fatalf("Packet in PacketIn message is not matching wanted packet=IPV4 TTL1") + } + + } else { + captureHopLimit := decodePacket6(t, packet.Pkt.GetPayload()) + if captureHopLimit != HopLimit1 { + t.Fatalf("Packet in PacketIn message is not matching wanted packet=IPV6 HopLimit1") + } + } + } + + // Metadata comparision + if metaData := packet.Pkt.GetMetadata(); metaData != nil { + if got := metaData[0].GetMetadataId(); got == MetadataIngressPort { + if gotPortID := string(metaData[0].GetValue()); gotPortID != args.packetIO.GetIngressPort() { + t.Fatalf("Ingress Port Id mismatch: want %s, got %s", args.packetIO.GetIngressPort(), gotPortID) + } + } else { + t.Fatalf("Metadata ingress port mismatch: want %d, got %d", MetadataIngressPort, got) + } + if got := metaData[1].GetMetadataId(); got == MetadataEgressPort { + countPkts[string(metaData[1].GetValue())]++ + if gotPortID := string(metaData[1].GetValue()); !EgressPortMap[gotPortID] { + t.Fatalf("Egress Port Id mismatch: got %s", gotPortID) + } + } else { + t.Fatalf("Metadata egress port mismatch: want %d, got %d", MetadataEgressPort, got) + } + } else { + t.Fatalf("Packet missing metadata information.") + } + gotPkts++ + } + } + if got, want := gotPkts, test.wantPkts; got != want { + t.Errorf("Number of PacketIn, got: %d, want: %d", got, want) + } + }) + } + loadBalancePercent := []float64{float64(countPkts["11"]) / float64(pktOut), float64(countPkts["12"]) / float64(pktOut), + float64(countPkts["13"]) / float64(pktOut), float64(countPkts["14"]) / float64(pktOut), float64(countPkts["15"]) / float64(pktOut), + float64(countPkts["16"]) / float64(pktOut), float64(countPkts["17"]) / float64(pktOut)} + + return loadBalancePercent +} + +func ateInterface(t *testing.T, topo gosnappi.Config, portID string) gosnappi.Port { + for _, p := range topo.Ports().Items() { + if p.Name() == portID { + return p + } + } + return nil +} diff --git a/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/traceroute_packetin_test.go b/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/traceroute_packetin_test.go new file mode 100644 index 00000000000..d3e831d795a --- /dev/null +++ b/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/traceroute_packetin_test.go @@ -0,0 +1,264 @@ +// 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 to test P4RT with traceroute traffic of IPV4 and IPV6 with TTL/HopLimit as 0&1. +// go test -v . -testbed /root/ondatra/featureprofiles/topologies/atedut_2.testbed -binding /root/ondatra/featureprofiles/topologies/atedut_2.binding -outputs_dir logs + +package traceroute_packetin_with_vrf_selection_test + +import ( + "context" + "errors" + "fmt" + "testing" + "time" + + "github.com/cisco-open/go-p4/p4rt_client" + "github.com/cisco-open/go-p4/utils" + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/p4rtutils" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ygot/ygot" + pb "github.com/p4lang/p4runtime/go/p4/v1" +) + +// configureDeviceIDs configures p4rt device-id on the DUT. +func configureDeviceID(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice) { + nodes := p4rtutils.P4RTNodesByPort(t, dut) + p4rtNode, ok := nodes["port1"] + if !ok { + t.Fatal("Couldn't find P4RT Node for port: port1") + } + t.Logf("Configuring P4RT Node: %s", p4rtNode) + c := oc.Component{} + c.Name = ygot.String(p4rtNode) + c.IntegratedCircuit = &oc.Component_IntegratedCircuit{} + c.IntegratedCircuit.NodeId = ygot.Uint64(deviceID) + gnmi.Replace(t, dut, gnmi.OC().Component(p4rtNode).Config(), &c) +} + +// setupP4RTClient sends client arbitration message for both leader and follower clients, +// then sends setforwordingpipelineconfig with leader client. +func setupP4RTClient(ctx context.Context, args *testArgs) error { + // Setup p4rt-client stream parameters + streamParameter := p4rt_client.P4RTStreamParameters{ + Name: streamName, + DeviceId: deviceID, + ElectionIdH: uint64(0), + ElectionIdL: electionID, + } + + // Send ClientArbitration message on both p4rt leader and follower clients. + clients := []*p4rt_client.P4RTClient{args.leader, args.follower} + for index, client := range clients { + if client != nil { + client.StreamChannelCreate(&streamParameter) + if err := client.StreamChannelSendMsg(&streamName, &pb.StreamMessageRequest{ + Update: &pb.StreamMessageRequest_Arbitration{ + Arbitration: &pb.MasterArbitrationUpdate{ + DeviceId: streamParameter.DeviceId, + ElectionId: &pb.Uint128{ + High: streamParameter.ElectionIdH, + Low: streamParameter.ElectionIdL - uint64(index), + }, + }, + }, + }); err != nil { + return fmt.Errorf("errors seen when sending ClientArbitration message: %v", err) + } + if _, _, arbErr := client.StreamChannelGetArbitrationResp(&streamName, 1); arbErr != nil { + if err := p4rtutils.StreamTermErr(client.StreamTermErr); err != nil { + return err + } + return fmt.Errorf("errors seen in ClientArbitration response: %v", arbErr) + } + } + } + + // Load p4info file. + p4Info, err := utils.P4InfoLoad(p4InfoFile) + if err != nil { + return errors.New("errors seen when loading p4info file") + } + + // Send SetForwardingPipelineConfig for p4rt leader client. + if err := args.leader.SetForwardingPipelineConfig(&pb.SetForwardingPipelineConfigRequest{ + DeviceId: deviceID, + ElectionId: &pb.Uint128{High: uint64(0), Low: electionID}, + Action: pb.SetForwardingPipelineConfigRequest_VERIFY_AND_COMMIT, + Config: &pb.ForwardingPipelineConfig{ + P4Info: p4Info, + Cookie: &pb.ForwardingPipelineConfig_Cookie{ + Cookie: 159, + }, + }, + }); err != nil { + return errors.New("errors seen when sending SetForwardingPipelineConfig") + } + return nil +} + +// getTracerouteParameter returns Traceroute related parameters for testPacketIn testcase. +func getTracerouteParameter(t *testing.T) PacketIO { + return &TraceroutePacketIO{ + PacketIOPacket: PacketIOPacket{ + TTL: &TTL1, + HopLimit: &HopLimit1, + }, + IngressPort: fmt.Sprint(portID), + EgressPort: fmt.Sprint(portID + 7), + } +} + +// testPacket setup p4RT client, table entry and send traffic and returns the +// percentage of packets sent out of each egress port +func testPacket(t *testing.T, args *testArgs, cs gosnappi.ControlState, flowValues []*flowArgs, EgressPortMap map[string]bool) []float64 { + dut := ondatra.DUT(t, "dut") + ctx := context.Background() + ate := ondatra.ATE(t, "ate") + top := args.otgConfig + top.Flows().Clear() + + configureDeviceID(ctx, t, dut) + + ate.OTG().PushConfig(t, top) + ate.OTG().StartProtocols(t) + time.Sleep(30 * time.Second) + + args = &testArgs{ + ctx: ctx, + leader: args.leader, + follower: args.follower, + dut: dut, + ate: ate, + top: top, + } + + if err := setupP4RTClient(ctx, args); err != nil { + t.Fatalf("Could not setup p4rt client: %v", err) + } + args.packetIO = getTracerouteParameter(t) + return testPacketIn(ctx, t, args, true, cs, flowValues, EgressPortMap) +} + +type TraceroutePacketIO struct { + PacketIOPacket + IngressPort string + EgressPort string +} + +// GetTableEntry creates p4rtutils acl entry which is used to get the configured p4rt trap rules. +// A packet is sent to controller based on the trap rules +func (traceroute *TraceroutePacketIO) GetTableEntry(delete bool, IsIpv4 bool) []*p4rtutils.ACLWbbIngressTableEntryInfo { + if IsIpv4 { + actionType := pb.Update_INSERT + if delete { + actionType = pb.Update_DELETE + } + return []*p4rtutils.ACLWbbIngressTableEntryInfo{{ + Type: actionType, + IsIpv4: 0x1, + TTL: 0x1, + TTLMask: 0xFF, + Priority: 1, + }, + { + Type: actionType, + IsIpv4: 0x1, + TTL: 0x0, + TTLMask: 0xFF, + Priority: 1, + }} + } + actionType := pb.Update_INSERT + if delete { + actionType = pb.Update_DELETE + } + return []*p4rtutils.ACLWbbIngressTableEntryInfo{{ + Type: actionType, + IsIpv6: 0x1, + TTL: 0x1, + TTLMask: 0xFF, + Priority: 1, + }, + { + Type: actionType, + IsIpv6: 0x1, + TTL: 0x0, + TTLMask: 0xFF, + Priority: 1, + }} +} + +// GetPacketTemplate returns expected packets in PacketIn. +func (traceroute *TraceroutePacketIO) GetPacketTemplate() *PacketIOPacket { + return &traceroute.PacketIOPacket +} + +func (traceroute *TraceroutePacketIO) GetTrafficFlow(ate *ondatra.ATEDevice, dstMac string, isIpv4 bool, + TTL uint8, frameSize uint32, frameRate uint64, dstIP string, flowValues *flowArgs) gosnappi.Flow { + + flow := gosnappi.NewFlow() + flow.SetName(flowValues.flowName) + ethHeader := flow.Packet().Add().Ethernet() + ethHeader.Src().SetValue(tracerouteSrcMAC) + ethHeader.Dst().SetValue(dstMac) + + ipHeader := flow.Packet().Add().Ipv4() + ipHeader.Src().SetValue(flowValues.outHdrSrcIP) + ipHeader.Dst().SetValue(flowValues.outHdrDstIP) + ipHeader.TimeToLive().SetValue(uint32(TTL)) + if len(flowValues.outHdrDscp) != 0 { + ipHeader.Priority().Dscp().Phb().SetValues(flowValues.outHdrDscp) + } + if flowValues.udp { + UDPHeader := flow.Packet().Add().Udp() + UDPHeader.DstPort().Increment().SetStart(1).SetCount(50000).SetStep(1) + UDPHeader.SrcPort().Increment().SetStart(1).SetCount(50000).SetStep(1) + } + + if flowValues.proto != 0 { + innerIPHdr := flow.Packet().Add().Ipv4() + innerIPHdr.Protocol().SetValue(flowValues.proto) + innerIPHdr.Src().SetValue(flowValues.InnHdrSrcIP) + innerIPHdr.Dst().SetValue(flowValues.InnHdrDstIP) + innerIPHdr.TimeToLive().SetValue(uint32(TTL)) + } else { + if flowValues.isInnHdrV4 { + innerIPHdr := flow.Packet().Add().Ipv4() + innerIPHdr.Src().SetValue(flowValues.InnHdrSrcIP) + innerIPHdr.Dst().SetValue(flowValues.InnHdrDstIP) + UDPHeader := flow.Packet().Add().Udp() + UDPHeader.DstPort().Increment().SetStart(1).SetCount(50000).SetStep(1) + UDPHeader.SrcPort().Increment().SetStart(1).SetCount(50000).SetStep(1) + } else { + innerIpv6Hdr := flow.Packet().Add().Ipv6() + innerIpv6Hdr.Src().SetValue(flowValues.InnHdrSrcIPv6) + innerIpv6Hdr.Dst().SetValue(flowValues.InnHdrDstIPv6) + UDPHeader := flow.Packet().Add().Udp() + UDPHeader.DstPort().Increment().SetStart(1).SetCount(50000).SetStep(1) + UDPHeader.SrcPort().Increment().SetStart(1).SetCount(50000).SetStep(1) + } + } + + flow.Size().SetFixed(uint32(frameSize)) + flow.Rate().SetPps(uint64(frameRate)) + return flow +} + +// GetIngressPort return expected ingress port info in Packetin. +func (traceroute *TraceroutePacketIO) GetIngressPort() string { + return traceroute.IngressPort +} diff --git a/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/traceroute_packetin_with_vrf_selection_test.go b/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/traceroute_packetin_with_vrf_selection_test.go new file mode 100644 index 00000000000..1d757dab977 --- /dev/null +++ b/feature/experimental/p4rt/otg_tests/traceroute_packetin_with_vrf_selection_test/traceroute_packetin_with_vrf_selection_test.go @@ -0,0 +1,1141 @@ +// 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 traceroute_packetin_with_vrf_selection_test + +import ( + "context" + "flag" + "fmt" + "sort" + "strconv" + "testing" + "time" + + "github.com/cisco-open/go-p4/p4rt_client" + "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" + baseScenario "github.com/openconfig/featureprofiles/internal/encapfrr" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/gribi" + "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/ondatra/netutil" + "github.com/openconfig/ondatra/otg" + "github.com/openconfig/ygnmi/ygnmi" + "github.com/openconfig/ygot/ygot" +) + +// Settings for configuring the baseline testbed with the test +// 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 + +const ( + plenIPv4 = 30 + plenIPv6 = 126 + dscpEncapA1 = 10 + dscpEncapB1 = 20 + dscpEncapNoMatch = 30 + ipv4OuterSrc111Addr = "198.51.100.111" + ipv4OuterSrc222Addr = "198.51.100.222" + ipv4OuterSrcAddr = "198.100.200.123" + ipv4OuterDst111 = "192.51.100.64" + ipv4OuterDst111WithMask = "192.51.100.0/24" + ipv4InnerDst = "138.0.11.8" + noMatchEncapDest = "20.0.0.1" + maskLen24 = "24" + maskLen126 = "124" + maskLen32 = "32" + niDecapTeVrf = "DECAP_TE_VRF" + niEncapTeVrfA = "ENCAP_TE_VRF_A" + niEncapTeVrfB = "ENCAP_TE_VRF_B" + niTeVrf111 = "TE_VRF_111" + niTeVrf222 = "TE_VRF_222" + niRepairVrf = "REPAIR_VRF" + niTransitVRF = "TRANSIT_VRF" + tolerance = 0.02 + encapFlow = "encapFlow" + gribiIPv4EntryVRF1111 = "203.0.113.1" + gribiIPv4EntryVRF1112 = "203.0.113.2" + gribiIPv4EntryEncapVRF = "138.0.11.0" + gribiIPv6EntryEncapVRF = "2001:db8::138:0:11:0" + ipv6InnerDst = "2001:db8::138:0:11:8" + ipv6InnerDstNoEncap = "2001:db8::20:0:0:1" + + dutAreaAddress = "49.0001" + dutSysID = "1920.0000.2001" + otgSysID1 = "640000000001" + isisInstance = "DEFAULT" + otgIsisPort8LoopV4 = "203.0.113.10" + otgIsisPort8LoopV6 = "2001:db8::203:0:113:10" + dutAS = 65501 + peerGrpName1 = "BGP-PEER-GROUP1" + ipOverIPProtocol = 4 +) + +var ( + p4InfoFile = flag.String("p4info_file_location", "../../wbb.p4info.pb.txt", "Path to the p4info file.") + streamName = "p4rt" + tracerouteSrcMAC = "00:01:00:02:00:03" + deviceID = uint64(1) + portID = uint32(10) + electionID = uint64(100) + MetadataIngressPort = uint32(1) + MetadataEgressPort = uint32(2) + TTL1 = uint8(1) + HopLimit1 = uint8(1) + + dutPort1 = attrs.Attributes{ + Desc: "dutPort1", + 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", + MAC: "02:00:01:01:01:01", + IPv6: "2001:db8::192:0:2:2", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + dutPort2 = attrs.Attributes{ + Desc: "dutPort2", + 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", + MAC: "02:00:01:01:01:02", + IPv6: "2001:db8::192:0:2:6", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + dutPort3 = attrs.Attributes{ + Desc: "dutPort3", + IPv4: "192.0.2.9", + IPv6: "2001:db8::192:0:2:9", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + atePort3 = attrs.Attributes{ + Name: "atePort3", + IPv4: "192.0.2.10", + MAC: "02:00:01:01:01:03", + IPv6: "2001:db8::192:0:2:a", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + dutPort4 = attrs.Attributes{ + Desc: "dutPort4", + IPv4: "192.0.2.13", + IPv6: "2001:db8::192:0:2:d", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + atePort4 = attrs.Attributes{ + Name: "atePort4", + IPv4: "192.0.2.14", + MAC: "02:00:01:01:01:04", + IPv6: "2001:db8::192:0:2:e", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + dutPort5 = attrs.Attributes{ + Desc: "dutPort5", + IPv4: "192.0.2.17", + IPv6: "2001:db8::192:0:2:11", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + atePort5 = attrs.Attributes{ + Name: "atePort5", + IPv4: "192.0.2.18", + MAC: "02:00:01:01:01:05", + IPv6: "2001:db8::192:0:2:12", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + dutPort6 = attrs.Attributes{ + Desc: "dutPort6", + IPv4: "192.0.2.21", + IPv6: "2001:db8::192:0:2:15", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + atePort6 = attrs.Attributes{ + Name: "atePort6", + IPv4: "192.0.2.22", + MAC: "02:00:01:01:01:06", + IPv6: "2001:db8::192:0:2:16", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + dutPort7 = attrs.Attributes{ + Desc: "dutPort7", + IPv4: "192.0.2.25", + IPv6: "2001:db8::192:0:2:19", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + atePort7 = attrs.Attributes{ + Name: "atePort7", + IPv4: "192.0.2.26", + MAC: "02:00:01:01:01:07", + IPv6: "2001:db8::192:0:2:1a", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + dutPort8 = attrs.Attributes{ + Desc: "dutPort8", + IPv4: "192.0.2.29", + IPv6: "2001:db8::192:0:2:1d", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + atePort8 = attrs.Attributes{ + Name: "atePort8", + IPv4: "192.0.2.30", + MAC: "02:00:01:01:01:08", + IPv6: "2001:db8::192:0:2:1e", + IPv4Len: plenIPv4, + IPv6Len: plenIPv6, + } + + // loopbackIntfName string + // TODO : https://github.com/open-traffic-generator/fp-testbed-juniper/issues/42 + // Below code will be uncommented once ixia issue is fixed. + // tolerance = 0.2 + + portsMap = map[string]attrs.Attributes{ + "dutPort1": dutPort1, + "atePort1": atePort1, + "dutPort2": dutPort2, + "atePort2": atePort2, + "dutPort3": dutPort3, + "atePort3": atePort3, + "dutPort4": dutPort4, + "atePort4": atePort4, + "dutPort5": dutPort5, + "atePort5": atePort5, + "dutPort6": dutPort6, + "atePort6": atePort6, + "dutPort7": dutPort7, + "atePort7": atePort7, + "dutPort8": dutPort8, + "atePort8": atePort8, + } + portsIPv4 = map[string]string{ + "dut:port1": "192.0.2.1", + "ate:port1": "192.0.2.2", + + "dut:port2": "192.0.2.5", + "ate:port2": "192.0.2.6", + + "dut:port3": "192.0.2.9", + "ate:port3": "192.0.2.10", + + "dut:port4": "192.0.2.13", + "ate:port4": "192.0.2.14", + + "dut:port5": "192.0.2.17", + "ate:port5": "192.0.2.18", + + "dut:port6": "192.0.2.21", + "ate:port6": "192.0.2.22", + + "dut:port7": "192.0.2.25", + "ate:port7": "192.0.2.26", + + "dut:port8": "192.0.2.29", + "ate:port8": "192.0.2.30", + } + portsIPv6 = map[string]string{ + "dut:port1": "2001:db8::192:0:2:1", + "ate:port1": "2001:db8::192:0:2:2", + + "dut:port2": "2001:db8::192:0:2:5", + "ate:port2": "2001:db8::192:0:2:6", + + "dut:port3": "2001:db8::192:0:2:9", + "ate:port3": "2001:db8::192:0:2:a", + + "dut:port4": "2001:db8::192:0:2:d", + "ate:port4": "2001:db8::192:0:2:e", + + "dut:port5": "2001:db8::192:0:2:11", + "ate:port5": "2001:db8::192:0:2:12", + + "dut:port6": "2001:db8::192:0:2:15", + "ate:port6": "2001:db8::192:0:2:16", + + "dut:port7": "2001:db8::192:0:2:19", + "ate:port7": "2001:db8::192:0:2:1a", + + "dut:port8": "2001:db8::192:0:2:1d", + "ate:port8": "2001:db8::192:0:2:1e", + } + otgPortDevices []gosnappi.Device + dutlo0Attrs = attrs.Attributes{ + Desc: "Loopback ip", + IPv4: "203.0.113.11", + IPv6: "2001:db8::203:0:113:1", + IPv4Len: 32, + IPv6Len: 128, + } + loopbackIntfName string + atePortNamelist []string +) + +// awaitTimeout calls a fluent client Await, adding a timeout to the context. +func awaitTimeout(ctx context.Context, t testing.TB, c *fluent.GRIBIClient, timeout time.Duration) error { + t.Helper() + subctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + return c.Await(subctx, t) +} + +type testArgs struct { + ctx context.Context + client *fluent.GRIBIClient + dut *ondatra.DUTDevice + ate *ondatra.ATEDevice + otgConfig gosnappi.Config + top gosnappi.Config + electionID gribi.Uint128 + otg *otg.OTG + leader *p4rt_client.P4RTClient + follower *p4rt_client.P4RTClient + packetIO PacketIO +} + +type flowArgs struct { + flowName string + outHdrSrcIP, outHdrDstIP string + InnHdrSrcIP, InnHdrDstIP string + InnHdrSrcIPv6, InnHdrDstIPv6 string + udp, isInnHdrV4 bool + outHdrDscp []uint32 + proto uint32 +} + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +func sortPorts(ports []*ondatra.Port) []*ondatra.Port { + sort.Slice(ports, func(i, j int) bool { + idi, idj := ports[i].ID(), ports[j].ID() + li, lj := len(idi), len(idj) + if li == lj { + return idi < idj + } + return li < lj // "port2" < "port10" + }) + return ports +} + +// dutInterface builds a DUT interface ygot struct for a given port +// according to portsIPv4. Returns nil if the port has no IP address +// mapping. +func dutInterface(p *ondatra.Port, dut *ondatra.DUTDevice, portIDx uint32) *oc.Interface { + id := fmt.Sprintf("%s:%s", p.Device().ID(), p.ID()) + i := &oc.Interface{ + Name: ygot.String(p.Name()), + Description: ygot.String(p.String()), + Type: oc.IETFInterfaces_InterfaceType_ethernetCsmacd, + Id: ygot.Uint32(portIDx), + } + if deviations.InterfaceEnabled(dut) { + 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 + } + ipv6, ok := portsIPv6[id] + if !ok { + return nil + } + s := i.GetOrCreateSubinterface(0) + s4 := s.GetOrCreateIpv4() + if deviations.InterfaceEnabled(dut) && !deviations.IPv4MissingEnabled(dut) { + s4.Enabled = ygot.Bool(true) + } + + a := s4.GetOrCreateAddress(ipv4) + a.PrefixLength = ygot.Uint8(plenIPv4) + s6 := s.GetOrCreateIpv6() + if deviations.InterfaceEnabled(dut) { + s6.Enabled = ygot.Bool(true) + } + a6 := s6.GetOrCreateAddress(ipv6) + a6.PrefixLength = ygot.Uint8(plenIPv6) + + return i +} + +// configureDUT configures all the interfaces on the DUT. +func configureDUT(t *testing.T, dut *ondatra.DUTDevice, dutPortList []*ondatra.Port) { + dc := gnmi.OC() + for idx, dp := range dutPortList { + portIDx := portID + uint32(idx) + if i := dutInterface(dp, dut, portIDx); i != nil { + gnmi.Replace(t, dut, dc.Interface(dp.Name()).Config(), i) + } else { + t.Fatalf("No address found for port %v", dp) + } + } + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + for _, dp := range dut.Ports() { + fptest.AssignToNetworkInstance(t, dut, dp.Name(), deviations.DefaultNetworkInstance(dut), 0) + } + } + if deviations.ExplicitPortSpeed(dut) { + for _, dp := range dut.Ports() { + fptest.SetPortSpeed(t, dp) + } + } + + loopbackIntfName = netutil.LoopbackInterface(t, dut, 0) + lo0 := gnmi.OC().Interface(loopbackIntfName).Subinterface(0) + ipv4Addrs := gnmi.LookupAll(t, dut, lo0.Ipv4().AddressAny().State()) + ipv6Addrs := gnmi.LookupAll(t, dut, lo0.Ipv6().AddressAny().State()) + if len(ipv4Addrs) == 0 && len(ipv6Addrs) == 0 { + loop1 := dutlo0Attrs.NewOCInterface(loopbackIntfName, dut) + loop1.Type = oc.IETFInterfaces_InterfaceType_softwareLoopback + gnmi.Update(t, dut, dc.Interface(loopbackIntfName).Config(), loop1) + } else { + v4, ok := ipv4Addrs[0].Val() + if ok { + dutlo0Attrs.IPv4 = v4.GetIp() + } + v6, ok := ipv6Addrs[0].Val() + if ok { + dutlo0Attrs.IPv6 = v6.GetIp() + } + t.Logf("Got DUT IPv4 loopback address: %v", dutlo0Attrs.IPv4) + t.Logf("Got DUT IPv6 loopback address: %v", dutlo0Attrs.IPv6) + } +} + +// configureAdditionalGribiAft configures additional AFT entries for Gribi. +func configureAdditionalGribiAft(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, args *testArgs) { + args.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(3000).WithNextHopNetworkInstance(niRepairVrf), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(3000).AddNextHop(3000, 1), + + fluent.IPv4Entry().WithNetworkInstance(niRepairVrf).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF1111+"/"+maskLen32).WithNextHopGroup(1000), + + fluent.IPv4Entry().WithNetworkInstance(niRepairVrf).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF1112+"/"+maskLen32).WithNextHopGroup(1001), + + fluent.IPv6Entry().WithNetworkInstance(niEncapTeVrfA). + WithPrefix(gribiIPv6EntryEncapVRF+"/"+maskLen126).WithNextHopGroup(101). + 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) + } + + defaultVRFIPList := []string{gribiIPv4EntryVRF1111, gribiIPv4EntryVRF1112} + for ip := range defaultVRFIPList { + chk.HasResult(t, args.client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(defaultVRFIPList[ip]+"/32"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } + // Programming AFT entries for prefixes in ENCAP_TE_VRF_A + args.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(200).WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(200).AddNextHop(200, 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.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(102).AddNextHop(101, 3).AddNextHop(102, 1).WithBackupNHG(200), + fluent.IPv4Entry().WithNetworkInstance(niEncapTeVrfB). + WithPrefix(gribiIPv4EntryEncapVRF+"/"+maskLen24).WithNextHopGroup(102). + WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.IPv6Entry().WithNetworkInstance(niEncapTeVrfB). + WithPrefix(gribiIPv6EntryEncapVRF+"/"+maskLen126).WithNextHopGroup(102). + 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) + } + + chk.HasResult(t, args.client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(gribiIPv4EntryEncapVRF+"/24"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + + chk.HasResult(t, args.client.Results(t), + fluent.OperationResult(). + WithIPv6Operation(gribiIPv6EntryEncapVRF+"/124"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + +} + +// configureGribiRoute configures Gribi route for prefix +func configureGribiRoute(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, args *testArgs, prefWithMask string) { + t.Helper() + // Using gRIBI, install an IPv4Entry for the prefix 192.51.100.1/24 that points to a + // NextHopGroup that contains a single NextHop that specifies decapsulating the IPv4 + // header and specifies the DEFAULT network instance.This IPv4Entry should be installed + // into the DECAP_TE_VRF. + + args.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(3001).WithDecapsulateHeader(fluent.IPinIP), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(3001).AddNextHop(3001, 1), + fluent.IPv4Entry().WithNetworkInstance(niDecapTeVrf). + WithPrefix(prefWithMask).WithNextHopGroup(3001). + 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) + } + + chk.HasResult(t, args.client.Results(t), + fluent.OperationResult().WithIPv4Operation(prefWithMask).WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB).AsResult(), + chk.IgnoreOperationID(), + ) +} + +// configureISIS configures ISIS on the DUT. +func configureISIS(t *testing.T, dut *ondatra.DUTDevice, intfName, dutAreaAddress, dutSysID 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 + + isisIntf := isis.GetOrCreateInterface(intfName) + isisIntf.GetOrCreateInterfaceRef().Interface = ygot.String(intfName) + 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.Enabled = ygot.Bool(true) + isisIntfLevelAfiv6 := isisIntfLevel.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV6, oc.IsisTypes_SAFI_TYPE_UNICAST) + + isisIntfLevelAfiv4.Metric = ygot.Uint32(20) + isisIntfLevelAfiv6.Metric = ygot.Uint32(20) + + isisIntfLevelAfiv6.Enabled = ygot.Bool(true) + if deviations.MissingIsisInterfaceAfiSafiEnable(dut) { + isisIntfLevelAfiv4.Enabled = nil + isisIntfLevelAfiv6.Enabled = nil + } + gnmi.Update(t, dut, gnmi.OC().Config(), d) +} + +// bgpCreateNbr creates BGP neighbor configuration +func bgpCreateNbr(localAs uint32, dut *ondatra.DUTDevice) *oc.NetworkInstance_Protocol { + 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(dutlo0Attrs.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) + pg1 := bgp.GetOrCreatePeerGroup(peerGrpName1) + pg1.PeerAs = ygot.Uint32(localAs) + + bgpNbr := bgp.GetOrCreateNeighbor(otgIsisPort8LoopV4) + bgpNbr.PeerGroup = ygot.String(peerGrpName1) + bgpNbr.PeerAs = ygot.Uint32(localAs) + bgpNbr.Enabled = ygot.Bool(true) + bgpNbrT := bgpNbr.GetOrCreateTransport() + bgpNbrT.LocalAddress = ygot.String(dutlo0Attrs.IPv4) + 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(true) + + return niProto +} + +// verifyISISTelemetry verifies ISIS telemetry. +func verifyISISTelemetry(t *testing.T, dut *ondatra.DUTDevice, dutIntf string) { + t.Helper() + statePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance).Isis() + + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + dutIntf = dutIntf + ".0" + } + nbrPath := statePath.Interface(dutIntf) + query := nbrPath.LevelAny().AdjacencyAny().AdjacencyState().State() + _, ok := gnmi.WatchAll(t, dut, query, time.Minute, func(val *ygnmi.Value[oc.E_Isis_IsisInterfaceAdjState]) bool { + state, present := val.Val() + return present && state == oc.Isis_IsisInterfaceAdjState_UP + }).Await(t) + if !ok { + t.Logf("IS-IS state on %v has no adjacencies", dutIntf) + t.Fatal("No IS-IS adjacencies reported.") + } +} + +// verifyBgpTelemetry verifies BGP telemetry. +func verifyBgpTelemetry(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + t.Logf("Verifying BGP state.") + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + + nbrPath := bgpPath.Neighbor(otgIsisPort8LoopV4) + // Get BGP adjacency state. + t.Logf("Waiting for BGP neighbor to establish...") + 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", otgIsisPort8LoopV4, state) + if want := oc.Bgp_Neighbor_SessionState_ESTABLISHED; state != want { + t.Errorf("BGP peer %s status got %d, want %d", otgIsisPort8LoopV4, state, want) + } +} + +// configureOTG configures the topology of the ATE. +func configureOTG(t testing.TB, otg *otg.OTG, atePorts []*ondatra.Port) gosnappi.Config { + t.Helper() + t.Logf("configureOTG") + config := gosnappi.NewConfig() + 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()) + + port := config.Ports().Add().SetName(ap.ID()) + atePortNamelist = append(atePortNamelist, port.Name()) + portName := fmt.Sprintf("atePort%s", strconv.Itoa(i+1)) + dev := config.Devices().Add().SetName(portName) + macAddress := portsMap[portName].MAC + eth := dev.Ethernets().Add().SetName(portName + ".Eth").SetMac(macAddress) + eth.Connection().SetPortName(port.Name()) + eth.Ipv4Addresses().Add().SetName(portName + ".IPv4"). + SetAddress(portsIPv4[ateid]).SetGateway(portsIPv4[dutid]). + SetPrefix(plenIPv4) + eth.Ipv6Addresses().Add().SetName(portName + ".IPv6"). + SetAddress(portsIPv6[ateid]).SetGateway(portsIPv6[dutid]). + SetPrefix(plenIPv6) + + otgPortDevices = append(otgPortDevices, dev) + if i == 7 { + iDut8LoopV4 := dev.Ipv4Loopbacks().Add().SetName("Port8LoopV4").SetEthName(eth.Name()) + iDut8LoopV4.SetAddress(otgIsisPort8LoopV4) + iDut8LoopV6 := dev.Ipv6Loopbacks().Add().SetName("Port8LoopV6").SetEthName(eth.Name()) + iDut8LoopV6.SetAddress(otgIsisPort8LoopV6) + isisDut := dev.Isis().SetName("ISIS1").SetSystemId(otgSysID1) + isisDut.Basic().SetIpv4TeRouterId(portsIPv4[ateid]).SetHostname(isisDut.Name()).SetLearnedLspFilter(true) + isisDut.Interfaces().Add().SetEthName(dev.Ethernets().Items()[0].Name()). + SetName("devIsisInt1"). + SetLevelType(gosnappi.IsisInterfaceLevelType.LEVEL_2). + SetNetworkType(gosnappi.IsisInterfaceNetworkType.POINT_TO_POINT) + + // Advertise OTG Port8 loopback address via ISIS. + isisPort2V4 := dev.Isis().V4Routes().Add().SetName("ISISPort8V4").SetLinkMetric(10) + isisPort2V4.Addresses().Add().SetAddress(otgIsisPort8LoopV4).SetPrefix(32) + isisPort2V6 := dev.Isis().V6Routes().Add().SetName("ISISPort8V6").SetLinkMetric(10) + isisPort2V6.Addresses().Add().SetAddress(otgIsisPort8LoopV6).SetPrefix(uint32(128)) + iDutBgp := dev.Bgp().SetRouterId(otgIsisPort8LoopV4) + iDutBgp4Peer := iDutBgp.Ipv4Interfaces().Add().SetIpv4Name(iDut8LoopV4.Name()).Peers().Add().SetName(ap.ID() + ".BGP4.peer") + iDutBgp4Peer.SetPeerAddress(dutlo0Attrs.IPv4).SetAsNumber(dutAS).SetAsType(gosnappi.BgpV4PeerAsType.IBGP) + iDutBgp4Peer.Capability().SetIpv4Unicast(true).SetIpv6Unicast(true) + iDutBgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) + + bgpNeti1Bgp4PeerRoutes := iDutBgp4Peer.V4Routes().Add().SetName(port.Name() + ".BGP4.Route") + bgpNeti1Bgp4PeerRoutes.SetNextHopIpv4Address(otgIsisPort8LoopV4). + SetNextHopAddressType(gosnappi.BgpV4RouteRangeNextHopAddressType.IPV4). + SetNextHopMode(gosnappi.BgpV4RouteRangeNextHopMode.MANUAL). + Advanced().SetLocalPreference(100).SetIncludeLocalPreference(true) + bgpNeti1Bgp4PeerRoutes.Addresses().Add().SetAddress(ipv4InnerDst).SetPrefix(32). + SetCount(1).SetStep(1) + bgpNeti1Bgp4PeerRoutes.Addresses().Add().SetAddress(noMatchEncapDest).SetPrefix(32). + SetCount(1).SetStep(1) + + bgpNeti1Bgp6PeerRoutes := iDutBgp4Peer.V6Routes().Add().SetName(atePort8.Name + ".BGP6.Route") + bgpNeti1Bgp6PeerRoutes.SetNextHopIpv6Address(otgIsisPort8LoopV6). + SetNextHopAddressType(gosnappi.BgpV6RouteRangeNextHopAddressType.IPV6). + SetNextHopMode(gosnappi.BgpV6RouteRangeNextHopMode.MANUAL). + Advanced().SetLocalPreference(100).SetIncludeLocalPreference(true) + bgpNeti1Bgp6PeerRoutes.Addresses().Add().SetAddress(ipv6InnerDst).SetPrefix(128). + SetCount(1).SetStep(1) + bgpNeti1Bgp6PeerRoutes.Addresses().Add().SetAddress(ipv6InnerDstNoEncap).SetPrefix(128). + SetCount(1).SetStep(1) + + } + + } + config.Captures().Add().SetName("packetCapture"). + 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) + time.Sleep(30 * time.Second) + pb, _ := config.Marshal().ToProto() + t.Log(pb.GetCaptures()) + return config +} + +func startCapture(t *testing.T, args *testArgs, capturePortList []string) gosnappi.ControlState { + t.Helper() + args.otgConfig.Captures().Clear() + args.otgConfig.Captures().Add().SetName("packetCapture"). + SetPortNames(capturePortList). + SetFormat(gosnappi.CaptureFormat.PCAP) + args.otg.PushConfig(t, args.otgConfig) + time.Sleep(30 * time.Second) + args.otg.StartProtocols(t) + time.Sleep(30 * time.Second) + cs := gosnappi.NewControlState() + cs.Port().Capture().SetState(gosnappi.StatePortCaptureState.START) + args.otg.SetControlState(t, cs) + return cs +} + +func verifyPortStatus(t *testing.T, args *testArgs, portList []string, portStatus bool) { + wantStatus := oc.Interface_OperStatus_UP + if !portStatus { + wantStatus = oc.Interface_OperStatus_DOWN + } + for _, port := range portList { + p := args.dut.Port(t, port) + t.Log("Check for port status") + gnmi.Await(t, args.dut, gnmi.OC().Interface(p.Name()).OperStatus().State(), 1*time.Minute, wantStatus) + operStatus := gnmi.Get(t, args.dut, gnmi.OC().Interface(p.Name()).OperStatus().State()) + if operStatus != wantStatus { + t.Errorf("Get(DUT %v oper status): got %v, want %v", port, operStatus, wantStatus) + } + } +} + +func validateTrafficDistribution(t *testing.T, ate *ondatra.ATEDevice, wantWeights []float64, gotWeights []float64) { + t.Log("Verify packet load balancing as per the programmed weight") + t.Log("got ratio:", gotWeights) + t.Log("want ratio:", wantWeights) + if diff := cmp.Diff(wantWeights, gotWeights, cmpopts.EquateApprox(0, tolerance)); diff != "" { + t.Errorf("Packet distribution ratios -want,+got:\n%s", diff) + } +} + +// testGribiMatchNoSourceNoProtocolMacthDSCP is to test based on packet which doesn't match source IP and protocol +// but match DSCP value +// Test-1 +func testGribiMatchNoSourceNoProtocolMacthDSCP(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, args *testArgs) { + t.Log("Flush existing gRIBI routes before test.") + if err := gribi.FlushAll(args.client); err != nil { + t.Fatal(err) + } + + // Configure GRIBi baseline AFTs. + baseScenario.ConfigureBaseGribiRoutes(ctx, t, dut, args.client) + configureAdditionalGribiAft(ctx, t, dut, args) + + baseCapturePortList := []string{atePortNamelist[1], atePortNamelist[5]} + EgressPortMap := map[string]bool{"11": true, "12": true, "13": true, "14": false, "15": true, "16": false, "17": false} + LoadBalancePercent := []float64{0.0156, 0.0468, 0.1875, 0, 0.75, 0, 0} + flow := []*flowArgs{{flowName: "flow4in4", + outHdrSrcIP: ipv4OuterSrcAddr, outHdrDstIP: ipv4InnerDst, outHdrDscp: []uint32{dscpEncapA1}, + InnHdrSrcIP: ipv4OuterSrcAddr, InnHdrDstIP: ipv4InnerDst, isInnHdrV4: true, udp: true}} + captureState := startCapture(t, args, baseCapturePortList) + gotWeights := testPacket(t, args, captureState, flow, EgressPortMap) + validateTrafficDistribution(t, args.ate, LoadBalancePercent, gotWeights) +} + +// testTunnelTrafficMatchDefaultTerm is to test Tunnel traffic match default term +// Test-2 +func testTunnelTrafficMatchDefaultTerm(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, args *testArgs) { + t.Log("Flush existing gRIBI routes before test.") + if err := gribi.FlushAll(args.client); err != nil { + t.Fatal(err) + } + + // Configure GRIBi baseline AFTs. + baseScenario.ConfigureBaseGribiRoutes(ctx, t, dut, args.client) + configureAdditionalGribiAft(ctx, t, dut, args) + + baseCapturePortList := []string{atePortNamelist[1], atePortNamelist[5]} + EgressPortMap := map[string]bool{"11": false, "12": false, "13": false, "14": false, "15": false, "16": false, "17": true} + LoadBalancePercent := []float64{0, 0, 0, 0, 0, 0, 1} + flow := []*flowArgs{{flowName: "flow4in4", + outHdrSrcIP: ipv4OuterSrcAddr, outHdrDstIP: noMatchEncapDest, + InnHdrSrcIP: ipv4OuterSrcAddr, InnHdrDstIP: noMatchEncapDest, isInnHdrV4: true, udp: true}} + captureState := startCapture(t, args, baseCapturePortList) + gotWeights := testPacket(t, args, captureState, flow, EgressPortMap) + validateTrafficDistribution(t, args.ate, LoadBalancePercent, gotWeights) +} + +// testGribiDecapMatchSrcProtoDSCP is to test Gribi decap match src proto DSCP +// Test-3 +func testGribiDecapMatchSrcProtoDSCP(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, args *testArgs) { + t.Log("Flush existing gRIBI routes before test.") + if err := gribi.FlushAll(args.client); err != nil { + t.Fatal(err) + } + + // Configure GRIBi baseline AFTs. + baseScenario.ConfigureBaseGribiRoutes(ctx, t, dut, args.client) + configureAdditionalGribiAft(ctx, t, dut, args) + baseCapturePortList := []string{atePortNamelist[1], atePortNamelist[5]} + EgressPortMap := map[string]bool{"11": true, "12": true, "13": true, "14": false, "15": false, "16": false, "17": false} + LoadBalancePercent := []float64{0.0625, 0.1875, 0.75, 0, 0, 0, 0} + flow := []*flowArgs{{flowName: "flow4in4", + outHdrSrcIP: ipv4OuterSrc111Addr, outHdrDstIP: gribiIPv4EntryVRF1111, outHdrDscp: []uint32{dscpEncapA1}, + InnHdrSrcIP: atePort1.IPv4, InnHdrDstIP: ipv4InnerDst, isInnHdrV4: true}} + captureState := startCapture(t, args, baseCapturePortList) + gotWeights := testPacket(t, args, captureState, flow, EgressPortMap) + validateTrafficDistribution(t, args.ate, LoadBalancePercent, gotWeights) +} + +// testTunnelTrafficNoDecap is to test Tunnel traffic no decap +// Test-6 +func testTunnelTrafficNoDecap(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, args *testArgs) { + t.Helper() + baseCapturePortList := []string{atePortNamelist[1], atePortNamelist[5]} + EgressPortMap := map[string]bool{"11": false, "12": false, "13": false, "14": false, "15": false, "16": false, "17": true} + LoadBalancePercent := []float64{0, 0, 0, 0, 0, 0, 1} + + cases := []struct { + desc string + prefixWithMask string + }{{ + desc: "Mask Length 24", + prefixWithMask: "192.51.100.0/24", + }, { + desc: "Mask Length 32", + prefixWithMask: "192.51.100.64/32", + }, { + desc: "Mask Length 28", + prefixWithMask: "192.51.100.64/28", + }, { + desc: "Mask Length 22", + prefixWithMask: "192.51.100.0/22", + }} + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + t.Log("Flush existing gRIBI routes before test.") + if err := gribi.FlushAll(args.client); err != nil { + t.Fatal(err) + } + + // Configure GRIBi baseline AFTs. + baseScenario.ConfigureBaseGribiRoutes(ctx, t, dut, args.client) + configureAdditionalGribiAft(ctx, t, dut, args) + + t.Run("Program gRIBi route for Prefix "+tc.prefixWithMask, func(t *testing.T) { + configureGribiRoute(ctx, t, dut, args, tc.prefixWithMask) + }) + t.Run("Create ip-in-ip and ipv6-in-ip flows, send traffic and verify decap functionality", + func(t *testing.T) { + // Send both 6in4 and 4in4 packets. Verify that the packets have their outer + // v4 header stripped and are forwarded according to the route in the DEFAULT + // VRF that matches the inner IP address. + flow := []*flowArgs{{flowName: "flow4in4", + outHdrSrcIP: ipv4OuterSrc111Addr, outHdrDstIP: ipv4OuterDst111, outHdrDscp: []uint32{dscpEncapNoMatch}, + InnHdrSrcIP: ipv4OuterSrcAddr, InnHdrDstIP: ipv4InnerDst, isInnHdrV4: true}, + {flowName: "flow6in4", + outHdrSrcIP: ipv4OuterSrc111Addr, outHdrDstIP: ipv4OuterDst111, outHdrDscp: []uint32{dscpEncapNoMatch}, + InnHdrSrcIPv6: atePort1.IPv6, InnHdrDstIPv6: ipv6InnerDst, isInnHdrV4: false}} + captureState := startCapture(t, args, baseCapturePortList) + gotWeights := testPacket(t, args, captureState, flow, EgressPortMap) + validateTrafficDistribution(t, args.ate, LoadBalancePercent, gotWeights) + }) + }) + } +} + +// testTunnelTrafficDecapEncap is to test Tunnel traffic decap encap +// Test-9 +func testTunnelTrafficDecapEncap(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, args *testArgs) { + t.Log("Flush existing gRIBI routes before test.") + if err := gribi.FlushAll(args.client); err != nil { + t.Fatal(err) + } + + // Configure GRIBi baseline AFTs. + baseScenario.ConfigureBaseGribiRoutes(ctx, t, dut, args.client) + configureAdditionalGribiAft(ctx, t, dut, args) + configureGribiRoute(ctx, t, dut, args, ipv4OuterDst111WithMask) + + baseCapturePortList := []string{atePortNamelist[1], atePortNamelist[5]} + EgressPortMap := map[string]bool{"11": true, "12": true, "13": true, "14": false, "15": true, "16": false, "17": false} + LoadBalancePercent := []float64{0.0156, 0.0468, 0.1875, 0, 0.75, 0, 0} + flow := []*flowArgs{{flowName: "flow4in4", + outHdrSrcIP: ipv4OuterSrc222Addr, outHdrDstIP: ipv4OuterDst111, outHdrDscp: []uint32{dscpEncapA1}, + InnHdrSrcIP: atePort1.IPv4, InnHdrDstIP: ipv4InnerDst, isInnHdrV4: true}, + {flowName: "flow6in4", + outHdrSrcIP: ipv4OuterSrc111Addr, outHdrDstIP: ipv4OuterDst111, outHdrDscp: []uint32{dscpEncapA1}, + InnHdrSrcIPv6: atePort1.IPv6, InnHdrDstIPv6: ipv6InnerDst, isInnHdrV4: false}} + captureState := startCapture(t, args, baseCapturePortList) + gotWeights := testPacket(t, args, captureState, flow, EgressPortMap) + validateTrafficDistribution(t, args.ate, LoadBalancePercent, gotWeights) + + LoadBalancePercent = []float64{0.0468, 0.1406, 0.5625, 0, 0.25, 0, 0} + flow = []*flowArgs{{flowName: "flow4in4", + outHdrSrcIP: ipv4OuterSrc111Addr, outHdrDstIP: ipv4OuterDst111, outHdrDscp: []uint32{dscpEncapB1}, + InnHdrSrcIP: atePort1.IPv4, InnHdrDstIP: ipv4InnerDst, isInnHdrV4: true}, + {flowName: "flow6in4", + outHdrSrcIP: ipv4OuterSrc222Addr, outHdrDstIP: ipv4OuterDst111, outHdrDscp: []uint32{dscpEncapB1}, + InnHdrSrcIPv6: atePort1.IPv6, InnHdrDstIPv6: ipv6InnerDst, isInnHdrV4: false}} + captureState = startCapture(t, args, baseCapturePortList) + gotWeights = testPacket(t, args, captureState, flow, EgressPortMap) + validateTrafficDistribution(t, args.ate, LoadBalancePercent, gotWeights) +} + +// testTraceRoute is to test Test FRR behaviors with encapsulation scenarios +func TestTraceRoute(t *testing.T) { + ctx := context.Background() + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + otg := ate.OTG() + gribic := dut.RawAPIs().GRIBI(t) + top := gosnappi.NewConfig() + dutPorts := sortPorts(dut.Ports())[0:8] + atePorts := sortPorts(ate.Ports())[0:8] + + t.Log("Configure Default Network Instance") + fptest.ConfigureDefaultNetworkInstance(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) + } + + t.Run("Configure Interface on DUT", func(t *testing.T) { + configureDUT(t, dut, dutPorts) + }) + + t.Log("Apply vrf selection policy_c to DUT port-1") + vrfpolicy.ConfigureVRFSelectionPolicy(t, dut, vrfpolicy.VRFPolicyC) + + if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) { + // staticARPWithMagicUniversalIP(t, dut) + baseScenario.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) + + dutConfPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + gnmi.Delete(t, dut, dutConfPath.Config()) + dutConf := bgpCreateNbr(dutAS, dut) + gnmi.Replace(t, dut, dutConfPath.Config(), dutConf) + fptest.LogQuery(t, "DUT BGP Config", dutConfPath.Config(), gnmi.Get(t, dut, dutConfPath.Config())) + + var otgConfig gosnappi.Config + t.Run("Configure OTG", func(t *testing.T) { + otgConfig = configureOTG(t, otg, atePorts) + }) + + // Connect gRIBI client to DUT referred to as gRIBI - using PRESERVE persistence and + // SINGLE_PRIMARY mode, with FIB ACK requested. Specify gRIBI as the leader. + client := fluent.NewClient() + client.Connection().WithStub(gribic).WithPersistence().WithInitialElectionID(1, 0). + WithFIBACK().WithRedundancyMode(fluent.ElectedPrimaryClient) + client.Start(ctx, t) + + client.StartSending(ctx, t) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Fatalf("Await got error during session negotiation for clientA: %v", err) + } + eID := gribi.BecomeLeader(t, client) + + leader := p4rt_client.NewP4RTClient(&p4rt_client.P4RTClientParameters{}) + if err := leader.P4rtClientSet(dut.RawAPIs().P4RT(t)); err != nil { + t.Fatalf("Could not initialize p4rt client: %v", err) + } + + follower := p4rt_client.NewP4RTClient(&p4rt_client.P4RTClientParameters{}) + if err := follower.P4rtClientSet(dut.RawAPIs().P4RT(t)); err != nil { + t.Fatalf("Could not initialize p4rt client: %v", err) + } + args := &testArgs{ + ctx: ctx, + client: client, + dut: dut, + ate: ate, + otgConfig: otgConfig, + top: top, + electionID: eID, + otg: otg, + leader: leader, + follower: follower, + } + t.Log("Configure gRIBI routes") + t.Log("Flush existing gRIBI routes before test.") + if err := gribi.FlushAll(client); err != nil { + t.Fatal(err) + } + + t.Log("Verify whether the ports are in up state") + portList := []string{"port2", "port3", "port4", "port5", "port6", "port7", "port8"} + verifyPortStatus(t, args, portList, true) + + t.Log("Verify ISIS telemetry") + verifyISISTelemetry(t, dut, dut.Port(t, "port8").Name()) + t.Log("Verify BGP telemetry") + verifyBgpTelemetry(t, dut) + + t.Run("Test-1: Match on DSCP, no Source and no Protocol", func(t *testing.T) { + testGribiMatchNoSourceNoProtocolMacthDSCP(ctx, t, dut, args) + }) + + t.Log("Delete vrf selection policy C and Apply vrf selectioin policy W") + vrfpolicy.ConfigureVRFSelectionPolicy(t, dut, vrfpolicy.VRFPolicyW) + + t.Run("Test-2: Match on default term and send to default VRF", func(t *testing.T) { + testTunnelTrafficMatchDefaultTerm(ctx, t, dut, args) + }) + t.Run("Test-3: Match on source, protocol and DSCP, VRF_DECAP hit -> VRF_ENCAP_A miss -> DEFAULT", func(t *testing.T) { + testGribiDecapMatchSrcProtoDSCP(ctx, t, dut, args) + }) + // Below test case will implement later + /* + t.Run("Test-4: Tests that traceroute respects transit FRR", func(t *testing.T) { + + }) + t.Run("Test-5: Tests that traceroute respects transit FRR when the backup is also unviable.", func(t *testing.T) { + + })*/ + t.Run("Test-6: Tunneled traffic with no decap", func(t *testing.T) { + testTunnelTrafficNoDecap(ctx, t, dut, args) + }) + // Below test case will implement later + /* + t.Run("Test-7: Encap failure cases (TBD on confirmation)", func(t *testing.T) { + + }) + t.Run("Test-8: Tests that traceroute for a packet with a route lookup miss has an unset target_egress_port.", func(t *testing.T) { + + })*/ + t.Run("Test-9: Decap then encap", func(t *testing.T) { + testTunnelTrafficDecapEncap(ctx, t, dut, args) + }) +} diff --git a/feature/experimental/p4rt/otg_tests/traceroute_packetout_test/README.md b/feature/experimental/p4rt/otg_tests/traceroute_packetout_test/README.md index 36f0890ad29..409e605ef8e 100644 --- a/feature/experimental/p4rt/otg_tests/traceroute_packetout_test/README.md +++ b/feature/experimental/p4rt/otg_tests/traceroute_packetout_test/README.md @@ -50,13 +50,23 @@ setting must not be interpreted as the actual egress port id. * Validate: * Traffic received over the appropriate ATE port. - - -## Protocol/RPC Parameter Coverage - -* No new configuration covered. - - -## Telemetry Parameter Coverage - -* No new telemetry covered. +## 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: + # config paths + /interfaces/interface/config/id: + /components/component/integrated-circuit/config/node-id: + platform_type: ["INTEGRATED_CIRCUIT"] + # state paths + /interfaces/interface/state/id: + /components/component/integrated-circuit/state/node-id: + platform_type: ["INTEGRATED_CIRCUIT"] + +rpcs: + gnmi: + gNMI.Set: + gNMI.Subscribe: +``` diff --git a/feature/experimental/p4rt/otg_tests/traceroute_packetout_test/traceroute_packetout_test.go b/feature/experimental/p4rt/otg_tests/traceroute_packetout_test/traceroute_packetout_test.go index 2b6cbf087ea..74b7b98b949 100644 --- a/feature/experimental/p4rt/otg_tests/traceroute_packetout_test/traceroute_packetout_test.go +++ b/feature/experimental/p4rt/otg_tests/traceroute_packetout_test/traceroute_packetout_test.go @@ -32,6 +32,7 @@ import ( "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/featureprofiles/internal/p4rtutils" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" @@ -238,6 +239,8 @@ func TestPacketOut(t *testing.T) { otg := ate.OTG() otg.PushConfig(t, top) otg.StartProtocols(t) + otgutils.WaitForARP(t, ate.OTG(), top, "IPv4") + otgutils.WaitForARP(t, ate.OTG(), top, "IPv6") configureDeviceID(ctx, t, dut) diff --git a/feature/experimental/policy/otg_tests/prefix_set_test/README.md b/feature/experimental/policy/otg_tests/prefix_set_test/README.md new file mode 100644 index 00000000000..20ccedcaaa8 --- /dev/null +++ b/feature/experimental/policy/otg_tests/prefix_set_test/README.md @@ -0,0 +1,148 @@ +# RT-1.53: prefix-list test + +## Summary + +- Prefix list is updated and replaced correctly after restarting the process + with supports gNOI to validate that internal state of OC agent is in sync + with the running configuration. + +## Testbed type + +* https://github.com/openconfig/featureprofiles/blob/main/topologies/dut.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 + +### RT-1.53.1 [TODO:https://github.com/openconfig/featureprofiles/issues/3306] + +#### Create a prefix-set with 2 prefixes + +* Create a prefix-set with name "prefix-set-a" + * /routing-policy/defined-sets/prefix-sets/prefix-set/config/name +* Set the mode to IPv4 + * /routing-policy/defined-sets/prefix-sets/prefix-set/config/mode +* Define two prefixes 10.240.31.48/28 and 173.36.128.0/20 with mask "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 +* Validate that the prefix-list is created correctly with two prefixes i.e. + 10.240.31.48/28 and 173.36.128.0/20 + * /routing-policy/defined-sets/prefix-sets/prefix-set/state/name + * /routing-policy/defined-sets/prefix-sets/prefix-set/state/mode + * /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/state/ip-prefix + * /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/state/masklength-range + +### RT-1.53.2 [TODO:https://github.com/openconfig/featureprofiles/issues/3306] + +#### Replace the prefix-set by replacing an existing prefix with new prefix + +* Define two prefixes 10.240.31.48/28 and 173.36.144.0/20 with mask "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 +* Replace the previous prefix-list +* Validate that the prefix-list is created correctly with two prefixes i.e. + 10.240.31.48/28 and 173.36.144.0/20 + * /routing-policy/defined-sets/prefix-sets/prefix-set/state/name + * /routing-policy/defined-sets/prefix-sets/prefix-set/state/mode + * /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/state/ip-prefix + * /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/state/masklength-range + +### RT-1.53.3 [TODO:https://github.com/openconfig/featureprofiles/issues/3306] + +### Replace the prefix-set with 2 existing and a new prR + +* Define three prefixes 10.240.31.48/28, 10.240.31.64/28 and 173.36.144.0/20 + with mask "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 +* Replace the previous prefix-list +* Validate that the prefix-list is created correctly with three prefixes + 10.240.31.48/28, 10.240.31.64/28 and 173.36.144.0/20 + * /routing-policy/defined-sets/prefix-sets/prefix-set/state/name + * /routing-policy/defined-sets/prefix-sets/prefix-set/state/mode + * /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/state/ip-prefix + * /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/state/masklength-range + +### RT-1.53.4 [TODO:https://github.com/openconfig/featureprofiles/issues/3306] + +### Create prefix list and replace with gnmi. + +* Send a gNMI SET request that contains below prefixes under TAG_3_IPV4 prefix-set + ``` + 10.240.31.48/28 + 10.244.187.32/28 + 173.36.128.0/20 + 173.37.128.0/20 + 173.38.128.0/20 + 173.39.128.0/20 + 173.40.128.0/20 + 173.41.128.0/20 + 173.42.128.0/20 + 173.43.128.0/20 + ``` +* Validate that the prefix-list is created correctly with 10 prefixes. +* Use gNOI to kill the process supporting gNMI. +* Send a gNMI SET request that contains additional prefixes within the same + prefix-set, TAG_3_IPV4. + ``` + 173.49.128.0/20 + 173.46.128.0/20 + 10.240.31.48/28 + 173.44.128.0/20 + 173.43.128.0/20 + 173.47.128.0/20 + 173.40.128.0/20 + 173.37.128.0/20 + 173.39.128.0/20 + 173.38.128.0/20 + 173.42.128.0/20 + 10.244.187.32/28 + 173.41.128.0/20 + 173.36.128.0/20 + 173.50.128.0/20 + 173.51.128.0/20 + 173.52.128.0/20 + 173.53.128.0/20 + 173.54.128.0/20 + 173.55.128.0/20 + 173.48.128.0/20 + 173.45.128.0/20 + ``` +* Validate that the prefix-list is created correctly with 22 prefixes. + +## 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 + ### prefix-set + /routing-policy/defined-sets/prefix-sets/prefix-set/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: + + ## State paths + ### prefix-list + /routing-policy/defined-sets/prefix-sets/prefix-set/state/name: + /routing-policy/defined-sets/prefix-sets/prefix-set/state/mode: + /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/state/ip-prefix: + /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/state/masklength-range: + +rpcs: + gnmi: + gNMI.Set: + gNMI.Subscribe: + gnoi: + system.System.KillProcess: +``` + +## Required DUT platform + +- vRX diff --git a/feature/experimental/policy/otg_tests/prefix_set_test/metadata.textproto b/feature/experimental/policy/otg_tests/prefix_set_test/metadata.textproto new file mode 100644 index 00000000000..b47a1749263 --- /dev/null +++ b/feature/experimental/policy/otg_tests/prefix_set_test/metadata.textproto @@ -0,0 +1,15 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "619040ac-21a0-403f-b38e-6d5d0aed433a" +plan_id: "RT-1.53" +description: "prefix-list test" +testbed: TESTBED_DUT +platform_exceptions: { + platform: { + vendor: NOKIA + } + deviations: { + skip_prefix_set_mode: true + } + } \ No newline at end of file diff --git a/feature/experimental/policy/otg_tests/prefix_set_test/prefix_set_test.go b/feature/experimental/policy/otg_tests/prefix_set_test/prefix_set_test.go new file mode 100644 index 00000000000..c52f4887069 --- /dev/null +++ b/feature/experimental/policy/otg_tests/prefix_set_test/prefix_set_test.go @@ -0,0 +1,170 @@ +// 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 prefix_set_test + +import ( + "testing" + + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/gnoi" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" +) + +const ( + prefixSetA = "PFX_SET_A" + tag3IPv4 = "TAG_3_IPV4" + pfx1 = "10.240.31.48/28" + pfx2 = "173.36.128.0/20" + pfx3 = "173.36.144.0/20" + pfx4 = "10.240.31.64/28" + mskLen = "exact" +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +func TestPrefixSet(t *testing.T) { + dut := ondatra.DUT(t, "dut") + + dutOcRoot := &oc.Root{} + rp := dutOcRoot.GetOrCreateRoutingPolicy() + ds := rp.GetOrCreateDefinedSets() + + // create a prefix-set with 2 prefixes + v4PrefixSet := ds.GetOrCreatePrefixSet(prefixSetA) + if !deviations.SkipPrefixSetMode(dut) { + v4PrefixSet.SetMode(oc.PrefixSet_Mode_IPV4) + } + v4PrefixSet.GetOrCreatePrefix(pfx1, mskLen) + v4PrefixSet.GetOrCreatePrefix(pfx2, mskLen) + + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(prefixSetA).Config(), v4PrefixSet) + prefixSet := gnmi.Get[*oc.RoutingPolicy_DefinedSets_PrefixSet](t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(prefixSetA).State()) + if len(prefixSet.Prefix) != 2 { + t.Errorf("Prefix set has %v prefixes, want 2", len(prefixSet.Prefix)) + } + for _, pfx := range []string{pfx1, pfx2} { + if x := prefixSet.GetPrefix(pfx, mskLen); x == nil { + t.Errorf("%s not found in prefix-set %s", pfx, prefixSetA) + } + } + + // replace the prefix-set by replacing an existing prefix with new prefix + v4PrefixSet = ds.GetOrCreatePrefixSet(prefixSetA) + if !deviations.SkipPrefixSetMode(dut) { + v4PrefixSet.SetMode(oc.PrefixSet_Mode_IPV4) + } + v4PrefixSet.GetOrCreatePrefix(pfx1, mskLen) + v4PrefixSet.GetOrCreatePrefix(pfx3, mskLen) + v4PrefixSet.DeletePrefix(pfx2, mskLen) + + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(prefixSetA).Config(), v4PrefixSet) + prefixSet = gnmi.Get[*oc.RoutingPolicy_DefinedSets_PrefixSet](t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(prefixSetA).State()) + if len(prefixSet.Prefix) != 2 { + t.Errorf("Prefix set has %v prefixes, want 2", len(prefixSet.Prefix)) + } + for _, pfx := range []string{pfx1, pfx3} { + if x := prefixSet.GetPrefix(pfx, mskLen); x == nil { + t.Errorf("%s not found in prefix-set %s", pfx, prefixSetA) + } + } + + // replace the prefix-set with 2 existing and a new prefix + v4PrefixSet = ds.GetOrCreatePrefixSet(prefixSetA) + if !deviations.SkipPrefixSetMode(dut) { + v4PrefixSet.SetMode(oc.PrefixSet_Mode_IPV4) + } + v4PrefixSet.GetOrCreatePrefix(pfx1, mskLen) + v4PrefixSet.GetOrCreatePrefix(pfx3, mskLen) + v4PrefixSet.GetOrCreatePrefix(pfx4, mskLen) + v4PrefixSet.DeletePrefix(pfx2, mskLen) + + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(prefixSetA).Config(), v4PrefixSet) + prefixSet = gnmi.Get[*oc.RoutingPolicy_DefinedSets_PrefixSet](t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(prefixSetA).State()) + if len(prefixSet.Prefix) != 3 { + t.Errorf("Prefix set has %v prefixes, want 3", len(prefixSet.Prefix)) + } + for _, pfx := range []string{pfx1, pfx3, pfx4} { + if x := prefixSet.GetPrefix(pfx, mskLen); x == nil { + t.Errorf("%s not found in prefix-set %s", pfx, prefixSetA) + } + } +} + +func TestPrefixSetWithOCAgentRestart(t *testing.T) { + dut := ondatra.DUT(t, "dut") + + dutOcRoot := &oc.Root{} + rp := dutOcRoot.GetOrCreateRoutingPolicy() + ds := rp.GetOrCreateDefinedSets() + v4PrefixSet := ds.GetOrCreatePrefixSet(tag3IPv4) + if !deviations.SkipPrefixSetMode(dut) { + v4PrefixSet.SetMode(oc.PrefixSet_Mode_IPV4) + } + v4PrefixSet.GetOrCreatePrefix("10.240.31.48/28", mskLen) + v4PrefixSet.GetOrCreatePrefix("10.244.187.32/28", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.36.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.37.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.38.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.39.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.40.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.41.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.42.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.43.128.0/20", mskLen) + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(tag3IPv4).Config(), v4PrefixSet) + prefixSet := gnmi.Get[*oc.RoutingPolicy_DefinedSets_PrefixSet](t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(tag3IPv4).State()) + if got, want := len(prefixSet.Prefix), 10; got != want { + t.Errorf("Prefix set has %v prefixes, want %v", got, want) + } + + gnoi.KillProcess(t, dut, gnoi.OCAGENT, gnoi.SigTerm, true, true) + + v4PrefixSet = ds.GetOrCreatePrefixSet(tag3IPv4) + if !deviations.SkipPrefixSetMode(dut) { + v4PrefixSet.SetMode(oc.PrefixSet_Mode_IPV4) + } + v4PrefixSet.GetOrCreatePrefix("173.49.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.46.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("10.240.31.48/28", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.44.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.43.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.47.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.40.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.37.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.39.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.38.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.42.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("10.244.187.32/28", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.41.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.36.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.50.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.51.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.52.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.53.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.54.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.55.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.48.128.0/20", mskLen) + v4PrefixSet.GetOrCreatePrefix("173.45.128.0/20", mskLen) + + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(tag3IPv4).Config(), v4PrefixSet) + prefixSet = gnmi.Get[*oc.RoutingPolicy_DefinedSets_PrefixSet](t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(tag3IPv4).State()) + if got, want := len(prefixSet.Prefix), 22; got != want { + t.Errorf("Prefix set has %v prefixes, want %v", got, want) + } +} diff --git a/feature/experimental/policy/policy_vrf_selection/otg_tests/base_vrf_selection/README.md b/feature/experimental/policy/policy_vrf_selection/otg_tests/base_vrf_selection/README.md index 6f1c9db21b1..dd52ba7e1f4 100644 --- a/feature/experimental/policy/policy_vrf_selection/otg_tests/base_vrf_selection/README.md +++ b/feature/experimental/policy/policy_vrf_selection/otg_tests/base_vrf_selection/README.md @@ -93,3 +93,12 @@ Test different VRF selection policies. * Native IPv6 * Flow#10: Native IPv6 flow with any source address and destination as ATE-DEST-IPv6-VLAN20 + +## OpenConfig Path and RPC Coverage +```yaml +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: +``` diff --git a/feature/experimental/policy/policy_vrf_selection/otg_tests/base_vrf_selection/metadata.textproto b/feature/experimental/policy/policy_vrf_selection/otg_tests/base_vrf_selection/metadata.textproto index 27f20808b20..86c15e72596 100644 --- a/feature/experimental/policy/policy_vrf_selection/otg_tests/base_vrf_selection/metadata.textproto +++ b/feature/experimental/policy/policy_vrf_selection/otg_tests/base_vrf_selection/metadata.textproto @@ -32,7 +32,6 @@ platform_exceptions: { deviations: { static_protocol_name: "STATIC" interface_config_vrf_before_address: true - deprecated_vlan_id: true interface_enabled: true default_network_instance: "default" } diff --git a/feature/experimental/policy/policy_vrf_selection/otg_tests/protocol_dscp_rules_for_vrf_selection_test/README.md b/feature/experimental/policy/policy_vrf_selection/otg_tests/protocol_dscp_rules_for_vrf_selection_test/README.md index 741f901e70b..944b0aa33ff 100644 --- a/feature/experimental/policy/policy_vrf_selection/otg_tests/protocol_dscp_rules_for_vrf_selection_test/README.md +++ b/feature/experimental/policy/policy_vrf_selection/otg_tests/protocol_dscp_rules_for_vrf_selection_test/README.md @@ -50,21 +50,21 @@ It's ok that some NOS does not support this config (duplicated matching conditio Ensure that unspecified fields are wildcard and IPinIP packets are only received at VLAN 10 subinterface. -## Config Parameter Coverage - * /openconfig-network-instance/network-instances/network-instance/policy-forwarding/policies/policy/config/type - * /openconfig-network-instance/network-instances/network-instance/policy-forwarding/policies/policy/policy-id - * /openconfig-network-instance/network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/sequence-id - * /openconfig-network-instance/network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv4/config/dscp-set - * /openconfig-network-instance/network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv4/config/protocol - * /openconfig-network-instance/network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/config/network-instance - * /openconfig-network-instance/network-instances/network-instance/policy-forwarding/interfaces/interface/interface-id - * /openconfig-network-instance/network-instances/network-instance/policy-forwarding/interfaces/interface/config/apply-vrf-selection-policy - -## Paths - -* /openconfig-network-instance/network-instances/network-instance/policy-forwarding -* /openconfig-network-instance/network-instances/network-instance/policy-forwarding/policies/policy -* /openconfig-network-instance/network-instances/network-instance/policy-forwarding/policies/policy/rules/rule -* /openconfig-network-instance/network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv4 -* /openconfig-network-instance/network-instances/network-instance/policy-forwarding/interfaces/interface/config/apply-vrf-selection-policy +## OpenConfig Path and RPC Coverage +```yaml +paths: + /network-instances/network-instance/policy-forwarding/policies/policy/config/type: + /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/config/dscp-set: + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv4/config/protocol: + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/config/network-instance: + /network-instances/network-instance/policy-forwarding/interfaces/interface/interface-id: + /network-instances/network-instance/policy-forwarding/interfaces/interface/config/apply-vrf-selection-policy: +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: +``` diff --git a/feature/experimental/policy/policy_vrf_selection/otg_tests/protocol_dscp_rules_for_vrf_selection_test/metadata.textproto b/feature/experimental/policy/policy_vrf_selection/otg_tests/protocol_dscp_rules_for_vrf_selection_test/metadata.textproto index f3b9f600304..aebaee33c52 100644 --- a/feature/experimental/policy/policy_vrf_selection/otg_tests/protocol_dscp_rules_for_vrf_selection_test/metadata.textproto +++ b/feature/experimental/policy/policy_vrf_selection/otg_tests/protocol_dscp_rules_for_vrf_selection_test/metadata.textproto @@ -39,7 +39,6 @@ platform_exceptions: { vendor: ARISTA } deviations: { - deprecated_vlan_id: true interface_enabled: true default_network_instance: "default" } diff --git a/feature/experimental/system/gnmi/benchmarking/tests/full_configuration_replace_test/README.md b/feature/experimental/system/gnmi/benchmarking/tests/full_configuration_replace_test/README.md index 671e98cab28..2589aeda5c4 100644 --- a/feature/experimental/system/gnmi/benchmarking/tests/full_configuration_replace_test/README.md +++ b/feature/experimental/system/gnmi/benchmarking/tests/full_configuration_replace_test/README.md @@ -17,9 +17,26 @@ Measure time for Set to complete. Notes: This test does not measure the time to an entirely converged state, only to completion of the gNMI update. -## Config Parameter Coverage +## OpenConfig Path and RPC Coverage +```yaml +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: -## Telemetry Parameter Coverage +paths: + ## Config Parameter coverage + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/config/set-med: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-as-path-prepend/config/repeat-n: + /network-instances/network-instance/protocols/protocol/isis/interfaces/interface/levels/level/afi-safi/af/state/metric: + /network-instances/network-instance/protocols/protocol/isis/global/lsp-bit/overload-bit/state/set-bit: + +``` + +## Minimum DUT Required + +vRX - Virtual Router Device diff --git a/feature/experimental/system/gnmi/benchmarking/tests/full_configuration_replace_test/metadata.textproto b/feature/experimental/system/gnmi/benchmarking/tests/full_configuration_replace_test/metadata.textproto index 6f08f440766..64eed0547a0 100644 --- a/feature/experimental/system/gnmi/benchmarking/tests/full_configuration_replace_test/metadata.textproto +++ b/feature/experimental/system/gnmi/benchmarking/tests/full_configuration_replace_test/metadata.textproto @@ -23,7 +23,6 @@ platform_exceptions: { vendor: JUNIPER } deviations: { - route_policy_under_afi_unsupported: true isis_level_enabled: true } } diff --git a/feature/experimental/system/health/tests/system_generic_health_check/README.md b/feature/experimental/system/health/tests/system_generic_health_check/README.md index 7e7c8e7ab70..fbb026098c2 100644 --- a/feature/experimental/system/health/tests/system_generic_health_check/README.md +++ b/feature/experimental/system/health/tests/system_generic_health_check/README.md @@ -35,50 +35,41 @@ Generic Health Check N/A -## Telemetry Parameter Coverage +## OpenConfig Path and RPC Coverage -* /components/component/state/oper-status -* /components/component/cpu/utilization/state/avg -* /components/component/state/memory -* /system/processes/process/state/cpu-utilization -* /system/processes/process/state/memory-utilization -* /qos/interfaces/interface/input/queues/queue/state/dropped-pkts -* /qos/interfaces/interface/output/queues/queue/state/dropped-pkts -* /qos/interfaces/interface/input/virtual-output-queues/voq-interface/queues/queue/state/dropped-pkts -* /interfaces/interface/state/counters/in-discards -* /interfaces/interface/state/counters/in-errors -* /interfaces/interface/state/counters/in-multicast-pkts -* /interfaces/interface/state/counters/in-unknown-protos -* /interfaces/interface/state/counters/out-discards -* /interfaces/interface/state/counters/out-errors -* /interfaces/interface/state/oper-status -* /interfaces/interface/state/admin-status -* /interfaces/interface/state/counters/out-octets -* /interfaces/interface/state/description -* /interfaces/interface/state/type -* /interfaces/interface/state/counters/out-octets/in-fcs-errors -* /interfaces/interface/subinterfaces/subinterface/state/counters/in-discards -* /interfaces/interface/subinterfaces/subinterface/state/counters/in-errors -* /interfaces/interface/subinterfaces/subinterface/state/counters/in-unknown-protos -* /interfaces/interface/subinterfaces/subinterface/state/counters/out-discards -* /interfaces/interface/subinterfaces/subinterface/state/counters/out-errors -* /interfaces/interface/subinterfaces/subinterface/state/counters/out-octets/in-fcs-errors -* /interfaces/interface/ethernet/state/counters/in-mac-pause-frames -* /interfaces/interface/ethernet/state/counters/out-mac-pause-frames -* /interfaces/interface/ethernet/state/counters/in-crc-errors -* /interfaces/interface/ethernet/state/counters/in-block-errors -* /components/component/integrated-circuit/pipeline-counters/drop/lookup-block/state/acl-drops -* /components/component/integrated-circuit/pipeline-counters/drop/lookup-block/state/forwarding-policy -* /components/component/integrated-circuit/pipeline-counters/drop/lookup-block/state/fragment-total-drops -* /components/component/integrated-circuit/pipeline-counters/drop/lookup-block/state/incorrect-software-state -* /components/component/integrated-circuit/pipeline-counters/drop/lookup-block/state/invalid-packet -* /components/component/integrated-circuit/pipeline-counters/drop/lookup-block/state/no-label -* /components/component/integrated-circuit/pipeline-counters/drop/lookup-block/state/no-nexthop -* /components/component/integrated-circuit/pipeline-counters/drop/lookup-block/state/no-route -* /components/component/integrated-circuit/pipeline-counters/drop/lookup-block/state/rate-limit -* /components/component/integrated-circuit/pipeline-counters/drop/interface-block/state/in-drops -* /components/component/integrated-circuit/pipeline-counters/drop/interface-block/state/out-drops -* /components/component/integrated-circuit/pipeline-counters/drop/interface-block/state/oversubscription -* /components/component/integrated-circuit/pipeline-counters/drop/fabric-block/state/lost-packets +```yaml +rpcs: + gnmi: + gNMI.Get: -## Protocol/RPC Parameter Coverage \ No newline at end of file +paths: + ## Config Parameter coverage + + /system/processes/process/state/cpu-utilization: + /system/processes/process/state/memory-utilization: + /qos/interfaces/interface/input/queues/queue/state/dropped-pkts: + /qos/interfaces/interface/output/queues/queue/state/dropped-pkts: + /qos/interfaces/interface/input/virtual-output-queues/voq-interface/queues/queue/state/dropped-pkts: + /interfaces/interface/state/counters/in-discards: + /interfaces/interface/state/counters/in-errors: + /interfaces/interface/state/counters/in-multicast-pkts: + /interfaces/interface/state/counters/in-unknown-protos: + /interfaces/interface/state/counters/out-discards: + /interfaces/interface/state/counters/out-errors: + /interfaces/interface/state/oper-status: + /interfaces/interface/state/admin-status: + /interfaces/interface/state/counters/out-octets: + /interfaces/interface/state/description: + /interfaces/interface/state/type: + /interfaces/interface/subinterfaces/subinterface/state/counters/in-discards: + /interfaces/interface/subinterfaces/subinterface/state/counters/in-errors: + /interfaces/interface/subinterfaces/subinterface/state/counters/in-unknown-protos: + /interfaces/interface/subinterfaces/subinterface/state/counters/out-discards: + /interfaces/interface/subinterfaces/subinterface/state/counters/out-errors: + /interfaces/interface/ethernet/state/counters/in-mac-pause-frames: + /interfaces/interface/ethernet/state/counters/out-mac-pause-frames: + /interfaces/interface/ethernet/state/counters/in-crc-errors: + /interfaces/interface/ethernet/state/counters/in-block-errors: +``` + +## Protocol/RPC Parameter Coverage diff --git a/feature/experimental/system/health/tests/system_generic_health_check/metadata.textproto b/feature/experimental/system/health/tests/system_generic_health_check/metadata.textproto index d45bb55810b..7c47220cea4 100644 --- a/feature/experimental/system/health/tests/system_generic_health_check/metadata.textproto +++ b/feature/experimental/system/health/tests/system_generic_health_check/metadata.textproto @@ -16,6 +16,7 @@ platform_exceptions: { fabric_drop_counter_unsupported: true linecard_memory_utilization_unsupported: true qos_voq_drop_counter_unsupported: true + qos_inqueue_drop_counter_unsupported: true } } tags: TAGS_AGGREGATION diff --git a/feature/experimental/system/health/tests/system_generic_health_check/system_generic_health_check_test.go b/feature/experimental/system/health/tests/system_generic_health_check/system_generic_health_check_test.go index ee962069738..2c182375063 100644 --- a/feature/experimental/system/health/tests/system_generic_health_check/system_generic_health_check_test.go +++ b/feature/experimental/system/health/tests/system_generic_health_check/system_generic_health_check_test.go @@ -157,6 +157,10 @@ func TestComponentStatus(t *testing.T) { // check oper-status of the components is Active. for _, component := range checkComponents { t.Run(component, func(t *testing.T) { + compMtyVal, compMtyPresent := gnmi.Lookup(t, dut, gnmi.OC().Component(component).Empty().State()).Val() + if compMtyPresent && compMtyVal { + t.Skipf("INFO: Skip status check as %s is empty", component) + } val, present := gnmi.Lookup(t, dut, gnmi.OC().Component(component).OperStatus().State()).Val() if !present { t.Errorf("ERROR: Get component %s oper-status failed", component) @@ -366,12 +370,23 @@ func TestNoQueueDrop(t *testing.T) { for _, intf := range interfaces { t.Run(intf, func(t *testing.T) { qosInterface := gnmi.OC().Qos().Interface(intf) + if deviations.QOSInQueueDropCounterUnsupported(dut) { + t.Skipf("INFO: Skipping test due to %s does not support Queue Input Dropped packets", dut.Vendor()) + counters := gnmi.LookupAll(t, dut, qosInterface.Input().QueueAny().DroppedPkts().State()) + t.Logf("counters: %s", counters) + if len(counters) == 0 { + t.Errorf("%s Interface Queue Input Dropped packets Telemetry Value is not present", intf) + } + for queueID, dropPkt := range counters { + dropCount, present := dropPkt.Val() + if !present { + t.Errorf("%s Interface %s Telemetry Value is not present", intf, dropPkt.Path) + } else { + t.Logf("%s Interface %s, Queue %d has %d drop(s)", dropPkt.Path.GetOrigin(), intf, queueID, dropCount) + } + } + } cases := []testCase{ - { - desc: "Queue Input Dropped packets", - path: "/qos/interfaces/interface/input/queues/queue/state/dropped-pkts", - counters: gnmi.LookupAll(t, dut, qosInterface.Input().QueueAny().DroppedPkts().State()), - }, { desc: "Queue Output Dropped packets", path: "/qos/interfaces/interface/output/queues/queue/state/dropped-pkts", @@ -582,6 +597,7 @@ func TestInterfacesubIntfs(t *testing.T) { t.Fatalf("ERROR: subIntf index value doesn't exist") } subIntfPath := gnmi.OC().Interface(intf).Subinterface(subIntfIndex) + IntfPath := gnmi.OC().Interface(intf) subIntfState := gnmi.Get(t, dut, subIntfPath.State()) subIntf := subIntfState.GetName() @@ -613,7 +629,7 @@ func TestInterfacesubIntfs(t *testing.T) { t.Errorf("ERROR: Counter InMulticastPkts is not present on interface %s, %s", subIntf, intf) } - counters := subIntfPath.Counters() + counters := IntfPath.Counters() parentCounters := gnmi.OC().Interface(intf).Counters() cases := []struct { @@ -652,7 +668,7 @@ func TestInterfacesubIntfs(t *testing.T) { parentCounter: parentCounters.InFcsErrors().State(), }, } - + t.Logf("Verifying counters for Interfaces: %s", interfaces) for _, c := range cases { t.Run(c.desc, func(t *testing.T) { if val, present := gnmi.Lookup(t, dut, c.counter).Val(); present { 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 7e0b5875853..9918782cd47 100644 --- a/feature/gnmi/otg_tests/telemetry_basic_check_test/metadata.textproto +++ b/feature/gnmi/otg_tests/telemetry_basic_check_test/metadata.textproto @@ -31,7 +31,6 @@ platform_exceptions: { vendor: NOKIA } deviations: { - explicit_p4rt_node_component: true explicit_port_speed: true explicit_interface_in_default_vrf: true interface_enabled: true @@ -42,7 +41,6 @@ platform_exceptions: { vendor: JUNIPER } deviations: { - sw_version_unsupported: true qos_dropped_octets: true } } diff --git a/feature/gnmi/otg_tests/telemetry_basic_check_test/telemetry_basic_check_test.go b/feature/gnmi/otg_tests/telemetry_basic_check_test/telemetry_basic_check_test.go index 8599ebf4f60..e66ca0d9af4 100644 --- a/feature/gnmi/otg_tests/telemetry_basic_check_test/telemetry_basic_check_test.go +++ b/feature/gnmi/otg_tests/telemetry_basic_check_test/telemetry_basic_check_test.go @@ -15,7 +15,6 @@ package telemetry_basic_check_test import ( - "fmt" "math" "regexp" "strconv" @@ -920,58 +919,10 @@ func ConfigureDUTIntf(t *testing.T, dut *ondatra.DUTDevice) { } } -func explicitP4RTNodes() map[string]string { - return map[string]string{ - "port1": *args.P4RTNodeName1, - "port2": *args.P4RTNodeName2, - } -} - -var nokiaPortNameRE = regexp.MustCompile("ethernet-([0-9]+)/([0-9]+)") - -// inferP4RTNodesNokia infers the P4RT node name from the port name for Nokia devices. -func inferP4RTNodesNokia(t testing.TB, dut *ondatra.DUTDevice) map[string]string { - res := make(map[string]string) - for _, p := range dut.Ports() { - m := nokiaPortNameRE.FindStringSubmatch(p.Name()) - if len(m) != 3 { - continue - } - - fpc := m[1] - port, err := strconv.Atoi(m[2]) - if err != nil { - t.Fatalf("Error generating P4RT Node Name: %v", err) - } - asic := 0 - if port > 18 { - asic = 1 - } - res[p.ID()] = fmt.Sprintf("SwitchChip%s/%d", fpc, asic) - } - - if _, ok := res["port1"]; !ok { - res["port1"] = *args.P4RTNodeName1 - } - if _, ok := res["port2"]; !ok { - res["port2"] = *args.P4RTNodeName2 - } - return res -} - // P4RTNodesByPort returns a map of : for the reserved ondatra // ports using the component and the interface OC tree. func P4RTNodesByPort(t testing.TB, dut *ondatra.DUTDevice) map[string]string { t.Helper() - if deviations.ExplicitP4RTNodeComponent(dut) { - switch dut.Vendor() { - case ondatra.NOKIA: - return inferP4RTNodesNokia(t, dut) - default: - return explicitP4RTNodes() - } - } - ports := make(map[string][]string) // :[] for _, p := range dut.Ports() { hp := gnmi.Lookup(t, dut, gnmi.OC().Interface(p.Name()).HardwarePort().State()) diff --git a/feature/gnmi/otg_tests/telemetry_port_speed_test/telemetry_port_speed_test.go b/feature/gnmi/otg_tests/telemetry_port_speed_test/telemetry_port_speed_test.go index 90f4f8d72fb..23cc5a9411b 100644 --- a/feature/gnmi/otg_tests/telemetry_port_speed_test/telemetry_port_speed_test.go +++ b/feature/gnmi/otg_tests/telemetry_port_speed_test/telemetry_port_speed_test.go @@ -353,7 +353,7 @@ func TestGNMIPortDown(t *testing.T) { ate.OTG().SetControlState(t, portStateAction) want := oc.Interface_OperStatus_DOWN - gnmi.Await(t, dut, gnmi.OC().Interface(dutPort.Name()).OperStatus().State(), 1*time.Minute, want) + gnmi.Await(t, dut, gnmi.OC().Interface(dutPort.Name()).OperStatus().State(), 2*time.Minute, want) dutPortStatus := gnmi.Get(t, dut, gnmi.OC().Interface(dutPort.Name()).OperStatus().State()) if dutPortStatus != want { t.Errorf("Get(DUT port1 status): got %v, want %v", dutPortStatus, want) diff --git a/feature/gnmi/subscribe/tests/gnmi_sample_mode_test/README.md b/feature/gnmi/subscribe/tests/gnmi_sample_mode_test/README.md index efeacde8591..cc08bec8a9b 100644 --- a/feature/gnmi/subscribe/tests/gnmi_sample_mode_test/README.md +++ b/feature/gnmi/subscribe/tests/gnmi_sample_mode_test/README.md @@ -44,10 +44,15 @@ Test to validate basic gNMI streaming telemetry works with `SAMPLE` mode. The below yaml defines the OC paths intended to be covered by this test. OC paths used for test setup are not listed here. -TODO(OCPATHS): Add paths ```yaml + paths: +## Config Paths ## +/interfaces/interface/config/description: + +## State Paths ## +/interfaces/interface/state/description: rpcs: gnmi: 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 1b31e0a73d4..e7f72c9e4ea 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,24 +35,14 @@ 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() - if desc == nil { - t.Errorf("Interface %q telemetry not received before config", p1.Name()) - } else { - v, ok := desc.Val() - if !ok { - t.Errorf("Interface %q telemetry empty before config", p1.Name()) - } - t.Logf("Description before config: %s", v) - } - gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), dut)) - desc = p1Stream.Next() + desc := p1Stream.Next() if desc == nil { t.Errorf("Interface %q telemetry not received after config", p1.Name()) } else { v, ok := desc.Val() + t.Logf("Description from stream : %s", v) if !ok { t.Errorf("Interface %q telemetry empty after config", p1.Name()) } @@ -69,6 +59,7 @@ func TestGNMISampleMode(t *testing.T) { t.Errorf("Interface %q telemetry not received after description update", p1.Name()) } else { v, ok := desc.Val() + t.Logf("Description from stream : %s", v) if !ok { t.Errorf("Interface %q telemetry empty after description update", p1.Name()) } diff --git a/feature/gnmi/subscribe/tests/gnmi_sample_mode_test/metadata.textproto b/feature/gnmi/subscribe/tests/gnmi_sample_mode_test/metadata.textproto index 87d02fedf47..9f4c6a67ba7 100644 --- a/feature/gnmi/subscribe/tests/gnmi_sample_mode_test/metadata.textproto +++ b/feature/gnmi/subscribe/tests/gnmi_sample_mode_test/metadata.textproto @@ -4,7 +4,7 @@ uuid: "abcc6890-c3e1-4b0e-984e-749c85b54e5a" plan_id: "gNMI-1.27" description: "gNMI Sample Mode Test" -testbed: TESTBED_DUT +testbed: TESTBED_DUT_ATE_2LINKS platform_exceptions: { platform: { vendor: ARISTA @@ -18,3 +18,11 @@ platform_exceptions: { isis_interface_afi_unsupported: true } } +platform_exceptions: { + platform: { + vendor: NOKIA + } + deviations: { + explicit_interface_in_default_vrf: true + } +} diff --git a/feature/gnoi/factory_reset/tests/factory_reset_test/README.md b/feature/gnoi/factory_reset/tests/factory_reset_test/README.md index 0bfcfb35233..8c4a5e8fd9d 100644 --- a/feature/gnoi/factory_reset/tests/factory_reset_test/README.md +++ b/feature/gnoi/factory_reset/tests/factory_reset_test/README.md @@ -1,16 +1,27 @@ -# gNOI-6.1: Factory Reset +# gNOI-6.1: Factory Reset ## Summary -Performs Factory Reset with and without disk-encryption + +Performs Factory Reset ## Procedure -* Create dummy files in the harddisk of the router using bash dd -* Checks for disk-encryption status and performs reset on both the scenarios -* Secure ZTP server should be up and running in the background for the router to boot up with the base config once factory reset command is sent on the box. -* Send out Factory reset via GNOI Raw API - * Wait for the box to boot up via Secure ZTP - * The base config is updated on the box via Secure ZTP -* Connect to the router and check if the files in the harddisk are removed as a part of verifying Factory reset. + +### Scenario 1 + +* Create a sample file in the harddisk of the router using gNOI PUT RPC +* Secure ZTP server should be up and running in the background for the router + to boot up with the base config once factory reset command is sent on the + box. +* Send out Factory reset via GNOI Raw API + * Wait for the box to boot up via Secure ZTP + * The base config is updated on the box via Secure ZTP +* Send a gNOI file STAT RPC to check if the file in the harddisk are removed + as a part of verifying Factory reset. + +### Scenario 2 + +* Check startup-config file exists in mount path. +* Perform the same steps are `Scenario 1` for startup-config file. ## OpenConfig Path and RPC Coverage @@ -21,4 +32,6 @@ paths used for test setup are not listed here. rpcs: gnoi: factory_reset.FactoryReset.Start: + file.File.Put: + file.File.Stat: ``` diff --git a/feature/gnoi/factory_reset/tests/factory_reset_test/factory_reset_test.go b/feature/gnoi/factory_reset/tests/factory_reset_test/factory_reset_test.go index 93d161332a5..4cc0240c1af 100644 --- a/feature/gnoi/factory_reset/tests/factory_reset_test/factory_reset_test.go +++ b/feature/gnoi/factory_reset/tests/factory_reset_test/factory_reset_test.go @@ -1,26 +1,46 @@ -package factoryreset +// 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 factory_reset_test import ( "context" - "fmt" - "path" - "strings" + "crypto/md5" + "crypto/rand" + "io" + "path/filepath" + "regexp" "testing" "time" "github.com/openconfig/featureprofiles/internal/fptest" frpb "github.com/openconfig/gnoi/factory_reset" + fpb "github.com/openconfig/gnoi/file" + "github.com/openconfig/gnoi/types" + "github.com/openconfig/gnoigo" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" "github.com/openconfig/testt" ) var ( - filesCreated = []string{} - fileCreateDevRand = "bash dd if=/dev/urandom of=%s bs=1M count=2" - checkFileExists = "bash [ -f \"%s\" ] && echo \"YES_exists\"" - fileExists = "YES_exists" - fileCreate = "bash fallocate -l %dM %s" + remoteFilePath = map[ondatra.Vendor]string{ + ondatra.CISCO: "/misc/disk1/", + ondatra.NOKIA: "/tmp/", + ondatra.JUNIPER: "/var/tmp/", + ondatra.ARISTA: "/mnt/flash/", + } ) const maxRebootTime = 40 // 40 mins wait time for the factory reset and sztp to kick in @@ -28,54 +48,6 @@ func TestMain(m *testing.M) { fptest.RunTests(m) } -type encryptionCommands struct { - EncryptionStatus string - EncryptionActivate string - EncryptionDeactivate string - DevicePaths []string -} - -var enCiscoCommands encryptionCommands - -// creating files before factory reset -func createFiles(t *testing.T, dut *ondatra.DUTDevice, devicePaths []string) { - for _, folderPath := range devicePaths { - fPath := path.Join(folderPath, "devrandom.log") - dut.CLI().Run(t, fmt.Sprintf(fileCreateDevRand, fPath)) - t.Log("Check if the file is created") - time.Sleep(30 * time.Second) - filesCreated = append(filesCreated, fPath) - fPath = path.Join(folderPath, ".devrandom.log") - dut.CLI().Run(t, fmt.Sprintf(fileCreateDevRand, fPath)) - - filesCreated = append(filesCreated, fPath) - fPath = path.Join(folderPath, "largeFile.log") - dut.CLI().Run(t, fmt.Sprintf(fileCreate, 100, fPath)) - - filesCreated = append(filesCreated, fPath) - } - for _, f := range filesCreated { - resp := dut.CLI().Run(t, fmt.Sprintf(checkFileExists, f)) - t.Logf("%v", resp) - if !strings.Contains(resp, fileExists) { - t.Fatalf("Unable to Create a file object %s in device %s", f, dut.Name()) - } - } - -} - -// checkFiles check if the files created are deleted from the device after factory reset -func checkFiles(t *testing.T, dut *ondatra.DUTDevice) { - for _, f := range filesCreated { - resp := dut.CLI().Run(t, fmt.Sprintf(checkFileExists, f)) - t.Logf(resp) - if strings.Contains(resp, fileExists) == true { - t.Fatalf("File %s not cleared by system Reset, in device %s", f, dut.Name()) - } - - } -} - func deviceBootStatus(t *testing.T, dut *ondatra.DUTDevice) { startReboot := time.Now() t.Logf("Wait for DUT to boot up by polling the telemetry output.") @@ -100,70 +72,153 @@ func deviceBootStatus(t *testing.T, dut *ondatra.DUTDevice) { t.Logf("Device boot time: %.2f minutes", time.Since(startReboot).Minutes()) } -// performs factory reset -func factoryReset(t *testing.T, dut *ondatra.DUTDevice, devicePaths []string) { - createFiles(t, dut, devicePaths) +func gNOIPutFile(t *testing.T, dut *ondatra.DUTDevice, gnoiClient gnoigo.Clients, fName string) { + dutVendor := dut.Vendor() + fullPath := filepath.Join(remoteFilePath[dutVendor], fName) + stream, err := gnoiClient.File().Put(context.Background()) + t.Logf("Attempting to send gNOI File Put here: %v", fullPath) + if err != nil { + t.Fatalf("Failed to create stream channel: %v", err) + } + defer stream.CloseSend() + h := md5.New() + fPutOpen := &fpb.PutRequest_Open{ + Open: &fpb.PutRequest_Details{ + RemoteFile: fullPath, + Permissions: 744, + }, + } + err = stream.Send(&fpb.PutRequest{ + Request: fPutOpen, + }) + if err != nil { + t.Fatalf("Stream failed to send PutRequest: %v", err) + } + + b := make([]byte, 64*1024) + n, err := rand.Read(b) + if err != nil && err != io.EOF { + t.Fatalf("Error reading bytes: %v", err) + } + h.Write(b[:n]) + req := &fpb.PutRequest{ + Request: &fpb.PutRequest_Contents{ + Contents: b[:n], + }, + } + err = stream.Send(req) + if err != nil { + t.Fatalf("Stream failed to send Req: %v", err) + } + + hashReq := &fpb.PutRequest{ + Request: &fpb.PutRequest_Hash{ + Hash: &types.HashType{ + Method: types.HashType_MD5, + Hash: h.Sum(nil), + }, + }, + } + err = stream.Send(hashReq) + if err != nil { + t.Fatalf("Stream failed to send hash: %v", err) + } + + _, err = stream.CloseAndRecv() + if err != nil { + t.Fatalf("Problem closing the stream: %v", err) + } +} + +func gNOIStatFile(t *testing.T, dut *ondatra.DUTDevice, fName string, reset bool) { + dutVendor := dut.Vendor() + fullPath := filepath.Join(remoteFilePath[dutVendor], fName) gnoiClient, err := dut.RawAPIs().BindingDUT().DialGNOI(context.Background()) if err != nil { t.Fatalf("Error dialing gNOI: %v", err) } - facRe, err := gnoiClient.FactoryReset().Start(context.Background(), &frpb.StartRequest{FactoryOs: false, ZeroFill: false}) + if _, ok := remoteFilePath[dutVendor]; !ok { + t.Fatalf("Please add support for vendor %v in var remoteFilePath ", dutVendor) + } + + in := &fpb.StatRequest{ + Path: remoteFilePath[dutVendor], + } + statResp, err := gnoiClient.File().Stat(context.Background(), in) if err != nil { - t.Fatalf("Failed to initiate Factory Reset on the device, Error : %v ", err) - } - t.Logf("Factory reset Response %v ", facRe) - time.Sleep(2 * time.Minute) - deviceBootStatus(t, dut) - dutNew := ondatra.DUT(t, "dut") - checkFiles(t, dutNew) - t.Log("Factory reset successfull") + t.Fatalf("Error fetching stat path %v for the created file on DUT. %v", remoteFilePath[dutVendor], err) + } + + if len(statResp.GetStats()) == 0 { + t.Log("gNOI STAT did not find any files") + } + + r := regexp.MustCompile(fName) + var isCreatedFile bool + + for _, fileStats := range statResp.GetStats() { + isCreatedFile = r.MatchString(fileStats.GetPath()) && (fileStats.GetSize() == uint64(64*1024)) + if isCreatedFile { + break + } + } + if isCreatedFile { + if !reset { + t.Logf("gNOI PUT successfully created file: %s", fullPath) + } else { + t.Errorf("gNOI PUT file was found after Factory Reset: %s", fullPath) + } + } + if !isCreatedFile { + if !reset { + t.Error("gNOI PUT file was never Created") + } else { + t.Logf("Did not find %s in the list of files", fullPath) + } + } } func TestFactoryReset(t *testing.T) { dut := ondatra.DUT(t, "dut") - switch dut.Vendor() { - case ondatra.CISCO: - enCiscoCommands = encryptionCommands{EncryptionStatus: "show disk-encryption status", EncryptionActivate: "disk-encryption activate", EncryptionDeactivate: "disk-encryption deactivate", DevicePaths: []string{"/misc/disk1"}} - t.Logf("Cisco commands for disk encryption %v ", enCiscoCommands) - default: - t.Fatalf("Disk Encryption commands is missing for %v ", dut.Vendor().String()) - } - - showDiskEncryptionStatus := dut.CLI().Run(t, enCiscoCommands.EncryptionStatus) - t.Logf("Disk encryption status %v", showDiskEncryptionStatus) - - if strings.Contains(showDiskEncryptionStatus, "Not Encrypted") { - t.Log("Performing Factory reset without Encryption\n") - factoryReset(t, dut, enCiscoCommands.DevicePaths) - t.Log("Stablise after factory reset\n") - time.Sleep(5 * time.Minute) - t.Log("Activate Encryption\n") - encrypt := dut.CLI().Run(t, enCiscoCommands.EncryptionActivate) - t.Logf("Sleep for 5 mins after disk-encryption activate") - time.Sleep(5 * time.Minute) - t.Logf("Device encryption acrivare: %v", encrypt) - deviceBootStatus(t, dut) - encrypt = dut.CLI().Run(t, enCiscoCommands.EncryptionStatus) - t.Logf("Show device encryption status: %v", encrypt) - t.Log("Wait for the system to stabilize\n") - time.Sleep(5 * time.Minute) - factoryReset(t, dut, enCiscoCommands.DevicePaths) - } else { - t.Log("Performing Factory reset with Encryption\n") - factoryReset(t, dut, enCiscoCommands.DevicePaths) - t.Log("Stablise after factory reset\n") - time.Sleep(5 * time.Minute) - t.Log("Deactivate Encryption\n") - encrypt := dut.CLI().Run(t, enCiscoCommands.EncryptionDeactivate) - t.Logf("Device encrytion deactivate: %v", encrypt) - t.Logf("Sleep for 5 mins after disk-encryption deactivate") - time.Sleep(5 * time.Minute) - deviceBootStatus(t, dut) - encrypt = dut.CLI().Run(t, enCiscoCommands.EncryptionStatus) - t.Logf("Show device encrytion status: %v", encrypt) - t.Logf("Wait for the system to stabilize\n") - time.Sleep(5 * time.Minute) - factoryReset(t, dut, enCiscoCommands.DevicePaths) + testCases := []struct { + name string + fileName string + fileExist bool + }{ + { + name: "Random file", + fileName: "devrandom.log", + fileExist: false, + }, + { + name: "Startup config", + fileName: "startup-config", + fileExist: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + gnoiClient, err := dut.RawAPIs().BindingDUT().DialGNOI(context.Background()) + if err != nil { + t.Fatalf("Error dialing gNOI: %v", err) + } + + if !tc.fileExist { + gNOIPutFile(t, dut, gnoiClient, tc.fileName) + } + gNOIStatFile(t, dut, tc.fileName, tc.fileExist) + + res, err := gnoiClient.FactoryReset().Start(context.Background(), &frpb.StartRequest{FactoryOs: false, ZeroFill: false}) + if err != nil { + t.Fatalf("Failed to initiate Factory Reset on the device, Error : %v ", err) + } + t.Logf("Factory reset Response %v ", res) + time.Sleep(2 * time.Minute) + + deviceBootStatus(t, dut) + gNOIStatFile(t, dut, tc.fileName, true) + }) } } diff --git a/feature/gnoi/factory_reset/tests/factory_reset_test/metadata.textproto b/feature/gnoi/factory_reset/tests/factory_reset_test/metadata.textproto index 231f9b845d8..9ce3bad8abe 100644 --- a/feature/gnoi/factory_reset/tests/factory_reset_test/metadata.textproto +++ b/feature/gnoi/factory_reset/tests/factory_reset_test/metadata.textproto @@ -4,4 +4,4 @@ uuid: "15e3ade1-e3c4-4da3-8553-3a9ef5fba344" plan_id: "gNOI-6.1" description: "Factory Reset" -testbed: TESTBED_DUT_ATE_2LINKS +testbed: TESTBED_DUT diff --git a/feature/gnoi/os/tests/osinstall/metadata.textproto b/feature/gnoi/os/tests/osinstall/metadata.textproto index 5e6d7ef194b..b3af04b638b 100644 --- a/feature/gnoi/os/tests/osinstall/metadata.textproto +++ b/feature/gnoi/os/tests/osinstall/metadata.textproto @@ -5,14 +5,6 @@ uuid: "562b09f5-05f6-4523-9bb2-c0b4a32310fe" plan_id: "gNOI-4.1" description: "Software Upgrade" testbed: TESTBED_DUT_ATE_2LINKS -platform_exceptions: { - platform: { - vendor: JUNIPER - } - deviations: { - sw_version_unsupported: true - } -} platform_exceptions: { platform: { vendor: NOKIA 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 550fef5802e..903abd7b548 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 @@ -5,14 +5,6 @@ uuid: "e98e61ec-03cd-4bff-8094-2fc69491962a" plan_id: "gNOI-2.1" description: "Packet-based Link Qualification" testbed: TESTBED_DUT_DUT_4LINKS -platform_exceptions: { - platform: { - vendor: JUNIPER - } - deviations: { - skip_plq_interface_oper_status_check: true - } -} platform_exceptions: { platform: { vendor: ARISTA diff --git a/feature/gnoi/system/tests/copying_debug_files_test/copying_debug_files_test.go b/feature/gnoi/system/tests/copying_debug_files_test/copying_debug_files_test.go index 5fe2605d143..39c752d9f74 100644 --- a/feature/gnoi/system/tests/copying_debug_files_test/copying_debug_files_test.go +++ b/feature/gnoi/system/tests/copying_debug_files_test/copying_debug_files_test.go @@ -19,11 +19,11 @@ import ( "time" "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/system" hpb "github.com/openconfig/gnoi/healthz" spb "github.com/openconfig/gnoi/system" tpb "github.com/openconfig/gnoi/types" "github.com/openconfig/ondatra" - "github.com/openconfig/ondatra/gnmi" ) var ( @@ -53,7 +53,7 @@ func TestMain(m *testing.M) { // DUT // // Test notes: -//. Note: Initiating checkin to experimental +// Note: Initiating checkin to experimental // - KillProcess system call is used to kill a process. // - The healthz call needs to be modified to reflect the right component and its path. // @@ -67,10 +67,14 @@ func TestCopyingDebugFiles(t *testing.T) { if _, ok := processName[dut.Vendor()]; !ok { t.Fatalf("Please add support for vendor %v in var processName", dut.Vendor()) } + pID := system.FindProcessIDByName(t, dut, processName[dut.Vendor()]) + if pID == 0 { + t.Fatalf("process %v not found on device", processName[dut.Vendor()]) + } killProcessRequest := &spb.KillProcessRequest{ Signal: spb.KillProcessRequest_SIGNAL_KILL, Name: processName[dut.Vendor()], - Pid: findProcessByName(context.Background(), t, dut, processName[dut.Vendor()]), + Pid: uint32(pID), Restart: true, } processKillResponse, err := gnoiClient.System().KillProcess(context.Background(), killProcessRequest) @@ -109,19 +113,6 @@ func TestCopyingDebugFiles(t *testing.T) { } } -// findProcessByName uses telemetry to find out the PID of a process -func findProcessByName(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, pName string) uint32 { - pList := gnmi.GetAll(t, dut, gnmi.OC().System().ProcessAny().State()) - var pID uint32 - for _, proc := range pList { - if proc.GetName() == pName { - pID = uint32(proc.GetPid()) - t.Logf("Pid of daemon '%s' is '%d'", pName, pID) - } - } - return pID -} - func TestChassisComponentArtifacts(t *testing.T) { dut := ondatra.DUT(t, "dut") gnoiClient := dut.RawAPIs().GNOI(t) @@ -153,10 +144,10 @@ func TestChassisComponentArtifacts(t *testing.T) { t.Logf("Artifacts received for component %v: %v", componentName["name"], artifacts) // Fetch artifact details by executing ArtifactRequest and passing the artifact ID along. for _, artifact := range artifacts { - artId := artifact.GetId() - t.Logf("Executing ArtifactRequest for artifact ID %v", artId) + artID := artifact.GetId() + t.Logf("Executing ArtifactRequest for artifact ID %v", artID) artReq := &hpb.ArtifactRequest{ - Id: artId, + Id: artID, } // Verify that a valid response is received. artRes, err := gnoiClient.Healthz().Artifact(context.Background(), artReq) @@ -164,9 +155,9 @@ func TestChassisComponentArtifacts(t *testing.T) { t.Fatalf("Unexpected error on executing Healthz Artifact RPC: %v", err) } h1, err := artRes.Header() - t.Logf("Header of artifact %v: %v", artId, h1) + t.Logf("Header of artifact %v: %v", artID, h1) if err != nil { - t.Fatalf("Unexpected error when fetching the header of artifact %v: %v", artId, err) + t.Fatalf("Unexpected error when fetching the header of artifact %v: %v", artID, err) } } } 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 f64bcea4013..7e578982254 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 @@ -31,7 +31,6 @@ platform_exceptions: { vendor: NOKIA } deviations: { - explicit_interface_ref_definition: true explicit_port_speed: true explicit_interface_in_default_vrf: true interface_enabled: true diff --git a/feature/gribi/ate_tests/hierarchical_weight_resolution_test/README.md b/feature/gribi/ate_tests/hierarchical_weight_resolution_test/README.md index a7b35daed86..63bfca04b3c 100644 --- a/feature/gribi/ate_tests/hierarchical_weight_resolution_test/README.md +++ b/feature/gribi/ate_tests/hierarchical_weight_resolution_test/README.md @@ -121,22 +121,24 @@ WCMP width of 16 nexthops: 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 +## OpenConfig Path and RPC Coverage +```yaml +paths: + ## State Paths ## + /network-instances/network-instance/afts/next-hop-groups/next-hop-group/next-hops/next-hop/state/weight: + +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: + gribi: + gRIBI.Get: + gRIBI.Modify: + gRIBI.Flush: +``` ## Minimum DUT platform requirement -vRX +* vRX - virtual router device 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 63b7b4f20f6..fdd67d8cbab 100644 --- a/feature/gribi/ate_tests/hierarchical_weight_resolution_test/metadata.textproto +++ b/feature/gribi/ate_tests/hierarchical_weight_resolution_test/metadata.textproto @@ -29,7 +29,6 @@ platform_exceptions: { vendor: NOKIA } deviations: { - explicit_interface_ref_definition: true explicit_port_speed: true explicit_interface_in_default_vrf: true interface_enabled: true diff --git a/feature/gribi/otg_tests/backup_nhg_multiple_nh_test/README.md b/feature/gribi/otg_tests/backup_nhg_multiple_nh_test/README.md index f27a9c16e44..f9faa11d5bc 100644 --- a/feature/gribi/otg_tests/backup_nhg_multiple_nh_test/README.md +++ b/feature/gribi/otg_tests/backup_nhg_multiple_nh_test/README.md @@ -10,17 +10,21 @@ Ensure that backup NHGs are honoured with NextHopGroup entries containing >1 NH. DUT port-3, and ATE port-4 to DUT port-4. * Create a L3 routing instance (VRF-A), and assign DUT port-1 to VRF-A. * Create a L3 routing instance (VRF-B) that includes no interface. -* Connect a gRIBI client to the DUT, make it become leader and inject the +* TODO: Create a L3 routing instance (VRF-C) that includes no interface. +* TODO: Connect a gRIBI client to the DUT, make it become leader and inject the following: - * An IPv4Entry in VRF-A, pointing to a NextHopGroup (in DEFAULT VRF) + * An IPv4Entry in VRF-A for IP-1, pointing to a NextHopGroup (in DEFAULT VRF) containing: * Two primary next-hops: * IP of ATE port-2 * IP of ATE port-3 - * A backup NHG containing a single next-hop pointing to VRF-B. - * The same IPv4Entry but in VRF-B, pointing to a NextHopGroup (in DEFAULT - VRF) containing a primary next-hop to the IP of ATE port-4. -* Ensure that traffic forwarded to the destination is received at ATE port-2 + * An IPv4Entry VRF-B for IP-1, pointing to a NextHopGroup (in + DEFAULT VRF) containing a primary next-hop that + decaps-and-reencaps traffic to IP-2 and redirects to VRF-C. + * An IPv4Entry for IP-2 in VRF-C, pointing to a NextHopGroup (in DEFAULT VRF) + containing: + * One primary next-hop pointing to IP of ATE port-4 +* TODO: Ensure that traffic with IP-1 as an outer IP (and an inner packet) is received at ATE port-2 and port-3. Validate that AFT telemetry covers this case. * Disable ATE port-2. Ensure that traffic for the destination is received at ATE port-3. diff --git a/feature/gribi/otg_tests/base_hierarchical_nhg_update/README.md b/feature/gribi/otg_tests/base_hierarchical_nhg_update/README.md index 85eda40e170..b0d5b53d498 100644 --- a/feature/gribi/otg_tests/base_hierarchical_nhg_update/README.md +++ b/feature/gribi/otg_tests/base_hierarchical_nhg_update/README.md @@ -6,6 +6,8 @@ Validate NHG update in hierarchical resolution scenario ## Procedure +### Validate NHG update in hierarchical resolution scenario + * Connect ATE port-1 to DUT port-1, ATE port-2 to DUT port-2, ATE port-3 to DUT port-3. * Create a non-default VRF (VRF-1) that includes DUT port-1. @@ -43,60 +45,8 @@ and it should not change the expected test result. * Replace the existing VRF selection policy with `vrf_selection_policy_w` as in -## Telemetry Parameter coverage - -For prefix: - -* /network-instances/network-instance/afts/ - -Parameters: - -* ipv4-unicast/ipv4-entry/state -* ipv4-unicast/ipv4-entry/state/next-hop-group -* ipv4-unicast/ipv4-entry/state/origin-protocol -* ipv4-unicast/ipv4-entry/state/prefix -* next-hop-groups/next-hop-group/id -* next-hop-groups/next-hop-group/next-hops -* next-hop-groups/next-hop-group/next-hops/next-hop -* next-hop-groups/next-hop-group/next-hops/next-hop/index -* next-hop-groups/next-hop-group/next-hops/next-hop/state -* next-hop-groups/next-hop-group/next-hops/next-hop/state/index -* next-hop-groups/next-hop-group/state -* next-hop-groups/next-hop-group/state/id -* next-hops/next-hop/index -* next-hops/next-hop/interface-ref -* next-hops/next-hop/interface-ref/state -* next-hops/next-hop/interface-ref/state/interface -* next-hops/next-hop/interface-ref/state/subinterface -* next-hops/next-hop/state -* next-hops/next-hop/state/index -* next-hops/next-hop/state/ip-address -* next-hops/next-hop/state/mac-address - -## OpenConfig Path and RPC Coverage -```yaml -rpcs: - gnmi: - gNMI.Get: - gNMI.Set: - gNMI.Subscribe: - gribi: - gRIBI.Get: - gRIBI.Modify: - gRIBI.Flush: -``` - -## Minimum DUT platform requirement - -vRX if the vendor implementation supports FIB-ACK simulation, otherwise FFF. - -# TE-3.7: Drain Implementation Test. - -## Summary - -Validate NHG update in Drain Implementation Test. -## Procedure +### Validate NHG update in Drain Implementation Test. * Steps: * Topology @@ -130,3 +80,51 @@ Validate NHG update in Drain Implementation Test. * Expect FIB ACKs and validate that the traffic is moved back to trunk-2 and trunk-3 with less than ms traffic loss. + +## OpenConfig Path and RPC Coverage +```yaml +paths: + ## Config parameter coverage + /network-instances/network-instance/afts/ipv4-unicast/ipv4-entry/state/next-hop-group: + /network-instances/network-instance/afts/ipv4-unicast/ipv4-entry/state/origin-protocol: + /network-instances/network-instance/afts/ipv4-unicast/ipv4-entry/state/prefix: + /network-instances/network-instance/afts/next-hop-groups/next-hop-group/id: + /network-instances/network-instance/afts/next-hop-groups/next-hop-group/next-hops/next-hop/index: + /network-instances/network-instance/afts/next-hop-groups/next-hop-group/state/backup-next-hop-group: + /network-instances/network-instance/afts/next-hop-groups/next-hop-group/state/id: + /network-instances/network-instance/afts/next-hops/next-hop/interface-ref/state/interface: + /network-instances/network-instance/afts/next-hops/next-hop/interface-ref/state/subinterface: + /network-instances/network-instance/protocols/protocol/static-routes/static/next-hops/next-hop/interface-ref/config/interface: + /network-instances/network-instance/protocols/protocol/static-routes/static/next-hops/next-hop/interface-ref/config/subinterface: + /network-instances/network-instance/afts/next-hops/next-hop/state/index: + /network-instances/network-instance/afts/next-hops/next-hop/state/ip-address: + /network-instances/network-instance/afts/next-hops/next-hop/state/mac-address: + + ## Protocol/RPC Parameter Coverage +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: + gribi: + gRIBI.Get: + gRIBI.Modify: + gRIBI.Flush: +``` + +## Required DUT platform + +* vRX if the vendor implementation supports FIB-ACK simulation, otherwise FFF. + +## OpenConfig Path and RPC Coverage +```yaml +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: + gribi: + gRIBI.Get: + gRIBI.Modify: + gRIBI.Flush: +``` \ No newline at end of file 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 16c26bfcead..ae9289fb225 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 @@ -164,14 +164,14 @@ var ( IPv4Len: 30, } dutPort2DummyIP = attrs.Attributes{ - Desc: "dutPort2", - IPv4: "192.0.2.21", - IPv4Len: 30, + Desc: "dutPort2", + IPv4Sec: "192.0.2.21", + IPv4LenSec: 30, } dutPort3DummyIP = attrs.Attributes{ - Desc: "dutPort3", - IPv4: "192.0.2.41", - IPv4Len: 30, + Desc: "dutPort3", + IPv4Sec: "192.0.2.41", + IPv4LenSec: 30, } atePort2DummyIP = attrs.Attributes{ Desc: "atePort2", @@ -224,16 +224,16 @@ func TestBaseHierarchicalNHGUpdate(t *testing.T) { desc: "Usecase for NHG update in hierarchical resolution scenario", fn: testBaseHierarchialNHG, }, - { - 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, }, + { + name: "testRecursiveIPv4EntrywithVRFSelectionPolW", + desc: "Usecase for NHG update in hierarchical resolution scenario with VRF Selection Policy W", + fn: testBaseHierarchialNHGwithVrfPolW, + }, } // Configure the gRIBI client client := gribi.Client{ @@ -285,9 +285,14 @@ func testBaseHierarchialNHGwithVrfPolW(ctx context.Context, t *testing.T, args * } vrfpolicy.ConfigureVRFSelectionPolicy(t, args.dut, vrfpolicy.VRFPolicyW) + // Remove interface from VRF-1. + gnmi.Delete(t, args.dut, gnmi.OC().NetworkInstance(vrfName).Config()) + p1 := args.dut.Port(t, "port1") + gnmi.Update(t, args.dut, gnmi.OC().Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), args.dut)) + ctx = context.WithValue(ctx, transitKey{}, true) testBaseHierarchialNHG(ctx, t, args) - //Delete Policy-forwarding PolicyW from the ingress interface + // Delete Policy-forwarding PolicyW from the ingress interface vrfpolicy.DeletePolicyForwarding(t, args.dut, "port1") } @@ -316,27 +321,18 @@ func testBaseHierarchialNHG(ctx context.Context, t *testing.T, args *testArgs) { var nh fluent.GRIBIEntry var op1, op3 *client.OpResult - if !deviations.ExplicitGRIBIUnderNetworkInstance(args.dut) { - if deviations.GRIBIMACOverrideWithStaticARP(args.dut) || deviations.GRIBIMACOverrideStaticARPStaticRoute(args.dut) { - nh, op1 = gribi.NHEntry(p2NHID, "MACwithInterface", dni, fluent.InstalledInFIB, &gribi.NHOptions{Interface: dutP2, Mac: pMAC, Dest: atePort2DummyIP.IPv4}) - } else { - nh, op1 = gribi.NHEntry(p2NHID, "MACwithInterface", dni, fluent.InstalledInFIB, &gribi.NHOptions{Interface: dutP2, Mac: pMAC}) - } - nhg, op2 := gribi.NHGEntry(virtualIPNHGID, map[uint64]uint64{p2NHID: 1}, dni, fluent.InstalledInFIB) - args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, []*client.OpResult{op1, op2}) - args.client.AddIPv4(t, virtualPfx, virtualIPNHGID, dni, dni, fluent.InstalledInFIB) - - nh, op1 = gribi.NHEntry(dstNHID, virtualIP, dni, fluent.InstalledInFIB) - nhg, op2 = gribi.NHGEntry(dstNHGID, map[uint64]uint64{dstNHID: 1}, dni, fluent.InstalledInFIB) - args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, []*client.OpResult{op1, op2}) + if deviations.GRIBIMACOverrideWithStaticARP(args.dut) || deviations.GRIBIMACOverrideStaticARPStaticRoute(args.dut) { + nh, op1 = gribi.NHEntry(p2NHID, "MACwithInterface", dni, fluent.InstalledInFIB, &gribi.NHOptions{Interface: dutP2, Mac: pMAC, Dest: atePort2DummyIP.IPv4}) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh}, []*client.OpResult{op1}) } else { args.client.AddNH(t, p2NHID, "MACwithInterface", dni, fluent.InstalledInFIB, &gribi.NHOptions{Interface: dutP2, Mac: pMAC}) - args.client.AddNHG(t, virtualIPNHGID, map[uint64]uint64{p2NHID: 1}, dni, fluent.InstalledInFIB) - args.client.AddIPv4(t, virtualPfx, virtualIPNHGID, dni, dni, fluent.InstalledInFIB) - - args.client.AddNH(t, dstNHID, virtualIP, dni, fluent.InstalledInFIB) - args.client.AddNHG(t, dstNHGID, map[uint64]uint64{dstNHID: 1}, dni, fluent.InstalledInFIB) } + args.client.AddNHG(t, virtualIPNHGID, map[uint64]uint64{p2NHID: 1}, dni, fluent.InstalledInFIB) + args.client.AddIPv4(t, virtualPfx, virtualIPNHGID, dni, dni, fluent.InstalledInFIB) + + args.client.AddNH(t, dstNHID, virtualIP, dni, fluent.InstalledInFIB) + args.client.AddNHG(t, dstNHGID, map[uint64]uint64{dstNHID: 1}, dni, fluent.InstalledInFIB) + if transit { args.client.AddIPv4(t, dstPfx, dstNHGID, niTeVrf111, dni, fluent.InstalledInFIB) } else { @@ -346,41 +342,23 @@ func testBaseHierarchialNHG(ctx context.Context, t *testing.T, args *testArgs) { validateTrafficFlows(t, args.ate, []gosnappi.Flow{p2Flow}, []gosnappi.Flow{p3Flow}, nil, startTraffic, args.client, false) t.Logf("Adding a new NH via port %v with ID %v", dutP3, p3NHID) - if !deviations.ExplicitGRIBIUnderNetworkInstance(args.dut) { - if deviations.GRIBIMACOverrideWithStaticARP(args.dut) || deviations.GRIBIMACOverrideStaticARPStaticRoute(args.dut) { - nh, op3 = gribi.NHEntry(p3NHID, "MACwithInterface", dni, fluent.InstalledInFIB, &gribi.NHOptions{Interface: dutP3, Mac: pMAC, Dest: atePort3DummyIP.IPv4}) - } else { - nh, op3 = gribi.NHEntry(p3NHID, "MACwithInterface", dni, fluent.InstalledInFIB, &gribi.NHOptions{Interface: dutP3, Mac: pMAC}) - } + if deviations.GRIBIMACOverrideWithStaticARP(args.dut) || deviations.GRIBIMACOverrideStaticARPStaticRoute(args.dut) { + nh, op3 = gribi.NHEntry(p3NHID, "MACwithInterface", dni, fluent.InstalledInFIB, &gribi.NHOptions{Interface: dutP3, Mac: pMAC, Dest: atePort3DummyIP.IPv4}) + args.client.AddEntries(t, []fluent.GRIBIEntry{nh}, []*client.OpResult{op3}) } else { args.client.AddNH(t, p3NHID, "MACwithInterface", dni, fluent.InstalledInFIB, &gribi.NHOptions{Interface: dutP3, Mac: pMAC}) } t.Logf("Performing implicit in-place replace with two next-hops (NH IDs: %v and %v)", p2NHID, p3NHID) - if !deviations.ExplicitGRIBIUnderNetworkInstance(args.dut) { - nhg, op2 := gribi.NHGEntry(virtualIPNHGID, map[uint64]uint64{p2NHID: 1, p3NHID: 1}, dni, fluent.InstalledInFIB) - args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, []*client.OpResult{op1, op3, op2}) - } else { - args.client.AddNHG(t, virtualIPNHGID, map[uint64]uint64{p2NHID: 1, p3NHID: 1}, dni, fluent.InstalledInFIB) - } + args.client.AddNHG(t, virtualIPNHGID, map[uint64]uint64{p2NHID: 1, p3NHID: 1}, dni, fluent.InstalledInFIB) validateTrafficFlows(t, args.ate, nil, nil, []gosnappi.Flow{p2Flow, p3Flow}, startTraffic, args.client, false) t.Logf("Performing implicit in-place replace using the next-hop with ID %v", p3NHID) - if !deviations.ExplicitGRIBIUnderNetworkInstance(args.dut) { - nhg, op2 := gribi.NHGEntry(virtualIPNHGID, map[uint64]uint64{p3NHID: 1}, dni, fluent.InstalledInFIB) - args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, []*client.OpResult{op3, op2}) - } else { - args.client.AddNHG(t, virtualIPNHGID, map[uint64]uint64{p3NHID: 1}, dni, fluent.InstalledInFIB) - } + args.client.AddNHG(t, virtualIPNHGID, map[uint64]uint64{p3NHID: 1}, dni, fluent.InstalledInFIB) validateTrafficFlows(t, args.ate, []gosnappi.Flow{p3Flow}, []gosnappi.Flow{p2Flow}, nil, startTraffic, args.client, false) t.Logf("Performing implicit in-place replace using the next-hop with ID %v", p2NHID) - if !deviations.ExplicitGRIBIUnderNetworkInstance(args.dut) { - nhg, op2 := gribi.NHGEntry(virtualIPNHGID, map[uint64]uint64{p2NHID: 1}, dni, fluent.InstalledInFIB) - args.client.AddEntries(t, []fluent.GRIBIEntry{nh, nhg}, []*client.OpResult{op1, op2}) - } else { - args.client.AddNHG(t, virtualIPNHGID, map[uint64]uint64{p2NHID: 1}, dni, fluent.InstalledInFIB) - } + args.client.AddNHG(t, virtualIPNHGID, map[uint64]uint64{p2NHID: 1}, dni, fluent.InstalledInFIB) validateTrafficFlows(t, args.ate, []gosnappi.Flow{p2Flow}, []gosnappi.Flow{p3Flow}, nil, startTraffic, args.client, false) } @@ -452,10 +430,6 @@ func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { fptest.AssignToNetworkInstance(t, dut, p3.Name(), deviations.DefaultNetworkInstance(dut), 0) fptest.AssignToNetworkInstance(t, dut, p4.Name(), deviations.DefaultNetworkInstance(dut), 0) } - if deviations.ExplicitGRIBIUnderNetworkInstance(dut) { - fptest.EnableGRIBIUnderNetworkInstance(t, dut, deviations.DefaultNetworkInstance(dut)) - fptest.EnableGRIBIUnderNetworkInstance(t, dut, vrfName) - } if deviations.GRIBIMACOverrideWithStaticARP(dut) { staticARPWithSecondaryIP(t, dut, false) 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 fca5d3f83ec..c8a1b5253ac 100644 --- a/feature/gribi/otg_tests/base_hierarchical_nhg_update/metadata.textproto +++ b/feature/gribi/otg_tests/base_hierarchical_nhg_update/metadata.textproto @@ -30,7 +30,6 @@ platform_exceptions: { vendor: NOKIA } deviations: { - explicit_gribi_under_network_instance: true explicit_port_speed: true explicit_interface_in_default_vrf: true static_protocol_name: "static" diff --git a/feature/gribi/otg_tests/base_hierarchical_route_installation_test/README.md b/feature/gribi/otg_tests/base_hierarchical_route_installation_test/README.md index 2be2eb6cea2..2f80c28934d 100644 --- a/feature/gribi/otg_tests/base_hierarchical_route_installation_test/README.md +++ b/feature/gribi/otg_tests/base_hierarchical_route_installation_test/README.md @@ -123,3 +123,16 @@ No configuration relevant. ## Minimum DUT platform requirement vRX if the vendor implementation supports FIB-ACK simulation, otherwise FFF. + +## OpenConfig Path and RPC Coverage +```yaml +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: + gribi: + gRIBI.Get: + gRIBI.Modify: + gRIBI.Flush: +``` 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 6b7b4eb0003..f36cb255de7 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 @@ -108,9 +108,9 @@ var ( } dutPort2DummyIP = attrs.Attributes{ - Desc: "dutPort2", - IPv4: "192.0.2.21", - IPv4Len: 30, + Desc: "dutPort2", + IPv4Sec: "192.0.2.21", + IPv4LenSec: 30, } atePort2DummyIP = attrs.Attributes{ 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 ff82417d6fb..665aff9ca0d 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 @@ -24,7 +24,6 @@ platform_exceptions: { deviations: { explicit_port_speed: true explicit_interface_in_default_vrf: true - explicit_interface_ref_definition: true static_protocol_name: "static" interface_enabled: true skip_pbf_with_decap_encap_vrf: true 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 5e9548e7507..d5109eef5f1 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 @@ -95,7 +95,7 @@ const ( ipv4EntryPrefixLen = 24 ipv6FlowIP = "2015:aa8::1" ipv6EntryPrefix = "2015:aa8::" - ipv6EntryPrefixLen = 32 + ipv6EntryPrefixLen = 64 ratioTunEncap1 = 0.25 // 1/4 ratioTunEncap2 = 0.75 // 3/4 ratioTunEncapTol = 0.05 // 5/100 @@ -364,7 +364,7 @@ func TestBasicEncap(t *testing.T) { }{ { name: fmt.Sprintf("Test1 IPv4 Traffic WCMP Encap dscp %d", dscpEncapA1), - pattr: packetAttr{dscp: dscpEncapA1, protocol: ipipProtocol}, + pattr: packetAttr{dscp: dscpEncapA1, protocol: ipipProtocol, ttl: 99}, flows: []gosnappi.Flow{fa4.getFlow("ipv4", "ip4a1", dscpEncapA1)}, weights: wantWeights, capturePorts: otgDstPorts, @@ -372,7 +372,7 @@ func TestBasicEncap(t *testing.T) { }, { name: fmt.Sprintf("Test2 IPv6 Traffic WCMP Encap dscp %d", dscpEncapA1), - pattr: packetAttr{dscp: dscpEncapA1, protocol: ipv6ipProtocol}, + pattr: packetAttr{dscp: dscpEncapA1, protocol: ipv6ipProtocol, ttl: 99}, flows: []gosnappi.Flow{fa6.getFlow("ipv6", "ip6a1", dscpEncapA1)}, weights: wantWeights, capturePorts: otgDstPorts, @@ -380,7 +380,7 @@ func TestBasicEncap(t *testing.T) { }, { name: fmt.Sprintf("Test3 IPinIP Traffic WCMP Encap dscp %d", dscpEncapA1), - pattr: packetAttr{dscp: dscpEncapA1, protocol: ipipProtocol}, + pattr: packetAttr{dscp: dscpEncapA1, protocol: ipipProtocol, ttl: 99}, flows: []gosnappi.Flow{faIPinIP.getFlow("ipv4in4", "ip4in4a1", dscpEncapA1), faIPinIP.getFlow("ipv6in4", "ip6in4a1", dscpEncapA1), }, @@ -390,7 +390,7 @@ func TestBasicEncap(t *testing.T) { }, { name: fmt.Sprintf("No Match Dscp %d Traffic", dscpEncapNoMatch), - pattr: packetAttr{protocol: udpProtocol, dscp: dscpEncapNoMatch}, + pattr: packetAttr{protocol: udpProtocol, dscp: dscpEncapNoMatch, ttl: 99}, flows: []gosnappi.Flow{fa4.getFlow("ipv4", "ip4nm", dscpEncapNoMatch)}, weights: noMatchWeight, capturePorts: otgDstPorts[:1], @@ -791,7 +791,7 @@ func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { 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 { + if p.PMD() == ondatra.PMD100GBASEFR && dut.Vendor() != ondatra.CISCO && dut.Vendor() != ondatra.JUNIPER { e := intf.GetOrCreateEthernet() e.AutoNegotiate = ygot.Bool(false) e.DuplexMode = oc.Ethernet_DuplexMode_FULL @@ -918,6 +918,7 @@ func (fa *flowAttr) getFlow(flowType string, name string, dscp uint32) gosnappi. innerV4 := flow.Packet().Add().Ipv4() innerV4.Src().SetValue(innerV4SrcIP) innerV4.Dst().SetValue(innerV4DstIP) + innerV4.Priority().Dscp().Phb().SetValue(dscp) } // add inner ipv6 headers @@ -925,6 +926,7 @@ func (fa *flowAttr) getFlow(flowType string, name string, dscp uint32) gosnappi. innerV6 := flow.Packet().Add().Ipv6() innerV6.Src().SetValue(InnerV6SrcIP) innerV6.Dst().SetValue(InnerV6DstIP) + innerV6.TrafficClass().SetValue(dscp << 2) } } else if flowType == "ipv6" { v6 := flow.Packet().Add().Ipv6() @@ -945,8 +947,13 @@ func sendTraffic(t *testing.T, args *testArgs, flows []gosnappi.Flow, capture bo otg := args.ate.OTG() args.topo.Flows().Clear().Items() args.topo.Flows().Append(flows...) + otg.PushConfig(t, args.topo) otg.StartProtocols(t) + + otgutils.WaitForARP(t, args.ate.OTG(), args.topo, "IPv4") + otgutils.WaitForARP(t, args.ate.OTG(), args.topo, "IPv6") + if capture { startCapture(t, args.ate) defer stopCapture(t, args.ate) diff --git a/feature/gribi/otg_tests/basic_encap_test/metadata.textproto b/feature/gribi/otg_tests/basic_encap_test/metadata.textproto index adf9ef8379d..29fb9f8def3 100644 --- a/feature/gribi/otg_tests/basic_encap_test/metadata.textproto +++ b/feature/gribi/otg_tests/basic_encap_test/metadata.textproto @@ -18,6 +18,14 @@ platform_exceptions: { pf_require_sequential_order_pbr_rules: true } } +platform_exceptions: { + platform: { + vendor: JUNIPER + } + deviations: { + ttl_copy_unsupported: true + } +} platform_exceptions: { platform: { vendor: ARISTA @@ -26,7 +34,6 @@ platform_exceptions: { 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 } diff --git a/feature/gribi/otg_tests/encap_decap_scale/encap_decap_scale_test.go b/feature/gribi/otg_tests/encap_decap_scale/encap_decap_scale_test.go index 70a5513da3d..c729fa18bd8 100644 --- a/feature/gribi/otg_tests/encap_decap_scale/encap_decap_scale_test.go +++ b/feature/gribi/otg_tests/encap_decap_scale/encap_decap_scale_test.go @@ -20,6 +20,7 @@ import ( "encoding/binary" "fmt" "net" + "net/netip" "strings" "testing" "time" @@ -109,11 +110,15 @@ const ( encapNhgcount = 200 encapIPv4Count = 5000 encapIPv6Count = 5000 - encapNhSize = 8 decapIPv4Count = 48 - decapIPv4ScaleCount = 1000 decapScale = true tolerancePct = 2 + seqIDBase = 10 +) + +var ( + encapNhSize = 8 + decapIPv4ScaleCount = 1000 ) var ( @@ -223,7 +228,7 @@ func incrementIP(ip string, i int) string { } type policyFwRule struct { - SeqId uint32 + SeqID uint32 protocol oc.UnionUint8 dscpSet []uint8 sourceAddr string @@ -237,49 +242,49 @@ func configureVrfSelectionPolicyW(t *testing.T, dut *ondatra.DUTDevice) { d := &oc.Root{} dutPolFwdPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).PolicyForwarding() - pfRule1 := &policyFwRule{SeqId: 1, protocol: 4, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc222WithMask, + pfRule1 := &policyFwRule{SeqID: 1, protocol: 4, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc222WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfA, decapFallbackNi: niTeVrf222} - pfRule2 := &policyFwRule{SeqId: 2, protocol: 41, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc222WithMask, + pfRule2 := &policyFwRule{SeqID: 2, protocol: 41, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc222WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfA, decapFallbackNi: niTeVrf222} - pfRule3 := &policyFwRule{SeqId: 3, protocol: 4, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc111WithMask, + pfRule3 := &policyFwRule{SeqID: 3, protocol: 4, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc111WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfA, decapFallbackNi: niTeVrf111} - pfRule4 := &policyFwRule{SeqId: 4, protocol: 41, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc111WithMask, + pfRule4 := &policyFwRule{SeqID: 4, protocol: 41, dscpSet: []uint8{dscpEncapA1, dscpEncapA2}, sourceAddr: ipv4OuterSrc111WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfA, decapFallbackNi: niTeVrf111} - pfRule5 := &policyFwRule{SeqId: 5, protocol: 4, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc222WithMask, + pfRule5 := &policyFwRule{SeqID: 5, protocol: 4, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc222WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfB, decapFallbackNi: niTeVrf222} - pfRule6 := &policyFwRule{SeqId: 6, protocol: 41, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc222WithMask, + pfRule6 := &policyFwRule{SeqID: 6, protocol: 41, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc222WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfB, decapFallbackNi: niTeVrf222} - pfRule7 := &policyFwRule{SeqId: 7, protocol: 4, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc111WithMask, + pfRule7 := &policyFwRule{SeqID: 7, protocol: 4, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc111WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfB, decapFallbackNi: niTeVrf111} - pfRule8 := &policyFwRule{SeqId: 8, protocol: 41, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc111WithMask, + pfRule8 := &policyFwRule{SeqID: 8, protocol: 41, dscpSet: []uint8{dscpEncapB1, dscpEncapB2}, sourceAddr: ipv4OuterSrc111WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfB, decapFallbackNi: niTeVrf111} - pfRule9 := &policyFwRule{SeqId: 9, protocol: 4, dscpSet: []uint8{dscpEncapC1, dscpEncapC2}, sourceAddr: ipv4OuterSrc222WithMask, + pfRule9 := &policyFwRule{SeqID: 9, protocol: 4, dscpSet: []uint8{dscpEncapC1, dscpEncapC2}, sourceAddr: ipv4OuterSrc222WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfC, decapFallbackNi: niTeVrf222} - pfRule10 := &policyFwRule{SeqId: 10, protocol: 41, dscpSet: []uint8{dscpEncapC1, dscpEncapC2}, sourceAddr: ipv4OuterSrc222WithMask, + pfRule10 := &policyFwRule{SeqID: 10, protocol: 41, dscpSet: []uint8{dscpEncapC1, dscpEncapC2}, sourceAddr: ipv4OuterSrc222WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfC, decapFallbackNi: niTeVrf222} - pfRule11 := &policyFwRule{SeqId: 11, protocol: 4, dscpSet: []uint8{dscpEncapC1, dscpEncapC2}, sourceAddr: ipv4OuterSrc111WithMask, + pfRule11 := &policyFwRule{SeqID: 11, protocol: 4, dscpSet: []uint8{dscpEncapC1, dscpEncapC2}, sourceAddr: ipv4OuterSrc111WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfC, decapFallbackNi: niTeVrf111} - pfRule12 := &policyFwRule{SeqId: 12, protocol: 41, dscpSet: []uint8{dscpEncapC1, dscpEncapC2}, sourceAddr: ipv4OuterSrc111WithMask, + pfRule12 := &policyFwRule{SeqID: 12, protocol: 41, dscpSet: []uint8{dscpEncapC1, dscpEncapC2}, sourceAddr: ipv4OuterSrc111WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfC, decapFallbackNi: niTeVrf111} - pfRule13 := &policyFwRule{SeqId: 13, protocol: 4, dscpSet: []uint8{dscpEncapD1, dscpEncapD2}, sourceAddr: ipv4OuterSrc222WithMask, + pfRule13 := &policyFwRule{SeqID: 13, protocol: 4, dscpSet: []uint8{dscpEncapD1, dscpEncapD2}, sourceAddr: ipv4OuterSrc222WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfD, decapFallbackNi: niTeVrf222} - pfRule14 := &policyFwRule{SeqId: 14, protocol: 41, dscpSet: []uint8{dscpEncapD1, dscpEncapD2}, sourceAddr: ipv4OuterSrc222WithMask, + pfRule14 := &policyFwRule{SeqID: 14, protocol: 41, dscpSet: []uint8{dscpEncapD1, dscpEncapD2}, sourceAddr: ipv4OuterSrc222WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfD, decapFallbackNi: niTeVrf222} - pfRule15 := &policyFwRule{SeqId: 15, protocol: 4, dscpSet: []uint8{dscpEncapD1, dscpEncapD2}, sourceAddr: ipv4OuterSrc111WithMask, + pfRule15 := &policyFwRule{SeqID: 15, protocol: 4, dscpSet: []uint8{dscpEncapD1, dscpEncapD2}, sourceAddr: ipv4OuterSrc111WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfD, decapFallbackNi: niTeVrf111} - pfRule16 := &policyFwRule{SeqId: 16, protocol: 41, dscpSet: []uint8{dscpEncapD1, dscpEncapD2}, sourceAddr: ipv4OuterSrc111WithMask, + pfRule16 := &policyFwRule{SeqID: 16, protocol: 41, dscpSet: []uint8{dscpEncapD1, dscpEncapD2}, sourceAddr: ipv4OuterSrc111WithMask, decapNi: niDecapTeVrf, postDecapNi: niEncapTeVrfD, decapFallbackNi: niTeVrf111} - pfRule17 := &policyFwRule{SeqId: 17, protocol: 4, sourceAddr: ipv4OuterSrc222WithMask, + pfRule17 := &policyFwRule{SeqID: 17, protocol: 4, sourceAddr: ipv4OuterSrc222WithMask, decapNi: niDecapTeVrf, postDecapNi: niDefault, decapFallbackNi: niTeVrf222} - pfRule18 := &policyFwRule{SeqId: 18, protocol: 41, sourceAddr: ipv4OuterSrc222WithMask, + pfRule18 := &policyFwRule{SeqID: 18, protocol: 41, sourceAddr: ipv4OuterSrc222WithMask, decapNi: niDecapTeVrf, postDecapNi: niDefault, decapFallbackNi: niTeVrf222} - pfRule19 := &policyFwRule{SeqId: 19, protocol: 4, sourceAddr: ipv4OuterSrc111WithMask, + pfRule19 := &policyFwRule{SeqID: 19, protocol: 4, sourceAddr: ipv4OuterSrc111WithMask, decapNi: niDecapTeVrf, postDecapNi: niDefault, decapFallbackNi: niTeVrf111} - pfRule20 := &policyFwRule{SeqId: 20, protocol: 41, sourceAddr: ipv4OuterSrc111WithMask, + pfRule20 := &policyFwRule{SeqID: 20, protocol: 41, sourceAddr: ipv4OuterSrc111WithMask, decapNi: niDecapTeVrf, postDecapNi: niDefault, decapFallbackNi: niTeVrf111} pfRuleList := []*policyFwRule{pfRule1, pfRule2, pfRule3, pfRule4, pfRule5, pfRule6, @@ -287,12 +292,13 @@ func configureVrfSelectionPolicyW(t *testing.T, dut *ondatra.DUTDevice) { pfRule15, pfRule16, pfRule17, pfRule18, pfRule19, pfRule20} ni := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + ni.SetType(oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_DEFAULT_INSTANCE) niP := ni.GetOrCreatePolicyForwarding() niPf := niP.GetOrCreatePolicy(vrfPolW) niPf.SetType(oc.Policy_Type_VRF_SELECTION_POLICY) for _, pfRule := range pfRuleList { - pfR := niPf.GetOrCreateRule(pfRule.SeqId) + pfR := niPf.GetOrCreateRule(seqIDOffset(dut, pfRule.SeqID)) pfRProtoIPv4 := pfR.GetOrCreateIpv4() pfRProtoIPv4.Protocol = oc.UnionUint8(pfRule.protocol) if pfRule.dscpSet != nil { @@ -304,9 +310,22 @@ func configureVrfSelectionPolicyW(t *testing.T, dut *ondatra.DUTDevice) { pfRAction.PostDecapNetworkInstance = ygot.String(pfRule.postDecapNi) pfRAction.DecapFallbackNetworkInstance = ygot.String(pfRule.decapFallbackNi) } - pfR := niPf.GetOrCreateRule(21) - pfRAction := pfR.GetOrCreateAction() - pfRAction.NetworkInstance = ygot.String(niDefault) + + if deviations.PfRequireMatchDefaultRule(dut) { + pfR21 := niPf.GetOrCreateRule(seqIDOffset(dut, 21)) + pfR21.GetOrCreateL2().SetEthertype(oc.PacketMatchTypes_ETHERTYPE_ETHERTYPE_IPV4) + pfRAction := pfR21.GetOrCreateAction() + pfRAction.NetworkInstance = ygot.String(niDefault) + + pfR22 := niPf.GetOrCreateRule(seqIDOffset(dut, 22)) + pfR22.GetOrCreateL2().SetEthertype(oc.PacketMatchTypes_ETHERTYPE_ETHERTYPE_IPV6) + pfRAction = pfR22.GetOrCreateAction() + pfRAction.NetworkInstance = ygot.String(niDefault) + } else { + pfR := niPf.GetOrCreateRule(seqIDOffset(dut, 21)) + pfRAction := pfR.GetOrCreateAction() + pfRAction.NetworkInstance = ygot.String(niDefault) + } p1 := dut.Port(t, "port1") interfaceID := p1.Name() @@ -329,6 +348,7 @@ func createIPv6Entries(startIP string, count uint64) []string { _, netCIDR, _ := net.ParseCIDR(startIP) netMask := binary.BigEndian.Uint64(netCIDR.Mask) + maskSize, _ := netCIDR.Mask.Size() firstIP := binary.BigEndian.Uint64(netCIDR.IP) lastIP := (firstIP & netMask) | (netMask ^ 0xffffffff) entries := []string{} @@ -336,8 +356,10 @@ func createIPv6Entries(startIP string, count uint64) []string { for i := firstIP; i <= lastIP; i++ { ipv6 := make(net.IP, 16) binary.BigEndian.PutUint64(ipv6, i) - entries = append(entries, fmt.Sprint(ipv6)) - if uint64(len(entries)) > count { + // make last byte non-zero + p, _ := netip.ParsePrefix(fmt.Sprintf("%v/%d", ipv6, maskSize)) + entries = append(entries, p.Addr().Next().String()) + if uint64(len(entries)) >= count { break } } @@ -435,46 +457,43 @@ func createAndSendTrafficFlows(t *testing.T, args *testArgs, decapEntries []stri t.Helper() flow1 := createFlow(&flowArgs{flowName: "flow1", isInnHdrV4: true, - InnHdrSrcIP: atePort1.IPv4, InnHdrDstIP: encapVrfAIPv4Enries, inHdrDscp: []uint32{dscpEncapA1}, - outHdrSrcIP: ipv4OuterSrc111, outHdrDstIPs: decapEntries, outHdrDscp: []uint32{dscpEncapA1}, + InnHdrSrcIP: atePort1.IPv4, InnHdrDstIP: encapVrfAIPv4Enries, inHdrDscp: dscpEncapA1, + outHdrSrcIP: ipv4OuterSrc111, outHdrDstIPs: decapEntries, outHdrDscp: dscpEncapA1, }) flow2 := createFlow(&flowArgs{flowName: "flow2", isInnHdrV4: true, - InnHdrSrcIP: atePort1.IPv4, InnHdrDstIP: encapVrfBIPv4Enries, inHdrDscp: []uint32{dscpEncapB1}, - outHdrSrcIP: ipv4OuterSrc222, outHdrDstIPs: decapEntries, outHdrDscp: []uint32{dscpEncapB1}, + InnHdrSrcIP: atePort1.IPv4, InnHdrDstIP: encapVrfBIPv4Enries, inHdrDscp: dscpEncapB1, + outHdrSrcIP: ipv4OuterSrc222, outHdrDstIPs: decapEntries, outHdrDscp: dscpEncapB1, }) flow3 := createFlow(&flowArgs{flowName: "flow3", isInnHdrV4: true, - InnHdrSrcIP: atePort1.IPv4, InnHdrDstIP: encapVrfCIPv4Enries, inHdrDscp: []uint32{dscpEncapC1}, - outHdrSrcIP: ipv4OuterSrc111, outHdrDstIPs: decapEntries, outHdrDscp: []uint32{dscpEncapC1}, + InnHdrSrcIP: atePort1.IPv4, InnHdrDstIP: encapVrfCIPv4Enries, inHdrDscp: dscpEncapC1, + outHdrSrcIP: ipv4OuterSrc111, outHdrDstIPs: decapEntries, outHdrDscp: dscpEncapC1, }) flow4 := createFlow(&flowArgs{flowName: "flow4", isInnHdrV4: true, - InnHdrSrcIP: atePort1.IPv4, InnHdrDstIP: encapVrfDIPv4Enries, inHdrDscp: []uint32{dscpEncapD1}, - outHdrSrcIP: ipv4OuterSrc222, outHdrDstIPs: decapEntries, outHdrDscp: []uint32{dscpEncapD1}, + InnHdrSrcIP: atePort1.IPv4, InnHdrDstIP: encapVrfDIPv4Enries, inHdrDscp: dscpEncapD1, + outHdrSrcIP: ipv4OuterSrc222, outHdrDstIPs: decapEntries, outHdrDscp: dscpEncapD1, }) - // Below v6 flows will fail due to ixia issue. - // https://github.com/open-traffic-generator/fp-testbed-juniper/issues/49 - flow5 := createFlow(&flowArgs{flowName: "flow5", isInnHdrV4: false, - InnHdrSrcIPv6: atePort1.IPv6, InnHdrDstIPv6: encapVrfAIPv6Enries, inHdrDscp: []uint32{dscpEncapA2}, - outHdrSrcIP: ipv4OuterSrc111, outHdrDstIPs: decapEntries, outHdrDscp: []uint32{dscpEncapA2}, + InnHdrSrcIPv6: atePort1.IPv6, InnHdrDstIPv6: encapVrfAIPv6Enries, inHdrDscp: dscpEncapA2, + outHdrSrcIP: ipv4OuterSrc111, outHdrDstIPs: decapEntries, outHdrDscp: dscpEncapA2, }) flow6 := createFlow(&flowArgs{flowName: "flow6", isInnHdrV4: false, - InnHdrSrcIPv6: atePort1.IPv6, InnHdrDstIPv6: encapVrfBIPv6Enries, inHdrDscp: []uint32{dscpEncapB2}, - outHdrSrcIP: ipv4OuterSrc222, outHdrDstIPs: decapEntries, outHdrDscp: []uint32{dscpEncapB2}, + InnHdrSrcIPv6: atePort1.IPv6, InnHdrDstIPv6: encapVrfBIPv6Enries, inHdrDscp: dscpEncapB2, + outHdrSrcIP: ipv4OuterSrc222, outHdrDstIPs: decapEntries, outHdrDscp: dscpEncapB2, }) flow7 := createFlow(&flowArgs{flowName: "flow7", isInnHdrV4: false, - InnHdrSrcIPv6: atePort1.IPv6, InnHdrDstIPv6: encapVrfCIPv6Enries, inHdrDscp: []uint32{dscpEncapC2}, - outHdrSrcIP: ipv4OuterSrc111, outHdrDstIPs: decapEntries, outHdrDscp: []uint32{dscpEncapC2}, + InnHdrSrcIPv6: atePort1.IPv6, InnHdrDstIPv6: encapVrfCIPv6Enries, inHdrDscp: dscpEncapC2, + outHdrSrcIP: ipv4OuterSrc111, outHdrDstIPs: decapEntries, outHdrDscp: dscpEncapC2, }) flow8 := createFlow(&flowArgs{flowName: "flow8", isInnHdrV4: false, - InnHdrSrcIPv6: atePort1.IPv6, InnHdrDstIPv6: encapVrfDIPv6Enries, inHdrDscp: []uint32{dscpEncapD2}, - outHdrSrcIP: ipv4OuterSrc222, outHdrDstIPs: decapEntries, outHdrDscp: []uint32{dscpEncapD2}, + InnHdrSrcIPv6: atePort1.IPv6, InnHdrDstIPv6: encapVrfDIPv6Enries, inHdrDscp: dscpEncapD2, + outHdrSrcIP: ipv4OuterSrc222, outHdrDstIPs: decapEntries, outHdrDscp: dscpEncapD2, }) flowList := []gosnappi.Flow{flow1, flow2, flow3, flow4, flow5, flow6, flow7, flow8} @@ -485,7 +504,10 @@ func createAndSendTrafficFlows(t *testing.T, args *testArgs, decapEntries []stri } args.ate.OTG().PushConfig(t, args.top) + time.Sleep(30 * time.Second) args.ate.OTG().StartProtocols(t) + // wait for glean adjacencies to be resolved + time.Sleep(240 * time.Second) otgutils.WaitForARP(t, args.ate.OTG(), args.top, "IPv4") t.Logf("Starting traffic") @@ -525,19 +547,21 @@ func verifyTraffic(t *testing.T, args *testArgs, flowList []string) { } func pushDecapEntries(t *testing.T, args *testArgs) []string { - decapIPBlocks := []string{"102.51.100.1/22", "107.51.105.1/24", "112.51.110.1/26", "117.51.115.1/28"} + decapIPBlocks := []string{} + decapIPBlocks = append(decapIPBlocks, generateIPv4Subnets("102.51.100.1/22", 12)...) + decapIPBlocks = append(decapIPBlocks, generateIPv4Subnets("107.51.105.1/24", 12)...) + decapIPBlocks = append(decapIPBlocks, generateIPv4Subnets("112.51.110.1/26", 12)...) + decapIPBlocks = append(decapIPBlocks, generateIPv4Subnets("117.51.115.1/28", 12)...) + nhIndex := uint64(lastNhIndex) nhgIndex := uint64(lastNhgIndex) decapEntries := []string{} - for _, ipBlock := range decapIPBlocks { - mask := strings.Split(ipBlock, "/")[1] - entries := iputil.GenerateIPs(ipBlock, 12) + for i, ipBlock := range decapIPBlocks { + entries := iputil.GenerateIPs(ipBlock, 1) decapEntries = append(decapEntries, entries...) - for i := 0; i < len(entries); i++ { - nhgIndex = nhgIndex + 1 - nhIndex = nhIndex + 1 - installDecapEntry(t, args, nhIndex, nhgIndex, entries[i]+"/"+mask) - } + nhgIndex = nhgIndex + 1 + nhIndex = nhIndex + 1 + installDecapEntry(t, args, nhIndex, nhgIndex, decapIPBlocks[i]) } lastNhIndex = int(nhIndex) + 1 @@ -573,7 +597,8 @@ func pushDecapScaleEntries(t *testing.T, args *testArgs, decapEntries []string) func installDecapEntry(t *testing.T, args *testArgs, nhIndex, nhgIndex uint64, prefix string) { args.client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(args.dut)). - WithIndex(nhIndex).WithDecapsulateHeader(fluent.IPinIP), + WithIndex(nhIndex).WithDecapsulateHeader(fluent.IPinIP). + WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(args.dut)), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(args.dut)). WithID(nhgIndex).AddNextHop(nhIndex, 1), fluent.IPv4Entry().WithNetworkInstance(niDecapTeVrf). @@ -591,8 +616,8 @@ type flowArgs struct { InnHdrSrcIPv6 string InnHdrDstIPv6 []string isInnHdrV4 bool - outHdrDscp []uint32 - inHdrDscp []uint32 + outHdrDscp uint32 + inHdrDscp uint32 } func createFlow(flowValues *flowArgs) gosnappi.Flow { @@ -612,22 +637,18 @@ func createFlow(flowValues *flowArgs) gosnappi.Flow { outerIPHdr := flow.Packet().Add().Ipv4() outerIPHdr.Src().SetValue(flowValues.outHdrSrcIP) outerIPHdr.Dst().SetValues(flowValues.outHdrDstIPs) - outerIPHdr.Priority().Dscp().Phb().SetValues(flowValues.outHdrDscp) + outerIPHdr.Priority().Dscp().Phb().SetValue(flowValues.outHdrDscp) if flowValues.isInnHdrV4 { innerIPHdr := flow.Packet().Add().Ipv4() innerIPHdr.Src().SetValue(flowValues.InnHdrSrcIP) innerIPHdr.Dst().SetValues(flowValues.InnHdrDstIP) - if len(flowValues.inHdrDscp) != 0 { - innerIPHdr.Priority().Dscp().Phb().SetValues(flowValues.inHdrDscp) - } + innerIPHdr.Priority().Dscp().Phb().SetValue(flowValues.inHdrDscp) } else { innerIpv6Hdr := flow.Packet().Add().Ipv6() innerIpv6Hdr.Src().SetValue(flowValues.InnHdrSrcIPv6) innerIpv6Hdr.Dst().SetValues(flowValues.InnHdrDstIPv6) - if len(flowValues.inHdrDscp) != 0 { - innerIpv6Hdr.TrafficClass().SetValues(flowValues.inHdrDscp) - } + innerIpv6Hdr.TrafficClass().SetValue(flowValues.inHdrDscp << 2) } return flow } @@ -659,8 +680,10 @@ func installEncapEntries(t *testing.T, vrf string, routeParams *routesParam, arg nhgEntry := fluent.NextHopGroupEntry(). WithNetworkInstance(deviations.DefaultNetworkInstance(args.dut)). WithID(index). - WithBackupNHG(uint64(routeParams.backupNHG)). WithElectionID(args.electionID.Low, args.electionID.High) + if routeParams.backupNHG != 0 { + nhgEntry.WithBackupNHG(uint64(routeParams.backupNHG)) + } for j := 0; j < routeParams.numNHPerNHG; j++ { nhgEntry.AddNextHop(nextHopIndices[(i*routeParams.numNHPerNHG+j)%len(nextHopIndices)], uint64(routeParams.nextHopWeight[j])) } @@ -724,21 +747,18 @@ func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { // createVrf takes in a list of VRF names and creates them on the target devices. func createVrf(t *testing.T, dut *ondatra.DUTDevice, vrfs []string) { + d := &oc.Root{} for _, vrf := range vrfs { if vrf != deviations.DefaultNetworkInstance(dut) { // configure non-default VRFs - d := &oc.Root{} i := d.GetOrCreateNetworkInstance(vrf) i.Type = oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_L3VRF gnmi.Replace(t, dut, gnmi.OC().NetworkInstance(vrf).Config(), i) } else { // configure DEFAULT vrf - dutConfNIPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)) - gnmi.Replace(t, dut, dutConfNIPath.Type().Config(), oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_DEFAULT_INSTANCE) - - } - if deviations.ExplicitGRIBIUnderNetworkInstance(dut) { - fptest.EnableGRIBIUnderNetworkInstance(t, dut, vrf) + i := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + i.Type = oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_DEFAULT_INSTANCE + gnmi.Update(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Config(), i) } } } @@ -792,7 +812,13 @@ func configureDUTSubIfs(t *testing.T, dut *ondatra.DUTDevice, dutPort *ondatra.P } dutIPv4 := incrementIP(subifBaseIP, (4*i)+2) ateIPv4 := incrementIP(subifBaseIP, (4*i)+1) + mac, err := incrementMAC(atePort1.MAC, i+1) + if err != nil { + t.Fatalf("failed to increment MAC: %v", err) + } gnmi.BatchUpdate(batchConfig, gnmi.OC().Interface(dutPort.Name()).Subinterface(index).Config(), createSubifDUT(t, d, dut, dutPort, index, vlanID, dutIPv4, ipv4PrefixLen)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().Interface(dutPort.Name()).Subinterface(index).Config(), createStaticArpEntries(dutPort.Name(), index, ateIPv4, mac)) + if deviations.ExplicitInterfaceInDefaultVRF(dut) { fptest.AssignToNetworkInstance(t, dut, dutPort.Name(), deviations.DefaultNetworkInstance(dut), index) } @@ -818,7 +844,10 @@ func configureATESubIfs(t *testing.T, top gosnappi.Config, atePort *ondatra.Port dutIPv4 := incrementIP(subifBaseIP, (4*i)+2) ateIPv4 := incrementIP(subifBaseIP, (4*i)+1) name := fmt.Sprintf(`dst%d`, i) - mac, _ := incrementMAC(atePort1.MAC, i+1) + mac, err := incrementMAC(atePort1.MAC, i+1) + if err != nil { + t.Fatalf("failed to increment MAC: %v", err) + } configureATE(t, top, atePort, vlanID, name, mac, dutIPv4, ateIPv4) nextHops = append(nextHops, ateIPv4) } @@ -870,6 +899,8 @@ type testArgs struct { func TestGribiEncapDecapScaling(t *testing.T) { dut := ondatra.DUT(t, "dut") + overrideScaleParams(dut) + ate := ondatra.ATE(t, "ate") ctx := context.Background() gribic := dut.RawAPIs().GRIBI(t) @@ -938,6 +969,10 @@ func TestGribiEncapDecapScaling(t *testing.T) { }, ) for _, vrfConfig := range vrfConfigs { + // skip adding unwanted entries + if vrfConfig.Name == "vrf_rd" { + continue + } entries := append(vrfConfig.NHs, vrfConfig.NHGs...) entries = append(entries, vrfConfig.V4Entries...) client.Modify().AddEntry(t, entries...) @@ -965,12 +1000,64 @@ func TestGribiEncapDecapScaling(t *testing.T) { if _, err := gribi.Flush(client, args.electionID, niDecapTeVrf); err != nil { t.Error(err) } + time.Sleep(240 * time.Second) } - + t.Log("installing scaled decap entries") // Install decapIPv4ScaleCount entries with fixed prefix length of /32 in DECAP_TE_VRF. decapScaleEntries := iputil.GenerateIPs(IPBlockDecap, decapIPv4ScaleCount) pushDecapScaleEntries(t, args, decapScaleEntries) - // Send traffic and verify packets to DUT-1. createAndSendTrafficFlows(t, args, decapScaleEntries) } + +// createStaticArpEntries creates static ARP entries for the given subinterface. +func createStaticArpEntries(portName string, index uint32, ipv4Addr string, macAddr string) *oc.Interface_Subinterface { + d := &oc.Root{} + i := d.GetOrCreateInterface(portName) + s := i.GetOrCreateSubinterface(index) + s4 := s.GetOrCreateIpv4() + n4 := s4.GetOrCreateNeighbor(ipv4Addr) + n4.LinkLayerAddress = ygot.String(macAddr) + return s +} + +// generateIPv4Subnets creates IPv4 prefixes with a given seedBlock and subNets count +func generateIPv4Subnets(seedBlock string, subNets uint32) []string { + + _, netCIDR, _ := net.ParseCIDR(seedBlock) + maskSize, _ := netCIDR.Mask.Size() + incrSize := 0x00000001 << (32 - maskSize) + firstIP := binary.BigEndian.Uint32(netCIDR.IP) + entries := []string{} + for i := firstIP; subNets > 0; subNets-- { + ip := make(net.IP, 4) + binary.BigEndian.PutUint32(ip, i) + tip := netip.MustParsePrefix(fmt.Sprintf("%v/%d", ip, maskSize)) + if tip.Addr().IsValid() { + entries = append(entries, tip.String()) + } + i = i + uint32(incrSize) + } + return entries +} + +// 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 +} + +// overrideScaleParams allows to override the default scale parameters based on the DUT vendor. +func overrideScaleParams(dut *ondatra.DUTDevice) { + if deviations.OverrideDefaultNhScale(dut) { + if dut.Vendor() == ondatra.CISCO { + *fpargs.V4TunnelCount = 1024 + encapNhSize = 2 + decapIPv4ScaleCount = 400 + } + } +} diff --git a/feature/gribi/otg_tests/encap_decap_scale/metadata.textproto b/feature/gribi/otg_tests/encap_decap_scale/metadata.textproto index 34c994953f9..874c434c54f 100644 --- a/feature/gribi/otg_tests/encap_decap_scale/metadata.textproto +++ b/feature/gribi/otg_tests/encap_decap_scale/metadata.textproto @@ -11,7 +11,10 @@ platform_exceptions: { } deviations: { ipv4_missing_enabled: true - interface_ref_config_unsupported: true + interface_ref_interface_id_format: true + pf_require_match_default_rule: true + pf_require_sequential_order_pbr_rules: true + override_default_nh_scale: true } } platform_exceptions: { @@ -20,8 +23,6 @@ platform_exceptions: { } deviations: { no_mix_of_tagged_and_untagged_subinterfaces: true - explicit_interface_ref_definition: true - explicit_gribi_under_network_instance: true explicit_port_speed: true explicit_interface_in_default_vrf: true interface_enabled: true @@ -32,6 +33,7 @@ platform_exceptions: { vendor: JUNIPER } deviations: { + no_mix_of_tagged_and_untagged_subinterfaces: true explicit_interface_ref_definition: true gribi_decap_mixed_plen_unsupported: true } @@ -41,10 +43,8 @@ platform_exceptions: { vendor: ARISTA } deviations: { - deprecated_vlan_id: true interface_enabled: true default_network_instance: "default" omit_l2_mtu: true } } - diff --git a/feature/gribi/otg_tests/encap_frr/README.md b/feature/gribi/otg_tests/encap_frr/README.md index 3ae508d48a6..c16d78ca49d 100644 --- a/feature/gribi/otg_tests/encap_frr/README.md +++ b/feature/gribi/otg_tests/encap_frr/README.md @@ -440,13 +440,23 @@ 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" } +1. Update `NHG#1000` and `NHG#1001` to the following: -NHG#1001 (Default VRF) { {NH#1001, DEFAULT VRF} } NH#1001 -> { -decapsulate_header: OPENCONFIGAFTTYPESDECAPSULATIONHEADERTYPE_IPV4 -network_instance: "DEFAULT" } ``` +``` +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. 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 04a9d884de3..621a680fd91 100644 --- a/feature/gribi/otg_tests/encap_frr/encap_frr_test.go +++ b/feature/gribi/otg_tests/encap_frr/encap_frr_test.go @@ -34,6 +34,7 @@ import ( "github.com/open-traffic-generator/snappi/gosnappi" "github.com/openconfig/featureprofiles/internal/attrs" "github.com/openconfig/featureprofiles/internal/deviations" + baseScenario "github.com/openconfig/featureprofiles/internal/encapfrr" "github.com/openconfig/featureprofiles/internal/fptest" "github.com/openconfig/featureprofiles/internal/gribi" "github.com/openconfig/featureprofiles/internal/vrfpolicy" @@ -70,41 +71,31 @@ func TestMain(m *testing.M) { const ( plenIPv4 = 30 plenIPv6 = 126 - numPorts = 8 dscpEncapA1 = 10 - dscpEncapA2 = 18 - dscpEncapB1 = 20 - dscpEncapB2 = 28 - dscpEncapNoMatch = 30 - ipv4OuterSrc111Addr = "198.51.100.111" - ipv4OuterSrc222Addr = "198.51.100.222" ipv4OuterSrcAddr = "198.100.200.123" ipv4InnerDst = "138.0.11.8" ipv4OuterDst333 = "192.58.200.7" noMatchEncapDest = "20.0.0.1" - prot4 = 4 - prot41 = 41 - polName = "pol1" - gribiIPv4entry = "192.51.100.0" - maskLen24 = "24" - maskLen32 = "32" niDecapTeVrf = "DECAP_TE_VRF" niEncapTeVrfA = "ENCAP_TE_VRF_A" - niEncapTeVrfB = "ENCAP_TE_VRF_B" - niTeVrf111 = "TE_VRF_111" - niTeVrf222 = "TE_VRF_222" - niDefault = "DEFAULT" + niRepairVrf = "REPAIR_VRF" tolerancePct = 2 tolerance = 0.2 encapFlow = "encapFlow" correspondingHopLimit = 64 magicIP = "192.168.1.1" - magicMac = "02:00:00:00:00:01" + magicMAC = "02:00:00:00:00:01" + maskLen24 = "24" + maskLen32 = "32" gribiIPv4EntryDefVRF1 = "192.0.2.101" gribiIPv4EntryDefVRF2 = "192.0.2.102" gribiIPv4EntryDefVRF3 = "192.0.2.103" gribiIPv4EntryDefVRF4 = "192.0.2.104" gribiIPv4EntryDefVRF5 = "192.0.2.105" + niTEVRF111 = "TE_VRF_111" + niTEVRF222 = "TE_VRF_222" + ipv4OuterSrc111Addr = "198.51.100.111" + ipv4OuterSrc222Addr = "198.51.100.222" gribiIPv4EntryVRF1111 = "203.0.113.1" gribiIPv4EntryVRF1112 = "203.0.113.2" gribiIPv4EntryVRF2221 = "203.0.113.100" @@ -140,7 +131,7 @@ const ( // Maximum reboot time is 900 seconds (15 minutes). maxRebootTime = 900 // Maximum wait time for all components to be in responsive state - maxCompWaitTime = 600 + maxCompWaitTime = 900 ) var ( @@ -202,75 +193,6 @@ var ( IPv4Len: 32, IPv6Len: 128, } - dutPort2DummyIP = attrs.Attributes{ - Desc: "dutPort2", - IPv4Sec: "192.0.2.33", - IPv4LenSec: plenIPv4, - } - - otgPort2DummyIP = attrs.Attributes{ - Desc: "otgPort2", - IPv4: "192.0.2.34", - IPv4Len: plenIPv4, - } - - dutPort3DummyIP = attrs.Attributes{ - Desc: "dutPort3", - IPv4Sec: "192.0.2.37", - IPv4LenSec: plenIPv4, - } - - otgPort3DummyIP = attrs.Attributes{ - Desc: "otgPort3", - IPv4: "192.0.2.38", - IPv4Len: plenIPv4, - } - - dutPort4DummyIP = attrs.Attributes{ - Desc: "dutPort4", - IPv4Sec: "192.0.2.41", - IPv4LenSec: plenIPv4, - } - - otgPort4DummyIP = attrs.Attributes{ - Desc: "otgPort4", - IPv4: "192.0.2.42", - IPv4Len: plenIPv4, - } - - dutPort5DummyIP = attrs.Attributes{ - Desc: "dutPort5", - IPv4Sec: "192.0.2.45", - IPv4LenSec: plenIPv4, - } - - otgPort5DummyIP = attrs.Attributes{ - Desc: "otgPort5", - IPv4: "192.0.2.46", - IPv4Len: plenIPv4, - } - dutPort6DummyIP = attrs.Attributes{ - Desc: "dutPort5", - IPv4Sec: "192.0.2.49", - IPv4LenSec: plenIPv4, - } - - otgPort6DummyIP = attrs.Attributes{ - Desc: "otgPort5", - IPv4: "192.0.2.50", - IPv4Len: plenIPv4, - } - dutPort7DummyIP = attrs.Attributes{ - Desc: "dutPort5", - IPv4Sec: "192.0.2.53", - IPv4LenSec: plenIPv4, - } - - otgPort7DummyIP = attrs.Attributes{ - Desc: "otgPort5", - IPv4: "192.0.2.54", - IPv4Len: plenIPv4, - } loopbackIntfName string atePortNamelist []string ) @@ -284,7 +206,6 @@ func awaitTimeout(ctx context.Context, t testing.TB, c *fluent.GRIBIClient, time } type testArgs struct { - ctx context.Context client *fluent.GRIBIClient dut *ondatra.DUTDevice ate *ondatra.ATEDevice @@ -370,29 +291,6 @@ func dutInterface(p *ondatra.Port, dut *ondatra.DUTDevice) *oc.Interface { return i } -// staticARPWithSecondaryIP configures secondary IPs and static ARP. -func staticARPWithSpecificIP(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") - gnmi.Update(t, dut, gnmi.OC().Interface(p2.Name()).Config(), dutPort2DummyIP.NewOCInterface(p2.Name(), dut)) - gnmi.Update(t, dut, gnmi.OC().Interface(p3.Name()).Config(), dutPort3DummyIP.NewOCInterface(p3.Name(), dut)) - gnmi.Update(t, dut, gnmi.OC().Interface(p4.Name()).Config(), dutPort4DummyIP.NewOCInterface(p4.Name(), dut)) - gnmi.Update(t, dut, gnmi.OC().Interface(p5.Name()).Config(), dutPort5DummyIP.NewOCInterface(p5.Name(), dut)) - gnmi.Update(t, dut, gnmi.OC().Interface(p6.Name()).Config(), dutPort6DummyIP.NewOCInterface(p6.Name(), dut)) - gnmi.Update(t, dut, gnmi.OC().Interface(p7.Name()).Config(), dutPort7DummyIP.NewOCInterface(p7.Name(), dut)) - gnmi.Update(t, dut, gnmi.OC().Interface(p2.Name()).Config(), configStaticArp(p2.Name(), otgPort2DummyIP.IPv4, magicMac)) - gnmi.Update(t, dut, gnmi.OC().Interface(p3.Name()).Config(), configStaticArp(p3.Name(), otgPort3DummyIP.IPv4, magicMac)) - gnmi.Update(t, dut, gnmi.OC().Interface(p4.Name()).Config(), configStaticArp(p4.Name(), otgPort4DummyIP.IPv4, magicMac)) - gnmi.Update(t, dut, gnmi.OC().Interface(p5.Name()).Config(), configStaticArp(p5.Name(), otgPort5DummyIP.IPv4, magicMac)) - gnmi.Update(t, dut, gnmi.OC().Interface(p6.Name()).Config(), configStaticArp(p6.Name(), otgPort6DummyIP.IPv4, magicMac)) - gnmi.Update(t, dut, gnmi.OC().Interface(p7.Name()).Config(), configStaticArp(p7.Name(), otgPort7DummyIP.IPv4, magicMac)) -} - // configureDUT configures all the interfaces on the DUT. func configureDUT(t *testing.T, dut *ondatra.DUTDevice, dutPortList []*ondatra.Port) { dc := gnmi.OC() @@ -436,228 +334,50 @@ func configureDUT(t *testing.T, dut *ondatra.DUTDevice, dutPortList []*ondatra.P t.Logf("Got DUT IPv6 loopback address: %v", dutlo0Attrs.IPv6) } if deviations.GRIBIMACOverrideWithStaticARP(dut) { - staticARPWithSpecificIP(t, dut) + baseScenario.StaticARPWithSpecificIP(t, dut) } } -// 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) { +// configureGribiRoute configures Gribi route as per the requirements +func configureGribiRoute(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, client *fluent.GRIBIClient) { 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 programAftWithDummyIP(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()). - WithIPAddress(otgPort2DummyIP.IPv4), - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(12).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port3").Name()). - WithIPAddress(otgPort3DummyIP.IPv4), - 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()). - WithIPAddress(otgPort4DummyIP.IPv4), - 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()). - WithIPAddress(otgPort5DummyIP.IPv4), - 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()). - WithIPAddress(otgPort6DummyIP.IPv4), - 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()). - WithIPAddress(otgPort7DummyIP.IPv4), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(15).AddNextHop(16, 1), - fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF5+"/"+maskLen32).WithNextHopGroup(15), - ) -} - -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 if deviations.GRIBIMACOverrideWithStaticARP(dut) { - programAftWithDummyIP(t, dut, args) - } 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.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF1+"/"+maskLen32).WithNextHopGroup(11), - fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF2+"/"+maskLen32).WithNextHopGroup(12), - fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF3+"/"+maskLen32).WithNextHopGroup(13), - fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF4+"/"+maskLen32).WithNextHopGroup(14), - 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) - } - - defaultVRFIPList := []string{gribiIPv4EntryDefVRF1, gribiIPv4EntryDefVRF2, gribiIPv4EntryDefVRF3, gribiIPv4EntryDefVRF4, gribiIPv4EntryDefVRF5} - for ip := range defaultVRFIPList { - chk.HasResult(t, args.client.Results(t), - fluent.OperationResult(). - WithIPv4Operation(defaultVRFIPList[ip]+"/32"). - WithOperationType(constants.Add). - WithProgrammingResult(fluent.InstalledInFIB). - AsResult(), - chk.IgnoreOperationID(), - ) - } + baseScenario.ConfigureBaseGribiRoutes(ctx, t, dut, client) // Programming AFT entries for backup NHG - args.client.Modify().AddEntry(t, + client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(2000).WithDecapsulateHeader(fluent.IPinIP).WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(2000).AddNextHop(2000, 1), ) - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) } // Programming AFT entries for prefixes in TE_VRF_222 - args.client.Modify().AddEntry(t, + client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(3).WithIPAddress(gribiIPv4EntryDefVRF3), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(2).AddNextHop(3, 1).WithBackupNHG(2000), - fluent.IPv4Entry().WithNetworkInstance(niTeVrf222).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + 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).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + 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 { + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) } teVRF222IPList := []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF2222} for ip := range teVRF222IPList { - chk.HasResult(t, args.client.Results(t), + chk.HasResult(t, client.Results(t), fluent.OperationResult(). WithIPv4Operation(teVRF222IPList[ip]+"/32"). WithOperationType(constants.Add). @@ -668,50 +388,50 @@ func configureGribiRoute(ctx context.Context, t *testing.T, dut *ondatra.DUTDevi } // Programming AFT entries for backup NHG - args.client.Modify().AddEntry(t, + client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(1000).WithDecapsulateHeader(fluent.IPinIP).WithEncapsulateHeader(fluent.IPinIP). WithIPinIP(ipv4OuterSrc222Addr, gribiIPv4EntryVRF2221). - WithNextHopNetworkInstance(niTeVrf222), + WithNextHopNetworkInstance(niTEVRF222), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(1000).AddNextHop(1000, 1), fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(1001).WithDecapsulateHeader(fluent.IPinIP).WithEncapsulateHeader(fluent.IPinIP). WithIPinIP(ipv4OuterSrc222Addr, gribiIPv4EntryVRF2222). - WithNextHopNetworkInstance(niTeVrf222), + WithNextHopNetworkInstance(niTEVRF222), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(1001).AddNextHop(1001, 1), ) - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) } // Programming AFT entries for prefixes in TE_VRF_111 - args.client.Modify().AddEntry(t, + client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(1).WithIPAddress(gribiIPv4EntryDefVRF1), fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(2).WithIPAddress(gribiIPv4EntryDefVRF2), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(1).AddNextHop(1, 1).AddNextHop(2, 3).WithBackupNHG(1000), - fluent.IPv4Entry().WithNetworkInstance(niTeVrf111).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + 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).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + 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 { + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) } teVRF111IPList := []string{gribiIPv4EntryVRF1111, gribiIPv4EntryVRF1112} for ip := range teVRF111IPList { - chk.HasResult(t, args.client.Results(t), + chk.HasResult(t, client.Results(t), fluent.OperationResult(). WithIPv4Operation(teVRF111IPList[ip]+"/32"). WithOperationType(constants.Add). @@ -722,25 +442,26 @@ func configureGribiRoute(ctx context.Context, t *testing.T, dut *ondatra.DUTDevi } // Programming AFT entries for prefixes in ENCAP_TE_VRF_A - args.client.Modify().AddEntry(t, + client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(101).WithEncapsulateHeader(fluent.IPinIP). WithIPinIP(ipv4OuterSrc111Addr, gribiIPv4EntryVRF1111). - WithNextHopNetworkInstance(niTeVrf111), + WithNextHopNetworkInstance(niTEVRF111), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(102).WithEncapsulateHeader(fluent.IPinIP). WithIPinIP(ipv4OuterSrc111Addr, gribiIPv4EntryVRF1112). - WithNextHopNetworkInstance(niTeVrf111), + WithNextHopNetworkInstance(niTEVRF111), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(101).AddNextHop(101, 1).AddNextHop(102, 3), 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 { + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) } - chk.HasResult(t, args.client.Results(t), + chk.HasResult(t, client.Results(t), fluent.OperationResult(). WithIPv4Operation(gribiIPv4EntryEncapVRF+"/24"). WithOperationType(constants.Add). @@ -931,7 +652,7 @@ func configureOTG(t testing.TB, otg *otg.OTG, atePorts []*ondatra.Port) gosnappi atePortNamelist[5], atePortNamelist[6], atePortNamelist[7]}). SetFormat(gosnappi.CaptureFormat.PCAP) - // Disable FEC for 100G-FR ports because Novus does not support it. + // 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") @@ -1390,7 +1111,7 @@ func TestEncapFrr(t *testing.T) { vrfpolicy.ConfigureVRFSelectionPolicy(t, dut, vrfpolicy.VRFPolicyC) if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) { - staticARPWithMagicUniversalIP(t, dut) + baseScenario.StaticARPWithMagicUniversalIP(t, dut) } t.Log("Install BGP route resolved by ISIS.") @@ -1431,7 +1152,6 @@ func TestEncapFrr(t *testing.T) { eID := gribi.BecomeLeader(t, client) args := &testArgs{ - ctx: ctx, client: client, dut: dut, ate: ate, @@ -1441,81 +1161,9 @@ func TestEncapFrr(t *testing.T) { otg: otg, } - cases := []struct { - desc string - downPortList []string - capturePortList []string - encapHeaderOuterIPList []string - encapHeaderInnerIPList []string - trafficDestIP string - loadBalancePercent []float64 - encapUnviable string - }{{ - desc: "Test-1: primary encap unviable but backup encap viable for single tunnel", - downPortList: []string{"port2", "port3", "port4"}, - capturePortList: []string{atePortNamelist[4], atePortNamelist[5]}, - encapHeaderOuterIPList: []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF1112}, - encapHeaderInnerIPList: []string{ipv4InnerDst, ipv4InnerDst}, - trafficDestIP: ipv4InnerDst, - loadBalancePercent: []float64{0, 0, 0, 0.25, 0.75, 0, 0}, - encapUnviable: "primarySingle", - }, { - desc: "Test-2: primary and backup encap unviable for single tunnel", - downPortList: []string{"port2", "port3", "port4", "port5"}, - capturePortList: []string{atePortNamelist[5], atePortNamelist[7]}, - encapHeaderOuterIPList: []string{gribiIPv4EntryVRF1112}, - encapHeaderInnerIPList: []string{ipv4InnerDst}, - trafficDestIP: ipv4InnerDst, - loadBalancePercent: []float64{0, 0, 0, 0, 0.75, 0, 0.25}, - encapUnviable: "primaryBackupSingle", - }, { - desc: "Test-3: primary encap unviable with backup to routing for single tunnel", - downPortList: []string{"port2", "port3", "port4"}, - capturePortList: []string{atePortNamelist[5], atePortNamelist[7]}, - encapHeaderOuterIPList: []string{gribiIPv4EntryVRF1112}, - encapHeaderInnerIPList: []string{ipv4InnerDst}, - trafficDestIP: ipv4InnerDst, - loadBalancePercent: []float64{0, 0, 0, 0, 0.75, 0, 0.25}, - encapUnviable: "primaryBackupRoutingSingle", - }, { - desc: "Test-4: primary encap unviable but backup encap viable for all tunnels", - downPortList: []string{"port2", "port3", "port4", "port6"}, - capturePortList: []string{atePortNamelist[4], atePortNamelist[6]}, - encapHeaderOuterIPList: []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF2222}, - encapHeaderInnerIPList: []string{ipv4InnerDst, ipv4InnerDst}, - trafficDestIP: ipv4InnerDst, - loadBalancePercent: []float64{0, 0, 0, 0.25, 0, 0.75, 0}, - encapUnviable: "primaryAll", - }, { - desc: "Test-5: primary and backup encap unviable for all tunnels", - downPortList: []string{"port2", "port3", "port4", "port5", "port6", "port7"}, - capturePortList: []string{atePortNamelist[7]}, - encapHeaderOuterIPList: []string{}, - encapHeaderInnerIPList: []string{ipv4InnerDst}, - trafficDestIP: ipv4InnerDst, - loadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, - encapUnviable: "primaryBackupAll", - }, { - desc: "Test-6: primary encap unviable with backup to routing for all tunnels", - downPortList: []string{"port2", "port3", "port4", "port6"}, - capturePortList: []string{atePortNamelist[7]}, - encapHeaderOuterIPList: []string{}, - encapHeaderInnerIPList: []string{ipv4InnerDst}, - trafficDestIP: ipv4InnerDst, - loadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, - encapUnviable: "primaryBackupRoutingAll", - }, { - desc: "Test-7: no match in encap VRF", - downPortList: []string{}, - capturePortList: []string{atePortNamelist[7]}, - encapHeaderOuterIPList: []string{}, - encapHeaderInnerIPList: []string{noMatchEncapDest}, - trafficDestIP: noMatchEncapDest, - loadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, - encapUnviable: "encapNoMatch", - }} - for _, tc := range cases { - t.Run(tc.desc, func(t *testing.T) { + testCases := baseScenario.TestCases(atePortNamelist, ipv4InnerDst) + for _, tc := range testCases { + t.Run(tc.Desc, func(t *testing.T) { t.Log("Verify whether the ports are in up state") portList := []string{"port2", "port3", "port4", "port5", "port6", "port7", "port8"} verifyPortStatus(t, args, portList, true) @@ -1525,7 +1173,7 @@ func TestEncapFrr(t *testing.T) { t.Fatal(err) } baseCapturePortList := []string{atePortNamelist[1], atePortNamelist[5]} - configureGribiRoute(ctx, t, dut, args) + configureGribiRoute(ctx, t, dut, client) createFlow(t, otgConfig, otg, ipv4InnerDst) captureState := startCapture(t, args, baseCapturePortList) sendTraffic(t, args, baseCapturePortList, captureState) @@ -1533,7 +1181,7 @@ func TestEncapFrr(t *testing.T) { baseLoadBalancePercent := []float64{0.0156, 0.0468, 0.1875, 0, 0.75, 0, 0} verifyTraffic(t, args, baseCapturePortList, baseLoadBalancePercent, !wantLoss, checkEncap, baseHeaderDstIP) - if tc.encapUnviable == "primaryBackupRoutingSingle" { + if tc.TestID == "primaryBackupRoutingSingle" { args.client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(1100).WithDecapsulateHeader(fluent.IPinIP). @@ -1541,28 +1189,28 @@ func TestEncapFrr(t *testing.T) { fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(1000).AddNextHop(1100, 1), ) - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + if err := awaitTimeout(ctx, t, args.client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) } } - if tc.encapUnviable == "primaryBackupRoutingAll" { + if tc.TestID == "primaryBackupRoutingAll" { args.client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(1100).WithDecapsulateHeader(fluent.IPinIP). + WithIndex(1200).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(1101).WithDecapsulateHeader(fluent.IPinIP). + WithIndex(1201).WithDecapsulateHeader(fluent.IPinIP). WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(1001).AddNextHop(1101, 1), + WithID(1000).AddNextHop(1200, 1), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1001).AddNextHop(1201, 1), ) - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + if err := awaitTimeout(ctx, t, args.client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) } } - if tc.encapUnviable == "encapNoMatch" { + if tc.TestID == "encapNoMatch" { args.client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(1003).WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), @@ -1571,23 +1219,23 @@ func TestEncapFrr(t *testing.T) { fluent.IPv4Entry().WithNetworkInstance(niEncapTeVrfA). WithPrefix("0.0.0.0/0").WithNextHopGroup(1003).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)), ) - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + if err := awaitTimeout(ctx, t, args.client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) } createFlow(t, otgConfig, otg, noMatchEncapDest) } - captureState = startCapture(t, args, tc.capturePortList) - if len(tc.downPortList) > 0 { - t.Logf("Bring down ports %s", tc.downPortList) - portState(t, args, tc.downPortList, false) - defer portState(t, args, tc.downPortList, true) + captureState = startCapture(t, args, tc.CapturePortList) + if len(tc.DownPortList) > 0 { + t.Logf("Bring down ports %s", tc.DownPortList) + portState(t, args, tc.DownPortList, false) + defer portState(t, args, tc.DownPortList, true) t.Log("Verify the port status after bringing down the ports") - verifyPortStatus(t, args, tc.downPortList, false) + verifyPortStatus(t, args, tc.DownPortList, false) } - sendTraffic(t, args, tc.capturePortList, captureState) - headerDstIP := map[string][]string{"outerIP": tc.encapHeaderOuterIPList, "innerIP": tc.encapHeaderInnerIPList} - verifyTraffic(t, args, tc.capturePortList, tc.loadBalancePercent, !wantLoss, checkEncap, headerDstIP) + sendTraffic(t, args, tc.CapturePortList, captureState) + headerDstIP := map[string][]string{"outerIP": tc.EncapHeaderOuterIPList, "innerIP": tc.EncapHeaderInnerIPList} + verifyTraffic(t, args, tc.CapturePortList, tc.LoadBalancePercent, !wantLoss, checkEncap, headerDstIP) }) } } diff --git a/feature/gribi/otg_tests/encap_frr/metadata.textproto b/feature/gribi/otg_tests/encap_frr/metadata.textproto index 7837a2abc82..698d268910b 100644 --- a/feature/gribi/otg_tests/encap_frr/metadata.textproto +++ b/feature/gribi/otg_tests/encap_frr/metadata.textproto @@ -46,10 +46,10 @@ platform_exceptions: { 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 + encap_tunnel_shut_backup_nhg_zero_traffic: true } } diff --git a/feature/gribi/otg_tests/encap_frr_with_repair_vrf/README.md b/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/README.md similarity index 99% rename from feature/gribi/otg_tests/encap_frr_with_repair_vrf/README.md rename to feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/README.md index 3298b85d563..06412bb00e6 100644 --- a/feature/gribi/otg_tests/encap_frr_with_repair_vrf/README.md +++ b/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/README.md @@ -540,7 +540,7 @@ NH#1001 -> { 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 +#### Test-9, 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. @@ -606,11 +606,18 @@ NH#102 -> { * 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 +## OpenConfig Path and RPC Coverage +```yaml +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: + gribi: + gRIBI.Get: + gRIBI.Modify: + gRIBI.Flush: +``` ## Required DUT platform diff --git a/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/encap_frr_with_reencap_vrf_test.go b/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/encap_frr_with_reencap_vrf_test.go new file mode 100644 index 00000000000..fdd5e3329a7 --- /dev/null +++ b/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/encap_frr_with_reencap_vrf_test.go @@ -0,0 +1,1196 @@ +// 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 encap_frr_with_reencap_vrf_test + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "net" + "os" + "sort" + "strconv" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + baseScenario "github.com/openconfig/featureprofiles/internal/encapfrr" + "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/ondatra/netutil" + "github.com/openconfig/ondatra/otg" + "github.com/openconfig/ygnmi/ygnmi" + "github.com/openconfig/ygot/ygot" +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +// Settings for configuring the baseline testbed with the test +// 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 + +const ( + plenIPv4 = 30 + plenIPv6 = 126 + dscpEncapA1 = 10 + ipv4OuterSrcAddr = "198.100.200.123" + ipv4InnerDst = "138.0.11.8" + ipv4OuterDst333 = "192.58.200.7" + noMatchEncapDest = "20.0.0.1" + niDecapTeVrf = "DECAP_TE_VRF" + niEncapTeVrfA = "ENCAP_TE_VRF_A" + niRepairVrf = "REPAIR_VRF" + tolerancePct = 2 + tolerance = 0.2 + encapFlow = "encapFlow" + correspondingHopLimit = 64 + magicIP = "192.168.1.1" + magicMAC = "02:00:00:00:00:01" + maskLen24 = "24" + maskLen32 = "32" + gribiIPv4EntryDefVRF1 = "192.0.2.101" + gribiIPv4EntryDefVRF2 = "192.0.2.102" + gribiIPv4EntryDefVRF3 = "192.0.2.103" + gribiIPv4EntryDefVRF4 = "192.0.2.104" + gribiIPv4EntryDefVRF5 = "192.0.2.105" + niTEVRF111 = "TE_VRF_111" + niTEVRF222 = "TE_VRF_222" + ipv4OuterSrc111Addr = "198.51.100.111" + ipv4OuterSrc222Addr = "198.51.100.222" + gribiIPv4EntryVRF1111 = "203.0.113.1" + gribiIPv4EntryVRF1112 = "203.0.113.2" + gribiIPv4EntryVRF2221 = "203.0.113.100" + gribiIPv4EntryVRF2222 = "203.0.113.101" + gribiIPv4EntryEncapVRF = "138.0.11.0" + + dutAreaAddress = "49.0001" + dutSysID = "1920.0000.2001" + otgSysID1 = "640000000001" + isisInstance = "DEFAULT" + + otgIsisPort8LoopV4 = "203.0.113.10" + otgIsisPort8LoopV6 = "2001:db8::203:0:113:10" + + dutAS = 65501 + peerGrpName1 = "BGP-PEER-GROUP1" + + ateSrcPort = "ate:port1" + ateSrcPortMac = "02:00:01:01:01:01" + ateSrcNetName = "srcnet" + ateSrcNet = "198.51.100.0" + ateSrcNetCIDR = "198.51.100.0/24" + ateSrcNetFirstIP = "198.51.100.1" + ateSrcNetCount = 250 + ipOverIPProtocol = 4 + + checkEncap = true + wantLoss = true +) + +var ( + portsIPv4 = map[string]string{ + "dut:port1": "192.0.2.1", + "ate:port1": "192.0.2.2", + + "dut:port2": "192.0.2.5", + "ate:port2": "192.0.2.6", + + "dut:port3": "192.0.2.9", + "ate:port3": "192.0.2.10", + + "dut:port4": "192.0.2.13", + "ate:port4": "192.0.2.14", + + "dut:port5": "192.0.2.17", + "ate:port5": "192.0.2.18", + + "dut:port6": "192.0.2.21", + "ate:port6": "192.0.2.22", + + "dut:port7": "192.0.2.25", + "ate:port7": "192.0.2.26", + + "dut:port8": "192.0.2.29", + "ate:port8": "192.0.2.30", + } + portsIPv6 = map[string]string{ + "dut:port1": "2001:db8::192:0:2:1", + "ate:port1": "2001:db8::192:0:2:2", + + "dut:port2": "2001:db8::192:0:2:5", + "ate:port2": "2001:db8::192:0:2:6", + + "dut:port3": "2001:db8::192:0:2:9", + "ate:port3": "2001:db8::192:0:2:a", + + "dut:port4": "2001:db8::192:0:2:d", + "ate:port4": "2001:db8::192:0:2:e", + + "dut:port5": "2001:db8::192:0:2:11", + "ate:port5": "2001:db8::192:0:2:12", + + "dut:port6": "2001:db8::192:0:2:15", + "ate:port6": "2001:db8::192:0:2:16", + + "dut:port7": "2001:db8::192:0:2:19", + "ate:port7": "2001:db8::192:0:2:1a", + + "dut:port8": "2001:db8::192:0:2:1d", + "ate:port8": "2001:db8::192:0:2:1e", + } + otgPortDevices []gosnappi.Device + dutlo0Attrs = attrs.Attributes{ + Desc: "Loopback ip", + IPv4: "203.0.113.11", + IPv6: "2001:db8::203:0:113:1", + IPv4Len: 32, + IPv6Len: 128, + } + loopbackIntfName string + atePortNamelist []string +) + +// awaitTimeout calls a fluent client Await, adding a timeout to the context. +func awaitTimeout(ctx context.Context, t testing.TB, c *fluent.GRIBIClient, timeout time.Duration) error { + t.Helper() + subctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + return c.Await(subctx, t) +} + +type testArgs struct { + client *fluent.GRIBIClient + dut *ondatra.DUTDevice + ate *ondatra.ATEDevice + otgConfig gosnappi.Config + top gosnappi.Config + electionID gribi.Uint128 + otg *otg.OTG +} + +// 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 +} + +func sortPorts(ports []*ondatra.Port) []*ondatra.Port { + sort.Slice(ports, func(i, j int) bool { + idi, idj := ports[i].ID(), ports[j].ID() + li, lj := len(idi), len(idj) + if li == lj { + return idi < idj + } + return li < lj // "port2" < "port10" + }) + return ports +} + +// dutInterface builds a DUT interface ygot struct for a given port +// according to portsIPv4. Returns nil if the port has no IP address +// mapping. +func dutInterface(p *ondatra.Port, dut *ondatra.DUTDevice) *oc.Interface { + id := fmt.Sprintf("%s:%s", p.Device().ID(), p.ID()) + i := &oc.Interface{ + Name: ygot.String(p.Name()), + Description: ygot.String(p.String()), + Type: oc.IETFInterfaces_InterfaceType_ethernetCsmacd, + } + if deviations.InterfaceEnabled(dut) { + 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 + } + ipv6, ok := portsIPv6[id] + if !ok { + return nil + } + s := i.GetOrCreateSubinterface(0) + s4 := s.GetOrCreateIpv4() + if deviations.InterfaceEnabled(dut) && !deviations.IPv4MissingEnabled(dut) { + s4.Enabled = ygot.Bool(true) + } + + a := s4.GetOrCreateAddress(ipv4) + a.PrefixLength = ygot.Uint8(plenIPv4) + s6 := s.GetOrCreateIpv6() + if deviations.InterfaceEnabled(dut) { + s6.Enabled = ygot.Bool(true) + } + a6 := s6.GetOrCreateAddress(ipv6) + a6.PrefixLength = ygot.Uint8(plenIPv6) + + return i +} + +// configureDUT configures all the interfaces on the DUT. +func configureDUT(t *testing.T, dut *ondatra.DUTDevice, dutPortList []*ondatra.Port) { + dc := gnmi.OC() + for _, dp := range dutPortList { + + if i := dutInterface(dp, dut); i != nil { + gnmi.Replace(t, dut, dc.Interface(dp.Name()).Config(), i) + } else { + t.Fatalf("No address found for port %v", dp) + } + } + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + for _, dp := range dut.Ports() { + fptest.AssignToNetworkInstance(t, dut, dp.Name(), deviations.DefaultNetworkInstance(dut), 0) + } + } + if deviations.ExplicitPortSpeed(dut) { + for _, dp := range dut.Ports() { + fptest.SetPortSpeed(t, dp) + } + } + + loopbackIntfName = netutil.LoopbackInterface(t, dut, 0) + lo0 := gnmi.OC().Interface(loopbackIntfName).Subinterface(0) + ipv4Addrs := gnmi.LookupAll(t, dut, lo0.Ipv4().AddressAny().State()) + ipv6Addrs := gnmi.LookupAll(t, dut, lo0.Ipv6().AddressAny().State()) + if len(ipv4Addrs) == 0 && len(ipv6Addrs) == 0 { + loop1 := dutlo0Attrs.NewOCInterface(loopbackIntfName, dut) + loop1.Type = oc.IETFInterfaces_InterfaceType_softwareLoopback + gnmi.Update(t, dut, dc.Interface(loopbackIntfName).Config(), loop1) + } else { + v4, ok := ipv4Addrs[0].Val() + if ok { + dutlo0Attrs.IPv4 = v4.GetIp() + } + v6, ok := ipv6Addrs[0].Val() + if ok { + dutlo0Attrs.IPv6 = v6.GetIp() + } + t.Logf("Got DUT IPv4 loopback address: %v", dutlo0Attrs.IPv4) + t.Logf("Got DUT IPv6 loopback address: %v", dutlo0Attrs.IPv6) + } + if deviations.GRIBIMACOverrideWithStaticARP(dut) { + baseScenario.StaticARPWithSpecificIP(t, dut) + } +} + +// configureGribiRoute configures Gribi route as per the requirements +func configureGribiRoute(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, client *fluent.GRIBIClient) { + t.Helper() + + baseScenario.ConfigureBaseGribiRoutes(ctx, t, dut, client) + + // Programming AFT entries for backup NHG + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(2000).WithDecapsulateHeader(fluent.IPinIP).WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(2000).AddNextHop(2000, 1), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + // Programming AFT entries for prefixes in TE_VRF_222 + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(3).WithIPAddress(gribiIPv4EntryDefVRF3), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(2).AddNextHop(3, 1).WithBackupNHG(2000), + 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).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF2222+"/"+maskLen32).WithNextHopGroup(4), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + teVRF222IPList := []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF2222} + for ip := range teVRF222IPList { + chk.HasResult(t, client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(teVRF222IPList[ip]+"/32"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } + + // Programming AFT entries for backup NHG + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1000).WithDecapsulateHeader(fluent.IPinIP).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc222Addr, gribiIPv4EntryVRF2221). + WithNextHopNetworkInstance(niTEVRF222), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1000).AddNextHop(1000, 1).WithBackupNHG(2000), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1001).WithDecapsulateHeader(fluent.IPinIP).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc222Addr, gribiIPv4EntryVRF2222). + WithNextHopNetworkInstance(niTEVRF222), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1001).AddNextHop(1001, 1).WithBackupNHG(2000), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(3000).WithNextHopNetworkInstance(niRepairVrf), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(3000).AddNextHop(3000, 1), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + // Programming AFT entries for prefixes in TE_VRF_111 + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1).WithIPAddress(gribiIPv4EntryDefVRF1), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(2).WithIPAddress(gribiIPv4EntryDefVRF2), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1).AddNextHop(1, 1).AddNextHop(2, 3).WithBackupNHG(3000), + 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(3000), + fluent.IPv4Entry().WithNetworkInstance(niTEVRF111).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF1112+"/"+maskLen32).WithNextHopGroup(3), + + fluent.IPv4Entry().WithNetworkInstance(niRepairVrf).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF1111+"/"+maskLen32).WithNextHopGroup(1000), + fluent.IPv4Entry().WithNetworkInstance(niRepairVrf).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF1112+"/"+maskLen32).WithNextHopGroup(1001), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + teVRF111IPList := []string{gribiIPv4EntryVRF1111, gribiIPv4EntryVRF1112} + for ip := range teVRF111IPList { + chk.HasResult(t, client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(teVRF111IPList[ip]+"/32"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } + + // Programming AFT entries for backup NHG + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(2001).WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(2001).AddNextHop(2001, 1), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + // Programming AFT entries for prefixes in ENCAP_TE_VRF_A + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(101).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc111Addr, gribiIPv4EntryVRF1111). + WithNextHopNetworkInstance(niTEVRF111), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(102).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc111Addr, gribiIPv4EntryVRF1112). + WithNextHopNetworkInstance(niTEVRF111), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(101).AddNextHop(101, 1).AddNextHop(102, 3).WithBackupNHG(2001), + fluent.IPv4Entry().WithNetworkInstance(niEncapTeVrfA).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryEncapVRF+"/"+maskLen24).WithNextHopGroup(101), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + chk.HasResult(t, client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(gribiIPv4EntryEncapVRF+"/24"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) +} + +// 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 configureISIS(t *testing.T, dut *ondatra.DUTDevice, intfName, dutAreaAddress, dutSysID string) { + t.Helper() + d := &oc.Root{} + dutConfIsisPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance) + 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() + 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) + if deviations.ISISInstanceEnabledRequired(dut) { + globalISIS.Instance = ygot.String(isisInstance) + } + isisLevel2 := isis.GetOrCreateLevel(2) + isisLevel2.MetricStyle = oc.Isis_MetricStyle_WIDE_METRIC + if deviations.ISISLevelEnabled(dut) { + isisLevel2.Enabled = ygot.Bool(true) + } + + isisIntf := isis.GetOrCreateInterface(intfName) + isisIntf.Enabled = ygot.Bool(true) + isisIntf.CircuitType = oc.Isis_CircuitType_POINT_TO_POINT + isisIntfLevel := isisIntf.GetOrCreateLevel(2) + isisIntfLevel.Enabled = ygot.Bool(true) + isisIntfLevelAfi := isisIntfLevel.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST) + isisIntfLevelAfi.Metric = ygot.Uint32(200) + if deviations.ISISInterfaceAfiUnsupported(dut) { + isisIntf.Af = nil + } + if deviations.MissingIsisInterfaceAfiSafiEnable(dut) { + isisIntfLevelAfi.Enabled = nil + } + + gnmi.Replace(t, dut, dutConfIsisPath.Config(), prot) +} + +func bgpCreateNbr(localAs uint32, dut *ondatra.DUTDevice) *oc.NetworkInstance_Protocol { + 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(dutlo0Attrs.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) + pg1 := bgp.GetOrCreatePeerGroup(peerGrpName1) + pg1.PeerAs = ygot.Uint32(localAs) + + bgpNbr := bgp.GetOrCreateNeighbor(otgIsisPort8LoopV4) + bgpNbr.PeerGroup = ygot.String(peerGrpName1) + bgpNbr.PeerAs = ygot.Uint32(localAs) + bgpNbr.Enabled = ygot.Bool(true) + bgpNbrT := bgpNbr.GetOrCreateTransport() + bgpNbrT.LocalAddress = ygot.String(dutlo0Attrs.IPv4) + 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(true) + + return niProto +} + +func verifyISISTelemetry(t *testing.T, dut *ondatra.DUTDevice, dutIntf string) { + t.Helper() + statePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance).Isis() + + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + dutIntf = dutIntf + ".0" + } + nbrPath := statePath.Interface(dutIntf) + query := nbrPath.LevelAny().AdjacencyAny().AdjacencyState().State() + _, ok := gnmi.WatchAll(t, dut, query, time.Minute, func(val *ygnmi.Value[oc.E_Isis_IsisInterfaceAdjState]) bool { + state, present := val.Val() + return present && state == oc.Isis_IsisInterfaceAdjState_UP + }).Await(t) + if !ok { + t.Logf("IS-IS state on %v has no adjacencies", dutIntf) + t.Fatal("No IS-IS adjacencies reported.") + } +} + +func verifyBgpTelemetry(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + t.Logf("Verifying BGP state.") + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + + nbrPath := bgpPath.Neighbor(otgIsisPort8LoopV4) + // Get BGP adjacency state. + t.Logf("Waiting for BGP neighbor to establish...") + 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", otgIsisPort8LoopV4, state) + if want := oc.Bgp_Neighbor_SessionState_ESTABLISHED; state != want { + t.Errorf("BGP peer %s status got %d, want %d", otgIsisPort8LoopV4, state, want) + } +} + +// configureOTG configures the topology of the ATE. +func configureOTG(t testing.TB, otg *otg.OTG, atePorts []*ondatra.Port) gosnappi.Config { + t.Helper() + config := gosnappi.NewConfig() + 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()) + + port := config.Ports().Add().SetName(ap.ID()) + atePortNamelist = append(atePortNamelist, port.Name()) + portName := fmt.Sprintf("atePort%s", strconv.Itoa(i)) + dev := config.Devices().Add().SetName(portName) + macAddress, _ := incrementMAC(ateSrcPortMac, i) + eth := dev.Ethernets().Add().SetName(portName + ".Eth").SetMac(macAddress) + eth.Connection().SetPortName(port.Name()) + eth.Ipv4Addresses().Add().SetName(portName + ".IPv4"). + SetAddress(portsIPv4[ateid]).SetGateway(portsIPv4[dutid]). + SetPrefix(plenIPv4) + eth.Ipv6Addresses().Add().SetName(portName + ".IPv6"). + SetAddress(portsIPv6[ateid]).SetGateway(portsIPv6[dutid]). + SetPrefix(plenIPv6) + + otgPortDevices = append(otgPortDevices, dev) + if i == 7 { + iDut8LoopV4 := dev.Ipv4Loopbacks().Add().SetName("Port8LoopV4").SetEthName(eth.Name()) + iDut8LoopV4.SetAddress(otgIsisPort8LoopV4) + iDut8LoopV6 := dev.Ipv6Loopbacks().Add().SetName("Port8LoopV6").SetEthName(eth.Name()) + iDut8LoopV6.SetAddress(otgIsisPort8LoopV6) + isisDut := dev.Isis().SetName("ISIS1").SetSystemId(otgSysID1) + isisDut.Basic().SetIpv4TeRouterId(portsIPv4[ateid]).SetHostname(isisDut.Name()).SetLearnedLspFilter(true) + isisDut.Interfaces().Add().SetEthName(dev.Ethernets().Items()[0].Name()). + SetName("devIsisInt1"). + SetLevelType(gosnappi.IsisInterfaceLevelType.LEVEL_2). + SetNetworkType(gosnappi.IsisInterfaceNetworkType.POINT_TO_POINT) + + // Advertise OTG Port8 loopback address via ISIS. + isisPort2V4 := dev.Isis().V4Routes().Add().SetName("ISISPort8V4").SetLinkMetric(10) + isisPort2V4.Addresses().Add().SetAddress(otgIsisPort8LoopV4).SetPrefix(32) + isisPort2V6 := dev.Isis().V6Routes().Add().SetName("ISISPort8V6").SetLinkMetric(10) + isisPort2V6.Addresses().Add().SetAddress(otgIsisPort8LoopV6).SetPrefix(uint32(128)) + iDutBgp := dev.Bgp().SetRouterId(otgIsisPort8LoopV4) + iDutBgp4Peer := iDutBgp.Ipv4Interfaces().Add().SetIpv4Name(iDut8LoopV4.Name()).Peers().Add().SetName(ap.ID() + ".BGP4.peer") + iDutBgp4Peer.SetPeerAddress(dutlo0Attrs.IPv4).SetAsNumber(dutAS).SetAsType(gosnappi.BgpV4PeerAsType.IBGP) + iDutBgp4Peer.Capability().SetIpv4Unicast(true).SetIpv6Unicast(true) + iDutBgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) + + bgpNeti1Bgp4PeerRoutes := iDutBgp4Peer.V4Routes().Add().SetName(port.Name() + ".BGP4.Route") + bgpNeti1Bgp4PeerRoutes.SetNextHopIpv4Address(otgIsisPort8LoopV4). + SetNextHopAddressType(gosnappi.BgpV4RouteRangeNextHopAddressType.IPV4). + SetNextHopMode(gosnappi.BgpV4RouteRangeNextHopMode.MANUAL). + Advanced().SetLocalPreference(100).SetIncludeLocalPreference(true) + bgpNeti1Bgp4PeerRoutes.Addresses().Add().SetAddress(ipv4InnerDst).SetPrefix(32). + SetCount(1).SetStep(1) + bgpNeti1Bgp4PeerRoutes.Addresses().Add().SetAddress(noMatchEncapDest).SetPrefix(32). + SetCount(1).SetStep(1) + } + + } + config.Captures().Add().SetName("packetCapture"). + 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) + time.Sleep(30 * time.Second) + pb, _ := config.Marshal().ToProto() + t.Log(pb.GetCaptures()) + return config +} + +func createFlow(t *testing.T, config gosnappi.Config, otg *otg.OTG, trafficDestIP string) { + t.Helper() + + config.Flows().Clear() + + flow1 := gosnappi.NewFlow().SetName(encapFlow) + flow1.Metrics().SetEnable(true) + flow1.TxRx().Device(). + SetTxNames([]string{otgPortDevices[0].Name() + ".IPv4"}). + SetRxNames([]string{otgPortDevices[1].Name() + ".IPv4", otgPortDevices[2].Name() + ".IPv4", otgPortDevices[3].Name() + ".IPv4", + otgPortDevices[4].Name() + ".IPv4", otgPortDevices[5].Name() + ".IPv4", otgPortDevices[6].Name() + ".IPv4", + otgPortDevices[7].Name() + ".IPv4", + }) + flow1.Size().SetFixed(512) + flow1.Rate().SetPps(100) + flow1.Duration().Continuous() + + ethHeader1 := flow1.Packet().Add().Ethernet() + ethHeader1.Src().SetValue(ateSrcPortMac) + + IPHeader := flow1.Packet().Add().Ipv4() + IPHeader.Src().Increment().SetCount(1000).SetStep("0.0.0.1").SetStart(ipv4OuterSrcAddr) + IPHeader.Dst().SetValue(trafficDestIP) + IPHeader.Priority().Dscp().Phb().SetValue(dscpEncapA1) + + UDPHeader := flow1.Packet().Add().Udp() + UDPHeader.DstPort().Increment().SetStart(1).SetCount(50000).SetStep(1) + UDPHeader.SrcPort().Increment().SetStart(1).SetCount(50000).SetStep(1) + + config.Flows().Append(flow1) + + t.Logf("Pushing traffic flows to OTG and starting protocols...") + otg.PushConfig(t, config) + time.Sleep(30 * time.Second) + otg.StartProtocols(t) + time.Sleep(30 * time.Second) +} + +func startCapture(t *testing.T, args *testArgs, capturePortList []string) gosnappi.ControlState { + t.Helper() + args.otgConfig.Captures().Clear() + args.otgConfig.Captures().Add().SetName("packetCapture"). + SetPortNames(capturePortList). + SetFormat(gosnappi.CaptureFormat.PCAP) + args.otg.PushConfig(t, args.otgConfig) + time.Sleep(30 * time.Second) + args.otg.StartProtocols(t) + time.Sleep(30 * time.Second) + cs := gosnappi.NewControlState() + cs.Port().Capture().SetState(gosnappi.StatePortCaptureState.START) + args.otg.SetControlState(t, cs) + return cs +} + +func sendTraffic(t *testing.T, args *testArgs, capturePortList []string, cs gosnappi.ControlState) { + t.Helper() + otgutils.WaitForARP(t, args.otg, args.top, "IPv4") + t.Logf("Starting traffic") + args.otg.StartTraffic(t) + time.Sleep(15 * time.Second) + t.Logf("Stop traffic") + args.otg.StopTraffic(t) + cs.Port().Capture().SetState(gosnappi.StatePortCaptureState.STOP) + args.otg.SetControlState(t, cs) +} + +func verifyTraffic(t *testing.T, args *testArgs, capturePortList []string, loadBalancePercent []float64, wantLoss, checkEncap bool, headerDstIP map[string][]string) { + t.Helper() + t.Logf("Verifying flow metrics for the flow: encapFlow\n") + recvMetric := gnmi.Get(t, args.otg, gnmi.OTG().Flow(encapFlow).State()) + txPackets := recvMetric.GetCounters().GetOutPkts() + rxPackets := recvMetric.GetCounters().GetInPkts() + lostPackets := txPackets - rxPackets + var lossPct uint64 + if txPackets != 0 { + lossPct = lostPackets * 100 / txPackets + } else { + t.Errorf("Traffic stats are not correct %v", recvMetric) + } + if wantLoss { + if lossPct < 100-tolerancePct { + t.Errorf("Traffic is expected to fail %s\n got %v, want 100%% failure", encapFlow, lossPct) + } else { + t.Logf("Traffic Loss Test Passed!") + } + } else { + if lossPct > tolerancePct { + t.Errorf("Traffic Loss Pct for Flow: %s\n got %v, want 0", encapFlow, lossPct) + } else { + t.Logf("Traffic Test Passed!") + } + } + t.Log("Verify packet load balancing as per the programmed weight") + validateTrafficDistribution(t, args.ate, loadBalancePercent) + var pcapFileList []string + for _, capturePort := range capturePortList { + bytes := args.otg.GetCapture(t, gosnappi.NewCaptureRequest().SetPortName(capturePort)) + pcapFileName, err := os.CreateTemp("", "pcap") + if err != nil { + t.Errorf("ERROR: Could not create temporary pcap file: %v\n", err) + } + if _, err := pcapFileName.Write(bytes); err != nil { + t.Errorf("ERROR: Could not write bytes to pcap file: %v\n", err) + } + pcapFileName.Close() + pcapFileList = append(pcapFileList, pcapFileName.Name()) + } + validatePackets(t, pcapFileList, checkEncap, headerDstIP) + args.otgConfig.Captures().Clear() + args.otg.PushConfig(t, args.otgConfig) + time.Sleep(30 * time.Second) +} + +func validatePackets(t *testing.T, filename []string, checkEncap bool, headerDstIP map[string][]string) { + t.Helper() + for index, file := range filename { + fileStat, err := os.Stat(file) + if err != nil { + t.Errorf("Filestat for pcap file failed %s", err) + } + fileSize := fileStat.Size() + if fileSize > 0 { + handle, err := pcap.OpenOffline(file) + if err != nil { + t.Errorf("Unable to open the pcap file, error: %s", err) + } else { + packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) + if checkEncap { + validateTrafficEncap(t, packetSource, headerDstIP, index) + } + } + defer handle.Close() + } + } +} + +func validateTrafficEncap(t *testing.T, packetSource *gopacket.PacketSource, headerDstIP map[string][]string, index int) { + t.Helper() + for packet := range packetSource.Packets() { + ipLayer := packet.Layer(layers.LayerTypeIPv4) + if ipLayer == nil { + continue + } + ipPacket, _ := ipLayer.(*layers.IPv4) + encapHeaderListLength := len(headerDstIP["outerIP"]) + if index <= encapHeaderListLength-1 { + innerPacket := gopacket.NewPacket(ipPacket.Payload, ipPacket.NextLayerType(), gopacket.Default) + ipInnerLayer := innerPacket.Layer(layers.LayerTypeIPv4) + if ipInnerLayer != nil { + destIP := ipPacket.DstIP.String() + t.Logf("Outer dest ip in received packet %s", destIP) + if ipPacket.DstIP.String() != headerDstIP["outerIP"][index] { + t.Errorf("Packets are not encapsulated") + } + ipInnerPacket, _ := ipInnerLayer.(*layers.IPv4) + if ipInnerPacket.DstIP.String() != headerDstIP["innerIP"][index] { + t.Errorf("Packets are not encapsulated") + } + t.Logf("Traffic for encap routes passed.") + break + } else { + t.Errorf("Packet is not encapsulated") + } + } else if index >= encapHeaderListLength || encapHeaderListLength == 0 { + if ipPacket.DstIP.String() == otgIsisPort8LoopV4 { + continue + } else if ipPacket.DstIP.String() != headerDstIP["innerIP"][0] { + destIP := ipPacket.DstIP.String() + t.Logf("Dest ip in received packet %s", destIP) + t.Errorf("Packets are encapsulated which is not expected") + } else { + t.Logf("Traffic for non-encap routes passed.") + break + } + } + } +} + +func verifyPortStatus(t *testing.T, args *testArgs, portList []string, portStatus bool) { + wantStatus := oc.Interface_OperStatus_UP + if !portStatus { + wantStatus = oc.Interface_OperStatus_DOWN + } + for _, port := range portList { + p := args.dut.Port(t, port) + t.Log("Check for port status") + gnmi.Await(t, args.dut, gnmi.OC().Interface(p.Name()).OperStatus().State(), 1*time.Minute, wantStatus) + operStatus := gnmi.Get(t, args.dut, gnmi.OC().Interface(p.Name()).OperStatus().State()) + if operStatus != wantStatus { + t.Errorf("Get(DUT %v oper status): got %v, want %v", port, operStatus, wantStatus) + } + } +} + +// setDUTInterfaceState sets the admin state on the dut interface +func setDUTInterfaceWithState(t testing.TB, dut *ondatra.DUTDevice, p *ondatra.Port, state bool) { + dc := gnmi.OC() + i := &oc.Interface{} + i.Enabled = ygot.Bool(state) + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + i.Name = ygot.String(p.Name()) + gnmi.Update(t, dut, dc.Interface(p.Name()).Config(), i) +} + +func portState(t *testing.T, args *testArgs, portList []string, portEnabled bool) { + t.Logf("Change port enable state to %t", portEnabled) + if deviations.ATEPortLinkStateOperationsUnsupported(args.ate) { + for _, port := range portList { + p := args.dut.Port(t, port) + if portEnabled { + setDUTInterfaceWithState(t, args.dut, p, true) + } else { + setDUTInterfaceWithState(t, args.dut, p, false) + } + } + } else { + var portNames []string + for _, p := range portList { + portNames = append(portNames, args.ate.Port(t, p).ID()) + } + portStateAction := gosnappi.NewControlState() + if portEnabled { + portStateAction.Port().Link().SetPortNames(portNames).SetState(gosnappi.StatePortLinkState.UP) + } else { + portStateAction.Port().Link().SetPortNames(portNames).SetState(gosnappi.StatePortLinkState.DOWN) + } + args.ate.OTG().SetControlState(t, portStateAction) + } +} + +func normalize(xs []uint64) (ys []float64, sum uint64) { + for _, x := range xs { + sum += x + } + ys = make([]float64, len(xs)) + for i, x := range xs { + ys[i] = float64(x) / float64(sum) + } + return ys, sum +} + +func validateTrafficDistribution(t *testing.T, ate *ondatra.ATEDevice, wantWeights []float64) { + inFramesAllPorts := gnmi.GetAll(t, ate.OTG(), gnmi.OTG().PortAny().Counters().InFrames().State()) + // Skip source port, Port1. + gotWeights, _ := normalize(inFramesAllPorts[1:]) + + t.Log("got ratio:", gotWeights) + t.Log("want ratio:", wantWeights) + if diff := cmp.Diff(wantWeights, gotWeights, cmpopts.EquateApprox(0, tolerance)); diff != "" { + t.Errorf("Packet distribution ratios -want,+got:\n%s", diff) + } +} + +// TestEncapFrr is to test Test FRR behaviors with encapsulation scenarios +func TestEncapFrr(t *testing.T) { + ctx := context.Background() + dut := ondatra.DUT(t, "dut") + + gribic := dut.RawAPIs().GRIBI(t) + ate := ondatra.ATE(t, "ate") + top := gosnappi.NewConfig() + dutPorts := sortPorts(dut.Ports())[0:8] + atePorts := sortPorts(ate.Ports())[0:8] + + t.Log("Configure Default Network Instance") + fptest.ConfigureDefaultNetworkInstance(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") + vrfpolicy.ConfigureVRFSelectionPolicy(t, dut, vrfpolicy.VRFPolicyC) + + 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) + + dutConfPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + gnmi.Delete(t, dut, dutConfPath.Config()) + dutConf := bgpCreateNbr(dutAS, dut) + gnmi.Replace(t, dut, dutConfPath.Config(), dutConf) + fptest.LogQuery(t, "DUT BGP Config", dutConfPath.Config(), gnmi.Get(t, dut, dutConfPath.Config())) + + otg := ate.OTG() + otgConfig := configureOTG(t, otg, atePorts) + + verifyISISTelemetry(t, dut, dutPorts[7].Name()) + verifyBgpTelemetry(t, dut) + + // Connect gRIBI client to DUT referred to as gRIBI - using PRESERVE persistence and + // SINGLE_PRIMARY mode, with FIB ACK requested. Specify gRIBI as the leader. + client := fluent.NewClient() + client.Connection().WithStub(gribic).WithPersistence().WithInitialElectionID(1, 0). + WithFIBACK().WithRedundancyMode(fluent.ElectedPrimaryClient) + client.Start(ctx, t) + defer client.Stop(t) + + defer func() { + // Flush all entries after test. + if err := gribi.FlushAll(client); err != nil { + t.Error(err) + } + }() + + client.StartSending(ctx, t) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Fatalf("Await got error during session negotiation for clientA: %v", err) + } + eID := gribi.BecomeLeader(t, client) + + args := &testArgs{ + client: client, + dut: dut, + ate: ate, + otgConfig: otgConfig, + top: top, + electionID: eID, + otg: otg, + } + + testCases := baseScenario.TestCases(atePortNamelist, ipv4InnerDst) + testCases = append(testCases, + &baseScenario.TestCase{ + Desc: "Test-8: no match in TE_VRF_222", + DownPortList: []string{"port2", "port3", "port4", "port6"}, + CapturePortList: []string{atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, + TestID: "teVrf222NoMatch", + }, + &baseScenario.TestCase{ + Desc: "Test-9: no match in TE_VRF_111", + DownPortList: []string{"port2", "port3", "port4", "port6"}, + CapturePortList: []string{atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, + TestID: "teVrf111NoMatch", + }, + ) + for _, tc := range testCases { + t.Run(tc.Desc, func(t *testing.T) { + t.Log("Verify whether the ports are in up state") + portList := []string{"port2", "port3", "port4", "port5", "port6", "port7", "port8"} + verifyPortStatus(t, args, portList, true) + + t.Log("Flush existing gRIBI routes before test.") + if err := gribi.FlushAll(client); err != nil { + t.Fatal(err) + } + baseCapturePortList := []string{atePortNamelist[1], atePortNamelist[5]} + configureGribiRoute(ctx, t, dut, args.client) + createFlow(t, otgConfig, otg, ipv4InnerDst) + captureState := startCapture(t, args, baseCapturePortList) + sendTraffic(t, args, baseCapturePortList, captureState) + baseHeaderDstIP := map[string][]string{"outerIP": {gribiIPv4EntryVRF1111, gribiIPv4EntryVRF1112}, "innerIP": {ipv4InnerDst, ipv4InnerDst}} + baseLoadBalancePercent := []float64{0.0156, 0.0468, 0.1875, 0, 0.75, 0, 0} + verifyTraffic(t, args, baseCapturePortList, baseLoadBalancePercent, !wantLoss, checkEncap, baseHeaderDstIP) + + if tc.TestID == "primaryBackupRoutingSingle" { + args.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1100).WithDecapsulateHeader(fluent.IPinIP). + WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1000).AddNextHop(1100, 1), + ) + if err := awaitTimeout(ctx, t, args.client, 2*time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + } + if tc.TestID == "primaryBackupRoutingAll" { + args.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1200).WithDecapsulateHeader(fluent.IPinIP). + WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1201).WithDecapsulateHeader(fluent.IPinIP). + WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1000).AddNextHop(1200, 1), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1001).AddNextHop(1201, 1), + ) + if err := awaitTimeout(ctx, t, args.client, 2*time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + } + if tc.TestID == "encapNoMatch" { + args.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1003).WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1003).AddNextHop(1003, 1), + fluent.IPv4Entry().WithNetworkInstance(niEncapTeVrfA). + WithPrefix("0.0.0.0/0").WithNextHopGroup(1003).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)), + ) + if err := awaitTimeout(ctx, t, args.client, 2*time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + createFlow(t, otgConfig, otg, noMatchEncapDest) + } + if tc.TestID == "teVrf222NoMatch" { + args.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1300).WithDecapsulateHeader(fluent.IPinIP).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc222Addr, gribiIPv4EntryVRF2221).WithNextHopNetworkInstance(niTEVRF222), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1301).WithDecapsulateHeader(fluent.IPinIP).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc222Addr, gribiIPv4EntryVRF2222).WithNextHopNetworkInstance(niTEVRF222), + + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1000).AddNextHop(1300, 1), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1001).AddNextHop(1301, 1), + ) + if err := awaitTimeout(ctx, t, args.client, 2*time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + } + if tc.TestID == "teVrf111NoMatch" { + args.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(201).WithEncapsulateHeader(fluent.IPinIP).WithIPinIP(ipv4OuterSrc111Addr, "203.100.113.1"). + WithNextHopNetworkInstance(niTEVRF111), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(202).WithEncapsulateHeader(fluent.IPinIP).WithIPinIP(ipv4OuterSrc111Addr, "203.100.113.2"). + WithNextHopNetworkInstance(niTEVRF111), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(101).AddNextHop(201, 1).AddNextHop(202, 3).WithBackupNHG(2001), + ) + if err := awaitTimeout(ctx, t, args.client, 2*time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + } + + captureState = startCapture(t, args, tc.CapturePortList) + if len(tc.DownPortList) > 0 { + t.Logf("Bring down ports %s", tc.DownPortList) + portState(t, args, tc.DownPortList, false) + defer portState(t, args, tc.DownPortList, true) + t.Log("Verify the port status after bringing down the ports") + verifyPortStatus(t, args, tc.DownPortList, false) + } + sendTraffic(t, args, tc.CapturePortList, captureState) + headerDstIP := map[string][]string{"outerIP": tc.EncapHeaderOuterIPList, "innerIP": tc.EncapHeaderInnerIPList} + + if deviations.EncapTunnelShutBackupNhgZeroTraffic(dut) { + if tc.TestID == "primarySingle" || tc.TestID == "primaryBackupSingle" || tc.TestID == "primaryBackupRoutingSingle" { + tc.LoadBalancePercent = []float64{0, 0, 0, 0, 1, 0, 0} + } else if tc.TestID == "primaryAll" { + tc.LoadBalancePercent = []float64{0, 0, 0, 0, 0, 0, 1} + } + } + verifyTraffic(t, args, tc.CapturePortList, tc.LoadBalancePercent, !wantLoss, checkEncap, headerDstIP) + }) + } +} diff --git a/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/metadata.textproto b/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/metadata.textproto new file mode 100644 index 00000000000..1f42b8e4509 --- /dev/null +++ b/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/metadata.textproto @@ -0,0 +1,56 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "f8eee005-f5dc-4c58-a7f2-444571bcd49f" +plan_id: "TE-16.3" +description: "encapsulation FRR scenarios" +testbed: TESTBED_DUT_ATE_8LINKS + +platform_exceptions: { + platform: { + vendor: CISCO + } + 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: { + platform: { + vendor: JUNIPER + } + deviations: { + isis_level_enabled: true + } +} +platform_exceptions: { + platform: { + vendor: NOKIA + } + deviations: { + explicit_port_speed: true + explicit_interface_in_default_vrf: true + interface_enabled: true + } +} +platform_exceptions: { + platform: { + vendor: ARISTA + } + 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 + omit_l2_mtu: true + backup_nhg_requires_vrf_with_decap: true + missing_isis_interface_afi_safi_enable: true + isis_interface_afi_unsupported: true + encap_tunnel_shut_backup_nhg_zero_traffic: true + } +} + diff --git a/feature/gribi/otg_tests/gribi_scaling/gribi_scaling_test.go b/feature/gribi/otg_tests/gribi_scaling/gribi_scaling_test.go index 6e817877d7d..0d601a390aa 100644 --- a/feature/gribi/otg_tests/gribi_scaling/gribi_scaling_test.go +++ b/feature/gribi/otg_tests/gribi_scaling/gribi_scaling_test.go @@ -263,6 +263,7 @@ func incrementMAC(mac string, i int) (string, error) { func TestScaling(t *testing.T) { dut := ondatra.DUT(t, "dut") + overrideScaleParams(dut) ate := ondatra.ATE(t, "ate") ctx := context.Background() @@ -313,6 +314,8 @@ func TestScaling(t *testing.T) { V4ReEncapNHGCount: *fpargs.V4ReEncapNHGCount, }, ) + createFlow(t, ate, top, vrfConfigs[1]) + for _, vrfConfig := range vrfConfigs { entries := append(vrfConfig.NHs, vrfConfig.NHGs...) entries = append(entries, vrfConfig.V4Entries...) @@ -323,7 +326,6 @@ func TestScaling(t *testing.T) { t.Logf("Created %d NHs, %d NHGs, %d IPv4Entries in %s VRF", len(vrfConfig.NHs), len(vrfConfig.NHGs), len(vrfConfig.V4Entries), vrfConfig.Name) } - createFlow(t, ate, top, vrfConfigs[1]) checkTraffic(t, ate, top) } @@ -377,3 +379,12 @@ func checkTraffic(t *testing.T, ate *ondatra.ATEDevice, top gosnappi.Config) { t.Errorf("FAIL- Got %v%% packet loss for %s ; expected < 1%%", lossPct, "flow") } } + +// overrideScaleParams allows to override the default scale parameters based on the DUT vendor. +func overrideScaleParams(dut *ondatra.DUTDevice) { + if deviations.OverrideDefaultNhScale(dut) { + if dut.Vendor() == ondatra.CISCO { + *fpargs.V4TunnelCount = 3328 + } + } +} diff --git a/feature/gribi/otg_tests/gribi_scaling/metadata.textproto b/feature/gribi/otg_tests/gribi_scaling/metadata.textproto index 9d8d3c39135..981e65d1ab9 100644 --- a/feature/gribi/otg_tests/gribi_scaling/metadata.textproto +++ b/feature/gribi/otg_tests/gribi_scaling/metadata.textproto @@ -12,6 +12,7 @@ platform_exceptions: { deviations: { ipv4_missing_enabled: true interface_ref_interface_id_format: true + override_default_nh_scale: true } } platform_exceptions: { @@ -20,7 +21,6 @@ platform_exceptions: { } deviations: { no_mix_of_tagged_and_untagged_subinterfaces: true - explicit_interface_ref_definition: true explicit_port_speed: true explicit_interface_in_default_vrf: true interface_enabled: true @@ -40,7 +40,6 @@ platform_exceptions: { vendor: ARISTA } deviations: { - deprecated_vlan_id: true interface_enabled: true default_network_instance: "default" omit_l2_mtu: true diff --git a/feature/gribi/otg_tests/gribigo_compliance_test/README.md b/feature/gribi/otg_tests/gribigo_compliance_test/README.md index 03a1b975784..f5fb9926170 100644 --- a/feature/gribi/otg_tests/gribigo_compliance_test/README.md +++ b/feature/gribi/otg_tests/gribigo_compliance_test/README.md @@ -21,3 +21,22 @@ For each compliance test case in the test suite: * If the case expects a t.Fatal result, use testt.ExpectFatal. * If the case expects a t.Error result, use testt.ExpectError. * Otherwise, call the test case function directly. + +## OpenConfig Path and RPC Coverage + +The below yaml defines the OC paths and RPC intended to be covered by this test. + +```yaml +paths: + /interfaces/interface/config/enabled: + + +rpcs: + gnmi: + gNMI.Subscribe: + ON_CHANGE: true + + gnoi: + system.System.Reboot: + +``` diff --git a/feature/gribi/otg_tests/gribigo_compliance_test/gribigo_compliance_test.go b/feature/gribi/otg_tests/gribigo_compliance_test/gribigo_compliance_test.go index 398959eae6a..1792e620550 100644 --- a/feature/gribi/otg_tests/gribigo_compliance_test/gribigo_compliance_test.go +++ b/feature/gribi/otg_tests/gribigo_compliance_test/gribigo_compliance_test.go @@ -16,8 +16,10 @@ package gribigo_compliance_test import ( + "context" "strings" "testing" + "time" "flag" @@ -27,12 +29,15 @@ import ( "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/compliance" + "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/testt" + "github.com/openconfig/ygot/ygot" ) var ( @@ -82,6 +87,14 @@ var ( } ) +type testArgs struct { + ctx context.Context + dut *ondatra.DUTDevice + ate *ondatra.ATEDevice + client *fluent.GRIBIClient + electionID gribi.Uint128 +} + func TestMain(m *testing.M) { fptest.RunTests(m) } @@ -177,6 +190,58 @@ func TestCompliance(t *testing.T) { tt.In.Fn(c, t, opts...) }) } + ctx := context.Background() + c := fluent.NewClient() + c.Connection().WithStub(gribic).WithPersistence().WithInitialElectionID(1, 0). + WithFIBACK().WithRedundancyMode(fluent.ElectedPrimaryClient) + c.Start(ctx, t) + c.StartSending(ctx, t) + eID := gribi.BecomeLeader(t, c) + tcArgs := &testArgs{ + ctx: ctx, + client: c, + dut: dut, + ate: ate, + electionID: eID, + } + testAdditionalCompliance(tcArgs, t) +} + +// testAdditionalCompliance tests has additional compliance tests that are not covered by the compliance +// test suite. +func testAdditionalCompliance(tcArgs *testArgs, t *testing.T) { + + tests := []struct { + desc string + fn func(*testArgs, fluent.ProgrammingResult, testing.TB) + }{ + { + desc: "Add IPv4 Entry with NHG which point to NH IP which doesn't resolved with in topology", + fn: addNHGReferencingToUnresolvedNH, + }, + { + desc: "Add IPv4 Entry with NHG which point to NH with Down port", + fn: addNHGReferencingToDownPort, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if err := gribi.FlushAll(tcArgs.client); err != nil { + t.Fatal(err) + } + tt.fn(tcArgs, fluent.InstalledInFIB, t) + }) + } +} + +func setDUTInterfaceWithState(t testing.TB, dut *ondatra.DUTDevice, p *ondatra.Port, state bool) { + dc := gnmi.OC() + i := &oc.Interface{} + i.Enabled = ygot.Bool(state) + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + i.Name = ygot.String(p.Name()) + gnmi.Update(t, dut, dc.Interface(p.Name()).Config(), i) + } // configureDUT configures port1-3 on the DUT. @@ -225,3 +290,70 @@ func configureATE(t *testing.T, ate *ondatra.ATEDevice) gosnappi.Config { return top } + +// addNHGReferencingToUnresolvedNH tests a case where a nexthop group references a nexthop IP 1.0.0.1 +// that does not Resolved with in the topology +func addNHGReferencingToUnresolvedNH(tcArgs *testArgs, wantACK fluent.ProgrammingResult, t testing.TB) { + t.Log("Flush existing gRIBI routes before test.") + if err := gribi.FlushAll(tcArgs.client); err != nil { + t.Fatal(err) + } + tcArgs.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(tcArgs.dut)). + WithIndex(2000).WithIPAddress("1.0.0.1"), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(tcArgs.dut)). + WithID(2000).AddNextHop(2000, 1), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(tcArgs.dut)). + WithPrefix("203.0.113.101/32").WithNextHopGroup(2000)) + + if err := awaitTimeout(tcArgs.ctx, t, tcArgs.client, 2*time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + chk.HasResult(t, tcArgs.client.Results(t), + fluent.OperationResult(). + WithNextHopGroupOperation(2000). + WithOperationType(constants.Add). + WithProgrammingResult(wantACK). + AsResult(), + chk.IgnoreOperationID()) +} + +// addNHGReferencingToDownPort tests a case where a nexthop group references to nexthop port that is down. +// Entry must be expected to be installed in FIB. +func addNHGReferencingToDownPort(tcArgs *testArgs, wantACK fluent.ProgrammingResult, t testing.TB) { + t.Log("Setting port2 to down...") + p := tcArgs.dut.Port(t, "port2") + t.Log("Flush existing gRIBI routes before test.") + if err := gribi.FlushAll(tcArgs.client); err != nil { + t.Fatal(err) + } + setDUTInterfaceWithState(t, tcArgs.dut, p, false) + + tcArgs.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(tcArgs.dut)). + WithIndex(2000).WithIPAddress(atePort2.IPv4), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(tcArgs.dut)). + WithID(2000).AddNextHop(2000, 1), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(tcArgs.dut)). + WithPrefix("203.0.113.100/32").WithNextHopGroup(2000), + ) + if err := awaitTimeout(tcArgs.ctx, t, tcArgs.client, 2*time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + chk.HasResult(t, tcArgs.client.Results(t), + fluent.OperationResult(). + WithIPv4Operation("203.0.113.100/32"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) +} + +// awaitTimeout calls a fluent client Await, adding a timeout to the context. +func awaitTimeout(ctx context.Context, t testing.TB, c *fluent.GRIBIClient, timeout time.Duration) error { + t.Helper() + subctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + return c.Await(subctx, t) +} 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 index 14e31ed3229..bde47271652 100644 --- 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 @@ -295,7 +295,7 @@ func (a *attributes) configInterfaceDUT(t *testing.T, d *ondatra.DUTDevice) { a.configSubinterfaceDUT(t, i, d) intfPath := gnmi.OC().Interface(p.Name()) - gnmi.Replace(t, d, intfPath.Config(), i) + gnmi.Update(t, d, intfPath.Config(), i) fptest.LogQuery(t, "DUT", intfPath.Config(), gnmi.Get(t, d, intfPath.Config())) } 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 index f64bcea4013..18b3f8b387b 100644 --- a/feature/gribi/otg_tests/hierarchical_weight_resolution_pbf_test/metadata.textproto +++ b/feature/gribi/otg_tests/hierarchical_weight_resolution_pbf_test/metadata.textproto @@ -31,7 +31,6 @@ platform_exceptions: { vendor: NOKIA } deviations: { - explicit_interface_ref_definition: true explicit_port_speed: true explicit_interface_in_default_vrf: true interface_enabled: true @@ -43,7 +42,6 @@ platform_exceptions: { } deviations: { omit_l2_mtu: true - deprecated_vlan_id: true interface_enabled: true default_network_instance: "default" } diff --git a/feature/gribi/otg_tests/hierarchical_weight_resolution_test/README.md b/feature/gribi/otg_tests/hierarchical_weight_resolution_test/README.md index a7b35daed86..63bfca04b3c 100644 --- a/feature/gribi/otg_tests/hierarchical_weight_resolution_test/README.md +++ b/feature/gribi/otg_tests/hierarchical_weight_resolution_test/README.md @@ -121,22 +121,24 @@ WCMP width of 16 nexthops: 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 +## OpenConfig Path and RPC Coverage +```yaml +paths: + ## State Paths ## + /network-instances/network-instance/afts/next-hop-groups/next-hop-group/next-hops/next-hop/state/weight: + +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: + gribi: + gRIBI.Get: + gRIBI.Modify: + gRIBI.Flush: +``` ## Minimum DUT platform requirement -vRX +* vRX - virtual router device diff --git a/feature/gribi/otg_tests/hierarchical_weight_resolution_test/metadata.textproto b/feature/gribi/otg_tests/hierarchical_weight_resolution_test/metadata.textproto index 63b7b4f20f6..a7f0ebfbfb0 100644 --- a/feature/gribi/otg_tests/hierarchical_weight_resolution_test/metadata.textproto +++ b/feature/gribi/otg_tests/hierarchical_weight_resolution_test/metadata.textproto @@ -29,7 +29,6 @@ platform_exceptions: { vendor: NOKIA } deviations: { - explicit_interface_ref_definition: true explicit_port_speed: true explicit_interface_in_default_vrf: true interface_enabled: true @@ -41,7 +40,6 @@ platform_exceptions: { } deviations: { omit_l2_mtu: true - deprecated_vlan_id: true interface_enabled: true default_network_instance: "default" } diff --git a/feature/gribi/otg_tests/ipv4_entry_test/README.md b/feature/gribi/otg_tests/ipv4_entry_test/README.md index 429945f5de6..2eba21c0f13 100644 --- a/feature/gribi/otg_tests/ipv4_entry_test/README.md +++ b/feature/gribi/otg_tests/ipv4_entry_test/README.md @@ -73,3 +73,16 @@ N/A * AFTResult: * id * status + +## OpenConfig Path and RPC Coverage +```yaml +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: + gribi: + gRIBI.Get: + gRIBI.Modify: + gRIBI.Flush: +``` diff --git a/feature/gribi/otg_tests/ipv4_entry_test/ipv4_entry_test.go b/feature/gribi/otg_tests/ipv4_entry_test/ipv4_entry_test.go index f9a4522b457..7b44b9d500d 100644 --- a/feature/gribi/otg_tests/ipv4_entry_test/ipv4_entry_test.go +++ b/feature/gribi/otg_tests/ipv4_entry_test/ipv4_entry_test.go @@ -78,14 +78,14 @@ var ( IPv4Len: 30, } dutPort2DummyIP = attrs.Attributes{ - Desc: "DUT Port 2", - IPv4: "192.0.2.21", - IPv4Len: 30, + Desc: "DUT Port 2", + IPv4Sec: "192.0.2.21", + IPv4LenSec: 30, } dutPort3DummyIP = attrs.Attributes{ - Desc: "DUT Port 3", - IPv4: "192.0.2.41", - IPv4Len: 30, + Desc: "DUT Port 3", + IPv4Sec: "192.0.2.41", + IPv4LenSec: 30, } atePort1 = attrs.Attributes{ diff --git a/feature/gribi/otg_tests/supervisor_failure_test/README.md b/feature/gribi/otg_tests/supervisor_failure_test/README.md index d6d404d3473..53f8bd30b2e 100644 --- a/feature/gribi/otg_tests/supervisor_failure_test/README.md +++ b/feature/gribi/otg_tests/supervisor_failure_test/README.md @@ -29,26 +29,38 @@ Ensure that gRIBI entries are persisted over supervisor failure. the prefix `203.0.113.0/24` pointing to ATE port-2 is present and traffic flows 100% from ATE port-1 to ATE port-2. -## Protocol/RPC Parameter coverage - -* gNOI: - * System - * SwitchControlProcessor - -## Config parameter coverage - -## Telemery parameter coverage - -* CHASSIS: - - * /components/component[name=]/state/last-reboot-time - * /components/component[name=]/state/last-reboot-reason - -* CONTROLLER_CARD: - - * /components/component[name=]/state/redundant-role - * /components/component[name=]/state/last-switchover-time - * /components/component[name=]/state/last-switchover-reason/trigger - * /components/component[name=]/state/last-switchover-reason/details - * /components/component[name=]/state/last-reboot-time - * /components/component[name=]/state/last-reboot-reason +## 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 Parameter coverage + + ## Telemetry Parameter coverage + + /components/component/state/last-reboot-time: + platform_type: ["CHASSIS", "CONTROLLER_CARD"] + /components/component/state/last-reboot-reason: + platform_type: ["CHASSIS", "CONTROLLER_CARD" ] + /components/component/state/redundant-role: + platform_type: [ "CONTROLLER_CARD" ] + /components/component/state/last-switchover-time: + platform_type: [ "CONTROLLER_CARD" ] + /components/component/state/last-switchover-reason/trigger: + platform_type: [ "CONTROLLER_CARD" ] + /components/component/state/last-switchover-reason/details: + platform_type: [ "CONTROLLER_CARD" ] + +rpcs: + gnmi: + gNMI.Set: + gNMI.Get: + gNMI.Subscribe: + gnoi: + system.System.SwitchControlProcessor: +``` + +## Minimum DUT Required + +vRX - Virtual Router Device diff --git a/feature/gribi/otg_tests/supervisor_failure_test/supervisor_failure_test.go b/feature/gribi/otg_tests/supervisor_failure_test/supervisor_failure_test.go index 091c7b0c78e..4dd22321943 100644 --- a/feature/gribi/otg_tests/supervisor_failure_test/supervisor_failure_test.go +++ b/feature/gribi/otg_tests/supervisor_failure_test/supervisor_failure_test.go @@ -220,9 +220,10 @@ func findSecondaryController(t *testing.T, dut *ondatra.DUTDevice, controllers [ } // validateTelemetry validates telemetry sensors -func validateTelemetry(t *testing.T, dut *ondatra.DUTDevice, primaryAfterSwitch string) { +func validateTelemetry(t *testing.T, dut *ondatra.DUTDevice, primaryAfterSwitch, secondaryAfterSwitch string) { t.Log("Validate OC Switchover time/reason.") primary := gnmi.OC().Component(primaryAfterSwitch) + secondary := gnmi.OC().Component(secondaryAfterSwitch) if !gnmi.Lookup(t, dut, primary.LastSwitchoverTime().State()).IsPresent() { t.Errorf("primary.LastSwitchoverTime().Lookup(t).IsPresent(): got false, want true") } else { @@ -244,16 +245,16 @@ func validateTelemetry(t *testing.T, dut *ondatra.DUTDevice, primaryAfterSwitch t.Errorf("primary.GetLastSwitchoverReason().GetTrigger(): got %s, want %s.", got, want) } - if !gnmi.Lookup(t, dut, primary.LastRebootTime().State()).IsPresent() { - t.Errorf("primary.LastRebootTime.().Lookup(t).IsPresent(): got false, want true") + if !gnmi.Lookup(t, dut, secondary.LastRebootTime().State()).IsPresent() { + t.Errorf("secondary.LastRebootTime.().Lookup(t).IsPresent(): got false, want true") } else { - lastrebootTime := gnmi.Get(t, dut, primary.LastRebootTime().State()) + lastrebootTime := gnmi.Get(t, dut, secondary.LastRebootTime().State()) t.Logf("Found lastRebootTime.GetDetails(): %v", lastrebootTime) } - if !gnmi.Lookup(t, dut, primary.LastRebootReason().State()).IsPresent() { - t.Errorf("primary.LastRebootReason.().Lookup(t).IsPresent(): got false, want true") + if !gnmi.Lookup(t, dut, secondary.LastRebootReason().State()).IsPresent() { + t.Errorf("secondary.LastRebootReason.().Lookup(t).IsPresent(): got false, want true") } else { - lastrebootReason := gnmi.Get(t, dut, primary.LastRebootReason().State()) + lastrebootReason := gnmi.Get(t, dut, secondary.LastRebootReason().State()) t.Logf("Found lastRebootReason.GetDetails(): %v", lastrebootReason) } } @@ -312,6 +313,7 @@ func TestSupFailure(t *testing.T) { t.Logf("Starting traffic") ate.OTG().StartTraffic(t) time.Sleep(15 * time.Second) + ate.OTG().StopTraffic(t) otgutils.LogFlowMetrics(t, ate.OTG(), top) verifyTraffic(t, args.ate) @@ -353,8 +355,8 @@ func TestSupFailure(t *testing.T) { // Old secondary controller becomes primary after switchover. primaryAfterSwitch := secondaryBeforeSwitch - - validateTelemetry(t, dut, primaryAfterSwitch) + secondaryAfterSwitch := secondaryBeforeSwitch + validateTelemetry(t, dut, primaryAfterSwitch, secondaryAfterSwitch) // Assume Controller Switchover happened, ensure traffic flows without loss. // Verify the entry for 203.0.113.0/24 is active through AFT Telemetry. // Try starting the gribi client twice as switchover may reset the connection. diff --git a/feature/interface/aggregate/otg_tests/aggregate_all_not_viable_test/README.md b/feature/interface/aggregate/otg_tests/aggregate_all_not_viable_test/README.md index e7d548bf238..5949b55c30a 100644 --- a/feature/interface/aggregate/otg_tests/aggregate_all_not_viable_test/README.md +++ b/feature/interface/aggregate/otg_tests/aggregate_all_not_viable_test/README.md @@ -1,17 +1,18 @@ -# RT-5.7 Aggregate Not Viable All +# RT-5.7: Aggregate Not Viable All [TODO: test automation/coding; issue https://github.com/openconfig/featureprofiles/issues/1655] ## Summary Test forwarding-viable with LAG and routing -Ensure that when **all LAG member** become set with forwarding-viable == FALSE. -- forwarding-viable=false impact **only** transmit traffic on all member port. -- All member ports set with forwarding-viable=false can receive all type of - traffic and forward it normally (same as with forwarding-viable=true) -- ISIS adjacency is established on LAG port w/ all member set to - forwarding-viable == FALSE -- Traffic that normally egress LAG with all members set to forwarding-viable == - FALSE is forwarded by the next best egress interface/LAG. +Ensure that when --all LAG member-- become set with forwarding-viable == FALSE + +- forwarding-viable=false impact --only-- transmit traffic on all member port +- All member ports set with forwarding-viable=false can receive all type of traffic \ + and forward it normally (same as with forwarding-viable=true) +- ISIS adjacency is established on LAG port w/ all member set to forwarding-viable \ + == FALSE +- Traffic that normally egress LAG with all members set to forwarding-viable == FALSE \ + is forwarded by the next best egress interface/LAG. ## Procedure @@ -22,117 +23,131 @@ Ensure that when **all LAG member** become set with forwarding-viable == FALSE. | | | + - - - + + - - - -| | | | | + - - - + + - - - -| .-------.| |.-------. | | +-------+-+--------+ ( pfx2 ) -( pfx1 ) | . | | p7 : ; p7 | `-------'| +( pfx1 ) | . | | p6 : ; p6 | `-------'| |`-------' | p1 ; : p1 | DUT | ' | .-------.| | |----+-+-----| | | ( pfx3 ) -| | | | | | p8 . p8 | `-------'| +| | | | | | p7 . p7 | `-------'| | | | | | +-------;-:--------+ .-------.| | | : ; | | | | | ( pfx4 ) | | ' | | | | | `-------'| | | LAG_1 | +-------+-+--------+ | -+------------+ +-------------+ p9 : ; p9 +--------------+ ++------------+ +-------------+ p8 : ; p8 +--------------+ ' LAG_3 ``` -- Connect ATE port-1 to DUT port-1, and ATE ports 2 through 7 to DUT ports 2-7, - and ATE ports 8, 9 to DUT ports 8, 9 +- Connect ATE port-1 to DUT port-1, and ATE ports 2 through 8 to DUT ports 2-8 - Configure ATE and DUT ports 1 to be LAG_1 w/ LACP running. -- Configure ATE and DUT ports 2-7 to be LAG_2 w/ LACP running. -- Configure ATE and DUT ports 8-9 to be LAG_3 w/ LACP running. +- Configure ATE and DUT ports 2-6 to be LAG_2 w/ LACP running. +- Configure ATE and DUT ports 7-8 to be LAG_3 w/ LACP running. - Establish ISIS adjacencies on LAG_1, LAG_2, LAG_3. 1. Advertise one network prefix (pfx1) from ATE LAG_1 1. Advertise one network prefix (pfx2) from ATE LAG_2 and ATE LAG_3. -- Establish iBGP between ATE and DUT over LGA_1 using LAG_1 interface IPs and advertise prefix pfx3 with BGP NH from pfx2 range. -- Programm via gRIBI route for prefix pfx4 with single NHG pointing LAG_2 (al - ports are forwarding-viable at this point). - -- For ISIS cost of LAG_2 lower then ISIS cost of LAG_3: - - Run traffic: - - From prefix pfx1 to all three: pfx2, pfx3, pfx4 - - From prefix pfx2 to: pfx1 - - Make the forwarding-viable transitions from TRUE --> FALSE on ports 3-7 - within the LAG_2 on the DUT - - ensure that only DUT port 2 of LAG ports has bidirectional traffic. - - Ensure there is no traffic transmitted out of DUT ports 3-7 - - ensure that traffic is received on all port2-7 and delivered to ATE port1 - - ensure there are no packet losses in steady state (no congestion). - - Ensure there is no traffic received on DUT LAG_3 - - Ensure there is no traffic transmitted on DUT LAG_3 - - Disable/deactive laser on ATE port2; All LAG_2 members are either down (port2) or - set with forwarding-viable=FALSE - - Ensure ISIS adjacency is UP on DUT LAG_2 and ATE LAG_2 - - Ensure there is no traffic transmitted out of DUT ports 2-7 (LAG_2) - - ensure that traffic is received on all port3-7 and delivered to ATE LAG_1 - - ensure there are no packet losses in steady state (no congestion) for - traffic from ATE LAG_2 to ATE LAG_1 (pfx_1). - - ensure there are no packet losses in steady state (no congestion) for - traffic from ATE LAG_1 to ATE LAG_3 (pfx_2, pfx3). - - Ensure there is no traffic received on DUT LAG_3 - - Ensure that traffic from ATE port1 to pfx2, pfx3 are transmitted via DUT - LAG3 - - Ensure that traffic from ATE port1 to pfx4 are discarded on DUT - - Make the forwarding-viable transitions from FALSE --> TRUE on a ports 7 - within the LAG_2 on the DUT - - ensure that only DUT port 7 of LAG ports has bidirectional traffic. - - Ensure there is no traffic transmitted out of DUT ports 2-6 - - ensure that traffic is received on all port3-7 and delivered to ATE port1 - - ensure there are no packet losses in steady state (no congestion). - - Ensure there is no traffic received on DUT LAG_3 - - Ensure there is no traffic transmitted on DUT LAG_3 - - Enable/activate laser on ATE port2; Make the forwarding-viable transitions - from FALSE --> TRUE on a ports 3-7 +- Establish iBGP between ATE and DUT over LGA using LAG interface IPs and + advertise prefix pfx3 with BGP NH from pfx2 range. +- Programm via gRIBI route for prefix pfx4 with NHG pointing LAG_2 & LAG_3(all + ports are forwarding-viable at this point) with equal weight. + +## RT-5.7.1: For ISIS cost of LAG_2 lower than ISIS cost of LAG_3: +#### Run traffic: +- From prefix pfx1 to all three: pfx2, pfx3, pfx4 +- From prefix pfx2 to: pfx1 + +#### RT-5.7.1.1: Make the forwarding-viable transitions from TRUE --> FALSE on ports 3-6 within the LAG_2 on the DUT +- Ensure that only DUT port 2 of LAG ports has bidirectional traffic. +- Ensure there is no traffic transmitted out of DUT ports 3-6 +- Ensure that traffic is received on all port2-6 and delivered to ATE port1 +- Ensure there are no packet losses in steady state (no congestion). +- Ensure there is no traffic received on DUT LAG_3 +- Ensure there is no traffic transmitted on DUT LAG_3 + +#### RT-5.7.1.2: Verify forwarding-viable behavior on an aggregate interface with all members down or set with forwarding-viable=FALSE. +- Ensure ISIS adjacency is UP on DUT LAG_2 and ATE LAG_2 +- Disable/deactive laser on ATE port2; Now all LAG_2 members are either down + (port2) or set with forwarding-viable=FALSE +- Ensure that the ISIS adjacency times out on DUT LAG_2 and ATE LAG_2 +- Ensure there is no layer3 traffic transmitted out of DUT ports 2-6 (LAG_2) +- Ensure that traffic is received on all port3-6 and delivered to ATE LAG_1 +- Ensure there are no packet losses in steady state (no congestion) for + traffic from ATE LAG_2 to ATE LAG_1 (pfx_1). +- Ensure there are no packet losses in steady state (no congestion) for + traffic from ATE LAG_1 to ATE LAG_3 (pfx_2, pfx3). +- Ensure there is no traffic received on DUT LAG_3 +- Ensure that traffic from ATE port1 to pfx2, pfx3 are transmitted via DUT LAG3 +- Ensure that traffic from ATE port1 to pfx4 are transmitted out through NH pointing to LAG3 + +#### RT-5.7.1.3: Make the forwarding-viable transitions from FALSE --> TRUE on a ports 6 within the LAG_2 on the DUT +- Ensure that only DUT port 6 of LAG ports has bidirectional traffic. +- Ensure there is no traffic transmitted out of DUT ports 2-6 +- Ensure that traffic is received on all port3-6 and delivered to ATE port1 +- Ensure there are no packet losses in steady state (no congestion). +- Ensure there is no traffic received on DUT LAG_3 +- Ensure there is no traffic transmitted on DUT LAG_3 + -- For ISIS cost of LAG_2 equall to ISIS cost of LAG_3 - - Run traffic: - - From prefix pfx1 to all three: pfx2, pfx3, pfx4 - - From prefix pfx2 to: pfx1 - - Make the forwarding-viable transitions from TRUE --> FALSE on ports 3-7 - within the LAG_2 on the DUT - - ensure that only DUT port 2 of LAG_2 and all ports of LAG_3 ports has bidirectional - traffic. The traffic split between LAG_2 and LAG_3 should be 50:50. - - Ensure there is no traffic transmitted out of DUT ports 3-7 - - ensure that traffic is received on all port2-7 and ports8-9 and delivered to ATE port1 - - ensure there are no packet losses in steady state (no congestion). - - Disable/deactive laser on ATE port2; All LAG_2 members are either down (port2) or - set with forwarding-viable=FALSE. - - Ensure ISIS adjacency is UP on DUT LAG_2 and ATE LAG_2 - - Ensure there is no traffic transmitted out of DUT ports 2-7 (LAG_2) - - ensure that traffic received on all port3-7 and ports8-9 is delivered to ATE LAG_1 - - ensure there are no packet losses in steady state (no congestion) for - traffic from ATE LAG_2, LAG_3 to ATE LAG_1 (pfx_1). - - ensure there are no packet losses in steady state (no congestion) for - traffic from ATE LAG_1 to ATE LAG_3 (pfx_2, pfx3). - - Ensure that traffic from ATE port1 to pfx2, pfx3 are transmitted via DUT - LAG3 - - Ensure that traffic from ATE port1 to pfx4 are discarded on DUT - - Make the forwarding-viable transitions from FALSE --> TRUE on a ports 7 - within the LAG_2 on the DUT - - ensure that only DUT port 7 of LAG_2 and all ports of LAG_3 ports has bidirectional traffic. - - Ensure there is no traffic transmitted out of DUT ports 2-6 - - ensure that traffic received on all port3-7 and ports8-9 is delivered to ATE port1 - - ensure there are no packet losses in steady state (no congestion). - - Enable/activate laser on ATE port2; Make the forwarding-viable transitions - from FALSE --> TRUE on a ports 3-6 - -### Deviation option - -It is foreseen that implementation may drop ISIS adjacency if all members of LAG -are set with forwarding-viable = FALSE. This scenario may be -handled via the yet to be defined deviation `logicalInterfaceUPonNonViableAll`. - -## Config Parameter coverage - -- /interfaces/interface/ethernet/config/aggregate-id -- /interfaces/interface/ethernet/config/forwarding-viable -- /interfaces/interface/aggregation/config/lag-type -- /lacp/config/system-priority -- /lacp/interfaces/interface/config/name -- /lacp/interfaces/interface/config/interval -- /lacp/interfaces/interface/config/lacp-mode -- /lacp/interfaces/interface/config/system-id-mac -- /lacp/interfaces/interface/config/system-priority +## RT-5.7.2: For ISIS cost of LAG_2 equal to ISIS cost of LAG_3 +#### Run traffic: +- From prefix pfx1 to all three: pfx2, pfx3, pfx4 +- From prefix pfx2 to: pfx1 + +#### RT-5.7.2.1: Make the forwarding-viable transitions from TRUE --> FALSE on ports 3-6 within the LAG_2 on the DUT +- Ensure that only DUT port 2 of LAG_2 and all ports of LAG_3 ports has bidirectional + traffic. +- The traffic split between LAG_2 and LAG_3 should be 50:50 +- Ensure there is no traffic transmitted out of DUT ports 3-6 +- Ensure that traffic is received on all port2-6 and ports7-8 and delivered to ATE port1 +- Ensure there are no packet losses in steady state (no congestion) + +#### RT-5.7.2.2: Verify forwarding-viable behavior on an aggregate interface with all members down or set with forwarding-viable=FALSE. +- Ensure ISIS adjacency is UP on DUT LAG_2 and ATE LAG_2 +- Disable/deactive laser on ATE port2; Now all LAG_2 members are either down (port2) or + set with forwarding-viable=FALSE +- Ensure that the ISIS adjacency times out on DUT LAG_2 and ATE LAG_2 +- Ensure there is no traffic transmitted out of DUT ports 2-6 (LAG_2) +- Ensure that traffic received on all port3-6 and ports7-8 is delivered to ATE LAG_1 +- Ensure there are no packet losses in steady state (no congestion) for + traffic from ATE LAG_2, LAG_3 to ATE LAG_1 (pfx_1). +- Ensure there are no packet losses in steady state (no congestion) for + traffic from ATE LAG_1 to ATE LAG_3 (pfx_2, pfx3). +- Ensure that traffic from ATE port1 to pfx2, pfx3 are transmitted via DUT LAG3 +- Ensure that traffic from ATE port1 to pfx4 are transmitted out through NH pointing to LAG3 + +#### RT-5.7.2.3: Make the forwarding-viable transitions from FALSE --> TRUE on a ports 6 within the LAG_2 on the DUT +- Ensure that only DUT port 6 of LAG_2 and all ports of LAG_3 ports has bidirectional traffic. +- Ensure there is no traffic transmitted out of DUT ports 2-6 +- Ensure that traffic received on all port3-6 and ports7-8 is delivered to ATE port1 +- Ensure there are no packet losses in steady state (no congestion). + +## OpenConfig Path and RPC Coverage + +The below yaml defines the OC paths and RPC intended to be covered by this test. + +```yaml +paths: + /interfaces/interface/ethernet/config/aggregate-id: + ## Config forwarding Viable to True/false + /interfaces/interface/config/forwarding-viable: + ## Define Lag type + /interfaces/interface/aggregation/config/lag-type: + ## Configure LACP + /lacp/config/system-priority: + /lacp/interfaces/interface/config/name: + /lacp/interfaces/interface/config/lacp-mode: + /lacp/interfaces/interface/config/interval: + /lacp/interfaces/interface/config/system-id-mac: + /lacp/interfaces/interface/config/system-priority: + + +rpcs: + gnmi: + gNMI.Subscribe: + ON_CHANGE: true + + gnoi: + system.System.Reboot: + +``` ## Telemetry Parameter coverage diff --git a/feature/interface/aggregate/otg_tests/aggregate_all_not_viable_test/aggregate_all_not_forwarding_viable_test.go b/feature/interface/aggregate/otg_tests/aggregate_all_not_viable_test/aggregate_all_not_forwarding_viable_test.go new file mode 100644 index 00000000000..48cacf68dd8 --- /dev/null +++ b/feature/interface/aggregate/otg_tests/aggregate_all_not_viable_test/aggregate_all_not_forwarding_viable_test.go @@ -0,0 +1,1050 @@ +// 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. + +// For LACP. Make the following forwarding-viable transitions on a port within the LAG on the DUT. +// 1. Tag the forwarding-viable=true to allow all the ports to pass traffic in +// port-channel. +// 2. Transition from forwarding-viable=true to forwarding-viable=false. +// For each condition above, ensure following two things: +// - traffic is load-balanced across the remaining interfaces in the LAG. +// - there is no packet tx on port with forwarding-viable=false. +// - there is packet rx on the port and process it to destination with forwarding-viable=false. + +// What is forwarding viable ? +// If set to false, the interface is not used for forwarding traffic, +// but as long as it is up, the interface still maintains its layer-2 adjacencies and runs its configured layer-2 functions (e.g. LLDP, etc.). + +package aggregate_all_not_forwarding_viable_test + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "net" + "strconv" + "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/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/ondatra/gnmi/oc/ocpath" + "github.com/openconfig/ondatra/netutil" + "github.com/openconfig/ygnmi/ygnmi" + "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 = 2500000 + srcTrafficV4 = "100.0.1.1" + srcTrafficV6 = "2002:db8:64:64::1" + dstTrafficV4 = "100.0.2.1" + dstTrafficV6 = "2003:db8:64:64::1" + v4Count = 254 + v6Count = 100000000 + lagTypeLACP = oc.IfAggregate_AggregationType_LACP + ieee8023adLag = oc.IETFInterfaces_InterfaceType_ieee8023adLag + ethernetCsmacd = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + LAG1 = "lag1" + LAG2 = "lag2" + LAG3 = "lag3" +) + +type aggPortData struct { + dutIPv4 string + ateIPv4 string + dutIPv6 string + ateIPv6 string + ateAggName string + ateAggMAC string + ateISISSysID string + ateLagCount uint32 +} + +type ipAddr struct { + ip string + prefix uint32 +} + +// testArgs holds the objects needed by a test case. +type testArgs struct { + dut *ondatra.DUTDevice + ate *ondatra.ATEDevice + top gosnappi.Config + ctx context.Context + client *fluent.GRIBIClient +} + +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", + ateISISSysID: "640000000002", + ateLagCount: 1, + } + 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:02:01", + ateISISSysID: "640000000003", + ateLagCount: 2, + } + agg3 = &aggPortData{ + dutIPv4: "192.0.2.9", + ateIPv4: "192.0.2.10", + dutIPv6: "2001:db8::9", + ateIPv6: "2001:db8::a", + ateAggName: LAG3, + ateAggMAC: "02:00:01:01:03:01", + ateISISSysID: "640000000004", + ateLagCount: 1, + } + + dutLoopback = attrs.Attributes{ + Desc: "Loopback ip", + IPv4: "192.0.2.21", + IPv6: "2001:db8::21", + IPv4Len: 32, + IPv6Len: 128, + } + + pfx1AdvV4 = &ipAddr{ip: "100.0.1.0", prefix: 24} + pfx1AdvV6 = &ipAddr{ip: "2002:db8:64:64::0", prefix: 64} + pfx2AdvV4 = &ipAddr{ip: "100.0.2.0", prefix: 24} + pfx2AdvV6 = &ipAddr{ip: "2003:db8:64:64::0", prefix: 64} + pfx3AdvV4 = &ipAddr{ip: "100.0.3.0", prefix: 24} + pfx4AdvV4 = &ipAddr{ip: "100.0.4.0", prefix: 24} + pmd100GFRPorts []string + dutPortList []*ondatra.Port + atePortList []*ondatra.Port + rxPktsBeforeTraffic map[*ondatra.Port]uint64 + txPktsBeforeTraffic map[*ondatra.Port]uint64 + equalDistributionWeights = []uint64{50, 50} + ecmpTolerance = uint64(1) + ipRange = []uint32{254, 500} + + dutAggMac []string +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +// TestAggregateAllNotForwardingViable Test forwarding-viable with LAG and routing +func TestAggregateAllNotForwardingViable(t *testing.T) { + + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + + aggIDs := configureDUT(t, dut) + changeMetric(t, dut, aggIDs[2], 30) + top := configureATE(t, ate) + installGRIBIRoutes(t, dut, ate, top) + ate.OTG().PushConfig(t, top) + ate.OTG().StartProtocols(t) + for _, aggID := range aggIDs { + gnmi.Await(t, dut, gnmi.OC().Interface(aggID).OperStatus().State(), 60*time.Second, oc.Interface_OperStatus_UP) + } + + flows := createFlows(t, ate, top) + ate.OTG().PushConfig(t, top) + ate.OTG().StartProtocols(t) + for _, agg := range []*aggPortData{agg1, agg2, agg3} { + bgpPath := ocpath.Root().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + gnmi.Await(t, dut, bgpPath.Neighbor(agg.ateIPv4).SessionState().State(), time.Minute, oc.Bgp_Neighbor_SessionState_ESTABLISHED) + } + + t.Logf("ISIS cost of LAG_2 lower then ISIS cost of LAG_3 Test-01") + t.Run("RT-5.7.1.1: Setting Forwarding-Viable to False on Lag2 all ports except port 2", func(t *testing.T) { + configForwardingViable(t, dut, dutPortList[2:agg2.ateLagCount+1], false) + startTraffic(t, dut, ate, top) + if err := checkBidirectionalTraffic(t, dut, dutPortList[1:2]); err != nil { + t.Fatal(err) + } + if err := confirmNonViableForwardingTraffic(t, dut, ate, atePortList[2:agg2.ateLagCount+1], dutPortList[2:agg2.ateLagCount+1]); err != nil { + t.Fatal(err) + } + // Ensure there is no traffic received/transmiited on DUT LAG_3 + if got := validateLag3Traffic(t, dut, ate, dutPortList[(agg2.ateLagCount+1):]); got == true { + t.Fatal("Packets are Received and Transmitted on LAG_3") + } + if ok := verifyTrafficFlow(t, ate, flows, false); !ok { + t.Fatal("Packet Dropped, LossPct for flow ") + } + }) + t.Run("RT-5.7.1.2: Setting Forwarding-Viable to False for Lag2 all ports", func(t *testing.T) { + // Ensure ISIS Adjacency is up on LAG_2 + if ok := awaitAdjacency(t, dut, aggIDs[1], oc.Isis_IsisInterfaceAdjState_UP); !ok { + t.Fatal("ISIS Adjacency is Down on LAG_2") + } + configForwardingViable(t, dut, dutPortList[1:2], false) + // Ensure ISIS Adjacency is Down on LAG_2 + + if ok := awaitAdjacency(t, dut, aggIDs[1], oc.Isis_IsisInterfaceAdjState_DOWN); !ok { + if presence := gnmi.LookupAll(t, dut, ocpath.Root().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance).Isis().Interface(aggIDs[1]).LevelAny().AdjacencyAny().AdjacencyState().State()); len(presence) > 0 { + t.Fatalf("ISIS Adjacency is Established on LAG_2 ") + } + } + startTraffic(t, dut, ate, top) + if err := confirmNonViableForwardingTraffic(t, dut, ate, atePortList[1:agg2.ateLagCount+1], dutPortList[1:agg2.ateLagCount+1]); err != nil { + t.Fatal(err) + } + // Ensure that traffic from ATE port1 to pfx4 transmitted out using LAG3 + if ok := verifyTrafficFlow(t, ate, flows[1:2], true); !ok { + t.Fatal("Packet Dropped, LossPct for flow ", flows[1].Name()) + } + // Ensure there is no traffic received on DUT LAG_3 + if got := validateLag3Traffic(t, dut, ate, dutPortList[(agg2.ateLagCount+1):]); got == true { + t.Fatal("Packets are Received on DUT LAG_3") + } + if ok := verifyTrafficFlow(t, ate, flows[0:1], true); !ok { + t.Fatal("Packet Dropped, LossPct for flow ", flows[0].Name()) + } + }) + + t.Run("RT-5.7.1.3: Setting Forwarding-Viable to True for Lag2 one of the port", func(t *testing.T) { + configForwardingViable(t, dut, dutPortList[agg2.ateLagCount:agg2.ateLagCount+1], true) + startTraffic(t, dut, ate, top) + if err := checkBidirectionalTraffic(t, dut, dutPortList[agg2.ateLagCount:agg2.ateLagCount+1]); err != nil { + t.Fatal(err) + } + if err := confirmNonViableForwardingTraffic(t, dut, ate, atePortList[1:agg2.ateLagCount], dutPortList[1:agg2.ateLagCount]); err != nil { + t.Fatal(err) + } + // Ensure there is no traffic received/transmiited on DUT LAG_3 + if got := validateLag3Traffic(t, dut, ate, dutPortList[(agg2.ateLagCount+1):]); got == true { + t.Fatal("Packets are Received and Transmitted on LAG_3") + } + if ok := verifyTrafficFlow(t, ate, flows, false); !ok { + t.Fatal("Packet Dropped, LossPct for flow ") + } + }) + // Reset Forwarding-Viable to True for all the ports of LAG_2 + configForwardingViable(t, dut, dutPortList[1:6], true) + // Change ISIS metric Equal for Both LAG_2 and LAG_3 + changeMetric(t, dut, aggIDs[2], 20) + + t.Logf("ISIS cost of LAG_2 equal to ISIS cost of LAG_3 Test-02") + t.Run("RT-5.7.2.1: Setting Forwarding-Viable to False for Lag2 ports except port 2", func(t *testing.T) { + configForwardingViable(t, dut, dutPortList[2:agg2.ateLagCount+1], false) + flows = append(flows, configureFlows(t, top, pfx2AdvV4, pfx1AdvV4, "pfx2ToPfx1Lag3", agg3, []*aggPortData{agg1}, dutAggMac[2], ipRange[0])) + ate.OTG().PushConfig(t, top) + ate.OTG().StartProtocols(t) + startTraffic(t, dut, ate, top) + if err := checkBidirectionalTraffic(t, dut, dutPortList[1:2]); err != nil { + t.Fatal(err) + } + if err := checkBidirectionalTraffic(t, dut, dutPortList[(agg2.ateLagCount+1):]); err != nil { + t.Fatal(err) + } + if err := confirmNonViableForwardingTraffic(t, dut, ate, atePortList[2:(agg2.ateLagCount+1)], dutPortList[2:(agg2.ateLagCount+1)]); err != nil { + t.Fatal(err) + } + // Ensure Load Balancing 50:50 on LAG_2 and LAG_3 for prefix's pfx2, pfx3 and pfx4 + weights := trafficRXWeights(t, ate, []string{agg2.ateAggName, agg3.ateAggName}, flows[0]) + 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) + } + } + if ok := verifyTrafficFlow(t, ate, flows, false); !ok { + t.Fatal("Packet Dropped, LossPct for flow ") + } + }) + + t.Run("RT-5.7.2.2: Setting Forwarding-Viable to False for Lag2 all ports", func(t *testing.T) { + // Ensure ISIS Adjacency is up on LAG_2 + if ok := awaitAdjacency(t, dut, aggIDs[1], oc.Isis_IsisInterfaceAdjState_UP); !ok { + t.Fatal("ISIS Adjacency is Down on LAG_2") + } + configForwardingViable(t, dut, dutPortList[1:2], false) + // Ensure ISIS Adjacency is Down on LAG_2 + if ok := awaitAdjacency(t, dut, aggIDs[1], oc.Isis_IsisInterfaceAdjState_DOWN); !ok { + if presence := gnmi.LookupAll(t, dut, ocpath.Root().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance).Isis().Interface(aggIDs[1]).LevelAny().AdjacencyAny().AdjacencyState().State()); len(presence) > 0 { + t.Fatalf("ISIS Adjacency is Established on LAG_2") + } + } + startTraffic(t, dut, ate, top) + if err := confirmNonViableForwardingTraffic(t, dut, ate, atePortList[1:(agg2.ateLagCount+1)], dutPortList[1:(agg2.ateLagCount+1)]); err != nil { + t.Fatal(err) + } + // Ensure that traffic from ATE port1 to pfx4 are discarded on DUT + if ok := verifyTrafficFlow(t, ate, flows[1:2], true); !ok { + t.Fatal("Packet Dropped, LossPct for flow ", flows[1].Name()) + } + // Ensure there is traffic received on DUT LAG_3 + if got := validateLag3Traffic(t, dut, ate, dutPortList[(agg2.ateLagCount+1):]); got == false { + t.Fatal("Packets are not Received on LAG_3") + } + if ok := verifyTrafficFlow(t, ate, flows[0:1], true); !ok { + t.Fatal("Packet Dropped, LossPct for flow ", flows[0].Name()) + } + }) + + t.Run("RT-5.7.2.3: Setting Forwarding-Viable to True for Lag2 one of the port", func(t *testing.T) { + configForwardingViable(t, dut, dutPortList[agg2.ateLagCount:(agg2.ateLagCount+1)], true) + startTraffic(t, dut, ate, top) + if err := checkBidirectionalTraffic(t, dut, dutPortList[agg2.ateLagCount:]); err != nil { + t.Fatal(err) + } + if err := confirmNonViableForwardingTraffic(t, dut, ate, atePortList[1:agg2.ateLagCount], dutPortList[1:agg2.ateLagCount]); err != nil { + t.Fatal(err) + } + if ok := verifyTrafficFlow(t, ate, flows, false); !ok { + t.Fatal("Packet Dropped, LossPct for flow ") + } + }) +} + +// configureDUT configures DUT +func configureDUT(t *testing.T, dut *ondatra.DUTDevice) []string { + + t.Helper() + fptest.ConfigureDefaultNetworkInstance(t, dut) + if len(dut.Ports()) < 4 { + t.Fatalf("Testbed requires at least 4 ports, got %d", len(dut.Ports())) + } + if len(dut.Ports()) > 4 { + agg2.ateLagCount = uint32(len(dut.Ports()) - 3) + agg3.ateLagCount = 2 + } + var aggIDs []string + for _, a := range []*aggPortData{agg1, agg2, agg3} { + d := gnmi.OC() + aggID := netutil.NextAggregateInterface(t, dut) + aggIDs = append(aggIDs, aggID) + portList := initializePort(t, dut, a) + + if deviations.AggregateAtomicUpdate(dut) { + clearAggregate(t, dut, aggID, a, portList) + setupAggregateAtomically(t, dut, aggID, a, portList) + } + lacp := &oc.Lacp_Interface{Name: ygot.String(aggID)} + lacp.LacpMode = oc.Lacp_LacpActivityType_ACTIVE + lacpPath := d.Lacp().Interface(aggID) + fptest.LogQuery(t, "LACP", lacpPath.Config(), lacp) + gnmi.Replace(t, dut, lacpPath.Config(), lacp) + + aggInt := &oc.Interface{Name: ygot.String(aggID)} + configAggregateDUT(dut, aggInt, a) + + aggPath := d.Interface(aggID) + fptest.LogQuery(t, aggID, aggPath.Config(), aggInt) + gnmi.Replace(t, dut, aggPath.Config(), aggInt) + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + fptest.AssignToNetworkInstance(t, dut, aggID, deviations.DefaultNetworkInstance(dut), 0) + } + for _, port := range portList { + i := &oc.Interface{Name: ygot.String(port.Name())} + i.Type = ethernetCsmacd + e := i.GetOrCreateEthernet() + e.AggregateId = ygot.String(aggID) + 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 + } + + configMemberDUT(dut, i, port, aggID) + iPath := d.Interface(port.Name()) + fptest.LogQuery(t, port.String(), iPath.Config(), i) + gnmi.Replace(t, dut, iPath.Config(), i) + } + + if deviations.ExplicitPortSpeed(dut) { + for _, dp := range portList { + fptest.SetPortSpeed(t, dp) + } + } + } + + configureRoutingPolicy(t, dut) + configureDUTISIS(t, dut, aggIDs) + + if !deviations.MaxEcmpPaths(dut) { + isisPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance).Isis() + gnmi.Update(t, dut, isisPath.Global().MaxEcmpPaths().Config(), 2) + } + configureDUTBGP(t, dut, aggIDs) + return aggIDs +} + +// configDstMemberDUT enables destination ports, add other details like description, +// port and aggregate ID. +func configMemberDUT(dut *ondatra.DUTDevice, i *oc.Interface, p *ondatra.Port, aggID string) { + i.Description = ygot.String(p.String()) + i.Type = ethernetCsmacd + + if deviations.InterfaceEnabled(dut) { + i.Enabled = ygot.Bool(true) + } + e := i.GetOrCreateEthernet() + e.AggregateId = ygot.String(aggID) +} + +// initializePort initializes ports for aggregate on DUT +func initializePort(t *testing.T, dut *ondatra.DUTDevice, a *aggPortData) []*ondatra.Port { + var portList []*ondatra.Port + var portIdx uint32 + switch a.ateAggName { + case LAG1: + portList = append(portList, dut.Port(t, fmt.Sprintf("port%d", portIdx+1))) + dutPortList = append(dutPortList, dut.Port(t, fmt.Sprintf("port%d", portIdx+1))) + case LAG2: + for portIdx < a.ateLagCount { + portList = append(portList, dut.Port(t, fmt.Sprintf("port%d", portIdx+2))) + dutPortList = append(dutPortList, dut.Port(t, fmt.Sprintf("port%d", portIdx+2))) + portIdx++ + } + case LAG3: + for portIdx < a.ateLagCount { + portList = append(portList, dut.Port(t, fmt.Sprintf("port%d", portIdx+agg2.ateLagCount+2))) + dutPortList = append(dutPortList, dut.Port(t, fmt.Sprintf("port%d", portIdx+agg2.ateLagCount+2))) + portIdx++ + } + } + return portList +} + +// configDstAggregateDUT configures port-channel destination ports +func configAggregateDUT(dut *ondatra.DUTDevice, i *oc.Interface, a *aggPortData) { + i.Description = ygot.String(a.ateAggName) + if deviations.InterfaceEnabled(dut) { + i.Enabled = ygot.Bool(true) + } + s := i.GetOrCreateSubinterface(0) + s4 := s.GetOrCreateIpv4() + if deviations.InterfaceEnabled(dut) { + s4.Enabled = ygot.Bool(true) + } + a4 := s4.GetOrCreateAddress(a.dutIPv4) + a4.PrefixLength = ygot.Uint8(ipv4PLen) + + // n4 := s4.GetOrCreateNeighbor(a.ateIPv4) + // n4.LinkLayerAddress = ygot.String(a.ateAggMAC) + + s6 := s.GetOrCreateIpv6() + if deviations.InterfaceEnabled(dut) { + s6.Enabled = ygot.Bool(true) + } + s6.GetOrCreateAddress(a.dutIPv6).PrefixLength = ygot.Uint8(ipv6PLen) + i.Type = ieee8023adLag + g := i.GetOrCreateAggregation() + g.LagType = lagTypeLACP +} + +// setupAggregateAtomically setup port-channel based on LAG type. +func setupAggregateAtomically(t *testing.T, dut *ondatra.DUTDevice, aggID string, agg *aggPortData, portList []*ondatra.Port) { + d := &oc.Root{} + d.GetOrCreateLacp().GetOrCreateInterface(aggID) + + aggr := d.GetOrCreateInterface(aggID) + aggr.GetOrCreateAggregation().LagType = oc.IfAggregate_AggregationType_LACP + aggr.Type = oc.IETFInterfaces_InterfaceType_ieee8023adLag + + for _, port := range portList { + 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) + } + } + p := gnmi.OC() + fptest.LogQuery(t, fmt.Sprintf("%s to Update()", dut), p.Config(), d) + gnmi.Update(t, dut, p.Config(), d) +} + +// clearAggregate delete any previously existing members of aggregate. +func clearAggregate(t *testing.T, dut *ondatra.DUTDevice, aggID string, agg *aggPortData, portList []*ondatra.Port) { + // Clear the aggregate minlink. + gnmi.Delete(t, dut, gnmi.OC().Interface(aggID).Aggregation().MinLinks().Config()) + // Clear the members of the aggregate. + for _, port := range portList { + resetBatch := &gnmi.SetBatch{} + gnmi.BatchDelete(resetBatch, gnmi.OC().Interface(port.Name()).Ethernet().AggregateId().Config()) + gnmi.BatchDelete(resetBatch, gnmi.OC().Interface(port.Name()).ForwardingViable().Config()) + resetBatch.Set(t, dut) + } +} + +// configureDUTBGP configure BGP on DUT +func configureDUTBGP(t *testing.T, dut *ondatra.DUTDevice, aggIDs []string) { + 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} { + bgpNbrV4 := bgp.GetOrCreateNeighbor(a.ateIPv4) + bgpNbrV4.PeerGroup = ygot.String(pgName) + bgpNbrV4.PeerAs = ygot.Uint32(asn) + bgpNbrV4.Enabled = ygot.Bool(true) + bgpNbrV4T := bgpNbrV4.GetOrCreateTransport() + + bgpNbrV4T.LocalAddress = ygot.String(a.dutIPv4) + 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.ateIPv6) + bgpNbrV6.PeerGroup = ygot.String(pgName) + bgpNbrV6.PeerAs = ygot.Uint32(asn) + bgpNbrV6.Enabled = ygot.Bool(true) + bgpNbrV6T := bgpNbrV6.GetOrCreateTransport() + + bgpNbrV6T.LocalAddress = ygot.String(a.dutIPv6) + 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) +} + +// configureRoutingPolicy configure routing policy on DUT +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) +} + +// configureDUTISIS configure ISIS on 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.Enabled = ygot.Bool(true) + isisIntfLevelAfiv6 := isisIntfLevel.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV6, oc.IsisTypes_SAFI_TYPE_UNICAST) + + isisIntfLevelAfiv4.Metric = ygot.Uint32(20) + isisIntfLevelAfiv6.Metric = ygot.Uint32(20) + + isisIntfLevelAfiv6.Enabled = ygot.Bool(true) + if deviations.MissingIsisInterfaceAfiSafiEnable(dut) { + isisIntfLevelAfiv4.Enabled = nil + isisIntfLevelAfiv6.Enabled = nil + } + } + gnmi.Update(t, dut, gnmi.OC().Config(), d) + +} + +// changeMetric change metric for ISIS on Interface +func changeMetric(t *testing.T, dut *ondatra.DUTDevice, intf string, metric uint32) { + t.Logf("Updating ISIS metric of LAG2 equal to LAG3 ") + d := &oc.Root{} + netInstance := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + isis := netInstance.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance).GetOrCreateIsis() + isisIntfLevel := isis.GetOrCreateInterface(intf).GetOrCreateLevel(2) + isisIntfLevelAfiv4 := isisIntfLevel.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST) + isisIntfLevelAfiv4.Metric = ygot.Uint32(metric) + isisIntfLevelAfiv6 := isisIntfLevel.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV6, oc.IsisTypes_SAFI_TYPE_UNICAST) + isisIntfLevelAfiv6.Metric = ygot.Uint32(metric) + + if deviations.ISISRequireSameL1MetricWithL2Metric(dut) { + l1 := isis.GetOrCreateInterface(intf).GetOrCreateLevel(1) + l1V4 := l1.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST) + l1V4.Metric = ygot.Uint32(metric) + l1V6 := l1.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV6, oc.IsisTypes_SAFI_TYPE_UNICAST) + l1V6.Metric = ygot.Uint32(metric) + } + gnmi.Update(t, dut, gnmi.OC().Config(), d) +} + +// configureATE configure ATE +func configureATE(t *testing.T, ate *ondatra.ATEDevice) gosnappi.Config { + t.Helper() + top := gosnappi.NewConfig() + + for _, a := range []*aggPortData{agg1, agg2, agg3} { + var portList []*ondatra.Port + var portIdx uint32 + switch a.ateAggName { + case LAG1: + portList = append(portList, ate.Port(t, fmt.Sprintf("port%d", portIdx+1))) + atePortList = append(atePortList, ate.Port(t, fmt.Sprintf("port%d", portIdx+1))) + case LAG2: + for portIdx < a.ateLagCount { + portList = append(portList, ate.Port(t, fmt.Sprintf("port%d", portIdx+2))) + atePortList = append(atePortList, ate.Port(t, fmt.Sprintf("port%d", portIdx+2))) + portIdx++ + } + case LAG3: + for portIdx < a.ateLagCount { + portList = append(portList, ate.Port(t, fmt.Sprintf("port%d", portIdx+agg2.ateLagCount+2))) + atePortList = append(atePortList, ate.Port(t, fmt.Sprintf("port%d", portIdx+agg2.ateLagCount+2))) + portIdx++ + } + } + configureOTGPorts(t, ate, top, portList, a) + } + // 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 +} + +// configureOTGPorts define ATE ports +func configureOTGPorts(t *testing.T, ate *ondatra.ATEDevice, top gosnappi.Config, portList []*ondatra.Port, a *aggPortData) []string { + agg := top.Lags().Add().SetName(a.ateAggName) + agg.Protocol().Lacp().SetActorKey(1).SetActorSystemPriority(1).SetActorSystemId(a.ateAggMAC) + 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) + for aggIdx, pList := range portList { + top.Ports().Add().SetName(pList.ID()) + if pList.PMD() == ondatra.PMD100GBASEFR { + pmd100GFRPorts = append(pmd100GFRPorts, pList.ID()) + } + newMac, err := incrementMAC(a.ateAggMAC, aggIdx+1) + if err != nil { + t.Fatal(err) + } + lagPort := agg.Ports().Add().SetPortName(pList.ID()) + lagPort.Ethernet().SetMac(newMac).SetName(a.ateAggName + "." + strconv.Itoa(aggIdx)) + lagPort.Lacp().SetActorActivity("active").SetActorPortNumber(uint32(aggIdx) + 1).SetActorPortPriority(1).SetLacpduTimeout(0) + } + + if a.ateAggName == LAG1 { + configureOTGISIS(t, lagDev, a, pfx1AdvV4) + configureOTGBGP(t, lagDev, a, pfx1AdvV4, pfx1AdvV6) + } else { + configureOTGISIS(t, lagDev, a, pfx2AdvV4) + configureOTGBGP(t, lagDev, a, pfx2AdvV4, pfx2AdvV6) + } + return pmd100GFRPorts +} + +// configureOTGISIS configure ISIS on ATE +func configureOTGISIS(t *testing.T, dev gosnappi.Device, agg *aggPortData, advV4 *ipAddr) { + 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() + + isisInt = isisInt.SetEthName(dev.Ethernets(). + Items()[0].Name()).SetName(agg.ateAggName + ".ISISInt"). + SetNetworkType(gosnappi.IsisInterfaceNetworkType.POINT_TO_POINT). + SetLevelType(gosnappi.IsisInterfaceLevelType.LEVEL_2).SetMetric(20) + isisInt.Advanced().SetAutoAdjustMtu(true).SetAutoAdjustArea(true).SetAutoAdjustSupportedProtocols(true) + + devIsisRoutes4 := isis.V4Routes().Add().SetName(agg.ateAggName + ".isisnet4").SetLinkMetric(10) + devIsisRoutes4.Addresses().Add(). + SetAddress(advV4.ip).SetPrefix(advV4.prefix).SetCount(1).SetStep(1) + +} + +// configureOTGBGP configure BGP on ATE +func configureOTGBGP(t *testing.T, dev gosnappi.Device, agg *aggPortData, advV4, advV6 *ipAddr) { + t.Helper() + + iDutBgp := dev.Bgp().SetRouterId(agg.ateIPv4) + iDutBgp4Peer := iDutBgp.Ipv4Interfaces().Add().SetIpv4Name(agg.ateAggName + ".IPv4").Peers().Add().SetName(agg.ateAggName + ".BGP4.peer") + iDutBgp4Peer.SetPeerAddress(agg.dutIPv4).SetAsNumber(asn).SetAsType(gosnappi.BgpV4PeerAsType.IBGP) + iDutBgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(false) + + iDutBgp6Peer := iDutBgp.Ipv6Interfaces().Add().SetIpv6Name(agg.ateAggName + ".IPv6").Peers().Add().SetName(agg.ateAggName + ".BGP6.peer") + iDutBgp6Peer.SetPeerAddress(agg.dutIPv6).SetAsNumber(asn).SetAsType(gosnappi.BgpV6PeerAsType.IBGP) + iDutBgp6Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(false).SetUnicastIpv6Prefix(true) + + bgpNeti1Bgp4PeerRoutes := iDutBgp4Peer.V4Routes().Add().SetName(agg.ateAggName + ".BGP4.Route") + if agg.ateAggName != LAG1 { + bgpNeti1Bgp4PeerRoutes.SetNextHopIpv4Address(pfx2AdvV4.ip + "1"). + SetNextHopAddressType(gosnappi.BgpV4RouteRangeNextHopAddressType.IPV4). + SetNextHopMode(gosnappi.BgpV4RouteRangeNextHopMode.MANUAL) + bgpNeti1Bgp4PeerRoutes.Addresses().Add().SetAddress(pfx3AdvV4.ip).SetPrefix(pfx3AdvV4.prefix).SetCount(1) + } else { + bgpNeti1Bgp4PeerRoutes.SetNextHopIpv4Address(agg.ateIPv4). + SetNextHopAddressType(gosnappi.BgpV4RouteRangeNextHopAddressType.IPV4). + SetNextHopMode(gosnappi.BgpV4RouteRangeNextHopMode.MANUAL) + } +} + +// configForwardingViable is to set forwarding viable on DUT ports +func configForwardingViable(t *testing.T, dut *ondatra.DUTDevice, dutPorts []*ondatra.Port, forwardingViable bool) { + for _, port := range dutPorts { + if forwardingViable { + gnmi.Update(t, dut, gnmi.OC().Interface(port.Name()).ForwardingViable().Config(), forwardingViable) + } else { + gnmi.Update(t, dut, gnmi.OC().Interface(port.Name()).ForwardingViable().Config(), forwardingViable) + } + } +} + +// incrementMAC uses a mac string and increments it by the given i +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 +} + +func createFlows(t *testing.T, ate *ondatra.ATEDevice, top gosnappi.Config) []gosnappi.Flow { + for _, aggID := range []*aggPortData{agg1, agg2, agg3} { + dutAggMac = append(dutAggMac, gnmi.Get(t, ate.OTG(), gnmi.OTG().Interface(aggID.ateAggName+".Eth").Ipv4Neighbor(aggID.dutIPv4).LinkLayerAddress().State())) + } + f1V4 := configureFlows(t, top, pfx1AdvV4, pfx2AdvV4, "pfx1ToPfx2_3", agg1, []*aggPortData{agg2, agg3}, dutAggMac[0], ipRange[1]) + f2V4 := configureFlows(t, top, pfx1AdvV4, pfx4AdvV4, "pfx1ToPfx4", agg1, []*aggPortData{agg2, agg3}, dutAggMac[0], ipRange[0]) + f3V4 := configureFlows(t, top, pfx2AdvV4, pfx1AdvV4, "pfx2ToPfx1Lag2", agg2, []*aggPortData{agg1}, dutAggMac[1], ipRange[0]) + return []gosnappi.Flow{f1V4, f2V4, f3V4} +} + +// configureFlows configure flows for traffic on ATE +func configureFlows(t *testing.T, top gosnappi.Config, srcV4 *ipAddr, dstV4 *ipAddr, flowName string, srcAgg *aggPortData, + dstAgg []*aggPortData, dutAggMac string, ipRange uint32) gosnappi.Flow { + + t.Helper() + flowV4 := top.Flows().Add().SetName(flowName) + flowV4.Metrics().SetEnable(true) + flowV4.TxRx().Port(). + SetTxName(srcAgg.ateAggName) + + if flowName == "pfx2ToPfx1Lag2" || flowName == "pfx2ToPfx1Lag3" { + flowV4.TxRx().Port(). + SetRxNames([]string{dstAgg[0].ateAggName}) + } else { + flowV4.TxRx().Port(). + SetRxNames([]string{dstAgg[0].ateAggName, dstAgg[1].ateAggName}) + } + flowV4.Size().SetFixed(1500) + flowV4.Rate().SetPps(trafficPPS) + eV4 := flowV4.Packet().Add().Ethernet() + eV4.Src().SetValue(srcAgg.ateAggMAC) + eV4.Dst().SetValue(dutAggMac) + v4 := flowV4.Packet().Add().Ipv4() + v4.Src().Increment().SetStart(srcV4.ip).SetCount(v4Count) + v4.Dst().Increment().SetStart(dstV4.ip).SetCount(ipRange) + return flowV4 +} + +// installGRIBIRoutes configure route using gRIBI client +func installGRIBIRoutes(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, top gosnappi.Config) { + t.Helper() + ctx := context.Background() + gribic := dut.RawAPIs().GRIBI(t) + client := fluent.NewClient() + client.Connection().WithStub(gribic).WithPersistence().WithInitialElectionID(12, 0). + WithRedundancyMode(fluent.ElectedPrimaryClient).WithFIBACK() + client.Start(ctx, t) + defer client.Stop(t) + gribi.FlushAll(client) + client.StartSending(ctx, t) + gribi.BecomeLeader(t, client) + + tcArgs := &testArgs{ + ctx: ctx, + client: client, + dut: dut, + ate: ate, + top: top, + } + + t.Logf("An IPv4Entry for %s is pointing to ATE LAG2 via gRIBI", pfx4AdvV4.ip+"/24") + + tcArgs.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(tcArgs.dut)). + WithIndex(uint64(100)).WithIPAddress(agg2.ateIPv4), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(tcArgs.dut)). + WithIndex(uint64(101)).WithIPAddress(agg3.ateIPv4), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(tcArgs.dut)). + WithID(uint64(100)).AddNextHop(uint64(100), uint64(1)).AddNextHop(uint64(101), uint64(1))) + + tcArgs.client.Modify().AddEntry(t, + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(tcArgs.dut)). + WithPrefix(pfx4AdvV4.ip+"/24").WithNextHopGroup(uint64(100))) + + if err := awaitTimeout(tcArgs.ctx, t, tcArgs.client, 5*time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + defaultVRFIPList := []string{pfx4AdvV4.ip} + for ip := range defaultVRFIPList { + chk.HasResult(t, tcArgs.client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(defaultVRFIPList[ip]+"/24"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } +} + +// awaitTimeout calls a fluent client Await, adding a timeout to the context. +func awaitTimeout(ctx context.Context, t testing.TB, c *fluent.GRIBIClient, timeout time.Duration) error { + t.Helper() + subctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + return c.Await(subctx, t) +} + +// startTraffic start traffic on ATE +func startTraffic(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, top gosnappi.Config) { + t.Helper() + capturePktsBeforeTraffic(t, dut, dutPortList) + time.Sleep(10 * time.Second) + ate.OTG().StartTraffic(t) + time.Sleep(time.Minute) + ate.OTG().StopTraffic(t) + time.Sleep(time.Second * 40) + otgutils.LogFlowMetrics(t, ate.OTG(), top) + otgutils.LogLAGMetrics(t, ate.OTG(), top) + otgutils.LogPortMetrics(t, ate.OTG(), top) +} + +// capturePktsBeforeTraffic capture the pkts before traffic on DUT Ports +func capturePktsBeforeTraffic(t *testing.T, dut *ondatra.DUTDevice, dutPortList []*ondatra.Port) { + rxPktsBeforeTraffic = map[*ondatra.Port]uint64{} + txPktsBeforeTraffic = map[*ondatra.Port]uint64{} + for _, port := range dutPortList { + rxPktsBeforeTraffic[port] = gnmi.Get(t, dut, gnmi.OC().Interface(port.Name()).Counters().InPkts().State()) + txPktsBeforeTraffic[port] = gnmi.Get(t, dut, gnmi.OC().Interface(port.Name()).Counters().OutPkts().State()) + } +} + +// verifyTrafficFlow verify the each flow on ATE +func verifyTrafficFlow(t *testing.T, ate *ondatra.ATEDevice, flows []gosnappi.Flow, status bool) bool { + if flows[0].Name() == "pfx1ToPfx4" { + rxPkts := gnmi.Get(t, ate.OTG(), gnmi.OTG().Flow(flows[0].Name()).Counters().InPkts().State()) + txPkts := gnmi.Get(t, ate.OTG(), gnmi.OTG().Flow(flows[0].Name()).Counters().OutPkts().State()) + lostPkt := txPkts - rxPkts + if status { + if got := (lostPkt * 100 / txPkts); got >= 51 { + return false + } + } else if got := (lostPkt * 100 / txPkts); got > 0 { + return false + } + } else { + for _, flow := range flows { + rxPkts := gnmi.Get(t, ate.OTG(), gnmi.OTG().Flow(flow.Name()).Counters().InPkts().State()) + txPkts := gnmi.Get(t, ate.OTG(), gnmi.OTG().Flow(flow.Name()).Counters().OutPkts().State()) + lostPkt := txPkts - rxPkts + if got := (lostPkt * 100 / txPkts); got > 0 { + return false + } + } + } + return true +} + +// awaitAdjacency wait for adjacency to be up/down +func awaitAdjacency(t *testing.T, dut *ondatra.DUTDevice, intfName string, state oc.E_Isis_IsisInterfaceAdjState) bool { + isisPath := ocpath.Root().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance).Isis() + intf := isisPath.Interface(intfName) + query := intf.LevelAny().AdjacencyAny().AdjacencyState().State() + _, ok := gnmi.WatchAll(t, dut, query, 90*time.Second, func(val *ygnmi.Value[oc.E_Isis_IsisInterfaceAdjState]) bool { + v, ok := val.Val() + return v == state && ok + }).Await(t) + + return ok +} + +// checkBidirectionalTraffic verify the bidirectional traffic on DUT ports. +func checkBidirectionalTraffic(t *testing.T, dut *ondatra.DUTDevice, portList []*ondatra.Port) error { + + for _, port := range portList { + txPkts := gnmi.Get(t, dut, gnmi.OC().Interface(port.Name()).Counters().OutPkts().State()) + rxPkts := gnmi.Get(t, dut, gnmi.OC().Interface(port.Name()).Counters().InPkts().State()) + if got := (rxPkts - rxPktsBeforeTraffic[port]) / 100; got == 0 { + return fmt.Errorf("No Packet received, LossPct on Port %s: got %d", port.Name(), got) + } + if got := (txPkts - txPktsBeforeTraffic[port]) / 100; got == 0 { + return fmt.Errorf("No Packet transmitted, LossPct on Port %s: got %d", port.Name(), got) + } + } + return nil +} + +// confirmNonViableForwardingTraffic verify the traffic received on DUT +// interfaces and transmitted to ATE-1 +func confirmNonViableForwardingTraffic(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, + atePort []*ondatra.Port, dutPort []*ondatra.Port) error { + + // Ensure no traffic is transmitted out of DUT ports with Forwarding Viable False + for _, port := range atePort { + rxPkts := gnmi.Get(t, ate.OTG(), gnmi.OTG().Port(port.ID()).Counters().InFrames().State()) + if got := rxPkts / 100; got > 0 { + return fmt.Errorf("Packets are transmiited out of %s: got %d, want 0", port.Name(), got) + } + } + // Ensure that traffic is delivered to ATE-1 port1 + for _, port := range dutPort { + rxPkts := gnmi.Get(t, dut, gnmi.OC().Interface(port.Name()).Counters().InPkts().State()) - rxPktsBeforeTraffic[port] + txPkts := gnmi.Get(t, dut, gnmi.OC().Interface(dutPortList[0].Name()).Counters().OutPkts().State()) - txPktsBeforeTraffic[port] + if got := rxPkts / 100; got == 0 { + return fmt.Errorf("No Packet received on Interface %s: got %d, want packet", port.Name(), got) + } + if got := txPkts / 100; got == 0 { + return fmt.Errorf("No Packet transmitted on Interface %s: got %d, want packet", port.Name(), got) + } + } + return nil +} + +// validateLag3Traffic to ensure traffic Received/Transmitted on DUT LAG_3 +func validateLag3Traffic(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, dutPortList []*ondatra.Port) bool { + result := false + for _, port := range dutPortList { + rxPkts := gnmi.Get(t, dut, gnmi.OC().Interface(port.Name()).Counters().InPkts().State()) - rxPktsBeforeTraffic[port] + txPkts := gnmi.Get(t, dut, gnmi.OC().Interface(port.Name()).Counters().OutPkts().State()) - txPktsBeforeTraffic[port] + if got := rxPkts / 100; got > 0 { + if got := txPkts / 100; got > 0 { + result = true + } + } else { + result = false + } + } + return result +} + +// trafficRXWeights to ensure 50:50 Load Balancing +func trafficRXWeights(t *testing.T, ate *ondatra.ATEDevice, aggNames []string, flow gosnappi.Flow) []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 +} diff --git a/feature/interface/aggregate/otg_tests/aggregate_all_not_viable_test/metadata.textproto b/feature/interface/aggregate/otg_tests/aggregate_all_not_viable_test/metadata.textproto new file mode 100644 index 00000000000..0896d869a61 --- /dev/null +++ b/feature/interface/aggregate/otg_tests/aggregate_all_not_viable_test/metadata.textproto @@ -0,0 +1,42 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "2beaac46-9b7b-49c4-9bde-62ad530aa5c6" +plan_id: "RT-5.7" +description: "Aggregate Not Viable All" +testbed: TESTBED_DUT_ATE_8LINKS + +platform_exceptions: { + platform: { + vendor: NOKIA + } + deviations: { + explicit_port_speed: true + explicit_interface_in_default_vrf: true + aggregate_atomic_update: true + interface_enabled: true + missing_value_for_defaults: true + missing_isis_interface_afi_safi_enable: true + } +} + +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" + aggregate_atomic_update: true + missing_value_for_defaults: true + max_ecmp_paths: true + explicit_interface_in_default_vrf: false + } +} diff --git a/feature/interface/ip/ipv6_ND/otg_tests/disable_ipv6_nd_ra_test/disable_ipv6_nd_ra_test.go b/feature/interface/ip/ipv6_ND/otg_tests/disable_ipv6_nd_ra_test/disable_ipv6_nd_ra_test.go index 12714efd330..b332c3b3f3b 100644 --- a/feature/interface/ip/ipv6_ND/otg_tests/disable_ipv6_nd_ra_test/disable_ipv6_nd_ra_test.go +++ b/feature/interface/ip/ipv6_ND/otg_tests/disable_ipv6_nd_ra_test/disable_ipv6_nd_ra_test.go @@ -102,17 +102,17 @@ func configInterfaceDUT(i *oc.Interface, a *attrs.Attributes, dut *ondatra.DUTDe s6a := s6.GetOrCreateAddress(a.IPv6) s6a.PrefixLength = ygot.Uint8(plen6) routerAdvert := s6.GetOrCreateRouterAdvertisement() - routerAdvert.SetInterval(*ygot.Uint32(routerAdvertisementTimeInterval)) + routerAdvert.SetInterval(routerAdvertisementTimeInterval) if deviations.Ipv6RouterAdvertisementConfigUnsupported(dut) { - routerAdvert.SetSuppress(*ygot.Bool(routerAdvertisementDisabled)) + routerAdvert.SetSuppress(routerAdvertisementDisabled) } else { - routerAdvert.SetEnable(*ygot.Bool(false)) + routerAdvert.SetEnable(false) routerAdvert.SetMode(oc.RouterAdvertisement_Mode_ALL) } return i } -// Configures OTG interfaces to send and recieve ipv6 packets. +// Configures OTG interfaces to send and receive ipv6 packets. func configureOTG(t *testing.T, ate *ondatra.ATEDevice) gosnappi.Config { topo := gosnappi.NewConfig() t.Logf("Configuring OTG port1") diff --git a/feature/interface/ip/ipv6_ND/otg_tests/disable_ipv6_nd_ra_test/metadata.textproto b/feature/interface/ip/ipv6_ND/otg_tests/disable_ipv6_nd_ra_test/metadata.textproto index 0ef0c0fd1e3..e16c5587870 100644 --- a/feature/interface/ip/ipv6_ND/otg_tests/disable_ipv6_nd_ra_test/metadata.textproto +++ b/feature/interface/ip/ipv6_ND/otg_tests/disable_ipv6_nd_ra_test/metadata.textproto @@ -13,3 +13,11 @@ platform_exceptions: { ipv6_router_advertisement_config_unsupported: true } } +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + interface_enabled: true + } +} diff --git a/feature/interface/ip/ipv6_ND/otg_tests/suppress_ipv6_nd_ra_test/README.md b/feature/interface/ip/ipv6_ND/otg_tests/suppress_ipv6_nd_ra_test/README.md new file mode 100644 index 00000000000..f84e63747fe --- /dev/null +++ b/feature/interface/ip/ipv6_ND/otg_tests/suppress_ipv6_nd_ra_test/README.md @@ -0,0 +1,39 @@ +# RT-5.11: Suppress IPv6 ND Router Advertisement + +## Summary + +Validate IPv6 ND Router Advertisement (RA) is suppresed. No periodic RA are sent. + +## Procedure +* Connect DUT port-1 to OTG port-1 +* Configure IPv6 address on DUT port-1 +* Enable IPv6 ND RA suppression on DUT port-1 + +### RT-5.11.1: No periodical Router Advertisement are sent + +* Verify over period of 10 seconds that IPv6 ND RA **doesn't** arrives on OTG Port-1 ([rfc4861 section 6.2.1](https://datatracker.ietf.org/doc/html/rfc4861#section-6.2.1)) + +### RT-5.11.2: Router Advertisement response is sent to Router Solicitation + +* Send IPv6 ND Router Solicitation from OTG Port-1 +* Verify over period of 1 seconds that IPv6 ND RA does arrives on OTG Port-1 ([rfc4861 section 6.2.6](https://datatracker.ietf.org/doc/html/rfc4861#section-6.2.6)) + +## OpenConfig Path and RPC Coverage + +```yaml +paths: + /interfaces/interface/subinterfaces/subinterface/ipv6/router-advertisement/config/interval: + /interfaces/interface/subinterfaces/subinterface/ipv6/router-advertisement/config/suppress: + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + replace: true + gNMI.Subscribe: + on_change: true +``` + +## Required DUT platform + +* FFF \ No newline at end of file diff --git a/feature/interface/ip/ipv6_slaac_link_local/otg_tests/ipv6_slaac_link_local_test/ipv6_slaac_link_local_test.go b/feature/interface/ip/ipv6_slaac_link_local/otg_tests/ipv6_slaac_link_local_test/ipv6_slaac_link_local_test.go index e7bec8c5d06..2e0021e617a 100644 --- a/feature/interface/ip/ipv6_slaac_link_local/otg_tests/ipv6_slaac_link_local_test/ipv6_slaac_link_local_test.go +++ b/feature/interface/ip/ipv6_slaac_link_local/otg_tests/ipv6_slaac_link_local_test/ipv6_slaac_link_local_test.go @@ -40,6 +40,9 @@ func configureDUTLinkLocalInterface(t *testing.T, dut *ondatra.DUTDevice, p *ond } s.GetOrCreateIpv6().GetOrCreateAutoconf() gnmi.Replace(t, dut, gnmi.OC().Interface(p.Name()).Config(), intf) + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + fptest.AssignToNetworkInstance(t, dut, intf.GetName(), deviations.DefaultNetworkInstance(dut), 0) + } } func getAllIPv6Addresses(t *testing.T, dut *ondatra.DUTDevice, p *ondatra.Port) []string { diff --git a/feature/interface/ip/ipv6_slaac_link_local/otg_tests/ipv6_slaac_link_local_test/metadata.textproto b/feature/interface/ip/ipv6_slaac_link_local/otg_tests/ipv6_slaac_link_local_test/metadata.textproto index 47f885c4bde..7cdc467c77e 100644 --- a/feature/interface/ip/ipv6_slaac_link_local/otg_tests/ipv6_slaac_link_local_test/metadata.textproto +++ b/feature/interface/ip/ipv6_slaac_link_local/otg_tests/ipv6_slaac_link_local_test/metadata.textproto @@ -4,8 +4,7 @@ uuid: "22ba3cd3-0dcb-4aba-9a09-d7180785e92c" plan_id: "RT-5.10" description: "IPv6 Link Local generated by SLAAC" -testbed: TESTBED_DUT -tags: TAGS_AGGREGATION +testbed: TESTBED_DUT_ATE_2LINKS tags: TAGS_TRANSIT tags: TAGS_DATACENTER_EDGE @@ -27,3 +26,20 @@ platform_exceptions: { interface_enabled: true } } +platform_exceptions: { + platform: { + vendor: NOKIA + } + deviations: { + interface_enabled: true + explicit_interface_in_default_vrf: true + } +} +platform_exceptions: { + platform: { + vendor: JUNIPER + } + deviations: { + interface_enabled: true + } +} diff --git a/feature/isis/otg_tests/static_route_isis_redistribution/metadata.textproto b/feature/isis/otg_tests/static_route_isis_redistribution/metadata.textproto index 4f658288c55..bbced9e82ee 100644 --- a/feature/isis/otg_tests/static_route_isis_redistribution/metadata.textproto +++ b/feature/isis/otg_tests/static_route_isis_redistribution/metadata.textproto @@ -31,5 +31,6 @@ platform_exceptions: { ipv6_static_route_with_ipv4_next_hop_requires_static_arp: true routing_policy_tag_set_embedded: true same_policy_attached_to_all_afis: true + match_tag_set_condition_unsupported: true } } diff --git a/feature/isis/otg_tests/static_route_isis_redistribution/static_route_isis_redistribution_test.go b/feature/isis/otg_tests/static_route_isis_redistribution/static_route_isis_redistribution_test.go index 9501309f22e..231b7b19aa5 100644 --- a/feature/isis/otg_tests/static_route_isis_redistribution/static_route_isis_redistribution_test.go +++ b/feature/isis/otg_tests/static_route_isis_redistribution/static_route_isis_redistribution_test.go @@ -478,6 +478,10 @@ func TestStaticToISISRedistribution(t *testing.T) { }} for _, tc := range cases { + if deviations.MatchTagSetConditionUnsupported(ts.DUT) && tc.TagSetCondition { + t.Skipf("Skipping test case %s due to match tag set condition not supported", tc.desc) + } + dni := deviations.DefaultNetworkInstance(ts.DUT) t.Run(tc.desc, func(t *testing.T) { diff --git a/feature/isis/otg_tests/weighted_ecmp_test/metadata.textproto b/feature/isis/otg_tests/weighted_ecmp_test/metadata.textproto index 178c2e58a26..8057787e787 100644 --- a/feature/isis/otg_tests/weighted_ecmp_test/metadata.textproto +++ b/feature/isis/otg_tests/weighted_ecmp_test/metadata.textproto @@ -20,5 +20,18 @@ platform_exceptions: { route_policy_under_afi_unsupported: true static_protocol_name: "STATIC" rib_wecmp: true + explicit_port_speed: true + } +} +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + interface_ref_config_unsupported:true + rib_wecmp: true + wecmp_auto_unsupported: true + isis_loopback_required: true + weighted_ecmp_fixed_packet_verification: 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 index 0b6f7fa2942..9e434f24e2a 100644 --- a/feature/isis/otg_tests/weighted_ecmp_test/weighted_ecmp_test.go +++ b/feature/isis/otg_tests/weighted_ecmp_test/weighted_ecmp_test.go @@ -12,11 +12,13 @@ import ( "github.com/openconfig/featureprofiles/internal/cfgplugins" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/helpers" "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" ) @@ -37,6 +39,7 @@ const ( dstTrafficV6 = "2001:db8:64:64::1" v4Count = 254 v6Count = 1000 // Should be 10000000 + fixedPackets = 1000000 ) type aggPortData struct { @@ -111,7 +114,6 @@ var ( ateLoopbackV4: "192.0.2.18", ateLoopbackV6: "2001:db8::18", } - dutLoopback = attrs.Attributes{ Desc: "Loopback ip", IPv4: "192.0.2.21", @@ -128,20 +130,23 @@ var ( unequalDistributionWeights = []uint64{20, 40, 40} ecmpTolerance = uint64(1) + + lb string + + vendor ondatra.Vendor + + isisLevel = 2 ) 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 + vendor = dut.Vendor() + // Enable weighted ECMP in ISIS 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) @@ -154,18 +159,29 @@ func TestWeightedECMPForISIS(t *testing.T) { } b.Set(t, dut) } - + if deviations.WecmpAutoUnsupported(dut) { + var weight string + switch dut.Vendor() { + case ondatra.CISCO: + weight = fmt.Sprintf(" router isis DEFAULT \n interface %s \n address-family ipv4 unicast \n weight 100 \n address-family ipv6 unicast \n weight 100 \n ! \n interface %s \n address-family ipv4 unicast \n weight 100 \n address-family ipv6 unicast \n weight 100 \n ! \n interface %s \n address-family ipv4 unicast \n weight 100 \n address-family ipv6 unicast \n weight 100 \n", aggIDs[1], aggIDs[2], aggIDs[3]) + default: + t.Fatalf("Unsupported vendor %s for deviation 'WecmpAutoUnsupported'", dut.Vendor()) + } + helpers.GnmiCLIConfig(t, dut, weight) + } top := configureATE(t, ate) flows := configureFlows(t, top, ate1AdvV4, ate1AdvV6, ate2AdvV4, ate2AdvV6) ate.OTG().PushConfig(t, top) ate.OTG().StartProtocols(t) + VerifyISISTelemetry(t, dut, aggIDs, []*aggPortData{agg1, agg2}) 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) + gnmi.Await(t, dut, bgpPath.Neighbor(agg.ateLoopbackV4).SessionState().State(), 2*time.Minute, oc.Bgp_Neighbor_SessionState_ESTABLISHED) + gnmi.Await(t, dut, bgpPath.Neighbor(agg.ateLoopbackV6).SessionState().State(), 2*time.Minute, oc.Bgp_Neighbor_SessionState_ESTABLISHED) } startTraffic(t, ate, top) + time.Sleep(time.Minute) 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) @@ -173,6 +189,7 @@ func TestWeightedECMPForISIS(t *testing.T) { t.Errorf("Flow %s loss: got %f, want %f", flow.Name(), got, want) } } + time.Sleep(time.Minute) 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 { @@ -200,9 +217,33 @@ func TestWeightedECMPForISIS(t *testing.T) { } p3 := dut.Port(t, "port3") gnmi.Await(t, dut, gnmi.OC().Interface(p3.Name()).OperStatus().State(), time.Minute*2, oc.Interface_OperStatus_DOWN) + + if deviations.WecmpAutoUnsupported(dut) { + var weight string + switch dut.Vendor() { + case ondatra.CISCO: + weight = fmt.Sprintf(" router isis DEFAULT \n interface %s \n address-family ipv4 unicast \n weight 200 \n address-family ipv6 unicast \n weight 200 \n ! \n interface %s \n address-family ipv4 unicast \n weight 400 \n address-family ipv6 unicast \n weight 400 \n ! \n interface %s \n address-family ipv4 unicast \n weight 400 \n address-family ipv6 unicast \n weight 400 \n", aggIDs[1], aggIDs[2], aggIDs[3]) + default: + t.Fatalf("Unsupported vendor %s for deviation 'WecmpAutoUnsupported'", dut.Vendor()) + } + helpers.GnmiCLIConfig(t, dut, weight) + } + top.Flows().Clear() + if deviations.ISISLoopbackRequired(dut) { + flows = configureFlows(t, top, ate1AdvV4, ate1AdvV6, ate2AdvV4, ate2AdvV6) + ate.OTG().PushConfig(t, top) + ate.OTG().StartProtocols(t) + VerifyISISTelemetry(t, dut, aggIDs, []*aggPortData{agg1, agg2}) + 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(), 3*time.Minute, oc.Bgp_Neighbor_SessionState_ESTABLISHED) + gnmi.Await(t, dut, bgpPath.Neighbor(agg.ateLoopbackV6).SessionState().State(), 3*time.Minute, oc.Bgp_Neighbor_SessionState_ESTABLISHED) + } + } startTraffic(t, ate, top) + time.Sleep(time.Minute) t.Run("Unequal_Distribution_Of_Traffic", func(t *testing.T) { for _, flow := range flows { @@ -211,6 +252,7 @@ func TestWeightedECMPForISIS(t *testing.T) { t.Errorf("Flow %s loss: got %f, want %f", flow.Name(), got, want) } } + time.Sleep(time.Minute) 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 { @@ -262,8 +304,12 @@ func randRange(t *testing.T, start, end uint32, count int) []uint32 { func configureFlows(t *testing.T, top gosnappi.Config, srcV4, srcV6, dstV4, dstV6 *ipAddr) []gosnappi.Flow { t.Helper() + dut := ondatra.DUT(t, "dut") top.Flows().Clear() fV4 := top.Flows().Add().SetName("flowV4") + if deviations.WeightedEcmpFixedPacketVerification(dut) { + fV4.Duration().FixedPackets().SetPackets(fixedPackets) + } fV4.Metrics().SetEnable(true) fV4.TxRx().Device(). SetTxNames([]string{agg1.ateAggName + ".IPv4"}). @@ -276,10 +322,13 @@ func configureFlows(t *testing.T, top gosnappi.Config, srcV4, srcV6, dstV4, dstV 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)) + udp.SrcPort().SetValues(randRange(t, 34525, 65535, 5000)) + udp.DstPort().SetValues(randRange(t, 49152, 65535, 5000)) fV6 := top.Flows().Add().SetName("flowV6") + if deviations.WeightedEcmpFixedPacketVerification(dut) { + fV6.Duration().FixedPackets().SetPackets(fixedPackets) + } fV6.Metrics().SetEnable(true) fV6.TxRx().Device(). SetTxNames([]string{agg1.ateAggName + ".IPv6"}). @@ -293,8 +342,9 @@ func configureFlows(t *testing.T, top gosnappi.Config, srcV4, srcV6, dstV4, dstV 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)) + udpv6.SrcPort().SetValues(randRange(t, 35521, 65535, 5000)) + udpv6.DstPort().SetValues(randRange(t, 49152, 65535, 5000)) + return []gosnappi.Flow{fV4, fV6} } @@ -377,6 +427,7 @@ func configureOTGBGP(t *testing.T, dev gosnappi.Device, agg *aggPortData, advV4, func configureOTGISIS(t *testing.T, dev gosnappi.Device, agg *aggPortData) { t.Helper() + dut := ondatra.DUT(t, "dut") isis := dev.Isis().SetSystemId(agg.ateISISSysID).SetName(agg.ateAggName + ".ISIS") isis.Basic().SetHostname(isis.Name()) isis.Advanced().SetAreaAddresses([]string{ateAreaAddress}) @@ -386,6 +437,14 @@ func configureOTGISIS(t *testing.T, dev gosnappi.Device, agg *aggPortData) { SetNetworkType(gosnappi.IsisInterfaceNetworkType.POINT_TO_POINT). SetLevelType(gosnappi.IsisInterfaceLevelType.LEVEL_2).SetMetric(10) isisInt.Advanced().SetAutoAdjustMtu(true).SetAutoAdjustArea(true).SetAutoAdjustSupportedProtocols(true) + if deviations.ISISLoopbackRequired(dut) { + // configure ISIS loopback interface and advertise them via ISIS. + isisPort2V4 := dev.Isis().V4Routes().Add().SetName(agg.ateAggName + ".ISISV4").SetLinkMetric(10) + isisPort2V4.Addresses().Add().SetAddress(agg.ateLoopbackV4).SetPrefix(32) + isisPort2V6 := dev.Isis().V6Routes().Add().SetName(agg.ateAggName + ".ISISV6").SetLinkMetric(10) + isisPort2V6.Addresses().Add().SetAddress(agg.ateLoopbackV6).SetPrefix(uint32(128)) + } + } func configureDUT(t *testing.T, dut *ondatra.DUTDevice) []string { @@ -455,7 +514,9 @@ func configureDUT(t *testing.T, dut *ondatra.DUTDevice) []string { for _, aggID := range aggIDs { gnmi.Await(t, dut, gnmi.OC().Interface(aggID).AdminStatus().State(), 60*time.Second, oc.Interface_AdminStatus_UP) } - configureStaticRouteToATELoopbacks(t, dut) + if !deviations.ISISLoopbackRequired(dut) { + configureStaticRouteToATELoopbacks(t, dut) + } configureRoutingPolicy(t, dut) configureDUTISIS(t, dut, aggIDs) configureDUTBGP(t, dut) @@ -475,7 +536,7 @@ func configureRoutingPolicy(t *testing.T, dut *ondatra.DUTDevice) { func configureDUTLoopback(t *testing.T, dut *ondatra.DUTDevice) { t.Helper() - lb := netutil.LoopbackInterface(t, dut, 0) + 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()) @@ -548,7 +609,10 @@ func configureStaticRouteToATELoopbacks(t *testing.T, dut *ondatra.DUTDevice) { func configureDUTISIS(t *testing.T, dut *ondatra.DUTDevice, aggIDs []string) { t.Helper() + d := &oc.Root{} + dutConfIsisPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance) + netInstance := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) prot := netInstance.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance) prot.Enabled = ygot.Bool(true) @@ -568,7 +632,12 @@ func configureDUTISIS(t *testing.T, dut *ondatra.DUTDevice, aggIDs []string) { isisLevel2 := isis.GetOrCreateLevel(2) isisLevel2.MetricStyle = oc.Isis_MetricStyle_WIDE_METRIC - + if deviations.ISISLoopbackRequired(dut) { + gnmi.Update(t, dut, gnmi.OC().Config(), d) + // add loopback interface to ISIS + aggIDs = append(aggIDs, "Loopback0") + } + // Add other ISIS interfaces for _, aggID := range aggIDs { isisIntf := isis.GetOrCreateInterface(aggID) isisIntf.GetOrCreateInterfaceRef().Interface = ygot.String(aggID) @@ -598,12 +667,15 @@ func configureDUTISIS(t *testing.T, dut *ondatra.DUTDevice, aggIDs []string) { isisIntfLevelAfiv6.Enabled = nil } } - gnmi.Update(t, dut, gnmi.OC().Config(), d) + if deviations.ISISLoopbackRequired(dut) { + gnmi.Update(t, dut, dutConfIsisPath.Config(), prot) + } else { + 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") @@ -641,7 +713,11 @@ func configureDUTBGP(t *testing.T, dut *ondatra.DUTDevice) { bgpNbrV4.PeerAs = ygot.Uint32(asn) bgpNbrV4.Enabled = ygot.Bool(true) bgpNbrV4T := bgpNbrV4.GetOrCreateTransport() - bgpNbrV4T.LocalAddress = ygot.String(dutLoopback.IPv4) + localAddressLeafv4 := dutLoopback.IPv4 + if deviations.ISISLoopbackRequired(dut) { + localAddressLeafv4 = lb + } + bgpNbrV4T.LocalAddress = ygot.String(localAddressLeafv4) 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) @@ -652,7 +728,12 @@ func configureDUTBGP(t *testing.T, dut *ondatra.DUTDevice) { bgpNbrV6.PeerAs = ygot.Uint32(asn) bgpNbrV6.Enabled = ygot.Bool(true) bgpNbrV6T := bgpNbrV6.GetOrCreateTransport() - bgpNbrV6T.LocalAddress = ygot.String(dutLoopback.IPv6) + localAddressLeafv6 := dutLoopback.IPv6 + if deviations.ISISLoopbackRequired(dut) { + localAddressLeafv6 = lb + } + bgpNbrV6T.LocalAddress = ygot.String(localAddressLeafv6) + 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) @@ -660,4 +741,54 @@ func configureDUTBGP(t *testing.T, dut *ondatra.DUTDevice) { } gnmi.Replace(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Config(), niProto) + +} +func VerifyISISTelemetry(t *testing.T, dut *ondatra.DUTDevice, dutIntfs []string, loopBacks []*aggPortData) { + t.Helper() + statePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance).Isis() + for _, dutIntf := range dutIntfs { + + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + dutIntf = dutIntf + ".0" + } + nbrPath := statePath.Interface(dutIntf) + query := nbrPath.LevelAny().AdjacencyAny().AdjacencyState().State() + _, ok := gnmi.WatchAll(t, dut, query, 3*time.Minute, func(val *ygnmi.Value[oc.E_Isis_IsisInterfaceAdjState]) bool { + state, present := val.Val() + return present && state == oc.Isis_IsisInterfaceAdjState_UP + }).Await(t) + if !ok { + t.Logf("IS-IS state on %v has no adjacencies", dutIntf) + t.Fatal("No IS-IS adjacencies reported.") + } + } + if deviations.ISISLoopbackRequired(dut) { + // verify loopback has been received via ISIS + t.Log("Starting route check") + for _, loopBack := range loopBacks { + batch := gnmi.OCBatch() + statePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)) + id := formatID(loopBack.ateISISSysID) + iPv4Query := statePath.Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance).Isis().Level(uint8(isisLevel)).Lsp(id).Tlv(oc.IsisLsdbTypes_ISIS_TLV_TYPE_EXTENDED_IPV4_REACHABILITY).ExtendedIpv4Reachability().Prefix(fmt.Sprintf(loopBack.ateLoopbackV4 + "/32")) + iPv6Query := statePath.Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance).Isis().Level(uint8(isisLevel)).Lsp(id).Tlv(oc.IsisLsdbTypes_ISIS_TLV_TYPE_IPV6_REACHABILITY).ExtendedIpv4Reachability().Prefix(fmt.Sprintf(loopBack.ateLoopbackV6 + "/128")) + batch.AddPaths(iPv4Query, iPv6Query) + _, ok := gnmi.Watch(t, dut, batch.State(), 5*time.Minute, func(val *ygnmi.Value[*oc.Root]) bool { + _, present := val.Val() + return present + }).Await(t) + if !ok { + t.Fatalf("ISIS did not receive the route loopback %s", loopBack.ateLoopbackV4) + } + } + } +} + +func formatID(input string) string { + part1 := input[:4] + part2 := input[4:8] + part3 := input[8:12] + + formatted := fmt.Sprintf("%s.%s.%s.00-00", part1, part2, part3) + + return formatted } diff --git a/feature/lldp/ate_tests/core_lldp_tlv_population_test/README.md b/feature/lldp/ate_tests/core_lldp_tlv_population_test/README.md index 6e0e134a35d..e40267537ed 100644 --- a/feature/lldp/ate_tests/core_lldp_tlv_population_test/README.md +++ b/feature/lldp/ate_tests/core_lldp_tlv_population_test/README.md @@ -17,29 +17,31 @@ Determine LLDP advertisement and reception operates correctly. configuration of lldp/interfaces/interface/config/enabled (TRUE or FALSE) on any interface. -## Config Parameter coverage - -* /lldp/config/enabled -* /lldp/interfaces/interface/config/enabled - -## Telemetry Parameter coverage - -* /lldp/interfaces/interface/neighbors/neighbor/state/chassis-id -* /lldp/interfaces/interface/neighbors/neighbor/state/chassis-id-subtype -* /lldp/interfaces/interface/neighbors/neighbor/state/port-id -* /lldp/interfaces/interface/neighbors/neighbor/state/port-id-subtype -* /lldp/interfaces/interface/neighbors/neighbor/state/system-name -* /lldp/interfaces/interface/state/name -* /lldp/state/chassis-id -* /lldp/state/chassis-id-type -* /lldp/state/system-name - -## Protocol/RPC Parameter coverage - -LLDP: - -* /lldp/config/enabled = true -* /lldp/interfaces/interface/config/enabled = true +## 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 ## + /lldp/config/enabled: + /lldp/interfaces/interface/config/enabled: + + ## State Paths ## + /lldp/interfaces/interface/neighbors/neighbor/state/chassis-id: + /lldp/interfaces/interface/neighbors/neighbor/state/port-id: + /lldp/interfaces/interface/neighbors/neighbor/state/system-name: + /lldp/interfaces/interface/state/name: + /lldp/state/chassis-id: + /lldp/state/chassis-id-type: + /lldp/state/system-name: + +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + +``` ## Minimum DUT platform requirement diff --git a/feature/lldp/ate_tests/core_lldp_tlv_population_test/core_lldp_tlv_population_test.go b/feature/lldp/ate_tests/core_lldp_tlv_population_test/core_lldp_tlv_population_test.go index 61532d41ea1..73498534b12 100644 --- a/feature/lldp/ate_tests/core_lldp_tlv_population_test/core_lldp_tlv_population_test.go +++ b/feature/lldp/ate_tests/core_lldp_tlv_population_test/core_lldp_tlv_population_test.go @@ -94,19 +94,21 @@ func TestCoreLLDPTLVPopulation(t *testing.T) { func configureNode(t *testing.T, name string, lldpEnabled bool) (*ondatra.DUTDevice, *oc.Lldp) { node := ondatra.DUT(t, name) p := node.Port(t, portName) - lldp := gnmi.OC().Lldp() + d := &oc.Root{} + lldp := d.GetOrCreateLldp() + llint := lldp.GetOrCreateInterface(p.Name()) - gnmi.Replace(t, node, lldp.Enabled().Config(), lldpEnabled) + gnmi.Replace(t, node, gnmi.OC().Lldp().Enabled().Config(), lldpEnabled) if lldpEnabled { - gnmi.Replace(t, node, lldp.Interface(p.Name()).Enabled().Config(), lldpEnabled) + gnmi.Replace(t, node, gnmi.OC().Lldp().Interface(p.Name()).Config(), llint) } if deviations.InterfaceEnabled(node) { gnmi.Replace(t, node, gnmi.OC().Interface(p.Name()).Enabled().Config(), true) } - return node, gnmi.Get(t, node, lldp.Config()) + return node, gnmi.Get(t, node, gnmi.OC().Lldp().Config()) } // verifyNodeConfig verifies the config by comparing against the telemetry state object. diff --git a/feature/lldp/otg_tests/core_lldp_tlv_population_test/README.md b/feature/lldp/otg_tests/core_lldp_tlv_population_test/README.md index 6e0e134a35d..e40267537ed 100644 --- a/feature/lldp/otg_tests/core_lldp_tlv_population_test/README.md +++ b/feature/lldp/otg_tests/core_lldp_tlv_population_test/README.md @@ -17,29 +17,31 @@ Determine LLDP advertisement and reception operates correctly. configuration of lldp/interfaces/interface/config/enabled (TRUE or FALSE) on any interface. -## Config Parameter coverage - -* /lldp/config/enabled -* /lldp/interfaces/interface/config/enabled - -## Telemetry Parameter coverage - -* /lldp/interfaces/interface/neighbors/neighbor/state/chassis-id -* /lldp/interfaces/interface/neighbors/neighbor/state/chassis-id-subtype -* /lldp/interfaces/interface/neighbors/neighbor/state/port-id -* /lldp/interfaces/interface/neighbors/neighbor/state/port-id-subtype -* /lldp/interfaces/interface/neighbors/neighbor/state/system-name -* /lldp/interfaces/interface/state/name -* /lldp/state/chassis-id -* /lldp/state/chassis-id-type -* /lldp/state/system-name - -## Protocol/RPC Parameter coverage - -LLDP: - -* /lldp/config/enabled = true -* /lldp/interfaces/interface/config/enabled = true +## 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 ## + /lldp/config/enabled: + /lldp/interfaces/interface/config/enabled: + + ## State Paths ## + /lldp/interfaces/interface/neighbors/neighbor/state/chassis-id: + /lldp/interfaces/interface/neighbors/neighbor/state/port-id: + /lldp/interfaces/interface/neighbors/neighbor/state/system-name: + /lldp/interfaces/interface/state/name: + /lldp/state/chassis-id: + /lldp/state/chassis-id-type: + /lldp/state/system-name: + +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + +``` ## Minimum DUT platform requirement diff --git a/feature/lldp/otg_tests/core_lldp_tlv_population_test/core_lldp_tlv_population_test.go b/feature/lldp/otg_tests/core_lldp_tlv_population_test/core_lldp_tlv_population_test.go index 532595c9e28..e34e217dcd9 100644 --- a/feature/lldp/otg_tests/core_lldp_tlv_population_test/core_lldp_tlv_population_test.go +++ b/feature/lldp/otg_tests/core_lldp_tlv_population_test/core_lldp_tlv_population_test.go @@ -138,18 +138,21 @@ func TestLLDPDisabled(t *testing.T) { func configureDUT(t *testing.T, name string, lldpEnabled bool) (*ondatra.DUTDevice, *oc.Lldp) { node := ondatra.DUT(t, name) p := node.Port(t, portName) - lldp := gnmi.OC().Lldp() + d := &oc.Root{} + lldp := d.GetOrCreateLldp() + llint := lldp.GetOrCreateInterface(p.Name()) - gnmi.Replace(t, node, lldp.Enabled().Config(), lldpEnabled) + gnmi.Replace(t, node, gnmi.OC().Lldp().Enabled().Config(), lldpEnabled) if lldpEnabled { - gnmi.Replace(t, node, lldp.Interface(p.Name()).Enabled().Config(), lldpEnabled) + gnmi.Replace(t, node, gnmi.OC().Lldp().Interface(p.Name()).Config(), llint) } + if deviations.InterfaceEnabled(node) { gnmi.Replace(t, node, gnmi.OC().Interface(p.Name()).Enabled().Config(), true) } - return node, gnmi.Get(t, node, lldp.Config()) + return node, gnmi.Get(t, node, gnmi.OC().Lldp().Config()) } func configureATE(t *testing.T, otg *otg.OTG) gosnappi.Config { diff --git a/feature/mpls/otg_tests/label_block/README.md b/feature/mpls/otg_tests/label_block/README.md new file mode 100644 index 00000000000..39aeb3511f2 --- /dev/null +++ b/feature/mpls/otg_tests/label_block/README.md @@ -0,0 +1,60 @@ +# MPLS-1.1: MPLS label blocks using ISIS + +## Summary + +Define reserved MPLS label blocks: static and MPLS-SR. + +## Testbed type + +* [`featureprofiles/topologies/atedut_2.testbed`](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_2.testbed) + +## Procedure + +Topology: ATE1—DUT1 + +On DUT1 configure: + +* ISIS adjacency between ATE1 and DUT1 +* Enable MPLS-SR for ISIS (`/network-instances/network-instance/protocols/protocol/isis/global/segment-routing/config/enabled`) +* reserved-label-block (lower-bound: 1000000 upper-bound: 1048576) +* Segment Routing Global Block (srgb) with lower-bound: 400000 upper-bound: 465001 +* Segment Routing Local Block (srlb) with lower-bound: 40000 upper-bound: 41000) + +Verify: + +* Defined blocks are configured on DUT1. +* DUT1 advertises its SRGB and SRLB to ATE1. + + +## OpenConfig Path and RPC Coverage + +```yaml +paths: + # configuration + /network-instances/network-instance/mpls/global/interface-attributes/interface/config/mpls-enabled: + /network-instances/network-instance/mpls/global/reserved-label-blocks/reserved-label-block/config/local-id: + /network-instances/network-instance/mpls/global/reserved-label-blocks/reserved-label-block/config/lower-bound: + /network-instances/network-instance/mpls/global/reserved-label-blocks/reserved-label-block/config/upper-bound: + /network-instances/network-instance/segment-routing/srgbs/srgb/config/local-id: + /network-instances/network-instance/segment-routing/srgbs/srgb/config/mpls-label-blocks: + /network-instances/network-instance/segment-routing/srlbs/srlb/local-id: + /network-instances/network-instance/segment-routing/srlbs/srlb/config/mpls-label-block: + /network-instances/network-instance/protocols/protocol/isis/global/segment-routing/config/enabled: + /network-instances/network-instance/protocols/protocol/isis/global/segment-routing/config/srgb: + /network-instances/network-instance/protocols/protocol/isis/global/segment-routing/config/srlb: + # telemetry + /network-instances/network-instance/mpls/global/reserved-label-blocks/reserved-label-block/state/local-id: + /network-instances/network-instance/mpls/global/reserved-label-blocks/reserved-label-block/state/lower-bound: + /network-instances/network-instance/mpls/global/reserved-label-blocks/reserved-label-block/state/upper-bound: + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + gNMI.Subscribe: + on_change: true +``` + +## Required DUT platform + +* FFF diff --git a/feature/mpls/otg_tests/static_lsp/README.md b/feature/mpls/otg_tests/static_lsp/README.md new file mode 100644 index 00000000000..ba2191e8cd1 --- /dev/null +++ b/feature/mpls/otg_tests/static_lsp/README.md @@ -0,0 +1,42 @@ +# TE-9.2: MPLS based forwarding Static LSP + +## Summary + +Validate static lsp functionality. + +## Procedure + +* Create topology ATE1–DUT1-ATE2 +* Enable MPLS forwarding and create egress static LSP to pop the label and forward to ATE2: +* Match incoming label (1000001) +* Set IP next-hop +* Set egress interface +* Set the action to pop label +* Start 2 traffic flows with specified MPLS tags IPv4-MPLS[1000002]-MPLS[1000001] +* Verify that traffic is received at ATE2 with MPLS label [1000001] removed + +## 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 + /network-instances/network-instance/mpls/lsps/static-lsps/static-lsp/egress/config/next-hop: + /network-instances/network-instance/mpls/lsps/static-lsps/static-lsp/egress/config/incoming-label: + /network-instances/network-instance/mpls/lsps/static-lsps/static-lsp/egress/config/push-label: + + ## State paths + /network-instances/network-instance/mpls/lsps/static-lsps/static-lsp/egress/state/next-hop: + /network-instances/network-instance/mpls/lsps/static-lsps/static-lsp/egress/state/incoming-label: + /network-instances/network-instance/mpls/lsps/static-lsps/static-lsp/egress/state/push-label: + /network-instances/network-instance/mpls/lsps/static-lsps/static-lsp/egress/state/metric: + /network-instances/network-instance/mpls/lsps/static-lsps/static-lsp/egress/state/interface: + /network-instances/network-instance/mpls/lsps/static-lsps/static-lsp/egress/state/subinterface: + +rpcs: + gnmi: + gNMI.Set: + gNMI.Subscribe: +``` diff --git a/feature/mpls/sr/otg_tests/isis_node_sid_forward/README.md b/feature/mpls/sr/otg_tests/isis_node_sid_forward/README.md new file mode 100644 index 00000000000..394f59200a1 --- /dev/null +++ b/feature/mpls/sr/otg_tests/isis_node_sid_forward/README.md @@ -0,0 +1,65 @@ +# SR-1.1: Transit forwarding to Node-SID via ISIS + +## Summary + +MPLS-SR transit forwarding to Node-SID distributed over ISIS + +## Testbed type + +* [`featureprofiles/topologies/atedut_2.testbed`](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_2.testbed) + +## Procedure + +### Configuration + +Topology: ATE1—DUT1–ATE2 + +* Configure Segment Routing Global Block (srgb) lower-bound: 400000 upper-bound: 465001) +* Enable Segment Routing for the ISIS +* Enable MPLS forwarding. + +* Prefix (1) with node-SID is advertised by the direct ISIS neighbor +* Prefix (2) with node-SID is advertised by simulated indirect ISIS speaker + +### Test + +Verify that: + +* DUT advertises both prefixes with node-SID to ATE2. + +Generate traffic: +* Send labeled traffic transiting through the DUT matching direct prefix (1). Verify that ATE2 receives traffic with node-SID label popped. +* Send labeled traffic transiting through the DUT matching indirect prefix (2). Verify that ATE2 receives traffic with the node-SID label intact. +* Verify that corresponding SID forwarding counters are incremented. +* Traffic arrives without packet loss. + +## OpenConfig Path and RPC Coverage + +```yaml +paths: + # srgb definition + /network-instances/network-instance/mpls/global/reserved-label-blocks/reserved-label-block/config/local-id: + /network-instances/network-instance/mpls/global/reserved-label-blocks/reserved-label-block/config/lower-bound: + /network-instances/network-instance/mpls/global/reserved-label-blocks/reserved-label-block/config/upper-bound: + # sr config + /network-instances/network-instance/mpls/global/interface-attributes/interface/config/mpls-enabled: + /network-instances/network-instance/segment-routing/srgbs/srgb/config/local-id: + /network-instances/network-instance/segment-routing/srgbs/srgb/config/mpls-label-blocks: + /network-instances/network-instance/protocols/protocol/isis/global/segment-routing/config/enabled: + /network-instances/network-instance/protocols/protocol/isis/global/segment-routing/config/srgb: + # telemetry + /network-instances/network-instance/protocols/protocol/isis/global/segment-routing/state/enabled: + /network-instances/network-instance/mpls/signaling-protocols/segment-routing/aggregate-sid-counters/aggregate-sid-counter/state/in-pkts: + /network-instances/network-instance/mpls/signaling-protocols/segment-routing/aggregate-sid-counters/aggregate-sid-counter/state/out-pkts: + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + replace: true + gNMI.Subscribe: + on_change: true +``` +## Required DUT platform + +* FFF diff --git a/feature/mtu/largeippacket/otg_tests/large_ip_packet_transmission/large_ip_packet_transmission_test.go b/feature/mtu/largeippacket/otg_tests/large_ip_packet_transmission/large_ip_packet_transmission_test.go index 0f7304d92d5..99580892350 100644 --- a/feature/mtu/largeippacket/otg_tests/large_ip_packet_transmission/large_ip_packet_transmission_test.go +++ b/feature/mtu/largeippacket/otg_tests/large_ip_packet_transmission/large_ip_packet_transmission_test.go @@ -153,7 +153,6 @@ func createFlow(flowName string, flowSize uint32, ipv string) gosnappi.Flow { SetRxNames([]string{fmt.Sprintf("%s.%s", ateDst.Name, ipv)}) ethHdr := flow.Packet().Add().Ethernet() ethHdr.Src().SetValue(ateSrc.MAC) - ethHdr.Dst().SetValue(ateDst.MAC) flow.SetSize(gosnappi.NewFlowSize().SetFixed(flowSize)) switch ipv { @@ -180,8 +179,8 @@ func runTest(t *testing.T, tt testDefinition, td testData, waitF func(t *testing td.otgConfig.Flows().Clear() td.otgConfig.Flows().Append(flowParams) td.otg.PushConfig(t, td.otgConfig) + time.Sleep(time.Second * 30) td.otg.StartProtocols(t) - waitF(t) td.otg.StartTraffic(t) @@ -325,19 +324,17 @@ func TestLargeIPPacketTransmission(t *testing.T) { dut := ondatra.DUT(t, "dut") ate := ondatra.ATE(t, "ate") otg := ate.OTG() - configureDUT(t, dut) - otgConfig := configureATE(t, ate) t.Cleanup(func() { + deleteBatch := &gnmi.SetBatch{} if deviations.ExplicitInterfaceInDefaultVRF(dut) { netInst := &oc.NetworkInstance{Name: ygot.String(deviations.DefaultNetworkInstance(dut))} for portName := range dutPorts { - gnmi.Delete( - t, - dut, + gnmi.BatchDelete( + deleteBatch, gnmi.OC(). NetworkInstance(*netInst.Name). Interface(fmt.Sprintf("%s.%d", dut.Port(t, portName).Name(), subInterfaceIndex)). @@ -347,16 +344,16 @@ func TestLargeIPPacketTransmission(t *testing.T) { } for portName := range dutPorts { - gnmi.Delete(t, dut, gnmi.OC().Interface(dut.Port(t, portName).Name()).Mtu().Config()) - gnmi.Delete( - t, - dut, + gnmi.BatchDelete( + deleteBatch, gnmi.OC(). Interface(dut.Port(t, portName).Name()). Subinterface(subInterfaceIndex). Config(), ) + gnmi.BatchDelete(deleteBatch, gnmi.OC().Interface(dut.Port(t, portName).Name()).Mtu().Config()) } + deleteBatch.Set(t, dut) }) for _, tt := range testCases { @@ -377,13 +374,13 @@ func TestLargeIPPacketTransmission(t *testing.T) { func configureDUTBundle(t *testing.T, dut *ondatra.DUTDevice, lag *attrs.Attributes, bundleMembers []*ondatra.Port) string { bundleID := netutil.NextAggregateInterface(t, dut) ocRoot := &oc.Root{} - if deviations.AggregateAtomicUpdate(dut) { - gnmi.Delete(t, dut, gnmi.OC().Interface(bundleID).Aggregation().MinLinks().Config()) + deleteBatch := &gnmi.SetBatch{} + gnmi.BatchDelete(deleteBatch, gnmi.OC().Interface(bundleID).Aggregation().MinLinks().Config()) for _, port := range bundleMembers { - gnmi.Delete(t, dut, gnmi.OC().Interface(port.Name()).Ethernet().AggregateId().Config()) + gnmi.BatchDelete(deleteBatch, gnmi.OC().Interface(port.Name()).Ethernet().AggregateId().Config()) } - + deleteBatch.Set(t, dut) bundle := ocRoot.GetOrCreateInterface(bundleID) bundle.GetOrCreateAggregation().LagType = oc.IfAggregate_AggregationType_STATIC bundle.Type = oc.IETFInterfaces_InterfaceType_ieee8023adLag @@ -405,13 +402,6 @@ func configureDUTBundle(t *testing.T, dut *ondatra.DUTDevice, lag *attrs.Attribu gnmi.Update(t, dut, gnmi.OC().Config(), ocRoot) } - lacp := &oc.Lacp_Interface{ - Name: ygot.String(bundleID), - LacpMode: oc.Lacp_LacpActivityType_UNSET, - } - lacpPath := gnmi.OC().Lacp().Interface(bundleID) - gnmi.Replace(t, dut, lacpPath.Config(), lacp) - agg := ocRoot.GetOrCreateInterface(bundleID) agg.Type = oc.IETFInterfaces_InterfaceType_ieee8023adLag agg.Description = ygot.String(fmt.Sprintf("dutLag-%s", bundleID)) @@ -566,7 +556,6 @@ func TestLargeIPPacketTransmissionBundle(t *testing.T) { allDutPorts := sortPorts(dut.Ports()) allAtePorts := sortPorts(ate.Ports()) - if len(allDutPorts) < 2 { t.Fatalf("testbed requires at least two dut ports, but only has %d", len(allDutPorts)) } diff --git a/feature/mtu/largeippacket/otg_tests/large_ip_packet_transmission/metadata.textproto b/feature/mtu/largeippacket/otg_tests/large_ip_packet_transmission/metadata.textproto index 6a27032d6bd..400605a46de 100644 --- a/feature/mtu/largeippacket/otg_tests/large_ip_packet_transmission/metadata.textproto +++ b/feature/mtu/largeippacket/otg_tests/large_ip_packet_transmission/metadata.textproto @@ -14,6 +14,7 @@ platform_exceptions: { explicit_interface_in_default_vrf: true aggregate_atomic_update: true interface_enabled: true + omit_l2_mtu: true } } platform_exceptions: { @@ -26,5 +27,4 @@ platform_exceptions: { interface_enabled: true default_network_instance: "default" } -} - +} \ No newline at end of file diff --git a/feature/mtu/otg_tests/pmtu_handing/README.md b/feature/mtu/otg_tests/pmtu_handing/README.md new file mode 100644 index 00000000000..0a09af0543a --- /dev/null +++ b/feature/mtu/otg_tests/pmtu_handing/README.md @@ -0,0 +1,83 @@ +# MTU-1.5: Path MTU handing + +## Summary + +This tests ensures that DUT generates ICMP "Fragmentation Needed and Don't Fragment was Set" for packets exceeding egress interface MTU. + +## Testbed type + +* [`featureprofiles/topologies/atedut_2.testbed`](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_2.testbed) + +## Procedure + +### Test environment setup + + ``` + | | | | + [ ATE Port 1 ] ---- | DUT | ---- | ATE Port 2 | + | | | | + ``` + + +### Configuration + +* Configure DUT with routed ports on DUT. +* Configure Ethernet MTU of 9216 on DUT port 1 and Ethernet MTU of 1514 on DUT port 2. +* Configure static routes on DUT for IPV4-DST and IPV6-DST to ATE Port 2 + + +### MTU-1.5.1 IPv4 Path MTU + +Run traffic flows to IPV4-DST with the sizes at 50% linerate for 30 seconds: +- 2000 Bytes +- 4000 Bytes +- 9000 Bytes + +Verify: +* Ensure that ATE Port-1 receives ICMP type-3, code-4 for packet of every flow sent. +* DUT pipeline counters report fragment packet discards. +* Verify the amount of traffic forwarded and dropped to the control-plane and compare to the amount of packets sent. Default rate-limiting of fragment + traffic is permitted. +* Verify low CPU (<20%) utilization on control plane. + +### MTU-1.5.2 IPv6 Path MTU + +Run traffic flows to IPV6-DST with the sizes at 50% linerate for 30 seconds: +- 2000 Bytes +- 4000 Bytes +- 9000 Bytes + +* Ensure that ATE Port-1 receives ICMPv6 type-2 code-0 for packet of every flow sent. +* Verify that DUT pipeline counters report fragment packet discards. +* Verify the amount of traffic forwarded and dropped to the control-plane and compare to the amount of packets sent. Default rate-limiting of fragment + traffic is permitted. +* Verify low CPU (<20%) utilization on control plane. + +## OpenConfig Path and RPC Coverage + +```yaml +paths: + # tunnel interfaces + /interfaces/interface/config/mtu: + # telemetry + /components/component/integrated-circuit/pipeline-counters/drop/state/packet-processing-aggregate: + platform_type: [ "INTEGRATED_CIRCUIT" ] + /components/component/integrated-circuit/pipeline-counters/drop/lookup-block/state/fragment-total-drops: + platform_type: [ "INTEGRATED_CIRCUIT" ] + + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + replace: true + gNMI.Subscribe: + on_change: true +``` + +The device may support some vendor proprietary leafs to count MTU exceeded packets which are dropped due to control plane policing rules in the `components/component/integrated-circuit/pipeline-counters/control-plane-traffic/vendor` tree. +Implementation should add code with a switch statement to expose these counters, if they exist. + +## Required DUT platform + +* FFF diff --git a/feature/p4rt/otg_tests/p4rt_daemon_failure_test/README.md b/feature/p4rt/otg_tests/p4rt_daemon_failure_test/README.md index c9ad228379e..4719f77f44b 100644 --- a/feature/p4rt/otg_tests/p4rt_daemon_failure_test/README.md +++ b/feature/p4rt/otg_tests/p4rt_daemon_failure_test/README.md @@ -23,13 +23,16 @@ Ensure that data plane traffic is not interrupted by P4RT daemon failure. test tables only configure the control plane traffic. Instead, this test configures the data plane using gRIBI. -## Protocol/RPC Parameter Coverage - -* gRIBI - * ModifyRequest - * GetRequest +## OpenConfig Path and RPC Coverage +```yaml +rpcs: + gribi: + gRIBI.Get: + gRIBI.Modify: + gRIBI.Flush: +``` ## Telemetry Parameter Coverage * /network-instances/network-instance/afts/ipv4-unicast/ipv4-entry/state/prefix/ * /interfaces/interface/state/id -* /interfaces/interface/state/name \ No newline at end of file +* /interfaces/interface/state/name 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 239ea5ba306..2638b2aee7d 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 @@ -15,7 +15,6 @@ package p4rt_daemon_failure_test import ( - "context" "fmt" "testing" "time" @@ -24,6 +23,7 @@ import ( "github.com/openconfig/featureprofiles/internal/attrs" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/gnoi" "github.com/openconfig/featureprofiles/internal/gribi" "github.com/openconfig/featureprofiles/internal/p4rtutils" "github.com/openconfig/gribigo/fluent" @@ -34,7 +34,6 @@ import ( "github.com/openconfig/ygot/ygot" gpb "github.com/openconfig/gnmi/proto/gnmi" - syspb "github.com/openconfig/gnoi/system" ) func TestMain(m *testing.M) { @@ -80,13 +79,6 @@ var ( IPv4: "192.0.2.6", IPv4Len: ipv4PrefixLen, } - - p4rtDaemons = map[ondatra.Vendor]string{ - ondatra.ARISTA: "P4Runtime", - ondatra.CISCO: "emsd", - ondatra.JUNIPER: "p4-switch", - ondatra.NOKIA: "sr_grpc_server", - } ) // configInterfaceDUT returns the OC Interface config for a given port. @@ -192,18 +184,6 @@ func startTraffic(t *testing.T, ate *ondatra.ATEDevice, top gosnappi.Config) gos return flow } -// pidByName uses telemetry to find out the PID of a process -func pidByName(t *testing.T, dut *ondatra.DUTDevice, process string) (uint64, error) { - t.Helper() - ps := gnmi.GetAll(t, dut, gnmi.OC().System().ProcessAny().State()) - for _, p := range ps { - if p.GetName() == process { - return p.GetPid(), nil - } - } - return 0, fmt.Errorf("could not find PID for process: %s", process) -} - func installRoutes(t *testing.T, dut *ondatra.DUTDevice) error { t.Helper() @@ -299,11 +279,6 @@ func subscribeOnChangeInterfaceName(t *testing.T, dut *ondatra.DUTDevice, p *ond func TestP4RTDaemonFailure(t *testing.T) { dut := ondatra.DUT(t, "dut") - p4rtD, ok := p4rtDaemons[dut.Vendor()] - if !ok { - t.Fatalf("Please add support for vendor %v in var p4rtDaemons", dut.Vendor()) - } - t.Logf("Configure DUT") configureDUT(t, dut) @@ -334,23 +309,7 @@ func TestP4RTDaemonFailure(t *testing.T) { flow := startTraffic(t, ate, top) - pID, err := pidByName(t, dut, p4rtD) - if err != nil { - t.Fatal(err) - } - - c := dut.RawAPIs().GNOI(t) - req := &syspb.KillProcessRequest{ - Name: p4rtD, - Pid: uint32(pID), - Signal: syspb.KillProcessRequest_SIGNAL_TERM, - Restart: true, - } - resp, err := c.System().KillProcess(context.Background(), req) - t.Logf("Got kill process response: %v", resp) - if err != nil { - t.Fatalf("FAIL: to execute gNOI.KillProcess, error received: %v", err) - } + gnoi.KillProcess(t, dut, gnoi.P4RT, gnoi.SigTerm, true, true) // let traffic keep running for another 10 seconds. time.Sleep(10 * time.Second) diff --git a/feature/platform/controllercard/tests/port/README.md b/feature/platform/controllercard/tests/port/README.md index f66189a90dd..5ee88e7e7a8 100644 --- a/feature/platform/controllercard/tests/port/README.md +++ b/feature/platform/controllercard/tests/port/README.md @@ -19,18 +19,19 @@ expected OC paths. The operational use case is: There are no DUT configuration pre-requisites for this test. The DUT must contain the following component types: - 2 `CONTROLLER_CARD` components - Each CONTROLLER_CARD contains at least one `PORT` + +* At least one `CONTROLLER_CARD` component +* Each CONTROLLER_CARD must contain at least one `PORT` ## Procedure -* gNMI-1.22: Validate component PORT attributes attached to a CONTROLLER_CARD +* gNMI-1.22.1: Validate component PORT attributes attached to a CONTROLLER_CARD * gNMI Subscribe to the /components and /interfaces tree using ONCE option. * Verify each PORT present on a CONTROLLER_CARD has the following paths set: * Search the components to to find components of type PORT with parent = CONTROLLER_CARD * /components/component/state/parent = the appropriate component of type CONTROLLER_CARD * Search the /interfaces/interface/state/hardware-port values to find the expected /components/component/name for the physical port on the CONTROLLER_CARD - * For each of these interfaces, verify /interfaces/interface/state/cpu = TRUE + * For each of these interfaces, verify /interfaces/interface/state/management = TRUE ## OpenConfig Path and RPC Coverage @@ -50,7 +51,7 @@ paths: "PORT" ] /interfaces/interface/state/name: - /interfaces/interface/state/cpu: + /interfaces/interface/state/management: /interfaces/interface/state/hardware-port: rpcs: 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 f62de8c426e..099686c7f73 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 @@ -13,15 +13,6 @@ platform_exceptions: { ipv4_missing_enabled: true } } -platform_exceptions: { - platform: { - vendor: JUNIPER - } - deviations: { - explicit_interface_ref_definition: true - backplane_facing_capacity_unsupported: true - } -} platform_exceptions: { platform: { vendor: NOKIA 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 3e7f39995df..651bb61d582 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 @@ -117,6 +117,19 @@ func TestOnChangeBackplaneCapacityCounters(t *testing.T) { t.Logf("IntegratedCircuit components count: %d", len(ics)) fabrics := components.FindComponentsByType(t, dut, oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_FABRIC) + t.Logf("fabrics are %v", fabrics) + removable_fabrics := make([]string, 0) + for _, f := range fabrics { + compMtyVal, compMtyPresent := gnmi.Lookup(t, dut, gnmi.OC().Component(f).Empty().State()).Val() + if compMtyPresent && compMtyVal { + continue + } + if gnmi.Get(t, dut, gnmi.OC().Component(f).Removable().State()) { + removable_fabrics = append(removable_fabrics, f) + } + } + t.Logf("removable_fabrics are %v", removable_fabrics) + fabrics = removable_fabrics if len(fabrics) == 0 { t.Skipf("Get Fabric card list for %q: got 0, want > 0", dut.Model()) } @@ -126,6 +139,11 @@ func TestOnChangeBackplaneCapacityCounters(t *testing.T) { fc := (len(fabrics) / 2) + 1 for _, f := range fabrics[:fc] { + empty, ok := gnmi.Lookup(t, dut, gnmi.OC().Component(f).Empty().State()).Val() + if ok && empty { + t.Logf("Fabric Component %s is empty, hence skipping", f) + continue + } 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) } @@ -134,6 +152,11 @@ func TestOnChangeBackplaneCapacityCounters(t *testing.T) { ts2, tocs2, apct2 := getBackplaneCapacityCounters(t, dut, ics) for _, f := range fabrics[:fc] { + empty, ok := gnmi.Lookup(t, dut, gnmi.OC().Component(f).Empty().State()).Val() + if ok && empty { + t.Logf("Fabric Component %s is empty, hence skipping", f) + continue + } gnmi.Replace(t, dut, gnmi.OC().Component(f).Fabric().PowerAdminState().Config(), oc.Platform_ComponentPowerType_POWER_ENABLED) if deviations.MissingValueForDefaults(dut) { time.Sleep(time.Minute) @@ -166,7 +189,7 @@ func TestOnChangeBackplaneCapacityCounters(t *testing.T) { switch { case !ok1 || !ok2 || !ok3: t.Errorf("BackplaneFacingCapacity Total not present: ok1 %t, ok2 %t, ok3 %t", ok1, ok2, ok3) - case v1 <= v2 || v1 != v3: + case v1 != v2 || v1 != v3: t.Errorf("BackplaneFacingCapacity Total are not valid: v1 %d, v2 %d, v3 %d", v1, v2, v3) } diff --git a/feature/platform/integrated_circuit/otg_tests/utilization_test/README.md b/feature/platform/integrated_circuit/otg_tests/utilization_test/README.md index 3aaeaef989b..42c2fa101f5 100644 --- a/feature/platform/integrated_circuit/otg_tests/utilization_test/README.md +++ b/feature/platform/integrated_circuit/otg_tests/utilization_test/README.md @@ -31,19 +31,29 @@ Test `used-threshold-upper` configuration and telemetry for hardware resources. * Get utilization percentages again and validate decrease in utilization. -## Config Parameter coverage - -* /system/utilization/resources/resource/config/name -* /system/utilization/resources/resource/config/used-threshold-upper -* /system/utilization/resources/resource/config/used-threshold-upper-clear - -## Telemetry Parameter coverage - -* /system/utilization/resources/resource/state/name -* /system/utilization/resources/resource/state/used-threshold-upper -* /system/utilization/resources/resource/state/used-threshold-upper-clear -* /components/component/integrated_circuit/utilization/resources/resource/state/name -* /components/component/integrated_circuit/utilization/resources/resource/state/used -* /components/component/integrated_circuit/utilization/resources/resource/state/free -* /components/component/integrated_circuit/utilization/resources/resource/state/used-threshold-upper -* /components/component/integrated_circuit/utilization/resources/resource/state/used-threshold-upper-clear +## 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: + ## Config parameter coverage + /system/utilization/resources/resource/config/name: + /system/utilization/resources/resource/config/used-threshold-upper: + /system/utilization/resources/resource/config/used-threshold-upper-clear: + + ## Telemetry parameter coverage + /system/utilization/resources/resource/state/name: + /system/utilization/resources/resource/state/used-threshold-upper: + /system/utilization/resources/resource/state/used-threshold-upper-clear: + /components/component/integrated-circuit/utilization/resources/resource/state/name: + platform_type: ["INTEGRATED_CIRCUIT"] + /components/component/integrated-circuit/utilization/resources/resource/state/used: + platform_type: ["INTEGRATED_CIRCUIT"] + /components/component/integrated-circuit/utilization/resources/resource/state/free: + platform_type: ["INTEGRATED_CIRCUIT"] +rpcs: + gnmi: + gNMI.Set: + gNMI.Subscribe: + Mode: [ "ON_CHANGE", "SAMPLE" ] +``` diff --git a/feature/platform/integrated_circuit/otg_tests/utilization_test/metadata.textproto b/feature/platform/integrated_circuit/otg_tests/utilization_test/metadata.textproto index 1fe40a1b24b..45d28a4dcdf 100644 --- a/feature/platform/integrated_circuit/otg_tests/utilization_test/metadata.textproto +++ b/feature/platform/integrated_circuit/otg_tests/utilization_test/metadata.textproto @@ -17,4 +17,13 @@ platform_exceptions: { missing_hardware_resource_telemetry_before_config: true } } +platform_exceptions: { + platform: { + vendor: NOKIA + } + deviations: { + explicit_interface_in_default_vrf: true + interface_enabled: true + } +} tags: TAGS_TRANSIT diff --git a/feature/platform/integrated_circuit/otg_tests/utilization_test/utilization_test.go b/feature/platform/integrated_circuit/otg_tests/utilization_test/utilization_test.go index 3cc22db7bf5..db1719b5c3f 100644 --- a/feature/platform/integrated_circuit/otg_tests/utilization_test/utilization_test.go +++ b/feature/platform/integrated_circuit/otg_tests/utilization_test/utilization_test.go @@ -20,6 +20,7 @@ import ( "github.com/open-traffic-generator/snappi/gosnappi" "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/components" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" "github.com/openconfig/featureprofiles/internal/otgutils" @@ -45,6 +46,7 @@ const ( var ( fibResource = map[ondatra.Vendor]string{ ondatra.ARISTA: "Routing/Resource6", + ondatra.NOKIA: "ip-lpm-routes", } dutPort1 = attrs.Attributes{ Desc: "dutPort1", @@ -105,58 +107,22 @@ func TestResourceUtilization(t *testing.T) { otgV6Peer, otgPort1, otgConfig := configureOTG(t, otg) verifyBgpTelemetry(t, dut) - - val, ok := gnmi.Watch(t, dut, gnmi.OC().System().Utilization().Resource(fibResource[dut.Vendor()]).ActiveComponentList().State(), time.Minute, func(v *ygnmi.Value[[]string]) bool { - cs, present := v.Val() - return present && len(cs) > 0 - }).Await(t) - if !ok { - switch { - case deviations.MissingHardwareResourceTelemetryBeforeConfig(dut): - t.Log("FIB resource is not active in any available components") - default: - t.Fatalf("FIB resource is not active in any available components") - } - } - comps, _ := val.Val() - gnmi.Replace(t, dut, gnmi.OC().System().Utilization().Resource(fibResource[dut.Vendor()]).Config(), &oc.System_Utilization_Resource{ Name: ygot.String(fibResource[dut.Vendor()]), UsedThresholdUpper: ygot.Uint8(usedThresholdUpper), UsedThresholdUpperClear: ygot.Uint8(usedThresholdUpperClear), }) - - val, ok = gnmi.Watch(t, dut, gnmi.OC().System().Utilization().Resource(fibResource[dut.Vendor()]).ActiveComponentList().State(), time.Minute, func(v *ygnmi.Value[[]string]) bool { - cs, present := v.Val() - return present && len(cs) > 0 - }).Await(t) - if !ok { - t.Fatalf("FIB resource is not active in any available components") - } - comps, _ = val.Val() - + comps := components.FindActiveComponentsByType(t, dut, oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_INTEGRATED_CIRCUIT) beforeUtzs := componentUtilizations(t, dut, comps) if len(beforeUtzs) != len(comps) { - t.Fatalf("Couldn't retrieve Utilization information for all Components in active-component-list") + t.Fatalf("Couldn't retrieve Utilization information for all Active Components") } - t.Run("Utilization Thresholds per Component", func(t *testing.T) { - for _, c := range comps { - t.Run(c, func(t *testing.T) { - if got, want := beforeUtzs[c].upperThreshold, usedThresholdUpper; got != want { - t.Errorf("used-upper-threshold mismatch for component: %s, got: %d, want: %d", c, got, want) - } - if got, want := beforeUtzs[c].upperThresholdClear, usedThresholdUpperClear; got != want { - t.Errorf("used-upper-threshold-clear mismatch for component: %s, got: %d, want: %d", c, got, want) - } - }) - } - }) injectBGPRoutes(t, otg, otgV6Peer, otgPort1, otgConfig) afterUtzs := componentUtilizations(t, dut, comps) if len(afterUtzs) != len(comps) { - t.Fatalf("Couldn't retrieve Utilization information for all Components in active-component-list") + t.Fatalf("Couldn't retrieve Utilization information for all Active Components") } t.Run("Utilization after BGP route installation", func(t *testing.T) { @@ -174,7 +140,7 @@ func TestResourceUtilization(t *testing.T) { afterClearUtzs := componentUtilizations(t, dut, comps) if len(afterClearUtzs) != len(comps) { - t.Fatalf("Couldn't retrieve Utilization information for all Components in active-component-list") + t.Fatalf("Couldn't retrieve Utilization information for all Active Components") } t.Run("Utilization after BGP route clear", func(t *testing.T) { diff --git a/feature/platform/tests/optics_power_and_bias_current_test/metadata.textproto b/feature/platform/tests/optics_power_and_bias_current_test/metadata.textproto index 4d62560e540..1b52681fdb6 100644 --- a/feature/platform/tests/optics_power_and_bias_current_test/metadata.textproto +++ b/feature/platform/tests/optics_power_and_bias_current_test/metadata.textproto @@ -14,14 +14,7 @@ platform_exceptions: { transceiver_thresholds_unsupported: true } } -platform_exceptions: { - platform: { - vendor: JUNIPER - } - deviations: { - transceiver_thresholds_unsupported: true - } -} + platform_exceptions: { platform: { vendor: ARISTA diff --git a/feature/platform/tests/telemetry_inventory_test/README.md b/feature/platform/tests/telemetry_inventory_test/README.md index 4b8a4368e5f..8fc1a679831 100644 --- a/feature/platform/tests/telemetry_inventory_test/README.md +++ b/feature/platform/tests/telemetry_inventory_test/README.md @@ -6,7 +6,7 @@ Validate Telemetry for each FRU within chassis. ## Procedure -For each of the following component types (linecard, chassis, fan, controller +For each of the following component types (linecard, chassis, fan, fan_tray, controller card, power supply, disk, flash, NPU, transceiver, fabric card), validate: * Presence of component within gNMI telemetry. @@ -22,39 +22,44 @@ card, power supply, disk, flash, NPU, transceiver, fabric card), validate: ## OpenConfig Path and RPC Coverage TODO: - - /components/component/storage - /components/component/software-module - /components/component/software-module/state/module-type - /components/component/state/mfg-date - /components/component/state/software-version + /components/component/storage + /components/component/software-module + /components/component/software-module/state/module-type + /components/component/state/mfg-date + /components/component/state/software-version ```yaml paths: /components/component/state/description: - platform_type: ["CHASSIS", "CONTROLLER_CARD", "FABRIC", "FAN", "LINECARD", "POWER_SUPPLY"] + platform_type: ["CHASSIS", "CONTROLLER_CARD", "FABRIC", "FAN", "FAN_TRAY", "LINECARD", "POWER_SUPPLY"] /components/component/state/firmware-version: platform_type: ["TRANSCEIVER"] /components/component/state/hardware-version: platform_type: ["CHASSIS", "CONTROLLER_CARD", "FABRIC", "LINECARD", "POWER_SUPPLY", "TRANSCEIVER"] /components/component/state/id: - platform_type: ["CONTROLLER_CARD", "FABRIC", "FAN", "INTEGRATED_CIRCUIT", "LINECARD", "POWER_SUPPLY", "SENSOR"] + platform_type: ["CONTROLLER_CARD", "FABRIC", "FAN", "FAN_TRAY", "INTEGRATED_CIRCUIT", "LINECARD", "POWER_SUPPLY", "SENSOR"] + /components/component/state/install-component: + platform_type: ["FABRIC", "FAN", "FAN_TRAY", "FRU", "CONTROLLER_CARD", "LINECARD", "POWER_SUPPLY", "TRANSCEIVER"] + /components/component/state/install-position: + platform_type: ["FABRIC", "FAN", "FAN_TRAY", "FRU", "CONTROLLER_CARD", "LINECARD", "POWER_SUPPLY", "TRANSCEIVER"] + /components/component/state/location: + platform_type: ["FABRIC", "FAN", "FAN_TRAY", "FRU", "CONTROLLER_CARD", "LINECARD", "POWER_SUPPLY", "TRANSCEIVER"] /components/component/state/mfg-name: platform_type: ["CHASSIS", "CONTROLLER_CARD", "FABRIC", "LINECARD", "POWER_SUPPLY", "TRANSCEIVER"] /components/component/state/model-name: platform_type: ["CHASSIS"] /components/component/state/name: - platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "INTEGRATED_CIRCUIT", "LINECARD", "POWER_SUPPLY", "SENSOR", "STORAGE", "TRANSCEIVER"] + platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "FAN_TRAY", "INTEGRATED_CIRCUIT", "LINECARD", "POWER_SUPPLY", "SENSOR", "STORAGE", "TRANSCEIVER"] /components/component/state/oper-status: - platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "INTEGRATED_CIRCUIT", "LINECARD", "POWER_SUPPLY", "STORAGE", "TRANSCEIVER"] + platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "FAN_TRAY", "INTEGRATED_CIRCUIT", "LINECARD", "POWER_SUPPLY", "STORAGE", "TRANSCEIVER"] /components/component/state/parent: - platform_type: ["CONTROLLER_CARD", "FABRIC", "LINECARD", "POWER_SUPPLY"] + platform_type: ["CONTROLLER_CARD", "FABRIC", "FAN", "FAN_TRAY", "LINECARD", "POWER_SUPPLY"] /components/component/state/part-no: - platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "LINECARD", "POWER_SUPPLY", "STORAGE", "TRANSCEIVER"] + platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "FAN_TRAY", "LINECARD", "POWER_SUPPLY", "STORAGE", "TRANSCEIVER"] /components/component/state/serial-no: - platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "LINECARD", "POWER_SUPPLY", "STORAGE", "TRANSCEIVER"] + platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "FAN_TRAY", "LINECARD", "POWER_SUPPLY", "STORAGE", "TRANSCEIVER"] /components/component/state/type: - platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "INTEGRATED_CIRCUIT", "LINECARD", "POWER_SUPPLY", "SENSOR", "STORAGE", "TRANSCEIVER"] + platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "FAN_TRAY", "INTEGRATED_CIRCUIT", "LINECARD", "POWER_SUPPLY", "SENSOR", "STORAGE", "TRANSCEIVER"] /components/component/state/temperature/alarm-status: platform_type: ["SENSOR"] /components/component/state/temperature/instant: @@ -63,6 +68,10 @@ paths: platform_type: ["SENSOR"] /components/component/state/temperature/max-time: platform_type: ["SENSOR"] + /components/component/subcomponents/subcomponent/name: + platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "FAN_TRAY", "INTEGRATED_CIRCUIT", "LINECARD", "POWER_SUPPLY", "SENSOR", "STORAGE", "TRANSCEIVER"] + /components/component/subcomponents/subcomponent/state/name: + platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "FAN_TRAY", "INTEGRATED_CIRCUIT", "LINECARD", "POWER_SUPPLY", "SENSOR", "STORAGE", "TRANSCEIVER"] /components/component/integrated-circuit/backplane-facing-capacity/state/available-pct: platform_type: ["INTEGRATED_CIRCUIT"] /components/component/integrated-circuit/backplane-facing-capacity/state/consumed-capacity: @@ -71,6 +80,12 @@ paths: platform_type: ["INTEGRATED_CIRCUIT"] /components/component/integrated-circuit/backplane-facing-capacity/state/total-operational-capacity: platform_type: ["INTEGRATED_CIRCUIT"] + /components/component/controller-card/config/power-admin-state: + platform_type: ["CONTROLLER_CARD"] + /components/component/fabric/config/power-admin-state: + platform_type: ["FABRIC"] + /components/component/linecard/config/power-admin-state: + platform_type: ["LINECARD"] rpcs: gnmi: diff --git a/feature/platform/tests/telemetry_inventory_test/metadata.textproto b/feature/platform/tests/telemetry_inventory_test/metadata.textproto index 0d7abed865b..74c89711589 100644 --- a/feature/platform/tests/telemetry_inventory_test/metadata.textproto +++ b/feature/platform/tests/telemetry_inventory_test/metadata.textproto @@ -10,6 +10,7 @@ platform_exceptions: { vendor: ARISTA } deviations: { + install_position_and_install_component_unsupported: true model_name_unsupported: true } } @@ -18,6 +19,7 @@ platform_exceptions: { vendor: CISCO } deviations: { + install_position_and_install_component_unsupported: true model_name_unsupported: true } } @@ -26,10 +28,11 @@ platform_exceptions: { vendor: JUNIPER } deviations: { - switch_chip_id_unsupported: true backplane_facing_capacity_unsupported: true + install_position_and_install_component_unsupported: true model_name_unsupported: true storage_component_unsupported: true + switch_chip_id_unsupported: true } } platform_exceptions: { @@ -37,8 +40,8 @@ platform_exceptions: { vendor: NOKIA } deviations: { - backplane_facing_capacity_unsupported: true + install_position_and_install_component_unsupported: true model_name_unsupported: true + skip_controller_card_power_admin: true } } - 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 21818e3a90a..22212c8ffd7 100644 --- a/feature/platform/tests/telemetry_inventory_test/telemetry_inventory_test.go +++ b/feature/platform/tests/telemetry_inventory_test/telemetry_inventory_test.go @@ -34,6 +34,7 @@ var componentType = map[string]oc.E_PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT{ "Fabric": oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_FABRIC, "Linecard": oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_LINECARD, "Fan": oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_FAN, + "Fan Tray": oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_FAN_TRAY, "PowerSupply": oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_POWER_SUPPLY, "Supervisor": oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CONTROLLER_CARD, "SwitchChip": oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_INTEGRATED_CIRCUIT, @@ -43,18 +44,49 @@ var componentType = map[string]oc.E_PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT{ "Storage": oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_STORAGE, } +// validInstallComponentTypes indicates for each component type, which types of +// install-component it can have (i.e., what types of components can it be installed into). +var validInstallComponentTypes = map[oc.Component_Type_Union]map[oc.Component_Type_Union]bool{ + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CONTROLLER_CARD: { + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CHASSIS: true, + }, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_FABRIC: { + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CHASSIS: true, + }, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_FAN: { + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CHASSIS: true, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_FAN_TRAY: true, + }, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_FAN_TRAY: { + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CHASSIS: true, + }, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_LINECARD: { + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CHASSIS: true, + }, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_POWER_SUPPLY: { + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CHASSIS: true, + // Sometimes the parent is the power tray, which has type FRU. + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_FRU: true, + }, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_TRANSCEIVER: { + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CHASSIS: true, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_LINECARD: true, + }, +} + // use this map to cache related components used in subtests to run the test faster. var componentsByType map[string][]*oc.Component // Define a superset of the checklist for each component type properties struct { - descriptionValidation bool - idValidation bool - nameValidation bool - partNoValidation bool - serialNoValidation bool - mfgNameValidation bool - mfgDateValidation bool + descriptionValidation bool + idValidation bool + installPositionAndComponentValidation bool + nameValidation bool + partNoValidation bool + serialNoValidation bool + mfgNameValidation bool + mfgDateValidation bool // If modelNameValidation is being used, the /components/component/state/model-name // of the chassis component must be equal to the ondatra hardware_model name // of its device. @@ -84,6 +116,7 @@ func TestMain(m *testing.M) { // - Fabric card // - FabricChip // - Fan +// - Fan Tray // - Supervisor or Controller // - Validate telemetry components/component/state/software-version. // - SwitchChip @@ -92,6 +125,8 @@ func TestMain(m *testing.M) { // - integrated-circuit/backplane-facing-capacity/state/consumed-capacity // - integrated-circuit/backplane-facing-capacity/state/total // - integrated-circuit/backplane-facing-capacity/state/total-operational-capacity +// - components/component/subcomponents/subcomponent/name +// - components/component/subcomponents/subcomponent/state/name // - Transceiver // - Storage // - Validate telemetry /components/component/storage exists. @@ -149,19 +184,20 @@ func TestHardwareCards(t *testing.T) { }, { desc: "Fabric", cardFields: properties{ - descriptionValidation: true, - idValidation: true, - nameValidation: true, - partNoValidation: true, - serialNoValidation: true, - mfgNameValidation: true, - mfgDateValidation: false, - hwVerValidation: true, - fwVerValidation: false, - rrValidation: false, - operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, - parentValidation: true, - pType: componentType["Fabric"], + descriptionValidation: true, + idValidation: true, + installPositionAndComponentValidation: true, + nameValidation: true, + partNoValidation: true, + serialNoValidation: true, + mfgNameValidation: true, + mfgDateValidation: false, + hwVerValidation: true, + fwVerValidation: false, + rrValidation: false, + operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, + parentValidation: true, + pType: componentType["Fabric"], }, }, { desc: "Fan", @@ -177,78 +213,99 @@ func TestHardwareCards(t *testing.T) { fwVerValidation: false, rrValidation: false, operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, - parentValidation: false, + parentValidation: true, pType: componentType["Fan"], }, }, { - desc: "Linecard", + desc: "Fan Tray", cardFields: properties{ descriptionValidation: true, - idValidation: true, + idValidation: false, nameValidation: true, partNoValidation: true, serialNoValidation: true, - mfgNameValidation: true, + mfgNameValidation: false, mfgDateValidation: false, - hwVerValidation: true, + hwVerValidation: false, fwVerValidation: false, rrValidation: false, operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, parentValidation: true, - pType: oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_LINECARD, + pType: componentType["Fan Tray"], + }, + }, { + desc: "Linecard", + cardFields: properties{ + descriptionValidation: true, + idValidation: true, + installPositionAndComponentValidation: true, + nameValidation: true, + partNoValidation: true, + serialNoValidation: true, + mfgNameValidation: true, + mfgDateValidation: false, + hwVerValidation: true, + fwVerValidation: false, + rrValidation: false, + operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, + parentValidation: true, + pType: oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_LINECARD, }, }, { desc: "PowerSupply", cardFields: properties{ - descriptionValidation: true, - idValidation: true, - nameValidation: true, - partNoValidation: true, - serialNoValidation: true, - mfgNameValidation: true, - mfgDateValidation: false, - hwVerValidation: true, - fwVerValidation: false, - rrValidation: false, - operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, - parentValidation: true, - pType: componentType["PowerSupply"], + descriptionValidation: true, + idValidation: true, + installPositionAndComponentValidation: true, + nameValidation: true, + partNoValidation: true, + serialNoValidation: true, + mfgNameValidation: true, + mfgDateValidation: false, + hwVerValidation: true, + fwVerValidation: false, + rrValidation: false, + operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, + parentValidation: true, + pType: componentType["PowerSupply"], }, }, { desc: "Supervisor", cardFields: properties{ - descriptionValidation: true, - idValidation: true, - nameValidation: true, - partNoValidation: true, - serialNoValidation: true, - mfgNameValidation: true, - mfgDateValidation: false, - swVerValidation: false, - hwVerValidation: true, - fwVerValidation: false, - rrValidation: true, - operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, - parentValidation: true, - pType: componentType["Supervisor"], + descriptionValidation: true, + idValidation: true, + installPositionAndComponentValidation: true, + nameValidation: true, + partNoValidation: true, + serialNoValidation: true, + mfgNameValidation: true, + mfgDateValidation: false, + swVerValidation: false, + hwVerValidation: true, + fwVerValidation: false, + rrValidation: true, + operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, + parentValidation: true, + pType: componentType["Supervisor"], }, }, { desc: "Transceiver", cardFields: properties{ - descriptionValidation: false, - idValidation: false, - nameValidation: true, - partNoValidation: true, - serialNoValidation: true, - mfgNameValidation: true, - mfgDateValidation: false, - swVerValidation: false, - hwVerValidation: true, - fwVerValidation: true, - rrValidation: false, - operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_UNSET, - parentValidation: false, - pType: componentType["Transceiver"], + descriptionValidation: false, + idValidation: false, + installPositionAndComponentValidation: true, + nameValidation: true, + partNoValidation: true, + serialNoValidation: true, + mfgNameValidation: true, + mfgDateValidation: false, + swVerValidation: false, + hwVerValidation: true, + fwVerValidation: true, + rrValidation: false, + operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_UNSET, + parentValidation: false, + pType: componentType["Transceiver"], }, }, { desc: "Cpu", @@ -299,6 +356,10 @@ func TestHardwareCards(t *testing.T) { t.Skip("Skip Linecard Telemetry check for fixed form factor devices.") } else if tc.desc == "Supervisor" && *args.NumControllerCards <= 0 { t.Skip("Skip Supervisor Telemetry check for fixed form factor devices.") + } else if tc.desc == "Fan Tray" && *args.NumFanTrays == 0 { + t.Skip("Skip Fan Tray Telemetry check for fixed form factor devices.") + } else if tc.desc == "Fan" && *args.NumFans == 0 { + t.Skip("Skip Fan Telemetry check for fixed form factor devices.") } cards := components[tc.desc] t.Logf("%s components count: %d", tc.desc, len(cards)) @@ -540,6 +601,26 @@ func TestControllerCardEmpty(t *testing.T) { } } +// validateSubcomponentsExistAsComponents checks that if the given component has subcomponents, that +// those subcomponents exist as components on the device (i.e. the leafref is valid). +func validateSubcomponentsExistAsComponents(c *oc.Component, components []*oc.Component, t *testing.T, dut *ondatra.DUTDevice) { + cName := c.GetName() + subcomponentsValue := gnmi.Lookup(t, dut, gnmi.OC().Component(cName).SubcomponentMap().State()) + subcomponents, ok := subcomponentsValue.Val() + if !ok { + // Not all components have subcomponents + // If the component doesn't have subcomponent, skip the check and return early + return + } + for _, subc := range subcomponents { + subcName := subc.GetName() + subComponent := gnmi.Lookup(t, dut, gnmi.OC().Component(subcName).State()) + if !subComponent.IsPresent() { + t.Errorf("Subcomponent %s does not exist as a component on the device", subcName) + } + } +} + func ValidateComponentState(t *testing.T, dut *ondatra.DUTDevice, cards []*oc.Component, p properties) { var validCards []*oc.Component switch p.pType { @@ -564,6 +645,7 @@ func ValidateComponentState(t *testing.T, dut *ondatra.DUTDevice, cards []*oc.Co } cName := card.GetName() t.Run(cName, func(t *testing.T) { + validateSubcomponentsExistAsComponents(card, validCards, t, dut) if p.descriptionValidation { t.Logf("Component %s Description: %s", cName, card.GetDescription()) if card.GetDescription() == "" { @@ -588,6 +670,14 @@ func ValidateComponentState(t *testing.T, dut *ondatra.DUTDevice, cards []*oc.Co } } + if p.installPositionAndComponentValidation && !deviations.InstallPositionAndInstallComponentUnsupported(dut) { + // If the component has a location and is removable, then it needs to have install-component + // and install-position. + if card.GetLocation() != "" && card.GetRemovable() { + testInstallComponentAndInstallPosition(t, card, validCards) + } + } + if p.nameValidation { name := card.GetName() t.Logf("Component %s Name: %s", cName, name) @@ -784,6 +874,38 @@ func ValidateComponentState(t *testing.T, dut *ondatra.DUTDevice, cards []*oc.Co } } +func testInstallComponentAndInstallPosition(t *testing.T, c *oc.Component, components []*oc.Component) { + icName := c.GetInstallComponent() + ip := c.GetInstallPosition() + hasInstallComponentAndPosition(t, c, icName, ip) + validateInstallComponent(t, icName, components, c) +} + +func validateInstallComponent(t *testing.T, icName string, components []*oc.Component, c *oc.Component) { + compMap := compNameMap(t, ondatra.DUT(t, "dut")) + ic, ok := compMap[icName] + if !ok { + t.Errorf("Component %s's install-component %s is not in component tree", icName, c.GetName()) + return + } + validTypes := validInstallComponentTypes[c.GetType()] + icType := ic.GetType() + if !validTypes[icType] { + t.Errorf("Component %s's install-component %s is not a supported parent type (%s)", c.GetName(), icName, icType) + } +} + +func hasInstallComponentAndPosition(t *testing.T, c *oc.Component, icName string, ip string) { + if icName == "" { + t.Errorf("Component %s is missing install-component", c.GetName()) + return + } + if ip == "" { + t.Errorf("Component %s is missing install-position", c.GetName()) + return + } +} + func TestStorage(t *testing.T) { // TODO: Add Storage test case here once supported. t.Skipf("Telemetry path /components/component/storage is not supported.") @@ -818,14 +940,19 @@ func TestHeatsinkTempSensor(t *testing.T) { t.Skipf("/components/component[name=]/state/temperature/instant is not supported.") } -func TestInterfaceComponentHierarchy(t *testing.T) { - dut := ondatra.DUT(t, "dut") - - // Map of component Name to corresponding Component OC object. +// Creates a map of component Name to corresponding Component OC object. +func compNameMap(t *testing.T, dut *ondatra.DUTDevice) map[string]*oc.Component { compMap := make(map[string]*oc.Component) for _, c := range gnmi.GetAll(t, dut, gnmi.OC().ComponentAny().State()) { compMap[c.GetName()] = c } + return compMap +} + +func TestInterfaceComponentHierarchy(t *testing.T) { + dut := ondatra.DUT(t, "dut") + + compMap := compNameMap(t, dut) // Map of populated Transceivers to a random integer. transceivers := make(map[string]int) @@ -898,3 +1025,57 @@ func TestInterfaceComponentHierarchy(t *testing.T) { t.Fatalf("Couldn't find chassis for %q", dut.Model()) } } + +func TestDefaultPowerAdminState(t *testing.T) { + dut := ondatra.DUT(t, "dut") + + fabrics := []*oc.Component{} + linecards := []*oc.Component{} + supervisors := []*oc.Component{} + + components := gnmi.GetAll(t, dut, gnmi.OC().ComponentAny().State()) + for compName := range componentType { + for _, c := range components { + if c.GetType() == nil || c.GetType() != componentType[compName] { + continue + } + switch compName { + case "Fabric": + fabrics = append(fabrics, c) + case "Linecard": + linecards = append(linecards, c) + case "Supervisor": + supervisors = append(supervisors, c) + } + } + } + + t.Logf("Fabrics: %v", fabrics) + t.Logf("Linecards: %v", linecards) + t.Logf("Supervisors: %v", supervisors) + + if len(fabrics) != 0 { + pas := gnmi.Get(t, dut, gnmi.OC().Component(fabrics[0].GetName()).Fabric().PowerAdminState().Config()) + t.Logf("Component %s PowerAdminState: %v", fabrics[0].GetName(), pas) + if pas == oc.Platform_ComponentPowerType_UNSET { + t.Errorf("Component %s PowerAdminState is unset", fabrics[0].GetName()) + } + } + + if len(linecards) != 0 { + pas := gnmi.Get(t, dut, gnmi.OC().Component(linecards[0].GetName()).Linecard().PowerAdminState().Config()) + t.Logf("Component %s PowerAdminState: %v", linecards[0].GetName(), pas) + if pas == oc.Platform_ComponentPowerType_UNSET { + t.Errorf("Component %s PowerAdminState is unset", linecards[0].GetName()) + } + } + if !deviations.SkipControllerCardPowerAdmin(dut) { + if len(supervisors) != 0 { + pas := gnmi.Get(t, dut, gnmi.OC().Component(supervisors[0].GetName()).ControllerCard().PowerAdminState().Config()) + t.Logf("Component %s PowerAdminState: %v", supervisors[0].GetName(), pas) + if pas == oc.Platform_ComponentPowerType_UNSET { + t.Errorf("Component %s PowerAdminState is unset", supervisors[0].GetName()) + } + } + } +} diff --git a/feature/platform/transceiver/tests/zr_cd_test/metadata.textproto b/feature/platform/transceiver/tests/zr_cd_test/metadata.textproto index 5810d2322b9..0cbcbfbaae7 100644 --- a/feature/platform/transceiver/tests/zr_cd_test/metadata.textproto +++ b/feature/platform/transceiver/tests/zr_cd_test/metadata.textproto @@ -9,8 +9,7 @@ platform_exceptions: { 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_cd_test/zr_cd_test.go b/feature/platform/transceiver/tests/zr_cd_test/zr_cd_test.go index 08bf2f7e209..95312d2398b 100644 --- a/feature/platform/transceiver/tests/zr_cd_test/zr_cd_test.go +++ b/feature/platform/transceiver/tests/zr_cd_test/zr_cd_test.go @@ -4,19 +4,18 @@ import ( "testing" "time" - "github.com/openconfig/featureprofiles/internal/components" + "github.com/openconfig/featureprofiles/internal/cfgplugins" "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 + minCDValue = -200 maxCDValue = 2400 inActiveCDValue = 0.0 timeout = 10 * time.Minute @@ -31,29 +30,18 @@ const ( ) var ( - frequencies = []uint64{191400000, 196100000} - targetOutputPowers = []float64{-6, -10} + frequencies = []uint64{191400000, 196100000} // 400ZR OIF wavelength range + targetOutputPowers = []float64{-13, -9} // 400ZR OIF Tx power range ) 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 := components.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() + CDSampleNexts := pStream.Nexts(2) + CDSample := CDSampleNexts[1] + t.Logf("CDSampleNexts %v", CDSampleNexts) if CDSample == nil { t.Fatalf("CD telemetry %s was not streamed in the most recent subscription interval", sensorName) } @@ -68,85 +56,99 @@ func verifyCDValue(t *testing.T, dut1 *ondatra.DUTDevice, pStream *samplestream. t.Fatalf("The inactive CD is %v, expected %v", CDVal, inActiveCDValue) } case status == enabled: - if CDVal < minCDValue && CDVal > maxCDValue { + 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) + // Get current time + now := time.Now() + // Format the time string + formattedTime := now.Format("2006-01-02 15:04:05") + t.Logf("%s Device %v CD %s value at status %v: %v", formattedTime, dut1.Name(), sensorName, status, CDVal) + return CDVal } +// TODO: Avg and Instant value checks are not available. Need to align their sample streaming windows. 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) - } - + verifyCDValue(t, dut1, p1StreamInstant, "Instant", status) + verifyCDValue(t, dut1, p1StreamMax, "Max", status) + verifyCDValue(t, dut1, p1StreamMin, "Min", status) + 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) + dut := ondatra.DUT(t, "dut") + fptest.ConfigureDefaultNetworkInstance(t, dut) + + dp1 := dut.Port(t, "port1") + dp2 := dut.Port(t, "port2") + cfgplugins.InterfaceConfig(t, dut, dp1) + cfgplugins.InterfaceConfig(t, dut, dp2) - // 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) + tr1 := gnmi.Get(t, dut, gnmi.OC().Interface(dp1.Name()).Transceiver().State()) + tr2 := gnmi.Get(t, dut, gnmi.OC().Interface(dp2.Name()).Transceiver().State()) + och1 := gnmi.Get(t, dut, gnmi.OC().Component(tr1).Transceiver().Channel(0).AssociatedOpticalChannel().State()) + och2 := gnmi.Get(t, dut, gnmi.OC().Component(tr2).Transceiver().Channel(0).AssociatedOpticalChannel().State()) + component1 := gnmi.OC().Component(och1) for _, frequency := range frequencies { for _, targetOutputPower := range targetOutputPowers { - interfaceConfig(t, dut1, dp1, frequency, targetOutputPower) - interfaceConfig(t, dut1, dp2, frequency, targetOutputPower) + cfgplugins.ConfigOpticalChannel(t, dut, och1, frequency, targetOutputPower, dp16QAM) + cfgplugins.ConfigOpticalChannel(t, dut, och2, frequency, targetOutputPower, dp16QAM) + // 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) + gnmi.Await(t, dut, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + gnmi.Await(t, dut, 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) + p1StreamInstant := samplestream.New(t, dut, component1.OpticalChannel().ChromaticDispersion().Instant().State(), samplingInterval) + p1StreamMin := samplestream.New(t, dut, component1.OpticalChannel().ChromaticDispersion().Min().State(), samplingInterval) + p1StreamMax := samplestream.New(t, dut, component1.OpticalChannel().ChromaticDispersion().Max().State(), samplingInterval) + p1StreamAvg := samplestream.New(t, dut, component1.OpticalChannel().ChromaticDispersion().Avg().State(), samplingInterval) - verifyAllCDValues(t, dut1, p1StreamInstant, p1StreamMax, p1StreamMin, p1StreamAvg, enabled) + defer p1StreamInstant.Close() + defer p1StreamMin.Close() + defer p1StreamMax.Close() + defer p1StreamAvg.Close() - // 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) + verifyAllCDValues(t, dut, p1StreamInstant, p1StreamMax, p1StreamMin, p1StreamAvg, enabled) + + // Disable interface. + for _, p := range dut.Ports() { + cfgplugins.ToggleInterface(t, dut, p.Name(), 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) + gnmi.Await(t, dut, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_DOWN) + gnmi.Await(t, dut, gnmi.OC().Interface(dp2.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_DOWN) + t.Logf("Interfaces are down: %v, %v", dp1.Name(), dp2.Name()) + verifyAllCDValues(t, dut, p1StreamInstant, p1StreamMax, p1StreamMin, p1StreamAvg, enabled) - 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) + // Enable interface. + for _, p := range dut.Ports() { + cfgplugins.ToggleInterface(t, dut, p.Name(), 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) + gnmi.Await(t, dut, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + gnmi.Await(t, dut, gnmi.OC().Interface(dp2.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + t.Logf("Interfaces are up: %v, %v", dp1.Name(), dp2.Name()) + verifyAllCDValues(t, dut, p1StreamInstant, p1StreamMax, p1StreamMin, p1StreamAvg, enabled) - p1StreamMin.Close() - p1StreamMax.Close() - p1StreamAvg.Close() - p1StreamInstant.Close() } } } 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 index ce879e31d9c..b71efad53db 100644 --- 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 @@ -112,11 +112,11 @@ func TestZRFirmwareVersionStateInterfaceFlap(t *testing.T) { 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) + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), 2*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) + gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), 2*time.Minute, oc.Interface_OperStatus_UP) verifyFirmwareVersionValue(t, dut1, p1Stream) } diff --git a/feature/platform/transceiver/tests/zr_input_output_power_test/README.md b/feature/platform/transceiver/tests/zr_input_output_power_test/README.md index 0a6fbcdf506..f31a15d0b0b 100644 --- a/feature/platform/transceiver/tests/zr_input_output_power_test/README.md +++ b/feature/platform/transceiver/tests/zr_input_output_power_test/README.md @@ -97,21 +97,41 @@ power. * Typical min/max value range for RX Signal Power -14 to 0 dbm. * Typical min/max value range for TX Output Power -10 to -6 dbm. -## Config Parameter coverage - -* /components/component/transceiver/config/enabled - -## Telemetry Parameter coverage - -* /components/component/optical-channel/state/input-power/instant -* /components/component/optical-channel/state/input-power/avg -* /components/component/optical-channel/state/input-power/min -* /components/component/optical-channel/state/input-power/max -* /components/component/optical-channel/state/output-power/instant -* /components/component/optical-channel/state/output-power/avg -* /components/component/optical-channel/state/output-power/min -* /components/component/optical-channel/state/output-power/max -* /components/component/transceiver/physical-channel/channel/state/input-power/instant -* /components/component/transceiver/physical-channel/channel/state/input-power/min -* /components/component/transceiver/physical-channel/channel/state/input-power/max -* /components/component/transceiver/physical-channel/channel/state/input-power/avg \ No newline at end of file +## OpenConfig Path and RPC Coverage + +```yaml +paths: + # Config Parameter coverage + /interfaces/interface/config/enabled: + # Telemetry Parameter coverage + /components/component/optical-channel/state/input-power/instant: + platform_type: ["OPTICAL_CHANNEL"] + /components/component/optical-channel/state/input-power/avg: + platform_type: ["OPTICAL_CHANNEL"] + /components/component/optical-channel/state/input-power/min: + platform_type: ["OPTICAL_CHANNEL"] + /components/component/optical-channel/state/input-power/max: + platform_type: ["OPTICAL_CHANNEL"] + /components/component/optical-channel/state/output-power/instant: + platform_type: ["OPTICAL_CHANNEL"] + /components/component/optical-channel/state/output-power/avg: + platform_type: ["OPTICAL_CHANNEL"] + /components/component/optical-channel/state/output-power/min: + platform_type: ["OPTICAL_CHANNEL"] + /components/component/optical-channel/state/output-power/max: + platform_type: ["OPTICAL_CHANNEL"] + /components/component/transceiver/physical-channels/channel/state/input-power/instant: + platform_type: [ "TRANSCEIVER" ] + /components/component/transceiver/physical-channels/channel/state/input-power/min: + platform_type: [ "TRANSCEIVER" ] + /components/component/transceiver/physical-channels/channel/state/input-power/max: + platform_type: [ "TRANSCEIVER" ] + /components/component/transceiver/physical-channels/channel/state/input-power/avg: + platform_type: [ "TRANSCEIVER" ] + +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: +``` \ No newline at end of file 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 index cfabe297f68..70cbe802de8 100644 --- a/feature/platform/transceiver/tests/zr_input_output_power_test/metadata.textproto +++ b/feature/platform/transceiver/tests/zr_input_output_power_test/metadata.textproto @@ -4,4 +4,12 @@ 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 +testbed: TESTBED_DUT_400ZR +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + default_network_instance: "default" + } +} 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 index 0ed4461fcee..5395d293aeb 100644 --- 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 @@ -1,413 +1,201 @@ -// 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/cfgplugins" "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" + "github.com/openconfig/ygnmi/ygnmi" ) 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 + dp16QAM = uint16(1) + samplingInterval = 10 * time.Second + inactiveOCHRxPower = -30.0 + inactiveOCHTxPower = -30.0 + inactiveTransceiverRxPower = -20.0 + rxPowerReadingError = 2 + txPowerReadingError = 0.5 + timeout = 10 * time.Minute ) -type testData struct { - transceiverName string - dut *ondatra.DUTDevice - transceiverOpticalChannelName string - interfaceName string -} +var ( + frequencies = []uint64{191400000, 196100000} + targetOpticalPowers = []float64{-9, -13} +) 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()) -} +func TestOpticalPower(t *testing.T) { + dut := ondatra.DUT(t, "dut") -// 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()) + fptest.ConfigureDefaultNetworkInstance(t, dut) - 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) - } + var ( + trs = make(map[string]string) + ochs = make(map[string]string) + ) - 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()) + for _, 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()) } - 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) + // 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()) } - 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()) - } + for _, frequency := range frequencies { + for _, targetOpticalPower := range targetOpticalPowers { + // Configure OCH component and OTN and ETH logical channels. + for _, p := range dut.Ports() { + cfgplugins.ConfigOpticalChannel(t, dut, ochs[p.Name()], frequency, targetOpticalPower, dp16QAM) + } - } 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()) - } -} + // Create sample steams for each port. + ochStreams := make(map[string]*samplestream.SampleStream[*oc.Component_OpticalChannel]) + trStreams := make(map[string]*samplestream.SampleStream[*oc.Component_Transceiver_Channel]) + interfaceStreams := make(map[string]*samplestream.SampleStream[*oc.Interface]) + for portName, och := range ochs { + ochStreams[portName] = samplestream.New(t, dut, gnmi.OC().Component(och).OpticalChannel().State(), samplingInterval) + trStreams[portName] = samplestream.New(t, dut, gnmi.OC().Component(trs[portName]).Transceiver().Channel(0).State(), samplingInterval) + interfaceStreams[portName] = samplestream.New(t, dut, gnmi.OC().Interface(portName).State(), samplingInterval) + defer ochStreams[portName].Close() + defer trStreams[portName].Close() + defer interfaceStreams[portName].Close() + } -// 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) + // Enable interface. + for _, p := range dut.Ports() { + cfgplugins.ToggleInterface(t, dut, p.Name(), true) + } - 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) + // Wait for streaming telemetry to report the channels as up. + for _, p := range dut.Ports() { + gnmi.Await(t, dut, gnmi.OC().Interface(p.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + } - 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)) - } + time.Sleep(3 * samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. - 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) + validateAllSampleStreams(t, dut, true, interfaceStreams, ochStreams, trStreams, targetOpticalPower) - 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() + // Disable interface. + for _, p := range dut.Ports() { + cfgplugins.ToggleInterface(t, dut, p.Name(), false) + } - transceiverOpticalChanelOutputPower, ok := transceiverOpticalOutputPowerStream.Next().Val() - if !ok { - t.Errorf("[Error]:Trasceiver = %v Output Power not received !", tc.transceiverOpticalChannelName) - } + // Wait for streaming telemetry to report the channels as down. + for _, p := range dut.Ports() { + gnmi.Await(t, dut, gnmi.OC().Interface(p.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_DOWN) + } + time.Sleep(3 * samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. - transceiverOpticalChanelInputPower, ok := opticalInputPowerStream.Next().Val() - if !ok { - t.Errorf("[Error]:Trasceiver = %v Power not received !", tc.transceiverOpticalChannelName) - } + validateAllSampleStreams(t, dut, false, interfaceStreams, ochStreams, trStreams, targetOpticalPower) - verifyValidTxOutputPower(t, transceiverOpticalChanelOutputPower, false, tc.transceiverOpticalChannelName, tc.transceiverName) - verifyValidRxInputPower(t, transceiverOpticalChanelInputPower, false, tc.transceiverOpticalChannelName, tc.transceiverName) + // Re-enable transceivers. + for _, p := range dut.Ports() { + cfgplugins.ToggleInterface(t, dut, p.Name(), true) + } - 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() + // Wait for streaming telemetry to report the channels as up. + for _, p := range dut.Ports() { + gnmi.Await(t, dut, gnmi.OC().Interface(p.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + } + time.Sleep(3 * samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. - if transceiverRxTotalInputPower == nil || got == false { - t.Errorf("[Error]:Didn't recieve Rx total InputPower sample in 10 seconds for the transceiever = %v", tc.transceiverName) + validateAllSampleStreams(t, dut, true, interfaceStreams, ochStreams, trStreams, targetOpticalPower) + } } +} - 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()) +// validateAllSampleStreams validates all the sample streams. +func validateAllSampleStreams(t *testing.T, dut *ondatra.DUTDevice, isEnabled bool, interfaceStreams map[string]*samplestream.SampleStream[*oc.Interface], ochStreams map[string]*samplestream.SampleStream[*oc.Component_OpticalChannel], transceiverStreams map[string]*samplestream.SampleStream[*oc.Component_Transceiver_Channel], targetOpticalPower float64) { + for _, p := range dut.Ports() { + for valIndex := range interfaceStreams[p.Name()].All() { + if valIndex >= len(ochStreams[p.Name()].All()) || valIndex >= len(transceiverStreams[p.Name()].All()) { + break + } + operStatus := validateSampleStream(t, interfaceStreams[p.Name()].All()[valIndex], ochStreams[p.Name()].All()[valIndex], transceiverStreams[p.Name()].All()[valIndex], p.Name(), targetOpticalPower) + switch operStatus { + case oc.Interface_OperStatus_UP: + if !isEnabled { + t.Errorf("Invalid %v operStatus value: want DOWN, got %v", p.Name(), operStatus) + } + case oc.Interface_OperStatus_DOWN: + if isEnabled { + t.Errorf("Invalid %v operStatus value: want UP, got %v", p.Name(), operStatus) + } + } + } } - } -// 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) +// validateSampleStream validates the stream data. +func validateSampleStream(t *testing.T, interfaceData *ygnmi.Value[*oc.Interface], ochData *ygnmi.Value[*oc.Component_OpticalChannel], transceiverData *ygnmi.Value[*oc.Component_Transceiver_Channel], portName string, targetOpticalPower float64) oc.E_Interface_OperStatus { + if interfaceData == nil { + t.Errorf("Data not received for port %v.", portName) + return oc.Interface_OperStatus_UNSET } - rebootRequest := &gnps.RebootRequest{ - Method: gnps.RebootMethod_COLD, - Force: true, + interfaceValue, ok := interfaceData.Val() + if !ok { + t.Errorf("Channel data is empty for port %v.", portName) + return oc.Interface_OperStatus_UNSET } - 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()) - } + operStatus := interfaceValue.GetOperStatus() + if operStatus == oc.Interface_OperStatus_UNSET { + t.Errorf("Link state data is empty for port %v", portName) + return oc.Interface_OperStatus_UNSET } - - 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) + ochValue, ok := ochData.Val() + if !ok { + t.Errorf("Terminal Device data is empty for port %v.", portName) + return oc.Interface_OperStatus_UNSET } - 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) + if inPow := ochValue.GetInputPower(); inPow == nil { + t.Errorf("InputPower data is empty for port %v", portName) + } else { + validatePowerValue(t, portName, "OpticalChannelInputPower", inPow.GetInstant(), inPow.GetMin(), inPow.GetMax(), inPow.GetAvg(), targetOpticalPower-rxPowerReadingError, targetOpticalPower+rxPowerReadingError, inactiveOCHRxPower, operStatus) } - 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) - } + if outPow := ochValue.GetOutputPower(); outPow == nil { + t.Errorf("OutputPower data is empty for port %v", portName) + } else { + validatePowerValue(t, portName, "OpticalChannelOutputPower", outPow.GetInstant(), outPow.GetMin(), outPow.GetMax(), outPow.GetAvg(), targetOpticalPower-txPowerReadingError, targetOpticalPower+txPowerReadingError, inactiveOCHTxPower, operStatus) } - 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) + transceiverValue, ok := transceiverData.Val() + if !ok { + t.Errorf("Transceiver data is empty for port %v.", portName) + return oc.Interface_OperStatus_UNSET } - -} - -// 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(), - }, + if inPow := transceiverValue.GetInputPower(); inPow == nil { + t.Errorf("InputPower data is empty for port %v", portName) + } else { + validatePowerValue(t, portName, "TransceiverInputPower", inPow.GetInstant(), inPow.GetMin(), inPow.GetMax(), inPow.GetAvg(), targetOpticalPower-rxPowerReadingError, targetOpticalPower+rxPowerReadingError, inactiveTransceiverRxPower, operStatus) } + return operStatus +} - 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) +// validatePowerValue validates the power value. +func validatePowerValue(t *testing.T, portName, pm string, instant, min, max, avg, minAllowed, maxAllowed, inactiveValue float64, operStatus oc.E_Interface_OperStatus) { + switch operStatus { + case oc.Interface_OperStatus_UP: + if instant < minAllowed || instant > 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 } - }) - 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) + case oc.Interface_OperStatus_DOWN: + if instant > 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 } - }) - - // Todo: ## TRANSCEIVER-4.4 . - + } + t.Logf("Valid %v sample when %v is %v --> min : %v, max : %v, avg : %v, instant : %v", pm, portName, operStatus, min, max, avg, instant) } diff --git a/feature/platform/transceiver/tests/zr_inventory_test/metadata.textproto b/feature/platform/transceiver/tests/zr_inventory_test/metadata.textproto index 224ebd16ed8..02b3b95028f 100644 --- a/feature/platform/transceiver/tests/zr_inventory_test/metadata.textproto +++ b/feature/platform/transceiver/tests/zr_inventory_test/metadata.textproto @@ -9,7 +9,7 @@ platform_exceptions: { 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_inventory_test/zr_inventory_test.go b/feature/platform/transceiver/tests/zr_inventory_test/zr_inventory_test.go index fcd271c36e1..66703f9a251 100644 --- a/feature/platform/transceiver/tests/zr_inventory_test/zr_inventory_test.go +++ b/feature/platform/transceiver/tests/zr_inventory_test/zr_inventory_test.go @@ -4,13 +4,12 @@ import ( "testing" "time" - "github.com/openconfig/featureprofiles/internal/components" + "github.com/openconfig/featureprofiles/internal/cfgplugins" "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 ( @@ -20,28 +19,10 @@ const ( 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 := components.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() @@ -72,58 +53,62 @@ func verifyAllInventoryValues(t *testing.T, pStreamsStr []*samplestream.SampleSt } func TestInventory(t *testing.T) { - dut1 := ondatra.DUT(t, "dut") - dp1 := dut1.Port(t, "port1") - dp2 := dut1.Port(t, "port2") - fptest.ConfigureDefaultNetworkInstance(t, dut1) + dut := ondatra.DUT(t, "dut") + dp1 := dut.Port(t, "port1") + dp2 := dut.Port(t, "port2") + fptest.ConfigureDefaultNetworkInstance(t, dut) + cfgplugins.InterfaceConfig(t, dut, dp1) + cfgplugins.InterfaceConfig(t, dut, dp2) // 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()) + tr1 := gnmi.Get(t, dut, gnmi.OC().Interface(dp1.Name()).Transceiver().State()) + tr2 := gnmi.Get(t, dut, 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) + gnmi.Await(t, dut, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + gnmi.Await(t, dut, 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] + + // TODO: b/333021032 - Uncomment the description check from the test once the bug is fixed. 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), + samplestream.New(t, dut, component1.SerialNo().State(), samplingInterval), + samplestream.New(t, dut, component1.PartNo().State(), samplingInterval), + samplestream.New(t, dut, component1.MfgName().State(), samplingInterval), + samplestream.New(t, dut, component1.MfgDate().State(), samplingInterval), + samplestream.New(t, dut, component1.HardwareVersion().State(), samplingInterval), + samplestream.New(t, dut, component1.FirmwareVersion().State(), samplingInterval), + // samplestream.New(t, dut1, component1.Description().State(), samplingInterval), ) - p1StreamsUnion = append(p1StreamsUnion, samplestream.New(t, dut1, component1.Type().State(), samplingInterval)) + p1StreamsUnion = append(p1StreamsUnion, samplestream.New(t, dut, 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) + for _, p := range dut.Ports() { + cfgplugins.ToggleInterface(t, dut, p.Name(), 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) + gnmi.Await(t, dut, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_DOWN) + gnmi.Await(t, dut, 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) + for _, p := range dut.Ports() { + cfgplugins.ToggleInterface(t, dut, p.Name(), 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) + gnmi.Await(t, dut, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + gnmi.Await(t, dut, 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) 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 index f7ad7308c3c..06dca4f91ed 100644 --- 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 @@ -6,19 +6,25 @@ import ( "github.com/google/go-cmp/cmp" "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/cfgplugins" "github.com/openconfig/featureprofiles/internal/components" "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 + samplingInterval = 10 * time.Second + timeout = 10 * time.Minute + otnIndex1 = uint32(4001) + otnIndex2 = uint32(4002) + ethernetIndex1 = uint32(40001) + ethernetIndex2 = uint32(40002) ) var ( @@ -52,75 +58,35 @@ func Test400ZRLogicalChannels(t *testing.T) { oc1 := components.OpticalChannelComponentFromPort(t, dut, p1) oc2 := components.OpticalChannelComponentFromPort(t, dut, p2) + tr1 := gnmi.Get(t, dut, gnmi.OC().Interface(p1.Name()).Transceiver().State()) + tr2 := gnmi.Get(t, dut, gnmi.OC().Interface(p2.Name()).Transceiver().State()) - configureLogicalChannels(t, dut, 40000, 40001, oc1) - configureLogicalChannels(t, dut, 40002, 40003, oc2) + cfgplugins.ConfigOpticalChannel(t, dut, oc1, frequency, targetOutputPower, dp16QAM) + cfgplugins.ConfigOTNChannel(t, dut, oc1, otnIndex1, ethernetIndex1) + cfgplugins.ConfigETHChannel(t, dut, p1.Name(), tr1, otnIndex1, ethernetIndex1) + cfgplugins.ConfigOpticalChannel(t, dut, oc2, frequency, targetOutputPower, dp16QAM) + cfgplugins.ConfigOTNChannel(t, dut, oc2, otnIndex2, ethernetIndex2) + cfgplugins.ConfigETHChannel(t, dut, p2.Name(), tr2, otnIndex2, ethernetIndex2) - ethChan1 := samplestream.New(t, dut, gnmi.OC().TerminalDevice().Channel(40000).State(), 10*time.Second) + ethChan1 := samplestream.New(t, dut, gnmi.OC().TerminalDevice().Channel(ethernetIndex1).State(), samplingInterval) 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) + ethChan2 := samplestream.New(t, dut, gnmi.OC().TerminalDevice().Channel(ethernetIndex2).State(), samplingInterval) 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) + otnChan1 := samplestream.New(t, dut, gnmi.OC().TerminalDevice().Channel(otnIndex1).State(), samplingInterval) + defer otnChan1.Close() + otnChan2 := samplestream.New(t, dut, gnmi.OC().TerminalDevice().Channel(otnIndex2).State(), samplingInterval) + defer otnChan2.Close() + + gnmi.Await(t, dut, gnmi.OC().Interface(p1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + gnmi.Await(t, dut, gnmi.OC().Interface(p2.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) + + validateEthernetChannelTelemetry(t, otnIndex1, ethernetIndex1, ethChan1) + validateEthernetChannelTelemetry(t, otnIndex2, ethernetIndex2, ethChan2) + validateOTNChannelTelemetry(t, otnIndex1, ethernetIndex1, oc1, otnChan1) + validateOTNChannelTelemetry(t, otnIndex2, ethernetIndex2, oc2, otnChan2) } -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]) { +func validateEthernetChannelTelemetry(t *testing.T, otnChIdx, ethernetChIdx 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") @@ -139,11 +105,6 @@ func validateEthernetChannelTelemetry(t *testing.T, ethernetChIdx, coherentChIdx got: ec.GetIndex(), want: ethernetChIdx, }, - { - desc: "Admin State", - got: ec.GetAdminState().String(), - want: oc.TerminalDevice_AdminStateType_ENABLED.String(), - }, { desc: "Description", got: ec.GetDescription(), @@ -154,11 +115,6 @@ func validateEthernetChannelTelemetry(t *testing.T, ethernetChIdx, coherentChIdx 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(), @@ -166,27 +122,27 @@ func validateEthernetChannelTelemetry(t *testing.T, ethernetChIdx, coherentChIdx }, { desc: "Assignment: Index", - got: ec.GetAssignment(1).GetIndex(), - want: uint32(1), + got: ec.GetAssignment(0).GetIndex(), + want: uint32(0), }, { desc: "Assignment: Logical Channel", - got: ec.GetAssignment(1).GetLogicalChannel(), - want: coherentChIdx, + got: ec.GetAssignment(0).GetLogicalChannel(), + want: otnChIdx, }, { desc: "Assignment: Description", - got: ec.GetAssignment(1).GetDescription(), - want: "ETH to Coherent", + got: ec.GetAssignment(0).GetDescription(), + want: "ETH to OTN", }, { desc: "Assignment: Allocation", - got: ec.GetAssignment(1).GetAllocation(), + got: ec.GetAssignment(0).GetAllocation(), want: float64(400), }, { desc: "Assignment: Type", - got: ec.GetAssignment(1).GetAssignmentType().String(), + got: ec.GetAssignment(0).GetAssignmentType().String(), want: oc.Assignment_AssignmentType_LOGICAL_CHANNEL.String(), }, } @@ -199,34 +155,29 @@ func validateEthernetChannelTelemetry(t *testing.T, ethernetChIdx, coherentChIdx } } -func validateCoherentChannelTelemetry(t *testing.T, coherentChIdx uint32, opticalChannel string, stream *samplestream.SampleStream[*oc.TerminalDevice_Channel]) { +func validateOTNChannelTelemetry(t *testing.T, otnChIdx uint32, ethChIdx 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") + t.Fatalf("OTN 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") + t.Fatalf("OTN 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", + want: "OTN Logical Channel", }, { desc: "Index", got: cc.GetIndex(), - want: coherentChIdx, + want: otnChIdx, }, { desc: "Logical Channel Type", @@ -234,36 +185,61 @@ func validateCoherentChannelTelemetry(t *testing.T, coherentChIdx uint32, optica want: oc.TransportTypes_LOGICAL_ELEMENT_PROTOCOL_TYPE_PROT_OTN.String(), }, { - desc: "Assignment: Index", + desc: "Optical Channel Assignment: Index", + got: cc.GetAssignment(0).GetIndex(), + want: uint32(0), + }, + { + desc: "Optical Channel Assignment: Optical Channel", + got: cc.GetAssignment(0).GetOpticalChannel(), + want: opticalChannel, + }, + { + desc: "Optical Channel Assignment: Description", + got: cc.GetAssignment(0).GetDescription(), + want: "OTN to Optical Channel", + }, + { + desc: "Optical Channel Assignment: Allocation", + got: cc.GetAssignment(0).GetAllocation(), + want: float64(400), + }, + { + desc: "Optical Channel Assignment: Type", + got: cc.GetAssignment(0).GetAssignmentType().String(), + want: oc.Assignment_AssignmentType_OPTICAL_CHANNEL.String(), + }, + { + desc: "Ethernet Assignment: Index", got: cc.GetAssignment(1).GetIndex(), want: uint32(1), }, { - desc: "Assignment: Optical Channel", - got: cc.GetAssignment(1).GetOpticalChannel(), - want: opticalChannel, + desc: "Ethernet Assignment: Logical Channel", + got: cc.GetAssignment(1).GetLogicalChannel(), + want: ethChIdx, }, { - desc: "Assignment: Description", + desc: "Ethernet Assignment: Description", got: cc.GetAssignment(1).GetDescription(), - want: "Coherent to Optical", + want: "OTN to ETH", }, { - desc: "Assignment: Allocation", + desc: "Ethernet Assignment: Allocation", got: cc.GetAssignment(1).GetAllocation(), want: float64(400), }, { - desc: "Assignment: Type", + desc: "Ethernet Assignment: Type", got: cc.GetAssignment(1).GetAssignmentType().String(), - want: oc.Assignment_AssignmentType_OPTICAL_CHANNEL.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("Coherent Logical Channel: %s, diff (-got +want):\n%s", tc.desc, diff) + t.Errorf("OTN Logical Channel: %s, diff (-got +want):\n%s", tc.desc, diff) } }) } 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 index 710cfff6c75..455bf38a6cf 100644 --- a/feature/platform/transceiver/tests/zr_pm_test/zr_pm_test.go +++ b/feature/platform/transceiver/tests/zr_pm_test/zr_pm_test.go @@ -95,7 +95,7 @@ func TestPM(t *testing.T) { for _, p := range dut.Ports() { gnmi.Await(t, dut, gnmi.OC().Interface(p.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) } - time.Sleep(samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. + time.Sleep(3 * samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. validateAllSamples(t, dut, true, interfaceStreams, otnStreams) @@ -108,7 +108,7 @@ func TestPM(t *testing.T) { for _, p := range dut.Ports() { gnmi.Await(t, dut, gnmi.OC().Interface(p.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_DOWN) } - time.Sleep(samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. + time.Sleep(3 * samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. validateAllSamples(t, dut, false, interfaceStreams, otnStreams) @@ -121,7 +121,7 @@ func TestPM(t *testing.T) { for _, p := range dut.Ports() { gnmi.Await(t, dut, gnmi.OC().Interface(p.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) } - time.Sleep(samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. + time.Sleep(3 * samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. validateAllSamples(t, dut, true, interfaceStreams, otnStreams) } @@ -195,18 +195,18 @@ func validateSampleStream(t *testing.T, interfaceData *ygnmi.Value[*oc.Interface } // validatePMValue validates the pm value. -func validatePMValue(t *testing.T, portName, pm string, instant, min, max, avg, minAllowed, maxAllowed, inactiveValue float64, linkState oc.E_Interface_OperStatus) { - switch linkState { +func validatePMValue(t *testing.T, portName, pm string, instant, min, max, avg, minAllowed, maxAllowed, inactiveValue float64, operStatus oc.E_Interface_OperStatus) { + switch operStatus { case oc.Interface_OperStatus_UP: if instant < minAllowed || instant > 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.Interface_OperStatus_DOWN: - if instant != inactiveValue || avg != inactiveValue || min != inactiveValue || max != inactiveValue { + if instant != 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) + t.Logf("Valid %v sample when %v is %v --> min : %v, max : %v, avg : %v, instant : %v", pm, portName, operStatus, min, max, avg, instant) } diff --git a/feature/policy_forwarding/encapsulation/otg_tests/decap_gre_ipv4/README.md b/feature/policy_forwarding/encapsulation/otg_tests/decap_gre_ipv4/README.md new file mode 100644 index 00000000000..b71df8ef5ec --- /dev/null +++ b/feature/policy_forwarding/encapsulation/otg_tests/decap_gre_ipv4/README.md @@ -0,0 +1,168 @@ +# PF-1.3: Policy-based IPv4 GRE Decapsulation + +## Summary + +This test verifies the functionality of policy-based forwarding (PF) to decapsulate GRE-encapsulated traffic. +The test verified IPv4, IPv6 and MPLS encapsulated traffic. +The test also confirms the correct forwarding of traffic not matching the decapsulation policy. + +## Testbed type + +* [`featureprofiles/topologies/atedut_2.testbed`](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_2.testbed) + +## Procedure + +### Test environment setup + +* DUT has an ingress port and an egress port. + + ``` + | | + [ ATE Port 1 ] ---- | DUT | ---- [ ATE Port 2 ] + | | + ``` + +* ATE Port 1: Generates GRE-encapsulated traffic with various inner (original) destinations. +* ATE Port 2: Receives decapsulated traffic whose inner destination matches the policy. + +### DUT Configuration + +1. Interfaces: Configure all DUT ports as singleton IP interfaces. + +2. Static Routes/LSPs: + * Configure an IPv4 static route to GRE decapsulation destination (DECAP-DST) to Null0. + * Configure static routes for encapsulated traffic destinations IPV4-DST1 and IPV6-DST1 towards ATE Port 2. + * Configure static MPLS label binding (LBL1) towards ATE Port 2. Next hop of ATE Port 1 should be indicated for MPLS pop action. + * Configure static routes for destination IPV4-DST2 and IPV6-DST2 towards ATE Port 2. + +3. Policy-Based Forwarding: + * Rule 1: Match GRE traffic with destination DECAP-DST using destination-address-prefix-set and decapsulate. + * Rule 2: Match all other traffic and forward (no decapsulation). + * Apply the defined policy with to the ingress ATE Port 1 interface. + + **TODO:** OC model does not have a provision to apply decap policy at the network-instance level for traffic destined to device loopback interface (see Cisco CLI config exepmt below). Needs clarification and/or augmentation by vendors if required. [PR #1150](https://github.com/openconfig/public/pull/1150) + + ``` + vrf-policy + vrf default address-family ipv4 policy type pbr input DECAP-POLICY + ``` + + +### PF-1.3.1: GRE Decapsulation of IPv4 traffic +- Push DUT configuration. + +Traffic: +- Generate GRE-encapsulated traffic from ATE Port 1 with destinations matching DECAP-DST. +- Inner IPv4 destination should match IPV4-DST1. +- Inner-packet DSCP value should be set to 32. + +Verification: +- Decapsulated IPv4 traffic is received on ATE Port 2. +- No packet loss. +- Inner-packet DSCP should be preserved. +- PF counters reflect decapsulated packets. + +### PF-1.3.2: GRE Decapsulation of IPv6 traffic +- Push DUT configuration. + +Traffic: +- Generate IPv6 GRE-encapsulated traffic from ATE Port 1 with destinations matching DECAP-DST. +- Inner IPv6 destination should match IPV6-DST1. +- Inner-packet traffic-class should be set to 128. + +Verification: +- Decapsulated IPv6 traffic is received on ATE Port 2. +- No packet loss. +- Inner-packet traffic-class should be preserved. +- PF counters reflect decapsulated packets. + + +### PF-1.3.3: GRE Decapsulation of IPv4-over-MPLS traffic +- Push DUT configuration. + +Traffic: +- Generate GRE-encapsulated IPv4-over-MPLS traffic from ATE Port 1 with destinations matching DECAP-DST. +- Encapsulated MPLS top label should match LBL1. +- Inner IPv4 packet DSCP should be set to 32. + +Verification: +- Decapsulated IPv4 traffic is received on ATE Port 2. +- No packet loss. +- TTL should be taken from the outer GRE header, decremented by 1 and copied to egress IP packet header. +- Inner-packet DSCP should be preserved. +- PF counters reflect decapsulated packets. + + +### PF-1.3.4: GRE Decapsulation of IPv6-over-MPLS traffic +- Push DUT configuration. + +Traffic: +- Generate GRE-encapsulated IPv4-over-MPLS traffic from ATE Port 1 with destinations matching DECAP-DST. +- Encapsulated MPLS top label should match LBL1. +- Inner IPv6 packet traffic-class should be set to 128. + +Verification: +- Decapsulated IPv6 traffic is received on ATE Port 2. +- No packet loss. +- TTL should be taken from the outer GRE header, decremented by 1 and copied to egress IP packet header. +- Inner-packet traffic-class should be preserved. +- PF counters reflect decapsulated packets. + + + +### PF-1.3.5: GRE Decapsulation of multi-label MPLS traffic +- Push DUT configuration. + +Traffic: +- Generate GRE-encapsulated MPLS traffic from ATE Port 1 with destinations matching DECAP-DST. +- MPLS packets will have 2 labels. +- Top label should match LBL1. +- MPLS second label can be any. +- MPLS EXP bit on both labels should be set to 4. + +Verification: +- Decapsulated MPLS traffic is received on ATE Port 2. +- TTL should be taken from the outer GRE header, decremented by 1 and copied to egress MPLS packet header. +- No packet loss. +- PF counters reflect decapsulated packets. +- EXP should set to original value. + + +### PF-1.3.6: GRE Pass-through (Negative) +- Push DUT configuration. + +Traffic: +- Generate GRE-encapsulated traffic from ATE Port 1 with destinations that match IPV4-DST1/IPV6-DST2. + +Verification: +- Traffic is forwarded to ATE Port 2 unchanged. + + +## OpenConfig Path and RPC Coverage + +```yaml +paths: + # match condition + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv4/config/destination-address-prefix-set: + # decap action + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/config/decapsulate-gre: + # application to the interface + /network-instances/network-instance/policy-forwarding/interfaces/interface/config/apply-forwarding-policy: + # TODO: provision apply decap to network-instance level does not exist. Needs clarification and/or augmentation by vendors. + + # telemetry + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/state/matched-pkts: + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/state/matched-octets: + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + replace: true + gNMI.Subscribe: + on_change: true +``` + +## Required DUT platform + +* FFF diff --git a/feature/qos/otg_tests/egress_strict_priority_scheduler_test/README.md b/feature/qos/otg_tests/egress_strict_priority_scheduler_test/README.md new file mode 100644 index 00000000000..be2d7963ed2 --- /dev/null +++ b/feature/qos/otg_tests/egress_strict_priority_scheduler_test/README.md @@ -0,0 +1,224 @@ +# DP-1.15: Egress Strict Priority scheduler + +## Summary + +This test validates the proper functionality of an egress strict priority scheduler on a network device. By configuring multiple priority queues with specific traffic classes and generating traffic loads that exceed interface capacity, we will verify that the scheduler adheres to the strict priority scheme, prioritizing higher-priority traffic even under congestion. + +## Testbed type + +* [`featureprofiles/topologies/atedut_4.testbed`](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_4.testbed) + +## Procedure + +### Test environment setup + +* DUT has 2 ingress ports and 1 egress port with the same port speed. The + interface can be a physical interface or LACP bundle interface with the + same aggregated speed. + + ``` + | | ---- | ATE Port 1 | + [ ATE Port 3 ] ---- | DUT | | | + | | ---- | ATE Port 2 | + ``` + +* Traffic classes: + + * We will use 6 traffic classes NC1, AF4, AF3, AF2, AF1 and BE1. + +* Traffic types: + + * All the traffic tests apply to both IPv4 and IPv6 and also MPLS traffic. + +* Queue types: + + * NC1/AF4/AF3/AF2/AF1/BE1 will have strict priority queues (be1 - priority 6, af1 - priority 5, ..., nc1 - priority 1) + +* Test results should be independent of the location of interfaces. For + example, 2 input interfaces and output interface could be located on + + * Same ASIC-based forwarding engine + * Different ASIC-based forwarding engine on same line card + * Different ASIC-based forwarding engine on different line cards + +* Test results should be the same for port speeds 100G and 400G. + +* Counters should be also verified for each test case: + + * /qos/interfaces/interface/output/queues/queue/state/transmit-pkts + * /qos/interfaces/interface/output/queues/queue/state/dropped-pkts + * transmit-pkts should be equal to the number of Rx pkts on Ixia port + * dropped-pkts should be equal to diff between the number of Tx and the + number Rx pkts on Ixia ports + +* Latency: + + * Should be < 100000ns + +#### Configuration + +* Forwarding Classes: Configure six forwarding classes (be1, af1, af2, af3, af4, nc1) based on the classification table provided. +* Egress Scheduler: Apply a multi-level strict-priority scheduling policy on the desired egress interface. Assign priorities to each forwarding class according to the strict priority test traffic tables (be1 - priority 6, af1 - priority 5, ..., nc1 - priority 1). + +* Classification table + + IPv4 TOS | IPv6 TC | MPLS EXP | Forwarding class + ------------- | ----------------------- | ----------------------- | --------------------- + 0 | 0-7 | 0 | be1 + 1 | 8-15 | 1 | af1 + 2 | 16-23 | 2 | af2 + 3 | 24-31 | 3 | af3 + 4,5 | 32-47 | 4,5 | af4 + 6,7 | 48-63 | 6,7 | nc1 + +### DP-1.15.1: Egress Strict Priority scheduler for IPv4 Traffic + +* Traffic Generation: + * Traffic Profiles: Define traffic profiles for each forwarding class using the ATE, adhering to the linerates (%) specified in the strict priority test traffic tables. + + * Strict Priority Test traffic table for ATE Port 1 + + Forwarding class | Priority | Traffic linerate (%) | Frame size | Expected Loss % + ----------------- |--------------------- | --------------------------- |--------------------- | ----------------------------------- + be1 | 6 | 12 | 512 | 100 + af1 | 5 | 12 | 512 | 100 + af2 | 4 | 10 | 512 | 50 + af3 | 3 | 12 | 512 | 0 + af4 | 2 | 30 | 512 | 0 + nc1 | 1 | 1 | 512 | 0 + + * Strict Priority Test traffic table for ATE Port 2 + + Forwarding class | Priority | Traffic linerate (%) | Frame size | Expected Loss % + ----------------- |--------------------- | --------------------------- |--------------------- | ----------------------------------- + be1 | 6 | 12 | 512 | 100 + af1 | 5 | 12 | 512 | 100 + af2 | 4 | 10 | 512 | 50 + af3 | 3 | 12 | 512 | 0 + af4 | 2 | 30 | 512 | 0 + nc1 | 1 | 1 | 512 | 0 + + +* Verification: + * Loss Rate: Capture packet loss for every generated flow and verify that loss for each flow does not exceed expected loss specified in the tables above. + * Telemetry: Utilize OpenConfig telemetry parameters to validate that per queue dropped packets statistics corresponds (with error margin) to the packet loss reported for every flow matching that particular queue. + +### DP-1.15.2: Egress Strict Priority scheduler for IPv6 Traffic + +* Traffic Generation: + * Traffic Profiles: Define traffic profiles for each forwarding class using the ATE, adhering to the linerates (%) specified in the strict priority test traffic tables. + + * Strict Priority Test traffic table for ATE Port 1 + + Forwarding class | Priority | Traffic linerate (%) | Frame size | Expected Loss % + ----------------- |--------------------- | --------------------------- |--------------------- | ----------------------------------- + be1 | 6 | 12 | 512 | 100 + af1 | 5 | 12 | 512 | 100 + af2 | 4 | 10 | 512 | 50 + af3 | 3 | 12 | 512 | 0 + af4 | 2 | 30 | 512 | 0 + nc1 | 1 | 1 | 512 | 0 + + * Strict Priority Test traffic table for ATE Port 2 + + Forwarding class | Priority | Traffic linerate (%) | Frame size | Expected Loss % + ----------------- |--------------------- | --------------------------- |--------------------- | ----------------------------------- + be1 | 6 | 12 | 512 | 100 + af1 | 5 | 12 | 512 | 100 + af2 | 4 | 10 | 512 | 50 + af3 | 3 | 12 | 512 | 0 + af4 | 2 | 30 | 512 | 0 + nc1 | 1 | 1 | 512 | 0 + + +* Verification: + * Loss Rate: Capture packet loss for every generated flow and verify that loss for each flow does not exceed expected loss specified in the tables above. + * Telemetry: Utilize OpenConfig telemetry parameters to validate that per queue dropped packets statistics corresponds (with error margin) to the packet loss reported for every flow matching that particular queue. + +### DP-1.15.3: Egress Strict Priority scheduler for MPLS Traffic + +* Traffic Generation: + * Traffic Profiles: Define traffic profiles for each forwarding class using the ATE, adhering to the linerates (%) specified in the strict priority test traffic tables. + + * Strict Priority Test traffic table for ATE Port 1 + + Forwarding class | Priority | Traffic linerate (%) | Frame size | Expected Loss % + ----------------- |--------------------- | --------------------------- |--------------------- | ----------------------------------- + be1 | 6 | 12 | 512 | 100 + af1 | 5 | 12 | 512 | 100 + af2 | 4 | 10 | 512 | 50 + af3 | 3 | 12 | 512 | 0 + af4 | 2 | 30 | 512 | 0 + nc1 | 1 | 1 | 512 | 0 + + * Strict Priority Test traffic table for ATE Port 2 + + Forwarding class | Priority | Traffic linerate (%) | Frame size | Expected Loss % + ----------------- |--------------------- | --------------------------- |--------------------- | ----------------------------------- + be1 | 6 | 12 | 512 | 100 + af1 | 5 | 12 | 512 | 100 + af2 | 4 | 10 | 512 | 50 + af3 | 3 | 12 | 512 | 0 + af4 | 2 | 30 | 512 | 0 + nc1 | 1 | 1 | 512 | 0 + + +* Verification: + * Loss Rate: Capture packet loss for every generated flow and verify that loss for each flow does not exceed expected loss specified in the tables above. + * Telemetry: Utilize OpenConfig telemetry parameters to validate that per queue dropped packets statistics corresponds (with error margin) to the packet loss reported for every flow matching that particular queue. + +## OpenConfig Path and RPC Coverage + +The below yaml defines the OC paths intended to be covered by this test. + +```yaml +paths: + ## Config paths + ### Classifiers + /qos/classifiers/classifier/config/name: + /qos/classifiers/classifier/config/type: + /qos/classifiers/classifier/terms/term/config/id: + /qos/classifiers/classifier/terms/term/conditions/ipv4/config/dscp-set: + /qos/classifiers/classifier/terms/term/conditions/ipv6/config/dscp-set: + /qos/classifiers/classifier/terms/term/conditions/mpls/config/traffic-class: + /qos/classifiers/classifier/terms/term/actions/config/target-group: + + ### Forwarding Groups + /qos/forwarding-groups/forwarding-group/config/name: + /qos/forwarding-groups/forwarding-group/config/output-queue: + + ### Queue + /qos/queues/queue/config/name: + + ### Interfaces + /qos/interfaces/interface/input/classifiers/classifier/config/type: + /qos/interfaces/interface/input/classifiers/classifier/config/name: + /qos/interfaces/interface/output/queues/queue/config/name: + /qos/interfaces/interface/output/scheduler-policy/config/name: + + ### Scheduler policy + /qos/scheduler-policies/scheduler-policy/config/name: + /qos/scheduler-policies/scheduler-policy/schedulers/scheduler/config/priority: + /qos/scheduler-policies/scheduler-policy/schedulers/scheduler/config/sequence: + /qos/scheduler-policies/scheduler-policy/schedulers/scheduler/config/type: + /qos/scheduler-policies/scheduler-policy/schedulers/scheduler/inputs/input/config/id: + /qos/scheduler-policies/scheduler-policy/schedulers/scheduler/inputs/input/config/input-type: + /qos/scheduler-policies/scheduler-policy/schedulers/scheduler/inputs/input/config/queue: + + ## State paths + /qos/interfaces/interface/output/queues/queue/state/name: + /qos/interfaces/interface/output/queues/queue/state/transmit-pkts: + /qos/interfaces/interface/output/queues/queue/state/transmit-octets: + /qos/interfaces/interface/output/queues/queue/state/dropped-pkts: + /qos/interfaces/interface/output/queues/queue/state/dropped-octets: + +rpcs: + gnmi: + gNMI.Set: + gNMI.Subscribe: +``` + +## Minimum DUT platform requirement + +* MFF - A modular form factor device containing LINECARDs, FABRIC and redundant CONTROLLER_CARD components +* FFF - fixed form factor diff --git a/feature/qos/otg_tests/egress_strict_priority_scheduler_with_bursty_traffic_test/README.md b/feature/qos/otg_tests/egress_strict_priority_scheduler_with_bursty_traffic_test/README.md new file mode 100644 index 00000000000..cf4b779a975 --- /dev/null +++ b/feature/qos/otg_tests/egress_strict_priority_scheduler_with_bursty_traffic_test/README.md @@ -0,0 +1,220 @@ +# DP-1.5: Egress Strict Priority scheduler with bursty traffic + +## Summary + +This test verifies the behavior of an egress strict priority scheduler under bursty traffic conditions. By configuring multiple priority queues with specific traffic classes and generating bursty traffic that exceeds the interface capacity in short durations, we will validate that the scheduler maintains strict priority order, prioritizing the transmission of higher-priority traffic even during bursts, potentially leading to drops in lower-priority traffic. + +## Testbed type + +* [`featureprofiles/topologies/atedut_4.testbed`](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_4.testbed) + +## Procedure + +### Test environment setup + +* DUT has 2 ingress ports and 1 egress port with the same port speed. The + interface can be a physical interface or LACP bundle interface with the + same aggregated speed. + + ``` + | | ---- | ATE Port 1 | + [ ATE Port 3 ] ---- | DUT | | | + | | ---- | ATE Port 2 | + ``` + +* Traffic classes: + * We will use 6 traffic classes NC1, AF4, AF3, AF2, AF1 and BE1. + +* Traffic types: + * All the traffic tests apply to both IPv4 and IPv6 and also MPLS traffic. + +* Queue types: + * NC1/AF4/AF3/AF2/AF1/BE1 will have strict priority queues (be1 - priority 6, af1 - priority 5, ..., nc1 - priority 1) + +* Test results should be independent of the location of interfaces. For + example, 2 input interfaces and output interface could be located on + * Same ASIC-based forwarding engine + * Different ASIC-based forwarding engine on same line card + * Different ASIC-based forwarding engine on different line cards + +* Test results should be the same for port speeds 100G and 400G. + +* Counters should be also verified for each test case: + * /qos/interfaces/interface/output/queues/queue/state/transmit-pkts + * /qos/interfaces/interface/output/queues/queue/state/dropped-pkts + * transmit-pkts should be equal to the number of Rx pkts on Ixia port + * dropped-pkts should be equal to diff between the number of Tx and the + number Rx pkts on Ixia ports + +* Latency: + * Should be < 100000ns + +#### Configuration + +* Forwarding Classes: Configure six forwarding classes (be1, af1, af2, af3, af4, nc1) based on the classification table provided. +* Classification table + + IPv4 TOS | IPv6 TC | MPLS EXP | Forwarding Group + ------------- | ----------------------- | ----------------------- | --------------------- + 0 | 0-7 | 0 | be1 + 1 | 8-15 | 1 | af1 + 2 | 16-23 | 2 | af2 + 3 | 24-31 | 3 | af3 + 4,5 | 32-47 | 4,5 | af4 + 6,7 | 48-63 | 6,7 | nc1 + +* Egress Scheduler: Apply a multi-level strict-priority scheduling policy on the desired egress interface. Assign priorities to each forwarding class as below: + * be1 - priority 6 + * af1 - priority 5 + * af2 - priority 4 + * af3 - priority 3 + * af4 - priority 2 + * nc1 - priority 1 + +### DP1-5.1 Egress Strict Priority scheduler with bursty traffic for IPv4 + +* Traffic Generation: + * Generate the IPv4 traffic as below + + * Interface Port 1: + + Forwarding Group | Traffic linerate (%) | Frame size | Expected Loss % + ----------------- | --------------------------- |--------------------- | ----------------------------------- + be1 | 12 | 512 | 100 + af1 | 12 | 512 | 100 + af2 | 15 | 512 | 50 + af3 | 12 | 512 | 0 + af4 | 30 | 512 | 0 + nc1 | 1 | 512 | 0 + + * Interface Port 2: + + Forwarding Group | Traffic linerate (%) | Frame size | Burst size | Inter-pkt gap | inter-burst gap | Expected Loss % + -------------- | -------------- | ---------- | ----------- | ------------- | --------------- | --------------- + be1 | 20 | 256 | 50000 | 12 | 100 | 100 + af1 | 13 | 256 | 50000 | 12 | 100 | 100 + af2 | 17 | 256 | 50000 | 12 | 100 | 50 + af3 | 10 | 256 | 50000 | 12 | 100 | 0 + af4 | 20 | 256 | 50000 | 12 | 100 | 0 + nc1 | 10 | 256 | 50000 | 12 | 100 | 0 + +* Verification: + * Loss Rate: Capture packet loss for every generated flow and verify that loss for each flow does not exceed expected loss specified in the tables above. + * Telemetry: Utilize OpenConfig telemetry parameters to validate that per queue dropped packets statistics corresponds (with error margin) to the packet loss reported for every flow matching that particular queue. + +### DP1-5.2 Egress Strict Priority scheduler with bursty traffic for IPv6 + +* Traffic Generation: + * Generate the IPv6 traffic as below + + * Interface Port 1: + + Forwarding Group | Traffic linerate (%) | Frame size | Expected Loss % + ----------------- | --------------------------- |--------------------- | ----------------------------------- + be1 | 12 | 512 | 100 + af1 | 12 | 512 | 100 + af2 | 15 | 512 | 50 + af3 | 12 | 512 | 0 + af4 | 30 | 512 | 0 + nc1 | 1 | 512 | 0 + + * Interface Port 2: + + Forwarding Group | Traffic linerate (%) | Frame size | Burst size | Inter-pkt gap | inter-burst gap | Expected Loss % + -------------- | -------------- | ---------- | ----------- | ------------- | --------------- | --------------- + be1 | 20 | 256 | 50000 | 12 | 100 | 100 + af1 | 13 | 256 | 50000 | 12 | 100 | 100 + af2 | 17 | 256 | 50000 | 12 | 100 | 50 + af3 | 10 | 256 | 50000 | 12 | 100 | 0 + af4 | 20 | 256 | 50000 | 12 | 100 | 0 + nc1 | 10 | 256 | 50000 | 12 | 100 | 0 + +* Verification: + * Loss Rate: Capture packet loss for every generated flow and verify that loss for each flow does not exceed expected loss specified in the tables above. + * Telemetry: Utilize OpenConfig telemetry parameters to validate that per queue dropped packets statistics corresponds (with error margin) to the packet loss reported for every flow matching that particular queue. + +### DP1-5.3 Egress Strict Priority scheduler with bursty traffic for MPLS + +* Traffic Generation: + * Generate the MPLS traffic as below + + * Interface Port 1: + + Forwarding Group | Traffic linerate (%) | Frame size | Expected Loss % + ----------------- | --------------------------- |--------------------- | ----------------------------------- + be1 | 12 | 512 | 100 + af1 | 12 | 512 | 100 + af2 | 15 | 512 | 50 + af3 | 12 | 512 | 0 + af4 | 30 | 512 | 0 + nc1 | 1 | 512 | 0 + + * Interface Port 2: + + Forwarding Group | Traffic linerate (%) | Frame size | Burst size | Inter-pkt gap | inter-burst gap | Expected Loss % + -------------- | -------------- | ---------- | ----------- | ------------- | --------------- | --------------- + be1 | 20 | 256 | 50000 | 12 | 100 | 100 + af1 | 13 | 256 | 50000 | 12 | 100 | 100 + af2 | 17 | 256 | 50000 | 12 | 100 | 50 + af3 | 10 | 256 | 50000 | 12 | 100 | 0 + af4 | 20 | 256 | 50000 | 12 | 100 | 0 + nc1 | 10 | 256 | 50000 | 12 | 100 | 0 + +* Verification: + * Loss Rate: Capture packet loss for every generated flow and verify that loss for each flow does not exceed expected loss specified in the tables above. + * Telemetry: Utilize OpenConfig telemetry parameters to validate that per queue dropped packets statistics corresponds (with error margin) to the packet loss reported for every flow matching that particular queue. + +## OpenConfig Path and RPC Coverage + +The below yaml defines the OC paths intended to be covered by this test. + +```yaml +paths: + ## Config paths + ### Classifiers + /qos/classifiers/classifier/config/name: + /qos/classifiers/classifier/config/type: + /qos/classifiers/classifier/terms/term/config/id: + /qos/classifiers/classifier/terms/term/conditions/ipv4/config/dscp-set: + /qos/classifiers/classifier/terms/term/conditions/ipv6/config/dscp-set: + /qos/classifiers/classifier/terms/term/conditions/mpls/config/traffic-class: + /qos/classifiers/classifier/terms/term/actions/config/target-group: + + ### Forwarding Groups + /qos/forwarding-groups/forwarding-group/config/name: + /qos/forwarding-groups/forwarding-group/config/output-queue: + + ### Queue + /qos/queues/queue/config/name: + + ### Interfaces + /qos/interfaces/interface/input/classifiers/classifier/config/name: + /qos/interfaces/interface/output/queues/queue/config/name: + /qos/interfaces/interface/output/scheduler-policy/config/name: + + ### Scheduler policy + /qos/scheduler-policies/scheduler-policy/config/name: + /qos/scheduler-policies/scheduler-policy/schedulers/scheduler/config/priority: + /qos/scheduler-policies/scheduler-policy/schedulers/scheduler/config/sequence: + /qos/scheduler-policies/scheduler-policy/schedulers/scheduler/config/type: + /qos/scheduler-policies/scheduler-policy/schedulers/scheduler/inputs/input/config/id: + /qos/scheduler-policies/scheduler-policy/schedulers/scheduler/inputs/input/config/input-type: + /qos/scheduler-policies/scheduler-policy/schedulers/scheduler/inputs/input/config/queue: + + ## State paths + /qos/interfaces/interface/output/queues/queue/state/name: + /qos/interfaces/interface/output/queues/queue/state/transmit-pkts: + /qos/interfaces/interface/output/queues/queue/state/transmit-octets: + /qos/interfaces/interface/output/queues/queue/state/dropped-pkts: + /qos/interfaces/interface/output/queues/queue/state/dropped-octets: + +rpcs: + gnmi: + gNMI.Set: + gNMI.Subscribe: +``` + +## Minimum DUT platform requirement + +* MFF - A modular form factor device containing LINECARDs, FABRIC and redundant CONTROLLER_CARD components +* FFF - fixed form factor diff --git a/feature/qos/otg_tests/ingress_traffic_classification_and_rewrite_test/README.md b/feature/qos/otg_tests/ingress_traffic_classification_and_rewrite_test/README.md new file mode 100644 index 00000000000..93feae4293b --- /dev/null +++ b/feature/qos/otg_tests/ingress_traffic_classification_and_rewrite_test/README.md @@ -0,0 +1,153 @@ +# DP-1.16: Ingress traffic classification and rewrite + +## Summary + +This test aims to validate the functionality of ingress traffic classification and subsequent packet remarking (rewrite) on a Device Under Test (DUT). The DUT's configuration will be evaluated against the OpenConfig QOS model, and traffic flows will be analyzed to ensure proper classification, marking, and forwarding. + +## Testbed type + +* [`featureprofiles/topologies/atedut_2.testbed`](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_2.testbed) + +## Procedure + +### Test environment setup + +* DUT has an ingress port and 1 egress port. + + ``` + | | + [ ATE Port 1 ] ---- | DUT | ---- [ ATE Port 2 ] + | | + ``` + +* Configure the DUT's ingress and egress interfaces. + +### Configuration + +* Apply QoS classifiers using the OpenConfig QOS model, matching packets based on DSCP/TC/EXP values as per the classification table. +* Configure packet remarking rules based on the marking table. +* QoS Classification and Marking table + + * Classification table + + IPv4 TOS | IPv6 TC | MPLS EXP | Forwarding Group + ------------- | ----------------------- | ----------------------- | --------------------- + 0 | 0-7 | 0 | be1 + 1 | 8-15 | 1 | af1 + 2 | 16-23 | 2 | af2 + 3 | 24-31 | 3 | af3 + 4,5 | 32-47 | 4,5 | af4 + 6,7 | 48-63 | 6,7 | nc1 + + * Marking table + + Forwarding Group | IPv4 TOS | IPv6 TC | MPLS EXP + --------------------|------------- | ----------------------- | ----------------------- + be1 | 0 | 0 | 0 + af1 | 1 | 8 | 1 + af2 | 2 | 16 | 2 + af3 | 3 | 24 | 3 + af4 | 4 | 32 | 4 + nc1 | 6 | 48 | 6 + +### DP-1.16.1 Ingress Classification and rewrite of IPv4 packets with various DSCP values + +* Traffic: + * Generate IPv4 traffic from ATE Port 1 with various DSCP values +* Verfication: + * Monitor telemetry on the DUT to verify that packets are being matched to the correct classifier terms. + * Capture packets on the ATE's ingress interface to verify packet marking according to the marking table. + * Analyze traffic flows to confirm that no packets are dropped on the DUT. +### DP-1.16.2 Ingress Classification and rewrite of IPv6 packets with various TC values + +* Traffic: + * Generate IPv6 traffic from ATE Port 1 with various TC values +* Verfication: + * Monitor telemetry on the DUT to verify that packets are being matched to the correct classifier terms. + * Capture packets on the ATE's ingress interface to verify packet marking according to the marking table. + * Analyze traffic flows to confirm that no packets are dropped on the DUT. +### DP-1.16.3 Ingress Classification and rewrite of MPLS traffic with swap action + +* Configuration: + * Configure Static MPLS LSP MPLS swap/forward actions for a specific labels range (100101-100200). +* Traffic: + * Generate MPLS traffic from ATE Port 1 with labels between 1000101 and 1000200 +* Verfication: + * Monitor telemetry on the DUT to verify that packets are being matched to the correct classifier terms. + * Capture packets on the ATE's ingress interface to verify packet marking according to the marking table. + * Analyze traffic flows to confirm that no packets are dropped on the DUT. +### DP-1.16.4 Ingress Classification and rewrite of IPv4-over-MPLS traffic with pop action + +* Configuration: + * Configure Static MPLS LSP with MPLS pop and IPv4/IPv6 forward actions for a specific labels range (100020-100100). +* Traffic: + * Generate MPLS traffic from ATE Port 1 with labels between 100020 and 1000100 +* Verfication: + * Monitor telemetry on the DUT to verify that packets are being matched to the correct classifier terms. + * Capture packets on the ATE's ingress interface to verify packet marking according to the marking table. + * Analyze traffic flows to confirm that no packets are dropped on the DUT. +### DP-1.16.5 Ingress Classification and rewrite of IPv6-over-MPLS traffic with pop action + +* Configuration: + * Configure Static MPLS LSP with MPLS pop and IPv4/IPv6 forward actions for a specific labels range (100020-100100). +* Traffic: + * Generate MPLS traffic from ATE Port 1 with labels between 100020 and 1000100 +* Verfication: + * Monitor telemetry on the DUT to verify that packets are being matched to the correct classifier terms. + * Capture packets on the ATE's ingress interface to verify packet marking according to the marking table. + * Analyze traffic flows to confirm that no packets are dropped on the DUT. +### DP-1.16.6 Ingress Classification and rewrite of IPv4 packets traffic with label push action + +* Configuration: + * Configure Static MPLS LSP with MPLS label (=100201) push action to a IPv4 subnet destination DST1. +* Traffic: + * Generate IPv4 traffic from ATE Port 1 with destinations matching DST1. +* Verfication: + * Monitor telemetry on the DUT to verify that packets are being matched to the correct classifier terms. + * Capture packets on the ATE's ingress interface to verify packet marking according to the marking table. + * Analyze traffic flows to confirm that no packets are dropped on the DUT. +### DP-1.16.7 Ingress Classification and rewrite of IPv6 packets traffic with label push action + +* Configuration: + * Configure Static MPLS LSP with MPLS label (=100202) push action to a IPv6 subnet destination DST2. +* Traffic: + * Generate IPv6 traffic from ATE Port 1 with destinations matching DST2. +* Verfication: + * Monitor telemetry on the DUT to verify that packets are being matched to the correct classifier terms. + * Capture packets on the ATE's ingress interface to verify packet marking according to the marking table. + * Analyze traffic flows to confirm that no packets are dropped on the DUT. + +## OpenConfig Path and RPC Coverage + +The below yaml defines the OC paths intended to be covered by this test. + +```yaml +paths: + ## Config paths + /qos/classifiers/classifier/config/name: + /qos/classifiers/classifier/config/type: + /qos/classifiers/classifier/terms/term/config/id: + /qos/classifiers/classifier/terms/term/actions/config/target-group: + /qos/classifiers/classifier/terms/term/conditions/ipv4/config/dscp: + /qos/classifiers/classifier/terms/term/conditions/ipv4/config/dscp-set: + /qos/classifiers/classifier/terms/term/conditions/ipv6/config/dscp: + /qos/classifiers/classifier/terms/term/conditions/ipv6/config/dscp-set: + /qos/classifiers/classifier/terms/term/conditions/mpls/config/traffic-class: + /qos/classifiers/classifier/terms/term/actions/remark/config/set-dscp: + /qos/classifiers/classifier/terms/term/actions/remark/config/set-mpls-tc: + /qos/interfaces/interface/input/classifiers/classifier/config/name: + /qos/interfaces/interface/input/classifiers/classifier/config/type: + + ## State paths + /qos/interfaces/interface/input/classifiers/classifier/terms/term/state/matched-packets: + /qos/interfaces/interface/input/classifiers/classifier/terms/term/state/matched-octets: + +rpcs: + gnmi: + gNMI.Set: + gNMI.Subscribe: +``` + +## Minimum DUT platform requirement + +* FFF - fixed form factor diff --git a/feature/qos/tests/qos_ecn_config_test/README.md b/feature/qos/tests/qos_ecn_config_test/README.md index 025b4b5a595..adb5a3f2d79 100644 --- a/feature/qos/tests/qos_ecn_config_test/README.md +++ b/feature/qos/tests/qos_ecn_config_test/README.md @@ -60,7 +60,7 @@ Verify QoS ECN feature configuration. * ECN * [TODO] qos/queue-management-profiles/queue-management-profile/wred/uniform/config/min-threshold-percent - * [TODO] qos/queue-management-profiles/queue-management-profile/wred/uniform/config/max-threshold-percent + * [TODO] qos/queue-management-profiles/queue-management-profile/wred/uniform/config/max-threshold-percent * qos/queue-management-profiles/queue-management-profile/wred/uniform/config/min-threshold * qos/queue-management-profiles/queue-management-profile/wred/uniform/config/max-threshold * qos/queue-management-profiles/queue-management-profile/wred/uniform/config/enable-ecn @@ -79,7 +79,7 @@ Verify QoS ECN feature configuration. * ECN * [TODO] qos/queue-management-profiles/queue-management-profile/wred/uniform/state/min-threshold-percent - * [TODO] qos/queue-management-profiles/queue-management-profile/wred/uniform/state/max-threshold-percent + * [TODO] qos/queue-management-profiles/queue-management-profile/wred/uniform/state/max-threshold-percent * qos/queue-management-profiles/queue-management-profile/wred/uniform/state/min-threshold * qos/queue-management-profiles/queue-management-profile/wred/uniform/state/max-threshold * qos/queue-management-profiles/queue-management-profile/wred/uniform/state/enable-ecn @@ -96,3 +96,39 @@ Verify QoS ECN feature configuration. ## platform * vRX + +## 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 + /qos/queue-management-profiles/queue-management-profile/wred/uniform/config/min-threshold: + /qos/queue-management-profiles/queue-management-profile/wred/uniform/config/max-threshold: + /qos/queue-management-profiles/queue-management-profile/wred/uniform/config/enable-ecn: + /qos/queue-management-profiles/queue-management-profile/wred/uniform/config/weight: + /qos/queue-management-profiles/queue-management-profile/wred/uniform/config/drop: + /qos/queue-management-profiles/queue-management-profile/wred/uniform/config/max-drop-probability-percent: + /qos/interfaces/interface/input/classifiers/classifier/config/name: + /qos/interfaces/interface/output/queues/queue/config/name: + /qos/interfaces/interface/output/queues/queue/config/queue-management-profile: + + ## State paths: + + /qos/queue-management-profiles/queue-management-profile/wred/uniform/state/min-threshold: + /qos/queue-management-profiles/queue-management-profile/wred/uniform/state/max-threshold: + /qos/queue-management-profiles/queue-management-profile/wred/uniform/state/enable-ecn: + /qos/queue-management-profiles/queue-management-profile/wred/uniform/state/weight: + /qos/queue-management-profiles/queue-management-profile/wred/uniform/state/drop: + /qos/queue-management-profiles/queue-management-profile/wred/uniform/state/max-drop-probability-percent: + /qos/interfaces/interface/input/classifiers/classifier/state/name: + /qos/interfaces/interface/output/queues/queue/state/name: + /qos/interfaces/interface/output/queues/queue/state/queue-management-profile: + +rpcs: + gnmi: + gNMI.Set: + Replace: +``` \ No newline at end of file diff --git a/feature/qos/tests/qos_ecn_config_test/metadata.textproto b/feature/qos/tests/qos_ecn_config_test/metadata.textproto index 729421003ce..2a2d548b8bb 100644 --- a/feature/qos/tests/qos_ecn_config_test/metadata.textproto +++ b/feature/qos/tests/qos_ecn_config_test/metadata.textproto @@ -9,10 +9,6 @@ platform_exceptions: { platform: { vendor: JUNIPER } - deviations: { - state_path_unsupported: true - drop_weight_leaves_unsupported: true - } } platform_exceptions: { platform: { diff --git a/feature/qos/tests/qos_ecn_config_test/qos_ecn_config_test.go b/feature/qos/tests/qos_ecn_config_test/qos_ecn_config_test.go index 0e9f12ffedf..ae94ee7fb04 100644 --- a/feature/qos/tests/qos_ecn_config_test/qos_ecn_config_test.go +++ b/feature/qos/tests/qos_ecn_config_test/qos_ecn_config_test.go @@ -302,46 +302,114 @@ func testQoSOutputIntfConfig(t *testing.T, q *oc.Qos) { dp := dut.Port(t, "port2") queues := netutil.CommonTrafficQueues(t, dut) + ecnConfig := struct { + ecnEnabled bool + dropEnabled bool + minThreshold uint64 + maxThreshold uint64 + maxDropProbabilityPercent uint8 + weight uint32 + }{ + ecnEnabled: true, + dropEnabled: false, + minThreshold: uint64(80000), + maxThreshold: uint64(80000), + maxDropProbabilityPercent: uint8(100), + weight: uint32(0), + } + + queueMgmtProfile := q.GetOrCreateQueueManagementProfile("DropProfile") + queueMgmtProfile.SetName("DropProfile") + wred := queueMgmtProfile.GetOrCreateWred() + uniform := wred.GetOrCreateUniform() + uniform.SetEnableEcn(ecnConfig.ecnEnabled) + uniform.SetDrop(ecnConfig.dropEnabled) + wantMinThreshold := ecnConfig.minThreshold + wantMaxThreshold := ecnConfig.maxThreshold + if deviations.EcnSameMinMaxThresholdUnsupported(dut) { + wantMinThreshold = CiscoMinThreshold + wantMaxThreshold = CiscoMaxThreshold + } + uniform.SetMinThreshold(wantMinThreshold) + uniform.SetMaxThreshold(wantMaxThreshold) + uniform.SetMaxDropProbabilityPercent(ecnConfig.maxDropProbabilityPercent) + if !deviations.QosSetWeightConfigUnsupported(dut) { + uniform.SetWeight(ecnConfig.weight) + } + cases := []struct { desc string queueName string ecnProfile string scheduler string + sequence uint32 + priority oc.E_Scheduler_Priority + inputID string + inputType oc.E_Input_InputType + weight uint64 }{{ desc: "output-interface-BE1", queueName: queues.BE1, ecnProfile: "DropProfile", scheduler: "scheduler", + sequence: uint32(1), + priority: oc.Scheduler_Priority_UNSET, + inputType: oc.Input_InputType_QUEUE, + weight: uint64(1), }, { desc: "output-interface-BE0", queueName: queues.BE0, ecnProfile: "DropProfile", scheduler: "scheduler", + sequence: uint32(1), + priority: oc.Scheduler_Priority_UNSET, + inputType: oc.Input_InputType_QUEUE, + weight: uint64(4), }, { desc: "output-interface-AF1", queueName: queues.AF1, ecnProfile: "DropProfile", scheduler: "scheduler", + sequence: uint32(1), + priority: oc.Scheduler_Priority_UNSET, + inputType: oc.Input_InputType_QUEUE, + weight: uint64(8), }, { desc: "output-interface-AF2", queueName: queues.AF2, ecnProfile: "DropProfile", scheduler: "scheduler", + sequence: uint32(1), + priority: oc.Scheduler_Priority_UNSET, + inputType: oc.Input_InputType_QUEUE, + weight: uint64(16), }, { desc: "output-interface-AF3", queueName: queues.AF3, ecnProfile: "DropProfile", scheduler: "scheduler", + sequence: uint32(1), + priority: oc.Scheduler_Priority_UNSET, + inputType: oc.Input_InputType_QUEUE, + weight: uint64(32), }, { desc: "output-interface-AF4", queueName: queues.AF4, ecnProfile: "DropProfile", scheduler: "scheduler", + sequence: uint32(0), + priority: oc.Scheduler_Priority_STRICT, + inputType: oc.Input_InputType_QUEUE, + weight: uint64(6), }, { desc: "output-interface-NC1", queueName: queues.NC1, ecnProfile: "DropProfile", scheduler: "scheduler", + sequence: uint32(0), + priority: oc.Scheduler_Priority_STRICT, + inputType: oc.Input_InputType_QUEUE, + weight: uint64(7), }} i := q.GetOrCreateInterface(dp.Name()) @@ -366,6 +434,14 @@ func testQoSOutputIntfConfig(t *testing.T, q *oc.Qos) { for _, tc := range cases { t.Run(tc.desc, func(t *testing.T) { qoscfg.SetForwardingGroup(t, dut, q, tc.queueName, tc.queueName) + s := schedulerPolicy.GetOrCreateScheduler(tc.sequence) + s.SetSequence(tc.sequence) + s.SetPriority(tc.priority) + input := s.GetOrCreateInput(tc.queueName) + input.SetId(tc.queueName) + input.SetInputType(tc.inputType) + input.SetQueue(tc.queueName) + input.SetWeight(tc.weight) output := i.GetOrCreateOutput() schedulerPolicy := output.GetOrCreateSchedulerPolicy() schedulerPolicy.SetName(tc.scheduler) diff --git a/feature/security/gnsi/certz/test_data/README.md b/feature/security/gnsi/certz/test_data/README.md index 2d1badf3128..736b7cd9486 100644 --- a/feature/security/gnsi/certz/test_data/README.md +++ b/feature/security/gnsi/certz/test_data/README.md @@ -21,15 +21,3 @@ Each CA set includes, for both RSA and ECDSA signature types: * CA trust bundle NOTE: Creation of bad data has not been completed yet. - -## 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. - -TODO(OCRPC): Record may not be correct or complete - -```yaml -rpcs: - gnsi: - certz.v1.Certz.GetProfileList: -``` 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 396ddfbaea4..fbfd8da98ef 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 @@ -2,7 +2,6 @@ package basic_static_route_support_test import ( "fmt" - "math" "net" "strings" "testing" @@ -300,8 +299,21 @@ func (td *testData) testRecursiveNextHopResolution(t *testing.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)) - 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)) + + _, ok := gnmi.Watch(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).State(), time.Second*60, func(v *ygnmi.Value[*oc.NetworkInstance_Protocol_Static]) bool { + val, present := v.Val() + return present && val.GetPrefix() == td.staticIPv4.cidr(t) + }).Await(t) + if !ok { + t.Errorf("IPv4 Static Route telemetry failed ") + } + _, ok = gnmi.Watch(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).State(), time.Second*60, func(v *ygnmi.Value[*oc.NetworkInstance_Protocol_Static]) bool { + val, present := v.Val() + return present && val.GetPrefix() == td.staticIPv6.cidr(t) + }).Await(t) + if !ok { + t.Errorf("IPv6 Static Route telemetry failed ") + } gotStatic := gnmi.Get(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).State()) if got, want := gotStatic.GetNextHop("0").GetNextHop(), oc.UnionString(td.advertisedIPv4.address); got != want { @@ -343,14 +355,20 @@ func (td *testData) testRecursiveNextHopResolutionDisabled(t *testing.T) { t.Run("Telemetry", func(t *testing.T) { - 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 static route next-hop recursive lookup is disabled - if got, want := gnmi.Get(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).NextHop("0").Recurse().State()), false; got != want { - t.Errorf("IPv4 Static Route next hop: got: %v, want: %v", got, want) + _, ok := gnmi.Watch(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).State(), time.Second*30, func(v *ygnmi.Value[*oc.NetworkInstance_Protocol_Static]) bool { + val, present := v.Val() + return !present || (present && !val.GetNextHop("0").GetRecurse()) + }).Await(t) + if !ok { + t.Errorf("Unable to set recurse to false for v4 prefix") } - if got, want := gnmi.Get(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).NextHop("0").Recurse().State()), false; got != want { - t.Errorf("IPv6 Static Route next hop: got: %v, want: %v", got, want) + + _, ok = gnmi.Watch(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).State(), time.Second*30, func(v *ygnmi.Value[*oc.NetworkInstance_Protocol_Static]) bool { + val, present := v.Val() + return !present || (present && !val.GetNextHop("0").GetRecurse()) + }).Await(t) + if !ok { + t.Errorf("Unable to set recurse to false for v6 prefix") } }) t.Run("Traffic", func(t *testing.T) { @@ -434,23 +452,44 @@ func (td *testData) testStaticRouteECMP(t *testing.T) { gnmi.Await(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).Prefix().State(), 120*time.Second, td.staticIPv4.cidr(t)) gnmi.Await(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).Prefix().State(), 120*time.Second, td.staticIPv6.cidr(t)) - // Validate both the routes i.e. ipv4-route-[a|b] are configured and reported - // correctly - gotStatic := gnmi.Get(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).State()) - if got, want := gotStatic.GetNextHop("0").GetNextHop(), oc.UnionString(atePort1.IPv4); got != want { - t.Errorf("IPv4 Static Route next hop: got: %s, want: %s", got, want) - } - if got, want := gotStatic.GetNextHop("1").GetNextHop(), oc.UnionString(atePort2.IPv4); got != want { - t.Errorf("IPv4 Static Route next hop: got: %s, want: %s", got, want) - } - // Validate both the routes i.e. ipv6-route-[a|b] are configured and reported - // correctly - gotStatic = gnmi.Get(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).State()) - if got, want := gotStatic.GetNextHop("0").GetNextHop(), oc.UnionString(atePort1.IPv6); got != want { - t.Errorf("IPv6 Static Route next hop: got: %s, want: %s", got, want) - } - if got, want := gotStatic.GetNextHop("1").GetNextHop(), oc.UnionString(atePort2.IPv6); got != want { - t.Errorf("IPv6 Static Route next hop: got: %s, want: %s", got, want) + if deviations.SkipStaticNexthopCheck(td.dut) { + nexthops := gnmi.LookupAll(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).NextHopAny().NextHop().State()) + if len(nexthops) != 2 { + t.Errorf("IPv4 Static Route next hop: want %d nexthops,got %d nexthops", 2, len(nexthops)) + } + for _, nexthop := range nexthops { + if got, ok := nexthop.Val(); !ok || !(got != oc.UnionString(atePort1.IPv4) || got != oc.UnionString(atePort2.IPv4)) { + t.Errorf("IPv4 Static Route next hop:got %s,want %s or %s", got, oc.UnionString(atePort1.IPv4), oc.UnionString(atePort2.IPv4)) + } + } + nexthops = gnmi.LookupAll(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).NextHopAny().NextHop().State()) + if len(nexthops) != 2 { + t.Errorf("IPv6 Static Route next hop: want %d nexthops,got %d nexthops", 2, len(nexthops)) + } + for _, nexthop := range nexthops { + if got, ok := nexthop.Val(); !ok || !(got != oc.UnionString(atePort1.IPv6) || got != oc.UnionString(atePort2.IPv6)) { + t.Errorf("IPv6 Static Route next hop: got %s,want %s or %s", got, oc.UnionString(atePort1.IPv6), oc.UnionString(atePort2.IPv6)) + } + } + } else { + // Validate both the routes i.e. ipv4-route-[a|b] are configured and reported + // correctly + gotStatic := gnmi.Get(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).State()) + if got, want := gotStatic.GetNextHop("0").GetNextHop(), oc.UnionString(atePort1.IPv4); got != want { + t.Errorf("IPv4 Static Route next hop: got: %s, want: %s", got, want) + } + if got, want := gotStatic.GetNextHop("1").GetNextHop(), oc.UnionString(atePort2.IPv4); got != want { + t.Errorf("IPv4 Static Route next hop: got: %s, want: %s", got, want) + } + // Validate both the routes i.e. ipv6-route-[a|b] are configured and reported + // correctly + gotStatic = gnmi.Get(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).State()) + if got, want := gotStatic.GetNextHop("0").GetNextHop(), oc.UnionString(atePort1.IPv6); got != want { + t.Errorf("IPv6 Static Route next hop: got: %s, want: %s", got, want) + } + if got, want := gotStatic.GetNextHop("1").GetNextHop(), oc.UnionString(atePort2.IPv6); got != want { + t.Errorf("IPv6 Static Route next hop: got: %s, want: %s", got, want) + } } }) @@ -653,11 +692,40 @@ func (td *testData) testStaticRouteWithPreference(t *testing.T) { 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("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("0").Preference().State()), port1Preference; got != want { - t.Errorf("IPv6 Static Route preference for NextHop 0, got: %d, want: %d", got, want) + if deviations.SkipStaticNexthopCheck(td.dut) { + gotStatic := gnmi.Get(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).State()) + indexes := gnmi.LookupAll(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).NextHopAny().Index().State()) + for _, index := range indexes { + if val, ok := index.Val(); ok { + if gotStatic.GetNextHop(val).GetNextHop() == oc.UnionString(atePort1.IPv4) { + if got, want := gotStatic.GetNextHop(val).GetPreference(), port1Preference; got != want { + t.Errorf("IPv4 Static Route preference for port1: got: %d, want: %d", got, want) + } + } + } else { + t.Errorf("Unable to fetch nexthop index") + } + } + gotStatic = gnmi.Get(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).State()) + indexes = gnmi.LookupAll(t, td.dut, sp.Static(td.staticIPv6.cidr(t)).NextHopAny().Index().State()) + for _, index := range indexes { + if val, ok := index.Val(); ok { + if gotStatic.GetNextHop(val).GetNextHop() == oc.UnionString(atePort1.IPv6) { + if got, want := gotStatic.GetNextHop(val).GetPreference(), port1Preference; got != want { + t.Errorf("IPv6 Static Route preference for port1: got: %d, want: %d", got, want) + } + } + } else { + t.Errorf("Unable to fetch nexthop index") + } + } + } else { + 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("0").Preference().State()), port1Preference; got != want { + t.Errorf("IPv6 Static Route preference for NextHop 0, got: %d, want: %d", got, want) + } } }) @@ -914,12 +982,25 @@ func (td *testData) testIPv4StaticRouteWithIPv6NextHop(t *testing.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)) gnmi.Await(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).Prefix().State(), 30*time.Second, td.staticIPv4.cidr(t)) - gotStatic := gnmi.Get(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).State()) - if got, want := gotStatic.GetNextHop("0").GetNextHop(), oc.UnionString(atePort1.IPv6); got != want { - t.Errorf("IPv4 Static Route next hop: got: %s, want: %s", got, want) - } - if got, want := gotStatic.GetNextHop("1").GetNextHop(), oc.UnionString(atePort2.IPv6); got != want { - t.Errorf("IPv4 Static Route next hop: got: %s, want: %s", got, want) + + if deviations.SkipStaticNexthopCheck(td.dut) { + nexthops := gnmi.LookupAll(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).NextHopAny().NextHop().State()) + if len(nexthops) != 2 { + t.Errorf("IPv4 Static Route next hop: want %d nexthops,got %d nexthops", 2, len(nexthops)) + } + for _, nexthop := range nexthops { + if got, ok := nexthop.Val(); !ok || !(got != oc.UnionString(atePort1.IPv6) || got != oc.UnionString(atePort2.IPv6)) { + t.Errorf("IPv4 Static Route next hop: got %s,want %s or %s", got, oc.UnionString(atePort1.IPv6), oc.UnionString(atePort2.IPv6)) + } + } + } else { + gotStatic := gnmi.Get(t, td.dut, sp.Static(td.staticIPv4.cidr(t)).State()) + if got, want := gotStatic.GetNextHop("0").GetNextHop(), oc.UnionString(atePort1.IPv6); got != want { + t.Errorf("IPv4 Static Route next hop: got: %s, want: %s", got, want) + } + if got, want := gotStatic.GetNextHop("1").GetNextHop(), oc.UnionString(atePort2.IPv6); got != want { + t.Errorf("IPv4 Static Route next hop: got: %s, want: %s", got, want) + } } }) @@ -1075,6 +1156,10 @@ func (td *testData) configureOTGFlows(t *testing.T) { v4FIp.Src().SetValue(srcV4.Address()) v4FIp.Dst().Increment().SetStart(v4TrafficStart).SetCount(254) + udp := v4F.Packet().Add().Udp() + udp.DstPort().Increment().SetStart(1).SetCount(500).SetStep(1) + udp.SrcPort().Increment().SetStart(1).SetCount(500).SetStep(1) + eth := v4F.EgressPacket().Add().Ethernet() ethTag := eth.Dst().MetricTags().Add() ethTag.SetName("MACTrackingv4").SetOffset(36).SetLength(12) @@ -1088,7 +1173,11 @@ func (td *testData) configureOTGFlows(t *testing.T) { v6FIP := v6F.Packet().Add().Ipv6() v6FIP.Src().SetValue(srcV6.Address()) - v6FIP.Dst().Increment().SetStart(v6TrafficStart).SetCount(math.MaxInt32) + v6FIP.Dst().Increment().SetStart(v6TrafficStart).SetCount(254) + + udp = v6F.Packet().Add().Udp() + udp.DstPort().Increment().SetStart(1).SetCount(500).SetStep(1) + udp.SrcPort().Increment().SetStart(1).SetCount(500).SetStep(1) eth = v6F.EgressPacket().Add().Ethernet() ethTag = eth.Dst().MetricTags().Add() @@ -1177,7 +1266,11 @@ func (td *testData) advertiseRoutesWithISIS(t *testing.T) { g.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST).Enabled = ygot.Bool(true) g.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV6, oc.IsisTypes_SAFI_TYPE_UNICAST).Enabled = ygot.Bool(true) - isis.GetOrCreateLevel(2).SetMetricStyle(oc.Isis_MetricStyle_WIDE_METRIC) + isisLevel2 := isis.GetOrCreateLevel(2) + isisLevel2.MetricStyle = oc.Isis_MetricStyle_WIDE_METRIC + if deviations.ISISLevelEnabled(td.dut) { + isisLevel2.Enabled = ygot.Bool(true) + } p1Name := td.dut.Port(t, "port1").Name() p2Name := td.dut.Port(t, "port2").Name() 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 af2d54b1fdc..34fb8af4eaf 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 @@ -39,4 +39,14 @@ platform_exceptions: { } } +platform_exceptions: { + platform: { + vendor: JUNIPER + } + deviations: { + ipv6_static_route_with_ipv4_nh_unsupported: true + skip_static_nexthop_check: true + isis_level_enabled: true + } +} tags: TAGS_DATACENTER_EDGE diff --git a/feature/system/gnmi/set/tests/gnmi_set_test/README.md b/feature/system/gnmi/set/tests/gnmi_set_test/README.md index 98aa53351ce..f87f4045c14 100644 --- a/feature/system/gnmi/set/tests/gnmi_set_test/README.md +++ b/feature/system/gnmi/set/tests/gnmi_set_test/README.md @@ -196,3 +196,13 @@ This test checks that the static protocol name is usable. ## RPC Coverage * gNMI.Set + +## OpenConfig Path and RPC Coverage + +```yaml +rpcs: + gnmi: + gNMI.Get: + gNMI.Subscribe: + +``` 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 9fe05d0e4b2..13cf7f56ecd 100644 --- a/feature/system/gnmi/set/tests/gnmi_set_test/metadata.textproto +++ b/feature/system/gnmi/set/tests/gnmi_set_test/metadata.textproto @@ -22,6 +22,7 @@ platform_exceptions: { } deviations: { skip_static_nexthop_check: true + interface_ref_interface_id_format: true } } platform_exceptions: { 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 index 56dc565f366..b1da32ac404 100644 --- 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 @@ -18,6 +18,7 @@ import ( "fmt" "math" "sort" + "strconv" "testing" "time" @@ -31,6 +32,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" ) @@ -41,6 +43,7 @@ const ( prefixesCount = 1 pathID = 1 defaultRoute = "0:0:0:0:0:0:0:0" + ateNetPrefix = "2001:0db8::192:0:3:1" ) var ( @@ -80,7 +83,14 @@ func TestManagementHA1(t *testing.T) { true, true, ) - if deviations.SetNoPeerGroup(dut) { + if deviations.BgpAfiSafiInDefaultNiBeforeOtherNi(dut) { + g := bs.DUTConf.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)).GetOrCreateProtocol(cfgplugins.PTBGP, "BGP").GetOrCreateBgp().GetOrCreateGlobal() + g.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_L3VPN_IPV6_UNICAST).Enabled = ygot.Bool(true) + } + bgp := bs.DUTConf.GetOrCreateNetworkInstance(mgmtVRF).GetOrCreateProtocol(cfgplugins.PTBGP, "BGP").GetOrCreateBgp() + bgp.GetOrCreateGlobal().GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateUseMultiplePaths().GetOrCreateEbgp() + + if deviations.SetNoPeerGroup(dut) || deviations.PeerGroupDefEbgpVrfUnsupported(dut) { 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 { @@ -193,7 +203,7 @@ func createFlowV6(t *testing.T, bs *cfgplugins.BGPSession) { 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.Src().SetValue(ateNetPrefix) v6.Dst().Increment().SetStart(prefixesStart).SetCount(1) icmp1 := v6Flow.Packet().Add().Icmp() icmp1.SetEcho(gosnappi.NewFlowIcmpEcho()) @@ -249,10 +259,13 @@ func configureLoopbackOnDUT(t *testing.T, dut *ondatra.DUTDevice) { func createInterfaces(t *testing.T, dut *ondatra.DUTDevice, intfNames []string) { root := &oc.Root{} - for _, intfName := range intfNames { + for index, intfName := range intfNames { i := root.GetOrCreateInterface(intfName) + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + i.Description = ygot.String(fmt.Sprintf("Port %s", strconv.Itoa(index+1))) if intfName == netutil.LoopbackInterface(t, dut, 1) { i.Type = oc.IETFInterfaces_InterfaceType_softwareLoopback + i.Description = ygot.String(fmt.Sprintf("Port %s", intfName)) } si := i.GetOrCreateSubinterface(0) si.Enabled = ygot.Bool(true) @@ -315,7 +328,16 @@ func advertiseDUTLoopbackToATE(t *testing.T, dut *ondatra.DUTDevice, bs *cfgplug gnmi.BatchUpdate(batchSet, gnmi.OC().RoutingPolicy().Config(), rp) if deviations.TableConnectionsUnsupported(dut) { - stmt.GetOrCreateConditions().SetInstallProtocolEq(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_DIRECTLY_CONNECTED) + if deviations.RedisConnectedUnderEbgpVrfUnsupported(dut) && dut.Vendor() == ondatra.CISCO { + cliPath, err := schemaless.NewConfig[string]("", "cli") + if err != nil { + t.Fatalf("Failed to create CLI ygnmi query: %v", err) + } + cliCfg := getCiscoCLIRedisConfig("BGP", cfgplugins.DutAS, mgmtVRF) + gnmi.BatchUpdate(batchSet, cliPath, cliCfg) + } else { + stmt.GetOrCreateConditions().SetInstallProtocolEq(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_DIRECTLY_CONNECTED) + } stmt.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE 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() @@ -396,3 +418,11 @@ func configureImportExportBGPPolicy(t *testing.T, bs *cfgplugins.BGPSession, dut func lossPct(tx, rx float64) float64 { return (math.Abs(tx-rx) * 100) / tx } + +func getCiscoCLIRedisConfig(instanceName string, as uint32, vrf string) string { + cfg := fmt.Sprintf("router bgp %d instance %s\n", as, instanceName) + cfg = cfg + fmt.Sprintf(" vrf %s\n", vrf) + cfg = cfg + " address-family ipv6 unicast\n" + cfg = cfg + " redistribute connected\n" + return cfg +} diff --git a/feature/system/management/otg_tests/management_ha_test/metadata.textproto b/feature/system/management/otg_tests/management_ha_test/metadata.textproto index e9db4bd8c9f..bbc70c53bc1 100644 --- a/feature/system/management/otg_tests/management_ha_test/metadata.textproto +++ b/feature/system/management/otg_tests/management_ha_test/metadata.textproto @@ -33,5 +33,17 @@ platform_exceptions: { table_connections_unsupported: true } } +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + explicit_enable_bgp_on_default_vrf: true + peer_group_def_ebgp_vrf_unsupported: true + redis_connected_under_ebgp_vrf_unsupported: true + table_connections_unsupported: true + bgp_afi_safi_in_default_ni_before_other_ni: true + } +} tags: TAGS_TRANSIT tags: TAGS_DATACENTER_EDGE diff --git a/feature/system/ntp/tests/system_ntp_test/metadata.textproto b/feature/system/ntp/tests/system_ntp_test/metadata.textproto index cf10ef334d1..28b530604bb 100644 --- a/feature/system/ntp/tests/system_ntp_test/metadata.textproto +++ b/feature/system/ntp/tests/system_ntp_test/metadata.textproto @@ -5,14 +5,6 @@ uuid: "9e5ec4a5-0adb-48aa-b6c2-c0d604d15934" plan_id: "OC-26.1" description: "Network Time Protocol (NTP)" testbed: TESTBED_DUT -platform_exceptions: { - platform: { - vendor: JUNIPER - } - deviations: { - ntp_non_default_vrf_unsupported: true - } -} platform_exceptions: { platform: { vendor: NOKIA @@ -21,4 +13,3 @@ platform_exceptions: { ntp_non_default_vrf_unsupported: true } } - diff --git a/go.mod b/go.mod index ff25c88cf95..cbbf859e66c 100644 --- a/go.mod +++ b/go.mod @@ -16,9 +16,10 @@ require ( 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.3.0 + github.com/openconfig/containerz v0.0.0-20240620162940-e0bf23af17d6 github.com/openconfig/entity-naming v0.0.0-20230912181021-7ac806551a31 github.com/openconfig/gnmi v0.11.0 - github.com/openconfig/gnoi v0.4.0 + github.com/openconfig/gnoi v0.4.1-0.20240501161656-1d16819bab6a github.com/openconfig/gnoigo v0.0.0-20240320202954-ebd033e3542c github.com/openconfig/gnsi v1.4.5 github.com/openconfig/gocloser v0.0.0-20220310182203-c6c950ed3b0b @@ -27,7 +28,7 @@ require ( github.com/openconfig/gribigo v0.0.0-20231213034307-d0abeba7f432 github.com/openconfig/kne v0.1.18 github.com/openconfig/models-ci v1.0.2-0.20231113233730-f0986391428e - github.com/openconfig/ondatra v0.5.8 + github.com/openconfig/ondatra v0.6.0 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 @@ -44,11 +45,11 @@ require ( golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 golang.org/x/text v0.14.0 google.golang.org/api v0.162.0 - google.golang.org/grpc v1.63.0 - google.golang.org/protobuf v1.33.0 + google.golang.org/grpc v1.63.2 + google.golang.org/protobuf v1.34.1 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/klog/v2 v2.100.1 + k8s.io/klog/v2 v2.120.1 ) require ( @@ -120,6 +121,7 @@ require ( github.com/networkop/meshnet-cni v0.3.1-0.20230525201116-d7c306c635cf // indirect github.com/open-traffic-generator/keng-operator v0.3.28 // indirect github.com/openconfig/attestz v0.2.0 // indirect + github.com/openconfig/gnpsi v0.3.2 // indirect 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 @@ -152,7 +154,7 @@ require ( 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/sys v0.20.0 // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.19.0 // indirect diff --git a/go.sum b/go.sum index 2772513b114..0838dc64992 100644 --- a/go.sum +++ b/go.sum @@ -1366,7 +1366,6 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -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/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -1638,6 +1637,8 @@ github.com/open-traffic-generator/snappi/gosnappi v1.3.0 h1:6SFSuZLTuncLW1xMcBG5 github.com/open-traffic-generator/snappi/gosnappi v1.3.0/go.mod h1:CaE4nisXftNXdXWvTSqb4eiW2WMFIXkJsH5rqPoipcg= github.com/openconfig/attestz v0.2.0 h1:VuksFIG1OlGnRuUpdTFAkMyAY59ITvyLbp4AtiTXV64= github.com/openconfig/attestz v0.2.0/go.mod h1:byY6H68zm3VXmQHEb4O4OZtRtFyHEjkmzrvIljYc79Y= +github.com/openconfig/containerz v0.0.0-20240620162940-e0bf23af17d6 h1:4SPV//llewH/1v5l3+ogzUubksBGSeI+hHLXTAw2T1A= +github.com/openconfig/containerz v0.0.0-20240620162940-e0bf23af17d6/go.mod h1:Byu9uT5Yyz8XEKv9eUBcWqAoUADVvfmN8m+BGqTQPoc= 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= @@ -1645,10 +1646,12 @@ github.com/openconfig/gnmi v0.0.0-20200508230933-d19cebf5e7be/go.mod h1:M/EcuapN 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/gnoi v0.4.1-0.20240501161656-1d16819bab6a h1:wDllmsI9aUalYMF3Z0VQa2GZv8hcaHYfZsSNJARuUfk= +github.com/openconfig/gnoi v0.4.1-0.20240501161656-1d16819bab6a/go.mod h1:QVnt7KL8l6WphIfLuHHpgZfNO+MoXE610gSLOLV9VcI= 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/gnpsi v0.3.2 h1:+bl1bXMOTrWOcGydWB+8wGgvxlgvL8Y6joAiWFU5sog= +github.com/openconfig/gnpsi v0.3.2/go.mod h1:+Qj2PwadJ/jvGkH6H/A3XO9ZRKQRVtl3A30ubwz0M18= github.com/openconfig/gnsi v1.4.5 h1:ZkUVN+HDg0GFe3wIYocWjPqjOHZP3vVEB+NIctaYV6c= github.com/openconfig/gnsi v1.4.5/go.mod h1:i4guw9Vn3Mn/aUKB/2z84Moe2P811H3SV3g0Cu0X4h8= github.com/openconfig/gocloser v0.0.0-20220310182203-c6c950ed3b0b h1:NSYuxdlOWLldNpid1dThR6Dci96juXioUguMho6aliI= @@ -1673,8 +1676,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.8 h1:hTkewcIRtOC6FOpcGThFZPS38EgCbfqh+uwlppMotiA= -github.com/openconfig/ondatra v0.5.8/go.mod h1:6vT2uWz1neH/hDa4ZX8H8pT6sYZHvIftrUugQyG9RrA= +github.com/openconfig/ondatra v0.6.0 h1:wKedHwumAjJTFrbtEdWu71S4S6fO2IEXWgI6mQPtHho= +github.com/openconfig/ondatra v0.6.0/go.mod h1:8w9H5yGPctJrdeORW/WCK1sos+goQCqIx8ABTLNPR68= 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= @@ -1753,8 +1756,8 @@ github.com/sirikothe/gotextfsm v1.0.1-0.20200816110946-6aa2cfd355e4 h1:FHUL2HofY github.com/sirikothe/gotextfsm v1.0.1-0.20200816110946-6aa2cfd355e4/go.mod h1:CJYqpTg9u5VPCoD0VEl9E68prCIiWQD8m457k098DdQ= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= @@ -2189,8 +2192,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2678,8 +2681,8 @@ google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9Y 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/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 v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/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= @@ -2700,8 +2703,9 @@ google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -2742,8 +2746,8 @@ k8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k= k8s.io/apimachinery v0.26.3/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= k8s.io/client-go v0.26.3 h1:k1UY+KXfkxV2ScEL3gilKcF7761xkYsSD6BC9szIu8s= k8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ= -k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= -k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a h1:gmovKNur38vgoWfGtP5QOGNOA7ki4n6qNYoFAgMlNvg= k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY= k8s.io/utils v0.0.0-20230313181309-38a27ef9d749 h1:xMMXJlJbsU8w3V5N2FLDQ8YgU8s1EoULdbQBcAeNJkY= diff --git a/internal/args/args.go b/internal/args/args.go index c39bf70920c..1afbf3aa1f3 100644 --- a/internal/args/args.go +++ b/internal/args/args.go @@ -27,8 +27,8 @@ var ( NumControllerCards = flag.Int("arg_num_controller_cards", -1, "The expected number of controller cards. Some devices with a single controller report 0, which is a valid expected value. Expectation is not checked for values < 0.") NumLinecards = flag.Int("arg_num_linecards", -1, "The expected number of linecards. Some devices with a single linecard report 0, which is a valid expected value. Expectation is not checked for values < 0.") NumFabrics = flag.Int("arg_num_fabrics", -1, "The expected number of fabrics. Some devices with a single fabric report 0, which is a valid expected value. Expectation is not checked for values < 0.") - P4RTNodeName1 = flag.String("arg_p4rt_node_name_1", "", "The P4RT Node Name for the first FAP. Test that reserves ports in the same FAP should configure this P4RT Node. The value will only be used if deviation ExplicitP4RTNodeComponent is applied.") - P4RTNodeName2 = flag.String("arg_p4rt_node_name_2", "", "The P4RT Node Name for the second FAP. Test that reserves ports in two different FAPs should configure this P4RT Node in addition to the Node defined in P4RTNodeName1. The value will only be used if deviation ExplicitP4RTNodeComponent is applied.") + NumFans = flag.Int("arg_num_fans", 0, "The expected number of fans (default is 0, meaning the device is not expected to have fans so none are validated).") + NumFanTrays = flag.Int("arg_num_fan_trays", 0, "The expected number of fan trays (default is 0, meaning the device is not expected to have fan trays so none are validated).") FullConfigReplaceTime = flag.Duration("arg_full_config_replace_time", 0, "Time taken for gNMI set operation to complete full configuration replace. Expected duration is in nanoseconds. Expectation is not checked when value is 0.") SubsetConfigReplaceTime = flag.Duration("arg_subset_config_replace_time", 0, "Time taken for gNMI set operation to modify a subset of configuration. Expected duration is in nanoseconds. Expectation is not checked when value is 0.") QoSBaseConfigPresent = flag.Bool("arg_qos_baseconfig_present", true, "QoS Counter subtest in gNMI-1.10 requires related base config to be loaded. Use this flag to skip the when base config is not loaded.") diff --git a/internal/cfgplugins/bgp.go b/internal/cfgplugins/bgp.go index c5ff799f29d..b1b69e1b003 100644 --- a/internal/cfgplugins/bgp.go +++ b/internal/cfgplugins/bgp.go @@ -93,7 +93,7 @@ var ( Name: "port4", IPv4: "192.0.2.13", IPv4Len: plenIPv4, - IPv6: "2001:0db8::192:0:2:13", + IPv6: "2001:0db8::192:0:2:d", IPv6Len: plenIPv6, } diff --git a/internal/cntrsrv/build/Dockerfile.local b/internal/cntrsrv/build/Dockerfile.local new file mode 100644 index 00000000000..997c221adf2 --- /dev/null +++ b/internal/cntrsrv/build/Dockerfile.local @@ -0,0 +1,5 @@ +FROM alpine:3.16 + +COPY cntrsrv / + +CMD ["./cntrsrv"] diff --git a/internal/components/components.go b/internal/components/components.go index a3d6b90a304..6878df539d0 100644 --- a/internal/components/components.go +++ b/internal/components/components.go @@ -58,6 +58,28 @@ func FindComponentsByType(t *testing.T, dut *ondatra.DUTDevice, cType oc.E_Platf return s } +// FindActiveComponentsByType finds the list of active components based on hardware type. +func FindActiveComponentsByType(t *testing.T, dut *ondatra.DUTDevice, cType oc.E_PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT) []string { + components := gnmi.GetAll[*oc.Component](t, dut, gnmi.OC().ComponentAny().State()) + var s []string + for _, c := range components { + if c.GetType() == nil { + t.Logf("Component %s type is missing from telemetry", c.GetName()) + continue + } + t.Logf("Component %s has type: %v", c.GetName(), c.GetType()) + switch v := c.GetType().(type) { + case oc.E_PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT: + if v == cType && c.OperStatus == oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE { + s = append(s, c.GetName()) + } + default: + t.Logf("Detected non-hardware component: (%T, %v)", c.GetType(), c.GetType()) + } + } + return s +} + // FindSWComponentsByType finds the list of SW components based on a type. func FindSWComponentsByType(t *testing.T, dut *ondatra.DUTDevice, cType oc.E_PlatformTypes_OPENCONFIG_SOFTWARE_COMPONENT) []string { components := gnmi.GetAll[*oc.Component](t, dut, gnmi.OC().ComponentAny().State()) diff --git a/internal/core/core.go b/internal/core/core.go index 438c646b297..20f0032fccf 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -20,6 +20,7 @@ package core import ( "bytes" "context" + "errors" "fmt" "regexp" "sync" @@ -212,7 +213,7 @@ func registerAfter(_ *eventlis.AfterTestsEvent) error { glog.Infof(msg) ondatra.Report().AddSuiteProperty("validator.core.end", report) if foundCores { - return fmt.Errorf(msg) + return errors.New(msg) } return nil } diff --git a/internal/deviations/deviations.go b/internal/deviations/deviations.go index 411f7d4c216..f740a28aecd 100644 --- a/internal/deviations/deviations.go +++ b/internal/deviations/deviations.go @@ -146,12 +146,6 @@ func DefaultNetworkInstance(dut *ondatra.DUTDevice) string { return "DEFAULT" } -// ExplicitP4RTNodeComponent returns if device does not report P4RT node names in the component hierarchy. -// Fully compliant devices should report the PORT hardware components with the INTEGRATED_CIRCUIT components as their parents, as the P4RT node names. -func ExplicitP4RTNodeComponent(dut *ondatra.DUTDevice) bool { - return lookupDUTDeviations(dut).GetExplicitP4RtNodeComponent() -} - // ISISRestartSuppressUnsupported returns whether the device should skip isis restart-suppress check. func ISISRestartSuppressUnsupported(dut *ondatra.DUTDevice) bool { return lookupDUTDeviations(dut).GetIsisRestartSuppressUnsupported() @@ -185,11 +179,6 @@ func StaticProtocolName(dut *ondatra.DUTDevice) string { return "DEFAULT" } -// UseVendorNativeACLConfig returns whether a device requires native model to configure ACL, specifically for RT-1.4. -func UseVendorNativeACLConfig(dut *ondatra.DUTDevice) bool { - return lookupDUTDeviations(dut).GetUseVendorNativeAclConfig() -} - // SwitchChipIDUnsupported returns whether the device supports id leaf for SwitchChip components. func SwitchChipIDUnsupported(dut *ondatra.DUTDevice) bool { return lookupDUTDeviations(dut).GetSwitchChipIdUnsupported() @@ -397,11 +386,6 @@ func QOSDroppedOctets(dut *ondatra.DUTDevice) bool { return lookupDUTDeviations(dut).GetQosDroppedOctets() } -// ExplicitGRIBIUnderNetworkInstance returns if device requires gribi-protocol to be enabled under network-instance. -func ExplicitGRIBIUnderNetworkInstance(dut *ondatra.DUTDevice) bool { - return lookupDUTDeviations(dut).GetExplicitGribiUnderNetworkInstance() -} - // BGPMD5RequiresReset returns if device requires a BGP session reset to utilize a new MD5 key. func BGPMD5RequiresReset(dut *ondatra.DUTDevice) bool { return lookupDUTDeviations(dut).GetBgpMd5RequiresReset() @@ -912,11 +896,6 @@ 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() @@ -1024,6 +1003,11 @@ func DefaultRoutePolicyUnsupported(dut *ondatra.DUTDevice) bool { return lookupDUTDeviations(dut).GetDefaultRoutePolicyUnsupported() } +// CommunityMatchWithRedistributionUnsupported is set to true for devices that do not support matching community at the redistribution attach point. +func CommunityMatchWithRedistributionUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetCommunityMatchWithRedistributionUnsupported() +} + // BgpMaxMultipathPathsUnsupported returns true if the device does not support // bgp max multipaths. func BgpMaxMultipathPathsUnsupported(dut *ondatra.DUTDevice) bool { @@ -1041,3 +1025,97 @@ func MultipathUnsupportedNeighborOrAfisafi(dut *ondatra.DUTDevice) bool { func ModelNameUnsupported(dut *ondatra.DUTDevice) bool { return lookupDUTDeviations(dut).GetModelNameUnsupported() } + +// InstallPositionAndInstallComponentUnsupported returns true if install +// position and install component are not supported. +func InstallPositionAndInstallComponentUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetInstallPositionAndInstallComponentUnsupported() +} + +// EncapTunnelShutBackupNhgZeroTraffic returns true when encap tunnel is shut then zero traffic flows to backup NHG +func EncapTunnelShutBackupNhgZeroTraffic(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetEncapTunnelShutBackupNhgZeroTraffic() +} + +// MaxEcmpPaths supported for isis max ecmp path +func MaxEcmpPaths(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetMaxEcmpPaths() +} + +// WecmpAutoUnsupported returns true if wecmp auto is not supported +func WecmpAutoUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetWecmpAutoUnsupported() +} + +// RoutingPolicyChainingUnsupported returns true if policy chaining is unsupported +func RoutingPolicyChainingUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetRoutingPolicyChainingUnsupported() +} + +// ISISLoopbackRequired returns true if isis loopback is required. +func ISISLoopbackRequired(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetIsisLoopbackRequired() +} + +// WeightedEcmpFixedPacketVerification returns true if fixed packet is used in traffic flow +func WeightedEcmpFixedPacketVerification(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetWeightedEcmpFixedPacketVerification() +} + +// OverrideDefaultNhScale returns true if default NextHop scale needs to be modified +// else returns false +func OverrideDefaultNhScale(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetOverrideDefaultNhScale() +} + +// BgpExtendedCommunitySetUnsupported returns true if set bgp extended community is unsupported +func BgpExtendedCommunitySetUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetBgpExtendedCommunitySetUnsupported() +} + +// BgpSetExtCommunitySetRefsUnsupported returns true if bgp set ext community refs is unsupported +func BgpSetExtCommunitySetRefsUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetBgpSetExtCommunitySetRefsUnsupported() +} + +// BgpDeleteLinkBandwidthUnsupported returns true if bgp delete link bandwidth is unsupported +func BgpDeleteLinkBandwidthUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetBgpDeleteLinkBandwidthUnsupported() +} + +// QOSInQueueDropCounterUnsupported returns true if /qos/interfaces/interface/input/queues/queue/state/dropped-pkts +// is not supported for any component type. +func QOSInQueueDropCounterUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetQosInqueueDropCounterUnsupported() +} + +// BgpExplicitExtendedCommunityEnable returns true if explicit extended community enable is needed +func BgpExplicitExtendedCommunityEnable(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetBgpExplicitExtendedCommunityEnable() +} + +// MatchTagSetConditionUnsupported returns true if match tag set condition is not supported +func MatchTagSetConditionUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetMatchTagSetConditionUnsupported() +} + +// PeerGroupDefEbgpVrfUnsupported returns true if peer group definition under ebgp vrf is unsupported +func PeerGroupDefEbgpVrfUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetPeerGroupDefEbgpVrfUnsupported() +} + +// RedisConnectedUnderEbgpVrfUnsupported returns true if redistribution of routes under ebgp vrf is unsupported +func RedisConnectedUnderEbgpVrfUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetRedisConnectedUnderEbgpVrfUnsupported() +} + +// BgpAfiSafiInDefaultNiBeforeOtherNi returns true if certain AFI SAFIs are configured in default network instance before other network instances +func BgpAfiSafiInDefaultNiBeforeOtherNi(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetBgpAfiSafiInDefaultNiBeforeOtherNi() +} + +// DefaultImportExportPolicyUnsupported returns true when device +// does not support default import export policy. +func DefaultImportExportPolicyUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetDefaultImportExportPolicyUnsupported() +} diff --git a/internal/encap_frr/base.go b/internal/encap_frr/base.go new file mode 100644 index 00000000000..30a0203e072 --- /dev/null +++ b/internal/encap_frr/base.go @@ -0,0 +1,578 @@ +// 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 base contains utility functions for encap frr using repair VRF. +package base + +import ( + "context" + "strconv" + "testing" + "time" + + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + "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" +) + +const ( + plenIPv4 = 30 + magicIP = "192.168.1.1" + magicMAC = "02:00:00:00:00:01" + maskLen24 = "24" + maskLen32 = "32" + niEncapTeVrfA = "ENCAP_TE_VRF_A" + niTEVRF111 = "TE_VRF_111" + niTEVRF222 = "TE_VRF_222" + niRepairVrf = "REPAIR_VRF" + ipv4OuterSrc111Addr = "198.51.100.111" + ipv4OuterSrc222Addr = "198.51.100.222" + gribiIPv4EntryDefVRF1 = "192.0.2.101" + gribiIPv4EntryDefVRF2 = "192.0.2.102" + gribiIPv4EntryDefVRF3 = "192.0.2.103" + gribiIPv4EntryDefVRF4 = "192.0.2.104" + gribiIPv4EntryDefVRF5 = "192.0.2.105" + gribiIPv4EntryVRF1111 = "203.0.113.1" + gribiIPv4EntryVRF1112 = "203.0.113.2" + gribiIPv4EntryVRF2221 = "203.0.113.100" + gribiIPv4EntryVRF2222 = "203.0.113.101" + gribiIPv4EntryEncapVRF = "138.0.11.0" + noMatchEncapDest = "20.0.0.1" +) + +var ( + dutPort2DummyIP = attrs.Attributes{ + Desc: "dutPort2", + IPv4Sec: "192.0.2.33", + IPv4LenSec: plenIPv4, + } + + otgPort2DummyIP = attrs.Attributes{ + Desc: "otgPort2", + IPv4: "192.0.2.34", + IPv4Len: plenIPv4, + } + + dutPort3DummyIP = attrs.Attributes{ + Desc: "dutPort3", + IPv4Sec: "192.0.2.37", + IPv4LenSec: plenIPv4, + } + + otgPort3DummyIP = attrs.Attributes{ + Desc: "otgPort3", + IPv4: "192.0.2.38", + IPv4Len: plenIPv4, + } + + dutPort4DummyIP = attrs.Attributes{ + Desc: "dutPort4", + IPv4Sec: "192.0.2.41", + IPv4LenSec: plenIPv4, + } + + otgPort4DummyIP = attrs.Attributes{ + Desc: "otgPort4", + IPv4: "192.0.2.42", + IPv4Len: plenIPv4, + } + + dutPort5DummyIP = attrs.Attributes{ + Desc: "dutPort5", + IPv4Sec: "192.0.2.45", + IPv4LenSec: plenIPv4, + } + + otgPort5DummyIP = attrs.Attributes{ + Desc: "otgPort5", + IPv4: "192.0.2.46", + IPv4Len: plenIPv4, + } + dutPort6DummyIP = attrs.Attributes{ + Desc: "dutPort5", + IPv4Sec: "192.0.2.49", + IPv4LenSec: plenIPv4, + } + + otgPort6DummyIP = attrs.Attributes{ + Desc: "otgPort5", + IPv4: "192.0.2.50", + IPv4Len: plenIPv4, + } + dutPort7DummyIP = attrs.Attributes{ + Desc: "dutPort5", + IPv4Sec: "192.0.2.53", + IPv4LenSec: plenIPv4, + } + + otgPort7DummyIP = attrs.Attributes{ + Desc: "otgPort5", + IPv4: "192.0.2.54", + IPv4Len: plenIPv4, + } +) + +func programAftWithDummyIP(t *testing.T, dut *ondatra.DUTDevice, client *fluent.GRIBIClient) { + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(11).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port2").Name()). + WithIPAddress(otgPort2DummyIP.IPv4), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(12).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port3").Name()). + WithIPAddress(otgPort3DummyIP.IPv4), + 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()). + WithIPAddress(otgPort4DummyIP.IPv4), + 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()). + WithIPAddress(otgPort5DummyIP.IPv4), + 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()). + WithIPAddress(otgPort6DummyIP.IPv4), + 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()). + WithIPAddress(otgPort7DummyIP.IPv4), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(15).AddNextHop(16, 1), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF5+"/"+maskLen32).WithNextHopGroup(15), + ) +} + +// 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 +} + +// StaticARPWithSpecificIP configures secondary IPs and static ARP. +func StaticARPWithSpecificIP(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") + gnmi.Update(t, dut, gnmi.OC().Interface(p2.Name()).Config(), dutPort2DummyIP.NewOCInterface(p2.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p3.Name()).Config(), dutPort3DummyIP.NewOCInterface(p3.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p4.Name()).Config(), dutPort4DummyIP.NewOCInterface(p4.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p5.Name()).Config(), dutPort5DummyIP.NewOCInterface(p5.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p6.Name()).Config(), dutPort6DummyIP.NewOCInterface(p6.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p7.Name()).Config(), dutPort7DummyIP.NewOCInterface(p7.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p2.Name()).Config(), configStaticArp(p2.Name(), otgPort2DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p3.Name()).Config(), configStaticArp(p3.Name(), otgPort3DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p4.Name()).Config(), configStaticArp(p4.Name(), otgPort4DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p5.Name()).Config(), configStaticArp(p5.Name(), otgPort5DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p6.Name()).Config(), configStaticArp(p6.Name(), otgPort6DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p7.Name()).Config(), configStaticArp(p7.Name(), otgPort7DummyIP.IPv4, magicMAC)) +} + +// StaticARPWithMagicUniversalIP programs the static ARP with magic universal IP +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) +} + +// ConfigureBaseGribiRoutes programs the base gribi routes for encap FRR using repair VRF +func ConfigureBaseGribiRoutes(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, client *fluent.GRIBIClient) { + t.Helper() + + // Programming AFT entries for prefixes in DEFAULT VRF + if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) { + 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 if deviations.GRIBIMACOverrideWithStaticARP(dut) { + programAftWithDummyIP(t, dut, client) + } else { + 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(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + client.Modify().AddEntry(t, + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF1+"/"+maskLen32).WithNextHopGroup(11), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF2+"/"+maskLen32).WithNextHopGroup(12), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF3+"/"+maskLen32).WithNextHopGroup(13), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF4+"/"+maskLen32).WithNextHopGroup(14), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF5+"/"+maskLen32).WithNextHopGroup(15), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + defaultVRFIPList := []string{gribiIPv4EntryDefVRF1, gribiIPv4EntryDefVRF2, gribiIPv4EntryDefVRF3, gribiIPv4EntryDefVRF4, gribiIPv4EntryDefVRF5} + for ip := range defaultVRFIPList { + chk.HasResult(t, client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(defaultVRFIPList[ip]+"/32"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } + + // Programming AFT entries for backup NHG + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(2000).WithDecapsulateHeader(fluent.IPinIP).WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(2000).AddNextHop(2000, 1), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + // Programming AFT entries for prefixes in TE_VRF_222 + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(3).WithIPAddress(gribiIPv4EntryDefVRF3), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(2).AddNextHop(3, 1).WithBackupNHG(2000), + 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).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF2222+"/"+maskLen32).WithNextHopGroup(4), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + teVRF222IPList := []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF2222} + for ip := range teVRF222IPList { + chk.HasResult(t, client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(teVRF222IPList[ip]+"/32"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } + + // Programming AFT entries for backup NHG + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1000).WithDecapsulateHeader(fluent.IPinIP).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc222Addr, gribiIPv4EntryVRF2221). + WithNextHopNetworkInstance(niTEVRF222), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1000).AddNextHop(1000, 1).WithBackupNHG(2000), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1001).WithDecapsulateHeader(fluent.IPinIP).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc222Addr, gribiIPv4EntryVRF2222). + WithNextHopNetworkInstance(niTEVRF222), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1001).AddNextHop(1001, 1).WithBackupNHG(2000), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(3000).WithNextHopNetworkInstance(niRepairVrf), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(3000).AddNextHop(3000, 1), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + // Programming AFT entries for prefixes in TE_VRF_111 + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1).WithIPAddress(gribiIPv4EntryDefVRF1), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(2).WithIPAddress(gribiIPv4EntryDefVRF2), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1).AddNextHop(1, 1).AddNextHop(2, 3).WithBackupNHG(3000), + 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(3000), + fluent.IPv4Entry().WithNetworkInstance(niTEVRF111).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF1112+"/"+maskLen32).WithNextHopGroup(3), + + fluent.IPv4Entry().WithNetworkInstance(niRepairVrf).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF1111+"/"+maskLen32).WithNextHopGroup(1000), + fluent.IPv4Entry().WithNetworkInstance(niRepairVrf).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF1112+"/"+maskLen32).WithNextHopGroup(1001), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + teVRF111IPList := []string{gribiIPv4EntryVRF1111, gribiIPv4EntryVRF1112} + for ip := range teVRF111IPList { + chk.HasResult(t, client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(teVRF111IPList[ip]+"/32"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } + + // Programming AFT entries for backup NHG + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(2001).WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(2001).AddNextHop(2001, 1), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + // Programming AFT entries for prefixes in ENCAP_TE_VRF_A + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(101).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc111Addr, gribiIPv4EntryVRF1111). + WithNextHopNetworkInstance(niTEVRF111), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(102).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc111Addr, gribiIPv4EntryVRF1112). + WithNextHopNetworkInstance(niTEVRF111), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(101).AddNextHop(101, 1).AddNextHop(102, 3).WithBackupNHG(2001), + fluent.IPv4Entry().WithNetworkInstance(niEncapTeVrfA).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryEncapVRF+"/"+maskLen24).WithNextHopGroup(101), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + chk.HasResult(t, client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(gribiIPv4EntryEncapVRF+"/24"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) +} + +// TestCase is a struct to hold the parameters for FRR test cases. +type TestCase struct { + Desc string + DownPortList []string + CapturePortList []string + EncapHeaderOuterIPList []string + EncapHeaderInnerIPList []string + TrafficDestIP string + LoadBalancePercent []float64 + TestID string +} + +// TestCases returns a list of base test cases for FRR tests. +func TestCases(atePortNamelist []string, ipv4InnerDst string) []*TestCase { + cases := []*TestCase{ + { + Desc: "Test-1: primary encap unviable but backup encap viable for single tunnel", + DownPortList: []string{"port2", "port3", "port4"}, + CapturePortList: []string{atePortNamelist[4], atePortNamelist[5]}, + EncapHeaderOuterIPList: []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF1112}, + EncapHeaderInnerIPList: []string{ipv4InnerDst, ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0.25, 0.75, 0, 0}, + TestID: "primarySingle", + }, { + Desc: "Test-2: primary and backup encap unviable for single tunnel", + DownPortList: []string{"port2", "port3", "port4", "port5"}, + CapturePortList: []string{atePortNamelist[5], atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{gribiIPv4EntryVRF1112}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0.75, 0, 0.25}, + TestID: "primaryBackupSingle", + }, { + Desc: "Test-3: primary encap unviable with backup to routing for single tunnel", + DownPortList: []string{"port2", "port3", "port4"}, + CapturePortList: []string{atePortNamelist[5], atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{gribiIPv4EntryVRF1112}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0.75, 0, 0.25}, + TestID: "primaryBackupRoutingSingle", + }, { + Desc: "Test-4: primary encap unviable but backup encap viable for all tunnels", + DownPortList: []string{"port2", "port3", "port4", "port6"}, + CapturePortList: []string{atePortNamelist[4], atePortNamelist[6]}, + EncapHeaderOuterIPList: []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF2222}, + EncapHeaderInnerIPList: []string{ipv4InnerDst, ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0.25, 0, 0.75, 0}, + TestID: "primaryAll", + }, { + Desc: "Test-5: primary and backup encap unviable for all tunnels", + DownPortList: []string{"port2", "port3", "port4", "port5", "port6", "port7"}, + CapturePortList: []string{atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, + TestID: "primaryBackupAll", + }, { + Desc: "Test-6: primary encap unviable with backup to routing for all tunnels", + DownPortList: []string{"port2", "port3", "port4", "port6"}, + CapturePortList: []string{atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, + TestID: "primaryBackupRoutingAll", + }, { + Desc: "Test-7: no match in encap VRF", + DownPortList: []string{}, + CapturePortList: []string{atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{}, + EncapHeaderInnerIPList: []string{noMatchEncapDest}, + TrafficDestIP: noMatchEncapDest, + LoadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, + TestID: "encapNoMatch", + }, + } + + return cases +} + +// awaitTimeout calls a fluent client Await, adding a timeout to the context. +func awaitTimeout(ctx context.Context, t testing.TB, c *fluent.GRIBIClient, timeout time.Duration) error { + t.Helper() + subctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + return c.Await(subctx, t) +} diff --git a/internal/encapfrr/base.go b/internal/encapfrr/base.go new file mode 100644 index 00000000000..8274906bc99 --- /dev/null +++ b/internal/encapfrr/base.go @@ -0,0 +1,425 @@ +// 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 encapfrr contains utility functions for encap frr using repair VRF. +package encapfrr + +import ( + "context" + "strconv" + "testing" + "time" + + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + "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" +) + +const ( + plenIPv4 = 30 + magicIP = "192.168.1.1" + magicMAC = "02:00:00:00:00:01" + maskLen32 = "32" + gribiIPv4EntryDefVRF1 = "192.0.2.101" + gribiIPv4EntryDefVRF2 = "192.0.2.102" + gribiIPv4EntryDefVRF3 = "192.0.2.103" + gribiIPv4EntryDefVRF4 = "192.0.2.104" + gribiIPv4EntryDefVRF5 = "192.0.2.105" + gribiIPv4EntryVRF1111 = "203.0.113.1" + gribiIPv4EntryVRF1112 = "203.0.113.2" + gribiIPv4EntryVRF2221 = "203.0.113.100" + gribiIPv4EntryVRF2222 = "203.0.113.101" + gribiIPv4EntryEncapVRF = "138.0.11.0" + noMatchEncapDest = "20.0.0.1" +) + +var ( + dutPort2DummyIP = attrs.Attributes{ + Desc: "dutPort2", + IPv4Sec: "192.0.2.33", + IPv4LenSec: plenIPv4, + } + + otgPort2DummyIP = attrs.Attributes{ + Desc: "otgPort2", + IPv4: "192.0.2.34", + IPv4Len: plenIPv4, + } + + dutPort3DummyIP = attrs.Attributes{ + Desc: "dutPort3", + IPv4Sec: "192.0.2.37", + IPv4LenSec: plenIPv4, + } + + otgPort3DummyIP = attrs.Attributes{ + Desc: "otgPort3", + IPv4: "192.0.2.38", + IPv4Len: plenIPv4, + } + + dutPort4DummyIP = attrs.Attributes{ + Desc: "dutPort4", + IPv4Sec: "192.0.2.41", + IPv4LenSec: plenIPv4, + } + + otgPort4DummyIP = attrs.Attributes{ + Desc: "otgPort4", + IPv4: "192.0.2.42", + IPv4Len: plenIPv4, + } + + dutPort5DummyIP = attrs.Attributes{ + Desc: "dutPort5", + IPv4Sec: "192.0.2.45", + IPv4LenSec: plenIPv4, + } + + otgPort5DummyIP = attrs.Attributes{ + Desc: "otgPort5", + IPv4: "192.0.2.46", + IPv4Len: plenIPv4, + } + dutPort6DummyIP = attrs.Attributes{ + Desc: "dutPort5", + IPv4Sec: "192.0.2.49", + IPv4LenSec: plenIPv4, + } + + otgPort6DummyIP = attrs.Attributes{ + Desc: "otgPort5", + IPv4: "192.0.2.50", + IPv4Len: plenIPv4, + } + dutPort7DummyIP = attrs.Attributes{ + Desc: "dutPort5", + IPv4Sec: "192.0.2.53", + IPv4LenSec: plenIPv4, + } + + otgPort7DummyIP = attrs.Attributes{ + Desc: "otgPort5", + IPv4: "192.0.2.54", + IPv4Len: plenIPv4, + } +) + +func programAftWithDummyIP(t *testing.T, dut *ondatra.DUTDevice, client *fluent.GRIBIClient) { + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(11).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port2").Name()). + WithIPAddress(otgPort2DummyIP.IPv4), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(12).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port3").Name()). + WithIPAddress(otgPort3DummyIP.IPv4), + 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()). + WithIPAddress(otgPort4DummyIP.IPv4), + 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()). + WithIPAddress(otgPort5DummyIP.IPv4), + 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()). + WithIPAddress(otgPort6DummyIP.IPv4), + 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()). + WithIPAddress(otgPort7DummyIP.IPv4), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(15).AddNextHop(16, 1), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF5+"/"+maskLen32).WithNextHopGroup(15), + ) +} + +// 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 +} + +// StaticARPWithSpecificIP configures secondary IPs and static ARP. +func StaticARPWithSpecificIP(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") + gnmi.Update(t, dut, gnmi.OC().Interface(p2.Name()).Config(), dutPort2DummyIP.NewOCInterface(p2.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p3.Name()).Config(), dutPort3DummyIP.NewOCInterface(p3.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p4.Name()).Config(), dutPort4DummyIP.NewOCInterface(p4.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p5.Name()).Config(), dutPort5DummyIP.NewOCInterface(p5.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p6.Name()).Config(), dutPort6DummyIP.NewOCInterface(p6.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p7.Name()).Config(), dutPort7DummyIP.NewOCInterface(p7.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p2.Name()).Config(), configStaticArp(p2.Name(), otgPort2DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p3.Name()).Config(), configStaticArp(p3.Name(), otgPort3DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p4.Name()).Config(), configStaticArp(p4.Name(), otgPort4DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p5.Name()).Config(), configStaticArp(p5.Name(), otgPort5DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p6.Name()).Config(), configStaticArp(p6.Name(), otgPort6DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p7.Name()).Config(), configStaticArp(p7.Name(), otgPort7DummyIP.IPv4, magicMAC)) +} + +// StaticARPWithMagicUniversalIP programs the static ARP with magic universal IP +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) +} + +// ConfigureBaseGribiRoutes programs the base gribi routes for encap FRR using repair VRF +func ConfigureBaseGribiRoutes(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, client *fluent.GRIBIClient) { + t.Helper() + + // Programming AFT entries for prefixes in DEFAULT VRF + if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) { + 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 if deviations.GRIBIMACOverrideWithStaticARP(dut) { + programAftWithDummyIP(t, dut, client) + } else { + 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(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + client.Modify().AddEntry(t, + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF1+"/"+maskLen32).WithNextHopGroup(11), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF2+"/"+maskLen32).WithNextHopGroup(12), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF3+"/"+maskLen32).WithNextHopGroup(13), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF4+"/"+maskLen32).WithNextHopGroup(14), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF5+"/"+maskLen32).WithNextHopGroup(15), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + defaultVRFIPList := []string{gribiIPv4EntryDefVRF1, gribiIPv4EntryDefVRF2, gribiIPv4EntryDefVRF3, gribiIPv4EntryDefVRF4, gribiIPv4EntryDefVRF5} + for ip := range defaultVRFIPList { + chk.HasResult(t, client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(defaultVRFIPList[ip]+"/32"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } +} + +// TestCase is a struct to hold the parameters for FRR test cases. +type TestCase struct { + Desc string + DownPortList []string + CapturePortList []string + EncapHeaderOuterIPList []string + EncapHeaderInnerIPList []string + TrafficDestIP string + LoadBalancePercent []float64 + TestID string +} + +// TestCases returns a list of base test cases for FRR tests. +func TestCases(atePortNamelist []string, ipv4InnerDst string) []*TestCase { + cases := []*TestCase{ + { + Desc: "Test-1: primary encap unviable but backup encap viable for single tunnel", + DownPortList: []string{"port2", "port3", "port4"}, + CapturePortList: []string{atePortNamelist[4], atePortNamelist[5]}, + EncapHeaderOuterIPList: []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF1112}, + EncapHeaderInnerIPList: []string{ipv4InnerDst, ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0.25, 0.75, 0, 0}, + TestID: "primarySingle", + }, { + Desc: "Test-2: primary and backup encap unviable for single tunnel", + DownPortList: []string{"port2", "port3", "port4", "port5"}, + CapturePortList: []string{atePortNamelist[5], atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{gribiIPv4EntryVRF1112}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0.75, 0, 0.25}, + TestID: "primaryBackupSingle", + }, { + Desc: "Test-3: primary encap unviable with backup to routing for single tunnel", + DownPortList: []string{"port2", "port3", "port4"}, + CapturePortList: []string{atePortNamelist[5], atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{gribiIPv4EntryVRF1112}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0.75, 0, 0.25}, + TestID: "primaryBackupRoutingSingle", + }, { + Desc: "Test-4: primary encap unviable but backup encap viable for all tunnels", + DownPortList: []string{"port2", "port3", "port4", "port6"}, + CapturePortList: []string{atePortNamelist[4], atePortNamelist[6]}, + EncapHeaderOuterIPList: []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF2222}, + EncapHeaderInnerIPList: []string{ipv4InnerDst, ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0.25, 0, 0.75, 0}, + TestID: "primaryAll", + }, { + Desc: "Test-5: primary and backup encap unviable for all tunnels", + DownPortList: []string{"port2", "port3", "port4", "port5", "port6", "port7"}, + CapturePortList: []string{atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, + TestID: "primaryBackupAll", + }, { + Desc: "Test-6: primary encap unviable with backup to routing for all tunnels", + DownPortList: []string{"port2", "port3", "port4", "port6"}, + CapturePortList: []string{atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, + TestID: "primaryBackupRoutingAll", + }, { + Desc: "Test-7: no match in encap VRF", + DownPortList: []string{}, + CapturePortList: []string{atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{}, + EncapHeaderInnerIPList: []string{noMatchEncapDest}, + TrafficDestIP: noMatchEncapDest, + LoadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, + TestID: "encapNoMatch", + }, + } + + return cases +} + +// awaitTimeout calls a fluent client Await, adding a timeout to the context. +func awaitTimeout(ctx context.Context, t testing.TB, c *fluent.GRIBIClient, timeout time.Duration) error { + t.Helper() + subctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + return c.Await(subctx, t) +} diff --git a/internal/fptest/networkinstance.go b/internal/fptest/networkinstance.go index a9168d79a3b..07f85d80cba 100644 --- a/internal/fptest/networkinstance.go +++ b/internal/fptest/networkinstance.go @@ -15,12 +15,10 @@ package fptest import ( - "context" "fmt" "testing" "github.com/openconfig/featureprofiles/internal/deviations" - gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" "github.com/openconfig/ondatra/gnmi/oc" @@ -55,50 +53,3 @@ func AssignToNetworkInstance(t testing.TB, d *ondatra.DUTDevice, i string, ni st gnmi.Update(t, d, gnmi.OC().NetworkInstance(ni).Config(), netInst) } } - -// EnableGRIBIUnderNetworkInstance enables GRIBI protocol under network instance. -func EnableGRIBIUnderNetworkInstance(t testing.TB, d *ondatra.DUTDevice, ni string) { - t.Helper() - if ni == "" { - t.Fatalf("Network instance not provided for gRIBI protocol definition") - } - - switch d.Vendor() { - case ondatra.NOKIA: - gpbSetRequest := &gpb.SetRequest{ - Prefix: &gpb.Path{ - Origin: "srl", - }, - Update: []*gpb.Update{{ - Path: &gpb.Path{ - Elem: []*gpb.PathElem{ - { - Name: "network-instance", - Key: map[string]string{"name": ni}, - }, - { - Name: "protocols", - }, - { - Name: "gribi", - }, - { - Name: "admin-state", - }, - }, - }, - Val: &gpb.TypedValue{ - Value: &gpb.TypedValue_JsonIetfVal{ - JsonIetfVal: []byte(`"enable"`), - }, - }, - }}, - } - gnmiClient := d.RawAPIs().GNMI(t) - if _, err := gnmiClient.Set(context.Background(), gpbSetRequest); err != nil { - t.Fatalf("Enabling Gribi on network-instance %s failed with unexpected error: %v", ni, err) - } - default: - t.Fatalf("Vendor %s does not support 'deviation_explicit_gribi_under_network_instance'", d.Vendor()) - } -} diff --git a/internal/gnoi/gnoi.go b/internal/gnoi/gnoi.go new file mode 100644 index 00000000000..48eec464b83 --- /dev/null +++ b/internal/gnoi/gnoi.go @@ -0,0 +1,133 @@ +// 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 gnoi provides utilities for interacting with the gNOI API. +package gnoi + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/openconfig/featureprofiles/internal/system" + gpb "github.com/openconfig/gnmi/proto/gnmi" + spb "github.com/openconfig/gnoi/system" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ygnmi/ygnmi" +) + +var ( + daemonProcessNames = map[ondatra.Vendor]map[Daemon]string{ + ondatra.ARISTA: { + GRIBI: "Gribi", + OCAGENT: "Octa", + P4RT: "P4Runtime", + ROUTING: "Bgp-main", + }, + ondatra.CISCO: { + GRIBI: "emsd", + P4RT: "emsd", + ROUTING: "emsd", + }, + ondatra.JUNIPER: { + GRIBI: "rpd", + P4RT: "p4-switch", + ROUTING: "rpd", + }, + ondatra.NOKIA: { + GRIBI: "sr_grpc_server", + OCAGENT: "sr_oc_mgmt_serv", + P4RT: "sr_grpc_server", + ROUTING: "sr_bgp_mgr", + }, + } +) + +// Daemon is the type of the daemon on the device. +type Daemon string + +const ( + // GRIBI is the gRIBI daemon. + GRIBI Daemon = "GRIBI" + // OCAGENT is the OpenConfig agent daemon. + OCAGENT Daemon = "OCAGENT" + // P4RT is the P4RT daemon. + P4RT Daemon = "P4RT" + // ROUTING is the routing daemon. + ROUTING Daemon = "ROUTING" +) + +// signal type of termination request +const ( + SigTerm = spb.KillProcessRequest_SIGNAL_TERM + SigKill = spb.KillProcessRequest_SIGNAL_KILL + SigHup = spb.KillProcessRequest_SIGNAL_HUP + SigAbort = spb.KillProcessRequest_SIGNAL_ABRT + SigUnspecified = spb.KillProcessRequest_SIGNAL_UNSPECIFIED +) + +// KillProcess terminates the daemon on the DUT. +func KillProcess(t *testing.T, dut *ondatra.DUTDevice, daemon Daemon, signal spb.KillProcessRequest_Signal, restart bool, waitForRestart bool) { + t.Helper() + + daemonName, err := FetchProcessName(dut, daemon) + if err != nil { + t.Fatalf("Daemon %s not defined for vendor %s", daemon, dut.Vendor().String()) + } + pid := system.FindProcessIDByName(t, dut, daemonName) + if pid == 0 { + t.Fatalf("process %s not found on device", daemonName) + } + + gnoiClient := dut.RawAPIs().GNOI(t) + killProcessRequest := &spb.KillProcessRequest{ + Signal: signal, + Name: daemonName, + Pid: uint32(pid), + Restart: restart, + } + gnoiClient.System().KillProcess(context.Background(), killProcessRequest) + + if waitForRestart { + gnmi.WatchAll( + t, + dut.GNMIOpts().WithYGNMIOpts(ygnmi.WithSubscriptionMode(gpb.SubscriptionMode_ON_CHANGE)), + gnmi.OC().System().ProcessAny().State(), + time.Minute, + func(p *ygnmi.Value[*oc.System_Process]) bool { + val, ok := p.Val() + if !ok { + return false + } + return val.GetName() == daemonName && val.GetPid() != pid + }, + ) + } +} + +// FetchProcessName returns the name of the daemon on the DUT based on the vendor. +func FetchProcessName(dut *ondatra.DUTDevice, daemon Daemon) (string, error) { + daemons, ok := daemonProcessNames[dut.Vendor()] + if !ok { + return "", fmt.Errorf("unsupported vendor: %s", dut.Vendor().String()) + } + d, ok := daemons[daemon] + if !ok { + return "", fmt.Errorf("daemon %s not defined for vendor %s", daemon, dut.Vendor().String()) + } + return d, nil +} diff --git a/internal/isissession/isissession.go b/internal/isissession/isissession.go index 11b43dce24b..3fc1dae70a8 100644 --- a/internal/isissession/isissession.go +++ b/internal/isissession/isissession.go @@ -58,8 +58,6 @@ const ( ) var ( - // DUTNET is the Network Entity Title for the DUT - DUTNET = fmt.Sprintf("%v.%v.00", DUTAreaAddress, DUTSysID) // DUTISISAttrs has attributes for the DUT ISIS connection on port1 DUTISISAttrs = &attrs.Attributes{ Desc: "DUT to ATE with IS-IS", diff --git a/internal/otgutils/arp.go b/internal/otgutils/arp.go index ee5c0d1440e..7ac5512a36a 100644 --- a/internal/otgutils/arp.go +++ b/internal/otgutils/arp.go @@ -22,14 +22,14 @@ func WaitForARP(t *testing.T, otg *otg.OTG, c gosnappi.Config, ipType string) { for _, intf := range intfs { switch ipType { case "IPv4": - got, ok := gnmi.WatchAll(t, otg, gnmi.OTG().Interface(intf).Ipv4NeighborAny().LinkLayerAddress().State(), time.Minute, func(val *ygnmi.Value[string]) bool { + got, ok := gnmi.WatchAll(t, otg, gnmi.OTG().Interface(intf).Ipv4NeighborAny().LinkLayerAddress().State(), 2*time.Minute, func(val *ygnmi.Value[string]) bool { return val.IsPresent() }).Await(t) if !ok { t.Fatalf("Did not receive OTG Neighbor entry for interface %s, last got: %v", intf, got) } case "IPv6": - got, ok := gnmi.WatchAll(t, otg, gnmi.OTG().Interface(intf).Ipv6NeighborAny().LinkLayerAddress().State(), time.Minute, func(val *ygnmi.Value[string]) bool { + got, ok := gnmi.WatchAll(t, otg, gnmi.OTG().Interface(intf).Ipv6NeighborAny().LinkLayerAddress().State(), 2*time.Minute, func(val *ygnmi.Value[string]) bool { return val.IsPresent() }).Await(t) if !ok { diff --git a/internal/p4rtutils/p4rtutils.go b/internal/p4rtutils/p4rtutils.go index 33e8c8bc9be..34611fb2384 100644 --- a/internal/p4rtutils/p4rtutils.go +++ b/internal/p4rtutils/p4rtutils.go @@ -25,8 +25,6 @@ import ( "github.com/cisco-open/go-p4/p4rt_client" "github.com/golang/glog" - "github.com/openconfig/featureprofiles/internal/args" - "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" "github.com/openconfig/ondatra/gnmi/oc" @@ -184,21 +182,10 @@ func ACLWbbIngressTableEntryGet(infoList []*ACLWbbIngressTableEntryInfo) []*p4_v return updates } -func explicitP4RTNodes() map[string]string { - return map[string]string{ - "port1": *args.P4RTNodeName1, - "port2": *args.P4RTNodeName2, - } -} - // P4RTNodesByPort returns a map of : for the reserved ondatra // ports using the component and the interface OC tree. func P4RTNodesByPort(t testing.TB, dut *ondatra.DUTDevice) map[string]string { t.Helper() - if deviations.ExplicitP4RTNodeComponent(dut) { - return explicitP4RTNodes() - } - ports := make(map[string][]string) // :[] for _, p := range dut.Ports() { hp := gnmi.Lookup(t, dut, gnmi.OC().Interface(p.Name()).HardwarePort().State()) diff --git a/internal/security/gen/generate.go b/internal/security/gen/generate.go index 93e5ada0d7f..5797703d400 100644 --- a/internal/security/gen/generate.go +++ b/internal/security/gen/generate.go @@ -37,7 +37,6 @@ import ( gribipb "github.com/openconfig/gribi/v1/proto/service" bpb "github.com/openconfig/gnoi/bgp" - cpb "github.com/openconfig/gnoi/cert" dpb "github.com/openconfig/gnoi/diag" frpb "github.com/openconfig/gnoi/factory_reset" fpb "github.com/openconfig/gnoi/file" @@ -70,7 +69,6 @@ var ( "gnsi.cred": credpb.File_github_com_openconfig_gnsi_credentialz_credentialz_proto.Services(), "gnsi.acc": accpb.File_github_com_openconfig_gnsi_acctz_acctz_proto.Services(), "gnoi.bgp": bpb.File_bgp_bgp_proto.Services(), - "gnoi.cert": cpb.File_cert_cert_proto.Services(), "gnoi.diag": dpb.File_diag_diag_proto.Services(), "gnoi.factory_reset": frpb.File_factory_reset_factory_reset_proto.Services(), "gnoi.file": fpb.File_file_file_proto.Services(), diff --git a/internal/security/gnxi/rpcexec.go b/internal/security/gnxi/rpcexec.go index f91274b8633..866be871b57 100644 --- a/internal/security/gnxi/rpcexec.go +++ b/internal/security/gnxi/rpcexec.go @@ -102,51 +102,6 @@ func GnoiBgpClearBGPNeighbor(_ context.Context, _ *ondatra.DUTDevice, _ []grpc.D return status.Errorf(codes.Unimplemented, "exec function for RPC /gnoi.bgp.BGP/ClearBGPNeighbor is not implemented") } -// GnoiCertificatemanagementAllRPC implements a sample request for service /gnoi.certificate.CertificateManagement/* to validate if authz works as expected. -func GnoiCertificatemanagementAllRPC(_ context.Context, _ *ondatra.DUTDevice, _ []grpc.DialOption, _ ...any) error { - return status.Errorf(codes.Unimplemented, "exec function for RPC /gnoi.certificate.CertificateManagement/* is not implemented") -} - -// GnoiCertificatemanagementCanGenerateCSR implements a sample request for service /gnoi.certificate.CertificateManagement/CanGenerateCSR to validate if authz works as expected. -func GnoiCertificatemanagementCanGenerateCSR(_ context.Context, _ *ondatra.DUTDevice, _ []grpc.DialOption, _ ...any) error { - return status.Errorf(codes.Unimplemented, "exec function for RPC /gnoi.certificate.CertificateManagement/CanGenerateCSR is not implemented") -} - -// GnoiCertificatemanagementGenerateCSR implements a sample request for service /gnoi.certificate.CertificateManagement/GenerateCSR to validate if authz works as expected. -func GnoiCertificatemanagementGenerateCSR(_ context.Context, _ *ondatra.DUTDevice, _ []grpc.DialOption, _ ...any) error { - return status.Errorf(codes.Unimplemented, "exec function for RPC /gnoi.certificate.CertificateManagement/GenerateCSR is not implemented") -} - -// GnoiCertificatemanagementGetCertificates implements a sample request for service /gnoi.certificate.CertificateManagement/GetCertificates to validate if authz works as expected. -func GnoiCertificatemanagementGetCertificates(_ context.Context, _ *ondatra.DUTDevice, _ []grpc.DialOption, _ ...any) error { - return status.Errorf(codes.Unimplemented, "exec function for RPC /gnoi.certificate.CertificateManagement/GetCertificates is not implemented") -} - -// GnoiCertificatemanagementInstall implements a sample request for service /gnoi.certificate.CertificateManagement/Install to validate if authz works as expected. -func GnoiCertificatemanagementInstall(_ context.Context, _ *ondatra.DUTDevice, _ []grpc.DialOption, _ ...any) error { - return status.Errorf(codes.Unimplemented, "exec function for RPC /gnoi.certificate.CertificateManagement/Install is not implemented") -} - -// GnoiCertificatemanagementLoadCertificate implements a sample request for service /gnoi.certificate.CertificateManagement/LoadCertificate to validate if authz works as expected. -func GnoiCertificatemanagementLoadCertificate(_ context.Context, _ *ondatra.DUTDevice, _ []grpc.DialOption, _ ...any) error { - return status.Errorf(codes.Unimplemented, "exec function for RPC /gnoi.certificate.CertificateManagement/LoadCertificate is not implemented") -} - -// GnoiCertificatemanagementLoadCertificateAuthorityBundle implements a sample request for service /gnoi.certificate.CertificateManagement/LoadCertificateAuthorityBundle to validate if authz works as expected. -func GnoiCertificatemanagementLoadCertificateAuthorityBundle(_ context.Context, _ *ondatra.DUTDevice, _ []grpc.DialOption, _ ...any) error { - return status.Errorf(codes.Unimplemented, "exec function for RPC /gnoi.certificate.CertificateManagement/LoadCertificateAuthorityBundle is not implemented") -} - -// GnoiCertificatemanagementRevokeCertificates implements a sample request for service /gnoi.certificate.CertificateManagement/RevokeCertificates to validate if authz works as expected. -func GnoiCertificatemanagementRevokeCertificates(_ context.Context, _ *ondatra.DUTDevice, _ []grpc.DialOption, _ ...any) error { - return status.Errorf(codes.Unimplemented, "exec function for RPC /gnoi.certificate.CertificateManagement/RevokeCertificates is not implemented") -} - -// GnoiCertificatemanagementRotate implements a sample request for service /gnoi.certificate.CertificateManagement/Rotate to validate if authz works as expected. -func GnoiCertificatemanagementRotate(_ context.Context, _ *ondatra.DUTDevice, _ []grpc.DialOption, _ ...any) error { - return status.Errorf(codes.Unimplemented, "exec function for RPC /gnoi.certificate.CertificateManagement/Rotate is not implemented") -} - // GnoiDiagAllRPC implements a sample request for service /gnoi.diag.Diag/* to validate if authz works as expected. func GnoiDiagAllRPC(_ context.Context, _ *ondatra.DUTDevice, _ []grpc.DialOption, _ ...any) error { return status.Errorf(codes.Unimplemented, "exec function for RPC /gnoi.diag.Diag/* is not implemented") diff --git a/internal/security/gnxi/rpcs.go b/internal/security/gnxi/rpcs.go index 71552e1b1ce..5f44cb8bf54 100644 --- a/internal/security/gnxi/rpcs.go +++ b/internal/security/gnxi/rpcs.go @@ -3,110 +3,101 @@ package gnxi type rpcs struct { - AllRPC *RPC - GnmiAllRPC *RPC - GnmiGet *RPC - GnmiSet *RPC - GnmiSubscribe *RPC - GnmiCapabilities *RPC - GnoiBgpAllRPC *RPC - GnoiBgpClearBGPNeighbor *RPC - GnoiCertificatemanagementAllRPC *RPC - GnoiCertificatemanagementCanGenerateCSR *RPC - GnoiCertificatemanagementGenerateCSR *RPC - GnoiCertificatemanagementGetCertificates *RPC - GnoiCertificatemanagementInstall *RPC - GnoiCertificatemanagementLoadCertificate *RPC - GnoiCertificatemanagementLoadCertificateAuthorityBundle *RPC - GnoiCertificatemanagementRevokeCertificates *RPC - GnoiCertificatemanagementRotate *RPC - GnoiDiagAllRPC *RPC - GnoiDiagGetBERTResult *RPC - GnoiDiagStopBERT *RPC - GnoiDiagStartBERT *RPC - GnoiFactoryresetAllRPC *RPC - GnoiFactoryresetStart *RPC - GnoiFileAllRPC *RPC - GnoiFilePut *RPC - GnoiFileRemove *RPC - GnoiFileStat *RPC - GnoiFileTransferToRemote *RPC - GnoiFileGet *RPC - GnoiHealthzAcknowledge *RPC - GnoiHealthzAllRPC *RPC - GnoiHealthzArtifact *RPC - GnoiHealthzCheck *RPC - GnoiHealthzList *RPC - GnoiHealthzGet *RPC - GnoiLayer2AllRPC *RPC - GnoiLayer2ClearLLDPInterface *RPC - GnoiLayer2ClearSpanningTree *RPC - GnoiLayer2PerformBERT *RPC - GnoiLayer2SendWakeOnLAN *RPC - GnoiLayer2ClearNeighborDiscovery *RPC - GnoiLinkqualificationCreate *RPC - GnoiMplsAllRPC *RPC - GnoiMplsClearLSPCounters *RPC - GnoiMplsMPLSPing *RPC - GnoiMplsClearLSP *RPC - GnoiOtdrAllRPC *RPC - GnoiWavelengthrouterAdjustSpectrum *RPC - GnoiWavelengthrouterAllRPC *RPC - GnoiWavelengthrouterCancelAdjustPSD *RPC - GnoiWavelengthrouterCancelAdjustSpectrum *RPC - GnoiOsActivate *RPC - GnoiOsAllRPC *RPC - GnoiOsVerify *RPC - GnoiOsInstall *RPC - GnoiOtdrInitiate *RPC - GnoiLinkqualificationAllRPC *RPC - GnoiLinkqualificationCapabilities *RPC - GnoiLinkqualificationDelete *RPC - GnoiLinkqualificationGet *RPC - GnoiLinkqualificationList *RPC - GnoiSystemAllRPC *RPC - GnoiSystemCancelReboot *RPC - GnoiSystemKillProcess *RPC - GnoiSystemReboot *RPC - GnoiSystemRebootStatus *RPC - GnoiSystemSetPackage *RPC - GnoiSystemSwitchControlProcessor *RPC - GnoiSystemTime *RPC - GnoiSystemTraceroute *RPC - GnoiSystemPing *RPC - GnoiWavelengthrouterAdjustPSD *RPC - GnsiAcctzAllRPC *RPC - GnsiAcctzRecordSubscribe *RPC - GnsiAuthzAllRPC *RPC - GnsiAuthzGet *RPC - GnsiAuthzProbe *RPC - GnsiAuthzRotate *RPC - GnsiCertzAddProfile *RPC - GnsiCertzAllRPC *RPC - GnsiCertzCanGenerateCSR *RPC - GnsiCertzDeleteProfile *RPC - GnsiCertzGetProfileList *RPC - GnsiCertzRotate *RPC - GnsiCredentialzAllRPC *RPC - GnsiCredentialzCanGenerateKey *RPC - GnsiCredentialzGetPublicKeys *RPC - GnsiCredentialzRotateHostParameters *RPC - GnsiCredentialzRotateAccountCredentials *RPC - GnsiPathzAllRPC *RPC - GnsiPathzGet *RPC - GnsiPathzProbe *RPC - GnsiPathzRotate *RPC - GribiAllRPC *RPC - GribiFlush *RPC - GribiGet *RPC - GribiModify *RPC - P4P4runtimeAllRPC *RPC - P4P4runtimeCapabilities *RPC - P4P4runtimeGetForwardingPipelineConfig *RPC - P4P4runtimeRead *RPC - P4P4runtimeSetForwardingPipelineConfig *RPC - P4P4runtimeStreamChannel *RPC - P4P4runtimeWrite *RPC + AllRPC *RPC + GnmiAllRPC *RPC + GnmiGet *RPC + GnmiSet *RPC + GnmiSubscribe *RPC + GnmiCapabilities *RPC + GnoiBgpAllRPC *RPC + GnoiBgpClearBGPNeighbor *RPC + GnoiDiagAllRPC *RPC + GnoiDiagGetBERTResult *RPC + GnoiDiagStopBERT *RPC + GnoiDiagStartBERT *RPC + GnoiFactoryresetAllRPC *RPC + GnoiFactoryresetStart *RPC + GnoiFileAllRPC *RPC + GnoiFilePut *RPC + GnoiFileRemove *RPC + GnoiFileStat *RPC + GnoiFileTransferToRemote *RPC + GnoiFileGet *RPC + GnoiHealthzAcknowledge *RPC + GnoiHealthzAllRPC *RPC + GnoiHealthzArtifact *RPC + GnoiHealthzCheck *RPC + GnoiHealthzList *RPC + GnoiHealthzGet *RPC + GnoiLayer2AllRPC *RPC + GnoiLayer2ClearLLDPInterface *RPC + GnoiLayer2ClearSpanningTree *RPC + GnoiLayer2PerformBERT *RPC + GnoiLayer2SendWakeOnLAN *RPC + GnoiLayer2ClearNeighborDiscovery *RPC + GnoiLinkqualificationCreate *RPC + GnoiMplsAllRPC *RPC + GnoiMplsClearLSPCounters *RPC + GnoiMplsMPLSPing *RPC + GnoiMplsClearLSP *RPC + GnoiOtdrAllRPC *RPC + GnoiWavelengthrouterAdjustSpectrum *RPC + GnoiWavelengthrouterAllRPC *RPC + GnoiWavelengthrouterCancelAdjustPSD *RPC + GnoiWavelengthrouterCancelAdjustSpectrum *RPC + GnoiOsActivate *RPC + GnoiOsAllRPC *RPC + GnoiOsVerify *RPC + GnoiOsInstall *RPC + GnoiOtdrInitiate *RPC + GnoiLinkqualificationAllRPC *RPC + GnoiLinkqualificationCapabilities *RPC + GnoiLinkqualificationDelete *RPC + GnoiLinkqualificationGet *RPC + GnoiLinkqualificationList *RPC + GnoiSystemAllRPC *RPC + GnoiSystemCancelReboot *RPC + GnoiSystemKillProcess *RPC + GnoiSystemReboot *RPC + GnoiSystemRebootStatus *RPC + GnoiSystemSetPackage *RPC + GnoiSystemSwitchControlProcessor *RPC + GnoiSystemTime *RPC + GnoiSystemTraceroute *RPC + GnoiSystemPing *RPC + GnoiWavelengthrouterAdjustPSD *RPC + GnsiAcctzAllRPC *RPC + GnsiAcctzRecordSubscribe *RPC + GnsiAuthzAllRPC *RPC + GnsiAuthzGet *RPC + GnsiAuthzProbe *RPC + GnsiAuthzRotate *RPC + GnsiCertzAddProfile *RPC + GnsiCertzAllRPC *RPC + GnsiCertzCanGenerateCSR *RPC + GnsiCertzDeleteProfile *RPC + GnsiCertzGetProfileList *RPC + GnsiCertzRotate *RPC + GnsiCredentialzAllRPC *RPC + GnsiCredentialzCanGenerateKey *RPC + GnsiCredentialzGetPublicKeys *RPC + GnsiCredentialzRotateHostParameters *RPC + GnsiCredentialzRotateAccountCredentials *RPC + GnsiPathzAllRPC *RPC + GnsiPathzGet *RPC + GnsiPathzProbe *RPC + GnsiPathzRotate *RPC + GribiAllRPC *RPC + GribiFlush *RPC + GribiGet *RPC + GribiModify *RPC + P4P4runtimeAllRPC *RPC + P4P4runtimeCapabilities *RPC + P4P4runtimeGetForwardingPipelineConfig *RPC + P4P4runtimeRead *RPC + P4P4runtimeSetForwardingPipelineConfig *RPC + P4P4runtimeStreamChannel *RPC + P4P4runtimeWrite *RPC } var ( @@ -167,69 +158,6 @@ var ( Path: "/gnoi.bgp.BGP/ClearBGPNeighbor", Exec: GnoiBgpClearBGPNeighbor, } - gnoicertificateCertificateManagementALL = &RPC{ - Name: "*", - Service: "gnoi.certificate.CertificateManagement", - FQN: "gnoi.certificate.CertificateManagement.*", - Path: "/gnoi.certificate.CertificateManagement/*", - Exec: GnoiCertificatemanagementAllRPC, - } - gnoicertificateCertificateManagementCanGenerateCSR = &RPC{ - Name: "CanGenerateCSR", - Service: "gnoi.certificate.CertificateManagement", - FQN: "gnoi.certificate.CertificateManagement.CanGenerateCSR", - Path: "/gnoi.certificate.CertificateManagement/CanGenerateCSR", - Exec: GnoiCertificatemanagementCanGenerateCSR, - } - gnoicertificateCertificateManagementGenerateCSR = &RPC{ - Name: "GenerateCSR", - Service: "gnoi.certificate.CertificateManagement", - FQN: "gnoi.certificate.CertificateManagement.GenerateCSR", - Path: "/gnoi.certificate.CertificateManagement/GenerateCSR", - Exec: GnoiCertificatemanagementGenerateCSR, - } - gnoicertificateCertificateManagementGetCertificates = &RPC{ - Name: "GetCertificates", - Service: "gnoi.certificate.CertificateManagement", - FQN: "gnoi.certificate.CertificateManagement.GetCertificates", - Path: "/gnoi.certificate.CertificateManagement/GetCertificates", - Exec: GnoiCertificatemanagementGetCertificates, - } - gnoicertificateCertificateManagementInstall = &RPC{ - Name: "Install", - Service: "gnoi.certificate.CertificateManagement", - FQN: "gnoi.certificate.CertificateManagement.Install", - Path: "/gnoi.certificate.CertificateManagement/Install", - Exec: GnoiCertificatemanagementInstall, - } - gnoicertificateCertificateManagementLoadCertificate = &RPC{ - Name: "LoadCertificate", - Service: "gnoi.certificate.CertificateManagement", - FQN: "gnoi.certificate.CertificateManagement.LoadCertificate", - Path: "/gnoi.certificate.CertificateManagement/LoadCertificate", - Exec: GnoiCertificatemanagementLoadCertificate, - } - gnoicertificateCertificateManagementLoadCertificateAuthorityBundle = &RPC{ - Name: "LoadCertificateAuthorityBundle", - Service: "gnoi.certificate.CertificateManagement", - FQN: "gnoi.certificate.CertificateManagement.LoadCertificateAuthorityBundle", - Path: "/gnoi.certificate.CertificateManagement/LoadCertificateAuthorityBundle", - Exec: GnoiCertificatemanagementLoadCertificateAuthorityBundle, - } - gnoicertificateCertificateManagementRevokeCertificates = &RPC{ - Name: "RevokeCertificates", - Service: "gnoi.certificate.CertificateManagement", - FQN: "gnoi.certificate.CertificateManagement.RevokeCertificates", - Path: "/gnoi.certificate.CertificateManagement/RevokeCertificates", - Exec: GnoiCertificatemanagementRevokeCertificates, - } - gnoicertificateCertificateManagementRotate = &RPC{ - Name: "Rotate", - Service: "gnoi.certificate.CertificateManagement", - FQN: "gnoi.certificate.CertificateManagement.Rotate", - Path: "/gnoi.certificate.CertificateManagement/Rotate", - Exec: GnoiCertificatemanagementRotate, - } gnoidiagALL = &RPC{ Name: "*", Service: "gnoi.diag.Diag", @@ -851,123 +779,105 @@ var ( GnmiCapabilities: gnmiCapabilities, GnoiBgpAllRPC: gnoibgpALL, GnoiBgpClearBGPNeighbor: gnoibgpClearBGPNeighbor, - GnoiCertificatemanagementAllRPC: gnoicertificateCertificateManagementALL, - GnoiCertificatemanagementCanGenerateCSR: gnoicertificateCertificateManagementCanGenerateCSR, - GnoiCertificatemanagementGenerateCSR: gnoicertificateCertificateManagementGenerateCSR, - GnoiCertificatemanagementGetCertificates: gnoicertificateCertificateManagementGetCertificates, - GnoiCertificatemanagementInstall: gnoicertificateCertificateManagementInstall, - GnoiCertificatemanagementLoadCertificate: gnoicertificateCertificateManagementLoadCertificate, - GnoiCertificatemanagementLoadCertificateAuthorityBundle: gnoicertificateCertificateManagementLoadCertificateAuthorityBundle, - GnoiCertificatemanagementRevokeCertificates: gnoicertificateCertificateManagementRevokeCertificates, - GnoiCertificatemanagementRotate: gnoicertificateCertificateManagementRotate, - GnoiDiagAllRPC: gnoidiagALL, - GnoiDiagGetBERTResult: gnoidiagGetBERTResult, - GnoiDiagStopBERT: gnoidiagStopBERT, - GnoiDiagStartBERT: gnoidiagStartBERT, - GnoiFactoryresetAllRPC: gnoifactory_resetFactoryResetALL, - GnoiFactoryresetStart: gnoifactory_resetFactoryResetStart, - GnoiFileAllRPC: gnoifileALL, - GnoiFilePut: gnoifilePut, - GnoiFileRemove: gnoifileRemove, - GnoiFileStat: gnoifileStat, - GnoiFileTransferToRemote: gnoifileTransferToRemote, - GnoiFileGet: gnoifileGet, - GnoiHealthzAcknowledge: gnoihealthzAcknowledge, - GnoiHealthzAllRPC: gnoihealthzALL, - GnoiHealthzArtifact: gnoihealthzArtifact, - GnoiHealthzCheck: gnoihealthzCheck, - GnoiHealthzList: gnoihealthzList, - GnoiHealthzGet: gnoihealthzGet, - GnoiLayer2AllRPC: gnoilayer2ALL, - GnoiLayer2ClearLLDPInterface: gnoilayer2ClearLLDPInterface, - GnoiLayer2ClearSpanningTree: gnoilayer2ClearSpanningTree, - GnoiLayer2PerformBERT: gnoilayer2PerformBERT, - GnoiLayer2SendWakeOnLAN: gnoilayer2SendWakeOnLAN, - GnoiLayer2ClearNeighborDiscovery: gnoilayer2ClearNeighborDiscovery, - GnoiLinkqualificationCreate: gnoipacket_link_qualificationLinkQualificationCreate, - GnoiMplsAllRPC: gnoimplsALL, - GnoiMplsClearLSPCounters: gnoimplsClearLSPCounters, - GnoiMplsMPLSPing: gnoimplsMPLSPing, - GnoiMplsClearLSP: gnoimplsClearLSP, - GnoiOtdrAllRPC: gnoiopticalOTDRALL, - GnoiWavelengthrouterAdjustSpectrum: gnoiopticalWavelengthRouterAdjustSpectrum, - GnoiWavelengthrouterAllRPC: gnoiopticalWavelengthRouterALL, - GnoiWavelengthrouterCancelAdjustPSD: gnoiopticalWavelengthRouterCancelAdjustPSD, - GnoiWavelengthrouterCancelAdjustSpectrum: gnoiopticalWavelengthRouterCancelAdjustSpectrum, - GnoiOsActivate: gnoiosActivate, - GnoiOsAllRPC: gnoiosALL, - GnoiOsVerify: gnoiosVerify, - GnoiOsInstall: gnoiosInstall, - GnoiOtdrInitiate: gnoiopticalOTDRInitiate, - GnoiLinkqualificationAllRPC: gnoipacket_link_qualificationLinkQualificationALL, - GnoiLinkqualificationCapabilities: gnoipacket_link_qualificationLinkQualificationCapabilities, - GnoiLinkqualificationDelete: gnoipacket_link_qualificationLinkQualificationDelete, - GnoiLinkqualificationGet: gnoipacket_link_qualificationLinkQualificationGet, - GnoiLinkqualificationList: gnoipacket_link_qualificationLinkQualificationList, - GnoiSystemAllRPC: gnoisystemALL, - GnoiSystemCancelReboot: gnoisystemCancelReboot, - GnoiSystemKillProcess: gnoisystemKillProcess, - GnoiSystemReboot: gnoisystemReboot, - GnoiSystemRebootStatus: gnoisystemRebootStatus, - GnoiSystemSetPackage: gnoisystemSetPackage, - GnoiSystemSwitchControlProcessor: gnoisystemSwitchControlProcessor, - GnoiSystemTime: gnoisystemTime, - GnoiSystemTraceroute: gnoisystemTraceroute, - GnoiSystemPing: gnoisystemPing, - GnoiWavelengthrouterAdjustPSD: gnoiopticalWavelengthRouterAdjustPSD, - GnsiAcctzAllRPC: gnsiacctzv1AcctzALL, - GnsiAcctzRecordSubscribe: gnsiacctzv1AcctzRecordSubscribe, - GnsiAuthzAllRPC: gnsiauthzv1AuthzALL, - GnsiAuthzGet: gnsiauthzv1AuthzGet, - GnsiAuthzProbe: gnsiauthzv1AuthzProbe, - GnsiAuthzRotate: gnsiauthzv1AuthzRotate, - GnsiCertzAddProfile: gnsicertzv1CertzAddProfile, - GnsiCertzAllRPC: gnsicertzv1CertzALL, - GnsiCertzCanGenerateCSR: gnsicertzv1CertzCanGenerateCSR, - GnsiCertzDeleteProfile: gnsicertzv1CertzDeleteProfile, - GnsiCertzGetProfileList: gnsicertzv1CertzGetProfileList, - GnsiCertzRotate: gnsicertzv1CertzRotate, - GnsiCredentialzAllRPC: gnsicredentialzv1CredentialzALL, - GnsiCredentialzCanGenerateKey: gnsicredentialzv1CredentialzCanGenerateKey, - GnsiCredentialzGetPublicKeys: gnsicredentialzv1CredentialzGetPublicKeys, - GnsiCredentialzRotateHostParameters: gnsicredentialzv1CredentialzRotateHostParameters, - GnsiCredentialzRotateAccountCredentials: gnsicredentialzv1CredentialzRotateAccountCredentials, - GnsiPathzAllRPC: gnsipathzv1PathzALL, - GnsiPathzGet: gnsipathzv1PathzGet, - GnsiPathzProbe: gnsipathzv1PathzProbe, - GnsiPathzRotate: gnsipathzv1PathzRotate, - GribiAllRPC: gribiALL, - GribiFlush: gribiFlush, - GribiGet: gribiGet, - GribiModify: gribiModify, - P4P4runtimeAllRPC: p4v1P4RuntimeALL, - P4P4runtimeCapabilities: p4v1P4RuntimeCapabilities, - P4P4runtimeGetForwardingPipelineConfig: p4v1P4RuntimeGetForwardingPipelineConfig, - P4P4runtimeRead: p4v1P4RuntimeRead, - P4P4runtimeSetForwardingPipelineConfig: p4v1P4RuntimeSetForwardingPipelineConfig, - P4P4runtimeStreamChannel: p4v1P4RuntimeStreamChannel, - P4P4runtimeWrite: p4v1P4RuntimeWrite, + GnoiDiagAllRPC: gnoidiagALL, + GnoiDiagGetBERTResult: gnoidiagGetBERTResult, + GnoiDiagStopBERT: gnoidiagStopBERT, + GnoiDiagStartBERT: gnoidiagStartBERT, + GnoiFactoryresetAllRPC: gnoifactory_resetFactoryResetALL, + GnoiFactoryresetStart: gnoifactory_resetFactoryResetStart, + GnoiFileAllRPC: gnoifileALL, + GnoiFilePut: gnoifilePut, + GnoiFileRemove: gnoifileRemove, + GnoiFileStat: gnoifileStat, + GnoiFileTransferToRemote: gnoifileTransferToRemote, + GnoiFileGet: gnoifileGet, + GnoiHealthzAcknowledge: gnoihealthzAcknowledge, + GnoiHealthzAllRPC: gnoihealthzALL, + GnoiHealthzArtifact: gnoihealthzArtifact, + GnoiHealthzCheck: gnoihealthzCheck, + GnoiHealthzList: gnoihealthzList, + GnoiHealthzGet: gnoihealthzGet, + GnoiLayer2AllRPC: gnoilayer2ALL, + GnoiLayer2ClearLLDPInterface: gnoilayer2ClearLLDPInterface, + GnoiLayer2ClearSpanningTree: gnoilayer2ClearSpanningTree, + GnoiLayer2PerformBERT: gnoilayer2PerformBERT, + GnoiLayer2SendWakeOnLAN: gnoilayer2SendWakeOnLAN, + GnoiLayer2ClearNeighborDiscovery: gnoilayer2ClearNeighborDiscovery, + GnoiLinkqualificationCreate: gnoipacket_link_qualificationLinkQualificationCreate, + GnoiMplsAllRPC: gnoimplsALL, + GnoiMplsClearLSPCounters: gnoimplsClearLSPCounters, + GnoiMplsMPLSPing: gnoimplsMPLSPing, + GnoiMplsClearLSP: gnoimplsClearLSP, + GnoiOtdrAllRPC: gnoiopticalOTDRALL, + GnoiWavelengthrouterAdjustSpectrum: gnoiopticalWavelengthRouterAdjustSpectrum, + GnoiWavelengthrouterAllRPC: gnoiopticalWavelengthRouterALL, + GnoiWavelengthrouterCancelAdjustPSD: gnoiopticalWavelengthRouterCancelAdjustPSD, + GnoiWavelengthrouterCancelAdjustSpectrum: gnoiopticalWavelengthRouterCancelAdjustSpectrum, + GnoiOsActivate: gnoiosActivate, + GnoiOsAllRPC: gnoiosALL, + GnoiOsVerify: gnoiosVerify, + GnoiOsInstall: gnoiosInstall, + GnoiOtdrInitiate: gnoiopticalOTDRInitiate, + GnoiLinkqualificationAllRPC: gnoipacket_link_qualificationLinkQualificationALL, + GnoiLinkqualificationCapabilities: gnoipacket_link_qualificationLinkQualificationCapabilities, + GnoiLinkqualificationDelete: gnoipacket_link_qualificationLinkQualificationDelete, + GnoiLinkqualificationGet: gnoipacket_link_qualificationLinkQualificationGet, + GnoiLinkqualificationList: gnoipacket_link_qualificationLinkQualificationList, + GnoiSystemAllRPC: gnoisystemALL, + GnoiSystemCancelReboot: gnoisystemCancelReboot, + GnoiSystemKillProcess: gnoisystemKillProcess, + GnoiSystemReboot: gnoisystemReboot, + GnoiSystemRebootStatus: gnoisystemRebootStatus, + GnoiSystemSetPackage: gnoisystemSetPackage, + GnoiSystemSwitchControlProcessor: gnoisystemSwitchControlProcessor, + GnoiSystemTime: gnoisystemTime, + GnoiSystemTraceroute: gnoisystemTraceroute, + GnoiSystemPing: gnoisystemPing, + GnoiWavelengthrouterAdjustPSD: gnoiopticalWavelengthRouterAdjustPSD, + GnsiAcctzAllRPC: gnsiacctzv1AcctzALL, + GnsiAcctzRecordSubscribe: gnsiacctzv1AcctzRecordSubscribe, + GnsiAuthzAllRPC: gnsiauthzv1AuthzALL, + GnsiAuthzGet: gnsiauthzv1AuthzGet, + GnsiAuthzProbe: gnsiauthzv1AuthzProbe, + GnsiAuthzRotate: gnsiauthzv1AuthzRotate, + GnsiCertzAddProfile: gnsicertzv1CertzAddProfile, + GnsiCertzAllRPC: gnsicertzv1CertzALL, + GnsiCertzCanGenerateCSR: gnsicertzv1CertzCanGenerateCSR, + GnsiCertzDeleteProfile: gnsicertzv1CertzDeleteProfile, + GnsiCertzGetProfileList: gnsicertzv1CertzGetProfileList, + GnsiCertzRotate: gnsicertzv1CertzRotate, + GnsiCredentialzAllRPC: gnsicredentialzv1CredentialzALL, + GnsiCredentialzCanGenerateKey: gnsicredentialzv1CredentialzCanGenerateKey, + GnsiCredentialzGetPublicKeys: gnsicredentialzv1CredentialzGetPublicKeys, + GnsiCredentialzRotateHostParameters: gnsicredentialzv1CredentialzRotateHostParameters, + GnsiCredentialzRotateAccountCredentials: gnsicredentialzv1CredentialzRotateAccountCredentials, + GnsiPathzAllRPC: gnsipathzv1PathzALL, + GnsiPathzGet: gnsipathzv1PathzGet, + GnsiPathzProbe: gnsipathzv1PathzProbe, + GnsiPathzRotate: gnsipathzv1PathzRotate, + GribiAllRPC: gribiALL, + GribiFlush: gribiFlush, + GribiGet: gribiGet, + GribiModify: gribiModify, + P4P4runtimeAllRPC: p4v1P4RuntimeALL, + P4P4runtimeCapabilities: p4v1P4RuntimeCapabilities, + P4P4runtimeGetForwardingPipelineConfig: p4v1P4RuntimeGetForwardingPipelineConfig, + P4P4runtimeRead: p4v1P4RuntimeRead, + P4P4runtimeSetForwardingPipelineConfig: p4v1P4RuntimeSetForwardingPipelineConfig, + P4P4runtimeStreamChannel: p4v1P4RuntimeStreamChannel, + P4P4runtimeWrite: p4v1P4RuntimeWrite, } // RPCMAP is a helper that maps path to RPCs data that may be needed in tests. RPCMAP = map[string]*RPC{ - "*": ALL, - "/gnmi.gNMI/*": gnmiALL, - "/gnmi.gNMI/Get": gnmiGet, - "/gnmi.gNMI/Set": gnmiSet, - "/gnmi.gNMI/Subscribe": gnmiSubscribe, - "/gnmi.gNMI/Capabilities": gnmiCapabilities, - "/gnoi.bgp.BGP/*": gnoibgpALL, - "/gnoi.bgp.BGP/ClearBGPNeighbor": gnoibgpClearBGPNeighbor, - "/gnoi.certificate.CertificateManagement/*": gnoicertificateCertificateManagementALL, - "/gnoi.certificate.CertificateManagement/CanGenerateCSR": gnoicertificateCertificateManagementCanGenerateCSR, - "/gnoi.certificate.CertificateManagement/GenerateCSR": gnoicertificateCertificateManagementGenerateCSR, - "/gnoi.certificate.CertificateManagement/GetCertificates": gnoicertificateCertificateManagementGetCertificates, - "/gnoi.certificate.CertificateManagement/Install": gnoicertificateCertificateManagementInstall, - "/gnoi.certificate.CertificateManagement/LoadCertificate": gnoicertificateCertificateManagementLoadCertificate, - "/gnoi.certificate.CertificateManagement/LoadCertificateAuthorityBundle": gnoicertificateCertificateManagementLoadCertificateAuthorityBundle, - "/gnoi.certificate.CertificateManagement/RevokeCertificates": gnoicertificateCertificateManagementRevokeCertificates, - "/gnoi.certificate.CertificateManagement/Rotate": gnoicertificateCertificateManagementRotate, + "*": ALL, + "/gnmi.gNMI/*": gnmiALL, + "/gnmi.gNMI/Get": gnmiGet, + "/gnmi.gNMI/Set": gnmiSet, + "/gnmi.gNMI/Subscribe": gnmiSubscribe, + "/gnmi.gNMI/Capabilities": gnmiCapabilities, + "/gnoi.bgp.BGP/*": gnoibgpALL, + "/gnoi.bgp.BGP/ClearBGPNeighbor": gnoibgpClearBGPNeighbor, "/gnoi.diag.Diag/*": gnoidiagALL, "/gnoi.diag.Diag/GetBERTResult": gnoidiagGetBERTResult, "/gnoi.diag.Diag/StopBERT": gnoidiagStopBERT, diff --git a/internal/system/system.go b/internal/system/system.go new file mode 100644 index 00000000000..d21116e2750 --- /dev/null +++ b/internal/system/system.go @@ -0,0 +1,39 @@ +// 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 system provides helper functions for gNMI system related operations. +package system + +import ( + "testing" + + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" +) + +// FindProcessIDByName uses telemetry to find out the PID of a process. +func FindProcessIDByName(t *testing.T, dut *ondatra.DUTDevice, pName string) uint64 { + t.Helper() + + var pid uint64 + pList := gnmi.GetAll[*oc.System_Process](t, dut, gnmi.OC().System().ProcessAny().State()) + for _, proc := range pList { + if proc.GetName() == pName { + pid = proc.GetPid() + break + } + } + return pid +} diff --git a/internal/vrfpolicy/vrfpolicy.go b/internal/vrfpolicy/vrfpolicy.go index 603a836b89e..09d687b26a3 100644 --- a/internal/vrfpolicy/vrfpolicy.go +++ b/internal/vrfpolicy/vrfpolicy.go @@ -36,6 +36,7 @@ const ( niEncapTeVrfB = "ENCAP_TE_VRF_B" niEncapTeVrfC = "ENCAP_TE_VRF_C" niEncapTeVrfD = "ENCAP_TE_VRF_D" + niRepairVrf = "REPAIR_VRF" niDefault = "DEFAULT" dscpEncapA1 = 10 dscpEncapA2 = 18 @@ -74,7 +75,7 @@ type policyFwRule struct { func configNonDefaultNetworkInstance(t *testing.T, dut *ondatra.DUTDevice) { t.Helper() c := &oc.Root{} - vrfs := []string{niDecapTeVrf, niEncapTeVrfA, niEncapTeVrfB, niEncapTeVrfC, niEncapTeVrfD, niTeVrf111, niTeVrf222} + vrfs := []string{niDecapTeVrf, niEncapTeVrfA, niEncapTeVrfB, niEncapTeVrfC, niEncapTeVrfD, niTeVrf111, niTeVrf222, niRepairVrf} for _, vrf := range vrfs { ni := c.GetOrCreateNetworkInstance(vrf) ni.Type = oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_L3VRF diff --git a/proto/metadata.proto b/proto/metadata.proto index 30a90e2ee86..2fe7cd171a3 100644 --- a/proto/metadata.proto +++ b/proto/metadata.proto @@ -144,10 +144,6 @@ message Metadata { // Use this deviation when the device does not support a mix of tagged and // untagged subinterfaces. bool no_mix_of_tagged_and_untagged_subinterfaces = 34; - // Device does not report P4RT node names in the component hierarchy. - bool explicit_p4rt_node_component = 35; - // Configure ACLs using vendor native model specifically for RT-1.4. - bool use_vendor_native_acl_config = 36; // Device does not support reporting software version according to the // requirements in gNMI-1.10. bool sw_version_unsupported = 37; @@ -157,8 +153,6 @@ message Metadata { // Device does not support telemetry path /components/component/storage. // Juniper: partnerissuetracker.corp.google.com/284239001 bool storage_component_unsupported = 39; - // Device requires gribi-protocol to be enabled under network-instance. - bool explicit_gribi_under_network_instance = 40; // Device requires port-speed to be set because its default value may not be // usable. bool explicit_port_speed = 41; @@ -500,9 +494,6 @@ message Metadata { 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; // Ensure no configurations exist under BGP Peer Groups @@ -569,10 +560,54 @@ message Metadata { // /components/component/state/model-name of the chassis component must be // equal to the canonical hardware model name of its device. bool model_name_unsupported = 194; - + // community_match_with_redistribution_unsupported is set to true for devices that do not support matching community at the redistribution attach point. + bool community_match_with_redistribution_unsupported = 195; + // Devices that do not support components/component/state/install-component + // and components/component/state/install-position. + bool install_position_and_install_component_unsupported = 196; + // Encap tunnel is shut then zero traffic will flow to backup NHG + bool encap_tunnel_shut_backup_nhg_zero_traffic = 197; + // Flag to indicate support for max ecmp paths for isis. + bool max_ecmp_paths = 198; + // wecmp_auto_unsupported is set to true for devices that do not support auto wecmp + bool wecmp_auto_unsupported = 199; + // policy chaining, ie. more than one policy at an attachement point is not supported + bool routing_policy_chaining_unsupported = 200; + // isis loopback config required + bool isis_loopback_required = 201; + // weighted ecmp feature verification using fixed packet + bool weighted_ecmp_fixed_packet_verification = 202; + // Override default NextHop scale while enabling encap/decap scale + // CISCO: + bool override_default_nh_scale = 203; + // Devices that donot support setting bgp extended community set + bool bgp_extended_community_set_unsupported = 204; + // Devices that do not support setting bgp extended community set refs + bool bgp_set_ext_community_set_refs_unsupported = 205; + // Devices that do not support deleting link bandwidth + bool bgp_delete_link_bandwidth_unsupported = 206; + // qos_inqueue_drop_counter_Unsupported is set to true for devices that do not support qos ingress queue drop counters. + // Juniper: b/341130490 + bool qos_inqueue_drop_counter_unsupported = 207; + // Devices that need bgp extended community enable explicitly + bool bgp_explicit_extended_community_enable = 208; + // devices that do not support match tag set condition + bool match_tag_set_condition_unsupported = 209; + // peer_group_def_bgp_vrf_unsupported is set to true for devices that do not support peer group definition under bgp vrf configuration. + bool peer_group_def_ebgp_vrf_unsupported = 210; + // redis_uconnected_under_ebgp_vrf_unsupported is set to true for devices that do not support redistribution of connected routes under ebgp vrf configuration. + bool redis_connected_under_ebgp_vrf_unsupported = 211; + // bgp_afisafi_in_default_ni_before_other_ni is set to true for devices that require certain afi/safis to be enabled + // in default network instance (ni) before enabling afi/safis for neighbors in default or non-default ni. + bool bgp_afi_safi_in_default_ni_before_other_ni = 212; + // Devices which do not support default import export policy. + bool default_import_export_policy_unsupported = 213; + + // Reserved field numbers and identifiers. - reserved 84, 9, 28, 20, 90, 97, 55, 89, 19; + reserved 84, 9, 28, 20, 90, 97, 55, 89, 19, 36, 35, 40, 173; } + message PlatformExceptions { Platform platform = 1; Deviations deviations = 2; diff --git a/proto/metadata_go_proto/metadata.pb.go b/proto/metadata_go_proto/metadata.pb.go index 798d67044ca..eda6f45c574 100644 --- a/proto/metadata_go_proto/metadata.pb.go +++ b/proto/metadata_go_proto/metadata.pb.go @@ -14,18 +14,19 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.12 +// protoc-gen-go v1.33.0 +// protoc v3.6.1 // source: metadata.proto package metadata_go_proto 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 ( @@ -424,10 +425,6 @@ type Metadata_Deviations struct { // Use this deviation when the device does not support a mix of tagged and // untagged subinterfaces. NoMixOfTaggedAndUntaggedSubinterfaces bool `protobuf:"varint,34,opt,name=no_mix_of_tagged_and_untagged_subinterfaces,json=noMixOfTaggedAndUntaggedSubinterfaces,proto3" json:"no_mix_of_tagged_and_untagged_subinterfaces,omitempty"` - // Device does not report P4RT node names in the component hierarchy. - ExplicitP4RtNodeComponent bool `protobuf:"varint,35,opt,name=explicit_p4rt_node_component,json=explicitP4rtNodeComponent,proto3" json:"explicit_p4rt_node_component,omitempty"` - // Configure ACLs using vendor native model specifically for RT-1.4. - UseVendorNativeAclConfig bool `protobuf:"varint,36,opt,name=use_vendor_native_acl_config,json=useVendorNativeAclConfig,proto3" json:"use_vendor_native_acl_config,omitempty"` // Device does not support reporting software version according to the // requirements in gNMI-1.10. SwVersionUnsupported bool `protobuf:"varint,37,opt,name=sw_version_unsupported,json=swVersionUnsupported,proto3" json:"sw_version_unsupported,omitempty"` @@ -437,8 +434,6 @@ type Metadata_Deviations struct { // Device does not support telemetry path /components/component/storage. // Juniper: partnerissuetracker.corp.google.com/284239001 StorageComponentUnsupported bool `protobuf:"varint,39,opt,name=storage_component_unsupported,json=storageComponentUnsupported,proto3" json:"storage_component_unsupported,omitempty"` - // Device requires gribi-protocol to be enabled under network-instance. - ExplicitGribiUnderNetworkInstance bool `protobuf:"varint,40,opt,name=explicit_gribi_under_network_instance,json=explicitGribiUnderNetworkInstance,proto3" json:"explicit_gribi_under_network_instance,omitempty"` // Device requires port-speed to be set because its default value may not be // usable. ExplicitPortSpeed bool `protobuf:"varint,41,opt,name=explicit_port_speed,json=explicitPortSpeed,proto3" json:"explicit_port_speed,omitempty"` @@ -781,9 +776,6 @@ type Metadata_Deviations struct { 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"` // Ensure no configurations exist under BGP Peer Groups @@ -850,6 +842,48 @@ type Metadata_Deviations struct { // /components/component/state/model-name of the chassis component must be // equal to the canonical hardware model name of its device. ModelNameUnsupported bool `protobuf:"varint,194,opt,name=model_name_unsupported,json=modelNameUnsupported,proto3" json:"model_name_unsupported,omitempty"` + // community_match_with_redistribution_unsupported is set to true for devices that do not support matching community at the redistribution attach point. + CommunityMatchWithRedistributionUnsupported bool `protobuf:"varint,195,opt,name=community_match_with_redistribution_unsupported,json=communityMatchWithRedistributionUnsupported,proto3" json:"community_match_with_redistribution_unsupported,omitempty"` + // Devices that do not support components/component/state/install-component + // and components/component/state/install-position. + InstallPositionAndInstallComponentUnsupported bool `protobuf:"varint,196,opt,name=install_position_and_install_component_unsupported,json=installPositionAndInstallComponentUnsupported,proto3" json:"install_position_and_install_component_unsupported,omitempty"` + // Encap tunnel is shut then zero traffic will flow to backup NHG + EncapTunnelShutBackupNhgZeroTraffic bool `protobuf:"varint,197,opt,name=encap_tunnel_shut_backup_nhg_zero_traffic,json=encapTunnelShutBackupNhgZeroTraffic,proto3" json:"encap_tunnel_shut_backup_nhg_zero_traffic,omitempty"` + // Flag to indicate support for max ecmp paths for isis. + MaxEcmpPaths bool `protobuf:"varint,198,opt,name=max_ecmp_paths,json=maxEcmpPaths,proto3" json:"max_ecmp_paths,omitempty"` + // wecmp_auto_unsupported is set to true for devices that do not support auto wecmp + WecmpAutoUnsupported bool `protobuf:"varint,199,opt,name=wecmp_auto_unsupported,json=wecmpAutoUnsupported,proto3" json:"wecmp_auto_unsupported,omitempty"` + // policy chaining, ie. more than one policy at an attachement point is not supported + RoutingPolicyChainingUnsupported bool `protobuf:"varint,200,opt,name=routing_policy_chaining_unsupported,json=routingPolicyChainingUnsupported,proto3" json:"routing_policy_chaining_unsupported,omitempty"` + // isis loopback config required + IsisLoopbackRequired bool `protobuf:"varint,201,opt,name=isis_loopback_required,json=isisLoopbackRequired,proto3" json:"isis_loopback_required,omitempty"` + // weighted ecmp feature verification using fixed packet + WeightedEcmpFixedPacketVerification bool `protobuf:"varint,202,opt,name=weighted_ecmp_fixed_packet_verification,json=weightedEcmpFixedPacketVerification,proto3" json:"weighted_ecmp_fixed_packet_verification,omitempty"` + // Override default NextHop scale while enabling encap/decap scale + // CISCO: + OverrideDefaultNhScale bool `protobuf:"varint,203,opt,name=override_default_nh_scale,json=overrideDefaultNhScale,proto3" json:"override_default_nh_scale,omitempty"` + // Devices that donot support setting bgp extended community set + BgpExtendedCommunitySetUnsupported bool `protobuf:"varint,204,opt,name=bgp_extended_community_set_unsupported,json=bgpExtendedCommunitySetUnsupported,proto3" json:"bgp_extended_community_set_unsupported,omitempty"` + // Devices that do not support setting bgp extended community set refs + BgpSetExtCommunitySetRefsUnsupported bool `protobuf:"varint,205,opt,name=bgp_set_ext_community_set_refs_unsupported,json=bgpSetExtCommunitySetRefsUnsupported,proto3" json:"bgp_set_ext_community_set_refs_unsupported,omitempty"` + // Devices that do not support deleting link bandwidth + BgpDeleteLinkBandwidthUnsupported bool `protobuf:"varint,206,opt,name=bgp_delete_link_bandwidth_unsupported,json=bgpDeleteLinkBandwidthUnsupported,proto3" json:"bgp_delete_link_bandwidth_unsupported,omitempty"` + // qos_inqueue_drop_counter_Unsupported is set to true for devices that do not support qos ingress queue drop counters. + // Juniper: b/341130490 + QosInqueueDropCounterUnsupported bool `protobuf:"varint,207,opt,name=qos_inqueue_drop_counter_unsupported,json=qosInqueueDropCounterUnsupported,proto3" json:"qos_inqueue_drop_counter_unsupported,omitempty"` + // Devices that need bgp extended community enable explicitly + BgpExplicitExtendedCommunityEnable bool `protobuf:"varint,208,opt,name=bgp_explicit_extended_community_enable,json=bgpExplicitExtendedCommunityEnable,proto3" json:"bgp_explicit_extended_community_enable,omitempty"` + // devices that do not support match tag set condition + MatchTagSetConditionUnsupported bool `protobuf:"varint,209,opt,name=match_tag_set_condition_unsupported,json=matchTagSetConditionUnsupported,proto3" json:"match_tag_set_condition_unsupported,omitempty"` + // peer_group_def_bgp_vrf_unsupported is set to true for devices that do not support peer group definition under bgp vrf configuration. + PeerGroupDefEbgpVrfUnsupported bool `protobuf:"varint,210,opt,name=peer_group_def_ebgp_vrf_unsupported,json=peerGroupDefEbgpVrfUnsupported,proto3" json:"peer_group_def_ebgp_vrf_unsupported,omitempty"` + // redis_uconnected_under_ebgp_vrf_unsupported is set to true for devices that do not support redistribution of connected routes under ebgp vrf configuration. + RedisConnectedUnderEbgpVrfUnsupported bool `protobuf:"varint,211,opt,name=redis_connected_under_ebgp_vrf_unsupported,json=redisConnectedUnderEbgpVrfUnsupported,proto3" json:"redis_connected_under_ebgp_vrf_unsupported,omitempty"` + // bgp_afisafi_in_default_ni_before_other_ni is set to true for devices that require certain afi/safis to be enabled + // in default network instance (ni) before enabling afi/safis for neighbors in default or non-default ni. + BgpAfiSafiInDefaultNiBeforeOtherNi bool `protobuf:"varint,212,opt,name=bgp_afi_safi_in_default_ni_before_other_ni,json=bgpAfiSafiInDefaultNiBeforeOtherNi,proto3" json:"bgp_afi_safi_in_default_ni_before_other_ni,omitempty"` + // Devices which do not support default import export policy. + DefaultImportExportPolicyUnsupported bool `protobuf:"varint,213,opt,name=default_import_export_policy_unsupported,json=defaultImportExportPolicyUnsupported,proto3" json:"default_import_export_policy_unsupported,omitempty"` } func (x *Metadata_Deviations) Reset() { @@ -1094,20 +1128,6 @@ func (x *Metadata_Deviations) GetNoMixOfTaggedAndUntaggedSubinterfaces() bool { return false } -func (x *Metadata_Deviations) GetExplicitP4RtNodeComponent() bool { - if x != nil { - return x.ExplicitP4RtNodeComponent - } - return false -} - -func (x *Metadata_Deviations) GetUseVendorNativeAclConfig() bool { - if x != nil { - return x.UseVendorNativeAclConfig - } - return false -} - func (x *Metadata_Deviations) GetSwVersionUnsupported() bool { if x != nil { return x.SwVersionUnsupported @@ -1129,13 +1149,6 @@ func (x *Metadata_Deviations) GetStorageComponentUnsupported() bool { return false } -func (x *Metadata_Deviations) GetExplicitGribiUnderNetworkInstance() bool { - if x != nil { - return x.ExplicitGribiUnderNetworkInstance - } - return false -} - func (x *Metadata_Deviations) GetExplicitPortSpeed() bool { if x != nil { return x.ExplicitPortSpeed @@ -1976,13 +1989,6 @@ func (x *Metadata_Deviations) GetSkipBgpSendCommunityType() bool { 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 @@ -2130,6 +2136,139 @@ func (x *Metadata_Deviations) GetModelNameUnsupported() bool { return false } +func (x *Metadata_Deviations) GetCommunityMatchWithRedistributionUnsupported() bool { + if x != nil { + return x.CommunityMatchWithRedistributionUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetInstallPositionAndInstallComponentUnsupported() bool { + if x != nil { + return x.InstallPositionAndInstallComponentUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetEncapTunnelShutBackupNhgZeroTraffic() bool { + if x != nil { + return x.EncapTunnelShutBackupNhgZeroTraffic + } + return false +} + +func (x *Metadata_Deviations) GetMaxEcmpPaths() bool { + if x != nil { + return x.MaxEcmpPaths + } + return false +} + +func (x *Metadata_Deviations) GetWecmpAutoUnsupported() bool { + if x != nil { + return x.WecmpAutoUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetRoutingPolicyChainingUnsupported() bool { + if x != nil { + return x.RoutingPolicyChainingUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetIsisLoopbackRequired() bool { + if x != nil { + return x.IsisLoopbackRequired + } + return false +} + +func (x *Metadata_Deviations) GetWeightedEcmpFixedPacketVerification() bool { + if x != nil { + return x.WeightedEcmpFixedPacketVerification + } + return false +} + +func (x *Metadata_Deviations) GetOverrideDefaultNhScale() bool { + if x != nil { + return x.OverrideDefaultNhScale + } + return false +} + +func (x *Metadata_Deviations) GetBgpExtendedCommunitySetUnsupported() bool { + if x != nil { + return x.BgpExtendedCommunitySetUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetBgpSetExtCommunitySetRefsUnsupported() bool { + if x != nil { + return x.BgpSetExtCommunitySetRefsUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetBgpDeleteLinkBandwidthUnsupported() bool { + if x != nil { + return x.BgpDeleteLinkBandwidthUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetQosInqueueDropCounterUnsupported() bool { + if x != nil { + return x.QosInqueueDropCounterUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetBgpExplicitExtendedCommunityEnable() bool { + if x != nil { + return x.BgpExplicitExtendedCommunityEnable + } + return false +} + +func (x *Metadata_Deviations) GetMatchTagSetConditionUnsupported() bool { + if x != nil { + return x.MatchTagSetConditionUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetPeerGroupDefEbgpVrfUnsupported() bool { + if x != nil { + return x.PeerGroupDefEbgpVrfUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetRedisConnectedUnderEbgpVrfUnsupported() bool { + if x != nil { + return x.RedisConnectedUnderEbgpVrfUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetBgpAfiSafiInDefaultNiBeforeOtherNi() bool { + if x != nil { + return x.BgpAfiSafiInDefaultNiBeforeOtherNi + } + return false +} + +func (x *Metadata_Deviations) GetDefaultImportExportPolicyUnsupported() bool { + if x != nil { + return x.DefaultImportExportPolicyUnsupported + } + return false +} + type Metadata_PlatformExceptions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2193,7 +2332,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, 0xf8, 0x6d, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf5, 0x77, 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, @@ -2227,7 +2366,7 @@ var file_metadata_proto_rawDesc = []byte{ 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, 0xcb, 0x65, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x61, 0x74, + 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x1a, 0xc8, 0x6f, 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, @@ -2360,720 +2499,800 @@ var file_metadata_proto_rawDesc = []byte{ 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, + 0x65, 0x73, 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, 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, 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, + 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, 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, + 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, 0x70, 0x70, 0x73, 0x18, 0xa6, 0x01, 0x20, 0x01, 0x28, 0x04, + 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, 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, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x65, 0x74, 0x5f, 0x6e, 0x6f, 0x5f, 0x70, - 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0xaf, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0e, 0x73, 0x65, 0x74, 0x4e, 0x6f, 0x50, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x12, 0x46, 0x0a, 0x20, 0x62, 0x67, 0x70, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, - 0x79, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x69, 0x73, 0x5f, 0x61, 0x5f, 0x73, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x18, 0xb0, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x62, 0x67, 0x70, - 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, - 0x73, 0x41, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x59, 0x0a, 0x2a, 0x69, 0x70, 0x76, 0x34, - 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x77, 0x69, - 0x74, 0x68, 0x5f, 0x69, 0x70, 0x76, 0x36, 0x5f, 0x6e, 0x68, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xb1, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x24, 0x69, - 0x70, 0x76, 0x34, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x57, 0x69, - 0x74, 0x68, 0x49, 0x70, 0x76, 0x36, 0x4e, 0x68, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, - 0x74, 0x65, 0x64, 0x12, 0x59, 0x0a, 0x2a, 0x69, 0x70, 0x76, 0x36, 0x5f, 0x73, 0x74, 0x61, 0x74, + 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, + 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, 0x12, + 0x2a, 0x0a, 0x11, 0x73, 0x65, 0x74, 0x5f, 0x6e, 0x6f, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x18, 0xaf, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x65, 0x74, + 0x4e, 0x6f, 0x50, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x46, 0x0a, 0x20, 0x62, + 0x67, 0x70, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x5f, 0x69, 0x73, 0x5f, 0x61, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, + 0xb0, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x62, 0x67, 0x70, 0x43, 0x6f, 0x6d, 0x6d, 0x75, + 0x6e, 0x69, 0x74, 0x79, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x73, 0x41, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x12, 0x59, 0x0a, 0x2a, 0x69, 0x70, 0x76, 0x34, 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, 0x68, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, - 0x64, 0x18, 0xb2, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x24, 0x69, 0x70, 0x76, 0x36, 0x53, 0x74, + 0x76, 0x36, 0x5f, 0x6e, 0x68, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x18, 0xb1, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x24, 0x69, 0x70, 0x76, 0x34, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x49, 0x70, 0x76, - 0x34, 0x4e, 0x68, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x39, - 0x0a, 0x19, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x77, - 0x69, 0x74, 0x68, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x6e, 0x68, 0x18, 0xb3, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x15, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x57, - 0x69, 0x74, 0x68, 0x44, 0x72, 0x6f, 0x70, 0x4e, 0x68, 0x12, 0x49, 0x0a, 0x21, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x65, - 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0xb4, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x12, 0x44, 0x0a, 0x1e, 0x62, 0x67, 0x70, 0x5f, 0x64, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xb5, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x62, - 0x67, 0x70, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, - 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4a, 0x0a, 0x22, 0x65, 0x78, - 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x67, - 0x70, 0x5f, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x72, 0x66, - 0x18, 0xb6, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, - 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x67, 0x70, 0x4f, 0x6e, 0x44, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x56, 0x72, 0x66, 0x12, 0x45, 0x0a, 0x1f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, - 0x67, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x74, 0x61, 0x67, 0x5f, 0x73, 0x65, 0x74, - 0x5f, 0x65, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x18, 0xb7, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x1b, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, - 0x61, 0x67, 0x53, 0x65, 0x74, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x12, 0x50, 0x0a, - 0x26, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x61, 0x66, 0x69, 0x5f, 0x73, 0x61, 0x66, 0x69, 0x5f, 0x70, - 0x61, 0x74, 0x68, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x62, 0x67, 0x70, 0x5f, 0x6d, 0x75, 0x6c, 0x74, - 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x61, 0x73, 0x18, 0xb8, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, - 0x73, 0x6b, 0x69, 0x70, 0x41, 0x66, 0x69, 0x53, 0x61, 0x66, 0x69, 0x50, 0x61, 0x74, 0x68, 0x46, - 0x6f, 0x72, 0x42, 0x67, 0x70, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x41, 0x73, 0x12, - 0x4c, 0x0a, 0x22, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, - 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xb9, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x63, 0x6f, - 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x67, - 0x65, 0x78, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x46, 0x0a, - 0x20, 0x73, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x61, 0x74, 0x74, - 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x61, 0x66, 0x69, - 0x73, 0x18, 0xba, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x73, 0x61, 0x6d, 0x65, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x54, 0x6f, 0x41, 0x6c, - 0x6c, 0x41, 0x66, 0x69, 0x73, 0x12, 0x49, 0x0a, 0x21, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x73, 0x65, - 0x74, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, - 0x66, 0x6f, 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0xbb, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x1d, 0x73, 0x6b, 0x69, 0x70, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x6f, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x12, 0x42, 0x0a, 0x1d, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, - 0x67, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x18, 0xbc, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x73, 0x6b, 0x69, 0x70, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x12, 0x55, 0x0a, 0x27, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x5f, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6d, 0x75, 0x6c, 0x74, - 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, - 0xbd, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, - 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x48, 0x0a, 0x20, 0x64, - 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, - 0xbe, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, - 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x35, 0x0a, 0x16, 0x73, 0x6c, 0x61, 0x61, 0x63, 0x5f, 0x70, - 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x31, 0x32, 0x38, 0x18, - 0xbf, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x73, 0x6c, 0x61, 0x61, 0x63, 0x50, 0x72, 0x65, - 0x66, 0x69, 0x78, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x31, 0x32, 0x38, 0x12, 0x4d, 0x0a, 0x23, - 0x62, 0x67, 0x70, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x74, - 0x68, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, - 0x74, 0x65, 0x64, 0x18, 0xc0, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x62, 0x67, 0x70, 0x4d, - 0x61, 0x78, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x74, 0x68, 0x50, 0x61, 0x74, 0x68, 0x73, - 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x59, 0x0a, 0x29, 0x6d, - 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, - 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x65, 0x69, 0x67, 0x68, 0x62, 0x6f, 0x72, 0x5f, 0x6f, 0x72, - 0x5f, 0x61, 0x66, 0x69, 0x73, 0x61, 0x66, 0x69, 0x18, 0xc1, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x25, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x74, 0x68, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, - 0x6f, 0x72, 0x74, 0x65, 0x64, 0x4e, 0x65, 0x69, 0x67, 0x68, 0x62, 0x6f, 0x72, 0x4f, 0x72, 0x41, - 0x66, 0x69, 0x73, 0x61, 0x66, 0x69, 0x12, 0x35, 0x0a, 0x16, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x18, 0xc2, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x4e, 0x61, - 0x6d, 0x65, 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, + 0x36, 0x4e, 0x68, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x59, + 0x0a, 0x2a, 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, 0x68, + 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xb2, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x24, 0x69, 0x70, 0x76, 0x36, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x49, 0x70, 0x76, 0x34, 0x4e, 0x68, 0x55, 0x6e, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x39, 0x0a, 0x19, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x64, + 0x72, 0x6f, 0x70, 0x5f, 0x6e, 0x68, 0x18, 0xb3, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x44, 0x72, + 0x6f, 0x70, 0x4e, 0x68, 0x12, 0x49, 0x0a, 0x21, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, + 0x69, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0xb4, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x1d, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x57, 0x69, 0x74, + 0x68, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, + 0x44, 0x0a, 0x1e, 0x62, 0x67, 0x70, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x18, 0xb5, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x62, 0x67, 0x70, 0x44, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4a, 0x0a, 0x22, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, + 0x74, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x67, 0x70, 0x5f, 0x6f, 0x6e, 0x5f, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x72, 0x66, 0x18, 0xb6, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x1d, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x45, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x42, 0x67, 0x70, 0x4f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x72, + 0x66, 0x12, 0x45, 0x0a, 0x1f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x5f, 0x74, 0x61, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x65, 0x6d, 0x62, 0x65, + 0x64, 0x64, 0x65, 0x64, 0x18, 0xb7, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x72, 0x6f, 0x75, + 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x61, 0x67, 0x53, 0x65, 0x74, + 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x12, 0x50, 0x0a, 0x26, 0x73, 0x6b, 0x69, 0x70, + 0x5f, 0x61, 0x66, 0x69, 0x5f, 0x73, 0x61, 0x66, 0x69, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x66, + 0x6f, 0x72, 0x5f, 0x62, 0x67, 0x70, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, + 0x61, 0x73, 0x18, 0xb8, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x73, 0x6b, 0x69, 0x70, 0x41, + 0x66, 0x69, 0x53, 0x61, 0x66, 0x69, 0x50, 0x61, 0x74, 0x68, 0x46, 0x6f, 0x72, 0x42, 0x67, 0x70, + 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x41, 0x73, 0x12, 0x4c, 0x0a, 0x22, 0x63, 0x6f, + 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x72, + 0x65, 0x67, 0x65, 0x78, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x18, 0xb9, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, + 0x74, 0x79, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x67, 0x65, 0x78, 0x55, 0x6e, 0x73, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x20, 0x73, 0x61, 0x6d, 0x65, + 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, + 0x5f, 0x74, 0x6f, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x61, 0x66, 0x69, 0x73, 0x18, 0xba, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x1b, 0x73, 0x61, 0x6d, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x41, + 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x54, 0x6f, 0x41, 0x6c, 0x6c, 0x41, 0x66, 0x69, 0x73, + 0x12, 0x49, 0x0a, 0x21, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0xbb, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x73, 0x6b, + 0x69, 0x70, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x46, 0x6f, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x42, 0x0a, 0x1d, 0x73, + 0x6b, 0x69, 0x70, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0xbc, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x1a, 0x73, 0x6b, 0x69, 0x70, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, + 0x67, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, + 0x55, 0x0a, 0x27, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0xbd, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x23, 0x66, 0x6c, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x57, 0x69, 0x74, 0x68, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x48, 0x0a, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x75, + 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xbe, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x1d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x12, 0x35, 0x0a, 0x16, 0x73, 0x6c, 0x61, 0x61, 0x63, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, + 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x31, 0x32, 0x38, 0x18, 0xbf, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x14, 0x73, 0x6c, 0x61, 0x61, 0x63, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x31, 0x32, 0x38, 0x12, 0x4d, 0x0a, 0x23, 0x62, 0x67, 0x70, 0x5f, 0x6d, + 0x61, 0x78, 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x74, + 0x68, 0x73, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xc0, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x62, 0x67, 0x70, 0x4d, 0x61, 0x78, 0x4d, 0x75, 0x6c, + 0x74, 0x69, 0x70, 0x61, 0x74, 0x68, 0x50, 0x61, 0x74, 0x68, 0x73, 0x55, 0x6e, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x59, 0x0a, 0x29, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, + 0x61, 0x74, 0x68, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, + 0x6e, 0x65, 0x69, 0x67, 0x68, 0x62, 0x6f, 0x72, 0x5f, 0x6f, 0x72, 0x5f, 0x61, 0x66, 0x69, 0x73, + 0x61, 0x66, 0x69, 0x18, 0xc1, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x25, 0x6d, 0x75, 0x6c, 0x74, + 0x69, 0x70, 0x61, 0x74, 0x68, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x4e, 0x65, 0x69, 0x67, 0x68, 0x62, 0x6f, 0x72, 0x4f, 0x72, 0x41, 0x66, 0x69, 0x73, 0x61, 0x66, + 0x69, 0x12, 0x35, 0x0a, 0x16, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, + 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xc2, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x14, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x73, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x65, 0x0a, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, + 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x77, 0x69, 0x74, 0x68, + 0x5f, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xc3, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x2b, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x4d, 0x61, 0x74, + 0x63, 0x68, 0x57, 0x69, 0x74, 0x68, 0x52, 0x65, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, + 0x6a, 0x0a, 0x32, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, + 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xc4, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x2d, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6e, 0x64, + 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, + 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x57, 0x0a, 0x29, 0x65, + 0x6e, 0x63, 0x61, 0x70, 0x5f, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x68, 0x75, 0x74, + 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x6e, 0x68, 0x67, 0x5f, 0x7a, 0x65, 0x72, 0x6f, + 0x5f, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x18, 0xc5, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x23, 0x65, 0x6e, 0x63, 0x61, 0x70, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x68, 0x75, 0x74, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4e, 0x68, 0x67, 0x5a, 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, + 0x66, 0x66, 0x69, 0x63, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x63, 0x6d, 0x70, + 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0xc6, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6d, + 0x61, 0x78, 0x45, 0x63, 0x6d, 0x70, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x35, 0x0a, 0x16, 0x77, + 0x65, 0x63, 0x6d, 0x70, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xc7, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x77, 0x65, + 0x63, 0x6d, 0x70, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x12, 0x4e, 0x0a, 0x23, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x6e, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x20, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x12, 0x35, 0x0a, 0x16, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x6c, 0x6f, 0x6f, 0x70, 0x62, + 0x61, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0xc9, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x14, 0x69, 0x73, 0x69, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x62, 0x61, 0x63, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x55, 0x0a, 0x27, 0x77, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x63, 0x6d, 0x70, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, + 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xca, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x77, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x65, 0x64, 0x45, 0x63, 0x6d, 0x70, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x61, + 0x63, 0x6b, 0x65, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x3a, 0x0a, 0x19, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6e, 0x68, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x18, 0xcb, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x44, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4e, 0x68, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x53, 0x0a, 0x26, + 0x62, 0x67, 0x70, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 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, 0xcc, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x22, 0x62, + 0x67, 0x70, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, + 0x69, 0x74, 0x79, 0x53, 0x65, 0x74, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x12, 0x59, 0x0a, 0x2a, 0x62, 0x67, 0x70, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x65, 0x78, 0x74, + 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, + 0xcd, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x24, 0x62, 0x67, 0x70, 0x53, 0x65, 0x74, 0x45, 0x78, + 0x74, 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, 0x51, 0x0a, 0x25, + 0x62, 0x67, 0x70, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, + 0x62, 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xce, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x21, 0x62, 0x67, + 0x70, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x69, 0x6e, 0x6b, 0x42, 0x61, 0x6e, 0x64, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, + 0x4f, 0x0a, 0x24, 0x71, 0x6f, 0x73, 0x5f, 0x69, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 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, 0xcf, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x20, + 0x71, 0x6f, 0x73, 0x49, 0x6e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x44, 0x72, 0x6f, 0x70, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x65, 0x72, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x12, 0x53, 0x0a, 0x26, 0x62, 0x67, 0x70, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, + 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, + 0x69, 0x74, 0x79, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0xd0, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x22, 0x62, 0x67, 0x70, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x45, 0x78, + 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x45, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x4d, 0x0a, 0x23, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x74, + 0x61, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xd1, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x1f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x54, 0x61, 0x67, 0x53, 0x65, 0x74, + 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x12, 0x4c, 0x0a, 0x23, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x5f, 0x64, 0x65, 0x66, 0x5f, 0x65, 0x62, 0x67, 0x70, 0x5f, 0x76, 0x72, 0x66, 0x5f, + 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xd2, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x1e, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x66, + 0x45, 0x62, 0x67, 0x70, 0x56, 0x72, 0x66, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x12, 0x5a, 0x0a, 0x2a, 0x72, 0x65, 0x64, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x65, 0x62, 0x67, 0x70, + 0x5f, 0x76, 0x72, 0x66, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x18, 0xd3, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x25, 0x72, 0x65, 0x64, 0x69, 0x73, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x45, 0x62, 0x67, 0x70, + 0x56, 0x72, 0x66, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x57, + 0x0a, 0x2a, 0x62, 0x67, 0x70, 0x5f, 0x61, 0x66, 0x69, 0x5f, 0x73, 0x61, 0x66, 0x69, 0x5f, 0x69, + 0x6e, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6e, 0x69, 0x5f, 0x62, 0x65, 0x66, + 0x6f, 0x72, 0x65, 0x5f, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x6e, 0x69, 0x18, 0xd4, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x22, 0x62, 0x67, 0x70, 0x41, 0x66, 0x69, 0x53, 0x61, 0x66, 0x69, 0x49, + 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4e, 0x69, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, + 0x4f, 0x74, 0x68, 0x65, 0x72, 0x4e, 0x69, 0x12, 0x57, 0x0a, 0x28, 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, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, + 0x74, 0x65, 0x64, 0x18, 0xd5, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x24, 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, 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, 0x4a, 0x04, 0x08, 0x24, 0x10, 0x25, 0x4a, 0x04, 0x08, 0x23, + 0x10, 0x24, 0x4a, 0x04, 0x08, 0x28, 0x10, 0x29, 0x4a, 0x06, 0x08, 0xad, 0x01, 0x10, 0xae, 0x01, + 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/proto/nosimage.proto b/proto/nosimage.proto index 8410d0c06e0..c55e49d7272 100644 --- a/proto/nosimage.proto +++ b/proto/nosimage.proto @@ -41,6 +41,10 @@ message NOSImageProfile { // /system/state/software-version. string software_version = 3; + // The name of the vendor's networking hardware device that is compatible with + // the NOS software version. + string hardware_name = 7; + // The date the network operating system is released. // The date could be a value in the future indicating a future release. google.protobuf.Timestamp release_date = 4; diff --git a/proto/nosimage_go_proto/nosimage.pb.go b/proto/nosimage_go_proto/nosimage.pb.go index 543cdc8cf92..3eed2fb8051 100644 --- a/proto/nosimage_go_proto/nosimage.pb.go +++ b/proto/nosimage_go_proto/nosimage.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.28.1 // protoc v3.21.12 // source: nosimage.proto @@ -60,6 +60,9 @@ type NOSImageProfile struct { // This should match the output of the OpenConfig Path // /system/state/software-version. SoftwareVersion string `protobuf:"bytes,3,opt,name=software_version,json=softwareVersion,proto3" json:"software_version,omitempty"` + // The name of the vendor's networking hardware device that is compatible with + // the NOS software version. + HardwareName string `protobuf:"bytes,7,opt,name=hardware_name,json=hardwareName,proto3" json:"hardware_name,omitempty"` // The date the network operating system is released. // The date could be a value in the future indicating a future release. ReleaseDate *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=release_date,json=releaseDate,proto3" json:"release_date,omitempty"` @@ -122,6 +125,13 @@ func (x *NOSImageProfile) GetSoftwareVersion() string { return "" } +func (x *NOSImageProfile) GetHardwareName() string { + if x != nil { + return x.HardwareName + } + return "" +} + func (x *NOSImageProfile) GetReleaseDate() *timestamppb.Timestamp { if x != nil { return x.ReleaseDate @@ -161,7 +171,7 @@ var file_nosimage_proto_rawDesc = []byte{ 0x72, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x62, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xac, 0x02, 0x0a, 0x0f, 0x4e, 0x4f, 0x53, 0x49, + 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd1, 0x02, 0x0a, 0x0f, 0x4e, 0x4f, 0x53, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x33, 0x0a, 0x09, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x72, 0x61, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, @@ -169,18 +179,21 @@ var file_nosimage_proto_rawDesc = []byte{ 0x12, 0x10, 0x0a, 0x03, 0x6e, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6e, 0x6f, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x6f, - 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, - 0x0c, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0b, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x44, 0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, 0x07, - 0x6f, 0x63, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, - 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x6f, 0x63, 0x70, 0x61, 0x74, - 0x68, 0x73, 0x2e, 0x4f, 0x43, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x07, 0x6f, 0x63, 0x70, 0x61, - 0x74, 0x68, 0x73, 0x12, 0x31, 0x0a, 0x06, 0x6f, 0x63, 0x72, 0x70, 0x63, 0x73, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x2e, 0x6f, 0x63, 0x72, 0x70, 0x63, 0x73, 0x2e, 0x4f, 0x43, 0x52, 0x50, 0x43, 0x73, 0x52, 0x06, - 0x6f, 0x63, 0x72, 0x70, 0x63, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, + 0x0d, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x64, 0x61, + 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x44, 0x61, 0x74, + 0x65, 0x12, 0x35, 0x0a, 0x07, 0x6f, 0x63, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, + 0x6f, 0x63, 0x70, 0x61, 0x74, 0x68, 0x73, 0x2e, 0x4f, 0x43, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, + 0x07, 0x6f, 0x63, 0x70, 0x61, 0x74, 0x68, 0x73, 0x12, 0x31, 0x0a, 0x06, 0x6f, 0x63, 0x72, 0x70, + 0x63, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x6f, 0x63, 0x72, 0x70, 0x63, 0x73, 0x2e, 0x4f, 0x43, 0x52, + 0x50, 0x43, 0x73, 0x52, 0x06, 0x6f, 0x63, 0x72, 0x70, 0x63, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/proto/testregistry_go_proto/testregistry.pb.go b/proto/testregistry_go_proto/testregistry.pb.go new file mode 100644 index 00000000000..98feb5421cd --- /dev/null +++ b/proto/testregistry_go_proto/testregistry.pb.go @@ -0,0 +1,293 @@ +// 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 +// +// 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. +// +// +// testregistry.proto -- specifying structure of a list of tests +// in featureprofiles + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.12 +// source: testregistry.proto + +package testregistry_go_proto + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type TestRegistry struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // name -- the human readable name of this TestSuite + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Test []*Test `protobuf:"bytes,2,rep,name=test,proto3" json:"test,omitempty"` +} + +func (x *TestRegistry) Reset() { + *x = TestRegistry{} + if protoimpl.UnsafeEnabled { + mi := &file_testregistry_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TestRegistry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TestRegistry) ProtoMessage() {} + +func (x *TestRegistry) ProtoReflect() protoreflect.Message { + mi := &file_testregistry_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TestRegistry.ProtoReflect.Descriptor instead. +func (*TestRegistry) Descriptor() ([]byte, []int) { + return file_testregistry_proto_rawDescGZIP(), []int{0} +} + +func (x *TestRegistry) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *TestRegistry) GetTest() []*Test { + if x != nil { + return x.Test + } + return nil +} + +// Test specifies resources for a single functional test that applies to a +// Feature. It +type Test struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // id -- Test ID, required, must be unique, must match the regex: + // + // [A-Z][A-Z]+\-[0-9]+(\.[0-9]+)? + // Test ID should match the rundata.TestPlanID of the linked exec. + // For example: AA-1.1 + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // version -- should be incremented each time any changes are made to the + // + // Test message instance for a given Test ID. + Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` + // description -- should be a human readable common name for the Test + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + // readme -- must be a URL, should be a link to the human readable + // + // readme.md or other documentation describing the test + Readme []string `protobuf:"bytes,4,rep,name=readme,proto3" json:"readme,omitempty"` + // exec -- must be a URL, may be a link to google3 code, should be a + // + // link to an ondatra test in the public repo location + Exec string `protobuf:"bytes,5,opt,name=exec,proto3" json:"exec,omitempty"` +} + +func (x *Test) Reset() { + *x = Test{} + if protoimpl.UnsafeEnabled { + mi := &file_testregistry_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Test) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Test) ProtoMessage() {} + +func (x *Test) ProtoReflect() protoreflect.Message { + mi := &file_testregistry_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Test.ProtoReflect.Descriptor instead. +func (*Test) Descriptor() ([]byte, []int) { + return file_testregistry_proto_rawDescGZIP(), []int{1} +} + +func (x *Test) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Test) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *Test) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Test) GetReadme() []string { + if x != nil { + return x.Readme + } + return nil +} + +func (x *Test) GetExec() string { + if x != nil { + return x.Exec + } + return "" +} + +var File_testregistry_proto protoreflect.FileDescriptor + +var file_testregistry_proto_rawDesc = []byte{ + 0x0a, 0x12, 0x74, 0x65, 0x73, 0x74, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x27, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x22, 0x65, 0x0a, + 0x0c, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x41, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x2d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x66, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x74, 0x65, 0x73, + 0x74, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52, 0x04, + 0x74, 0x65, 0x73, 0x74, 0x22, 0x7e, 0x0a, 0x04, 0x54, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, + 0x6d, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x65, 0x78, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x65, 0x78, 0x65, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_testregistry_proto_rawDescOnce sync.Once + file_testregistry_proto_rawDescData = file_testregistry_proto_rawDesc +) + +func file_testregistry_proto_rawDescGZIP() []byte { + file_testregistry_proto_rawDescOnce.Do(func() { + file_testregistry_proto_rawDescData = protoimpl.X.CompressGZIP(file_testregistry_proto_rawDescData) + }) + return file_testregistry_proto_rawDescData +} + +var file_testregistry_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_testregistry_proto_goTypes = []interface{}{ + (*TestRegistry)(nil), // 0: openconfig.featureprofiles.testregistry.TestRegistry + (*Test)(nil), // 1: openconfig.featureprofiles.testregistry.Test +} +var file_testregistry_proto_depIdxs = []int32{ + 1, // 0: openconfig.featureprofiles.testregistry.TestRegistry.test:type_name -> openconfig.featureprofiles.testregistry.Test + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_testregistry_proto_init() } +func file_testregistry_proto_init() { + if File_testregistry_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_testregistry_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TestRegistry); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_testregistry_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Test); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_testregistry_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_testregistry_proto_goTypes, + DependencyIndexes: file_testregistry_proto_depIdxs, + MessageInfos: file_testregistry_proto_msgTypes, + }.Build() + File_testregistry_proto = out.File + file_testregistry_proto_rawDesc = nil + file_testregistry_proto_goTypes = nil + file_testregistry_proto_depIdxs = nil +} diff --git a/testregistry.textproto b/testregistry.textproto index 92451754997..f6201525b5b 100644 --- a/testregistry.textproto +++ b/testregistry.textproto @@ -2,17 +2,6 @@ # proto-message: TestRegistry name: "WBB Test Registry" -test: { - id: "ACL-1.1" - description: "Layer 3 filtering" - exec: " " -} -test: { - id: "ACL-1.2" - description: "ACL Update (Make-before-break)" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/acl/otg_tests/acl_update_test/README.md" - exec: " " -} test: { id: "ACCTZ-1.1" description: "gNSI.acctz.v1 (Accounting) Test Record Subscribe Full" @@ -22,6 +11,11 @@ test: { id: "ACCTZ-1.1" readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/security/gnsi/acctz/RecordSubscribeFull/README.md" } +test: { + id: "ACCTZ-10.1" + description: "gNSI.acctz.v1 (Accounting) Test Accounting Authentication Error - Multi-transaction" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/security/gnsi/acctz/AccountingAuthenErrorMulti/README.md" +} test: { id: "ACCTZ-2.1" description: "gNSI.acctz.v1 (Accounting) Test Record Subscribe Partial" @@ -68,9 +62,15 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/security/gnsi/acctz/AccountingPrivEscalation/README.md" } test: { - id: "ACCTZ-10.1" - description: "gNSI.acctz.v1 (Accounting) Test Accounting Authentication Error - Multi-transaction" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/security/gnsi/acctz/AccountingAuthenErrorMulti/README.md" + id: "ACL-1.1" + description: "Layer 3 filtering" + exec: " " +} +test: { + id: "ACL-1.2" + description: "ACL Update (Make-before-break)" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/acl/otg_tests/acl_update_test/README.md" + exec: " " } test: { id: "Authz-1" @@ -132,86 +132,6 @@ test: { readme: "" exec: " " } -test: { - id: "bootz-1.1" - description: "Missing configuration - Device fails with status invalid parameter" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-1.2" - description: "Invalid configuration - Device fails with status invalid parameter" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-1.3" - description: "Valid configuration - Device succeded with status ok" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-2.1" - description: "Software version is different - Device is upgraded to the new version" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-2.2" - description: "Invalid software image - Device fails with status invalid parameter" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-3" - description: "bootz-3: Validate Ownership Voucher in bootz configuration" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-3.1" - description: "No ownership voucher - Device boots without OV present" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-3.2" - description: "Invalid OV - Device fails with status invalid parameter" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-3.3" - description: "OV fails - Device fails with status invalid parameter" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-3.4" - description: "OV valid - Device boots with OV installed" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-4" - description: "bootz-4: Validate device properly resets if provided invalid image" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-4.1" - description: "no OS provided - Device boots with existing image" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-4.2" - description: "Invalid OS image provided - Device fails with status invalid parameter" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-4.3" - description: "failed to fetch image from remote URL - Device fails with status invalid parameter" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-4.4" - description: "OS checksum doesn't match - Device fails with invalid parameter" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} -test: { - id: "bootz-5" - description: "Validate gNSI components in bootz configuration" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" -} test: { id: "Certz-1" readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/security/gnsi/certz/client_certificates/README.md" @@ -282,6 +202,18 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/qos/ecn/otg_tests/DSCP-transparency/README.md" exec: " " } +test: { + id: "DP-1.15" + description: "Egress Strict Priority scheduler" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/qos/otg_tests/egress_strict_priority_scheduler_test/README.md" + exec: " " +} +test: { + id: "DP-1.16" + description: "Ingress traffic classification and rewrite" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/qos/otg_tests/ingress_traffic_classification_and_rewrite_test/README.md" + exec: " " +} test: { id: "DP-1.2" description: "QoS policy feature config" @@ -299,6 +231,11 @@ test: { description: "QoS Interface Output Queue Counters" readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/qos/otg_tests/qos_output_queue_counters_test/README.md" } +test: { + id: "DP-1.5" + description: "Egress Strict Priority scheduler with bursty traffic" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/qos/otg_tests/egress_strict_priority_scheduler_with_bursty_traffic_test/README.md" +} test: { id: "DP-1.7" description: "One strict priority queue traffic test" @@ -311,6 +248,17 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/qos/ate_tests/two_sp_queue_traffic_test/README.md" exec: " " } +test: { + id: "DP-1.9" + description: "WRR traffic test" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/qos/ate_tests/wrr_traffic_test/README.md" + exec: " " +} +test: { + id: "FP-1.1" + description: "Power admin DOWN/UP Test" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/tests/power_admin_down_up_test/README.md" +} test: { id: "Health-1.1" description: "Generic Health Check" @@ -330,15 +278,16 @@ test: { exec: " " } test: { - id: "DP-1.9" - description: "WRR traffic test" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/qos/ate_tests/wrr_traffic_test/README.md" + id: "MGT-1" + description: "Management HA test" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/management/README.md" exec: " " } test: { - id: "FP-1.1" - description: "Power admin DOWN/UP Test" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/tests/power_admin_down_up_test/README.md" + id: "MPLS-1.1" + description: "MPLS label blocks: Static and MPLS-SR" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/mpls/otg_tests/label_block/README.md" + exec: " " } test: { id: "OC-1.1" @@ -403,8 +352,65 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/experimental/p4rt/otg_tests/lldp_packetout_test/README.md" } test: { - id: "Replay-1.2" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/experimental/replay/tests/p4rt_replay/README.md" + id: "PF-1.1" + description: "IPv4/IPv6 policy-forwarding to indirect NH matching DSCP/TC" + readme: "https://github.com/openconfig/featureprofiles/feature/policy_forwarding/otg_tests/match_dscp_indirect_next_hop/README.md" + exec: " " +} +test: { + id: "PF-1.2" + description: "Policy-based traffic GRE Encapsulation to IPv4 GRE tunnel" + readme: "https://github.com/openconfig/featureprofiles/feature/policy_forwarding/encapsulation/otg_tests/encap_gre_ipv4/README.md" + exec: " " +} +test: { + id: "PLT-1.1" + description: "Interface breakout Test" + readme: "https://github.com/openconfig/featureprofiles/feature/experimental/platform/tests/breakout_configuration/README.md" + exec: " " +} +test: { + id: "RT-1.2" + description: "BGP Policy & Route Installation" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/policybase/otg_tests/route_installation_test/README.md" + exec: " " +} +test: { + id: "RT-1.3" + description: "BGP Route Propagation" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/addpath/ate_tests/route_propagation_test/README.md" + exec: " " +} +test: { + id: "RT-1.4" + description: "BGP Graceful Restart" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/gracefulrestart/ate_tests/bgp_graceful_restart_test/README.md" + exec: " " +} +test: { + id: "RT-1.5" + description: "BGP Prefix Limit" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/prefixlimit/otg_tests/bgp_prefix_limit_test/README.md" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/prefixlimit/ate_tests/bgp_prefix_limit_test/README.md" + exec: " " +} +test: { + id: "RT-1.7" + description: "Local BGP Test" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/tests/local_bgp_test/README.md" + exec: " " +} +test: { + id: "RT-1.8" + description: "BGP Route Reflector test at scale" + readme: "" + exec: " " +} +test: { + id: "RT-1.9" + description: "BGP Transport Parameters test" + readme: "" + exec: " " } test: { id: "RT-1.10" @@ -449,9 +455,21 @@ test: { exec: " " } test: { - id: "RT-1.2" - description: "BGP Policy & Route Installation" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/policybase/otg_tests/route_installation_test/README.md" + id: "RT-1.17" + description: "RT-1.17: BGP route-refresh capability" + readme: "" + exec: " " +} +test: { + id: "RT-1.18" + description: "RT-1.18: Support for MP-BGP w/ Multiple AFI-SAFI Tuples on a single neighborship" + readme: "" + exec: " " +} +test: { + id: "RT-1.19" + description: "BGP 2-Byte and 4-Byte ASN support" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn/README.md" exec: " " } test: { @@ -475,7 +493,7 @@ test: { test: { id: "RT-1.24" description: "BGP 2-byte and 4-byte ASN support with policy" - readme: "" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/experimental/bgp/otg_tests/bgp_2byte_4byte_asn_policy_test/README.md" exec: " " } test: { @@ -531,22 +549,9 @@ test: { readme: "https://github.com/openconfig/featureprofiles/feature/bgp/policybase/otg_tests/prefix_set_test/README.md" } test: { - id: "RT-1.3" - description: "BGP Route Propagation" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/addpath/ate_tests/route_propagation_test/README.md" - exec: " " -} -test: { - id: "RT-1.4" - description: "BGP Graceful Restart" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/gracefulrestart/ate_tests/bgp_graceful_restart_test/README.md" - exec: " " -} -test: { - id: "RT-1.5" - description: "BGP Prefix Limit" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/prefixlimit/otg_tests/bgp_prefix_limit_test/README.md" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/prefixlimit/ate_tests/bgp_prefix_limit_test/README.md" + id: "RT-1.34" + description: "Administrative Distance test for iBGP and eBGP" + readme: "https://github.com/openconfig/featureprofiles/feature/bgp/admin_distance/README.md" exec: " " } test: { @@ -561,6 +566,12 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/multipath/otg_tests/bgp_multipath_wecmp_lbw_community_test/README.md" exec: " " } +test: { + id: "RT-1.53" + description: "Prefix-set test" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/experimental/policy/prefix_set/README.md" + exec: " " +} test: { id: "RT-1.55" description: "BGP session mode (active/passive)" @@ -568,50 +579,45 @@ test: { exec: " " } test: { - id: "RT-1.7" - description: "Local BGP Test" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/tests/local_bgp_test/README.md" + id: "RT-1.56" + description: "BGP extended Next-hop support (RFC 5549)" + readme: " " exec: " " } test: { - id: "RT-1.8" - description: "BGP Route Reflector test at scale" - readme: "" + id: "RT-1.57" + description: "Support [E|I]BGP updates for IPv6 NLRI (including ULA/64 address space) with IPv4 Next-hop." + readme: " " exec: " " } test: { - id: "RT-1.9" - description: "BGP Transport Parameters test" - readme: "" + id: "RT-1.58" + description: "Support for learning IPv6 ULA/64 address space over [E|I]BGP sessions" + readme: " " exec: " " } test: { - id: "RT-2.2" - description: "IS-IS LSP Updates" - readme: "https://github.com/openconfig/featureprofiles/feature/experimental/isis/otg_tests/lsp_updates_test/README.md" -} -test: { - id: "RT-2.6" - description: "IS-IS Hello-Padding enabled at interface level" - readme: "" + id: "RT-1.59" + description: "Support for configuring static IPv6 ULA/64 routes with higher administrative distance" + readme: " " exec: " " } test: { - id: "RT-2.7" - description: "IS-IS Passive is enabled at interface level" - readme: "" + id: "RT-1.60" + description: "ECMP hashing support for [E|I]BGP learnt ULA/64 address space" + readme: " " exec: " " } test: { - id: "RT-2.8" - description: "IS-IS Passive is enabled at the area level" - readme: "" + id: "RT-1.61" + description: "ECMP hashing support for statically confifgured ULA/64 address space" + readme: " " exec: " " } test: { - id: "RT-2.9" - description: "IS-IS metric style wide enabled" - readme: "" + id: "RT-1.62" + description: "ECMP hash testing for [E|I]BGP learnt IPv6 NLRI (including ULA/64 address space) with IPv4 Next-hop." + readme: " " exec: " " } test: { @@ -643,6 +649,35 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/experimental/isis/otg_tests/isis_drain_test/README.md" exec: " " } +test: { + id: "RT-2.2" + description: "IS-IS LSP Updates" + readme: "https://github.com/openconfig/featureprofiles/feature/experimental/isis/otg_tests/lsp_updates_test/README.md" +} +test: { + id: "RT-2.6" + description: "IS-IS Hello-Padding enabled at interface level" + readme: "" + exec: " " +} +test: { + id: "RT-2.7" + description: "IS-IS Passive is enabled at interface level" + readme: "" + exec: " " +} +test: { + id: "RT-2.8" + description: "IS-IS Passive is enabled at the area level" + readme: "" + exec: " " +} +test: { + id: "RT-2.9" + description: "IS-IS metric style wide enabled" + readme: "" + exec: " " +} test: { id: "RT-3.1" description: "Policy based VRF selection base" @@ -673,6 +708,12 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/interface/singleton/ate_tests/singleton_test/README.md" exec: " " } +test: { + id: "RT-5.10" + description: "IPv6 Link Local generated by SLAAC" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/interface/ip/ipv6_slaac_link_local_test/otg_tests/ipv6_slaac_link_local_test/README.md" + exec: "https://github.com/openconfig/featureprofiles/blob/main/feature/interface/ip/ipv6_slaac_link_local_test/otg_tests/ipv6_slaac_link_local_test/ipv6_slaac_link_local_test.go" +} test: { id: "RT-5.2" description: "Aggregate Interfaces" @@ -714,12 +755,6 @@ test: { description: "Interface Loopback mode" readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/experimental/interface/interface_loopback_aggregate/otg_tests/interface_loopback_aggregate/README.md" } -test: { - id: "RT-9" - description: "Interface IPv6 ND RA disable" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/interface/holdtime/otg_tests/holdtime_test/README.md" - exec: " " -} test: { id: "RT-5.7" description: "Aggregate Not Viable All" @@ -738,12 +773,6 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/interface/ip/ipv6_ND/otg_tests/disable_ipv6_nd_ra_test/README.md" exec: " " } -test: { - id: "RT-5.10" - description: "IPv6 Link Local generated by SLAAC" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/interface/ip/ipv6_slaac_link_local_test/otg_tests/ipv6_slaac_link_local_test/README.md" - exec: "https://github.com/openconfig/featureprofiles/blob/main/feature/interface/ip/ipv6_slaac_link_local_test/otg_tests/ipv6_slaac_link_local_test/ipv6_slaac_link_local_test.go" -} test: { id: "RT-6.1" description: "Core LLDP TLV Population" @@ -760,6 +789,12 @@ test: { id: "RT-7.1" readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/bgp/policybase/otg_tests/default_policies_test/README.md" } +test: { + 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_test/README.md" + exec: " " +} test: { id: "RT-7.2" description: "BGP Policy Community Set" @@ -802,9 +837,9 @@ test: { exec: " " } test: { - 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_test/README.md" + id: "RT-7.9" + description: "Support for configuring ECMP hashing based on *any* subset of the following (L3 src IP, L3 dst IP, protocol, L4 src port, L4 dst port, L3 IPv6 flow label)" + readme: " " exec: " " } test: { @@ -813,6 +848,12 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/interface/singleton/tests/singleton_with_breakouts/README.md" exec: " " } +test: { + id: "RT-9" + description: "Interface IPv6 ND RA disable" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/interface/holdtime/otg_tests/holdtime_test/README.md" + exec: " " +} test: { id: "RT-9.1" description: "Equal distribution of traffic" @@ -825,10 +866,8 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/isis/weighted_ECMP/README.md" } test: { - id: "RT-9" - description: "Weighted-ECMP for IS-IS" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/isis/weighted_ECMP/README.md" - exec: " " + id: "Replay-1.2" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/experimental/replay/tests/p4rt_replay/README.md" } test: { id: "SFLOW-1" @@ -852,6 +891,11 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/interface/staticarp/ate_tests/static_arp_test/README.md" exec: " " } +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-11.2" description: "Backup NHG: Multiple NH" @@ -892,6 +936,11 @@ test: { id: "TE-16.1" readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/gribi/otg_tests/basic_encap_test/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: "TE-17.1" readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/experimental/gribi/otg_tests/vrf_policy_driven_te/README.md" @@ -1056,16 +1105,6 @@ test: { 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" @@ -1078,6 +1117,66 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/logging/console_vty_file/tests/README.md" exec: " " } +test: { + id: "TRANSCEIVER-1" + 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: { + id: "TRANSCEIVER-10" + description: "400ZR Optics FEC(Forward Error Correction) Uncorrectable Frames Streaming" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_fec_uncorrectable_frames_test/README.md" + exec: " " +} +test: { + id: "TRANSCEIVER-11" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_logical_channels_test/README.md" +} +test: { + id: "TRANSCEIVER-12" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_supply_voltage_test/README.md" +} +test: { + id: "TRANSCEIVER-13" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_low_power_mode_test/README.md" +} +test: { + id: "TRANSCEIVER-3" + description: "400ZR Optics firmware version streaming" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_firmware_version_test/README.md" + exec: " " +} +test: { + id: "TRANSCEIVER-4" + description: "400ZR Optics RX Input and TX Output Power streaming" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_input_output_power_test/README.md" + exec: " " +} +test: { + id: "TRANSCEIVER-5" + description: "400ZR channel frequency and output TX launch power setting" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_tunable_parameters_test/README.md" + exec: " " +} +test: { + id: "TRANSCEIVER-6" + 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" + description: "400ZR Optics module temperature streaming" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_temperature_test/README.md" + exec: " " +} +test: { + id: "TRANSCEIVER-9" + description: "400ZR TX laser bias current telemetry values streaming" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_laser_bias_current_test/README.md" + exec: " " +} test: { id: "TUN-1.1" description: "Filter based IPv4 GRE encapsulation" @@ -1162,6 +1261,152 @@ test: { readme: "" exec: " " } +test: { + id: "TUN-2.6" + description: "MPLSoUDP Encap" + readme: "" + exec: " " +} +test: { + id: "TUN-2.7" + description: "MPLSoUDP Decap" + readme: "" + exec: " " +} +test: { + id: "TUN-2.8" + description: "ECMP hashing based on outer header of MPLSoUDP packets" + readme: "" + exec: " " +} +test: { + id: "TUN-2.9" + description: "ECMP hashing for GUE flows with IPv4 and IPv6 outer and inner headers" + readme: "" + exec: " " +} +test: { + id: "TUN-2.10" + description: "ECMP hashing based on outer and Inner header for GRE packets" + readme: "" + exec: " " +} +test: { + id: "TUN-2.11" + description: "Filter based GUE decap with IPv4 tunnel endpoints" + readme: "" + exec: " " +} +test: { + id: "TUN-2.12" + description: "Filter based GUE decap with IPv6 tunnel endpoints" + readme: "" + exec: " " +} +test: { + id: "TUN-2.13" + description: "Interface based GUE decap with IPv4 tunnel endpoints" + readme: "" + exec: " " +} +test: { + id: "TUN-2.14" + description: "Interface based GUE decap with IPv6 tunnel endpoints" + readme: "" + exec: " " +} +test: { + id: "TUN-2.15" + description: "Interface based GUE encapsulation with IPv4 outer header" + readme: "" + exec: " " +} +test: { + id: "TUN-2.16" + description: "Interface based GUE encapsulation with IPv6 outer header" + readme: "" + exec: " " +} +test: { + id: "bootz-1.1" + description: "Missing configuration - Device fails with status invalid parameter" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-1.2" + description: "Invalid configuration - Device fails with status invalid parameter" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-1.3" + description: "Valid configuration - Device succeded with status ok" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-2.1" + description: "Software version is different - Device is upgraded to the new version" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-2.2" + description: "Invalid software image - Device fails with status invalid parameter" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-3" + description: "bootz-3: Validate Ownership Voucher in bootz configuration" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-3.1" + description: "No ownership voucher - Device boots without OV present" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-3.2" + description: "Invalid OV - Device fails with status invalid parameter" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-3.3" + description: "OV fails - Device fails with status invalid parameter" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-3.4" + description: "OV valid - Device boots with OV installed" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-4" + description: "bootz-4: Validate device properly resets if provided invalid image" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-4.1" + description: "no OS provided - Device boots with existing image" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-4.2" + description: "Invalid OS image provided - Device fails with status invalid parameter" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-4.3" + description: "failed to fetch image from remote URL - Device fails with status invalid parameter" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-4.4" + description: "OS checksum doesn't match - Device fails with invalid parameter" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} +test: { + id: "bootz-5" + description: "Validate gNSI components in bootz configuration" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/bootz/tests/README.md" +} test: { id: "gNMI-1.1" description: "cli Origin" @@ -1207,7 +1452,6 @@ test: { test: { id: "gNMI-1.14" description: "OpenConfig metadate consistency during large config push" - ## test intended to cover bug reported in b/271476345 readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/gnmi/metadata/tests/large_set_consistency_test/README.md" exec: " " } @@ -1358,74 +1602,62 @@ test: { exec: " " } test: { - id: "TRANSCEIVER-1" - description: "400ZR Chromatic Dispersion(CD) telemetry values streaming" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_cd_test/README.md" + id: "CNTR-1" + description: "Basic container lifecycle via `gnoi.Containerz`." + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/container/containerz/tests/container_lifecycle/README.md" exec: " " } test: { - id: "TRANSCEIVER-3" - description: "400ZR Optics firmware version streaming" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_firmware_version_test/README.md" + id: "CNTR-1.1" + description: "Deploy and Start a Container" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/container/containerz/tests/container_lifecycle/README.md" exec: " " } test: { - id: "TRANSCEIVER-4" - description: "400ZR Optics RX Input and TX Output Power streaming" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_input_output_power_test/README.md" + id: "CNTR-1.2" + description: "Retrieve a running container's logs." + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/container/containerz/tests/container_lifecycle/README.md" exec: " " } test: { - id: "TRANSCEIVER-5" - description: "400ZR channel frequency and output TX launch power setting" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_tunable_parameters_test/README.md" + id: "CNTR-1.3" + description: "List the running containers on a DUT" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/container/containerz/tests/container_lifecycle/README.md" exec: " " } test: { - id: "TRANSCEIVER-6" - description: "400ZR Optics performance metrics (pm) streaming." - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_pm_test/README.md" + id: "CNTR-1.4" + description: "Stop a container running on a DUT." + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/container/containerz/tests/container_lifecycle/README.md" exec: " " } test: { - id: "TRANSCEIVER-8" - description: "400ZR Optics module temperature streaming" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_temperature_test/README.md" + id: "CNTR-2" + description: "Container network connectivity tests" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/container/networking/tests/container_connectivity/README.md" exec: " " } test: { - id: "TRANSCEIVER-9" - description: "400ZR TX laser bias current telemetry values streaming" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_laser_bias_current_test/README.md" + id: "CNTR-2.1" + description: "Connect to container from external client." + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/container/networking/tests/container_connectivity/README.md" exec: " " } test: { - id: "TRANSCEIVER-10" - description: "400ZR Optics FEC(Forward Error Correction) Uncorrectable Frames Streaming" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_fec_uncorrectable_frames_test/README.md" + id: "CNTR-2.2" + description: "Connect to locally running service." + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/container/networking/tests/container_connectivity/README.md" exec: " " } test: { - id: "TRANSCEIVER-11" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_logical_channels_test/README.md" -} -test: { - id: "TRANSCEIVER-12" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_supply_voltage_test/README.md" -} -test: { - id: "TRANSCEIVER-13" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/platform/transceiver/zr_low_power_mode_test/README.md" -} -test: { - id: "PLT-1.1" - description: "Interface breakout Test" - readme: "https://github.com/openconfig/featureprofiles/feature/experimental/platform/tests/breakout_configuration/README.md" + id: "CNTR-2.3" + description: "Connect to a remote node." + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/container/networking/tests/container_connectivity/README.md" exec: " " } test: { - id: "MGT-1" - description: "Management HA test" - readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/management/README.md" + id: "CNTR-2.4" + description: "Connect to another container on a local node" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/container/networking/tests/container_connectivity/README.md" exec: " " } diff --git a/tools/fpcli/README.md b/tools/fpcli/README.md index c553032636e..b864ab0459b 100644 --- a/tools/fpcli/README.md +++ b/tools/fpcli/README.md @@ -13,9 +13,8 @@ fpcli show rpcs gnoi -d tmp Output: ``` -gnoi.bgp.BGP.ClearBGPNeighbor gnoi.bootconfig.BootConfig.GetBootConfig +gnoi.bgp.BGP.ClearBGPNeighbor +gnoi.bootconfig.BootConfig.GetBootConfig gnoi.bootconfig.BootConfig.SetBootConfig -gnoi.certificate.CertificateManagement.CanGenerateCSR -gnoi.certificate.CertificateManagement.GenerateCSR ... ``` diff --git a/tools/fpcli/cmd/root.go b/tools/fpcli/cmd/root.go index 939880b664d..41753078e03 100644 --- a/tools/fpcli/cmd/root.go +++ b/tools/fpcli/cmd/root.go @@ -39,8 +39,6 @@ $ fpcli show rpcs gnoi -d tmp gnoi.bgp.BGP.ClearBGPNeighbor gnoi.bootconfig.BootConfig.GetBootConfig gnoi.bootconfig.BootConfig.SetBootConfig -gnoi.certificate.CertificateManagement.CanGenerateCSR -gnoi.certificate.CertificateManagement.GenerateCSR ...`, // Uncomment the following line if your bare application // has an action associated with it: diff --git a/tools/fpcli/cmd/rpcs.go b/tools/fpcli/cmd/rpcs.go index 2e900ec0ae4..fe2de683f2d 100644 --- a/tools/fpcli/cmd/rpcs.go +++ b/tools/fpcli/cmd/rpcs.go @@ -38,8 +38,6 @@ $ fpcli show rpcs gnoi -d tmp gnoi.bgp.BGP.ClearBGPNeighbor gnoi.bootconfig.BootConfig.GetBootConfig gnoi.bootconfig.BootConfig.SetBootConfig -gnoi.certificate.CertificateManagement.CanGenerateCSR -gnoi.certificate.CertificateManagement.GenerateCSR ...`, Run: func(cmd *cobra.Command, args []string) { downloadPath := viper.GetString("download-dir") diff --git a/tools/fpcli/cmd/show.go b/tools/fpcli/cmd/show.go index 28f223b0a18..fe35a60d4a4 100644 --- a/tools/fpcli/cmd/show.go +++ b/tools/fpcli/cmd/show.go @@ -32,8 +32,6 @@ $ fpcli show rpcs gnoi -d tmp gnoi.bgp.BGP.ClearBGPNeighbor gnoi.bootconfig.BootConfig.GetBootConfig gnoi.bootconfig.BootConfig.SetBootConfig -gnoi.certificate.CertificateManagement.CanGenerateCSR -gnoi.certificate.CertificateManagement.GenerateCSR ...`, // Uncomment the following line if "show" // has an action associated with it: diff --git a/tools/internal/ocpaths/ocpaths_test.go b/tools/internal/ocpaths/ocpaths_test.go index f4582df917f..237e8947b59 100644 --- a/tools/internal/ocpaths/ocpaths_test.go +++ b/tools/internal/ocpaths/ocpaths_test.go @@ -164,7 +164,7 @@ func TestValidatePath(t *testing.T) { err := validatePath(ocpath, root) if diff := errdiff.Substring(err, tt.wantErrSubstr); diff != "" { - t.Errorf(diff) + t.Error(diff) } if err != nil { diff --git a/tools/nosimage/example/generate_example.go b/tools/nosimage/example/generate_example.go index 00871279ffc..f26e24796d4 100644 --- a/tools/nosimage/example/generate_example.go +++ b/tools/nosimage/example/generate_example.go @@ -21,8 +21,10 @@ import ( "flag" "fmt" "os" + "path/filepath" "time" + log "github.com/golang/glog" "github.com/protocolbuffers/txtpbfmt/parser" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" @@ -34,10 +36,13 @@ import ( timestamppb "google.golang.org/protobuf/types/known/timestamppb" ) +//go:generate go run generate_example.go -folder-path . +//go:generate go run generate_example.go -folder-path . -invalid + // Config is the set of flags for this binary. type Config struct { - FilePath string - Invalid bool + FolderPath string + Invalid bool } // New registers a flagset with the configuration needed by this binary. @@ -47,8 +52,8 @@ func New(fs *flag.FlagSet) *Config { if fs == nil { fs = flag.CommandLine } - fs.StringVar(&c.FilePath, "file-path", "example_nosimage.textproto", "generate an example file at the given path rather than validating a file.") - fs.BoolVar(&c.Invalid, "invalid", false, "generate an invalid example") + fs.StringVar(&c.FolderPath, "folder-path", "", "generate an example file in the given folder rather than validating a file.") + fs.BoolVar(&c.Invalid, "invalid", false, "generate invalid examples") return c } @@ -61,19 +66,30 @@ func init() { config = New(nil) } -func generateExample(filepath string, valid bool) error { +func generateExample(invalidPaths, invalidProtocols, invalidSoftwareName, invalidHardwareName bool) ([]byte, error) { componentPrefix := "/components/component" softwareComponent := "OPERATING_SYSTEM" interfaceLeafName := "name" - if !valid { + if invalidPaths { componentPrefix = "/componentsssssssssss/component" softwareComponent = "JOVIAN_ATMOSPHERE" interfaceLeafName = "does-not-exist" } - bs, err := formatTxtpb(&npb.NOSImageProfile{ + var ( + softwareVersion string + hardwareName string + ) + if !invalidSoftwareName { + softwareVersion = "7a1cb734c83f0d9ba5b273f920bc002ad0056178" + } + if !invalidHardwareName { + hardwareName = "lemming" + } + return formatTxtpb(&npb.NOSImageProfile{ VendorId: opb.Device_OPENCONFIG, Nos: "lemming", - SoftwareVersion: "7a1cb734c83f0d9ba5b273f920bc002ad0056178", + SoftwareVersion: softwareVersion, + HardwareName: hardwareName, ReleaseDate: timestamppb.New(time.Date(2023, time.November, 16, 16, 20, 0, 0, time.FixedZone("UTC-8", -8*60*60))), Ocpaths: &ppb.OCPaths{ Version: "2.5.0", @@ -96,7 +112,7 @@ func generateExample(filepath string, valid bool) error { }, Ocrpcs: &rpb.OCRPCs{ OcProtocols: func() map[string]*rpb.OCProtocol { - if valid { + if !invalidProtocols { return map[string]*rpb.OCProtocol{ "gnmi": { Version: "0.10.0", @@ -143,10 +159,6 @@ func generateExample(filepath string, valid bool) error { }(), }, }) - if err != nil { - return err - } - return os.WriteFile(filepath, bs, 0664) } func formatTxtpb(msg proto.Message) ([]byte, error) { @@ -171,13 +183,50 @@ func formatTxtpb(msg proto.Message) ([]byte, error) { func main() { flag.Parse() - if config.FilePath == "" { - fmt.Println("must provide example file path to write") - os.Exit(1) + if config.FolderPath == "" { + log.Exitln("must provide example folder path to write to") } - if err := generateExample(config.FilePath, !config.Invalid); err != nil { - fmt.Println(err) - os.Exit(1) + fileSpecs := []struct { + name string + isInvalid bool + invalidPaths bool + invalidProtocols bool + invalidSoftwareName bool + invalidHardwareName bool + }{{ + name: "valid", + }, { + name: "invalid-path", + isInvalid: true, + invalidPaths: true, + }, { + name: "invalid-protocols", + isInvalid: true, + invalidProtocols: true, + }, { + name: "invalid-software-name", + isInvalid: true, + invalidSoftwareName: true, + }, { + name: "invalid-hw-name", + isInvalid: true, + invalidHardwareName: true, + }} + + for _, spec := range fileSpecs { + if config.Invalid != spec.isInvalid { + continue + } + + bs, err := generateExample(spec.invalidPaths, spec.invalidProtocols, spec.invalidSoftwareName, spec.invalidHardwareName) + if err != nil { + log.Exitln(err) + } + path := filepath.Join(config.FolderPath, spec.name+"_example_nosimageprofile.textproto") + fmt.Printf("writing to %q\n", path) + if err := os.WriteFile(path, bs, 0664); err != nil { + log.Exitln(err) + } } } diff --git a/tools/nosimage/example/example_nosimageprofile.textproto b/tools/nosimage/example/invalid-hw-name_example_nosimageprofile.textproto similarity index 100% rename from tools/nosimage/example/example_nosimageprofile.textproto rename to tools/nosimage/example/invalid-hw-name_example_nosimageprofile.textproto diff --git a/tools/nosimage/example/example_nosimageprofile_invalid.textproto b/tools/nosimage/example/invalid-path_example_nosimageprofile.textproto similarity index 95% rename from tools/nosimage/example/example_nosimageprofile_invalid.textproto rename to tools/nosimage/example/invalid-path_example_nosimageprofile.textproto index 6cfeb5348ad..bdff079de5d 100644 --- a/tools/nosimage/example/example_nosimageprofile_invalid.textproto +++ b/tools/nosimage/example/invalid-path_example_nosimageprofile.textproto @@ -5,6 +5,7 @@ vendor_id: OPENCONFIG nos: "lemming" software_version: "7a1cb734c83f0d9ba5b273f920bc002ad0056178" +hardware_name: "lemming" release_date: { seconds: 1700180400 } @@ -41,14 +42,12 @@ ocrpcs: { value: { method_name: "gnmi.gNMI.Set" method_name: "gnmi.gNMI.Subscribe" - method_name: "gnmi.gNMI.Whatsup" version: "0.10.0" } } oc_protocols: { key: "gnoi" value: { - method_name: "gnmi.gNMI.Get" method_name: "gnoi.bgp.BGP.ClearBGPNeighbor" method_name: "gnoi.healthz.Healthz.Acknowledge" method_name: "gnoi.healthz.Healthz.Artifact" diff --git a/tools/nosimage/example/invalid-protocols_example_nosimageprofile.textproto b/tools/nosimage/example/invalid-protocols_example_nosimageprofile.textproto new file mode 100644 index 00000000000..eaeffbf6780 --- /dev/null +++ b/tools/nosimage/example/invalid-protocols_example_nosimageprofile.textproto @@ -0,0 +1,62 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/nosimage.proto +# proto-message: NOSImageProfile +# txtpbfmt: expand_all_children +# txtpbfmt: sort_repeated_fields_by_content +vendor_id: OPENCONFIG +nos: "lemming" +software_version: "7a1cb734c83f0d9ba5b273f920bc002ad0056178" +hardware_name: "lemming" +release_date: { + seconds: 1700180400 +} +ocpaths: { + ocpaths: { + name: "/interfaces/interface/config/name" + } + ocpaths: { + name: "/components/component/state/location" + ocpath_constraint: { + platform_type: "PORT" + } + } + ocpaths: { + name: "/components/component/state/serial-no" + ocpath_constraint: { + platform_type: "STORAGE" + } + } + ocpaths: { + name: "/components/component/state/software-version" + ocpath_constraint: { + platform_type: "OPERATING_SYSTEM" + } + } + ocpaths: { + name: "/network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/state/peer-as" + } + version: "2.5.0" +} +ocrpcs: { + oc_protocols: { + key: "gnmi" + value: { + method_name: "gnmi.gNMI.Set" + method_name: "gnmi.gNMI.Subscribe" + method_name: "gnmi.gNMI.Whatsup" + version: "0.10.0" + } + } + oc_protocols: { + key: "gnoi" + value: { + method_name: "gnmi.gNMI.Get" + 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" + version: "0.3.0" + } + } +} diff --git a/tools/nosimage/example/invalid-software-name_example_nosimageprofile.textproto b/tools/nosimage/example/invalid-software-name_example_nosimageprofile.textproto new file mode 100644 index 00000000000..e6ec07a2843 --- /dev/null +++ b/tools/nosimage/example/invalid-software-name_example_nosimageprofile.textproto @@ -0,0 +1,59 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/nosimage.proto +# proto-message: NOSImageProfile +# txtpbfmt: expand_all_children +# txtpbfmt: sort_repeated_fields_by_content +vendor_id: OPENCONFIG +nos: "lemming" +hardware_name: "lemming" +release_date: { + seconds: 1700180400 +} +ocpaths: { + ocpaths: { + name: "/interfaces/interface/config/name" + } + ocpaths: { + name: "/components/component/state/location" + ocpath_constraint: { + platform_type: "PORT" + } + } + ocpaths: { + name: "/components/component/state/serial-no" + ocpath_constraint: { + platform_type: "STORAGE" + } + } + ocpaths: { + name: "/components/component/state/software-version" + ocpath_constraint: { + platform_type: "OPERATING_SYSTEM" + } + } + ocpaths: { + name: "/network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/state/peer-as" + } + version: "2.5.0" +} +ocrpcs: { + oc_protocols: { + key: "gnmi" + value: { + method_name: "gnmi.gNMI.Set" + method_name: "gnmi.gNMI.Subscribe" + version: "0.10.0" + } + } + 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" + version: "0.3.0" + } + } +} diff --git a/tools/nosimage/example/valid_example_nosimageprofile.textproto b/tools/nosimage/example/valid_example_nosimageprofile.textproto new file mode 100644 index 00000000000..42f544e5054 --- /dev/null +++ b/tools/nosimage/example/valid_example_nosimageprofile.textproto @@ -0,0 +1,60 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/nosimage.proto +# proto-message: NOSImageProfile +# txtpbfmt: expand_all_children +# txtpbfmt: sort_repeated_fields_by_content +vendor_id: OPENCONFIG +nos: "lemming" +software_version: "7a1cb734c83f0d9ba5b273f920bc002ad0056178" +hardware_name: "lemming" +release_date: { + seconds: 1700180400 +} +ocpaths: { + ocpaths: { + name: "/interfaces/interface/config/name" + } + ocpaths: { + name: "/components/component/state/location" + ocpath_constraint: { + platform_type: "PORT" + } + } + ocpaths: { + name: "/components/component/state/serial-no" + ocpath_constraint: { + platform_type: "STORAGE" + } + } + ocpaths: { + name: "/components/component/state/software-version" + ocpath_constraint: { + platform_type: "OPERATING_SYSTEM" + } + } + ocpaths: { + name: "/network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/state/peer-as" + } + version: "2.5.0" +} +ocrpcs: { + oc_protocols: { + key: "gnmi" + value: { + method_name: "gnmi.gNMI.Set" + method_name: "gnmi.gNMI.Subscribe" + version: "0.10.0" + } + } + 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" + version: "0.3.0" + } + } +} diff --git a/tools/nosimage/validate/validate.go b/tools/nosimage/validate/validate.go index e6fab17705c..a43203d4e4d 100644 --- a/tools/nosimage/validate/validate.go +++ b/tools/nosimage/validate/validate.go @@ -20,6 +20,7 @@ import ( "fmt" "os" + log "github.com/golang/glog" "github.com/openconfig/featureprofiles/tools/internal/ocpaths" "github.com/openconfig/featureprofiles/tools/internal/ocrpcs" "google.golang.org/protobuf/encoding/prototext" @@ -79,6 +80,14 @@ func main() { os.Exit(1) } + if profile.GetSoftwareVersion() == "" { + log.Exitln("Software version must be specified") + } + + if profile.GetHardwareName() == "" { + log.Exitln("HW name must be specified") + } + if err := os.MkdirAll(config.DownloadPath, 0750); err != nil { fmt.Println(fmt.Errorf("cannot create download path directory: %v", config.DownloadPath)) } diff --git a/tools/sort_testregistry/sort_testregistry.go b/tools/sort_testregistry/sort_testregistry.go new file mode 100644 index 00000000000..fa271a1daa3 --- /dev/null +++ b/tools/sort_testregistry/sort_testregistry.go @@ -0,0 +1,93 @@ +// Binary sort_registry sorts the test registry lexically such that it is easier +// for humans to add to the file and find the next available ID. It can be run +// by running: +// +// go run tools/sort_testregistry/sort_testregistry.go +// +// prior to submitting. +package main + +import ( + "bytes" + "flag" + "os" + "sort" + + log "github.com/golang/glog" + "google.golang.org/protobuf/encoding/prototext" + "google.golang.org/protobuf/proto" + + tpb "github.com/openconfig/featureprofiles/proto/testregistry_go_proto" +) + +var ( + file = flag.String("file", "testregistry.textproto", "input file to read registry from") +) + +func main() { + flag.Parse() + r := &tpb.TestRegistry{} + + f, err := os.ReadFile(*file) + if err != nil { + log.Exitf("cannot read input, err: %v", err) + } + if err := prototext.Unmarshal(f, r); err != nil { + log.Exitf("invalid registry input, err: %v", err) + } + + ids := []string{} + tests := map[string][]*tpb.Test{} + for _, t := range r.GetTest() { + if _, ok := tests[t.GetId()]; !ok { + tests[t.GetId()] = []*tpb.Test{} + ids = append(ids, t.GetId()) + } + + // Deduplicate identical entries. + var skip bool + for _, existing := range tests[t.GetId()] { + if proto.Equal(existing, t) { + log.Infof("skipping duplicate for %s", t.GetId()) + skip = true + } + } + if skip { + continue + } + + tests[t.GetId()] = append(tests[t.GetId()], t) + + } + + n := &tpb.TestRegistry{ + Name: r.GetName(), + Test: []*tpb.Test{}, + } + + sort.Strings(ids) + for _, i := range ids { + for _, tc := range tests[i] { + log.Infof("appending %s to %s", tc.GetId(), i) + n.Test = append(n.Test, tc) + } + } + mo := &prototext.MarshalOptions{ + Multiline: true, + Indent: " ", + } + + s, err := mo.Marshal(n) + if err != nil { + log.Exitf("cannot marshal proceessed proto, err: %v", err) + } + + b := &bytes.Buffer{} + b.WriteString("# proto-file: /proto/testregistry.proto\n") + b.WriteString("# proto-message: TestRegistry\n\n") + b.Write(s) + + if err := os.WriteFile(*file, b.Bytes(), 0644); err != nil { + log.Exitf("cannot write out processed file, err: %v", err) + } +} diff --git a/topologies/binding/binding.go b/topologies/binding/binding.go index 71f7c21c034..43c9eb3e895 100644 --- a/topologies/binding/binding.go +++ b/topologies/binding/binding.go @@ -27,27 +27,25 @@ import ( grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" "github.com/open-traffic-generator/snappi/gosnappi" + bindpb "github.com/openconfig/featureprofiles/topologies/proto/binding" + gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/gnoigo" + grpb "github.com/openconfig/gribi/v1/proto/service" "github.com/openconfig/ondatra/binding" "github.com/openconfig/ondatra/binding/grpcutil" "github.com/openconfig/ondatra/binding/introspect" "github.com/openconfig/ondatra/binding/ixweb" + opb "github.com/openconfig/ondatra/proto" + p4pb "github.com/p4lang/p4runtime/go/p4/v1" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/knownhosts" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" - - bindpb "github.com/openconfig/featureprofiles/topologies/proto/binding" - gpb "github.com/openconfig/gnmi/proto/gnmi" - grpb "github.com/openconfig/gribi/v1/proto/service" - opb "github.com/openconfig/ondatra/proto" - p4pb "github.com/p4lang/p4runtime/go/p4/v1" ) 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/nokia/srlinux/dut.textproto b/topologies/kne/nokia/srlinux/dut.textproto index 4861b80218f..63b1358d791 100644 --- a/topologies/kne/nokia/srlinux/dut.textproto +++ b/topologies/kne/nokia/srlinux/dut.textproto @@ -4,7 +4,7 @@ name: "nokia-srlinux-dut" nodes: { name: "dut" vendor: NOKIA - model: "ixr10" + model: "ixr10e" config: { image: "srlinux:latest" file: "config.cfg" diff --git a/topologies/kne/nokia/srlinux/dutate.textproto b/topologies/kne/nokia/srlinux/dutate.textproto index 3fb4002cdd4..e658396485a 100644 --- a/topologies/kne/nokia/srlinux/dutate.textproto +++ b/topologies/kne/nokia/srlinux/dutate.textproto @@ -4,7 +4,7 @@ name: "nokia-srlinux-dutate" nodes: { name: "dut" vendor: NOKIA - model: "ixr10" + model: "ixr10e" config: { image: "srlinux:latest" file: "config.cfg" diff --git a/topologies/kne/nokia/srlinux/dutate_lag.textproto b/topologies/kne/nokia/srlinux/dutate_lag.textproto index e7738b3f529..9809cc3f623 100644 --- a/topologies/kne/nokia/srlinux/dutate_lag.textproto +++ b/topologies/kne/nokia/srlinux/dutate_lag.textproto @@ -4,7 +4,7 @@ name: "nokia-srlinux-dutate-lag" nodes: { name: "dut" vendor: NOKIA - model: "ixr10" + model: "ixr10e" config: { image: "srlinux:latest" file: "config.cfg" diff --git a/topologies/kne/nokia/srlinux/dutdut.textproto b/topologies/kne/nokia/srlinux/dutdut.textproto index 809db2f577f..ec6b3957c93 100644 --- a/topologies/kne/nokia/srlinux/dutdut.textproto +++ b/topologies/kne/nokia/srlinux/dutdut.textproto @@ -4,7 +4,7 @@ name: "nokia-srlinux-dutdut" nodes: { name: "dut1" vendor: NOKIA - model: "ixr10" + model: "ixr10e" config: { image: "srlinux:latest" file: "config.cfg" @@ -72,7 +72,7 @@ nodes: { nodes: { name: "dut2" vendor: NOKIA - model: "ixr10" + model: "ixr10e" config: { image: "srlinux:latest" file: "config.cfg" diff --git a/topologies/kne/nokia/srlinux/dutdutate.textproto b/topologies/kne/nokia/srlinux/dutdutate.textproto index 95e29436d0e..f3f72c1e8b5 100644 --- a/topologies/kne/nokia/srlinux/dutdutate.textproto +++ b/topologies/kne/nokia/srlinux/dutdutate.textproto @@ -4,7 +4,7 @@ name: "nokia-srlinux-dutdut" nodes: { name: "dut1" vendor: NOKIA - model: "ixr10" + model: "ixr10e" config: { image: "srlinux:latest" file: "config.cfg" @@ -60,7 +60,7 @@ nodes: { nodes: { name: "dut2" vendor: NOKIA - model: "ixr10" + model: "ixr10e" config: { image: "srlinux:latest" file: "config.cfg" diff --git a/topologies/kne/nokia/srlinux/topology.textproto b/topologies/kne/nokia/srlinux/topology.textproto index 52cf5569932..b3d7fed2c37 100644 --- a/topologies/kne/nokia/srlinux/topology.textproto +++ b/topologies/kne/nokia/srlinux/topology.textproto @@ -4,7 +4,7 @@ name: "nokia-srlinux" nodes: { name: "dut1" vendor: NOKIA - model: "ixr10" + model: "ixr10e" config: { image: "srlinux:latest" file: "config.cfg" @@ -102,7 +102,7 @@ nodes: { nodes: { name: "dut2" vendor: NOKIA - model: "ixr10" + model: "ixr10e" config: { image: "srlinux:latest" file: "config.cfg"