diff --git a/cmd/collectors/zapi/collector/zapi.go b/cmd/collectors/zapi/collector/zapi.go index 693ed7d55..05d23bea9 100644 --- a/cmd/collectors/zapi/collector/zapi.go +++ b/cmd/collectors/zapi/collector/zapi.go @@ -6,6 +6,7 @@ package zapi import ( "fmt" + "github.com/netapp/harvest/v2/cmd/collectors/zapi/plugins/aggregate" "github.com/netapp/harvest/v2/cmd/collectors/zapi/plugins/certificate" "github.com/netapp/harvest/v2/cmd/collectors/zapi/plugins/qospolicyadaptive" "github.com/netapp/harvest/v2/cmd/collectors/zapi/plugins/qospolicyfixed" @@ -156,7 +157,8 @@ func (z *Zapi) LoadPlugin(kind string, abc *plugin.AbstractPlugin) plugin.Plugin return qospolicyfixed.New(abc) case "QosPolicyAdaptive": return qospolicyadaptive.New(abc) - + case "Aggregate": + return aggregate.New(abc) default: z.Logger.Info().Msgf("no zapi plugin found for %s", kind) } diff --git a/cmd/collectors/zapi/plugins/aggregate/aggregate.go b/cmd/collectors/zapi/plugins/aggregate/aggregate.go new file mode 100644 index 000000000..56dc3e16a --- /dev/null +++ b/cmd/collectors/zapi/plugins/aggregate/aggregate.go @@ -0,0 +1,98 @@ +package aggregate + +import ( + "errors" + "github.com/netapp/harvest/v2/cmd/collectors" + "github.com/netapp/harvest/v2/cmd/poller/plugin" + "github.com/netapp/harvest/v2/pkg/api/ontapi/zapi" + "github.com/netapp/harvest/v2/pkg/conf" + "github.com/netapp/harvest/v2/pkg/errs" + "github.com/netapp/harvest/v2/pkg/matrix" + "github.com/netapp/harvest/v2/pkg/tree/node" + "strings" +) + +type Aggregate struct { + *plugin.AbstractPlugin + client *zapi.Client + aggrCloudStoresMap map[string][]string // aggregate-uuid -> slice of cloud stores map +} + +func New(p *plugin.AbstractPlugin) plugin.Plugin { + return &Aggregate{AbstractPlugin: p} +} + +func (a *Aggregate) Init() error { + + var err error + + if err = a.InitAbc(); err != nil { + return err + } + + if a.client, err = zapi.New(conf.ZapiPoller(a.ParentParams), a.Auth); err != nil { + a.Logger.Error().Stack().Err(err).Msg("connecting") + return err + } + + if err = a.client.Init(5); err != nil { + return err + } + + a.aggrCloudStoresMap = make(map[string][]string) + return nil +} + +func (a *Aggregate) Run(dataMap map[string]*matrix.Matrix) ([]*matrix.Matrix, error) { + data := dataMap[a.Object] + + // invoke aggr-object-store-get-iter zapi and populate cloud stores info + if err := a.getCloudStores(); err != nil { + if errors.Is(err, errs.ErrNoInstance) { + a.Logger.Debug().Err(err).Msg("Failed to collect cloud store data") + return nil, nil + } + return nil, err + } + + // update aggregate instance label with cloud stores info + if len(a.aggrCloudStoresMap) > 0 { + for aggrUUID, aggr := range data.GetInstances() { + aggr.SetLabel("cloud_stores", strings.Join(a.aggrCloudStoresMap[aggrUUID], ",")) + } + } + return nil, nil +} + +func (a *Aggregate) getCloudStores() error { + var ( + result []*node.Node + err error + ) + + a.aggrCloudStoresMap = make(map[string][]string) + request := node.NewXMLS("aggr-object-store-get-iter") + request.NewChildS("max-records", collectors.DefaultBatchSize) + + desired := node.NewXMLS("desired-attributes") + objectStoreInfo := node.NewXMLS("object-store-information") + objectStoreInfo.NewChildS("aggregate-uuid", "") + objectStoreInfo.NewChildS("object-store-name", "") + desired.AddChild(objectStoreInfo) + request.AddChild(desired) + + if result, err = a.client.InvokeZapiCall(request); err != nil { + return err + } + + if len(result) == 0 || result == nil { + return errs.New(errs.ErrNoInstance, "no records found") + } + + for _, objectStore := range result { + aggregateUUID := objectStore.GetChildContentS("aggregate-uuid") + objectStoreName := objectStore.GetChildContentS("object-store-name") + a.aggrCloudStoresMap[aggregateUUID] = append(a.aggrCloudStoresMap[aggregateUUID], objectStoreName) + } + return nil +} diff --git a/conf/zapi/cdot/9.8.0/aggr.yaml b/conf/zapi/cdot/9.8.0/aggr.yaml index a2b4cfd39..f8ae35c7b 100644 --- a/conf/zapi/cdot/9.8.0/aggr.yaml +++ b/conf/zapi/cdot/9.8.0/aggr.yaml @@ -61,7 +61,8 @@ counters: - flexvol-count plugins: - LabelAgent: + - Aggregate + - LabelAgent: exclude_equals: - root_aggr `true` # metric label zapi_value rest_value `default_value` @@ -73,6 +74,7 @@ export_options: - aggr - node instance_labels: + - cloud_stores - is_encrypted - state - type