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

docs: Add example for developing agents with custom VPP plugins #1665

Merged
merged 5 commits into from
Jun 9, 2020
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
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
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.

Original file line number Diff line number Diff line change
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.

Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Loading