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

Experiment refactoring #404

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
127 changes: 127 additions & 0 deletions pkg/frr/frr.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"net"
"os"
"os/exec"
"os/user"
"path"
"reflect"
"strconv"
Expand All @@ -23,16 +24,48 @@ import (
"github.com/opiproject/opi-evpn-bridge/pkg/config"
"github.com/opiproject/opi-evpn-bridge/pkg/infradb"
"github.com/opiproject/opi-evpn-bridge/pkg/infradb/common"
"github.com/opiproject/opi-evpn-bridge/pkg/infradb/subscriberframework/actionbus"
"github.com/opiproject/opi-evpn-bridge/pkg/infradb/subscriberframework/eventbus"
"github.com/opiproject/opi-evpn-bridge/pkg/utils"
)

// frrComp string constant
const frrComp string = "frr"

// replayThreshold time threshold for replay
const replayThreshold = 64 * time.Second

// ModulefrrHandler empty structure
type ModulefrrHandler struct{}

// ModuleFrrActionHandler empty structure
type ModuleFrrActionHandler struct {
// runningFrrConfFile holds the running configuration of FRR daemon
runningFrrConfFile string
// basicFrrConfFile holds the basic/initial configuration of FRR daemon
basicFrrConfFile string
// backupFrrConfFile holds the backup configuration the current running config of FRR daemon
backupFrrConfFile string
}

// NewModuleFrrActionHandler initializes a default ModuleFrrActionHandler
func NewModuleFrrActionHandler() *ModuleFrrActionHandler {
return &ModuleFrrActionHandler{
runningFrrConfFile: "/etc/frr/frr.conf",
basicFrrConfFile: "/etc/frr/frr-basic.conf",
backupFrrConfFile: "/etc/frr/frr.conf.bak",
}
}

// NewModuleFrrActionHandlerWithArgs initializes a ModuleFrrActionHandler
func NewModuleFrrActionHandlerWithArgs(runningFrrConfFile, basicFrrConfFile, backupFrrConfFile string) *ModuleFrrActionHandler {
return &ModuleFrrActionHandler{
runningFrrConfFile: runningFrrConfFile,
basicFrrConfFile: basicFrrConfFile,
backupFrrConfFile: backupFrrConfFile,
}
}

