Skip to content

Commit

Permalink
Sketch of a sample app, API revisions, controller state, etc. (#27)
Browse files Browse the repository at this point in the history
* Sketch of a sample app, API revisions, controller state, etc.

* Tweaked a test
  • Loading branch information
tomikazi authored Jan 31, 2023
1 parent ad69a54 commit 0b2f8e3
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 21 deletions.
24 changes: 24 additions & 0 deletions pkg/api/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,30 @@ import (
p4api "github.com/p4lang/p4runtime/go/p4/v1"
)

// State represents the various states of controller lifecycle
type State int

// Disconnected => Connected => Synchronizing => Synchronized => Validating => Synchronized

const (
// Disconnected represents the default/initial state
Disconnected State = iota
// Connected represents state where connection to the P4Runtime endpoint has been established
Connected
// Synchronizing represents state where pipeline is being validated and the initial synchronization of entries is in progress
Synchronizing
// Synchronized represents state where all entries have been synchronized
Synchronized
// Validating represents state where the validation of entry synchronization is in progress
Validating
)

// DeviceControl is an abstraction of an entity allowing control over the
// forwarding behavior of a single device
type DeviceControl interface {
// State returns the current state of the controller
State() State

// Read receives a query and returns back all requested control entries on the given channel
Read(ctx context.Context, entities *[]p4api.Entity, ch chan<- []*p4api.Entity) error

Expand All @@ -32,6 +53,9 @@ type DeviceControl interface {

// Version returns the P4Runtime version of the target
Version() string

// TODO: Add means for application to watch the state?
// TODO: Consider changing the read to use iterator pattern rather than a channel
}

// PacketHandler is an abstraction of an entity capable of handling an incoming packet-in
Expand Down
12 changes: 11 additions & 1 deletion pkg/api/translation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package api

import (
"github.com/onosproject/onos-net-lib/pkg/p4utils"
p4info "github.com/p4lang/p4runtime/go/p4/config/v1"
p4api "github.com/p4lang/p4runtime/go/p4/v1"
)
Expand All @@ -31,11 +32,20 @@ type identityTranslator struct {
p4info *p4info.P4Info
}

// NewIdentityTranslator returns a new identity pipeline translator
// NewIdentityTranslator returns a new identity pipeline translator for the specified pipeline info
func NewIdentityTranslator(info *p4info.P4Info) PipelineTranslator {
return &identityTranslator{p4info: info}
}

// NewIdentityTranslatorFromFile returns a new identity pipeline translator for pipeline info loaded from the given file
func NewIdentityTranslatorFromFile(path string) (PipelineTranslator, error) {
info, err := p4utils.LoadP4Info(path)
if err != nil {
return nil, err
}
return NewIdentityTranslator(info), nil
}

// Translate returns the same entities as what was provided to it.
func (t *identityTranslator) Translate(entities *[]p4api.Entity) *[]p4api.Entity {
return entities
Expand Down
11 changes: 10 additions & 1 deletion pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,26 @@ type deviceController struct {
id topo.ID
endpoint string
translator api.PipelineTranslator
store store.EntityStore
version string
state api.State
}

func newDeviceController(id topo.ID, endpoint string, store store.EntityStore, translator api.PipelineTranslator) api.DeviceControl {
func newDeviceController(id topo.ID, endpoint string, entityStore store.EntityStore, translator api.PipelineTranslator) api.DeviceControl {
return &deviceController{
id: id,
endpoint: endpoint,
translator: translator,
store: entityStore,
}
}

// State returns the current state of the controller
func (d *deviceController) State() api.State {
// TODO: Add synchronization
return d.state
}

// Read receives a query and returns back all requested control entries on the given channel
func (d *deviceController) Read(ctx context.Context, entities *[]p4api.Entity, ch chan<- []*p4api.Entity) error {
// TODO: Implement me
Expand Down
13 changes: 7 additions & 6 deletions pkg/controller/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ package controller

import (
"context"
"github.com/onosproject/onos-api/go/onos/stratum"
"github.com/atomix/go-sdk/pkg/primitive"
"github.com/onosproject/onos-api/go/onos/topo"
"github.com/onosproject/onos-control/pkg/api"
"github.com/onosproject/onos-control/pkg/store"
p4api "github.com/p4lang/p4runtime/go/p4/v1"
"sync"
)

type devicesController struct {
api.Devices
role stratum.P4RoleConfig
role *p4api.Role
stores store.Stores

mu sync.RWMutex
Expand All @@ -25,10 +26,10 @@ type devicesController struct {

// NewController creates a new controller for device control contexts using the supplied role descriptor
// and pipeline translator
func NewController(role stratum.P4RoleConfig, stores store.Stores) api.Devices {
func NewController(role *p4api.Role, client primitive.Client) api.Devices {
return &devicesController{
role: role,
stores: stores,
stores: store.NewStoreManager(client),
}
}

Expand All @@ -41,11 +42,11 @@ func (c *devicesController) Add(ctx context.Context, id topo.ID, p4rtEndpoint st
return d, nil
}

store, err := c.stores.Get(ctx, id, translator.FromPipeline())
s, err := c.stores.Get(ctx, id, translator.FromPipeline())
if err != nil {
return nil, err
}
return newDeviceController(id, p4rtEndpoint, store, translator), nil
return newDeviceController(id, p4rtEndpoint, s, translator), nil
}

// Remove requests removal of device control context
Expand Down
30 changes: 18 additions & 12 deletions pkg/store/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/atomix/go-sdk/pkg/test"
"github.com/onosproject/onos-net-lib/pkg/p4utils"
testutils "github.com/onosproject/onos-net-lib/pkg/test"
p4info "github.com/p4lang/p4runtime/go/p4/config/v1"
p4api "github.com/p4lang/p4runtime/go/p4/v1"
"github.com/stretchr/testify/assert"
"math/rand"
Expand All @@ -20,7 +21,7 @@ func TestStoreBasics(t *testing.T) {

ctx := context.TODO()

info, err := p4utils.LoadP4Info("p4info.txt")
info, err := p4utils.LoadP4Info("../../test/p4info.txt")
assert.NoError(t, err)

store, err := NewEntityStore(ctx, client, "foo", info)
Expand All @@ -30,17 +31,7 @@ func TestStoreBasics(t *testing.T) {
assert.Len(t, es.tables, 20)

// Generate a slew of random updates and store them
updates := make([]*p4api.Update, 0, 512)
tl := int32(len(info.Tables))
for i := 0; i < cap(updates); i++ {
tableInfo := info.Tables[rand.Int31n(tl)]
for tableInfo.Size < 128 || tableInfo.IsConstTable {
tableInfo = info.Tables[rand.Int31n(tl)]
}
entry := testutils.GenerateTableEntry(tableInfo, rand.Int31n(10), nil)
update := &p4api.Update{Type: p4api.Update_INSERT, Entity: &p4api.Entity{Entity: &p4api.Entity_TableEntry{TableEntry: entry}}}
updates = append(updates, update)
}
updates := generateRandomUpdates(info)
err = store.Write(ctx, updates)
assert.NoError(t, err)

Expand Down Expand Up @@ -79,6 +70,21 @@ func TestStoreBasics(t *testing.T) {
readEntries(ctx, t, store, query, 0)
}

func generateRandomUpdates(info *p4info.P4Info) []*p4api.Update {
updates := make([]*p4api.Update, 0, 512)
tl := int32(len(info.Tables))
for i := 0; i < cap(updates); i++ {
tableInfo := info.Tables[rand.Int31n(tl)]
for tableInfo.Size < 128 || tableInfo.IsConstTable {
tableInfo = info.Tables[rand.Int31n(tl)]
}
entry := testutils.GenerateTableEntry(tableInfo, rand.Int31n(10), nil)
update := &p4api.Update{Type: p4api.Update_INSERT, Entity: &p4api.Entity{Entity: &p4api.Entity_TableEntry{TableEntry: entry}}}
updates = append(updates, update)
}
return updates
}

func readEntries(ctx context.Context, t *testing.T, store EntityStore, query []*p4api.Entity, count int) []*p4api.Entity {
ch := make(chan *p4api.Entity, 1024)
errs := store.Read(ctx, query, ch)
Expand Down
2 changes: 1 addition & 1 deletion pkg/store/stores_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func TestStoresBasics(t *testing.T) {

ctx := context.TODO()

info, err := p4utils.LoadP4Info("p4info.txt")
info, err := p4utils.LoadP4Info("../../test/p4info.txt")
assert.NoError(t, err)

stores := NewStoreManager(client)
Expand Down
47 changes: 47 additions & 0 deletions test/app/sample.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-FileCopyrightText: 2023-present Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0

// Package app hosts code snippets simulating various application use-cases of the onos-control library
package app

import (
"context"
"github.com/atomix/go-sdk/pkg/client"
"github.com/onosproject/onos-control/pkg/api"
"github.com/onosproject/onos-control/pkg/controller"
"github.com/onosproject/onos-lib-go/pkg/logging"
"github.com/onosproject/onos-net-lib/pkg/p4utils"
)

var log = logging.GetLogger("sample")

// InitExample sketches out steps necessary to initialize the library, add a device controller and query its status.
func InitExample() error {
// Node: Consider creating a StratumRoleBuilder
role := p4utils.NewStratumRole("sample", 0, []byte{}, false, false)
devices := controller.NewController(role, client.NewClient())

// Note: Consider including a utility to easily add all devices from onos-topo using a realm label.
// This would fetch all devices matching the realm-label and the required onos.topo.StratumAgents, and
// onos.provisioner.DeviceConfig aspects, which it would use to get the P4Info from the device provisioner and for
// P4Runtime endpoint. This would require either specifying or internally creating onos-topo and device-provisioner
// client. E.g. sdk.AddRealmDevices(ctx, devices, topoClient, provisionerClient, realmLabel, realmValue)

// Create a translator from the specified p4info file
translator, err := api.NewIdentityTranslatorFromFile("../../test/p4info.txt")
if err != nil {
log.Errorf("Unable to create pipeline translator: %+v", err)
return err
}

ctx := context.Background()
fooDevice, err := devices.Add(ctx, "foo", "fabric-sim:20000", translator)
if err != nil {
log.Errorf("Unable to add controller for device %s: %+v", "foo", err)
return err
}

log.Infof("Controller state: %s", fooDevice.State())
return nil
}
File renamed without changes.

0 comments on commit 0b2f8e3

Please sign in to comment.