internal/processmetrics/hanavolume/hanavolume.go (122 lines of code) (raw):

/* Copyright 2024 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // Package hanavolume is responsible for collection of HANA Volume metrics. package hanavolume import ( "context" "path" "strings" "github.com/cenkalti/backoff/v4" "github.com/GoogleCloudPlatform/sapagent/internal/utils/protostruct" "github.com/GoogleCloudPlatform/workloadagentplatform/sharedlibraries/cloudmonitoring" "github.com/GoogleCloudPlatform/workloadagentplatform/sharedlibraries/commandlineexecutor" "github.com/GoogleCloudPlatform/workloadagentplatform/sharedlibraries/log" "github.com/GoogleCloudPlatform/workloadagentplatform/sharedlibraries/timeseries" mrpb "google.golang.org/genproto/googleapis/monitoring/v3" tspb "google.golang.org/protobuf/types/known/timestamppb" cnfpb "github.com/GoogleCloudPlatform/sapagent/protos/configuration" ) // Properties struct contains the parameters necessary for hanavolume package common methods. type Properties struct { Executor commandlineexecutor.Execute Config *cnfpb.Configuration Client cloudmonitoring.TimeSeriesCreator CommandParams commandlineexecutor.Params PMBackoffPolicy backoff.BackOffContext } const ( metricURL = "workload.googleapis.com" volumePath = "/sap/hana/volumes" hanaLog = "/hana/log" hanaShared = "/hana/shared" hanaBackup = "/hanabackup" hanaData = "/hana/data" usrSap = "/usr/sap" ) var mountPaths = map[string]string{ hanaLog: hanaLog, hanaShared: hanaShared, hanaBackup: hanaBackup, hanaData: hanaData, usrSap: usrSap, } /* Collect is an implementation of Collector interface defined in processmetrics.go. Collect method collects metrics to show the details about Hana logical volumes including total size, available space, amount used and usage percentage, for each volumes. */ func (p *Properties) Collect(ctx context.Context) ([]*mrpb.TimeSeries, error) { log.CtxLogger(ctx).Debug("Collecting hana volume metrics") result := p.Executor(ctx, commandlineexecutor.Params{ Executable: p.CommandParams.Executable, ArgsToSplit: p.CommandParams.ArgsToSplit, }) if result.Error != nil { log.CtxLogger(ctx).Debugw("could not execute df -h command", "error", result.Error) return nil, result.Error } return p.createTSList(ctx, result.StdOut), nil } // CollectWithRetry decorates the Collect method with retry mechanism. func (p *Properties) CollectWithRetry(ctx context.Context) ([]*mrpb.TimeSeries, error) { attempt := 1 var res []*mrpb.TimeSeries err := backoff.Retry(func() error { select { case <-ctx.Done(): log.CtxLogger(ctx).Debugw("Context cancelled, exiting CollectWithRetry") return nil default: var err error res, err = p.Collect(ctx) if err != nil { log.CtxLogger(ctx).Debugw("Error in Collection", "attempt", attempt, "error", err) attempt++ } return err } }, p.PMBackoffPolicy) if err != nil { log.CtxLogger(ctx).Infow("Retry limit exceeded", "error", err) } return res, err } // createTSList creates a slice of TimeSeries HANA logical volume metrics // to send to cloud monitoring. func (p *Properties) createTSList(ctx context.Context, cmdOutput string) []*mrpb.TimeSeries { var metrics []*mrpb.TimeSeries lines := strings.Split(cmdOutput, "\n") for _, line := range lines { items := strings.Fields(line) if len(items) == 0 { continue } if path, ok := mountPaths[items[len(items)-1]]; ok { if len(items) < 6 { log.CtxLogger(ctx).Debugw("too few items. need exactly 6", "length:", len(items), "line:", line) continue } size := items[1] used := items[2] avail := items[3] usage := items[4] volMetrics := p.collectVolumeMetrics(ctx, path, size, used, avail, usage) if volMetrics != nil { metrics = append(metrics, volMetrics...) } } } log.CtxLogger(ctx).Debug("HANA Volume Metrics created", metrics) return metrics } func (p *Properties) collectVolumeMetrics(ctx context.Context, path, size, used, avail, usage string) []*mrpb.TimeSeries { labels := map[string]string{ "mountPath": path, "size": size, "used": used, "avail": avail, "usage": usage, } return []*mrpb.TimeSeries{p.createMetric(labels)} } func (p *Properties) createMetric(labels map[string]string) *mrpb.TimeSeries { log.Logger.Debugw("Creating metric for instance", "metric", labels["mountPath"], "value", true, "labels", labels) ts := timeseries.Params{ CloudProp: protostruct.ConvertCloudPropertiesToStruct(p.Config.CloudProperties), MetricType: path.Join(metricURL, volumePath), MetricLabels: labels, Timestamp: tspb.Now(), BareMetal: p.Config.BareMetal, BoolValue: true, } log.Logger.Debug("Created metric path: ", ts.MetricType) return timeseries.BuildBool(ts) }