Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding test files for 400G ZR++ firmware version test and input output power test #3701

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
750341e
add deviation for ETH channal rate class non-supported. Arista device…
jianchen-g Dec 16, 2024
1361494
bug fix for channel definition in the interface.go file
jianchen-g Dec 17, 2024
dfd5acd
Revert "add deviation for ETH channal rate class non-supported. Arist…
jianchen-g Dec 17, 2024
d92e938
Arista device is not supporting ETH channel rate class yet. Adding th…
jianchen-g Dec 17, 2024
9f297f8
adding a new PMD typeof PMD_400GBASE_ZR_PLUS for the functional testing
jianchen-g Dec 18, 2024
7ed0c4f
adding new testbed topology for new optics PMD 400G ZR plus
jianchen-g Dec 23, 2024
2c4b489
Update interface.go
self-maurya Jan 6, 2025
bdb5f2b
updated metadata.pb.go file
self-maurya Jan 6, 2025
374c8f2
Merge branch 'main' into jchenjian-g/OC_github
yiwenhu-g Jan 6, 2025
a4cd890
Arista device is not supporting ETH channel rate class yet. Adding th…
jianchen-g Dec 17, 2024
88bc7bd
adding newly introduced deviation of unsupported rate class to the tests
jianchen-g Jan 7, 2025
8588449
removing folder of tmp
jianchen-g Jan 7, 2025
1a7256f
removing empty file called values
jianchen-g Jan 7, 2025
cc32867
add new line in the file topologies/dut_400zr_plus.testbed; merge main
jianchen-g Jan 15, 2025
06c863b
add deviation for ETH channal rate class non-supported. Arista device…
jianchen-g Dec 16, 2024
18a226d
bug fix for channel definition in the interface.go file
jianchen-g Dec 17, 2024
dd8aec1
Revert "add deviation for ETH channal rate class non-supported. Arist…
jianchen-g Dec 17, 2024
278644b
Arista device is not supporting ETH channel rate class yet. Adding th…
jianchen-g Dec 17, 2024
36161da
adding a new PMD typeof PMD_400GBASE_ZR_PLUS for the functional testing
jianchen-g Dec 18, 2024
58f7994
adding new testbed topology for new optics PMD 400G ZR plus
jianchen-g Dec 23, 2024
16dfe94
Update interface.go
self-maurya Jan 6, 2025
94ee556
updated metadata.pb.go file
self-maurya Jan 6, 2025
1d563c6
fixed metadata.pb.go
self-maurya Jan 16, 2025
974ea1b
pulling changes
jianchen-g Jan 16, 2025
99e79ce
Merge branch 'main' into jchenjian-g/OC_github
jianchen-g Jan 17, 2025
569e6a5
adding test files for 400G ZR++ firmware version test and input outpu…
jianchen-g Jan 18, 2025
27925c2
Merge branch 'main' into jchenjian-g/OC_github
jianchen-g Jan 22, 2025
d396656
updating the ondatra release version
jianchen-g Jan 22, 2025
230a961
updating the go.sum
jianchen-g Jan 22, 2025
969224f
run go mode tidy
jianchen-g Jan 22, 2025
997b732
bug fix
jianchen-g Jan 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# TRANSCEIVER-3: Telemetry: 400ZR_PLUS Optics firmware version streaming
# TRANSCEIVER-3 (400ZR_PLUS): Telemetry: 400ZR_PLUS Optics firmware version streaming

## Summary

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto
# proto-message: Metadata

