Skip to content

Commit 65dd897

Browse files
committed
add proxmox pkg
1 parent ad2109e commit 65dd897

File tree

5 files changed

+241
-14
lines changed

5 files changed

+241
-14
lines changed

api/qemu_agent_type.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package api
2+
3+
type OSInfo struct {
4+
ID string `json:"id"`
5+
KernelRelease string `json:"kernel-release"`
6+
KernelVersion string `json:"kernel-version"`
7+
Machine string `json:"machine"`
8+
Name string `json:"name"`
9+
PrettyName string `json:"pretty-name"`
10+
Version string `json:"version"`
11+
VersionID string `json:"version-id"`
12+
}

proxmox/client.go

Lines changed: 0 additions & 14 deletions
This file was deleted.

proxmox/node.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package proxmox
2+
3+
import (
4+
"context"
5+
6+
"github.com/sp-yduck/proxmox-go/api"
7+
)
8+
9+
func (s *Service) Nodes(ctx context.Context) ([]*api.Node, error) {
10+
return s.restclient.GetNodes(ctx)
11+
}
12+
13+
func (s *Service) Node(ctx context.Context, name string) (*api.Node, error) {
14+
return s.restclient.GetNode(ctx, name)
15+
}

proxmox/qemu.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package proxmox
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"regexp"
8+
"strings"
9+
10+
"github.com/sp-yduck/proxmox-go/api"
11+
"github.com/sp-yduck/proxmox-go/rest"
12+
)
13+
14+
type VirtualMachine struct {
15+
restclient *rest.RESTClient
16+
node string
17+
vm *api.VirtualMachine
18+
config *api.VirtualMachineConfig
19+
}
20+
21+
const (
22+
UUIDFormat = `[a-f\d]{8}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{12}`
23+
)
24+
25+
// VirtualMachines returns all qemus across all proxmox nodes
26+
func (s *Service) VirtualMachines(ctx context.Context) ([]*api.VirtualMachine, error) {
27+
nodes, err := s.Nodes(ctx)
28+
if err != nil {
29+
return nil, err
30+
}
31+
var vms []*api.VirtualMachine
32+
for _, node := range nodes {
33+
v, err := s.restclient.GetVirtualMachines(ctx, node.Node)
34+
if err != nil {
35+
return nil, err
36+
}
37+
vms = append(vms, v...)
38+
}
39+
return vms, nil
40+
}
41+
42+
func (s *Service) NewVirtualMachine(ctx context.Context, vmid int) (*VirtualMachine, error) {
43+
nodes, err := s.Nodes(ctx)
44+
if err != nil {
45+
return nil, err
46+
}
47+
for _, node := range nodes {
48+
vm, err := s.restclient.GetVirtualMachine(ctx, node.Node, vmid)
49+
if err != nil {
50+
if rest.IsNotFound(err) {
51+
continue
52+
}
53+
return nil, err
54+
}
55+
return &VirtualMachine{restclient: s.restclient, vm: vm, node: node.Node}, nil
56+
}
57+
return nil, rest.NotFoundErr
58+
}
59+
60+
func (s *Service) VirtualMachineFromUUID(ctx context.Context, uuid string) (*VirtualMachine, error) {
61+
nodes, err := s.Nodes(ctx)
62+
if err != nil {
63+
return nil, err
64+
}
65+
for _, node := range nodes {
66+
vms, err := s.restclient.GetVirtualMachines(ctx, node.Node)
67+
if err != nil {
68+
return nil, err
69+
}
70+
for _, vm := range vms {
71+
config, err := s.restclient.GetVirtualMachineConfig(ctx, node.Node, vm.VMID)
72+
if err != nil {
73+
return nil, err
74+
}
75+
vmuuid, err := convertSMBiosToUUID(config.SMBios1)
76+
if err != nil {
77+
return nil, err
78+
}
79+
if vmuuid == uuid {
80+
return &VirtualMachine{restclient: s.restclient, vm: vm, node: node.Node, config: config}, nil
81+
}
82+
}
83+
}
84+
return nil, rest.NotFoundErr
85+
}
86+
87+
func convertSMBiosToUUID(smbios string) (string, error) {
88+
re := regexp.MustCompile(fmt.Sprintf("uuid=%s", UUIDFormat))
89+
match := re.FindString(smbios)
90+
if match == "" {
91+
return "", errors.New("failed to fetch uuid form smbios")
92+
}
93+
// match: uuid=<uuid>
94+
return strings.Split(match, "=")[1], nil
95+
}
96+
97+
func (c *VirtualMachine) GetConfig(ctx context.Context) (*api.VirtualMachineConfig, error) {
98+
if c.config != nil {
99+
return c.config, nil
100+
}
101+
config, err := c.restclient.GetVirtualMachineConfig(ctx, c.node, c.vm.VMID)
102+
if err != nil {
103+
return nil, err
104+
}
105+
c.config = config
106+
return c.config, err
107+
}
108+
109+
func (c *VirtualMachine) GetOSInfo(ctx context.Context) (*api.OSInfo, error) {
110+
var osInfo *api.OSInfo
111+
path := fmt.Sprintf("/nodes/%s/qemu/%d/agent/get-osinfo", c.node, c.vm.VMID)
112+
if err := c.restclient.Get(ctx, path, &osInfo); err != nil {
113+
return nil, err
114+
}
115+
return osInfo, nil
116+
}

