| /* |
| * 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 |
| } |