diff --git a/feature/system/gnmi/set/tests/gnmi_set_test/gnmi_set_test.go b/feature/system/gnmi/set/tests/gnmi_set_test/gnmi_set_test.go index 5f5d489e81a..8152a803887 100644 --- a/feature/system/gnmi/set/tests/gnmi_set_test/gnmi_set_test.go +++ b/feature/system/gnmi/set/tests/gnmi_set_test/gnmi_set_test.go @@ -15,12 +15,14 @@ package gnmi_set_test import ( + "context" "fmt" "regexp" "strconv" "strings" "sync" "testing" + "time" "flag" @@ -31,6 +33,7 @@ import ( "github.com/openconfig/ondatra/gnmi" "github.com/openconfig/ondatra/gnmi/oc" "github.com/openconfig/ondatra/netutil" + "github.com/openconfig/ygnmi/schemaless" "github.com/openconfig/ygnmi/ygnmi" "github.com/openconfig/ygot/ygot" "github.com/openconfig/ygot/ytypes" @@ -53,7 +56,11 @@ var ( pruneQoS = flag.Bool("prune_qos", true, "Prune QoS config.") // Experimental flags that will likely become a deviation. - cannotDeleteVRF = flag.Bool("cannot_delete_vrf", true, "Device cannot delete VRF.") // See "Note about cannotDeleteVRF" below. + cannotDeleteVRF = flag.Bool("cannot_delete_vrf", true, "Device cannot delete VRF.") // See "Note about cannotDeleteVRF" below. + cannotConfigurePortSpeed = flag.Bool("cannot_config_port_speed", false, "Some devices depending on the type of line card may not allow changing port speed, while still supporting the port speed leaf.") + + // Flags to ensure test passes without any dependency to the device config + baseOCConfigIsPresent = flag.Bool("base_oc_config_is_present", false, "No OC config is loaded on router, so Get config on the root returns no data.") ) var ( @@ -74,6 +81,29 @@ var ( } ) +// Options are optional parameters to pass when deleting configs from the collected running config used in removeStatementsBetweenWords +type Options struct { + interfaces []string +} + +// breakout struct parameters define the speed and number of physical channels +type breakout struct { + breakoutSpeed oc.E_IfEthernet_ETHERNET_SPEED + numPhysicalChannels *uint8 +} + +// showRunningConfig gets the running config from the router +func showRunningConfig(t testing.TB, dut *ondatra.DUTDevice) string { + if ondatra.DUT(t, "dut").Vendor() == ondatra.CISCO { + runningConfig, err := dut.RawAPIs().CLI(t).RunCommand(context.Background(), "show running-config") + if err != nil { + t.Fatalf("'show running-config' failed: %v", err) + } + return runningConfig.Output() + } + return "" +} + // Implementation Note // // Tests have three push variants: ItemOp, ContainerOp, and RootOp. @@ -108,8 +138,10 @@ func TestGetSet(t *testing.T) { // Configuring basic interface and network instance as some devices only populate OC after configuration. gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), dut)) gnmi.Replace(t, dut, gnmi.OC().Interface(p2.Name()).Config(), dutPort2.NewOCInterface(p2.Name(), dut)) - gnmi.Replace(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Type().Config(), - oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_DEFAULT_INSTANCE) + gnmi.Update(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Config(), &oc.NetworkInstance{ + Name: ygot.String(deviations.DefaultNetworkInstance(dut)), + Type: oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_DEFAULT_INSTANCE, + }) scope := defaultPushScope(dut) @@ -142,12 +174,12 @@ func TestDeleteInterface(t *testing.T) { op.push(t, dut, config, scope) t.Run("VerifyBeforeDelete", func(t *testing.T) { - v1 := gnmi.Lookup(t, dut, q1) - if got1, ok := v1.Val(); !ok || got1 != want1 { + v1, ok := gnmi.Await(t, dut, q1, 60*time.Second, want1).Val() + if !ok { t.Errorf("State got %v, want %v", v1, want1) } - v2 := gnmi.Lookup(t, dut, q2) - if got2, ok := v2.Val(); !ok || got2 != want2 { + v2, ok := gnmi.Await(t, dut, q2, 60*time.Second, want2).Val() + if !ok { t.Errorf("State got %v, want %v", v2, want2) } }) @@ -157,12 +189,8 @@ func TestDeleteInterface(t *testing.T) { config.DeleteInterface(p1.Name()) config.DeleteInterface(p2.Name()) - for _, iname := range scope.interfaces { - iface := config.GetInterface(iname) - if iface == nil { - config.Interface = nil - - } + if len(config.Interface) == 0 { + config.Interface = nil } op.push(t, dut, config, scope) @@ -208,6 +236,10 @@ func TestReuseIP(t *testing.T) { forEachPushOp(t, dut, func(t *testing.T, op pushOp, config *oc.Root) { t.Log("Initialize") + if deviations.SkipMacaddressCheck(dut) { + *setEthernetFromState = false + } + config.DeleteInterface(p1.Name()) config.DeleteInterface(agg1) configMember(config.GetOrCreateInterface(p1.Name()), agg1, dut) @@ -321,6 +353,10 @@ func TestDeleteNonDefaultVRF(t *testing.T) { config.DeleteInterface(p1.Name()) config.DeleteInterface(p2.Name()) + if deviations.ReorderCallsForVendorCompatibilty(dut) { + op.push(t, dut, config, scope) + } + ip1.ConfigOCInterface(config.GetOrCreateInterface(p1.Name()), dut) ip2.ConfigOCInterface(config.GetOrCreateInterface(p2.Name()), dut) @@ -328,8 +364,8 @@ func TestDeleteNonDefaultVRF(t *testing.T) { ni := config.GetOrCreateNetworkInstance(vrf) ni.Type = oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_L3VRF - id1 := attachInterface(ni, p1.Name(), 0) - id2 := attachInterface(ni, p2.Name(), 0) + id1 := attachInterface(dut, ni, p1.Name(), 0) + id2 := attachInterface(dut, ni, p2.Name(), 0) op.push(t, dut, config, scope) @@ -341,7 +377,10 @@ func TestDeleteNonDefaultVRF(t *testing.T) { }) t.Log("Cleanup") - + if deviations.ReorderCallsForVendorCompatibilty(dut) { + config.DeleteInterface(p1.Name()) + config.DeleteInterface(p2.Name()) + } config.DeleteNetworkInstance(vrf) op.push(t, dut, config, scope) @@ -369,6 +408,7 @@ func testMoveInterfaceBetweenVRF(t *testing.T, dut *ondatra.DUTDevice, firstVRF, p1 := dut.Port(t, "port1") p2 := dut.Port(t, "port2") + var id1, id2 string scope := &pushScope{ interfaces: []string{p1.Name(), p2.Name()}, @@ -387,11 +427,18 @@ func testMoveInterfaceBetweenVRF(t *testing.T, dut *ondatra.DUTDevice, firstVRF, config.DeleteNetworkInstance(firstVRF) ni := config.GetOrCreateNetworkInstance(firstVRF) ni.Type = oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_L3VRF + // add interface to firstVRF + if deviations.ReorderCallsForVendorCompatibilty(dut) { + id1 = attachInterface(dut, ni, p1.Name(), 0) + id2 = attachInterface(dut, ni, p2.Name(), 0) + } } - firstni := config.GetOrCreateNetworkInstance(firstVRF) - id1 := attachInterface(firstni, p1.Name(), 0) - id2 := attachInterface(firstni, p2.Name(), 0) + if !deviations.ReorderCallsForVendorCompatibilty(dut) { + firstni := config.GetOrCreateNetworkInstance(firstVRF) + id1 = attachInterface(dut, firstni, p1.Name(), 0) + id2 = attachInterface(dut, firstni, p2.Name(), 0) + } config.DeleteNetworkInstance(secondVRF) if *cannotDeleteVRF { @@ -404,9 +451,11 @@ func testMoveInterfaceBetweenVRF(t *testing.T, dut *ondatra.DUTDevice, firstVRF, t.Run("VerifyBeforeMove", func(t *testing.T) { verifyInterface(t, dut, p1.Name(), &ip1) verifyInterface(t, dut, p2.Name(), &ip2) - verifyAttachment(t, dut, firstVRF, id1, p1.Name()) - verifyAttachment(t, dut, firstVRF, id2, p2.Name()) - + // verify the added interface to first Non default VRF + if !deviations.ReorderCallsForVendorCompatibilty(dut) || firstVRF != defaultVRF { + verifyAttachment(t, dut, firstVRF, id1, p1.Name()) + verifyAttachment(t, dut, firstVRF, id2, p2.Name()) + } // We don't check /network-instances/network-instance/vlans/vlan/members because // these are for L2 switched ports, not L3 routed ports. }) @@ -416,18 +465,37 @@ func testMoveInterfaceBetweenVRF(t *testing.T, dut *ondatra.DUTDevice, firstVRF, if firstVRF != defaultVRF { // It is not necessary to explicitly remove the interface attachments since the VRF // is being deleted. + // delete interface before deleting NI + if deviations.ReorderCallsForVendorCompatibilty(dut) { + config.DeleteInterface(p1.Name()) + config.DeleteInterface(p2.Name()) + } config.DeleteNetworkInstance(firstVRF) } else { - // Remove just the interface attachments but keep the VRF. - firstni.DeleteInterface(id1) - firstni.DeleteInterface(id2) + // Delete interface from default NI before modifying the attachement + if deviations.ReorderCallsForVendorCompatibilty(dut) { + config.DeleteInterface(p1.Name()) + config.DeleteInterface(p2.Name()) + } else { + // Remove just the interface attachments but keep the VRF. + firstni := config.GetOrCreateNetworkInstance(firstVRF) + firstni.DeleteInterface(id1) + firstni.DeleteInterface(id2) + } } + op.push(t, dut, config, scope) secondni := config.GetOrCreateNetworkInstance(secondVRF) secondni.Type = oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_L3VRF - attachInterface(secondni, p1.Name(), 0) - attachInterface(secondni, p2.Name(), 0) - + if deviations.ReorderCallsForVendorCompatibilty(dut) { + id1 = attachInterface(dut, secondni, p1.Name(), 0) + id2 = attachInterface(dut, secondni, p2.Name(), 0) + ip1.ConfigOCInterface(config.GetOrCreateInterface(p1.Name()), dut) + ip2.ConfigOCInterface(config.GetOrCreateInterface(p2.Name()), dut) + } else { + attachInterface(dut, secondni, p1.Name(), 0) + attachInterface(dut, secondni, p2.Name(), 0) + } op.push(t, dut, config, scope) t.Run("VerifyAfterMove", func(t *testing.T) { @@ -438,7 +506,11 @@ func testMoveInterfaceBetweenVRF(t *testing.T, dut *ondatra.DUTDevice, firstVRF, }) t.Log("Cleanup") - + // delete interface before deleting NI + if deviations.ReorderCallsForVendorCompatibilty(dut) { + config.DeleteInterface(p1.Name()) + config.DeleteInterface(p2.Name()) + } config.DeleteNetworkInstance(secondVRF) op.push(t, dut, config, scope) @@ -447,6 +519,9 @@ func testMoveInterfaceBetweenVRF(t *testing.T, dut *ondatra.DUTDevice, firstVRF, func TestStaticProtocol(t *testing.T) { dut := ondatra.DUT(t, "dut") + if deviations.SkipContainerOp(dut) { + *skipContainerOp = true + } if deviations.StaticRouteNextHopInterfaceRefUnsupported(dut) { t.Skip() } @@ -487,8 +562,8 @@ func TestStaticProtocol(t *testing.T) { otherni := config.GetOrCreateNetworkInstance(otherVRF) otherni.Type = oc.NetworkInstanceTypes_NETWORK_INSTANCE_TYPE_L3VRF - id1 := attachInterface(otherni, p1.Name(), 0) - id2 := attachInterface(otherni, p2.Name(), 0) + id1 := attachInterface(dut, otherni, p1.Name(), 0) + id2 := attachInterface(dut, otherni, p2.Name(), 0) protocol := otherni.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, staticName) @@ -600,7 +675,11 @@ func TestStaticProtocol(t *testing.T) { }) t.Log("Cleanup") - + // delete interface before deleting NI + if deviations.ReorderCallsForVendorCompatibilty(dut) { + config.DeleteInterface(p1.Name()) + config.DeleteInterface(p2.Name()) + } config.DeleteNetworkInstance(otherVRF) config.DeleteNetworkInstance(unusedVRF) op.push(t, dut, config, scope) @@ -673,8 +752,8 @@ func configAggregate(i *oc.Interface, a *attrs.Attributes, dut *ondatra.DUTDevic func verifyMember(t testing.TB, p *ondatra.Port, aggID string) { t.Helper() q := gnmi.OC().Interface(p.Name()).Ethernet().AggregateId().State() - v := gnmi.Lookup(t, p.Device(), q) - if got, ok := v.Val(); !ok || got != aggID { + v, ok := gnmi.Await(t, p.Device(), q, 60*time.Second, aggID).Val() + if !ok { t.Errorf("State got %v, want %v", v, aggID) } } @@ -683,9 +762,9 @@ func verifyMember(t testing.TB, p *ondatra.Port, aggID string) { func verifyAggregate(t testing.TB, dev gnmi.DeviceOrOpts, aggID string, a *attrs.Attributes) { t.Helper() q := gnmi.OC().Interface(aggID).Aggregation().LagType().State() - v := gnmi.Lookup(t, dev, q) const want = oc.IfAggregate_AggregationType_STATIC - if got, ok := v.Val(); !ok || got != want { + v, ok := gnmi.Await(t, dev, q, 60*time.Second, want).Val() + if !ok { t.Errorf("State got %v, want %v", v, want) } verifyInterface(t, dev, aggID, a) @@ -695,8 +774,8 @@ func verifyAggregate(t testing.TB, dev gnmi.DeviceOrOpts, aggID string, a *attrs func verifyInterface(t testing.TB, dev gnmi.DeviceOrOpts, name string, a *attrs.Attributes) { t.Helper() q := gnmi.OC().Interface(name).Subinterface(0).Ipv4().Address(a.IPv4).PrefixLength().State() - v := gnmi.Lookup(t, dev, q) - if got, ok := v.Val(); !ok || got != a.IPv4Len { + v, ok := gnmi.Await(t, dev, q, 60*time.Second, a.IPv4Len).Val() + if !ok { t.Errorf("State got %v, want %v", v, a.IPv4Len) } else { t.Logf("Verified %v", v) @@ -704,12 +783,14 @@ func verifyInterface(t testing.TB, dev gnmi.DeviceOrOpts, name string, a *attrs. } // attachInterface attaches an interface name and subinterface sub to a network instance. -func attachInterface(ni *oc.NetworkInstance, name string, sub int) string { +func attachInterface(dut *ondatra.DUTDevice, ni *oc.NetworkInstance, name string, sub int) string { id := name // Possibly vendor specific? May have to use sub. niface := ni.GetOrCreateInterface(id) niface.Interface = ygot.String(name) niface.Subinterface = ygot.Uint32(uint32(sub)) - id = fmt.Sprintf("%s.%d", id, sub) + if deviations.InterfaceRefInterfaceIDFormat(dut) { + id = fmt.Sprintf("%s.%d", id, sub) + } return id } @@ -718,8 +799,8 @@ func attachInterface(ni *oc.NetworkInstance, name string, sub int) string { func verifyAttachment(t testing.TB, dev gnmi.DeviceOrOpts, vrf string, id string, name string) { t.Helper() q := gnmi.OC().NetworkInstance(vrf).Interface(id).Interface().State() - v := gnmi.Lookup(t, dev, q) - if got, ok := v.Val(); !ok || got != name { + v, ok := gnmi.Await(t, dev, q, 60*time.Second, name).Val() + if !ok { t.Errorf("State got %v, want %v", v, name) } else { t.Logf("Verified %v", v) @@ -789,6 +870,56 @@ func getDeviceConfig(t testing.TB, dev gnmi.DeviceOrOpts) *oc.Root { config := gnmi.Get[*oc.Root](t, dev, gnmi.OC().Config()) fptest.WriteQuery(t, "Untouched", gnmi.OC().Config(), config) + // load the base oc config from the device state when no oc config is loaded + if !*baseOCConfigIsPresent { + if ondatra.DUT(t, "dut").Vendor() == ondatra.CISCO { + intfsState := gnmi.GetAll(t, dev, gnmi.OC().InterfaceAny().State()) + for _, intf := range intfsState { + ygot.PruneConfigFalse(oc.SchemaTree["Interface"], intf) + config.DeleteInterface(intf.GetName()) + if intf.GetName() == "Loopback0" || intf.GetName() == "PTP0/RP1/CPU0/0" || intf.GetName() == "Null0" || intf.GetName() == "PTP0/RP0/CPU0/0" { + continue + } + intf.ForwardingViable = nil + intf.Mtu = nil + intf.HoldTime = nil + if intf.Subinterface != nil { + if intf.Subinterface[0].Ipv6 != nil { + intf.Subinterface[0].Ipv6.Autoconf = nil + } + } + config.AppendInterface(intf) + } + vrfsStates := gnmi.GetAll(t, dev, gnmi.OC().NetworkInstanceAny().State()) + for _, vrf := range vrfsStates { + // only needed for containerOp + if vrf.GetName() == "**iid" { + continue + } + if vrf.GetName() == "DEFAULT" { + config.NetworkInstance = nil + vrf.Interface = nil + for _, ni := range config.NetworkInstance { + ni.Mpls = nil + } + } + ygot.PruneConfigFalse(oc.SchemaTree["NetworkInstance"], vrf) + vrf.Table = nil + vrf.RouteLimit = nil + vrf.Mpls = nil + for _, intf := range vrf.Interface { + intf.AssociatedAddressFamilies = nil + } + for _, protocol := range vrf.Protocol { + for _, routes := range protocol.Static { + routes.Description = nil + } + } + config.AppendNetworkInstance(vrf) + } + } + } + if *pruneComponents { for cname, component := range config.Component { // Keep the port components in order to preserve the breakout-mode config. @@ -810,16 +941,39 @@ func getDeviceConfig(t testing.TB, dev gnmi.DeviceOrOpts) *oc.Root { // Ethernet config may not contain meaningful values if it wasn't explicitly // configured, so use its current state for the config, but prune non-config leaves. intf := gnmi.Get(t, dev, gnmi.OC().Interface(iname).State()) - breakout := config.GetComponent(intf.GetHardwarePort()).GetPort().GetBreakoutMode() e := intf.GetEthernet() - // Set port speed to unknown for non breakout interfaces - if breakout.GetGroup(1) == nil && e != nil { - e.SetPortSpeed(oc.IfEthernet_ETHERNET_SPEED_SPEED_UNKNOWN) + if len(intf.GetHardwarePort()) != 0 { + breakout := config.GetComponent(intf.GetHardwarePort()).GetPort().GetBreakoutMode() + e := intf.GetEthernet() + // Set port speed to unknown for non breakout interfaces + if breakout.GetGroup(1) == nil && e != nil { + e.SetPortSpeed(oc.IfEthernet_ETHERNET_SPEED_SPEED_UNKNOWN) + } } ygot.PruneConfigFalse(oc.SchemaTree["Interface_Ethernet"], e) if e.PortSpeed != 0 && e.PortSpeed != oc.IfEthernet_ETHERNET_SPEED_SPEED_UNKNOWN { iface.Ethernet = e } + // need to set mac address for mgmt interface to nil + if intf.GetName() == "MgmtEth0/RP0/CPU0/0" || intf.GetName() == "MgmtEth0/RP1/CPU0/0" && deviations.SkipMacaddressCheck(ondatra.DUT(t, "dut")) { + e.MacAddress = nil + } + // need to set mac address for bundle interface to nil + if iface.Ethernet.AggregateId != nil && deviations.SkipMacaddressCheck(ondatra.DUT(t, "dut")) { + iface.Ethernet.MacAddress = nil + continue + } + } + } + + if !*cannotConfigurePortSpeed { + for _, iface := range config.Interface { + if iface.GetEthernet() == nil { + continue + } + iface.GetEthernet().PortSpeed = oc.IfEthernet_ETHERNET_SPEED_UNSET + iface.GetEthernet().DuplexMode = oc.Ethernet_DuplexMode_UNSET + iface.GetEthernet().EnableFlowControl = nil } } @@ -887,7 +1041,14 @@ func (op rootOp) push(t testing.TB, dev gnmi.DeviceOrOpts, config *oc.Root, _ *p setEthernetFromBase(t, op.base, config) } fptest.WriteQuery(t, "RootOp", gnmi.OC().Config(), config) - gnmi.Replace(t, dev, gnmi.OC().Config(), config) + dut := ondatra.DUT(t, "dut") + if deviations.AddMissingBaseConfigViaCli(dut) { + if ondatra.DUT(t, "dut").Vendor() == ondatra.CISCO { + addMissingConfigForRootReplace(t, dev, config) + } + } else { + gnmi.Replace(t, dev, gnmi.OC().Config(), config) + } } // containerOp pushes config using replace of containers of lists directly under root in @@ -905,6 +1066,22 @@ func (op containerOp) push(t testing.TB, dev gnmi.DeviceOrOpts, config *oc.Root, fptest.WriteQuery(t, "ContainerOp", gnmi.OC().Config(), config) batch := &gnmi.SetBatch{} + if deviations.AddMissingBaseConfigViaCli(ondatra.DUT(t, "dut")) { + if ondatra.DUT(t, "dut").Vendor() == ondatra.CISCO { + supContainerConfig := addMissingConfigForContainerReplace(t, dev) + for port, data := range supContainerConfig { + gnmi.Update(t, ondatra.DUT(t, "dut"), gnmi.OC().Component(port).Config(), &oc.Component{ + Name: ygot.String(port), + }) + bmode := &oc.Component_Port_BreakoutMode{} + gp := bmode.GetOrCreateGroup(0) + gp.BreakoutSpeed = data.breakoutSpeed + gp.NumBreakouts = ygot.Uint8(*data.numPhysicalChannels + 1) + bmp := gnmi.OC().Component(port).Port().BreakoutMode() + gnmi.BatchReplace(batch, bmp.Config(), bmode) + } + } + } gnmi.BatchReplace(batch, interfacesQuery, &Interfaces{Interface: config.Interface}) gnmi.BatchReplace(batch, networkInstancesQuery, &NetworkInstances{NetworkInstance: config.NetworkInstance}) batch.Set(t, dev) @@ -1034,3 +1211,106 @@ type NetworkInstances struct { } func (*NetworkInstances) IsYANGGoStruct() {} + +func removeStatementsBetweenWords(inputStr, startWord, endWord string, opts ...*Options) string { + lines := strings.Split(inputStr, "\n") + result := []string{} + betweenWords := false + var start bool + for _, line := range lines { + if strings.HasPrefix(line, startWord) { + if len(opts) != 0 { + for _, opt := range opts { + for _, intf := range opt.interfaces { + if strings.Contains(line, intf) { + start = true + betweenWords = true + continue + } + } + } + } else { + start = true + betweenWords = true + continue + } + } + if strings.HasPrefix(line, endWord) { + betweenWords = false + if start == true { + start = false + continue + } + } + if !betweenWords { + result = append(result, line) + } + } + return strings.Join(result, "\n") +} + +func addMissingConfigForContainerReplace(t testing.TB, dev gnmi.DeviceOrOpts) map[string]breakout { + intfsState := gnmi.GetAll(t, dev, gnmi.OC().InterfaceAny().State()) + breakoutPortsMap := make(map[string]breakout) // which holds map of optic: {BreakoutSpeed:10, NumBreakouts:4} + port := make(map[string]uint8) + var trackspeed oc.E_IfEthernet_ETHERNET_SPEED + + for _, intf := range intfsState { + if intf.HardwarePort == nil || intf.PhysicalChannel == nil { + continue + } + hwp := strings.Split(intf.GetHardwarePort(), "Port")[1] + name := strings.Split(intf.GetName(), "GigE")[1] + channel := strconv.Itoa(int(intf.GetPhysicalChannel()[0])) + + if hwp+"/"+(channel) == name { + var speed oc.E_IfEthernet_ETHERNET_SPEED + + _, keyExists := breakoutPortsMap[intf.GetHardwarePort()] + if !keyExists && speed == oc.IfEthernet_ETHERNET_SPEED_UNSET { + if intf.GetEthernet().PortSpeed.String() == "SPEED_100GB" { + trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_100GB + } else if intf.GetEthernet().PortSpeed.String() == "SPEED_10GB" { + trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_10GB + } + } + + numChannels := make([]*uint8, len(intf.GetPhysicalChannel())) + truncated := uint8(intf.GetPhysicalChannel()[0]) + numChannels[0] = &truncated + + _, keyExists = port[intf.GetHardwarePort()] + if !keyExists { + breakoutPortsMap[intf.GetHardwarePort()] = breakout{numPhysicalChannels: numChannels[0], breakoutSpeed: trackspeed} + port[intf.GetHardwarePort()] = 0 + } + + if port[intf.GetHardwarePort()] < *numChannels[0] { + breakoutPortsMap[intf.GetHardwarePort()] = breakout{numPhysicalChannels: numChannels[0], breakoutSpeed: trackspeed} + port[intf.GetHardwarePort()] = *numChannels[0] + } + } + } + return breakoutPortsMap +} + +func addMissingConfigForRootReplace(t testing.TB, dev gnmi.DeviceOrOpts, config *oc.Root) { + batch := &gnmi.SetBatch{} + running := showRunningConfig(t, ondatra.DUT(t, "dut")) + //editing config while removing NI and interface since it will be part of another replace call + data := "hostname " + strings.Split(running, "hostname ")[1] + modifiedStr := strings.Replace(data, "\r\n", "\n", -1) + // remove interface config from the running configure + fileString := removeStatementsBetweenWords(modifiedStr, "interface ", "!", &Options{interfaces: []string{"HundredGigE", "FourHundredGigE", "TenGigE", "Bundle-Ether", "Loopback", "MgmtEth0", "FortyGigE", "PTP0/RP"}}) + // remove router static config from the running config + fileString = removeStatementsBetweenWords(fileString, "router static ", "!") + // need to explicitly remove configured NI "BLUE" since it is still present in running config and will overwrite config parameter which doesn't set it + fileString = removeStatementsBetweenWords(fileString, "vrf BLUE", "!") + cliPath, err := schemaless.NewConfig[string]("", "cli") + if err != nil { + t.Fatalf("Failed to create CLI ygnmi query: %v", err) + } + gnmi.BatchReplace(batch, cliPath, fileString) + gnmi.BatchReplace(batch, gnmi.OC().Config(), config) + batch.Set(t, dev) +} diff --git a/feature/system/gnmi/set/tests/gnmi_set_test/metadata.textproto b/feature/system/gnmi/set/tests/gnmi_set_test/metadata.textproto index d6df13be2ef..9fe05d0e4b2 100644 --- a/feature/system/gnmi/set/tests/gnmi_set_test/metadata.textproto +++ b/feature/system/gnmi/set/tests/gnmi_set_test/metadata.textproto @@ -10,7 +10,10 @@ platform_exceptions: { vendor: CISCO } deviations: { - ipv4_missing_enabled: true + skip_container_op: true + reorder_calls_for_vendor_compatibilty: true + add_missing_base_config_via_cli: true + skip_macaddress_check: true } } platform_exceptions: { diff --git a/internal/deviations/deviations.go b/internal/deviations/deviations.go index 23880679ca0..008a5ae0cc3 100644 --- a/internal/deviations/deviations.go +++ b/internal/deviations/deviations.go @@ -801,3 +801,27 @@ func PfRequireMatchDefaultRule(dut *ondatra.DUTDevice) bool { func MissingPortToOpticalChannelMapping(dut *ondatra.DUTDevice) bool { return lookupDUTDeviations(dut).GetMissingPortToOpticalChannelComponentMapping() } + +// SkipContainerOp returns true if gNMI container OP needs to be skipped. +// Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 +func SkipContainerOp(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetSkipContainerOp() +} + +// ReorderCallsForVendorCompatibilty returns true if call needs to be updated/added/deleted. +// Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 +func ReorderCallsForVendorCompatibilty(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetReorderCallsForVendorCompatibilty() +} + +// AddMissingBaseConfigViaCli returns true if missing base config needs to be added using CLI. +// Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 +func AddMissingBaseConfigViaCli(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetAddMissingBaseConfigViaCli() +} + +// SkipMacaddressCheck returns true if mac address for an interface via gNMI needs to be skipped. +// Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 +func SkipMacaddressCheck(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetSkipMacaddressCheck() +} diff --git a/proto/metadata.proto b/proto/metadata.proto index 6cfed151bbc..b8f53519fab 100644 --- a/proto/metadata.proto +++ b/proto/metadata.proto @@ -427,6 +427,18 @@ message Metadata { // Devices missing component tree mapping from hardware port // to optical channel. bool missing_port_to_optical_channel_component_mapping = 150; + // Skip gNMI container OP tc. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + bool skip_container_op = 151; + // Reorder calls for vendor compatibility. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + bool reorder_calls_for_vendor_compatibilty = 152; + // Add missing base config using cli. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + bool add_missing_base_config_via_cli = 153; + // skip_macaddress_check returns true if mac address for an interface via gNMI needs to be skipped. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + bool skip_macaddress_check = 154; // Reserved field numbers and identifiers. reserved 84, 9, 28, 20, 90, 97, 55, 89, 19; } diff --git a/proto/metadata_go_proto/metadata.pb.go b/proto/metadata_go_proto/metadata.pb.go index add2dc9e174..5efd1976ab2 100644 --- a/proto/metadata_go_proto/metadata.pb.go +++ b/proto/metadata_go_proto/metadata.pb.go @@ -14,8 +14,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.12 +// protoc-gen-go v1.30.0 +// protoc v3.19.3 // source: metadata.proto package metadata_go_proto @@ -698,6 +698,18 @@ type Metadata_Deviations struct { // Devices missing component tree mapping from hardware port // to optical channel. MissingPortToOpticalChannelComponentMapping bool `protobuf:"varint,150,opt,name=missing_port_to_optical_channel_component_mapping,json=missingPortToOpticalChannelComponentMapping,proto3" json:"missing_port_to_optical_channel_component_mapping,omitempty"` + // Skip gNMI container OP tc. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + SkipContainerOp bool `protobuf:"varint,151,opt,name=skip_container_op,json=skipContainerOp,proto3" json:"skip_container_op,omitempty"` + // Reorder calls for vendor compatibility. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + ReorderCallsForVendorCompatibilty bool `protobuf:"varint,152,opt,name=reorder_calls_for_vendor_compatibilty,json=reorderCallsForVendorCompatibilty,proto3" json:"reorder_calls_for_vendor_compatibilty,omitempty"` + // Add missing base config using cli. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + AddMissingBaseConfigViaCli bool `protobuf:"varint,153,opt,name=add_missing_base_config_via_cli,json=addMissingBaseConfigViaCli,proto3" json:"add_missing_base_config_via_cli,omitempty"` + // skip_macaddress_check returns true if mac address for an interface via gNMI needs to be skipped. + // Cisco: https://partnerissuetracker.corp.google.com/issues/322291556 + SkipMacaddressCheck bool `protobuf:"varint,154,opt,name=skip_macaddress_check,json=skipMacaddressCheck,proto3" json:"skip_macaddress_check,omitempty"` } func (x *Metadata_Deviations) Reset() { @@ -1670,6 +1682,34 @@ func (x *Metadata_Deviations) GetMissingPortToOpticalChannelComponentMapping() b return false } +func (x *Metadata_Deviations) GetSkipContainerOp() bool { + if x != nil { + return x.SkipContainerOp + } + return false +} + +func (x *Metadata_Deviations) GetReorderCallsForVendorCompatibilty() bool { + if x != nil { + return x.ReorderCallsForVendorCompatibilty + } + return false +} + +func (x *Metadata_Deviations) GetAddMissingBaseConfigViaCli() bool { + if x != nil { + return x.AddMissingBaseConfigViaCli + } + return false +} + +func (x *Metadata_Deviations) GetSkipMacaddressCheck() bool { + if x != nil { + return x.SkipMacaddressCheck + } + return false +} + type Metadata_PlatformExceptions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1733,7 +1773,7 @@ var file_metadata_proto_rawDesc = []byte{ 0x74, 0x69, 0x6e, 0x67, 0x1a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x72, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x62, 0x65, - 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfe, 0x53, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf9, 0x55, 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, @@ -1764,7 +1804,7 @@ var file_metadata_proto_rawDesc = []byte{ 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x67, 0x65, 0x78, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x0e, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, - 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x1a, 0xff, 0x4b, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, + 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x1a, 0xfa, 0x4d, 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, @@ -2369,43 +2409,59 @@ var file_metadata_proto_rawDesc = []byte{ 0x96, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x2b, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x72, 0x74, 0x54, 0x6f, 0x4f, 0x70, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x70, - 0x69, 0x6e, 0x67, 0x4a, 0x04, 0x08, 0x54, 0x10, 0x55, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, - 0x04, 0x08, 0x1c, 0x10, 0x1d, 0x4a, 0x04, 0x08, 0x14, 0x10, 0x15, 0x4a, 0x04, 0x08, 0x5a, 0x10, - 0x5b, 0x4a, 0x04, 0x08, 0x61, 0x10, 0x62, 0x4a, 0x04, 0x08, 0x37, 0x10, 0x38, 0x4a, 0x04, 0x08, - 0x59, 0x10, 0x5a, 0x4a, 0x04, 0x08, 0x13, 0x10, 0x14, 0x1a, 0xa0, 0x01, 0x0a, 0x12, 0x50, 0x6c, - 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x41, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, + 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, 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, 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, + 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 (