Skip to content

Commit

Permalink
docs: Add example for developing agents with custom VPP plugins (#1665)
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrej-fabry committed Jun 9, 2020
1 parent 218575e commit 639daee
Show file tree
Hide file tree
Showing 18 changed files with 1,581 additions and 118 deletions.
6 changes: 4 additions & 2 deletions Makefile
Expand Up @@ -105,7 +105,8 @@ clean-cmd: ## Clean commands

examples: ## Build examples
@echo "# building examples"
cd examples/custom_model && go build -tags="${GO_BUILD_TAGS}" ${GO_BUILD_ARGS}
cd examples/customize/custom_api_model && go build -tags="${GO_BUILD_TAGS}" ${GO_BUILD_ARGS}
cd examples/customize/custom_vpp_plugin && go build -tags="${GO_BUILD_TAGS}" ${GO_BUILD_ARGS}
cd examples/govpp_call && go build -tags="${GO_BUILD_TAGS}" ${GO_BUILD_ARGS}
cd examples/grpc_vpp/remote_client && go build -tags="${GO_BUILD_TAGS}" ${GO_BUILD_ARGS}
cd examples/grpc_vpp/notifications && go build -tags="${GO_BUILD_TAGS}" ${GO_BUILD_ARGS}
Expand All @@ -124,7 +125,8 @@ examples: ## Build examples

clean-examples: ## Clean examples
@echo "# cleaning examples"
cd examples/custom_model && go clean
cd examples/customize/custom_api_model && go clean
cd examples/customize/custom_vpp_plugin && go clean
cd examples/govpp_call && go clean
cd examples/grpc_vpp/remote_client && go clean
cd examples/grpc_vpp/notifications && go clean
Expand Down
13 changes: 0 additions & 13 deletions examples/custom_model/proto/models.go

This file was deleted.

Expand Up @@ -19,20 +19,20 @@ import (
"fmt"
"log"
"net"
"sync"
"os"
"time"

"github.com/golang/protobuf/proto"
"github.com/namsral/flag"
"go.ligato.io/cn-infra/v2/agent"
"go.ligato.io/cn-infra/v2/infra"
"go.ligato.io/cn-infra/v2/logging/logrus"
"go.ligato.io/cn-infra/v2/logging"
"google.golang.org/grpc"

"go.ligato.io/vpp-agent/v3/client"
"go.ligato.io/vpp-agent/v3/client/remoteclient"
"go.ligato.io/vpp-agent/v3/cmd/vpp-agent/app"
mymodel "go.ligato.io/vpp-agent/v3/examples/custom_model/proto"
"go.ligato.io/vpp-agent/v3/examples/customize/custom_api_model/proto/custom"
"go.ligato.io/vpp-agent/v3/plugins/orchestrator"
"go.ligato.io/vpp-agent/v3/proto/ligato/linux"
linux_interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/linux/interfaces"
Expand All @@ -42,101 +42,84 @@ import (
vpp_l2 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l2"
)

//go:generate protoc --proto_path=. --go_out=paths=source_relative:. proto/model.proto
//go:generate protoc --proto_path=. --go_out=paths=source_relative:. proto/custom/model.proto

var (
address = flag.String("address", "127.0.0.1:9111", "address of GRPC server")
socketType = flag.String("socket-type", "tcp", "socket type [tcp, tcp4, tcp6, unix, unixpacket]")

dialTimeout = time.Second * 3
clientType = flag.String("client", "local", "Client type used in demonstration [local, remote]")
)

var exampleFinished = make(chan struct{})
func init() {
log.SetFlags(log.Lshortfile | log.Lmicroseconds)
}

func main() {
ep := &ExamplePlugin{}
ep.Deps = Deps{
VPP: app.DefaultVPP(),
Linux: app.DefaultLinux(),
Orchestrator: &orchestrator.DefaultPlugin,
}
ep.SetName("custom-model-example")
ep.Setup()
example := NewExample()

a := agent.NewAgent(
agent.AllPlugins(ep),
agent.QuitOnClose(exampleFinished),
agent.AllPlugins(example),
)
if err := a.Run(); err != nil {

if err := a.Start(); err != nil {
log.Fatal(err)
}
}

// ExamplePlugin demonstrates the use of the remoteclient to locally transport example configuration into the default VPP plugins.
type ExamplePlugin struct {
Deps
var c client.ConfigClient

switch *clientType {
case "local":
// Local client - using direct in-process calls.
c = client.LocalClient
logging.Info("Using Local Client - in-process calls")

case "remote":
// Remote client - using gRPC connection to the agent.
conn, err := grpc.Dial("unix",
grpc.WithInsecure(),
grpc.WithDialer(dialer("tcp", "127.0.0.1:9111", time.Second*3)),
)
if err != nil {
log.Fatal(err)
}
defer conn.Close()

c = remoteclient.NewClientGRPC(conn)
logging.Info("Using Remote Client - gRPC API")

default:
log.Printf("unknown client type: %q", *clientType)
flag.Usage()
os.Exit(1)
}

conn *grpc.ClientConn
demonstrateClient(c)

wg sync.WaitGroup
cancel context.CancelFunc
if err := a.Stop(); err != nil {
log.Fatal(err)
}
}

type Deps struct {
// Example demonstrates the use of the remoteclient to locally transport example configuration into the default VPP plugins.
type Example struct {
infra.PluginDeps
app.VPP
app.Linux
Orchestrator *orchestrator.Plugin
}

// Init initializes example plugin.
func (p *ExamplePlugin) Init() (err error) {
_, p.cancel = context.WithCancel(context.Background())

// Set up connection to the server.
p.conn, err = grpc.Dial("unix",
grpc.WithInsecure(),
grpc.WithDialer(dialer(*socketType, *address, dialTimeout)),
)
if err != nil {
return err
func NewExample() *Example {
ep := &Example{
VPP: app.DefaultVPP(),
Linux: app.DefaultLinux(),
Orchestrator: &orchestrator.DefaultPlugin,
}

p.Log.Info("Init complete")
return nil
}

// AfterInit executes client demo.
func (p *ExamplePlugin) AfterInit() (err error) {
go func() {
time.Sleep(time.Second)

// remoteclient
c := remoteclient.NewClientGRPC(p.conn)
demonstrateClient(c)

//time.Sleep(time.Second * 3)

// localclient
//demonstrateClient(client.LocalClient)

logrus.DefaultLogger().Info("Closing example")
close(exampleFinished)
}()
return nil
ep.SetName("custom-api-model-example")
ep.SetupLog()
return ep
}

// Close cleans up the resources.
func (p *ExamplePlugin) Close() error {
logrus.DefaultLogger().Info("Closing example")

p.cancel()
p.wg.Wait()

if err := p.conn.Close(); err != nil {
return err
}

// Init initializes example plugin.
func (p *Example) Init() (err error) {
p.Log.Info("Initialization complete")
return nil
}

Expand All @@ -145,7 +128,6 @@ func demonstrateClient(c client.ConfigClient) {
Compact: true,
ExpandAny: true,
}
log.SetFlags(log.Lshortfile | log.Lmicroseconds)

// List known models
fmt.Println("# ==========================================")
Expand All @@ -165,7 +147,7 @@ func demonstrateClient(c client.ConfigClient) {
fmt.Println("# ==========================================")
fmt.Println("# Requesting config resync..")
fmt.Println("# ==========================================")
customModel := &mymodel.MyModel{
customModel := &custom.MyModel{
Name: "TheModel",
}
err = c.ResyncConfig(
Expand All @@ -185,13 +167,13 @@ func demonstrateClient(c client.ConfigClient) {
fmt.Println("# ==========================================")
memif1.Enabled = false
memif1.Mtu = 666
custom := &mymodel.MyModel{
mymodel := &custom.MyModel{
Name: "my1",
Mynum: 33,
Value: 33,
}

req := c.ChangeRequest()
req.Update(afp1, memif1, bd1, vppRoute1, custom)
req.Update(afp1, memif1, bd1, vppRoute1, mymodel)
req.Delete(memif2)
if err := req.Send(context.Background()); err != nil {
log.Fatalln(err)
Expand All @@ -205,7 +187,7 @@ func demonstrateClient(c client.ConfigClient) {
type config struct {
VPP vpp.ConfigData
Linux linux.ConfigData
MyModels []*mymodel.MyModel
MyModels []*custom.MyModel
}
var cfg config
if err := c.GetConfig(&cfg.VPP, &cfg.Linux, &cfg); err != nil {
Expand Down Expand Up @@ -321,10 +303,4 @@ var (
GwAddr: "10.10.5.254",
Scope: linux_l3.Route_GLOBAL,
}
routeBad = &linux.Route{
DstNetwork: "192.168.6.0/24",
OutgoingInterface: "myVETH1",
GwAddr: "10.10.3.2545",
Scope: linux_l3.Route_GLOBAL,
}
)

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

@@ -1,8 +1,8 @@
syntax = "proto3";

package mymodel;
package custom;

message MyModel {
string name = 1;
int32 mynum = 2;
int32 value = 2;
}
27 changes: 27 additions & 0 deletions examples/customize/custom_api_model/proto/custom/models.go
@@ -0,0 +1,27 @@
// Copyright (c) 2020 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 custom

import (
"go.ligato.io/vpp-agent/v3/pkg/models"
)

func init() {
models.Register(&MyModel{}, models.Spec{
Module: "custom",
Type: "mymodel",
Version: "v1",
})
}
13 changes: 13 additions & 0 deletions examples/customize/custom_vpp_plugin/README.md
@@ -0,0 +1,13 @@
# Custom VPP plugin

The Custom VPP plugin example contains a working example of custom agent which
adds support for a custom VPP plugin. This example can serve as a skeleton
code for developing custom agents adding new VPP functionality that is not
part of official VPP Agent.

## Structure

- [binapi](binapi/) - contains generated Go code of VPP binary API for a specific VPP version
- [proto](proto/) - contains Protobuf definition and model registration for the Northbound API
- [syslog](syslog/) - contains Ligato plugin Syslog that initializes vppcalls handler and registers KV descriptors that describe the Syslog models and their behaviour (CRUD operations, validation, dependencies, etc.)
- [main.go](main.go) - contains the agent example that wires all the components together and runs the agent

0 comments on commit 639daee

Please sign in to comment.