storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/config/storageclass/storage-class.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2019 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package storageclass 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "strconv" 24 "strings" 25 "sync" 26 27 "storj.io/minio/cmd/config" 28 "storj.io/minio/pkg/env" 29 ) 30 31 // Standard constants for all storage class 32 const ( 33 // Reduced redundancy storage class 34 RRS = "REDUCED_REDUNDANCY" 35 // Standard storage class 36 STANDARD = "STANDARD" 37 // DMA storage class 38 DMA = "DMA" 39 40 // Valid values are "write" and "read+write" 41 DMAWrite = "write" 42 DMAReadWrite = "read+write" 43 ) 44 45 // Standard constats for config info storage class 46 const ( 47 ClassStandard = "standard" 48 ClassRRS = "rrs" 49 ClassDMA = "dma" 50 51 // Reduced redundancy storage class environment variable 52 RRSEnv = "MINIO_STORAGE_CLASS_RRS" 53 // Standard storage class environment variable 54 StandardEnv = "MINIO_STORAGE_CLASS_STANDARD" 55 // DMA storage class environment variable 56 DMAEnv = "MINIO_STORAGE_CLASS_DMA" 57 58 // Supported storage class scheme is EC 59 schemePrefix = "EC" 60 61 // Min parity disks 62 minParityDisks = 2 63 64 // Default RRS parity is always minimum parity. 65 defaultRRSParity = minParityDisks 66 67 // Default DMA value 68 defaultDMA = DMAReadWrite 69 ) 70 71 // DefaultKVS - default storage class config 72 var ( 73 DefaultKVS = config.KVS{ 74 config.KV{ 75 Key: ClassStandard, 76 Value: "", 77 }, 78 config.KV{ 79 Key: ClassRRS, 80 Value: "EC:2", 81 }, 82 config.KV{ 83 Key: ClassDMA, 84 Value: defaultDMA, 85 }, 86 } 87 ) 88 89 // StorageClass - holds storage class information 90 type StorageClass struct { 91 Parity int 92 } 93 94 // ConfigLock is a global lock for storage-class config 95 var ConfigLock = sync.RWMutex{} 96 97 // Config storage class configuration 98 type Config struct { 99 Standard StorageClass `json:"standard"` 100 RRS StorageClass `json:"rrs"` 101 DMA string `json:"dma"` 102 } 103 104 // UnmarshalJSON - Validate SS and RRS parity when unmarshalling JSON. 105 func (sCfg *Config) UnmarshalJSON(data []byte) error { 106 type Alias Config 107 aux := &struct { 108 *Alias 109 }{ 110 Alias: (*Alias)(sCfg), 111 } 112 return json.Unmarshal(data, &aux) 113 } 114 115 // IsValid - returns true if input string is a valid 116 // storage class kind supported. 117 func IsValid(sc string) bool { 118 return sc == RRS || sc == STANDARD 119 } 120 121 // UnmarshalText unmarshals storage class from its textual form into 122 // storageClass structure. 123 func (sc *StorageClass) UnmarshalText(b []byte) error { 124 scStr := string(b) 125 if scStr == "" { 126 return nil 127 } 128 s, err := parseStorageClass(scStr) 129 if err != nil { 130 return err 131 } 132 sc.Parity = s.Parity 133 return nil 134 } 135 136 // MarshalText - marshals storage class string. 137 func (sc *StorageClass) MarshalText() ([]byte, error) { 138 if sc.Parity != 0 { 139 return []byte(fmt.Sprintf("%s:%d", schemePrefix, sc.Parity)), nil 140 } 141 return []byte{}, nil 142 } 143 144 func (sc *StorageClass) String() string { 145 if sc.Parity != 0 { 146 return fmt.Sprintf("%s:%d", schemePrefix, sc.Parity) 147 } 148 return "" 149 } 150 151 // Parses given storageClassEnv and returns a storageClass structure. 152 // Supported Storage Class format is "Scheme:Number of parity disks". 153 // Currently only supported scheme is "EC". 154 func parseStorageClass(storageClassEnv string) (sc StorageClass, err error) { 155 s := strings.Split(storageClassEnv, ":") 156 157 // only two elements allowed in the string - "scheme" and "number of parity disks" 158 if len(s) > 2 { 159 return StorageClass{}, config.ErrStorageClassValue(nil).Msg("Too many sections in " + storageClassEnv) 160 } else if len(s) < 2 { 161 return StorageClass{}, config.ErrStorageClassValue(nil).Msg("Too few sections in " + storageClassEnv) 162 } 163 164 // only allowed scheme is "EC" 165 if s[0] != schemePrefix { 166 return StorageClass{}, config.ErrStorageClassValue(nil).Msg("Unsupported scheme " + s[0] + ". Supported scheme is EC") 167 } 168 169 // Number of parity disks should be integer 170 parityDisks, err := strconv.Atoi(s[1]) 171 if err != nil { 172 return StorageClass{}, config.ErrStorageClassValue(err) 173 } 174 175 return StorageClass{ 176 Parity: parityDisks, 177 }, nil 178 } 179 180 // ValidateParity validate standard storage class parity. 181 func ValidateParity(ssParity, setDriveCount int) error { 182 // SS parity disks should be greater than or equal to minParityDisks. 183 // Parity below minParityDisks is not supported. 184 if ssParity > 0 && ssParity < minParityDisks { 185 return fmt.Errorf("Standard storage class parity %d should be greater than or equal to %d", 186 ssParity, minParityDisks) 187 } 188 189 if ssParity > setDriveCount/2 { 190 return fmt.Errorf("Standard storage class parity %d should be less than or equal to %d", ssParity, setDriveCount/2) 191 } 192 193 return nil 194 } 195 196 // Validates the parity disks. 197 func validateParity(ssParity, rrsParity, setDriveCount int) (err error) { 198 // SS parity disks should be greater than or equal to minParityDisks. 199 // Parity below minParityDisks is not supported. 200 if ssParity > 0 && ssParity < minParityDisks { 201 return fmt.Errorf("Standard storage class parity %d should be greater than or equal to %d", 202 ssParity, minParityDisks) 203 } 204 205 // RRS parity disks should be greater than or equal to minParityDisks. 206 // Parity below minParityDisks is not supported. 207 if rrsParity > 0 && rrsParity < minParityDisks { 208 return fmt.Errorf("Reduced redundancy storage class parity %d should be greater than or equal to %d", rrsParity, minParityDisks) 209 } 210 211 if ssParity > setDriveCount/2 { 212 return fmt.Errorf("Standard storage class parity %d should be less than or equal to %d", ssParity, setDriveCount/2) 213 } 214 215 if rrsParity > setDriveCount/2 { 216 return fmt.Errorf("Reduced redundancy storage class parity %d should be less than or equal to %d", rrsParity, setDriveCount/2) 217 } 218 219 if ssParity > 0 && rrsParity > 0 { 220 if ssParity > 0 && ssParity < rrsParity { 221 return fmt.Errorf("Standard storage class parity disks %d should be greater than or equal to Reduced redundancy storage class parity disks %d", ssParity, rrsParity) 222 } 223 } 224 return nil 225 } 226 227 // GetParityForSC - Returns the data and parity drive count based on storage class 228 // If storage class is set using the env vars MINIO_STORAGE_CLASS_RRS and 229 // MINIO_STORAGE_CLASS_STANDARD or server config fields corresponding values are 230 // returned. 231 // 232 // -- if input storage class is empty then standard is assumed 233 // -- if input is RRS but RRS is not configured default '2' parity 234 // for RRS is assumed 235 // -- if input is STANDARD but STANDARD is not configured '0' parity 236 // is returned, the caller is expected to choose the right parity 237 // at that point. 238 func (sCfg Config) GetParityForSC(sc string) (parity int) { 239 ConfigLock.RLock() 240 defer ConfigLock.RUnlock() 241 switch strings.TrimSpace(sc) { 242 case RRS: 243 // set the rrs parity if available 244 if sCfg.RRS.Parity == 0 { 245 return defaultRRSParity 246 } 247 return sCfg.RRS.Parity 248 default: 249 return sCfg.Standard.Parity 250 } 251 } 252 253 // Update update storage-class with new config 254 func (sCfg Config) Update(newCfg Config) { 255 ConfigLock.Lock() 256 defer ConfigLock.Unlock() 257 sCfg.RRS = newCfg.RRS 258 sCfg.DMA = newCfg.DMA 259 sCfg.Standard = newCfg.Standard 260 } 261 262 // GetDMA - returns DMA configuration. 263 func (sCfg Config) GetDMA() string { 264 ConfigLock.RLock() 265 defer ConfigLock.RUnlock() 266 return sCfg.DMA 267 } 268 269 // Enabled returns if etcd is enabled. 270 func Enabled(kvs config.KVS) bool { 271 ssc := kvs.Get(ClassStandard) 272 rrsc := kvs.Get(ClassRRS) 273 return ssc != "" || rrsc != "" 274 } 275 276 // LookupConfig - lookup storage class config and override with valid environment settings if any. 277 func LookupConfig(kvs config.KVS, setDriveCount int) (cfg Config, err error) { 278 cfg = Config{} 279 280 if err = config.CheckValidKeys(config.StorageClassSubSys, kvs, DefaultKVS); err != nil { 281 return Config{}, err 282 } 283 284 ssc := env.Get(StandardEnv, kvs.Get(ClassStandard)) 285 rrsc := env.Get(RRSEnv, kvs.Get(ClassRRS)) 286 dma := env.Get(DMAEnv, kvs.Get(ClassDMA)) 287 // Check for environment variables and parse into storageClass struct 288 if ssc != "" { 289 cfg.Standard, err = parseStorageClass(ssc) 290 if err != nil { 291 return Config{}, err 292 } 293 } 294 295 if rrsc != "" { 296 cfg.RRS, err = parseStorageClass(rrsc) 297 if err != nil { 298 return Config{}, err 299 } 300 } 301 if cfg.RRS.Parity == 0 { 302 cfg.RRS.Parity = defaultRRSParity 303 } 304 305 if dma == "" { 306 dma = defaultDMA 307 } 308 if dma != DMAReadWrite && dma != DMAWrite { 309 return Config{}, errors.New(`valid dma values are "read-write" and "write"`) 310 } 311 cfg.DMA = dma 312 313 // Validation is done after parsing both the storage classes. This is needed because we need one 314 // storage class value to deduce the correct value of the other storage class. 315 if err = validateParity(cfg.Standard.Parity, cfg.RRS.Parity, setDriveCount); err != nil { 316 return Config{}, err 317 } 318 319 return cfg, nil 320 }