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

Add derived (property) key-value marking interface with at least one IP address #1403

Merged
merged 1 commit into from
Jul 2, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
32 changes: 32 additions & 0 deletions api/models/vpp/interfaces/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,13 @@ const (
rxModesKeyTemplate = "vpp/interface/{iface}/rx-modes"
)

/* Interface with IP address (derived, property) */
const (
// interfaceWithIPKeyTemplate is a template for keys derived from all interfaces
// but created only after at least one IP address is assigned.
interfaceWithIPKeyTemplate = "vpp/interface/{iface}/has-IP-address"
)

const (
// InvalidKeyPart is used in key for parts which are invalid
InvalidKeyPart = "<invalid>"
Expand Down Expand Up @@ -477,6 +484,31 @@ func ParseLinkStateKey(key string) (ifaceName string, isLinkUp bool, isLinkState
return
}

/* Interface with IP address (derived property) */

// InterfaceWithIPKey returns key derived from every VPP interface but created only
// after at least one IP address was assigned to it.
func InterfaceWithIPKey(ifaceName string) string {
if ifaceName == "" {
ifaceName = InvalidKeyPart
}
return strings.Replace(interfaceWithIPKeyTemplate, "{iface}", ifaceName, 1)
}

// ParseInterfaceWithIPKey parses key derived from every VPP interface but created only
// after at least one IP address was assigned to it
func ParseInterfaceWithIPKey(key string) (ifaceName string, isInterfaceWithIPKey bool) {
if suffix := strings.TrimPrefix(key, "vpp/interface/"); suffix != key {
if prefix := strings.TrimSuffix(suffix, "/has-IP-address"); prefix != suffix {
if prefix != InvalidKeyPart {
ifaceName = prefix
isInterfaceWithIPKey = true
}
}
}
return
}

/* Rx placement (derived) */

// RxPlacementKey returns a key representing rx-placement configured for a given
Expand Down
83 changes: 83 additions & 0 deletions api/models/vpp/interfaces/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1152,3 +1152,86 @@ func TestParseRxModesKey(t *testing.T) {
})
}
}

func TestInterfaceWithIPKey(t *testing.T) {
tests := []struct {
name string
iface string
expectedKey string
}{
{
name: "memif",
iface: "memif0",
expectedKey: "vpp/interface/memif0/has-IP-address",
},
{
name: "invalid interface name",
iface: "",
expectedKey: "vpp/interface/<invalid>/has-IP-address",
},
{
name: "Gbe interface",
iface: "GigabitEthernet0/8/0",
expectedKey: "vpp/interface/GigabitEthernet0/8/0/has-IP-address",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
key := InterfaceWithIPKey(test.iface)
if key != test.expectedKey {
t.Errorf("failed for: iface=%s\n"+
"expected key:\n\t%q\ngot key:\n\t%q",
test.iface, test.expectedKey, key)
}
})
}
}

func TestParseInterfaceWithIPKey(t *testing.T) {
tests := []struct {
name string
key string
expectedIface string
expectedIsIfaceWithIPKey bool
}{
{
name: "memif",
key: "vpp/interface/memif0/has-IP-address",
expectedIface: "memif0",
expectedIsIfaceWithIPKey: true,
},
{
name: "Gbe",
key: "vpp/interface/GigabitEthernet0/8/0/has-IP-address",
expectedIface: "GigabitEthernet0/8/0",
expectedIsIfaceWithIPKey: true,
},
{
name: "invalid interface name",
key: "vpp/interface/<invalid>/has-IP-address",
expectedIsIfaceWithIPKey: false,
},
{
name: "missing has-IP-address suffix",
key: "vpp/interface/<invalid>",
expectedIsIfaceWithIPKey: false,
},
{
name: "not has-IP-address key",
key: "vpp/interface/memif0/address/192.168.1.12/24",
expectedIsIfaceWithIPKey: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
iface, isInterfaceWithIPKey := ParseInterfaceWithIPKey(test.key)
if isInterfaceWithIPKey != test.expectedIsIfaceWithIPKey {
t.Errorf("expected isInterfaceWithIPKey: %v\tgot: %v",
test.expectedIsIfaceWithIPKey, isInterfaceWithIPKey)
}
if iface != test.expectedIface {
t.Errorf("expected iface: %s\tgot: %s", test.expectedIface, iface)
}
})
}
}
12 changes: 11 additions & 1 deletion plugins/vpp/ifplugin/descriptor/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,9 @@ func (d *InterfaceDescriptor) Dependencies(key string, intf *interfaces.Interfac
// - one empty value for every IP address to be assigned to the interface
// - one empty value for VRF table to put the interface into
// - one value with interface configuration reduced to RxMode if set
// - one Interface_RxPlacement for every queue with configured Rx placement.
// - one Interface_RxPlacement for every queue with configured Rx placement
// - one empty value which will be created once at least one IP address is
// assigned to the interface.
func (d *InterfaceDescriptor) DerivedValues(key string, intf *interfaces.Interface) (derValues []kvs.KeyValuePair) {
// unnumbered interface
if intf.GetUnnumbered() != nil {
Expand Down Expand Up @@ -622,6 +624,14 @@ func (d *InterfaceDescriptor) DerivedValues(key string, intf *interfaces.Interfa
})
}

// with-IP address (property)
if len(intf.GetIpAddresses()) > 0 {
derValues = append(derValues, kvs.KeyValuePair{
Key: interfaces.InterfaceWithIPKey(intf.GetName()),
Value: &prototypes.Empty{},
})
}

// TODO: define derived value for UP/DOWN state (needed for subinterfaces)

return derValues
Expand Down
85 changes: 85 additions & 0 deletions plugins/vpp/ifplugin/descriptor/interface_with_address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) 2019 Cisco and/or its affiliates.
//
// 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 descriptor

