Skip to content

Commit 7c9d33c

Browse files
authored
Merge pull request #104 from sp-yduck/feature/storage
fix #93 : support different types of storages
2 parents cd2051d + 2f47c17 commit 7c9d33c

14 files changed

+126
-46
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ RELEASE_DIR := $(shell pwd)/out
196196
$(RELEASE_DIR):
197197
mkdir -p $(RELEASE_DIR)
198198

199-
RELEASE_TAG := $(shell git describe --abbrev=0 2>/dev/null)
199+
RELEASE_TAG := $(shell git describe --abbrev=0 --tags)
200200

201201
.PHONY: release
202202
release: ## Builds all the manifests/config files to publish with a release

api/v1beta1/proxmoxcluster_types.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,8 @@ type ProxmoxClusterSpec struct {
3535
// ServerRef is used for configuring Proxmox client
3636
ServerRef ServerRef `json:"serverRef"`
3737

38-
// storage is for proxmox storage used by vm instances
39-
// +optional
40-
Storage Storage `json:"storage"`
38+
// storage is used for storing cloud init snippet
39+
Storage Storage `json:"storage,omitempty"`
4140
}
4241

4342
// ProxmoxClusterStatus defines the observed state of ProxmoxCluster

api/v1beta1/proxmoxmachine_types.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,22 @@ type ProxmoxMachineSpec struct {
3434
ProviderID *string `json:"providerID,omitempty"`
3535

3636
// Node is proxmox node hosting vm instance which used for ProxmoxMachine
37-
// +optional
3837
Node string `json:"node,omitempty"`
3938

39+
// Storage is name of proxmox storage used by this node.
40+
// The storage must support "images(VM Disks)" type of content.
41+
// cappx will use random storage if empty
42+
Storage string `json:"storage,omitempty"`
43+
4044
// +kubebuilder:validation:Minimum:=0
4145
// VMID is proxmox qemu's id
42-
// +optional
4346
VMID *int `json:"vmID,omitempty"`
4447

4548
// Image is the image to be provisioned
4649
Image Image `json:"image"`
4750

4851
// CloudInit defines options related to the bootstrapping systems where
4952
// CloudInit is used.
50-
// +optional
5153
CloudInit CloudInit `json:"cloudInit,omitempty"`
5254

5355
// Hardware
@@ -56,8 +58,7 @@ type ProxmoxMachineSpec struct {
5658
// Network
5759
Network Network `json:"network,omitempty"`
5860

59-
// Options
60-
// +optional
61+
// Options for QEMU instance
6162
Options Options `json:"options,omitempty"`
6263

6364
// FailureDomain is the failure domain unique identifier this Machine should be attached to, as defined in Cluster API.
@@ -94,7 +95,9 @@ type ProxmoxMachineStatus struct {
9495
//+kubebuilder:subresource:status
9596
// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels.cluster\\.x-k8s\\.io/cluster-name",description="Cluster to which this VSphereMachine belongs"
9697
// +kubebuilder:printcolumn:name="Machine",type="string",JSONPath=".metadata.ownerReferences[?(@.kind==\"Machine\")].name",description="Machine object which owns with this ProxmoxMachine",priority=1
97-
// +kubebuilder:printcolumn:name="vmid",type=string,JSONPath=`.spec.vmID`,priority=1
98+
// +kubebuilder:printcolumn:name="VMID",type=string,JSONPath=`.spec.vmID`,priority=1
99+
// +kubebuilder:printcolumn:name="Node",type=string,JSONPath=`.spec.node`,priority=1
100+
// +kubebuilder:printcolumn:name="Storage",type=string,JSONPath=`.spec.storage`,priority=1
98101
// +kubebuilder:printcolumn:name="ProviderID",type=string,JSONPath=`.spec.providerID`
99102
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.instanceStatus`
100103
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of Machine"

cloud/interfaces.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ type MachineGetter interface {
5757
GetProviderID() string
5858
GetBootstrapData() (string, error)
5959
GetInstanceStatus() *infrav1.InstanceStatus
60-
GetStorage() infrav1.Storage
60+
GetClusterStorage() infrav1.Storage
61+
GetStorage() string
6162
GetCloudInit() infrav1.CloudInit
6263
GetNetwork() infrav1.Network
6364
GetHardware() infrav1.Hardware
@@ -72,6 +73,7 @@ type MachineSetter interface {
7273
SetNodeName(name string)
7374
SetVMID(vmid int)
7475
SetConfigStatus(config api.VirtualMachineConfig)
76+
SetStorage(name string)
7577
// SetFailureMessage(v error)
7678
// SetFailureReason(v capierrors.MachineStatusError)
7779
// SetAnnotation(key, value string)

cloud/scope/cluster.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package scope
1818

1919
import (
2020
"context"
21+
"fmt"
2122

2223
"github.com/pkg/errors"
2324
"github.com/sp-yduck/proxmox-go/proxmox"
@@ -26,6 +27,7 @@ import (
2627
"sigs.k8s.io/controller-runtime/pkg/client"
2728

2829
infrav1 "github.com/sp-yduck/cluster-api-provider-proxmox/api/v1beta1"
30+
"github.com/sp-yduck/cluster-api-provider-proxmox/cloud/services/compute/storage"
2931
)
3032

3133
type ClusterScopeParams struct {
@@ -92,7 +94,14 @@ func (s *ClusterScope) ControlPlaneEndpoint() clusterv1.APIEndpoint {
9294
return s.ProxmoxCluster.Spec.ControlPlaneEndpoint
9395
}
9496

97+
// return default values if they are not specified
9598
func (s *ClusterScope) Storage() infrav1.Storage {
99+
if s.ProxmoxCluster.Spec.Storage.Name == "" {
100+
s.ProxmoxCluster.Spec.Storage.Name = fmt.Sprintf("local-dir-%s", s.Name())
101+
}
102+
if s.ProxmoxCluster.Spec.Storage.Path == "" {
103+
s.ProxmoxCluster.Spec.Storage.Path = fmt.Sprintf("%s/%s", storage.DefaultBasePath, s.ProxmoxCluster.Spec.Storage.Name)
104+
}
96105
return s.ProxmoxCluster.Spec.Storage
97106
}
98107

cloud/scope/machine.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,12 @@ func (m *MachineScope) CloudClient() *proxmox.Service {
8383
return m.ClusterGetter.CloudClient()
8484
}
8585

86-
func (m *MachineScope) GetStorage() infrav1.Storage {
87-
return m.ClusterGetter.ProxmoxCluster.Spec.Storage
86+
func (m *MachineScope) GetClusterStorage() infrav1.Storage {
87+
return m.ClusterGetter.Storage()
88+
}
89+
90+
func (m *MachineScope) GetStorage() string {
91+
return m.ProxmoxMachine.Spec.Storage
8892
}
8993

9094
func (m *MachineScope) Name() string {
@@ -103,6 +107,10 @@ func (m *MachineScope) SetNodeName(name string) {
103107
m.ProxmoxMachine.Spec.Node = name
104108
}
105109

110+
func (m *MachineScope) SetStorage(name string) {
111+
m.ProxmoxMachine.Spec.Storage = name
112+
}
113+
106114
// func (m *MachineScope) Client() Compute {
107115
// return m.ClusterGetter.Client()
108116
// }

cloud/services/compute/instance/cloudinit.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func (s *Service) reconcileCloudInit(ctx context.Context) error {
3030

3131
// delete CloudConfig
3232
func (s *Service) deleteCloudConfig(ctx context.Context) error {
33-
storageName := s.scope.GetStorage().Name
33+
storageName := s.scope.GetClusterStorage().Name
3434
path := userSnippetPath(s.scope.Name())
3535
volumeID := fmt.Sprintf("%s:%s", storageName, path)
3636

@@ -79,7 +79,7 @@ func (s *Service) reconcileCloudInitUser(ctx context.Context) error {
7979
return err
8080
}
8181
defer vnc.Close()
82-
filePath := fmt.Sprintf("%s/%s", s.scope.GetStorage().Path, userSnippetPath(vmName))
82+
filePath := fmt.Sprintf("%s/%s", s.scope.GetClusterStorage().Path, userSnippetPath(vmName))
8383
if err := vnc.WriteFile(context.TODO(), configYaml, filePath); err != nil {
8484
return errors.Errorf("failed to write file error : %v", err)
8585
}

cloud/services/compute/instance/image.go

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,14 @@ func (s *Service) reconcileBootDevice(ctx context.Context, vm *proxmox.VirtualMa
3636
}
3737

3838
// setCloudImage downloads OS image into Proxmox node
39-
// and then sets it to specified storage
39+
// so that proxmox can import image to the storage from there
4040
func (s *Service) setCloudImage(ctx context.Context) error {
4141
log := log.FromContext(ctx)
4242
log.Info("setting cloud image")
4343

4444
image := s.scope.GetImage()
4545
rawImageFilePath := rawImageFilePath(image)
4646

47-
// workaround
48-
// API does not support something equivalent of "qm importdisk"
4947
vnc, err := s.vncClient(s.scope.NodeName())
5048
if err != nil {
5149
return errors.Errorf("failed to create vnc client: %v", err)
@@ -68,14 +66,6 @@ func (s *Service) setCloudImage(ctx context.Context) error {
6866
return errors.Errorf("failed to confirm checksum: %v", err)
6967
}
7068
}
71-
72-
// convert downloaded image to raw format and set it to storage
73-
vmid := s.scope.GetVMID()
74-
destPath := fmt.Sprintf("%s/images/%d/vm-%d-disk-0.raw", s.scope.GetStorage().Path, *vmid, *vmid)
75-
out, _, err := vnc.Exec(context.TODO(), fmt.Sprintf("/usr/bin/qemu-img convert -O raw %s %s", rawImageFilePath, destPath))
76-
if err != nil {
77-
return errors.Errorf("failed to convert iamge : %s : %v", out, err)
78-
}
7969
return nil
8070
}
8171

cloud/services/compute/instance/qemu.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ func (s *Service) getQEMU(ctx context.Context, vmid *int) (*proxmox.VirtualMachi
4848
func (s *Service) createQEMU(ctx context.Context, nodeName string, vmid *int) (*proxmox.VirtualMachine, error) {
4949
log := log.FromContext(ctx)
5050

51+
if err := s.ensureStorageAvailable(ctx); err != nil {
52+
return nil, err
53+
}
54+
5155
// get node
5256
if nodeName == "" {
5357
// temp solution
@@ -107,10 +111,15 @@ func (s *Service) getRandomNode(ctx context.Context) (*api.Node, error) {
107111

108112
func (s *Service) generateVMOptions() api.VirtualMachineCreateOptions {
109113
vmName := s.scope.Name()
110-
storageName := s.scope.GetStorage().Name
114+
snippetStorageName := s.scope.GetClusterStorage().Name
115+
imageStorageName := s.scope.GetStorage()
111116
network := s.scope.GetNetwork()
112117
hardware := s.scope.GetHardware()
113118
options := s.scope.GetOptions()
119+
cicustom := fmt.Sprintf("user=%s:%s", snippetStorageName, userSnippetPath(vmName))
120+
ide2 := fmt.Sprintf("file=%s:cloudinit,media=cdrom", imageStorageName)
121+
scsi0 := fmt.Sprintf("%s:0,import-from=%s", imageStorageName, rawImageFilePath(s.scope.GetImage()))
122+
net0 := "model=virtio,bridge=vmbr0,firewall=1"
114123

115124
vmoptions := api.VirtualMachineCreateOptions{
116125
ACPI: boolToInt8(options.ACPI),
@@ -119,12 +128,12 @@ func (s *Service) generateVMOptions() api.VirtualMachineCreateOptions {
119128
Balloon: options.Balloon,
120129
BIOS: string(hardware.BIOS),
121130
Boot: fmt.Sprintf("order=%s", bootDvice),
122-
CiCustom: fmt.Sprintf("user=%s:%s", storageName, userSnippetPath(vmName)),
131+
CiCustom: cicustom,
123132
Cores: hardware.CPU,
124133
CpuLimit: hardware.CPULimit,
125134
Description: options.Description,
126135
HugePages: options.HugePages.String(),
127-
Ide: api.Ide{Ide2: fmt.Sprintf("file=%s:cloudinit,media=cdrom", storageName)},
136+
Ide: api.Ide{Ide2: ide2},
128137
IPConfig: api.IPConfig{IPConfig0: network.IPConfig.String()},
129138
KeepHugePages: boolToInt8(options.KeepHugePages),
130139
KVM: boolToInt8(options.KVM),
@@ -133,13 +142,13 @@ func (s *Service) generateVMOptions() api.VirtualMachineCreateOptions {
133142
Memory: hardware.Memory,
134143
Name: vmName,
135144
NameServer: network.NameServer,
136-
Net: api.Net{Net0: "model=virtio,bridge=vmbr0,firewall=1"},
145+
Net: api.Net{Net0: net0},
137146
Numa: boolToInt8(options.NUMA),
138147
OnBoot: boolToInt8(options.OnBoot),
139148
OSType: api.OSType(options.OSType),
140149
Protection: boolToInt8(options.Protection),
141150
Reboot: int(boolToInt8(options.Reboot)),
142-
Scsi: api.Scsi{Scsi0: fmt.Sprintf("file=%s:8", storageName)},
151+
Scsi: api.Scsi{Scsi0: scsi0},
143152
ScsiHw: api.VirtioScsiPci,
144153
SearchDomain: network.SearchDomain,
145154
Serial: api.Serial{Serial0: "socket"},
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package instance
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/sp-yduck/proxmox-go/api"
9+
)
10+
11+
// make sure storage exists and supports "images" type of content
12+
func (s *Service) ensureStorageAvailable(ctx context.Context) error {
13+
storageName := s.scope.GetStorage()
14+
if storageName == "" { // no storage specified, find available storage
15+
storage, err := s.findVMStorage(ctx)
16+
if err != nil {
17+
return err
18+
}
19+
storageName = storage.Storage
20+
s.scope.SetStorage(storageName)
21+
} else { // storage specified, check if it supports "images" type of content
22+
storage, err := s.client.RESTClient().GetStorage(ctx, storageName)
23+
if err != nil {
24+
return err
25+
}
26+
if supportsImage(storage) {
27+
return fmt.Errorf("storage %s does not support \"images\" type of content", storageName)
28+
}
29+
}
30+
return nil
31+
}
32+
33+
// get one storage supporting "images" type of content
34+
func (s *Service) findVMStorage(ctx context.Context) (*api.Storage, error) {
35+
storages, err := s.client.RESTClient().GetStorages(ctx)
36+
if err != nil {
37+
return nil, err
38+
}
39+
for _, storage := range storages {
40+
if supportsImage(storage) {
41+
return storage, nil
42+
}
43+
}
44+
return nil, fmt.Errorf("no available storage")
45+
}
46+
47+
func supportsImage(storage *api.Storage) bool {
48+
return strings.Contains(storage.Content, "images")
49+
}

0 commit comments

Comments
 (0)