diff --git a/cmd/display.go b/cmd/display.go index 56018220..90e743d3 100644 --- a/cmd/display.go +++ b/cmd/display.go @@ -179,6 +179,25 @@ func hearbeat() { } } +func backgroundHearbeat() { + for { + if captureEnded { + return + } + + if capture != Metric { + updateTableAndSuggestions() + // simply print flow into logs + rows := getTableRows() + for _, row := range rows { + log.Println(row) + } + } + + time.Sleep(5 * time.Second) + } +} + func pause(pause bool) { paused = pause playPauseButton.SetLabel(getPlayPauseText()) diff --git a/cmd/flow_capture.go b/cmd/flow_capture.go index 92917943..935fd658 100644 --- a/cmd/flow_capture.go +++ b/cmd/flow_capture.go @@ -24,8 +24,14 @@ var flowCmd = &cobra.Command{ func runFlowCapture(_ *cobra.Command, _ []string) { capture = Flow - go startFlowCollector() - createFlowDisplay() + showCount = defaultFlowShowCount + if isBackground { + go backgroundHearbeat() // show table periodically in background + startFlowCollector() + } else { + go startFlowCollector() + createFlowDisplay() + } } func startFlowCollector() { diff --git a/cmd/flow_display.go b/cmd/flow_display.go index 13987cc5..87047cb6 100644 --- a/cmd/flow_display.go +++ b/cmd/flow_display.go @@ -56,7 +56,6 @@ var ( func createFlowDisplay() { focus = "inputField" - showCount = defaultFlowShowCount app = tview.NewApplication(). SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { //nolint:exhaustive @@ -460,33 +459,28 @@ func AppendFlow(genericMap config.GenericMap) { return } - if errAdvancedDisplay != nil { - // simply print flow into logs - log.Printf("%v\n", genericMap) - } else { - // lock since we are updating lastFlows concurrently - mutex.Lock() - - // add new flow to the array - genericMap["Index"] = flowIndex - flowIndex++ - lastFlows = append(lastFlows, genericMap) - - // sort flows according to time - sort.Slice(lastFlows, func(i, j int) bool { - if capture == Flow { - return toFloat64(lastFlows[i], "TimeFlowEndMs") < toFloat64(lastFlows[j], "TimeFlowEndMs") - } - return toFloat64(lastFlows[i], "Time") < toFloat64(lastFlows[j], "Time") - }) + // lock since we are updating lastFlows concurrently + mutex.Lock() - // limit flows kept in memory - if len(lastFlows) > keepCount { - lastFlows = lastFlows[len(lastFlows)-keepCount:] + // add new flow to the array + genericMap["Index"] = flowIndex + flowIndex++ + lastFlows = append(lastFlows, genericMap) + + // sort flows according to time + sort.Slice(lastFlows, func(i, j int) bool { + if capture == Flow { + return toFloat64(lastFlows[i], "TimeFlowEndMs") < toFloat64(lastFlows[j], "TimeFlowEndMs") } + return toFloat64(lastFlows[i], "Time") < toFloat64(lastFlows[j], "Time") + }) - mutex.Unlock() + // limit flows kept in memory + if len(lastFlows) > keepCount { + lastFlows = lastFlows[len(lastFlows)-keepCount:] } + + mutex.Unlock() } func updateDisplayEnrichmentTexts() { @@ -599,6 +593,23 @@ func getFlows() []config.GenericMap { return flows } +func getTableRows() []string { + arr := []string{} + if len(tableData.cols) == 0 || len(tableData.flows) == 0 { + return arr + } + + for i := range len(tableData.flows) + 1 { + str := "" + for j := range tableData.cols { + str += tableData.GetCell(i, j).Text + } + arr = append(arr, str) + } + + return arr +} + func updateTableAndSuggestions() { // update tableData tableData.cols = getCols() diff --git a/cmd/metric_capture.go b/cmd/metric_capture.go index f880fbf8..f9a7de24 100644 --- a/cmd/metric_capture.go +++ b/cmd/metric_capture.go @@ -31,8 +31,14 @@ var ( func runMetricCapture(c *cobra.Command, _ []string) { capture = Metric - go startMetricCollector(c.Context()) - createMetricDisplay() + + updateGraphs(false) // initial update of graphs to have something to display + if isBackground { + startMetricCollector(c.Context()) + } else { + go startMetricCollector(c.Context()) + createMetricDisplay() + } } func startMetricCollector(ctx context.Context) { @@ -80,15 +86,26 @@ func startMetricCollector(ctx context.Context) { func queryGraphs(ctx context.Context, client api.Client) { for index := range graphs { - go queryGraph(ctx, client, index) + if isBackground { + queryGraph(ctx, client, index) // keep logical order for background mode + } else { + go queryGraph(ctx, client, index) + } } } func queryGraph(ctx context.Context, client api.Client, index int) { query, result := queryProm(ctx, client, graphs[index].Query.PromQL) - if errAdvancedDisplay != nil { + if app == nil || errAdvancedDisplay != nil { // simply print metrics into logs - log.Printf("%v\n", result) + log.Print(query.PromQL) + if result == nil || len(*result) == 0 { + log.Print(" No result") + } else { + for _, stream := range *result { + log.Printf(" %s", stream.String()) + } + } } else { appendMetrics(query, result, index) } diff --git a/cmd/metric_display.go b/cmd/metric_display.go index f3639b65..44c77ea5 100644 --- a/cmd/metric_display.go +++ b/cmd/metric_display.go @@ -66,7 +66,6 @@ var ( func createMetricDisplay() { updateShowMetricCount() - updateGraphs(false) app = tview.NewApplication(). SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { diff --git a/cmd/packet_capture.go b/cmd/packet_capture.go index 7be3f9a5..9f074ef5 100644 --- a/cmd/packet_capture.go +++ b/cmd/packet_capture.go @@ -35,8 +35,13 @@ var ( func runPacketCapture(_ *cobra.Command, _ []string) { capture = Packet - go startPacketCollector() - createFlowDisplay() + if isBackground { + go backgroundHearbeat() // show table periodically in background + startPacketCollector() + } else { + go startPacketCollector() + createFlowDisplay() + } } //nolint:cyclop diff --git a/cmd/root.go b/cmd/root.go index cc3a0b20..1463c92f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -52,6 +52,7 @@ var ( captureEnded = false stopReceived = false useMocks = false + isBackground = false ) // Execute executes the root command. @@ -102,7 +103,10 @@ func onInit() { printBanner() log.Infof("Log level: %s\nOption(s): %s", logLevel, options) - + if strings.Contains(options, "background") && !strings.Contains(options, "background=false") { + isBackground = true + log.Infof("Running in background mode") + } showKernelVersion() if useMocks { @@ -143,7 +147,7 @@ func onLimitReached() bool { if app != nil && errAdvancedDisplay == nil { app.Stop() } - if strings.Contains(options, "background=true") { + if isBackground { out, err := exec.Command("/oc-netobserv", "stop").Output() if err != nil { log.Fatal(err) diff --git a/cmd/root_test.go b/cmd/root_test.go index 9fda3d2c..359cac70 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -94,23 +94,6 @@ func setup(t *testing.T) { assert.Equal(t, nil, err) } -func getTableRows() []string { - arr := []string{} - if len(tableData.cols) == 0 || len(tableData.flows) == 0 { - return arr - } - - for i := range len(tableData.flows) + 1 { - str := "" - for j := range tableData.cols { - str += tableData.GetCell(i, j).Text - } - arr = append(arr, str) - } - - return arr -} - func resetTime() { // set timezone to Paris time for all tests loc, err := time.LoadLocation("Europe/Paris") diff --git a/docs/netobserv_cli.adoc b/docs/netobserv_cli.adoc index 4f3e37d5..55ef4bb4 100644 --- a/docs/netobserv_cli.adoc +++ b/docs/netobserv_cli.adoc @@ -64,6 +64,7 @@ $ oc netobserv flows [] [] |--enable_rtt| enable RTT tracking | false |--enable_udn_mapping| enable User Defined Network mapping | false |--get-subnets| get subnets information | false +|--privileged| force eBPF agent privileged mode | auto |--sampling| packets sampling interval | 1 |--background| run in background | false |--copy| copy the output files locally | prompt @@ -167,7 +168,10 @@ $ oc netobserv metrics [