import (
"github.com/gogo/protobuf/proto"
"github.com/ligato/cn-infra/logging"

interfaces "github.com/ligato/vpp-agent/api/models/vpp/interfaces"
kvs "github.com/ligato/vpp-agent/plugins/kvscheduler/api"
)

const (
// InterfaceWithAddressDescriptorName is the name of the descriptor for marking
// interfaces with at least one IP address assigned.
InterfaceWithAddressDescriptorName = "vpp-interface-has-address"

// dependency labels
interfaceHasIPDep = "interface-has-IP"
)

// InterfaceWithAddrDescriptor assigns property key-value pairs to interfaces
// with at least one IP address.
type InterfaceWithAddrDescriptor struct {
log logging.Logger
}

// NewInterfaceWithAddrDescriptor creates a new instance of InterfaceWithAddrDescriptor.
func NewInterfaceWithAddrDescriptor(log logging.PluginLogger) *kvs.KVDescriptor {

descrCtx := &InterfaceWithAddrDescriptor{
log: log.NewLogger("interface-has-address-descriptor"),
}
return &kvs.KVDescriptor{
Name: InterfaceWithAddressDescriptorName,
KeySelector: descrCtx.IsInterfaceWithAddressKey,
Create: descrCtx.Create,
Delete: descrCtx.Delete,
Dependencies: descrCtx.Dependencies,
}
}

// IsInterfaceWithAddressKey returns true if the key is a property assigned to interface
// with at least one IP address.
func (d *InterfaceWithAddrDescriptor) IsInterfaceWithAddressKey(key string) bool {
_, isIfaceWithIPKey := interfaces.ParseInterfaceWithIPKey(key)
return isIfaceWithIPKey
}

// Create is NOOP (the key-value pair is a property).
func (d *InterfaceWithAddrDescriptor) Create(key string, emptyVal proto.Message) (metadata kvs.Metadata, err error) {
return nil, nil
}

// Delete is NOOP (the key-value pair is a property)
func (d *InterfaceWithAddrDescriptor) Delete(key string, emptyVal proto.Message, metadata kvs.Metadata) (err error) {
return nil
}

// Dependencies ensures that the property is created only after at least one IP
// address is successfully assigned to the interface.
func (d *InterfaceWithAddrDescriptor) Dependencies(key string, emptyVal proto.Message) (deps []kvs.Dependency) {
ifaceName, _ := interfaces.ParseInterfaceWithIPKey(key)
return []kvs.Dependency{
{
Label: interfaceHasIPDep,
AnyOf: kvs.AnyOfDependency{
KeyPrefixes: []string{interfaces.InterfaceAddressPrefix(ifaceName)},
},
},
}
}

4 changes: 1 addition & 3 deletions plugins/vpp/ifplugin/descriptor/unnumbered.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,7 @@ func (d *UnnumberedIfDescriptor) Dependencies(key string, unIntf *interfaces.Int
deps = []kvs.Dependency{
{
Label: unnumberedInterfaceHasIPDep,
AnyOf: kvs.AnyOfDependency{
KeyPrefixes: []string{interfaces.InterfaceAddressPrefix(unIntf.InterfaceWithIp)},
},
Key: interfaces.InterfaceWithIPKey(unIntf.InterfaceWithIp),
},
}

Expand Down
3 changes: 3 additions & 0 deletions plugins/vpp/ifplugin/ifplugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,14 @@ func (p *IfPlugin) Init() error {
p.ifHandler, p.intfIndex, p.Log)
linkStateDescriptor, p.linkStateDescriptor = descriptor.NewLinkStateDescriptor(
p.KVScheduler, p.ifHandler, p.intfIndex, p.Log)

rxModeDescriptor := descriptor.NewRxModeDescriptor(p.ifHandler, p.intfIndex, p.Log)
rxPlacementDescriptor := descriptor.NewRxPlacementDescriptor(p.ifHandler, p.intfIndex, p.Log)
addrDescriptor := descriptor.NewInterfaceAddressDescriptor(p.ifHandler, p.intfIndex, p.Log)
unIfDescriptor := descriptor.NewUnnumberedIfDescriptor(p.ifHandler, p.intfIndex, p.Log)
bondIfDescriptor, _ := descriptor.NewBondedInterfaceDescriptor(p.ifHandler, p.intfIndex, p.Log)
vrfDescriptor := descriptor.NewInterfaceVrfDescriptor(p.ifHandler, p.intfIndex, p.Log)
withAddrDescriptor := descriptor.NewInterfaceWithAddrDescriptor(p.Log)

err = p.KVScheduler.RegisterKVDescriptor(
dhcpDescriptor,
Expand All @@ -196,6 +198,7 @@ func (p *IfPlugin) Init() error {
unIfDescriptor,
bondIfDescriptor,
vrfDescriptor,
withAddrDescriptor,
)
if err != nil {
return err
Expand Down