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

Merge branch...

Merge branch '163-removing-an-sbi-from-the-pnd-s-sbi-store-should-also-remove-the-devices-using-that-specific-sbi' into 'develop'

Resolve "Removing an SBI from the PND's SBI store should also remove the devices using that specific SBI"

See merge request !234
parents 09f8ba1e 5ecf90f1
No related branches found
No related tags found
5 merge requests!246Develop,!245Develop into Master,!244Master into develop2 into master,!234Resolve "Removing an SBI from the PND's SBI store should also remove the devices using that specific SBI",!138Develop
Pipeline #98214 passed
...@@ -8,6 +8,7 @@ require ( ...@@ -8,6 +8,7 @@ require (
code.fbi.h-da.de/danet/forks/google v0.0.0-20210709163519-47ee8958ef40 code.fbi.h-da.de/danet/forks/google v0.0.0-20210709163519-47ee8958ef40
code.fbi.h-da.de/danet/yang-models v0.1.0 code.fbi.h-da.de/danet/yang-models v0.1.0
github.com/docker/docker v20.10.11+incompatible github.com/docker/docker v20.10.11+incompatible
github.com/google/go-cmp v0.5.6
github.com/google/uuid v1.2.0 github.com/google/uuid v1.2.0
github.com/openconfig/gnmi v0.0.0-20210914185457-51254b657b7d github.com/openconfig/gnmi v0.0.0-20210914185457-51254b657b7d
github.com/openconfig/goyang v0.3.1 github.com/openconfig/goyang v0.3.1
...@@ -29,7 +30,6 @@ require ( ...@@ -29,7 +30,6 @@ require (
github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/golang/glog v1.0.0 // indirect github.com/golang/glog v1.0.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect
...@@ -49,6 +49,7 @@ require ( ...@@ -49,6 +49,7 @@ require (
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect
golang.org/x/sys v0.0.0-20211123173158-ef496fb156ab // indirect golang.org/x/sys v0.0.0-20211123173158-ef496fb156ab // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 // indirect google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 // indirect
gopkg.in/ini.v1 v1.64.0 // indirect gopkg.in/ini.v1 v1.64.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
......
...@@ -142,11 +142,18 @@ func (pnd *pndImplementation) AddSbi(s southbound.SouthboundInterface) error { ...@@ -142,11 +142,18 @@ func (pnd *pndImplementation) AddSbi(s southbound.SouthboundInterface) error {
return pnd.addSbi(s) return pnd.addSbi(s)
} }
// RemoveSbi removes a SBI from the PND // RemoveSbi removes a SBI and all the associated devices from the PND
// TODO: this should to recursively through
// devices and remove the devices using
// this SBI
func (pnd *pndImplementation) RemoveSbi(id uuid.UUID) error { func (pnd *pndImplementation) RemoveSbi(id uuid.UUID) error {
associatedDevices, err := pnd.devices.GetDevicesAssociatedWithSbi(id)
if err != nil {
return err
}
// range over associated devices and remove each one of them
for _, d := range associatedDevices {
if err := pnd.removeDevice(d.ID()); err != nil {
return err
}
}
return pnd.removeSbi(id) return pnd.removeSbi(id)
} }
......
...@@ -409,6 +409,11 @@ func Test_pndImplementation_RemoveDevice(t *testing.T) { ...@@ -409,6 +409,11 @@ func Test_pndImplementation_RemoveDevice(t *testing.T) {
} }
func Test_pndImplementation_RemoveSbi(t *testing.T) { func Test_pndImplementation_RemoveSbi(t *testing.T) {
opts := &tpb.TransportOption{
TransportOption: &tpb.TransportOption_GnmiTransportOption{
GnmiTransportOption: &tpb.GnmiTransportOption{},
},
}
type args struct { type args struct {
id uuid.UUID id uuid.UUID
} }
...@@ -420,6 +425,7 @@ func Test_pndImplementation_RemoveSbi(t *testing.T) { ...@@ -420,6 +425,7 @@ func Test_pndImplementation_RemoveSbi(t *testing.T) {
{name: "default", args: args{id: defaultSbiID}, wantErr: false}, {name: "default", args: args{id: defaultSbiID}, wantErr: false},
{name: "fails", args: args{id: uuid.New()}, wantErr: true}, {name: "fails", args: args{id: uuid.New()}, wantErr: true},
{name: "fails empty", args: args{id: defaultSbiID}, wantErr: true}, {name: "fails empty", args: args{id: defaultSbiID}, wantErr: true},
{name: "exclusively remove associated devices", args: args{id: defaultSbiID}, wantErr: false},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
...@@ -430,16 +436,42 @@ func Test_pndImplementation_RemoveSbi(t *testing.T) { ...@@ -430,16 +436,42 @@ func Test_pndImplementation_RemoveSbi(t *testing.T) {
devices: store.NewDeviceStore(defaultPndID), devices: store.NewDeviceStore(defaultPndID),
Id: defaultPndID, Id: defaultPndID,
} }
if tt.name != "fails empty" { if tt.name != "fails empty" && tt.name != "fails" {
if err := pnd.addSbi(&OpenConfig{id: defaultSbiID}); err != nil { if err := pnd.addSbi(&OpenConfig{id: defaultSbiID}); err != nil {
t.Error(err) t.Error(err)
} }
if err := pnd.AddDevice("associatedDevice", opts, tt.args.id); err != nil {
t.Error(err)
}
if err := pnd.AddDevice("associatedDevice2", opts, tt.args.id); err != nil {
t.Error(err)
}
if tt.name == "exclusively remove associated devices" {
newID := uuid.New()
if err := pnd.addSbi(&OpenConfig{id: newID}); err != nil {
t.Error(err)
}
if err := pnd.AddDevice("associatedDevice2", opts, newID); err != nil {
t.Error(err)
}
}
} }
if err := pnd.RemoveSbi(tt.args.id); (err != nil) != tt.wantErr { if err := pnd.RemoveSbi(tt.args.id); (err != nil) != tt.wantErr {
t.Errorf("RemoveSbi() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("RemoveSbi() error = %v, wantErr %v", err, tt.wantErr)
} }
if pnd.sbic.Exists(tt.args.id) { if pnd.sbic.Exists(tt.args.id) {
t.Errorf("RemoveDevice() SBI still in SBI store %v", pnd.sbic) t.Errorf("RemoveSbi() SBI still in SBI store %v", pnd.sbic)
}
if tt.name == "exclusively remove associated devices" {
if len(pnd.devices.Store) != 1 {
t.Errorf("RemoveSbi() non associated devices should remain in the storage %v", pnd.devices)
}
} else {
if len(pnd.devices.Store) != 0 {
t.Errorf("RemoveSbi() associated devices have not been removed correctly %v", len(pnd.devices.Store))
}
} }
}) })
} }
......
...@@ -37,16 +37,15 @@ func (s *DeviceStore) GetDevice(id uuid.UUID, parseErrors ...error) (device.Devi ...@@ -37,16 +37,15 @@ func (s *DeviceStore) GetDevice(id uuid.UUID, parseErrors ...error) (device.Devi
foundID = id foundID = id
for _, parseErrs := range parseErrors { for _, parseErr := range parseErrors {
if parseErrs != nil { if parseErr != nil {
switch e := parseErrs.(type) { switch e := parseErr.(type) {
case *errors.ErrInvalidUUID: case *errors.ErrInvalidUUID:
myID, ok := s.DeviceNameToUUIDLookup[e.DeviceName] myID, ok := s.DeviceNameToUUIDLookup[e.DeviceName]
if !ok { if !ok {
log.Debug(fmt.Sprintf("no device named %s found", foundID)) log.Debug(fmt.Sprintf("no device named %s found", foundID))
return nil, &errors.ErrNotFound{ID: foundID} return nil, &errors.ErrNotFound{ID: foundID}
} }
foundID = myID foundID = myID
} }
} }
...@@ -71,6 +70,30 @@ func (s *DeviceStore) GetDevice(id uuid.UUID, parseErrors ...error) (device.Devi ...@@ -71,6 +70,30 @@ func (s *DeviceStore) GetDevice(id uuid.UUID, parseErrors ...error) (device.Devi
return d, nil return d, nil
} }
// GetDevicesAssociatedWithSbi ranges over devices within the device store and
// checks if they are associated with the provided SBI. Returns a slice of
// device.Device with all associated devices.
func (s *DeviceStore) GetDevicesAssociatedWithSbi(sid uuid.UUID) ([]device.Device, error) {
var devices []device.Device
// range over all storable items within the device store
for _, item := range s.Store {
d, ok := item.(device.Device)
if !ok {
return nil, &errors.ErrInvalidTypeAssertion{
Value: d,
Type: reflect.TypeOf((*device.Device)(nil)),
}
}
// check if the device uses the provided SBI and add it to the devices
// slice.
if d.SBI().ID() == sid {
devices = append(devices, d)
}
}
return devices, nil
}
// Add adds a device to the device store. // Add adds a device to the device store.
// It also adds the name of the device to the lookup table. // It also adds the name of the device to the lookup table.
func (s *DeviceStore) Add(item store.Storable, name string) error { func (s *DeviceStore) Add(item store.Storable, name string) error {
......
...@@ -8,6 +8,8 @@ import ( ...@@ -8,6 +8,8 @@ import (
"code.fbi.h-da.de/danet/gosdn/interfaces/device" "code.fbi.h-da.de/danet/gosdn/interfaces/device"
"code.fbi.h-da.de/danet/gosdn/interfaces/store" "code.fbi.h-da.de/danet/gosdn/interfaces/store"
"code.fbi.h-da.de/danet/gosdn/mocks" "code.fbi.h-da.de/danet/gosdn/mocks"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/uuid" "github.com/google/uuid"
) )
...@@ -95,3 +97,146 @@ func Test_deviceStore_get(t *testing.T) { ...@@ -95,3 +97,146 @@ func Test_deviceStore_get(t *testing.T) {
}) })
} }
} }
func Test_deviceStore_GetDevicesAsssociatedWithSbi(t *testing.T) {
mockSBI := &mocks.SouthboundInterface{}
mockSBI.On("ID").Return(defaultSbiID)
createDeviceMock := func(name string, sid uuid.UUID) *mocks.Device {
dm := &mocks.Device{}
dm.On("ID").Return(uuid.New())
dm.On("Name").Return("did")
dm.On("SBI").Return(mockSBI)
return dm
}
associatedDevices := []device.Device{
createDeviceMock("mockDevice1", defaultSbiID),
createDeviceMock("mockDevice2", defaultSbiID),
createDeviceMock("mockDevice3", defaultSbiID),
}
nonAssociatedDevice := createDeviceMock("mockDevice4", uuid.New())
pndMock := &mocks.NetworkDomain{}
pndMock.On("ID").Return(did)
// options to apply to cmp.Equal
opts := []cmp.Option{
// create custom comparer that simply checks if the device ID's are the
// same.
cmp.Comparer(func(x, y device.Device) bool {
return x.ID() == y.ID()
}),
// compare option to treat slices of length zero as equal
cmpopts.EquateEmpty(),
// sort the slices based on the ID
cmpopts.SortSlices(func(x, y device.Device) bool {
return x.ID().ID() < y.ID().ID()
}),
}
type fields struct {
genericStore *genericStore
}
type args struct {
sid uuid.UUID
}
tests := []struct {
name string
fields fields
args args
want []device.Device
wantErr bool
}{
{
name: "return devices associated with SBI",
fields: fields{
&genericStore{
Store: map[uuid.UUID]store.Storable{
associatedDevices[0].ID(): associatedDevices[0],
associatedDevices[1].ID(): associatedDevices[1],
associatedDevices[2].ID(): associatedDevices[2],
},
storeLock: sync.RWMutex{},
},
},
args: args{
sid: defaultSbiID,
},
want: associatedDevices,
wantErr: false,
},
{
name: "non associated devices should not be part of the returned slice",
fields: fields{
&genericStore{
Store: map[uuid.UUID]store.Storable{
associatedDevices[0].ID(): associatedDevices[0],
associatedDevices[1].ID(): associatedDevices[1],
associatedDevices[2].ID(): associatedDevices[2],
nonAssociatedDevice.ID(): nonAssociatedDevice,
},
storeLock: sync.RWMutex{},
},
},
args: args{
sid: defaultSbiID,
},
want: append(associatedDevices, nonAssociatedDevice),
wantErr: false,
},
{
name: "empty",
fields: fields{
&genericStore{
Store: map[uuid.UUID]store.Storable{},
storeLock: sync.RWMutex{},
},
},
want: []device.Device{},
wantErr: false,
},
{
name: "no device associated",
fields: fields{
&genericStore{
Store: map[uuid.UUID]store.Storable{
nonAssociatedDevice.ID(): nonAssociatedDevice,
},
storeLock: sync.RWMutex{},
},
},
want: []device.Device{},
wantErr: false,
},
{
name: "fails wrong type",
fields: fields{
&genericStore{
Store: map[uuid.UUID]store.Storable{
defaultPndID: pndMock,
},
storeLock: sync.RWMutex{},
},
},
args: args{sid: defaultSbiID},
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := DeviceStore{genericStore: tt.fields.genericStore}
got, err := s.GetDevicesAssociatedWithSbi(tt.args.sid)
if (err != nil) != tt.wantErr {
t.Errorf("GetDevicesAssociatedWithSbi() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !cmp.Equal(got, tt.want, opts...) {
t.Errorf("GetDevicesAssociatedWithSbi() got = %v, want %v", got, tt.want)
}
})
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment