From ddb15d885614f2101a7e2d6875206991f34d6813 Mon Sep 17 00:00:00 2001 From: rezachit <39000986+rezachit@users.noreply.github.com> Date: Tue, 16 Jul 2024 09:49:53 -0700 Subject: [PATCH] Input output power (#3302) * modify power range, update logical chanel config and remove min/max/avg check * Modify README file * Modify README * adding library to properly configure logical channels * update in/out power to align with other zr tests * update in/out power README * update in/out power README one more time * update in/out power README one more time * update in/out power README one more time * update in/out power README one more time * update in/out power README one more time * update in/out power README one more time --- .../zr_input_output_power_test/README.md | 56 +- .../metadata.textproto | 10 +- .../zr_input_output_power_test.go | 502 +++++------------- 3 files changed, 192 insertions(+), 376 deletions(-) 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) }