diff --git a/cli/cli_test.go b/cli/cli_test.go new file mode 100644 index 0000000000000000000000000000000000000000..df6c6ec4d09668c3cf7996599a03bf129441ccf4 --- /dev/null +++ b/cli/cli_test.go @@ -0,0 +1,259 @@ +package cli + +import ( + "code.fbi.h-da.de/cocsn/gosdn/forks/google/gnmi" + "context" + gpb "github.com/openconfig/gnmi/proto/gnmi" + "github.com/openconfig/ygot/ygot" + "reflect" + "testing" +) + +func TestCapabilities(t *testing.T) { + type args struct { + a string + u string + p string + } + tests := []struct { + name string + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Capabilities(tt.args.a, tt.args.u, tt.args.p); (err != nil) != tt.wantErr { + t.Errorf("Capabilities() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestGet(t *testing.T) { + type args struct { + a string + u string + p string + args []string + } + tests := []struct { + name string + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Get(tt.args.a, tt.args.u, tt.args.p, tt.args.args...); (err != nil) != tt.wantErr { + t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestHttpGet(t *testing.T) { + type args struct { + apiEndpoint string + f string + args []string + } + tests := []struct { + name string + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := HttpGet(tt.args.apiEndpoint, tt.args.f, tt.args.args...); (err != nil) != tt.wantErr { + t.Errorf("HttpGet() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestLeafPaths(t *testing.T) { + tests := []struct { + name string + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := LeafPaths(); (err != nil) != tt.wantErr { + t.Errorf("LeafPaths() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestPathTraversal(t *testing.T) { + tests := []struct { + name string + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := PathTraversal(); (err != nil) != tt.wantErr { + t.Errorf("PathTraversal() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestSet(t *testing.T) { + type args struct { + a string + u string + p string + typ string + args []string + } + tests := []struct { + name string + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Set(tt.args.a, tt.args.u, tt.args.p, tt.args.typ, tt.args.args...); (err != nil) != tt.wantErr { + t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestSubscribe(t *testing.T) { + type args struct { + a string + u string + p string + sample int64 + heartbeat int64 + args []string + } + tests := []struct { + name string + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Subscribe(tt.args.a, tt.args.u, tt.args.p, tt.args.sample, tt.args.heartbeat, tt.args.args...); (err != nil) != tt.wantErr { + t.Errorf("Subscribe() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestTarget(t *testing.T) { + type args struct { + bindAddr string + } + tests := []struct { + name string + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Target(tt.args.bindAddr); (err != nil) != tt.wantErr { + t.Errorf("Target() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_callback(t *testing.T) { + type args struct { + newConfig ygot.ValidatedGoStruct + } + tests := []struct { + name string + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := callback(tt.args.newConfig); (err != nil) != tt.wantErr { + t.Errorf("callback() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_newServer(t *testing.T) { + type args struct { + model *gnmi.Model + config []byte + } + tests := []struct { + name string + args args + want *server + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := newServer(tt.args.model, tt.args.config) + if (err != nil) != tt.wantErr { + t.Errorf("newServer() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("newServer() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_server_Get(t *testing.T) { + type fields struct { + Server *gnmi.Server + } + type args struct { + ctx context.Context + req *gpb.GetRequest + } + tests := []struct { + name string + fields fields + args args + want *gpb.GetResponse + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &server{ + Server: tt.fields.Server, + } + got, err := s.Get(tt.args.ctx, tt.args.req) + if (err != nil) != tt.wantErr { + t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Get() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/nucleus/controller.go b/nucleus/controller.go index e239bca0d3f6e3cc57d948ea8fffb3e10575ec4f..d11781461112cf87a6da752f87d09076099f22b2 100644 --- a/nucleus/controller.go +++ b/nucleus/controller.go @@ -20,7 +20,7 @@ type Core struct { var c *Core //Initialize does start-up housekeeping like reading controller config files -func initialize(ctx context.Context) error { +func initialize() error { c = &Core{ database: database.Database{}, pndc: pndStore{}, @@ -36,9 +36,9 @@ func initialize(ctx context.Context) error { if err := createSouthboundInterfaces(); err != nil { return err } - // TODO: Start grpc listener here - if err := httpApi(ctx); err != nil { + // TODO: Start grpc listener here + if err := httpApi(); err != nil { return err } @@ -48,21 +48,35 @@ func initialize(ctx context.Context) error { } // deprecated -// AttachDatabase connects to the database and passes the connection to the controller core +// attachDatabase connects to the database and passes the connection to the controller core func attachDatabase() { c.database = database.NewDatabaseClient() } -// CreateSouthboundInterfaces initializes the controller with its supported SBIs +// createSouthboundInterfaces initializes the controller with its supported SBIs func createSouthboundInterfaces() error { - if err := c.sbic.add(&OpenConfig{id: uuid.New()}); err != nil { + sbi := &OpenConfig{id: uuid.New()} + 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) + if err != nil { + return err + } + err = c.pndc.add(pnd) + if err != nil { return err } return nil } func Run(ctx context.Context) error { - if err := initialize(ctx); err != nil { + if err := initialize(); err != nil { log.WithFields(log.Fields{}).Error(err) return err } diff --git a/nucleus/errors_test.go b/nucleus/errors_test.go new file mode 100644 index 0000000000000000000000000000000000000000..09d172db926b14465fb4e16f641017a4f6d430ae --- /dev/null +++ b/nucleus/errors_test.go @@ -0,0 +1,196 @@ +package nucleus + +import "testing" + +func TestErrAlreadyExists_Error(t *testing.T) { + type fields struct { + item interface{} + } + tests := []struct { + name string + fields fields + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &ErrAlreadyExists{ + item: tt.fields.item, + } + if got := e.Error(); got != tt.want { + t.Errorf("Error() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestErrInvalidParameters_Error(t *testing.T) { + type fields struct { + f interface{} + r interface{} + } + tests := []struct { + name string + fields fields + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := ErrInvalidParameters{ + f: tt.fields.f, + r: tt.fields.r, + } + if got := e.Error(); got != tt.want { + t.Errorf("Error() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestErrInvalidTransportOptions_Error(t *testing.T) { + type fields struct { + t interface{} + } + tests := []struct { + name string + fields fields + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := ErrInvalidTransportOptions{ + t: tt.fields.t, + } + if got := e.Error(); got != tt.want { + t.Errorf("Error() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestErrInvalidTypeAssertion_Error(t *testing.T) { + type fields struct { + v interface{} + t interface{} + } + tests := []struct { + name string + fields fields + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := ErrInvalidTypeAssertion{ + v: tt.fields.v, + t: tt.fields.t, + } + if got := e.Error(); got != tt.want { + t.Errorf("Error() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestErrNilClient_Error(t *testing.T) { + tests := []struct { + name string + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &ErrNilClient{} + if got := e.Error(); got != tt.want { + t.Errorf("Error() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestErrNil_Error(t *testing.T) { + tests := []struct { + name string + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &ErrNil{} + if got := e.Error(); got != tt.want { + t.Errorf("Error() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestErrNotFound_Error(t *testing.T) { + type fields struct { + id interface{} + } + tests := []struct { + name string + fields fields + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &ErrNotFound{ + id: tt.fields.id, + } + if got := e.Error(); got != tt.want { + t.Errorf("Error() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestErrNotYetImplemented_Error(t *testing.T) { + tests := []struct { + name string + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := ErrNotYetImplemented{} + if got := e.Error(); got != tt.want { + t.Errorf("Error() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestErrUnsupportedPath_Error(t *testing.T) { + type fields struct { + p interface{} + } + tests := []struct { + name string + fields fields + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := ErrUnsupportedPath{ + p: tt.fields.p, + } + if got := e.Error(); got != tt.want { + t.Errorf("Error() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/nucleus/http.go b/nucleus/http.go index 5d6e17763a453df3296dd274da715e8ffac5203e..3c2980c9a031b785bed60b3a23eab13544547eb2 100644 --- a/nucleus/http.go +++ b/nucleus/http.go @@ -2,7 +2,6 @@ package nucleus import ( "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" - "context" "fmt" "github.com/google/uuid" gpb "github.com/openconfig/gnmi/proto/gnmi" @@ -13,124 +12,121 @@ import ( const basePath = "/api" -func httpApi(ctx context.Context) (err error) { - id := c.sbic.UUIDs()[0] - sbi, err := c.sbic.get(id) +// deprecated +func httpApi() (err error) { + http.HandleFunc(basePath, httpHandler) + + go func() { + err = http.ListenAndServe(":8080", nil) + if err != nil { + return + } + }() + return nil +} + +func httpHandler(writer http.ResponseWriter, request *http.Request) { + log.WithFields(log.Fields{ + "request": request, + }).Debug("incoming request") + + query, err := url.ParseQuery(request.URL.RawQuery) if err != nil { + log.Error(err) return } - pnd, err := NewPND("http", "http base pnd", uuid.New(), sbi) + + id, err := uuid.Parse(query.Get("uuid")) if err != nil { - return + log.Error(err) } - err = c.pndc.add(pnd) + + pid, err := uuid.Parse(query.Get("pnd")) if err != nil { - return + log.Error(err) } - httpHandler := func(writer http.ResponseWriter, request *http.Request) { - log.WithFields(log.Fields{ - "request": request, - }).Debug("incoming request") + pnd, err := c.pndc.get(pid) + + sbi := pnd.GetSBIs() - query, err := url.ParseQuery(request.URL.RawQuery) + switch query.Get("q") { + case "addDevice": + d, err := NewDevice(sbi, &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(), + RespChan: make(chan *gpb.SubscribeResponse), + }) + err = pnd.AddDevice(d) if err != nil { + writer.WriteHeader(http.StatusBadRequest) log.Error(err) return } - - id, err := uuid.Parse(query.Get("uuid")) + writer.WriteHeader(http.StatusCreated) + fmt.Fprintf(writer, "device added\n") + fmt.Fprintf(writer, "UUID: %v\n", d.Uuid) + case "request": + err = pnd.Request(id, query.Get("path")) if err != nil { + switch err.(type) { + case *ErrNotFound: + writer.WriteHeader(http.StatusNotFound) + default: + writer.WriteHeader(http.StatusInternalServerError) + } log.Error(err) + return } - - switch query.Get("q") { - case "addDevice": - d, err := NewDevice(sbi, &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(), - RespChan: make(chan *gpb.SubscribeResponse), - }) - err = pnd.AddDevice(d) - if err != nil { - writer.WriteHeader(http.StatusBadRequest) - log.Error(err) - return - } - writer.WriteHeader(http.StatusCreated) - fmt.Fprintf(writer, "device added\n") - fmt.Fprintf(writer, "UUID: %v\n", d.Uuid) - case "request": - err = pnd.Request(id, query.Get("path")) - if err != nil { - switch err.(type) { - case *ErrNotFound: - writer.WriteHeader(http.StatusNotFound) - default: - writer.WriteHeader(http.StatusInternalServerError) - } - log.Error(err) - return - } - writer.WriteHeader(http.StatusOK) - case "requestAll": - err = pnd.RequestAll(query.Get("path")) - if err != nil { - switch err.(type) { - case *ErrNotFound: - writer.WriteHeader(http.StatusNotFound) - default: - writer.WriteHeader(http.StatusInternalServerError) - } - log.Error(err) - return - } - writer.WriteHeader(http.StatusOK) - case "getDevice": - device, err := pnd.MarshalDevice(id) - if err != nil { - switch err.(type) { - case *ErrNotFound: - writer.WriteHeader(http.StatusNotFound) - default: - writer.WriteHeader(http.StatusInternalServerError) - } - log.Error(err) - return - } - writer.Header().Set("Content-Type", "application/json") - fmt.Fprintf(writer, "%v", device) - case "getIDs": - ids := pnd.(*pndImplementation).devices.UUIDs() - for i, id := range ids { - fmt.Fprintf(writer, "%v: %v\n", i+1, id) + writer.WriteHeader(http.StatusOK) + case "requestAll": + err = pnd.RequestAll(query.Get("path")) + if err != nil { + switch err.(type) { + case *ErrNotFound: + writer.WriteHeader(http.StatusNotFound) + default: + writer.WriteHeader(http.StatusInternalServerError) } - case "set": - resp, err := pnd.(*pndImplementation).Set(id, query.Get("path"), query.Get("value")) - if err != nil { + log.Error(err) + return + } + writer.WriteHeader(http.StatusOK) + case "getDevice": + device, err := pnd.MarshalDevice(id) + if err != nil { + switch err.(type) { + case *ErrNotFound: + writer.WriteHeader(http.StatusNotFound) + default: writer.WriteHeader(http.StatusInternalServerError) - log.Error(err) - return } - writer.WriteHeader(http.StatusOK) - fmt.Fprintln(writer, resp) - default: - writer.WriteHeader(http.StatusBadRequest) + log.Error(err) + return } - } - http.HandleFunc(basePath, httpHandler) - - go func() { - err = http.ListenAndServe(":8080", nil) + writer.Header().Set("Content-Type", "application/json") + fmt.Fprintf(writer, "%v", device) + case "getIDs": + ids := pnd.(*pndImplementation).devices.UUIDs() + for i, id := range ids { + fmt.Fprintf(writer, "%v: %v\n", i+1, id) + } + case "set": + resp, err := pnd.(*pndImplementation).Set(id, query.Get("path"), query.Get("value")) if err != nil { + writer.WriteHeader(http.StatusInternalServerError) + log.Error(err) return } - }() - return nil -} + writer.WriteHeader(http.StatusOK) + fmt.Fprintln(writer, resp) + default: + writer.WriteHeader(http.StatusBadRequest) + } +} \ No newline at end of file diff --git a/nucleus/http_test.go b/nucleus/http_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9221ee253c7b7d4890e472d98dcf9122f59d164c --- /dev/null +++ b/nucleus/http_test.go @@ -0,0 +1,26 @@ +package nucleus + +import ( + "context" + "testing" +) + +func Test_httpApi(t *testing.T) { + type args struct { + ctx context.Context + } + tests := []struct { + name string + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := httpApi(tt.args.ctx); (err != nil) != tt.wantErr { + t.Errorf("httpApi() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +}