-
André Sterba authoredAndré Sterba authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
networkElement.go 12.95 KiB
package model
import (
"context"
"encoding/json"
tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
"code.fbi.h-da.de/danet/gosdn/controller/conflict"
"github.com/docker/docker/pkg/namesgenerator"
"github.com/google/uuid"
"github.com/openconfig/ygot/ygot"
log "github.com/sirupsen/logrus"
"go.mongodb.org/mongo-driver/bson"
"google.golang.org/protobuf/proto"
gpb "github.com/openconfig/gnmi/proto/gnmi"
)
// NetworkElement represents an Managed Network Element (MNE) which is managed by
// nucleus.
type NetworkElement interface {
ID() uuid.UUID
GetModel() ([]byte, error)
GetPlugin() PluginIface
GetModelAsFilteredCopy() ([]byte, error)
Transport() Transport
Name() string
ProcessResponse(proto.Message) error
IsTransportValid() bool
GetModelAsString() (string, error)
TransportAddress() string
GetMetadata() conflict.Metadata
PndID() uuid.UUID
}
type LoadedNetworkElement struct {
// ID represents the UUID of the LoadedNetworkElement.
ID string `json:"id" bson:"_id"`
// Name represents the name of the LoadedNetworkElement.
Name string `json:"name,omitempty"`
// TransportType represent the type of the transport in use of the LoadedNetworkElement.
TransportType string `json:"transport_type,omitempty" bson:"transport_type,omitempty"`
// TransportAddress represents the address from which the network element can be reached via the transport method.
TransportAddress string `json:"transport_address,omitempty" bson:"transport_address,omitempty"`
// TransportUsername is used for authentication via the transport method in use.
TransportUsername string `json:"transport_username,omitempty" bson:"transport_username,omitempty"`
// TransportPassword is used for authentication via the transport method in use.
TransportPassword string `json:"transport_password,omitempty" bson:"transport_password,omitempty"`
// Note: deprecated, should be removed.
TransportOptionCsbi bool `json:"transport_option_csbi,omitempty" bson:"transport_option_csbi,omitempty"`
// TransportTLS uses TLS from the client side when if true.
TransportTLS bool `json:"transport_tls,omitempty" bson:"transport_tls,omitempty"`
// SBI indicates the southbound interface, which is used by this network element as UUID.
Plugin string `json:"plugin"`
Model string `json:"model,omitempty" bson:"model,omitempty"`
Metadata conflict.Metadata `json:"metadata" bson:"metadata"`
PndID string `json:"pnd_id" bson:"pnd_id"`
}
// NewNetworkElement creates a network element.
func NewNetworkElement(
name string,
uuidInput uuid.UUID,
opt *tpb.TransportOption,
pndID uuid.UUID,
plugin PluginIface,
metadata conflict.Metadata) (NetworkElement, error) {
t, err := NewTransport(opt, plugin)
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)
}
// if opt.Type == spb.Type_TYPE_CONTAINERISED {
// return &CsbiNetworkElement{
// CommonNetworkElement: CommonNetworkElement{
// UUID: uuidInput,
// Plugin: plugin,
// transport: t,
// name: name,
// transportOptions: opt,
// Metadata: metadata,
// pndID: pndID,
// },
// }, nil
// }
return &CommonNetworkElement{
UUID: uuidInput,
Plugin: plugin,
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
// Plugin embeds a gosdn ygot plugin. Allows to work on a devices config
// based its supported YANG models. The code for this is generated through
// ygot.
Plugin PluginIface
// Transport is the network element's Transport implementation
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() ([]byte, error) {
return n.Plugin.Model(false)
}
// GetModelAsFilteredCopy returns the ygot representation of the Network
// Element, but as copy with read-only fields removed.
func (n *CommonNetworkElement) GetModelAsFilteredCopy() ([]byte, error) {
return n.Plugin.Model(true)
}
// Transport returns the Transport of the network element.
func (n *CommonNetworkElement) Transport() Transport {
return n.transport
}
// Transport returns the Transport of the network element.
func (d *CommonNetworkElement) GetPlugin() PluginIface {
return d.Plugin
}
// 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
}
// SetTransport sets the Network Element's Transport.
func (n *CommonNetworkElement) SetTransport(t Transport) {
n.transport = t
}
// SetName sets the Network Element's name.
func (n *CommonNetworkElement) SetName(name string) {
n.name = name
}
// ProcessResponse processes a response for the Network Element.
func (d *CommonNetworkElement) ProcessResponse(resp proto.Message) error {
return d.transport.ProcessResponse(resp)
}
// 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
}
// PndID returns the ID of the associated PND.
func (n *CommonNetworkElement) PndID() uuid.UUID {
return n.pndID
}
// SetPnd sets the Network Element's PndId.
func (n *CommonNetworkElement) SetPnd(id uuid.UUID) {
n.pndID = id
}
// 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 (d *CsbiNetworkElement) GetModel() ([]byte, error) {
return d.Plugin.Model(false)
}
// GetModelAsFilteredCopy returns the ygot representation of the Network
// Element, but as copy with read-only fields removed.
func (n *CsbiNetworkElement) GetModelAsFilteredCopy() ([]byte, error) {
return n.Plugin.Model(true)
}
// Transport returns the Transport of the network element.
func (n *CsbiNetworkElement) Transport() Transport {
return n.transport
}
// Transport returns the Transport of the device.
func (d *CsbiNetworkElement) GetPlugin() PluginIface {
return d.Plugin
}
// Name returns the name of the network element.
func (n *CsbiNetworkElement) Name() string {
return n.name
}
// GetMetadata returns the metadate of a network element.
func (n *CsbiNetworkElement) GetMetadata() conflict.Metadata {
return n.Metadata
}
// PndID returns the ID of the associated PND.
func (n *CsbiNetworkElement) PndID() uuid.UUID {
return n.pndID
}
// ProcessResponse processes a response for the Network Element.
func (d *CsbiNetworkElement) ProcessResponse(resp proto.Message) error {
// TODO: callback to send response to caller
return d.transport.ProcessResponse(resp)
}
// 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 transportTLS bool
// 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"
transportTLS = false
} else {
transportType = n.transport.Type()
transportAddress = n.transportOptions.Address
transportUsername = n.transportOptions.Username
transportPassword = n.transportOptions.Password
transportTLS = n.transportOptions.Tls
}
pluginUUID := n.Plugin.ID()
var pndUUID uuid.UUID
if n.pndID == uuid.Nil {
pndUUID = uuid.UUID{}
} else {
pndUUID = n.pndID
}
modelAsString, err := n.Plugin.Model(false)
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"`
TransportTLS bool `json:"transport_tls"`
Plugin uuid.UUID `json:"plugin,omitempty"`
Model string `json:"model,omitempty"`
PndID uuid.UUID `json:"pnd_id,omitempty"`
}{
ID: n.ID(),
Name: n.Name(),
TransportType: transportType,
TransportAddress: transportAddress,
TransportUsername: transportUsername,
TransportPassword: transportPassword,
TransportTLS: transportTLS,
Plugin: pluginUUID,
Model: string(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 transportTLS bool
// 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"
transportTLS = false
} else {
transportType = n.transport.Type()
transportAddress = n.transportOptions.Address
transportUsername = n.transportOptions.Username
transportPassword = n.transportOptions.Password
transportTLS = n.transportOptions.Tls
}
pluginUUID := n.Plugin.ID()
modelAsString, err := n.GetModelAsString()
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"`
TransportTLS bool `bson:"transport_tls"`
Plugin string `bson:"plugin,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,
Plugin: pluginUUID.String(),
Model: modelAsString,
PndID: n.pndID.String(),
TransportTLS: transportTLS,
})
}
// GetModelAsString returns the YANG model of a network element as string.
func (d *CommonNetworkElement) GetModelAsString() (string, error) {
byteModel, err := d.Plugin.Model(false)
return string(byteModel), err
}
// EnsureIntendedConfigurationIsAppliedOnNetworkElement pushes the stored
// configuration to a network element.
// TODO: find a better place for this function.
func EnsureIntendedConfigurationIsAppliedOnNetworkElement(mne NetworkElement) error {
model, err := mne.GetModelAsFilteredCopy()
if err != nil {
return err
}
req := &gpb.SetRequest{}
path, err := ygot.StringToStructuredPath("/")
if err != nil {
return err
}
req.Update = []*gpb.Update{{
Path: path,
Val: &gpb.TypedValue{
Value: &gpb.TypedValue_JsonIetfVal{JsonIetfVal: []byte(model)},
},
}}
response, err := mne.Transport().CustomSet(context.Background(), req)
if err != nil {
log.Errorf("Failed to apply model of network element err=%+v, response=%+v", err, response)
return err
}
return nil
}