mirror of https://github.com/minio/minio.git

committed by
GitHub

No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 233 additions and 0 deletions
-
5docs/debugging/reorder-disks/go.mod
-
2docs/debugging/reorder-disks/go.sum
-
226docs/debugging/reorder-disks/main.go
@ -0,0 +1,5 @@ |
|||||
|
module github.com/minio/minio/docs/debugging/reorder-disks |
||||
|
|
||||
|
go 1.19 |
||||
|
|
||||
|
require github.com/minio/pkg v1.6.4 |
@ -0,0 +1,2 @@ |
|||||
|
github.com/minio/pkg v1.6.4 h1:k6XlhyJ8zOn90PI4csuMeePx7BQrcX1jorKriR5J2fo= |
||||
|
github.com/minio/pkg v1.6.4/go.mod h1:0iX1IuJGSCnMvIvrEJauk1GgQSX9JdU6Kh0P3EQRGkI= |
@ -0,0 +1,226 @@ |
|||||
|
// Copyright (c) 2015-2023 MinIO, Inc.
|
||||
|
//
|
||||
|
// This file is part of MinIO Object Storage stack
|
||||
|
//
|
||||
|
// This program is free software: you can redistribute it and/or modify
|
||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||
|
// (at your option) any later version.
|
||||
|
//
|
||||
|
// This program is distributed in the hope that it will be useful
|
||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
|
// GNU Affero General Public License for more details.
|
||||
|
//
|
||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
|
||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"bufio" |
||||
|
"encoding/json" |
||||
|
"errors" |
||||
|
"flag" |
||||
|
"fmt" |
||||
|
"io/ioutil" |
||||
|
"log" |
||||
|
"net/url" |
||||
|
"os" |
||||
|
"path/filepath" |
||||
|
"strings" |
||||
|
"syscall" |
||||
|
|
||||
|
"github.com/minio/pkg/ellipses" |
||||
|
) |
||||
|
|
||||
|
type xl struct { |
||||
|
This string `json:"this"` |
||||
|
Sets [][]string `json:"sets"` |
||||
|
} |
||||
|
|
||||
|
type format struct { |
||||
|
ID string `json:"id"` |
||||
|
XL xl `json:"xl"` |
||||
|
} |
||||
|
|
||||
|
func getMountMap() (map[string]string, error) { |
||||
|
result := make(map[string]string) |
||||
|
|
||||
|
mountInfo, err := os.Open("/proc/self/mountinfo") |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
defer mountInfo.Close() |
||||
|
|
||||
|
scanner := bufio.NewScanner(mountInfo) |
||||
|
for scanner.Scan() { |
||||
|
s := strings.Split(scanner.Text(), " ") |
||||
|
if len(s) != 11 { |
||||
|
return nil, errors.New("unsupport /proc/self/mountinfo format") |
||||
|
} |
||||
|
result[s[2]] = s[9] |
||||
|
} |
||||
|
|
||||
|
if err := scanner.Err(); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return result, nil |
||||
|
} |
||||
|
|
||||
|
func getDiskUUIDMap() (map[string]string, error) { |
||||
|
result := make(map[string]string) |
||||
|
err := filepath.Walk("/dev/disk/by-uuid/", |
||||
|
func(path string, info os.FileInfo, err error) error { |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
realPath, err := filepath.EvalSymlinks(path) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
result[realPath] = strings.TrimPrefix(path, "/dev/disk/by-uuid/") |
||||
|
return nil |
||||
|
}) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return result, nil |
||||
|
} |
||||
|
|
||||
|
type localDisk struct { |
||||
|
index int |
||||
|
path string |
||||
|
} |
||||
|
|
||||
|
func getMajorMinor(path string) (string, error) { |
||||
|
var stat syscall.Stat_t |
||||
|
if err := syscall.Stat(path, &stat); err != nil { |
||||
|
return "", err |
||||
|
} |
||||
|
|
||||
|
major := (stat.Dev & 0x00000000000fff00) >> 8 |
||||
|
major |= (stat.Dev & 0xfffff00000000000) >> 32 |
||||
|
minor := (stat.Dev & 0x00000000000000ff) >> 0 |
||||
|
minor |= (stat.Dev & 0x00000ffffff00000) >> 12 |
||||
|
|
||||
|
return fmt.Sprintf("%d:%d", major, minor), nil |
||||
|
} |
||||
|
|
||||
|
func filterLocalDisks(node, args string) ([]localDisk, error) { |
||||
|
var result []localDisk |
||||
|
|
||||
|
argP, err := ellipses.FindEllipsesPatterns(args) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
exp := argP.Expand() |
||||
|
|
||||
|
if node == "" { |
||||
|
for index, e := range exp { |
||||
|
result = append(result, localDisk{index: index, path: strings.Join(e, "")}) |
||||
|
} |
||||
|
} else { |
||||
|
for index, e := range exp { |
||||
|
u, err := url.Parse(strings.Join(e, "")) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
if strings.Contains(u.Host, node) { |
||||
|
result = append(result, localDisk{index: index, path: u.Path}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return result, nil |
||||
|
} |
||||
|
|
||||
|
func getFormatJSON(path string) (format, error) { |
||||
|
formatJSON, err := ioutil.ReadFile(filepath.Join(path, ".minio.sys/format.json")) |
||||
|
if err != nil { |
||||
|
return format{}, err |
||||
|
} |
||||
|
var f format |
||||
|
err = json.Unmarshal(formatJSON, &f) |
||||
|
if err != nil { |
||||
|
return format{}, err |
||||
|
} |
||||
|
|
||||
|
return f, nil |
||||
|
} |
||||
|
|
||||
|
func getDiskLocation(f format) (string, error) { |
||||
|
for i, set := range f.XL.Sets { |
||||
|
for j, disk := range set { |
||||
|
if disk == f.XL.This { |
||||
|
return fmt.Sprintf("%d-%d", i, j), nil |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return "", errors.New("disk not found") |
||||
|
} |
||||
|
|
||||
|
func main() { |
||||
|
|
||||
|
var node, args string |
||||
|
|
||||
|
flag.StringVar(&node, "filter-node", "", "Filter disks which belong to the given node") |
||||
|
flag.StringVar(&args, "args", "", "arguments passed to MinIO server") |
||||
|
|
||||
|
flag.Parse() |
||||
|
|
||||
|
localDisks, err := filterLocalDisks(node, args) |
||||
|
if err != nil { |
||||
|
log.Fatal(err) |
||||
|
} |
||||
|
|
||||
|
if len(localDisks) == 0 { |
||||
|
log.Fatal("Fix --filter-node or/and --args to select local disks.") |
||||
|
} |
||||
|
|
||||
|
format, err := getFormatJSON(localDisks[0].path) |
||||
|
if err != nil { |
||||
|
log.Fatal(err) |
||||
|
} |
||||
|
|
||||
|
setSize := len(format.XL.Sets[0]) |
||||
|
|
||||
|
expectedDisksName := make(map[string]string) |
||||
|
actualDisksName := make(map[string]string) |
||||
|
|
||||
|
// Calculate the set/disk index
|
||||
|
for _, disk := range localDisks { |
||||
|
expectedDisksName[fmt.Sprintf("%d-%d", disk.index/setSize, disk.index%setSize)] = disk.path |
||||
|
format, err := getFormatJSON(disk.path) |
||||
|
if err != nil { |
||||
|
log.Fatal(err) |
||||
|
} |
||||
|
foundDiskLoc, err := getDiskLocation(format) |
||||
|
if err != nil { |
||||
|
log.Fatal(err) |
||||
|
} |
||||
|
actualDisksName[foundDiskLoc] = disk.path |
||||
|
} |
||||
|
|
||||
|
uuidMap, err := getDiskUUIDMap() |
||||
|
if err != nil { |
||||
|
log.Fatal(err) |
||||
|
} |
||||
|
|
||||
|
mountMap, err := getMountMap() |
||||
|
if err != nil { |
||||
|
log.Fatal(err) |
||||
|
} |
||||
|
|
||||
|
for loc, expectedDiskName := range expectedDisksName { |
||||
|
diskName := actualDisksName[loc] |
||||
|
mami, err := getMajorMinor(diskName) |
||||
|
if err != nil { |
||||
|
log.Fatal(err) |
||||
|
} |
||||
|
devName := mountMap[mami] |
||||
|
uuid := uuidMap[devName] |
||||
|
fmt.Printf("UID=%s\t%s\txfs\tdefaults,noatime\t0\t2\n", uuid, expectedDiskName) |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue