Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Tracking cluster image update progress via Rest #3306

Merged
merged 15 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
260 changes: 260 additions & 0 deletions cmd/collectors/rest/plugins/clustersoftware/clustersoftware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
package clustersoftware

import (
"github.com/netapp/harvest/v2/cmd/poller/plugin"
"github.com/netapp/harvest/v2/pkg/conf"
"github.com/netapp/harvest/v2/pkg/matrix"
"github.com/netapp/harvest/v2/pkg/slogx"
"github.com/netapp/harvest/v2/pkg/tree/node"
"github.com/netapp/harvest/v2/pkg/util"
"github.com/netapp/harvest/v2/third_party/tidwall/gjson"
"log/slog"
)

const (
clusterSoftware = "cluster_software"
updateMatrix = "update"
statusMatrix = "status"
validationMatrix = "validation"
)

type ClusterSoftware struct {
*plugin.AbstractPlugin
data map[string]*matrix.Matrix
}

func New(p *plugin.AbstractPlugin) plugin.Plugin {
return &ClusterSoftware{AbstractPlugin: p}
}

func (c *ClusterSoftware) Init(conf.Remote) error {
if err := c.InitAbc(); err != nil {
return err
}

c.data = make(map[string]*matrix.Matrix)
if err := c.createUpdateMetrics(); err != nil {
return err
}
if err := c.createStatusMetrics(); err != nil {
return err
}
if err := c.createValidationMetrics(); err != nil {
return err
}

return nil
}

func (c *ClusterSoftware) Run(dataMap map[string]*matrix.Matrix) ([]*matrix.Matrix, *util.Metadata, error) {
globalLabels := dataMap[c.Object].GetGlobalLabels()

for _, instance := range dataMap[c.Object].GetInstances() {
instance.SetExportable(false)
// generate update details metrics
updateDetails := instance.GetLabel("update_details")
updateDetailsJSON := gjson.Result{Type: gjson.JSON, Raw: "[" + updateDetails + "]"}
c.handleUpdateDetails(updateDetailsJSON, globalLabels)

// generate status details metrics
statusDetails := instance.GetLabel("status_details")
statusDetailsJSON := gjson.Result{Type: gjson.JSON, Raw: "[" + statusDetails + "]"}
c.handleStatusDetails(statusDetailsJSON, globalLabels)

// generate update details metrics
validationResults := instance.GetLabel("validation_results")
validationResultsJSON := gjson.Result{Type: gjson.JSON, Raw: "[" + validationResults + "]"}
c.handleValidationDetails(validationResultsJSON, globalLabels)
}

softwareMetrics := make([]*matrix.Matrix, 0, len(c.data))
for _, val := range c.data {
softwareMetrics = append(softwareMetrics, val)
}

return softwareMetrics, nil, nil
}

func (c *ClusterSoftware) createUpdateMetrics() error {
mat := matrix.New(c.Parent+"."+updateMatrix, clusterSoftware, clusterSoftware)
exportOptions := node.NewS("export_options")
instanceKeys := exportOptions.NewChildS("instance_keys", "")
instanceKeys.NewChildS("", "phase")
instanceKeys.NewChildS("", "state")
instanceKeys.NewChildS("", "node")
instanceKeys.NewChildS("", "elapsed_duration")

mat.SetExportOptions(exportOptions)

if _, err := mat.NewMetricFloat64(updateMatrix); err != nil {
c.SLogger.Error("Failed to create metric", slogx.Err(err), slog.String("metric", updateMatrix))
return err
}

c.data[updateMatrix] = mat
return nil
}

func (c *ClusterSoftware) createStatusMetrics() error {
mat := matrix.New(c.Parent+"."+statusMatrix, clusterSoftware, clusterSoftware)
exportOptions := node.NewS("export_options")
instanceKeys := exportOptions.NewChildS("instance_keys", "")
instanceKeys.NewChildS("", "state")
instanceKeys.NewChildS("", "node")
instanceKeys.NewChildS("", "name")

mat.SetExportOptions(exportOptions)

if _, err := mat.NewMetricFloat64(statusMatrix); err != nil {
c.SLogger.Error("Failed to create metric", slogx.Err(err), slog.String("metric", statusMatrix))
return err
}

c.data[statusMatrix] = mat
return nil
}

func (c *ClusterSoftware) createValidationMetrics() error {
mat := matrix.New(c.Parent+"."+validationMatrix, clusterSoftware, clusterSoftware)
exportOptions := node.NewS("export_options")
instanceKeys := exportOptions.NewChildS("instance_keys", "")
instanceKeys.NewChildS("", "status")
instanceKeys.NewChildS("", "update_check")

mat.SetExportOptions(exportOptions)

if _, err := mat.NewMetricFloat64(validationMatrix); err != nil {
c.SLogger.Error("Failed to create metric", slogx.Err(err), slog.String("metric", validationMatrix))
return err
}

c.data[validationMatrix] = mat
return nil
}

func (c *ClusterSoftware) handleUpdateDetails(updateDetailsJSON gjson.Result, globalLabels map[string]string) {
var (
clusterUpdateInstance *matrix.Instance
key string
err error
)
// Purge and reset data
c.data[updateMatrix].PurgeInstances()
c.data[updateMatrix].Reset()

// Set all global labels
c.data[updateMatrix].SetGlobalLabels(globalLabels)

for _, updateDetail := range updateDetailsJSON.Array() {
phase := updateDetail.Get("phase").ClonedString()
state := updateDetail.Get("state").ClonedString()
elapsedDuration := updateDetail.Get("elapsed_duration").ClonedString()
nodeName := updateDetail.Get("node.name").ClonedString()
key = phase + state + nodeName

if clusterUpdateInstance, err = c.data[updateMatrix].NewInstance(key); err != nil {
c.SLogger.Error("Failed to create instance", slogx.Err(err), slog.String("key", key))
continue
}
clusterUpdateInstance.SetLabel("node", nodeName)
clusterUpdateInstance.SetLabel("state", state)
clusterUpdateInstance.SetLabel("phase", phase)
clusterUpdateInstance.SetLabel("elapsed_duration", elapsedDuration)

// populate numeric data
value := 0.0
if state == "completed" {
value = 1.0
}

met := c.data[updateMatrix].GetMetric(updateMatrix)
if err := met.SetValueFloat64(clusterUpdateInstance, value); err != nil {
c.SLogger.Error("Failed to parse value", slogx.Err(err), slog.Float64("value", value))
} else {
c.SLogger.Debug("added value", slog.Float64("value", value))
}
}
}

func (c *ClusterSoftware) handleStatusDetails(statusDetailsJSON gjson.Result, globalLabels map[string]string) {
var (
clusterStatusInstance *matrix.Instance
key string
err error
)
// Purge and reset data
c.data[statusMatrix].PurgeInstances()
c.data[statusMatrix].Reset()

// Set all global labels
c.data[statusMatrix].SetGlobalLabels(globalLabels)

for _, updateDetail := range statusDetailsJSON.Array() {
name := updateDetail.Get("name").ClonedString()
state := updateDetail.Get("state").ClonedString()
nodeName := updateDetail.Get("node.name").ClonedString()
key = name + state + nodeName

if clusterStatusInstance, err = c.data[statusMatrix].NewInstance(key); err != nil {
c.SLogger.Error("Failed to create instance", slogx.Err(err), slog.String("key", key))
continue
}
clusterStatusInstance.SetLabel("node", nodeName)
clusterStatusInstance.SetLabel("state", state)
clusterStatusInstance.SetLabel("name", name)

// populate numeric data
value := 0.0
if state == "completed" {
value = 1.0
}

met := c.data[statusMatrix].GetMetric(statusMatrix)
if err := met.SetValueFloat64(clusterStatusInstance, value); err != nil {
c.SLogger.Error("Failed to parse value", slogx.Err(err), slog.Float64("value", value))
} else {
c.SLogger.Debug("added value", slog.Float64("value", value))
}
}
}

