| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * Minio Cloud Storage, (C) 2016 Minio, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2016-05-27 05:13:10 +08:00
										 |  |  | 	"sort" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-26 15:11:53 +08:00
										 |  |  | 	"github.com/minio/minio-go/pkg/set" | 
					
						
							| 
									
										
										
										
											2016-05-27 05:13:10 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/disk" | 
					
						
							| 
									
										
										
										
											2016-07-04 07:58:21 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/objcache" | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-31 07:51:59 +08:00
										 |  |  | // XL constants.
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2016-05-31 07:51:59 +08:00
										 |  |  | 	// Format config file carries backend format specific details.
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	formatConfigFile = "format.json" | 
					
						
							| 
									
										
										
										
											2016-06-23 08:18:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Format config tmp file carries backend format.
 | 
					
						
							|  |  |  | 	formatConfigFileTmp = "format.json.tmp" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-31 07:51:59 +08:00
										 |  |  | 	// XL metadata file carries per object metadata.
 | 
					
						
							|  |  |  | 	xlMetaJSONFile = "xl.json" | 
					
						
							| 
									
										
										
										
											2016-06-23 08:18:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-31 07:51:59 +08:00
										 |  |  | 	// Uploads metadata file carries per multipart object metadata.
 | 
					
						
							|  |  |  | 	uploadsJSONFile = "uploads.json" | 
					
						
							| 
									
										
										
										
											2016-07-04 07:58:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 8GiB cache by default.
 | 
					
						
							|  |  |  | 	maxCacheSize = 8 * 1024 * 1024 * 1024 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Maximum erasure blocks.
 | 
					
						
							|  |  |  | 	maxErasureBlocks = 16 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Minimum erasure blocks.
 | 
					
						
							| 
									
										
										
										
											2016-07-20 10:24:32 +08:00
										 |  |  | 	minErasureBlocks = 4 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-31 07:51:59 +08:00
										 |  |  | // xlObjects - Implements XL object layer.
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | type xlObjects struct { | 
					
						
							| 
									
										
										
										
											2016-08-26 08:16:34 +08:00
										 |  |  | 	storageDisks []StorageAPI // Collection of initialized backend disks.
 | 
					
						
							|  |  |  | 	dataBlocks   int          // dataBlocks count caculated for erasure.
 | 
					
						
							|  |  |  | 	parityBlocks int          // parityBlocks count calculated for erasure.
 | 
					
						
							|  |  |  | 	readQuorum   int          // readQuorum minimum required disks to read data.
 | 
					
						
							|  |  |  | 	writeQuorum  int          // writeQuorum minimum required disks to write data.
 | 
					
						
							| 
									
										
										
										
											2016-05-31 07:51:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-04 07:58:21 +08:00
										 |  |  | 	// ListObjects pool management.
 | 
					
						
							| 
									
										
										
										
											2016-06-06 02:55:45 +08:00
										 |  |  | 	listPool *treeWalkPool | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-04 07:58:21 +08:00
										 |  |  | 	// Object cache for caching objects.
 | 
					
						
							|  |  |  | 	objCache *objcache.Cache | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-04 07:58:21 +08:00
										 |  |  | 	// Object cache enabled.
 | 
					
						
							|  |  |  | 	objCacheEnabled bool | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-29 10:31:59 +08:00
										 |  |  | func repairDiskMetadata(storageDisks []StorageAPI) error { | 
					
						
							| 
									
										
										
										
											2016-06-14 16:12:15 +08:00
										 |  |  | 	// Attempt to load all `format.json`.
 | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 	formatConfigs, sErrs := loadAllFormats(storageDisks) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-29 18:13:44 +08:00
										 |  |  | 	// Generic format check validates
 | 
					
						
							|  |  |  | 	// if (no quorum) return error
 | 
					
						
							|  |  |  | 	// if (disks not recognized) // Always error.
 | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 	if err := genericFormatCheck(formatConfigs, sErrs); err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-29 10:31:59 +08:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Handles different cases properly.
 | 
					
						
							|  |  |  | 	switch reduceFormatErrs(sErrs, len(storageDisks)) { | 
					
						
							| 
									
										
										
										
											2016-07-29 07:49:59 +08:00
										 |  |  | 	case errCorruptedFormat: | 
					
						
							|  |  |  | 		if err := healFormatXLCorruptedDisks(storageDisks); err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-29 10:31:59 +08:00
										 |  |  | 			return fmt.Errorf("Unable to repair corrupted format, %s", err) | 
					
						
							| 
									
										
										
										
											2016-07-29 07:49:59 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 	case errSomeDiskUnformatted: | 
					
						
							|  |  |  | 		// All drives online but some report missing format.json.
 | 
					
						
							| 
									
										
										
										
											2016-07-26 18:18:47 +08:00
										 |  |  | 		if err := healFormatXLFreshDisks(storageDisks); err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 			// There was an unexpected unrecoverable error during healing.
 | 
					
						
							| 
									
										
										
										
											2016-08-29 10:31:59 +08:00
										 |  |  | 			return fmt.Errorf("Unable to heal backend %s", err) | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	case errSomeDiskOffline: | 
					
						
							| 
									
										
										
										
											2016-07-11 05:38:15 +08:00
										 |  |  | 		// FIXME: in future.
 | 
					
						
							| 
									
										
										
										
											2016-08-29 10:31:59 +08:00
										 |  |  | 		return fmt.Errorf("Unable to initialize format %s and %s", errSomeDiskOffline, errSomeDiskUnformatted) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // newXLObjects - initialize new xl object layer.
 | 
					
						
							|  |  |  | func newXLObjects(disks, ignoredDisks []string) (ObjectLayer, error) { | 
					
						
							|  |  |  | 	if disks == nil { | 
					
						
							|  |  |  | 		return nil, errInvalidArgument | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-08-29 10:31:59 +08:00
										 |  |  | 	disksSet := set.NewStringSet() | 
					
						
							|  |  |  | 	if len(ignoredDisks) > 0 { | 
					
						
							|  |  |  | 		disksSet = set.CreateStringSet(ignoredDisks...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Bootstrap disks.
 | 
					
						
							|  |  |  | 	storageDisks := make([]StorageAPI, len(disks)) | 
					
						
							|  |  |  | 	for index, disk := range disks { | 
					
						
							|  |  |  | 		// Check if disk is ignored.
 | 
					
						
							|  |  |  | 		if disksSet.Contains(disk) { | 
					
						
							|  |  |  | 			storageDisks[index] = nil | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var err error | 
					
						
							|  |  |  | 		// Intentionally ignore disk not found errors. XL is designed
 | 
					
						
							|  |  |  | 		// to handle these errors internally.
 | 
					
						
							|  |  |  | 		storageDisks[index], err = newStorageAPI(disk) | 
					
						
							|  |  |  | 		if err != nil && err != errDiskNotFound { | 
					
						
							|  |  |  | 			switch diskType := storageDisks[index].(type) { | 
					
						
							|  |  |  | 			case networkStorage: | 
					
						
							|  |  |  | 				diskType.rpcClient.Close() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Fix format files in case of fresh or corrupted disks
 | 
					
						
							|  |  |  | 	repairDiskMetadata(storageDisks) | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-02 16:59:28 +08:00
										 |  |  | 	// Runs house keeping code, like t, cleaning up tmp files etc.
 | 
					
						
							|  |  |  | 	if err := xlHouseKeeping(storageDisks); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	// Load saved XL format.json and validate.
 | 
					
						
							|  |  |  | 	newPosixDisks, err := loadFormatXL(storageDisks) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-06-03 07:34:15 +08:00
										 |  |  | 		// errCorruptedDisk - healing failed
 | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("Unable to recognize backend format, %s", err) | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Calculate data and parity blocks.
 | 
					
						
							|  |  |  | 	dataBlocks, parityBlocks := len(newPosixDisks)/2, len(newPosixDisks)/2 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-09 11:34:27 +08:00
										 |  |  | 	// Initialize object cache.
 | 
					
						
							|  |  |  | 	objCache := objcache.New(globalMaxCacheSize, globalCacheExpiry) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize list pool.
 | 
					
						
							|  |  |  | 	listPool := newTreeWalkPool(globalLookupTimeout) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-02 07:43:31 +08:00
										 |  |  | 	// Initialize xl objects.
 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	xl := xlObjects{ | 
					
						
							| 
									
										
										
										
											2016-07-09 11:34:27 +08:00
										 |  |  | 		storageDisks:    newPosixDisks, | 
					
						
							|  |  |  | 		dataBlocks:      dataBlocks, | 
					
						
							|  |  |  | 		parityBlocks:    parityBlocks, | 
					
						
							|  |  |  | 		listPool:        listPool, | 
					
						
							|  |  |  | 		objCache:        objCache, | 
					
						
							| 
									
										
										
										
											2016-07-04 07:58:21 +08:00
										 |  |  | 		objCacheEnabled: globalMaxCacheSize > 0, | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Figure out read and write quorum based on number of storage disks.
 | 
					
						
							| 
									
										
										
										
											2016-07-20 10:24:32 +08:00
										 |  |  | 	// READ and WRITE quorum is always set to (N/2) number of disks.
 | 
					
						
							|  |  |  | 	xl.readQuorum = len(xl.storageDisks) / 2 | 
					
						
							| 
									
										
										
										
											2016-07-28 17:20:34 +08:00
										 |  |  | 	xl.writeQuorum = len(xl.storageDisks)/2 + 1 | 
					
						
							| 
									
										
										
										
											2016-05-21 11:48:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Return successfully initialized object layer.
 | 
					
						
							|  |  |  | 	return xl, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-05-27 05:13:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 14:55:48 +08:00
										 |  |  | // Shutdown function for object storage interface.
 | 
					
						
							|  |  |  | func (xl xlObjects) Shutdown() error { | 
					
						
							|  |  |  | 	// Add any object layer shutdown activities here.
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-29 10:31:59 +08:00
										 |  |  | // HealDiskMetadata function for object storage interface.
 | 
					
						
							|  |  |  | func (xl xlObjects) HealDiskMetadata() error { | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	// generates random string on setting MINIO_DEBUG=lock, else returns empty string.
 | 
					
						
							|  |  |  | 	// used for instrumentation on locks.
 | 
					
						
							|  |  |  | 	opsID := getOpsID() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nsMutex.Lock(minioMetaBucket, formatConfigFile, opsID) | 
					
						
							|  |  |  | 	defer nsMutex.Unlock(minioMetaBucket, formatConfigFile, opsID) | 
					
						
							| 
									
										
										
										
											2016-08-29 10:31:59 +08:00
										 |  |  | 	return repairDiskMetadata(xl.storageDisks) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-27 05:13:10 +08:00
										 |  |  | // byDiskTotal is a collection satisfying sort.Interface.
 | 
					
						
							|  |  |  | type byDiskTotal []disk.Info | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (d byDiskTotal) Len() int      { return len(d) } | 
					
						
							|  |  |  | func (d byDiskTotal) Swap(i, j int) { d[i], d[j] = d[j], d[i] } | 
					
						
							|  |  |  | func (d byDiskTotal) Less(i, j int) bool { | 
					
						
							|  |  |  | 	return d[i].Total < d[j].Total | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // StorageInfo - returns underlying storage statistics.
 | 
					
						
							|  |  |  | func (xl xlObjects) StorageInfo() StorageInfo { | 
					
						
							|  |  |  | 	var disksInfo []disk.Info | 
					
						
							| 
									
										
										
										
											2016-08-26 08:16:34 +08:00
										 |  |  | 	for _, storageDisk := range xl.storageDisks { | 
					
						
							|  |  |  | 		info, err := storageDisk.DiskInfo() | 
					
						
							| 
									
										
										
										
											2016-05-27 05:13:10 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-26 08:16:34 +08:00
										 |  |  | 			errorIf(err, "Unable to fetch disk info for %#v", storageDisk) | 
					
						
							| 
									
										
										
										
											2016-05-27 05:13:10 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		disksInfo = append(disksInfo, info) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Sort so that the first element is the smallest.
 | 
					
						
							|  |  |  | 	sort.Sort(byDiskTotal(disksInfo)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Return calculated storage info, choose the lowest Total and
 | 
					
						
							|  |  |  | 	// Free as the total aggregated values. Total capacity is always
 | 
					
						
							|  |  |  | 	// the multiple of smallest disk among the disk list.
 | 
					
						
							|  |  |  | 	return StorageInfo{ | 
					
						
							|  |  |  | 		Total: disksInfo[0].Total * int64(len(xl.storageDisks)), | 
					
						
							|  |  |  | 		Free:  disksInfo[0].Free * int64(len(xl.storageDisks)), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |