Skip to content

Commit

Permalink
add proxmox pkg
Browse files Browse the repository at this point in the history
  • Loading branch information
sp-yduck committed Jul 22, 2023
1 parent ad2109e commit 65dd897
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 14 deletions.
12 changes: 12 additions & 0 deletions api/qemu_agent_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package api

type OSInfo struct {
ID string `json:"id"`
KernelRelease string `json:"kernel-release"`
KernelVersion string `json:"kernel-version"`
Machine string `json:"machine"`
Name string `json:"name"`
PrettyName string `json:"pretty-name"`
Version string `json:"version"`
VersionID string `json:"version-id"`
}
14 changes: 0 additions & 14 deletions proxmox/client.go

This file was deleted.

15 changes: 15 additions & 0 deletions proxmox/node.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package proxmox

import (
"context"

"github.com/sp-yduck/proxmox-go/api"
)

func (s *Service) Nodes(ctx context.Context) ([]*api.Node, error) {
return s.restclient.GetNodes(ctx)
}

func (s *Service) Node(ctx context.Context, name string) (*api.Node, error) {
return s.restclient.GetNode(ctx, name)
}
116 changes: 116 additions & 0 deletions proxmox/qemu.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package proxmox

import (
"context"
"errors"
"fmt"
"regexp"
"strings"

"github.com/sp-yduck/proxmox-go/api"
"github.com/sp-yduck/proxmox-go/rest"
)

type VirtualMachine struct {
restclient *rest.RESTClient
node string
vm *api.VirtualMachine
config *api.VirtualMachineConfig
}

const (
UUIDFormat = `[a-f\d]{8}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{12}`
)

// VirtualMachines returns all qemus across all proxmox nodes
func (s *Service) VirtualMachines(ctx context.Context) ([]*api.VirtualMachine, error) {
nodes, err := s.Nodes(ctx)
if err != nil {
return nil, err
}
var vms []*api.VirtualMachine
for _, node := range nodes {
v, err := s.restclient.GetVirtualMachines(ctx, node.Node)
if err != nil {
return nil, err
}
vms = append(vms, v...)
}
return vms, nil
}

func (s *Service) NewVirtualMachine(ctx context.Context, vmid int) (*VirtualMachine, error) {
nodes, err := s.Nodes(ctx)
if err != nil {
return nil, err
}
for _, node := range nodes {
vm, err := s.restclient.GetVirtualMachine(ctx, node.Node, vmid)
if err != nil {
if rest.IsNotFound(err) {
continue
}
return nil, err
}
return &VirtualMachine{restclient: s.restclient, vm: vm, node: node.Node}, nil
}
return nil, rest.NotFoundErr
}

func (s *Service) VirtualMachineFromUUID(ctx context.Context, uuid string) (*VirtualMachine, error) {
nodes, err := s.Nodes(ctx)
if err != nil {
return nil, err
}
for _, node := range nodes {
vms, err := s.restclient.GetVirtualMachines(ctx, node.Node)
if err != nil {
return nil, err
}
for _, vm := range vms {
config, err := s.restclient.GetVirtualMachineConfig(ctx, node.Node, vm.VMID)
if err != nil {
return nil, err
}
vmuuid, err := convertSMBiosToUUID(config.SMBios1)
if err != nil {
return nil, err
}
if vmuuid == uuid {
return &VirtualMachine{restclient: s.restclient, vm: vm, node: node.Node, config: config}, nil
}
}
}
return nil, rest.NotFoundErr
}

func convertSMBiosToUUID(smbios string) (string, error) {
re := regexp.MustCompile(fmt.Sprintf("uuid=%s", UUIDFormat))
match := re.FindString(smbios)
if match == "" {
return "", errors.New("failed to fetch uuid form smbios")
}
// match: uuid=<uuid>
return strings.Split(match, "=")[1], nil
}

func (c *VirtualMachine) GetConfig(ctx context.Context) (*api.VirtualMachineConfig, error) {
if c.config != nil {
return c.config, nil
}
config, err := c.restclient.GetVirtualMachineConfig(ctx, c.node, c.vm.VMID)
if err != nil {
return nil, err
}
c.config = config
return c.config, err
}

func (c *VirtualMachine) GetOSInfo(ctx context.Context) (*api.OSInfo, error) {
var osInfo *api.OSInfo
path := fmt.Sprintf("/nodes/%s/qemu/%d/agent/get-osinfo", c.node, c.vm.VMID)
if err := c.restclient.Get(ctx, path, &osInfo); err != nil {
return nil, err
}
return osInfo, nil
}
98 changes: 98 additions & 0 deletions proxmox/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package proxmox

import (
"crypto/tls"
"errors"
"net/http"

"github.com/sp-yduck/proxmox-go/rest"
)

type Service struct {
restclient *rest.RESTClient
}

type AuthConfig struct {
Username string
Password string
TokenID string
Secret string
}

func NewService(url string, authConfig AuthConfig, insecure bool) (*Service, error) {
var loginOption rest.ClientOption
if authConfig.Username != "" && authConfig.Password != "" {
loginOption = rest.WithUserPassword(authConfig.Username, authConfig.Password)
} else if authConfig.TokenID != "" && authConfig.Secret != "" {
loginOption = rest.WithAPIToken(authConfig.TokenID, authConfig.Secret)
} else {
return nil, errors.New("invalid authentication config")
}
clientOptions := []rest.ClientOption{loginOption}

if insecure {
baseClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
clientOptions = append(clientOptions, rest.WithClient(baseClient))
}

restclient, err := rest.NewRESTClient(url, clientOptions...)
if err != nil {
return nil, err
}
return &Service{restclient: restclient}, nil
}

func NewServiceWithUserPassword(url, user, password string, insecure bool) (*Service, error) {
clientOptions := []rest.ClientOption{
rest.WithUserPassword(user, password),
}

if insecure {
baseClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
clientOptions = append(clientOptions, rest.WithClient(baseClient))
}

restclient, err := rest.NewRESTClient(url, clientOptions...)
if err != nil {
return nil, err
}
return &Service{restclient: restclient}, nil
}

func NewServiceWithAPIToken(url, tokenid, secret string, insecure bool) (*Service, error) {
clientOptions := []rest.ClientOption{
rest.WithAPIToken(tokenid, secret),
}
if insecure {
baseClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
clientOptions = append(clientOptions, rest.WithClient(baseClient))
}

restclient, err := rest.NewRESTClient(url, clientOptions...)
if err != nil {
return nil, err
}
return &Service{restclient: restclient}, nil
}

func (s *Service) RESTClient() *rest.RESTClient {
return s.restclient
}

0 comments on commit 65dd897

Please sign in to comment.