From 1cdaef7c36f7ed6e03d5a4754c560d1432b3585e Mon Sep 17 00:00:00 2001
From: Malte Bauch <malte.bauch@stud.h-da.de>
Date: Mon, 17 Jan 2022 18:11:33 +0100
Subject: [PATCH] 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.
---
 grpc.go      |   6 +-
 templates.go | 161 +++++++++++++++------------------------------------
 write.go     |  34 +----------
 3 files changed, 50 insertions(+), 151 deletions(-)

diff --git a/grpc.go b/grpc.go
index d914cfde..13021616 100644
--- a/grpc.go
+++ b/grpc.go
@@ -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)
 		}
diff --git a/templates.go b/templates.go
index ad71ef19..ec0811d3 100644
--- a/templates.go
+++ b/templates.go
@@ -1,10 +1,6 @@
 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"`
-}
diff --git a/write.go b/write.go
index 2b37cfe0..d3233ae7 100644
--- a/write.go
+++ b/write.go
@@ -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)
-}
-- 
GitLab