uuid: "aa5d6c79-e7e8-4783-bb30-e1331a4e956f"
plan_id: "TRANSCEIVER-3 (400ZR_PLUS)"
description: "Telemetry: 400ZR_PLUS Optics firmware version streaming"
testbed: TESTBED_DUT_400ZR_PLUS
platform_exceptions: {
platform: {
vendor: ARISTA
}
deviations: {
interface_enabled: true
default_network_instance: "default"
missing_port_to_optical_channel_component_mapping: true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// 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 zrp_firmware_version_test

import (
"flag"
"reflect"
"testing"
"time"

"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"
)

const (
targetOutputPower = -3
frequency = 193100000
timeout = 10 * time.Minute
)

var (
operationalModeFlag = flag.Int("operational_mode", 5, "vendor-specific operational-mode for the channel")
operationalMode uint16
)

func TestMain(m *testing.M) {
fptest.RunTests(m)
}

// Topology: dut:port1 <--> port2:dut

func verifyFirmwareVersionValue(t *testing.T, dut1 *ondatra.DUTDevice, pStream *samplestream.SampleStream[string]) {
firmwareVersionSample := pStream.Next()
if firmwareVersionSample == nil {
t.Fatalf("Firmware telemetry %v was not streamed in the most recent subscription interval", firmwareVersionSample)
}
firmwareVersionVal, ok := firmwareVersionSample.Val()
if !ok {
t.Fatalf("Firmware version %q telemetry is not present", firmwareVersionSample)
}
// Check firmware version return value of correct type
if reflect.TypeOf(firmwareVersionVal).Kind() != reflect.String {
t.Fatalf("Return value is not type string")
}
t.Logf("%v", firmwareVersionVal)
}

func TestZRPFirmwareVersionState(t *testing.T) {
if operationalModeFlag != nil {
operationalMode = uint16(*operationalModeFlag)
} else {
t.Fatalf("Please specify the vendor-specific operational-mode flag")
}
dut1 := ondatra.DUT(t, "dut")
dp1 := dut1.Port(t, "port1")
dp2 := dut1.Port(t, "port2")
t.Logf("dut1: %v", dut1)
t.Logf("dut1 dp1 name: %v", dp1.Name())
och1 := components.OpticalChannelComponentFromPort(t, dut1, dp1)
och2 := components.OpticalChannelComponentFromPort(t, dut1, dp2)
cfgplugins.ConfigOpticalChannel(t, dut1, och1, frequency, targetOutputPower, operationalMode)
cfgplugins.ConfigOpticalChannel(t, dut1, och2, frequency, targetOutputPower, operationalMode)
gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP)
transceiverName := gnmi.Get(t, dut1, gnmi.OC().Interface(dp1.Name()).Transceiver().State())
// Check if TRANSCEIVER is of type 400ZR_PLUS
if dp1.PMD() != ondatra.PMD400GBASEZRP {
t.Fatalf("%s Transceiver is not 400ZR_PLUS its of type: %v", transceiverName, dp1.PMD())
}
component1 := gnmi.OC().Component(transceiverName)

p1Stream := samplestream.New(t, dut1, component1.FirmwareVersion().State(), 10*time.Second)

verifyFirmwareVersionValue(t, dut1, p1Stream)

p1Stream.Close()
}

func TestZRPFirmwareVersionStateInterfaceFlap(t *testing.T) {
if operationalModeFlag != nil {
operationalMode = uint16(*operationalModeFlag)
} else {
t.Fatalf("Please specify the vendor-specific operational-mode flag")
}
dut1 := ondatra.DUT(t, "dut")
dp1 := dut1.Port(t, "port1")
dp2 := dut1.Port(t, "port2")
t.Logf("dut1: %v", dut1)
t.Logf("dut1 dp1 name: %v", dp1.Name())
och1 := components.OpticalChannelComponentFromPort(t, dut1, dp1)
och2 := components.OpticalChannelComponentFromPort(t, dut1, dp2)
cfgplugins.ConfigOpticalChannel(t, dut1, och1, frequency, targetOutputPower, operationalMode)
cfgplugins.ConfigOpticalChannel(t, dut1, och2, frequency, targetOutputPower, operationalMode)
gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP)
transceiverName := gnmi.Get(t, dut1, gnmi.OC().Interface(dp1.Name()).Transceiver().State())
// Check if TRANSCEIVER is of type 400ZR_PLUS
if dp1.PMD() != ondatra.PMD400GBASEZRP {
t.Fatalf("%s Transceiver is not 400ZR_PLUS its of type: %v", transceiverName, dp1.PMD())
}
// Disable interface
// TODO: jchenjian - Add support for module reset (not supported in current implementation)
cfgplugins.ToggleInterface(t, dut1, dp1.Name(), false)
component1 := gnmi.OC().Component(transceiverName)

p1Stream := samplestream.New(t, dut1, component1.FirmwareVersion().State(), 10*time.Second)

// Wait 60 sec cooling-off period
gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_DOWN)
t.Logf("Interfaces are down: %v", dp1.Name())
verifyFirmwareVersionValue(t, dut1, p1Stream)

