Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added some configurable options for ProxMox API via CloudConfig #1370

Closed
wants to merge 3 commits into from
Closed
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
2 changes: 2 additions & 0 deletions cmd/cmd_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ func instanceCreateCommandHandler(cmd *cobra.Command, args []string) {
if err != nil {
exitWithError(err.Error())
}

fmt.Printf("%s instance '%s' created...\n", c.CloudConfig.Platform, c.RunConfig.InstanceName)
}

func instanceListCommand() *cobra.Command {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ require (
github.com/docker/distribution v2.8.0+incompatible
github.com/docker/docker v20.10.7+incompatible
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/dustin/go-humanize v1.0.0
github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect
github.com/go-errors/errors v1.0.1
Expand Down
97 changes: 97 additions & 0 deletions lepton/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"math"
"sort"
"strconv"
"strings"
"time"
)

Expand All @@ -16,6 +18,18 @@ const (
Month = 30 * Day
Year = 12 * Month
LongTime = 37 * Year

KB = 1000
MB = 1000 * KB
GB = 1000 * MB
TB = 1000 * GB
PB = 1000 * TB

KiB = 1024
MiB = 1024 * KiB
GiB = 1024 * MiB
TiB = 1024 * GiB
PiB = 1024 * TiB
)

// Time2Human formats a time into a relative string.
Expand Down Expand Up @@ -45,6 +59,8 @@ type RelTimeMagnitude struct {
DivBy time.Duration
}

type unitMap map[byte]int64

var defaultMagnitudes = []RelTimeMagnitude{
{time.Second, "now", time.Second},
{2 * time.Second, "1 second %s", 1},
Expand All @@ -65,6 +81,13 @@ var defaultMagnitudes = []RelTimeMagnitude{
{math.MaxInt64, "a long while %s", 1},
}

var (
decimalMap = unitMap{'k': KB, 'm': MB, 'g': GB, 't': TB, 'p': PB}
binaryMap = unitMap{'k': KiB, 'm': MiB, 'g': GiB, 't': TiB, 'p': PiB}
decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
)

// RelTime formats a time into a relative string.
//
// It takes two times and two labels. In addition to the generic time
Expand Down Expand Up @@ -132,3 +155,77 @@ func Bytes2Human(b int64) string {
return fmt.Sprintf("%.1f %cB",
float64(b)/float64(div), "kMGTPE"[exp])
}

// RAMInBytes parses a human-readable string representing an amount of RAM
// in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and
// returns the number of bytes, or -1 if the string is unparseable.
// Units are case-insensitive, and the 'b' suffix is optional.
func RAMInBytes(size string) (int64, error) {
return parseSize(size, binaryMap)
}

// Parses the human-readable size string into the amount it represents.
func parseSize(sizeStr string, uMap unitMap) (int64, error) {
// TODO: rewrite to use strings.Cut if there's a space
// once Go < 1.18 is deprecated.
sep := strings.LastIndexAny(sizeStr, "01234567890. ")
if sep == -1 {
// There should be at least a digit.
return -1, fmt.Errorf("invalid size: '%s'", sizeStr)
}
var num, sfx string
if sizeStr[sep] != ' ' {
num = sizeStr[:sep+1]
sfx = sizeStr[sep+1:]
} else {
// Omit the space separator.
num = sizeStr[:sep]
sfx = sizeStr[sep+1:]
}

size, err := strconv.ParseFloat(num, 64)
if err != nil {
return -1, err
}
// Backward compatibility: reject negative sizes.
if size < 0 {
return -1, fmt.Errorf("invalid size: '%s'", sizeStr)
}

if len(sfx) == 0 {
return int64(size), nil
}

// Process the suffix.

if len(sfx) > 3 { // Too long.
goto badSuffix
}
sfx = strings.ToLower(sfx)
// Trivial case: b suffix.
if sfx[0] == 'b' {
if len(sfx) > 1 { // no extra characters allowed after b.
goto badSuffix
}
return int64(size), nil
}
// A suffix from the map.
if mul, ok := uMap[sfx[0]]; ok {
size *= float64(mul)
} else {
goto badSuffix
}

// The suffix may have extra "b" or "ib" (e.g. KiB or MB).
switch {
case len(sfx) == 2 && sfx[1] != 'b':
goto badSuffix
case len(sfx) == 3 && sfx[1:] != "ib":
goto badSuffix
}

return int64(size), nil

badSuffix:
return -1, fmt.Errorf("invalid suffix: '%s'", sfx)
}
116 changes: 109 additions & 7 deletions proxmox/proxmox_checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
"strings"
)

type pData struct {
Expand All @@ -22,12 +23,23 @@ type sData struct {
Active int `json:"active"`
Enabled int `json:"enabled"`
Storage string `json:"storage"`
Content string `json:"content"`
}

type sDataArr struct {
Data []sData `json:"data"`
}

type bData struct {
Active int `json:"active"`
Type string `json:"type"`
Iface string `json:"iface"`
}

type bDataArr struct {
Data []bData `json:"data"`
}

// CheckInit return custom error on {"data": null} or {"data": []} result come from ProxMox API /api2/json/pools
func (p *ProxMox) CheckInit() error {

Expand Down Expand Up @@ -75,7 +87,7 @@ func (p *ProxMox) CheckInit() error {
}

// CheckStorage return error when not found configured storage or any storages via ProxMox API
func (p *ProxMox) CheckStorage(storage string) error {
func (p *ProxMox) CheckStorage(storage string, stype string) error {

var err error

Expand All @@ -87,6 +99,8 @@ func (p *ProxMox) CheckStorage(storage string) error {
enb := errors.New("storage is disabled: " + storage)
ect := errors.New("storage is not active: " + storage)
ecs := errors.New("not found storage: " + storage)
eim := errors.New("storage is not configured for containing disk images: " + storage)
eis := errors.New("storage is not configured for containing iso images: " + storage)

req, err := http.NewRequest("GET", p.apiURL+"/api2/json/nodes/"+p.nodeNAME+"/storage", &b)
if err != nil {
Expand Down Expand Up @@ -137,6 +151,94 @@ func (p *ProxMox) CheckStorage(storage string) error {
return enb
}

if stype == "images" {

if !strings.Contains(v.Content, stype) {
return eim
}

} else if stype == "iso" {

if !strings.Contains(v.Content, stype) {
return eis
}

} else {
return errors.New("unknown type of storage")
}

return nil

}
}

return ecs

}

// CheckBridge return error when not found configured bridge any network interfaces via ProxMox API
func (p *ProxMox) CheckBridge(bridge string) error {

var err error

var brs bDataArr

var b bytes.Buffer

edk := errors.New("no any bridges is configured")
ebr := errors.New("is not a bridge: " + bridge)
ect := errors.New("bridge is not active: " + bridge)
ecs := errors.New("not found bridge: " + bridge)

req, err := http.NewRequest("GET", p.apiURL+"/api2/json/nodes/"+p.nodeNAME+"/network", &b)
if err != nil {
return err
}

tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}

req.Header.Add("Authorization", "PVEAPIToken="+p.tokenID+"="+p.secret)

resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}

err = json.Unmarshal(body, &brs)
if err != nil {
return err
}

if err == nil && brs.Data == nil {
return edk
}

debug := false
if debug {
fmt.Println(string(body))
}

for _, v := range brs.Data {

if v.Iface == bridge {

if v.Active != 1 {
return ect
}

if v.Type != "bridge" {
return ebr
}

return nil

}
Expand Down Expand Up @@ -168,7 +270,7 @@ func (p *ProxMox) CheckResult(body []byte) error {
}

// CheckResultType return error or custom error based on type of check, when {"data": null} or {"data": []} result come from ProxMox API
func (p *ProxMox) CheckResultType(body []byte, rtype string) error {
func (p *ProxMox) CheckResultType(body []byte, rtype string, rname string) error {

var err error

Expand All @@ -177,17 +279,17 @@ func (p *ProxMox) CheckResultType(body []byte, rtype string) error {

switch rtype {
case "createimage":
ecs = errors.New("can not create disk image in 'local' storage, " + edef)
ecs = errors.New("can not create disk image in " + rname + " storage, " + edef)
case "listimages":
ecs = errors.New("no disk images found in 'local' storage or " + edef)
ecs = errors.New("no disk images found in " + rname + " storage or " + edef)
case "createinstance":
ecs = errors.New("can not create machine instance with disk image from 'local' storage, " + edef)
ecs = errors.New("can not create machine instance with disk image from " + rname + " image/storage, " + edef)
case "getnextid":
ecs = errors.New("can not get next id, " + edef)
case "movdisk":
ecs = errors.New("can not move iso to raw disk on 'local-lvm' storage, " + edef)
ecs = errors.New("can not move iso to raw disk on " + rname + " storage, " + edef)
case "addvirtiodisk":
ecs = errors.New("can not add virtio disk from 'local' storage, " + edef)
ecs = errors.New("can not add virtio disk from " + rname + " storage, " + edef)
case "bootorderset":
ecs = errors.New("can not set boot order, " + edef)
}
Expand Down
33 changes: 27 additions & 6 deletions proxmox/proxmox_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,15 @@ func (p *ProxMox) CreateImage(ctx *lepton.Context, imagePath string) error {

c := ctx.Config()

imageName := c.CloudConfig.ImageName
imageName := c.ProxmoxConfig.ImageName

err = p.CheckStorage("local")
isoStorageName := c.ProxmoxConfig.IsoStorageName

if isoStorageName == "" {
isoStorageName = "local"
}

err = p.CheckStorage(isoStorageName, "iso")
if err != nil {
return err
}
Expand Down Expand Up @@ -105,7 +111,7 @@ func (p *ProxMox) CreateImage(ctx *lepton.Context, imagePath string) error {

w.Close()

req, err := http.NewRequest("POST", p.apiURL+"/api2/json/nodes/"+p.nodeNAME+"/storage/local/upload", &b)
req, err := http.NewRequest("POST", p.apiURL+"/api2/json/nodes/"+p.nodeNAME+"/storage/"+isoStorageName+"/upload", &b)
if err != nil {
fmt.Println(err)
return err
Expand All @@ -131,7 +137,7 @@ func (p *ProxMox) CreateImage(ctx *lepton.Context, imagePath string) error {
return err
}

err = p.CheckResultType(body, "createimage")
err = p.CheckResultType(body, "createimage", isoStorageName)
if err != nil {
return err
}
Expand Down Expand Up @@ -163,7 +169,22 @@ type ImageInfo struct {
// ListImages lists images on ProxMox
func (p *ProxMox) ListImages(ctx *lepton.Context) error {

req, err := http.NewRequest("GET", p.apiURL+"/api2/json/nodes/"+p.nodeNAME+"/storage/local/content", nil)
var err error

c := ctx.Config()

isoStorageName := c.ProxmoxConfig.StorageName

if isoStorageName == "" {
isoStorageName = "local"
}

err = p.CheckStorage(isoStorageName, "iso")
if err != nil {
return err
}

req, err := http.NewRequest("GET", p.apiURL+"/api2/json/nodes/"+p.nodeNAME+"/storage/"+isoStorageName+"/content", nil)
if err != nil {
fmt.Println(err)
return err
Expand All @@ -187,7 +208,7 @@ func (p *ProxMox) ListImages(ctx *lepton.Context) error {
return err
}

err = p.CheckResultType(body, "listimages")
err = p.CheckResultType(body, "listimages", isoStorageName)
if err != nil {
return err
}
Expand Down
Loading