Repository: NetApp/harvest
Pull request #1511 (2022-11-18 07:56:10)

docs: docker upgrade instructions
Pulled latest nightly and tried to see if commands in doc is working. Reference https://stackoverflow.com/a/56687449

Restart is not upgrading containers to latest image

<img width="813" alt="image" src="https://user-images.githubusercontent.com/25551691/202649983-eacf6101-12ab-4177-b5be-37a78ad1f2e7.png">


simply up does

<img width="904" alt="image" src="https://user-images.githubusercontent.com/25551691/202650041-deb20b05-751c-40fe-8f3e-c85eeea736f9.png">



Patch:
diff --git a/cmd/collectors/restperf/restperf.go b/cmd/collectors/restperf/restperf.go
index 5a40fe3b..35c4b81a 100644
--- a/cmd/collectors/restperf/restperf.go
+++ b/cmd/collectors/restperf/restperf.go
@@ -25,6 +25,7 @@ import (
 const (
 	latencyIoReqd = 10
 	BILLION       = 1_000_000_000
+	arrayKeyToken = "#"
 )
 
 var qosQuery = "api/cluster/counter/tables/qos"
@@ -312,18 +313,59 @@ func arrayMetricToString(value string) string {
 }
 
 func parseMetricResponse(instanceData gjson.Result, metric string) *metricResponse {
-	t := gjson.Get(instanceData.String(), "counters.#.name")
-
+	instanceDataS := instanceData.String()
+	t := gjson.Get(instanceDataS, "counters.#.name")
 	for _, name := range t.Array() {
 		if name.String() == metric {
-			value := gjson.Get(instanceData.String(), "counters.#(name="+metric+").value")
+			metricPath := "counters.#(name=" + metric + ")"
+			many := gjson.GetMany(instanceDataS,
+				metricPath+".value",
+				metricPath+".values",
+				metricPath+".labels",
+				metricPath+".counters.#.label",
+				metricPath+".counters.#.values",
+			)
+			value := many[0]
+			values := many[1]
+			labels := many[2]
+			subLabels := many[3]
+			subValues := many[4]
 			if value.String() != "" {
 				return &metricResponse{value: value.String(), label: "", isArray: false}
 			}
-			values := gjson.Get(instanceData.String(), "counters.#(name="+metric+").values")
 			if values.String() != "" {
-				label := gjson.Get(instanceData.String(), "counters.#(name="+metric+").labels")
-				return &metricResponse{value: arrayMetricToString(values.String()), label: arrayMetricToString(label.String()), isArray: true}
+				return &metricResponse{
+					value: arrayMetricToString(values.String()),
+					label: arrayMetricToString(labels.String()), isArray: true,
+				}
+			}
+
+			// check for sub metrics
+			if subLabels.String() != "" {
+				var finalLabels []string
+				var finalValues []string
+				subLabelsS := labels.String()
+				subLabelsS = arrayMetricToString(subLabelsS)
+				subLabelSlice := strings.Split(subLabelsS, ",")
+				ls := subLabels.Array()
+				vs := subValues.Array()
+				var vLen int
+				for i, v := range vs {
+					label := ls[i].String()
+					m := arrayMetricToString(v.String())
+					ms := strings.Split(m, ",")
+					for range ms {
+						finalLabels = append(finalLabels, label+arrayKeyToken+subLabelSlice[vLen])
+						vLen += 1
+					}
+					if vLen > len(subLabelSlice) {
+						break
+					}
+					finalValues = append(finalValues, ms...)
+				}
+				if vLen == len(subLabelSlice) {
+					return &metricResponse{value: strings.Join(finalValues, ","), label: strings.Join(finalLabels, ","), isArray: true}
+				}
 			}
 		}
 	}
@@ -660,7 +702,7 @@ func (r *RestPerf) PollData() (map[string]*matrix.Matrix, error) {
 							}
 
 							for i, label := range labels {
-								k := name + "." + label
+								k := name + arrayKeyToken + label
 								metr, ok := newData.GetMetrics()[k]
 								if !ok {
 									if metr, err = newData.NewMetricFloat64(k); err != nil {
@@ -670,7 +712,12 @@ func (r *RestPerf) PollData() (map[string]*matrix.Matrix, error) {
 										continue
 									}
 									metr.SetName(metric.Label)
-									metr.SetLabel("metric", label)
+									if x := strings.Split(label, arrayKeyToken); len(x) == 2 {
+										metr.SetLabel("metric", x[0])
+										metr.SetLabel("submetric", x[1])
+									} else {
+										metr.SetLabel("metric", label)
+									}
 									// differentiate between array and normal counter
 									metr.SetArray(true)
 									metr.SetExportable(metric.Exportable)
@@ -739,7 +786,7 @@ func (r *RestPerf) PollData() (map[string]*matrix.Matrix, error) {
 						}
 					}
 				} else {
-					r.Logger.Warn().Str("counter", name).Msg("Counter is nil. Unable to process. Check template")
+					r.Logger.Warn().Str("counter", name).Msg("Counter is missing or unable to parse.")
 				}
 			}
 			if err = newData.GetMetric("timestamp").SetValueFloat64(instance, ts); err != nil {
@@ -799,7 +846,7 @@ func (r *RestPerf) PollData() (map[string]*matrix.Matrix, error) {
 					orderedDenominatorKeys = append(orderedDenominatorKeys, key)
 				}
 			} else {
-				r.Logger.Warn().Str("counter", metric.GetName()).Msg("Counter is nil. Unable to process. Check template")
+				r.Logger.Warn().Str("counter", metric.GetName()).Msg("Counter is missing or unable to parse")
 			}
 		}
 	}
