1 package mapper
2
3 import (
4 "fmt"
5 "reflect"
6
7 corev1 "k8s.io/api/core/v1"
8 kr "k8s.io/apimachinery/pkg/api/resource"
9 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10 "k8s.io/apimachinery/pkg/runtime/schema"
11 virtv1 "kubevirt.io/api/core/v1"
12 virtbeta "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"
13
14 "edge-infra.dev/pkg/edge/api/graph/model"
15 nodemeta "edge-infra.dev/pkg/sds/ien/node"
16 )
17
18 var (
19 storageTypeVM = "local-path"
20 interfaceModelVM = "e1000"
21 nodeAffinityKeyVM = nodemeta.ClassLabel
22 nodeAffinityValuesVM = []string{model.TerminalClassTypeTouchpoint.String()}
23 )
24
25 func VirtualMachineToKubeVirtualMachine(virtualMachine *model.VirtualMachine) (virtv1.VirtualMachine, error) {
26 return virtualMachineToKubeVirtualMachine(virtualMachine)
27 }
28
29 func virtualMachineToKubeVirtualMachine(virtualMachine *model.VirtualMachine) (virtv1.VirtualMachine, error) {
30 virtualMachineSpec, err := getVirtualMachineSpec(virtualMachine)
31 if err != nil {
32 return virtv1.VirtualMachine{}, err
33 }
34 kubeVM := virtv1.VirtualMachine{
35 ObjectMeta: metav1.ObjectMeta{
36 Name: virtualMachine.Hostname,
37 Namespace: virtualMachine.Namespace,
38 Annotations: map[string]string{
39 "linkerd.io/inject": "disabled",
40 },
41 },
42 Spec: virtualMachineSpec,
43 Status: virtv1.VirtualMachineStatus{},
44 }
45
46 kubeVM.SetGroupVersionKind(schema.GroupVersionKind{
47 Group: virtv1.GroupVersion.Group,
48 Kind: reflect.TypeOf(virtv1.VirtualMachine{}).Name(),
49 Version: virtv1.GroupVersion.Version,
50 })
51
52 return kubeVM, nil
53 }
54
55 func getKubeVMInstanceSpec(virtualMachine *model.VirtualMachine) (virtv1.VirtualMachineInstanceSpec, error) {
56 memoryQuantity, err := kr.ParseQuantity(virtualMachine.Memory)
57 if err != nil {
58 return virtv1.VirtualMachineInstanceSpec{}, err
59 }
60
61 return virtv1.VirtualMachineInstanceSpec{
62 Domain: virtv1.DomainSpec{
63 CPU: &virtv1.CPU{
64 Cores: uint32(virtualMachine.Cpus),
65 },
66 Memory: &virtv1.Memory{
67 Guest: &memoryQuantity,
68 },
69 Devices: virtv1.Devices{
70 ClientPassthrough: &virtv1.ClientPassthroughDevices{},
71 Disks: virtualMachineDisksToKubeDisks(virtualMachine.Disks, virtualMachine.Hostname),
72 Interfaces: []virtv1.Interface{
73 {
74 Model: interfaceModelVM,
75 Name: "default",
76 InterfaceBindingMethod: virtv1.InterfaceBindingMethod{
77 Masquerade: &virtv1.InterfaceMasquerade{},
78 },
79 },
80 },
81 },
82 Machine: &virtv1.Machine{
83 Type: string(virtualMachine.MachineType),
84 },
85 },
86 Networks: []virtv1.Network{
87 {
88 Name: "default",
89 NetworkSource: virtv1.NetworkSource{
90 Pod: &virtv1.PodNetwork{},
91 },
92 },
93 },
94 Volumes: virtualMachineDisksToKubeVolumes(virtualMachine.Disks, virtualMachine.Hostname),
95 Affinity: getVirtualMachineAffinity(),
96 }, nil
97 }
98
99 func virtualMachineDisksToKubeDisks(disks []*model.VirtualMachineDisk, hostname string) []virtv1.Disk {
100 kubeDisks := []virtv1.Disk{}
101 for _, disk := range disks {
102 bootOrder := uint(disk.BootOrder)
103 kubeDisk := virtv1.Disk{
104 BootOrder: &bootOrder,
105 Name: getKubeDiskName(hostname, disk.DiskID),
106 DiskDevice: getKubeDiskDevice(disk),
107 }
108 kubeDisks = append(kubeDisks, kubeDisk)
109 }
110 return kubeDisks
111 }
112
113 func virtualMachineDisksToKubeVolumes(disks []*model.VirtualMachineDisk, hostname string) []virtv1.Volume {
114 kubeVolumes := []virtv1.Volume{}
115 for _, disk := range disks {
116 volumeName := getKubeDiskName(hostname, disk.DiskID)
117 kubeVolume := virtv1.Volume{
118 Name: volumeName,
119 VolumeSource: virtv1.VolumeSource{
120 DataVolume: &virtv1.DataVolumeSource{
121 Name: volumeName,
122 },
123 },
124 }
125 kubeVolumes = append(kubeVolumes, kubeVolume)
126 }
127 return kubeVolumes
128 }
129
130 func getKubeDiskName(hostname string, diskID string) string {
131 return fmt.Sprintf("%s-%s", hostname, diskID)
132 }
133
134 func getKubeDiskDevice(disk *model.VirtualMachineDisk) virtv1.DiskDevice {
135 diskDevice := virtv1.DiskDevice{}
136 diskBus := virtv1.DiskBus(disk.Bus)
137 switch disk.Type {
138 case model.DiskTypeCdrom:
139 diskDevice.CDRom = &virtv1.CDRomTarget{
140 Bus: diskBus,
141 }
142 case model.DiskTypeDisk:
143 diskDevice.Disk = &virtv1.DiskTarget{
144 Bus: diskBus,
145 }
146 }
147 return diskDevice
148 }
149
150 func getKubeDataVolumeSource(containerImageURL string) *virtbeta.DataVolumeSource {
151 if containerImageURL == "" {
152 return &virtbeta.DataVolumeSource{
153 Blank: &virtbeta.DataVolumeBlankImage{},
154 }
155 }
156
157 pullMethod := virtbeta.RegistryPullNode
158 return &virtbeta.DataVolumeSource{
159 Registry: &virtbeta.DataVolumeSourceRegistry{
160 URL: &containerImageURL,
161 PullMethod: &pullMethod,
162 },
163 }
164 }
165
166 func getKubeVMDataVolumeTemplates(virtualMachine *model.VirtualMachine) ([]virtv1.DataVolumeTemplateSpec, error) {
167 storageClassName := storageTypeVM
168 volumeTemplates := []virtv1.DataVolumeTemplateSpec{}
169 for _, disk := range virtualMachine.Disks {
170 sizeQuantity, err := kr.ParseQuantity(disk.Size)
171 if err != nil {
172 return []virtv1.DataVolumeTemplateSpec{}, err
173 }
174 volumeTemplate := virtv1.DataVolumeTemplateSpec{
175 ObjectMeta: metav1.ObjectMeta{
176 Name: getKubeDiskName(virtualMachine.Hostname, disk.DiskID),
177 Annotations: map[string]string{
178 "linkerd.io/inject": "disabled",
179 },
180 },
181 Spec: virtbeta.DataVolumeSpec{
182 PVC: &corev1.PersistentVolumeClaimSpec{
183 AccessModes: []corev1.PersistentVolumeAccessMode{
184 corev1.ReadWriteOnce,
185 },
186 StorageClassName: &storageClassName,
187 Resources: corev1.VolumeResourceRequirements{
188 Requests: map[corev1.ResourceName]kr.Quantity{
189 corev1.ResourceStorage: sizeQuantity,
190 },
191 },
192 },
193 Source: getKubeDataVolumeSource(disk.ContainerImageURL),
194 },
195 }
196 volumeTemplates = append(volumeTemplates, volumeTemplate)
197 }
198 return volumeTemplates, nil
199 }
200
201 func getVirtualMachineSpec(virtualMachine *model.VirtualMachine) (virtv1.VirtualMachineSpec, error) {
202 kubeVMInstanceSpec, err := getKubeVMInstanceSpec(virtualMachine)
203 if err != nil {
204 return virtv1.VirtualMachineSpec{}, err
205 }
206 dataVolumeTemplates, err := getKubeVMDataVolumeTemplates(virtualMachine)
207 if err != nil {
208 return virtv1.VirtualMachineSpec{}, err
209 }
210 return virtv1.VirtualMachineSpec{
211 Running: &virtualMachine.TargetPowerState,
212 Template: &virtv1.VirtualMachineInstanceTemplateSpec{
213 ObjectMeta: metav1.ObjectMeta{
214 Labels: map[string]string{
215 "kubevirt.io/domain": virtualMachine.Hostname,
216 },
217 Annotations: map[string]string{
218 "linkerd.io/inject": "disabled",
219 },
220 },
221 Spec: kubeVMInstanceSpec,
222 },
223 DataVolumeTemplates: dataVolumeTemplates,
224 }, nil
225 }
226
227 func getVirtualMachineAffinity() *corev1.Affinity {
228 return &corev1.Affinity{
229 NodeAffinity: &corev1.NodeAffinity{
230 RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{
231 NodeSelectorTerms: []corev1.NodeSelectorTerm{
232 {
233 MatchExpressions: []corev1.NodeSelectorRequirement{
234 {
235 Key: nodeAffinityKeyVM,
236 Operator: corev1.NodeSelectorOpIn,
237 Values: nodeAffinityValuesVM,
238 },
239 },
240 },
241 },
242 },
243 },
244 }
245 }
246
View as plain text