pve-go/node.go

233 lines
4.6 KiB
Go

// Copyright (C) 2020 Marius Schellenberger
package pve
import (
"context"
"errors"
"fmt"
)
var (
ErrNodesOffline = errors.New("one or more nodes are offline")
ErrNodesNotSearched = errors.New("one or more nodes could not be searched")
ErrEmptyID = errors.New("id is empty")
)
type NodeStatus string
const (
NodeStatusOnline NodeStatus = "online"
NodeStatusOffline NodeStatus = "offline"
NodeStatusUnknown NodeStatus = "unknown"
)
type Node struct {
Name string `json:"node"`
Status NodeStatus `json:"status"`
CPU float64 `json:"cpu"`
MaxCPU float64 `json:"maxcpu"`
Mem int64 `json:"mem"`
MaxMem int64 `json:"maxmem"`
Disk int64 `json:"disk"`
MaxDisk int64 `json:"maxdisk"`
ServerList ServerList `json:"-"`
}
func (n *Node) MemFreePercent() float64 {
return float64(n.Mem) / float64(n.MaxMem) * 100
}
type NodeList []*Node
func (nl NodeList) sortByFreeMem() {
sort.Sort(memSorter(nl))
}
func (nl *NodeList) remove(i int) {
(*nl)[i] = (*nl)[len(*nl)-1]
*nl = (*nl)[:len(*nl)-1]
}
func (nl *NodeList) Filter(filters ...NodeFilter) {
for _, f := range filters {
f(nl)
}
}
func (nl *NodeList) GetServerList(ctx context.Context, c *NodeClient) (err error) {
for _, n := range *nl {
n.ServerList, err = c.ListServers(ctx, n)
if err != nil {
return
}
}
}
type memSorter NodeList
func (m memSorter) Len() int { return len(m) }
func (m memSorter) Less(i, j int) bool { return m[i].MemFreePercent() < m[j].MemFreePercent() }
func (m memSorter) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
type NodeFilter func(*NodeList)
func FilterOnlineNode() NodeFilter {
return func(nl *NodeList) {
for i, n := range *nl {
if n.Status != NodeStatusOnline {
nl.remove(i)
}
}
}
}
func FilterNodeName(name string) NodeFilter {
return func(nl *NodeList) {
for i, n := range *nl {
if n.Name == name {
nl.remove(i)
return
}
}
}
}
func FilterMaxCores(cores int64) NodeFilter {
return func(nl *NodeList) {
for i, n := range *nl {
if cores > n.MaxCPU {
nl.remove(i)
}
}
}
}
const (
mib = 1024 * 1024
gib = mib * 1024
)
func FilterFreeMem(mem int64) NodeFilter {
return func(nl *NodeList) {
for i, n := range *nl {
if (mem * gib) >= n.MaxMem-n.Mem {
nl.remove(i)
}
}
}
}
// Useful? only local disk?
func FilterFreeDisk(disk int64) NodeFilter {
return func(nl *NodeList) {
for i, n := range *nl {
if (disk * gib) >= n.MaxDisk-n.Disk {
nl.remove(i)
}
}
}
}
func FilterVMAntiAffinity(url string) NodeFilter {
return func(nl *NodeList) {
_, _, id, err := ParseURL(url)
if err != nil {
return
}
for i, n := range *nl {
for _, s := range n.ServerList {
if s.ID == id {
nl.remove(i)
return
}
}
}
}
}
type NodeClient struct {
client *Client
}
func (c *NodeClient) Schedule(ctx context.Context) (n *Node, err error) {
return
}
func (c *NodeClient) List(ctx context.Context) (nl NodeList, err error) {
req, err := c.client.NewRequest(ctx, "GET", "/nodes", nil)
if err != nil {
return
}
_, err = c.client.Do(req, &nl)
return
}
func (c *NodeClient) ListServers(ctx context.Context, n *Node) (sl ServerList, err error) {
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/nodes/%s/qemu", n.Name), nil)
if err != nil {
return
}
_, err = c.client.Do(req, &sl)
if err == nil {
for i := range sl {
sl[i].Node = n.Name
}
}
return
}
func (c *NodeClient) FindServer(ctx context.Context, name, id string) (s *Server, err error) {
nl, err := c.List(ctx)
if err != nil {
return
}
nodeOffline := false
nodeNotSearched := false
for _, node := range nl {
if node.Status != NodeStatusOnline {
nodeOffline = true
continue
}
sl, err := c.ListServers(ctx, node)
if err != nil {
nodeNotSearched = true
continue
}
for _, s := range sl {
if id != "" && s.ID == id {
return s, nil
}
if name != "" && s.Name == name {
return s, nil
}
}
}
if nodeOffline {
return nil, ErrNodesOffline
}
if nodeNotSearched {
return nil, ErrNodesNotSearched
}
return nil, ErrServerNotFound
}
func (c *NodeClient) FindServerByName(ctx context.Context, name string) (s *Server, err error) {
return c.FindServer(ctx, name, "")
}
func (c *NodeClient) FindServerByID(ctx context.Context, id string) (s *Server, err error) {
if id == "" {
err = ErrEmptyID
return
}
return c.FindServer(ctx, "", id)
}
func (c *NodeClient) FindServerByURL(ctx context.Context, url string) (s *Server, err error) {
_, _, id, err := ParseURL(url)
if err != nil {
return
}
return c.FindServerByID(ctx, id)
}