From 4f498c3eaed4c68563ba4e9660a5025eebca2865 Mon Sep 17 00:00:00 2001 From: Rahul Gupta Date: Fri, 16 Jun 2023 13:44:15 +0530 Subject: [PATCH] feat: volume clone info for ZAPI --- cmd/collectors/zapi/plugins/volume/volume.go | 152 +++++++++++++++---- conf/rest/9.12.0/volume.yaml | 3 - conf/zapi/cdot/9.8.0/volume.yaml | 4 + 3 files changed, 124 insertions(+), 35 deletions(-) diff --git a/cmd/collectors/zapi/plugins/volume/volume.go b/cmd/collectors/zapi/plugins/volume/volume.go index 79a962277..77208e6c9 100644 --- a/cmd/collectors/zapi/plugins/volume/volume.go +++ b/cmd/collectors/zapi/plugins/volume/volume.go @@ -20,84 +20,172 @@ type Volume struct { } type aggrData struct { - aggrUUID string - aggrName string + uuid string + name string +} + +type volumeClone struct { + aggregate string + name string + svm string + parentSnapshot string + parentVolume string + parentSvm string + splitEstimate string } func New(p *plugin.AbstractPlugin) plugin.Plugin { return &Volume{AbstractPlugin: p} } -func (my *Volume) Init() error { +func (v *Volume) Init() error { var err error - if err = my.InitAbc(); err != nil { + if err = v.InitAbc(); err != nil { return err } - if my.client, err = zapi.New(conf.ZapiPoller(my.ParentParams), my.Auth); err != nil { - my.Logger.Error().Stack().Err(err).Msg("connecting") + if v.client, err = zapi.New(conf.ZapiPoller(v.ParentParams), v.Auth); err != nil { + v.Logger.Error().Stack().Err(err).Msg("connecting") return err } - if err = my.client.Init(5); err != nil { + if err = v.client.Init(5); err != nil { return err } - my.aggrsMap = make(map[string]string) + v.aggrsMap = make(map[string]string) // Assigned the value to currentVal so that plugin would be invoked first time to populate cache. - my.currentVal = my.SetPluginInterval() + v.currentVal = v.SetPluginInterval() return nil } -func (my *Volume) Run(dataMap map[string]*matrix.Matrix) ([]*matrix.Matrix, error) { +func (v *Volume) Run(dataMap map[string]*matrix.Matrix) ([]*matrix.Matrix, error) { - data := dataMap[my.Object] - if my.currentVal >= my.PluginInvocationRate { - my.currentVal = 0 + data := dataMap[v.Object] + if v.currentVal >= v.PluginInvocationRate { + v.currentVal = 0 // invoke disk-encrypt-get-iter zapi and populate disk info - disks, err1 := my.getEncryptedDisks() + disks, err1 := v.getEncryptedDisks() // invoke aggr-status-get-iter zapi and populate aggr disk mapping info - aggrDiskMap, err2 := my.getAggrDiskMapping() + aggrDiskMap, err2 := v.getAggrDiskMapping() if err1 != nil { if errors.Is(err1, errs.ErrNoInstance) { - my.Logger.Debug().Err(err1).Msg("Failed to collect disk data") + v.Logger.Debug().Err(err1).Msg("Failed to collect disk data") } else { - my.Logger.Error().Err(err1).Msg("Failed to collect disk data") + v.Logger.Error().Err(err1).Msg("Failed to collect disk data") } } if err2 != nil { if errors.Is(err2, errs.ErrNoInstance) { - my.Logger.Debug().Err(err2).Msg("Failed to collect aggregate-disk mapping data") + v.Logger.Debug().Err(err2).Msg("Failed to collect aggregate-disk mapping data") } else { - my.Logger.Error().Err(err2).Msg("Failed to collect aggregate-disk mapping data") + v.Logger.Error().Err(err2).Msg("Failed to collect aggregate-disk mapping data") } } // update aggrsMap based on disk data and addr disk mapping - my.updateAggrMap(disks, aggrDiskMap) + v.updateAggrMap(disks, aggrDiskMap) + } + + volumeCloneMap, err := v.getVolumeCloneInfo() + + if err != nil { + v.Logger.Error().Err(err).Msg("Failed to update clone data") } // update volume instance labels - my.updateVolumeLabels(data) + v.updateVolumeLabels(data, volumeCloneMap) - my.currentVal++ + v.currentVal++ return nil, nil } -func (my *Volume) updateVolumeLabels(data *matrix.Matrix) { +func (v *Volume) updateVolumeLabels(data *matrix.Matrix, volumeCloneMap map[string]volumeClone) { + var err error for _, volume := range data.GetInstances() { aggrUUID := volume.GetLabel("aggrUuid") - _, exist := my.aggrsMap[aggrUUID] + _, exist := v.aggrsMap[aggrUUID] volume.SetLabel("isHardwareEncrypted", strconv.FormatBool(exist)) + + name := volume.GetLabel("volume") + svm := volume.GetLabel("svm") + key := name + svm + + if vc, ok := volumeCloneMap[key]; ok { + volume.SetLabel("clone_parent_snapshot", vc.parentSnapshot) + volume.SetLabel("clone_parent_volume", vc.parentVolume) + volume.SetLabel("clone_parent_svm", vc.parentSvm) + splitEstimate := data.GetMetric("clone_split_estimate") + if splitEstimate == nil { + if splitEstimate, err = data.NewMetricFloat64("clone_split_estimate"); err != nil { + v.Logger.Error().Err(err).Str("metric", "clone_split_estimate").Msg("add metric") + continue + } + } + + // splitEstimate is 4KB blocks, Convert to bytes as in REST + + var splitEstimateBytes float64 + if splitEstimateBytes, err = strconv.ParseFloat(vc.splitEstimate, 64); err != nil { + v.Logger.Error().Err(err).Str("metric", "clone_split_estimate").Str("value", vc.splitEstimate).Msg("parse metric") + continue + } else { + splitEstimateBytes = splitEstimateBytes * 4 * 1024 + } + if err = splitEstimate.SetValueFloat64(volume, splitEstimateBytes); err != nil { + v.Logger.Error().Err(err).Str("metric", "clone_split_estimate").Str("value", vc.splitEstimate).Msg("set metric") + continue + } + } } } -func (my *Volume) getEncryptedDisks() ([]string, error) { +func (v *Volume) getVolumeCloneInfo() (map[string]volumeClone, error) { + var ( + result []*node.Node + volumeCloneMap map[string]volumeClone + err error + ) + + volumeCloneMap = make(map[string]volumeClone) + request := node.NewXMLS("volume-clone-get-iter") + request.NewChildS("max-records", collectors.DefaultBatchSize) + if result, err = v.client.InvokeZapiCall(request); err != nil { + return volumeCloneMap, err + } + + if len(result) == 0 || result == nil { + return volumeCloneMap, nil + } + + for _, clone := range result { + name := clone.GetChildContentS("volume") + vserver := clone.GetChildContentS("vserver") + parentSnapshot := clone.GetChildContentS("parent-snapshot") + parentVolume := clone.GetChildContentS("parent-volume") + parentSvm := clone.GetChildContentS("parent-vserver") + splitEstimate := clone.GetChildContentS("split-estimate") + volC := volumeClone{ + name: name, + svm: vserver, + parentSnapshot: parentSnapshot, + parentVolume: parentVolume, + parentSvm: parentSvm, + splitEstimate: splitEstimate, + } + key := volC.name + volC.svm + volumeCloneMap[key] = volC + } + + return volumeCloneMap, nil +} + +func (v *Volume) getEncryptedDisks() ([]string, error) { var ( result []*node.Node diskNames []string @@ -113,7 +201,7 @@ func (my *Volume) getEncryptedDisks() ([]string, error) { encryptInfoQuery.NewChildS("protection-mode", "open|part|miss") // fetching only disks whose protection-mode is open/part/miss - if result, err = my.client.InvokeZapiCall(request); err != nil { + if result, err = v.client.InvokeZapiCall(request); err != nil { return nil, err } @@ -128,19 +216,19 @@ func (my *Volume) getEncryptedDisks() ([]string, error) { return diskNames, nil } -func (my *Volume) updateAggrMap(disks []string, aggrDiskMap map[string]aggrData) { +func (v *Volume) updateAggrMap(disks []string, aggrDiskMap map[string]aggrData) { if disks != nil && aggrDiskMap != nil { // Clean aggrsMap map - my.aggrsMap = make(map[string]string) + v.aggrsMap = make(map[string]string) for _, disk := range disks { aggr := aggrDiskMap[disk] - my.aggrsMap[aggr.aggrUUID] = aggr.aggrName + v.aggrsMap[aggr.uuid] = aggr.name } } } -func (my *Volume) getAggrDiskMapping() (map[string]aggrData, error) { +func (v *Volume) getAggrDiskMapping() (map[string]aggrData, error) { var ( result []*node.Node aggrsDisksMap map[string]aggrData @@ -152,7 +240,7 @@ func (my *Volume) getAggrDiskMapping() (map[string]aggrData, error) { request.NewChildS("max-records", collectors.DefaultBatchSize) aggrsDisksMap = make(map[string]aggrData) - if result, err = my.client.InvokeZapiCall(request); err != nil { + if result, err = v.client.InvokeZapiCall(request); err != nil { return nil, err } @@ -166,7 +254,7 @@ func (my *Volume) getAggrDiskMapping() (map[string]aggrData, error) { aggrDiskList := aggrDiskData.GetChildS("aggr-plex-list").GetChildS("aggr-plex-info").GetChildS("aggr-raidgroup-list").GetChildS("aggr-raidgroup-info").GetChildS("aggr-disk-list").GetChildren() for _, aggrDisk := range aggrDiskList { diskName = aggrDisk.GetChildContentS("disk") - aggrsDisksMap[diskName] = aggrData{aggrUUID: aggrUUID, aggrName: aggrName} + aggrsDisksMap[diskName] = aggrData{uuid: aggrUUID, name: aggrName} } } return aggrsDisksMap, nil diff --git a/conf/rest/9.12.0/volume.yaml b/conf/rest/9.12.0/volume.yaml index 536c6c279..ea3e3d1e6 100644 --- a/conf/rest/9.12.0/volume.yaml +++ b/conf/rest/9.12.0/volume.yaml @@ -73,8 +73,6 @@ endpoints: - ^clone.parent_snapshot.name => clone_parent_snapshot - ^clone.parent_svm.name => clone_parent_svm - ^clone.parent_volume.name => clone_parent_volume - - ^clone.split_initiated => clone_split_initiated - - clone.split_complete_percent => clone_split_complete_percent - clone.split_estimate => clone_split_estimate - hidden_fields: - clone @@ -137,7 +135,6 @@ export_options: - clone_parent_snapshot - clone_parent_svm - clone_parent_volume - - clone_split_initiated - isEncrypted - isHardwareEncrypted - is_sis_volume diff --git a/conf/zapi/cdot/9.8.0/volume.yaml b/conf/zapi/cdot/9.8.0/volume.yaml index 5222fbbfe..7f28e6246 100644 --- a/conf/zapi/cdot/9.8.0/volume.yaml +++ b/conf/zapi/cdot/9.8.0/volume.yaml @@ -112,6 +112,9 @@ export_options: - svm - volume instance_labels: + - clone_parent_snapshot + - clone_parent_svm + - clone_parent_volume - isEncrypted - isHardwareEncrypted - is_sis_volume @@ -121,3 +124,4 @@ export_options: - state - svm_root - type +