Skip to content
Snippets Groups Projects
Commit 1cdaef7c authored by Malte Bauch's avatar Malte Bauch
Browse files

first steps moving towards plugin creation within the gosdn controller

first basic steps towards a plugin implementation which is handled by
the gosdn itself. the csbi orchestrator still generates code and
launches cSBIs but the plugin itself is build within the goSDN
controller. simplifies the headache of plugin generation for multiple
platforms.
parent a14f824f
No related branches found
No related tags found
No related merge requests found
......@@ -104,11 +104,7 @@ func (s server) CreatePlugin(req *pb.CreateRequest, stream pb.Csbi_CreatePluginS
if err != nil {
return handleRPCError(labels, err)
}
err = buildPlugin(d.ID.String())
if err != nil {
return handleRPCError(labels, err)
}
file, err := os.Open(filepath.Join(d.ID.String(), "plugin.so"))
file, err := os.Open(filepath.Join(d.ID.String(), "gostructs.go"))
if err != nil {
return handleRPCError(labels, err)
}
......
package csbi
import (
"html/template"
"github.com/openconfig/ygot/ygen"
)
import "github.com/openconfig/ygot/ygen"
var pluginStruct = ygen.GoStructCodeSnippet{
StructName: "Csbi",
......@@ -13,55 +9,30 @@ var pluginStruct = ygen.GoStructCodeSnippet{
id uuid.UUID
}`,
Methods: `
func (sbi *Csbi) SbiIdentifier() string {
return "plugin sbi"
// SbiIdentifier returns the identifier as a
func (csbi *Csbi) SbiIdentifier() string {
return "csbi"
}
func (sbi *Csbi) SetNode(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error {
log.WithFields(log.Fields{
"schema": schema.Name,
"path": path,
"val": val,
"opts": opts,
}).Trace("entering SetNode()")
// SetNode injects schema specific model representation to the transport.
// Needed for type assertion.
func (csbi *Csbi) SetNode(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error {
return ytypes.SetNode(schema, root.(*Device), path, val, opts...)
}
func (sbi *Csbi) Schema() *ytypes.Schema {
schema, err := Schema()
sbi.schema = schema
if err != nil {
log.Fatal(err)
}
return schema
}
func (sbi *Csbi) ID() uuid.UUID {
return sbi.id
}
func (sbi *Csbi) Type() spb.Type {
return spb.Type_CONTAINERISED
}
func (sbi *Csbi) Unmarshal(bytes []byte, fields []string, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error {
log.WithFields(log.Fields{}).Trace("entering Unmarshal()")
return unmarshal(sbi.Schema(), bytes, fields, goStruct, opt...)
// Unmarshal injects schema specific model representation to the transport.
// Needed for type assertion.
func (csbi *Csbi) Unmarshal(bytes []byte, path *gpb.Path, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error {
return unmarshal(csbi.Schema(), bytes, path, goStruct, opt...)
}
// unmarshal parses gNMI response to a go struct.
func unmarshal(schema *ytypes.Schema, bytes []byte, fields []string, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error {
//unmarshal parses a gNMI response to a go struct.
func unmarshal(schema *ytypes.Schema, bytes []byte, path *gpb.Path, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error {
defer func() {
if r := recover(); r != nil {
log.Error(r.(error))
}
}()
log.WithFields(log.Fields{
"schema": schema.RootSchema().Name,
"fields": fields,
"bytes": len(bytes),
"opts": opt,
}).Trace("entering unmarshal()")
// Load SBI definition
root, err := ygot.DeepCopy(schema.Root)
......@@ -70,94 +41,58 @@ func unmarshal(schema *ytypes.Schema, bytes []byte, fields []string, goStruct yg
}
validatedDeepCopy, ok := root.(ygot.ValidatedGoStruct)
if !ok {
return fmt.Errorf("no validated go struct")
return &errors.ErrInvalidTypeAssertion{}
}
ygot.BuildEmptyTree(validatedDeepCopy)
log.Trace("built empty tree")
var fieldStruct ygot.ValidatedGoStruct
if len(fields) != 0 {
log.Trace("fetching fields")
fieldStruct, err = getField(validatedDeepCopy, fields)
if err != nil {
return err
}
} else {
log.Trace("using root struct")
fieldStruct = validatedDeepCopy
// returns the node we want to fill with the data contained in 'bytes',
// using the specified 'path'.
createdNode, _, err := ytypes.GetOrCreateNode(schema.RootSchema(), validatedDeepCopy, path)
if err != nil {
return err
}
validatedCreatedNode, ok := createdNode.(ygot.ValidatedGoStruct)
if !ok {
return &errors.ErrInvalidTypeAssertion{}
}
if err := Unmarshal(bytes, fieldStruct, opt...); err != nil {
if err := Unmarshal(bytes, validatedCreatedNode, opt...); err != nil {
return err
}
ygot.PruneEmptyBranches(validatedDeepCopy)
log.Trace("pruned empty branches")
log.Trace("merging structs...")
return ygot.MergeStructInto(goStruct, validatedDeepCopy)
opts := []ygot.MergeOpt{&ygot.MergeOverwriteExistingFields{}}
return ygot.MergeStructInto(goStruct, validatedDeepCopy, opts...)
}
// getField traverses the GoStruct and returns the field that represents the
// tail of the path
func getField(inStruct ygot.ValidatedGoStruct, fields []string) (ygot.ValidatedGoStruct, error) {
defer func() {
if r := recover(); r != nil {
log.Error(r.(error))
}
}()
log.WithFields(log.Fields{
"fields": fields,
}).Trace("getting field")
f := fields[0]
log.Tracef("field name: %v", f)
s := reflect.ValueOf(inStruct)
h := reflect.Indirect(s).FieldByName(f).Interface()
outStruct, ok := h.(ygot.ValidatedGoStruct)
if !ok {
t := reflect.TypeOf(h)
if !(util.IsTypeStruct(t) || util.IsTypeStructPtr(t)) {
return nil, fmt.Errorf("cannot process entry of type %v, request longer or shorter path", t)
}
return nil, fmt.Errorf("expected ValidatedGoStruct got %v", t)
}
if len(fields) > 1 {
log.Trace("fetching fields")
return getField(outStruct, fields[1:])
// Schema is holding the default OpenConfig schema for minimal compatibility
// to gosdn interfaces
func (csbi *Csbi) Schema() *ytypes.Schema {
schema, err := Schema()
csbi.schema = schema
if err != nil {
log.Fatal(err)
}
return outStruct, nil
return schema
}
// ID returns the Southbound's UUID
func (csbi *Csbi) ID() uuid.UUID {
return csbi.id
}
// Type returns the Southbound's type
func (csbi *Csbi) Type() spb.Type {
return spb.Type_CONTAINERISED
}`,
}
const pluginImportAmendmend = `
"code.fbi.h-da.de/danet/gosdn/nucleus/errors"
spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound"
"github.com/google/uuid"
"github.com/openconfig/ygot/util"
log "github.com/sirupsen/logrus"
)
var PluginSymbol Csbi
`
var templater *template.Template
const pluginGoModTemplate = `module << .ModuleName >>
go << .GoVersion >>
require (<<range $element := .Dependencies>>
<<$element.Name>> v<<$element.Version>>
<<end>>)
`
// Module represents a Go module entry in go.mod. Name and version needed.
type Module struct {
Name string `json:"name"`
Version string `json:"version"`
}
// GoMod represents a go.mod file used for templates.
type GoMod struct {
ModuleName string `json:"name"`
GoVersion string `json:"go_version"`
Dependencies []Module `json:"dependencies"`
}
......@@ -3,9 +3,7 @@ package csbi
import (
"bytes"
"context"
"encoding/json"
"fmt"
"html/template"
"io/fs"
"net"
"os"
......@@ -93,11 +91,7 @@ func writeCsbi(ctx context.Context, code *ygen.GeneratedGoCode, path string) err
}
func writePlugin(code *ygen.GeneratedGoCode, path string) error {
err := writeCode(path, code)
if err != nil {
return err
}
return writeGoMod(path)
return writeCode(path, code)
}
func copyFile(path, filename string) error {
......@@ -153,29 +147,3 @@ func writeCode(path string, code *ygen.GeneratedGoCode) error {
}
return nil
}
func writeGoMod(path string) error {
// Read dependencies from JSON file
deps, err := os.ReadFile(filepath.Join("resources", "plugin_deps.json"))
if err != nil {
return err
}
module := GoMod{}
if err := json.Unmarshal(deps, &module); err != nil {
return err
}
// Create go.mod in destination directory and write template
file := filepath.Join(path, "go.mod")
goMod, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY, 0755)
if err != nil {
return err
}
defer goMod.Sync()
templater := template.New("goMod").Delims("<<", ">>")
_, err = templater.Parse(pluginGoModTemplate)
if err != nil {
return err
}
return templater.Execute(goMod, module)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment