First Commit of Voltha-Go-Controller from Radisys

Change-Id: I8e2e908e7ab09a4fe3d86849da18b6d69dcf4ab0
diff --git a/internal/pkg/application/meters.go b/internal/pkg/application/meters.go
new file mode 100644
index 0000000..4b5fd71
--- /dev/null
+++ b/internal/pkg/application/meters.go
@@ -0,0 +1,331 @@
+/*
+* Copyright 2022-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 application
+
+import (
+	"encoding/json"
+	"errors"
+	"sync"
+
+	cntlr "voltha-go-controller/internal/pkg/controller"
+	"voltha-go-controller/database"
+	"voltha-go-controller/internal/pkg/of"
+	"github.com/opencord/voltha-lib-go/v7/pkg/log"
+)
+
+// VoltShaperConfig is shaper profile configuration structure
+type VoltShaperConfig struct {
+	Name      string
+	BurstSize uint32
+}
+
+// VoltBwConfig is bandwidth profile configuration structure
+type VoltBwConfig struct {
+	Name string
+	Fir  uint32
+	Air  uint32
+	Eir  uint32
+}
+
+// VoltBandwidthProf is bandwidth profile stored at VGC
+type VoltBandwidthProf struct {
+	VoltBwConfig
+}
+
+// VoltShaperProf is shaper profile stored at VGC
+type VoltShaperProf struct {
+	VoltShaperConfig
+}
+
+// VoltMeterProf is meter profile stored at VGC
+type VoltMeterProf struct {
+	VoltMeter
+}
+
+// MeterMgr structure
+type MeterMgr struct {
+	Meters      sync.Map
+	MetersByID  sync.Map
+	LastMeterID uint32
+}
+
+// Init to initialize MeterMgr
+func (m *MeterMgr) Init() {
+	m.LastMeterID = 0
+}
+
+// VoltMeter : A VOLT meter is a combination of BW and shaper profiles
+// The ID is generated by the VOLT application
+type VoltMeter struct {
+	Name               string
+	ID                 uint32
+	Fir                uint32
+	Air                uint32
+	Eir                uint32
+	BurstSize          uint32
+	AssociatedServices uint32
+	Version            string
+	Cir                uint32
+	Cbs                uint32
+	Pir                uint32
+	Pbs                uint32
+	Gir                uint32
+	Ebs                uint32
+}
+
+// WriteToDb to write a meter profile to DB
+func (vm *VoltMeter) WriteToDb() error {
+	vm.Version = database.PresentVersionMap[database.MeterPath]
+	b, err := json.Marshal(vm)
+	if err != nil {
+		return err
+	}
+	if err1 := db.PutMeter(vm.Name, string(b)); err1 != nil {
+		return err1
+	}
+	return nil
+}
+
+// DelFromDb to delete a meter profile from DB
+func (vm *VoltMeter) DelFromDb() {
+	_ = db.DelMeter(vm.Name)
+}
+
+// GetMeterByName to get meter by name
+func (m *MeterMgr) GetMeterByName(name string) (*VoltMeter, bool) {
+	meter, ok := m.Meters.Load(name)
+	logger.Infow(ctx, "Meter Obtained Name", log.Fields{"Meter": meter})
+	if ok {
+		return meter.(*VoltMeter), ok
+	}
+	return nil, ok
+}
+
+// GetMeterByID to get meter by ID
+func (m *MeterMgr) GetMeterByID(id uint32) (*VoltMeter, bool) {
+	meter, ok := m.MetersByID.Load(id)
+	logger.Infow(ctx, "Meter Obtained ID", log.Fields{"Meter": meter})
+	if ok {
+		return meter.(*VoltMeter), ok
+	}
+	return nil, ok
+}
+
+// AddMeter to add meter
+func (m *MeterMgr) AddMeter(meter *VoltMeter) {
+	m.Meters.Store(meter.Name, meter)
+	m.MetersByID.Store(meter.ID, meter)
+	logger.Infow(ctx, "Meter Added/Updated", log.Fields{"Meter": meter, "Name": meter.Name, "Id": meter.ID})
+}
+
+// DelMeter to delete meter
+func (m *MeterMgr) DelMeter(meter *VoltMeter) {
+	m.Meters.Delete(meter.Name)
+	m.MetersByID.Delete(meter.ID)
+	logger.Infow(ctx, "Meter Deleted", log.Fields{"Meter": meter, "Name": meter.Name, "Id": meter.ID})
+}
+
+// AddToDevice to add meter to the device
+func (vm *VoltMeter) AddToDevice(port string, device string, aggVM *VoltMeter) {
+	logger.Debugw(ctx, "Adding Meter To Device", log.Fields{"Id": vm.ID, "Device": device, "Port": port})
+	meter := of.NewMeter(vm.ID)
+	// meter.AddBand(vm.Air, vm.BurstSize)
+	// meter.AddBand(vm.Eir, vm.BurstSize)
+	// if aggVM != nil {
+	// 	meter.AddBand(aggVM.Air, aggVM.BurstSize)
+	// 	meter.AddBand(aggVM.Eir, aggVM.BurstSize)
+	// }
+
+	//Community VGC Impl
+
+	//Set Cir
+	if vm.Cir != 0 {
+		meter.AddBand(vm.Cir, vm.Cbs)
+	}
+
+	//Set Air to 0 if both air & gir are set
+	if vm.Air != 0 && vm.Gir != 0 {
+		vm.Air = 0
+	}
+
+	//Set Pir & Pbs
+	var pir uint32
+	var pbs uint32
+	if vm.Pir != 0 {
+		pir = vm.Pir
+	} else {
+		pir = vm.Eir + vm.Cir + vm.Gir + vm.Air
+	}
+
+	if vm.Pbs != 0 {
+		pbs = vm.Pbs
+	} else {
+		pbs = vm.Ebs + vm.Cbs
+	}
+	meter.AddBand(pir, pbs)
+
+	//Set Gir
+	if vm.Gir != 0 {
+		meter.AddBand(vm.Gir, 0)
+	}
+
+	logger.Infow(ctx, "Meter Config", log.Fields{"Cir": vm.Cir, "Air": vm.Air, "Pir": vm.Pir, "Gir": vm.Gir, "Eir": vm.Eir})
+	logger.Infow(ctx, "Meter Burst Config", log.Fields{"Cbs": vm.Cbs, "Pbs": vm.Pbs})
+	logger.Infow(ctx, "Meter Burst Oper", log.Fields{"Pir": pir, "Pbs": pbs})
+	//Set Air
+	// Air is used in place of Gir only if Gir is
+	// not present and Air is not 0
+	if vm.Air != 0 {
+		meter.AddBand(vm.Air, 0)
+	}
+
+	logger.Debug(ctx, "Total Bands are", log.Fields{"meter": *meter})
+	if err := cntlr.GetController().ModMeter(port, device, of.MeterCommandAdd, meter); err != nil {
+		logger.Warnw(ctx, "Add meter to device Failed", log.Fields{"Id": vm.ID, "meter": *meter, "Error": err})
+	}
+}
+
+// AddMeterToDevice to add meter to the device
+func (m *MeterMgr) AddMeterToDevice(port string, device string, meterID uint32, aggMeterID uint32) {
+	var aggVM *VoltMeter
+	vm, err := m.GetMeterByProfID(meterID)
+	if err == nil {
+		if 0 != aggMeterID { //Assuming valid meter id will never be 0
+			if aggVM, err = m.GetMeterByProfID(aggMeterID); err != nil {
+				logger.Warnw(ctx, "Aggregated Meter not found", log.Fields{"Id": aggMeterID})
+			}
+		}
+		vm.AddToDevice(port, device, aggVM)
+	} else {
+		logger.Warnw(ctx, "Meter not found", log.Fields{"Id": meterID})
+	}
+}
+
+// RestoreMetersFromDb to read from the DB and restore all the services
+func (m *MeterMgr) RestoreMetersFromDb() {
+	// VNETS must be learnt first
+	logger.Infow(ctx, "LastMeterID on restart", log.Fields{"LastMeterID": m.LastMeterID})
+	ms, _ := db.GetMeters()
+	for _, mt := range ms {
+		b, ok := mt.Value.([]byte)
+		if !ok {
+			logger.Warn(ctx, "The value type is not []byte")
+			continue
+		}
+		var meter VoltMeter
+		err := json.Unmarshal(b, &meter)
+		if err != nil {
+			logger.Warn(ctx, "Unmarshal of meter profile failed")
+			continue
+		}
+		logger.Infow(ctx, "Retrieved Meter", log.Fields{"Meter": meter.Name})
+		m.AddMeter(&meter)
+		if meter.ID > m.LastMeterID {
+			m.LastMeterID = meter.ID
+		}
+	}
+	logger.Infow(ctx, "LastMeterID on reading DB", log.Fields{"LastMeterID": m.LastMeterID})
+}
+
+// AddMeterProf to add the meter profile name as key
+func (va *VoltApplication) AddMeterProf(cfg VoltMeter) {
+
+	mm := &va.MeterMgr
+	if _, ok := mm.GetMeterByName(cfg.Name); ok {
+		logger.Warnw(ctx, "Meter profile exists", log.Fields{"Name": cfg.Name})
+		return
+	}
+
+	mm.LastMeterID++
+	//FIX-ME: Hardcoded the meter-id temp till meter delete is introduced
+	//Restriction: Only one meter profile should be used across all services
+	//	id := uint32(1) //mm.LastMeterId
+	id := mm.LastMeterID
+	cfg.ID = id
+	mm.AddMeter(&cfg)
+	if err := cfg.WriteToDb(); err != nil {
+		logger.Warnw(ctx, "MeterProf Write to DB Failed", log.Fields{"MeterConfig": cfg, "Error": err})
+	}
+}
+
+// UpdateMeterProf to update the meter profile
+func (va *VoltApplication) UpdateMeterProf(cfg VoltMeter) {
+	mm := &va.MeterMgr
+	if _, ok := mm.GetMeterByName(cfg.Name); !ok {
+		logger.Warnw(ctx, "Meter profile does not exist", log.Fields{"Name": cfg.Name})
+		return
+	}
+	mm.AddMeter(&cfg)
+	if err := cfg.WriteToDb(); err != nil {
+		logger.Warnw(ctx, "MeterProf Write to DB Failed", log.Fields{"MeterConfig": cfg, "Error": err})
+	}
+}
+
+// GetMeterByProfID to get a meter based on the identities of bandwidth profile and shaper
+// profile names.
+func (m *MeterMgr) GetMeterByProfID(id uint32) (*VoltMeter, error) {
+	if mtr, ok := m.GetMeterByID(id); ok {
+		return mtr, nil
+	}
+	return nil, errors.New("Meter Missing")
+}
+
+// GetMeter to get a meter based on the identities of bandwidth profile and shaper
+// profile names.
+func (m *MeterMgr) GetMeter(meterID string) (*VoltMeter, error) {
+	if mt, ok := m.GetMeterByName(meterID); ok {
+		return mt, nil
+	}
+	return nil, errors.New("Meter Missing")
+}
+
+// DeleteFromDevice to delete meter from the device
+func (vm *VoltMeter) DeleteFromDevice(port string, device string) {
+
+	meter := of.NewMeter(vm.ID)
+
+	logger.Debugw(ctx, "Delete meter from device", log.Fields{"Id": vm.ID, "meter": *meter})
+	if err := cntlr.GetController().ModMeter(port, device, of.MeterCommandDel, meter); err != nil {
+		logger.Warnw(ctx, "Delete meter from device Failed", log.Fields{"Id": vm.ID, "meter": *meter, "Error": err})
+	}
+}
+
+// DelMeterProf to delete meter profile
+func (va *VoltApplication) DelMeterProf(name string) error {
+	mm := &va.MeterMgr
+	if _, ok := mm.GetMeterByName(name); !ok {
+		logger.Warnw(ctx, "Meter profile does not exist", log.Fields{"Name": name})
+		return errors.New("Meter profile doesn't exist")
+	}
+	cfg, _ := mm.GetMeterByName(name)
+	if cfg.AssociatedServices != 0 {
+		logger.Warnw(ctx, "Mismatch in submgr and vgc oeter profile service reference",
+			log.Fields{"MeterProfile": name, "serviceCount": cfg.AssociatedServices})
+		return errors.New("Service reference is not 0")
+	}
+	//TODO : delete from all devices
+	delmeterFromDevice := func(key interface{}, value interface{}) bool {
+		device := key.(string)
+		port, _ := GetApplication().GetNniPort(device)
+		cfg.DeleteFromDevice(port, device)
+		return true
+	}
+	va.DevicesDisc.Range(delmeterFromDevice)
+	cfg.DelFromDb()
+	//Delete meter from device will be invoked by caller separately
+	mm.DelMeter(cfg)
+	return nil
+}