Skip to content
Snippets Groups Projects
Commit 604d7ab5 authored by Malte Bauch's avatar Malte Bauch
Browse files

Resolve "Restarting the controller after devices are registered is throwing a panic"


See merge request !373

Co-authored-by: default avatarMalte Bauch <malte.bauch@extern.h-da.de>
parent 9212f3ff
Branches
Tags
2 merge requests!373Resolve "Restarting the controller after devices are registered is throwing a panic",!333WIP: Develop
Pipeline #115027 passed
This commit is part of merge request !333. Comments created here will be created in the context of that merge request.
Showing with 125 additions and 170 deletions
...@@ -51,18 +51,4 @@ type LoadedDevice struct { ...@@ -51,18 +51,4 @@ type LoadedDevice struct {
// SBI indicates the southbound interface, which is used by this device as UUID. // SBI indicates the southbound interface, which is used by this device as UUID.
SBI string `json:"sbi"` SBI string `json:"sbi"`
Model string `json:"model,omitempty" bson:"model,omitempty"` Model string `json:"model,omitempty" bson:"model,omitempty"`
convertFunc func(LoadedDevice) (Device, error)
}
// SetConvertFunction allows to set the LoadedDevice's convert function. This
// function should take a LoadedDevice and returns a Device.
func (ld *LoadedDevice) SetConvertFunction(cf func(LoadedDevice) (Device, error)) {
ld.convertFunc = cf
}
// ConvertToDevice calls the LoadedDevice's convert function and converts the
// LoadedDevice into a Device.
func (ld LoadedDevice) ConvertToDevice() (Device, error) {
return ld.convertFunc(ld)
} }
...@@ -11,5 +11,6 @@ type Service interface { ...@@ -11,5 +11,6 @@ type Service interface {
UpdateModel(Device, string) error UpdateModel(Device, string) error
Delete(Device) error Delete(Device) error
Get(store.Query) (Device, error) Get(store.Query) (Device, error)
GetAll() ([]LoadedDevice, error) GetAll() ([]Device, error)
GetAllAsLoaded() ([]LoadedDevice, error)
} }
...@@ -20,7 +20,8 @@ type NetworkDomain interface { ...@@ -20,7 +20,8 @@ type NetworkDomain interface {
GetDevice(identifier string) (device.Device, error) GetDevice(identifier string) (device.Device, error)
RemoveDevice(uuid.UUID) error RemoveDevice(uuid.UUID) error
UpdateDevice(device.Device, string) error UpdateDevice(device.Device, string) error
Devices() []device.LoadedDevice Devices() []device.Device
FlattenedDevices() []device.LoadedDevice
ChangeOND(uuid uuid.UUID, operation ppb.ApiOperation, path string, value ...string) (uuid.UUID, error) ChangeOND(uuid uuid.UUID, operation ppb.ApiOperation, path string, value ...string) (uuid.UUID, error)
Request(uuid.UUID, string) (proto.Message, error) Request(uuid.UUID, string) (proto.Message, error)
RequestAll(string) error RequestAll(string) error
......
...@@ -150,7 +150,23 @@ func (_m *NetworkDomain) Destroy() error { ...@@ -150,7 +150,23 @@ func (_m *NetworkDomain) Destroy() error {
} }
// Devices provides a mock function with given fields: // Devices provides a mock function with given fields:
func (_m *NetworkDomain) Devices() []device.LoadedDevice { func (_m *NetworkDomain) Devices() []device.Device {
ret := _m.Called()
var r0 []device.Device
if rf, ok := ret.Get(0).(func() []device.Device); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]device.Device)
}
}
return r0
}
// FlattenedDevices provides a mock function with given fields:
func (_m *NetworkDomain) FlattenedDevices() []device.LoadedDevice {
ret := _m.Called() ret := _m.Called()
var r0 []device.LoadedDevice var r0 []device.LoadedDevice
......
...@@ -3,10 +3,9 @@ ...@@ -3,10 +3,9 @@
package mocks package mocks
import ( import (
device "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" controllerevent "code.fbi.h-da.de/danet/gosdn/controller/event"
mock "github.com/stretchr/testify/mock"
store "code.fbi.h-da.de/danet/gosdn/controller/store" mock "github.com/stretchr/testify/mock"
) )
// Service is an autogenerated mock type for the Service type // Service is an autogenerated mock type for the Service type
...@@ -14,101 +13,18 @@ type Service struct { ...@@ -14,101 +13,18 @@ type Service struct {
mock.Mock mock.Mock
} }
// Add provides a mock function with given fields: _a0 // CloseConnection provides a mock function with given fields:
func (_m *Service) Add(_a0 device.Device) error { func (_m *Service) CloseConnection() {
ret := _m.Called(_a0) _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func(device.Device) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// Delete provides a mock function with given fields: _a0
func (_m *Service) Delete(_a0 device.Device) error {
ret := _m.Called(_a0)
var r0 error
if rf, ok := ret.Get(0).(func(device.Device) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// Get provides a mock function with given fields: _a0
func (_m *Service) Get(_a0 store.Query) (device.Device, error) {
ret := _m.Called(_a0)
var r0 device.Device
if rf, ok := ret.Get(0).(func(store.Query) device.Device); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(device.Device)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(store.Query) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetAll provides a mock function with given fields:
func (_m *Service) GetAll() ([]device.Device, error) {
ret := _m.Called()
var r0 []device.Device
if rf, ok := ret.Get(0).(func() []device.Device); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]device.Device)
}
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Update provides a mock function with given fields: _a0
func (_m *Service) Update(_a0 device.Device) error {
ret := _m.Called(_a0)
var r0 error
if rf, ok := ret.Get(0).(func(device.Device) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
} }
// UpdateModel provides a mock function with given fields: _a0, _a1 // PublishEvent provides a mock function with given fields: topic, _a1
func (_m *Service) UpdateModel(_a0 device.Device, _a1 string) error { func (_m *Service) PublishEvent(topic string, _a1 controllerevent.Event) error {
ret := _m.Called(_a0, _a1) ret := _m.Called(topic, _a1)
var r0 error var r0 error
if rf, ok := ret.Get(0).(func(device.Device, string) error); ok { if rf, ok := ret.Get(0).(func(string, controllerevent.Event) error); ok {
r0 = rf(_a0, _a1) r0 = rf(topic, _a1)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
package mocks package mocks
import ( import (
southbound "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" store "code.fbi.h-da.de/danet/gosdn/controller/interfaces/store"
store "code.fbi.h-da.de/danet/gosdn/controller/store" uuid "github.com/google/uuid"
mock "github.com/stretchr/testify/mock" mock "github.com/stretchr/testify/mock"
) )
...@@ -13,13 +13,13 @@ type Store struct { ...@@ -13,13 +13,13 @@ type Store struct {
mock.Mock mock.Mock
} }
// Add provides a mock function with given fields: _a0 // Add provides a mock function with given fields: item
func (_m *Store) Add(_a0 southbound.SouthboundInterface) error { func (_m *Store) Add(item store.Storable) error {
ret := _m.Called(_a0) ret := _m.Called(item)
var r0 error var r0 error
if rf, ok := ret.Get(0).(func(southbound.SouthboundInterface) error); ok { if rf, ok := ret.Get(0).(func(store.Storable) error); ok {
r0 = rf(_a0) r0 = rf(item)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }
...@@ -27,13 +27,13 @@ func (_m *Store) Add(_a0 southbound.SouthboundInterface) error { ...@@ -27,13 +27,13 @@ func (_m *Store) Add(_a0 southbound.SouthboundInterface) error {
return r0 return r0
} }
// Delete provides a mock function with given fields: _a0 // Delete provides a mock function with given fields: id
func (_m *Store) Delete(_a0 southbound.SouthboundInterface) error { func (_m *Store) Delete(id uuid.UUID) error {
ret := _m.Called(_a0) ret := _m.Called(id)
var r0 error var r0 error
if rf, ok := ret.Get(0).(func(southbound.SouthboundInterface) error); ok { if rf, ok := ret.Get(0).(func(uuid.UUID) error); ok {
r0 = rf(_a0) r0 = rf(id)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }
...@@ -41,20 +41,36 @@ func (_m *Store) Delete(_a0 southbound.SouthboundInterface) error { ...@@ -41,20 +41,36 @@ func (_m *Store) Delete(_a0 southbound.SouthboundInterface) error {
return r0 return r0
} }
// Get provides a mock function with given fields: _a0 // Exists provides a mock function with given fields: id
func (_m *Store) Get(_a0 store.Query) (southbound.LoadedSbi, error) { func (_m *Store) Exists(id uuid.UUID) bool {
ret := _m.Called(_a0) ret := _m.Called(id)
var r0 southbound.LoadedSbi var r0 bool
if rf, ok := ret.Get(0).(func(store.Query) southbound.LoadedSbi); ok { if rf, ok := ret.Get(0).(func(uuid.UUID) bool); ok {
r0 = rf(_a0) r0 = rf(id)
} else { } else {
r0 = ret.Get(0).(southbound.LoadedSbi) r0 = ret.Get(0).(bool)
}
return r0
}
// Get provides a mock function with given fields: id
func (_m *Store) Get(id uuid.UUID) (store.Storable, error) {
ret := _m.Called(id)
var r0 store.Storable
if rf, ok := ret.Get(0).(func(uuid.UUID) store.Storable); ok {
r0 = rf(id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.Storable)
}
} }
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(store.Query) error); ok { if rf, ok := ret.Get(1).(func(uuid.UUID) error); ok {
r1 = rf(_a0) r1 = rf(id)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
} }
...@@ -62,27 +78,20 @@ func (_m *Store) Get(_a0 store.Query) (southbound.LoadedSbi, error) { ...@@ -62,27 +78,20 @@ func (_m *Store) Get(_a0 store.Query) (southbound.LoadedSbi, error) {
return r0, r1 return r0, r1
} }
// GetAll provides a mock function with given fields: // UUIDs provides a mock function with given fields:
func (_m *Store) GetAll() ([]southbound.LoadedSbi, error) { func (_m *Store) UUIDs() []uuid.UUID {
ret := _m.Called() ret := _m.Called()
var r0 []southbound.LoadedSbi var r0 []uuid.UUID
if rf, ok := ret.Get(0).(func() []southbound.LoadedSbi); ok { if rf, ok := ret.Get(0).(func() []uuid.UUID); ok {
r0 = rf() r0 = rf()
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).([]southbound.LoadedSbi) r0 = ret.Get(0).([]uuid.UUID)
} }
} }
var r1 error return r0
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
} }
type mockConstructorTestingTNewStore interface { type mockConstructorTestingTNewStore interface {
......
...@@ -54,10 +54,6 @@ func (d *DeviceServer) GetAll(ctx context.Context, request *dpb.GetAllDeviceRequ ...@@ -54,10 +54,6 @@ func (d *DeviceServer) GetAll(ctx context.Context, request *dpb.GetAllDeviceRequ
onds := []*dpb.Device{} onds := []*dpb.Device{}
for _, device := range devices { for _, device := range devices {
device, err := device.ConvertToDevice()
if err != nil {
return nil, status.Errorf(codes.Aborted, "%v", err)
}
ygotStructAsJSON, err := device.GetModelAsString() ygotStructAsJSON, err := device.GetModelAsString()
if err != nil { if err != nil {
log.Error(err) log.Error(err)
......
...@@ -58,7 +58,7 @@ func (p PndServer) GetOnd(ctx context.Context, request *ppb.GetOndRequest) (*ppb ...@@ -58,7 +58,7 @@ func (p PndServer) GetOnd(ctx context.Context, request *ppb.GetOndRequest) (*ppb
return nil, status.Errorf(codes.Aborted, "%v", err) return nil, status.Errorf(codes.Aborted, "%v", err)
} }
ond, err := fillOndBySpecificPath(pnd, device, "/") ond, err := fillOndBySpecificPath(device, "/")
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err) return nil, status.Errorf(codes.Aborted, "%v", err)
...@@ -92,12 +92,8 @@ func (p PndServer) GetOndList(ctx context.Context, request *ppb.GetOndListReques ...@@ -92,12 +92,8 @@ func (p PndServer) GetOndList(ctx context.Context, request *ppb.GetOndListReques
} }
onds := make([]*ppb.OrchestratedNetworkingDevice, len(pnd.Devices())) onds := make([]*ppb.OrchestratedNetworkingDevice, len(pnd.Devices()))
for i, loadedDevice := range pnd.Devices() { for i, device := range pnd.Devices() {
device, err := loadedDevice.ConvertToDevice() ond, err := fillOndBySpecificPath(device, "/")
if err != nil {
return nil, status.Errorf(codes.Aborted, "%v", err)
}
ond, err := fillOndBySpecificPath(pnd, device, "/")
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err) return nil, status.Errorf(codes.Aborted, "%v", err)
...@@ -132,7 +128,7 @@ func (p PndServer) GetFlattenedOndList(ctx context.Context, request *ppb.GetOndL ...@@ -132,7 +128,7 @@ func (p PndServer) GetFlattenedOndList(ctx context.Context, request *ppb.GetOndL
return nil, status.Errorf(codes.Aborted, "%v", err) return nil, status.Errorf(codes.Aborted, "%v", err)
} }
onds := pnd.Devices() onds := pnd.FlattenedDevices()
flattenedOnds := make([]*ppb.FlattenedOrchestratedNetworkingDevice, len(onds)) flattenedOnds := make([]*ppb.FlattenedOrchestratedNetworkingDevice, len(onds))
for i, ond := range onds { for i, ond := range onds {
ond := &ppb.FlattenedOrchestratedNetworkingDevice{ ond := &ppb.FlattenedOrchestratedNetworkingDevice{
...@@ -155,7 +151,7 @@ func (p PndServer) GetFlattenedOndList(ctx context.Context, request *ppb.GetOndL ...@@ -155,7 +151,7 @@ func (p PndServer) GetFlattenedOndList(ctx context.Context, request *ppb.GetOndL
}, nil }, nil
} }
func fillOndBySpecificPath(pnd networkdomain.NetworkDomain, d device.Device, path string) (*ppb.OrchestratedNetworkingDevice, error) { func fillOndBySpecificPath(d device.Device, path string) (*ppb.OrchestratedNetworkingDevice, error) {
gnmiPath, err := ygot.StringToStructuredPath(path) gnmiPath, err := ygot.StringToStructuredPath(path)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
...@@ -354,7 +350,7 @@ func (p PndServer) GetPath(ctx context.Context, request *ppb.GetPathRequest) (*p ...@@ -354,7 +350,7 @@ func (p PndServer) GetPath(ctx context.Context, request *ppb.GetPathRequest) (*p
return nil, status.Errorf(codes.Aborted, "%v", err) return nil, status.Errorf(codes.Aborted, "%v", err)
} }
ond, err := fillOndBySpecificPath(pnd, device, path) ond, err := fillOndBySpecificPath(device, path)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err) return nil, status.Errorf(codes.Aborted, "%v", err)
......
...@@ -49,8 +49,6 @@ func (s *DeviceService) Get(query store.Query) (device.Device, error) { ...@@ -49,8 +49,6 @@ func (s *DeviceService) Get(query store.Query) (device.Device, error) {
return nil, err return nil, err
} }
loadedDevice.SetConvertFunction(s.createDeviceFromStore)
device, err := s.createDeviceFromStore(loadedDevice) device, err := s.createDeviceFromStore(loadedDevice)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -60,14 +58,34 @@ func (s *DeviceService) Get(query store.Query) (device.Device, error) { ...@@ -60,14 +58,34 @@ func (s *DeviceService) Get(query store.Query) (device.Device, error) {
} }
// GetAll returns all stored devices. // GetAll returns all stored devices.
func (s *DeviceService) GetAll() ([]device.LoadedDevice, error) { func (s *DeviceService) GetAll() ([]device.Device, error) {
var devices []device.Device
loadedDevices, err := s.deviceStore.GetAll() loadedDevices, err := s.deviceStore.GetAll()
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, loadedDevice := range loadedDevices { for _, loadedDevice := range loadedDevices {
loadedDevice.SetConvertFunction(s.createDeviceFromStore) device, err := s.createDeviceFromStore(loadedDevice)
if err != nil {
return nil, err
}
devices = append(devices, device)
}
return devices, nil
}
// GetAllAsLoaded returns all stored devices as LoadedDevice.
// This method should be used if there is no need for a device.Device, since
// requesting device information through this method is a lot faster than the
// usual `GetAll` method.
func (s *DeviceService) GetAllAsLoaded() ([]device.LoadedDevice, error) {
loadedDevices, err := s.deviceStore.GetAll()
if err != nil {
return nil, err
} }
return loadedDevices, nil return loadedDevices, nil
......
...@@ -89,7 +89,18 @@ func (t *DeviceServiceMock) Get(query store.Query) (device.Device, error) { ...@@ -89,7 +89,18 @@ func (t *DeviceServiceMock) Get(query store.Query) (device.Device, error) {
} }
// GetAll gets all items. // GetAll gets all items.
func (t *DeviceServiceMock) GetAll() ([]device.LoadedDevice, error) { func (t *DeviceServiceMock) GetAll() ([]device.Device, error) {
var allItems []device.Device
for _, item := range t.Store {
allItems = append(allItems, item)
}
return allItems, nil
}
// GetAllAsLoaded gets all items as `device.LoadedDevice`.
func (t *DeviceServiceMock) GetAllAsLoaded() ([]device.LoadedDevice, error) {
var allItems []device.LoadedDevice var allItems []device.LoadedDevice
for _, item := range t.Store { for _, item := range t.Store {
......
...@@ -68,8 +68,7 @@ func (d *DeviceWatcher) SubToDevices(paths [][]string, opts *gnmi.SubscribeOptio ...@@ -68,8 +68,7 @@ func (d *DeviceWatcher) SubToDevices(paths [][]string, opts *gnmi.SubscribeOptio
} }
func (d *DeviceWatcher) subscribeToPndDevices(pndID string, pnd networkdomain.NetworkDomain, opts *gnmi.SubscribeOptions) { func (d *DeviceWatcher) subscribeToPndDevices(pndID string, pnd networkdomain.NetworkDomain, opts *gnmi.SubscribeOptions) {
for _, loadedDevice := range pnd.Devices() { for _, device := range pnd.Devices() {
device, _ := loadedDevice.ConvertToDevice()
subID := uuid.New() subID := uuid.New()
stopContext, cancel := context.WithCancel(context.Background()) stopContext, cancel := context.WithCancel(context.Background())
......
...@@ -157,12 +157,18 @@ func (pnd *pndImplementation) ID() uuid.UUID { ...@@ -157,12 +157,18 @@ func (pnd *pndImplementation) ID() uuid.UUID {
return pnd.Id return pnd.Id
} }
func (pnd *pndImplementation) Devices() []device.LoadedDevice { func (pnd *pndImplementation) Devices() []device.Device {
allDevices, _ := pnd.deviceService.GetAll() allDevices, _ := pnd.deviceService.GetAll()
return allDevices return allDevices
} }
func (pnd *pndImplementation) FlattenedDevices() []device.LoadedDevice {
allDevices, _ := pnd.deviceService.GetAllAsLoaded()
return allDevices
}
// GetName returns the name of the PND. // GetName returns the name of the PND.
func (pnd *pndImplementation) GetName() string { func (pnd *pndImplementation) GetName() string {
return pnd.Name return pnd.Name
...@@ -206,7 +212,7 @@ func (pnd *pndImplementation) AddSbi(s southbound.SouthboundInterface) error { ...@@ -206,7 +212,7 @@ func (pnd *pndImplementation) AddSbi(s southbound.SouthboundInterface) error {
func (pnd *pndImplementation) RemoveSbi(sid uuid.UUID) error { func (pnd *pndImplementation) RemoveSbi(sid uuid.UUID) error {
var associatedDevices []device.LoadedDevice var associatedDevices []device.LoadedDevice
allExistingDevices, err := pnd.deviceService.GetAll() allExistingDevices, err := pnd.deviceService.GetAllAsLoaded()
if err != nil { if err != nil {
return err return err
} }
...@@ -436,7 +442,7 @@ func (pnd *pndImplementation) Request(uuid uuid.UUID, path string) (proto.Messag ...@@ -436,7 +442,7 @@ func (pnd *pndImplementation) Request(uuid uuid.UUID, path string) (proto.Messag
// RequestAll sends a request for all registered devices. // RequestAll sends a request for all registered devices.
func (pnd *pndImplementation) RequestAll(path string) error { func (pnd *pndImplementation) RequestAll(path string) error {
allDevices, err := pnd.deviceService.GetAll() allDevices, err := pnd.deviceService.GetAllAsLoaded()
if err != nil { if err != nil {
return err return err
} }
......
...@@ -785,7 +785,7 @@ func Test_pndImplementation_ChangeOND(t *testing.T) { ...@@ -785,7 +785,7 @@ func Test_pndImplementation_ChangeOND(t *testing.T) {
return return
} }
devices, err := pnd.deviceService.GetAll() devices, err := pnd.deviceService.GetAllAsLoaded()
if err != nil { if err != nil {
err := errors.New("error fetching device") err := errors.New("error fetching device")
t.Error(err) t.Error(err)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment