blob: cc61a4ff5a0d31ea2cf2eb95c677c185fb86de17 [file] [log] [blame]
/*
* 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 (
"context"
"encoding/json"
"errors"
"sync"
"voltha-go-controller/database"
cntlr "voltha-go-controller/internal/pkg/controller"
"voltha-go-controller/internal/pkg/of"
"voltha-go-controller/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
Version string
ID uint32
Fir uint32
Air uint32
Eir uint32
BurstSize uint32
AssociatedServices uint32
Cir uint32
Cbs uint32
Pir uint32
Pbs uint32
Gir uint32
Ebs uint32
}
// WriteToDb to write a meter profile to DB
func (vm *VoltMeter) WriteToDb(cntx context.Context) error {
vm.Version = database.PresentVersionMap[database.MeterPath]
b, err := json.Marshal(vm)
if err != nil {
return err
}
if err1 := db.PutMeter(cntx, vm.Name, string(b)); err1 != nil {
return err1
}
return nil
}
// DelFromDb to delete a meter profile from DB
func (vm *VoltMeter) DelFromDb(cntx context.Context) {
_ = db.DelMeter(cntx, 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{"Name": name, "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, "GetMeter Obtained ID", log.Fields{"ID": id, "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, "Add 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.Debugw(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) {
logger.Debugw(ctx, "Adding Meter To Device", log.Fields{"Agg MeterID": aggMeterID, "Device": device, "Port": port, "MeterID": meterID})
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(cntx context.Context) {
// VNETS must be learnt first
logger.Infow(ctx, "Received RestoreMetersFromDb and LastMeterID on restart", log.Fields{"LastMeterID": m.LastMeterID})
ms, _ := db.GetMeters(cntx)
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.Debugw(ctx, "Retrieved Meter", log.Fields{"Meter": meter.Name})
m.AddMeter(&meter)
if meter.ID > m.LastMeterID {
m.LastMeterID = meter.ID
}
}
logger.Debugw(ctx, "LastMeterID on reading DB", log.Fields{"LastMeterID": m.LastMeterID})
}
// AddMeterProf to add the meter profile name as key
func (va *VoltApplication) AddMeterProf(cntx context.Context, cfg VoltMeter) {
logger.Infow(ctx, "Add the meter profile name as key", log.Fields{"MeterConfig": cfg})
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(cntx); 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(cntx context.Context, cfg VoltMeter) {
logger.Debugw(ctx, "Update the meter profile name as key", log.Fields{"MeterConfig": cfg})
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(cntx); 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) {
logger.Debugw(ctx, "Get Meter by Profile id", log.Fields{"ID": id})
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) {
logger.Debugw(ctx, "Get Meter by Meter id", log.Fields{"MeterID": meterID})
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) {
logger.Debugw(ctx, "Delete meter from device", log.Fields{"Id": vm.ID, "Port": port, "Device": device})
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(cntx context.Context, name string) error {
logger.Debugw(ctx, "Delete meter profile", log.Fields{"Name": name})
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(cntx)
// Delete meter from device will be invoked by caller separately
mm.DelMeter(cfg)
return nil
}