storage: refactor for manual partitioning

Always store the current partition table, with free space,
in the disk block device information.
Find free space from the already loaded partition table.

This information will be used for manual disk partitioning.

Use separate mkpart and format flags

Signed-off-by: Mark D Horn <mark.d.horn@intel.com>
This commit is contained in:
Mark D Horn
2019-04-18 12:48:36 -07:00
committed by Mark Horn
parent ee6ba0e079
commit 49529add0c
4 changed files with 546 additions and 175 deletions
+1 -1
View File
@@ -211,7 +211,7 @@ func Install(rootDir string, model *model.SystemInstall, options args.Args) erro
}
// Do not overwrite File System content for pre-existing
if !ch.IsUserDefined() {
if !ch.FormatPartition {
msg := fmt.Sprintf("Skipping new file system for %s", ch.Name)
log.Debug(msg)
continue
+316 -53
View File
@@ -240,11 +240,33 @@ func (bd *BlockDevice) WritePartitionTable(legacyBios bool, wholeDisk bool) erro
var start uint64
maxFound := false
// First remove any user removed partitions
log.Debug("WritePartitionTable: remove partitions : %v", bd.removedParts)
if len(bd.removedParts) > 0 {
rmArgs := []string{
"parted",
"-a",
"optimal",
bd.GetDeviceFile(),
"unit", "MB",
"--script",
"--",
}
for _, curr := range bd.removedParts {
rmArgs = append(rmArgs, fmt.Sprintf("rm %d", curr))
}
err = cmd.RunAndLog(rmArgs...)
if err != nil {
log.Warning("Failed to remove existing partition: %v (%s)", bd.removedParts, err)
}
}
// Initialize the partition list before we add new ones
currentPartitions := bd.getPartitionList()
// Make the needed new partitions
for _, curr := range bd.Children {
log.Debug("WritePartitionTable: processing child: %v", curr)
baseArgs := []string{
"parted",
"-a",
@@ -255,7 +277,7 @@ func (bd *BlockDevice) WritePartitionTable(legacyBios bool, wholeDisk bool) erro
"--",
}
if !curr.userDefined {
if !curr.MakePartition {
log.Debug("WritePartitionTable: skipping partition %s", curr.Name)
continue
}
@@ -276,9 +298,11 @@ func (bd *BlockDevice) WritePartitionTable(legacyBios bool, wholeDisk bool) erro
size := uint64(curr.Size)
end := start + size
if !wholeDisk {
start = curr.partStart
end = curr.partEnd
start, end = bd.getPartitionStartEnd(curr.partition)
} else {
log.Debug("WritePartitionTable: WholeDisk mode")
}
log.Debug("WritePartitionTable: start: %d, end: %d", start, end)
if size < 1 {
if maxFound {
@@ -314,7 +338,7 @@ func (bd *BlockDevice) WritePartitionTable(legacyBios bool, wholeDisk bool) erro
// Get the new list of partitions
newPartitions := bd.getPartitionList()
// The current partition is new one added
curr.SetPartitionNumber(findNewPartition(currentPartitions, newPartitions).number)
curr.SetPartitionNumber(findNewPartition(currentPartitions, newPartitions).Number)
log.Debug("WritePartitionTable: Found partition number %d for %s", curr.partition, curr.Name)
start = end
@@ -338,7 +362,7 @@ func (bd *BlockDevice) WritePartitionTable(legacyBios bool, wholeDisk bool) erro
}
// Only set GUIDs on newly created partitions
if !curr.userDefined {
if !curr.MakePartition {
continue
}
@@ -383,7 +407,7 @@ func (bd *BlockDevice) WritePartitionTable(legacyBios bool, wholeDisk bool) erro
// need to set / as boot
for _, curr := range bd.Children {
// Only check for / in new partitions
if !curr.userDefined {
if !curr.MakePartition {
continue
}
@@ -421,8 +445,8 @@ func (bd *BlockDevice) WritePartitionTable(legacyBios bool, wholeDisk bool) erro
return nil
}
func (bd *BlockDevice) getPartitionList() []partedPartition {
var partitionList []partedPartition
func (bd *BlockDevice) getPartitionList() []*PartedPartition {
var partitionList []*PartedPartition
var err error
partTable := bytes.NewBuffer(nil)
@@ -450,30 +474,30 @@ func (bd *BlockDevice) getPartitionList() []partedPartition {
return partitionList
}
var partition partedPartition
for _, line := range strings.Split(partTable.String(), ";\n") {
partition := &PartedPartition{}
fields := strings.Split(line, ":")
if len(fields) == 7 {
partition.number, err = strconv.ParseUint(fields[0], 10, 64)
partition.Number, err = strconv.ParseUint(fields[0], 10, 64)
if err != nil {
log.Warning("getPartitionList: Failed to parse partition number from: %s", line)
}
partition.start, err = strconv.ParseUint(strings.TrimRight(fields[1], "B"), 10, 64)
partition.Start, err = strconv.ParseUint(strings.TrimRight(fields[1], "B"), 10, 64)
if err != nil {
log.Warning("getPartitionList: Failed to parse start position from: %s", line)
}
partition.end, err = strconv.ParseUint(strings.TrimRight(fields[2], "B"), 10, 64)
partition.End, err = strconv.ParseUint(strings.TrimRight(fields[2], "B"), 10, 64)
if err != nil {
log.Warning("getPartitionList: Failed to parse end position from: %s", line)
}
partition.size, err = strconv.ParseUint(strings.TrimRight(fields[3], "B"), 10, 64)
partition.Size, err = strconv.ParseUint(strings.TrimRight(fields[3], "B"), 10, 64)
if err != nil {
log.Warning("getPartitionList: Failed to parse partition size from: %s", line)
}
partition.fileSystem = fields[4]
partition.name = fields[5]
partition.flags = fields[6]
partition.FileSystem = fields[4]
partition.Name = fields[5]
partition.Flags = fields[6]
partitionList = append(partitionList, partition)
}
@@ -482,8 +506,8 @@ func (bd *BlockDevice) getPartitionList() []partedPartition {
return partitionList
}
func findNewPartition(currentPartitions, newPartitions []partedPartition) partedPartition {
var newPartition partedPartition
func findNewPartition(currentPartitions, newPartitions []*PartedPartition) *PartedPartition {
newPartition := &PartedPartition{}
if len(newPartitions) <= len(currentPartitions) {
log.Warning("findNewPartition: number of new partitions is not greater than the current")
return newPartition
@@ -496,7 +520,7 @@ func findNewPartition(currentPartitions, newPartitions []partedPartition) parted
for _, newPart := range newPartitions {
found := true
for _, curPart := range currentPartitions {
if curPart.number == newPart.number {
if curPart.Number == newPart.Number {
found = false
continue
}
@@ -542,29 +566,22 @@ func (bd *BlockDevice) getPartitionTable() *bytes.Buffer {
return partTable
}
func largestContiguousFreeSpace(partTable *bytes.Buffer, minSize uint64) (uint64, uint64) {
var start, end, size uint64
size = minSize - 1
func (bd *BlockDevice) getPartitionStartEnd(partNumber uint64) (uint64, uint64) {
var start, end uint64
devFile := bd.GetDeviceFile()
for _, line := range strings.Split(partTable.String(), ";\n") {
log.Debug("largestContiguousFreeSpace() line is %q", line)
if !utils.IntSliceContains([]int{BlockDeviceTypeDisk, BlockDeviceTypeLoop}, int(bd.Type)) {
log.Warning("getPartitionStartEnd() called on non-disk %q", devFile)
return start, end
}
fields := strings.Split(line, ":")
if len(fields) == 5 && fields[4] == "free" {
lineSize, err := strconv.ParseUint(strings.TrimRight(fields[3], "B"), 10, 64)
if err == nil {
if lineSize > size {
lineStart, errStart := strconv.ParseUint(strings.TrimRight(fields[1], "B"), 10, 64)
lineEnd, errEnd := strconv.ParseUint(strings.TrimRight(fields[2], "B"), 10, 64)
if errStart == nil && errEnd == nil {
start = lineStart
end = lineEnd
}
}
}
for _, part := range bd.PartTable {
if part.Number == partNumber {
return part.Start, part.End
}
}
log.Warning("getPartitionStartEnd() did not find partition %s for disk %q", partNumber, devFile)
return start, end
}
@@ -572,7 +589,7 @@ func largestContiguousFreeSpace(partTable *bytes.Buffer, minSize uint64) (uint64
// space in the partition table for the block device.
// If none found, returns {0, 0}
func (bd *BlockDevice) LargestContiguousFreeSpace(minSize uint64) (uint64, uint64) {
var start, end uint64
var start, end, size uint64
devFile := bd.GetDeviceFile()
if !utils.IntSliceContains([]int{BlockDeviceTypeDisk, BlockDeviceTypeLoop}, int(bd.Type)) {
@@ -580,14 +597,268 @@ func (bd *BlockDevice) LargestContiguousFreeSpace(minSize uint64) (uint64, uint6
return start, end
}
// Read the partition table for the device
partTable := bd.getPartitionTable()
size = minSize - 1
start, end = largestContiguousFreeSpace(partTable, minSize)
for _, part := range bd.PartTable {
if part.Number == 0 && part.FileSystem == "free" {
if part.Size > size {
start = part.Start
end = part.End
}
}
}
return start, end
}
// AddFromFreePartition reduces the free partition by the size given
// User when adding a new partition to a disk from free space
func (bd *BlockDevice) AddFromFreePartition(parted *PartedPartition, child *BlockDevice) {
var next uint64
var partitionList []*PartedPartition
devFile := bd.GetDeviceFile()
if !utils.IntSliceContains([]int{BlockDeviceTypeDisk, BlockDeviceTypeLoop}, int(bd.Type)) {
log.Warning("AddFromFreePartition() called on non-disk %q", devFile)
return
}
const (
maxPartitions = 127
)
found := false
next = 1
for !found && next < maxPartitions {
present := false
for _, partition := range bd.PartTable {
if partition.Number == next {
present = true
break
}
}
if present {
next = next + 1
} else {
found = true
}
}
if next >= maxPartitions {
log.Warning("AddFromFreePartition() could not add new partition: %v", child)
return
}
for _, partition := range bd.PartTable {
// Find the partition to update/remove
if partition.Number == parted.Number &&
partition.Start == parted.Start {
log.Debug("Found the free partition to update: %v", partition)
addPart := partition.Clone()
addPart.Number = next
addPart.End = addPart.Start + (child.Size - 1)
addPart.Size = child.Size
addPart.FileSystem = ""
log.Debug("Adding the new partition: %v", addPart)
partitionList = append(partitionList, addPart)
child.SetPartitionNumber(addPart.Number)
bd.AddChild(child)
log.Debug("Added new child partition: %v", child)
newSize := partition.Size - addPart.Size
newStart := addPart.End + 1
log.Debug("Free partition newStart: %d, newSize: %d", newStart, newSize)
if (int(partition.End) - int(newStart)) <= 0 {
log.Debug("No Free space left: %v", partition)
continue
}
if newSize > (10 * 1024 * 1024) {
newPart := partition.Clone()
newPart.Start = newStart
newPart.Size = newSize
log.Debug("Found enough free to add back: %v", newPart)
partitionList = append(partitionList, newPart)
}
continue
}
log.Debug("Not the right partition, adding back: %v", partition)
partitionList = append(partitionList, partition)
}
bd.PartTable = partitionList
for i, p := range bd.PartTable {
log.Debug("Dump of PartTable %d: %v", i, p)
}
// Consolidate neighboring free partitions
bd.consolidateFree()
for i, p := range bd.PartTable {
log.Debug("Dump of PartTable post consolidate %d: %v", i, p)
}
}
func (bd *BlockDevice) consolidateFree() {
last := &PartedPartition{}
var newPartTable []*PartedPartition
for _, part := range bd.PartTable {
log.Debug("consolidateFree() checking part %v", part)
// Found a free partition
if part.Number == 0 && part.FileSystem == "free" {
log.Debug("consolidateFree() part is free %v", part)
// And the last partition was also free, then consolidate
if last.Number == 0 && last.FileSystem == "free" {
log.Debug("consolidateFree() last is also free %v", last)
last.End = part.End
last.Size = last.Size + part.Size
continue
}
}
newPart := part.Clone()
newPartTable = append(newPartTable, newPart)
last = newPart
}
bd.PartTable = newPartTable
}
// RemovePartition remove a child from the disk and updates
// frees the space in the partition table
func (bd *BlockDevice) RemovePartition(child *BlockDevice) *PartedPartition {
log.Debug("RemovePartition() called")
var removedPartition *PartedPartition
devFile := bd.GetDeviceFile()
if !utils.IntSliceContains([]int{BlockDeviceTypeDisk, BlockDeviceTypeLoop}, int(bd.Type)) {
log.Warning("RemovePartition() called on non-disk %q", devFile)
return removedPartition
}
deleteIndex := -1
for idx, curr := range bd.Children {
if curr.Name == child.Name {
child.Parent = nil
deleteIndex = idx
break
}
}
if deleteIndex < 0 {
log.Warning("RemovePartition() fail to find (and remove) child: %v", child)
return removedPartition
}
log.Debug("RemovePartition() found child partition index to delete %d", deleteIndex)
// keep a reference to the child
delelteChild := bd.Children[deleteIndex].Clone()
// Remove the child for the block devices
bd.Children = append(bd.Children[:deleteIndex], bd.Children[deleteIndex+1:]...)
partString := devNameSuffixExp.FindString(delelteChild.Name)
partNumber, err := strconv.ParseUint(partString, 10, 64)
if err != nil {
log.Warning("RemovePartition() fail to find child partition number: %v", child)
return removedPartition
}
log.Debug("RemovePartition() Need to add partition %d to the remove list", partNumber)
for _, partition := range bd.PartTable {
// Find the partition to free/remove
if partition.Number == partNumber {
log.Debug("Found the partition to free partition: %v", partition)
partition.Number = 0
partition.FileSystem = "free"
partition.Name = ""
partition.Flags = ""
removedPartition = partition.Clone()
break
}
}
// Consolidate neighboring free partitions
bd.consolidateFree()
if !delelteChild.MakePartition {
bd.addRemovePartition(partNumber)
log.Debug("RemovePartition() Add partition to be removed %d", partNumber)
}
return removedPartition
}
// Populate the current partition table for a disk device
func (bd *BlockDevice) setPartitionTable(partTable *bytes.Buffer) {
var partitionList []*PartedPartition
devFile := bd.GetDeviceFile()
if !utils.IntSliceContains([]int{BlockDeviceTypeDisk, BlockDeviceTypeLoop}, int(bd.Type)) {
log.Warning("setPartitionTable() called on non-disk %q", devFile)
return
}
var err error
for _, line := range strings.Split(partTable.String(), ";\n") {
partition := &PartedPartition{}
log.Debug("setPartitionTable() line is %q", line)
fields := strings.Split(line, ":")
if len(fields) == 7 {
partition.Number, err = strconv.ParseUint(fields[0], 10, 64)
if err != nil {
log.Warning("setPartitionTable: Failed to parse partition number from: %s", line)
}
partition.Start, err = strconv.ParseUint(strings.TrimRight(fields[1], "B"), 10, 64)
if err != nil {
log.Warning("setPartitionTable: Failed to parse start position from: %s", line)
}
partition.End, err = strconv.ParseUint(strings.TrimRight(fields[2], "B"), 10, 64)
if err != nil {
log.Warning("setPartitionTable: Failed to parse end position from: %s", line)
}
partition.Size, err = strconv.ParseUint(strings.TrimRight(fields[3], "B"), 10, 64)
if err != nil {
log.Warning("setPartitionTable: Failed to parse partition size from: %s", line)
}
partition.FileSystem = fields[4]
partition.Name = fields[5]
partition.Flags = fields[6]
partitionList = append(partitionList, partition)
continue
}
if len(fields) == 5 && fields[4] == "free" {
partition.Number = 0 // We use 0 to special case as a free partition
partition.Start, err = strconv.ParseUint(strings.TrimRight(fields[1], "B"), 10, 64)
if err != nil {
log.Warning("setPartitionTable: Failed to parse start position from: %s", line)
}
partition.End, err = strconv.ParseUint(strings.TrimRight(fields[2], "B"), 10, 64)
if err != nil {
log.Warning("setPartitionTable: Failed to parse end position from: %s", line)
}
partition.Size, err = strconv.ParseUint(strings.TrimRight(fields[3], "B"), 10, 64)
if err != nil {
log.Warning("setPartitionTable: Failed to parse partition size from: %s", line)
}
partition.FileSystem = fields[4]
partitionList = append(partitionList, partition)
}
}
bd.PartTable = partitionList
}
// MountMetaFs mounts proc, sysfs and devfs in the target installation directory
func MountMetaFs(rootDir string) error {
err := mountProcFs(rootDir)
@@ -923,22 +1194,14 @@ type InstallTarget struct {
WholeDisk bool // Can we use the whole disk?
Removable bool // Is this removable/hotswap media?
EraseDisk bool // Are we wiping the disk? New partition table
DataLoss bool // Are we making changes which will lose data
Manual bool // Was this disk manually configured?
FreeStart uint64 // Starting position of free space
FreeEnd uint64 // Ending position of free space
}
type partedPartition struct {
number uint64 // partition number
start uint64 // starting byte location
end uint64 // ending byte location
size uint64 // size in bytes
fileSystem string // file system Type
name string // partition name
flags string // flags for partition
}
const (
// MinimumServerInstallSize is the smallest installation size in bytes for a Desktop
// MinimumServerInstallSize is the smallest installation size in bytes for a Server
MinimumServerInstallSize = 4294967296
// MinimumDesktopInstallSize is the smallest installation size in bytes for a Desktop
+208 -108
View File
@@ -22,31 +22,45 @@ import (
"github.com/clearlinux/clr-installer/utils"
)
// PartedPartition hold partition information
// Number 0 and FileSystem "free" are free spaces
type PartedPartition struct {
Number uint64 // partition number 0 indicates free space
Start uint64 // starting byte location
End uint64 // ending byte location
Size uint64 // size in bytes
FileSystem string // file system Type
Name string // partition name
Flags string // flags for partition
}
// A BlockDevice describes a block device and its partitions
type BlockDevice struct {
Name string // device name
MappedName string // mapped device name
Model string // device model
MajorMinor string // major:minor device number
PtType string // partition table type
FsType string // filesystem type
UUID string // filesystem uuid
Serial string // device serial number
MountPoint string // where the device is mounted
Label string // label for the partition; set with mkfs
Size uint64 // size of the device
Type BlockDeviceType // device type
State BlockDeviceState // device state (running, live etc)
ReadOnly bool // read-only device
RemovableDevice bool // removable device
Children []*BlockDevice // children devices/partitions
Parent *BlockDevice // Parent block device; nil for disk
userDefined bool // was this value set by user?
available bool // was it mounted the moment we loaded?
partStart uint64 // Start of the partition
partEnd uint64 // End of the partition
partition uint64 // Assigned partition for media - can't set until after mkpart
Options string // arbitrary mkfs.* options
Name string // device name
MappedName string // mapped device name
Model string // device model
MajorMinor string // major:minor device number
PtType string // partition table type
FsType string // filesystem type
UUID string // filesystem uuid
Serial string // device serial number
MountPoint string // where the device is mounted
Label string // label for the partition; set with mkfs
Size uint64 // size of the device
Type BlockDeviceType // device type
State BlockDeviceState // device state (running, live etc)
ReadOnly bool // read-only device
RemovableDevice bool // removable device
Children []*BlockDevice // children devices/partitions
Parent *BlockDevice // Parent block device; nil for disk
UserDefined bool // was this value set by user?
MakePartition bool // Do we need to make a new partition?
FormatPartition bool // Do we need to format the partition
Options string // arbitrary mkfs.* options
available bool // was it mounted the moment we loaded?
partition uint64 // Assigned partition for media - can't set until after mkpart
PartTable []*PartedPartition // Existing Disk partition table from parted
removedParts []uint64 // List of manually removed partitions
}
// Version used for reading and writing YAML
@@ -116,9 +130,9 @@ var (
avBlockDevices []*BlockDevice
lsblkBinary = "lsblk"
storageExp = regexp.MustCompile(`^([0-9]*(\.)?[0-9]*)([bkmgtp]{1}){0,1}$`)
devNameSuffixExp = regexp.MustCompile(`([0-9]*)$`)
labelExp = regexp.MustCompile(`^([[:word:]-+_]+)$`)
mountExp = regexp.MustCompile(`^(/|(/[[:word:]-+_]+)+)$`)
devNameSuffixExp = regexp.MustCompile(`([0-9]*)$`)
blockDeviceStateMap = map[BlockDeviceState]string{
BlockDeviceStateRunning: "running",
BlockDeviceStateLive: "live",
@@ -170,14 +184,17 @@ func (bd *BlockDevice) ExpandName(alias map[string]string) {
}
}
// SetPartitionNumber is sed when we add a new partition to a disk
// GetNewPartitionName returns the name with the new partition number
func (bd *BlockDevice) GetNewPartitionName(partition uint64) string {
// Replace the last set of digits with the current partition number
return devNameSuffixExp.ReplaceAllString(bd.Name, fmt.Sprintf("%d", partition))
}
// SetPartitionNumber is set when we add a new partition to a disk
// which stores the newly allocated partition number, and then corrects
// the devices partition name
func (bd *BlockDevice) SetPartitionNumber(partition uint64) {
bd.partition = partition
// Replace the last set of digits with the current partition number
bd.Name = devNameSuffixExp.ReplaceAllString(bd.Name, fmt.Sprintf("%d", partition))
}
// GetDeviceFile formats the block device's file path
@@ -239,6 +256,36 @@ func parseBlockDeviceState(bds string) (BlockDeviceState, error) {
return BlockDeviceStateUnknown, errors.Errorf("Unrecognized block device state: %s", bds)
}
func (bd *BlockDevice) findFree(size uint64) *PartedPartition {
var freePart *PartedPartition
for _, part := range bd.PartTable {
if part.Number == 0 && part.FileSystem == "free" {
if part.Size >= size {
freePart = part.Clone()
break
}
}
}
return freePart
}
// Clone creates a copies a PartedPartition
func (part *PartedPartition) Clone() *PartedPartition {
clone := &PartedPartition{
Number: part.Number,
Start: part.Start,
End: part.End,
Size: part.Size,
FileSystem: part.FileSystem,
Name: part.Name,
Flags: part.Flags,
}
return clone
}
// Clone creates a copies a BlockDevice and its children
func (bd *BlockDevice) Clone() *BlockDevice {
clone := &BlockDevice{
@@ -257,11 +304,13 @@ func (bd *BlockDevice) Clone() *BlockDevice {
ReadOnly: bd.ReadOnly,
RemovableDevice: bd.RemovableDevice,
Parent: bd.Parent,
userDefined: bd.userDefined,
UserDefined: bd.UserDefined,
MakePartition: bd.MakePartition,
FormatPartition: bd.FormatPartition,
available: bd.available,
partStart: bd.partStart,
partEnd: bd.partEnd,
partition: bd.partition,
PartTable: bd.PartTable,
removedParts: bd.removedParts,
}
clone.Children = []*BlockDevice{}
@@ -279,7 +328,7 @@ func (bd *BlockDevice) Clone() *BlockDevice {
// IsUserDefined returns true if the configuration was interactively
// defined by the user
func (bd *BlockDevice) IsUserDefined() bool {
return bd.userDefined
return bd.UserDefined
}
// IsAvailable returns true if the media is not a installer media, returns false otherwise
@@ -404,6 +453,11 @@ func (bd *BlockDevice) RemoveChild(child *BlockDevice) {
}
}
// addRemovePartition adds a partition to the list to be removed
func (bd *BlockDevice) addRemovePartition(part uint64) {
bd.removedParts = append(bd.removedParts, part)
}
// AddChild adds a partition to a disk block device
func (bd *BlockDevice) AddChild(child *BlockDevice) {
if bd.Children == nil {
@@ -422,8 +476,13 @@ func (bd *BlockDevice) AddChild(child *BlockDevice) {
}
if child.Name == "" {
child.Name = fmt.Sprintf("%s%s%d", bd.Name, partPrefix, len(bd.Children))
if child.partition < 1 {
child.Name = fmt.Sprintf("%s%s?", bd.Name, partPrefix)
} else {
child.Name = fmt.Sprintf("%s%s%d", bd.Name, partPrefix, child.partition)
}
}
log.Debug("AddChild: child.Name is %q", child.Name)
}
// HumanReadableSizeWithUnitAndPrecision converts the size representation in bytes to the
@@ -555,6 +614,10 @@ func listBlockDevices(userDefined []*BlockDevice) ([]*BlockDevice, error) {
if err = bd.PartProbe(); err != nil {
return nil, err
}
// Read the partition table for the device
partTable := bd.getPartitionTable()
bd.setPartitionTable(partTable)
}
if userDefined == nil || len(userDefined) == 0 {
@@ -772,21 +835,6 @@ func getNextBoolToken(dec *json.Decoder, name string) (bool, error) {
return false, errors.Errorf("Unknown ro value: %s", str)
}
// MaxParitionSize returns largest size the partition
// can be in bytes. Return 0 if there is an error; in theory
// the maximum size should at least be the current size.
func (bd *BlockDevice) MaxParitionSize() uint64 {
if bd.Parent != nil {
free, err := bd.Parent.FreeSpace()
if err == nil {
return (bd.Size + free)
}
}
return 0
}
// IsValidLabel returns empty string if label is valid
func IsValidLabel(label string, fstype string) string {
if label == "" {
@@ -818,7 +866,7 @@ func IsValidMount(str string) string {
// -- size is suffixed with B, K, M, G, T, P
// -- size is greater than MinimumPartitionSize
// -- size is less than (or equal to) current size + free space
func (bd *BlockDevice) IsValidSize(str string) string {
func (bd *BlockDevice) IsValidSize(str string, maxPartSize uint64) string {
str = strings.ToLower(str)
if !storageExp.MatchString(str) {
@@ -832,7 +880,6 @@ func (bd *BlockDevice) IsValidSize(str string) string {
return "Size too small"
}
maxPartSize := bd.MaxParitionSize()
if maxPartSize == 0 {
return "Unknown free space"
} else if size > maxPartSize {
@@ -879,6 +926,43 @@ func ParseVolumeSize(str string) (uint64, error) {
return size, nil
}
// ParseVolumeHumanSize will parse a string formatted (1M, 10G, 2T) size and
// return its representation in human bytes; MB, not MiB
func ParseVolumeHumanSize(str string) (uint64, error) {
var size uint64
str = strings.ToLower(str)
if !storageExp.MatchString(str) {
return strconv.ParseUint(str, 0, 64)
}
unit := storageExp.ReplaceAllString(str, `$3`)
fsize, err := strconv.ParseFloat(storageExp.ReplaceAllString(str, `$1`), 64)
if err != nil {
return 0, errors.Wrap(err)
}
switch unit {
case "b":
fsize = fsize * (1.0)
case "k":
fsize = fsize * (1.0 * 1000.0)
case "m":
fsize = fsize * (1.0 * 1000.0 * 1000.0)
case "g":
fsize = fsize * (1.0 * 1000.0 * 1000.0 * 1000.0)
case "t":
fsize = fsize * (1.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)
case "p":
fsize = fsize * (1.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)
}
size = uint64(math.Round(fsize))
return size, nil
}
// UnmarshalJSON decodes a BlockDevice, targeted to integrate with json
// decoding framework
func (bd *BlockDevice) UnmarshalJSON(b []byte) error {
@@ -1095,7 +1179,8 @@ func (bd *BlockDevice) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
bd.Type = iType
if iType != BlockDeviceTypeDisk {
bd.userDefined = true
bd.MakePartition = true
bd.FormatPartition = true
}
}
@@ -1180,53 +1265,50 @@ func MaxLabelLength(fstype string) int {
}
// AddBootStandardPartition will add to disk a new standard Boot partition
func AddBootStandardPartition(disk *BlockDevice, start uint64) uint64 {
end := start + bootSize
disk.AddChild(&BlockDevice{
Size: bootSize,
Type: BlockDeviceTypePart,
FsType: "vfat",
MountPoint: "/boot",
Label: "boot",
userDefined: true,
partStart: start,
partEnd: end,
func AddBootStandardPartition(disk *BlockDevice) uint64 {
freePart := disk.findFree(bootSize)
disk.AddFromFreePartition(freePart, &BlockDevice{
Size: bootSize,
Type: BlockDeviceTypePart,
FsType: "vfat",
MountPoint: "/boot",
Label: "boot",
UserDefined: true,
MakePartition: true,
FormatPartition: true,
})
return end
return bootSize
}
// AddSwapStandardPartition will add to disk a new standard Swap partition
func AddSwapStandardPartition(disk *BlockDevice, start uint64) uint64 {
end := start + swapSize
disk.AddChild(&BlockDevice{
Size: swapSize,
Type: BlockDeviceTypePart,
FsType: "swap",
Label: "swap",
userDefined: true,
partStart: start,
partEnd: end,
func AddSwapStandardPartition(disk *BlockDevice) uint64 {
freePart := disk.findFree(swapSize)
disk.AddFromFreePartition(freePart, &BlockDevice{
Size: swapSize,
Type: BlockDeviceTypePart,
FsType: "swap",
Label: "swap",
UserDefined: true,
MakePartition: true,
FormatPartition: true,
})
return end
return swapSize
}
// AddRootStandardPartition will add to disk a new standard Root partition
func AddRootStandardPartition(disk *BlockDevice, size uint64, start uint64) {
end := start + size
disk.AddChild(&BlockDevice{
Size: size,
Type: BlockDeviceTypePart,
FsType: "ext4",
MountPoint: "/",
Label: "root",
userDefined: true,
partStart: start,
partEnd: end,
func AddRootStandardPartition(disk *BlockDevice, rootSize uint64) {
freePart := disk.findFree(rootSize)
disk.AddFromFreePartition(freePart, &BlockDevice{
Size: rootSize,
Type: BlockDeviceTypePart,
FsType: "ext4",
MountPoint: "/",
Label: "root",
UserDefined: true,
MakePartition: true,
FormatPartition: true,
})
}
@@ -1234,33 +1316,51 @@ func AddRootStandardPartition(disk *BlockDevice, size uint64, start uint64) {
// default set of partitions required for an installation
func NewStandardPartitions(disk *BlockDevice) {
disk.Children = nil
newFreePart := &PartedPartition{
Number: 0,
Start: 0,
End: disk.Size,
Size: disk.Size,
FileSystem: "free",
}
disk.PartTable = nil
disk.PartTable = append(disk.PartTable, newFreePart)
rootSize := uint64(disk.Size - bootSize - swapSize)
disk.AddChild(&BlockDevice{
Size: bootSize,
Type: BlockDeviceTypePart,
FsType: "vfat",
MountPoint: "/boot",
Label: "boot",
userDefined: true,
freePart := disk.findFree(bootSize)
disk.AddFromFreePartition(freePart, &BlockDevice{
Size: bootSize,
Type: BlockDeviceTypePart,
FsType: "vfat",
MountPoint: "/boot",
Label: "boot",
UserDefined: true,
MakePartition: true,
FormatPartition: true,
})
disk.AddChild(&BlockDevice{
Size: swapSize,
Type: BlockDeviceTypePart,
FsType: "swap",
Label: "swap",
userDefined: true,
freePart = disk.findFree(swapSize)
disk.AddFromFreePartition(freePart, &BlockDevice{
Size: swapSize,
Type: BlockDeviceTypePart,
FsType: "swap",
Label: "swap",
UserDefined: true,
MakePartition: true,
FormatPartition: true,
})
disk.AddChild(&BlockDevice{
Size: rootSize,
Type: BlockDeviceTypePart,
FsType: "ext4",
MountPoint: "/",
Label: "root",
userDefined: true,
freePart = disk.findFree(rootSize)
disk.AddFromFreePartition(freePart, &BlockDevice{
Size: rootSize,
Type: BlockDeviceTypePart,
FsType: "ext4",
MountPoint: "/",
Label: "root",
UserDefined: true,
MakePartition: true,
FormatPartition: true,
})
}
+21 -13
View File
@@ -921,37 +921,45 @@ BYT;
`
var start, end, twentyGig, fourGig uint64
children := make([]*BlockDevice, 0)
bd := &BlockDevice{Name: "sda", Children: children}
twentyGig = 21474836480
fourGig = 4294967296
t.Logf("getPartAllFreeOutput: twentyGig: %d, fourGig: %d", twentyGig, fourGig)
start, end = largestContiguousFreeSpace(bytes.NewBuffer([]byte(getPartAllFreeOutput)), twentyGig)
bd.setPartitionTable(bytes.NewBuffer([]byte(getPartAllFreeOutput)))
start, end = bd.LargestContiguousFreeSpace(twentyGig)
if start == 0 && end == 0 {
t.Fatalf("Should have found %d free in getPartAllFreeOutput", twentyGig)
}
t.Logf("getPartAllFreeOutput: start: %d, end: %d", start, end)
start, end = largestContiguousFreeSpace(bytes.NewBuffer([]byte(getPartSomeFreeOutput)), twentyGig)
bd.setPartitionTable(bytes.NewBuffer([]byte(getPartSomeFreeOutput)))
start, end = bd.LargestContiguousFreeSpace(twentyGig)
if start == 0 && end == 0 {
t.Fatalf("Should have found %d free in getPartSomeFreeOutput", twentyGig)
}
t.Logf("getPartSomeFreeOutput: start: %d, end: %d", start, end)
start, end = largestContiguousFreeSpace(bytes.NewBuffer([]byte(getPartNotEnoughFreeOutput)), fourGig)
bd.setPartitionTable(bytes.NewBuffer([]byte(getPartNotEnoughFreeOutput)))
start, end = bd.LargestContiguousFreeSpace(fourGig)
if start != 0 || end != 0 {
t.Logf("getPartNotEnoughFreeOutput: start: %d, end: %d", start, end)
t.Fatalf("Should NOT have found %d free in getPartNotEnoughFreeOutput", twentyGig)
}
t.Logf("getPartNotEnoughFreeOutput: start: %d, end: %d", start, end)
start, end = largestContiguousFreeSpace(bytes.NewBuffer([]byte(getPartNotEnoughFree2Output)), twentyGig)
bd.setPartitionTable(bytes.NewBuffer([]byte(getPartNotEnoughFree2Output)))
start, end = bd.LargestContiguousFreeSpace(twentyGig)
if start != 0 || end != 0 {
t.Logf("getPartNotEnoughFree2Output: start: %d, end: %d", start, end)
t.Fatalf("Should NOT have found %d free in getPartNotEnoughFree2Output", twentyGig)
}
t.Logf("getPartNotEnoughFree2Output: start: %d, end: %d", start, end)
start, end = largestContiguousFreeSpace(bytes.NewBuffer([]byte(getPartNotEnoughFree3Output)), twentyGig)
bd.setPartitionTable(bytes.NewBuffer([]byte(getPartNotEnoughFree3Output)))
start, end = bd.LargestContiguousFreeSpace(twentyGig)
if start != 0 || end != 0 {
t.Logf("getPartNotEnoughFree3Output: start: %d, end: %d", start, end)
t.Fatalf("Should NOT have found %d free in getPartNotEnoughFree3Output", twentyGig)
@@ -982,7 +990,7 @@ func TestSwapCheck(t *testing.T) {
}
bd = &BlockDevice{Size: MinimumServerInstallSize}
_ = AddBootStandardPartition(bd, 0)
_ = AddBootStandardPartition(bd)
if bd.DeviceHasSwap() {
t.Fatalf("Device should NOT have swap, but does: %v", bd)
}
@@ -992,15 +1000,15 @@ func TestSwapCheck(t *testing.T) {
func TestAddPartititions(t *testing.T) {
bd := &BlockDevice{Size: MinimumServerInstallSize}
end := AddBootStandardPartition(bd, 0)
if end != bootSize {
t.Fatalf("Boot partition should end at %d, but was %d", bootSize, end)
size := AddBootStandardPartition(bd)
if size != bootSize {
t.Fatalf("Boot partition should be %d, but was %d", bootSize, size)
}
end = AddSwapStandardPartition(bd, end)
if end != (bootSize + swapSize) {
t.Fatalf("Swap partition should end at %d, but was %d", (bootSize + swapSize), end)
size = AddSwapStandardPartition(bd)
if size != swapSize {
t.Fatalf("Swap partition should be %d, but was %d", swapSize, size)
}
rootSize := uint64(bd.Size - bootSize - swapSize)
AddRootStandardPartition(bd, rootSize, end)
AddRootStandardPartition(bd, rootSize)
}