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/cli/http.go b/cli/http.go index 2554574f2657fdd9dd845f1d77cdd7546a202300..1dc35dd178006d65a8813a8e92bfb95d0d216f79 100644 --- a/cli/http.go +++ b/cli/http.go @@ -3,6 +3,7 @@ package cli import ( "fmt" log "github.com/sirupsen/logrus" + "github.com/spf13/viper" "io/ioutil" "net/http" "strings" @@ -33,7 +34,15 @@ func HttpGet(apiEndpoint, f string, args ...string) error { if err != nil { return err } - fmt.Println(string(bytes)) + if f == "init" { + pnd := string(bytes[:36]) + sbi := string(bytes[36:]) + viper.Set("CLI_PND", pnd) + viper.Set("CLI_SBI", sbi) + return viper.WriteConfig() + } else { + fmt.Println(string(bytes)) + } case http.StatusCreated: defer resp.Body.Close() bytes, err := ioutil.ReadAll(resp.Body) diff --git a/cmd/addDevice.go b/cmd/addDevice.go index 59260a3c6e0b206183652b864ae94665efa8733a..66b68e55bc4b103ff1e823fc11fe305a0f725f84 100644 --- a/cmd/addDevice.go +++ b/cmd/addDevice.go @@ -48,6 +48,8 @@ var addDeviceCmd = &cobra.Command{ "address="+address, "password="+password, "username="+username, + "sbi="+cliSbi, + "pnd="+cliPnd, ) }, } diff --git a/cmd/cli.go b/cmd/cli.go index 86e2891e1d21e94b9918205deeab67c895a4111e..36ea21d806d15d8d375dad55a220f8fd1c102a16 100644 --- a/cmd/cli.go +++ b/cmd/cli.go @@ -32,7 +32,7 @@ POSSIBILITY OF SUCH DAMAGE. package cmd import ( - "errors" + "code.fbi.h-da.de/cocsn/gosdn/cli" "github.com/spf13/cobra" ) @@ -45,7 +45,7 @@ var cliCmd = &cobra.Command{ Short: "", Long: ``, RunE: func(cmd *cobra.Command, args []string) error { - return errors.New("no subcommand provided") + return cli.HttpGet(apiEndpoint, "init") }, } @@ -54,4 +54,5 @@ func init() { cliCmd.PersistentFlags().StringVar(&uuid, "uuid", "", "uuid of the requested device") cliCmd.PersistentFlags().StringVar(&apiEndpoint, "api-endpoint", "http://localhost:8080", "address of the target") + } diff --git a/cmd/cliSet.go b/cmd/cliSet.go index a06cf887eb45c7aa62cf37e9607f2ec501820040..b16b4b8f5f865a4b9d129df16060e204da16fa4e 100644 --- a/cmd/cliSet.go +++ b/cmd/cliSet.go @@ -46,6 +46,8 @@ var cliSetCmd = &cobra.Command{ apiEndpoint, "set", "uuid="+uuid, + "cliSbi="+cliSbi, + "cliPnd="+cliPnd, "path="+args[0], "address="+address, "value="+args[1], diff --git a/cmd/getDevice.go b/cmd/getDevice.go index 7cebe7e81f1e3e49d0ebbaed97d524c9edf059f9..1e36a0017be0d51917fddc557f5fb5641f60dc1d 100644 --- a/cmd/getDevice.go +++ b/cmd/getDevice.go @@ -42,7 +42,13 @@ var getDeviceCmd = &cobra.Command{ Short: "gets device information from the controller", Long: ``, RunE: func(cmd *cobra.Command, args []string) error { - return cli.HttpGet(apiEndpoint, "getDevice", "uuid="+uuid) + return cli.HttpGet( + apiEndpoint, + "getDevice", + "uuid="+uuid, + "sbi="+cliSbi, + "pnd="+cliPnd, + ) }, } diff --git a/cmd/init.go b/cmd/init.go new file mode 100644 index 0000000000000000000000000000000000000000..17fb0ca83c6fbc2ca0c8f2e3e0de0823edebc437 --- /dev/null +++ b/cmd/init.go @@ -0,0 +1,60 @@ +/* +Copyright © 2021 da/net research group <danet.fbi.h-da.de> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +package cmd + +import ( + "code.fbi.h-da.de/cocsn/gosdn/cli" + "github.com/spf13/cobra" +) + +// initCmd represents the init command +var initCmd = &cobra.Command{ + Use: "init", + Short: "initialise SBI and PND", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return cli.HttpGet(apiEndpoint, "init" ) + }, +} + +func init() { + cliCmd.AddCommand(initCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // initCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // initCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/request.go b/cmd/request.go index 9aaa75f6dc5a087d72a5765c80a1230367b3565c..a0c81e8a2a158bdd85934806e425e2b33da8fb21 100644 --- a/cmd/request.go +++ b/cmd/request.go @@ -42,7 +42,14 @@ var requestCmd = &cobra.Command{ Short: "requests a path from a specified device on the controller", Long: ``, RunE: func(cmd *cobra.Command, args []string) error { - return cli.HttpGet(apiEndpoint, "request", "uuid="+uuid, "path="+args[0]) + return cli.HttpGet( + apiEndpoint, + "request", + "uuid="+uuid, + "sbi="+cliSbi, + "pnd="+cliPnd, + "path="+args[0], + ) }, } diff --git a/cmd/requestAll.go b/cmd/requestAll.go index 54ee211d911ce138491fa90e9273fa2b86735c03..76aabb320f59b0ababc2623b22ce3efa43a4f381 100644 --- a/cmd/requestAll.go +++ b/cmd/requestAll.go @@ -42,7 +42,13 @@ var requestAllCmd = &cobra.Command{ Short: "requests specified path from all devices on the controller", Long: ``, RunE: func(cmd *cobra.Command, args []string) error { - return cli.HttpGet(apiEndpoint, "requestAll", "path="+args[0]) + return cli.HttpGet( + apiEndpoint, + "requestAll", + "sbi="+cliSbi, + "pnd="+cliPnd, + "path="+args[0], + ) }, } diff --git a/cmd/root.go b/cmd/root.go index ca20379fa3ca69fb47da76553dd7e076567ad4bb..ca92d95ce6b98af7d52ac3e7520b7f144ca1459f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -47,12 +47,14 @@ var password string var address string var loglevel string var grpcPort string +var cliPnd string +var cliSbi string // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "gosdn", Short: "starts the gosdn controller", - Long: `Set GOSDN_DEBUG environment variable to enalbe debug logging.`, + Long: `Set GOSDN_DEBUG environment variable to enable debug logging.`, RunE: func(cmd *cobra.Command, args []string) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -105,6 +107,8 @@ func initConfig() { } viper.SetDefault("socket", ":"+grpcPort) + cliPnd = viper.GetString("CLI_PND") + cliSbi = viper.GetString("CLI_SBI") loglevel = viper.GetString("GOSDN_LOG") log.SetReportCaller(true) diff --git a/nucleus/controller.go b/nucleus/controller.go index df9752387ef13a4dd73ec7439b1e879ed64b4f77..9532f3ffbda4fac73445f7d02153c6bfcd56f640 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,14 +48,28 @@ 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 @@ -63,7 +77,7 @@ func createSouthboundInterfaces() error { // Run calls initialize to start the controller 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..7693bcabd845652c9f0cb81cae78a0dbd1edfae6 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,154 @@ 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) + http.HandleFunc("/livez", healthCheck) + http.HandleFunc("/readyz", healthCheck) + + go func() { + err = http.ListenAndServe(":8080", nil) + if err != nil { + return + } + }() + return nil +} + +func healthCheck(writer http.ResponseWriter, request *http.Request) { + writer.WriteHeader(http.StatusOK) +} + +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") + sid, err := uuid.Parse(query.Get("sbi")) + if err != nil { + log.Error(err) + } - query, err := url.ParseQuery(request.URL.RawQuery) + var pnd PrincipalNetworkDomain + var sbi SouthboundInterface + if query.Get("q") != "init" && query.Get("q") != "getIDs" { + pnd, err = c.pndc.get(pid) if err != nil { log.Error(err) - return } + sbic := pnd.GetSBIs() + sbi, err = sbic.(*sbiStore).get(sid) + } - id, err := uuid.Parse(query.Get("uuid")) + 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 } - - 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.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) } - 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 + 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) } - 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 + 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) } - writer.Header().Set("Content-Type", "application/json") - fmt.Fprintf(writer, "%v", device) - case "getIDs": - ids := pnd.(*pndImplementation).devices.UUIDs() + log.Error(err) + return + } + writer.Header().Set("Content-Type", "application/json") + fmt.Fprintf(writer, "%v", device) + case "getIDs": + writeIDs := func(typ string, ids []uuid.UUID) { + fmt.Fprintf(writer, "%v:\n", typ) 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 + } + writeIDs("PNDs", c.pndc.UUIDs()) + writeIDs("SBIs", c.sbic.UUIDs()) + if pnd != nil { + writeIDs("Devices", pnd.(*pndImplementation).devices.UUIDs()) + } + case "init": + writeIDs := func(typ string, ids []uuid.UUID) { + for _, id := range ids { + fmt.Fprintf(writer, "%v", id) } - writer.WriteHeader(http.StatusOK) - fmt.Fprintln(writer, resp) - default: - writer.WriteHeader(http.StatusBadRequest) } - } - http.HandleFunc(basePath, httpHandler) - - go func() { - err = http.ListenAndServe(":8080", nil) + writeIDs("PNDs", c.pndc.UUIDs()) + writeIDs("SBIs", c.sbic.UUIDs()) + 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) + } + }) + } +}