Newer
Older
ppb "code.fbi.h-da.de/cocsn/api/go/gosdn/pnd"
tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
"code.fbi.h-da.de/cocsn/gosdn/interfaces/change"
"code.fbi.h-da.de/cocsn/gosdn/interfaces/device"
"code.fbi.h-da.de/cocsn/gosdn/interfaces/networkdomain"
"code.fbi.h-da.de/cocsn/gosdn/interfaces/southbound"
"code.fbi.h-da.de/cocsn/gosdn/interfaces/store"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
"github.com/google/uuid"
"github.com/openconfig/ygot/ygot"
"github.com/openconfig/ygot/ytypes"
// NewPND creates a Principle Network Domain
func NewPND(name, description string, id uuid.UUID, sbi southbound.SouthboundInterface, c cpb.CsbiClient, callback func(uuid.UUID, chan DeviceDetails)) (networkdomain.NetworkDomain, error) {
name: name,
description: description,
sbic: SbiStore{genericStore{}},
devices: NewDeviceStore(),
changes: ChangeStore{genericStore{}},
id: id,
errChans: make(map[uuid.UUID]chan error),
if err := pnd.sbic.Add(sbi); err != nil {
type pndImplementation struct {
name string
description string
sbic SbiStore
devices *DeviceStore
changes ChangeStore
id uuid.UUID
errChans map[uuid.UUID]chan error
csbiClient cpb.CsbiClient
callback func(uuid.UUID, chan DeviceDetails)
func (pnd *pndImplementation) PendingChanges() []uuid.UUID {
func (pnd *pndImplementation) CommittedChanges() []uuid.UUID {
func (pnd *pndImplementation) ConfirmedChanges() []uuid.UUID {
return pnd.changes.Confirmed()
}
func (pnd *pndImplementation) GetChange(cuid uuid.UUID) (change.Change, error) {
return pnd.changes.GetChange(cuid)
func (pnd *pndImplementation) Commit(u uuid.UUID) error {
if err != nil {
return err
}
func (pnd *pndImplementation) Confirm(u uuid.UUID) error {
if err != nil {
return err
}
func (pnd *pndImplementation) ID() uuid.UUID {
func (pnd *pndImplementation) Devices() []uuid.UUID {
return pnd.devices.UUIDs()
}
// GetName returns the name of the PND
// ContainsDevice checks if the given device uuid is registered for this PND
func (pnd *pndImplementation) ContainsDevice(id uuid.UUID) bool {
// GetDescription returns the current description of the PND
func (pnd *pndImplementation) GetDescription() string {
// GetSBIs returns the registered SBIs
func (pnd *pndImplementation) GetSBIs() store.Store {
// Destroy destroys the PND
func (pnd *pndImplementation) Destroy() error {
return destroy()
}
// AddSbi adds a SBI to the PND which will be supported
func (pnd *pndImplementation) AddSbi(s southbound.SouthboundInterface) error {
// devices and remove the devices using
// this SBI
func (pnd *pndImplementation) RemoveSbi(id uuid.UUID) error {
return pnd.removeSbi(id)
//AddDevice adds a new device to the PND
func (pnd *pndImplementation) AddDevice(name string, opt *tpb.TransportOption, sid uuid.UUID) error {
if opt.Csbi {
return pnd.handleCsbiEnrolment(name, opt)
}
if err != nil {
return err
}
d, err := NewDevice(name, opt, sbi)
if err != nil {
return err
func (pnd *pndImplementation) GetDevice(identifier string) (device.Device, error) {
d, err := pnd.devices.GetDevice(FromString(identifier))
if err != nil {
return nil, err
}
copiedDevice := &CommonDevice{name: d.Name(), UUID: d.ID(), GoStruct: copiedGoStruct}
return copiedDevice, nil
// RemoveDevice removes a device from the PND
func (pnd *pndImplementation) RemoveDevice(uuid uuid.UUID) error {
// Actual implementation, bind to struct if
// neccessary
func destroy() error {
return nil
}
func (pnd *pndImplementation) addSbi(sbi southbound.SouthboundInterface) error {
func (pnd *pndImplementation) removeSbi(id uuid.UUID) error {
func (pnd *pndImplementation) addDevice(device device.Device) error {
if err != nil {
return err
}
func (pnd *pndImplementation) removeDevice(id uuid.UUID) error {
d, err := pnd.devices.GetDevice(id)
if err != nil {
return err
}
switch d.(type) {
case *CsbiDevice:
return pnd.handleCsbiDeletion(id)
default:
return pnd.devices.Delete(id)
}
func (pnd *pndImplementation) MarshalDevice(identifier string) (string, error) {
foundDevice, err := pnd.devices.GetDevice(FromString(identifier))
if err != nil {
return "", err
}
jsonTree, err := json.MarshalIndent(foundDevice.Model(), "", "\t")
if err != nil {
return "", err
}
"pnd": pnd.id,
"Identifier": identifier,
"Name": foundDevice.Name,
return string(jsonTree), nil
// Request sends a get request to a specific device
func (pnd *pndImplementation) Request(uuid uuid.UUID, path string) (proto.Message, error) {
d, err := pnd.devices.GetDevice(FromString(uuid.String()))
resp, ok := res.(proto.Message)
if !ok {
return nil, &errors.ErrInvalidTypeAssertion{}
}
err = d.ProcessResponse(resp)
// RequestAll sends a request for all registered devices
func (pnd *pndImplementation) RequestAll(path string) error {
for _, k := range pnd.devices.UUIDs() {
_, err := pnd.Request(k, path)
if err != nil {
"path": path,
}).Info("sent request to all devices")
// ChangeOND creates a change from the provided Operation, path and value.
// The Change is Pending and times out after the specified timeout period
func (pnd *pndImplementation) ChangeOND(duid uuid.UUID, operation ppb.ApiOperation, path string, value ...string) (uuid.UUID, error) {
d, err := pnd.devices.GetDevice(duid)
p, err := ygot.StringToStructuredPath(path)
if err != nil {
if operation != ppb.ApiOperation_DELETE && len(value) != 1 {
return uuid.Nil, &errors.ErrInvalidParameters{
case ppb.ApiOperation_UPDATE, ppb.ApiOperation_REPLACE:
if err := ytypes.SetNode(d.SBI().Schema().RootSchema(), cpy, p, typedValue); err != nil {
if err := ytypes.DeleteNode(d.SBI().Schema().RootSchema(), cpy, p); err != nil {
return uuid.Nil, &errors.ErrOperationNotSupported{Op: operation}
callback := func(original ygot.GoStruct, modified ygot.GoStruct) error {
ctx := context.WithValue(context.Background(), types.CtxKeyOperation, operation) // nolint
payload := change.Payload{Original: original, Modified: modified}
return d.Transport().Set(ctx, payload)
errChan := make(chan error)
ch := NewChange(duid, d.Model(), cpy, callback, errChan)
if err := pnd.changes.Add(ch); err != nil {
return uuid.Nil, err
}
return ch.cuid, nil
func handleRollbackError(id uuid.UUID, err error) {
log.Error(err)
// TODO: Notion of invalid state needed.
}
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
func (pnd *pndImplementation) handleCsbiDeletion(id uuid.UUID) error {
log.Infof("csbi deletion triggered for %v", id)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
req := &cpb.DeleteRequest{
Timestamp: time.Now().UnixNano(),
Did: []string{id.String()},
}
resp, err := pnd.csbiClient.Delete(ctx, req)
if err != nil {
return err
}
log.WithFields(log.Fields{
"uuid": id,
"status": resp.Status,
}).Info("csbi deleted")
return nil
}
func (pnd *pndImplementation) handleCsbiEnrolment(name string, opt *tpb.TransportOption) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5)
defer cancel()
req := &cpb.CreateRequest{
Timestamp: time.Now().UnixNano(),
TransportOption: []*tpb.TransportOption{opt},
}
resp, err := pnd.csbiClient.Create(ctx, req)
if err != nil {
return err
}
for _, d := range resp.Deployments {
if err := pnd.createCsbiDevice(name, d, opt); err != nil {
log.Error(err)
}
}
return nil
}
// DeviceDetails contains details of a device used by the cSBI mechanism
type DeviceDetails struct {
ID string
Address string
TransportOption *tpb.TransportOption
}
func (pnd *pndImplementation) createCsbiDevice(name string, d *cpb.Deployment, opt *tpb.TransportOption) error {
defer func() {
if r := recover(); r != nil {
log.Errorf("recovered in sbi enrolment: %v", r)
}
}()
id, err := uuid.Parse(d.Id)
if err != nil {
return err
}
ch := make(chan DeviceDetails, 1)
pnd.callback(id, ch)
go func() {
deviceDetails := <-ch
log.Infof("syn from csbi %v", deviceDetails.ID)
id, err := uuid.Parse(deviceDetails.ID)
if err != nil {
panic(err)
}
opt.Address = deviceDetails.Address
d, err := NewDevice(name, opt, csbi)
if err != nil {
panic(err)
}
d.(*CsbiDevice).UUID = id
ch <- DeviceDetails{TransportOption: opt}
if err := pnd.devices.Add(d, d.Name()); err != nil {
panic(err)
}
pnd.callback(id, nil)
close(ch)
}()
return nil
}