Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 27 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ $ export AWS_REGION="us-east-1"

**Find Instance Types with 4 GiB of memory, 2 vcpus, and runs on the x86_64 CPU architecture**
```
$ ec2-instance-selector --memory 4096 --vcpus 2 --cpu-architecture x86_64 -r us-east-1
$ ec2-instance-selector --memory 4 --vcpus 2 --cpu-architecture x86_64 -r us-east-1
c5.large
c5d.large
t2.medium
Expand All @@ -93,26 +93,27 @@ r5n.24xlarge

**Short Table Output**
```
$ ec2-instance-selector --memory 4096 --vcpus 2 --cpu-architecture x86_64 -r us-east-1 -o table
Instance Type VCPUs Mem (MiB)
$ ec2-instance-selector --memory 4 --vcpus 2 --cpu-architecture x86_64 -r us-east-1 -o table
Instance Type VCPUs Mem (GiB)
------------- ----- ---------
c5.large 2 4096
c5d.large 2 4096
t2.medium 2 4096
t3.medium 2 4096
t3a.medium 2 4096
c5.large 2 4.000
c5d.large 2 4.000
t2.medium 2 4.000
t3.medium 2 4.000
t3a.medium 2 4.000
```

**Wide Table Output**
```
$ ec2-instance-selector --memory 4096 --vcpus 2 --cpu-architecture x86_64 -r us-east-1 -o table-wide
Instance Type VCPUs Mem (MiB) Hypervisor Current Gen Hibernation Support CPU Arch Network Performance ENIs GPUs
------------- ----- --------- ---------- ----------- ------------------- -------- ------------------- ---- ----
c5.large 2 4096 nitro true true x86_64 Up to 10 Gigabit 3 0
c5d.large 2 4096 nitro true false x86_64 Up to 10 Gigabit 3 0
t2.medium 2 4096 xen true true i386, x86_64 Low to Moderate 3 0
t3.medium 2 4096 nitro true false x86_64 Up to 5 Gigabit 3 0
t3a.medium 2 4096 nitro true false x86_64 Up to 5 Gigabit 3 0
$ ec2-instance-selector --memory 4 --vcpus 2 --cpu-architecture x86_64 -r us-east-1 -o table-wide
Instance Type VCPUs Mem (GiB) Hypervisor Current Gen Hibernation Support CPU Arch Network Performance ENIs GPUs
------------- ----- --------- ---------- ----------- ------------------- -------- ------------------- ---- ----
c5.large 2 4.000 nitro true true x86_64 Up to 10 Gigabit 3 0
c5a.large 2 4.000 nitro true false x86_64 Up to 10 Gigabit 3 0
c5d.large 2 4.000 nitro true false x86_64 Up to 10 Gigabit 3 0
t2.medium 2 4.000 xen true true i386, x86_64 Low to Moderate 3 0
t3.medium 2 4.000 nitro true false x86_64 Up to 5 Gigabit 3 0
t3a.medium 2 4.000 nitro true false x86_64 Up to 5 Gigabit 3 0
```

**All CLI Options**
Expand Down Expand Up @@ -144,17 +145,17 @@ Filter Flags:
--deny-list string List of instance types which should be excluded w/ regex syntax (Example: m[1-2]\.*)
-e, --ena-support Instance types where ENA is supported or required
-f, --fpga-support FPGA instance types
--gpu-memory-total int Number of GPUs' total memory in MiB (Example: 4096) (sets --gpu-memory-total-min and -max to the same value)
--gpu-memory-total-max int Maximum Number of GPUs' total memory in MiB (Example: 4096) If --gpu-memory-total-min is not specified, the lower bound will be 0
--gpu-memory-total-min int Minimum Number of GPUs' total memory in MiB (Example: 4096) If --gpu-memory-total-max is not specified, the upper bound will be infinity
--gpu-memory-total float Number of GPUs' total memory in GiB (Example: 4) (sets --gpu-memory-total-min and -max to the same value)
--gpu-memory-total-max float Maximum Number of GPUs' total memory in GiB (Example: 4) If --gpu-memory-total-min is not specified, the lower bound will be 0
--gpu-memory-total-min float Minimum Number of GPUs' total memory in GiB (Example: 4) If --gpu-memory-total-max is not specified, the upper bound will be infinity
-g, --gpus int Total Number of GPUs (Example: 4) (sets --gpus-min and -max to the same value)
--gpus-max int Maximum Total Number of GPUs (Example: 4) If --gpus-min is not specified, the lower bound will be 0
--gpus-min int Minimum Total Number of GPUs (Example: 4) If --gpus-max is not specified, the upper bound will be infinity
--hibernation-support Hibernation supported
--hypervisor string Hypervisor: [xen or nitro]
-m, --memory int Amount of Memory available in MiB (Example: 4096) (sets --memory-min and -max to the same value)
--memory-max int Maximum Amount of Memory available in MiB (Example: 4096) If --memory-min is not specified, the lower bound will be 0
--memory-min int Minimum Amount of Memory available in MiB (Example: 4096) If --memory-max is not specified, the upper bound will be infinity
-m, --memory float Amount of Memory available in GiB (Example: 4) (sets --memory-min and -max to the same value)
--memory-max float Maximum Amount of Memory available in GiB (Example: 4) If --memory-min is not specified, the lower bound will be 0
--memory-min float Minimum Amount of Memory available in GiB (Example: 4) If --memory-max is not specified, the upper bound will be infinity
--network-interfaces int Number of network interfaces (ENIs) that can be attached to the instance (sets --network-interfaces-min and -max to the same value)
--network-interfaces-max int Maximum Number of network interfaces (ENIs) that can be attached to the instance If --network-interfaces-min is not specified, the lower bound will be 0
--network-interfaces-min int Minimum Number of network interfaces (ENIs) that can be attached to the instance If --network-interfaces-max is not specified, the upper bound will be infinity
Expand Down Expand Up @@ -221,10 +222,10 @@ func main() {
LowerBound: 2,
UpperBound: 4,
}
// Instantiate an int range filter to specify min and max memory in MiB
memoryRange := selector.IntRangeFilter{
LowerBound: 1024,
UpperBound: 4096,
// Instantiate a float64 range filter to specify min and max memory in GiB
memoryRange := selector.Float64RangeFilter{
LowerBound: 1.0,
UpperBound: 4.0,
}
// Create a string for the CPU Architecture so that it can be passed as a pointer
// when creating the Filter struct
Expand Down
9 changes: 5 additions & 4 deletions cmd/examples/example1.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"fmt"

"github.com/aws/amazon-ec2-instance-selector/pkg/bytequantity"
"github.com/aws/amazon-ec2-instance-selector/pkg/selector"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
Expand All @@ -27,10 +28,10 @@ func main() {
LowerBound: 2,
UpperBound: 4,
}
// Instantiate an int range filter to specify min and max memory in MiB
memoryRange := selector.IntRangeFilter{
LowerBound: 1024,
UpperBound: 4096,
// Instantiate a byte quantity range filter to specify min and max memory in GiB
memoryRange := selector.ByteQuantityRangeFilter{
LowerBound: bytequantity.FromGiB(2),
UpperBound: bytequantity.FromGiB(4),
}
// Create a string for the CPU Architecture so that it can be passed as a pointer
// when creating the Filter struct
Expand Down
8 changes: 4 additions & 4 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,11 @@ Full docs can be found at github.com/aws/amazon-` + binName
// Filter Flags - These will be grouped at the top of the help flags

cli.IntMinMaxRangeFlags(vcpus, cli.StringMe("c"), nil, "Number of vcpus available to the instance type.")
cli.IntMinMaxRangeFlags(memory, cli.StringMe("m"), nil, "Amount of Memory available in MiB (Example: 4096)")
cli.ByteQuantityMinMaxRangeFlags(memory, cli.StringMe("m"), nil, "Amount of Memory available (Example: 4 GiB)")
cli.RatioFlag(vcpusToMemoryRatio, nil, nil, "The ratio of vcpus to memory in MiB. (Example: 1:2)")
cli.StringFlag(cpuArchitecture, cli.StringMe("a"), nil, "CPU architecture [x86_64, i386, or arm64]", nil)
cli.IntMinMaxRangeFlags(gpus, cli.StringMe("g"), nil, "Total Number of GPUs (Example: 4)")
cli.IntMinMaxRangeFlags(gpuMemoryTotal, nil, nil, "Number of GPUs' total memory in MiB (Example: 4096)")
cli.ByteQuantityMinMaxRangeFlags(gpuMemoryTotal, nil, nil, "Number of GPUs' total memory (Example: 4 GiB)")
cli.StringFlag(placementGroupStrategy, nil, nil, "Placement group strategy: [cluster, partition, spread]", nil)
cli.StringFlag(usageClass, cli.StringMe("u"), nil, "Usage class: [spot or on-demand]", nil)
cli.StringFlag(rootDeviceType, nil, nil, "Supported root device types: [ebs or instance-store]", nil)
Expand Down Expand Up @@ -189,11 +189,11 @@ Full docs can be found at github.com/aws/amazon-` + binName

filters := selector.Filters{
VCpusRange: cli.IntRangeMe(flags[vcpus]),
MemoryRange: cli.IntRangeMe(flags[memory]),
MemoryRange: cli.ByteQuantityRangeMe(flags[memory]),
VCpusToMemoryRatio: cli.Float64Me(flags[vcpusToMemoryRatio]),
CPUArchitecture: cli.StringMe(flags[cpuArchitecture]),
GpusRange: cli.IntRangeMe(flags[gpus]),
GpuMemoryRange: cli.IntRangeMe(flags[gpuMemoryTotal]),
GpuMemoryRange: cli.ByteQuantityRangeMe(flags[gpuMemoryTotal]),
PlacementGroupStrategy: cli.StringMe(flags[placementGroupStrategy]),
UsageClass: cli.StringMe(flags[usageClass]),
RootDeviceType: cli.StringMe(flags[rootDeviceType]),
Expand Down
153 changes: 153 additions & 0 deletions pkg/bytequantity/bytequantity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 bytequantity

import (
"fmt"
"math"
"regexp"
"strconv"
"strings"
)

const (
/// Examples: 1mb, 1 gb, 1.0tb, 1mib, 2g, 2.001 t
byteQuantityRegex = `^([0-9]+\.?[0-9]{0,3})[ ]?(mi?b?|gi?b?|ti?b?)?$`
mib = "MiB"
gib = "GiB"
tib = "TiB"
gbConvert = 1 << 10
tbConvert = gbConvert << 10
maxGiB = math.MaxUint64 / gbConvert
maxTiB = math.MaxUint64 / tbConvert
)

// ByteQuantity is a data type representing a byte quantity
type ByteQuantity struct {
Quantity uint64
}

// ParseToByteQuantity parses a string representation of a byte quantity to a ByteQuantity type.
// A unit can be appended such as 16 GiB. If no unit is appended, GiB is assumed.
func ParseToByteQuantity(byteQuantityStr string) (ByteQuantity, error) {
bqRegexp := regexp.MustCompile(byteQuantityRegex)
matches := bqRegexp.FindStringSubmatch(strings.ToLower(byteQuantityStr))
if len(matches) < 2 {
return ByteQuantity{}, fmt.Errorf("%s is not a valid byte quantity", byteQuantityStr)
}

quantityStr := matches[1]
unit := gib
if len(matches) > 2 && matches[2] != "" {
unit = matches[2]
}
quantity := uint64(0)
switch strings.ToLower(string(unit[0])) {
//mib
case "m":
inputDecSplit := strings.Split(quantityStr, ".")
if len(inputDecSplit) == 2 {
d, err := strconv.Atoi(inputDecSplit[1])
if err != nil {
return ByteQuantity{}, err
}
if d != 0 {
return ByteQuantity{}, fmt.Errorf("cannot accept floating point MB value, only integers are accepted")
}
}
// need error here so that this quantity doesn't bind in the local scope
var err error
quantity, err = strconv.ParseUint(inputDecSplit[0], 10, 64)
if err != nil {
return ByteQuantity{}, err
}
//gib
case "g":
quantityDec, err := strconv.ParseFloat(quantityStr, 10)
if err != nil {
return ByteQuantity{}, err
}
if quantityDec > maxGiB {
return ByteQuantity{}, fmt.Errorf("error GiB value is too large")
}
quantity = uint64(quantityDec * gbConvert)
//tib
case "t":
quantityDec, err := strconv.ParseFloat(quantityStr, 10)
if err != nil {
return ByteQuantity{}, err
}
if quantityDec > maxTiB {
return ByteQuantity{}, fmt.Errorf("error TiB value is too large")
}
quantity = uint64(quantityDec * tbConvert)
default:
return ByteQuantity{}, fmt.Errorf("error unit %s is not supported", unit)
}

return ByteQuantity{
Quantity: quantity,
}, nil
}

// FromTiB returns a byte quantity of the passed in tebibytes quantity
func FromTiB(tib uint64) ByteQuantity {
return ByteQuantity{
Quantity: tib * tbConvert,
}
}

// FromGiB returns a byte quantity of the passed in gibibytes quantity
func FromGiB(gib uint64) ByteQuantity {
return ByteQuantity{
Quantity: gib * gbConvert,
}
}

// FromMiB returns a byte quantity of the passed in mebibytes quantity
func FromMiB(mib uint64) ByteQuantity {
return ByteQuantity{
Quantity: mib,
}
}

// StringMiB returns a byte quantity in a mebibytes string representation
func (bq ByteQuantity) StringMiB() string {
return fmt.Sprintf("%.0f %s", bq.MiB(), mib)
}

// StringGiB returns a byte quantity in a gibibytes string representation
func (bq ByteQuantity) StringGiB() string {
return fmt.Sprintf("%.3f %s", bq.GiB(), gib)
}

// StringTiB returns a byte quantity in a tebibytes string representation
func (bq ByteQuantity) StringTiB() string {
return fmt.Sprintf("%.3f %s", bq.TiB(), tib)
}

// MiB returns a byte quantity in mebibytes
func (bq ByteQuantity) MiB() float64 {
return float64(bq.Quantity)
}

// GiB returns a byte quantity in gibibytes
func (bq ByteQuantity) GiB() float64 {
return float64(bq.Quantity) * 1 / gbConvert
}

// TiB returns a byte quantity in tebibytes
func (bq ByteQuantity) TiB() float64 {
return float64(bq.Quantity) * 1 / tbConvert
}
Loading