From 02100fc8afba05cb1b8c7a181d3f1caa1ff213af Mon Sep 17 00:00:00 2001 From: Vitaliy Guschin Date: Thu, 8 Feb 2024 16:34:57 +0400 Subject: [PATCH 1/4] Fix vl3 example displaying. Signed-off-by: Vitaliy Guschin --- main.go | 10 +++++----- model_utils.go | 19 +++++++++++++++---- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index 78a17d3..bfe41c7 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,6 @@ -// Copyright (c) 2023 Cisco and/or its affiliates. +// Copyright (c) 2024 Cisco and/or its affiliates. // -// Copyright (c) 2023 Pragmagic Inc. and/or its affiliates. +// Copyright (c) 2024 Pragmagic Inc. and/or its affiliates. // // SPDX-License-Identifier: Apache-2.0 // @@ -172,7 +172,7 @@ func getNseChannel(ctx context.Context, logger log.Logger, dialOptions []grpc.Di func updateConnections(logger log.Logger, nsmgrAddr string, event *networkservice.ConnectionEvent) { for _, connection := range event.Connections { connectionID := generateConnectionID(connection) - logger.Infof("Handling %q event on %q nsmgr connection: %q", event.Type.String(), nsmgrAddr, connectionID) + logger.Infof("Handling %q event on %q nsmgr connectionId: %q Connection: %q", event.Type.String(), nsmgrAddr, connectionID, connection) switch { case event.Type == networkservice.ConnectionEventType_DELETE: connections.Delete(connectionID) @@ -182,7 +182,7 @@ func updateConnections(logger log.Logger, nsmgrAddr string, event *networkservic addManagerConnection(nsmgrAddr, connectionID) } } - parceConnectionsToGraphicalModel() + parceConnectionsToGraphicalModel(logger) } func cleanupManager(logger log.Logger, nsmgrAddr string) { @@ -194,7 +194,7 @@ func cleanupManager(logger log.Logger, nsmgrAddr string) { }) managerConnections.Delete(nsmgrAddr) nsmgrs.Delete(nsmgrAddr) - parceConnectionsToGraphicalModel() + parceConnectionsToGraphicalModel(logger) } func configureAndRunRESTServer() { diff --git a/model_utils.go b/model_utils.go index 8f2e3a4..20255f7 100644 --- a/model_utils.go +++ b/model_utils.go @@ -1,6 +1,6 @@ -// Copyright (c) 2023 Cisco and/or its affiliates. +// Copyright (c) 2024 Cisco and/or its affiliates. // -// Copyright (c) 2023 Pragmagic Inc. and/or its affiliates. +// Copyright (c) 2024 Pragmagic Inc. and/or its affiliates. // // SPDX-License-Identifier: Apache-2.0 // @@ -27,6 +27,7 @@ import ( "github.com/edwarnicke/genericsync" "github.com/networkservicemesh/api/pkg/api/networkservice" + "github.com/networkservicemesh/sdk/pkg/tools/log" ) func updateStorage(nodes []Node, edges []Edge) { @@ -52,7 +53,7 @@ func removeManagerConnection(mgr, conn string) { } } -func parceConnectionsToGraphicalModel() { +func parceConnectionsToGraphicalModel(logger log.Logger) { nodeMap := make(map[string]Node) var edges []Edge @@ -70,6 +71,13 @@ func parceConnectionsToGraphicalModel() { // Create all path segment nodes, interfaces and interface connections pathSegments := conn.GetPath().GetPathSegments() + + // Skip looped endpoint-endpoint connections (vl3 scenario) + if pathSegments[0].GetName() == pathSegments[len(pathSegments)-1].GetName() { + logger.Infof("Connection %q looped on %q is skipped from conversion to graphical model.", connectionID, pathSegments[0].GetName()) + return true + } + var previousInterfaceID string for _, segment := range pathSegments { segmentType := getPathSegmentType(segment.GetName()) @@ -85,7 +93,10 @@ func parceConnectionsToGraphicalModel() { case segmentType == endpointNT: interfID := fmt.Sprintf("int-s--%s--%s", connectionID, node.Data.ID) nodeMap[interfID] = makeInterface(interfID, node.Data.ID, getInterfaceLabelFromMetrics(segment, serverInterface)) - edges = addEdge(edges, previousInterfaceID, interfID, interfaceConnection, healthy) + if previousInterfaceID != "" { + edges = addEdge(edges, previousInterfaceID, interfID, interfaceConnection, healthy) + } + previousInterfaceID = interfID case segmentType == forwarderNT: interfCID := fmt.Sprintf("int-c--%s--%s", connectionID, node.Data.ID) nodeMap[interfCID] = makeInterface(interfCID, node.Data.ID, getInterfaceLabelFromMetrics(segment, clientInterface)) From 830602cc81d80a7cb41f2d2172cf9f4026c531fa Mon Sep 17 00:00:00 2001 From: Vitaliy Guschin Date: Thu, 8 Feb 2024 20:46:28 +0400 Subject: [PATCH 2/4] Fix copyright. Signed-off-by: Vitaliy Guschin --- main.go | 4 +--- model_utils.go | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index bfe41c7..a226bbe 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,4 @@ -// Copyright (c) 2024 Cisco and/or its affiliates. -// -// Copyright (c) 2024 Pragmagic Inc. and/or its affiliates. +// Copyright (c) 2023-2024 Pragmagic Inc. and/or its affiliates. // // SPDX-License-Identifier: Apache-2.0 // diff --git a/model_utils.go b/model_utils.go index 20255f7..4b24253 100644 --- a/model_utils.go +++ b/model_utils.go @@ -1,6 +1,4 @@ -// Copyright (c) 2024 Cisco and/or its affiliates. -// -// Copyright (c) 2024 Pragmagic Inc. and/or its affiliates. +// Copyright (c) 2023-2024 Pragmagic Inc. and/or its affiliates. // // SPDX-License-Identifier: Apache-2.0 // From c38f2254f3b27cd80ee7b64a68927c98a46b8b2b Mon Sep 17 00:00:00 2001 From: Vitaliy Guschin Date: Tue, 13 Feb 2024 14:14:35 +0400 Subject: [PATCH 3/4] Don't filter-out looped connections. Signed-off-by: Vitaliy Guschin --- main.go | 4 ++-- model_utils.go | 10 +--------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/main.go b/main.go index a226bbe..e13a7e2 100644 --- a/main.go +++ b/main.go @@ -180,7 +180,7 @@ func updateConnections(logger log.Logger, nsmgrAddr string, event *networkservic addManagerConnection(nsmgrAddr, connectionID) } } - parceConnectionsToGraphicalModel(logger) + parceConnectionsToGraphicalModel() } func cleanupManager(logger log.Logger, nsmgrAddr string) { @@ -192,7 +192,7 @@ func cleanupManager(logger log.Logger, nsmgrAddr string) { }) managerConnections.Delete(nsmgrAddr) nsmgrs.Delete(nsmgrAddr) - parceConnectionsToGraphicalModel(logger) + parceConnectionsToGraphicalModel() } func configureAndRunRESTServer() { diff --git a/model_utils.go b/model_utils.go index 4b24253..675f3cf 100644 --- a/model_utils.go +++ b/model_utils.go @@ -25,7 +25,6 @@ import ( "github.com/edwarnicke/genericsync" "github.com/networkservicemesh/api/pkg/api/networkservice" - "github.com/networkservicemesh/sdk/pkg/tools/log" ) func updateStorage(nodes []Node, edges []Edge) { @@ -51,7 +50,7 @@ func removeManagerConnection(mgr, conn string) { } } -func parceConnectionsToGraphicalModel(logger log.Logger) { +func parceConnectionsToGraphicalModel() { nodeMap := make(map[string]Node) var edges []Edge @@ -69,13 +68,6 @@ func parceConnectionsToGraphicalModel(logger log.Logger) { // Create all path segment nodes, interfaces and interface connections pathSegments := conn.GetPath().GetPathSegments() - - // Skip looped endpoint-endpoint connections (vl3 scenario) - if pathSegments[0].GetName() == pathSegments[len(pathSegments)-1].GetName() { - logger.Infof("Connection %q looped on %q is skipped from conversion to graphical model.", connectionID, pathSegments[0].GetName()) - return true - } - var previousInterfaceID string for _, segment := range pathSegments { segmentType := getPathSegmentType(segment.GetName()) From 49574b2dc7cb51b94cdfea07a5f78adb0b35b09a Mon Sep 17 00:00:00 2001 From: Vitaliy Guschin Date: Wed, 14 Feb 2024 15:02:09 +0400 Subject: [PATCH 4/4] Add unit tests. Signed-off-by: Vitaliy Guschin --- model_utils.go | 20 +++-- model_utils_test.go | 184 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 173 insertions(+), 31 deletions(-) diff --git a/model_utils.go b/model_utils.go index 0f582c9..be68cec 100644 --- a/model_utils.go +++ b/model_utils.go @@ -77,22 +77,26 @@ func parceConnectionsToGraphicalModel() { switch { case segmentType == clientNT: - interfID := fmt.Sprintf("int-c--%s--%s", connectionID, node.Data.ID) - nodeMap[interfID] = makeInterface(interfID, node.Data.ID, getInterfaceLabelFromMetrics(segment, clientInterface)) + interfName := getInterfaceLabelFromMetrics(segment, clientInterface) + interfID := fmt.Sprintf("int-c--%s--%s--%s", connectionID, node.Data.ID, interfName) + nodeMap[interfID] = makeInterface(interfID, node.Data.ID, interfName) previousInterfaceID = interfID case segmentType == endpointNT: - interfID := fmt.Sprintf("int-s--%s--%s", connectionID, node.Data.ID) - nodeMap[interfID] = makeInterface(interfID, node.Data.ID, getInterfaceLabelFromMetrics(segment, serverInterface)) + interfName := getInterfaceLabelFromMetrics(segment, serverInterface) + interfID := fmt.Sprintf("int-s--%s--%s--%s", connectionID, node.Data.ID, interfName) + nodeMap[interfID] = makeInterface(interfID, node.Data.ID, interfName) if previousInterfaceID != "" { edges = addEdge(edges, previousInterfaceID, interfID, interfaceConnection, healthy) } previousInterfaceID = interfID case segmentType == forwarderNT: - interfEID := fmt.Sprintf("int-s--%s--%s", connectionID, node.Data.ID) - nodeMap[interfEID] = makeInterface(interfEID, node.Data.ID, getInterfaceLabelFromMetrics(segment, serverInterface)) + interfEName := getInterfaceLabelFromMetrics(segment, serverInterface) + interfEID := fmt.Sprintf("int-s--%s--%s--%s", connectionID, node.Data.ID, interfEName) + nodeMap[interfEID] = makeInterface(interfEID, node.Data.ID, interfEName) edges = addEdge(edges, previousInterfaceID, interfEID, interfaceConnection, healthy) - interfCID := fmt.Sprintf("int-c--%s--%s", connectionID, node.Data.ID) - nodeMap[interfCID] = makeInterface(interfCID, node.Data.ID, getInterfaceLabelFromMetrics(segment, clientInterface)) + interfCName := getInterfaceLabelFromMetrics(segment, clientInterface) + interfCID := fmt.Sprintf("int-c--%s--%s--%s", connectionID, node.Data.ID, interfCName) + nodeMap[interfCID] = makeInterface(interfCID, node.Data.ID, interfCName) edges = addEdge(edges, interfEID, interfCID, interfaceCrossConnection, healthy) previousInterfaceID = interfCID // TODO Aggregate statistics for the Overview page diff --git a/model_utils_test.go b/model_utils_test.go index 7233796..1a91c2c 100644 --- a/model_utils_test.go +++ b/model_utils_test.go @@ -31,11 +31,17 @@ func Test_Interface_Order(t *testing.T) { goleak.VerifyNone(t) }) - nsmgrAddr := "10.244.1.4:5001" - nscName := "alpine-0a9d9aa7" - fwd1Name := "forwarder-vpp-t2jgb" - fwd2Name := "forwarder-vpp-9dxwt" - nseName := "nse-kernel-zz6m5" + const nsmgrAddr = "10.244.1.4:5001" + const nscName = "alpine-0a9d9aa7" + const cifNscName = "KERNEL/nsm-1" + const fwd1Name = "forwarder-vpp-t2jgb" + const cifFwd1Name = "WIREGUARD TUNNEL/wg0" + const sifFwd1Name = "VIRTIO/tun0" + const fwd2Name = "forwarder-vpp-9dxwt" + const cifFwd2Name = "VIRTIO/tun0" + const sifFwd2Name = "WIREGUARD TUNNEL/wg0" + const nseName = "nse-kernel-zz6m5" + const sifNseName = "KERNEL/kernel2ip2-ff6c" connection := &networkservice.Connection{ NetworkService: "kernel2ip2kernel", @@ -45,7 +51,7 @@ func Test_Interface_Order(t *testing.T) { Name: nscName, Id: "alpine-0a9d9aa7-0", Metrics: map[string]string{ - "client_interface": "KERNEL/nsm-1", + "client_interface": cifNscName, }, }, { @@ -56,8 +62,8 @@ func Test_Interface_Order(t *testing.T) { Name: fwd1Name, Id: "23456", Metrics: map[string]string{ - "client_interface": "WIREGUARD TUNNEL/wg0", - "server_interface": "VIRTIO/tun0", + "client_interface": cifFwd1Name, + "server_interface": sifFwd1Name, }, }, { @@ -68,15 +74,15 @@ func Test_Interface_Order(t *testing.T) { Name: fwd2Name, Id: "45678", Metrics: map[string]string{ - "client_interface": "VIRTIO/tun0", - "server_interface": "WIREGUARD TUNNEL/wg0", + "client_interface": cifFwd2Name, + "server_interface": sifFwd2Name, }, }, { Name: nseName, Id: "56789", Metrics: map[string]string{ - "server_interface": "KERNEL/kernel2ip2-ff6c", + "server_interface": sifNseName, }, }, }, @@ -92,28 +98,28 @@ func Test_Interface_Order(t *testing.T) { require.Equal(t, 5, len(storageData.Edges)) // Connection 1 (client-forwarder1) - source1 := fmt.Sprintf("int-c--%s--%s", connectionID, nscName) - target1 := fmt.Sprintf("int-s--%s--%s", connectionID, fwd1Name) + source1 := fmt.Sprintf("int-c--%s--%s--%s", connectionID, nscName, cifNscName) + target1 := fmt.Sprintf("int-s--%s--%s--%s", connectionID, fwd1Name, sifFwd1Name) require.Equal(t, source1, storageData.Edges[0].Data.Source) require.Equal(t, target1, storageData.Edges[0].Data.Target) // Connection 2 (forwarder1 internal interface cross connection) - source2 := fmt.Sprintf("int-s--%s--%s", connectionID, fwd1Name) - target2 := fmt.Sprintf("int-c--%s--%s", connectionID, fwd1Name) + source2 := fmt.Sprintf("int-s--%s--%s--%s", connectionID, fwd1Name, sifFwd1Name) + target2 := fmt.Sprintf("int-c--%s--%s--%s", connectionID, fwd1Name, cifFwd1Name) require.Equal(t, source2, storageData.Edges[1].Data.Source) require.Equal(t, target2, storageData.Edges[1].Data.Target) // Connection 3 (forwarder1-forwarder2) - source3 := fmt.Sprintf("int-c--%s--%s", connectionID, fwd1Name) - target3 := fmt.Sprintf("int-s--%s--%s", connectionID, fwd2Name) + source3 := fmt.Sprintf("int-c--%s--%s--%s", connectionID, fwd1Name, cifFwd1Name) + target3 := fmt.Sprintf("int-s--%s--%s--%s", connectionID, fwd2Name, sifFwd2Name) require.Equal(t, source3, storageData.Edges[2].Data.Source) require.Equal(t, target3, storageData.Edges[2].Data.Target) // Connection 4 (forwarder2 internal interface cross connection) - source4 := fmt.Sprintf("int-s--%s--%s", connectionID, fwd2Name) - target4 := fmt.Sprintf("int-c--%s--%s", connectionID, fwd2Name) + source4 := fmt.Sprintf("int-s--%s--%s--%s", connectionID, fwd2Name, sifFwd2Name) + target4 := fmt.Sprintf("int-c--%s--%s--%s", connectionID, fwd2Name, cifFwd2Name) require.Equal(t, source4, storageData.Edges[3].Data.Source) require.Equal(t, target4, storageData.Edges[3].Data.Target) // Connection 5 (forwarder2-endpoint) - source5 := fmt.Sprintf("int-c--%s--%s", connectionID, fwd2Name) - target5 := fmt.Sprintf("int-s--%s--%s", connectionID, nseName) + source5 := fmt.Sprintf("int-c--%s--%s--%s", connectionID, fwd2Name, cifFwd2Name) + target5 := fmt.Sprintf("int-s--%s--%s--%s", connectionID, nseName, sifNseName) require.Equal(t, source5, storageData.Edges[4].Data.Source) require.Equal(t, target5, storageData.Edges[4].Data.Target) @@ -121,8 +127,140 @@ func Test_Interface_Order(t *testing.T) { connections.Delete(connectionID) managerConnections.Delete(nsmgrAddr) nsmgrs.Delete(nsmgrAddr) +} + +func Test_NSE_To_NSE_Connection(t *testing.T) { + t.Cleanup(func() { + goleak.VerifyNone(t) + }) + + const nsmgrAddr = "10.244.1.5:5001" + + connection := &networkservice.Connection{ + NetworkService: "ns1", + Path: &networkservice.Path{ + PathSegments: []*networkservice.PathSegment{ + { + Name: "nse-vl3-vpp-9ln7f", + Id: "12345", + Metrics: map[string]string{ + "server_interface": "MEMIF/memif2670642822/2", + }, + }, + { + Name: "nsmgr-j6x6m", + Id: "23456", + }, + { + Name: "forwarder-vpp-zpg6b", + Id: "34567", + Metrics: map[string]string{ + "client_interface": "WIREGUARD TUNNEL/wg1", + "server_interface": "MEMIF/memif2670642822/0", + }, + }, + { + Name: "nsmgr-rp4rr", + Id: "45678", + }, + { + Name: "forwarder-vpp-s7sw5", + Id: "56789", + Metrics: map[string]string{ + "client_interface": "MEMIF/memif2670642822/0", + "server_interface": "WIREGUARD TUNNEL/wg1", + }, + }, + { + Name: "nse-vl3-vpp-gcf6j", + Id: "67890", + Metrics: map[string]string{ + "server_interface": "MEMIF/memif2670642822/1", + }, + }, + }, + }, + } + connectionID := generateConnectionID(connection) + + connections.Store(connectionID, connection) + addManagerConnection(nsmgrAddr, connectionID) parceConnectionsToGraphicalModel() - require.Equal(t, 0, len(storageData.Nodes)) - require.Equal(t, 0, len(storageData.Edges)) + // 14: 1 cluster + 1 ns + 2 nse + 2 mgr + 2 fwd + 6 if + require.Equal(t, 14, len(storageData.Nodes)) + require.Equal(t, 5, len(storageData.Edges)) + + // Check that all connections have sources and targets + for _, edge := range storageData.Edges { + require.NotEmpty(t, edge.Data.Source) + require.NotEmpty(t, edge.Data.Target) + } + + // Cleanup + connections.Delete(connectionID) + managerConnections.Delete(nsmgrAddr) + nsmgrs.Delete(nsmgrAddr) +} + +func Test_Looped_NSE_To_NSE_Connection(t *testing.T) { + t.Cleanup(func() { + goleak.VerifyNone(t) + }) + + const nsmgrAddr = "10.244.1.6:5001" + + connection := &networkservice.Connection{ + NetworkService: "ns2", + Path: &networkservice.Path{ + PathSegments: []*networkservice.PathSegment{ + { + Name: "nse-vl3-vpp-gcf6j", + Id: "12345", + Metrics: map[string]string{ + "server_interface": "MEMIF/memif2670642822/2", + }, + }, + { + Name: "nsmgr-rp4rr", + Id: "23456", + }, + { + Name: "forwarder-vpp-s7sw5", + Id: "34567", + Metrics: map[string]string{ + "client_interface": "MEMIF/memif3519870697/0", + "server_interface": "VIRTIO/tun0", + }, + }, + { + Name: "nse-vl3-vpp-gcf6j", + Id: "12345", + Metrics: map[string]string{ + "server_interface": "MEMIF/memif2670642822/1", + }, + }, + }, + }, + } + connectionID := generateConnectionID(connection) + + connections.Store(connectionID, connection) + addManagerConnection(nsmgrAddr, connectionID) + parceConnectionsToGraphicalModel() + + // 9: 1 cluster + 1 ns + 1 nse + 1 mgr + 1 fwd + 4 if + require.Equal(t, 9, len(storageData.Nodes)) + require.Equal(t, 3, len(storageData.Edges)) + + // Check that all connections have sources and targets + for _, edge := range storageData.Edges { + require.NotEmpty(t, edge.Data.Source) + require.NotEmpty(t, edge.Data.Target) + } + + // Cleanup + connections.Delete(connectionID) + managerConnections.Delete(nsmgrAddr) + nsmgrs.Delete(nsmgrAddr) }