| 
									
										
										
										
											2019-10-29 01:27:49 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * MinIO Cloud Storage, (C) 2019 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2020-08-19 05:37:26 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2019-10-29 01:27:49 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-16 22:30:05 +08:00
										 |  |  | 	"github.com/dustin/go-humanize" | 
					
						
							| 
									
										
										
										
											2019-10-29 01:27:49 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | const defaultMonitorNewDiskInterval = time.Second * 10 | 
					
						
							| 
									
										
										
										
											2019-10-29 01:27:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-08 10:43:06 +08:00
										 |  |  | func initAutoHeal(ctx context.Context, objAPI ObjectLayer) { | 
					
						
							|  |  |  | 	z, ok := objAPI.(*erasureZones) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	initBackgroundHealing(ctx, objAPI) // start quick background healing
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var bgSeq *healSequence | 
					
						
							|  |  |  | 	var found bool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		bgSeq, found = globalBackgroundHealState.getHealSequenceByToken(bgHealingUUID) | 
					
						
							|  |  |  | 		if found { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		time.Sleep(time.Second) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | 	for _, ep := range getLocalDisksToHeal() { | 
					
						
							|  |  |  | 		globalBackgroundHealState.pushHealLocalDisks(ep) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if drivesToHeal := globalBackgroundHealState.healDriveCount(); drivesToHeal > 0 { | 
					
						
							|  |  |  | 		logger.Info(fmt.Sprintf("Found drives to heal %d, waiting until %s to heal the content...", | 
					
						
							|  |  |  | 			drivesToHeal, defaultMonitorNewDiskInterval)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-08 10:43:06 +08:00
										 |  |  | 		// Heal any disk format and metadata early, if possible.
 | 
					
						
							|  |  |  | 		if err := bgSeq.healDiskMeta(); err != nil { | 
					
						
							|  |  |  | 			if newObjectLayerFn() != nil { | 
					
						
							|  |  |  | 				// log only in situations, when object layer
 | 
					
						
							|  |  |  | 				// has fully initialized.
 | 
					
						
							|  |  |  | 				logger.LogIf(bgSeq.ctx, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | 	go monitorLocalDisksAndHeal(ctx, z, bgSeq) | 
					
						
							| 
									
										
										
										
											2019-10-29 01:27:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | func getLocalDisksToHeal() (disksToHeal Endpoints) { | 
					
						
							|  |  |  | 	for _, ep := range globalEndpoints { | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | 		for _, endpoint := range ep.Endpoints { | 
					
						
							|  |  |  | 			if !endpoint.IsLocal { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Try to connect to the current endpoint
 | 
					
						
							|  |  |  | 			// and reformat if the current disk is not formatted
 | 
					
						
							|  |  |  | 			_, _, err := connectEndpoint(endpoint) | 
					
						
							| 
									
										
										
										
											2020-08-19 05:37:26 +08:00
										 |  |  | 			if errors.Is(err, errUnformattedDisk) { | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | 				disksToHeal = append(disksToHeal, endpoint) | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | 	return disksToHeal | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-08 10:43:06 +08:00
										 |  |  | func initBackgroundHealing(ctx context.Context, objAPI ObjectLayer) { | 
					
						
							|  |  |  | 	// Run the background healer
 | 
					
						
							|  |  |  | 	globalBackgroundHealRoutine = newHealRoutine() | 
					
						
							|  |  |  | 	go globalBackgroundHealRoutine.run(ctx, objAPI) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	globalBackgroundHealState.LaunchNewHealSequence(newBgHealSequence()) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-29 01:27:49 +08:00
										 |  |  | // monitorLocalDisksAndHeal - ensures that detected new disks are healed
 | 
					
						
							|  |  |  | //  1. Only the concerned erasure set will be listed and healed
 | 
					
						
							|  |  |  | //  2. Only the node hosting the disk is responsible to perform the heal
 | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | func monitorLocalDisksAndHeal(ctx context.Context, z *erasureZones, bgSeq *healSequence) { | 
					
						
							| 
									
										
										
										
											2020-01-10 18:35:06 +08:00
										 |  |  | 	// Perform automatic disk healing when a disk is replaced locally.
 | 
					
						
							| 
									
										
										
										
											2019-10-29 01:27:49 +08:00
										 |  |  | 	for { | 
					
						
							| 
									
										
										
										
											2020-03-23 03:16:36 +08:00
										 |  |  | 		select { | 
					
						
							|  |  |  | 		case <-ctx.Done(): | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		case <-time.After(defaultMonitorNewDiskInterval): | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | 			waitForLowHTTPReq(int32(globalEndpoints.NEndpoints()), time.Second) | 
					
						
							| 
									
										
										
										
											2020-05-01 11:23:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 12:14:35 +08:00
										 |  |  | 			var erasureSetInZoneEndpointToHeal []map[int]Endpoints | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | 			healDisks := globalBackgroundHealState.getHealLocalDisks() | 
					
						
							| 
									
										
										
										
											2020-09-17 12:14:35 +08:00
										 |  |  | 			if len(healDisks) > 0 { | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | 				// Reformat disks
 | 
					
						
							|  |  |  | 				bgSeq.sourceCh <- healSource{bucket: SlashSeparator} | 
					
						
							| 
									
										
										
										
											2020-01-10 18:35:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | 				// Ensure that reformatting disks is finished
 | 
					
						
							|  |  |  | 				bgSeq.sourceCh <- healSource{bucket: nopHeal} | 
					
						
							| 
									
										
										
										
											2020-07-16 22:30:05 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 12:14:35 +08:00
										 |  |  | 				logger.Info(fmt.Sprintf("Found drives to heal %d, proceeding to heal content...", | 
					
						
							|  |  |  | 					len(healDisks))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				erasureSetInZoneEndpointToHeal = make([]map[int]Endpoints, len(z.zones)) | 
					
						
							|  |  |  | 				for i := range z.zones { | 
					
						
							|  |  |  | 					erasureSetInZoneEndpointToHeal[i] = map[int]Endpoints{} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// heal only if new disks found.
 | 
					
						
							|  |  |  | 			for _, endpoint := range healDisks { | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | 				// Load the new format of this passed endpoint
 | 
					
						
							|  |  |  | 				_, format, err := connectEndpoint(endpoint) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					printEndpointError(endpoint, err, true) | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-03-23 03:16:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | 				zoneIdx := globalEndpoints.GetLocalZoneIdx(endpoint) | 
					
						
							|  |  |  | 				if zoneIdx < 0 { | 
					
						
							|  |  |  | 					continue | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-10-29 01:27:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | 				// Calculate the set index where the current endpoint belongs
 | 
					
						
							|  |  |  | 				setIndex, _, err := findDiskIndex(z.zones[zoneIdx].format, format) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					printEndpointError(endpoint, err, false) | 
					
						
							|  |  |  | 					continue | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 12:14:35 +08:00
										 |  |  | 				erasureSetInZoneEndpointToHeal[zoneIdx][setIndex] = append(erasureSetInZoneEndpointToHeal[zoneIdx][setIndex], endpoint) | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | 			for i, setMap := range erasureSetInZoneEndpointToHeal { | 
					
						
							| 
									
										
										
										
											2020-09-17 12:14:35 +08:00
										 |  |  | 				for setIndex, endpoints := range setMap { | 
					
						
							|  |  |  | 					for _, ep := range endpoints { | 
					
						
							|  |  |  | 						logger.Info("Healing disk '%s' on %s zone", ep, humanize.Ordinal(i+1)) | 
					
						
							| 
									
										
										
										
											2020-09-05 08:09:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 12:14:35 +08:00
										 |  |  | 						if err := healErasureSet(ctx, setIndex, z.zones[i].sets[setIndex], z.zones[i].setDriveCount); err != nil { | 
					
						
							|  |  |  | 							logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 							continue | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 12:14:35 +08:00
										 |  |  | 						// Only upon success pop the healed disk.
 | 
					
						
							|  |  |  | 						globalBackgroundHealState.popHealLocalDisks(ep) | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-10-29 01:27:49 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |