Skip to content
Snippets Groups Projects
Commit e576199b authored by Andre Sterba's avatar Andre Sterba
Browse files

Refactor store interfaces and provide database as example implementation

See merge request !267
parent ecf8c480
Branches
Tags
2 merge requests!267Refactor store interfaces and provide database as example implementation,!264WIP: Develop
Pipeline #98810 passed
Showing
with 209 additions and 210 deletions
......@@ -46,11 +46,12 @@ var deviceListCmd = &cobra.Command{
Long: "List all orchestrated network devices within the current PND.",
RunE: func(cmd *cobra.Command, args []string) error {
resp, err := pndAdapter.GetDevices()
respONDs, err := pndAdapter.GetDevices()
if err != nil {
return err
}
for i, dev := range resp {
for i, dev := range respONDs {
log.Infof("OND %v: name: %v, uuid: %v", i+1, dev.Name, dev.Id)
sid, err := uuid.Parse(dev.GetSbi().GetId())
if err != nil {
......
......@@ -58,6 +58,7 @@ The --controller flag is required to change the controller address`,
if err != nil {
return err
}
sid := resp.Sbi[0].GetId()
viper.Set("CLI_SBI", sid)
log.Infof("SBI: %v", sid)
......
......@@ -12,11 +12,13 @@ import (
ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
"code.fbi.h-da.de/danet/gosdn/controller/config"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/device"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound"
"code.fbi.h-da.de/danet/gosdn/controller/mocks"
nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/server"
"code.fbi.h-da.de/danet/gosdn/controller/nucleus"
"code.fbi.h-da.de/danet/gosdn/controller/nucleus/util/proto"
"code.fbi.h-da.de/danet/gosdn/controller/store"
"code.fbi.h-da.de/danet/yang-models/generated/openconfig"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
......@@ -40,8 +42,8 @@ const changeID = "0992d600-f7d4-4906-9559-409b04d59a5f"
const sbiID = "f6fd4b35-f039-4111-9156-5e4501bb8a5a"
const ondID = "7e0ed8cc-ebf5-46fa-9794-741494914883"
var pndStore *store.PndStore
var sbiStore *store.SbiStore
var pndStore networkdomain.PndStore
var sbiStore southbound.SbiStore
var lis *bufconn.Listener
var pndUUID uuid.UUID
var sbiUUID uuid.UUID
......@@ -74,8 +76,8 @@ func bootstrapUnitTest() {
log.Fatal(err)
}
pndStore = store.NewPndStore()
sbiStore = store.NewSbiStore(pndUUID)
pndStore = nucleus.NewPndStore()
sbiStore = nucleus.NewSbiStore(pndUUID)
mockChange := &mocks.Change{}
mockChange.On("Age").Return(time.Hour)
......@@ -95,8 +97,13 @@ func bootstrapUnitTest() {
}, nil)
mockPnd.On("Commit", mock.Anything).Return(nil)
mockPnd.On("Confirm", mock.Anything).Return(nil)
mockPnd.On("Devices").Return([]uuid.UUID{deviceUUID})
mockPnd.On("GetSBIs").Return(sbiStore)
mockPnd.On("Devices").Return([]device.Device{
&nucleus.CommonDevice{
UUID: deviceUUID,
GoStruct: &openconfig.Device{},
},
})
mockPnd.On("GetSBIs").Return([]mocks.SouthboundInterface{})
mockPnd.On("ChangeOND", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(uuid.Nil, nil)
if err := pndStore.Add(&mockPnd); err != nil {
......
......@@ -16,6 +16,7 @@ const (
baseSouthBoundTypeKey = "baseSouthBoundType"
baseSouthBoundUUIDKey = "baseSouthBoundUUID"
changeTimeoutKey = "GOSDN_CHANGE_TIMEOUT"
databaseConnectionKey = "databaseConnection"
)
// BasePndUUID is an uuid for the base PND
......@@ -33,6 +34,9 @@ var ChangeTimeout time.Duration
// LogLevel ist the default log level
var LogLevel logrus.Level
// DatabaseConnection holds the credentials and address of the used database
var DatabaseConnection string
// Init gets called on module import
func Init() {
err := InitializeConfig()
......@@ -75,9 +79,17 @@ func InitializeConfig() error {
setLogLevel()
DatabaseConnection = getStringFromViper(databaseConnectionKey)
return nil
}
// UseDatabase enables other modules to decide if they should use
// a database as backend.
func UseDatabase() bool {
return len(DatabaseConnection) != 0
}
func getUUIDFromViper(viperKey string) (uuid.UUID, error) {
UUIDAsString := viper.GetString(viperKey)
if UUIDAsString == "" {
......@@ -98,6 +110,12 @@ func getUUIDFromViper(viperKey string) (uuid.UUID, error) {
return parsedUUID, nil
}
func getStringFromViper(viperKey string) string {
stringFromViper := viper.GetString(viperKey)
return stringFromViper
}
func setChangeTimeout() error {
e := os.Getenv(changeTimeoutKey)
if e != "" {
......
......@@ -15,11 +15,11 @@ func TestInit(t *testing.T) {
viper.Set("baseSouthBoundUUID", "bf8160d4-4659-4a1b-98fd-f409a04111eb")
viper.Set("basePNDUUID", "bf8160d4-4659-4a1b-98fd-f409a04111ec")
viper.Set("GOSDN_CHANGE_TIMEOUT", "10m")
viper.Set("databaseConnection", "test@test:test")
}
func TestUseExistingConfig(t *testing.T) {
TestInit(t)
err := InitializeConfig()
if err != nil {
t.Error(err)
......@@ -59,4 +59,29 @@ func TestUseExistingConfig(t *testing.T) {
logrus.InfoLevel, LogLevel)
}
}
if DatabaseConnection != "test@test:test" {
t.Fatalf("DatabaseConnection is not test@test:test. got=%s",
DatabaseConnection)
}
if UseDatabase() != true {
t.Fatalf("DatabaseConnection is not set, but should not be used. got=%t",
UseDatabase())
}
}
func TestUseDatabase(t *testing.T) {
viper.Set("databaseConnection", "")
err := InitializeConfig()
if err != nil {
t.Error(err)
return
}
if UseDatabase() != false {
t.Fatalf("DatabaseConnection is not set and should not be used. got=%t",
UseDatabase())
}
}
basepnduuid = "bf8160d4-4659-4a1b-98fd-f409a04111ec"
basesouthboundtype = 1
basesouthbounduuid = "bf8160d4-4659-4a1b-98fd-f409a04111eb"
databaseconnection = "test@test:test"
gosdn_change_timeout = "10m"
......@@ -22,6 +22,8 @@ import (
apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
"code.fbi.h-da.de/danet/gosdn/controller/config"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/device"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound"
"code.fbi.h-da.de/danet/gosdn/controller/northbound/server"
nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/server"
......@@ -36,7 +38,7 @@ var coreOnce sync.Once
// Core is the representation of the controller's core
type Core struct {
pndc *store.PndStore
pndc networkdomain.PndStore
httpServer *http.Server
grpcServer *grpc.Server
nbi *nbi.NorthboundInterface
......@@ -47,18 +49,21 @@ type Core struct {
var c *Core
func init() {
// initialize does start-up housekeeping like reading controller config files
func initialize() error {
err := config.InitializeConfig()
if err != nil {
return err
}
c = &Core{
pndc: store.NewPndStore(),
pndc: nucleus.NewPndStore(),
stopChan: make(chan os.Signal, 1),
}
// Setting up signal capturing
signal.Notify(c.stopChan, os.Interrupt, syscall.SIGTERM)
}
// initialize does start-up housekeeping like reading controller config files
func initialize() error {
if err := startGrpc(); err != nil {
return err
}
......@@ -67,22 +72,12 @@ func initialize() error {
startHttpServer()
coreLock.Unlock()
err := config.InitializeConfig()
if err != nil {
return err
}
err = restorePrincipalNetworkDomains()
err = config.InitializeConfig()
if err != nil {
return err
}
sbi, err := createSouthboundInterfaces()
if err != nil {
return err
}
err = createPrincipalNetworkDomain(sbi)
err = createPrincipalNetworkDomain()
if err != nil {
return err
}
......@@ -135,52 +130,28 @@ func createSouthboundInterfaces() (southbound.SouthboundInterface, error) {
}
// createPrincipalNetworkDomain initializes the controller with an initial PND
func createPrincipalNetworkDomain(s southbound.SouthboundInterface) error {
if !c.pndc.Exists(config.BasePndUUID) {
pnd, err := nucleus.NewPND("base", "gosdn base pnd", config.BasePndUUID, s, c.csbiClient, callback)
if err != nil {
return err
}
err = c.pndc.Add(pnd)
if err != nil {
return err
}
return nil
}
return nil
}
// restorePrincipalNetworkDomains restores previously stored PNDs
func restorePrincipalNetworkDomains() error {
pndsFromStore, err := c.pndc.Load()
if err != nil {
return err
}
sbi, err := createSouthboundInterfaces()
func createPrincipalNetworkDomain() error {
basePnd, err := c.pndc.Get(store.Query{ID: config.BasePndUUID})
if err != nil {
return err
log.Info(err)
}
for _, pndFromStore := range pndsFromStore {
log.Debugf("Restoring PND: %s\n", pndFromStore.Name)
newPnd, err := nucleus.NewPND(
pndFromStore.Name,
pndFromStore.Description,
pndFromStore.ID,
sbi,
if basePnd == nil {
pnd, err := nucleus.NewPND(
"base",
"gosdn base pnd",
config.BasePndUUID,
c.csbiClient,
callback,
)
if err != nil {
return err
}
err = c.pndc.Add(newPnd)
err = c.pndc.Add(pnd)
if err != nil {
return err
}
return nil
}
return nil
......@@ -215,7 +186,7 @@ func shutdown() error {
return stopHttpServer()
}
func callback(id uuid.UUID, ch chan store.DeviceDetails) {
func callback(id uuid.UUID, ch chan device.Details) {
if ch != nil {
c.pndc.AddPendingChannel(id, ch)
log.Infof("pending channel %v added", id)
......
......@@ -6,6 +6,8 @@ import (
"github.com/google/uuid"
"github.com/openconfig/ygot/ygot"
"google.golang.org/protobuf/proto"
tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
)
// Device represents an Orchestrated Network Device (OND) which is managed by
......@@ -18,3 +20,10 @@ type Device interface {
SBI() southbound.SouthboundInterface
ProcessResponse(proto.Message) error
}
// Details contains details of a device used by the cSBI mechanism
type Details struct {
ID string
Address string
TransportOption *tpb.TransportOption
}
package device
import (
"code.fbi.h-da.de/danet/gosdn/controller/store"
)
// Store describes an interface for device store implementations.
type Store interface {
Add(d Device) error
Update(d Device) error
Delete(Device) error
Get(store.Query) (Device, error)
GetAll() ([]Device, error)
}
......@@ -6,7 +6,6 @@ import (
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/change"
"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/store"
"github.com/google/uuid"
"google.golang.org/protobuf/proto"
)
......@@ -18,18 +17,17 @@ type NetworkDomain interface {
AddSbi(s southbound.SouthboundInterface) error
RemoveSbi(uuid.UUID) error
AddDevice(name string, opts *tpb.TransportOption, sid uuid.UUID) error
AddDeviceFromStore(name string, deviceUUID uuid.UUID, opt *tpb.TransportOption, sid uuid.UUID) error
GetDevice(identifier string) (device.Device, error)
RemoveDevice(uuid.UUID) error
Devices() []uuid.UUID
Devices() []device.Device
ChangeOND(uuid uuid.UUID, operation ppb.ApiOperation, path string, value ...string) (uuid.UUID, error)
Request(uuid.UUID, string) (proto.Message, error)
RequestAll(string) error
GetName() string
GetDescription() string
MarshalDevice(string) (string, error)
ContainsDevice(uuid.UUID) bool
GetSBIs() store.Store
GetSBIs() ([]southbound.SouthboundInterface, error)
GetSBI(uuid.UUID) (southbound.SouthboundInterface, error)
ID() uuid.UUID
PendingChanges() []uuid.UUID
CommittedChanges() []uuid.UUID
......
package networkdomain
import (
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/device"
"code.fbi.h-da.de/danet/gosdn/controller/store"
"github.com/google/uuid"
)
// PndStore describes an interface for pnd store implementations.
type PndStore interface {
Add(NetworkDomain) error
Delete(NetworkDomain) error
Get(store.Query) (NetworkDomain, error)
GetAll() ([]NetworkDomain, error)
PendingChannels(id uuid.UUID, parseErrors ...error) (chan device.Details, error)
AddPendingChannel(id uuid.UUID, ch chan device.Details)
RemovePendingChannel(id uuid.UUID)
}
......@@ -23,4 +23,5 @@ type SouthboundInterface interface { // nolint
SetID(id uuid.UUID)
Type() spb.Type
Unmarshal([]byte, *gpb.Path, ygot.ValidatedGoStruct, ...ytypes.UnmarshalOpt) error
Name() string
}
package southbound
import (
"code.fbi.h-da.de/danet/gosdn/controller/store"
)
// SbiStore describes an interface for sbi store implementations.
type SbiStore interface {
Add(SouthboundInterface) error
Delete(SouthboundInterface) error
Get(store.Query) (SouthboundInterface, error)
GetAll() ([]SouthboundInterface, error)
}
......@@ -3,11 +3,11 @@
package mocks
import (
time "time"
pnd "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
mock "github.com/stretchr/testify/mock"
time "time"
uuid "github.com/google/uuid"
)
......
// Code generated by mockery v2.9.4. DO NOT EDIT.
// Code generated by mockery v2.10.0. DO NOT EDIT.
package mocks
......
// Code generated by mockery v2.9.4. DO NOT EDIT.
// Code generated by mockery v2.10.0. DO NOT EDIT.
package mocks
......@@ -14,8 +14,6 @@ import (
southbound "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound"
store "code.fbi.h-da.de/danet/gosdn/controller/interfaces/store"
transport "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
uuid "github.com/google/uuid"
......@@ -40,20 +38,6 @@ func (_m *NetworkDomain) AddDevice(name string, opts *transport.TransportOption,
return r0
}
// AddDeviceFromStore provides a mock function with given fields: name, deviceUUID, opt, sid
func (_m *NetworkDomain) AddDeviceFromStore(name string, deviceUUID uuid.UUID, opt *transport.TransportOption, sid uuid.UUID) error {
ret := _m.Called(name, deviceUUID, opt, sid)
var r0 error
if rf, ok := ret.Get(0).(func(string, uuid.UUID, *transport.TransportOption, uuid.UUID) error); ok {
r0 = rf(name, deviceUUID, opt, sid)
} else {
r0 = ret.Error(0)
}
return r0
}
// AddSbi provides a mock function with given fields: s
func (_m *NetworkDomain) AddSbi(s southbound.SouthboundInterface) error {
ret := _m.Called(s)
......@@ -142,20 +126,6 @@ func (_m *NetworkDomain) Confirm(_a0 uuid.UUID) error {
return r0
}
// ContainsDevice provides a mock function with given fields: _a0
func (_m *NetworkDomain) ContainsDevice(_a0 uuid.UUID) bool {
ret := _m.Called(_a0)
var r0 bool
if rf, ok := ret.Get(0).(func(uuid.UUID) bool); ok {
r0 = rf(_a0)
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// Destroy provides a mock function with given fields:
func (_m *NetworkDomain) Destroy() error {
ret := _m.Called()
......@@ -171,15 +141,15 @@ func (_m *NetworkDomain) Destroy() error {
}
// Devices provides a mock function with given fields:
func (_m *NetworkDomain) Devices() []uuid.UUID {
func (_m *NetworkDomain) Devices() []device.Device {
ret := _m.Called()
var r0 []uuid.UUID
if rf, ok := ret.Get(0).(func() []uuid.UUID); ok {
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).([]uuid.UUID)
r0 = ret.Get(0).([]device.Device)
}
}
......@@ -260,20 +230,50 @@ func (_m *NetworkDomain) GetName() string {
return r0
}
// GetSBI provides a mock function with given fields: _a0
func (_m *NetworkDomain) GetSBI(_a0 uuid.UUID) (southbound.SouthboundInterface, error) {
ret := _m.Called(_a0)
var r0 southbound.SouthboundInterface
if rf, ok := ret.Get(0).(func(uuid.UUID) southbound.SouthboundInterface); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(southbound.SouthboundInterface)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(uuid.UUID) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetSBIs provides a mock function with given fields:
func (_m *NetworkDomain) GetSBIs() store.Store {
func (_m *NetworkDomain) GetSBIs() ([]southbound.SouthboundInterface, error) {
ret := _m.Called()
var r0 store.Store
if rf, ok := ret.Get(0).(func() store.Store); ok {
var r0 []southbound.SouthboundInterface
if rf, ok := ret.Get(0).(func() []southbound.SouthboundInterface); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.Store)
r0 = ret.Get(0).([]southbound.SouthboundInterface)
}
}
return r0
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ID provides a mock function with given fields:
......
// Code generated by mockery v2.9.4. DO NOT EDIT.
// Code generated by mockery v2.10.0. DO NOT EDIT.
package mocks
......
// Code generated by mockery v2.9.4. DO NOT EDIT.
// Code generated by mockery v2.10.0. DO NOT EDIT.
package mocks
import (
gosdnsouthbound "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
gnmi "github.com/openconfig/gnmi/proto/gnmi"
mock "github.com/stretchr/testify/mock"
uuid "github.com/google/uuid"
......@@ -38,6 +37,20 @@ func (_m *SouthboundInterface) ID() uuid.UUID {
return r0
}
// Name provides a mock function with given fields:
func (_m *SouthboundInterface) Name() string {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// Schema provides a mock function with given fields:
func (_m *SouthboundInterface) Schema() *ytypes.Schema {
ret := _m.Called()
......
// Code generated by mockery v2.9.4. DO NOT EDIT.
// Code generated by mockery v2.10.0. DO NOT EDIT.
package mocks
......
// Code generated by mockery v2.9.4. DO NOT EDIT.
package mocks
import (
store "code.fbi.h-da.de/danet/gosdn/controller/interfaces/store"
uuid "github.com/google/uuid"
mock "github.com/stretchr/testify/mock"
)
// Store is an autogenerated mock type for the Store type
type Store struct {
mock.Mock
}
// Add provides a mock function with given fields: item
func (_m *Store) Add(item store.Storable) error {
ret := _m.Called(item)
var r0 error
if rf, ok := ret.Get(0).(func(store.Storable) error); ok {
r0 = rf(item)
} else {
r0 = ret.Error(0)
}
return r0
}
// Delete provides a mock function with given fields: id
func (_m *Store) Delete(id uuid.UUID) error {
ret := _m.Called(id)
var r0 error
if rf, ok := ret.Get(0).(func(uuid.UUID) error); ok {
r0 = rf(id)
} else {
r0 = ret.Error(0)
}
return r0
}
// Exists provides a mock function with given fields: id
func (_m *Store) Exists(id uuid.UUID) bool {
ret := _m.Called(id)
var r0 bool
if rf, ok := ret.Get(0).(func(uuid.UUID) bool); ok {
r0 = rf(id)
} else {
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
if rf, ok := ret.Get(1).(func(uuid.UUID) error); ok {
r1 = rf(id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UUIDs provides a mock function with given fields:
func (_m *Store) UUIDs() []uuid.UUID {
ret := _m.Called()
var r0 []uuid.UUID
if rf, ok := ret.Get(0).(func() []uuid.UUID); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]uuid.UUID)
}
}
return r0
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment