// 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 }