-
Fabian Seidl authoredFabian Seidl authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
networkElement.go 11.78 KiB
package nucleus
import (
"encoding/json"
spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
"code.fbi.h-da.de/danet/gosdn/controller/conflict"
"code.fbi.h-da.de/danet/gosdn/controller/customerrs"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkelement"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/transport"
"github.com/docker/docker/pkg/namesgenerator"
"github.com/google/uuid"
"github.com/openconfig/ygot/ygot"
"go.mongodb.org/mongo-driver/bson"
"google.golang.org/protobuf/proto"
)
// NewNetworkElement creates a network element.
func NewNetworkElement(
name string,
uuidInput uuid.UUID,
opt *tpb.TransportOption,
sbi southbound.SouthboundInterface,
pndID uuid.UUID,
metadata conflict.Metadata,
) (networkelement.NetworkElement, error) {
t, err := NewTransport(opt, sbi)
if err != nil {
return nil, err
}
// TODO: this needs to check the case that the uuidInput is set, as the
// same uuid may be already stored.
if uuidInput == uuid.Nil {
uuidInput = uuid.New()
}
if name == "" {
name = namesgenerator.GetRandomName(0)
}
// We want a representation of the MNE's config (the SBI-schema's root created through ygot),
// but do not want to work on the sbi.Schema() directly.
// So the root of sbi.Schema() is never changed when a set or get on a network element will be called.
root, err := ygot.DeepCopy(sbi.Schema().Root)
if err != nil {
return nil, err
}
ygotDeepCopy, ok := root.(ygot.GoStruct)
if !ok {
return nil, &customerrs.InvalidTypeAssertionError{
Value: root,
Type: (*ygot.ValidatedGoStruct)(nil),
}
}
if opt.Type == spb.Type_TYPE_CONTAINERISED {
return &CsbiNetworkElement{
CommonNetworkElement: CommonNetworkElement{
UUID: uuidInput,
Model: ygotDeepCopy,
sbi: sbi,
transport: t,
name: name,
transportOptions: opt,
Metadata: metadata,
},
}, nil
}
return &CommonNetworkElement{
UUID: uuidInput,
Model: ygotDeepCopy,
sbi: sbi,
transport: t,
name: name,
transportOptions: opt,
PndID: pndID,
Metadata: metadata,
}, nil
}
// CommonNetworkElement represents an MNE.
type CommonNetworkElement struct {
// UUID represents the Network Elements UUID
UUID uuid.UUID
// Network Element embeds a ygot.GoStruct containing the network element details
Model ygot.GoStruct
// SBI is the network element's southbound interface implementation
sbi southbound.SouthboundInterface
// Transport is the network element's Transport implementation
transport transport.Transport
// Name is the network element's human readable name
name string
transportOptions *tpb.TransportOption
Metadata conflict.Metadata
// ID of the PND this network element is associated with.
PndID uuid.UUID
}
// ID returns the UUID of the Network Element.
func (n *CommonNetworkElement) ID() uuid.UUID {
return n.UUID
}
// GetModel returns the ygot representation of the Network Element.
func (n *CommonNetworkElement) GetModel() ygot.GoStruct {
return n.Model
}
// CreateModelCopy returns a copy of the ygot representation of the Network Element.
func (n *CommonNetworkElement) CreateModelCopy() (ygot.ValidatedGoStruct, error) {
return createValidatedCopy(n)
}
// Transport returns the Transport of the network element.
func (n *CommonNetworkElement) Transport() transport.Transport {
return n.transport
}
// TransportAddress returns the TransportAddress of the network element.
func (n *CommonNetworkElement) TransportAddress() string {
return n.transportOptions.Address
}
// Name returns the name of the network element.
func (n *CommonNetworkElement) Name() string {
return n.name
}
// SBI returns the sbi of the Network Element.
func (n *CommonNetworkElement) SBI() southbound.SouthboundInterface {
return n.sbi
}
// SetTransport sets the Network Element's Transport.
func (n *CommonNetworkElement) SetTransport(t transport.Transport) {
n.transport = t
}
// SetName sets the Network Element's name.
func (n *CommonNetworkElement) SetName(name string) {
n.name = name
}
// SetSBI sets the Network Element's SBI.
func (n *CommonNetworkElement) SetSBI(sbi southbound.SouthboundInterface) {
n.sbi = sbi
}
// ProcessResponse processes a response for the Network Element.
func (n *CommonNetworkElement) ProcessResponse(resp proto.Message) error {
return n.transport.ProcessResponse(resp, n.Model, n.sbi.Schema())
}
// IsTransportValid returns a boolean if the transport of a network element is valid.
func (n *CommonNetworkElement) IsTransportValid() bool {
if n.transportOptions != nil && n.transportOptions.Address != "" {
return true
}
return false
}
// GetMetadata returns the metadate of a network element.
func (n *CommonNetworkElement) GetMetadata() conflict.Metadata {
return n.Metadata
}
// CsbiNetworkElement is used for the cSBI functionality.
type CsbiNetworkElement struct {
CommonNetworkElement
}
// ID returns the UUID of the Network Element.
func (n *CsbiNetworkElement) ID() uuid.UUID {
return n.UUID
}
// GetModel returns the ygot representation of the Network Element.
func (n *CsbiNetworkElement) GetModel() ygot.GoStruct {
return n.Model
}
// CreateModelCopy returns a copy of the ygot representation of the Network Element.
func (n *CsbiNetworkElement) CreateModelCopy() (ygot.ValidatedGoStruct, error) {
return createValidatedCopy(n)
}
// Transport returns the Transport of the network element.
func (n *CsbiNetworkElement) Transport() transport.Transport {
return n.transport
}
// Name returns the name of the network element.
func (n *CsbiNetworkElement) Name() string {
return n.name
}
// SBI returns the sbi of the Network Element.
func (n *CsbiNetworkElement) SBI() southbound.SouthboundInterface {
return n.sbi
}
// GetMetadata returns the metadate of a network element.
func (n *CsbiNetworkElement) GetMetadata() conflict.Metadata {
return n.Metadata
}
// ProcessResponse processes a response for the Network Element.
func (n *CsbiNetworkElement) ProcessResponse(resp proto.Message) error {
// TODO: callback to send response to caller
return n.transport.ProcessResponse(resp, n.Model, n.sbi.Schema())
}
func createValidatedCopy(n networkelement.NetworkElement) (ygot.ValidatedGoStruct, error) {
cpy, err := ygot.DeepCopy(n.GetModel())
ygot.BuildEmptyTree(cpy)
if err != nil {
return nil, err
}
validatedCpy, ok := cpy.(ygot.ValidatedGoStruct)
if !ok {
return nil, customerrs.InvalidTypeAssertionError{
Value: validatedCpy,
Type: (*ygot.ValidatedGoStruct)(nil),
}
}
return validatedCpy, nil
}
// IsTransportValid returns a boolean if the transport of a network element is valid.
func (n *CsbiNetworkElement) IsTransportValid() bool {
if n.transportOptions != nil && n.transportOptions.Address != "" {
return true
}
return false
}
// MarshalJSON implements the MarshalJSON interface to store a network element as JSON.
func (n *CommonNetworkElement) MarshalJSON() ([]byte, error) {
var transportType string
var transportAddress string
var transportUsername string
var transportPassword string
var transportOptionType spb.Type
// Handling of these cases is necessary as we use partial network elements for testing.
// eg. in most tests no transport or sbi is defined.
// The marshaller will crash if we want to access a nil field.
if n.transport == nil || n.transportOptions == nil {
transportType = "testing"
transportAddress = "testing"
transportUsername = "testing"
transportPassword = "testing"
transportOptionType = spb.Type_TYPE_OPENCONFIG
} else {
transportType = n.transport.Type()
transportAddress = n.transportOptions.Address
transportUsername = n.transportOptions.Username
transportPassword = n.transportOptions.Password
transportOptionType = n.transportOptions.Type
}
var sbiUUID uuid.UUID
if n.sbi == nil {
sbiUUID = uuid.UUID{}
} else {
sbiUUID = n.sbi.ID()
}
var pndUUID uuid.UUID
if n.PndID == uuid.Nil {
pndUUID = uuid.UUID{}
} else {
pndUUID = n.PndID
}
modelAsString, err := ygot.EmitJSON(n.Model, n.getYgotEmitJSONConfig())
if err != nil {
return []byte{}, err
}
return json.Marshal(&struct {
ID uuid.UUID `json:"id,omitempty"`
Name string `json:"name,omitempty"`
TransportType string `json:"transport_type,omitempty"`
TransportAddress string `json:"transport_address,omitempty"`
TransportUsername string `json:"transport_username,omitempty"`
TransportPassword string `json:"transport_password,omitempty"`
TransportOptionType spb.Type `json:"transport_option"`
SBI uuid.UUID `json:"sbi,omitempty"`
Model string `bson:"model,omitempty"`
PndID uuid.UUID `json:"pnd_id,omitempty"`
}{
ID: n.ID(),
Name: n.Name(),
TransportType: transportType,
TransportAddress: transportAddress,
TransportUsername: transportUsername,
TransportPassword: transportPassword,
TransportOptionType: transportOptionType,
SBI: sbiUUID,
Model: modelAsString,
PndID: pndUUID,
})
}
// MarshalBSON implements the MarshalBSON interface to store a network element as BSON.
func (n *CommonNetworkElement) MarshalBSON() ([]byte, error) {
var transportType string
var transportAddress string
var transportUsername string
var transportPassword string
var transportOptionType spb.Type
// Handling of these cases is necessary as we use partial network elements for testing.
// eg. in most tests no transport or sbi is defined.
// The marshaller will crash if we want to access a nil field.
if n.transport == nil || n.transportOptions == nil {
transportType = "testing"
transportAddress = "testing"
transportUsername = "testing"
transportPassword = "testing"
transportOptionType = spb.Type_TYPE_OPENCONFIG
} else {
transportType = n.transport.Type()
transportAddress = n.transportOptions.Address
transportUsername = n.transportOptions.Username
transportPassword = n.transportOptions.Password
transportOptionType = n.transportOptions.Type
}
modelAsString, err := ygot.EmitJSON(n.Model, n.getYgotEmitJSONConfig())
if err != nil {
return []byte{}, err
}
return bson.Marshal(&struct {
ID string `bson:"_id,omitempty"`
Name string `bson:"name,omitempty"`
TransportType string `bson:"transport_type,omitempty"`
TransportAddress string `bson:"transport_address,omitempty"`
TransportUsername string `bson:"transport_username,omitempty"`
TransportPassword string `bson:"transport_password,omitempty"`
TransportOptionType spb.Type `bson:"transport_option"`
SBI string `bson:"sbi,omitempty"`
Model string `bson:"model,omitempty"`
PndID string `bson:"pnd_id,omitempty"`
}{
ID: n.ID().String(),
Name: n.Name(),
TransportType: transportType,
TransportAddress: transportAddress,
TransportUsername: transportUsername,
TransportPassword: transportPassword,
TransportOptionType: transportOptionType,
SBI: n.sbi.ID().String(),
Model: modelAsString,
PndID: n.PndID.String(),
})
}
// GetModelAsString returns the YANG model of a network element as string.
func (n *CommonNetworkElement) GetModelAsString() (string, error) {
modelAsString, err := ygot.EmitJSON(n.Model, n.getYgotEmitJSONConfig())
if err != nil {
return "", err
}
return modelAsString, nil
}
func (n *CommonNetworkElement) getYgotEmitJSONConfig() *ygot.EmitJSONConfig {
return &ygot.EmitJSONConfig{
Format: ygot.RFC7951,
Indent: "",
SkipValidation: true,
RFC7951Config: &ygot.RFC7951JSONConfig{
AppendModuleName: true,
}}
}