From 368a1ddf3c3c8f0e27fd9424024b55937dc8313e Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Sat, 9 Apr 2022 00:57:28 +0800 Subject: [PATCH 1/3] Add CSI VolumeSnapshot related metrics. Signed-off-by: Xun Jiang --- pkg/apis/velero/v1/backup.go | 16 ++++ pkg/backup/request.go | 2 + pkg/controller/backup_controller.go | 21 +++++ pkg/controller/backup_deletion_controller.go | 21 +++++ pkg/metrics/metrics.go | 85 +++++++++++++++++++- 5 files changed, 143 insertions(+), 2 deletions(-) diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 51ad422ec..6148f5306 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -17,6 +17,7 @@ limitations under the License. package v1 import ( + resource "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -310,6 +311,21 @@ type BackupStatus struct { // +optional // +nullable Progress *BackupProgress `json:"progress,omitempty"` + + // CsiVolumeSnapshotsAttempted is the total number of attempted + // CSI VolumeSnapshots for this backup. + // +optional + CsiVolumeSnapshotsAttempted int `json:"csiVolumeSnapshotsAttempted,omitempty"` + + // CsiVolumeSnapshotsCompleted is the total number of successfully + // completed CSI VolumeSnapshots for this backup. + // +optional + CsiVolumeSnapshotsCompleted int `json:"csiVolumeSnapshotsCompleted,omitempty"` + + // CsiVolumeSnapshotsStorageTotal is the total storage size of created + // snapshots for this backup. + // +optional + CsiVolumeSnapshotsStorageTotal resource.Quantity `json:"csiVolumeSnapshotsStorageTotal,omitempty"` } // BackupProgress stores information about the progress of a Backup's execution. diff --git a/pkg/backup/request.go b/pkg/backup/request.go index bc483ef97..b5bf8ae24 100644 --- a/pkg/backup/request.go +++ b/pkg/backup/request.go @@ -20,6 +20,7 @@ import ( "fmt" "sort" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" "github.com/vmware-tanzu/velero/internal/hook" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/framework" @@ -48,6 +49,7 @@ type Request struct { VolumeSnapshots []*volume.Snapshot PodVolumeBackups []*velerov1api.PodVolumeBackup BackedUpItems map[itemKey]struct{} + CsiSnapshots []*snapshotv1api.VolumeSnapshot } // BackupResourceList returns the list of backed up resources grouped by the API diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index db8a8b40d..a2bcfc0d4 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -636,6 +636,14 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { } } + backup.Status.CsiVolumeSnapshotsAttempted = len(backup.CsiSnapshots) + for _, vs := range backup.CsiSnapshots { + if *vs.Status.ReadyToUse { + backup.Status.CsiVolumeSnapshotsCompleted++ + backup.Status.CsiVolumeSnapshotsStorageTotal.Add(*vs.Status.RestoreSize) + } + } + backup.Status.Warnings = logCounter.GetCount(logrus.WarnLevel) backup.Status.Errors = logCounter.GetCount(logrus.ErrorLevel) @@ -694,6 +702,19 @@ func recordBackupMetrics(log logrus.FieldLogger, backup *velerov1api.Backup, bac serverMetrics.RegisterVolumeSnapshotAttempts(backupScheduleName, backup.Status.VolumeSnapshotsAttempted) serverMetrics.RegisterVolumeSnapshotSuccesses(backupScheduleName, backup.Status.VolumeSnapshotsCompleted) serverMetrics.RegisterVolumeSnapshotFailures(backupScheduleName, backup.Status.VolumeSnapshotsAttempted-backup.Status.VolumeSnapshotsCompleted) + + if features.IsEnabled(velerov1api.CSIFeatureFlag) { + serverMetrics.RegisterCsiSnapshotAttempts(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsAttempted) + serverMetrics.RegisterCsiSnapshotSuccesses(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsCompleted) + serverMetrics.RegisterCsiSnapshotFailures(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsAttempted-backup.Status.CsiVolumeSnapshotsCompleted) + storageSize, ret := backup.Status.CsiVolumeSnapshotsStorageTotal.AsInt64() + if !ret { + log.WithError(fmt.Errorf("fail to convert CSI snapshot size: %v to int64", backup.Status.CsiVolumeSnapshotsStorageTotal)) + storageSize = 0 + } + serverMetrics.RegisterCsiStorageSizeAdd(backupScheduleName, backup.Name, storageSize) + } + if backup.Status.Progress != nil { serverMetrics.RegisterBackupItemsTotalGauge(backupScheduleName, backup.Status.Progress.TotalItems) } diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 90707be70..d4c58553f 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -28,6 +28,7 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" @@ -39,6 +40,7 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/discovery" + "github.com/vmware-tanzu/velero/pkg/features" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" @@ -407,6 +409,25 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR c.metrics.RegisterBackupDeletionFailed(backupScheduleName) } + if features.IsEnabled(velerov1api.CSIFeatureFlag) { + vss, err := backupStore.GetCSIVolumeSnapshots(backup.Name) + if err != nil { + errs = append(errs, err.Error()) + } + + var restoreSizeTotal resource.Quantity + for _, vs := range vss { + restoreSizeTotal.Add(*vs.Status.RestoreSize) + } + + storageSize, ret := restoreSizeTotal.AsInt64() + if !ret { + log.WithError(fmt.Errorf("fail to convert CSI snapshot size: %v to int64", backup.Status.CsiVolumeSnapshotsStorageTotal)) + storageSize = 0 + } + c.metrics.RegisterCsiStorageSizeSub(backupScheduleName, backup.Name, storageSize) + } + // Update status to processed and record errors req, err = c.patchDeleteBackupRequest(req, func(r *velerov1api.DeleteBackupRequest) { r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 072db9134..0a9383456 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -54,6 +54,10 @@ const ( volumeSnapshotAttemptTotal = "volume_snapshot_attempt_total" volumeSnapshotSuccessTotal = "volume_snapshot_success_total" volumeSnapshotFailureTotal = "volume_snapshot_failure_total" + csiSnapshotAttemptTotal = "csi_snapshot_attempt_total" + csiSnapshotSuccessTotal = "csi_snapshot_success_total" + csiSnapshotFailureTotal = "csi_snapshot_failure_total" + csiSnapshotStorageTotal = "csi_snapshot_storage_total" // Restic metrics podVolumeBackupEnqueueTotal = "pod_volume_backup_enqueue_count" @@ -67,8 +71,6 @@ const ( pvbNameLabel = "pod_volume_backup" scheduleLabel = "schedule" backupNameLabel = "backupName" - - secondsInMinute = 60.0 ) // NewServerMetrics returns new ServerMetrics @@ -268,6 +270,38 @@ func NewServerMetrics() *ServerMetrics { }, []string{scheduleLabel}, ), + csiSnapshotAttemptTotal: prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: metricNamespace, + Name: csiSnapshotAttemptTotal, + Help: "Total number of CSI attempted volume snapshots", + }, + []string{scheduleLabel, backupNameLabel}, + ), + csiSnapshotSuccessTotal: prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: metricNamespace, + Name: csiSnapshotSuccessTotal, + Help: "Total number of CSI successful volume snapshots", + }, + []string{scheduleLabel, backupNameLabel}, + ), + csiSnapshotFailureTotal: prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: metricNamespace, + Name: csiSnapshotFailureTotal, + Help: "Total number of CSI failed volume snapshots", + }, + []string{scheduleLabel, backupNameLabel}, + ), + csiSnapshotStorageTotal: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: metricNamespace, + Name: csiSnapshotStorageTotal, + Help: "Total size of CSI volume snapshots storage size", + }, + []string{scheduleLabel, backupNameLabel}, + ), }, } } @@ -385,6 +419,18 @@ func (m *ServerMetrics) InitSchedule(scheduleName string) { if c, ok := m.metrics[volumeSnapshotFailureTotal].(*prometheus.CounterVec); ok { c.WithLabelValues(scheduleName).Add(0) } + if c, ok := m.metrics[csiSnapshotAttemptTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(scheduleName).Add(0) + } + if c, ok := m.metrics[csiSnapshotSuccessTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(scheduleName).Add(0) + } + if c, ok := m.metrics[csiSnapshotFailureTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(scheduleName).Add(0) + } + if c, ok := m.metrics[csiSnapshotStorageTotal].(*prometheus.GaugeVec); ok { + c.WithLabelValues(scheduleName).Add(0) + } } // InitSchedule initializes counter metrics for a node. @@ -593,3 +639,38 @@ func (m *ServerMetrics) RegisterVolumeSnapshotFailures(backupSchedule string, vo c.WithLabelValues(backupSchedule).Add(float64(volumeSnapshotsFailed)) } } + +// RegisterCsiSnapshotAttempts records an attempt to snapshot a volume by CSI plugin. +func (m *ServerMetrics) RegisterCsiSnapshotAttempts(backupSchedule, backupName string, csiSnapshotsAttempted int) { + if c, ok := m.metrics[csiSnapshotAttemptTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(backupSchedule, backupName).Add(float64(csiSnapshotsAttempted)) + } +} + +// RegisterCsiSnapshotSuccesses records a completed volume snapshot by CSI plugin. +func (m *ServerMetrics) RegisterCsiSnapshotSuccesses(backupSchedule, backupName string, csiSnapshotCompleted int) { + if c, ok := m.metrics[csiSnapshotSuccessTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(backupSchedule, backupName).Add(float64(csiSnapshotCompleted)) + } +} + +// RegisterCsiSnapshotFailures records a failed volume snapshot by CSI plugin. +func (m *ServerMetrics) RegisterCsiSnapshotFailures(backupSchedule, backupName string, csiSnapshotsFailed int) { + if c, ok := m.metrics[csiSnapshotFailureTotal].(*prometheus.CounterVec); ok { + c.WithLabelValues(backupSchedule, backupName).Add(float64(csiSnapshotsFailed)) + } +} + +// RegisterCsiStorageSizeAdd records volume snapshot's storage size increase created by CSI plugin. +func (m *ServerMetrics) RegisterCsiStorageSizeAdd(backupSchedule, backupName string, csiStorageSize int64) { + if g, ok := m.metrics[csiSnapshotStorageTotal].(*prometheus.GaugeVec); ok { + g.WithLabelValues(backupSchedule, backupName).Add(float64(csiStorageSize)) + } +} + +// RegisterCsiStorageSizeSub records volume snapshot's storage size decrease created by CSI plugin. +func (m *ServerMetrics) RegisterCsiStorageSizeSub(backupSchedule, backupName string, csiStorageSize int64) { + if g, ok := m.metrics[csiSnapshotStorageTotal].(*prometheus.GaugeVec); ok { + g.WithLabelValues(backupSchedule, backupName).Sub(float64(csiStorageSize)) + } +} From 4daeec7ab9529b2fb6501792bec5728d51797856 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Sat, 9 Apr 2022 04:45:06 +0000 Subject: [PATCH 2/3] Update CRD and GRPC. Signed-off-by: Xun Jiang --- changelogs/unreleased/4818-jxun | 1 + config/crd/v1/bases/velero.io_backups.yaml | 13 +++++++++++++ config/crd/v1/crds/crds.go | 2 +- pkg/apis/velero/v1/backup.go | 3 +-- pkg/backup/request.go | 1 + pkg/controller/backup_controller.go | 14 +++++++------- pkg/metrics/metrics.go | 12 ------------ 7 files changed, 24 insertions(+), 22 deletions(-) create mode 100644 changelogs/unreleased/4818-jxun diff --git a/changelogs/unreleased/4818-jxun b/changelogs/unreleased/4818-jxun new file mode 100644 index 000000000..f3e478c70 --- /dev/null +++ b/changelogs/unreleased/4818-jxun @@ -0,0 +1 @@ +Add CSI VolumeSnapshot related metrics. diff --git a/config/crd/v1/bases/velero.io_backups.yaml b/config/crd/v1/bases/velero.io_backups.yaml index fec76fbee..5cbdd76d3 100644 --- a/config/crd/v1/bases/velero.io_backups.yaml +++ b/config/crd/v1/bases/velero.io_backups.yaml @@ -354,6 +354,19 @@ spec: format: date-time nullable: true type: string + csiVolumeSnapshotsAttempted: + description: CsiVolumeSnapshotsAttempted is the total number of attempted + CSI VolumeSnapshots for this backup. + type: integer + csiVolumeSnapshotsCompleted: + description: CsiVolumeSnapshotsCompleted is the total number of successfully + completed CSI VolumeSnapshots for this backup. + type: integer + csiVolumeSnapshotsStorageTotal: + description: CsiVolumeSnapshotsStorageTotal is the total storage size + of created snapshots for this backup. + format: int64 + type: integer errors: description: Errors is a count of all error messages that were generated during execution of the backup. The actual errors are in the backup's diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index cb45808fc..da0f366b2 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -29,7 +29,7 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec<]o#9r\xef\xfe\x15\x05\xe7a\xee\x00K\xbeE\x1e\x12\xf8m\xd6\xe3E\x84\xdd\xcc\x19;\x8e\xf3\x10\xe4\x81\xea.Ib\x81F\xaf\xa5\xbe\xb2\x15f4\xd7\xde躺\x83\xf6\a?$\xe0\xe1\xd7\xf0#\x8f\xe6\x0f\x85\xb4\xee\xe7\xce\xc7_\xa4u\xfcCU\xd4F\x14\xcdL\xfc\xcdJ\xb5\xaf\va\xe2\xd7+\x00\x9b\xe9\n\xef\xe03MQ\x89\f\xf3+\x80\xb0\x1c\x9er\x15\x10>\xfe\xe0!d\a,\x85\xc7\x05@W\xa8>>n\x9e\xff\xf1K\xef3@\x8e63\xb2rL\x14\x8f\x18H\v\x02\x9eyY`\x02\xf9\xc1\x1d\x84\x03\x83\x95A\x8b\xcaYp\a\x84LT\xae6\bz\a?\xd7[4\n\x1d\xda\x064@V\xd4֡\x01\xeb\x84C\x10\x0e\x04TZ*\aR\x81\x93%\xc2\x1f>>n@o\xff\x82\x99\xb3 T\x0e\xc2Z\x9dI\xe10\x87\xa3.\xea\x12\xfd\xd8?\xae\x1b\xa8\x95\xd1\x15\x1a'#\x9d}\xebHU\xe7\xeb`y\x1f\x88\x02\xbe\x17\xe4$N\xe8\x97\x11\xa8\x88y \x1a\xad\xc7\x1d\xa4m\x97\xcb\x12\xd2\x03\f\xd4I\xa8\x80\xfc\x1a\xbe\xa0!0`\x0f\xba.r\x92\xc2#\x1a\"X\xa6\xf7J\xfew\x03ۂ\xd3\xa3\xc1`u\x0fg\x03ؤ\t\xa9hא\x11!\xf4T\xfb+)\xe6\x91\xc5\t\x83@r+\x95\x87\xc7:\xf7\x80\xa3\f\xa2&\x1d\x96#\xb8M\x8a\x99od*Ŷ\xc0;p\xa6\xc6\t\xca\bc\xc4i\x82.Ѽ\xa7\x92\xa5\xe9\x1f\xb4H!3\xb6?\x8d\xae`\xcaxk%\xcc9F\xf0{&\xcaA\xeb\x97%B\xfc\v\xf5i\xf5\x1ed\xec%\xc1\x16\x0f\xe2(\xb5\tK\x0ffh\x8b\x80_1\xab\x1d\x8eɿp\x90\xcb\xdd\x0e\r\xc1\xa9\x0e¢\xf5\xa6o\x9a \xd3[\x99\x9a\x99f\xe6\xd9:ZF\x92\xa4\xf2ʧP\xa7\r=\xdcW\xb1\x11\xa2d4\xc8mQ\xb9<ʼ\x16\x05He\x9dP\x99_\x8fh\xf0:_\x0f\xcc1\xf9\fg\xaf\x0e#\xe6ĉ\x9ej\xd4\nA\x1b(\xc9\x1e\x9cw\x1dZ\xb0\xb6M-{+H;i/\xa2\xa6.І\xa9rֹ\xad\x0e\xb8\x99\x04\xddp\xc4\xfb\x12\x85\xd8b\x01\x16\v̜6\xe3\xe4Xb\xb2o)zm\x82\x8a#\x1a\xae\xd5ݴ\xd4va3 \x81\xd4\xf6\xebAf\ao\xe6I\x82\x18\x0e\xe4\x1a-\xefrQU\xc5ij\x91\xb0\xc4\xf90\xc9\xdcFo\xdb\u0096\x1f\xc2\x1b\xdb\xfcmKЍm[В}\xca6\xe2\x00N\xcf.\xfb\xff'a\xa3\xda\u007f\x83\xd0nΆ\xbe\xaf\xd0\x12I%\xb9\xf3\x9b\x1d`Y\xb9\xd3\rH\x17\xbf.A$g\xa5\x9d\xff\xef\x981\x97K\xfcf8\xf2]%~\x96+K\x10\x89+\xcd\xf4\u007f\x87Lac\xf1%؊d\x86\xfc\xd2\x1du\x03r\xd70$\xbf\x81\x9d,\x1c\x9a\x01g~\xd3~y\x0fb\xa4\xd8;j\xa5p\xd9\xe1\xe1+y^\xb6M8%\xd2e8\xd8\xfb\xafџ\xef\x1b\xe6\x05\xb8\xc0\x01\xaa4X\xfa\xc0\xf7\x89\xa9\xd9~a\x8f\xea\xe3\xe7O\x98ϑ\a\xd2$\xefl!\x1f\a\xc8v\xa7\x0eNy\xea2\x82\xeb\xd3\xc47>\xa5q\x03\x02^\xf0\xe4=\x16\xa1\x80\x98#h\xa2\x89H\xe7\x9c8\x9c[a!{\xc1\x13\x83\tɒ\xc5ѩ\xa2\xe0\xdb\v\x9eR\xba\r\bH8I\x1b\x92@DI\xfa\xc0\x84\xe0\xd8:\x9dx\xc0\x89\xaf\xa8\x8b\x96\x17\a\xe9\x8a$\xb6H\xfb7,\xb3a['iȌ\xfd`=\x8bh\x17\x1cd\x95\xb8P2s`\x91wKL}=\x8bB\xe6\xcdD^\xee7j\xda\x1b\xee\xb7\xcf\xdam\xd4\r<|\x956d\x1f?i\xb4\x9f\xb5\xe3/߄\x9c\x1e\xf17\x10\xd3\x0f\xe4\xed\xa5\xbc\xda&:tsh\t\xc2\xed\xdb\xc6Gx\r{\xa4\x85\x8d\xa2\xb8%Ѓ3\xa2~\xbay\xfb\xd0oem9I\xa6\xb4Z\xb1\xa9\\\x8f\xcd䉝\bR\x9b\x1eG\xceQk&\xf5\x13&\x82}\"K\xe2\xc7\xfb\x1co!2\xcc!\xaf\x99\x98\x9c\x99\x14\x0e\xf72\x83\x12\xcd~\xcept[E\xfa=\r\x85D\xad\xebۅ\x12\x96f\xdac\v\xaa;_FfE;7\xa1Wd\xf6b\u05c9\x84\xe4t\xd7\xe5\x15\xb1\x89e\xffc\x91\xba\"\xcf\xf9,I\x14\x8f\x17h\xfc\vxqn\xfb=b\xdeB\x96\x82\x93\x8c\xffCf\x8e\x05\xfa\u007f\xa1\x12\xd2$\xec\xe1\x8f|4T`ol\xc8bu\xa7\xa1\x19\xa4\x05\xe2\xefQ\x14\xe7\xa9\xee\x91\xc5i\xd2-XxC\xaewg\x1e\xcb\r\xbc\x1e\xb4\xf56u'q4\xa5\xdao\xd2\xc2\xf5\v\x9e\xaeo\xce\xf4\xc0\xf5F]{\x03\u007f\xb1\xbai\xbc\x05\xad\x8a\x13\\\xf3\xd8\xeb\xdf\xe2\x04%JbR7>\x82Ku\x95)\x96\x8c\x9e\x00\rlΝ\xc8͝\xc3:I\x0e+m]2*\x8f\xda:\x9fY칥\x97d\xb1\xc0\xcbP\xc8^\x81\xd8\xf9\x93?m\xe2\x99\x0e\xa9\xbdA\u0095\xb8f\xe75,\xb1\xb1Ɉy\xa0\x14X]\xb7;\xd8\xeb\xd3k\u007f\xd0Ó\x88\x8c\x9d\x8bE\xb8\x95\xd1\x19Z;/\"\t\xdaz!I\xd8$\b\x85\x0f`\xfc\x81\xc9|R2\xb6t\x87\x94\x88t\xa1+\xff\U000354fd\xa4\xcdO\u007f/\tߥx\x01\xefٲ\x14Ó\xc1$\x14\xef\xfdȸM\x02 \x1f\x1a\x98}\xcd[=݃\f\x82\xf4{0ӥT\x1b\x9e\x00~xw\xb3\xde(I|\x8b\xe3~\x1fǶDo>\xf0\xeeM\xf5\x884g\xee\r\xf68w\x9e\xe7&G1\x11\xa4Ү\x9bN \xb8\x95\xce?X\xd8Ic]\x17\xd1T\xa1\xa8\x17v\u007f\xdb.\x8d\x9cԃ1o\n\x9c\xfe\xecGv\x12Y\a\xfd\x1a\xcfW'\x0f3\xc7\x1a\x1f\n!\xc8\x1dH\a\xa82]+N\xbf\xd0V\xe7)<\v\xbc\x82N&Y\x9a\x82\xa0\x86\xaa.\xd3\b\xb0b\xa9\x93j6O\xd3\xed\xfe\x93\x90ŷ`\x9b\x93%\xeaz\xd6p\xb6\xadǶ'?\xb2wP^\x8a\xaf\xb2\xacK\x10%\x91>5\xec\xd9\xf9\xe2\x98\x1e\xc7\xe1UHǖ\x83\xe0\xb2\x19q\x9a6UU\xa0Kݑ[\xdci\xc3\xfb\xd9\xca\x1c\x1b\xc3\x1c\xa4@+\x10\xb0\x13\xb2\xa8M\xa2\x86\xbc\x88\xb6\x97\xc4\x1aAY\xbc_\x10\x916\xf9\x8aI\x91\x90\x88Mt\x16\xe7\xb5ue\xd2]\xc5G\x83i\xee\xd9RR:\xbag\x95\x91$K\xfa\xbd=\xb4 bB\x9d\xbe\xbbhg\xed\xbb\x8b\xb6о\xbbh\x93\xed\xbb\x8b\xb6ܾ\xbbh\xa1}w\xd1b\xfb\xee\xa2}w\xd1\xe6\xba\xcdi\xeb%\x8c|\xc5\xfdď\x8bX$\x1cOϡ8\x03?TS\xdc\xfb\xea\xfb\xd4\n\xcb\xcd\xf8\xa8\x91\xba\xdaPֿ\xe2\x1b\tc\x12\xd0\x16]\xb4\xa6\xa4)\xb9\xa4\r\x12\xc5\xdb\x17\x10/\x14a&\x95S\x8eWߦ\x14\xfc,\x95\xf9\xf4\xebL\x9b2\x9bXh\xaa\xe3$#t\x887\x1b\xc8\xed\xed\u0590\xf4\xebu\xd8ύ\x98\xfe\xcdkP\x13Jq\x16\np\xe6\vs\xe7\xe85\b=\xfa\x043\xbd\x82\xd1\xdf\r\xbd\x16\xaad\xa6kc\xc2I\x10:q\xfca\xdd\xff\xc5\xe9P)\x03\xaf\xd2\x1dF\x96\xf2z@\xc5gXj\xdf-{\x8d\xf2\x16\xae\x98\f\xe9\bڀ\x92\x05\x93sFZ{\xe4\x85?W>\x84\xbbx_·\x1fi\xb54o\xae\xa0\xe9W\xc8L\xa8\xe8K\x8f\x8c\xd2\v\x85\xd3kd\xe6\x8bZ.\xa9\x8c\x19ֽL\x02]\xae\x87I\x89\x1c\x17j_\xdeP\xf1\x92X\xed\xf8\x9b\x0f\xc6RjZ\xdeTɲX\x10\x98X\xbfүL\x99\ayA\xd5J\x12q\x96+T.\xaeK\tu \xb3\xebH\xaeF\x19\xa93\x99\x05\xf0\xb4\xaaY\xac\x06Y\xf4\x91\xe7\xf1[\xac\xf7\xb8\xa4\xcac\x91bo\xac\xe8h*6&潴\x8e\xa3_\xa71\x014\xa5zc\xa2:c\x02\xe2l\xcdFjM\xc6\x04\xec\x05\xb3;+%3?\x8e_\x84\x84E\xfbV\xfc\xb5$\xea\xad\v\xd3&G3롧\xa29\x8bb?\xe35\x98sPf\x1f/NR\xaf\xae\xd7?\xc6rݔ\x84g\xf0\xb3T\xb9\x97\x13\x12\xf4\x8e\x9f\xc0\x17\x85\xb9(\xa6qWZ\u007fo\x1c\xe8 ҰX\t\xc37ɷ'\x9f\xad\xb0kx\x10١\xdf\x11\x0e\xc2RLZ\x8e\xbaa\xd7M\x98v\x1bGї\xeb5\xc0O\xba\x89\x84\xbb\u05ec\xac,\xab\xe2\x04\xb5E\xb8\xee\x0fy[\xcc1*\x01V\x89\xca\x1et\xbc\x03\xbb\x10v|\xe9\xf7\x1e\x89\xe8\xe3\rج\xd0u\xde@\x9f`\x9eP'x|f߇\xef\x0ef\xed=\xca\xe0\xdf\xc4Hbx\xcd\xf2\xc7\xf7\x8f\xf0\xad\xd3F\xec\xf1\x17\xed/#/Q\xa2\u07fbw\x13=谘q\x8b\x05Yb\x84\b\xe1Z\xf4\x00X\x9bH\x0f\xbb\xa1M~\x10\x96c\xeamf\xff9W,,\xe6\xe9\xe9\x17\xbf\x00'K\\\u007f\xaa}6eU\tc\x91\xa8\x19\x17\xe6\am\xe9\xbf\a\xfd:\xa6\xf0tX\xf3\x8fC\xbc\rr\xb2\x9e\x936\x17a\xef\xafMG\xc1\x8b$Z\x12\xd4\xe7\xf1Q\x9d@\xaf\xc3$\xbf\xcb\xf5ع\xc4\x14\x9c\xce\xeb\x12\x14X\xfbb\xbb\xf7\xbd\xfb;\xe5\xb1Lݿw\xc2\xd5v\xf9\x06>w\x8b\xefm\x84#\x9f\xda\xf0\xc5]\x0f\xc2_t}\xd3%\xfc\x90\xa1\uef412ϧ\xfb\xf3\x11\xfc҅\xc9=j\x9c\x1bonӿ\n\xdbd\xc1GM|\vΏd\x0f\x9a\xa0a\x0exD\x05Zqқ\xaf\xc4\xfa\xd7X\x86cƒI\x1d(!\xab^W\x85\x16y\xdc\xe1\xd1f\x85\x17<\x9eX\u007f\x99#\x9a\x0fv\x06&\xbf\x18\xb0\xd3f\x8c\b\xe7\n\xd3\x1b\x96;ȅ\xc3\xd5(\xd0$\xdd7*l|>\xb4xo\x9e;\xf9\x1dćK\xf1q\x03\u007f\xbaT\xa2\xb5b\x1f/̿\x92\x02ۣB\xb6\x9b#\xeb\x0f\xf1L{\x1aѿ.\xeeS*\"s\xb5\b\x13\xc4lR\xa7ׇ1\xb3R\xe8=\xecd\xc1]\xc3\xd3\x1eA\xb3O\xa9\x1d\xa9\x1c\xeeq\xe8\xae\xe2\xd7J\x9a\x14K\xf0\xd0t$\xdap>\x8d\xb5A\xfb\x04\x0e\x16r/I\x8d\x12\xb3\xf7\xc2l\xc5\x1eW\x99.\n\xe4҅s\xbc\xbe%\xaf=\xec\xd1'nΖ\xf6S\xb7o\xf4\xa7\x82\xb0{8\xf1ś\x9b`\xa1\xc7\x1d\xd4R\xfcE\x9b\x1b(\xa5\xa2\u007f\xc8\r\xe3\xc84\x0e\xbe\xc8\x1e\xf0\xeb\x02\vx?R\x9f\xe6@\xbb\xa3\xdd0\x8aٔ\xff0~\x88\xb9\x82\xcfxn\xee\xfc\xb9$\xe6\x9c{\x19{ׇ\xbalԣ\xd1{\n\x9dF~\xbc\x8f\xbal\xe4\xb7Ga\x9c\x14Eq\xf2\x93L\xce>\xf2\xc3'$m2iR\xc6\xc9\x1a\xb0\\\xa2l\xe8\xd6\xc6iRyI\xe0\x13ŭ\xae]o\x83\xb6\x1b|D,\xe2\x9ck\xf8\xac\x1d\xc64\x9e\xec\xc3$\xbb\x8a֭p\xb7\xd3\xc6\xf9\xb0p\xb5\x02\xb9\v&j\x04.ixN]\xfbgv@\xba6}\xd2J/{\x9f\x06\x85e\xe9u\xfc\xd8\x0f\x9f.\x89,#\x0f\bo\xad\x13ň\xd6\xf8M\xd9j\xf6\x05H\xfa0\xff\xb7\x11\xe3xF\xf0M\xb7\u007fS\xe2_\x97[\x1f\x1d18O9>\xfd\xf7\x1a\xb3\x98ʣl\x11\x15\xbc\x1a\xe9\x1ci\xa9nn\x1f\x1c饢\x00\xaba'&\x1et\x98ӗ\xfc\xbbv\xa2\xd8L\xe7\x94\xfaNg\xd39.\x8b\x87\x9f/N\x13[\xb6L\x82\x89e\xf9\xea2i\xe3Xbev\x10jOBet\xbd?D\xb9\x9c\xb07S\x99\xf8\x9a\x90\x82\xaa\xa8\xf7$\xea!7\xeej\xa3:qqȖ\xe7\x1dtE\xf62\x89i\xc8\x0eƧ\xden\xc3\x13\r\xab\x9d\xd1\xe5*\xf0\x82\xf3\x067!^5R\x93SF\xd1\xd5\x04\xd0\xf6.4\x8bAU\xa1\x02a\x03>\t\xa5o\xf3l\x9d\v\x1e\x9d0.\xd5\xd5\xfb\xd2\xeb\xbc\xe0\xe51\xe4q|\xbf\x84hܗ\x00\xde\x0f\x1fݣ\xb8Y\xc5W\xe6|\xb4\xefE\xc1\x92\xf3g\x90\x03\xa8\xd1\xf3\x8a3\xb7\xad\xe7\xa4\xf5\xd1\xff\xeb\xfag\xc7\xc6\xc2<\xa4xjσ\xee\x83sT\xda\xe5-\xc4\xe0]\x8d\xd0\xe3\x0fr\xe7\x0fR2\xc2\xfa\x8f\u007f\xf3\xf3\xd1c\x92\xcf\xf2a\xd6]aO\xa4\xf1;\xe0\x13V\x063ڽc\xcbx,\x90\xfc\b\x8b\xd8\xf7\x84>\\\xe4H\xf6\x03X\xfb\xd19,\xab\xd1\x19g\"\xd8vؔ\xb2\x14\xb1\xc3\xc8B\xe2\x03\x88\x11X(-\x9a\tY/XP\xe3\xc4\\\xb6\xa0f\xd8Ԃl\x9d\x91\xd2\xda\xd5\xe3欉\x03\xdfyu\xaf\xc2(\xa9\xf6K{\xec\xdfC\xb7\x91x(@\x18\x89\x88F\x96\xd1\xc4H\x8b\x11Q' \x8a8N\xbcK6\b\x92\xde)$\x1a\xb5\x03g\x1fY\x81杽\x1df\n_\xda,\x85\xc82$q\xfd<|\xe8\xf4\xfa\x9a\xff\x88o\x99\xf2\x9f\x99V\xde\xdc\xda;\xf8\x8f\xff\xbc\x82\x90\x06{\x8e\x8f\x96\xd2\xc7\xff\v\x00\x00\xff\xffy\x8fd\x10\x14V\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec<]o#9r\xef\xfe\x15\x05\xe7a\xee\x00K\xbeE\x82$\xf0۬Nj\x18\xbb\x993֎\xf3\x10\xe4\x81\xea.I\xa3\xb1R\xab\x1b\x10\x95\xc4/\x0e\x15\xfde\xd7/\xffj\xd7R_\x1f\xbe\xbbx\x91*\xbf\x81\xdb\xda:]\xfe\x8cV\xd7&\xc3O\xb8\x95J:\xa9\xd5E\x89N\xe4\u0089\x9b\v\x00\xa1\x94v\x82>[\xfa\x13 \xd3\xca\x19]\x14hV;T\xeb\x97z\x83\x9bZ\x169\x1a\x06\x1e\xa7>\xfci\xfd/\xeb?]\x00d\x06y\xf8\x93,\xd1:QV7\xa0ꢸ\x00P\xa2\xc4\x1b؈쥮\xec\xfa\x80\x05\x1a\xbd\x96\xfa\xc2V\x98\xd1\\;\xa3\xeb\xea\x06\xda\x1f\xfc\x90\x80\x87_\xc3\xf7<\x9a?\x14Һ\x1f;\x1f\u007f\x92\xd6\xf1\x0fUQ\x1bQ43\xf17+ծ.\x84\x89_/\x00l\xa6+\xbc\x81\xcf4E%2\xcc/\x00\xc2rx\xcaU@\xf8𝇐\xed\xb1\x14\x1e\x17\x00]\xa1\xfa\xf8p\xff\xfc\x8f\x8f\xbd\xcf\x009\xda\xcc\xc8\xca1Q\xa1\xc1`uw'\x03ؤ\t\xa9hא\x11!\xf4T\xfb+)\xe6\x91\xc5\t\x83@r+\x95\x87\xc7:w\x8f\xa3\f\xa2&\x1d\x96#\xb8M\x8a\x99od*Ŧ\xc0\x1bp\xa6\xc6\t\xca\bc\xc4q\x82.Ѽ\xa7\x92\xa5\xe9\x1f\xb4H!3\xb6?\x8d\xae`\xcaxk%\xcc)F\xf0[&\xca^\xeb\x97%B\xfc\x1b\xf5i\xf5\x1ed\xec%\xc1\x06\xf7\xe2 \xb5\tK\x0ffh\x83\x80_0\xab\x1d\x8eɿp\x90\xcb\xed\x16\r\xc1\xa9\xf6¢\xf5\xa6o\x9a \xd3[\x99\x9a\x99f\xe6\xc9:ZF\x92\xa4\xf2ʧP\xa7\r=\xdcW\xb1\x11\xa2d4\xc8mQ\xb9<ȼ\x16\x05He\x9dP\x99_\x8fh\xf0:]\x0f\xcc1\xf9\x04g\xaf\x0e#\xe6ĉ\x9ej\xd4\nA\x1b(\xc9\x1e\x9cv\x1dZ\xb0\xb6M-{#H;i/\xa2\xa6.І\xa9rֹ\xad\x0e\xb8\x9a\x04\xddp\xc4\xfb\x12\x85\xd8`\x01\x16\v̜6\xe3\xe4Xb\xb2o)zm\x82\x8a#\x1a\xae\xd5ݴ\xd4va3 \x81\xd4\xf6\xeb^f{o\xe6I\x82\x18\x0e\xe4\x1a-\xefrQU\xc5qj\x91\xb0\xc4\xf90\xc9\xdcFo\xdb\u0096\x1f\xc2\x1b\xdb\xfcmKЍm[В}\xca6\xe2\x00N\xcf.\xfb\uf4f0Q\xed\xbfAh\xefO\x86\xbe\xaf\xd0\x12I%\xb9\xf3\xf7[\xc0\xb2r\xc7+\x90.~]\x82H\xceJ;\xff\xef\x981\xe7K\xfc\xfdp\xe4\xbbJ\xfc,W\x96 \x12W\x9a\xe9\u007f\x87Lac\xf1\x18lE2C~ꎺ\x02\xb9m\x18\x92_\xc1V\x16\x0è3\xbfh\xbf\xbc\a1R\xec\x1d\xb5R\xb8l\u007f\xf7\x85~\xd2h?k\xc7_\xbe\n9=\xe2o \xa6\x1f\xc8\xdbKy\xb5Mt\xe8\xe6\xd0\x12\x84۷{\x1f\xe15\xec\x91\x16\xee\x15\xc5-\x81\x1e\x9c\x11\xf5\xd3\xcdۇ~+k\xcbI2\xa5ՊM\xe5zl&O\xecD\x90\xda\xf48r\x8aZ3\xa9\x9f0\x11\xec\x13Y\x12?\xde\xe7x\v\x91a\x0ey\xcd\xc4\xe4̤p\xb8\x93\x19\x94hvs\x86\xa3\xdb*\xd2\xefi($j]\xdfΔ\xb04\xd3\x1e[P\xdd\xf922+ڹ\t\xbd\"\xb3\x17\xbbN$$\xa7\xbb.\xaf\x88M,\xfb\x1f\x8b\xd4\x15y\xcegI\xa2x8C\xe3\x9f\xc1\x8bS\xdb\xef\x11\xf3\x16\xb2\x14\x9cd\xfc_2s,\xd0\xff\a\x95\x90&a\x0f\u007f䣡\x02{cC\x16\xab;\r\xcd -\x10\u007f\x0f\xa28Mu\x8f,N\x93n\xc1\xc2\x1br\xbd=\xf1X\xae\xe0u\xaf\xad\xb7\xa9[\x89\xa3)\xd5~\x93\x16._\xf0xyu\xa2\a.\xefե7\xf0g\xab\x9b\xc6[Ъ8\xc2%\x8f\xbd\xfc%NP\xa2$&u\xe3#\xb8TW\x99b\xc9\xe8\t\xd0\xc0\xe6܉\xdc\xdc9\xac\x93\xe4\xb0\xd2\xd6%\xa3\xf2\xa0\xad\xf3\x99Ş[zN\x16\v\xbc\f\x85\xec\x15\x88\xad?\xf9\xd3&\x9e\xe9\x90\xda\x1b$\\\x89kv^\xc3\x12\x1b\x9b\x8c\x98\aJ\x81\xd5e\xbb\x83\xbd>\xbd\xf4\a=<\x89\xc8عX\x84[\x19\x9d\xa1\xb5\xf3\"\x92\xa0\xad\x17\x92\x84M\x82P\xf8\x00\xc6\x1f\x98\xcc'%cKwH\x89Hg\xba\xf2w_:\xd9K\xda\xfc\xf4\xf7\x92\xf0\x9d\x8b\x17\xf0\x9e-K1<\x19LB\xf1֏\x8c\xdb$\x00\xf2\xa1\x81\xd9ռ\xd5\xd3=\xc8 H\xbf\x053]Ju\xcf\x13\xc0w\xefn\xd6\x1b%\x89oq\xdco\xe3ؖ\xe8\xcd\a\u07bd\xa9\x1e\x91\xe6̽\xc1\x1e\xe7N\xf3\xdc\xe4(&\x82T\xdau\xd3\t\x04\xb7\xd2\xf9\a\v[i\xac\xeb\"\x9a*\x14\xf5\xc2\xeeo۹\x91\x93\xba3\xe6M\x81ӟ\xfd\xc8N\"k\xaf_\xe3\xf9\xea\xe4a\xe6X\xe3C!\x04\xb9\x05\xe9\x00U\xa6k\xc5\xe9\x17\xda\xea<\x85g\x81W\xd0\xc9$KS\x10\xd4P\xd5e\x1a\x01V,uR\xcd\xe6i\xba\xdd\u007f\x10\xb2\xf8\x1als\xb2D]\xcf\x1aζ\xf5\xd8\xf6\xe4G\xf6\x0e\xcaK\xf1E\x96u\t\xa2$ҧ\x86=[_\x1c\xd3\xe38\xbc\n\xe9\xd8r\x10\\6#NӦ\xaa\nt\xa9;r\x83[mx?[\x99cc\x98\x83\x14h\x05\x02\xb6B\x16\xb5IԐg\xd1\xf6\x9cX#(\x8b\xf7\v\"\xd2&_1)\x12\x12\xb1\x89\xce⼶\xaeL\xba\xab\xf8`0\xcd=[JJG\xf7\xac2\x92dI\xbf\xb7\x87\x16DL\xa8\xe37\x17\xed\xa4}s\xd1\x16\xda7\x17m\xb2}sі\xdb7\x17-\xb4o.Zl\xdf\\\xb4o.\xda\\\xb79m\xbd\x84\x91\xaf\xb8\x9f\xf8q\x11\x8b\x84\xe3\xe99\x14g\xe0\x87j\x8a[_}\x9fZay?>j\xa4\xae6\x94\xf5\xaf\xf8F\u0098\x04\xb4E\x17\xad)iJ.i\x83D\xf1\xf6\x05\xc4\vE\x98I\xe5\x94\xe3շ)\x05?Ke>\xfd:Ӧ\xcc&\x16\x9a\xea8\xc9\b\x1d\xe2\xcd\x06r{\xbb5$\xfdz\x1d\xf6s#\xa6\u007f\xf3\x1aԄR\x9c\x85\x02\x9c\xf9\xc2\xdc9z\rB\x8f>\xc1L\xaf`\xf47C\xaf\x85*\x99\xe9ژp\x12\x84N\x1c\xbe[\xf7\u007fq:T\xca\xc0\xabt\xfb\x91\xa5\xbc\xeeQ\xf1\x19\x96\xdau\xcb^\xa3\xbc\x85+&C:\x826\xa0d\xc1䜑\xd6\x1ey\xe1ϕ\x0f\xe1\xceޗ\xf3\xe1GZ-͛+h\xfa\x152\x13*\xfa\xdc#\xa3\xf4B\xe1\xf4\x1a\x99\xf9\xa2\x96s*c\x86u/\x93@\x97\xebaR\"Džڗ7T\xbc$V;\xfe\u20f1\x94\x9a\x967U\xb2,\x16\x04&֯\xf4+S\xe6A\x9eQ\xb5\x92D\x9c\xe5\n\x95\xb3\xebRB\x1d\xc8\xec:\x92\xabQF\xeaLf\x01O֠\xccU\x97,d\xa5N+O\xd2kJfAs\xbd\xc9r%\xc9\xfbՋ\xbe\x87\x0f<\xadj\x16\xabA\x16}\xe4y\xfc\x16\xeb=Ω\xf2X\xa4\xd8\x1b+:\x9a\x8a\x8d\x89yϭ\xe3\xe8\xd7iL\x00M\xa9ޘ\xa8Θ\x808[\xb3\x91Z\x931\x01{\xc1\xec\xceJ\xc9̏\xe3\x17!aѾ\x15\xbf\x96D\xbdua\xda\xe4hf=\xf4T4gQ\xecg\xbc\x06s\x0e\xca\xec\xe3\xc5I\xea\xd5\xf5\xfa\xc7X\xae\x9b\x92\xf0\f~\x94*\xf7rB\x82\xde\xf1\x13\xf8\xa20\x17\xc54\xeeJ\xeb\xef\x8d\x03\x1dD\x1a\x16+a\xf8&\xf9\xe6\xe8\xb3\x15v\rw\"\xdb\xf7;\xc2^X\x8aI\xcbQ7\xec\xb2\tӮ\xe3(\xfar\xb9\x06\xf8A7\x91p\xf7\x9a\x95\x95eU\x1c\xa1\xb6\b\x97\xfd!o\x8b9F%\xc0*Qٽ\x8ew`\x17\u008e\xc7~\uf448>ހ\xcd\n]\xe7\r\xf4\t\xe6\tu\x84\x87g\xf6}\xf8\xee`\xd6ޣ\f\xfeM\x8c$\x86\xd7,\xbf\u007f\xff\b\xdf:m\xc4\x0e\u007f\xd2\xfe2\xf2\x12%\xfa\xbd{7у\x0e\x8b\x19\xb7X\x90%F\x88\x10\xaeE\x0f\x80\xb5\x89\xf4\xb0\x1b\xda\xe4\aa9\xa6\xdef\xf6\x9fs\xc5\xc2b\x9e\x9e~\xf2\vp\xb2\xc4\xf5\xa7\xdagSV\x950\x16\x89\x9aqa~І\xfe\xbbׯc\nO\x875\u007f?\xc4\xdb '\xeb9is\x16\xf6\xfe\xdat\x14\xbcH\xa2%A}\x1e\x1f\xd5\t\xf4:L\xf2\xbb\\\x8f\x9dKL\xc1\xe9\xbc.A\x81\xb5/\xb6{\u07fb\xbfS\x1e\xcb\xd4\xfd{'\\m\x97o\xe0s\xb7\xf8\xdeF8\xf2\xa9\r_\xdc\xf5 \xfcE\xd77]\xc2\x0f\x19\xea\xde\x1b(\xf3|\xba=\x1d\xc1/]\x98ܣƹ\xf1\xe66\xfd\xab\xb0M\x16|\xd4ķ\xe0\xfcH\xf6\xa0\t\x1a\xe6\x80\aT\xa0\x15'\xbd\xf9J\xac\u007f\x8de8f,\x99ԁ\x12\xb2\xeauUh\x91\xc7\x1d\x1emVx\xc1\xe3\x89\xf5\x979\xa0\xf9`g`\xf2\x8b\x01[mƈp\xaa0\xbda\xb9\x81\\8\\\x8d\x02M\xd2}\xa3\u0096Y\xd9\x17t\xfb\xd19\x8aGƼ\xf5>\xff\xa6GF\xfb\xeb\xb4\x13\x05\xa8\xba\xdcx\x83.b\x871\xfe=\xde\x0f\xb6\x9c\r\xc7 3\xdb\xcb/L*\x87;\x1c:\x9d\xa7+\xbb\x8d\xf2s\xf6ʚ\x91S+\xb3u\x96\xa1\xb5ۺ(Ƃ\x8cFr\u007f\x8de\x06\x93\xf2D(\x9e\xbd\xd2\xee\xe0\xfeb\x83\x91\x04+\xff:\x9a\xa6\xde\xfaW\x90\xb0c\xfa\x97W\x16E[*\xf7\xcf\xfftֺ\xf9`s\xf1\xc1\a\xee\xe4U?\x9f\x8a\xc6W9\xfc\xb1h\x89֊]|\xe9\xe1\x95,\xef\x0e\x15\xb2\xc37\xb2\xc4\x10\x88\xb7\xc7h\xfdw\x0e|.Pd\xae\x16a\x82\x98\x06\xed\xf4\xfa0\xe6\x0f\x15z\a[Yp\xd7\xf0&M\xa0\xf6y\xb2\x80_*iR\\\x98\xbb\xa6#ц\x13\xc1̦\xf6\xed&,\xe4N\x92\xfd'\x16\xee\x84و\x1d\xae2]\x14\xc857Ӝ\xfc\x1aJ\xca\xc3\x1e}\x9b\xe9di?t\xfbF\t\x0eZ\xdaÉO5]\x05\xd7r<\xb2*\xc5_\xb4\xb9\x82R*\xfa\x87\xe2\aN\xa9\xc4\xc1g92\xfc,\xc6\x02\xde\x0fԧ\xa9\xc4\xe8\x98e\x8cb6\xe5\xf8\x8e\x9f\xbe\xaf\xe03\x9e\xfai\xfe@\x1dsN\x1a\x8e=HE]\xeeՃ\xd1;\x8a\xf9G~lT\xe1\xc8o\x0f\xc28)\x8a\xe2\xe8'\x99\x9c}\xe4\x87OHfp\xd2\x17\x1a'k\xc0r\x89\xb2\xa1[\x9b`\x90\xcaK\x02\x1f\x85ot\xedz\x1b\xb4\xdd\xe0#b\x11\xe7\\\xc3g\xed0\xe6\x9fe\x1f&)<\xb4n\x85ۭ6\xce\xe73V+\x90\xdb\xe0[\x8d\xc0%ׄ\xcf\\\xfc\xfbP ]\x9b\xf7k\xa5\x97\xc3&\x83²\xf4:~\xa5\x8a\x8fEE\x96\x91\xeb\x8e\xd7։bDk\xfc\xa2c\x16vbI\xfa0\xff\x8f\x11\xaf\xee\x84\xe0\xf7\xdd\xfe\xcdݔ\xc6V28O9.[\xf1\x1as\xd4n\x02\x173\xa0\x82W#\x9d#-\xd5=\x94\x02Gz\xa9(\xc0j؊\x89\x97H\xe6\xf4%\xffN\xc6\xed~:\x19ڏ\x96\x9a\xceS\x8e@X\x9c&\xb6l\x98\x04\x13\xcb\xf2e\x91\xd2Ʊ\xc4\xcal/Ԏ\x84\xca\xe8z\xb7\x8fr9ao&\xe0\xe65!\x05UQ\xefH\xd4á\x8e\xab\x8d\xea$t\xc21O\xdeAWd/\x93\x98\x86\xb4v|\xa3\xf0:\xbc-\xb2\xda\x1a]\xae\x02/8\xe1u\x15\x12-Fj\x8a&\xdc~\x94\xe4\xd4\xdaK\xfc,\x06U\x85\n\x84\r\xf8$\xd4lγu.\xeb\xe1\x84q\xa91\xcac\xaf\xf3Bx\u0090\xc7\xf1}\fi$_\xbbz;|-\xf2\n\xacT\xf1yD\x9f\xa6\xf2\xa2`)j1ȑ\xff\xe8A\xdbI\xbcы.\xfa\xe8\xff\xba\x81š\xb10w)\x9e\xda\xf3\xa0\xfb\xa0\x00\x80vy\v1xW#\xf4\xf8\x83\xdc\xfa\x13\xc0\x8c\xb0\xfe\xe3\xdf\xfc`\xff\x90\xe4\xb3|\x98uW\xd8\x13i\xfc\x0e\xf8\x84\x95\xc1L\x8c\x861\x00\x0f\x05\x92\x1fa\x11\xfb\x9eЇ\xb3\x1c\xc9\xc3\xdbB\xc2\xf7\x8c\a\xe3˝\xef\x13%\x1d\xde\x16\t~\xb50\xf0}W\xf7*\x8c\x92j\xb7\xb4\xc7\xfe3t\x1b\x89\x87\x02\x84\x91\x88hd\x19M\x8c\xb4\x18\x11u\x02\xa2\x88\xe3ăz\x83 \xe9\x9dB\xa2Q;p\xf2\x91\x15h\xde\xd9\xdba\xa6\xf0\xa5M\xaf\x89,C\x12\xd7\xcf\xc3\x17z//\xf9\x8f\xf8\b/\xff\x99i\xe5ͭ\xbd\x81\xff\xfa\xef\v\b\xf9\xdb\xe7\xf8\xda.}\xfc\xff\x00\x00\x00\xff\xff\xf3|\x1d\xde\xcdX\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5饸p\x9e36\xfc\xfb?\x936yS\x86l<\x8a\x1f\xfa\x9f\xdaޥ\x00R\xbe\x97ş\x9c\xaa\x9a\xf4\xad\x10\xfe\xf6\x8fI\xba\x18\xc5s\xf9\xc0E\x93\xff\r\x00\x00\xff\xff\x04\x0e\x95\xf5\xa5\x1c\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V=s\xe36\x10\xed\xf9+v.\xc55\x11u7)\x92Q\x97\xf8\xae\xf0$\xf1x\xec\x1b7\x99\x14\x10\xb0\x127&\x01dw!\xc7\xf9\xf5\x19\x00\xa4%Q\xf4\xc5)\u008e\xfb\x85\x87\xf7v\x97lV\xabUc\"= \v\x05\xbf\x01\x13\t\xffR\xf4\xf9M\xda\xc7\x1f\xa4\xa5\xb0>|l\x1eɻ\r\\%\xd10ܡ\x84\xc4\x16?\xe1\x8e<)\x05\xdf\f\xa8\xc6\x195\x9b\x06\xc0x\x1f\xd4d\xb3\xe4W\x00\x1b\xbcr\xe8{\xe4\xd5\x1e}\xfb\x98\xb6\xb8M\xd4;\xe4R|:\xfa\xf0\xa1\xfd\xbe\xfd\xd0\x00Xƒ\xfe\x85\x06\x145C܀O}\xdf\x00x3\xe0\x06\x1c\xf6\xa8\xb85\xf61E\xc6?\x13\x8aJ{\xc0\x1e9\xb4\x14\x1a\x89h\xf3\xc1{\x0e)n\xe0\xe8\xa8\xf9#\xa8z\xa1O\xa5\xd4O\xa5\xd4]-U\xbc=\x89\xfe\xfcZ\xc4/4F\xc5>\xb1\xe9\x97\x01\x95\x00!\xbfO\xbd\xe1Ő\x06@l\x88\xb8\x81\x9b\f+\x1a\x8b\xae\x01\x18\xf9(0W\xe3\x8d\x0f\x1fk9\xdb\xe1`*~\x80\x10\xd1\xffx{\xfd\xf0\xdd\xfd\x99\x19\xc0\xa1X\xa6\xa8\x85\xd5\x05\xfc@\x02\x06F\x14\xa0a\x04\a\xc1#\x04\x86!0BE*\xedK\xd1\xc8!\"+M\xfc\xd5\xe7\xa4uN\xac3\b\xef3\xca\x1a\x05.\xf7\f\nh\x87\xd3Mэ\x17\x83\xb0\x03\xedH\x8012\n\xfa\xdaEg\x85!\a\x19\x0fa\xfb\aZm\xe1\x1e9\x97\x01\xe9B\xea]n\xb5\x03\xb2\x02\xa3\r{O\u007f\xbfԖ|\xcf|hot\x12\xf9\xf8\x90Wdoz8\x98>\xe1\xb7`\xbc\x83\xc1<\x03c>\x05\x92?\xa9WB\xa4\x85_3M\xe4wa\x03\x9dj\x94\xcdz\xbd'\x9dFƆaH\x9e\xf4y]\xba\x9f\xb6I\x03\xcb\xda\xe1\x01\xfb\xb5\xd0~e\xd8v\xa4h51\xaeM\xa4U\x81\xee\xcbش\x83\xfb\x86\xc7!\x93\xf7gX\xf597\x8c(\x93ߟ8J7\u007fE\x81\xdc\xcbU\xf6\x9aZoq$:\x9b2;w\x9f\xef\xbf\xc0tt\x11c\xce~\xe1\xfd\x98(G\t2a\xe4w\xc8U\xc4\x1d\x87\xa1\xd4D\xefb \xaf\xe5\xc5\xf6\x84~N\xbf\xa4\xed@*SKf\xadZ\xb8*{\x04\xb6\b):\xa3\xe8Z\xb8\xf6pe\x06쯌\xe0\xff.@fZV\x99طIp\xba\x02\xe7\xc1\x95\xb5\x13Ǵ\xa3^\xd1kah\xef#ڬ`&1gӎl\x19\x0f\xd8\x05\x86\xa7\x8el7\r\xed\x8cݗ\x01o\xcf\x1c\xcb\x03\x9d\x9fZ&/\xa5\xb9\xe7\xd5\xcbCю\x18g]\xb8:)\xf6&^\xd4h\x92\xff\xc8Lə\xb8\xb1\x89\x19\xbd\x8e\x95ʶXJz+\x17\xc8\x1c\xf8\xc2:\x03\xf5\xb9\x04\x95\xef\x9c!/`\xfc\xf3\x98\b\xda\x19\x85'\xe4<\x066\xa4\xbcgЁK\x17\xfc\x8d\xb4tX\xc5\xca\xc2F\x0e\x16Eڋ8R\x1c\x160}E\x9d\xfc\xe4o\xa8\xd9\xf6\xb8\x01儯(k\x98\xcd\xf3\xcc\x17;#\v\xadpF\xc1m\x8eY\xd2\x00\xebV\xc7\u007f\x17\xa1\xd0\xed\xd3py\xd2\nn\xf0i\xc1z\xedo9\xec\x19e\xde\xf2\xd9y[\xd9+\xdf\xd47\xb2\xb4ؔ\x17F\xc9\xfbΝ\xb0(\x1a\xd8\xec'^\x8f-l\xacŨ\xe8n\xe6\u007f\x1d\xefޝ\xfd>\x94W\x1b\xbc\xa3\xfa\xd3\x04\xbf\xfd\xdeԪ\xe8\x1e\xa6\xbf\x81l\xfc'\x00\x00\xff\xff\x8c\xdb\x1fܮ\t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdc6\f\xbd\xfbW\x10\xe9!-\x10{\x12\xf4\xd0bn\xed&\x87\xa0i\x10\xec\xa6{)z\xd0\xc8\x1c[]YREj6ۢ\xff\xbd\xa0dχdz\xbb9T7K\x14\xf5D>>\xd1U]ו\n\xe6\x16#\x19\xef֠\x82\xc1/\x8cN\xbe\xa8\xb9\xfb\x91\x1a\xe3W\xbb7՝q\xed\x1a\xae\x12\xb1\x1f\xae\x91|\x8a\x1a\xdf\xe2\xd68\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95L\x93|\x02h\xef8zk1\xd6\x1d\xba\xe6.mp\x93\x8cm1f\xe7\xd3ѻ\xd7\xcd\x0f\xcd\xeb\n@G\xcc\xdb?\x9b\x01\x89\xd5\x10\xd6\xe0\x92\xb5\x15\x80S\x03\xae\xa1\xf5\xf7\xcez\xd5F\xfc+!15;\xb4\x18}c|E\x01\xb5\x1c\xdaE\x9f\xc2\x1a\x0e\ve\xef\b\xa8\\\xe6\xed\xe8溸\xc9+\xd6\x10\xff\xb2\xb4\xfa\xc1\x8c\x16\xc1\xa6\xa8\xec9\x88\xbcH\xc6uɪx\xb6\\\x01\x90\xf6\x01\xd7\xf0Q`\x04\xa5\xb1\xad\x00ƻgX\xf5x\xbbݛ\xe2J\xf78\xa8\x82\x17\xc0\at?}z\u007f\xfb\xfd\xcd\xc94@\x8b\xa4\xa3\t\x9c#8\xc3\f\x86@\xc1\x88\x00\xd8\xefA\x81r\xa0\"\x9b\xad\xd2\f\xdb\xe8\a\xd8(}\x97\xc2\xde+\x80\xdf\xfc\x89\x9a\x81\xd8G\xd5\xe1+\xa0\xa4{P⯘\x82\xf5\x1dl\x8d\xc5f\xbf)D\x1f0\xb2\x99\xa2\\\xc6\x11\xb9\x8efg\xc0_\xca݊\x15\xb4\xc2*$\xe0\x1e\xa7\xf8`;\x86\x03\xfc\x16\xb87\x04\x11CDBWxv\xe2\x18\xc4H\xb9\xf1\x06\r\xdc`\x147@\xbdO\xb6\x152\xee02DԾs\xe6\xef\xbdo\x92\bɡV\xf1D\x87\xc30\x8e1:ea\xa7l\xc2W\xa0\\\v\x83z\x80\x889N\xc9\x1d\xf9\xcb&\xd4\xc0\xaf>\"\x18\xb7\xf5k\xe8\x99\x03\xadW\xab\xce\xf0TT\xda\x0fCr\x86\x1fV\xb9>\xcc&\xb1\x8f\xb4jq\x87vE\xa6\xabUԽaԜ\"\xaeT0u\x86\xeera5C\xfbM\x1cː^\x9e`\xe5\a\xa1\x19q4\xae;ZȜ\u007f$\x03\xc2\xfaB\x98\xb2\xb5\xdc\xe2\x10h\x99\x92\xe8\\\xbf\xbb\xf9\f\xd3\xd19\x19\xf3\xe8\x17\xe6\xec7\xd2!\x05\x120\xe3\xb6\x18K\x123\xf3\xc4'\xba6x\xe38\u007fhk\xd0\xcd\xc3Oi3\x18\xa6\x89̒\xab\x06\xae\xb2\xd2\xc0\x06!\x85V1\xb6\r\xbcwp\xa5\x06\xb4W\x8a\xf0\u007fO\x80D\x9aj\t\xec\xf3Rp,\x92s\xe3\x12\xb5\xa3\x85I\xc9.\xe4kV\xea7\x01\xb5dO\x02(;\xcd\xd6\xe8\\\x1a\xb0\xf5\x11ԡ\xf2\xc7\x006'\x9e\x97+7\x83S\xb1C\x9e\xcfΰ|\xceFr\xfc}\xafN\x85\xe6[l\xbaF\xb4\x82F E=\xbek\xce<^\xc6\x00\x8b\xec]D2\x91X\xc2 q\x15)\x10\x91:\xc6t~\xb4\ftiX>\xa0\x86\x9f3\xe6\x0f\xbe{t\xfd\xca;\x16\xba?jt\xebm\x1a\xf0Ʃ@\xbd\u007f\xc2\xf6=\xe3\xf0<\xcb\xe9A\xde?R\xe7\x86\xd7(R\x8e\x97/1\x1a\\#%{\xe1\xb8\v\xb4\x9eF~\xbe\x9eΑ<\x80S\x8edK\xd1t\x04i\v\xa2CF:\xc8˽\xe1~\xd1#\xc0}ot\x9f7\xe6\x04\x8br\x11ym\xb2\x0e|=|\xa9\v\x13q\x81du&\xdf´\x80?\x9b\xbeP͗\x0e\xa8\xc7\n{\x96\"\xb0\xe2D_\xa1\t\xd9~\n\xb5N1\xa2\xe3\xd1K~#\xe7\x1b\x9e+\nS%\xfdv\xfd\xe1\tex{\xb0\xcc]\xa02\xae\xa0\t\x11k2\x9d\xbc\xec\xb2&ڐk\xf6<\x18e\x9cv\x1a\xa7\x81Z\xcc(~\t&f\x05|\x02\u2efda\x110t\xe5q\x9a\xf7R\xd9!R~\xf8\xb5\x9a\xb7\x1c26\b-Zdla\xf3P\x94\xf8\x81\x18\x87s\xdc[\x1f\a\xc5k\x90G\xabf\xb3@#\xe9w\xd5\xc6\xe2\x1a8\xa6K,[\xbcx\xe8\x15-\x94\xe1ɝ?\x89\xcd\x121\xf6\xc5\xf8(3\xe0\xa2^\xd6\xf0\x11\xef\x17f?E\xaf\x91\b\xcf\xcb\xe8\xe2M\x16\x8b\xe0l\x92\xa4\xb3h\x8f\xa246\xac\xc73i\xb3\xef\x94&\xc4c)\xc1?\xffV\x87\xaaRZc`l?\xce\u007f\x14^\xbc8\xe9\xfc\xf3\xa7\xf6\xae5\xe5\x1f\a~\xff\xa3*\ac{;5\xf42\xf9_\x00\x00\x00\xff\xff\xcbT\xc3P]\r\x00\x00"), diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 6148f5306..184963305 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -17,7 +17,6 @@ limitations under the License. package v1 import ( - resource "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -325,7 +324,7 @@ type BackupStatus struct { // CsiVolumeSnapshotsStorageTotal is the total storage size of created // snapshots for this backup. // +optional - CsiVolumeSnapshotsStorageTotal resource.Quantity `json:"csiVolumeSnapshotsStorageTotal,omitempty"` + CsiVolumeSnapshotsStorageTotal int64 `json:"csiVolumeSnapshotsStorageTotal,omitempty"` } // BackupProgress stores information about the progress of a Backup's execution. diff --git a/pkg/backup/request.go b/pkg/backup/request.go index b5bf8ae24..1ca4c2c40 100644 --- a/pkg/backup/request.go +++ b/pkg/backup/request.go @@ -21,6 +21,7 @@ import ( "sort" snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + "github.com/vmware-tanzu/velero/internal/hook" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/plugin/framework" diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index a2bcfc0d4..f20e9f3c8 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -640,7 +640,12 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { for _, vs := range backup.CsiSnapshots { if *vs.Status.ReadyToUse { backup.Status.CsiVolumeSnapshotsCompleted++ - backup.Status.CsiVolumeSnapshotsStorageTotal.Add(*vs.Status.RestoreSize) + storageSize, ret := vs.Status.RestoreSize.AsInt64() + if !ret { + backupLog.WithError(fmt.Errorf("fail to convert CSI snapshot size: %v to int64", backup.Status.CsiVolumeSnapshotsStorageTotal)) + storageSize = 0 + } + backup.Status.CsiVolumeSnapshotsStorageTotal += storageSize } } @@ -707,12 +712,7 @@ func recordBackupMetrics(log logrus.FieldLogger, backup *velerov1api.Backup, bac serverMetrics.RegisterCsiSnapshotAttempts(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsAttempted) serverMetrics.RegisterCsiSnapshotSuccesses(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsCompleted) serverMetrics.RegisterCsiSnapshotFailures(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsAttempted-backup.Status.CsiVolumeSnapshotsCompleted) - storageSize, ret := backup.Status.CsiVolumeSnapshotsStorageTotal.AsInt64() - if !ret { - log.WithError(fmt.Errorf("fail to convert CSI snapshot size: %v to int64", backup.Status.CsiVolumeSnapshotsStorageTotal)) - storageSize = 0 - } - serverMetrics.RegisterCsiStorageSizeAdd(backupScheduleName, backup.Name, storageSize) + serverMetrics.RegisterCsiStorageSizeAdd(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsStorageTotal) } if backup.Status.Progress != nil { diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 0a9383456..c2d85a105 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -419,18 +419,6 @@ func (m *ServerMetrics) InitSchedule(scheduleName string) { if c, ok := m.metrics[volumeSnapshotFailureTotal].(*prometheus.CounterVec); ok { c.WithLabelValues(scheduleName).Add(0) } - if c, ok := m.metrics[csiSnapshotAttemptTotal].(*prometheus.CounterVec); ok { - c.WithLabelValues(scheduleName).Add(0) - } - if c, ok := m.metrics[csiSnapshotSuccessTotal].(*prometheus.CounterVec); ok { - c.WithLabelValues(scheduleName).Add(0) - } - if c, ok := m.metrics[csiSnapshotFailureTotal].(*prometheus.CounterVec); ok { - c.WithLabelValues(scheduleName).Add(0) - } - if c, ok := m.metrics[csiSnapshotStorageTotal].(*prometheus.GaugeVec); ok { - c.WithLabelValues(scheduleName).Add(0) - } } // InitSchedule initializes counter metrics for a node. From 9aa4e9e8602d96bf21d3d637b3052d6290e09574 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Sat, 9 Apr 2022 04:45:06 +0000 Subject: [PATCH 3/3] Update according to comments. Remove csiVolumeSnapshotsStorageTotal related code. Signed-off-by: Xun Jiang --- config/crd/v1/bases/velero.io_backups.yaml | 9 ++--- config/crd/v1/crds/crds.go | 2 +- pkg/apis/velero/v1/backup.go | 13 +++----- pkg/backup/request.go | 2 +- pkg/controller/backup_controller.go | 19 ++++------- pkg/controller/backup_deletion_controller.go | 21 ------------ pkg/metrics/metrics.go | 35 ++++---------------- 7 files changed, 20 insertions(+), 81 deletions(-) diff --git a/config/crd/v1/bases/velero.io_backups.yaml b/config/crd/v1/bases/velero.io_backups.yaml index 5cbdd76d3..259e3cd4f 100644 --- a/config/crd/v1/bases/velero.io_backups.yaml +++ b/config/crd/v1/bases/velero.io_backups.yaml @@ -355,18 +355,13 @@ spec: nullable: true type: string csiVolumeSnapshotsAttempted: - description: CsiVolumeSnapshotsAttempted is the total number of attempted + description: CSIVolumeSnapshotsAttempted is the total number of attempted CSI VolumeSnapshots for this backup. type: integer csiVolumeSnapshotsCompleted: - description: CsiVolumeSnapshotsCompleted is the total number of successfully + description: CSIVolumeSnapshotsCompleted is the total number of successfully completed CSI VolumeSnapshots for this backup. type: integer - csiVolumeSnapshotsStorageTotal: - description: CsiVolumeSnapshotsStorageTotal is the total storage size - of created snapshots for this backup. - format: int64 - type: integer errors: description: Errors is a count of all error messages that were generated during execution of the backup. The actual errors are in the backup's diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index da0f366b2..3819b66a8 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -29,7 +29,7 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec<]o#9r\xef\xfe\x15\x05\xe7a\xee\x00K\xbeE\x82$\xf0۬Nj\x18\xbb\x993֎\xf3\x10\xe4\x81\xea.I\xa3\xb1R\xab\x1b\x10\x95\xc4/\x0e\x15\xfde\xd7/\xffj\xd7R_\x1f\xbe\xbbx\x91*\xbf\x81\xdb\xda:]\xfe\x8cV\xd7&\xc3O\xb8\x95J:\xa9\xd5E\x89N\xe4\u0089\x9b\v\x00\xa1\x94v\x82>[\xfa\x13 \xd3\xca\x19]\x14hV;T\xeb\x97z\x83\x9bZ\x169\x1a\x06\x1e\xa7>\xfci\xfd/\xeb?]\x00d\x06y\xf8\x93,\xd1:QV7\xa0ꢸ\x00P\xa2\xc4\x1b؈쥮\xec\xfa\x80\x05\x1a\xbd\x96\xfa\xc2V\x98\xd1\\;\xa3\xeb\xea\x06\xda\x1f\xfc\x90\x80\x87_\xc3\xf7<\x9a?\x14Һ\x1f;\x1f\u007f\x92\xd6\xf1\x0fUQ\x1bQ43\xf17+ծ.\x84\x89_/\x00l\xa6+\xbc\x81\xcf4E%2\xcc/\x00\xc2rx\xcaU@\xf8𝇐\xed\xb1\x14\x1e\x17\x00]\xa1\xfa\xf8p\xff\xfc\x8f\x8f\xbd\xcf\x009\xda\xcc\xc8\xca1Q\xa1\xc1`uw'\x03ؤ\t\xa9hא\x11!\xf4T\xfb+)\xe6\x91\xc5\t\x83@r+\x95\x87\xc7:w\x8f\xa3\f\xa2&\x1d\x96#\xb8M\x8a\x99od*Ŧ\xc0\x1bp\xa6\xc6\t\xca\bc\xc4q\x82.Ѽ\xa7\x92\xa5\xe9\x1f\xb4H!3\xb6?\x8d\xae`\xcaxk%\xcc)F\xf0[&\xca^\xeb\x97%B\xfc\x1b\xf5i\xf5\x1ed\xec%\xc1\x06\xf7\xe2 \xb5\tK\x0ffh\x83\x80_0\xab\x1d\x8eɿp\x90\xcb\xed\x16\r\xc1\xa9\xf6¢\xf5\xa6o\x9a \xd3[\x99\x9a\x99f\xe6\xc9:ZF\x92\xa4\xf2ʧP\xa7\r=\xdcW\xb1\x11\xa2d4\xc8mQ\xb9<ȼ\x16\x05He\x9dP\x99_\x8fh\xf0:]\x0f\xcc1\xf9\x04g\xaf\x0e#\xe6ĉ\x9ej\xd4\nA\x1b(\xc9\x1e\x9cv\x1dZ\xb0\xb6M-{#H;i/\xa2\xa6.І\xa9rֹ\xad\x0e\xb8\x9a\x04\xddp\xc4\xfb\x12\x85\xd8`\x01\x16\v̜6\xe3\xe4Xb\xb2o)zm\x82\x8a#\x1a\xae\xd5ݴ\xd4va3 \x81\xd4\xf6\xeb^f{o\xe6I\x82\x18\x0e\xe4\x1a-\xefrQU\xc5qj\x91\xb0\xc4\xf90\xc9\xdcFo\xdb\u0096\x1f\xc2\x1b\xdb\xfcmKЍm[В}\xca6\xe2\x00N\xcf.\xfb\uf4f0Q\xed\xbfAh\xefO\x86\xbe\xaf\xd0\x12I%\xb9\xf3\xf7[\xc0\xb2r\xc7+\x90.~]\x82H\xceJ;\xff\xef\x981\xe7K\xfc\xfdp\xe4\xbbJ\xfc,W\x96 \x12W\x9a\xe9\u007f\x87Lac\xf1\x18lE2C~ꎺ\x02\xb9m\x18\x92_\xc1V\x16\x0è3\xbfh\xbf\xbc\a1R\xec\x1d\xb5R\xb8l\u007f\xf7\x85~\xd2h?k\xc7_\xbe\n9=\xe2o \xa6\x1f\xc8\xdbKy\xb5Mt\xe8\xe6\xd0\x12\x84۷{\x1f\xe15\xec\x91\x16\xee\x15\xc5-\x81\x1e\x9c\x11\xf5\xd3\xcdۇ~+k\xcbI2\xa5ՊM\xe5zl&O\xecD\x90\xda\xf48r\x8aZ3\xa9\x9f0\x11\xec\x13Y\x12?\xde\xe7x\v\x91a\x0ey\xcd\xc4\xe4̤p\xb8\x93\x19\x94hvs\x86\xa3\xdb*\xd2\xefi($j]\xdfΔ\xb04\xd3\x1e[P\xdd\xf922+ڹ\t\xbd\"\xb3\x17\xbbN$$\xa7\xbb.\xaf\x88M,\xfb\x1f\x8b\xd4\x15y\xcegI\xa2x8C\xe3\x9f\xc1\x8bS\xdb\xef\x11\xf3\x16\xb2\x14\x9cd\xfc_2s,\xd0\xff\a\x95\x90&a\x0f\u007f䣡\x02{cC\x16\xab;\r\xcd -\x10\u007f\x0f\xa28Mu\x8f,N\x93n\xc1\xc2\x1br\xbd=\xf1X\xae\xe0u\xaf\xad\xb7\xa9[\x89\xa3)\xd5~\x93\x16._\xf0xyu\xa2\a.\xefե7\xf0g\xab\x9b\xc6[Ъ8\xc2%\x8f\xbd\xfc%NP\xa2$&u\xe3#\xb8TW\x99b\xc9\xe8\t\xd0\xc0\xe6܉\xdc\xdc9\xac\x93\xe4\xb0\xd2\xd6%\xa3\xf2\xa0\xad\xf3\x99Ş[zN\x16\v\xbc\f\x85\xec\x15\x88\xad?\xf9\xd3&\x9e\xe9\x90\xda\x1b$\\\x89kv^\xc3\x12\x1b\x9b\x8c\x98\aJ\x81\xd5e\xbb\x83\xbd>\xbd\xf4\a=<\x89\xc8عX\x84[\x19\x9d\xa1\xb5\xf3\"\x92\xa0\xad\x17\x92\x84M\x82P\xf8\x00\xc6\x1f\x98\xcc'%cKwH\x89Hg\xba\xf2w_:\xd9K\xda\xfc\xf4\xf7\x92\xf0\x9d\x8b\x17\xf0\x9e-K1<\x19LB\xf1֏\x8c\xdb$\x00\xf2\xa1\x81\xd9ռ\xd5\xd3=\xc8 H\xbf\x053]Ju\xcf\x13\xc0w\xefn\xd6\x1b%\x89oq\xdco\xe3ؖ\xe8\xcd\a\u07bd\xa9\x1e\x91\xe6̽\xc1\x1e\xe7N\xf3\xdc\xe4(&\x82T\xdau\xd3\t\x04\xb7\xd2\xf9\a\v[i\xac\xeb\"\x9a*\x14\xf5\xc2\xeeo۹\x91\x93\xba3\xe6M\x81ӟ\xfd\xc8N\"k\xaf_\xe3\xf9\xea\xe4a\xe6X\xe3C!\x04\xb9\x05\xe9\x00U\xa6k\xc5\xe9\x17\xda\xea<\x85g\x81W\xd0\xc9$KS\x10\xd4P\xd5e\x1a\x01V,uR\xcd\xe6i\xba\xdd\u007f\x10\xb2\xf8\x1als\xb2D]\xcf\x1aζ\xf5\xd8\xf6\xe4G\xf6\x0e\xcaK\xf1E\x96u\t\xa2$ҧ\x86=[_\x1c\xd3\xe38\xbc\n\xe9\xd8r\x10\\6#NӦ\xaa\nt\xa9;r\x83[mx?[\x99cc\x98\x83\x14h\x05\x02\xb6B\x16\xb5IԐg\xd1\xf6\x9cX#(\x8b\xf7\v\"\xd2&_1)\x12\x12\xb1\x89\xce⼶\xaeL\xba\xab\xf8`0\xcd=[JJG\xf7\xac2\x92dI\xbf\xb7\x87\x16DL\xa8\xe37\x17\xed\xa4}s\xd1\x16\xda7\x17m\xb2}sі\xdb7\x17-\xb4o.Zl\xdf\\\xb4o.\xda\\\xb79m\xbd\x84\x91\xaf\xb8\x9f\xf8q\x11\x8b\x84\xe3\xe99\x14g\xe0\x87j\x8a[_}\x9fZay?>j\xa4\xae6\x94\xf5\xaf\xf8F\u0098\x04\xb4E\x17\xad)iJ.i\x83D\xf1\xf6\x05\xc4\vE\x98I\xe5\x94\xe3շ)\x05?Ke>\xfd:Ӧ\xcc&\x16\x9a\xea8\xc9\b\x1d\xe2\xcd\x06r{\xbb5$\xfdz\x1d\xf6s#\xa6\u007f\xf3\x1aԄR\x9c\x85\x02\x9c\xf9\xc2\xdc9z\rB\x8f>\xc1L\xaf`\xf47C\xaf\x85*\x99\xe9ژp\x12\x84N\x1c\xbe[\xf7\u007fq:T\xca\xc0\xabt\xfb\x91\xa5\xbc\xeeQ\xf1\x19\x96\xdau\xcb^\xa3\xbc\x85+&C:\x826\xa0d\xc1䜑\xd6\x1ey\xe1ϕ\x0f\xe1\xceޗ\xf3\xe1GZ-͛+h\xfa\x152\x13*\xfa\xdc#\xa3\xf4B\xe1\xf4\x1a\x99\xf9\xa2\x96s*c\x86u/\x93@\x97\xebaR\"Džڗ7T\xbc$V;\xfe\u20f1\x94\x9a\x967U\xb2,\x16\x04&֯\xf4+S\xe6A\x9eQ\xb5\x92D\x9c\xe5\n\x95\xb3\xebRB\x1d\xc8\xec:\x92\xabQF\xeaLf\x01O֠\xccU\x97,d\xa5N+O\xd2kJfAs\xbd\xc9r%\xc9\xfbՋ\xbe\x87\x0f<\xadj\x16\xabA\x16}\xe4y\xfc\x16\xeb=Ω\xf2X\xa4\xd8\x1b+:\x9a\x8a\x8d\x89yϭ\xe3\xe8\xd7iL\x00M\xa9ޘ\xa8Θ\x808[\xb3\x91Z\x931\x01{\xc1\xec\xceJ\xc9̏\xe3\x17!aѾ\x15\xbf\x96D\xbdua\xda\xe4hf=\xf4T4gQ\xecg\xbc\x06s\x0e\xca\xec\xe3\xc5I\xea\xd5\xf5\xfa\xc7X\xae\x9b\x92\xf0\f~\x94*\xf7rB\x82\xde\xf1\x13\xf8\xa20\x17\xc54\xeeJ\xeb\xef\x8d\x03\x1dD\x1a\x16+a\xf8&\xf9\xe6\xe8\xb3\x15v\rw\"\xdb\xf7;\xc2^X\x8aI\xcbQ7\xec\xb2\tӮ\xe3(\xfar\xb9\x06\xf8A7\x91p\xf7\x9a\x95\x95eU\x1c\xa1\xb6\b\x97\xfd!o\x8b9F%\xc0*Qٽ\x8ew`\x17\u008e\xc7~\uf448>ހ\xcd\n]\xe7\r\xf4\t\xe6\tu\x84\x87g\xf6}\xf8\xee`\xd6ޣ\f\xfeM\x8c$\x86\xd7,\xbf\u007f\xff\b\xdf:m\xc4\x0e\u007f\xd2\xfe2\xf2\x12%\xfa\xbd{7у\x0e\x8b\x19\xb7X\x90%F\x88\x10\xaeE\x0f\x80\xb5\x89\xf4\xb0\x1b\xda\xe4\aa9\xa6\xdef\xf6\x9fs\xc5\xc2b\x9e\x9e~\xf2\vp\xb2\xc4\xf5\xa7\xdagSV\x950\x16\x89\x9aqa~І\xfe\xbbׯc\nO\x875\u007f?\xc4\xdb '\xeb9is\x16\xf6\xfe\xdat\x14\xbcH\xa2%A}\x1e\x1f\xd5\t\xf4:L\xf2\xbb\\\x8f\x9dKL\xc1\xe9\xbc.A\x81\xb5/\xb6{\u07fb\xbfS\x1e\xcb\xd4\xfd{'\\m\x97o\xe0s\xb7\xf8\xdeF8\xf2\xa9\r_\xdc\xf5 \xfcE\xd77]\xc2\x0f\x19\xea\xde\x1b(\xf3|\xba=\x1d\xc1/]\x98ܣƹ\xf1\xe66\xfd\xab\xb0M\x16|\xd4ķ\xe0\xfcH\xf6\xa0\t\x1a\xe6\x80\aT\xa0\x15'\xbd\xf9J\xac\u007f\x8de8f,\x99ԁ\x12\xb2\xeauUh\x91\xc7\x1d\x1emVx\xc1\xe3\x89\xf5\x979\xa0\xf9`g`\xf2\x8b\x01[mƈp\xaa0\xbda\xb9\x81\\8\\\x8d\x02M\xd2}\xa3\u0096Y\xd9\x17t\xfb\xd19\x8aGƼ\xf5>\xff\xa6GF\xfb\xeb\xb4\x13\x05\xa8\xba\xdcx\x83.b\x871\xfe=\xde\x0f\xb6\x9c\r\xc7 3\xdb\xcb/L*\x87;\x1c:\x9d\xa7+\xbb\x8d\xf2s\xf6ʚ\x91S+\xb3u\x96\xa1\xb5ۺ(Ƃ\x8cFr\u007f\x8de\x06\x93\xf2D(\x9e\xbd\xd2\xee\xe0\xfeb\x83\x91\x04+\xff:\x9a\xa6\xde\xfaW\x90\xb0c\xfa\x97W\x16E[*\xf7\xcf\xfftֺ\xf9`s\xf1\xc1\a\xee\xe4U?\x9f\x8a\xc6W9\xfc\xb1h\x89֊]|\xe9\xe1\x95,\xef\x0e\x15\xb2\xc37\xb2\xc4\x10\x88\xb7\xc7h\xfdw\x0e|.Pd\xae\x16a\x82\x98\x06\xed\xf4\xfa0\xe6\x0f\x15z\a[Yp\xd7\xf0&M\xa0\xf6y\xb2\x80_*iR\\\x98\xbb\xa6#ц\x13\xc1̦\xf6\xed&,\xe4N\x92\xfd'\x16\xee\x84و\x1d\xae2]\x14\xc857Ӝ\xfc\x1aJ\xca\xc3\x1e}\x9b\xe9di?t\xfbF\t\x0eZ\xdaÉO5]\x05\xd7r<\xb2*\xc5_\xb4\xb9\x82R*\xfa\x87\xe2\aN\xa9\xc4\xc1g92\xfc,\xc6\x02\xde\x0fԧ\xa9\xc4\xe8\x98e\x8cb6\xe5\xf8\x8e\x9f\xbe\xaf\xe03\x9e\xfai\xfe@\x1dsN\x1a\x8e=HE]\xeeՃ\xd1;\x8a\xf9G~lT\xe1\xc8o\x0f\xc28)\x8a\xe2\xe8'\x99\x9c}\xe4\x87OHfp\xd2\x17\x1a'k\xc0r\x89\xb2\xa1[\x9b`\x90\xcaK\x02\x1f\x85ot\xedz\x1b\xb4\xdd\xe0#b\x11\xe7\\\xc3g\xed0\xe6\x9fe\x1f&)<\xb4n\x85ۭ6\xce\xe73V+\x90\xdb\xe0[\x8d\xc0%ׄ\xcf\\\xfc\xfbP ]\x9b\xf7k\xa5\x97\xc3&\x83²\xf4:~\xa5\x8a\x8fEE\x96\x91\xeb\x8e\xd7։bDk\xfc\xa2c\x16vbI\xfa0\xff\x8f\x11\xaf\xee\x84\xe0\xf7\xdd\xfe\xcdݔ\xc6V28O9.[\xf1\x1as\xd4n\x02\x173\xa0\x82W#\x9d#-\xd5=\x94\x02Gz\xa9(\xc0j؊\x89\x97H\xe6\xf4%\xffN\xc6\xed~:\x19ڏ\x96\x9a\xceS\x8e@X\x9c&\xb6l\x98\x04\x13\xcb\xf2e\x91\xd2Ʊ\xc4\xcal/Ԏ\x84\xca\xe8z\xb7\x8fr9ao&\xe0\xe65!\x05UQ\xefH\xd4á\x8e\xab\x8d\xea$t\xc21O\xdeAWd/\x93\x98\x86\xb4v|\xa3\xf0:\xbc-\xb2\xda\x1a]\xae\x02/8\xe1u\x15\x12-Fj\x8a&\xdc~\x94\xe4\xd4\xdaK\xfc,\x06U\x85\n\x84\r\xf8$\xd4lγu.\xeb\xe1\x84q\xa91\xcac\xaf\xf3Bx\u0090\xc7\xf1}\fi$_\xbbz;|-\xf2\n\xacT\xf1yD\x9f\xa6\xf2\xa2`)j1ȑ\xff\xe8A\xdbI\xbcы.\xfa\xe8\xff\xba\x81š\xb10w)\x9e\xda\xf3\xa0\xfb\xa0\x00\x80vy\v1xW#\xf4\xf8\x83\xdc\xfa\x13\xc0\x8c\xb0\xfe\xe3\xdf\xfc`\xff\x90\xe4\xb3|\x98uW\xd8\x13i\xfc\x0e\xf8\x84\x95\xc1L\x8c\x861\x00\x0f\x05\x92\x1fa\x11\xfb\x9eЇ\xb3\x1c\xc9\xc3\xdbB\xc2\xf7\x8c\a\xe3˝\xef\x13%\x1d\xde\x16\t~\xb50\xf0}W\xf7*\x8c\x92j\xb7\xb4\xc7\xfe3t\x1b\x89\x87\x02\x84\x91\x88hd\x19M\x8c\xb4\x18\x11u\x02\xa2\x88\xe3ăz\x83 \xe9\x9dB\xa2Q;p\xf2\x91\x15h\xde\xd9\xdba\xa6\xf0\xa5M\xaf\x89,C\x12\xd7\xcf\xc3\x17z//\xf9\x8f\xf8\b/\xff\x99i\xe5ͭ\xbd\x81\xff\xfa\xef\v\b\xf9\xdb\xe7\xf8\xda.}\xfc\xff\x00\x00\x00\xff\xff\xf3|\x1d\xde\xcdX\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec<]o#9r\xef\xfe\x15\x05\xe7a\xee\x00K\xbeE\x1e\x12\xf8m\xd63\x8b\b\xbb\x993֎\xf3\x10\xe4\x81\xea.Ib\x81F\xaf\xa5\xbe\xb2\x15f4\xd7\xde躺\x83\xf6\a?$\xe0\xe1\xd7\xf0#\x8f\xe6\x0f\x85\xb4\xee\xe7\xce\xc7_\xa4u\xfcCU\xd4F\x14\xcdL\xfc\xcdJ\xb5\xaf\va\xe2\xd7+\x00\x9b\xe9\n\xef\xe0\vMQ\x89\f\xf3+\x80\xb0\x1c\x9er\x15\x10>\xfe\xe0!d\a,\x85\xc7\x05@W\xa8>>l\x9e\xff\xf1\xb1\xf7\x19 G\x9b\x19Y9&\x8aG\f\xa4\x05\x01ϼ,0\x81\xfc\xe0\x0e\u0081\xc1ʠE\xe5,\xb8\x03B&*W\x1b\x04\xbd\x83\x9f\xeb-\x1a\x85\x0em\x03\x1a +j\xebЀu\xc2!\b\a\x02*-\x95\x03\xa9\xc0\xc9\x12\xe1\x0f\x1f\x1f6\xa0\xb7\u007f\xc1\xccY\x10*\aa\xadΤp\x98\xc3Q\x17u\x89~\xec\x1f\xd7\r\xd4\xca\xe8\n\x8d\x93\x91ξu\xa4\xaa\xf3u\xb0\xbc\x0fD\x01\xdf\vr\x12'\xf4\xcb\bT\xc4<\x10\x8d\xd6\xe3\x0eҶ\xcbe\t\xe9\x01\x06\xea$T@~\r\x8fh\b\f\u0603\xae\x8b\x9c\xa4\xf0\x88\x86\b\x96齒\xff\xdd\xc0\xb6\xe04OZ\b\x87A\x00\xda&\x95C\xa3D\x01GQ\xd4x\xc3$)\xc5\t\f\xd2,P\xab\x0e<\xeeb\xd7\xf0\xaf\xda H\xb5\xd3wpp\xae\xb2w\xb7\xb7{\xe9\xe2n\xcatY\xd6J\xba\xd3-o\f\xb9\xad\x9d6\xf66\xc7#\x16\xb7V\xeeW\xc2d\a\xe90#FފJ\xae\x18u\xc5;j]\xe6\xff\x10\x05\xc0~\xe8\xe1\xeaN$\x8c\xd6\x19\xa9\xf6\x9d\x1fX\xeag8@\x1b\xc0˗\x1f\xeaW\xd1\x12\x9a>\x11u~\xfd\xfc\xf8ԕ=i\x87\xd4g\xbaw\x04\xb2e\x01\x11L\xaa\x1d\x1a\xcfĝ\xd1%\xc3D\x95{\xe9c\xd1-$\xaa!\xf9m\xbd-\xa5#\xbe\xffW\x8d\x96\x84\\\xaf\xe1\x9eU\fl\x11\xea*'\xc9\\\xc3F\xc1\xbd(\xb1\xb8\x17\x16\xbf9\x03\x88\xd2vE\x84McAW;\x0e;{\xaau~\x88\xbal\x82_^!\xc3٫È9q\xa2\xa7\x1a\xb5B\xd0\x06J\xb2\a\xe7]\x87\x16\xacmS\xcb\xde\n\xd2Nڋ\xa8\xa9\v\xb4a\xaa\x9cun\xab\x03n&A7\x1c\xf1\xbeD!\xb6X\x80\xc5\x023\xa7\xcd89\x96\x98\xec[\x8a^\x9b\xa0∆ku7-\xb5]\xd8\fH \xb5\xfdz\x90\xd9\xc1\x9by\x92 \x86\x03\xb9F˻\\TUq\x9aZ$,q>L2\xb7\xd1۶\xb0\xe5\x87\xf0\xc66\u007f\xdb\x12tc\xdb\x16\xb4d\x9f\xb2\x8d8\x80ӳ\xcb\xfe\xffIب\xf6\xdf \xb4\x9b\xb3\xa1\xef+\xb4DRI\xee\xfcf\aXV\xeet\x03\xd2ůK\x10\xc9Yi\xe7\xff;f\xcc\xe5\x12\xbf\x19\x8e|W\x89\x9f\xe5\xca\x12D\xe2J3\xfd\xdf!S\xd8X<\x06[\x91̐_\xba\xa3n@\xee\x1a\x86\xe47\xb0\x93\x85C3\xe0\xcco\xda/\xefA\x8c\x14{G\xad\x14.;|\xfeJ\x9e\x97m\x13N\x89t\x19\x0e\xf6\xfek\xf4\xe7\xfb\x86y\x01.p\x80*\r\x96>\xf0}bj\xb6_أ\xfa\xf8\xe5\x13\xe6s\xe4\x814\xc9;[\xc8\xc7\x01\xb2ݩ\x83S\x9e\xba\x8c\xe0\xfa4\xf1\x8dOi܀\x80\x170!8\xb6N'\x1ep\xe2+\xea\xa2\xe5\xc5A\xba\"\x89-\xd2\xfe\r\xcbl\xd8\xd6I\x1a2c?X\xcf\"\xda\x05\aY%.\x94\xcc\x1cX\xe4\xdd\x12S_Ϣ\x90y3\x91\x97\xfb\x8d\x9a\xf6\x86\xfb\xed\x8bv\x1bu\x03\x9f\xbfJ\x1b\xb2\x8f\x9f4\xda/\xda\xf1\x97oBN\x8f\xf8\x1b\x88\xe9\a\xf2\xf6R^m\x13\x1d\xba9\xb4\x04\xe1\xf6m\xe3#\xbc\x86=\xd2\xc2FQ\xdc\x12\xe8\xc1\x19Q?ݼ}跲\xb6\x9c$SZ\xad\xd8T\xae\xc7f\xf2\xc4N\x04\xa9M\x8f#\xe7\xa85\x93\xfa\t\x13\xc1>\x91%\xf1\xe3}\x8e\xb7\x10\x19\xe6\x90\xd7LL\xceL\n\x87{\x99A\x89f?g8\xba\xad\"\xfd\x9e\x86B\xa2\xd6\xf5\xedB\tK3\xed\xb1\x05՝/#\xb3\xa2\x9d\x9b\xd0+2{\xb1\xebDBr\xba\xeb\xf2\x8a\xd8IJ\xff\xb1H]\x91\xe7|\x96$\x8a\x87\v4\xfe\x05\xbc8\xb7\xfd\x1e1o!K\xc1I\xc6\xff!3\xc7\x02\xfd\xbfP\ti\x12\xf6\xf0G>\x1a*\xb076d\xb1\xba\xd3\xd0\f\xd2\x02\xf1\xf7(\x8a\xf3T\xf7\xc8\xe24\xe9\x16,\xbc!\u05fb3\x8f\xe5\x06^\x0f\xdaz\x9b\xba\x938\x9aR\xed7i\xe1\xfa\x05O\xd77gz\xe0z\xa3\xae\xbd\x81\xbfX\xdd4ނV\xc5\t\xaey\xec\xf5oq\x82\x12%1\xa9\x1b\x1f\xc1\xa5\xba\xca\x14KFO\x80\x066\xe7N\xe4\xe6\xcea\x9d$\x87\x95\xb6.\x19\x95\am\x9d\xcf,\xf6\xdc\xd2K\xb2X\xe0e(d\xaf@\xec\xfcɟ6\xf1L\x87\xd4\xde \xe1J\\\xb3\xf3\x1a\x96\xd8\xd8d\xc4)\x19[\xbaCJD\xbaЕ\xff\xfc\xb5\x93\xbd\xa4\xcdO\u007f/\tߥx\x01\xefٲ\x14Ó\xc1$\x14\xef\xfdȸM\x02 \x1f\x1a\x98}\xcd[=݃\f\x82\xf4{0ӥT\x1b\x9e\x00~xw\xb3\xde(I|\x8b\xe3~\x1fǶDo>\xf0\xeeM\xf5\x884g\xee\r\xf68w\x9e\xe7&G1\x11\xa4Ү\x9bN \xb8\x95\xce?X\xd8Ic]\x17\xd1T\xa1\xa8\x17v\u007f\xdb.\x8d\x9c\xd4gc\xde\x148\xfdُ\xec$\xb2\x0e\xfa5\x9e\xafN\x1ef\x8e5>\x14B\x90;\x90\x0ePe\xbaV\x9c~\xa1\xad\xceSx\x16x\x05\x9dL\xb24\x05A\rU]\xa6\x11`\xc5R'\xd5l\x9e\xa6\xdb\xfd'!\x8bo\xc16'K\xd4\xf5\xac\xe1l[\x8fmO~d\uf83c\x14_eY\x97 J\"}jس\xf3\xc51=\x8eë\x90\x8e-\a\xc1e3\xe24m\xaa\xaa@\x97\xba#\xb7\xb8ӆ\xf7\xb3\x9596\x869H\x81V `'dQ\x9bD\ry\x11m/\x895\x82\xb2x\xbf \"m\xf2\x15\x93\"!\x11\x9b\xe8,\xcek\xebʤ\xbb\x8a\x0f\x06\xd3ܳ\xa5\xa4tt\xcf*#I\x96\xf4{{hAĄ:}w\xd1\xce\xdaw\x17m\xa1}w\xd1&\xdbw\x17m\xb9}w\xd1B\xfb\xee\xa2\xc5\xf6\xddE\xfb\xee\xa2\xcdu\x9b\xd3\xd6K\x18\xf9\x8a\xfb\x89\x1f\x17\xb1H8\x9e\x9eCq\x06~\xa8\xa6\xb8\xf7\xd5\xf7\xa9\x15\x96\x9b\xf1Q#u\xb5\xa1\xac\u007f\xc57\x12\xc6$\xa0-\xbahMISrI\x1b$\x8a\xb7/ ^(\xc2L*\xa7\x1c\xaf\xbeM)\xf8Y*\xf3\xe9י6e6\xb1\xd0T\xc7IF\xe8\x10o6\x90\xdbۭ!\xe9\xd7밟\x1b1\xfd\x9bנ&\x94\xe2,\x14\xe0\xcc\x17\xe6\xce\xd1k\x10z\xf4\tfz\x05\xa3\xbf\x1bz-T\xc9L\xd7Ƅ\x93 t\xe2\xf8ú\xff\x8bӡR\x06^\xa5;\x8c,\xe5\xf5\x80\x8aϰԾ[\xf6\x1a\xe5-\\1\x19\xd2\x11\xb4\x01%\v&猴\xf6\xc8\v\u007f\xae|\bw\xf1\xbe\x9c\x0f?\xd2ji\xde\\Aӯ\x90\x99Pї\x1e\x19\xa5\x17\n\xa7\xd7\xc8\xcc\x17\xb5\\R\x193\xac{\x99\x04\xba\\\x0f\x93\x129.Ծ\xbc\xa1\xe2%\xb1\xda\xf17\x1f\x8c\xa5Դ\xbc\xa9\x92e\xb1 0\xb1~\xa5_\x992\x0f\U00082a95$\xe2,W\xa8\\\\\x97\x12\xea@fב\\\x8d2Rg2\vx\xb2\x06e\xae\xbad!+u^y\x92^S2\v\x9a\xebM\x96+Iޯ^\xf4=|\xe0iU\xb3X\r\xb2\xe8#\xcf\xe3\xb7X\xefqI\x95\xc7\"\xc5\xdeX\xd1\xd1TlL\xcc{i\x1dG\xbfNc\x02hJ\xf5\xc6Du\xc6\x04\xc4ٚ\x8dԚ\x8c\t\xd8\vfwVJf~\x1c\xbf\b\t\x8b\xf6\xad\xf8kI\xd4[\x17\xa6M\x8ef\xd6COEs\x16\xc5~\xc6k0\xe7\xa0\xcc>^\x9c\xa4^]\xaf\u007f\x8c\xe5\xba)\t\xcf\xe0g\xa9r/'$\xe8\x1d?\x81/\nsQL㮴\xfe\xde8\xd0A\xa4a\xb1\x12\x86o\x92oO>[a\xd7\xf0Yd\x87~G8\bK1i9\xea\x86]7a\xdam\x1cE_\xae\xd7\x00?\xe9&\x12\xee^\xb3\xb2\xb2\xac\x8a\x13\xd4\x16\xe1\xba?\xe4m1Ǩ\x04X%*{\xd0\xf1\x0e\xecB\xd8\xf1\xd8\xef=\x12\xd1\xc7\x1b\xb0Y\xa1뼁>\xc1<\xa1N\xf0\xf0̾\x0f\xdf\x1d\xcc\xda{\x94\xc1\xbf\x89\x91\xc4\xf0\x9a\xe5\x8f\xef\x1f\xe1[\xa7\x8d\xd8\xe3/\xda_F^\xa2D\xbfw\xef&z\xd0a1\xe3\x16\v\xb2\xc4\b\x11µ\xe8\x01\xb06\x91\x1evC\x9b\xfc ,\xc7\xd4\xdb\xcc\xfes\xaeXX\xcc\xd3\xd3/~\x01N\x96\xb8\xfeT\xfblʪ\x12\xc6\"Q3.\xcc\x0f\xda\xd2\u007f\x0f\xfauL\xe1\xe9\xb0\xe6\x1f\x87x\x1b\xe4d='m.\xc2\xde_\x9b\x8e\x82\x17I\xb4$\xa8\xcf\xe3\xa3:\x81^\x87I~\x97\xeb\xb1s\x89)8\x9d\xd7%(\xb0\xf6\xc5v\xef{\xf7w\xcac\x99\xba\u007f\uf12b\xed\xf2\r|\xee\x16\xdf\xdb\bG>\xb5ዻ\x1e\x84\xbf\xe8\xfa\xa6K\xf8!C\xdd{\x03e\x9eO\xf7\xe7#\xf8\xa5\v\x93{\xd487\xdeܦ\u007f\x15\xb6ɂ\x8f\x9a\xf8\x16\x9c\x1f\xc9\x1e4A\xc3\x1c\xf0\x88\n\xb4\xe2\xa47_\x89\xf5\xaf\xb1\fnj%\x93:PBV\xbd\xae\n-\xf2\xb8ã\xcd\n/x<\xb1\xfe2G4\x1f\xec\fL~1`\xa7\xcd\x18\x11\xce\x15\xa67,w\x90\v\x87\xabQ\xa0I\xbaoT\xd82+\xfb\x82n?:G\xf1Ș\xb7\xde\xe7\xdf\xe3fjd\xb4\xbfN;Q\x80\xaa˭7\xe8\"v\x18\xe3\xdf\xe3f\xb0\xe5l8\x06\x99\xd9^~aR9\xdc\xe3\xd0\xe9<_\xd9}\x94\x9f\x8bW\u058c\x9cZ\x99\xad\xb3\f\xad\xdd\xd5E1\x16d4\x92\xfb\xfe\xcb\xe4\x03\xbeŇ\x0f\xb8\x93W\x81|:\x18_\xa7\xf0ǃ%Z+\xf6\xf1ŃW\xb2@{TȎ\xcf\xc8jB@\xda\x1e'\xf5\xef\xfb\xfb\x9c\x98\xc8\\-\xc2\x041\x1d\xd8\xe9\xf5a\xcc/(\xf4\x1ev\xb2\xe0\xae\xe1m\x96`\x9a/\xa4\xc9\xd7J\x9a\x14S\xfe\xb9\xe9H\xb4\xe1\x84(3\xa2}\xc3\b\v\xb9\x97d\a\x89I{a\xb6b\x8f\xabL\x17\x05r\xed\xc99^\xdfr\xb3zأo\x14\x9d-\xed\xa7n\xdf(\xb6A[y8\xf1ɢ\x9b\xe0b\x8dG\x18\xa5\xf8\x8b67PJE\xff\x90\x1fͩ\x858\xf8\"\x83\xce\xcfC,\xe0\xfd@}\x9a\x8a\x84\x8ey\xc2(fS\x0e\xe0\xf8)\xf4\n\xbe\u0e7f\xe2\x0f\x961\xe7\xe4\xd9\xd8\xc3L\xd4e\xa3\x1e\x8c\xdeS\xec;\xf2c\xa3\x12F~{\x10\xc6IQ\x14'?\xc9\xe4\xec#?|B2\a\x93>\xc18Y\x03\x96K\x94\r\xdd\xda@[*/\t|$\xbcյ\xebm\xd0v\x83\x8f\x88E\x9cs\r_\xb4Ø\x87\x95}\x98\xa4\xd2к\x15\xeev\xda8\x1fׯV w\xc1\xc7\x18\x81K&\x9a\xcf\x1e\xfc;I ]\x9b\xffj\xa5\x97\xc3\a\x83²\xf4:~\xad\x89\x8f\aE\x96\x91\v\x8b\xb7։bDk\xfc\xa6\xe3\x06v\xe6H\xfa0\xff\xb7\x11\xef\xe6\x8c\xe0\x9bn\xff\xe6\x8eFc3\x18\x9c\xa7\x1c\x97ox\x8d9j?\x80\x0f\xf5Q\xc1\xab\x91Α\x96\xea\x1e\u0380#\xbdT\x14`5\xec\xc4ċ\x1cs\xfa\x92\u007f'\x8b\xb6\x99N\n\xf6\xa3\x86\xa6\xf3\x94A\f\x8b\xd3Ė-\x93`bY\xbe\x86t\x8a\xaf\xe1\xbc\x1f\xbe\x9ax\x03V\xaa\xf8L\xa0O\xd7xQ\xb0\xe4\xbd\x1b\xe4\bx\xf4\xc0\xe9\xcc\xef\xeey\xd9}\xf4\xff\xba\x0e\xf6\xb1\xb10\x9fS<\xb5\xe7A\xf7\xc1A8\xed\xf2\x16b\xf0\xaeF\xe8\xf1\a\xb9\xf3'a\x19a\xfdǿ\xf9\x01\xf71\xc9g\xf90뮰'\xd2\xf8\x1d\xf0\t+\x83\x99\x18u\xe7\x01\x1e\n$?\xc2\"\xf6=\xa1\x0f\x179\x92Ƿ\x85F\xef\x19\x17\xc5\x17,\xdf'Z8\xbe-\"\xfaf\xe1\xd0\xfb\xae\xeeU\x18%\xd5~i\x8f\xfd{\xe86\x12\x0f\x05\b#\x11\xd1\xc82\x9a\x18i1\"\xea\x04D\x11lj\x87\xe5\x06A\xd2;\x85D\xa3v\xe0\xec#+м\xb3\xb7\xc3L\xe1K\x9bf\x12Y\x86$\xae_\x86/\xd5^_\xf3\x1f\xf11Z\xfe3\xd3ʛ[{\a\xff\xf1\x9fW\x10\xf2\x98\xcf\xf1\xd5Y\xfa\xf8\u007f\x01\x00\x00\xff\xff\x01\xf3\x19\x8b\xd5W\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5饸p\x9e36\xfc\xfb?\x936yS\x86l<\x8a\x1f\xfa\x9f\xdaޥ\x00R\xbe\x97ş\x9c\xaa\x9a\xf4\xad\x10\xfe\xf6\x8fI\xba\x18\xc5s\xf9\xc0E\x93\xff\r\x00\x00\xff\xff\x04\x0e\x95\xf5\xa5\x1c\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V=s\xe36\x10\xed\xf9+v.\xc55\x11u7)\x92Q\x97\xf8\xae\xf0$\xf1x\xec\x1b7\x99\x14\x10\xb0\x127&\x01dw!\xc7\xf9\xf5\x19\x00\xa4%Q\xf4\xc5)\u008e\xfb\x85\x87\xf7v\x97lV\xabUc\"= \v\x05\xbf\x01\x13\t\xffR\xf4\xf9M\xda\xc7\x1f\xa4\xa5\xb0>|l\x1eɻ\r\\%\xd10ܡ\x84\xc4\x16?\xe1\x8e<)\x05\xdf\f\xa8\xc6\x195\x9b\x06\xc0x\x1f\xd4d\xb3\xe4W\x00\x1b\xbcr\xe8{\xe4\xd5\x1e}\xfb\x98\xb6\xb8M\xd4;\xe4R|:\xfa\xf0\xa1\xfd\xbe\xfd\xd0\x00Xƒ\xfe\x85\x06\x145C܀O}\xdf\x00x3\xe0\x06\x1c\xf6\xa8\xb85\xf61E\xc6?\x13\x8aJ{\xc0\x1e9\xb4\x14\x1a\x89h\xf3\xc1{\x0e)n\xe0\xe8\xa8\xf9#\xa8z\xa1O\xa5\xd4O\xa5\xd4]-U\xbc=\x89\xfe\xfcZ\xc4/4F\xc5>\xb1\xe9\x97\x01\x95\x00!\xbfO\xbd\xe1Ő\x06@l\x88\xb8\x81\x9b\f+\x1a\x8b\xae\x01\x18\xf9(0W\xe3\x8d\x0f\x1fk9\xdb\xe1`*~\x80\x10\xd1\xffx{\xfd\xf0\xdd\xfd\x99\x19\xc0\xa1X\xa6\xa8\x85\xd5\x05\xfc@\x02\x06F\x14\xa0a\x04\a\xc1#\x04\x86!0BE*\xedK\xd1\xc8!\"+M\xfc\xd5\xe7\xa4uN\xac3\b\xef3\xca\x1a\x05.\xf7\f\nh\x87\xd3Mэ\x17\x83\xb0\x03\xedH\x8012\n\xfa\xdaEg\x85!\a\x19\x0fa\xfb\aZm\xe1\x1e9\x97\x01\xe9B\xea]n\xb5\x03\xb2\x02\xa3\r{O\u007f\xbfԖ|\xcf|hot\x12\xf9\xf8\x90Wdoz8\x98>\xe1\xb7`\xbc\x83\xc1<\x03c>\x05\x92?\xa9WB\xa4\x85_3M\xe4wa\x03\x9dj\x94\xcdz\xbd'\x9dFƆaH\x9e\xf4y]\xba\x9f\xb6I\x03\xcb\xda\xe1\x01\xfb\xb5\xd0~e\xd8v\xa4h51\xaeM\xa4U\x81\xee\xcbش\x83\xfb\x86\xc7!\x93\xf7gX\xf597\x8c(\x93ߟ8J7\u007fE\x81\xdc\xcbU\xf6\x9aZoq$:\x9b2;w\x9f\xef\xbf\xc0tt\x11c\xce~\xe1\xfd\x98(G\t2a\xe4w\xc8U\xc4\x1d\x87\xa1\xd4D\xefb \xaf\xe5\xc5\xf6\x84~N\xbf\xa4\xed@*SKf\xadZ\xb8*{\x04\xb6\b):\xa3\xe8Z\xb8\xf6pe\x06쯌\xe0\xff.@fZV\x99طIp\xba\x02\xe7\xc1\x95\xb5\x13Ǵ\xa3^\xd1kah\xef#ڬ`&1gӎl\x19\x0f\xd8\x05\x86\xa7\x8el7\r\xed\x8cݗ\x01o\xcf\x1c\xcb\x03\x9d\x9fZ&/\xa5\xb9\xe7\xd5\xcbCю\x18g]\xb8:)\xf6&^\xd4h\x92\xff\xc8Lə\xb8\xb1\x89\x19\xbd\x8e\x95ʶXJz+\x17\xc8\x1c\xf8\xc2:\x03\xf5\xb9\x04\x95\xef\x9c!/`\xfc\xf3\x98\b\xda\x19\x85'\xe4<\x066\xa4\xbcgЁK\x17\xfc\x8d\xb4tX\xc5\xca\xc2F\x0e\x16Eڋ8R\x1c\x160}E\x9d\xfc\xe4o\xa8\xd9\xf6\xb8\x01儯(k\x98\xcd\xf3\xcc\x17;#\v\xadpF\xc1m\x8eY\xd2\x00\xebV\xc7\u007f\x17\xa1\xd0\xed\xd3py\xd2\nn\xf0i\xc1z\xedo9\xec\x19e\xde\xf2\xd9y[\xd9+\xdf\xd47\xb2\xb4ؔ\x17F\xc9\xfbΝ\xb0(\x1a\xd8\xec'^\x8f-l\xacŨ\xe8n\xe6\u007f\x1d\xefޝ\xfd>\x94W\x1b\xbc\xa3\xfa\xd3\x04\xbf\xfd\xdeԪ\xe8\x1e\xa6\xbf\x81l\xfc'\x00\x00\xff\xff\x8c\xdb\x1fܮ\t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdc6\f\xbd\xfbW\x10\xe9!-\x10{\x12\xf4\xd0bn\xed&\x87\xa0i\x10\xec\xa6{)z\xd0\xc8\x1c[]YREj6ۢ\xff\xbd\xa0dχdz\xbb9T7K\x14\xf5D>>\xd1U]ו\n\xe6\x16#\x19\xef֠\x82\xc1/\x8cN\xbe\xa8\xb9\xfb\x91\x1a\xe3W\xbb7՝q\xed\x1a\xae\x12\xb1\x1f\xae\x91|\x8a\x1a\xdf\xe2\xd68\xc3ƻj@V\xadb\xb5\xae\x00\x94s\x9e\x95L\x93|\x02h\xef8zk1\xd6\x1d\xba\xe6.mp\x93\x8cm1f\xe7\xd3ѻ\xd7\xcd\x0f\xcd\xeb\n@G\xcc\xdb?\x9b\x01\x89\xd5\x10\xd6\xe0\x92\xb5\x15\x80S\x03\xae\xa1\xf5\xf7\xcez\xd5F\xfc+!15;\xb4\x18}c|E\x01\xb5\x1c\xdaE\x9f\xc2\x1a\x0e\ve\xef\b\xa8\\\xe6\xed\xe8溸\xc9+\xd6\x10\xff\xb2\xb4\xfa\xc1\x8c\x16\xc1\xa6\xa8\xec9\x88\xbcH\xc6uɪx\xb6\\\x01\x90\xf6\x01\xd7\xf0Q`\x04\xa5\xb1\xad\x00ƻgX\xf5x\xbbݛ\xe2J\xf78\xa8\x82\x17\xc0\at?}z\u007f\xfb\xfd\xcd\xc94@\x8b\xa4\xa3\t\x9c#8\xc3\f\x86@\xc1\x88\x00\xd8\xefA\x81r\xa0\"\x9b\xad\xd2\f\xdb\xe8\a\xd8(}\x97\xc2\xde+\x80\xdf\xfc\x89\x9a\x81\xd8G\xd5\xe1+\xa0\xa4{P⯘\x82\xf5\x1dl\x8d\xc5f\xbf)D\x1f0\xb2\x99\xa2\\\xc6\x11\xb9\x8efg\xc0_\xca݊\x15\xb4\xc2*$\xe0\x1e\xa7\xf8`;\x86\x03\xfc\x16\xb87\x04\x11CDBWxv\xe2\x18\xc4H\xb9\xf1\x06\r\xdc`\x147@\xbdO\xb6\x152\xee02DԾs\xe6\xef\xbdo\x92\bɡV\xf1D\x87\xc30\x8e1:ea\xa7l\xc2W\xa0\\\v\x83z\x80\x889N\xc9\x1d\xf9\xcb&\xd4\xc0\xaf>\"\x18\xb7\xf5k\xe8\x99\x03\xadW\xab\xce\xf0TT\xda\x0fCr\x86\x1fV\xb9>\xcc&\xb1\x8f\xb4jq\x87vE\xa6\xabUԽaԜ\"\xaeT0u\x86\xeera5C\xfbM\x1cː^\x9e`\xe5\a\xa1\x19q4\xae;ZȜ\u007f$\x03\xc2\xfaB\x98\xb2\xb5\xdc\xe2\x10h\x99\x92\xe8\\\xbf\xbb\xf9\f\xd3\xd19\x19\xf3\xe8\x17\xe6\xec7\xd2!\x05\x120\xe3\xb6\x18K\x123\xf3\xc4'\xba6x\xe38\u007fhk\xd0\xcd\xc3Oi3\x18\xa6\x89̒\xab\x06\xae\xb2\xd2\xc0\x06!\x85V1\xb6\r\xbcwp\xa5\x06\xb4W\x8a\xf0\u007fO\x80D\x9aj\t\xec\xf3Rp,\x92s\xe3\x12\xb5\xa3\x85I\xc9.\xe4kV\xea7\x01\xb5dO\x02(;\xcd\xd6\xe8\\\x1a\xb0\xf5\x11ԡ\xf2\xc7\x006'\x9e\x97+7\x83S\xb1C\x9e\xcfΰ|\xceFr\xfc}\xafN\x85\xe6[l\xbaF\xb4\x82F E=\xbek\xce<^\xc6\x00\x8b\xec]D2\x91X\xc2 q\x15)\x10\x91:\xc6t~\xb4\ftiX>\xa0\x86\x9f3\xe6\x0f\xbe{t\xfd\xca;\x16\xba?jt\xebm\x1a\xf0Ʃ@\xbd\u007f\xc2\xf6=\xe3\xf0<\xcb\xe9A\xde?R\xe7\x86\xd7(R\x8e\x97/1\x1a\\#%{\xe1\xb8\v\xb4\x9eF~\xbe\x9eΑ<\x80S\x8edK\xd1t\x04i\v\xa2CF:\xc8˽\xe1~\xd1#\xc0}ot\x9f7\xe6\x04\x8br\x11ym\xb2\x0e|=|\xa9\v\x13q\x81du&\xdf´\x80?\x9b\xbeP͗\x0e\xa8\xc7\n{\x96\"\xb0\xe2D_\xa1\t\xd9~\n\xb5N1\xa2\xe3\xd1K~#\xe7\x1b\x9e+\nS%\xfdv\xfd\xe1\tex{\xb0\xcc]\xa02\xae\xa0\t\x11k2\x9d\xbc\xec\xb2&ڐk\xf6<\x18e\x9cv\x1a\xa7\x81Z\xcc(~\t&f\x05|\x02\u2efda\x110t\xe5q\x9a\xf7R\xd9!R~\xf8\xb5\x9a\xb7\x1c26\b-Zdla\xf3P\x94\xf8\x81\x18\x87s\xdc[\x1f\a\xc5k\x90G\xabf\xb3@#\xe9w\xd5\xc6\xe2\x1a8\xa6K,[\xbcx\xe8\x15-\x94\xe1ɝ?\x89\xcd\x121\xf6\xc5\xf8(3\xe0\xa2^\xd6\xf0\x11\xef\x17f?E\xaf\x91\b\xcf\xcb\xe8\xe2M\x16\x8b\xe0l\x92\xa4\xb3h\x8f\xa246\xac\xc73i\xb3\xef\x94&\xc4c)\xc1?\xffV\x87\xaaRZc`l?\xce\u007f\x14^\xbc8\xe9\xfc\xf3\xa7\xf6\xae5\xe5\x1f\a~\xff\xa3*\ac{;5\xf42\xf9_\x00\x00\x00\xff\xff\xcbT\xc3P]\r\x00\x00"), diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 184963305..15c9eddfc 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -311,20 +311,15 @@ type BackupStatus struct { // +nullable Progress *BackupProgress `json:"progress,omitempty"` - // CsiVolumeSnapshotsAttempted is the total number of attempted + // CSIVolumeSnapshotsAttempted is the total number of attempted // CSI VolumeSnapshots for this backup. // +optional - CsiVolumeSnapshotsAttempted int `json:"csiVolumeSnapshotsAttempted,omitempty"` + CSIVolumeSnapshotsAttempted int `json:"csiVolumeSnapshotsAttempted,omitempty"` - // CsiVolumeSnapshotsCompleted is the total number of successfully + // CSIVolumeSnapshotsCompleted is the total number of successfully // completed CSI VolumeSnapshots for this backup. // +optional - CsiVolumeSnapshotsCompleted int `json:"csiVolumeSnapshotsCompleted,omitempty"` - - // CsiVolumeSnapshotsStorageTotal is the total storage size of created - // snapshots for this backup. - // +optional - CsiVolumeSnapshotsStorageTotal int64 `json:"csiVolumeSnapshotsStorageTotal,omitempty"` + CSIVolumeSnapshotsCompleted int `json:"csiVolumeSnapshotsCompleted,omitempty"` } // BackupProgress stores information about the progress of a Backup's execution. diff --git a/pkg/backup/request.go b/pkg/backup/request.go index 1ca4c2c40..38cd49917 100644 --- a/pkg/backup/request.go +++ b/pkg/backup/request.go @@ -50,7 +50,7 @@ type Request struct { VolumeSnapshots []*volume.Snapshot PodVolumeBackups []*velerov1api.PodVolumeBackup BackedUpItems map[itemKey]struct{} - CsiSnapshots []*snapshotv1api.VolumeSnapshot + CSISnapshots []*snapshotv1api.VolumeSnapshot } // BackupResourceList returns the list of backed up resources grouped by the API diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index f20e9f3c8..98982cec8 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -636,16 +636,10 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error { } } - backup.Status.CsiVolumeSnapshotsAttempted = len(backup.CsiSnapshots) - for _, vs := range backup.CsiSnapshots { + backup.Status.CSIVolumeSnapshotsAttempted = len(backup.CSISnapshots) + for _, vs := range backup.CSISnapshots { if *vs.Status.ReadyToUse { - backup.Status.CsiVolumeSnapshotsCompleted++ - storageSize, ret := vs.Status.RestoreSize.AsInt64() - if !ret { - backupLog.WithError(fmt.Errorf("fail to convert CSI snapshot size: %v to int64", backup.Status.CsiVolumeSnapshotsStorageTotal)) - storageSize = 0 - } - backup.Status.CsiVolumeSnapshotsStorageTotal += storageSize + backup.Status.CSIVolumeSnapshotsCompleted++ } } @@ -709,10 +703,9 @@ func recordBackupMetrics(log logrus.FieldLogger, backup *velerov1api.Backup, bac serverMetrics.RegisterVolumeSnapshotFailures(backupScheduleName, backup.Status.VolumeSnapshotsAttempted-backup.Status.VolumeSnapshotsCompleted) if features.IsEnabled(velerov1api.CSIFeatureFlag) { - serverMetrics.RegisterCsiSnapshotAttempts(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsAttempted) - serverMetrics.RegisterCsiSnapshotSuccesses(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsCompleted) - serverMetrics.RegisterCsiSnapshotFailures(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsAttempted-backup.Status.CsiVolumeSnapshotsCompleted) - serverMetrics.RegisterCsiStorageSizeAdd(backupScheduleName, backup.Name, backup.Status.CsiVolumeSnapshotsStorageTotal) + serverMetrics.RegisterCSISnapshotAttempts(backupScheduleName, backup.Name, backup.Status.CSIVolumeSnapshotsAttempted) + serverMetrics.RegisterCSISnapshotSuccesses(backupScheduleName, backup.Name, backup.Status.CSIVolumeSnapshotsCompleted) + serverMetrics.RegisterCSISnapshotFailures(backupScheduleName, backup.Name, backup.Status.CSIVolumeSnapshotsAttempted-backup.Status.CSIVolumeSnapshotsCompleted) } if backup.Status.Progress != nil { diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index d4c58553f..90707be70 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -28,7 +28,6 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" @@ -40,7 +39,6 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" pkgbackup "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/discovery" - "github.com/vmware-tanzu/velero/pkg/features" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" @@ -409,25 +407,6 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR c.metrics.RegisterBackupDeletionFailed(backupScheduleName) } - if features.IsEnabled(velerov1api.CSIFeatureFlag) { - vss, err := backupStore.GetCSIVolumeSnapshots(backup.Name) - if err != nil { - errs = append(errs, err.Error()) - } - - var restoreSizeTotal resource.Quantity - for _, vs := range vss { - restoreSizeTotal.Add(*vs.Status.RestoreSize) - } - - storageSize, ret := restoreSizeTotal.AsInt64() - if !ret { - log.WithError(fmt.Errorf("fail to convert CSI snapshot size: %v to int64", backup.Status.CsiVolumeSnapshotsStorageTotal)) - storageSize = 0 - } - c.metrics.RegisterCsiStorageSizeSub(backupScheduleName, backup.Name, storageSize) - } - // Update status to processed and record errors req, err = c.patchDeleteBackupRequest(req, func(r *velerov1api.DeleteBackupRequest) { r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index c2d85a105..2e593a046 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -57,7 +57,6 @@ const ( csiSnapshotAttemptTotal = "csi_snapshot_attempt_total" csiSnapshotSuccessTotal = "csi_snapshot_success_total" csiSnapshotFailureTotal = "csi_snapshot_failure_total" - csiSnapshotStorageTotal = "csi_snapshot_storage_total" // Restic metrics podVolumeBackupEnqueueTotal = "pod_volume_backup_enqueue_count" @@ -294,14 +293,6 @@ func NewServerMetrics() *ServerMetrics { }, []string{scheduleLabel, backupNameLabel}, ), - csiSnapshotStorageTotal: prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: metricNamespace, - Name: csiSnapshotStorageTotal, - Help: "Total size of CSI volume snapshots storage size", - }, - []string{scheduleLabel, backupNameLabel}, - ), }, } } @@ -628,37 +619,23 @@ func (m *ServerMetrics) RegisterVolumeSnapshotFailures(backupSchedule string, vo } } -// RegisterCsiSnapshotAttempts records an attempt to snapshot a volume by CSI plugin. -func (m *ServerMetrics) RegisterCsiSnapshotAttempts(backupSchedule, backupName string, csiSnapshotsAttempted int) { +// RegisterCSISnapshotAttempts records an attempt to snapshot a volume by CSI plugin. +func (m *ServerMetrics) RegisterCSISnapshotAttempts(backupSchedule, backupName string, csiSnapshotsAttempted int) { if c, ok := m.metrics[csiSnapshotAttemptTotal].(*prometheus.CounterVec); ok { c.WithLabelValues(backupSchedule, backupName).Add(float64(csiSnapshotsAttempted)) } } -// RegisterCsiSnapshotSuccesses records a completed volume snapshot by CSI plugin. -func (m *ServerMetrics) RegisterCsiSnapshotSuccesses(backupSchedule, backupName string, csiSnapshotCompleted int) { +// RegisterCSISnapshotSuccesses records a completed volume snapshot by CSI plugin. +func (m *ServerMetrics) RegisterCSISnapshotSuccesses(backupSchedule, backupName string, csiSnapshotCompleted int) { if c, ok := m.metrics[csiSnapshotSuccessTotal].(*prometheus.CounterVec); ok { c.WithLabelValues(backupSchedule, backupName).Add(float64(csiSnapshotCompleted)) } } -// RegisterCsiSnapshotFailures records a failed volume snapshot by CSI plugin. -func (m *ServerMetrics) RegisterCsiSnapshotFailures(backupSchedule, backupName string, csiSnapshotsFailed int) { +// RegisterCSISnapshotFailures records a failed volume snapshot by CSI plugin. +func (m *ServerMetrics) RegisterCSISnapshotFailures(backupSchedule, backupName string, csiSnapshotsFailed int) { if c, ok := m.metrics[csiSnapshotFailureTotal].(*prometheus.CounterVec); ok { c.WithLabelValues(backupSchedule, backupName).Add(float64(csiSnapshotsFailed)) } } - -// RegisterCsiStorageSizeAdd records volume snapshot's storage size increase created by CSI plugin. -func (m *ServerMetrics) RegisterCsiStorageSizeAdd(backupSchedule, backupName string, csiStorageSize int64) { - if g, ok := m.metrics[csiSnapshotStorageTotal].(*prometheus.GaugeVec); ok { - g.WithLabelValues(backupSchedule, backupName).Add(float64(csiStorageSize)) - } -} - -// RegisterCsiStorageSizeSub records volume snapshot's storage size decrease created by CSI plugin. -func (m *ServerMetrics) RegisterCsiStorageSizeSub(backupSchedule, backupName string, csiStorageSize int64) { - if g, ok := m.metrics[csiSnapshotStorageTotal].(*prometheus.GaugeVec); ok { - g.WithLabelValues(backupSchedule, backupName).Sub(float64(csiStorageSize)) - } -}