// HandleEvent handles the events
func (h *ModulefrrHandler) HandleEvent(eventType string, objectData *eventbus.ObjectData) {
switch eventType {
Expand All @@ -47,6 +80,82 @@ func (h *ModulefrrHandler) HandleEvent(eventType string, objectData *eventbus.Ob
}
}

// HandleAction handles the actions
func (h *ModuleFrrActionHandler) HandleAction(actionType string, actionData *actionbus.ActionData) {
switch actionType {
case "preReplay":
log.Printf("Module FRR received %s\n", actionType)
h.handlePreReplay(actionData)
default:
log.Printf("error: Unknown action type %s", actionType)
}
}

func (h *ModuleFrrActionHandler) handlePreReplay(actionData *actionbus.ActionData) {
var deferErr error

defer func() {
// The ErrCh is used in order to notify the sender that the preReplay step has
// been executed successfully.
actionData.ErrCh <- deferErr
}()

// Backup the current running config
deferErr = os.Rename(h.runningFrrConfFile, h.backupFrrConfFile)
if deferErr != nil {
log.Printf("FRR: handlePreReplay(): Failed to backup running config of FRR: %s\n", deferErr)
return
}

// Create a new running config based on the basic/initial FRR config
input, deferErr := os.ReadFile(h.basicFrrConfFile)
if deferErr != nil {
log.Printf("FRR: handlePreReplay(): Failed to read content of %s: %s\n", h.basicFrrConfFile, deferErr)
return
}

deferErr = os.WriteFile(h.runningFrrConfFile, input, 0600)
if deferErr != nil {
log.Printf("FRR: handlePreReplay(): Failed to write content to %s: %s\n", h.runningFrrConfFile, deferErr)
return
}

// Change ownership of the frr.conf to frr:frr
group, deferErr := user.Lookup("frr")
if deferErr != nil {
log.Printf("FRR: handlePreReplay(): Failed to lookup user frr %s\n", deferErr)
return
}

uid, deferErr := strconv.Atoi(group.Uid)
if deferErr != nil {
log.Printf("FRR: handlePreReplay(): Failed to convert frr user string in linux to int %s\n", deferErr)
return
}

gid, deferErr := strconv.Atoi(group.Gid)
if deferErr != nil {
log.Printf("FRR: handlePreReplay(): Failed to convert frr group string in linux to int %s\n", deferErr)
return
}

deferErr = os.Chown(h.runningFrrConfFile, uid, gid)
if deferErr != nil {
log.Printf("FRR: handlePreReplay(): Failed to chown of %s to frr:frr : %s\n", h.runningFrrConfFile, deferErr)
return
}

// Restart FRR daemon
_, errCmd := utils.Run([]string{"systemctl", "restart", "frr"}, false)
if errCmd != 0 {
log.Println("FRR: handlePreReplay(): Failed to restart FRR daemon")
deferErr = fmt.Errorf("restart FRR daemon failed")
return
}

log.Println("FRR: handlePreReplay(): The pre-replay procedure has executed successfully")
}

// handlesvi handles the svi functionality
//
//nolint:funlen,gocognit
Expand Down Expand Up @@ -106,6 +215,10 @@ func handlesvi(objectData *eventbus.ObjectData) {
comp.CompStatus = common.ComponentStatusError
}
log.Printf("%+v\n", comp)

// Checking the timer to decide if we need to replay or not
comp.Replay = utils.CheckReplayThreshold(comp.Timer, replayThreshold)

err := infradb.UpdateSviStatus(objectData.Name, objectData.ResourceVersion, objectData.NotificationID, nil, comp)
if err != nil {
log.Printf("error in updating svi status: %s\n", err)
Expand All @@ -125,6 +238,10 @@ func handlesvi(objectData *eventbus.ObjectData) {
comp.CompStatus = common.ComponentStatusError
}
log.Printf("%+v\n", comp)

// Checking the timer to decide if we need to replay or not
comp.Replay = utils.CheckReplayThreshold(comp.Timer, replayThreshold)

err := infradb.UpdateSviStatus(objectData.Name, objectData.ResourceVersion, objectData.NotificationID, nil, comp)
if err != nil {
log.Printf("error in updating svi status: %s\n", err)
Expand Down Expand Up @@ -199,6 +316,10 @@ func handlevrf(objectData *eventbus.ObjectData) {
comp.CompStatus = common.ComponentStatusError
}
log.Printf("%+v\n", comp)

// Checking the timer to decide if we need to replay or not
comp.Replay = utils.CheckReplayThreshold(comp.Timer, replayThreshold)

err := infradb.UpdateVrfStatus(objectData.Name, objectData.ResourceVersion, objectData.NotificationID, nil, comp)
if err != nil {
log.Printf("error in updating vrf status: %s\n", err)
Expand All @@ -218,6 +339,10 @@ func handlevrf(objectData *eventbus.ObjectData) {
comp.CompStatus = common.ComponentStatusError
}
log.Printf("%+v\n", comp)

// Checking the timer to decide if we need to replay or not
comp.Replay = utils.CheckReplayThreshold(comp.Timer, replayThreshold)

err := infradb.UpdateVrfStatus(objectData.Name, objectData.ResourceVersion, objectData.NotificationID, nil, comp)
if err != nil {
log.Printf("error in updating vrf status: %s\n", err)
Expand Down Expand Up @@ -252,13 +377,15 @@ var localas int
// subscribeInfradb function handles the infradb subscriptions
func subscribeInfradb(config *config.Config) {
eb := eventbus.EBus
ab := actionbus.ABus
for _, subscriberConfig := range config.Subscribers {
if subscriberConfig.Name == frrComp {
for _, eventType := range subscriberConfig.Events {
eb.StartSubscriber(subscriberConfig.Name, eventType, subscriberConfig.Priority, &ModulefrrHandler{})
}
}
}
ab.StartSubscriber(frrComp, "preReplay", NewModuleFrrActionHandler())
}

// ctx variable of type context
Expand Down
74 changes: 74 additions & 0 deletions pkg/infradb/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type LogicalBridgeMetadata struct{}

// LogicalBridge holds Logical Bridge info
type LogicalBridge struct {
Domain
Name string
Spec *LogicalBridgeSpec
Status *LogicalBridgeStatus
Expand Down Expand Up @@ -211,3 +212,76 @@ func (in *LogicalBridge) DeleteBridgePort(bpName, bpMac string) error {
func (in *LogicalBridge) GetName() string {
return in.Name
}

// setComponentState set the stat of the component
func (in *LogicalBridge) setComponentState(component common.Component) {
lbComponents := in.Status.Components
for i, comp := range lbComponents {
if comp.Name == component.Name {
in.Status.Components[i] = component
break
}
}
}

// checkForAllSuccess check if all the components are in Success state
func (in *LogicalBridge) checkForAllSuccess() bool {
for _, comp := range in.Status.Components {
if comp.CompStatus != common.ComponentStatusSuccess {
return false
}
}
return true
}

// parseMeta parse metadata
func (in *LogicalBridge) parseMeta(lbMeta *LogicalBridgeMetadata) {
if lbMeta != nil {
in.Metadata = lbMeta
}
}

func (in *LogicalBridge) getStatusComponents() []common.Component {
return in.Status.Components
}

func (in *LogicalBridge) setStatusComponents(components []common.Component) {
copy(in.Status.Components, components)
}

func (in *LogicalBridge) isOperationalStatus(operStatus OperStatus) bool {
switch operStatus {
case OperStatusUp:
return in.Status.LBOperStatus == LogicalBridgeOperStatusUp
case OperStatusDown:
return in.Status.LBOperStatus == LogicalBridgeOperStatusDown
case OperStatusToBeDeleted:
return in.Status.LBOperStatus == LogicalBridgeOperStatusToBeDeleted
case OperStatusUnspecified:
return in.Status.LBOperStatus == LogicalBridgeOperStatusUnspecified
default:
log.Println("isOperationalStatus(): operational status has not been identified")
return false
}
}

func (in *LogicalBridge) setOperationalStatus(operStatus OperStatus) {
switch operStatus {
case OperStatusUp:
in.Status.LBOperStatus = LogicalBridgeOperStatusUp
case OperStatusDown:
in.Status.LBOperStatus = LogicalBridgeOperStatusDown
case OperStatusToBeDeleted:
in.Status.LBOperStatus = LogicalBridgeOperStatusToBeDeleted
case OperStatusUnspecified:
in.Status.LBOperStatus = LogicalBridgeOperStatusUnspecified
default:
log.Println("setOperationalStatus(): operational status has not been identified")
}
}

// TODO: This function can probably be moved to the domain.go as the ResourceVersion
// field is common for all the child objects (VRF,LB, BP, SVI)
func (in *LogicalBridge) setNewResourceVersion() {
in.ResourceVersion = generateVersion()
}
2 changes: 2 additions & 0 deletions pkg/infradb/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ type Component struct {
// Free format json string
Details string
Timer time.Duration
// Replay is used when the module wants to trigger replay action
Replay bool
}

func ip4ToInt(ip net.IP) uint32 {
Expand Down
66 changes: 66 additions & 0 deletions pkg/infradb/domain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2023-2024 Intel Corporation, or its subsidiaries.
// Copyright (c) 2024 Ericsson AB.

package infradb

import (
"github.com/opiproject/opi-evpn-bridge/pkg/infradb/common"
"github.com/opiproject/opi-evpn-bridge/pkg/infradb/subscriberframework/eventbus"
)

// IDomain holds the utilities for child objects
type IDomain interface {
getStatusComponents() []common.Component
setStatusComponents([]common.Component)
isOperationalStatus(OperStatus) bool
setOperationalStatus(OperStatus)
setNewResourceVersion()
}

// OperStatus operational Status
type OperStatus int32

const (
// OperStatusUnspecified for "unknown" state
OperStatusUnspecified OperStatus = iota
// OperStatusUp for "up" state
OperStatusUp = iota
// OperStatusDown for "down" state
OperStatusDown = iota
// OperStatusToBeDeleted for "to be deleted" state
OperStatusToBeDeleted = iota
)

// Domain holds domain info
type Domain struct {
IDomain IDomain
}

// prepareObjectsForReplay prepares an object for replay by setting the unsuccessful components
// in pending state and returning a list of the components that need to be contacted for the
// replay of the particular object that called the function.
func (in *Domain) prepareObjectsForReplay(componentName string, vrfSubs []*eventbus.Subscriber) []*eventbus.Subscriber {
// We assume that the list of Components that are returned
// from DB is ordered based on the priority as that was the
// way that has been stored in the DB in first place.

vrfComponents := in.IDomain.getStatusComponents()
auxComponents := in.IDomain.getStatusComponents()
tempSubs := []*eventbus.Subscriber{}
for i, comp := range vrfComponents {
if comp.Name == componentName || comp.CompStatus != common.ComponentStatusSuccess {
auxComponents[i] = common.Component{Name: comp.Name, CompStatus: common.ComponentStatusPending, Details: ""}
tempSubs = append(tempSubs, vrfSubs[i])
}
}

in.IDomain.setStatusComponents(auxComponents)

if in.IDomain.isOperationalStatus(OperStatusUp) {
in.IDomain.setOperationalStatus(OperStatusDown)
}

in.IDomain.setNewResourceVersion()
return tempSubs
}
Loading
Loading