[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)
+}