@ -20,7 +20,7 @@ import (
"encoding/json"
"encoding/json"
"errors"
"errors"
"fmt"
"fmt"
"strings "
"reflect "
"sync"
"sync"
)
)
@ -296,21 +296,21 @@ func checkDisksConsistency(formatConfigs []*formatConfigV1) error {
// checkJBODConsistency - validate xl jbod order if they are consistent.
// checkJBODConsistency - validate xl jbod order if they are consistent.
func checkJBODConsistency ( formatConfigs [ ] * formatConfigV1 ) error {
func checkJBODConsistency ( formatConfigs [ ] * formatConfigV1 ) error {
var jbodStr string
var sentinelJBOD [ ] string
// Extract first valid JBOD.
// Extract first valid JBOD.
for _ , format := range formatConfigs {
for _ , format := range formatConfigs {
if format == nil {
if format == nil {
continue
continue
}
}
jbodStr = strings . Join ( format . XL . JBOD , "." )
sentinelJBOD = format . XL . JBOD
break
break
}
}
for _ , format := range formatConfigs {
for _ , format := range formatConfigs {
if format == nil {
if format == nil {
continue
continue
}
}
savedJBODStr := strings . Join ( format . XL . JBOD , "." )
currentJBOD := format . XL . JBOD
if jbodStr != savedJBODStr {
if ! reflect . DeepEqual ( sentinelJBOD , currentJBOD ) {
return errors . New ( "Inconsistent JBOD found." )
return errors . New ( "Inconsistent JBOD found." )
}
}
}
}
@ -412,14 +412,15 @@ func isFormatFound(formats []*formatConfigV1) bool {
// Heals any missing format.json on the drives. Returns error only for unexpected errors
// Heals any missing format.json on the drives. Returns error only for unexpected errors
// as regular errors can be ignored since there might be enough quorum to be operational.
// as regular errors can be ignored since there might be enough quorum to be operational.
func healFormatXL ( storageDisks [ ] StorageAPI ) error {
// Heals only fresh disks.
func healFormatXLFreshDisks ( storageDisks [ ] StorageAPI ) error {
formatConfigs := make ( [ ] * formatConfigV1 , len ( storageDisks ) )
formatConfigs := make ( [ ] * formatConfigV1 , len ( storageDisks ) )
var referenceConfig * formatConfigV1
var referenceConfig * formatConfigV1
// Loads `format.json` from all disks.
// Loads `format.json` from all disks.
for index , disk := range storageDisks {
for index , disk := range storageDisks {
// Disk not found or ignored is a valid case.
// Disk not found or ignored is a valid case.
if disk == nil {
if disk == nil {
// Proceed without healing .
// Return nil, one of the disk is offline .
return nil
return nil
}
}
formatXL , err := loadFormat ( disk )
formatXL , err := loadFormat ( disk )
@ -436,6 +437,7 @@ func healFormatXL(storageDisks []StorageAPI) error {
} // Success.
} // Success.
formatConfigs [ index ] = formatXL
formatConfigs [ index ] = formatXL
}
}
// All `format.json` has been read successfully, previously completed.
// All `format.json` has been read successfully, previously completed.
if isFormatFound ( formatConfigs ) {
if isFormatFound ( formatConfigs ) {
// Return success.
// Return success.
@ -446,6 +448,7 @@ func healFormatXL(storageDisks []StorageAPI) error {
if isFormatNotFound ( formatConfigs ) {
if isFormatNotFound ( formatConfigs ) {
return initFormatXL ( storageDisks )
return initFormatXL ( storageDisks )
}
}
// Validate format configs for consistency in JBOD and disks.
// Validate format configs for consistency in JBOD and disks.
if err := checkFormatXL ( formatConfigs ) ; err != nil {
if err := checkFormatXL ( formatConfigs ) ; err != nil {
return err
return err
@ -462,40 +465,61 @@ func healFormatXL(storageDisks []StorageAPI) error {
}
}
}
}
// Collect new format configs.
var newFormatConfigs = make ( [ ] * formatConfigV1 , len ( storageDisks ) )
// Collect new JBOD.
// Collect new JBOD.
newJBOD := referenceConfig . XL . JBOD
newJBOD := referenceConfig . XL . JBOD
// This section heals the format.json and updates the fresh disks
// Reorder the disks based on the JBOD order.
// by apply a new UUID for all the fresh disks.
orderedDisks , err := reorderDisks ( storageDisks , formatConfigs )
for index , format := range formatConfigs {
if err != nil {
if format == nil {
return err
}
// From ordered disks fill the UUID position.
for index , disk := range orderedDisks {
if disk == nil {
newJBOD [ index ] = getUUID ( )
newJBOD [ index ] = getUUID ( )
}
}
}
}
// Collect new format configs.
var newFormatConfigs = make ( [ ] * formatConfigV1 , len ( orderedDisks ) )
// Collect new format configs that need to be written.
// Collect new format configs that need to be written.
for index := range orderedDisks {
// New configs are generated since we are going
// to re-populate across all disks.
config := & formatConfigV1 {
Version : referenceConfig . Version ,
Format : referenceConfig . Format ,
XL : & xlFormat {
Version : referenceConfig . XL . Version ,
Disk : newJBOD [ index ] ,
JBOD : newJBOD ,
} ,
}
newFormatConfigs [ index ] = config
}
// Fill in the missing disk back from format configs.
// We need to make sure we have kept the previous order
// and allowed fresh disks to be arranged anywhere.
// Following block facilitates to put fresh disks.
for index , format := range formatConfigs {
for index , format := range formatConfigs {
// Format is missing so we go through ordered disks.
if format == nil {
if format == nil {
config := & formatConfigV1 {
// At this point when disk is missing the fresh disk
Version : referenceConfig . Version ,
// in the stack get it back from storageDisks.
Format : referenceConfig . Format ,
for oIndex , disk := range orderedDisks {
XL : & xlFormat {
if disk == nil {
Version : referenceConfig . XL . Version ,
orderedDisks [ oIndex ] = storageDisks [ index ]
Disk : newJBOD [ index ] ,
break
JBOD : newJBOD ,
}
} ,
}
}
newFormatConfigs [ index ] = config
continue
}
}
newFormatConfigs [ index ] = format
newFormatConfigs [ index ] . XL . JBOD = newJBOD
newFormatConfigs [ index ] . XL . Disk = newJBOD [ index ]
}
}
// Save new `format.json` across all disks.
return saveFormatXL ( storageDisks , newFormatConfigs )
// Save new `format.json` across all disks, in JBOD order.
return saveFormatXL ( orderedDisks , newFormatConfigs )
}
}
// loadFormatXL - loads XL `format.json` and returns back properly
// loadFormatXL - loads XL `format.json` and returns back properly