diff --git a/Makefile b/Makefile index 40571e0b23a8c615b9944adfcd4473a68e650683..c1e3f53ef0633d27c3a2134ae625b9b4531a8334 100644 --- a/Makefile +++ b/Makefile @@ -93,6 +93,12 @@ integration-tests: generate-certs containerize-gosdn containerize-plugin-registr go test -p 1 ./integration-tests/... docker-compose -f dev_env_data/docker-compose/integration-test_docker-compose.yml down +integration-tests-verbose: generate-certs containerize-gosdn containerize-plugin-registry + docker-compose -f dev_env_data/docker-compose/integration-test_docker-compose.yml down + docker-compose -f dev_env_data/docker-compose/integration-test_docker-compose.yml up -d + go test -p 1 ./integration-tests/... -v + docker-compose -f dev_env_data/docker-compose/integration-test_docker-compose.yml down + integration-tests-debug-up: generate-certs containerize-gosdn containerize-plugin-registry docker-compose -f dev_env_data/docker-compose/integration-test_docker-compose.yml up -d diff --git a/integration-tests/lab_tests/lab00/lab00_test.go b/integration-tests/lab_tests/lab00/lab00_test.go index 8b2f942d59c28540cba2aa179e13d734a6adc1d1..02479ef27fb37fe1890c94cd0b55b7ef451cc3e1 100644 --- a/integration-tests/lab_tests/lab00/lab00_test.go +++ b/integration-tests/lab_tests/lab00/lab00_test.go @@ -50,7 +50,7 @@ func TestMain(m *testing.M) { } // The following test describes a chain of commands (see -// https://code.fbi.h-da.de/danet/gosdn/-/wikis/Labs/Lab00-education). +// https://code.fbi.h-da.de/danet/gosdn/-/wikis/Labs/Lab00). func TestLab00(t *testing.T) { defer integration_test_utils.ApplySDNConfig(conn, ctx, defaultSDNConfig) diff --git a/integration-tests/lab_tests/lab01/lab01_test.go b/integration-tests/lab_tests/lab01/lab01_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5496661f09d73462a5746911e5fe961e99dfa6be --- /dev/null +++ b/integration-tests/lab_tests/lab01/lab01_test.go @@ -0,0 +1,206 @@ +package integration_test_lab_01 + +import ( + "context" + "fmt" + "strings" + "testing" + + mnepb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement" + integration_test_utils "code.fbi.h-da.de/danet/gosdn/integration-tests/integrationTestUtils" + lab_utils "code.fbi.h-da.de/danet/gosdn/integration-tests/lab_tests/utils" + "github.com/stretchr/testify/assert" + "google.golang.org/grpc" +) + +const ( + pndID = "5f20f34b-cbd0-4511-9ddc-c50cf6a3b49d" + targetAName = "gnmi-targetA" + targetAUUID = "e3cc56ad-2a6c-4b76-8315-5f17a6dc25bf" + targetBName = "gnmi-targetB" + targetBUUID = "87c8bcc1-d95c-401e-b447-f02c56abd8b1" + targetInterfaceEth1 = "eth1" + targetInterfaceEth2 = "eth2" + targetRoute = "route" + targetAInterfacePathEth1 = "switch0/s0-eth1.json" + targetAInterfacePathEth2 = "switch0/s0-eth2.json" + targetARoutePath = "switch0/s0-route.json" + targetBInterfacePathEth1 = "switch1/s1-eth1.json" + targetBInterfacePathEth2 = "switch1/s1-eth2.json" + targetBRoutePath = "switch1/s1-route.json" + relativePathToJSONSources = "../utils/json_sources/Lab01" +) + +// Represents the yang models stored in JSON files. +// Contains the target leaf in the managed network element, the path to the JSON file and the JSON value, parsed as string. +type configEntry struct { + leaf string + path string + value string +} + +// Represents the managed network elements. +type managedNetworkElement struct { + name string + id string + addr string + configEntries []configEntry +} + +var managedNetworkElements = []managedNetworkElement{ + { + name: targetAName, + id: targetAUUID, + addr: integration_test_utils.DefaultTargetAAdress, + configEntries: []configEntry{ + { + leaf: targetInterfaceEth1, + path: targetAInterfacePathEth1, + }, + { + leaf: targetInterfaceEth2, + path: targetAInterfacePathEth2, + }, + { + leaf: targetRoute, + path: targetARoutePath, + }, + }, + }, + { + name: targetBName, + id: targetBUUID, + addr: integration_test_utils.DefaultTargetBAdress, + configEntries: []configEntry{ + { + leaf: targetInterfaceEth1, + path: targetBInterfacePathEth1, + }, + { + leaf: targetInterfaceEth2, + path: targetBInterfacePathEth2, + }, + { + leaf: targetRoute, + path: targetBRoutePath, + }, + }, + }, +} + +// The connection to the controller to use in each test. +var conn *grpc.ClientConn + +// The context containing the credentials when authenticated. +var ctx context.Context + +// A defaultSDN config with default/empty values. +var defaultSDNConfig string + +func TestMain(m *testing.M) { + localConn, localCtx, err := integration_test_utils.CreateSecureConnection() + if err != nil { + fmt.Println(err.Error()) + } + conn = localConn + ctx = localCtx + + sndConfig, err := integration_test_utils.ExportCurrentSDNConfig(conn, ctx) + defaultSDNConfig = sndConfig + if err != nil { + fmt.Println(err.Error()) + } + + integration_test_utils.ApplySDNConfig(conn, ctx, defaultSDNConfig) + + m.Run() +} + +// The following test describes a chain of commands (see +// https://code.fbi.h-da.de/danet/gosdn/-/wikis/Labs/Lab01). + +func TestLab01(t *testing.T) { + defer integration_test_utils.ApplySDNConfig(conn, ctx, defaultSDNConfig) + mneService := mnepb.NewNetworkElementServiceClient(conn) + + // Create the two managed network elements. + for i, mne := range managedNetworkElements { + alr := lab_utils.CreateAddListRequestSingleMne(mne.addr, mne.name, mne.id, integration_test_utils.DefaultTargetUsername, integration_test_utils.DefaultTargetPassword, pndID) + + _, err := mneService.AddList(ctx, alr) + if err != nil { + t.Error(err) + } + + getAllMneRequest := &mnepb.GetAllRequest{ + Timestamp: integration_test_utils.GetTimestamp(), + Pid: pndID, + } + + getAllResponse, err := mneService.GetAll(ctx, getAllMneRequest) + if err != nil { + t.Error(err) + } + + assert.Equal(t, i+1, len(getAllResponse.GetMne()), "Amount of registered managed network elements is not correct.") + } + + // Cast JSON config files into strings + for _, mne := range managedNetworkElements { + for i := 0; i < len(mne.configEntries); i++ { + jsonValue, err := lab_utils.FileContentToString(relativePathToJSONSources + mne.configEntries[i].path) + if err != nil { + t.Error(err) + } + mne.configEntries[i].value = jsonValue + } + } + + // Modify the interface of both managed network elements. + for _, mne := range managedNetworkElements { + for i := 0; i < len(mne.configEntries); i++ { + // Interface change request + if strings.Contains(mne.configEntries[i].leaf, "eth") { + interfacePathListRequest, err := lab_utils.SetInterfacePathListRequestSingleMne(mne.configEntries[i].leaf, mne.id, mnepb.ApiOperation_API_OPERATION_UPDATE, mne.configEntries[i].value, pndID) + if err != nil { + t.Error(err) + } + setResp, err := mneService.SetPathList(ctx, interfacePathListRequest) + if err != nil { + t.Error(err) + } + + // Commit the change. + commitChange := lab_utils.CommitOrConfirmChange(setResp.GetResponses()[0].GetId(), mnepb.Operation_OPERATION_COMMIT, pndID) + _, err = mneService.SetChangeList(ctx, commitChange) + if err != nil { + t.Error(err) + } + + // Confirm the change. + confirmChange := lab_utils.CommitOrConfirmChange(setResp.GetResponses()[0].GetId(), mnepb.Operation_OPERATION_CONFIRM, pndID) + _, err = mneService.SetChangeList(ctx, confirmChange) + if err != nil { + t.Error(err) + } + + // Route change request + } else if strings.Contains(mne.configEntries[i].leaf, "route") { + fmt.Println("to be continued") + } + + } + + } + + // Commit Confirm + + // Check if the network interface has been changed + + // Modify the routing configuration of both managed network elements. + + // Check if the routing configuration has been changed + + // Ping to check connection between both managed network elements. + +} diff --git a/integration-tests/lab_tests/utils/json_sources/Lab01/switch0/s0-eth1.json b/integration-tests/lab_tests/utils/json_sources/Lab01/switch0/s0-eth1.json new file mode 100644 index 0000000000000000000000000000000000000000..53aac83b93737fd7fc1ab4d28b4cfda1e9f64cd6 --- /dev/null +++ b/integration-tests/lab_tests/utils/json_sources/Lab01/switch0/s0-eth1.json @@ -0,0 +1,36 @@ +{ + "openconfig-interfaces:config":{ + "enabled":true, + "loopback-mode":false, + "mtu":1500, + "name":"eth1" + }, + "openconfig-interfaces:name":"eth1", + "openconfig-interfaces:subinterfaces":{ + "subinterface":[ + { + "config":{ + "enabled":true, + "index":0 + }, + "index":0, + "openconfig-if-ip:ipv4":{ + "addresses":{ + "address":[ + { + "config":{ + "ip":"10.50.0.1", + "prefix-length":30 + }, + "ip":"10.50.0.1" + } + ] + }, + "config":{ + "enabled":true + } + } + } + ] + } +} diff --git a/integration-tests/lab_tests/utils/json_sources/Lab01/switch0/s0-eth2.json b/integration-tests/lab_tests/utils/json_sources/Lab01/switch0/s0-eth2.json new file mode 100644 index 0000000000000000000000000000000000000000..7c395f9c1678b02913e2556e266ae410de031085 --- /dev/null +++ b/integration-tests/lab_tests/utils/json_sources/Lab01/switch0/s0-eth2.json @@ -0,0 +1,36 @@ +{ + "openconfig-interfaces:config":{ + "enabled":true, + "loopback-mode":false, + "mtu":1500, + "name":"eth2" + }, + "openconfig-interfaces:name":"eth2", + "openconfig-interfaces:subinterfaces":{ + "subinterface":[ + { + "config":{ + "enabled":true, + "index":0 + }, + "index":0, + "openconfig-if-ip:ipv4":{ + "addresses":{ + "address":[ + { + "config":{ + "ip":"10.0.0.1", + "prefix-length":24 + }, + "ip":"10.0.0.1" + } + ] + }, + "config":{ + "enabled":true + } + } + } + ] + } +} diff --git a/integration-tests/lab_tests/utils/json_sources/Lab01/switch0/s0-route.json b/integration-tests/lab_tests/utils/json_sources/Lab01/switch0/s0-route.json new file mode 100644 index 0000000000000000000000000000000000000000..056d207163a8a919d3fb5ea6c7cc0fbf8f218306 --- /dev/null +++ b/integration-tests/lab_tests/utils/json_sources/Lab01/switch0/s0-route.json @@ -0,0 +1,62 @@ +{ + "openconfig-network-instance:protocol": [ + { + "config": { + "identifier": "openconfig-policy-types:STATIC", + "name": "STATIC" + }, + "identifier": "openconfig-policy-types:STATIC", + "name": "STATIC", + "static-routes": { + "static": [ + { + "config": { + "prefix": "192.168.0.0/24" + }, + "next-hops": { + "next-hop": [ + { + "config": { + "index": "AUTO_1_10-50-0-2", + "metric": 1, + "next-hop": "10.50.0.2" + }, + "index": "AUTO_1_10-50-0-2", + "interface-ref": { + "config": { + "interface": "eth1" + } + } + } + ] + }, + "prefix": "192.168.0.0/24" + }, + { + "config": { + "prefix": "10.0.0.100/32" + }, + "next-hops": { + "next-hop": [ + { + "config": { + "index": "AUTO_2_10-0-0-100", + "next-hop": "10.0.0.100" + }, + "index": "AUTO_2_10-0-0-100", + "interface-ref": { + "config": { + "interface": "eth2" + } + } + } + ] + }, + "prefix": "10.0.0.100/32" + } + ] + } + } + ] +} + diff --git a/integration-tests/lab_tests/utils/json_sources/Lab01/switch1/s1-eth1.json b/integration-tests/lab_tests/utils/json_sources/Lab01/switch1/s1-eth1.json new file mode 100644 index 0000000000000000000000000000000000000000..c67f4bae20211eb0cc736f151eefdb664b2d8855 --- /dev/null +++ b/integration-tests/lab_tests/utils/json_sources/Lab01/switch1/s1-eth1.json @@ -0,0 +1,36 @@ +{ + "openconfig-interfaces:config":{ + "enabled":true, + "loopback-mode":false, + "mtu":1500, + "name":"eth1" + }, + "openconfig-interfaces:name":"eth1", + "openconfig-interfaces:subinterfaces":{ + "subinterface":[ + { + "config":{ + "enabled":true, + "index":0 + }, + "index":0, + "openconfig-if-ip:ipv4":{ + "addresses":{ + "address":[ + { + "config":{ + "ip":"10.50.0.2", + "prefix-length":30 + }, + "ip":"10.50.0.2" + } + ] + }, + "config":{ + "enabled":true + } + } + } + ] + } +} diff --git a/integration-tests/lab_tests/utils/json_sources/Lab01/switch1/s1-eth2.json b/integration-tests/lab_tests/utils/json_sources/Lab01/switch1/s1-eth2.json new file mode 100644 index 0000000000000000000000000000000000000000..f87fc95fee3e4104dc6e57f63715ffe095142857 --- /dev/null +++ b/integration-tests/lab_tests/utils/json_sources/Lab01/switch1/s1-eth2.json @@ -0,0 +1,36 @@ +{ + "openconfig-interfaces:config":{ + "enabled":true, + "loopback-mode":false, + "mtu":1500, + "name":"eth2" + }, + "openconfig-interfaces:name":"eth2", + "openconfig-interfaces:subinterfaces":{ + "subinterface":[ + { + "config":{ + "enabled":true, + "index":0 + }, + "index":0, + "openconfig-if-ip:ipv4":{ + "addresses":{ + "address":[ + { + "config":{ + "ip":"192.168.0.1", + "prefix-length":24 + }, + "ip":"192.168.0.1" + } + ] + }, + "config":{ + "enabled":true + } + } + } + ] + } +} diff --git a/integration-tests/lab_tests/utils/json_sources/Lab01/switch1/s1-route.json b/integration-tests/lab_tests/utils/json_sources/Lab01/switch1/s1-route.json new file mode 100644 index 0000000000000000000000000000000000000000..a23a0a9eaa099d39a5975ad83aa8b4bf13106309 --- /dev/null +++ b/integration-tests/lab_tests/utils/json_sources/Lab01/switch1/s1-route.json @@ -0,0 +1,62 @@ +{ + "openconfig-network-instance:protocol": [ + { + "config": { + "identifier": "openconfig-policy-types:STATIC", + "name": "STATIC" + }, + "identifier": "openconfig-policy-types:STATIC", + "name": "STATIC", + "static-routes": { + "static": [ + { + "config": { + "prefix": "10.0.0.0/24" + }, + "next-hops": { + "next-hop": [ + { + "config": { + "index": "AUTO_1_10-50-0-1", + "metric": 1, + "next-hop": "10.50.0.1" + }, + "index": "AUTO_1_10-50-0-1", + "interface-ref": { + "config": { + "interface": "eth1" + } + } + } + ] + }, + "prefix": "10.0.0.0/24" + }, + { + "config": { + "prefix": "192.168.0.100/32" + }, + "next-hops": { + "next-hop": [ + { + "config": { + "index": "AUTO_2_192-168-0-100", + "next-hop": "192.168.0.100" + }, + "index": "AUTO_2_192-168-0-100", + "interface-ref": { + "config": { + "interface": "eth2" + } + } + } + ] + }, + "prefix": "192.168.0.100/32" + } + ] + } + } + ] +} + diff --git a/integration-tests/lab_tests/utils/labUtils.go b/integration-tests/lab_tests/utils/labUtils.go index c8d76943fed84db6eea52138e41c6bc3fe3edc34..03b9b6aebe9c710cc781077d747de81f7c771f3f 100644 --- a/integration-tests/lab_tests/utils/labUtils.go +++ b/integration-tests/lab_tests/utils/labUtils.go @@ -1,6 +1,8 @@ package lab_utils import ( + "os" + mnepb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement" tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport" integration_test_utils "code.fbi.h-da.de/danet/gosdn/integration-tests/integrationTestUtils" @@ -83,3 +85,40 @@ func CreateAddListRequestSingleMne(addr, name, id, username, password string, pn return alr } + +func SetInterfacePathListRequestSingleMne(networkInterface string, mneId string, operation mnepb.ApiOperation, newInterfaceConfig string, pndID string) (*mnepb.SetPathListRequest, error) { + // Extract network interface + interfacePathString := "interfaces/interface[name=" + networkInterface + "]" + interfacePath, err := ygot.StringToStructuredPath(interfacePathString) + if err != nil { + return nil, err + } + + // Create PathListRequest + splr := &mnepb.SetPathListRequest{ + Timestamp: integration_test_utils.GetTimestamp(), + ChangeRequest: []*mnepb.ChangeRequest{ + { + Mneid: mneId, + Path: interfacePath, + Value: &gnmi.TypedValue{ + Value: &gnmi.TypedValue_StringVal{ + StringVal: newInterfaceConfig, + }, + }, + ApiOp: operation, + }, + }, + } + + return splr, nil +} + +func FileContentToString(path string) (string, error) { + fileContent, err := os.ReadFile(path) + if err != nil { + return "", err + } + + return string(fileContent), nil +}