From 6123e78599feaf30bb127251782f9334e747e963 Mon Sep 17 00:00:00 2001 From: shefali-malhotra <91597668+shefali-malhotra@users.noreply.github.com> Date: Thu, 10 Oct 2024 12:13:38 +0530 Subject: [PATCH] Replication metrics support added for Powerstore (#85) * initial changes * fixed unit error Signed-off-by: shefali-malhotra * updated logic to read param of repl metrics * added mirror bw param to repl metrics * go mod tidy * added volume handle for metro * added volume handle for metro * fixed logic of volume handle Signed-off-by: shefali-malhotra * corrected the metrics tests * updated the metrics label * updated the label in volume test Signed-off-by: shefali-malhotra * updated the unit test cases Signed-off-by: shefali-malhotra * updated the unit test cases and go generate test cases Signed-off-by: shefali-malhotra * updated the unit test cases and go generate test cases Signed-off-by: shefali-malhotra * updated the licenses Signed-off-by: shefali-malhotra * reverted the unnecessary changes Signed-off-by: shefali-malhotra * updated the licenses Signed-off-by: shefali-malhotra * updated the licenses Signed-off-by: shefali-malhotra * uint64 issue Signed-off-by: shefali-malhotra * Delete ~ * Update service.go * added UTs to increase service coverage Signed-off-by: shefali-malhotra * fixed make format issue Signed-off-by: shefali-malhotra --------- Signed-off-by: shefali-malhotra --- Makefile | 4 + go.mod | 19 +-- go.sum | 49 ++----- internal/common/common.go | 2 +- internal/service/metrics.go | 89 +++++++++--- internal/service/metrics_test.go | 114 ++++++++++++++-- internal/service/mocks/metrics_mocks.go | 16 +-- .../service/mocks/powerstore_client_mocks.go | 15 ++ internal/service/service.go | 119 ++++++++++++---- internal/service/service_test.go | 128 +++++++++++++----- 10 files changed, 406 insertions(+), 149 deletions(-) diff --git a/Makefile b/Makefile index a8e11d8..a935eb5 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,10 @@ tag: check: ./scripts/check.sh ./cmd/... ./opentelemetry/... ./internal/... +.PHONY: format +format: + @gofmt -w -s . + .PHONY: download-csm-common download-csm-common: curl -O -L https://raw.githubusercontent.com/dell/csm/main/config/csm-common.mk diff --git a/go.mod b/go.mod index 2821938..b13a99d 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,12 @@ module github.com/dell/csm-metrics-powerstore -go 1.22.0 +go 1.23 + +toolchain go1.23.1 require ( github.com/dell/gocsi v1.11.0 - github.com/dell/gopowerstore v1.15.1 + github.com/dell/gopowerstore v1.15.2-0.20241008144633-6990d1b86e5b github.com/fsnotify/fsnotify v1.6.0 github.com/golang/mock v1.6.0 github.com/sirupsen/logrus v1.9.3 @@ -26,19 +28,18 @@ require ( ) require ( - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/errors v0.20.2 // indirect + github.com/go-openapi/errors v0.22.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/strfmt v0.21.2 // indirect + github.com/go-openapi/strfmt v0.23.0 // indirect github.com/go-openapi/swag v0.22.4 // indirect - github.com/go-stack/stack v1.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect @@ -66,14 +67,14 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.2.0 // indirect github.com/x448/float16 v0.8.4 // indirect - go.mongodb.org/mongo-driver v1.9.1 // indirect + go.mongodb.org/mongo-driver v1.17.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0 // indirect go.opentelemetry.io/proto/otlp v0.16.0 // indirect golang.org/x/net v0.26.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sys v0.21.0 // indirect + golang.org/x/sys v0.25.0 // indirect golang.org/x/term v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect google.golang.org/protobuf v1.34.2 // indirect diff --git a/go.sum b/go.sum index 57e68cc..a410ce2 100644 --- a/go.sum +++ b/go.sum @@ -37,9 +37,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/Shopify/sarama v1.30.0/go.mod h1:zujlQQx1kzHsh4jfV1USnptCQrHAEZ2Hk8fTKCulPVs= github.com/Shopify/toxiproxy/v2 v2.1.6-0.20210914104332-15ea381dcdae/go.mod h1:/cvHQkZ1fst0EmZnA5dFtiQdWCNCFYzb+uE2vqVgvx0= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= @@ -67,8 +66,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dell/gocsi v1.11.0 h1:P84VOPd1V55JQjx4tfd/6QOlVQRQkYUqmGqbzPKeyUQ= github.com/dell/gocsi v1.11.0/go.mod h1:LzGAsEIjBxVXJuabzsG3/MsdCOczxDE1IWOBxzXIUhw= -github.com/dell/gopowerstore v1.15.1 h1:m7pxlXqakt6ILy1W35VzXobOsvPOWIOImdhHBa8R38E= -github.com/dell/gopowerstore v1.15.1/go.mod h1:6oAr11lwtpjmlE+s+pDKx7qfQ1HvLFWtlFoNdI03ByQ= +github.com/dell/gopowerstore v1.15.2-0.20241008144633-6990d1b86e5b h1:wVpvFdhc4FIl7w73fm9aTGtE3iTfoO5Ag7/WQUNdHUU= +github.com/dell/gopowerstore v1.15.2-0.20241008144633-6990d1b86e5b/go.mod h1:EQEsDdulHtvpDIe2tMCW/2swr1enisuzW4Zmw6c2SKc= github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -100,21 +99,17 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8= -github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= +github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/strfmt v0.21.2 h1:5NDNgadiX1Vhemth/TH4gCGopWSTdDjxl60H3B7f+os= -github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= +github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= +github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -157,7 +152,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -170,7 +164,6 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -193,7 +186,6 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -242,7 +234,6 @@ github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaW github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -250,10 +241,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= @@ -321,23 +310,19 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= -go.mongodb.org/mongo-driver v1.9.1 h1:m078y9v7sBItkt1aaoe2YlvWEXcD263e1a4E1fBrJ1c= -go.mongodb.org/mongo-driver v1.9.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= +go.mongodb.org/mongo-driver v1.17.0 h1:Hp4q2MCjvY19ViwimTs00wHi7G4yzxh4/2+nTx8r40k= +go.mongodb.org/mongo-driver v1.17.0/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -369,10 +354,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -499,9 +482,8 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= @@ -513,8 +495,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -529,7 +511,6 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -664,7 +645,6 @@ google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6h google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -684,7 +664,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/common/common.go b/internal/common/common.go index be8b0cd..eebba02 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -90,7 +90,7 @@ func GetPowerStoreArrays(filePath string, logger *logrus.Logger) (map[string]*se if err != nil { logger.Errorf("can't get throttling rate limit, using default") } else { - clientOptions.SetRateLimit(uint64(rateLimit)) // #nosec G115 -- This is a false positive + clientOptions.SetRateLimit(rateLimit) // #nosec G115 -- This is a false positive } } diff --git a/internal/service/metrics.go b/internal/service/metrics.go index 3c3966a..236d853 100644 --- a/internal/service/metrics.go +++ b/internal/service/metrics.go @@ -32,7 +32,7 @@ type MetricsRecorder interface { Record(ctx context.Context, meta interface{}, readBW, writeBW, readIOPS, writeIOPS, - readLatency, writeLatency float32) error + readLatency, writeLatency, syncronizationBW, mirrorBW, dataRemaining float32) error RecordSpaceMetrics(ctx context.Context, meta interface{}, logicalProvisioned, logicalUsed int64) error RecordArraySpaceMetrics(ctx context.Context, arrayID, driver string, @@ -42,7 +42,7 @@ type MetricsRecorder interface { RecordFileSystemMetrics(ctx context.Context, meta interface{}, readBW, writeBW, readIOPS, writeIOPS, - readLatency, writeLatency float32) error + readLatency, writeLatency, syncronizationBW, mirrorBW, dataRemaining float32) error } // Float64UpDownCounterCreater creates a Float64UpDownCounter InstrumentProvider @@ -75,12 +75,15 @@ type ArraySpaceMetrics struct { // Metrics contains the list of metrics data that is collected type Metrics struct { - ReadBW asyncfloat64.UpDownCounter - WriteBW asyncfloat64.UpDownCounter - ReadIOPS asyncfloat64.UpDownCounter - WriteIOPS asyncfloat64.UpDownCounter - ReadLatency asyncfloat64.UpDownCounter - WriteLatency asyncfloat64.UpDownCounter + ReadBW asyncfloat64.UpDownCounter + WriteBW asyncfloat64.UpDownCounter + ReadIOPS asyncfloat64.UpDownCounter + WriteIOPS asyncfloat64.UpDownCounter + ReadLatency asyncfloat64.UpDownCounter + WriteLatency asyncfloat64.UpDownCounter + SyncronizationBW asyncfloat64.UpDownCounter + MirrorBW asyncfloat64.UpDownCounter + DataRemaining asyncfloat64.UpDownCounter } func (mw *MetricsWrapper) initMetrics(prefix, metaID string, labels []attribute.KeyValue) (*Metrics, error) { @@ -114,13 +117,31 @@ func (mw *MetricsWrapper) initMetrics(prefix, metaID string, labels []attribute. return nil, err } + syncBW, err := mw.Meter.AsyncFloat64().UpDownCounter(prefix + "syncronization_bw_megabytes_per_second") + if err != nil { + return nil, err + } + + mirrorBW, err := mw.Meter.AsyncFloat64().UpDownCounter(prefix + "mirror_bw_megabytes_per_second") + if err != nil { + return nil, err + } + + dataRemaining, err := mw.Meter.AsyncFloat64().UpDownCounter(prefix + "data_remaining_bytes") + if err != nil { + return nil, err + } + metrics := &Metrics{ - ReadBW: readBW, - WriteBW: writeBW, - ReadIOPS: readIOPS, - WriteIOPS: writeIOPS, - ReadLatency: readLatency, - WriteLatency: writeLatency, + ReadBW: readBW, + WriteBW: writeBW, + ReadIOPS: readIOPS, + WriteIOPS: writeIOPS, + ReadLatency: readLatency, + WriteLatency: writeLatency, + SyncronizationBW: syncBW, + MirrorBW: mirrorBW, + DataRemaining: dataRemaining, } mw.Metrics.Store(metaID, metrics) @@ -133,7 +154,7 @@ func (mw *MetricsWrapper) initMetrics(prefix, metaID string, labels []attribute. func (mw *MetricsWrapper) Record(ctx context.Context, meta interface{}, readBW, writeBW, readIOPS, writeIOPS, - readLatency, writeLatency float32, + readLatency, writeLatency, syncronizationBW, mirrorBW, dataRemaining float32, ) error { var prefix string var metaID string @@ -201,6 +222,9 @@ func (mw *MetricsWrapper) Record(ctx context.Context, meta interface{}, metrics.WriteIOPS.Observe(ctx, float64(writeIOPS), labels...) metrics.ReadLatency.Observe(ctx, float64(readLatency), labels...) metrics.WriteLatency.Observe(ctx, float64(writeLatency), labels...) + metrics.SyncronizationBW.Observe(ctx, float64(syncronizationBW), labels...) + metrics.MirrorBW.Observe(ctx, float64(mirrorBW), labels...) + metrics.DataRemaining.Observe(ctx, float64(dataRemaining), labels...) return nil } @@ -492,13 +516,31 @@ func (mw *MetricsWrapper) initFileSystemMetrics(prefix, metaID string, labels [] return nil, err } + syncBW, err := mw.Meter.AsyncFloat64().UpDownCounter(prefix + "syncronization_bw_megabytes_per_second") + if err != nil { + return nil, err + } + + mirrorBW, err := mw.Meter.AsyncFloat64().UpDownCounter(prefix + "mirror_bw_megabytes_per_second") + if err != nil { + return nil, err + } + + dataRemaining, err := mw.Meter.AsyncFloat64().UpDownCounter(prefix + "data_remaining_bytes") + if err != nil { + return nil, err + } + metrics := &Metrics{ - ReadBW: readBW, - WriteBW: writeBW, - ReadIOPS: readIOPS, - WriteIOPS: writeIOPS, - ReadLatency: readLatency, - WriteLatency: writeLatency, + ReadBW: readBW, + WriteBW: writeBW, + ReadIOPS: readIOPS, + WriteIOPS: writeIOPS, + ReadLatency: readLatency, + WriteLatency: writeLatency, + SyncronizationBW: syncBW, + MirrorBW: mirrorBW, + DataRemaining: dataRemaining, } mw.Metrics.Store(metaID, metrics) @@ -511,7 +553,7 @@ func (mw *MetricsWrapper) initFileSystemMetrics(prefix, metaID string, labels [] func (mw *MetricsWrapper) RecordFileSystemMetrics(ctx context.Context, meta interface{}, readBW, writeBW, readIOPS, writeIOPS, - readLatency, writeLatency float32, + readLatency, writeLatency, syncBW, mirrorBW, dataRemaining float32, ) error { var prefix string var metaID string @@ -580,6 +622,9 @@ func (mw *MetricsWrapper) RecordFileSystemMetrics(ctx context.Context, meta inte metrics.WriteIOPS.Observe(ctx, float64(writeIOPS), labels...) metrics.ReadLatency.Observe(ctx, float64(readLatency), labels...) metrics.WriteLatency.Observe(ctx, float64(writeLatency), labels...) + metrics.DataRemaining.Observe(ctx, float64(dataRemaining), labels...) + metrics.SyncronizationBW.Observe(ctx, float64(syncBW), labels...) + metrics.MirrorBW.Observe(ctx, float64(mirrorBW), labels...) return nil } diff --git a/internal/service/metrics_test.go b/internal/service/metrics_test.go index 4926305..750d1af 100644 --- a/internal/service/metrics_test.go +++ b/internal/service/metrics_test.go @@ -89,13 +89,31 @@ func Test_Metrics_Record(t *testing.T) { t.Fatal(err) } - meter.EXPECT().AsyncFloat64().Return(provider).Times(6) + syncBW, err := otMeter.AsyncFloat64().UpDownCounter(prefix + "syncronization_bw_megabytes_per_second") + if err != nil { + t.Fatal(err) + } + + mirrorBW, err := otMeter.AsyncFloat64().UpDownCounter(prefix + "mirror_bw_megabytes_per_second") + if err != nil { + t.Fatal(err) + } + + dataRemaining, err := otMeter.AsyncFloat64().UpDownCounter(prefix + "data_remaining_bytes") + if err != nil { + t.Fatal(err) + } + + meter.EXPECT().AsyncFloat64().Return(provider).Times(9) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readBW, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeBW, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readIOPS, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeIOPS, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readLatency, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeLatency, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(syncBW, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(mirrorBW, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(dataRemaining, nil) return &service.MetricsWrapper{ Meter: meter, @@ -349,7 +367,7 @@ func Test_Metrics_Record(t *testing.T) { t.Run(name, func(t *testing.T) { mws, checks := tc(t) for i := range mws { - err := mws[i].Record(context.Background(), metas[i], 1, 2, 3, 4, 5, 6) + err := mws[i].Record(context.Background(), metas[i], 1, 2, 3, 4, 5, 6, 7, 8, 9) for _, check := range checks { check(t, err) } @@ -410,13 +428,31 @@ func Test_Metrics_Record_Meta(t *testing.T) { t.Fatal(err) } - meter.EXPECT().AsyncFloat64().Return(provider).Times(6) + syncBW, err := otMeter.AsyncFloat64().UpDownCounter(prefix + "syncronization_bw_megabytes_per_second") + if err != nil { + t.Fatal(err) + } + + mirrorBW, err := otMeter.AsyncFloat64().UpDownCounter(prefix + "mirror_bw_megabytes_per_second") + if err != nil { + t.Fatal(err) + } + + dataRemaining, err := otMeter.AsyncFloat64().UpDownCounter(prefix + "data_remaining_bytes") + if err != nil { + t.Fatal(err) + } + + meter.EXPECT().AsyncFloat64().Return(provider).Times(9) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readBW, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeBW, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readIOPS, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeIOPS, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readLatency, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeLatency, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(syncBW, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(mirrorBW, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(dataRemaining, nil) return &service.MetricsWrapper{ Meter: meter, @@ -435,7 +471,7 @@ func Test_Metrics_Record_Meta(t *testing.T) { t.Run(name, func(t *testing.T) { mws, checks := tc(t) for i := range mws { - err := mws[i].Record(context.Background(), metas[i], 1, 2, 3, 4, 5, 6) + err := mws[i].Record(context.Background(), metas[i], 1, 2, 3, 4, 5, 6, 7, 8, 9) for _, check := range checks { check(t, err) } @@ -837,24 +873,42 @@ func Test_Volume_Metrics_Label_Update(t *testing.T) { t.Fatal(err) } - meter.EXPECT().AsyncFloat64().Return(provider).Times(6) + syncBW, err := otMeter.AsyncFloat64().UpDownCounter("syncronization_bw_megabytes_per_second") + if err != nil { + t.Fatal(err) + } + + mirrorBW, err := otMeter.AsyncFloat64().UpDownCounter("mirror_bw_megabytes_per_second") + if err != nil { + t.Fatal(err) + } + + dataRemaining, err := otMeter.AsyncFloat64().UpDownCounter("data_remaining_bytes") + if err != nil { + t.Fatal(err) + } + + meter.EXPECT().AsyncFloat64().Return(provider).Times(9) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readBW, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeBW, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readIOPS, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeIOPS, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readLatency, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeLatency, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(syncBW, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(mirrorBW, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(dataRemaining, nil) mw := &service.MetricsWrapper{ Meter: meter, } t.Run("success: volume metric labels updated", func(t *testing.T) { - err := mw.Record(context.Background(), metaFirst, 1, 2, 3, 4, 5, 6) + err := mw.Record(context.Background(), metaFirst, 1, 2, 3, 4, 5, 6, 7, 8, 9) if err != nil { t.Errorf("expected nil error (record #1), got %v", err) } - err = mw.Record(context.Background(), metaSecond, 1, 2, 3, 4, 5, 6) + err = mw.Record(context.Background(), metaSecond, 1, 2, 3, 4, 5, 6, 7, 8, 9) if err != nil { t.Errorf("expected nil error (record #2), got %v", err) } @@ -1195,24 +1249,42 @@ func Test_FileSystem_Metrics_Label_Update(t *testing.T) { t.Fatal(err) } - meter.EXPECT().AsyncFloat64().Return(provider).Times(6) + syncBW, err := otMeter.AsyncFloat64().UpDownCounter("syncronization_bw_megabytes_per_second") + if err != nil { + t.Fatal(err) + } + + mirrorBW, err := otMeter.AsyncFloat64().UpDownCounter("mirror_bw_megabytes_per_second") + if err != nil { + t.Fatal(err) + } + + dataRemaining, err := otMeter.AsyncFloat64().UpDownCounter("data_remaining_bytes") + if err != nil { + t.Fatal(err) + } + + meter.EXPECT().AsyncFloat64().Return(provider).Times(9) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readBW, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeBW, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readIOPS, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeIOPS, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readLatency, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeLatency, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(syncBW, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(mirrorBW, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(dataRemaining, nil) mw := &service.MetricsWrapper{ Meter: meter, } t.Run("success: filesystem metric labels updated", func(t *testing.T) { - err := mw.RecordFileSystemMetrics(context.Background(), metaFirst, 1, 2, 3, 4, 5, 6) + err := mw.RecordFileSystemMetrics(context.Background(), metaFirst, 1, 2, 3, 4, 5, 6, 7, 8, 9) if err != nil { t.Errorf("expected nil error (record #1), got %v", err) } - err = mw.RecordFileSystemMetrics(context.Background(), metaSecond, 1, 2, 3, 4, 5, 6) + err = mw.RecordFileSystemMetrics(context.Background(), metaSecond, 1, 2, 3, 4, 5, 6, 7, 8, 9) if err != nil { t.Errorf("expected nil error (record #2), got %v", err) } @@ -1294,13 +1366,31 @@ func Test_Record_FileSystem_Metrics(t *testing.T) { t.Fatal(err) } - meter.EXPECT().AsyncFloat64().Return(provider).Times(6) + syncBW, err := otMeter.AsyncFloat64().UpDownCounter("syncronization_bw_megabytes_per_second") + if err != nil { + t.Fatal(err) + } + + mirrorBW, err := otMeter.AsyncFloat64().UpDownCounter("mirror_bw_megabytes_per_second") + if err != nil { + t.Fatal(err) + } + + dataRemaining, err := otMeter.AsyncFloat64().UpDownCounter("data_remaining_bytes") + if err != nil { + t.Fatal(err) + } + + meter.EXPECT().AsyncFloat64().Return(provider).Times(9) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readBW, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeBW, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readIOPS, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeIOPS, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(readLatency, nil) provider.EXPECT().UpDownCounter(gomock.Any()).Return(writeLatency, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(syncBW, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(mirrorBW, nil) + provider.EXPECT().UpDownCounter(gomock.Any()).Return(dataRemaining, nil) return &service.MetricsWrapper{ Meter: meter, @@ -1551,7 +1641,7 @@ func Test_Record_FileSystem_Metrics(t *testing.T) { t.Run(name, func(t *testing.T) { mws, checks := tc(t) for i := range mws { - err := mws[i].RecordFileSystemMetrics(context.Background(), metas[i], 1, 2, 3, 4, 5, 6) + err := mws[i].RecordFileSystemMetrics(context.Background(), metas[i], 1, 2, 3, 4, 5, 6, 7, 8, 9) for _, check := range checks { check(t, err) } diff --git a/internal/service/mocks/metrics_mocks.go b/internal/service/mocks/metrics_mocks.go index 81d7989..d8a4e2a 100644 --- a/internal/service/mocks/metrics_mocks.go +++ b/internal/service/mocks/metrics_mocks.go @@ -52,17 +52,17 @@ func (m *MockMetricsRecorder) EXPECT() *MockMetricsRecorderMockRecorder { } // Record mocks base method. -func (m *MockMetricsRecorder) Record(arg0 context.Context, arg1 interface{}, arg2, arg3, arg4, arg5, arg6, arg7 float32) error { +func (m *MockMetricsRecorder) Record(arg0 context.Context, arg1 interface{}, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 float32) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Record", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) + ret := m.ctrl.Call(m, "Record", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) ret0, _ := ret[0].(error) return ret0 } // Record indicates an expected call of Record. -func (mr *MockMetricsRecorderMockRecorder) Record(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call { +func (mr *MockMetricsRecorderMockRecorder) Record(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Record", reflect.TypeOf((*MockMetricsRecorder)(nil).Record), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Record", reflect.TypeOf((*MockMetricsRecorder)(nil).Record), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) } // RecordArraySpaceMetrics mocks base method. @@ -80,17 +80,17 @@ func (mr *MockMetricsRecorderMockRecorder) RecordArraySpaceMetrics(arg0, arg1, a } // RecordFileSystemMetrics mocks base method. -func (m *MockMetricsRecorder) RecordFileSystemMetrics(arg0 context.Context, arg1 interface{}, arg2, arg3, arg4, arg5, arg6, arg7 float32) error { +func (m *MockMetricsRecorder) RecordFileSystemMetrics(arg0 context.Context, arg1 interface{}, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 float32) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RecordFileSystemMetrics", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) + ret := m.ctrl.Call(m, "RecordFileSystemMetrics", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) ret0, _ := ret[0].(error) return ret0 } // RecordFileSystemMetrics indicates an expected call of RecordFileSystemMetrics. -func (mr *MockMetricsRecorderMockRecorder) RecordFileSystemMetrics(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call { +func (mr *MockMetricsRecorderMockRecorder) RecordFileSystemMetrics(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordFileSystemMetrics", reflect.TypeOf((*MockMetricsRecorder)(nil).RecordFileSystemMetrics), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordFileSystemMetrics", reflect.TypeOf((*MockMetricsRecorder)(nil).RecordFileSystemMetrics), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) } // RecordSpaceMetrics mocks base method. diff --git a/internal/service/mocks/powerstore_client_mocks.go b/internal/service/mocks/powerstore_client_mocks.go index 454dbab..24f5dd9 100644 --- a/internal/service/mocks/powerstore_client_mocks.go +++ b/internal/service/mocks/powerstore_client_mocks.go @@ -110,3 +110,18 @@ func (mr *MockPowerStoreClientMockRecorder) SpaceMetricsByVolume(arg0, arg1, arg mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpaceMetricsByVolume", reflect.TypeOf((*MockPowerStoreClient)(nil).SpaceMetricsByVolume), arg0, arg1, arg2) } + +// VolumeMirrorTransferRate mocks base method. +func (m *MockPowerStoreClient) VolumeMirrorTransferRate(arg0 context.Context, arg1 string) ([]gopowerstore.VolumeMirrorTransferRateResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VolumeMirrorTransferRate", arg0, arg1) + ret0, _ := ret[0].([]gopowerstore.VolumeMirrorTransferRateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// VolumeMirrorTransferRate indicates an expected call of VolumeMirrorTransferRate. +func (mr *MockPowerStoreClientMockRecorder) VolumeMirrorTransferRate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeMirrorTransferRate", reflect.TypeOf((*MockPowerStoreClient)(nil).VolumeMirrorTransferRate), arg0, arg1) +} diff --git a/internal/service/service.go b/internal/service/service.go index e2553a4..541a031 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -36,8 +36,10 @@ const ( DefaultMaxPowerStoreConnections = 10 // ExpectedVolumeHandleProperties is the number of properties that the VolumeHandle contains ExpectedVolumeHandleProperties = 3 - scsiProtocol = "scsi" - nfsProtocol = "nfs" + // ExpectedVolumeHandleMetroProperties is the number of properties that the VolumeHandle of metro volumes contains + ExpectedVolumeHandleMetroProperties = 4 + scsiProtocol = "scsi" + nfsProtocol = "nfs" ) var _ Service = (*PowerStoreService)(nil) @@ -87,6 +89,7 @@ type PowerStoreClient interface { SpaceMetricsByVolume(context.Context, string, gopowerstore.MetricsIntervalEnum) ([]gopowerstore.SpaceMetricsByVolumeResponse, error) PerformanceMetricsByFileSystem(context.Context, string, gopowerstore.MetricsIntervalEnum) ([]gopowerstore.PerformanceMetricsByFileSystemResponse, error) GetFS(context.Context, string) (gopowerstore.FileSystem, error) + VolumeMirrorTransferRate(ctx context.Context, id string) ([]gopowerstore.VolumeMirrorTransferRateResponse, error) } // PowerStoreService represents the service for getting metrics data for a PowerStore system @@ -119,7 +122,8 @@ type VolumeMetricsRecord struct { volumeMeta *VolumeMeta readBW, writeBW, readIOPS, writeIOPS, - readLatency, writeLatency float32 + readLatency, writeLatency, + synchronizationBW, mirrorBW, remainingData float32 } // VolumeSpaceMetricsRecord used for holding output of the Volume space metrics query results @@ -187,6 +191,7 @@ func (s *PowerStoreService) gatherVolumeMetrics(ctx context.Context, volumes <-c var wg sync.WaitGroup sem := make(chan struct{}, s.MaxPowerStoreConnections) + var volumeID, arrayID, protocol string go func() { ctx, span := tracer.GetTracer(ctx, "gatherVolumeMetrics") defer span.End() @@ -204,15 +209,19 @@ func (s *PowerStoreService) gatherVolumeMetrics(ctx context.Context, volumes <-c // VolumeHandle is of the format "volume-id/array-ip/protocol" volumeProperties := strings.Split(volume.VolumeHandle, "/") - if len(volumeProperties) != ExpectedVolumeHandleProperties { + if len(volumeProperties) == ExpectedVolumeHandleProperties { + volumeID = volumeProperties[0] + arrayID = volumeProperties[1] + protocol = volumeProperties[2] + } else if len(volumeProperties) == ExpectedVolumeHandleMetroProperties { + volumeID = volumeProperties[0] + arrayID = volumeProperties[1] + protocol = strings.Split(volumeProperties[2], ":")[0] + } else { s.Logger.WithField("volume_handle", volume.VolumeHandle).Warn("unable to get Volume ID and Array IP from volume handle") return } - volumeID := volumeProperties[0] - arrayID := volumeProperties[1] - protocol := volumeProperties[2] - // skip Persistent Volumes that don't have a protocol of 'scsi', such as nfs file systems if !strings.EqualFold(protocol, scsiProtocol) { s.Logger.WithFields(logrus.Fields{"protocol": protocol, "persistent_volume": volume.PersistentVolume}).Debugf("persistent volume is not %s", scsiProtocol) @@ -234,12 +243,13 @@ func (s *PowerStoreService) gatherVolumeMetrics(ctx context.Context, volumes <-c } metrics, err := goPowerStoreClient.PerformanceMetricsByVolume(ctx, volumeID, gopowerstore.TwentySec) + if err != nil { s.Logger.WithError(err).WithField("volume_id", volumeMeta.ID).Error("getting performance metrics for volume") return } - var readBW, writeBW, readIOPS, writeIOPS, readLatency, writeLatency float32 + var readBW, writeBW, readIOPS, writeIOPS, readLatency, writeLatency, syncBW, mirrorBW, remainingData float32 s.Logger.WithFields(logrus.Fields{ "volume_performance_metrics": len(metrics), @@ -257,14 +267,33 @@ func (s *PowerStoreService) gatherVolumeMetrics(ctx context.Context, volumes <-c writeLatency = toMilliseconds(latestMetric.AvgWriteLatency) } + //Read the replication parameter + replicationMetrics, err := goPowerStoreClient.VolumeMirrorTransferRate(ctx, volumeID) + + s.Logger.WithFields(logrus.Fields{ + "volume_replication_metrics": len(replicationMetrics), + "volume_id": volumeMeta.ID, + "array_ip": volumeMeta.ArrayID, + }).Debug("volume replication metrics returned for volume") + + if len(replicationMetrics) > 0 { + latestRepMetrics := replicationMetrics[len(replicationMetrics)-1] + syncBW = toMegabytes(latestRepMetrics.SynchronizationBandwidth) + mirrorBW = toMegabytes(latestRepMetrics.MirrorBandwidth) + remainingData = toMegabytes(latestRepMetrics.DataRemaining) + } + s.Logger.WithFields(logrus.Fields{ - "volume_meta": volumeMeta, - "read_bandwidth": readBW, - "write_bandwidth": writeBW, - "read_iops": readIOPS, - "write_iops": writeIOPS, - "read_latency": readLatency, - "write_latency": writeLatency, + "volume_meta": volumeMeta, + "read_bandwidth": readBW, + "write_bandwidth": writeBW, + "read_iops": readIOPS, + "write_iops": writeIOPS, + "read_latency": readLatency, + "write_latency": writeLatency, + "syncronization_bandwidth": syncBW, + "mirror_bandwidth": mirrorBW, + "remaining_data": remainingData, }).Debug("volume metrics") ch <- &VolumeMetricsRecord{ @@ -272,6 +301,7 @@ func (s *PowerStoreService) gatherVolumeMetrics(ctx context.Context, volumes <-c readBW: readBW, writeBW: writeBW, readIOPS: readIOPS, writeIOPS: writeIOPS, readLatency: readLatency, writeLatency: writeLatency, + synchronizationBW: syncBW, mirrorBW: mirrorBW, remainingData: remainingData, } }(volume) } @@ -284,6 +314,7 @@ func (s *PowerStoreService) gatherVolumeMetrics(ctx context.Context, volumes <-c readBW: 0, writeBW: 0, readIOPS: 0, writeIOPS: 0, readLatency: 0, writeLatency: 0, + synchronizationBW: 0, mirrorBW: 0, remainingData: 0, } } wg.Wait() @@ -313,6 +344,7 @@ func (s *PowerStoreService) pushVolumeMetrics(ctx context.Context, volumeMetrics metrics.readBW, metrics.writeBW, metrics.readIOPS, metrics.writeIOPS, metrics.readLatency, metrics.writeLatency, + metrics.synchronizationBW, metrics.mirrorBW, metrics.remainingData, ) if err != nil { s.Logger.WithError(err).WithField("volume_id", metrics.volumeMeta.ID).Error("recording statistics for volume") @@ -777,6 +809,8 @@ func (s *PowerStoreService) gatherFileSystemMetrics(ctx context.Context, volumes var wg sync.WaitGroup sem := make(chan struct{}, s.MaxPowerStoreConnections) + var volumeID, arrayID, protocol string + go func() { ctx, span := tracer.GetTracer(ctx, "gatherFileSystemMetrics") defer span.End() @@ -794,15 +828,20 @@ func (s *PowerStoreService) gatherFileSystemMetrics(ctx context.Context, volumes // VolumeHandle is of the format "volume-id/array-ip/protocol" volumeProperties := strings.Split(volume.VolumeHandle, "/") - if len(volumeProperties) != ExpectedVolumeHandleProperties { + if len(volumeProperties) == ExpectedVolumeHandleProperties { + volumeID = volumeProperties[0] + arrayID = volumeProperties[1] + protocol = volumeProperties[2] + // VolumeHandle is of the format "src-volume-id/array-ip/protocol:dest-volume-id/dest-array-ip" + } else if len(volumeProperties) == ExpectedVolumeHandleMetroProperties { + volumeID = volumeProperties[0] + arrayID = volumeProperties[1] + protocol = strings.Split(volumeProperties[2], ":")[0] + } else { s.Logger.WithField("volume_handle", volume.VolumeHandle).Warn("unable to get Volume ID and Array IP from volume handle") return } - volumeID := volumeProperties[0] - arrayID := volumeProperties[1] - protocol := volumeProperties[2] - // skip Persistent Volumes that don't have a protocol of 'nfs' if !strings.EqualFold(protocol, nfsProtocol) { s.Logger.WithFields(logrus.Fields{"protocol": protocol, "persistent_volume": volume.PersistentVolume}).Debugf("persistent volume is not %s", nfsProtocol) @@ -830,7 +869,7 @@ func (s *PowerStoreService) gatherFileSystemMetrics(ctx context.Context, volumes return } - var readBW, writeBW, readIOPS, writeIOPS, readLatency, writeLatency float32 + var readBW, writeBW, readIOPS, writeIOPS, readLatency, writeLatency, syncBW, mirrorBW, remainingData float32 s.Logger.WithFields(logrus.Fields{ "filesystem_performance_metrics": len(metrics), @@ -848,14 +887,33 @@ func (s *PowerStoreService) gatherFileSystemMetrics(ctx context.Context, volumes writeLatency = toMilliseconds(latestMetric.AvgWriteLatency) } + //Read the replication parameter + replicationMetrics, err := goPowerStoreClient.VolumeMirrorTransferRate(ctx, volumeID) + + s.Logger.WithFields(logrus.Fields{ + "volume_replication_metrics": len(replicationMetrics), + "volume_id": volumeMeta.ID, + "array_ip": volumeMeta.ArrayID, + }).Debug("volume replication metrics returned for volume") + + if len(replicationMetrics) > 0 { + latestRepMetrics := replicationMetrics[len(replicationMetrics)-1] + syncBW = toMegabytes(latestRepMetrics.SynchronizationBandwidth) + mirrorBW = toMegabytes(latestRepMetrics.MirrorBandwidth) + remainingData = toMegabytes(latestRepMetrics.DataRemaining) + } + s.Logger.WithFields(logrus.Fields{ - "volume_meta": volumeMeta, - "read_bandwidth": readBW, - "write_bandwidth": writeBW, - "read_iops": readIOPS, - "write_iops": writeIOPS, - "read_latency": readLatency, - "write_latency": writeLatency, + "volume_meta": volumeMeta, + "read_bandwidth": readBW, + "write_bandwidth": writeBW, + "read_iops": readIOPS, + "write_iops": writeIOPS, + "read_latency": readLatency, + "write_latency": writeLatency, + "syncronization_bandwidth": syncBW, + "mirror_bandwidth": mirrorBW, + "remaining_data": remainingData, }).Debug("volume metrics") ch <- &VolumeMetricsRecord{ @@ -863,6 +921,7 @@ func (s *PowerStoreService) gatherFileSystemMetrics(ctx context.Context, volumes readBW: readBW, writeBW: writeBW, readIOPS: readIOPS, writeIOPS: writeIOPS, readLatency: readLatency, writeLatency: writeLatency, + synchronizationBW: syncBW, mirrorBW: mirrorBW, remainingData: remainingData, } }(volume) } @@ -875,6 +934,7 @@ func (s *PowerStoreService) gatherFileSystemMetrics(ctx context.Context, volumes readBW: 0, writeBW: 0, readIOPS: 0, writeIOPS: 0, readLatency: 0, writeLatency: 0, + synchronizationBW: 0, mirrorBW: 0, remainingData: 0, } } wg.Wait() @@ -904,6 +964,7 @@ func (s *PowerStoreService) pushFileSystemMetrics(ctx context.Context, volumeMet metrics.readBW, metrics.writeBW, metrics.readIOPS, metrics.writeIOPS, metrics.readLatency, metrics.writeLatency, + metrics.synchronizationBW, metrics.mirrorBW, metrics.remainingData, ) if err != nil { s.Logger.WithError(err).WithField("volume_id", metrics.volumeMeta.ID).Error("recording statistics for volume") diff --git a/internal/service/service_test.go b/internal/service/service_test.go index 904ee90..f420e04 100644 --- a/internal/service/service_test.go +++ b/internal/service/service_test.go @@ -40,7 +40,7 @@ func Test_ExportVolumeStatistics(t *testing.T) { "success": func(*testing.T) (service.PowerStoreService, *gomock.Controller) { ctrl := gomock.NewController(t) metrics := mocks.NewMockMetricsRecorder(ctrl) - metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) volFinder := mocks.NewMockVolumeFinder(ctrl) volFinder.EXPECT().GetPersistentVolumes(gomock.Any()).Return([]k8s.VolumeInfo{ @@ -48,16 +48,7 @@ func Test_ExportVolumeStatistics(t *testing.T) { PersistentVolume: "pv-1", VolumeHandle: "volume-1/127.0.0.1/scsi", }, - { - PersistentVolume: "pv-2", - VolumeHandle: "volume-2/127.0.0.1/scsi", - }, - { - PersistentVolume: "pv-3", - VolumeHandle: "volume-2/127.0.0.1/scsi", - }, }, nil).Times(1) - clients := make(map[string]service.PowerStoreClient) c := mocks.NewMockPowerStoreClient(ctrl) c.EXPECT().PerformanceMetricsByVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return([]gopowerstore.PerformanceMetricsByVolumeResponse{ @@ -73,7 +64,17 @@ func Test_ExportVolumeStatistics(t *testing.T) { AvgWriteLatency: 1, }, }, - }, nil).Times(3) + }, nil).Times(1) + + c.EXPECT().VolumeMirrorTransferRate(gomock.Any(), gomock.Any()).Return([]gopowerstore.VolumeMirrorTransferRateResponse{ + { + ID: "1", + SynchronizationBandwidth: 1, + MirrorBandwidth: 1, + DataRemaining: 1, + }, + }, nil).Times(1) + clients["127.0.0.1"] = c service := service.PowerStoreService{ @@ -86,7 +87,7 @@ func Test_ExportVolumeStatistics(t *testing.T) { "metrics not pushed if volume does not have scsi protocol": func(*testing.T) (service.PowerStoreService, *gomock.Controller) { ctrl := gomock.NewController(t) metrics := mocks.NewMockMetricsRecorder(ctrl) - metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) + metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) volFinder := mocks.NewMockVolumeFinder(ctrl) volFinder.EXPECT().GetPersistentVolumes(gomock.Any()).Return([]k8s.VolumeInfo{ @@ -108,10 +109,43 @@ func Test_ExportVolumeStatistics(t *testing.T) { } return service, ctrl }, + "metrics pushed if volume is metro and have scsi protocol": func(*testing.T) (service.PowerStoreService, *gomock.Controller) { + ctrl := gomock.NewController(t) + metrics := mocks.NewMockMetricsRecorder(ctrl) + metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) + + volFinder := mocks.NewMockVolumeFinder(ctrl) + volFinder.EXPECT().GetPersistentVolumes(gomock.Any()).Return([]k8s.VolumeInfo{ + { + PersistentVolume: "pv-1", + VolumeHandle: "volume-1/127.0.0.1/scsi:volume-2/127.0.0.1", + }, + }, nil).Times(1) + + clients := make(map[string]service.PowerStoreClient) + c := mocks.NewMockPowerStoreClient(ctrl) + c.EXPECT().PerformanceMetricsByVolume(gomock.Any(), gomock.Any(), gomock.Any()).Times(1) + c.EXPECT().VolumeMirrorTransferRate(gomock.Any(), gomock.Any()).Return([]gopowerstore.VolumeMirrorTransferRateResponse{ + { + ID: "1", + SynchronizationBandwidth: 1, + MirrorBandwidth: 1, + DataRemaining: 1, + }, + }, nil).Times(1) + clients["127.0.0.1"] = c + + service := service.PowerStoreService{ + MetricsWrapper: metrics, + VolumeFinder: volFinder, + PowerStoreClients: clients, + } + return service, ctrl + }, "metrics not pushed if error getting volume metrics": func(*testing.T) (service.PowerStoreService, *gomock.Controller) { ctrl := gomock.NewController(t) metrics := mocks.NewMockMetricsRecorder(ctrl) - metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) + metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) volFinder := mocks.NewMockVolumeFinder(ctrl) volFinder.EXPECT().GetPersistentVolumes(gomock.Any()).Return([]k8s.VolumeInfo{ @@ -136,7 +170,7 @@ func Test_ExportVolumeStatistics(t *testing.T) { "metrics not pushed if client not found for array ip in volume handle": func(*testing.T) (service.PowerStoreService, *gomock.Controller) { ctrl := gomock.NewController(t) metrics := mocks.NewMockMetricsRecorder(ctrl) - metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) + metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) volFinder := mocks.NewMockVolumeFinder(ctrl) volFinder.EXPECT().GetPersistentVolumes(gomock.Any()).Return([]k8s.VolumeInfo{ @@ -161,7 +195,7 @@ func Test_ExportVolumeStatistics(t *testing.T) { "metrics not pushed if volume handle is invalid": func(*testing.T) (service.PowerStoreService, *gomock.Controller) { ctrl := gomock.NewController(t) metrics := mocks.NewMockMetricsRecorder(ctrl) - metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) + metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) volFinder := mocks.NewMockVolumeFinder(ctrl) volFinder.EXPECT().GetPersistentVolumes(gomock.Any()).Return([]k8s.VolumeInfo{ @@ -186,7 +220,7 @@ func Test_ExportVolumeStatistics(t *testing.T) { "metrics not pushed if volume finder returns error": func(*testing.T) (service.PowerStoreService, *gomock.Controller) { ctrl := gomock.NewController(t) metrics := mocks.NewMockMetricsRecorder(ctrl) - metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) + metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) volFinder := mocks.NewMockVolumeFinder(ctrl) volFinder.EXPECT().GetPersistentVolumes(gomock.Any()).Return(nil, errors.New("error")).Times(1) @@ -237,7 +271,7 @@ func Test_ExportVolumeStatistics(t *testing.T) { VolumeFinder: volFinder, PowerStoreClients: clients, } - metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) + metrics.EXPECT().Record(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) return service, ctrl }, } @@ -721,7 +755,7 @@ func Test_ExportFileSystemStatistics(t *testing.T) { "success": func(*testing.T) (service.PowerStoreService, *gomock.Controller) { ctrl := gomock.NewController(t) metrics := mocks.NewMockMetricsRecorder(ctrl) - metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) volFinder := mocks.NewMockVolumeFinder(ctrl) volFinder.EXPECT().GetPersistentVolumes(gomock.Any()).Return([]k8s.VolumeInfo{ @@ -729,14 +763,6 @@ func Test_ExportFileSystemStatistics(t *testing.T) { PersistentVolume: "pv-1", VolumeHandle: "volume-1/127.0.0.1/nfs", }, - { - PersistentVolume: "pv-2", - VolumeHandle: "volume-2/127.0.0.1/nfs", - }, - { - PersistentVolume: "pv-3", - VolumeHandle: "volume-2/127.0.0.1/nfs", - }, }, nil).Times(1) clients := make(map[string]service.PowerStoreClient) @@ -750,7 +776,17 @@ func Test_ExportFileSystemStatistics(t *testing.T) { AvgReadLatency: 1, AvgWriteLatency: 1, }, - }, nil).Times(3) + }, nil).Times(1) + + c.EXPECT().VolumeMirrorTransferRate(gomock.Any(), gomock.Any()).Return([]gopowerstore.VolumeMirrorTransferRateResponse{ + { + ID: "1", + SynchronizationBandwidth: 1, + MirrorBandwidth: 1, + DataRemaining: 1, + }, + }, nil).Times(1) + clients["127.0.0.1"] = c service := service.PowerStoreService{ @@ -763,7 +799,7 @@ func Test_ExportFileSystemStatistics(t *testing.T) { "metrics not pushed if volume does not have scsi protocol": func(*testing.T) (service.PowerStoreService, *gomock.Controller) { ctrl := gomock.NewController(t) metrics := mocks.NewMockMetricsRecorder(ctrl) - metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) + metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) volFinder := mocks.NewMockVolumeFinder(ctrl) volFinder.EXPECT().GetPersistentVolumes(gomock.Any()).Return([]k8s.VolumeInfo{ @@ -785,10 +821,36 @@ func Test_ExportFileSystemStatistics(t *testing.T) { } return service, ctrl }, + "metrics pushed if volume is metro volume": func(*testing.T) (service.PowerStoreService, *gomock.Controller) { + ctrl := gomock.NewController(t) + metrics := mocks.NewMockMetricsRecorder(ctrl) + metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) + + volFinder := mocks.NewMockVolumeFinder(ctrl) + volFinder.EXPECT().GetPersistentVolumes(gomock.Any()).Return([]k8s.VolumeInfo{ + { + PersistentVolume: "pv-1", + VolumeHandle: "volume-1/127.0.0.1/nfs:volume-2/127.0.0.1", + }, + }, nil).Times(1) + + clients := make(map[string]service.PowerStoreClient) + c := mocks.NewMockPowerStoreClient(ctrl) + c.EXPECT().PerformanceMetricsByFileSystem(gomock.Any(), gomock.Any(), gomock.Any()).Times(1) + c.EXPECT().VolumeMirrorTransferRate(gomock.Any(), gomock.Any()).Times(1) + clients["127.0.0.1"] = c + + service := service.PowerStoreService{ + MetricsWrapper: metrics, + VolumeFinder: volFinder, + PowerStoreClients: clients, + } + return service, ctrl + }, "metrics not pushed if error getting volume metrics": func(*testing.T) (service.PowerStoreService, *gomock.Controller) { ctrl := gomock.NewController(t) metrics := mocks.NewMockMetricsRecorder(ctrl) - metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) + metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) volFinder := mocks.NewMockVolumeFinder(ctrl) volFinder.EXPECT().GetPersistentVolumes(gomock.Any()).Return([]k8s.VolumeInfo{ @@ -813,7 +875,7 @@ func Test_ExportFileSystemStatistics(t *testing.T) { "metrics not pushed if client not found for array ip in volume handle": func(*testing.T) (service.PowerStoreService, *gomock.Controller) { ctrl := gomock.NewController(t) metrics := mocks.NewMockMetricsRecorder(ctrl) - metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) + metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) volFinder := mocks.NewMockVolumeFinder(ctrl) volFinder.EXPECT().GetPersistentVolumes(gomock.Any()).Return([]k8s.VolumeInfo{ @@ -838,7 +900,7 @@ func Test_ExportFileSystemStatistics(t *testing.T) { "metrics not pushed if volume handle is invalid": func(*testing.T) (service.PowerStoreService, *gomock.Controller) { ctrl := gomock.NewController(t) metrics := mocks.NewMockMetricsRecorder(ctrl) - metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) + metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) volFinder := mocks.NewMockVolumeFinder(ctrl) volFinder.EXPECT().GetPersistentVolumes(gomock.Any()).Return([]k8s.VolumeInfo{ @@ -863,7 +925,7 @@ func Test_ExportFileSystemStatistics(t *testing.T) { "metrics not pushed if volume finder returns error": func(*testing.T) (service.PowerStoreService, *gomock.Controller) { ctrl := gomock.NewController(t) metrics := mocks.NewMockMetricsRecorder(ctrl) - metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) + metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) volFinder := mocks.NewMockVolumeFinder(ctrl) volFinder.EXPECT().GetPersistentVolumes(gomock.Any()).Return(nil, errors.New("error")).Times(1) @@ -914,7 +976,7 @@ func Test_ExportFileSystemStatistics(t *testing.T) { VolumeFinder: volFinder, PowerStoreClients: clients, } - metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) + metrics.EXPECT().RecordFileSystemMetrics(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) return service, ctrl }, }