// Enable interface
cfgplugins.ToggleInterface(t, dut1, dp1.Name(), true)
gnmi.Await(t, dut1, gnmi.OC().Interface(dp1.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP)
t.Logf("Interfaces are up: %v", dp1.Name())
verifyFirmwareVersionValue(t, dut1, p1Stream)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# TRANSCEIVER-4: Telemetry: 400ZR_PLUS RX input and TX output power telemetry values streaming.
# TRANSCEIVER-4 (400ZR_PLUS): Telemetry: 400ZR_PLUS RX input and TX output power telemetry values streaming.

## Summary

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto
# proto-message: Metadata

uuid: "8146c32a-d44a-4dac-b5a4-72306d8273d0"
plan_id: "TRANSCEIVER-4 (400ZR_PLUS)"
description: "Telemetry: 400ZR_PLUS RX input and TX output power telemetry values streaming."
testbed: TESTBED_DUT_400ZR_PLUS
platform_exceptions: {
platform: {
vendor: ARISTA
}
deviations: {
default_network_instance: "default"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package zrp_input_output_power_test

import (
"flag"
"reflect"
"testing"
"time"

"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/ygnmi/ygnmi"
)

const (
samplingInterval = 10 * time.Second
inactiveOCHRxPower = -30.0
inactiveOCHTxPower = -30.0
inactiveTransceiverRxPower = -20.0
rxPowerReadingError = 2
txPowerReadingError = 0.5
timeout = 10 * time.Minute
)

var (
frequencies = []uint64{191400000, 196100000}
targetOpticalPowers = []float64{0, -7}
operationalModeFlag = flag.Int("operational_mode", 5, "vendor-specific operational-mode for the channel")
operationalMode uint16
)

func TestMain(m *testing.M) {
fptest.RunTests(m)
}

func TestOpticalPower(t *testing.T) {
dut := ondatra.DUT(t, "dut")
if operationalModeFlag != nil {
operationalMode = uint16(*operationalModeFlag)
} else {
t.Fatalf("Please specify the vendor-specific operational-mode flag")
}
fptest.ConfigureDefaultNetworkInstance(t, dut)

var (
trs = make(map[string]string)
ochs = make(map[string]string)
)

for _, p := range dut.Ports() {
// Check the port PMD is 400ZR_PLUS.
if p.PMD() != ondatra.PMD400GBASEZRP {
t.Fatalf("%s PMD is %v, not 400ZR_PLUS", p.Name(), p.PMD())
}

// 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())
// ochs[p.Name()] = components.OpticalChannelComponentFromPort(t, dut, p)
}

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, operationalMode)
}

// 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()
}

// Enable interface.
for _, p := range dut.Ports() {
cfgplugins.ToggleInterface(t, dut, p.Name(), true)
}

// 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.
validateAllSampleStreams(t, dut, true, interfaceStreams, ochStreams, trStreams, targetOpticalPower)

// Disable interface.
for _, p := range dut.Ports() {
cfgplugins.ToggleInterface(t, dut, p.Name(), false)
}

// 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.

validateAllSampleStreams(t, dut, false, interfaceStreams, ochStreams, trStreams, targetOpticalPower)

// Re-enable transceivers.
for _, p := range dut.Ports() {
cfgplugins.ToggleInterface(t, dut, p.Name(), true)
}

// 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.

validateAllSampleStreams(t, dut, true, interfaceStreams, ochStreams, trStreams, targetOpticalPower)
}
}
}

// 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)
}
}
}
}
}

// 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
}
interfaceValue, ok := interfaceData.Val()
if !ok {
t.Errorf("Channel data is empty for port %v.", portName)
return oc.Interface_OperStatus_UNSET
}
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
}
ochValue, ok := ochData.Val()
if !ok {
t.Errorf("Terminal Device data is empty for port %v.", portName)
return oc.Interface_OperStatus_UNSET
}
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)
}
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)
}
transceiverValue, ok := transceiverData.Val()
if !ok {
t.Errorf("Transceiver data is empty for port %v.", portName)
return oc.Interface_OperStatus_UNSET
}
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
}

// 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:
// All power values should be float64.
for _, ele := range []any{instant, min, max, avg} {
if reflect.TypeOf(ele).Kind() != reflect.Float64 {
t.Fatalf("Value %v is not type float64", ele)
}
}
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
} else {
t.Logf("Valid %v sample when %v is UP --> min : %v, max : %v, avg : %v, instant : %v", pm, portName, min, max, avg, instant)
}
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
} else {
t.Logf("Valid %v sample when %v is DOWN --> min : %v, max : %v, avg : %v, instant : %v", pm, portName, 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)
}
Loading
Loading