proxmox/service.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package proxmox
2+
3+
import (
4+
"crypto/tls"
5+
"errors"
6+
"net/http"
7+
8+
"github.com/sp-yduck/proxmox-go/rest"
9+
)
10+
11+
type Service struct {
12+
restclient *rest.RESTClient
13+
}
14+
15+
type AuthConfig struct {
16+
Username string
17+
Password string
18+
TokenID string
19+
Secret string
20+
}
21+
22+
func NewService(url string, authConfig AuthConfig, insecure bool) (*Service, error) {
23+
var loginOption rest.ClientOption
24+
if authConfig.Username != "" && authConfig.Password != "" {
25+
loginOption = rest.WithUserPassword(authConfig.Username, authConfig.Password)
26+
} else if authConfig.TokenID != "" && authConfig.Secret != "" {
27+
loginOption = rest.WithAPIToken(authConfig.TokenID, authConfig.Secret)
28+
} else {
29+
return nil, errors.New("invalid authentication config")
30+
}
31+
clientOptions := []rest.ClientOption{loginOption}
32+
33+
if insecure {
34+
baseClient := &http.Client{
35+
Transport: &http.Transport{
36+
TLSClientConfig: &tls.Config{
37+
InsecureSkipVerify: true,
38+
},
39+
},
40+
}
41+
clientOptions = append(clientOptions, rest.WithClient(baseClient))
42+
}
43+
44+
restclient, err := rest.NewRESTClient(url, clientOptions...)
45+
if err != nil {
46+
return nil, err
47+
}
48+
return &Service{restclient: restclient}, nil
49+
}
50+
51+
func NewServiceWithUserPassword(url, user, password string, insecure bool) (*Service, error) {
52+
clientOptions := []rest.ClientOption{
53+
rest.WithUserPassword(user, password),
54+
}
55+
56+
if insecure {
57+
baseClient := &http.Client{
58+
Transport: &http.Transport{
59+
TLSClientConfig: &tls.Config{
60+
InsecureSkipVerify: true,
61+
},
62+
},
63+
}
64+
clientOptions = append(clientOptions, rest.WithClient(baseClient))
65+
}
66+
67+
restclient, err := rest.NewRESTClient(url, clientOptions...)
68+
if err != nil {
69+
return nil, err
70+
}
71+
return &Service{restclient: restclient}, nil
72+
}
73+
74+
func NewServiceWithAPIToken(url, tokenid, secret string, insecure bool) (*Service, error) {
75+
clientOptions := []rest.ClientOption{
76+
rest.WithAPIToken(tokenid, secret),
77+
}
78+
if insecure {
79+
baseClient := &http.Client{
80+
Transport: &http.Transport{
81+
TLSClientConfig: &tls.Config{
82+
InsecureSkipVerify: true,
83+
},
84+
},
85+
}
86+
clientOptions = append(clientOptions, rest.WithClient(baseClient))
87+
}
88+
89+
restclient, err := rest.NewRESTClient(url, clientOptions...)
90+
if err != nil {
91+
return nil, err
92+
}
93+
return &Service{restclient: restclient}, nil
94+
}
95+
96+
func (s *Service) RESTClient() *rest.RESTClient {
97+
return s.restclient
98+
}

0 commit comments

Comments
 (0)