@@ -928,7 +975,7 @@ func (r *RestPerf) PollData() (map[string]*matrix.Matrix, error) {
 				totalSkips += skips
 			}
 		} else {
-			r.Logger.Warn().Str("counter", metric.GetName()).Msg("Counter is nil. Unable to process. Check template ")
+			r.Logger.Warn().Str("counter", metric.GetName()).Msg("Counter is missing or unable to parse ")
 			continue
 		}
 	}
@@ -1034,8 +1081,7 @@ func (r *RestPerf) counterLookup(metric matrix.Metric, metricKey string) *counte
 	var c *counter
 
 	if metric.IsArray() {
-		lastInd := strings.LastIndex(metricKey, ".")
-		name := metricKey[:lastInd]
+		name, _, _ := strings.Cut(metricKey, arrayKeyToken)
 		c = r.perfProp.counterInfo[name]
 	} else {
 		c = r.perfProp.counterInfo[metricKey]
diff --git a/docs/install/containers.md b/docs/install/containers.md
index 8a9f3c83..8ae5fd1b 100644
--- a/docs/install/containers.md
+++ b/docs/install/containers.md
@@ -157,5 +157,5 @@ To upgrade Harvest:
 
 ```
 docker pull cr.netapp.io/harvest   # or if using Docker Hub: docker pull rahulguptajss/harvest
-docker-compose -f prom-stack.yml -f harvest-compose.yml restart
+docker-compose -f prom-stack.yml -f harvest-compose.yml up -d --remove-orphans
 ```


Test patch:
diff --git a/cmd/collectors/restperf/restperf_test.go b/cmd/collectors/restperf/restperf_test.go
new file mode 100644
index 00000000..3da27c74
--- /dev/null
+++ b/cmd/collectors/restperf/restperf_test.go
@@ -0,0 +1,45 @@
+package restperf
+
+import (
+	"github.com/tidwall/gjson"
+	"os"
+	"reflect"
+	"testing"
+)
+
+func Test_parseMetricResponse(t *testing.T) {
+	bytes, err := os.ReadFile("testdata/submetrics.json")
+	if err != nil {
+		t.Fatal(err)
+	}
+	instanceData := gjson.GetBytes(bytes, "records.0")
+	type args struct {
+		instanceData gjson.Result
+		metric       string
+	}
+	tests := []struct {
+		name string
+		args args
+		want *metricResponse
+	}{
+		{
+			name: "rss_matrix",
+			args: args{
+				instanceData: instanceData, metric: "rss_matrix",
+			}, want: &metricResponse{
+				label:   "tx_frames#queue_0,tx_frames#queue_1,tx_frames#queue_2,tx_bytes#queue_0,tx_bytes#queue_1,tx_bytes#queue_2",
+				value:   "6177010,1605252882,0,3,1,4",
+				isArray: true,
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := parseMetricResponse(tt.args.instanceData, tt.args.metric)
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("\ngot  %v\nwant %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/cmd/collectors/restperf/testdata/submetrics.json b/cmd/collectors/restperf/testdata/submetrics.json
new file mode 100644
index 00000000..ec74c31c
--- /dev/null
+++ b/cmd/collectors/restperf/testdata/submetrics.json
@@ -0,0 +1,442 @@
+{
+  "records": [
+    {
+      "counter_table": {
+        "name": "nic_common"
+      },
+      "id": "sti2750-231:e0a",
+      "properties": [
+        {
+          "name": "node.name",
+          "value": "sti2750-231"
+        },
+        {
+          "name": "node.uuid",
+          "value": "fa4fb73d-5d78-11ed-be3d-00a098bef221"
+        },
+        {
+          "name": "type",
+          "value": "nic_ixgbe"
+        },
+        {
+          "name": "link_current_state",
+          "value": "up"
+        },
+        {
+          "name": "link_media_state",
+          "value": "active"
+        },
+        {
+          "name": "link_duplex",
+          "value": "full"
+        },
+        {
+          "name": "link_speed",
+          "value": "10000M"
+        },
+        {
+          "name": "link_flowcontrol",
+          "value": "none"
+        },
+        {
+          "name": "rss_enabled",
+          "value": "yes"
+        }
+      ],
+      "counters": [
+        {
+          "name": "ipspace",
+          "value": 4294967294
+        },
+        {
+          "name": "receive_frames_per_sec",
+          "value": 0
+        },
+        {
+          "name": "receive_frames",
+          "value": 42923529
+        },
+        {
+          "name": "receive_bytes_per_sec",
+          "value": 0
+        },
+        {
+          "name": "receive_bytes",
+          "value": 11702390672
+        },
+        {
+          "name": "receive_errors_per_min",
+          "value": 0
+        },
+        {
+          "name": "receive_errors",
+          "value": 0
+        },
+        {
+          "name": "receive_discards_per_min",
+          "value": 0
+        },
+        {
+          "name": "receive_discards",
+          "value": 0
+        },
+        {
+          "name": "receive_total_frames",
+          "value": 42923529
+        },
+        {
+          "name": "receive_total_bytes",
+          "value": 11702390672
+        },
+        {
+          "name": "receive_total_errors",
+          "value": 0
+        },
+        {
+          "name": "receive_total_discards",
+          "value": 0
+        },
+        {
+          "name": "receive_multicast_broadcast",
+          "value": 443092
+        },
+        {
+          "name": "receive_no_buffers",
+          "value": 0
+        },
+        {
+          "name": "receive_non_primary_unicast",
+          "value": 0
+        },
+        {
+          "name": "receive_lro_segments",
+          "value": 41070557
+        },
+        {
+          "name": "receive_lro_bytes",
+          "value": 10745505246
+        },
+        {
+          "name": "receive_lro6_segments",
+          "value": 0
+        },
+        {
+          "name": "receive_lro6_bytes",
+          "value": 0
+        },
+        {
+          "name": "receive_multicast_v6_solicit",
+          "value": 0
+        },
+        {
+          "name": "receive_tag_drop",
+          "value": 0
+        },
+        {
+          "name": "receive_vlan_tag_drop",
+          "value": 0
+        },
+        {
+          "name": "receive_vlan_untag_drop",
+          "value": 0
+        },
+        {
+          "name": "receive_vlan_forwards",
+          "value": 0
+        },
+        {
+          "name": "receive_vlan_broadcasts",
+          "value": 0
+        },
+        {
+          "name": "receive_vlan_unicasts",
+          "value": 0
+        },
+        {
+          "name": "transmit_frames_per_sec",
+          "value": 0
+        },
+        {
+          "name": "transmit_frames",
+          "value": 43271659
+        },
+        {
+          "name": "transmit_bytes_per_sec",
+          "value": 0
+        },
+        {
+          "name": "transmit_bytes",
+          "value": 9963176026
+        },
+        {
+          "name": "transmit_errors_per_min",
+          "value": 0
+        },
+        {
+          "name": "transmit_errors",
+          "value": 0
+        },
+        {
+          "name": "transmit_discards_per_min",
+          "value": 0
+        },
+        {
+          "name": "transmit_discards",
+          "value": 0
+        },
+        {
+          "name": "transmit_total_frames",
+          "value": 43271659
+        },
+        {
+          "name": "transmit_total_bytes",
+          "value": 9963176026
+        },
+        {
+          "name": "transmit_total_errors",
+          "value": 0
+        },
+        {
+          "name": "transmit_total_discards",
+          "value": 0
+        },
+        {
+          "name": "transmit_multicast_broadcast",
+          "value": 84785
+        },
+        {
+          "name": "transmit_queue_overflows",
+          "value": 0
+        },
+        {
+          "name": "transmit_no_buffers",
+          "value": 0
+        },
+        {
+          "name": "transmit_frames_queued",
+          "value": 0
+        },
+        {
+          "name": "transmit_buffer_coalesces",
+          "value": 0
+        },
+        {
+          "name": "transmit_mtus_too_big",
+          "value": 0
+        },
+        {
+          "name": "transmit_tso_segments",
+          "value": 113117
+        },
+        {
+          "name": "transmit_tso_bytes",
+          "value": 1792908057
+        },
+        {
+          "name": "transmit_tso6_segments",
+          "value": 0
+        },
+        {
+          "name": "transmit_tso6_bytes",
+          "value": 0
+        },
+        {
+          "name": "transmit_multicast_v6_solicit",
+          "value": 0
+        },
+        {
+          "name": "hwassist",
+          "value": 7711
+        },
+        {
+          "name": "transmit_ltm_fast_path_calls",
+          "value": 0
+        },
+        {
+          "name": "transmit_ltm_busy_calls",
+          "value": 0
+        },
+        {
+          "name": "transmit_ltm_update_calls",
+          "value": 0
+        },
+        {
+          "name": "transmit_ltm_yield_calls",
+          "value": 0
+        },
+        {
+          "name": "transmit_ltm_total_calls",
+          "value": 0
+        },
+        {
+          "name": "total_bytes",
+          "value": 21665566698
+        },
+        {
+          "name": "total_errors",
+          "value": 0
+        },
+        {
+          "name": "receive_crc_errors",
+          "value": 0
+        },
+        {
+          "name": "receive_unsupported_op",
+          "value": 0
+        },
+        {
+          "name": "receive_length_errors",
+          "value": 0
+        },
+        {
+          "name": "receive_runt_frames",
+          "value": 0
+        },
+        {
+          "name": "receive_fragment",
+          "value": 0
+        },
+        {
+          "name": "receive_long_frames",
+          "value": 0
+        },
+        {
+          "name": "receive_jabber",
+          "value": 0
+        },
+        {
+          "name": "receive_illegal_symbol",
+          "value": 0
+        },
+        {
+          "name": "receive_error_symbol",
+          "value": 0
+        },
+        {
+          "name": "receive_alignment_errors",
+          "value": 0
+        },
+        {
+          "name": "receive_bus_overruns",
+          "value": 0
+        },
+        {
+          "name": "receive_xon",
+          "value": 0
+        },
+        {
+          "name": "receive_xoff",
+          "value": 0
+        },
+        {
+          "name": "receive_jumbo",
+          "value": 950923
+        },
+        {
+          "name": "transmit_hw_errors",
+          "value": 0
+        },
+        {
+          "name": "transmit_link_down_drop",
+          "value": 0
+        },
+        {
+          "name": "transmit_config_up_down",
+          "value": 2
+        },
+        {
+          "name": "transmit_bus_overruns",
+          "value": 0
+        },
+        {
+          "name": "collisions",
+          "value": 0
+        },
+        {
+          "name": "transmit_max_collisions",
+          "value": 0
+        },
+        {
+          "name": "transmit_single_collisions",
+          "value": 0
+        },
+        {
+          "name": "transmit_multiple_collisions",
+          "value": 0
+        },
+        {
+          "name": "transmit_late_collisions",
+          "value": 0
+        },
+        {
+          "name": "transmit_xon",
+          "value": 0
+        },
+        {
+          "name": "transmit_xoff",
+          "value": 0
+        },
+        {
+          "name": "transmit_jumbo",
+          "value": 339801
+        },
+        {
+          "name": "link_up_to_down",
+          "value": 2
+        },
+        {
+          "name": "rss_itable_updates",
+          "value": 0
+        },
+        {
+          "name": "rss_hash_errors",
+          "value": 0
+        },
+        {
+          "name": "rss_num_of_queues_used",
+          "value": 7
+        },
+        {
+          "name": "rss_matrix",
+          "labels": [
+            "queue_0",
+            "queue_1",
+            "queue_2",
+            "queue_0",
+            "queue_1",
+            "queue_2"
+          ],
+          "counters": [
+            {
+              "label": "tx_frames",
+              "values": [
+                6177010,
+                1605252882,
+                0
+              ]
+            },
+            {
+              "label": "tx_bytes",
+              "values": [
+                3,
+                1,
+                4
+              ]
+            }
+          ]
+        }
+      ],
+      "_links": {
+        "self": {
+          "href": "/api/cluster/counter/tables/nic_common/rows/sti2750-231%3Ae0a?fields=**"
+        }
+      }
+    }
+  ],
+  "num_records": 1,
+  "_links": {
+    "self": {
+      "href": "/api/cluster/counter/tables/nic_common/rows?return_records=true&fields=**"
+    }
+  }
+}
\ No newline at end of file