Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 25 additions & 11 deletions cmd/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ package main

import (
"fmt"
"net"
"os"
"sync"
"testing"

v2 "github.com/cloudevents/sdk-go/v2"
"k8s.io/utils/pointer"
"os"

"github.com/redhat-cne/cloud-event-proxy/pkg/common"
ceEvent "github.com/redhat-cne/sdk-go/pkg/event"
"github.com/redhat-cne/sdk-go/pkg/event/ptp"
"github.com/redhat-cne/sdk-go/pkg/types"

"sync"
"testing"

"github.com/redhat-cne/sdk-go/pkg/channel"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
Expand All @@ -40,22 +41,35 @@ func storeCleanUp() {
}
}

func freeTCPPort() (int, error) {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return 0, err
}
port := l.Addr().(*net.TCPAddr).Port
_ = l.Close()
return port, nil
}

func TestSidecar_Main(t *testing.T) {
apiPort = 8990
port, err := freeTCPPort()
assert.NoError(t, err)
apiPort = port

// Create a unique temporary directory for this test run to avoid conflicts
tempDir, err := os.MkdirTemp("", "sidecar-test-*")
// Unique store under STORE_PATH (if set) so parallel packages do not share pub.json.
base := os.TempDir()
if sPath, ok := os.LookupEnv("STORE_PATH"); ok && sPath != "" {
base = sPath
}
tempDir, err := os.MkdirTemp(base, "sidecar-test-*")
assert.NoError(t, err)
defer func() {
storeCleanUp()
os.RemoveAll(tempDir) // Clean up temp directory
}()

wg := &sync.WaitGroup{}
var storePath = tempDir
if sPath, ok := os.LookupEnv("STORE_PATH"); ok && sPath != "" {
storePath = sPath
}
storePath := tempDir
scConfig = &common.SCConfiguration{
EventInCh: make(chan *channel.DataChan, channelBufferSize),
EventOutCh: make(chan *channel.DataChan, channelBufferSize),
Expand Down
39 changes: 33 additions & 6 deletions pkg/plugins/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ package plugins

import (
"fmt"
"net"
"os"
"sync"
"testing"

"os"

"github.com/redhat-cne/cloud-event-proxy/pkg/common"
"github.com/redhat-cne/sdk-go/pkg/channel"
v1pubsub "github.com/redhat-cne/sdk-go/v1/pubsub"
Expand All @@ -38,17 +38,41 @@ var (
channelBufferSize int = 10
)

func init() {
var storePath = "."
func freeTCPPort() (int, error) {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return 0, err
}
port := l.Addr().(*net.TCPAddr).Port
_ = l.Close()
return port, nil
}

func uniqueStorePath(prefix string) (string, error) {
base := os.TempDir()
if sPath, ok := os.LookupEnv("STORE_PATH"); ok && sPath != "" {
storePath = sPath
base = sPath
}
return os.MkdirTemp(base, prefix)
}

func TestMain(m *testing.M) {
storePath, err := uniqueStorePath("plugins-handler-*")
if err != nil {
fmt.Fprintf(os.Stderr, "unique store path: %v\n", err)
os.Exit(1)
}
apiPort, err := freeTCPPort()
if err != nil {
fmt.Fprintf(os.Stderr, "free tcp port: %v\n", err)
os.Exit(1)
}

scConfig = &common.SCConfiguration{
EventInCh: make(chan *channel.DataChan, channelBufferSize),
EventOutCh: make(chan *channel.DataChan, channelBufferSize),
CloseCh: make(chan struct{}),
APIPort: 8989,
APIPort: apiPort,
APIPath: "/api/cne/",
PubSubAPI: v1pubsub.GetAPIInstance("../.."),
SubscriberAPI: subscriberApi.GetAPIInstance(storePath),
Expand All @@ -62,6 +86,9 @@ func init() {
Err: nil,
},
}
code := m.Run()
_ = os.RemoveAll(storePath)
os.Exit(code)
}

func TestLoadPTPPlugin(t *testing.T) {
Expand Down
17 changes: 17 additions & 0 deletions plugins/ptp_operator/metrics/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,23 @@ func (p *PTPEventManager) GetProfileType(name string) ptp4lconf.PtpProfileType {
return ptp4lconf.NONE
}

// GetProfileTypeByConfigName returns the cached ProfileType for a ptp4l config
// name (e.g. "ptp4l.0.config"). Returns NONE if the config is unknown.
func (p *PTPEventManager) GetProfileTypeByConfigName(configName types.ConfigName) ptp4lconf.PtpProfileType {
p.lock.RLock()
defer p.lock.RUnlock()
cfg, ok := p.Ptp4lConfigInterfaces[configName]
if !ok || cfg == nil {
return ptp4lconf.NONE
}
if cfg.ProfileType != ptp4lconf.NONE {
return cfg.ProfileType
}
// ProfileType may still be NONE if the config was registered before
// TBCProfiles was populated; resolve from the profile name.
return p.GetProfileType(cfg.Profile)
}

// RefreshProfileTypes re-evaluates the cached ProfileType on all registered
// PTP4lConfigs from the current TBCProfiles list. Call this after
// UpdatePTPSetting() has populated TBCProfiles so that configs registered
Expand Down
29 changes: 19 additions & 10 deletions plugins/ptp_operator/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,18 +284,27 @@ func (p *PTPEventManager) ExtractMetrics(msg string) {
switch interfaceName { //note: this is not interface type
case ClockRealTime: // CLOCK_REALTIME is active slave interface
// O-RAN O-Cloud API v04.00 Table 37: E3 = worst_of(phc2sys_state, E1_state).
// phc2sys is the single publisher — but if the upstream PHC is not
// traceable (E1 != LOCKED), E3 cannot be LOCKED even when
// phc2sys offset is within threshold.
// GetMainClockName() returns the correct key per profile type:
// T-BC → "T-BC", T-GM → "GM", OC/BC → "master"
// phc2sys publishes E3, but E3 cannot be LOCKED unless E1 is also LOCKED.

// E1 lookup key: T-BC → "T-BC"; T-GM/OC/BC → "master".
// T-BC/T-GM set E1 from delayed X-STATUS lines (T-BC-STATUS / T-GM-STATUS),
// so missing E1 defaults to FREERUN. OC/BC set E1 from ptp4l, so leave it unset.
var e1State ptp.SyncState
mainClockKey := ptpStats.GetMainClockName()
if e1Stat, ok := ptpStats[mainClockKey]; ok {
e1State := e1Stat.LastSyncState()
if e1State == ptp.FREERUN || e1State == ptp.HOLDOVER {
syncState = OverallState(syncState, e1State)
}
switch p.GetProfileTypeByConfigName(types.ConfigName(configName)) {
case ptp4lconf.TBC:
mainClockKey = types.IFace(stats.TBCMainClockName)
e1State = ptp.FREERUN
case ptp4lconf.TGM:
e1State = ptp.FREERUN
}
if e1Stat, ok := ptpStats[mainClockKey]; ok && e1Stat.LastSyncState() != "" {
e1State = e1Stat.LastSyncState()
}
if e1State != "" {
syncState = OverallState(syncState, e1State)
}

// for HA we can not rely on master ;since there will be 2 or more leaders; this condition will be skipped
// ptpStats clock realtime has its own stats objects
if r, ok := ptpStats[master]; ok && r.Role() == types.SLAVE { // publish event only if the master role is active
Expand Down
Loading
Loading