Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(idpool): idpool feature for generating id's #400

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 37 additions & 36 deletions pkg/LinuxGeneralModule/lgm.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,6 @@ import (
// ModulelgmHandler enmpty interface
type ModulelgmHandler struct{}

// RoutingTableMax max value of routing table
const RoutingTableMax = 4000

// RoutingTableMin min value of routing table
const RoutingTableMin = 1000

// lgmComp string constant
const lgmComp string = "lgm"

Expand All @@ -46,10 +40,11 @@ const brStr string = "br-"
// vxlanStr string constant
const vxlanStr string = "vxlan-"

// GenerateRouteTable range specification, note that min <= max
func GenerateRouteTable() uint32 {
return uint32(rand.Intn(RoutingTableMax-RoutingTableMin+1) + RoutingTableMin) //nolint:gosec
}
// routingTableMax max value of routing table
const routingTableMax = 4000

// routingTableMin min value of routing table
const routingTableMin = 1000

// run runs the commands
func run(cmd []string, flag bool) (string, int) {
Expand Down Expand Up @@ -350,6 +345,9 @@ var ctx context.Context
// nlink variable wrapper
var nlink utils.Netlink

// RouteTableGen table id generate variable
var RouteTableGen utils.IDPool

// Initialize initializes the config, logger and subscribers
func Initialize() {
eb := eventbus.EBus
Expand All @@ -363,7 +361,12 @@ func Initialize() {
brTenant = "br-tenant"
ipMtu = config.GlobalConfig.LinuxFrr.IPMtu
ctx = context.Background()
nlink = utils.NewNetlinkWrapperWithArgs(config.GlobalConfig.Tracer)
RouteTableGen = utils.IDPoolInit("RTtable", routingTableMin, routingTableMax)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDPoolInit can just return error if failed to initialize

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added bool return for the initialization status

if reflect.DeepEqual(RouteTableGen, utils.IDPool{}) {
log.Printf("LGM: Failed in the assigning id \n")
return
}
nlink = utils.NewNetlinkWrapperWithArgs(false)
// Set up the static configuration parts
_, err := nlink.LinkByName(ctx, brTenant)
if err != nil {
Expand Down Expand Up @@ -401,8 +404,8 @@ func setUpTenantBridge() {
}
}

// routingTableBusy checks if the route is in filterred list
func routingTableBusy(table uint32) (bool, error) {
// routingtableBusy checks if the route is in filterred list
func routingtableBusy(table uint32) (bool, error) {
routeList, err := nlink.RouteListFiltered(ctx, netlink.FAMILY_V4, &netlink.Route{Table: int(table)}, netlink.RT_FILTER_TABLE)
if err != nil {
return false, err
Expand Down Expand Up @@ -464,19 +467,17 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) {
}
vrf.Metadata.RoutingTable = make([]*uint32, 1)
vrf.Metadata.RoutingTable[0] = new(uint32)
var routingTable uint32
for {
routingTable = GenerateRouteTable()
isBusy, err := routingTableBusy(routingTable)
if err != nil {
log.Printf("LGM : Error occurred when checking if routing table %d is busy: %+v\n", routingTable, err)
return "", false
}
if !isBusy {
log.Printf("LGM: Routing Table %d is not busy\n", routingTable)
break
}
log.Printf("LGM: Routing Table %d is busy\n", routingTable)
var routingtable uint32
name := vrf.Name
routingtable, _ = RouteTableGen.GetID(name, 0)
log.Printf("LGM assigned id %+v for vrf name %s\n", routingtable, vrf.Name)
isbusy, err := routingtableBusy(routingtable)
if err != nil {
log.Printf("LGM : Error occurred when checking if routing table %d is busy: %+v\n", routingtable, err)
return "", false
}
if !isbusy {
log.Printf("LGM: Routing Table %d is not busy\n", routingtable)
}
var vtip string
if !reflect.ValueOf(vrf.Spec.VtepIP).IsZero() {
Expand All @@ -489,19 +490,19 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) {
return "", false
}
}
log.Printf("setUpVrf: %s %d\n", vtip, routingTable)
log.Printf("setUpVrf: %s %d\n", vtip, routingtable)
// Create the vrf interface for the specified routing table and add loopback address

linkAdderr := nlink.LinkAdd(ctx, &netlink.Vrf{
LinkAttrs: netlink.LinkAttrs{Name: path.Base(vrf.Name)},
Table: routingTable,
Table: routingtable,
})
if linkAdderr != nil {
log.Printf("LGM: Error in Adding vrf link table %d\n", routingTable)
log.Printf("LGM: Error in Adding vrf link table %d\n", routingtable)
return "", false
}

log.Printf("LGM: vrf link %s Added with table id %d\n", vrf.Name, routingTable)
log.Printf("LGM: vrf link %s Added with table id %d\n", vrf.Name, routingtable)

link, linkErr := nlink.LinkByName(ctx, path.Base(vrf.Name))
if linkErr != nil {
Expand Down Expand Up @@ -536,7 +537,7 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) {

Src1 := net.IPv4(0, 0, 0, 0)
route := netlink.Route{
Table: int(routingTable),
Table: int(routingtable),
Type: unix.RTN_THROW,
Protocol: 255,
Priority: 9999,
Expand All @@ -548,7 +549,7 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) {
return "", false
}

log.Printf("LGM : Added route throw default table %d proto opi_evpn_br metric 9999\n", routingTable)
log.Printf("LGM : Added route throw default table %d proto opi_evpn_br metric 9999\n", routingtable)
// Disable reverse-path filtering to accept ingress traffic punted by the pipeline
// disable_rp_filter("rep-"+vrf.Name)
// Configuration specific for VRFs associated with L3 EVPN
Expand Down Expand Up @@ -638,8 +639,8 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) {
return "", false
}
}
details := fmt.Sprintf("{\"routingTable\":\"%d\"}", routingTable)
*vrf.Metadata.RoutingTable[0] = routingTable
details := fmt.Sprintf("{\"routingtable\":\"%d\"}", routingtable)
*vrf.Metadata.RoutingTable[0] = routingtable
return details, true
}

Expand Down Expand Up @@ -775,7 +776,7 @@ func tearDownVrf(vrf *infradb.Vrf) bool {
if path.Base(vrf.Name) == "GRD" {
return true
}
routingTable := *vrf.Metadata.RoutingTable[0]
routingtable := *vrf.Metadata.RoutingTable[0]
// Delete the Linux networking artefacts in reverse order
if !reflect.ValueOf(vrf.Spec.Vni).IsZero() {
linkVxlan, linkErr := nlink.LinkByName(ctx, vxlanStr+path.Base(vrf.Name))
Expand All @@ -802,7 +803,7 @@ func tearDownVrf(vrf *infradb.Vrf) bool {
}
log.Printf("LGM : Delete br-%s\n", vrf.Name)
}
routeTable := fmt.Sprintf("%+v", routingTable)
routeTable := fmt.Sprintf("%+v", routingtable)
flusherr := nlink.RouteFlushTable(ctx, routeTable)
if flusherr != nil {
log.Printf("LGM: Error in flush table %+v\n", routeTable)
Expand Down
147 changes: 147 additions & 0 deletions pkg/utils/idpool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2022-2023 Intel Corporation, or its subsidiaries.
// Copyright (C) 2023 Nordix Foundation.

// Package utils has some utility functions and interfaces
package utils

import (
"fmt"
"log"
"reflect"
)

// IDPool structure
/* Helper class for uniquely assigning IDs from a specified integer set (e.g. a
# range) to keys. IDs are assigned (or read) with GetID(key) and returned back
# into the pool with ReleaseID(key). The IDPool remembers a once-assigned ID
# for keys so that the same ID is assigned for a key. Only when the pool runs
# out of unassigned keys, it will recycle released ids and assign them to new
# keys.
# Optionally, the IDPool supports reference tracking for key/ID pairs. Clients
# can provide a unique reference when fetching and releasing an ID for a key
# to support multiple independent clients.
# The pool will only release the ID for the key, when the last client has the
# released the ID with its reference. When a reference is specified in GetID()
# and ReleaseID() the IDPool returns the current number of reference for the
# ID so that a caller knows when an ID was newly assigned (ref_count 1) or
# finally released (ref_count 0).
*/
type IDPool struct {
name string // Name of pool
unusedIDs []uint32 // Yet unused IDs in pool Available ids
idsInUse map[interface{}]uint32 // Mapping key: id for currently assigned ids
idsForReuse map[interface{}]uint32 // Mapping key: id for previously assigned ids
refs map[uint32]map[interface{}]bool
size int // Size of the pool
}

// IDPoolInit initialize mod ptr pool
func IDPoolInit(name string, min uint32, max uint32) IDPool {
var pool IDPool
pool.name = name
var index int
pool.unusedIDs = make([]uint32, (max-min)+1)
if min > 0 {
for value := max; value >= min; value-- {
pool.unusedIDs[index] = value
index++
}
} else {
return IDPool{}
}
pool.size = len(pool.unusedIDs)
pool.idsInUse = make(map[interface{}]uint32)
pool.idsForReuse = make(map[interface{}]uint32)
pool.refs = make(map[uint32]map[interface{}]bool)
return pool
}

// GetPoolStatus get status of a pool
func (ip *IDPool) GetPoolStatus() string {
str := fmt.Sprintf("name=%s\n Inuse=%+v\n Refs=%+v\n Forreuse=%+v\n Unused=%+v\n ", ip.name, ip.idsInUse, ip.refs, ip.idsForReuse, ip.unusedIDs)
return str
}

func (ip *IDPool) assignid(key interface{}) uint32 {
// Check if there was an id assigned for that key earlier
var id uint32
ok := ip.idsForReuse[key]
if ok != 0 {
// Re-use the old id
delete(ip.idsForReuse, key)
} else {
if len(ip.unusedIDs) != 0 {
// Pick an unused id
id = ip.unusedIDs[len(ip.unusedIDs)-1]
ip.unusedIDs = ip.unusedIDs[0 : len(ip.unusedIDs)-1]
} else {
if len(ip.idsForReuse) != 0 {
// Pick one of the ids earlier used for another key
for oldKey := range ip.idsForReuse {
delete(ip.idsForReuse, oldKey)
break
}
} else {
log.Printf("IDPool: Failed to allocate id for %+v. No free ids in pool.", key)
return 0
}
}
}
// Store the assigned id, if any
if id != 0 {
ip.idsInUse[key] = id
}
return id
}

// GetID get the mod ptr id from pool
func (ip *IDPool) GetID(key interface{}, ref interface{}) (uint32, uint32) {
var id uint32
ok := ip.idsInUse[key]
if ok == 0 {
id = ip.assignid(key)
if id == 0 {
return 0, 0
}
} else {
id = ok
}
if ref != nil {
log.Printf("IDPool: GetID Assigning key : %+v , id %+v for ref %v", id, key, ref)
if reflect.ValueOf(ip.refs[id]).IsZero() {
ip.refs[id] = make(map[interface{}]bool, 0)
}
ip.refs[id][ref] = true
return id, uint32(len(ip.refs[id]))
}
log.Printf("IDPool: GetID Assigning id %v for key %v and ref %v", id, key, ref)
return id, uint32(0)
}

// ReleaseID get the reference id
func (ip *IDPool) ReleaseID(key interface{}, ref interface{}) (uint32, uint32) {
log.Printf("IDPool:ReleaseID Releasing id for key %v", key)
ok := ip.idsInUse[key]
if ok == 0 {
log.Printf("No id to release for key %v", key)
return 0, 0
}
id := ok
refSet := ip.refs[id]
if !reflect.ValueOf(refSet).IsZero() && !reflect.ValueOf(ref).IsZero() {
delete(refSet, ref)
}
if len(refSet) == 0 {
delete(ip.idsInUse, key)
delete(ip.refs, id)
ip.idsForReuse[key] = id
log.Printf("IDPool:ReleaseID Id %v has been released", id)
} else {
log.Printf("IDPool:ReleaseID Keep id:%+v remaining references %+v", id, len(refSet))
}
if ref != nil {
return id, uint32(len(refSet))
}
return id, uint32(0)
}
Loading