Skip to content
Snippets Groups Projects
Commit 881b7ad0 authored by Manuel Kieweg's avatar Manuel Kieweg
Browse files

moved controller to project root

parent a2da8015
No related branches found
No related tags found
9 merge requests!246Develop,!245Develop into Master,!244Master into develop2 into master,!219Draft: Testing,!214Test pipelines,!195DO NOT MERGE 2,!194DO NOT MERGE! just for testing,!154moved controller to project root,!138Develop
This commit is part of merge request !138. Comments created here will be created in the context of that merge request.
# Collection of programming stuff
## Dependencies
* github.com/spf13/cobra: used for basic cli of gosdn, such as starting the daemon, get versioning info etc
* grpc
* ygot
## Structure of the code
main.go: main() function
nucleus/: core functionality of gosdn
ygot (yang for go tools).
Checkout this in go: go get github.com/openconfig/ygot/ygot
## Usefull things to know
Regenerate gRPC code (https://grpc.io/docs/languages/go/quickstart/#regenerate-grpc-code)
* ( cd ~/go/src/github.com/grpc-go/cmd/protoc-gen-go-grpc/ && go install . )
*
protoc \
--go_out=Mgrpc/service_config/service_config.proto=/internal/proto/grpc_service_config:. \
--go-grpc_out=Mgrpc/service_config/service_config.proto=/internal/proto/grpc_service_config:. \
--go_opt=paths=source_relative \
--go-grpc_opt=paths=source_relative \
cliInterface/gosdnCLI.proto
Generate the ygot code:
just type: go generate
......@@ -4,7 +4,7 @@ import (
"context"
"os"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
nucleus "code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
log "github.com/sirupsen/logrus"
appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
......
......@@ -7,7 +7,7 @@ run:
- test
skip-dirs-default: true
skip-files:
- nucleus/http.go
- http.go
# output settings -> code-climate for GitLab
output:
format: code-climate
......
......@@ -44,6 +44,5 @@ unit-test:
controller-test:
script:
- cd ./nucleus
- go test -race -v -run TestRun
<<: *test
\ No newline at end of file
......@@ -3,6 +3,8 @@ package cli
import (
"context"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
gpb "github.com/openconfig/gnmi/proto/gnmi"
......@@ -32,7 +34,7 @@ func Get(a, u, p string, args ...string) (*gpb.GetResponse, error) {
log.Debug(resp)
r, ok := resp.(*gpb.GetResponse)
if !ok {
return nil, &nucleus.ErrInvalidTypeAssertion{}
return nil, &errors.ErrInvalidTypeAssertion{}
}
return r, nil
}
......@@ -8,6 +8,8 @@ import (
"syscall"
"time"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
gpb "github.com/openconfig/gnmi/proto/gnmi"
......@@ -48,7 +50,7 @@ func Subscribe(a, u, p string, sample, heartbeat int64, args ...string) error {
}
done := make(chan os.Signal, 1)
signal.Notify(done, syscall.SIGILL, syscall.SIGTERM)
ctx := context.WithValue(context.Background(), nucleus.CtxKeyOpts, opts) //nolint
ctx := context.WithValue(context.Background(), types.CtxKeyOpts, opts) //nolint
go func() {
if err := device.Transport.Subscribe(ctx); err != nil {
log.Fatal(err)
......
......@@ -35,7 +35,8 @@ import (
"context"
"os"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"code.fbi.h-da.de/cocsn/gosdn"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
......@@ -60,7 +61,7 @@ for REST API calls.`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
return nucleus.Run(ctx)
return gosdn.Run(ctx)
},
}
......
package nucleus
package gosdn
import (
"context"
......@@ -8,7 +8,10 @@ import (
"sync"
"time"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
"code.fbi.h-da.de/cocsn/gosdn/database"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
)
......@@ -21,8 +24,8 @@ type Core struct {
// deprecated
database database.Database
pndc pndStore
sbic sbiStore
pndc *nucleus.PndStore
sbic *nucleus.SbiStore
httpServer *http.Server
stopChan chan os.Signal
}
......@@ -32,8 +35,8 @@ var c *Core
func init() {
c = &Core{
database: database.Database{},
pndc: pndStore{store{}},
sbic: sbiStore{store{}},
pndc: nucleus.NewPndStore(),
sbic: nucleus.NewSbiStore(),
stopChan: make(chan os.Signal, 1),
}
......@@ -50,25 +53,26 @@ func initialize() error {
// TODO: Start grpc listener here
coreLock.Lock()
defer coreLock.Unlock()
return httpAPI()
startHttpServer()
return nil
}
// createSouthboundInterfaces initializes the controller with its supported SBIs
func createSouthboundInterfaces() error {
sbi := &OpenConfig{id: uuid.New()}
if err := c.sbic.add(sbi); err != nil {
sbi := nucleus.NewSBI(types.Openconfig)
if err := c.sbic.Add(sbi); err != nil {
return err
}
return createPrincipalNetworkDomain(sbi)
}
// createPrincipalNetworkDomain initializes the controller with an initial PND
func createPrincipalNetworkDomain(sbi SouthboundInterface) error {
pnd, err := NewPND("base", "gosdn base pnd", uuid.New(), sbi)
func createPrincipalNetworkDomain(s nucleus.SouthboundInterface) error {
pnd, err := nucleus.NewPND("base", "gosdn base pnd", uuid.New(), s)
if err != nil {
return err
}
err = c.pndc.add(pnd)
err = c.pndc.Add(pnd)
if err != nil {
return err
}
......
package nucleus
package gosdn
import (
"context"
......
gosdn.png

132 KiB

@startuml
namespace nucleus {
class ClientConfig << (S,Aquamarine) >> {
+ Identifier string
+ Endpoint string
+ Username string
+ Password string
+ GjsonDefaultPath string
+ GjsonConnectionsPath string
}
class Core << (S,Aquamarine) >> {
- devices <font color=blue>map</font>[string]Device
- southboundInterfaces <font color=blue>map</font>[string]interfaces.SouthboundInterface
- prinipalNetworkDomains <font color=blue>map</font>[uuid.UUID]interfaces.PrincipalNetworkDomain
- database database.Database
- config controllerConfig
+ IsRunning <font color=blue>chan</font> bool
- readControllerConfig(configFileController string) error
+ Init(socket string, configFileController string, configFileClient string, IsRunningChannel <font color=blue>chan</font> bool)
+ AttachDatabase()
+ Shutdown()
}
class Device << (S,Aquamarine) >> {
- device ygot.GoStruct
+ SBI interfaces.SouthboundInterface
+ Config DeviceConfig
}
class DeviceConfig << (S,Aquamarine) >> {
+ Identifier uuid.UUID
+ Endpoint string
+ Username string
+ Password string
}
interface PrincipalNetworkDomain {
+ Destroy() error
+ AddSbi() error
+ RemoveSbi() error
+ AddDevice( interfaces.Device) error
+ RemoveDevice(uuid uuid.UUID) error
}
interface SouthboundInterface {
}
class buf << (S,Aquamarine) >> {
+ Write(p []byte) (int, error)
}
class controllerConfig << (S,Aquamarine) >> {
+ CliSocket string
+ DatabaseSocket string
+ DatabaseUser string
+ DatabasePassword string
+ DatabaseCrypto bool
+ ConfigPath string
}
class logConnection << (S,Aquamarine) >> {
- stream proto.GrpcCli_CreateLogStreamServer
- id string
- active bool
- error <font color=blue>chan</font> error
}
class nucleus.buf << (T, #FF7700) >> {
}
class pndImplementation << (S,Aquamarine) >> {
- name string
- sbiContainer <font color=blue>map</font>[string]*interfaces.SouthboundInterface
- devices <font color=blue>map</font>[uuid.UUID]Device
+ Destroy() error
+ AddSbi() error
+ RemoveSbi() error
+ AddDevice(device Device) error
+ RemoveDevice(uuid uuid.UUID) error
}
class server << (S,Aquamarine) >> {
- core *Core
- logConnections []*logConnection
+ SayHello(ctx context.Context, in *proto.HelloRequest) (*proto.HelloReply, error)
+ CreateLogStream(req *emptypb.Empty, stream proto.GrpcCli_CreateLogStreamServer) error
+ BroadcastLog(log *proto.LogReply)
+ Shutdown(ctx context.Context, in *proto.ShutdownRequest) (*proto.ShutdownReply, error)
+ TAPIGetEdge(ctx context.Context, in *proto.TAPIRequest) (*proto.TAPIReply, error)
+ TAPIGetEdgeNode(ctx context.Context, in *proto.TAPIRequest) (*proto.TAPIReply, error)
+ TAPIGetLink(ctx context.Context, in *proto.TAPIRequest) (*proto.TAPIReply, error)
}
}
"proto.UnimplementedGrpcCliServer" *-- "nucleus.server"
namespace sbi {
class OpenConfig << (S,Aquamarine) >> {
- name string
- clientContainer []Client
+ AddClient() error
+ RemoveClient() error
+ CollectHeartbeats() error
+ ListClients() <font color=blue>map</font>[int]interfaces.Client
}
}
"__builtin__.[]byte" #.. "nucleus.buf"
@enduml
package nucleus
package gosdn
import (
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
"context"
"fmt"
"github.com/google/uuid"
......@@ -13,10 +16,10 @@ import (
"time"
)
var apiOpmap = map[string]Operation{
"update": TransportUpdate,
"replace": TransportReplace,
"delete": TransportDelete,
var apiOpmap = map[string]types.Operation{
"update": types.TransportUpdate,
"replace": types.TransportReplace,
"delete": types.TransportDelete,
}
func stopHttpServer() error {
......@@ -32,19 +35,17 @@ func registerHttpHandler() {
fmt.Println("Recovered in f", r)
}
}()
http.HandleFunc("/api", httpHandler)
http.HandleFunc("/api", httpApi)
http.HandleFunc("/livez", healthCheck)
http.HandleFunc("/readyz", readynessCheck)
}
// deprecated
func httpAPI() error {
func startHttpServer() {
registerHttpHandler()
c.httpServer = &http.Server{Addr: ":8080"}
go func() {
log.Info(c.httpServer.ListenAndServe())
}()
return nil
}
func healthCheck(writer http.ResponseWriter, request *http.Request) {
......@@ -55,8 +56,9 @@ func readynessCheck(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusOK)
}
// deprecated
// nolint
func httpHandler(writer http.ResponseWriter, request *http.Request) {
func httpApi(writer http.ResponseWriter, request *http.Request) {
log.WithFields(log.Fields{
"request": request,
}).Debug("incoming request")
......@@ -89,16 +91,16 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
}
}
var pnd PrincipalNetworkDomain
var sbi SouthboundInterface
var httpPnd nucleus.PrincipalNetworkDomain
var httpSbi nucleus.SouthboundInterface
if query.Get("q") != "init" && query.Get("q") != "getIDs" {
pnd, err = c.pndc.get(pid)
httpPnd, err = c.pndc.Get(pid)
if err != nil {
handleServerError(writer, err)
return
}
sbic := pnd.GetSBIs()
sbi, err = sbic.(*sbiStore).get(sid)
sbic := httpPnd.GetSBIs()
httpSbi, err = sbic.(*nucleus.SbiStore).Get(sid)
if err != nil {
handleServerError(writer, err)
return
......@@ -107,18 +109,18 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
switch q := query.Get("q"); q {
case "addDevice":
d, err := NewDevice(sbi, &GnmiTransportOptions{
d, err := nucleus.NewDevice(httpSbi, &nucleus.GnmiTransportOptions{
Config: gnmi.Config{
Addr: query.Get("address"),
Password: query.Get("password"),
Username: query.Get("username"),
Encoding: gpb.Encoding_JSON_IETF,
},
SetNode: sbi.SetNode(),
Unmarshal: sbi.(*OpenConfig).Unmarshal(),
SetNode: httpSbi.SetNode(),
Unmarshal: httpSbi.(*nucleus.OpenConfig).Unmarshal(),
RespChan: make(chan *gpb.SubscribeResponse),
})
err = pnd.AddDevice(d)
err = httpPnd.AddDevice(d)
if err != nil {
writer.WriteHeader(http.StatusInternalServerError)
log.Error(err)
......@@ -128,10 +130,10 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "device added\n")
fmt.Fprintf(writer, "UUID: %v\n", d.UUID)
case "request":
err = pnd.Request(id, query.Get("path"))
err = httpPnd.Request(id, query.Get("path"))
if err != nil {
switch err.(type) {
case *ErrNotFound:
case *errors.ErrNotFound:
writer.WriteHeader(http.StatusNotFound)
default:
writer.WriteHeader(http.StatusInternalServerError)
......@@ -141,10 +143,10 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
}
writer.WriteHeader(http.StatusOK)
case "requestAll":
err = pnd.RequestAll(query.Get("path"))
err = httpPnd.RequestAll(query.Get("path"))
if err != nil {
switch err.(type) {
case *ErrNotFound:
case *errors.ErrNotFound:
writer.WriteHeader(http.StatusNotFound)
default:
writer.WriteHeader(http.StatusInternalServerError)
......@@ -154,10 +156,10 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
}
writer.WriteHeader(http.StatusOK)
case "getDevice":
device, err := pnd.MarshalDevice(id)
device, err := httpPnd.MarshalDevice(id)
if err != nil {
switch err.(type) {
case *ErrNotFound:
case *errors.ErrNotFound:
writer.WriteHeader(http.StatusNotFound)
default:
writer.WriteHeader(http.StatusInternalServerError)
......@@ -173,7 +175,7 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
writeIDs(writer, "PNDs", pnds)
writeIDs(writer, "SBIs", c.sbic.UUIDs())
for _, id := range pnds {
p, err := c.pndc.get(id)
p, err := c.pndc.Get(id)
if err != nil {
handleServerError(writer, err)
return
......@@ -184,22 +186,22 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
writeIDs(writer, "PNDs", c.pndc.UUIDs())
writeIDs(writer, "SBIs", c.sbic.UUIDs())
case "update", "replace":
if err := pnd.ChangeOND(id, apiOpmap[q], query.Get("path"), query.Get("value")); err != nil {
if err := httpPnd.ChangeOND(id, apiOpmap[q], query.Get("path"), query.Get("value")); err != nil {
handleServerError(writer, err)
return
}
writer.WriteHeader(http.StatusOK)
case "delete":
if err := pnd.ChangeOND(id, TransportDelete, query.Get("path")); err != nil {
if err := httpPnd.ChangeOND(id, types.TransportDelete, query.Get("path")); err != nil {
handleServerError(writer, err)
return
}
writer.WriteHeader(http.StatusOK)
case "change-list":
changes := pnd.Committed()
changes := httpPnd.Committed()
writeIDs(writer, "Tentative changes", changes)
case "change-list-pending":
changes := pnd.Pending()
changes := httpPnd.Pending()
writeIDs(writer, "Pending changes", changes)
case "change-commit":
cuid, err := uuid.Parse(query.Get("cuid"))
......@@ -207,7 +209,7 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
handleServerError(writer, err)
return
}
if err := pnd.Commit(cuid); err != nil {
if err := httpPnd.Commit(cuid); err != nil {
handleServerError(writer, err)
return
}
......@@ -218,7 +220,7 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
handleServerError(writer, err)
return
}
if err := pnd.Confirm(cuid); err != nil {
if err := httpPnd.Confirm(cuid); err != nil {
handleServerError(writer, err)
return
}
......
package nucleus
package gosdn
import (
"errors"
"net/http"
"testing"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
"code.fbi.h-da.de/cocsn/gosdn/mocks"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/mock"
)
func testSetupHTTP() {
sbi = &OpenConfig{id: defaultSbiID}
sbi = nucleus.NewSBI(types.Openconfig)
sbi.Schema()
defaultSbiID = sbi.ID()
var err error
httpTestPND, err = NewPND("test", "test pnd", defaultPndID, sbi)
httpTestPND, err = nucleus.NewPND("test", "test pnd", defaultPndID, sbi)
if err != nil {
log.Fatal(err)
}
......@@ -31,10 +35,10 @@ func testSetupHTTP() {
}
args = "&uuid=" + mdid.String() + "&pnd=" + defaultPndID.String() + "&sbi=" + defaultSbiID.String()
argsNotFound = "&uuid=" + uuid.New().String() + "&pnd=" + defaultPndID.String() + "&sbi=" + defaultSbiID.String()
if err := c.sbic.add(sbi); err != nil {
if err := c.sbic.Add(sbi); err != nil {
log.Fatal(err)
}
if err := c.pndc.add(httpTestPND); err != nil {
if err := c.pndc.Add(httpTestPND); err != nil {
log.Fatal(err)
}
}
......@@ -183,10 +187,7 @@ func Test_httpApi(t *testing.T) {
},
}
coreLock.Lock()
if err := httpAPI(); err != nil {
t.Errorf("httpApi() error = %v", err)
return
}
startHttpServer()
coreLock.Unlock()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
......@@ -199,7 +200,7 @@ func Test_httpApi(t *testing.T) {
t.Errorf("httpApi() got: %v, want %v", got.StatusCode, tt.want.StatusCode)
}
if tt.name == "add-device" {
for k := range httpTestPND.(*pndImplementation).devices.store {
for _, k := range httpTestPND.Devices() {
if k != mdid {
if err := httpTestPND.RemoveDevice(k); err != nil {
t.Error(err)
......
package gosdn
import (
"context"
"os"
"testing"
"code.fbi.h-da.de/cocsn/gosdn/mocks"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/util/proto"
"github.com/google/uuid"
gpb "github.com/openconfig/gnmi/proto/gnmi"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/mock"
pb "google.golang.org/protobuf/proto"
)
const apiEndpoint = "http://localhost:8080"
// UUIDs for test cases
var mdid uuid.UUID
var defaultSbiID uuid.UUID
var defaultPndID uuid.UUID
var cuid uuid.UUID
var sbi nucleus.SouthboundInterface
var httpTestPND nucleus.PrincipalNetworkDomain
var gnmiMessages map[string]pb.Message
var httpTestDevice nucleus.Device
var args string
var argsNotFound string
var mockContext = mock.MatchedBy(func(ctx context.Context) bool { return true })
// TestMain bootstraps all tests. Humongous beast
// TODO: Move somewhere more sensible
func TestMain(m *testing.M) {
log.SetReportCaller(true)
if os.Getenv("GOSDN_LOG") == "nolog" {
log.SetLevel(log.PanicLevel)
}
gnmiMessages = map[string]pb.Message{
"./test/proto/cap-resp-arista-ceos": &gpb.CapabilityResponse{},
"./test/proto/req-full-node": &gpb.GetRequest{},
"./test/proto/req-full-node-arista-ceos": &gpb.GetRequest{},
"./test/proto/req-interfaces-arista-ceos": &gpb.GetRequest{},
"./test/proto/req-interfaces-interface-arista-ceos": &gpb.GetRequest{},
"./test/proto/req-interfaces-wildcard": &gpb.GetRequest{},
"./test/proto/resp-full-node": &gpb.GetResponse{},
"./test/proto/resp-full-node-arista-ceos": &gpb.GetResponse{},
"./test/proto/resp-interfaces-arista-ceos": &gpb.GetResponse{},
"./test/proto/resp-interfaces-interface-arista-ceos": &gpb.GetResponse{},
"./test/proto/resp-interfaces-wildcard": &gpb.GetResponse{},
"./test/proto/resp-set-system-config-hostname": &gpb.SetResponse{},
}
for k, v := range gnmiMessages {
if err := proto.Read(k, v); err != nil {
log.Fatalf("error parsing %v: %v", k, err)
}
}
readTestUUIDs()
testSetupHTTP()
os.Exit(m.Run())
}
func readTestUUIDs() {
var err error
mdid, err = uuid.Parse("688a264e-5f85-40f8-bd13-afc42fcd5c7a")
defaultPndID, err = uuid.Parse("b4016412-eec5-45a1-aa29-f59915357bad")
cuid, err = uuid.Parse("3e8219b0-e926-400d-8660-217f2a25a7c6")
if err != nil {
log.Fatal(err)
}
}
func mockDevice() nucleus.Device {
sbi := &nucleus.OpenConfig{}
return nucleus.Device{
UUID: mdid,
GoStruct: sbi.Schema().Root,
SBI: sbi,
Transport: &mocks.Transport{},
}
}
package pnd
package nucleus
import (
"errors"
......
package pnd
package nucleus
import (
"context"
......@@ -33,7 +33,7 @@ func TestChange_CommitRollback(t *testing.T) {
want := rollback
callback := make(chan string)
c := &Change{
cuid: changeUUID,
cuid: cuid,
duid: did,
timestamp: time.Now(),
previousState: rollbackDevice,
......@@ -67,7 +67,7 @@ func TestChange_CommitRollbackError(t *testing.T) {
wantErr := false
want := errors.New("this is an expected error")
c := &Change{
cuid: changeUUID,
cuid: cuid,
duid: did,
timestamp: time.Now(),
previousState: rollbackDevice,
......@@ -101,7 +101,7 @@ func TestChange_CommitRollbackError(t *testing.T) {
func TestChange_CommitError(t *testing.T) {
wantErr := true
c := &Change{
cuid: changeUUID,
cuid: cuid,
duid: did,
timestamp: time.Now(),
previousState: rollbackDevice,
......@@ -129,7 +129,7 @@ func TestChange_Commit(t *testing.T) {
callback := make(chan string)
c := &Change{
cuid: changeUUID,
cuid: cuid,
duid: did,
timestamp: time.Now(),
previousState: rollbackDevice,
......@@ -228,8 +228,8 @@ func TestChange_ID(t *testing.T) {
}{
{
name: "default",
fields: fields{cuid: changeUUID},
want: changeUUID,
fields: fields{cuid: cuid},
want: cuid,
},
}
for _, tt := range tests {
......
package nucleus
import (
"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
"github.com/google/uuid"
"github.com/openconfig/ygot/ygot"
)
......@@ -25,14 +26,14 @@ type Device struct {
func NewDevice(sbi SouthboundInterface, opts TransportOptions) (*Device, error) {
var transport Transport
var err error
switch opts.(type) {
switch o := opts.(type) {
case *GnmiTransportOptions:
transport, err = NewGnmiTransport(opts.(*GnmiTransportOptions))
transport, err = NewGnmiTransport(o)
if err != nil {
return nil, err
}
default:
return nil, &ErrInvalidTransportOptions{opts}
return nil, &errors.ErrInvalidTransportOptions{Opt: o}
}
return &Device{
......
package nucleus
package errors
import (
"fmt"
......@@ -24,42 +24,42 @@ func (e *ErrNil) Error() string {
// ErrNotFound implements the Error interface and is called if a specific ID
// of a storable item could not be found.
type ErrNotFound struct {
id interface{}
ID interface{}
}
func (e *ErrNotFound) Error() string {
return fmt.Sprintf("%v not found", e.id)
return fmt.Sprintf("%v not found", e.ID)
}
// ErrAlreadyExists implements the Error interface and is called if a specific ID
// of a storable item already exists.
type ErrAlreadyExists struct {
item interface{}
Item interface{}
}
func (e *ErrAlreadyExists) Error() string {
return fmt.Sprintf("%v already exists", e.item)
return fmt.Sprintf("%v already exists", e.Item)
}
// ErrInvalidTypeAssertion implements the Error interface and is called if the
// type of a storable item does not correspond to the expected type.
type ErrInvalidTypeAssertion struct {
v interface{}
t interface{}
Value interface{}
Type interface{}
}
func (e ErrInvalidTypeAssertion) Error() string {
return fmt.Sprintf("%v does not implement %v", e.v, e.t)
return fmt.Sprintf("%v does not implement %v", e.Value, e.Type)
}
// ErrUnsupportedPath implements the Error interface and is called if the
// given path is not supported.
type ErrUnsupportedPath struct {
p interface{}
Path interface{}
}
func (e ErrUnsupportedPath) Error() string {
return fmt.Sprintf("path %v is not supported", e.p)
return fmt.Sprintf("path %v is not supported", e.Path)
}
// ErrNotYetImplemented implements the Error interface and is called if a function
......@@ -73,30 +73,30 @@ func (e ErrNotYetImplemented) Error() string {
// ErrInvalidParameters implements the Error interface and is called if the wrong
// or no parameters have been provided.
type ErrInvalidParameters struct {
f interface{}
r interface{}
Func interface{}
Param interface{}
}
func (e ErrInvalidParameters) Error() string {
return fmt.Sprintf("invalid parameters for %v: %v", e.f, e.r)
return fmt.Sprintf("invalid parameters for %v: %v", e.Func, e.Param)
}
// ErrInvalidTransportOptions implements the Error interface and is called if the
// wrong TransportOptions have been provided.
type ErrInvalidTransportOptions struct {
t interface{}
Opt interface{}
}
func (e ErrInvalidTransportOptions) Error() string {
return fmt.Sprintf("invalid transport options: %v", reflect.TypeOf(e.t))
return fmt.Sprintf("invalid transport options: %v", reflect.TypeOf(e.Opt))
}
// ErrOperationNotSupported implements the Error interface and is called if the
// wrong Operation has been provided.
type ErrOperationNotSupported struct {
o interface{}
Op interface{}
}
func (e ErrOperationNotSupported) Error() string {
return fmt.Sprintf("transport operation not supported: %v", reflect.TypeOf(e.o))
return fmt.Sprintf("transport operation not supported: %v", reflect.TypeOf(e.Op))
}
......@@ -5,6 +5,8 @@ import (
"reflect"
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
pathutils "code.fbi.h-da.de/cocsn/gosdn/nucleus/util/path"
gpb "github.com/openconfig/gnmi/proto/gnmi"
"github.com/openconfig/gnmi/proto/gnmi_ext"
......@@ -14,25 +16,10 @@ import (
log "github.com/sirupsen/logrus"
)
// CtxKeyType is a custom type to be used as key in a context.WithValue() or
// context.Value() call. For more information see:
// https://www.calhoun.io/pitfalls-of-context-values-and-how-to-avoid-or-mitigate-them/
// TODO: Unexport to comply with best practice
type CtxKeyType string
const (
// CtxKeyOpts context key for gnmi.SubscribeOptions
CtxKeyOpts CtxKeyType = "opts"
// CtxKeyConfig is a context key for gnmi.Config
CtxKeyConfig = "config"
// CtxKeyOperation is a context key for a gNMI operation (update, replace, delete)
CtxKeyOperation = "op"
)
var opmap = map[Operation]string{
TransportUpdate: "update",
TransportReplace: "replace",
TransportDelete: "delete",
var opmap = map[types.Operation]string{
types.TransportUpdate: "update",
types.TransportReplace: "replace",
types.TransportDelete: "delete",
}
// Gnmi implements the Transport interface and provides an SBI with the
......@@ -78,7 +65,7 @@ func (g *Gnmi) GetOptions() interface{} {
// Get takes a slice of gnmi paths, splits them and calls get for each one of them.
func (g *Gnmi) Get(ctx context.Context, params ...string) (interface{}, error) {
if g.client == nil {
return nil, &ErrNilClient{}
return nil, &errors.ErrNilClient{}
}
paths := gnmi.SplitPaths(params)
return g.get(ctx, paths, "")
......@@ -88,12 +75,12 @@ func (g *Gnmi) Get(ctx context.Context, params ...string) (interface{}, error) {
// It can contain an additional arbitrary amount of operations and extensions.
func (g *Gnmi) Set(ctx context.Context, args ...interface{}) error {
if g.client == nil {
return &ErrNilClient{}
return &errors.ErrNilClient{}
}
if len(args) == 0 {
return &ErrInvalidParameters{
f: "gnmi.Set()",
r: "no parameters provided",
return &errors.ErrInvalidParameters{
Func: "gnmi.Set()",
Param: "no parameters provided",
}
}
......@@ -110,19 +97,19 @@ func (g *Gnmi) Set(ctx context.Context, args ...interface{}) error {
for _, o := range args {
attrs, ok := o.([]string)
if !ok {
return &ErrInvalidTypeAssertion{
v: o,
t: reflect.TypeOf("placeholder"),
return &errors.ErrInvalidTypeAssertion{
Value: o,
Type: reflect.TypeOf("placeholder"),
}
} else if attrs == nil || len(attrs) == 0 {
return &ErrInvalidParameters{
f: "gnmi.Set()",
r: "no parameters provided",
return &errors.ErrInvalidParameters{
Func: "gnmi.Set()",
Param: "no parameters provided",
}
}
opts = append(opts, &gnmi.Operation{
// Hardcoded TransportUpdate until multiple operations are supported
Type: opmap[TransportUpdate],
Type: opmap[types.TransportUpdate],
Origin: "",
Target: "",
Path: gnmi.SplitPath(attrs[0]),
......@@ -145,16 +132,16 @@ func (g *Gnmi) Set(ctx context.Context, args ...interface{}) error {
case *gnmi_ext.Extension:
exts = append(exts, p.(*gnmi_ext.Extension))
default:
return &ErrInvalidParameters{
f: "gnmi.Set()",
r: "args contain invalid type",
return &errors.ErrInvalidParameters{
Func: "gnmi.Set()",
Param: "args contain invalid type",
}
}
}
if len(ops) == 0 {
return &ErrInvalidParameters{
f: "gnmi.Set()",
r: "no operations provided",
return &errors.ErrInvalidParameters{
Func: "gnmi.Set()",
Param: "no operations provided",
}
}
resp, err := g.set(ctx, ops, exts...)
......@@ -167,21 +154,21 @@ func (g *Gnmi) Set(ctx context.Context, args ...interface{}) error {
func (g *Gnmi) applyDiff(ctx context.Context, payload ...interface{}) error {
if len(payload) != 2 {
return &ErrInvalidParameters{}
return &errors.ErrInvalidParameters{}
}
op := ctx.Value(CtxKeyOperation)
op := ctx.Value(types.CtxKeyOperation)
oldstate, ok := payload[0].(ygot.GoStruct)
if !ok {
return &ErrInvalidTypeAssertion{
v: payload[0],
t: reflect.TypeOf("ygot.GoStruct"),
return &errors.ErrInvalidTypeAssertion{
Value: payload[0],
Type: reflect.TypeOf("ygot.GoStruct"),
}
}
newstate, ok := payload[1].(ygot.GoStruct)
if !ok {
return &ErrInvalidTypeAssertion{
v: payload[1],
t: reflect.TypeOf("ygot.GoStruct"),
return &errors.ErrInvalidTypeAssertion{
Value: payload[1],
Type: reflect.TypeOf("ygot.GoStruct"),
}
}
......@@ -192,12 +179,12 @@ func (g *Gnmi) applyDiff(ctx context.Context, payload ...interface{}) error {
req := &gpb.SetRequest{}
if diff.Update != nil {
switch op {
case TransportUpdate:
case types.TransportUpdate:
req.Update = diff.Update
case TransportReplace:
case types.TransportReplace:
req.Replace = diff.Update
default:
return &ErrOperationNotSupported{}
return &errors.ErrOperationNotSupported{}
}
} else if diff.Delete != nil {
req.Delete = diff.Delete
......@@ -210,7 +197,7 @@ func (g *Gnmi) applyDiff(ctx context.Context, payload ...interface{}) error {
//Subscribe subscribes to a gNMI target
func (g *Gnmi) Subscribe(ctx context.Context, params ...string) error {
if g.client == nil {
return &ErrNilClient{}
return &errors.ErrNilClient{}
}
return g.subscribe(ctx)
}
......@@ -254,7 +241,7 @@ func (g *Gnmi) Capabilities(ctx context.Context) (interface{}, error) {
"target": g.Options.Addr,
}).Info("sending gNMI capabilities request")
ctx = gnmi.NewContext(ctx, &g.Options.Config)
ctx = context.WithValue(ctx, CtxKeyConfig, &g.Options.Config) //nolint
ctx = context.WithValue(ctx, types.CtxKeyConfig, &g.Options.Config) //nolint
resp, err := g.client.Capabilities(ctx, &gpb.CapabilityRequest{})
if err != nil {
return nil, err
......@@ -266,7 +253,7 @@ func (g *Gnmi) Capabilities(ctx context.Context) (interface{}, error) {
func (g *Gnmi) get(ctx context.Context, paths [][]string, origin string) (interface{}, error) {
ctx = gnmi.NewContext(ctx, &g.Options.Config)
ctx = context.WithValue(ctx, CtxKeyConfig, &g.Options.Config) //nolint
ctx = context.WithValue(ctx, types.CtxKeyConfig, &g.Options.Config) //nolint
req, err := gnmi.NewGetRequest(ctx, paths, origin)
if err != nil {
return nil, err
......@@ -278,7 +265,7 @@ func (g *Gnmi) get(ctx context.Context, paths [][]string, origin string) (interf
// and returns any response.
func (g *Gnmi) getWithRequest(ctx context.Context, req *gpb.GetRequest) (interface{}, error) {
if req == nil {
return nil, &ErrNil{}
return nil, &errors.ErrNil{}
}
log.WithFields(log.Fields{
"target": g.Options.Addr,
......@@ -315,11 +302,11 @@ func (g *Gnmi) set(ctx context.Context, setOps []*gnmi.Operation,
// Subscribe calls GNMI subscribe
func (g *Gnmi) subscribe(ctx context.Context) error {
ctx = gnmi.NewContext(ctx, &g.Options.Config)
opts, ok := ctx.Value(CtxKeyOpts).(*gnmi.SubscribeOptions)
opts, ok := ctx.Value(types.CtxKeyOpts).(*gnmi.SubscribeOptions)
if !ok {
return &ErrInvalidTypeAssertion{
v: reflect.TypeOf(ctx.Value(CtxKeyOpts)),
t: reflect.TypeOf(&gnmi.SubscribeOptions{}),
return &errors.ErrInvalidTypeAssertion{
Value: reflect.TypeOf(ctx.Value(types.CtxKeyOpts)),
Type: reflect.TypeOf(&gnmi.SubscribeOptions{}),
}
}
go func() {
......
......@@ -16,8 +16,6 @@ import (
pb "google.golang.org/protobuf/proto"
)
const apiEndpoint = "http://localhost:8080"
// UUIDs for test cases
var did uuid.UUID
var mdid uuid.UUID
......@@ -28,16 +26,11 @@ var iid uuid.UUID
var altIid uuid.UUID
var cuid uuid.UUID
var sbi SouthboundInterface
var httpTestPND PrincipalNetworkDomain
var gnmiMessages map[string]pb.Message
var gnmiConfig *gnmi.Config
var httpTestDevice Device
var startGnmiTarget chan string
var stopGnmiTarget chan bool
var args string
var argsNotFound string
var mockContext = mock.MatchedBy(func(ctx context.Context) bool { return true })
......@@ -72,7 +65,6 @@ func TestMain(m *testing.M) {
readTestUUIDs()
testSetupGnmi()
testSetupHTTP()
os.Exit(m.Run())
}
......@@ -136,11 +128,11 @@ func newPnd() pndImplementation {
return pndImplementation{
name: "default",
description: "default test pnd",
sbic: sbiStore{store{}},
devices: deviceStore{store{}},
pendingChanges: changeStore{store{}},
committedChanges: changeStore{store{}},
confirmedChanges: changeStore{store{}},
sbic: SbiStore{store{}},
devices: DeviceStore{store{}},
pendingChanges: ChangeStore{store{}},
committedChanges: ChangeStore{store{}},
confirmedChanges: ChangeStore{store{}},
id: defaultPndID,
errChans: make(map[uuid.UUID]chan error),
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment