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 7 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
254 changes: 254 additions & 0 deletions cmd/collectors/rest/plugins/clustersoftware/clustersoftware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
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"
Hardikl marked this conversation as resolved.
Show resolved Hide resolved
const updateMatrix = "update"
const StatusMatrix = "status"
const 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")

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()
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)

// 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)

// populate numeric data
value := 0.0
if status == "warning" {
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
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