github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/veeam-sos-api.go (about) 1 // Copyright (c) 2015-2023 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "bytes" 22 "context" 23 "encoding/xml" 24 "io" 25 26 "github.com/minio/madmin-go/v3" 27 ) 28 29 // From Veeam-SOSAPI_1.0_Document_v1.02d.pdf 30 // - SOSAPI Protocol Version 31 // - Model Name of the vendor plus version for statistical analysis. 32 // - List of Smart Object Storage protocol capabilities supported by the server. 33 // Currently, there are three capabilities supported: 34 // - Capacity Reporting 35 // - Backup data locality for upload sessions (Veeam Smart Entity) 36 // - Handover of IAM & STS Endpoints instead of manual definition in Veeam Backup & Replication. This allows Veeam 37 // Agents to directly backup to object storage. 38 // 39 // An object storage system can implement one, multiple, or all functions. 40 // 41 // - Optional (mandatory if <IAMSTS> is true): Set Endpoints for IAM and STS processing. 42 // 43 // - Optional: Set server preferences for Backup & Replication parallel sessions, batch size of deletes, and block sizes (before 44 // compression). This is an optional area; by default, there should be no <SystemRecommendations> section in the 45 // system.xml. Vendors can work with Veeam Product Management and the Alliances team on getting approval to integrate 46 // specific system recommendations based on current support case statistics and storage performance possibilities. 47 // Vendors might change the settings based on the configuration and scale out of the solution (more storage nodes => 48 // higher task limit). 49 // 50 // <S3ConcurrentTaskLimit> 51 // 52 // - Defines how many S3 operations are executed parallel within one Repository Task Slot (and within one backup object 53 // that gets offloaded). The same registry key setting overwrites the storage-defined setting. 54 // Optional value, default 64, range: 1-unlimited 55 // 56 // - <S3MultiObjectDeleteLimit> 57 // Some of the Veeam products use Multi Delete operations. This setting can reduce how many objects are included in one 58 // multi-delete operation. The same registry key setting overwrites the storage-defined setting. 59 // Optional value, default 1000, range: 1-unlimited (S3 standard maximum is 1000 and should not be set higher) 60 // 61 // - <StorageConcurrentTasksLimit> 62 // Setting reduces the parallel Repository Task slots that offload or write data to object storage. The same user interface 63 // setting overwrites the storage-defined setting. 64 // Optional value, default 0, range: 0-unlimited (0 equals unlimited, which means the maximum configured repository task 65 // slots are used for object offloading or writing) 66 // 67 // - <KbBlockSize> 68 // Veeam Block Size for backup and restore processing before compression is applied. The higher the block size, the more 69 // backup space is needed for incremental backups. Larger block sizes also mean less performance for random read restore 70 // methods like Instant Restore, File Level Recovery, and Database/Application restores. Veeam recommends that vendors 71 // optimize the storage system for the default value of 1MB minus compression object sizes. The setting simultaneously 72 // affects read from source, block, file, dedup, and object storage backup targets for a specific Veeam Job. When customers 73 // create a new backup job and select the object storage or a SOBR as a backup target with this setting, the job default 74 // setting will be set to this value. This setting will be only applied to newly created jobs (manual changes with Active Full 75 // processing possible from the customer side). 76 // Optional value, default 1024, allowed values 256,512,1024,4096,8192, value defined in KB size. 77 // 78 // - The object should be present in all buckets accessed by Veeam products that want to leverage the SOSAPI functionality. 79 // 80 // - The current protocol version is 1.0. 81 type apiEndpoints struct { 82 IAMEndpoint string `xml:"IAMEndpoint"` 83 STSEndpoint string `xml:"STSEndpoint"` 84 } 85 86 type systemInfo struct { 87 XMLName xml.Name `xml:"SystemInfo" json:"-"` 88 ProtocolVersion string `xml:"ProtocolVersion"` 89 ModelName string `xml:"ModelName"` 90 ProtocolCapabilities struct { 91 CapacityInfo bool `xml:"CapacityInfo"` 92 UploadSessions bool `xml:"UploadSessions"` 93 IAMSTS bool `xml:"IAMSTS"` 94 } `mxl:"ProtocolCapabilities"` 95 APIEndpoints *apiEndpoints `xml:"APIEndpoints,omitempty"` 96 SystemRecommendations struct { 97 S3ConcurrentTaskLimit int `xml:"S3ConcurrentTaskLimit,omitempty"` 98 S3MultiObjectDeleteLimit int `xml:"S3MultiObjectDeleteLimit,omitempty"` 99 StorageCurrentTaskLimit int `xml:"StorageCurrentTaskLimit,omitempty"` 100 KBBlockSize int `xml:"KbBlockSize"` 101 } `xml:"SystemRecommendations"` 102 } 103 104 // This optional functionality allows vendors to report space information to Veeam products, and Veeam will make placement 105 // decisions based on this information. For example, Veeam Backup & Replication has a Scale-out-Backup-Repository feature where 106 // multiple buckets can be used together. The placement logic for additional backup files is based on available space. Other values 107 // will augment the Veeam user interface and statistics, including free space warnings. 108 type capacityInfo struct { 109 XMLName xml.Name `xml:"CapacityInfo" json:"-"` 110 Capacity int64 `xml:"Capacity"` 111 Available int64 `xml:"Available"` 112 Used int64 `xml:"Used"` 113 } 114 115 const ( 116 systemXMLObject = ".system-d26a9498-cb7c-4a87-a44a-8ae204f5ba6c/system.xml" 117 capacityXMLObject = ".system-d26a9498-cb7c-4a87-a44a-8ae204f5ba6c/capacity.xml" 118 ) 119 120 func isVeeamSOSAPIObject(object string) bool { 121 switch object { 122 case systemXMLObject, capacityXMLObject: 123 return true 124 default: 125 return false 126 } 127 } 128 129 func veeamSOSAPIHeadObject(ctx context.Context, bucket, object string, opts ObjectOptions) (ObjectInfo, error) { 130 gr, err := veeamSOSAPIGetObject(ctx, bucket, object, nil, opts) 131 if gr != nil { 132 gr.Close() 133 return gr.ObjInfo, nil 134 } 135 return ObjectInfo{}, err 136 } 137 138 func veeamSOSAPIGetObject(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, opts ObjectOptions) (gr *GetObjectReader, err error) { 139 var buf []byte 140 switch object { 141 case systemXMLObject: 142 si := systemInfo{ 143 ProtocolVersion: `"1.0"`, 144 ModelName: "\"MinIO " + ReleaseTag + "\"", 145 } 146 si.ProtocolCapabilities.CapacityInfo = true 147 148 // Default recommended block size with MinIO 149 si.SystemRecommendations.KBBlockSize = 4096 150 151 buf = encodeResponse(&si) 152 case capacityXMLObject: 153 objAPI := newObjectLayerFn() 154 if objAPI == nil { 155 return nil, errServerNotInitialized 156 } 157 158 q, _ := globalBucketQuotaSys.Get(ctx, bucket) 159 binfo, _ := globalBucketQuotaSys.GetBucketUsageInfo(bucket) 160 161 ci := capacityInfo{ 162 Used: int64(binfo.Size), 163 } 164 165 var quotaSize int64 166 if q != nil && q.Type == madmin.HardQuota { 167 if q.Size > 0 { 168 quotaSize = int64(q.Size) 169 } else if q.Quota > 0 { 170 quotaSize = int64(q.Quota) 171 } 172 } 173 174 if quotaSize == 0 { 175 info := objAPI.StorageInfo(ctx, true) 176 info.Backend = objAPI.BackendInfo() 177 178 ci.Capacity = int64(GetTotalUsableCapacity(info.Disks, info)) 179 } else { 180 ci.Capacity = quotaSize 181 } 182 ci.Available = ci.Capacity - ci.Used 183 184 buf = encodeResponse(&ci) 185 default: 186 return nil, errFileNotFound 187 } 188 189 etag := getMD5Hash(buf) 190 r := bytes.NewReader(buf) 191 192 off, length := int64(0), r.Size() 193 if rs != nil { 194 off, length, err = rs.GetOffsetLength(r.Size()) 195 if err != nil { 196 return nil, err 197 } 198 } 199 r.Seek(off, io.SeekStart) 200 201 return NewGetObjectReaderFromReader(io.LimitReader(r, length), ObjectInfo{ 202 Bucket: bucket, 203 Name: object, 204 Size: r.Size(), 205 IsLatest: true, 206 ContentType: string(mimeXML), 207 NumVersions: 1, 208 ETag: etag, 209 ModTime: UTCNow(), 210 }, opts) 211 }