func (c *ClusterSoftware) handleValidationDetails(validationDetailsJSON gjson.Result, globalLabels map[string]string) {
var (
clusterValidationInstance *matrix.Instance
key string
err error
)
// Purge and reset data
c.data[validationMatrix].PurgeInstances()
c.data[validationMatrix].Reset()

// Set all global labels
c.data[validationMatrix].SetGlobalLabels(globalLabels)

for _, updateDetail := range validationDetailsJSON.Array() {
updateCheck := updateDetail.Get("update_check").ClonedString()
status := updateDetail.Get("status").ClonedString()
key = updateCheck + status

if clusterValidationInstance, err = c.data[validationMatrix].NewInstance(key); err != nil {
c.SLogger.Error("Failed to create instance", slogx.Err(err), slog.String("key", key))
continue
}
clusterValidationInstance.SetLabel("update_check", updateCheck)
clusterValidationInstance.SetLabel("status", status)

// ignore all the validation result which are not in warning status
if status != "warning" {
continue
}

// populate numeric data
value := 1.0
met := c.data[validationMatrix].GetMetric(validationMatrix)
if err := met.SetValueFloat64(clusterValidationInstance, value); err != nil {
c.SLogger.Error("Failed to parse value", slogx.Err(err), slog.Float64("value", value))
} else {
c.SLogger.Debug("added value", slog.Float64("value", value))
}
}
}
3 changes: 3 additions & 0 deletions cmd/collectors/rest/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/netapp/harvest/v2/cmd/collectors/rest/plugins/aggregate"
"github.com/netapp/harvest/v2/cmd/collectors/rest/plugins/certificate"
"github.com/netapp/harvest/v2/cmd/collectors/rest/plugins/cluster"
"github.com/netapp/harvest/v2/cmd/collectors/rest/plugins/clustersoftware"
"github.com/netapp/harvest/v2/cmd/collectors/rest/plugins/disk"
"github.com/netapp/harvest/v2/cmd/collectors/rest/plugins/health"
"github.com/netapp/harvest/v2/cmd/collectors/rest/plugins/metroclustercheck"
Expand Down Expand Up @@ -472,6 +473,8 @@ func (r *Rest) LoadPlugin(kind string, abc *plugin.AbstractPlugin) plugin.Plugin
return aggregate.New(abc)
case "Cluster":
return cluster.New(abc)
case "ClusterSoftware":
return clustersoftware.New(abc)
case "Disk":
return disk.New(abc)
case "Health":
Expand Down
1 change: 0 additions & 1 deletion cmd/tools/generate/counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ var (
"_labels",
"volume_arw_status",
"ALERTS",
"_tags",
}

// Exclude extra metrics for ZAPI
Expand Down
41 changes: 41 additions & 0 deletions cmd/tools/generate/counter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1988,3 +1988,44 @@ counters:
Endpoint: NA
ONTAPCounter: Harvest generated
Template: conf/rest/9.12.0/fru.yaml

- Name: cluster_tags
Description: This metric display tags at cluster level.
Hardikl marked this conversation as resolved.
Show resolved Hide resolved
APIs:
- API: REST
Endpoint: NA
ONTAPCounter: Harvest generated
Template: conf/rest/9.12.0/status.yaml

- Name: volume_tags
Description: This metric display tags at volume level.
Hardikl marked this conversation as resolved.
Show resolved Hide resolved
APIs:
- API: REST
Endpoint: NA
ONTAPCounter: Harvest generated
Template: conf/rest/9.12.0/volume.yaml


- Name: cluster_software_update
Description: This metric displays the software update phase with its status.
Hardikl marked this conversation as resolved.
Show resolved Hide resolved
APIs:
- API: REST
Endpoint: NA
ONTAPCounter: Harvest generated
Template: conf/rest/9.6.0/clustersoftware.yaml

- Name: cluster_software_status
Description: This metric displays the software job with its status.
Hardikl marked this conversation as resolved.
Show resolved Hide resolved
APIs:
- API: REST
Endpoint: NA
ONTAPCounter: Harvest generated
Template: conf/rest/9.6.0/clustersoftware.yaml

- Name: cluster_software_validation
Description: This metric displays the software pre validation checks with its status.
Hardikl marked this conversation as resolved.
Show resolved Hide resolved
APIs:
- API: REST
Endpoint: NA
ONTAPCounter: Harvest generated
Template: conf/rest/9.6.0/clustersoftware.yaml
14 changes: 14 additions & 0 deletions conf/rest/9.6.0/clustersoftware.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

name: ClusterSoftware
query: api/cluster/software
object: cluster_software

counters:
- ^status_details => status_details
- ^update_details => update_details
- ^validation_results => validation_results

plugins:
- ClusterSoftware


1 change: 1 addition & 0 deletions conf/rest/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ objects:
# CIFSShare: cifs_share.yaml
CloudTarget: cloud_target.yaml
ClusterPeer: clusterpeer.yaml
ClusterSoftware: clustersoftware.yaml
Disk: disk.yaml
EmsDestination: ems_destination.yaml
# ExportRule: exports.yaml
Expand Down
Loading