Skip to content
Snippets Groups Projects
southbound.go 5.45 KiB
Newer Older
  • Learn to ignore specific revisions
  • Manuel Kieweg's avatar
    Manuel Kieweg committed
    package nucleus
    
    
    import (
    
    	"fmt"
    
    Andre Sterba's avatar
    Andre Sterba committed
    	"reflect"
    
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	"code.fbi.h-da.de/danet/gosdn/nucleus/errors"
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound"
    	"code.fbi.h-da.de/danet/gosdn/interfaces/southbound"
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	"code.fbi.h-da.de/danet/yang-models/generated/openconfig"
    
    	"github.com/google/uuid"
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	gpb "github.com/openconfig/gnmi/proto/gnmi"
    	"github.com/openconfig/goyang/pkg/yang"
    
    	"github.com/openconfig/ygot/util"
    
    	"github.com/openconfig/ygot/ygot"
    
    	"github.com/openconfig/ygot/ytypes"
    
    	log "github.com/sirupsen/logrus"
    
    var csbi *Csbi
    
    func init() {
    	csbi = &Csbi{id: uuid.New()}
    }
    
    
    // NewSBI creates a SouthboundInterface of a given type.
    
    func NewSBI(southbound spb.Type, sbUUID ...uuid.UUID) southbound.SouthboundInterface {
    	var id uuid.UUID
    
    	if len(sbUUID) == 0 {
    		id = uuid.New()
    	} else {
    		id = sbUUID[0]
    	}
    
    
    	switch southbound {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	case spb.Type_OPENCONFIG:
    
    		return &OpenConfig{id: id}
    
    	default:
    		return nil
    	}
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    // OpenConfig is the implementation of an OpenConfig SBI.
    // The struct holds the YANG schema and a function that
    // returns an SBI specific SetNode function.
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    type OpenConfig struct {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	schema *ytypes.Schema
    
    	id     uuid.UUID
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    // SbiIdentifier returns the string representation of
    // the SBI
    // deprecated
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func (oc *OpenConfig) SbiIdentifier() string {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	return "openconfig"
    }
    
    
    // Schema returns a ygot generated openconfig Schema as ytypes.Schema
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func (oc *OpenConfig) Schema() *ytypes.Schema {
    	schema, err := openconfig.Schema()
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		log.Fatal(err)
    	}
    	return schema
    }
    
    // SetNode injects OpenConfig specific model representation to the transport.
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    // Needed for type assertion.
    
    func (oc *OpenConfig) SetNode(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error {
    	return ytypes.SetNode(schema, root.(*openconfig.Device), path, val, opts...)
    
    // Unmarshal injects OpenConfig specific model representation to the transport.
    
    // Needed for type assertion.
    
    func (oc *OpenConfig) Unmarshal(bytes []byte, fields []string, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error {
    	return unmarshal(oc.Schema(), bytes, fields, goStruct, opt...)
    
    // unmarshal parses gNMI response to a go struct. If it's a root level response
    // it uses
    func unmarshal(schema *ytypes.Schema, bytes []byte, fields []string, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error {
    
    	defer func() {
    		if r := recover(); r != nil {
    
    			log.Error(r.(error))
    
    	}()
    
    	// Load SBI definition
    
    	root, err := ygot.DeepCopy(schema.Root)
    
    	if err != nil {
    
    		return err
    
    	validatedDeepCopy, ok := root.(ygot.ValidatedGoStruct)
    	if !ok {
    		return &errors.ErrInvalidTypeAssertion{}
    
    	ygot.BuildEmptyTree(validatedDeepCopy)
    
    	var fieldStruct ygot.ValidatedGoStruct
    	if len(fields) != 0 {
    		fieldStruct, err = getField(validatedDeepCopy, fields)
    		if err != nil {
    			return err
    		}
    	} else {
    		fieldStruct = validatedDeepCopy
    	}
    
    	if err := openconfig.Unmarshal(bytes, fieldStruct, opt...); err != nil {
    		return err
    	}
    	ygot.PruneEmptyBranches(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))
    
    	f := fields[0]
    	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)
    
    		return getField(outStruct, fields[1:])
    
    	return outStruct, nil
    
    // ID returns the ID of the OpenConfig SBI
    func (oc *OpenConfig) ID() uuid.UUID {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    
    // Type returns the Southbound's type
    func (oc *OpenConfig) Type() spb.Type { return spb.Type_OPENCONFIG }
    
    
    // Csbi is a stub for the containerised SBI functionality.
    // It holds the standard goSDN OPENCONFIG schema for minimum
    // compatibility
    type Csbi struct {
    	schema *ytypes.Schema
    	id     uuid.UUID
    }
    
    // SbiIdentifier returns the identifier as a
    func (csbi *Csbi) SbiIdentifier() string {
    	return "csbi"
    }
    
    // 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.(*openconfig.Device), path, val, opts...)
    }
    
    // Unmarshal injects schema specific model representation to the transport.
    // Needed for type assertion.
    func (csbi *Csbi) Unmarshal(bytes []byte, fields []string, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error {
    	oc := OpenConfig{}
    	return unmarshal(oc.Schema(), bytes, fields, goStruct, opt...)
    }
    
    // Schema is holding the default OpenConfig schema for minimal compatibility
    // to gosdn interfaces
    func (csbi *Csbi) Schema() *ytypes.Schema {
    	schema, err := openconfig.Schema()
    	csbi.schema = schema
    	if err != nil {
    		log.Fatal(err)
    	}
    	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
    }