mirror of
https://github.com/minio/minio.git
synced 2024-12-26 23:25:54 -05:00
fb96779a8a
This PR implements an object layer which combines input erasure sets of XL layers into a unified namespace. This object layer extends the existing erasure coded implementation, it is assumed in this design that providing > 16 disks is a static configuration as well i.e if you started the setup with 32 disks with 4 sets 8 disks per pack then you would need to provide 4 sets always. Some design details and restrictions: - Objects are distributed using consistent ordering to a unique erasure coded layer. - Each pack has its own dsync so locks are synchronized properly at pack (erasure layer). - Each pack still has a maximum of 16 disks requirement, you can start with multiple such sets statically. - Static sets set of disks and cannot be changed, there is no elastic expansion allowed. - Static sets set of disks and cannot be changed, there is no elastic removal allowed. - ListObjects() across sets can be noticeably slower since List happens on all servers, and is merged at this sets layer. Fixes #5465 Fixes #5464 Fixes #5461 Fixes #5460 Fixes #5459 Fixes #5458 Fixes #5460 Fixes #5488 Fixes #5489 Fixes #5497 Fixes #5496
237 lines
5.7 KiB
Go
237 lines
5.7 KiB
Go
/*
|
|
* Minio Cloud Storage, (C) 2017, 2018 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 madmin
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
"time"
|
|
)
|
|
|
|
// HealOpts - collection of options for a heal sequence
|
|
type HealOpts struct {
|
|
Recursive bool `json:"recursive"`
|
|
DryRun bool `json:"dryRun"`
|
|
}
|
|
|
|
// HealStartSuccess - holds information about a successfully started
|
|
// heal operation
|
|
type HealStartSuccess struct {
|
|
ClientToken string `json:"clientToken"`
|
|
ClientAddress string `json:"clientAddress"`
|
|
StartTime time.Time `json:"startTime"`
|
|
}
|
|
|
|
// HealTaskStatus - status struct for a heal task
|
|
type HealTaskStatus struct {
|
|
Summary string `json:"summary"`
|
|
FailureDetail string `json:"detail"`
|
|
StartTime time.Time `json:"startTime"`
|
|
HealSettings HealOpts `json:"settings"`
|
|
NumDisks int `json:"numDisks"`
|
|
|
|
Items []HealResultItem `json:"items,omitempty"`
|
|
}
|
|
|
|
// HealItemType - specify the type of heal operation in a healing
|
|
// result
|
|
type HealItemType string
|
|
|
|
// HealItemType constants
|
|
const (
|
|
HealItemMetadata HealItemType = "metadata"
|
|
HealItemBucket = "bucket"
|
|
HealItemBucketMetadata = "bucket-metadata"
|
|
HealItemObject = "object"
|
|
)
|
|
|
|
// Drive state constants
|
|
const (
|
|
DriveStateOk string = "ok"
|
|
DriveStateOffline = "offline"
|
|
DriveStateCorrupt = "corrupt"
|
|
DriveStateMissing = "missing"
|
|
)
|
|
|
|
// HealDriveInfo - struct for an individual drive info item.
|
|
type HealDriveInfo struct {
|
|
UUID string `json:"uuid"`
|
|
Endpoint string `json:"endpoint"`
|
|
State string `json:"state"`
|
|
}
|
|
|
|
// HealResultItem - struct for an individual heal result item
|
|
type HealResultItem struct {
|
|
ResultIndex int64 `json:"resultId"`
|
|
Type HealItemType `json:"type"`
|
|
Bucket string `json:"bucket"`
|
|
Object string `json:"object"`
|
|
Detail string `json:"detail"`
|
|
ParityBlocks int `json:"parityBlocks,omitempty"`
|
|
DataBlocks int `json:"dataBlocks,omitempty"`
|
|
DiskCount int `json:"diskCount"`
|
|
SetCount int `json:"setCount"`
|
|
// below slices are from drive info.
|
|
Before struct {
|
|
Drives []HealDriveInfo `json:"drives"`
|
|
} `json:"before"`
|
|
After struct {
|
|
Drives []HealDriveInfo `json:"drives"`
|
|
} `json:"after"`
|
|
ObjectSize int64 `json:"objectSize"`
|
|
}
|
|
|
|
// GetMissingCounts - returns the number of missing disks before
|
|
// and after heal
|
|
func (hri *HealResultItem) GetMissingCounts() (b, a int) {
|
|
if hri == nil {
|
|
return
|
|
}
|
|
for _, v := range hri.Before.Drives {
|
|
if v.State == DriveStateMissing {
|
|
b++
|
|
}
|
|
}
|
|
for _, v := range hri.After.Drives {
|
|
if v.State == DriveStateMissing {
|
|
a++
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// GetOfflineCounts - returns the number of offline disks before
|
|
// and after heal
|
|
func (hri *HealResultItem) GetOfflineCounts() (b, a int) {
|
|
if hri == nil {
|
|
return
|
|
}
|
|
for _, v := range hri.Before.Drives {
|
|
if v.State == DriveStateOffline {
|
|
b++
|
|
}
|
|
}
|
|
for _, v := range hri.After.Drives {
|
|
if v.State == DriveStateOffline {
|
|
a++
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// GetCorruptedCounts - returns the number of corrupted disks before
|
|
// and after heal
|
|
func (hri *HealResultItem) GetCorruptedCounts() (b, a int) {
|
|
if hri == nil {
|
|
return
|
|
}
|
|
for _, v := range hri.Before.Drives {
|
|
if v.State == DriveStateCorrupt {
|
|
b++
|
|
}
|
|
}
|
|
for _, v := range hri.After.Drives {
|
|
if v.State == DriveStateCorrupt {
|
|
a++
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// GetOnlineCounts - returns the number of online disks before
|
|
// and after heal
|
|
func (hri *HealResultItem) GetOnlineCounts() (b, a int) {
|
|
if hri == nil {
|
|
return
|
|
}
|
|
for _, v := range hri.Before.Drives {
|
|
if v.State == DriveStateOk {
|
|
b++
|
|
}
|
|
}
|
|
for _, v := range hri.After.Drives {
|
|
if v.State == DriveStateOk {
|
|
a++
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Heal - API endpoint to start heal and to fetch status
|
|
func (adm *AdminClient) Heal(bucket, prefix string, healOpts HealOpts,
|
|
clientToken string, forceStart bool) (
|
|
healStart HealStartSuccess, healTaskStatus HealTaskStatus, err error) {
|
|
|
|
body, err := json.Marshal(healOpts)
|
|
if err != nil {
|
|
return healStart, healTaskStatus, err
|
|
}
|
|
|
|
path := fmt.Sprintf("/v1/heal/%s", bucket)
|
|
if bucket != "" && prefix != "" {
|
|
path += "/" + prefix
|
|
}
|
|
|
|
// execute POST request to heal api
|
|
queryVals := make(url.Values)
|
|
var contentBody io.Reader
|
|
if clientToken != "" {
|
|
queryVals.Set("clientToken", clientToken)
|
|
body = []byte{}
|
|
} else {
|
|
// Set a body only if clientToken is not given
|
|
contentBody = bytes.NewReader(body)
|
|
}
|
|
if forceStart {
|
|
queryVals.Set("forceStart", "true")
|
|
}
|
|
|
|
resp, err := adm.executeMethod("POST", requestData{
|
|
relPath: path,
|
|
contentBody: contentBody,
|
|
contentSHA256Bytes: sum256(body),
|
|
queryValues: queryVals,
|
|
})
|
|
defer closeResponse(resp)
|
|
if err != nil {
|
|
return healStart, healTaskStatus, err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return healStart, healTaskStatus, httpRespToErrorResponse(resp)
|
|
}
|
|
|
|
respBytes, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return healStart, healTaskStatus, err
|
|
}
|
|
|
|
// Was it a status request?
|
|
if clientToken == "" {
|
|
err = json.Unmarshal(respBytes, &healStart)
|
|
} else {
|
|
err = json.Unmarshal(respBytes, &healTaskStatus)
|
|
}
|
|
return healStart, healTaskStatus, err
|
|
}
|