Skip to content
Snippets Groups Projects
device.go 9.13 KiB
Newer Older
  • Learn to ignore specific revisions
  • Manuel Kieweg's avatar
    Manuel Kieweg committed
    package nucleus
    
    import (
    
    	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/interfaces/device"
    	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound"
    	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/transport"
    
    Andre Sterba's avatar
    Andre Sterba committed
    	"code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors"
    
    	"github.com/docker/docker/pkg/namesgenerator"
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	"github.com/google/uuid"
    	"github.com/openconfig/ygot/ygot"
    
    	"go.mongodb.org/mongo-driver/bson"
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	"google.golang.org/protobuf/proto"
    
    // NewDevice creates a Device
    
    func NewDevice(name string, uuidInput uuid.UUID, opt *tpb.TransportOption, sbi southbound.SouthboundInterface) (device.Device, 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)
    	}
    
    
    Andre Sterba's avatar
    Andre Sterba committed
    	// We want a representation of the OND'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 device will be called.
    
    	root, err := ygot.DeepCopy(sbi.Schema().Root)
    	if err != nil {
    		return nil, err
    	}
    
    	ygotDeepCopy, ok := root.(ygot.GoStruct)
    
    Andre Sterba's avatar
    Andre Sterba committed
    	if !ok {
    		return nil, &errors.ErrInvalidTypeAssertion{
    			Value: root,
    			Type:  (*ygot.ValidatedGoStruct)(nil),
    		}
    	}
    
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    	if opt.Type == spb.Type_TYPE_CONTAINERISED {
    
    		return &CsbiDevice{
    			CommonDevice: CommonDevice{
    
    				Model:            ygotDeepCopy,
    
    				sbi:              sbi,
    				transport:        t,
    				name:             name,
    				transportOptions: opt,
    			},
    		}, nil
    	}
    
    	return &CommonDevice{
    
    		Model:            ygotDeepCopy,
    
    		sbi:              sbi,
    		transport:        t,
    		name:             name,
    		transportOptions: opt,
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    // CommonDevice represents an OND
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    type CommonDevice struct {
    
    	// UUID represents the Devices UUID
    	UUID uuid.UUID
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	// Device embeds a ygot.GoStruct containing the device details
    
    	Model ygot.GoStruct
    
    
    	// SBI is the device's southbound interface implementation
    
    	sbi southbound.SouthboundInterface
    
    
    	// Transport is the device's Transport implementation
    
    	transport transport.Transport
    
    
    	// Name is the device's human readable name
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	name string
    
    
    	transportOptions *tpb.TransportOption
    
    // ID returns the UUID of the Device
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func (d *CommonDevice) ID() uuid.UUID {
    
    	return d.UUID
    
    Andre Sterba's avatar
    Andre Sterba committed
    // GetModel returns the ygot representation of the Device
    
    func (d *CommonDevice) GetModel() ygot.GoStruct {
    
    Andre Sterba's avatar
    Andre Sterba committed
    	return d.Model
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    // Transport returns the Transport of the device
    
    func (d *CommonDevice) Transport() transport.Transport {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	return d.transport
    }
    
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    // Name returns the name of the device
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func (d *CommonDevice) Name() string {
    	return d.name
    }
    
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    // SBI returns the sbi of the Device
    
    func (d *CommonDevice) SBI() southbound.SouthboundInterface {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	return d.sbi
    }
    
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    // SetTransport sets the Device's Transport
    
    func (d *CommonDevice) SetTransport(t transport.Transport) {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	d.transport = t
    }
    
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    // SetName sets the Device's name
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func (d *CommonDevice) SetName(n string) {
    	d.name = n
    }
    
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    // SetSBI sets the Device's SBI
    
    func (d *CommonDevice) SetSBI(sbi southbound.SouthboundInterface) {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	d.sbi = sbi
    }
    
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    // ProcessResponse processes a response for the Device
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func (d *CommonDevice) ProcessResponse(resp proto.Message) error {
    
    Andre Sterba's avatar
    Andre Sterba committed
    	return d.transport.ProcessResponse(resp, d.Model, d.sbi.Schema())
    
    // IsTransportValid returns a boolean if the transport of a device is valid
    func (d *CommonDevice) IsTransportValid() bool {
    	if d.transportOptions != nil && d.transportOptions.Address != "" {
    		return true
    	}
    
    	return false
    }
    
    
    // CsbiDevice is used for the cSBI functionality.
    type CsbiDevice struct {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	CommonDevice
    }
    
    // ID returns the UUID of the Device
    
    func (d *CsbiDevice) ID() uuid.UUID {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	return d.UUID
    }
    
    
    Andre Sterba's avatar
    Andre Sterba committed
    // GetModel returns the ygot representation of the Device
    
    func (d *CsbiDevice) GetModel() ygot.GoStruct {
    
    Andre Sterba's avatar
    Andre Sterba committed
    	return d.Model
    
    // Transport returns the Transport of the device
    func (d *CsbiDevice) Transport() transport.Transport {
    
    	return d.transport
    
    // Name returns the name of the device
    func (d *CsbiDevice) Name() string {
    
    	return d.name
    
    // SBI returns the sbi of the Device
    func (d *CsbiDevice) SBI() southbound.SouthboundInterface {
    
    	return d.sbi
    
    // ProcessResponse processes a response for the Device
    func (d *CsbiDevice) ProcessResponse(resp proto.Message) error {
    	// TODO: callback to send response to caller
    
    Andre Sterba's avatar
    Andre Sterba committed
    	return d.transport.ProcessResponse(resp, d.Model, d.sbi.Schema())
    
    // IsTransportValid returns a boolean if the transport of a device is valid
    func (d *CsbiDevice) IsTransportValid() bool {
    	if d.transportOptions != nil && d.transportOptions.Address != "" {
    		return true
    	}
    
    	return false
    }
    
    
    // MarshalJSON implements the MarshalJSON interface to store a device as JSON
    func (d *CommonDevice) MarshalJSON() ([]byte, error) {
    	var transportType string
    	var transportAddress string
    	var transportUsername string
    	var transportPassword string
    
    Malte Bauch's avatar
    Malte Bauch committed
    	var transportOptionType spb.Type
    
    
    	// Handling of these cases is necessary as we use partial devices 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 d.transport == nil || d.transportOptions == nil {
    		transportType = "testing"
    		transportAddress = "testing"
    		transportUsername = "testing"
    		transportPassword = "testing"
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    		transportOptionType = spb.Type_TYPE_OPENCONFIG
    
    	} else {
    		transportType = d.transport.Type()
    		transportAddress = d.transportOptions.Address
    		transportUsername = d.transportOptions.Username
    		transportPassword = d.transportOptions.Password
    
    Malte Bauch's avatar
    Malte Bauch committed
    		transportOptionType = d.transportOptions.Type
    
    	}
    
    	var sbiUUID uuid.UUID
    
    	if d.sbi == nil {
    		sbiUUID = uuid.UUID{}
    	} else {
    		sbiUUID = d.sbi.ID()
    	}
    
    
    Andre Sterba's avatar
    Andre Sterba committed
    	modelAsString, err := ygot.EmitJSON(d.Model, d.getYgotEmitJSONConfig())
    	if err != nil {
    		return []byte{}, err
    	}
    
    
    	return json.Marshal(&struct {
    		DeviceID            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"`
    
    Malte Bauch's avatar
    Malte Bauch committed
    		TransportOptionType spb.Type  `json:"transport_option"`
    
    		SBI                 uuid.UUID `json:"sbi,omitempty"`
    
    Andre Sterba's avatar
    Andre Sterba committed
    		Model               string    `bson:"model,omitempty"`
    
    	}{
    		DeviceID:            d.ID(),
    		Name:                d.Name(),
    		TransportType:       transportType,
    		TransportAddress:    transportAddress,
    		TransportUsername:   transportUsername,
    		TransportPassword:   transportPassword,
    
    Malte Bauch's avatar
    Malte Bauch committed
    		TransportOptionType: transportOptionType,
    
    Andre Sterba's avatar
    Andre Sterba committed
    		Model:               modelAsString,
    
    
    // MarshalBSON implements the MarshalBSON interface to store a device as BSON
    func (d *CommonDevice) 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 devices 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 d.transport == nil || d.transportOptions == nil {
    		transportType = "testing"
    		transportAddress = "testing"
    		transportUsername = "testing"
    		transportPassword = "testing"
    		transportOptionType = spb.Type_TYPE_OPENCONFIG
    	} else {
    		transportType = d.transport.Type()
    		transportAddress = d.transportOptions.Address
    		transportUsername = d.transportOptions.Username
    		transportPassword = d.transportOptions.Password
    		transportOptionType = d.transportOptions.Type
    	}
    
    
    Andre Sterba's avatar
    Andre Sterba committed
    	modelAsString, err := ygot.EmitJSON(d.Model, d.getYgotEmitJSONConfig())
    	if err != nil {
    		return []byte{}, err
    	}
    
    
    	return bson.Marshal(&struct {
    		DeviceID            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"`
    
    Andre Sterba's avatar
    Andre Sterba committed
    		Model               string   `bson:"model,omitempty"`
    
    	}{
    		DeviceID:            d.ID().String(),
    		Name:                d.Name(),
    		TransportType:       transportType,
    		TransportAddress:    transportAddress,
    		TransportUsername:   transportUsername,
    		TransportPassword:   transportPassword,
    		TransportOptionType: transportOptionType,
    		SBI:                 d.sbi.ID().String(),
    
    Andre Sterba's avatar
    Andre Sterba committed
    		Model:               modelAsString,
    
    Andre Sterba's avatar
    Andre Sterba committed
    
    func (d *CommonDevice) getYgotEmitJSONConfig() *ygot.EmitJSONConfig {
    	return &ygot.EmitJSONConfig{
    
    		Format:         ygot.RFC7951,
    		Indent:         "",
    		SkipValidation: true,
    
    Andre Sterba's avatar
    Andre Sterba committed
    		RFC7951Config: &ygot.RFC7951JSONConfig{
    			AppendModuleName: true,
    		}}
    }