[VOL-1800] Implement Performance configuration in Voltha Core.
This is a port of the exisiting voltha 1.x funtionality into
the Voltha 2.0 Core.
Change-Id: I87bf8836fd392c1c7f4a2c45e85323d1cbe0079f
diff --git a/tests/core/performance_metrics_test.go b/tests/core/performance_metrics_test.go
new file mode 100644
index 0000000..9e074f5
--- /dev/null
+++ b/tests/core/performance_metrics_test.go
@@ -0,0 +1,313 @@
+// +build integration
+
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * 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
+
+import (
+ "context"
+ "fmt"
+ "github.com/google/uuid"
+ "github.com/opencord/voltha-go/common/log"
+ tu "github.com/opencord/voltha-go/tests/utils"
+ "github.com/opencord/voltha-protos/go/common"
+ "github.com/opencord/voltha-protos/go/voltha"
+ "github.com/stretchr/testify/assert"
+ "google.golang.org/grpc/metadata"
+ "math"
+ "os"
+ "testing"
+ "time"
+)
+
+var stub voltha.VolthaServiceClient
+var volthaSerialNumberKey string
+
+/*
+ This local "integration" test uses one RW-Core, one simulated_olt and one simulated_onu adapter to test performance
+metrics, in a development environment. It uses docker-compose to set up the local environment. However, it can
+easily be extended to run in k8s environment.
+
+The compose files used are located under %GOPATH/src/github.com/opencord/voltha-go/compose. If the GOPATH is not set
+then you can specify the location of the compose files by using COMPOSE_PATH to set the compose files location.
+
+To run this test: DOCKER_HOST_IP=<local IP> go test -v
+
+*/
+
+var allDevices map[string]*voltha.Device
+var allLogicalDevices map[string]*voltha.LogicalDevice
+
+var composePath string
+
+const (
+ GRPC_PORT = 50057
+ NUM_OLTS = 1
+ NUM_ONUS_PER_OLT = 4 // This should coincide with the number of onus per olt in adapters-simulated.yml file
+)
+
+var parentPmNames = []string{
+ "tx_64_pkts",
+ "tx_65_127_pkts",
+ "tx_128_255_pkts",
+ "tx_256_511_pkts",
+ "tx_512_1023_pkts",
+ "tx_1024_1518_pkts",
+ "tx_1519_9k_pkts",
+ "rx_64_pkts",
+ "rx_65_127_pkts",
+ "rx_128_255_pkts",
+ "rx_256_511_pkts",
+ "rx_512_1023_pkts",
+ "rx_1024_1518_pkts",
+ "rx_1519_9k_pkts",
+}
+
+var childPmNames = []string{
+ "tx_64_pkts",
+ "tx_65_127_pkts",
+ "tx_128_255_pkts",
+ "tx_1024_1518_pkts",
+ "tx_1519_9k_pkts",
+ "rx_64_pkts",
+ "rx_64_pkts",
+ "rx_65_127_pkts",
+ "rx_128_255_pkts",
+ "rx_1024_1518_pkts",
+ "rx_1519_9k_pkts",
+}
+
+func setup() {
+ var err error
+
+ if _, err = log.AddPackage(log.JSON, log.WarnLevel, log.Fields{"instanceId": "testing"}); err != nil {
+ log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
+ }
+ log.UpdateAllLoggers(log.Fields{"instanceId": "testing"})
+ log.SetAllLogLevel(log.ErrorLevel)
+
+ volthaSerialNumberKey = "voltha_serial_number"
+ allDevices = make(map[string]*voltha.Device)
+ allLogicalDevices = make(map[string]*voltha.LogicalDevice)
+
+ grpcHostIP := os.Getenv("DOCKER_HOST_IP")
+ goPath := os.Getenv("GOPATH")
+ if goPath != "" {
+ composePath = fmt.Sprintf("%s/src/github.com/opencord/voltha-go/compose", goPath)
+ } else {
+ composePath = os.Getenv("COMPOSE_PATH")
+ }
+
+ fmt.Println("Using compose path:", composePath)
+
+ //Start the simulated environment
+ if err = tu.StartSimulatedEnv(composePath); err != nil {
+ fmt.Println("Failure starting simulated environment:", err)
+ os.Exit(10)
+ }
+
+ stub, err = tu.SetupGrpcConnectionToCore(grpcHostIP, GRPC_PORT)
+ if err != nil {
+ fmt.Println("Failure connecting to Voltha Core:", err)
+ os.Exit(11)
+ }
+
+ // Wait for the simulated devices to be registered in the Voltha Core
+ adapters := []string{"simulated_olt", "simulated_onu"}
+ if _, err = tu.WaitForAdapterRegistration(stub, adapters, 40); err != nil {
+ fmt.Println("Failure retrieving adapters:", err)
+ os.Exit(12)
+ }
+}
+
+func shutdown() {
+ err := tu.StopSimulatedEnv(composePath)
+ if err != nil {
+ fmt.Println("Failure stop simulated environment:", err)
+ }
+}
+
+func refreshLocalDeviceCache(stub voltha.VolthaServiceClient) error {
+ retrievedDevices, err := tu.ListDevices(stub)
+ if err != nil {
+ return err
+ }
+ for _, d := range retrievedDevices.Items {
+ allDevices[d.Id] = d
+ }
+
+ retrievedLogicalDevices, err := tu.ListLogicalDevices(stub)
+ if err != nil {
+ return err
+ }
+
+ for _, ld := range retrievedLogicalDevices.Items {
+ allLogicalDevices[ld.Id] = ld
+ }
+ return nil
+}
+
+func isPresent(pmName string, pmNames []string) bool {
+ for _, name := range pmNames {
+ if name == pmName {
+ return true
+ }
+ }
+ return false
+}
+
+func verifyDevicePMs(t *testing.T, stub voltha.VolthaServiceClient, device *voltha.Device, allPmNames []string, disabledPmNames []string, frequency uint32) {
+ ui := uuid.New()
+ ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(volthaSerialNumberKey, ui.String()))
+ pmConfigs, err := stub.ListDevicePmConfigs(ctx, &common.ID{Id: device.Id})
+ assert.Nil(t, err)
+ assert.Equal(t, device.Id, pmConfigs.Id)
+ assert.Equal(t, uint32(frequency), pmConfigs.DefaultFreq)
+ assert.Equal(t, false, pmConfigs.FreqOverride)
+ assert.Equal(t, false, pmConfigs.Grouped)
+ assert.Nil(t, pmConfigs.Groups)
+ assert.True(t, len(pmConfigs.Metrics) > 0)
+ metrics := pmConfigs.Metrics
+ for _, m := range metrics {
+ if m.Enabled {
+ assert.True(t, isPresent(m.Name, allPmNames))
+ } else {
+ assert.True(t, isPresent(m.Name, disabledPmNames))
+ }
+ assert.Equal(t, voltha.PmConfig_COUNTER, m.Type)
+ }
+}
+
+func verifyInitialPmConfigs(t *testing.T, stub voltha.VolthaServiceClient) {
+ fmt.Println("Verifying initial PM configs")
+ err := refreshLocalDeviceCache(stub)
+ assert.Nil(t, err)
+ for _, d := range allDevices {
+ if d.Root {
+ verifyDevicePMs(t, stub, d, parentPmNames, []string{}, 150)
+ } else {
+ verifyDevicePMs(t, stub, d, childPmNames, []string{}, 150)
+ }
+ }
+}
+
+func verifyPmFrequencyUpdate(t *testing.T, stub voltha.VolthaServiceClient, device *voltha.Device) {
+ ui := uuid.New()
+ ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(volthaSerialNumberKey, ui.String()))
+ pmConfigs, err := stub.ListDevicePmConfigs(ctx, &common.ID{Id: device.Id})
+ assert.Nil(t, err)
+ pmConfigs.DefaultFreq = 10
+ _, err = stub.UpdateDevicePmConfigs(ctx, pmConfigs)
+ assert.Nil(t, err)
+ if device.Root {
+ verifyDevicePMs(t, stub, device, parentPmNames, []string{}, 10)
+ } else {
+ verifyDevicePMs(t, stub, device, childPmNames, []string{}, 10)
+ }
+}
+
+func updatePmFrequencies(t *testing.T, stub voltha.VolthaServiceClient) {
+ fmt.Println("Verifying update to PMs frequencies")
+ err := refreshLocalDeviceCache(stub)
+ assert.Nil(t, err)
+ for _, d := range allDevices {
+ verifyPmFrequencyUpdate(t, stub, d)
+ }
+}
+
+func verifyDisablingSomePmMetrics(t *testing.T, stub voltha.VolthaServiceClient, device *voltha.Device) {
+ ui := uuid.New()
+ ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(volthaSerialNumberKey, ui.String()))
+ pmConfigs, err := stub.ListDevicePmConfigs(ctx, &common.ID{Id: device.Id})
+ assert.Nil(t, err)
+ metricsToDisable := []string{"tx_64_pkts", "rx_64_pkts", "tx_65_127_pkts", "rx_65_127_pkts"}
+ for _, m := range pmConfigs.Metrics {
+ if isPresent(m.Name, metricsToDisable) {
+ m.Enabled = false
+ }
+ }
+ _, err = stub.UpdateDevicePmConfigs(ctx, pmConfigs)
+ assert.Nil(t, err)
+ if device.Root {
+ verifyDevicePMs(t, stub, device, parentPmNames, metricsToDisable, 10)
+ } else {
+ verifyDevicePMs(t, stub, device, childPmNames, metricsToDisable, 10)
+ }
+}
+
+func disableSomePmMetrics(t *testing.T, stub voltha.VolthaServiceClient) {
+ fmt.Println("Verifying disabling of some PMs")
+ err := refreshLocalDeviceCache(stub)
+ assert.Nil(t, err)
+ for _, d := range allDevices {
+ verifyDisablingSomePmMetrics(t, stub, d)
+ }
+}
+
+func createAndEnableDevices(t *testing.T) {
+ err := tu.SetAllLogLevel(stub, voltha.Logging{Level: common.LogLevel_WARNING})
+ assert.Nil(t, err)
+
+ err = tu.SetLogLevel(stub, voltha.Logging{Level: common.LogLevel_DEBUG, PackageName: "github.com/opencord/voltha-go/rw_core/core"})
+ assert.Nil(t, err)
+
+ startTime := time.Now()
+
+ //Pre-provision the parent device
+ oltDevice, err := tu.PreProvisionDevice(stub)
+ assert.Nil(t, err)
+
+ fmt.Println("Creation of ", NUM_OLTS, " OLT devices took:", time.Since(startTime))
+
+ startTime = time.Now()
+
+ //Enable all parent device - this will enable the child devices as well as validate the child devices
+ err = tu.EnableDevice(stub, oltDevice, NUM_ONUS_PER_OLT)
+ assert.Nil(t, err)
+
+ fmt.Println("Enabling of OLT device took:", time.Since(startTime))
+
+ // Wait until the core and adapters sync up after an enabled
+ time.Sleep(time.Duration(math.Max(10, float64(NUM_OLTS*NUM_ONUS_PER_OLT)/2)) * time.Second)
+
+ err = tu.VerifyDevices(stub, NUM_ONUS_PER_OLT)
+ assert.Nil(t, err)
+
+ lds, err := tu.VerifyLogicalDevices(stub, oltDevice, NUM_ONUS_PER_OLT)
+ assert.Nil(t, err)
+ assert.Equal(t, 1, len(lds.Items))
+}
+
+func TestPerformanceMetrics(t *testing.T) {
+ //1. Test creation and activation of the devices. This will validate the devices as well as the logical device created/
+ createAndEnableDevices(t)
+
+ // 2. Test initial PMs on each device
+ verifyInitialPmConfigs(t, stub)
+
+ // 3. Test frequency update of the pmConfigs
+ updatePmFrequencies(t, stub)
+
+ // 4. Test disable some PM metrics
+ disableSomePmMetrics(t, stub)
+}
+
+func TestMain(m *testing.M) {
+ setup()
+ code := m.Run()
+ shutdown()
+ os.Exit(code)
+}