blob: 835d378644f62d70e7f0a1c307dde3eb8fd99ad9 [file] [log] [blame]
/*
* Copyright 2020-2023 Open Networking Foundation (ONF) and the ONF Contributors
*
* 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 core Common Logger initialization
package test
import (
"bufio"
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"time"
ca "github.com/opencord/voltha-protos/v5/go/core_adapter"
"math/rand"
"github.com/opencord/voltha-go/rw_core/config"
cm "github.com/opencord/voltha-go/rw_core/mocks"
"github.com/opencord/voltha-lib-go/v7/pkg/db/kvstore"
"github.com/opencord/voltha-lib-go/v7/pkg/log"
mock_etcd "github.com/opencord/voltha-lib-go/v7/pkg/mocks/etcd"
"github.com/opencord/voltha-lib-go/v7/pkg/version"
"github.com/opencord/voltha-protos/v5/go/voltha"
"github.com/phayes/freeport"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
const (
OltAdapter = iota
OnuAdapter
)
type AdapterInfo struct {
TotalReplica int32
Vendor string
DeviceType string
ChildDeviceType string
ChildVendor string
}
// prettyPrintDeviceUpdateLog is used just for debugging and exploring the Core logs
func prettyPrintDeviceUpdateLog(inputFile string, deviceID string) {
file, err := os.Open(filepath.Clean(inputFile))
if err != nil {
logger.Fatal(context.Background(), err)
}
defer func() {
if err := file.Close(); err != nil {
logger.Errorw(context.Background(), "file-close-error", log.Fields{"error": err})
}
}()
var logEntry struct {
Level string `json:"level"`
Ts string `json:"ts"`
Caller string `json:"caller"`
Msg string `json:"msg"`
RPC string `json:"rpc"`
DeviceID string `json:"device-id"`
RequestedBy string `json:"requested-by"`
StateChange string `json:"state-change"`
Status string `json:"status"`
Description string `json:"description"`
}
scanner := bufio.NewScanner(file)
fmt.Println("Timestamp\t\t\tDeviceId\t\t\t\tStatus\t\t\tRPC\t\t\tRequestedBy\t\t\tStateChange\t\t\tDescription")
for scanner.Scan() {
input := scanner.Text()
// Look for device update logs only
if !strings.Contains(input, "device-operation") || !strings.Contains(input, "requested-by") {
continue
}
// Check if deviceID is required
if deviceID != "" {
if !strings.Contains(input, deviceID) {
continue
}
}
if err := json.Unmarshal([]byte(input), &logEntry); err != nil {
logger.Fatal(context.Background(), err)
}
fmt.Println(
fmt.Sprintf(
"%s\t%s\t%s\t%-30.30q\t%-16.16s\t%-25.25s\t%s",
logEntry.Ts,
logEntry.DeviceID,
logEntry.Status,
logEntry.RPC,
logEntry.RequestedBy,
logEntry.StateChange,
logEntry.Description))
}
}
func omciLog(inputFile string, deviceID string) {
file, err := os.Open(filepath.Clean(inputFile))
if err != nil {
logger.Fatal(context.Background(), err)
}
defer func() {
if err := file.Close(); err != nil {
logger.Errorw(context.Background(), "file-close-error", log.Fields{"error": err})
}
}()
var logEntry struct {
Level string `json:"level"`
Ts string `json:"ts"`
Caller string `json:"caller"`
Msg string `json:"msg"`
InstanceID string `json:"instanceId"`
ChildDeviceID string `json:"child-device-id"`
OmciMsg string `json:"omciMsg"`
IntfID string `json:"intf-id"`
OnuID string `json:"onu-id"`
OmciTrns int `json:"omciTransactionID"`
}
scanner := bufio.NewScanner(file)
uniqueTnsIDs := map[int]int{}
for scanner.Scan() {
input := scanner.Text()
// Look for device update logs only
if !strings.Contains(input, "sent-omci-msg") {
continue
}
// Check if deviceID is required
if deviceID != "" {
if !strings.Contains(input, deviceID) {
continue
}
}
if err := json.Unmarshal([]byte(input), &logEntry); err != nil {
logger.Fatal(context.Background(), err)
}
uniqueTnsIDs[logEntry.OmciTrns]++
}
repeatedTrnsID := []int{}
for k, v := range uniqueTnsIDs {
if v != 1 {
repeatedTrnsID = append(repeatedTrnsID, k)
}
}
fmt.Println("RepeatedIDs", repeatedTrnsID, "TransID:", len(uniqueTnsIDs))
}
// CreateMockAdapter creates mock OLT and ONU adapters - this will automatically the grpc service hosted by that
// adapter
func CreateMockAdapter(
ctx context.Context,
adapterType int,
coreEndpoint string,
deviceType string,
vendor string,
childDeviceType string,
childVendor string,
) (interface{}, error) {
var adpt interface{}
switch adapterType {
case OltAdapter:
adpt = cm.NewOLTAdapter(ctx, coreEndpoint, deviceType, vendor, childDeviceType, childVendor)
case OnuAdapter:
adpt = cm.NewONUAdapter(ctx, coreEndpoint, deviceType, vendor)
default:
logger.Fatalf(ctx, "invalid-adapter-type-%d", adapterType)
}
return adpt, nil
}
// CreateAndRegisterAdapters creates mock ONU and OLT adapters and registers them to rw-core
func CreateAndRegisterAdapters(
ctx context.Context,
t *testing.T,
oltAdapters map[string]*AdapterInfo,
onuAdapters map[string]*AdapterInfo,
coreEndpoint string,
) (map[string][]*cm.OLTAdapter, map[string][]*cm.ONUAdapter) {
// Setup the ONU adapter first in this unit test environment. This makes it easier to test whether the
// Core is ready to send grpc requests to the adapters. The unit test uses grpc to communicate with the
// Core and as such it does not have inside knowledge when the adapters are ready.
// Setup the ONU Adapters
onuAdaptersMap := make(map[string][]*cm.ONUAdapter)
for adapterType, adapterInfo := range onuAdapters {
for replica := int32(1); replica <= adapterInfo.TotalReplica; replica++ {
adpt, err := CreateMockAdapter(ctx, OnuAdapter, coreEndpoint, adapterInfo.DeviceType, adapterInfo.Vendor, adapterInfo.ChildDeviceType, adapterInfo.ChildVendor)
assert.Nil(t, err)
onuAdapter, ok := adpt.(*cm.ONUAdapter)
assert.True(t, ok)
assert.NotNil(t, onuAdapter)
// Register the adapter
adapterID := fmt.Sprintf("%s-%d", adapterType, replica)
adapterToRegister := &voltha.Adapter{
Id: adapterID,
Vendor: adapterInfo.Vendor,
Version: version.VersionInfo.Version,
Type: adapterType,
CurrentReplica: replica,
TotalReplicas: adapterInfo.TotalReplica,
Endpoint: onuAdapter.GetEndPoint(),
}
types := []*voltha.DeviceType{{Id: adapterInfo.DeviceType, AdapterType: adapterType, AcceptsAddRemoveFlowUpdates: true}}
deviceTypes := &voltha.DeviceTypes{Items: types}
coreClient, err := onuAdapter.GetCoreClient()
assert.Nil(t, err)
assert.NotNil(t, coreClient)
if _, err := coreClient.RegisterAdapter(ctx, &ca.AdapterRegistration{
Adapter: adapterToRegister,
DTypes: deviceTypes}); err != nil {
logger.Errorw(ctx, "failed-to-register-adapter", log.Fields{"error": err, "adapter": adapterToRegister.Id})
assert.NotNil(t, err)
}
if _, ok := onuAdaptersMap[adapterType]; !ok {
onuAdaptersMap[adapterType] = []*cm.ONUAdapter{}
}
onuAdaptersMap[adapterType] = append(onuAdaptersMap[adapterType], onuAdapter)
}
}
// Setup the OLT Adapters
oltAdaptersMap := make(map[string][]*cm.OLTAdapter)
for adapterType, adapterInfo := range oltAdapters {
for replica := int32(1); replica <= adapterInfo.TotalReplica; replica++ {
adpt, err := CreateMockAdapter(ctx, OltAdapter, coreEndpoint, adapterInfo.DeviceType, adapterInfo.Vendor, adapterInfo.ChildDeviceType, adapterInfo.ChildVendor)
assert.Nil(t, err)
oltAdapter, ok := adpt.(*cm.OLTAdapter)
assert.True(t, ok)
assert.NotNil(t, oltAdapter)
// Register the adapter
adapterID := fmt.Sprintf("%s-%d", adapterType, replica)
adapterToRegister := &voltha.Adapter{
Id: adapterID,
Vendor: adapterInfo.Vendor,
Version: version.VersionInfo.Version,
Type: adapterType,
CurrentReplica: replica,
TotalReplicas: adapterInfo.TotalReplica,
Endpoint: oltAdapter.GetEndPoint(),
}
types := []*voltha.DeviceType{{Id: adapterInfo.DeviceType, AdapterType: adapterType, AcceptsAddRemoveFlowUpdates: true}}
deviceTypes := &voltha.DeviceTypes{Items: types}
coreClient, err := oltAdapter.GetCoreClient()
assert.Nil(t, err)
assert.NotNil(t, coreClient)
if _, err := coreClient.RegisterAdapter(ctx, &ca.AdapterRegistration{
Adapter: adapterToRegister,
DTypes: deviceTypes}); err != nil {
logger.Errorw(ctx, "failed-to-register-adapter", log.Fields{"error": err, "adapter": adapterToRegister.Id})
assert.NotNil(t, err)
}
if _, ok := oltAdaptersMap[adapterType]; !ok {
oltAdaptersMap[adapterType] = []*cm.OLTAdapter{}
}
oltAdaptersMap[adapterType] = append(oltAdaptersMap[adapterType], oltAdapter)
}
}
return oltAdaptersMap, onuAdaptersMap
}
// StartEmbeddedEtcdServer creates and starts an Embedded etcd server locally.
func StartEmbeddedEtcdServer(ctx context.Context, configName, storageDir, logLevel string) (*mock_etcd.EtcdServer, int, error) {
kvClientPort, err := freeport.GetFreePort()
if err != nil {
return nil, 0, err
}
peerPort, err := freeport.GetFreePort()
if err != nil {
return nil, 0, err
}
etcdServer := mock_etcd.StartEtcdServer(ctx, mock_etcd.MKConfig(ctx, configName, kvClientPort, peerPort, storageDir, logLevel))
if etcdServer == nil {
return nil, 0, status.Error(codes.Internal, "Embedded server failed to start")
}
return etcdServer, kvClientPort, nil
}
// StopEmbeddedEtcdServer stops the embedded etcd server
func StopEmbeddedEtcdServer(ctx context.Context, server *mock_etcd.EtcdServer) {
if server != nil {
server.Stop(ctx)
}
}
// SetupKVClient creates a new etcd client
func SetupKVClient(ctx context.Context, cf *config.RWCoreFlags, coreInstanceID string) kvstore.Client {
client, err := kvstore.NewEtcdClient(ctx, cf.KVStoreAddress, cf.KVStoreTimeout, log.FatalLevel)
if err != nil {
panic("no kv client")
}
return client
}
// getRandomMacAddress returns a random mac address
func getRandomMacAddress() string {
rand.Seed(time.Now().UnixNano() / int64(rand.Intn(255)+1))
return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x",
rand.Intn(255),
rand.Intn(255),
rand.Intn(255),
rand.Intn(255),
rand.Intn(255),
rand.Intn(255),
)
}