| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944 |
- // Copyright 2019 Yunion
- //
- // 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 disktool
- import (
- "fmt"
- "math"
- "strconv"
- "strings"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/utils"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- raiddrivers "yunion.io/x/onecloud/pkg/baremetal/utils/raid/drivers"
- "yunion.io/x/onecloud/pkg/cloudcommon/types"
- "yunion.io/x/onecloud/pkg/compute/baremetal"
- fileutils "yunion.io/x/onecloud/pkg/util/fileutils2"
- "yunion.io/x/onecloud/pkg/util/ssh"
- "yunion.io/x/onecloud/pkg/util/sysutils"
- )
- const (
- // MB_SECTORS = 2048 // 1MiB = 2014 sectors
- GPT_SECTORS = 34
- RAID_DRVIER = "raid"
- NONRAID_DRIVER = "nonraid"
- PCIE_DRIVER = "pcie"
- LABEL_MSDOS = "msdos"
- LABEL_GPT = "gpt"
- )
- type Partition struct {
- disk *DiskPartitions
- index int
- bootable bool
- start int64
- end int64
- count int64
- diskType string
- fs string
- dev string
- }
- func NewPartition(
- disk *DiskPartitions,
- index int, bootable bool,
- start int64, end int64, count int64,
- diskType string, fs string, dev string,
- ) *Partition {
- return &Partition{
- disk: disk,
- index: index,
- bootable: bootable,
- start: start,
- end: end,
- count: count,
- diskType: diskType,
- fs: fs,
- dev: dev,
- }
- }
- func (p *Partition) GetStart() int64 {
- return p.start
- }
- func (p *Partition) GetEnd() int64 {
- return p.end
- }
- func (p *Partition) GetDev() string {
- return p.dev
- }
- func (p *Partition) String() string {
- bootStr := ""
- if p.bootable {
- bootStr = " boot"
- }
- return fmt.Sprintf("%s %d %d %s %s%s", p.dev, p.start, p.end, p.diskType, p.fs, bootStr)
- }
- func (p *Partition) Format(fs string, uuid string) error {
- cmd := []string{}
- cmdUUID := []string{}
- switch fs {
- case "swap":
- cmd = []string{"mkswap", "-U", uuid}
- case "ext2":
- cmd = []string{"mkfs.ext2"}
- cmdUUID = []string{"tune2fs", "-U", uuid}
- case "ext3":
- cmd = []string{"mkfs.ext3"}
- cmdUUID = []string{"tune2fs", "-U", uuid}
- case "ext4":
- // for baremetal, force 64bit support large disks
- cmd = []string{"mkfs.ext4", "-O", "64bit", "-E", "lazy_itable_init=1", "-T", "largefile"}
- cmdUUID = []string{"tune2fs", "-U", uuid}
- case "ext4dev":
- cmd = []string{"mkfs.ext4dev", "-E", "lazy_itable_init=1"}
- cmdUUID = []string{"tune2fs", "-U", uuid}
- case "xfs":
- cmd = []string{"mkfs.xfs", "-f", "-m", "crc=0", "-i", "projid32bit=0", "-n", "ftype=0"}
- cmdUUID = []string{"PATH=/bin:/sbin:/usr/bin:/usr/sbin /usr/sbin/xfs_admin", "-U", uuid}
- default:
- return fmt.Errorf("Unsupported filesystem %s", fs)
- }
- cmd = append(cmd, p.dev)
- cmds := []string{strings.Join(cmd, " ")}
- if len(cmdUUID) != 0 {
- cmdUUID = append(cmdUUID, p.dev)
- cmds = append(cmds, strings.Join(cmdUUID, " "))
- }
- _, err := p.Run(cmds...)
- return err
- }
- func (p *Partition) Fsck() error {
- if p.fs == "" {
- return fmt.Errorf("filesystem is empty")
- }
- cmd := []string{}
- if strings.HasPrefix(p.fs, "ext") {
- cmd = []string{fmt.Sprintf("fsck.%s", p.fs), "-f", "-p"}
- } else if p.fs == "xfs" {
- cmd = []string{"fsck.xfs"}
- } else {
- return fmt.Errorf("Unsupported fsck filesystem: %s", p.fs)
- }
- cmd = append(cmd, p.dev)
- _, err := p.Run(strings.Join(cmd, " "))
- return err
- }
- func (p *Partition) Run(cmds ...string) ([]string, error) {
- return p.disk.Run(cmds...)
- }
- func (p *Partition) ResizeFs() error {
- if p.fs == "" {
- return nil
- }
- cmd := []string{}
- if strings.HasPrefix(p.fs, "linux-swap") {
- cmd = []string{"mkswap", p.dev}
- } else if strings.HasPrefix(p.fs, "ext") {
- if err := p.Fsck(); err != nil {
- log.Warningf("FSCK error: %v", err)
- }
- cmd = []string{"resize2fs", p.dev}
- } else if p.fs == "xfs" {
- return p.ResizeXfs()
- }
- if len(cmd) == 0 {
- return nil
- }
- _, err := p.Run(strings.Join(cmd, " "))
- return err
- }
- func (p *Partition) ResizeXfs() error {
- mountPath := fmt.Sprintf("/tmp/%s", strings.Replace(p.dev, "/", "", -1))
- cmds := []string{
- fmt.Sprintf("mkdir -p %s", mountPath),
- fmt.Sprintf("mount -t xfs %s %s", p.dev, mountPath),
- fmt.Sprintf("xfs_growfs -d %s", mountPath),
- fmt.Sprintf("umount %s", mountPath),
- fmt.Sprintf("rm -fr %s", mountPath),
- }
- _, err := p.Run(cmds...)
- if err != nil {
- log.Errorf("Resize xfs error: %v", err)
- cmds = []string{
- fmt.Sprintf("umount %s", mountPath),
- fmt.Sprintf("rm -rf %s", mountPath),
- }
- _, err = p.Run(cmds...)
- if err != nil {
- log.Errorf("Umount error: %v", err)
- return err
- }
- }
- return nil
- }
- func (p *Partition) GetSizeMB() (int64, error) {
- log.Infof("GetSizeMB: start %d, end: %d, count: %d", p.start, p.end, p.count)
- if p.count != (p.end - p.start + 1) {
- return 0, fmt.Errorf("Count(%d) != End(%d)-Start(%d)+1", p.count, p.end, p.start)
- }
- return p.count * 512 / 1024 / 1024, nil
- }
- func (p *Partition) GetDisk() *DiskPartitions {
- return p.disk
- }
- type DiskPartitions struct {
- driver string
- adapter int
- raidConfig string
- sizeMB int64 // MB
- diskType string // ssd, rotate, hybird
- tool *PartitionTool
- dev string
- devName string
- sectors int64
- blockSize int64
- rotate bool
- desc string
- label string
- pciPath string
- partitions []*Partition
- }
- func newDiskPartitions(driver string, adapter int, raidConfig string, sizeMB int64, blockSize int64, diskType string, softRaidIdx *int, tool *PartitionTool) *DiskPartitions {
- ps := new(DiskPartitions)
- ps.driver = driver
- ps.adapter = adapter
- ps.raidConfig = raidConfig
- ps.sizeMB = sizeMB
- ps.tool = tool
- ps.blockSize = blockSize
- ps.diskType = diskType
- ps.partitions = make([]*Partition, 0)
- // soft raid, mdadm
- if softRaidIdx != nil {
- ps.GetMdadmInfo(softRaidIdx)
- }
- return ps
- }
- func (ps *DiskPartitions) GetMdadmInfo(softRaidIdx *int) {
- devLinkName := fmt.Sprintf("/dev/md/md%d", *softRaidIdx)
- devLinkNickname := fmt.Sprintf("/dev/md/md%d_0", *softRaidIdx)
- cmd := fmt.Sprintf("readlink -f $(test -e %s && echo %s || echo %s)", devLinkName, devLinkName, devLinkNickname)
- out, err := ps.tool.Run(cmd)
- if err != nil || len(out) == 0 {
- log.Errorf("failed readlink of %s: %s", devLinkName, err)
- return
- }
- ps.dev = strings.TrimSpace(out[0])
- ps.devName = ps.dev
- uuid, sectors := ps.tool.GetMdadmUuidAndSector(ps.dev)
- ps.pciPath = uuid
- ps.sectors = sectors
- ps.blockSize = 512
- }
- func (p *DiskPartitions) IsRaidDriver() bool {
- return utils.IsInStringArray(p.driver, []string{
- baremetal.DISK_DRIVER_MEGARAID,
- baremetal.DISK_DRIVER_HPSARAID,
- baremetal.DISK_DRIVER_MARVELRAID,
- baremetal.DISK_DRIVER_MPT2SAS,
- })
- }
- func (p *DiskPartitions) GetDev() string {
- return p.dev
- }
- func getPCIPathPrefix(input string) string {
- input = strings.TrimSpace(input)
- parts := strings.Split(input, "/")
- return strings.Join(parts[0:len(parts)-2], "/")
- }
- func (p *DiskPartitions) SetInfo(info *types.SDiskInfo) (*DiskPartitions, error) {
- p.dev = fmt.Sprintf("/dev/%s", info.Dev)
- p.devName = info.Dev
- p.sectors = info.Sector
- p.desc = info.ModuleInfo
- p.blockSize = info.Block
- if p.blockSize == 4096 {
- p.sectors = (p.sectors >> 3)
- }
- // /sys/block/sda => /sys/devices/pci0000:00/0000:00:04.0/virtio1/host0/target0:0:0/0:0:0:0/block/sda
- // /sys/block/nvme1n1 => /sys/devices/pci0000:00/0000:00:1a.0/0000:03:00.0/nvme/nvme1/nvme1n1
- outputs, err := p.Run(fmt.Sprintf("readlink -f /sys/block/%s", info.Dev))
- if err != nil {
- return nil, errors.Wrapf(err, "failed to read device %s pci path", p.dev)
- }
- if len(outputs) > 0 {
- p.pciPath = getPCIPathPrefix(outputs[0])
- }
- return p, nil
- }
- func (p *DiskPartitions) ReInitInfo() error {
- cmd := "/lib/mos/lsdisk"
- lines, err := p.tool.Run(cmd)
- if err != nil {
- return errors.Wrapf(err, "Disk %#v reset info list disk", p)
- }
- for _, disk := range sysutils.ParseDiskInfo(lines, p.driver) {
- if disk.Dev == p.GetDevName() {
- if _, err := p.SetInfo(disk); err != nil {
- return errors.Wrapf(err, "set info of disk %v", disk)
- }
- }
- }
- return p.RetrievePartitionInfo()
- }
- func (ps *DiskPartitions) MBSectors() int64 {
- return int64(1024 * 1024 / ps.blockSize)
- }
- func (ps *DiskPartitions) String() string {
- return fmt.Sprintf("%s %d %s", ps.devName, ps.sizeMB, ps.driver)
- }
- func (ps *DiskPartitions) DebugString() string {
- partitionsStr := []string{}
- for _, p := range ps.partitions {
- partitionsStr = append(partitionsStr, fmt.Sprintf("{%#v}", *p))
- }
- return fmt.Sprintf("driver: %s, dev: %s, sectors: %d, partitions: %#v", ps.driver, ps.dev, ps.sectors, partitionsStr)
- }
- func (ps *DiskPartitions) IsReady() bool {
- if ps.dev == "" {
- return false
- }
- return true
- }
- func (ps *DiskPartitions) GetDevName() string {
- devName := ps.devName
- if ps.raidConfig == baremetal.DISK_CONF_NONE {
- return devName
- }
- raidDrv, err := raiddrivers.GetDriverWithInit(ps.driver, ps.tool.runner.Term())
- if err != nil {
- log.Errorf("Failed to find %s raid driver: %v", ps.driver, err)
- return devName
- }
- // find first raid adapter logical volume
- lv, err := raiddrivers.GetFirstLogicalVolume(raidDrv, ps.adapter)
- if err != nil {
- log.Errorf("Failed to find raid %s adapter %d first logical volume: %v", raidDrv.GetName(), ps.adapter, err)
- return devName
- }
- if len(lv.BlockDev) == 0 {
- log.Warningf("Raid %s adapter %d first logical volume block device is empty", raidDrv.GetName(), ps.adapter)
- return devName
- }
- devName = strings.TrimLeft(lv.BlockDev, "/dev/")
- return devName
- }
- func (ps *DiskPartitions) GetPCIPath() string {
- return ps.pciPath
- }
- func (ps *DiskPartitions) RetrievePartitionInfo() error {
- ps.partitions = make([]*Partition, 0)
- cmd := []string{"parted", "-s", ps.dev, "--", "unit", "s", "print"}
- ret, err := ps.Run(strings.Join(cmd, " "))
- if err != nil {
- return err
- }
- parts, label := fileutils.ParseDiskPartitions(ps.dev, ret)
- ps.label = label
- for _, part := range parts {
- ps.addPartition(part)
- }
- return nil
- }
- func (ps *DiskPartitions) addPartition(p fileutils.Partition) {
- part := NewPartition(ps, p.Index, p.Bootable, p.Start, p.End, p.Count, p.DiskType, p.Fs, p.DevName)
- ps.partitions = append(ps.partitions, part)
- }
- func (ps *DiskPartitions) GPTEndSector() int64 {
- return ps.sectors - int64(GPT_SECTORS)
- }
- func (ps *DiskPartitions) FsToTypeCode(fs string) string {
- if strings.Contains(fs, "swap") {
- return "8200"
- } else if strings.HasPrefix(fs, "ntfs") || strings.HasPrefix(fs, "fat") {
- return "0700"
- }
- return "8300"
- }
- func (ps *DiskPartitions) doResize(dev string, cmd string) error {
- cmds := []string{}
- cmds = append(cmds, cmd)
- cmds = append(cmds, fmt.Sprintf("hdparm -f %s", dev))
- cmds = append(cmds, fmt.Sprintf("hdparm -z %s", dev))
- _, err := ps.tool.Run(cmds...)
- if err != nil {
- return err
- }
- return ps.RetrievePartitionInfo()
- }
- func (ps *DiskPartitions) GetPartitions() []*Partition {
- return ps.partitions
- }
- func (ps *DiskPartitions) ResizePartition(offsetMB int64) error {
- if len(ps.partitions) == 0 {
- return fmt.Errorf("ResizePartitions error: total %d partitions", len(ps.partitions))
- }
- var cmd string
- if ps.label == LABEL_MSDOS {
- part := ps.partitions[len(ps.partitions)-1]
- if part.diskType == "extended" {
- log.Infof("Find last partition an empty extended partition, removed it")
- cmd := fmt.Sprintf("parted -a none -s %s -- rm %d", part.disk.dev, part.index)
- if err := ps.doResize(part.disk.dev, cmd); err != nil {
- return fmt.Errorf("Fail to remove empty extended partition: %v", err)
- }
- }
- }
- part := ps.partitions[len(ps.partitions)-1]
- var end int64
- if offsetMB <= 0 {
- end = ps.GPTEndSector()
- } else {
- end = offsetMB*ps.MBSectors() - 1
- if end > ps.GPTEndSector() {
- end = ps.GPTEndSector()
- }
- }
- if end < part.end {
- log.Warningf("Cannot reduce size %d %d, no need to resize", end, part.end)
- end = part.end
- }
- if ps.label == LABEL_MSDOS {
- if part.diskType == "logical" {
- extendIdx := -1
- for i := range ps.partitions {
- if ps.partitions[i].diskType == "extended" {
- log.Infof("Find extended at %d", i)
- extendIdx = i
- break
- }
- }
- if extendIdx < 0 {
- return fmt.Errorf("To resize logical parition, but fail to find extend partiton")
- }
- cmd = fmt.Sprintf("parted -a none -s %s -- unit s", part.disk.dev)
- partsLen := len(ps.partitions)
- for i := partsLen - 1; i > extendIdx-1; i-- {
- cmd = fmt.Sprintf("%s rm %d", cmd, ps.partitions[i].index)
- }
- for i := extendIdx; i < partsLen; i++ {
- cmdLet := fmt.Sprintf("mkpart %s %d", ps.partitions[i].diskType, ps.partitions[i].start)
- if i == extendIdx {
- cmdLet = fmt.Sprintf("%s %d", cmdLet, ps.GPTEndSector())
- } else if i == (len(ps.partitions) - 1) {
- cmdLet = fmt.Sprintf("%s %d", cmdLet, end)
- } else {
- cmdLet = fmt.Sprintf("%s %d", cmdLet, ps.partitions[i].end)
- }
- cmd = fmt.Sprintf("%s %s", cmd, cmdLet)
- }
- } else {
- cmd = fmt.Sprintf("parted -a none -s %s -- unit s rm %d mkpart %s", part.disk.dev, part.index, part.diskType)
- cmd = fmt.Sprintf("%s %d %d", cmd, part.start, end)
- if part.bootable {
- cmd = fmt.Sprintf("%s set %d boot on", cmd, part.index)
- }
- }
- } else {
- // gpt
- cmd = fmt.Sprintf("sgdisk --set-alignment=1 --delete=%d", part.index)
- cmd = fmt.Sprintf("%s --new=%d:%d:%d", cmd, part.index, part.start, end)
- if len(part.diskType) != 0 {
- cmd = fmt.Sprintf("%s --change-name=%d:\"%s\"", cmd, part.index, part.diskType)
- }
- if len(part.fs) != 0 {
- cmd = fmt.Sprintf("%s --typecode=%d:%s", cmd, part.index, ps.FsToTypeCode(part.fs))
- }
- cmd = fmt.Sprintf("%s %s", cmd, ps.dev)
- }
- log.Infof("Resize cmd: %s", cmd)
- if err := ps.doResize(part.disk.dev, cmd); err != nil {
- return err
- }
- return ps.partitions[len(ps.partitions)-1].ResizeFs()
- }
- func (ps *DiskPartitions) IsSpaceAvailable(sizeMB int64) bool {
- start := ps.getNextPartStart()
- freeSect := ps.GPTEndSector() - start
- if sizeMB <= 0 {
- sizeMB = 1
- }
- reqSect := sizeMB * ps.MBSectors()
- if reqSect > freeSect {
- log.Warningf("No space require %d(%d) left %d", reqSect, sizeMB, freeSect)
- return false
- }
- return true
- }
- func (ps *DiskPartitions) MakeLabel() error {
- label := LABEL_GPT
- if ps.sizeMB <= 1024*1024*2 {
- label = LABEL_MSDOS
- }
- return ps.makeLabel(label)
- }
- func (ps *DiskPartitions) makeLabel(label string) error {
- ps.label = label
- cmd := fmt.Sprintf("parted -s %s -- mklabel %s", ps.dev, ps.label)
- // cmd = ['/usr/sbin/sgdisk', '-og', self.dev]
- _, err := ps.Run(cmd)
- return err
- }
- func (ps *DiskPartitions) getNextPartIndex() int {
- max := 0
- for _, part := range ps.partitions {
- if max < part.index {
- max = part.index
- }
- }
- return max + 1
- }
- func (ps *DiskPartitions) getNextPartStart() int64 {
- var start int64
- if len(ps.partitions) == 0 {
- start = ps.MBSectors() // 1MB
- } else {
- var gap int64 = 2
- lastPart := ps.partitions[len(ps.partitions)-1]
- start = ((lastPart.end + gap) / ps.MBSectors()) * ps.MBSectors()
- if start < lastPart.end+gap {
- start += ps.MBSectors()
- }
- }
- return start
- }
- func (ps *DiskPartitions) Run(cmd ...string) ([]string, error) {
- return ps.tool.Run(cmd...)
- }
- func (ps *DiskPartitions) CreatePartition(sizeMB int64, fs string, doformat bool, uuid string) error {
- if len(ps.partitions) == 0 {
- if err := ps.MakeLabel(); err != nil {
- return err
- }
- }
- start := ps.getNextPartStart()
- var end int64
- if sizeMB <= 0 {
- end = start + (ps.GPTEndSector()-start)/ps.MBSectors()*ps.MBSectors() - 1
- } else {
- end = start + sizeMB*ps.MBSectors() - 1
- }
- partIdx := ps.getNextPartIndex()
- var cmd string
- var diskType string
- if ps.label == LABEL_MSDOS {
- if partIdx < 5 {
- diskType = "primary"
- } else if partIdx < 9 {
- diskType = "logical"
- } else {
- return fmt.Errorf("Too many partitions on a MSDOS disk")
- }
- cmd = fmt.Sprintf("parted -a none -s %s -- unit s mkpart %s", ps.dev, diskType)
- if len(fs) != 0 {
- cmd = fmt.Sprintf("%s %s", cmd, fileutils.FsFormatToDiskType(fs))
- }
- cmd = fmt.Sprintf("%s %d %d", cmd, start, end)
- } else {
- cmd = fmt.Sprintf("sgdisk --set-alignment=1 --new=%d:%d:%d", partIdx, start, end)
- if len(fs) != 0 {
- cmd = fmt.Sprintf("%s --typecode=%d:%s", cmd, partIdx, ps.FsToTypeCode(fs))
- }
- cmd = fmt.Sprintf("%s %s", cmd, ps.dev)
- }
- _, err := ps.Run(cmd)
- if err != nil {
- return err
- }
- if err := ps.RetrievePartitionInfo(); err != nil {
- return fmt.Errorf("Fail to RetrievePartitionInfo: %v", err)
- }
- if fs != "" && doformat {
- err = ps.partitions[len(ps.partitions)-1].Format(fs, uuid)
- if err != nil {
- return fmt.Errorf("Fail to format partition: %v", err)
- }
- if err := ps.RetrievePartitionInfo(); err != nil {
- return fmt.Errorf("Fail to RetrievePartitionInfo: %v", err)
- }
- }
- return nil
- }
- type IPartitionRunner interface {
- Run(cmds ...string) ([]string, error)
- Term() *ssh.Client
- }
- type PartitionTool struct {
- disks []*DiskPartitions
- diskTable map[string][]*DiskPartitions
- runner IPartitionRunner
- }
- func NewPartitionTool(runner IPartitionRunner) *PartitionTool {
- return &PartitionTool{
- disks: make([]*DiskPartitions, 0),
- diskTable: make(map[string][]*DiskPartitions),
- runner: runner,
- }
- }
- func (tool *PartitionTool) DebugString() string {
- ret := []string{}
- disksString := func(disks []*DiskPartitions) []string {
- for _, disk := range disks {
- ret = append(ret, disk.DebugString())
- }
- return ret
- }
- for driver, disks := range tool.diskTable {
- s := fmt.Sprintf("(%s: %v)", driver, disksString(disks))
- ret = append(ret, s)
- }
- return strings.Join(ret, "\n")
- }
- func (tool *PartitionTool) Disks() []*DiskPartitions {
- return tool.disks
- }
- func (tool *PartitionTool) parseDiskInfo(lines []string, driver string) []*types.SDiskInfo {
- disks := sysutils.ParseDiskInfo(lines, driver)
- return disks
- }
- func (tool *PartitionTool) parseLsDisk(lines []string, driver string) {
- disks := tool.parseDiskInfo(lines, driver)
- if len(disks) == 0 {
- return
- }
- minCnt := int(math.Min(float64(len(disks)), float64(len(tool.diskTable[driver]))))
- raidSsdDiskCnt := 0
- for i := 0; i < minCnt; i++ {
- driverDisk := tool.diskTable[driver][i]
- if utils.IsInStringArray(driver, []string{NONRAID_DRIVER, PCIE_DRIVER}) {
- // find size matched disk from disks
- size := driverDisk.sizeMB
- var remoteDisk *types.SDiskInfo
- for ri := range disks {
- if disks[ri].Size == size {
- remoteDisk = disks[ri]
- // remove matched disks
- disks = append(disks[:ri], disks[ri+1:]...)
- break
- }
- }
- driverDisk.SetInfo(remoteDisk)
- } else {
- // raid disk set by order
- if driverDisk.diskType == api.DISK_TYPE_SSD {
- // find ssd matched remoteDisk
- var remoteDisk *types.SDiskInfo
- for ri := range disks {
- if !disks[ri].Rotate {
- remoteDisk = disks[ri]
- disks = append(disks[:ri], disks[ri+1:]...)
- raidSsdDiskCnt++
- break
- }
- }
- if remoteDisk == nil {
- // not found ssd disk
- remoteDisk = disks[i-raidSsdDiskCnt]
- log.Warningf("not found ssd driver disk for %#v, using remote disk %#v", driverDisk, remoteDisk)
- }
- driverDisk.SetInfo(remoteDisk)
- } else {
- driverDisk.SetInfo(disks[i-raidSsdDiskCnt])
- }
- }
- }
- }
- func (tool *PartitionTool) FetchDiskConfs(diskConfs []baremetal.DiskConfiguration) *PartitionTool {
- for _, d := range diskConfs {
- disk := newDiskPartitions(d.Driver, d.Adapter, d.RaidConfig, d.Size, d.Block, d.DiskType, d.SoftRaidIdx, tool)
- tool.disks = append(tool.disks, disk)
- isSoftRaid := d.RaidConfig != baremetal.DISK_CONF_NONE
- var key string
- if d.Driver == baremetal.DISK_DRIVER_LINUX && !isSoftRaid {
- key = NONRAID_DRIVER
- } else if d.Driver == baremetal.DISK_DRIVER_PCIE && !isSoftRaid {
- key = PCIE_DRIVER
- } else {
- key = RAID_DRVIER
- }
- if _, ok := tool.diskTable[key]; !ok {
- tool.diskTable[key] = make([]*DiskPartitions, 0)
- }
- tool.diskTable[key] = append(tool.diskTable[key], disk)
- }
- return tool
- }
- func (tool *PartitionTool) reorderRootDisk(matcher *api.BaremetalRootDiskMatcher) {
- isDiskMatch := func(disk *DiskPartitions, matcher *api.BaremetalRootDiskMatcher) bool {
- if matcher.Device != "" {
- if disk.dev == matcher.Device {
- return true
- }
- if disk.devName == matcher.Device {
- return true
- }
- }
- if matcher.SizeMB > 0 {
- if disk.sizeMB == matcher.SizeMB {
- return true
- }
- }
- if matcher.SizeMBRange != nil {
- if disk.sizeMB >= matcher.SizeMBRange.Start && disk.sizeMB <= matcher.SizeMBRange.End {
- return true
- }
- }
- if matcher.PCIPath != "" {
- if disk.pciPath == matcher.PCIPath {
- return true
- }
- }
- return false
- }
- var rootDiskStr string
- var rootDiskIdx = 0
- for idx, disk := range tool.disks {
- if isDiskMatch(disk, matcher) {
- rootDiskIdx = idx
- rootDiskStr = disk.String()
- break
- }
- }
- log.Infof("Select %d %q as root disk by matcher: %s", rootDiskIdx, rootDiskStr, jsonutils.Marshal(matcher))
- newDisks := make([]*DiskPartitions, 0)
- newDisks = append(newDisks, tool.disks[rootDiskIdx])
- for idx := range tool.disks {
- if idx == rootDiskIdx {
- continue
- }
- newDisks = append(newDisks, tool.disks[idx])
- }
- tool.disks = newDisks
- }
- func (tool *PartitionTool) IsAllDisksReady() bool {
- for idx, d := range tool.disks {
- if !d.IsReady() {
- log.Errorf("disk.%d %#v not ready", idx, d)
- return false
- }
- }
- return true
- }
- func (tool *PartitionTool) GetMdadmUuidAndSector(devPath string) (string, int64) {
- var uuid string
- var sectorsRet int64
- // get md uuid as pci path
- cmd := fmt.Sprintf("/sbin/mdadm --detail %s | grep UUID", devPath)
- output, err := tool.Run(cmd)
- if err == nil && len(output) > 0 {
- uuidSeg := output[0]
- segs := strings.SplitN(strings.TrimSpace(uuidSeg), ":", 2)
- if len(segs) == 2 {
- uuid = strings.TrimSpace(segs[1])
- }
- }
- // get block size
- cmd = fmt.Sprintf("blockdev --getsz %s 2>/dev/null || echo 0", devPath)
- output, err = tool.Run(cmd)
- if err == nil && len(output) > 0 {
- if sectors, err := strconv.ParseInt(strings.TrimSpace(output[0]), 10, 64); err == nil {
- sectorsRet = sectors
- }
- }
- return uuid, sectorsRet
- }
- func (tool *PartitionTool) RetrieveDiskInfo(rootMatcher *api.BaremetalRootDiskMatcher) error {
- for _, disk := range tool.disks {
- if baremetal.DISK_DRIVERS_SOFT_RAID.Has(disk.driver) && disk.raidConfig != baremetal.DISK_CONF_NONE {
- log.Infof("Soft raid mdadm set diskinfo dev %s", disk.dev)
- uuid, sectors := tool.GetMdadmUuidAndSector(disk.dev)
- disk.pciPath = uuid
- disk.sectors = sectors
- disk.blockSize = 512
- }
- }
- for _, driver := range []string{RAID_DRVIER, NONRAID_DRIVER, PCIE_DRIVER} {
- cmd := fmt.Sprintf("/lib/mos/lsdisk --%s", driver)
- ret, err := tool.Run(cmd)
- if err != nil {
- return err
- }
- tool.parseLsDisk(ret, driver)
- }
- // reorder tool.disks
- if rootMatcher != nil {
- tool.reorderRootDisk(rootMatcher)
- }
- return nil
- }
- func (tool *PartitionTool) RetrievePartitionInfo() error {
- for _, disk := range tool.disks {
- if err := disk.RetrievePartitionInfo(); err != nil {
- return err
- }
- }
- return nil
- }
- func (tool *PartitionTool) ResizePartition(diskIdx int, sizeMB int64) error {
- if diskIdx >= 0 && diskIdx < len(tool.disks) {
- return tool.disks[diskIdx].ResizePartition(sizeMB)
- }
- return fmt.Errorf("Invalid disk index: %d", diskIdx)
- }
- func (tool *PartitionTool) GetDisks() []*DiskPartitions {
- return tool.disks
- }
- func (tool *PartitionTool) GetRootDisk() *DiskPartitions {
- if len(tool.disks) == 0 {
- return nil
- }
- return tool.disks[0]
- }
- func (tool *PartitionTool) GetPCIEDisks() []*DiskPartitions {
- disks := make([]*DiskPartitions, 0)
- for _, disk := range tool.disks {
- if disk.driver == PCIE_DRIVER {
- disks = append(disks, disk)
- }
- }
- return disks
- }
- func (tool *PartitionTool) CreatePartition(diskIdx int, sizeMB int64, fs string, doformat bool, driver string, uuid string) error {
- disks := tool.disks
- if driver == PCIE_DRIVER {
- disks = tool.GetPCIEDisks()
- }
- if diskIdx < 0 || diskIdx >= len(disks) {
- for _, disk := range disks {
- if disk.IsSpaceAvailable(sizeMB) {
- return disk.CreatePartition(sizeMB, fs, doformat, uuid)
- }
- }
- } else {
- disk := disks[diskIdx]
- if disk.IsSpaceAvailable(sizeMB) {
- return disk.CreatePartition(sizeMB, fs, doformat, uuid)
- }
- }
- return nil
- }
- func (tool *PartitionTool) GetPartitions() []*Partition {
- parts := make([]*Partition, 0)
- for _, d := range tool.disks {
- parts = append(parts, d.partitions...)
- }
- return parts
- }
- func (tool *PartitionTool) Run(cmds ...string) ([]string, error) {
- return tool.runner.Run(cmds...)
- }
- type SSHPartitionTool struct {
- *PartitionTool
- term *ssh.Client
- }
- func newSSHPartitionTool(term *ssh.Client) *SSHPartitionTool {
- tool := &SSHPartitionTool{
- term: term,
- }
- tool.PartitionTool = NewPartitionTool(tool)
- return tool
- }
- func NewSSHPartitionTool(term *ssh.Client, layouts []baremetal.Layout, rootMatcher *api.BaremetalRootDiskMatcher) (*SSHPartitionTool, error) {
- tool := newSSHPartitionTool(term)
- tool.FetchDiskConfs(baremetal.GetDiskConfigurations(layouts))
- if err := tool.RetrieveDiskInfo(rootMatcher); err != nil {
- return nil, errors.Wrapf(err, "RetrieveDiskInfo")
- }
- return tool, nil
- }
- func (tool *SSHPartitionTool) Run(cmds ...string) ([]string, error) {
- return tool.term.Run(cmds...)
- }
- func (tool *SSHPartitionTool) Term() *ssh.Client {
- return tool.term
- }
|