Skip to content

Commit

Permalink
Connect FP Params and FP Feautre. Add workaround for missing dump met…
Browse files Browse the repository at this point in the history
…hods in FP API.

FP - Flowprobe :)
  • Loading branch information
rewenset authored and rewenset committed Apr 17, 2020
1 parent 3543523 commit 3e880f5
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 54 deletions.
42 changes: 34 additions & 8 deletions plugins/vpp/ipfixplugin/descriptor/flowprobe_feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
package descriptor

import (
"errors"

"go.ligato.io/cn-infra/v2/logging"

kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
Expand All @@ -24,10 +26,18 @@ import (
)

const (
// FPFeatureDescriptorName is the name of the descriptor for
// VPP Flowprobe Feature configuration.
FPFeatureDescriptorName = "vpp-flowprobe-feature"
)

// FPFeatureDescriptor configures Flowprobe feature for VPP.
// Validation errors:
var (
// ErrIfaceNotDefined returned when interface in confiugration is empty string.
ErrIfaceNotDefined = errors.New("missing interface name for Flowprobe Feature")
)

// FPFeatureDescriptor configures Flowprobe Feature for VPP.
type FPFeatureDescriptor struct {
ipfixHandler vppcalls.IpfixVppAPI
log logging.Logger
Expand All @@ -45,38 +55,54 @@ func NewFPFeatureDescriptor(ipfixHandler vppcalls.IpfixVppAPI, log logging.Plugi
ValueTypeName: ipfix.ModelFlowprobeFeature.ProtoName(),
KeySelector: ipfix.ModelFlowprobeFeature.IsKeyValid,
KeyLabel: ipfix.ModelFlowprobeFeature.StripKeyPrefix,
WithMetadata: true,
Validate: ctx.Validate,
Create: ctx.Create,
Delete: ctx.Delete,
Retrieve: ctx.Retrieve,
Dependencies: ctx.Dependencies,
}
return adapter.NewFlowProbeFeatureDescriptor(typedDescr)
}

// Validate does nothing.
// Validate checks if Flowprobe Feature configuration is good to send to VPP.
func (d *FPFeatureDescriptor) Validate(key string, value *ipfix.FlowProbeFeature) error {
if value.GetInterface() == "" {
return kvs.NewInvalidValueError(ErrIfaceNotDefined, "interface")
}
return nil
}

// Create uses vppcalls to pass Flowprobe feature configuration for interface to VPP.
// Create uses vppcalls to pass Flowprobe Feature configuration for interface to VPP.
func (d *FPFeatureDescriptor) Create(key string, val *ipfix.FlowProbeFeature) (metadata interface{}, err error) {
err = d.ipfixHandler.AddFPFeature(val)
return
return val, err
}

// Delete uses vppcalls to remove Flowprobe feature configuration for interface..
// Delete uses vppcalls to remove Flowprobe Feature configuration for interface.
func (d *FPFeatureDescriptor) Delete(key string, val *ipfix.FlowProbeFeature, metadata interface{}) (err error) {
err = d.ipfixHandler.DelFPFeature(val)
return
}

// Dependencies sets Flowprobe params as a dependency which must be created
// before enabling Flowprobe feature on an interface.
func (d *FPFeatureDescriptor) Dependencies(key string, value *ipfix.FlowProbeFeature) []kvs.Dependency {
// Dependencies sets Flowprobe Params as a dependency which must be created
// before enabling Flowprobe Feature on an interface.
func (d *FPFeatureDescriptor) Dependencies(key string, val *ipfix.FlowProbeFeature) []kvs.Dependency {
return []kvs.Dependency{
{
Label: "flowprobe-params",
Key: ipfix.FlowprobeParamsKey(),
},
}
}

// Retrieve hopes that configuration in correlate is actual configuration in VPP.
// As soon as VPP devs will add dump API calls, this methods should be fixed.
// Also, this method sets metadata, so descriptor for Flowprobe Params would know
// that there are some interfaces with Flowprobe Feature enabled.
func (d *FPFeatureDescriptor) Retrieve(correlate []adapter.FlowProbeFeatureKVWithMetadata) (retrieved []adapter.FlowProbeFeatureKVWithMetadata, err error) {
for i := range correlate {
correlate[i].Metadata = correlate[i].Value
}
return correlate, nil
}
84 changes: 62 additions & 22 deletions plugins/vpp/ipfixplugin/descriptor/flowprobe_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package descriptor
import (
"errors"

"go.ligato.io/cn-infra/v2/idxmap"
"go.ligato.io/cn-infra/v2/logging"

kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
Expand All @@ -26,63 +27,102 @@ import (
)

const (
// FPParamsDescriptorName is the name of the descriptor for
// VPP Flowprobe Params configuration.
FPParamsDescriptorName = "vpp-flowprobe-params"
)

// FPParamsDescriptor configures Flowprobe params for VPP.
var (
// Validation errors:

// ErrAllRecordFieldsDisabled returned when all record fields are set to false.
ErrAllRecordFieldsDisabled = errors.New("at least one of record fields (l2, l3, l4) must be set")

// Non-retriable errors:

// ErrFeatureEnabled informs the reason why Flowprobe Params can not be changed.
ErrFeatureEnabled = errors.New("can not change Flowprobe Params when Flowprobe Feature enabled on some interface")
)

// FPParamsDescriptor configures Flowprobe Params for VPP.
type FPParamsDescriptor struct {
ipfixHandler vppcalls.IpfixVppAPI
featureMap idxmap.NamedMapping
log logging.Logger
}

// NewFPParamsDescriptor creates a new instance of FPParamsDescriptor.
func NewFPParamsDescriptor(ipfixHandler vppcalls.IpfixVppAPI, log logging.PluginLogger) *kvs.KVDescriptor {
func NewFPParamsDescriptor(ipfixHandler vppcalls.IpfixVppAPI, featureMap idxmap.NamedMapping, log logging.PluginLogger) *kvs.KVDescriptor {
ctx := &FPParamsDescriptor{
ipfixHandler: ipfixHandler,
featureMap: featureMap,
log: log.NewLogger("flowprobe-params-descriptor"),
}
typedDescr := &adapter.FlowProbeParamsDescriptor{
Name: FPParamsDescriptorName,
NBKeyPrefix: ipfix.ModelFlowprobeParams.KeyPrefix(),
ValueTypeName: ipfix.ModelFlowprobeParams.ProtoName(),
KeySelector: ipfix.ModelFlowprobeParams.IsKeyValid,
KeyLabel: ipfix.ModelFlowprobeParams.StripKeyPrefix,
Validate: ctx.Validate,
Create: ctx.Create,
Delete: ctx.Delete,
Update: ctx.Update,
Name: FPParamsDescriptorName,
NBKeyPrefix: ipfix.ModelFlowprobeParams.KeyPrefix(),
ValueTypeName: ipfix.ModelFlowprobeParams.ProtoName(),
KeySelector: ipfix.ModelFlowprobeParams.IsKeyValid,
KeyLabel: ipfix.ModelFlowprobeParams.StripKeyPrefix,
IsRetriableFailure: ctx.IsRetriableFailure,
Validate: ctx.Validate,
Create: ctx.Create,
Delete: ctx.Delete,
Update: ctx.Update,
Retrieve: ctx.Retrieve,
}
return adapter.NewFlowProbeParamsDescriptor(typedDescr)
}

// Validate validates Flowprobe params.
// Validate checks if Flowprobe Params are good to send to VPP.
func (d *FPParamsDescriptor) Validate(key string, value *ipfix.FlowProbeParams) error {
d.log.Debug("Validate Flowprobe Params")

if !(value.GetRecordL2() || value.GetRecordL3() || value.GetRecordL4()) {
err := errors.New("at least one of record fields (l2, l3, l4) must be set")
return kvs.NewInvalidValueError(err, "record_l2", "record_l3", "record_l4")
return kvs.NewInvalidValueError(ErrAllRecordFieldsDisabled,
"record_l2", "record_l3", "record_l4")
}

return nil
}

// Create uses vppcalls to pass Flowprobe params to VPP.
// IsRetriableFailure returns false if error is one of errors
// defined at the top of this file as non-retriable.
func (d *FPParamsDescriptor) IsRetriableFailure(err error) bool {
if errors.Is(err, ErrFeatureEnabled) {
return false
}
return true
}

// Create passes Flowprobe Params to Update method.
func (d *FPParamsDescriptor) Create(key string, val *ipfix.FlowProbeParams) (metadata interface{}, err error) {
d.log.Debug("Create Flowprobe Params")
err = d.ipfixHandler.SetFPParams(val)
_, err = d.Update(key, nil, val, nil)
return
}

// Update uses vppcalls to pass Flowprobe params to VPP.
// Update uses vppcalls to pass Flowprobe Params to VPP.
func (d *FPParamsDescriptor) Update(key string, oldVal, newVal *ipfix.FlowProbeParams, oldMetadata interface{}) (newMetadata interface{}, err error) {
d.log.Debug("Update Flowprobe Params")
// Check if there is at least one Flowporbe Feature configured on some interface.
if len(d.featureMap.ListAllNames()) > 0 {
err = ErrFeatureEnabled
return
}
err = d.ipfixHandler.SetFPParams(newVal)
return
}

// Delete does nothing.
//
// Since all Flowprobe Features are dependent on Flowprobe Params,
// calling this method will also disable (move to "pending" state)
// all Flowprobe Features on interfaces.
//
// All the work will be done by KVScheduler :)
func (d *FPParamsDescriptor) Delete(key string, val *ipfix.FlowProbeParams, metadata interface{}) (err error) {
d.log.Debug("Delete Flowprobe Params (nothing happens)")
return
}

// Retrieve hopes that configuration in correlate is actual configuration in VPP.
// As soon as VPP devs will add dump API calls, this methods should be fixed.
func (d *FPParamsDescriptor) Retrieve(correlate []adapter.FlowProbeParamsKVWithMetadata) (retrieved []adapter.FlowProbeParamsKVWithMetadata, err error) {
return correlate, nil
}
38 changes: 21 additions & 17 deletions plugins/vpp/ipfixplugin/descriptor/ipfix.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ const (
IPFIXDescriptorName = "vpp-ipfix"
)

// Validation errors:
var (
// ErrColAddrNotDefined returned when collector address in confiugration is empty string.
ErrColAddrNotDefined = errors.New("address of a collector was not provided")
// ErrSrcAddrNotDefined returned when source address in confiugration is empty string.
ErrSrcAddrNotDefined = errors.New("address of a source was not provided")
// ErrTooBigMTU informs about the maximum value for Path MTU.
ErrTooBigMTU = errors.New("too big value, maximum is 1450")
// ErrTooSmlMTU informs about the minimum value for Path MTU.
ErrTooSmlMTU = errors.New("too small value, minimum is 68")
)

// IPFIXDescriptor configures IPFIX for VPP.
type IPFIXDescriptor struct {
ipfixHandler vppcalls.IpfixVppAPI
Expand Down Expand Up @@ -59,42 +71,35 @@ func NewIPFIXDescriptor(ipfixHandler vppcalls.IpfixVppAPI, log logging.PluginLog
return adapter.NewIPFIXDescriptor(typedDescr)
}

// Validate validates VPP IPFIX configuration.
// Validate does basic check of VPP IPFIX configuration.
func (d *IPFIXDescriptor) Validate(key string, value *ipfix.IPFIX) error {
d.log.Debug("Validate IPFIX")
if value.GetCollector().GetAddress() == "" {
err := errors.New("address of a collector was not provided")
return kvs.NewInvalidValueError(err, "collector.address")
return kvs.NewInvalidValueError(ErrColAddrNotDefined, "collector.address")
}

if value.GetSourceAddress() == "" {
err := errors.New("address of a source was not provided")
return kvs.NewInvalidValueError(err, "source_address")
return kvs.NewInvalidValueError(ErrSrcAddrNotDefined, "source_address")
}

if mtu := value.GetPathMtu(); mtu == 0 {
// That's okay. No worries. Will use the default path-mtu value.
// That's okay. No worries. VPP will use the default Path MTU value.
} else if mtu > vppcalls.MaxPathMTU {
err := errors.New("too big value, maximum is 1450")
return kvs.NewInvalidValueError(err, "path_mtu")
return kvs.NewInvalidValueError(ErrTooBigMTU, "path_mtu")
} else if mtu < vppcalls.MinPathMTU {
err := errors.New("too small value, minimum is 68")
return kvs.NewInvalidValueError(err, "path_mtu")
return kvs.NewInvalidValueError(ErrTooSmlMTU, "path_mtu")
}

return nil
}

// Create sets VPP IPFIX configuration.
// Create calls Update method, because IPFIX configuration is always there and can not be created.
func (d *IPFIXDescriptor) Create(key string, val *ipfix.IPFIX) (metadata interface{}, err error) {
d.log.Debug("Create IPFIX")
err = d.ipfixHandler.SetExporter(val)
_, err = d.Update(key, nil, val, nil)
return
}

// Update sets VPP IPFIX configuration.
func (d *IPFIXDescriptor) Update(key string, oldVal, newVal *ipfix.IPFIX, oldMetadata interface{}) (newMetadata interface{}, err error) {
d.log.Debug("Update IPFIX")
err = d.ipfixHandler.SetExporter(newVal)
return
}
Expand All @@ -103,11 +108,10 @@ func (d *IPFIXDescriptor) Update(key string, oldVal, newVal *ipfix.IPFIX, oldMet
// nor reasons to delete VPP IPFIX configuration.
// You can only configure exporting in a way you want to.
func (d *IPFIXDescriptor) Delete(key string, val *ipfix.IPFIX, metadata interface{}) (err error) {
d.log.Debug("Delete IPFIX")
return nil
}

// Retrieve returns all configured IP flow infromation exporters.
// Retrieve returns configuration of IP Flow Infromation eXporter.
func (d *IPFIXDescriptor) Retrieve(correlate []adapter.IPFIXKVWithMetadata) (retrieved []adapter.IPFIXKVWithMetadata, err error) {
ipfixes, err := d.ipfixHandler.DumpExporters()
if err != nil {
Expand Down
26 changes: 19 additions & 7 deletions plugins/vpp/ipfixplugin/ipfixplugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,15 @@ import (
// IPFIXPlugin is a plugin that manages IPFIX configuration in VPP.
// IPFIX - IP Flow Information Export (IPFIX).
// It allows to:
// - configure export of flowprobe information;
// - configure flowprobe params;
// - enable/disable flowprobe feature for an interface.
// - configure export of Flowprobe information;
// - configure Flowprobe Params;
// - enable/disable Flowprobe Feature for an interface.
//
// Things to rememmber:
// - Flowprobe Feature can not be configured for any interface,
// if Flowprobe Params were not set.
// - Flowprobe Params can not be changed,
// if Flowprobe Feature was enabled for at least one interface.
type IPFIXPlugin struct {
Deps

Expand Down Expand Up @@ -75,14 +81,20 @@ func (p *IPFIXPlugin) Init() (err error) {
return err
}

fpParamsDescriptor := descriptor.NewFPParamsDescriptor(p.ipfixHandler, p.Log)
err = p.KVScheduler.RegisterKVDescriptor(fpParamsDescriptor)
fpFeatureDescriptor := descriptor.NewFPFeatureDescriptor(p.ipfixHandler, p.Log)
err = p.KVScheduler.RegisterKVDescriptor(fpFeatureDescriptor)
if err != nil {
return err
}

fpFeautreDescriptor := descriptor.NewFPFeatureDescriptor(p.ipfixHandler, p.Log)
err = p.KVScheduler.RegisterKVDescriptor(fpFeautreDescriptor)
// Descriptor for Flowprobe Params will use `fpFeatureMM` to check
// if Flowprobe Params can be updated. If at least one item is in this
// map, than there is at least one interface with Flowprobe Feature
// enabled, hence Flowprobe Params update is not allowed.
fpFeatureMM := p.KVScheduler.GetMetadataMap(fpFeatureDescriptor.Name)

fpParamsDescriptor := descriptor.NewFPParamsDescriptor(p.ipfixHandler, fpFeatureMM, p.Log)
err = p.KVScheduler.RegisterKVDescriptor(fpParamsDescriptor)
if err != nil {
return err
}
Expand Down

0 comments on commit 3e880f5

Please sign in to comment.