func TestCaptureApi(t *testing.T) { apiServer, err := createAPIServer(t, "basic") if err != nil { t.Fatal(err) } defer apiServer.Stop() apiClient, err := apiServer.GetClient() if err != nil { t.Fatal(err) } capture := api.NewCapture("G.V().Has('Name', 'br-int')", "port 80") if err := apiClient.create("capture", capture); err != nil { t.Fatalf("Failed to create alert: %s", err.Error()) } capture2 := &api.Capture{} if err := apiClient.Get("capture", capture.ID(), &capture2); err != nil { t.Error(err) } if *capture != *capture2 { t.Errorf("Capture corrupted: %+v != %+v", capture, capture2) } var captures map[string]api.Capture if err := apiClient.List("capture", &captures); err != nil { t.Error(err) } else { if len(captures) != 1 { t.Errorf("Wrong number of captures: got %d, expected 1", len(captures)) } } if captures[capture.ID()] != *capture { t.Errorf("Capture corrupted: %+v != %+v", captures[capture.ID()], capture) } if err := apiClient.Delete("capture", capture.ID()); err != nil { t.Errorf("Failed to delete capture: %s", err.Error()) } var captures2 map[string]api.Capture if err := apiClient.List("capture", &captures2); err != nil { t.Errorf("Failed to list captures: %s", err.Error()) } else { if len(captures2) != 0 { t.Errorf("Wrong number of captures: got %d, expected 0 (%+v)", len(captures2), captures2) } } if err := apiClient.Get("capture", capture.ID(), &capture2); err == nil { t.Errorf("Found delete capture: %s", capture.ID()) } }
func TestSFlowNodeTIDOvsInternalNetNS(t *testing.T) { ts := NewTestStorage() aa := helper.NewAgentAnalyzerWithConfig(t, confAgentAnalyzer, ts) aa.Start() defer aa.Stop() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture := api.NewCapture("G.V().Has('Name', 'br-sflow', 'Type', 'ovsbridge')", "") if err := client.Create("capture", capture); err != nil { t.Fatal(err.Error()) } time.Sleep(1 * time.Second) setupCmds := []helper.Cmd{ {"ovs-vsctl add-br br-sflow", true}, {"ovs-vsctl add-port br-sflow sflow-intf1 -- set interface sflow-intf1 type=internal", true}, {"ip netns add sflow-vm1", true}, {"ip link set sflow-intf1 netns sflow-vm1", true}, {"ip netns exec sflow-vm1 ip address add 169.254.33.33/24 dev sflow-intf1", true}, {"ip netns exec sflow-vm1 ip link set sflow-intf1 up", true}, {"ip netns exec sflow-vm1 ping -c 15 -I sflow-intf1 169.254.33.34", false}, } tearDownCmds := []helper.Cmd{ {"ip netns del sflow-vm1", true}, {"ovs-vsctl del-br br-sflow", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) aa.Flush() gh := helper.NewGremlinQueryHelper(&http.AuthenticationOpts{}) node := gh.GetNodeFromGremlinReply(t, `g.V().Has("Name", "br-sflow", "Type", "ovsbridge")`) ok := false for _, f := range ts.GetFlows() { if f.NodeTID == node.Metadata()["TID"].(string) && f.LayersPath == "Ethernet/ARP/Payload" { ok = true break } } if !ok { t.Error("Unable to find a flow with the expected NodeTID") } client.Delete("capture", capture.ID()) }
func TestFlowMetrics(t *testing.T) { ts := NewTestStorage() aa := helper.NewAgentAnalyzerWithConfig(t, confAgentAnalyzer, ts) aa.Start() defer aa.Stop() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture := api.NewCapture("G.V().Has('Name', 'br-sflow', 'Type', 'ovsbridge')", "") if err := client.Create("capture", capture); err != nil { t.Fatal(err.Error()) } time.Sleep(1 * time.Second) setupCmds := []helper.Cmd{ {"ovs-vsctl add-br br-sflow", true}, {"ovs-vsctl add-port br-sflow sflow-intf1 -- set interface sflow-intf1 type=internal", true}, {"ip netns add sflow-vm1", true}, {"ip link set sflow-intf1 netns sflow-vm1", true}, {"ip netns exec sflow-vm1 ip address add 169.254.33.33/24 dev sflow-intf1", true}, {"ip netns exec sflow-vm1 ip link set sflow-intf1 up", true}, {"ovs-vsctl add-port br-sflow sflow-intf2 -- set interface sflow-intf2 type=internal", true}, {"ip netns add sflow-vm2", true}, {"ip link set sflow-intf2 netns sflow-vm2", true}, {"ip netns exec sflow-vm2 ip address add 169.254.33.34/24 dev sflow-intf2", true}, {"ip netns exec sflow-vm2 ip link set sflow-intf2 up", true}, // wait to have everything ready, sflow, interfaces {"sleep 2", false}, {"ip netns exec sflow-vm1 ping -c 1 -s 1024 -I sflow-intf1 169.254.33.34", false}, } tearDownCmds := []helper.Cmd{ {"ip netns del sflow-vm1", true}, {"ip netns del sflow-vm2", true}, {"ovs-vsctl del-br br-sflow", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) time.Sleep(1 * time.Second) queryFlowMetrics(t, -1, 1) client.Delete("capture", capture.ID()) }
func TestSFlowWithPCAP(t *testing.T) { ts := NewTestStorage() aa := helper.NewAgentAnalyzerWithConfig(t, confAgentAnalyzer, ts) aa.Start() defer aa.Stop() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture := api.NewCapture("G.V().Has('Name', 'br-sflow', 'Type', 'ovsbridge')", "") if err := client.Create("capture", capture); err != nil { t.Fatal(err.Error()) } time.Sleep(1 * time.Second) setupCmds := []helper.Cmd{ {"ovs-vsctl add-br br-sflow", true}, } tearDownCmds := []helper.Cmd{ {"ovs-vsctl del-br br-sflow", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) time.Sleep(5 * time.Second) // FIX(safchain): need to be reworked as there is no more static sflow agent // running at a specific port and agent couldn't speak sflow at all for _, trace := range flowsTraces { fulltrace, _ := filepath.Abs(trace.filename) err := tools.PCAP2SFlowReplay("localhost", 55000, fulltrace, 1000, 5) if err != nil { t.Fatalf("Error during the replay: %s", err.Error()) } aa.Flush() pcapTraceValidate(t, ts.GetFlows(), &trace) } client.Delete("capture", capture.ID()) }
func TestFlowMetricsSum(t *testing.T) { ts := NewTestStorage() aa := helper.NewAgentAnalyzerWithConfig(t, confAgentAnalyzer, ts) aa.Start() defer aa.Stop() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture := api.NewCapture("G.V().Has('Name', 'br-sflow', 'Type', 'ovsbridge')", "") if err := client.Create("capture", capture); err != nil { t.Fatal(err.Error()) } time.Sleep(1 * time.Second) setupCmds := []helper.Cmd{ {"ovs-vsctl add-br br-sflow", true}, {"ovs-vsctl add-port br-sflow sflow-intf1 -- set interface sflow-intf1 type=internal", true}, {"ip netns add sflow-vm1", true}, {"ip link set sflow-intf1 netns sflow-vm1", true}, {"ip netns exec sflow-vm1 ip address add 169.254.33.33/24 dev sflow-intf1", true}, {"ip netns exec sflow-vm1 ip link set sflow-intf1 up", true}, {"ovs-vsctl add-port br-sflow sflow-intf2 -- set interface sflow-intf2 type=internal", true}, {"ip netns add sflow-vm2", true}, {"ip link set sflow-intf2 netns sflow-vm2", true}, {"ip netns exec sflow-vm2 ip address add 169.254.33.34/24 dev sflow-intf2", true}, {"ip netns exec sflow-vm2 ip link set sflow-intf2 up", true}, // wait to have everything ready, sflow, interfaces {"sleep 2", false}, {"ip netns exec sflow-vm1 ping -c 1 -s 1024 -I sflow-intf1 169.254.33.34", false}, } tearDownCmds := []helper.Cmd{ {"ip netns del sflow-vm1", true}, {"ip netns del sflow-vm2", true}, {"ovs-vsctl del-br br-sflow", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) // since the agent update ticker is about 10 sec according to the configuration // wait 11 sec to have the first update and the MetricRange filled time.Sleep(11 * time.Second) gh := helper.NewGremlinQueryHelper(&http.AuthenticationOpts{}) gremlin := `g.V().Has("Name", "br-sflow", "Type", "ovsbridge").Flows().Has("LayersPath", "Ethernet/IPv4/ICMPv4/Payload").Dedup().Metrics().Sum()` // this check needs to be close to the beginnig of the test since it's a time // based test and it will fail if we wait one more update tick metric := gh.GetFlowMetricFromGremlinReply(t, gremlin) if metric.ABPackets != 1 || metric.BAPackets != 1 || metric.ABBytes < 1066 || metric.BABytes < 1066 { t.Errorf("Wrong metric returned, got : %v", metric) } client.Delete("capture", capture.ID()) }
Short: "Manage captures", Long: "Manage captures", SilenceUsage: false, } var CaptureCreate = &cobra.Command{ Use: "create", Short: "Create capture", Long: "Create capture", Run: func(cmd *cobra.Command, args []string) { client, err := api.NewCrudClientFromConfig(&AuthenticationOpts) if err != nil { logging.GetLogger().Criticalf(err.Error()) } capture := api.NewCapture(gremlinQuery, bpfFilter) capture.Name = captureName capture.Description = captureDescription capture.Type = captureType if err := validator.Validate(capture); err != nil { fmt.Println(err.Error()) cmd.Usage() os.Exit(1) } if err := client.Create("capture", &capture); err != nil { logging.GetLogger().Errorf(err.Error()) os.Exit(1) } printJSON(&capture) }, }
func TestFlowGremlin(t *testing.T) { ts := NewTestStorage() aa := helper.NewAgentAnalyzerWithConfig(t, confAgentAnalyzer, ts) aa.Start() defer aa.Stop() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture := api.NewCapture("G.V().Has('Name', 'br-sflow', 'Type', 'ovsbridge')", "") if err := client.Create("capture", capture); err != nil { t.Fatal(err.Error()) } time.Sleep(1 * time.Second) setupCmds := []helper.Cmd{ {"ovs-vsctl add-br br-sflow", true}, {"ovs-vsctl add-port br-sflow sflow-intf1 -- set interface sflow-intf1 type=internal", true}, {"ip address add 169.254.33.33/24 dev sflow-intf1", true}, {"ip link set sflow-intf1 up", true}, {"ping -c 15 -I sflow-intf1 169.254.33.34", false}, } tearDownCmds := []helper.Cmd{ {"ovs-vsctl del-br br-sflow", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) gh := helper.NewGremlinQueryHelper(&http.AuthenticationOpts{}) node := gh.GetNodeFromGremlinReply(t, `g.V().Has("Name", "br-sflow", "Type", "ovsbridge")`) var count int64 gh.GremlinQuery(t, `G.V().Has("Name", "br-sflow", "Type", "ovsbridge").Count()`, &count) if count != 1 { t.Fatalf("Should return 1, got: %d", count) } tid := node.Metadata()["TID"].(string) flows := gh.GetFlowsFromGremlinReply(t, `g.V().Has("Name", "br-sflow", "Type", "ovsbridge").Flows().Has("NodeTID", "`+tid+`")`) if len(flows) == 0 { t.Fatalf("Should return at least 1 flow, got: %v", flows) } flowsOpt := gh.GetFlowsFromGremlinReply(t, `g.V().Has("Name", "br-sflow", "Type", "ovsbridge").Flows().Has("NodeTID", "`+tid+`")`) if len(flowsOpt) != len(flows) { t.Fatalf("Should return the same number of flows that without optimisation, got: %v", flowsOpt) } nodes := gh.GetNodesFromGremlinReply(t, `g.V().Has("Name", "br-sflow", "Type", "ovsbridge").Flows().Has("NodeTID", "`+tid+`").Out()`) if len(nodes) != 0 { t.Fatalf("Should return no destination node, got %d", len(nodes)) } nodes = gh.GetNodesFromGremlinReply(t, `g.V().Has("Name", "br-sflow", "Type", "ovsbridge").Flows().Has("NodeTID", "`+tid+`").Both().Dedup()`) if len(nodes) != 1 { t.Fatalf("Should return one node, got %d", len(nodes)) } nodes = gh.GetNodesFromGremlinReply(t, `g.V().Has("Name", "br-sflow", "Type", "ovsbridge").Flows().Has("NodeTID", "`+tid+`").In().Dedup()`) if len(nodes) != 1 { t.Fatalf("Should return one source node, got %d", len(nodes)) } gh.GremlinQuery(t, `g.V().Has("Name", "br-sflow", "Type", "ovsbridge").Flows().Has("NodeTID", "`+tid+`").Count()`, &count) if int(count) != len(flows) { t.Fatalf("Gremlin count doesn't correspond to the number of flows, got: %v, expected: %v", len(flows), count) } aa.Flush() if len(ts.GetFlows()) != int(count) { t.Fatalf("Should get the same number of flows in the database than previously in the agent, got: %v", ts.GetFlows()) } client.Delete("capture", capture.ID()) }
func TestFlowHops(t *testing.T) { ts := NewTestStorage() aa := helper.NewAgentAnalyzerWithConfig(t, confAgentAnalyzer, ts) aa.Start() defer aa.Stop() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture := api.NewCapture("G.V().Has('Name', 'br-sflow', 'Type', 'ovsbridge')", "") if err := client.Create("capture", capture); err != nil { t.Fatal(err.Error()) } time.Sleep(1 * time.Second) setupCmds := []helper.Cmd{ {"ovs-vsctl add-br br-sflow", true}, {"ovs-vsctl add-port br-sflow sflow-intf1 -- set interface sflow-intf1 type=internal", true}, {"ip netns add sflow-vm1", true}, {"ip link set sflow-intf1 netns sflow-vm1", true}, {"ip netns exec sflow-vm1 ip address add 169.254.33.33/24 dev sflow-intf1", true}, {"ip netns exec sflow-vm1 ip link set sflow-intf1 up", true}, {"ovs-vsctl add-port br-sflow sflow-intf2 -- set interface sflow-intf2 type=internal", true}, {"ip netns add sflow-vm2", true}, {"ip link set sflow-intf2 netns sflow-vm2", true}, {"ip netns exec sflow-vm2 ip address add 169.254.33.34/24 dev sflow-intf2", true}, {"ip netns exec sflow-vm2 ip link set sflow-intf2 up", true}, // wait to have everything ready, sflow, interfaces {"sleep 2", false}, {"ip netns exec sflow-vm1 ping -c 1 -s 1024 -I sflow-intf1 169.254.33.34", false}, } tearDownCmds := []helper.Cmd{ {"ip netns del sflow-vm1", true}, {"ip netns del sflow-vm2", true}, {"ovs-vsctl del-br br-sflow", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) // since the agent update ticker is about 10 sec according to the configuration // wait 11 sec to have the first update and the MetricRange filled time.Sleep(11 * time.Second) gh := helper.NewGremlinQueryHelper(&http.AuthenticationOpts{}) gremlin := `g.Flows().Has("LayersPath", "Ethernet/IPv4/ICMPv4/Payload")` flows := gh.GetFlowsFromGremlinReply(t, gremlin) if len(flows) != 1 { t.Fatal("We should receive only one ICMPv4 flow") } gremlin = fmt.Sprintf(`g.Flows().Has("TrackingID", "%s").Nodes()`, flows[0].TrackingID) tnodes := gh.GetNodesFromGremlinReply(t, gremlin) if len(tnodes) != 3 { t.Fatal("We should have 3 nodes NodeTID,A,B") } gremlin = `g.Flows().Has("LayersPath", "Ethernet/IPv4/ICMPv4/Payload").Hops()` nodes := gh.GetNodesFromGremlinReply(t, gremlin) if len(nodes) != 1 { t.Fatal("We should have 1 node NodeTID") } found := false m := nodes[0].Metadata() for _, n := range tnodes { if n.MatchMetadata(m) == true { found = true break } } if !found { t.Fatal("We should found the Hops nodes in the TrackingID nodes") } client.Delete("capture", capture.ID()) }
func TestSFlowSrcDstPath(t *testing.T) { ts := NewTestStorage() aa := helper.NewAgentAnalyzerWithConfig(t, confAgentAnalyzer, ts) aa.Start() defer aa.Stop() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture := api.NewCapture("G.V().Has('Name', 'br-sflow', 'Type', 'ovsbridge')", "") if err := client.Create("capture", capture); err != nil { t.Fatal(err.Error()) } defer client.Delete("capture", capture.ID()) time.Sleep(1 * time.Second) setupCmds := []helper.Cmd{ {"ovs-vsctl add-br br-sflow", true}, {"ovs-vsctl add-port br-sflow sflow-intf1 -- set interface sflow-intf1 type=internal", true}, {"ip netns add sflow-vm1", true}, {"ip link set sflow-intf1 netns sflow-vm1", true}, {"ip netns exec sflow-vm1 ip address add 169.254.33.33/24 dev sflow-intf1", true}, {"ip netns exec sflow-vm1 ip link set sflow-intf1 up", true}, {"ovs-vsctl add-port br-sflow sflow-intf2 -- set interface sflow-intf2 type=internal", true}, {"ip netns add sflow-vm2", true}, {"ip link set sflow-intf2 netns sflow-vm2", true}, {"ip netns exec sflow-vm2 ip address add 169.254.33.34/24 dev sflow-intf2", true}, {"ip netns exec sflow-vm2 ip link set sflow-intf2 up", true}, {"ip netns exec sflow-vm1 ping -c 25 -I sflow-intf1 169.254.33.34", false}, } tearDownCmds := []helper.Cmd{ {"ip netns del sflow-vm1", true}, {"ip netns del sflow-vm2", true}, {"ovs-vsctl del-br br-sflow", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) aa.Flush() gh := helper.NewGremlinQueryHelper(&http.AuthenticationOpts{}) node1 := gh.GetNodeFromGremlinReply(t, `g.V().Has("Name", "sflow-intf1", "Type", "internal")`) node2 := gh.GetNodeFromGremlinReply(t, `g.V().Has("Name", "sflow-intf2", "Type", "internal")`) if node1 == nil { t.Errorf("Could not find sflow-intf1 internal interface: %s", aa.Analyzer.GraphServer.Graph.String()) return } if node2 == nil { t.Errorf("Could not find sflow-intf2 internal interface: %s", aa.Analyzer.GraphServer.Graph.String()) return } ok := false for _, f := range ts.GetFlows() { // we can have both way depending on which packet has been seen first if (f.ANodeTID == node1.Metadata()["TID"].(string) && f.BNodeTID == node2.Metadata()["TID"].(string)) || (f.ANodeTID == node2.Metadata()["TID"].(string) && f.BNodeTID == node1.Metadata()["TID"].(string)) { ok = true break } } if !ok { t.Errorf("Unable to find flows with the expected path: %v\n %s", ts.GetFlows(), aa.Agent.Graph.String()) } }
func TestTableServer(t *testing.T) { ts := NewTestStorage() aa := helper.NewAgentAnalyzerWithConfig(t, confAgentAnalyzer, ts) fclient := flow.NewTableClient(aa.Analyzer.WSServer) aa.Start() defer aa.Stop() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture := api.NewCapture("G.V().Has('Name', 'br-sflow', 'Type', 'ovsbridge')", "") if err := client.Create("capture", capture); err != nil { t.Fatal(err.Error()) } time.Sleep(1 * time.Second) setupCmds := []helper.Cmd{ {"ovs-vsctl add-br br-sflow", true}, {"ovs-vsctl add-port br-sflow sflow-intf1 -- set interface sflow-intf1 type=internal", true}, {"ip address add 169.254.33.33/24 dev sflow-intf1", true}, {"ip link set sflow-intf1 up", true}, {"ping -c 15 -I sflow-intf1 169.254.33.34", false}, } tearDownCmds := []helper.Cmd{ {"ovs-vsctl del-br br-sflow", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) gh := helper.NewGremlinQueryHelper(&http.AuthenticationOpts{}) node := gh.GetNodeFromGremlinReply(t, `g.V().Has("Name", "br-sflow", "Type", "ovsbridge")`) hnmap := make(topology.HostNodeTIDMap) hnmap[node.Host()] = append(hnmap[node.Host()], node.Metadata()["TID"].(string)) fsq := flow.FlowSearchQuery{} flowset, err := fclient.LookupFlowsByNodes(hnmap, fsq) if err != nil { t.Fatal(err.Error()) } aa.Flush() if len(ts.GetFlows()) != len(flowset.Flows) { t.Fatalf("Should return the same number of flows than in the database, got: %v", flowset) } for _, f := range flowset.Flows { if f.NodeTID != node.Metadata()["TID"].(string) { t.Fatalf("Returned a non expected flow: %v", f) } } client.Delete("capture", capture.ID()) }
func TestSFlowTwoNodeTID(t *testing.T) { ts := NewTestStorage() aa := helper.NewAgentAnalyzerWithConfig(t, confAgentAnalyzer, ts) aa.Start() defer func() { aa.Stop() }() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture1 := api.NewCapture("G.V().Has('Name', 'br-sflow1', 'Type', 'ovsbridge')", "") if err := client.Create("capture", capture1); err != nil { t.Fatal(err.Error()) } capture2 := api.NewCapture("G.V().Has('Name', 'br-sflow2', 'Type', 'ovsbridge')", "") if err := client.Create("capture", capture2); err != nil { t.Fatal(err.Error()) } time.Sleep(5 * time.Second) setupCmds := []helper.Cmd{ {"ovs-vsctl add-br br-sflow1", true}, {"ovs-vsctl add-port br-sflow1 sflow-intf1 -- set interface sflow-intf1 type=internal", true}, {"ip netns add sflow-vm1", true}, {"ip link set sflow-intf1 netns sflow-vm1", true}, {"ip netns exec sflow-vm1 ip address add 169.254.33.33/24 dev sflow-intf1", true}, {"ip netns exec sflow-vm1 ip link set sflow-intf1 up", true}, {"ovs-vsctl add-br br-sflow2", true}, {"ovs-vsctl add-port br-sflow2 sflow-intf2 -- set interface sflow-intf2 type=internal", true}, {"ip netns add sflow-vm2", true}, {"ip link set sflow-intf2 netns sflow-vm2", true}, {"ip netns exec sflow-vm2 ip address add 169.254.33.34/24 dev sflow-intf2", true}, {"ip netns exec sflow-vm2 ip link set sflow-intf2 up", true}, // interfaces used to link br-sflow1 and br-sflow2 without a patch {"ovs-vsctl add-port br-sflow1 sflow-link1 -- set interface sflow-link1 type=internal", true}, {"ip link set sflow-link1 up", true}, {"ovs-vsctl add-port br-sflow2 sflow-link2 -- set interface sflow-link2 type=internal", true}, {"ip link set sflow-link2 up", true}, {"brctl addbr br-link", true}, {"ip link set br-link up", true}, {"brctl addif br-link sflow-link1", true}, {"brctl addif br-link sflow-link2", true}, {"ip netns exec sflow-vm2 ping -c 15 -I sflow-intf2 169.254.33.33", false}, } tearDownCmds := []helper.Cmd{ {"ip netns del sflow-vm1", true}, {"ip netns del sflow-vm2", true}, {"ovs-vsctl del-br br-sflow1", true}, {"ovs-vsctl del-br br-sflow2", true}, {"sleep 1", true}, {"ip link set br-link down", true}, {"brctl delbr br-link", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) aa.Flush() flows := []*flow.Flow{} for _, f := range ts.GetFlows() { if f.LayersPath == "Ethernet/IPv4/ICMPv4/Payload" { flows = append(flows, f) } } if len(flows) != 2 { t.Errorf("Should have 2 flow entries one per NodeTID got: %d", len(flows)) } gh := helper.NewGremlinQueryHelper(&http.AuthenticationOpts{}) node1 := gh.GetNodeFromGremlinReply(t, `g.V().Has("Name", "br-sflow1", "Type", "ovsbridge")`) node2 := gh.GetNodeFromGremlinReply(t, `g.V().Has("Name", "br-sflow2", "Type", "ovsbridge")`) if flows[0].NodeTID != node1.Metadata()["TID"].(string) && flows[0].NodeTID != node2.Metadata()["TID"].(string) { t.Errorf("Bad NodeTID for the first flow: %s", flows[0].NodeTID) } if flows[1].NodeTID != node1.Metadata()["TID"].(string) && flows[1].NodeTID != node2.Metadata()["TID"].(string) { t.Errorf("Bad NodeTID for the second flow: %s", flows[1].NodeTID) } if flows[0].TrackingID != flows[1].TrackingID { t.Errorf("Both flows should have the same TrackingID: %v", flows) } if flows[0].UUID == flows[1].UUID { t.Errorf("Both flows should have different UUID: %v", flows) } client.Delete("capture", capture1.ID()) client.Delete("capture", capture2.ID()) }
func TestPCAPProbeTLS(t *testing.T) { ts := NewTestStorage() params := []helper.HelperParams{make(helper.HelperParams)} cert, key := helper.GenerateFakeX509Certificate("server") params[0]["AnalyzerX509_cert"] = cert params[0]["AnalyzerX509_key"] = key cert, key = helper.GenerateFakeX509Certificate("client") params[0]["AgentX509_cert"] = cert params[0]["AgentX509_key"] = key aa := helper.NewAgentAnalyzerWithConfig(t, confAgentAnalyzer, ts, params...) aa.Start() defer aa.Stop() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture := api.NewCapture("G.V().Has('Name', 'br-pcap', 'Type', 'bridge')", "") if err := client.Create("capture", capture); err != nil { t.Fatal(err.Error()) } time.Sleep(1 * time.Second) setupCmds := []helper.Cmd{ {"brctl addbr br-pcap", true}, {"ip link set br-pcap up", true}, {"ip netns add vm1", true}, {"ip link add name vm1-eth0 type veth peer name eth0 netns vm1", true}, {"ip link set vm1-eth0 up", true}, {"ip netns exec vm1 ip link set eth0 up", true}, {"ip netns exec vm1 ip address add 169.254.66.66/24 dev eth0", true}, {"brctl addif br-pcap vm1-eth0", true}, {"ip netns add vm2", true}, {"ip link add name vm2-eth0 type veth peer name eth0 netns vm2", true}, {"ip link set vm2-eth0 up", true}, {"ip netns exec vm2 ip link set eth0 up", true}, {"ip netns exec vm2 ip address add 169.254.66.67/24 dev eth0", true}, {"brctl addif br-pcap vm2-eth0", true}, {"ip netns exec vm1 ping -c 15 169.254.66.67", false}, } tearDownCmds := []helper.Cmd{ {"ip link set br-pcap down", true}, {"brctl delbr br-pcap", true}, {"ip link del vm1-eth0", true}, {"ip link del vm2-eth0", true}, {"ip netns del vm1", true}, {"ip netns del vm2", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) aa.Flush() gh := helper.NewGremlinQueryHelper(&http.AuthenticationOpts{}) node := gh.GetNodeFromGremlinReply(t, `g.V().Has("Name", "br-pcap", "Type", "bridge")`) ok := false flows := ts.GetFlows() for _, f := range flows { if f.NodeTID == node.Metadata()["TID"].(string) { ok = true break } } if !ok { t.Errorf("Unable to find a flow with the expected NodeTID: %v\n%v", flows, aa.Agent.Graph.String()) } client.Delete("capture", capture.ID()) }
func TestFlowStorage(t *testing.T) { aa := helper.NewAgentAnalyzerWithConfig(t, confStorage, nil) aa.Start() defer aa.Stop() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture := api.NewCapture("G.V().Has('Name', 'br-sflow', 'Type', 'ovsbridge')", "") if err := client.Create("capture", capture); err != nil { t.Fatal(err.Error()) } time.Sleep(3 * time.Second) now := time.Now() setupCmds := []helper.Cmd{ {"ovs-vsctl add-br br-sflow", true}, {"ovs-vsctl add-port br-sflow sflow-intf1 -- set interface sflow-intf1 type=internal", true}, {"ip netns add sflow-vm1", true}, {"ip link set sflow-intf1 netns sflow-vm1", true}, {"ip netns exec sflow-vm1 ip address add 169.254.33.33/24 dev sflow-intf1", true}, {"ip netns exec sflow-vm1 ip link set sflow-intf1 up", true}, {"ovs-vsctl add-port br-sflow sflow-intf2 -- set interface sflow-intf2 type=internal", true}, {"ip netns add sflow-vm2", true}, {"ip link set sflow-intf2 netns sflow-vm2", true}, {"ip netns exec sflow-vm2 ip address add 169.254.33.34/24 dev sflow-intf2", true}, {"ip netns exec sflow-vm2 ip link set sflow-intf2 up", true}, // wait to have everything ready, sflow, interfaces {"sleep 2", false}, {"ip netns exec sflow-vm1 ping -c 10 -s 1024 -I sflow-intf1 169.254.33.34", false}, } tearDownCmds := []helper.Cmd{ {"ip netns del sflow-vm1", true}, {"ip netns del sflow-vm2", true}, {"ovs-vsctl del-br br-sflow", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) time.Sleep(time.Second) aa.Flush() // Wait for the flows to be indexed in elasticsearch time.Sleep(3 * time.Second) gh := helper.NewGremlinQueryHelper(&http.AuthenticationOpts{}) node := gh.GetNodeFromGremlinReply(t, `g.V().Has("Name", "br-sflow", "Type", "ovsbridge")`) id := string(node.ID) filters := flow.NewFilterForNodes([]string{id}) flows, err := aa.Analyzer.Storage.SearchFlows(filters, nil) if err != nil { t.Fatalf("Failed to query flows: %s", err.Error()) } if len(flows) == 0 { t.Fatalf("SearchFlows should return at least one flow, got %d", len(flows)) } queryFlowMetrics(t, now.Add(5*time.Second).Unix(), 10) client.Delete("capture", capture.ID()) }
func TestIPv6FlowGRETunnelIPv6(t *testing.T) { t.Skip("Fedora seems didn't support ip6gre tunnel for the moment") if !common.IPv6Supported() { t.Skipf("Platform doesn't support IPv6") } ts := NewTestStorage() aa := helper.NewAgentAnalyzerWithConfig(t, confAgentAnalyzerIPv6, ts) aa.Start() defer aa.Stop() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture1 := api.NewCapture("G.V().Has('Name', 'gre-vm1').Out().Has('Name', 'gre')", "") if err := client.Create("capture", capture1); err != nil { t.Fatal(err.Error()) } capture2 := api.NewCapture("G.V().Has('Name', 'gre-vm2-eth0')", "") if err := client.Create("capture", capture2); err != nil { t.Fatal(err.Error()) } time.Sleep(1 * time.Second) setupCmds := []helper.Cmd{ {"sudo ovs-vsctl add-br br-gre", true}, {"sudo ip netns add gre-vm1", true}, {"sudo ip link add gre-vm1-eth0 type veth peer name eth0 netns gre-vm1", true}, {"sudo ip link set gre-vm1-eth0 up", true}, {"sudo ip netns exec gre-vm1 ip link set eth0 up", true}, {"sudo ip netns exec gre-vm1 ip address add fd49:37c8:5229::1/48 dev eth0", true}, {"sudo ip netns add gre-vm2", true}, {"sudo ip link add gre-vm2-eth0 type veth peer name eth0 netns gre-vm2", true}, {"sudo ip link set gre-vm2-eth0 up", true}, {"sudo ip netns exec gre-vm2 ip link set eth0 up", true}, {"sudo ip netns exec gre-vm2 ip address add fd49:37c8:5229::2/48 dev eth0", true}, {"sudo ovs-vsctl add-port br-gre gre-vm1-eth0", true}, {"sudo ovs-vsctl add-port br-gre gre-vm2-eth0", true}, {"sudo ip netns exec gre-vm1 ip tunnel add gre mode ip6gre remote fd49:37c8:5229::2/48 local fd49:37c8:5229::1/48 ttl 255", true}, {"sudo ip netns exec gre-vm1 ip l set gre up", true}, {"sudo ip netns exec gre-vm1 ip link add name dummy0 type dummy", true}, {"sudo ip netns exec gre-vm1 ip l set dummy0 up", true}, {"sudo ip netns exec gre-vm1 ip a add fdfe:38b:489c::1/128 dev dummy0", true}, {"sudo ip netns exec gre-vm1 ip r add fdfe:38b:489c::/48 dev gre", true}, {"sudo ip netns exec gre-vm2 ip tunnel add gre mode ip6gre remote fd49:37c8:5229::1/48 local fd49:37c8:5229::2/48 ttl 255", true}, {"sudo ip netns exec gre-vm2 ip l set gre up", true}, {"sudo ip netns exec gre-vm2 ip link add name dummy0 type dummy", true}, {"sudo ip netns exec gre-vm2 ip l set dummy0 up", true}, {"sudo ip netns exec gre-vm2 ip a add fdfe:38b:489c::2/128 dev dummy0", true}, {"sudo ip netns exec gre-vm2 ip r add fdfe:38b:489c::/48 dev gre", true}, {"sudo ip netns exec gre-vm1 ping6 -c 5 -I fdfe:38b:489c::1 fdfe:38b:489c::2", false}, } tearDownCmds := []helper.Cmd{ {"ip netns del gre-vm1", true}, {"ip netns del gre-vm2", true}, {"ovs-vsctl del-br br-gre", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) gh := helper.NewGremlinQueryHelper(&http.AuthenticationOpts{}) flowsInnerTunnel := gh.GetFlowsFromGremlinReply(t, `G.V().Has('Name', 'gre-vm1').Out().Has('Name', 'gre').Flows()`) flowsBridge := gh.GetFlowsFromGremlinReply(t, `G.V().Has('Name', 'gre-vm2-eth0').Flows()`) var TrackID string for _, flow := range flowsInnerTunnel { if flow.LayersPath == "IPv6/ICMPv6/Payload" { if TrackID != "" { t.Errorf("We should only found one ICMPv6 flow in the tunnel %v", flowsInnerTunnel) } TrackID = flow.TrackingID } } success := false for _, f := range flowsBridge { if TrackID == f.TrackingID && strings.HasSuffix(f.LayersPath, "ICMPv6/Payload") && f.Network != nil && f.Network.Protocol == flow.FlowProtocol_IPV6 { success = true break } } if !success { t.Errorf("TrackingID not found in GRE tunnel: %v == %v", flowsInnerTunnel, flowsBridge) } client.Delete("capture", capture1.ID()) client.Delete("capture", capture2.ID()) }
func TestFlowGRETunnelPCAPLegacy(t *testing.T) { ts := NewTestStorage() aa := helper.NewAgentAnalyzerWithConfig(t, confAgentAnalyzer, ts) aa.Start() defer aa.Stop() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture1 := api.NewCapture("G.V().Has('Name', 'gre-vm1').Out().Has('Name', 'gre')", "") if err := client.Create("capture", capture1); err != nil { t.Fatal(err.Error()) } capture1.Type = "pcap" capture2 := api.NewCapture("G.V().Has('Name', 'gre-vm2-eth0')", "") if err := client.Create("capture", capture2); err != nil { t.Fatal(err.Error()) } capture2.Type = "pcap" time.Sleep(1 * time.Second) setupCmds := []helper.Cmd{ {"sudo ovs-vsctl add-br br-gre", true}, {"sudo ip netns add gre-vm1", true}, {"sudo ip link add gre-vm1-eth0 type veth peer name eth0 netns gre-vm1", true}, {"sudo ip link set gre-vm1-eth0 up", true}, {"sudo ip netns exec gre-vm1 ip link set eth0 up", true}, {"sudo ip netns exec gre-vm1 ip address add 172.16.0.1/24 dev eth0", true}, {"sudo ip netns add gre-vm2", true}, {"sudo ip link add gre-vm2-eth0 type veth peer name eth0 netns gre-vm2", true}, {"sudo ip link set gre-vm2-eth0 up", true}, {"sudo ip netns exec gre-vm2 ip link set eth0 up", true}, {"sudo ip netns exec gre-vm2 ip address add 172.16.0.2/24 dev eth0", true}, {"sudo ovs-vsctl add-port br-gre gre-vm1-eth0", true}, {"sudo ovs-vsctl add-port br-gre gre-vm2-eth0", true}, {"sudo ip netns exec gre-vm1 ip tunnel add gre mode gre remote 172.16.0.2 local 172.16.0.1 ttl 255", true}, {"sudo ip netns exec gre-vm1 ip l set gre up", true}, {"sudo ip netns exec gre-vm1 ip link add name dummy0 type dummy", true}, {"sudo ip netns exec gre-vm1 ip l set dummy0 up", true}, {"sudo ip netns exec gre-vm1 ip a add 192.168.0.1/32 dev dummy0", true}, {"sudo ip netns exec gre-vm1 ip r add 192.168.0.0/24 dev gre", true}, {"sudo ip netns exec gre-vm2 ip tunnel add gre mode gre remote 172.16.0.1 local 172.16.0.2 ttl 255", true}, {"sudo ip netns exec gre-vm2 ip l set gre up", true}, {"sudo ip netns exec gre-vm2 ip link add name dummy0 type dummy", true}, {"sudo ip netns exec gre-vm2 ip l set dummy0 up", true}, {"sudo ip netns exec gre-vm2 ip a add 192.168.0.2/32 dev dummy0", true}, {"sudo ip netns exec gre-vm2 ip r add 192.168.0.0/24 dev gre", true}, {"sleep 10", false}, {"sudo ip netns exec gre-vm1 ping -c 10 -I 192.168.0.1 192.168.0.2", false}, } tearDownCmds := []helper.Cmd{ {"ip netns del gre-vm1", true}, {"ip netns del gre-vm2", true}, {"ovs-vsctl del-br br-gre", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) gh := helper.NewGremlinQueryHelper(&http.AuthenticationOpts{}) flowsInnerTunnel := gh.GetFlowsFromGremlinReply(t, `G.V().Has('Name', 'gre-vm1').Out().Has('Name', 'gre').Flows()`) flowsBridge := gh.GetFlowsFromGremlinReply(t, `G.V().Has('Name', 'gre-vm2-eth0').Flows()`) var TrackID string for _, flow := range flowsInnerTunnel { if flow.LayersPath == "IPv4/ICMPv4/Payload" { if TrackID != "" { t.Errorf("We should only found one ICMPv4 flow in the tunnel %v", flowsInnerTunnel) } TrackID = flow.TrackingID } } success := false for _, f := range flowsBridge { if TrackID == f.TrackingID && strings.Contains(f.LayersPath, "ICMPv4/Payload") && f.Network != nil && f.Network.Protocol == flow.FlowProtocol_IPV4 { success = true break } } if !success { t.Errorf("TrackingID not found in GRE tunnel: %v == %v", flowsInnerTunnel, flowsBridge) } client.Delete("capture", capture1.ID()) client.Delete("capture", capture2.ID()) }
// tunnelType is gre or vxlan func testFlowTunnel(t *testing.T, tunnelType string) { ts := NewTestStorage() aa := helper.NewAgentAnalyzerWithConfig(t, confAgentAnalyzer, ts) aa.Start() defer aa.Stop() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture1 := api.NewCapture("G.V().Has('Name', 'tunnel-vm1').Out().Has('Name', 'tunnel')", "") if err := client.Create("capture", capture1); err != nil { t.Fatal(err.Error()) } capture2 := api.NewCapture("G.V().Has('Name', 'tunnel-vm2-eth0')", "") if err := client.Create("capture", capture2); err != nil { t.Fatal(err.Error()) } time.Sleep(1 * time.Second) setupCmds := []helper.Cmd{ {"sudo ovs-vsctl add-br br-tunnel", true}, {"sudo ip netns add tunnel-vm1", true}, {"sudo ip link add tunnel-vm1-eth0 type veth peer name eth0 netns tunnel-vm1", true}, {"sudo ip link set tunnel-vm1-eth0 up", true}, {"sudo ip netns exec tunnel-vm1 ip link set eth0 up", true}, {"sudo ip netns exec tunnel-vm1 ip address add 172.16.0.1/24 dev eth0", true}, {"sudo ip netns add tunnel-vm2", true}, {"sudo ip link add tunnel-vm2-eth0 type veth peer name eth0 netns tunnel-vm2", true}, {"sudo ip link set tunnel-vm2-eth0 up", true}, {"sudo ip netns exec tunnel-vm2 ip link set eth0 up", true}, {"sudo ip netns exec tunnel-vm2 ip address add 172.16.0.2/24 dev eth0", true}, {"sudo ovs-vsctl add-port br-tunnel tunnel-vm1-eth0", true}, {"sudo ovs-vsctl add-port br-tunnel tunnel-vm2-eth0", true}} tunnelAdd := "" if tunnelType == "gre" { tunnelAdd = "sudo ip netns exec tunnel-vm1 ip tunnel add tunnel mode gre remote 172.16.0.2 local 172.16.0.1 ttl 255" } else { tunnelAdd = "sudo ip netns exec tunnel-vm1 ip link add tunnel type vxlan id 10 remote 172.16.0.2 local 172.16.0.1 ttl 255 dev eth0 dstport 4789" } setupCmds = append(setupCmds, []helper.Cmd{ {tunnelAdd, true}, {"sudo ip netns exec tunnel-vm1 ip link set tunnel up", true}, {"sudo ip netns exec tunnel-vm1 ip link add name dummy0 type dummy", true}, {"sudo ip netns exec tunnel-vm1 ip link set dummy0 up", true}, {"sudo ip netns exec tunnel-vm1 ip a add 192.168.0.1/32 dev dummy0", true}, {"sudo ip netns exec tunnel-vm1 ip r add 192.168.0.0/24 dev tunnel", true}}...) if tunnelType == "gre" { tunnelAdd = "sudo ip netns exec tunnel-vm2 ip tunnel add tunnel mode gre remote 172.16.0.1 local 172.16.0.2 ttl 255" } else { tunnelAdd = "sudo ip netns exec tunnel-vm2 ip link add tunnel type vxlan id 10 remote 172.16.0.1 local 172.16.0.2 ttl 255 dev eth0 dstport 4789" } setupCmds = append(setupCmds, []helper.Cmd{ {tunnelAdd, true}, {"sudo ip netns exec tunnel-vm2 ip link set tunnel up", true}, {"sudo ip netns exec tunnel-vm2 ip link add name dummy0 type dummy", true}, {"sudo ip netns exec tunnel-vm2 ip link set dummy0 up", true}, {"sudo ip netns exec tunnel-vm2 ip a add 192.168.0.2/32 dev dummy0", true}, {"sudo ip netns exec tunnel-vm2 ip r add 192.168.0.0/24 dev tunnel", true}, {"sleep 10", false}, {"sudo ip netns exec tunnel-vm1 ping -c 5 -I 192.168.0.1 192.168.0.2", false}}...) tearDownCmds := []helper.Cmd{ {"ip netns del tunnel-vm1", true}, {"ip netns del tunnel-vm2", true}, {"ovs-vsctl del-br br-tunnel", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) gh := helper.NewGremlinQueryHelper(&http.AuthenticationOpts{}) flowsInnerTunnel := gh.GetFlowsFromGremlinReply(t, `G.V().Has('Name', 'tunnel-vm1').Out().Has('Name', 'tunnel').Flows()`) flowsBridge := gh.GetFlowsFromGremlinReply(t, `G.V().Has('Name', 'tunnel-vm2-eth0').Flows()`) var TrackID string for _, flow := range flowsInnerTunnel { // A vxlan innerpacket contains Ethernet while gre // innerpacket does not if strings.Contains(flow.LayersPath, "IPv4/ICMPv4/Payload") { if TrackID != "" { t.Errorf("We should only found one ICMPv4 flow in the tunnel %v", flowsInnerTunnel) } TrackID = flow.TrackingID } } success := false for _, f := range flowsBridge { if TrackID == f.TrackingID && strings.Contains(f.LayersPath, "ICMPv4/Payload") && f.Network != nil && f.Network.Protocol == flow.FlowProtocol_IPV4 { success = true break } } if !success { t.Errorf("TrackingID not found in %s tunnel: leaving the interface(%v) == seen in the tunnel(%v)", tunnelType, flowsInnerTunnel, flowsBridge) } client.Delete("capture", capture1.ID()) client.Delete("capture", capture2.ID()) }
func TestIPv6FlowHopsIPv6(t *testing.T) { if !common.IPv6Supported() { t.Skipf("Platform doesn't support IPv6") } ts := NewTestStorage() aa := helper.NewAgentAnalyzerWithConfig(t, confAgentAnalyzerIPv6, ts) aa.Start() defer aa.Stop() client, err := api.NewCrudClientFromConfig(&http.AuthenticationOpts{}) if err != nil { t.Fatal(err.Error()) } capture := api.NewCapture("G.V().Has('Name', 'br-sflow', 'Type', 'ovsbridge')", "") if err := client.Create("capture", capture); err != nil { t.Fatal(err.Error()) } time.Sleep(1 * time.Second) setupCmds := []helper.Cmd{ {"ovs-vsctl add-br br-sflow", true}, {"ovs-vsctl add-port br-sflow sflow-intf1 -- set interface sflow-intf1 type=internal", true}, {"ip netns add sflow-vm1", true}, {"ip link set sflow-intf1 netns sflow-vm1", true}, {"ip netns exec sflow-vm1 ip address add fd49:37c8:5229::1/48 dev sflow-intf1", true}, {"ip netns exec sflow-vm1 ip link set sflow-intf1 up", true}, {"ovs-vsctl add-port br-sflow sflow-intf2 -- set interface sflow-intf2 type=internal", true}, {"ip netns add sflow-vm2", true}, {"ip link set sflow-intf2 netns sflow-vm2", true}, {"ip netns exec sflow-vm2 ip address add fd49:37c8:5229::2/48 dev sflow-intf2", true}, {"ip netns exec sflow-vm2 ip link set sflow-intf2 up", true}, // wait to have everything ready, sflow, interfaces {"sleep 2", false}, {"ip netns exec sflow-vm1 ping6 -c 5 -s 1024 -I sflow-intf1 fd49:37c8:5229::2", true}, } tearDownCmds := []helper.Cmd{ {"ip netns del sflow-vm1", true}, {"ip netns del sflow-vm2", true}, {"ovs-vsctl del-br br-sflow", true}, } helper.ExecCmds(t, setupCmds...) defer helper.ExecCmds(t, tearDownCmds...) // since the agent update ticker is about 10 sec according to the configuration // wait 11 sec to have the first update and the MetricRange filled time.Sleep(11 * time.Second) gh := helper.NewGremlinQueryHelper(&http.AuthenticationOpts{}) gremlin := `g.Flows().Has("LayersPath", "Ethernet/IPv6/ICMPv6/Payload")` /* filterIPv6AddrAnd() as we received multicast/broadcast from fresh registered interfances announcement */ allFlows := gh.GetFlowsFromGremlinReply(t, gremlin) flows := helper.FilterIPv6AddrAnd(allFlows, "fd49:37c8:5229::1", "fd49:37c8:5229::2") if len(flows) != 1 { t.Fatal("We should receive only one ICMPv6 flow") } gremlin = fmt.Sprintf(`g.Flows().Has("TrackingID", "%s").Nodes()`, flows[0].TrackingID) tnodes := gh.GetNodesFromGremlinReply(t, gremlin) if len(tnodes) != 3 { t.Fatal("We should have 3 nodes NodeTID,A,B") } /* Dedup() here for same reason than above ^^^ */ gremlin = `g.Flows().Has("LayersPath", "Ethernet/IPv6/ICMPv6/Payload").Hops().Dedup()` nodes := gh.GetNodesFromGremlinReply(t, gremlin) if len(nodes) != 1 { t.Fatal("We should have 1 node NodeTID") } found := false m := nodes[0].Metadata() for _, n := range tnodes { if n.MatchMetadata(m) == true { found = true break } } if !found { t.Fatal("We should found the Hops nodes in the TrackingID nodes") } client.Delete("capture", capture.ID()) }