From c754102aac6de260e2b362b9204caa61dbfe9610 Mon Sep 17 00:00:00 2001 From: Manuel Kieweg <manuel.kieweg@h-da.de> Date: Tue, 25 May 2021 17:58:57 +0200 Subject: [PATCH] basic tests for grpc client --- cli/grpc.go | 133 +++- cli/grpc_test.go | 807 ++++++++++++++++++++++++ cli/http.go | 73 --- cmd/addPnd.go | 63 ++ cmd/cliSet.go | 13 +- cmd/commit.go | 7 +- cmd/confirm.go | 7 +- cmd/getIds.go | 16 +- cmd/getPnd.go | 71 +++ cmd/list.go | 6 +- cmd/pnd.go | 66 ++ northbound/server/pnd.go | 58 +- test/integration/cliIntegration_test.go | 104 --- test/integration/cmdIntegration_test.go | 74 +-- 14 files changed, 1234 insertions(+), 264 deletions(-) create mode 100644 cli/grpc_test.go delete mode 100644 cli/http.go create mode 100644 cmd/addPnd.go create mode 100644 cmd/getPnd.go create mode 100644 cmd/pnd.go diff --git a/cli/grpc.go b/cli/grpc.go index 098f09661..c91dd17dc 100644 --- a/cli/grpc.go +++ b/cli/grpc.go @@ -34,19 +34,13 @@ func Init(addr string) error { return nil } -func GetIds(addr string) error { +func GetIds(addr string) ([]*ppb.PrincipalNetworkDomain,error) { ctx := context.Background() resp, err := getAllCore(ctx, addr) if err != nil { - return err - } - for i, pnd := range resp.Pnd { - log.Infof("PND %v: %v", i+1, pnd.Id) - for j, sbi := range pnd.Sbi { - log.Infof("\tSBI %v: %v", j+1, sbi.Id) - } + return nil, err } - return nil + return resp.Pnd, nil } func getAllCore(ctx context.Context, addr string) (*pb.GetResponse, error) { @@ -61,6 +55,99 @@ func getAllCore(ctx context.Context, addr string) (*pb.GetResponse, error) { return coreClient.Get(ctx, req) } +func AddPnd(addr, name, description, sbi string) error { + coreClient, err := nbi.CoreClient(addr, grpcWithInsecure) + if err != nil { + return err + } + ctx := context.Background() + req := &pb.SetRequest{ + Timestamp: time.Now().UnixNano(), + Pnd: []*pb.SetPnd{ + { + Name: name, + Description: description, + Sbi: sbi, + }, + }, + } + + resp, err := coreClient.Set(ctx, req) + if err != nil { + return err + } + log.Info(resp.Status.String()) + return nil +} + +func GetPnd(addr string, args ...string) (*pb.GetResponse, error) { + coreClient, err := nbi.CoreClient(addr, grpcWithInsecure) + if err != nil { + return nil,err + } + ctx := context.Background() + req := &pb.GetRequest{ + Timestamp: time.Now().UnixNano(), + All: false, + Pid: args, + } + return coreClient.Get(ctx, req) +} + +func GetChanges(addr, pnd string) (*ppb.GetResponse, error) { + ctx := context.Background() + client, err := nbi.PndClient(addr, grpcWithInsecure) + if err != nil { + return nil, err + } + req := &ppb.GetRequest{ + Timestamp: time.Now().UnixNano(), + Request: &ppb.GetRequest_Change{ + Change: &ppb.GetChange{ + All: true, + }, + }, + Pid: pnd, + } + return client.Get(ctx, req) +} + +func Commit(addr, pnd string, cuids ...string) (*ppb.SetResponse,error) { + changes := make([]*ppb.SetChange, len(cuids)) + for i, arg := range cuids { + changes[i] = &ppb.SetChange{ + Cuid: arg, + Op: ppb.SetChange_COMMIT, + } + } + return commitConfirm(addr, pnd, changes) +} + +func Confirm(addr, pnd string, cuids ...string) (*ppb.SetResponse,error) { + changes := make([]*ppb.SetChange, len(cuids)) + for i, arg := range cuids { + changes[i] = &ppb.SetChange{ + Cuid: arg, + Op: ppb.SetChange_CONFIRM, + } + } + return commitConfirm(addr, pnd, changes) +} + +func commitConfirm(addr, pnd string, changes []*ppb.SetChange) (*ppb.SetResponse,error) { + ctx := context.Background() + client, err := nbi.PndClient(addr, grpcWithInsecure) + if err != nil { + return nil, err + } + req := &ppb.SetRequest{ + Timestamp: time.Now().UnixNano(), + Change: changes, + Pid: pnd, + } + return client.Set(ctx, req) +} + func AddDevice(addr, username, password, sbi, pnd, deviceAddress string) error { pndClient, err := nbi.PndClient(addr, grpcWithInsecure) if err != nil { @@ -90,10 +177,10 @@ func AddDevice(addr, username, password, sbi, pnd, deviceAddress string) error { return nil } -func GetDevice(addr, pid, path string, did ...string) error { +func GetDevice(addr, pid, path string, did ...string) (*ppb.GetResponse,error) { pndClient, err := nbi.PndClient(addr, grpcWithInsecure) if err != nil { - return err + return nil, err } var all bool @@ -112,15 +199,10 @@ func GetDevice(addr, pid, path string, did ...string) error { Pid: pid, } ctx := context.Background() - resp, err := pndClient.Get(ctx, req) - if err != nil { - return err - } - log.Info(resp.String()) - return nil + return pndClient.Get(ctx, req) } -func Update(addr, did, pid, path, value string) error { +func Update(addr, did, pid, path, value string) (*ppb.SetResponse, error) { req := &ppb.ChangeRequest{ Id: did, Path: path, @@ -130,7 +212,7 @@ func Update(addr, did, pid, path, value string) error { return sendChangeRequest(addr, pid, req) } -func Replace(addr, did, pid, path, value string) error { +func Replace(addr, did, pid, path, value string) (*ppb.SetResponse, error) { req := &ppb.ChangeRequest{ Id: did, Path: path, @@ -140,7 +222,7 @@ func Replace(addr, did, pid, path, value string) error { return sendChangeRequest(addr, pid, req) } -func Delete(addr, did, pid, path string) error { +func Delete(addr, did, pid, path string) (*ppb.SetResponse, error) { req := &ppb.ChangeRequest{ Id: did, Path: path, @@ -149,10 +231,10 @@ func Delete(addr, did, pid, path string) error { return sendChangeRequest(addr, pid, req) } -func sendChangeRequest(addr, pid string, req *ppb.ChangeRequest) error { +func sendChangeRequest(addr, pid string, req *ppb.ChangeRequest) (*ppb.SetResponse, error) { pndClient, err := nbi.PndClient(addr, grpcWithInsecure) if err != nil { - return err + return nil, err } ctx := context.Background() r := &ppb.SetRequest{ @@ -160,10 +242,5 @@ func sendChangeRequest(addr, pid string, req *ppb.ChangeRequest) error { ChangeRequest: []*ppb.ChangeRequest{req}, Pid: pid, } - resp, err := pndClient.Set(ctx, r) - if err != nil { - return err + return pndClient.Set(ctx, r) } - log.Info(resp.String()) - return nil -} diff --git a/cli/grpc_test.go b/cli/grpc_test.go new file mode 100644 index 000000000..9211ed97b --- /dev/null +++ b/cli/grpc_test.go @@ -0,0 +1,807 @@ +package cli + +import ( + pb "code.fbi.h-da.de/cocsn/api/go/gosdn/core" + ppb "code.fbi.h-da.de/cocsn/api/go/gosdn/pnd" + nbi "code.fbi.h-da.de/cocsn/gosdn/northbound/server" + "code.fbi.h-da.de/cocsn/gosdn/nucleus" + "context" + log "github.com/sirupsen/logrus" + "google.golang.org/grpc" + "k8s.io/apimachinery/pkg/util/rand" + "net" + "reflect" + "strconv" + "testing" + "time" +) + +const unreachable = "203.0.113.10:6030" +const testPath = "/system/config/hostname" +const testUser = "admin" +const testPassword = "admin" +const testPid = "06aa4994-80f7-4751-99a4-6826b72d0057" +const testCuid = "cf628f4b-393d-44a5-affb-949dfe5c3c27" +const testDid = "1c37ff20-0169-44e0-bf37-b64787a979d5" + +func startGrpcServer() (*grpc.Server, string, error) { + port := rand.IntnRange(1025, 65536) + sock := "localhost:" + strconv.Itoa(port) + pndc := nucleus.NewPndStore() + lis, err := net.Listen("tcp", sock) + if err != nil { + return nil, "", err + } + grpcServer := grpc.NewServer() + northbound := nbi.NewNBI(pndc) + pb.RegisterCoreServer(grpcServer, northbound.Core) + ppb.RegisterPndServer(grpcServer, northbound.Pnd) + go func() { + log.Fatal(grpcServer.Serve(lis)) + }() + return grpcServer, sock, nil +} + +func TestAddDevice(t *testing.T) { + type args struct { + username string + password string + sbi string + pnd string + deviceAddress string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "default", + args: args{ + username: testUser, + password: testPassword, + sbi: "openconfig", + pnd: testPid, + deviceAddress: "", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + if err := AddDevice(sock, tt.args.username, tt.args.password, tt.args.sbi, tt.args.pnd, tt.args.deviceAddress); (err != nil) != tt.wantErr { + t.Errorf("AddDevice() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestAddPnd(t *testing.T) { + type args struct { + name string + description string + sbi string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "default", + args: args{ + name: "test", + description: "test", + sbi: "openconfig", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + if err := AddPnd(sock, tt.args.name, tt.args.description, tt.args.sbi); (err != nil) != tt.wantErr { + t.Errorf("AddPnd() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestCommit(t *testing.T) { + type args struct { + pnd string + cuids []string + } + tests := []struct { + name string + args args + want ppb.SetResponseStatus + wantErr bool + }{ + { + name: "default", + args: args{ + pnd: testPid, + cuids: []string{ + testCuid, + }, + }, + want: ppb.SetResponse_OK, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + + got, err := Commit(sock, tt.args.pnd, tt.args.cuids...) + if err != nil { + if !tt.wantErr { + t.Errorf("Commit() error = %v, wantErr %v", err, tt.wantErr) + } + return + } + if !reflect.DeepEqual(got.Status, tt.want) { + t.Errorf("Commit() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestConfirm(t *testing.T) { + type args struct { + pnd string + cuids []string + } + tests := []struct { + name string + args args + want ppb.SetResponseStatus + wantErr bool + }{ + { + name: "default", + args: args{ + pnd: testPid, + cuids: []string{ + testCuid, + }, + }, + want: ppb.SetResponse_OK, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + + got, err := Confirm(sock, tt.args.pnd, tt.args.cuids...) + if err != nil { + if !tt.wantErr { + t.Errorf("Confirm() error = %v, wantErr %v", err, tt.wantErr) + } + return + } + if !reflect.DeepEqual(got.Status, tt.want) { + t.Errorf("Confirm() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDelete(t *testing.T) { + type args struct { + did string + pid string + path string + } + tests := []struct { + name string + args args + want ppb.SetResponseStatus + wantErr bool + }{ + { + name: "default", + args: args{ + did: "", + pid: "", + path: "", + }, + want: ppb.SetResponse_OK, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + + got, err := Delete(sock, tt.args.did, tt.args.pid, tt.args.path) + if (err != nil) != tt.wantErr { + t.Errorf("Delete() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Delete() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetChanges(t *testing.T) { + type args struct { + pnd string + } + tests := []struct { + name string + args args + want *ppb.GetResponse + wantErr bool + }{ + { + name: "default", + args: args{ + pnd: testPid, + }, + want: &ppb.GetResponse{ + Timestamp: time.Now().UnixNano(), + Pnd: &ppb.PrincipalNetworkDomain{}, + Ond: nil, + Sbi: nil, + Change: nil, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + + got, err := GetChanges(sock, tt.args.pnd) + if (err != nil) != tt.wantErr { + t.Errorf("GetChanges() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetChanges() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetDevice(t *testing.T) { + type args struct { + pid string + path string + did []string + } + tests := []struct { + name string + args args + want *ppb.GetResponse + wantErr bool + }{ + { + name: "default", + args: args{ + pid: testPid, + path: testPath, + did: []string{testDid}, + }, + want: &ppb.GetResponse{ + Timestamp: time.Now().UnixNano(), + Ond: []*ppb.OrchestratedNetworkingDevice{ + { + Id: testDid, + Name: "testOnd", + Device: nil, + Sbi: nil, + }, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + + got, err := GetDevice(sock, tt.args.pid, tt.args.path, tt.args.did...) + if (err != nil) != tt.wantErr { + t.Errorf("GetDevice() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetDevice() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetIds(t *testing.T) { + tests := []struct { + name string + want []*ppb.PrincipalNetworkDomain + wantErr bool + }{ + { + name: "default", + want: []*ppb.PrincipalNetworkDomain{ + { + Id: testPid, + Name: "testPnd", + Description: "testPnd", + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + + got, err := GetIds(sock) + if (err != nil) != tt.wantErr { + t.Errorf("GetIds() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetIds() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetPnd(t *testing.T) { + type args struct { + args []string + } + tests := []struct { + name string + args args + want *pb.GetResponse + wantErr bool + }{ + { + name: "default", + args: args{args: []string{testPid}}, + want: &pb.GetResponse{ + Timestamp: time.Now().UnixNano(), + Pnd: []*ppb.PrincipalNetworkDomain{ + { + Id: testPid, + Name: "testPnd", + Description: "testPnd", + }, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + + got, err := GetPnd(sock, tt.args.args...) + if (err != nil) != tt.wantErr { + t.Errorf("GetPnd() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetPnd() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestInit(t *testing.T) { + tests := []struct { + name string + wantErr bool + }{ + { + name: "default", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + + if err := Init(sock); (err != nil) != tt.wantErr { + t.Errorf("Init() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestReplace(t *testing.T) { + type args struct { + did string + pid string + path string + value string + } + tests := []struct { + name string + args args + want *ppb.SetResponse + wantErr bool + }{ + { + name: "default", + args: args{ + did: testDid, + pid: testPid, + path: testPath, + value: "ceos3000", + }, + want: &ppb.SetResponse{ + Timestamp: time.Now().UnixNano(), + Status: ppb.SetResponse_OK, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + + got, err := Replace(sock, tt.args.did, tt.args.pid, tt.args.path, tt.args.value) + if (err != nil) != tt.wantErr { + t.Errorf("Replace() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Replace() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestUpdate(t *testing.T) { + type args struct { + did string + pid string + path string + value string + } + tests := []struct { + name string + args args + want *ppb.SetResponse + wantErr bool + }{ + { + name: "default", + args: args{ + did: testDid, + pid: testPid, + path: testPath, + value: "ceos3000", + }, + want: &ppb.SetResponse{ + Timestamp: time.Now().UnixNano(), + Status: ppb.SetResponse_OK, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + + got, err := Update(sock, tt.args.did, tt.args.pid, tt.args.path, tt.args.value) + if (err != nil) != tt.wantErr { + t.Errorf("Update() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Update() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_commitConfirm(t *testing.T) { + type args struct { + pnd string + changes []*ppb.SetChange + } + tests := []struct { + name string + args args + want *ppb.SetResponse + wantErr bool + }{ + { + name: "default", + args: args{ + pnd: testPid, + changes: []*ppb.SetChange{ + { + Cuid: testCuid, + Op: ppb.SetChange_COMMIT, + }, + }, + }, + want: &ppb.SetResponse{ + Timestamp: time.Now().UnixNano(), + Status: ppb.SetResponse_OK, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + + got, err := commitConfirm(sock, tt.args.pnd, tt.args.changes) + if (err != nil) != tt.wantErr { + t.Errorf("commitConfirm() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("commitConfirm() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_getAllCore(t *testing.T) { + tests := []struct { + name string + want *pb.GetResponse + wantErr bool + }{ + { + name: "default", + want: &pb.GetResponse{ + Timestamp: time.Now().UnixNano(), + Pnd: []*ppb.PrincipalNetworkDomain{ + { + Id: testPid, + Name: "testPnd", + Description: "testPnd", + }, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + + ctx := context.Background() + got, err := getAllCore(ctx, sock) + if (err != nil) != tt.wantErr { + t.Errorf("getAllCore() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("getAllCore() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_sendChangeRequest(t *testing.T) { + type args struct { + pid string + req *ppb.ChangeRequest + } + tests := []struct { + name string + args args + want *ppb.SetResponse + wantErr bool + }{ + { + name: "update", + args: args{ + pid: testPid, + req: &ppb.ChangeRequest{ + Id: testCuid, + Path: testPath, + Value: "ceos3000", + ApiOp: ppb.ChangeRequest_UPDATE, + }, + }, + want: nil, + wantErr: false, + }, + { + name: "replace", + args: args{ + pid: testPid, + req: &ppb.ChangeRequest{ + Id: testCuid, + Path: testPath, + Value: "ceos3000", + ApiOp: ppb.ChangeRequest_REPLACE, + }, + }, + want: nil, + wantErr: false, + }, + { + name: "delete", + args: args{ + pid: testPid, + req: &ppb.ChangeRequest{ + Id: testCuid, + Path: testPath, + ApiOp: ppb.ChangeRequest_DELETE, + }, + }, + want: nil, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server, sock, err := startGrpcServer() + if err != nil { + if !tt.wantErr { + t.Error("startGrpcServer() error = cannot start grpc server") + } + return + } + defer server.Stop() + if tt.name == "unreachable" { + sock = unreachable + } + + got, err := sendChangeRequest(sock, tt.args.pid, tt.args.req) + if (err != nil) != tt.wantErr { + t.Errorf("sendChangeRequest() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("sendChangeRequest() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cli/http.go b/cli/http.go deleted file mode 100644 index b7c3254b6..000000000 --- a/cli/http.go +++ /dev/null @@ -1,73 +0,0 @@ -package cli - -import ( - "errors" - "fmt" - "io/ioutil" - "net/http" - "strings" - - log "github.com/sirupsen/logrus" - "github.com/spf13/viper" -) - -const apiRoot = "?" - -var builder *strings.Builder - -func init() { - builder = &strings.Builder{} -} - -// HTTPGet sends sends requests from the CLI to the gosdn HTTP API and processes any response data -func HTTPGet(apiEndpoint, f string, args ...string) error { - for _, p := range args { - builder.WriteString("&") - builder.WriteString(p) - } - resp, err := http.Get(apiEndpoint + apiRoot + "q=" + f + builder.String()) - if err != nil { - log.Info(fmt.Sprintf("Err: %s", err)) - return err - } - builder.Reset() - switch resp.StatusCode { - case http.StatusOK: - defer resp.Body.Close() - bytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - switch f { - case "init": - pnd := string(bytes[9:45]) - sbi := string(bytes[55:91]) - viper.Set("CLI_PND", pnd) - viper.Set("CLI_SBI", sbi) - err := viper.WriteConfig() - if err != nil { - log.Error(err) - } - default: - fmt.Println(string(bytes)) - } - - case http.StatusCreated: - defer resp.Body.Close() - bytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - uuid := string(bytes[19:55]) - viper.Set("LAST_DEVICE_UUID", uuid) - fmt.Println(string(bytes)) - - case http.StatusAccepted: - default: - log.WithFields(log.Fields{ - "status code": resp.StatusCode, - }).Error("operation unsuccessful") - return errors.New(resp.Status) - } - return nil -} diff --git a/cmd/addPnd.go b/cmd/addPnd.go new file mode 100644 index 000000000..e4e59c253 --- /dev/null +++ b/cmd/addPnd.go @@ -0,0 +1,63 @@ +/* +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" +) + +// addCmd represents the add command +var addCmd = &cobra.Command{ + Use: "add", + Short: "adds a new principal network domain to the controller", + Long: `A principal network domain is a main networking entity. This can be a +campus building or site. + +A description must be passed as positional argument. A name and default SBI can be +passed using flags`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return cli.AddPnd(apiEndpoint, pndName, args[0], pndDefaultSbi) + }, +} + +var pndName string +var pndDefaultSbi string + +func init() { + pndCmd.AddCommand(addCmd) + + addCmd.Flags().StringVar(&pndName, "name", "", "the name of the pnd") + addCmd.Flags().StringVar(&pndDefaultSbi, "sbi", "openconfig", "the default SBI of the pnd") + +} diff --git a/cmd/cliSet.go b/cmd/cliSet.go index e3f3a0743..118701920 100644 --- a/cmd/cliSet.go +++ b/cmd/cliSet.go @@ -46,15 +46,12 @@ only one value supported for now. Use "set replace" or "set delete" respectively`, RunE: func(cmd *cobra.Command, args []string) error { - return cli.HTTPGet( + return cli.Update( apiEndpoint, - "update", - "uuid="+uuid, - "cliSbi="+cliSbi, - "cliPnd="+cliPnd, - "path="+args[0], - "address="+address, - "value="+args[1], + uuid, + cliPnd, + args[0], + args[1], ) }, } diff --git a/cmd/commit.go b/cmd/commit.go index a6ccb3b10..8ad11bcae 100644 --- a/cmd/commit.go +++ b/cmd/commit.go @@ -43,11 +43,10 @@ var commitCmd = &cobra.Command{ Short: "Commit the given change for the active PND", Long: ``, RunE: func(cmd *cobra.Command, args []string) error { - return cli.HTTPGet( + return cli.Commit( apiEndpoint, - "change-commit", - "pnd="+cliPnd, - "cuid="+args[0], + cliPnd, + args[0], ) }, } diff --git a/cmd/confirm.go b/cmd/confirm.go index 59cda7698..9e719aee9 100644 --- a/cmd/confirm.go +++ b/cmd/confirm.go @@ -43,11 +43,10 @@ var confirmCmd = &cobra.Command{ Short: "Confirms the given change for the active PND", Long: ``, RunE: func(cmd *cobra.Command, args []string) error { - return cli.HTTPGet( + return cli.Confirm( apiEndpoint, - "change-confirm", - "pnd="+cliPnd, - "cuid="+args[0], + cliPnd, + args[0], ) }, } diff --git a/cmd/getIds.go b/cmd/getIds.go index 4aeebef79..b302f872a 100644 --- a/cmd/getIds.go +++ b/cmd/getIds.go @@ -33,6 +33,7 @@ package cmd import ( "code.fbi.h-da.de/cocsn/gosdn/cli" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -42,7 +43,20 @@ var getIdsCmd = &cobra.Command{ Short: "gets device IDs from the controller", Long: `Gets device IDs from the controller and lists them.`, RunE: func(cmd *cobra.Command, args []string) error { - return cli.GetIds(apiEndpoint) + resp, err := cli.GetIds(apiEndpoint) + if err != nil { + return err + } + for i, pnd := range resp { + log.Infof("PND %v: %v\n\tuuid: %v", i+1, pnd.Name, pnd.Id) + for j, ond := range pnd.Ond { + log.Infof("\tSBI %v: %v\n\tuuid: %v", j+1, ond.Name, ond.Id) + } + for k, sbi := range pnd.Sbi { + log.Infof("\tSBI %v: %v", k+1, sbi.Id) + } + } + return nil }, } diff --git a/cmd/getPnd.go b/cmd/getPnd.go new file mode 100644 index 000000000..c929efc35 --- /dev/null +++ b/cmd/getPnd.go @@ -0,0 +1,71 @@ +/* +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" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +// getCmd represents the get command +var getCmd = &cobra.Command{ + Use: "get", + Short: "get one or multiple pnds by uuid or name and print them to stdout", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + resp, err := cli.GetPnd(apiEndpoint, args...) + if err != nil { + return err + } + for i, pnd := range resp { + log.Infof("PND %v: %v\n\tuuid: %v", i+1, pnd.Name, pnd.Id) + if verbose { + for j, ond := range pnd.Ond { + log.Infof("\tSBI %v: %v\n\tuuid: %v", j+1, ond.Name, ond.Id) + } + for k, sbi := range pnd.Sbi { + log.Infof("\tSBI %v: %v", k+1, sbi.Id) + } + } + } + return nil + }, +} + +var verbose bool + +func init() { + pndCmd.AddCommand(getCmd) + + pndCmd.Flags().BoolVar(&verbose, "verbose", false, "show ond and sbi info") +} diff --git a/cmd/list.go b/cmd/list.go index 198662268..c5163e7b9 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -42,11 +42,7 @@ var listCmd = &cobra.Command{ Short: "Lists all committed changes", Long: ``, RunE: func(cmd *cobra.Command, args []string) error { - return cli.HTTPGet( - apiEndpoint, - "change-list", - "pnd="+cliPnd, - ) + return cli.ListChanges(apiEndpoint, cliPnd) }, } diff --git a/cmd/pnd.go b/cmd/pnd.go new file mode 100644 index 000000000..4a3cdd32d --- /dev/null +++ b/cmd/pnd.go @@ -0,0 +1,66 @@ +/* +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 ( + "fmt" + + "github.com/spf13/cobra" +) + +// pndCmd represents the pnd command +var pndCmd = &cobra.Command{ + Use: "pnd", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("pnd called") + }, +} + +func init() { + cliCmd.AddCommand(pndCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // pndCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // pndCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/northbound/server/pnd.go b/northbound/server/pnd.go index e694a2c52..bcabcd07f 100644 --- a/northbound/server/pnd.go +++ b/northbound/server/pnd.go @@ -184,5 +184,61 @@ func fillChanges(pnd nucleus.PrincipalNetworkDomain) ([]*ppb.Change, error) { } func (p pnd) Set(ctx context.Context, request *ppb.SetRequest) (*ppb.SetResponse, error) { - panic("implement me") + pid, err := uuid.Parse(request.Pid) + if err != nil { + return nil, err + } + ondResp, err := handleSetOnd(pid, request.Ond) + if err != nil { + return nil, err + } + sbiResp, err := handleSetSbi(pid, request.Sbi) + if err != nil { + return nil, err + } + changeResp, err := handleSetChange(pid, request.Change) + if err != nil { + return nil, err + } + changeRequestResp, err := handleChangeRequest(pid, request.ChangeRequest) + if err != nil { + return nil, err + } + return &ppb.SetResponse{ + Timestamp: time.Now().UnixNano(), + Status: ppb.SetResponse_OK, + Responses: []*ppb.SetResponse{ + ondResp, + sbiResp, + changeResp, + changeRequestResp, + }, + }, nil +} + +func handleSetOnd(pid uuid.UUID, req []*ppb.SetOnd) (*ppb.SetResponse, error) { + pndLock.Lock() + defer pndLock.Unlock() + pnd, err := pndc.Get(pid) + if err != nil { + return nil, err + } + for _,r := range req { + d, err := nucleus.NewDevice() + if err != nil { + return nil, err + } + } +} + +func handleSetSbi(pid uuid.UUID, req []*ppb.SetSbi) (*ppb.SetResponse, error) { + +} + +func handleSetChange(pid uuid.UUID, change []*ppb.SetChange) (*ppb.SetResponse, error) { + +} + +func handleChangeRequest(pid uuid.UUID, req []*ppb.ChangeRequest) (*ppb.SetResponse, error) { + } diff --git a/test/integration/cliIntegration_test.go b/test/integration/cliIntegration_test.go index dac342c5d..77e6efbd3 100644 --- a/test/integration/cliIntegration_test.go +++ b/test/integration/cliIntegration_test.go @@ -48,51 +48,6 @@ func TestCapabilities(t *testing.T) { } } -func TestGet(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test") - } - type args struct { - a string - u string - p string - args []string - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - name: "default", - args: args{ - a: testAddress, - u: testUsername, - p: testPassword, - args: defaultPath, - }, - wantErr: false, - }, - { - name: "destination unreachable", - args: args{ - a: unreachable, - u: testUsername, - p: testPassword, - args: defaultPath, - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if _, err := cli.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) { if testing.Short() { t.Skip("skipping integration test") @@ -134,62 +89,3 @@ func TestHttpGet(t *testing.T) { }) } } - -func TestSet(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test") - } - type args struct { - a string - u string - p string - typ string - args []string - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - name: "default", - args: args{ - a: testAddress, - u: testUsername, - p: testPassword, - typ: "update", - args: []string{"/system/config/hostname", "ceos3000"}, - }, - wantErr: false, - }, - { - name: "destination unreachable", - args: args{ - a: unreachable, - u: testUsername, - p: testPassword, - typ: "update", - args: []string{"/system/config/hostname", "ceos3000"}, - }, - wantErr: true, - }, - { - name: "invalid path", - args: args{ - a: testAddress, - u: testUsername, - p: testPassword, - typ: "update", - args: []string{"invalid/path", "ceos3000"}, - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := cli.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) - } - }) - } -} diff --git a/test/integration/cmdIntegration_test.go b/test/integration/cmdIntegration_test.go index cfe01aad5..1a5d53802 100644 --- a/test/integration/cmdIntegration_test.go +++ b/test/integration/cmdIntegration_test.go @@ -16,12 +16,12 @@ import ( ) const unreachable = "203.0.113.10:6030" +const testPath = "/system/config/hostname" var testAddress = "141.100.70.171:6030" var testAPIEndpoint = "http://gosdn-latest.apps.ocp.fbi.h-da.de/api" var testUsername = "admin" var testPassword = "arista" -var defaultPath = []string{"/system/config/hostname"} var opt *nucleus.GnmiTransportOptions func TestMain(m *testing.M) { @@ -102,7 +102,7 @@ func TestCmdIntegration(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { defer viper.Reset() - if err := cli.HTTPGet(testAPIEndpoint, "init"); (err != nil) != tt.wantErr { + if err := cli.Init(testAPIEndpoint); (err != nil) != tt.wantErr { switch err.(type) { case viper.ConfigFileNotFoundError: default: @@ -113,65 +113,67 @@ func TestCmdIntegration(t *testing.T) { cliPnd := viper.GetString("CLI_PND") cliSbi := viper.GetString("CLI_SBI") - if err := cli.HTTPGet( + if err := cli.AddDevice( testAPIEndpoint, - "addDevice", - "address="+testAddress, - "password="+testPassword, - "username="+testUsername, - "sbi="+cliSbi, - "pnd="+cliPnd, + testUsername, + testPassword, + cliSbi, + cliPnd, + testAddress, ); (err != nil) != tt.wantErr { t.Errorf("gosdn cli add-device error = %v, wantErr %v", err, tt.wantErr) return } did := viper.GetString("LAST_DEVICE_UUID") - if err := cli.HTTPGet( + _, err := cli.GetDevice( testAPIEndpoint, - "request", - "uuid="+did, - "sbi="+cliSbi, - "pnd="+cliPnd, - "path=/system/config/hostname", - ); (err != nil) != tt.wantErr { + cliPnd, + testPath, + did, + ) + if (err != nil) != tt.wantErr { t.Errorf("gosdn cli request error = %v, wantErr %v", err, tt.wantErr) return } - if err := cli.HTTPGet( + _, err = cli.GetDevice( testAPIEndpoint, - "getDevice", - "address="+testAddress, - "uuid="+did, - "sbi="+cliSbi, - "pnd="+cliPnd, - ); (err != nil) != tt.wantErr { + cliPnd, + "", + did, + ) + if (err != nil) != tt.wantErr { t.Errorf("gosdn cli get-device error = %v, wantErr %v", err, tt.wantErr) return } hostname := guuid.New().String() - if err := cli.HTTPGet( + _, err = cli.Update( testAPIEndpoint, - "update", - "address="+testAddress, - "uuid="+did, - "sbi="+cliSbi, - "pnd="+cliPnd, - "path=/system/config/hostname", - "value="+hostname, - ); (err != nil) != tt.wantErr { + did, + cliPnd, + testPath, + hostname, + ) + if (err != nil) != tt.wantErr { t.Errorf("gosdn cli set error = %v, wantErr %v", err, tt.wantErr) return } - resp, err := cli.Get(testAddress, testUsername, testPassword, "/system/config/hostname") - if (err != nil) != tt.wantErr { - t.Errorf("cli.Get() error = %v, wantErr %v", err, tt.wantErr) + resp, err := cli.GetDevice(testAddress, testUsername, testPassword, testPath) + if err != nil { + if !tt.wantErr { + t.Errorf("cli.Get() error = %v, wantErr %v", err, tt.wantErr) + } return } - got := resp.Notification[0].Update[0].Val.GetStringVal() + var got string + if resp != nil { + got = resp.Ond[0].Name + } else { + t.Errorf("integration test failed got cannot be nil") + } if got != hostname { t.Errorf("integration test failed = got: %v, want: %v", got, hostname) } -- GitLab