From 227bfb135ceaea6f3d3f425cbb3f95d41d0fcd75 Mon Sep 17 00:00:00 2001 From: Simon Dodsley Date: Mon, 11 Dec 2023 14:50:36 -0500 Subject: [PATCH 1/4] Add more detail to alerts --- Makefile | 2 +- build/docker/Dockerfile | 2 +- docs/deployment-examples.md | 6 +- .../openmetrics-exporter/alerts_collector.go | 15 +++-- .../alerts_collector_test.go | 55 +++++++++---------- specification/metrics/purefa-metrics.md | 5 ++ 6 files changed, 48 insertions(+), 37 deletions(-) diff --git a/Makefile b/Makefile index 7715eaa..f444772 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ GOTEST=$(GOCMD) test GOVET=$(GOCMD) vet BINARY_NAME=pure-fa-om-exporter MODULE_NAME=purestorage/fa-openmetrics-exporter -VERSION?=1.0.11 +VERSION?=1.0.12 SERVICE_PORT?=9490 DOCKER_REGISTRY?= quay.io/purestorage/ EXPORT_RESULT?=false # for CI please set EXPORT_RESULT to true diff --git a/build/docker/Dockerfile b/build/docker/Dockerfile index 9641287..a8368df 100644 --- a/build/docker/Dockerfile +++ b/build/docker/Dockerfile @@ -1,5 +1,5 @@ FROM golang:alpine as build -ARG VERSION=1.0.11 +ARG VERSION=1.0.12 WORKDIR /usr/src/app diff --git a/docs/deployment-examples.md b/docs/deployment-examples.md index 6c69ae5..0d8decf 100644 --- a/docs/deployment-examples.md +++ b/docs/deployment-examples.md @@ -144,7 +144,7 @@ Deploying the binary requires [go](https://go.dev) to compile the code and runni ```console $ ls out/bin $ .out/bin/pure-fa-openmetrics-exporter - Start Pure FlashArray exporter v1.0.11 on 0.0.0.0:9490 + Start Pure FlashArray exporter v1.0.12 on 0.0.0.0:9490 ``` 2. **Test the exporter** @@ -234,7 +234,7 @@ Follow steps 1-4 and 7-8 of the default binary deployment, but substitute the fo ```console $ ls out/bin $ .out/bin/pure-fa-openmetrics-exporter --tokens /directorypath/tokens.yaml - Start Pure FlashArray exporter v1.0.11 on 0.0.0.0:9490 + Start Pure FlashArray exporter v1.0.12 on 0.0.0.0:9490 ``` 3. **Test the exporter** @@ -333,4 +333,4 @@ Create the certificate and key and pass the exporter the files. There are many d Full check using certificate. ```console $ curl --cacert pure-ome.crt -H 'Authorization: Bearer 11111111-1111-1111-1111-111111111111' -X GET 'http://pure-ome.fqdn.com:9490/metrics/array?endpoint=array01' - ``` \ No newline at end of file + ``` diff --git a/internal/openmetrics-exporter/alerts_collector.go b/internal/openmetrics-exporter/alerts_collector.go index 08143c9..69f1986 100644 --- a/internal/openmetrics-exporter/alerts_collector.go +++ b/internal/openmetrics-exporter/alerts_collector.go @@ -4,7 +4,7 @@ import ( "fmt" client "purestorage/fa-openmetrics-exporter/internal/rest-client" "strings" - + "strconv" "github.com/prometheus/client_golang/prometheus" ) @@ -24,7 +24,14 @@ func (c *AlertsCollector) Collect(ch chan<- prometheus.Metric) { } al := make(map[string]float64) for _, alert := range alerts.Items { - al[fmt.Sprintf("%s,%s", alert.ComponentType, alert.Severity)] += 1 + al[fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s", + alert.ComponentType, + alert.Severity, + strconv.Itoa(alert.Created), + alert.Name, + strconv.Itoa(alert.Code), + alert.Summary, + alert.Issue)] += 1 } for a, n := range al { alert := strings.Split(a, ",") @@ -32,7 +39,7 @@ func (c *AlertsCollector) Collect(ch chan<- prometheus.Metric) { c.AlertsDesc, prometheus.GaugeValue, n, - alert[0], alert[1], + alert[0], alert[1], alert[2], alert[3], alert[4], alert[5], alert[6], ) } } @@ -42,7 +49,7 @@ func NewAlertsCollector(fa *client.FAClient) *AlertsCollector { AlertsDesc: prometheus.NewDesc( "purefa_alerts_open", "FlashArray open alert events", - []string{"component_type", "severity"}, + []string{"component_type", "severity", "created", "name", "code", "summary", "issue"}, prometheus.Labels{}, ), Client: fa, diff --git a/internal/openmetrics-exporter/alerts_collector_test.go b/internal/openmetrics-exporter/alerts_collector_test.go index fda39fa..ec1535b 100644 --- a/internal/openmetrics-exporter/alerts_collector_test.go +++ b/internal/openmetrics-exporter/alerts_collector_test.go @@ -1,17 +1,15 @@ package collectors - import ( + "encoding/json" "fmt" - "testing" - "regexp" - "strings" "net/http" "net/http/httptest" - "encoding/json" "os" - - "purestorage/fa-openmetrics-exporter/internal/rest-client" + client "purestorage/fa-openmetrics-exporter/internal/rest-client" + "regexp" + "strings" + "testing" ) func TestAlertsCollector(t *testing.T) { @@ -24,38 +22,39 @@ func TestAlertsCollector(t *testing.T) { json.Unmarshal(ropen, &aopen) json.Unmarshal(rall, &aall) server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - urlall := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/alerts$`) - urlopen := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/alerts\?filter=state%3D%27open%27$`) - if r.URL.Path == "/api/api_version" { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) + urlall := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/alerts$`) + urlopen := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/alerts\?filter=state%3D%27open%27$`) + if r.URL.Path == "/api/api_version" { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) w.Write([]byte(vers)) - } else if urlopen.MatchString(r.URL.Path + "?" + r.URL.RawQuery) { + } else if urlopen.MatchString(r.URL.Path + "?" + r.URL.RawQuery) { w.Header().Set("x-auth-token", "faketoken") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(ropen)) - } else if urlall.MatchString(r.URL.Path) { + } else if urlall.MatchString(r.URL.Path) { w.Header().Set("x-auth-token", "faketoken") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(rall)) } - })) - endp := strings.Split(server.URL, "/") - e := endp[len(endp)-1] - al := make(map[string]float64) - for _, a := range aopen.Items { - al[fmt.Sprintf("%s,%s", a.ComponentType, a.Severity)] += 1 - } + })) + endp := strings.Split(server.URL, "/") + e := endp[len(endp)-1] + al := make(map[string]float64) + for _, a := range aopen.Items { + al[fmt.Sprintf("%s,%s", a.ComponentType, a.Severity, a.Created, a.Name, a.Code, a.Summary, a.Issue)] += 1 + } want := make(map[string]bool) - for a, n := range al { - alert := strings.Split(a, ",") -// want[fmt.Sprintf("label:{name:\"component_type\" value:\"%s\"} label:{name:\"severity\" value:\"%s\"} gauge:{value:%g}", alert[0], alert[1], n)] = true - want[fmt.Sprintf("label:{name:\"component_type\" value:\"%s\"} label:{name:\"severity\" value:\"%s\"} gauge:{value:%g}", alert[0], alert[1], n)] = true + for a, n := range al { + alert := strings.Split(a, ",") + // want[fmt.Sprintf("label:{name:\"component_type\" value:\"%s\"} label:{name:\"severity\" value:\"%s\"} gauge:{value:%g}", alert[0], alert[1], n)] = true + // want[fmt.Sprintf("label:{name:\"component_type\" value:\"%s\"} label:{name:\"severity\" value:\"%s\"} gauge:{value:%g}", alert[0], alert[1], n)] = true + want[fmt.Sprintf("label:{name:\"component_type\" value:\"%s\"} label:{name:\"severity\" value:\"%s\"} gauge:{value:%g}", alert[0], alert[1], alert[2], alert[3], alert[4], alert[5], alert[6], n)] = true } - c := client.NewRestClient(e, "fake-api-token", "latest", false) + c := client.NewRestClient(e, "fake-api-token", "latest", false) ac := NewAlertsCollector(c) - metricsCheck(t, ac, want) - server.Close() + metricsCheck(t, ac, want) + server.Close() } diff --git a/specification/metrics/purefa-metrics.md b/specification/metrics/purefa-metrics.md index 0526fc0..2e33a78 100644 --- a/specification/metrics/purefa-metrics.md +++ b/specification/metrics/purefa-metrics.md @@ -66,6 +66,11 @@ This document describes the semantic conventions for Pure FlashArray Metrics. | --------- | ------------------ | ---------------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------- | ---------- | ---------------- | ------------------------------------------------------------------------------------------------------------------ | | Available | purefa_alerts_open | FlashArray open alert events | | Gauge | Double | `component_type` | `chassis`, `drive_bay`, `nvram_bay`, `power_supply`, `temp_sensor`, `controller`, `eth_port`, `cooling`, `fc_port` | | | | | | | | `severity` | `info`, `warning`, `critical`, `hidden` | +| | | | | | | `created` | (time the alert was created in milliseconds since the UNIX epoch) | +| | | | | | | `name` | (a locally unique, system-generated name) | +| | | | | | | `code` | (code number of the event) | +| | | | | | | `summary` | (summary of the alert) | +| | | | | | | `issue` | (information about the alert cause) | ### `purefa_array` - FlashArray metrics From 733a20f8257babecf08fd42de3620ba1dd1897fc Mon Sep 17 00:00:00 2001 From: Christopher Roberts Date: Wed, 13 Dec 2023 12:17:32 -0600 Subject: [PATCH 2/4] a little bit of clean up work for names being in a-z order, also added "category" --- .../openmetrics-exporter/alerts_collector.go | 18 ++++--- .../alerts_collector_test.go | 7 ++- internal/rest-client/alerts.go | 49 +++++++++---------- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/internal/openmetrics-exporter/alerts_collector.go b/internal/openmetrics-exporter/alerts_collector.go index 69f1986..b446116 100644 --- a/internal/openmetrics-exporter/alerts_collector.go +++ b/internal/openmetrics-exporter/alerts_collector.go @@ -4,7 +4,7 @@ import ( "fmt" client "purestorage/fa-openmetrics-exporter/internal/rest-client" "strings" - "strconv" + "github.com/prometheus/client_golang/prometheus" ) @@ -24,14 +24,16 @@ func (c *AlertsCollector) Collect(ch chan<- prometheus.Metric) { } al := make(map[string]float64) for _, alert := range alerts.Items { - al[fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s", + al[fmt.Sprintf("%s,%d,%s,%d,%s,%s,%s,%s", + alert.Category, + alert.Code, alert.ComponentType, - alert.Severity, - strconv.Itoa(alert.Created), + alert.Created, + alert.Issue, alert.Name, - strconv.Itoa(alert.Code), + alert.Severity, alert.Summary, - alert.Issue)] += 1 + )] += 1 } for a, n := range al { alert := strings.Split(a, ",") @@ -39,7 +41,7 @@ func (c *AlertsCollector) Collect(ch chan<- prometheus.Metric) { c.AlertsDesc, prometheus.GaugeValue, n, - alert[0], alert[1], alert[2], alert[3], alert[4], alert[5], alert[6], + alert[0], alert[1], alert[2], alert[3], alert[4], alert[5], alert[6], alert[7], ) } } @@ -49,7 +51,7 @@ func NewAlertsCollector(fa *client.FAClient) *AlertsCollector { AlertsDesc: prometheus.NewDesc( "purefa_alerts_open", "FlashArray open alert events", - []string{"component_type", "severity", "created", "name", "code", "summary", "issue"}, + []string{"category", "code", "component_type", "created", "issue", "name", "severity", "summary"}, prometheus.Labels{}, ), Client: fa, diff --git a/internal/openmetrics-exporter/alerts_collector_test.go b/internal/openmetrics-exporter/alerts_collector_test.go index ec1535b..12098b2 100644 --- a/internal/openmetrics-exporter/alerts_collector_test.go +++ b/internal/openmetrics-exporter/alerts_collector_test.go @@ -44,14 +44,13 @@ func TestAlertsCollector(t *testing.T) { e := endp[len(endp)-1] al := make(map[string]float64) for _, a := range aopen.Items { - al[fmt.Sprintf("%s,%s", a.ComponentType, a.Severity, a.Created, a.Name, a.Code, a.Summary, a.Issue)] += 1 + al[fmt.Sprintf("%s,%d,%s,%d,%s,%s,%s,%s", a.Category, a.Code, a.ComponentType, a.Created, a.Issue, a.Name, a.Severity, a.Summary)] += 1 } want := make(map[string]bool) for a, n := range al { alert := strings.Split(a, ",") - // want[fmt.Sprintf("label:{name:\"component_type\" value:\"%s\"} label:{name:\"severity\" value:\"%s\"} gauge:{value:%g}", alert[0], alert[1], n)] = true - // want[fmt.Sprintf("label:{name:\"component_type\" value:\"%s\"} label:{name:\"severity\" value:\"%s\"} gauge:{value:%g}", alert[0], alert[1], n)] = true - want[fmt.Sprintf("label:{name:\"component_type\" value:\"%s\"} label:{name:\"severity\" value:\"%s\"} gauge:{value:%g}", alert[0], alert[1], alert[2], alert[3], alert[4], alert[5], alert[6], n)] = true + + want[fmt.Sprintf("label:{name:\"category\" value:\"%s\"} label:{name:\"code\" value:\"%s\"} label:{name:\"component_type\" value:\"%s\"} label:{name:\"created\" value:\"%s\"} label:{name:\"issue\" value:\"%s\"} label:{name:\"name\" value:\"%s\"} label:{name:\"severity\" value:\"%s\"} label:{name:\"summary\" value:\"%s\"} gauge:{value:%g}", alert[0], alert[1], alert[2], alert[3], alert[4], alert[5], alert[6], alert[7], n)] = true } c := client.NewRestClient(e, "fake-api-token", "latest", false) ac := NewAlertsCollector(c) diff --git a/internal/rest-client/alerts.go b/internal/rest-client/alerts.go index 24be175..2147147 100644 --- a/internal/rest-client/alerts.go +++ b/internal/rest-client/alerts.go @@ -1,33 +1,32 @@ package client - type Alert struct { - Id string `json:"id"` - Name string `json:"name"` - Actual string `json:"actual"` - Closed int `json:"closed"` - Code int `json:"code"` - ComponentName string `json:"component_name"` - ComponentType string `json:"component_type"` - Created int `json:"created"` - Description string `json:"description"` - Expected string `json:"expected"` - Flagged bool `json:"flagged"` - Issue string `json:"issue"` - Index int `json:"index"` - KnowledgeBaseUrl string `json:"knowledge_base_url"` - Notified int `json:"notified"` - Severity string `json:"severity"` - State string `json:"state"` - Summary string `json:"summary"` - Updated int `json:"updated"` + Id string `json:"id"` + Name string `json:"name"` + Actual string `json:"actual"` + Category string `json:"category"` + Closed int64 `json:"closed"` + Code int64 `json:"code"` + ComponentName string `json:"component_name"` + ComponentType string `json:"component_type"` + Created int64 `json:"created"` + Description string `json:"description"` + Expected string `json:"expected"` + Flagged bool `json:"flagged"` + Issue string `json:"issue"` + KnowledgeBaseUrl string `json:"knowledge_base_url"` + Notified int64 `json:"notified"` + Severity string `json:"severity"` + State string `json:"state"` + Summary string `json:"summary"` + Updated int64 `json:"updated"` } type AlertsList struct { - ContinuationToken string `json:"continuation_token"` - TotalItemCount int `json:"total_item_count"` - MoreItemsRemaining bool `json:"more_items_remaining"` - Items []Alert `json:"items"` + ContinuationToken string `json:"continuation_token"` + TotalItemCount int32 `json:"total_item_count"` + MoreItemsRemaining bool `json:"more_items_remaining"` + Items []Alert `json:"items"` } func (fa *FAClient) GetAlerts(filter string) *AlertsList { @@ -48,6 +47,6 @@ func (fa *FAClient) GetAlerts(filter string) *AlertsList { if err != nil { fa.Error = err } - + return result } From d1be9c5437d04b5d117f1bad6cf0aa336ee5f0cb Mon Sep 17 00:00:00 2001 From: Christopher Roberts Date: Wed, 13 Dec 2023 15:10:53 -0600 Subject: [PATCH 3/4] Update purefa-metrics.md --- specification/metrics/purefa-metrics.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/specification/metrics/purefa-metrics.md b/specification/metrics/purefa-metrics.md index 2e33a78..81728d4 100644 --- a/specification/metrics/purefa-metrics.md +++ b/specification/metrics/purefa-metrics.md @@ -62,15 +62,17 @@ This document describes the semantic conventions for Pure FlashArray Metrics. **Description:** FlashArray Open Alerts -| Status | Name | Description | Units | Metric Type ([*](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#metric-types)) | Value Type | Attribute Key | Attribute Values | -| --------- | ------------------ | ---------------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------- | ---------- | ---------------- | ------------------------------------------------------------------------------------------------------------------ | -| Available | purefa_alerts_open | FlashArray open alert events | | Gauge | Double | `component_type` | `chassis`, `drive_bay`, `nvram_bay`, `power_supply`, `temp_sensor`, `controller`, `eth_port`, `cooling`, `fc_port` | -| | | | | | | `severity` | `info`, `warning`, `critical`, `hidden` | -| | | | | | | `created` | (time the alert was created in milliseconds since the UNIX epoch) | -| | | | | | | `name` | (a locally unique, system-generated name) | -| | | | | | | `code` | (code number of the event) | -| | | | | | | `summary` | (summary of the alert) | -| | | | | | | `issue` | (information about the alert cause) | +| Status | Name | Description | Units | Metric Type ([*](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#metric-types)) | Value Type | Attribute Key | Attribute Values | +| --------- | ------------------ | ------------------------------------------------------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------- | ---------- | ---------------- | ------------------------------------------------------------------------------------------------------------------ | +| Available | purefa_alerts_open | FlashArray open alert event | | Gauge | Double | `name` | (name) | +| | | The code number of the event | | | | `code` | (code) | +| | | | | | | `component_type` | `chassis`, `drive_bay`, `nvram_bay`, `power_supply`, `temp_sensor`, `controller`, `eth_port`, `cooling`, `fc_port` | +| | | The time the alert was created in milliseconds since the UNIX epoch | | | | `created` | (created) | +| | | Information about the alert cause | | | | `issue` | (issue) | +| | | The category of the alert. | | | | `category` | `array`, `hardware`, `software` | +| | | | | | | `severity` | `info`, `warning`, `critical`, `hidden` | +| | | A summary of the alert | | | | `summary` | (summary) | +| | ### `purefa_array` - FlashArray metrics From c905e2a83b2e35f7e0d1538064540add95aabbec Mon Sep 17 00:00:00 2001 From: Christopher Roberts Date: Wed, 13 Dec 2023 15:16:43 -0600 Subject: [PATCH 4/4] update go net and go sys --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 3f28832..9ba45aa 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/prometheus/common v0.45.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect - golang.org/x/net v0.18.0 // indirect - golang.org/x/sys v0.14.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sys v0.15.0 // indirect google.golang.org/protobuf v1.31.0 // indirect ) diff --git a/go.sum b/go.sum index 73585ec..e58e217 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -51,8 +51,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=