Skip to content

Commit 9887456

Browse files
committed
SMM is not enabled when TPM is enabled in VMware source client
Related to: harvester/harvester#7984 Signed-off-by: Volker Theile <[email protected]>
1 parent 5288366 commit 9887456

File tree

5 files changed

+190
-54
lines changed

5 files changed

+190
-54
lines changed

pkg/source/helper.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package source
2+
3+
import (
4+
"k8s.io/utils/pointer"
5+
kubevirt "kubevirt.io/api/core/v1"
6+
)
7+
8+
func VMSpecSetupUEFISettings(vmSpec *kubevirt.VirtualMachineSpec, secureBoot, tpm bool) {
9+
firmware := &kubevirt.Firmware{
10+
Bootloader: &kubevirt.Bootloader{
11+
EFI: &kubevirt.EFI{
12+
SecureBoot: pointer.Bool(false),
13+
},
14+
},
15+
}
16+
if secureBoot {
17+
firmware.Bootloader.EFI.SecureBoot = pointer.Bool(true)
18+
}
19+
vmSpec.Template.Spec.Domain.Firmware = firmware
20+
if tpm {
21+
vmSpec.Template.Spec.Domain.Devices.TPM = &kubevirt.TPMDevice{}
22+
}
23+
if secureBoot || tpm {
24+
vmSpec.Template.Spec.Domain.Features.SMM = &kubevirt.FeatureState{
25+
Enabled: pointer.Bool(true),
26+
}
27+
}
28+
}

pkg/source/helper_test.go

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package source
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
kubevirt "kubevirt.io/api/core/v1"
8+
)
9+
10+
func Test_vmSpecSetupUefiSettings(t *testing.T) {
11+
assert := require.New(t)
12+
testCases := []struct {
13+
desc string
14+
secureBoot bool
15+
tpm bool
16+
}{
17+
{
18+
desc: "SecureBoot enabled, TPM disabled",
19+
secureBoot: true,
20+
tpm: false,
21+
}, {
22+
desc: "SecureBoot disabled, TPM enabled",
23+
secureBoot: false,
24+
tpm: true,
25+
}, {
26+
desc: "SecureBoot enabled, TPM enabled",
27+
secureBoot: true,
28+
tpm: true,
29+
}, {
30+
desc: "SecureBoot disabled, TPM disabled",
31+
secureBoot: false,
32+
tpm: false,
33+
},
34+
}
35+
36+
for _, tc := range testCases {
37+
vmSpec := kubevirt.VirtualMachineSpec{
38+
Template: &kubevirt.VirtualMachineInstanceTemplateSpec{
39+
Spec: kubevirt.VirtualMachineInstanceSpec{
40+
Domain: kubevirt.DomainSpec{
41+
Features: &kubevirt.Features{},
42+
},
43+
},
44+
},
45+
}
46+
VMSpecSetupUEFISettings(&vmSpec, tc.secureBoot, tc.tpm)
47+
if tc.secureBoot {
48+
assert.True(*vmSpec.Template.Spec.Domain.Firmware.Bootloader.EFI.SecureBoot, "expected SecureBoot to be enabled")
49+
} else {
50+
assert.False(*vmSpec.Template.Spec.Domain.Firmware.Bootloader.EFI.SecureBoot, "expected SecureBoot to be disabled")
51+
}
52+
if tc.secureBoot || tc.tpm {
53+
assert.True(*vmSpec.Template.Spec.Domain.Features.SMM.Enabled, "expected SMM to be enabled")
54+
} else {
55+
assert.Nil(vmSpec.Template.Spec.Domain.Features.SMM, "expected SMM to be nil")
56+
}
57+
}
58+
}

pkg/source/openstack/client.go

+7-24
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ import (
2727
corev1 "k8s.io/api/core/v1"
2828
"k8s.io/apimachinery/pkg/api/resource"
2929
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
"k8s.io/utils/pointer"
3031
kubevirt "kubevirt.io/api/core/v1"
3132

3233
migration "github.com/harvester/vm-import-controller/pkg/apis/migration.harvesterhci.io/v1beta1"
3334
"github.com/harvester/vm-import-controller/pkg/server"
35+
"github.com/harvester/vm-import-controller/pkg/source"
3436
)
3537

3638
const (
@@ -409,8 +411,6 @@ func (c *Client) IsPoweredOff(vm *migration.VirtualMachineImport) (bool, error)
409411
}
410412

411413
func (c *Client) GenerateVirtualMachine(vm *migration.VirtualMachineImport) (*kubevirt.VirtualMachine, error) {
412-
var boolFalse = false
413-
var boolTrue = true
414414
vmObj, err := c.findVM(vm.Spec.VirtualMachineName)
415415
if err != nil {
416416
return nil, fmt.Errorf("error finding VM in GenerateVirtualMachine: %v", err)
@@ -421,7 +421,7 @@ func (c *Client) GenerateVirtualMachine(vm *migration.VirtualMachineImport) (*ku
421421
return nil, fmt.Errorf("error looking up flavor: %v", err)
422422
}
423423

424-
uefi, tpm, secureboot, err := c.ImageFirmwareSettings(&vmObj.Server)
424+
uefi, tpm, secureBoot, err := c.ImageFirmwareSettings(&vmObj.Server)
425425
if err != nil {
426426
return nil, fmt.Errorf("error getting firware settings: %v", err)
427427
}
@@ -471,7 +471,7 @@ func (c *Client) GenerateVirtualMachine(vm *migration.VirtualMachineImport) (*ku
471471
},
472472
Features: &kubevirt.Features{
473473
ACPI: kubevirt.FeatureState{
474-
Enabled: &boolTrue,
474+
Enabled: pointer.Bool(true),
475475
},
476476
},
477477
},
@@ -520,32 +520,15 @@ func (c *Client) GenerateVirtualMachine(vm *migration.VirtualMachineImport) (*ku
520520
})
521521
}
522522

523+
// Setup BIOS/EFI, SecureBoot and TPM settings.
523524
if uefi {
524-
firmware := &kubevirt.Firmware{
525-
Bootloader: &kubevirt.Bootloader{
526-
EFI: &kubevirt.EFI{
527-
SecureBoot: &boolFalse,
528-
},
529-
},
530-
}
531-
if secureboot {
532-
firmware.Bootloader.EFI.SecureBoot = &boolTrue
533-
vmSpec.Template.Spec.Domain.Features.SMM = &kubevirt.FeatureState{
534-
Enabled: &boolTrue,
535-
}
536-
}
537-
vmSpec.Template.Spec.Domain.Firmware = firmware
538-
if tpm {
539-
vmSpec.Template.Spec.Domain.Features.SMM = &kubevirt.FeatureState{
540-
Enabled: &boolTrue,
541-
}
542-
vmSpec.Template.Spec.Domain.Devices.TPM = &kubevirt.TPMDevice{}
543-
}
525+
source.VMSpecSetupUEFISettings(&vmSpec, secureBoot, tpm)
544526
}
545527

546528
vmSpec.Template.Spec.Networks = networkConfig
547529
vmSpec.Template.Spec.Domain.Devices.Interfaces = interfaces
548530
newVM.Spec = vmSpec
531+
549532
// disk attachment needs query by core controller for storage classes, so will be added by the migration controller
550533
return newVM, nil
551534
}

pkg/source/vmware/client.go

+12-26
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ import (
2121
corev1 "k8s.io/api/core/v1"
2222
"k8s.io/apimachinery/pkg/api/resource"
2323
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
"k8s.io/utils/pointer"
2425
kubevirt "kubevirt.io/api/core/v1"
2526

2627
migration "github.com/harvester/vm-import-controller/pkg/apis/migration.harvesterhci.io/v1beta1"
2728
"github.com/harvester/vm-import-controller/pkg/qemu"
2829
"github.com/harvester/vm-import-controller/pkg/server"
30+
"github.com/harvester/vm-import-controller/pkg/source"
2931
)
3032

3133
type Client struct {
@@ -293,12 +295,9 @@ func (c *Client) IsPoweredOff(vm *migration.VirtualMachineImport) (bool, error)
293295
}
294296

295297
func (c *Client) GenerateVirtualMachine(vm *migration.VirtualMachineImport) (*kubevirt.VirtualMachine, error) {
296-
var boolFalse = false
297-
var boolTrue = true
298-
299298
vmObj, err := c.findVM(vm.Spec.Folder, vm.Spec.VirtualMachineName)
300299
if err != nil {
301-
return nil, fmt.Errorf("error quering vm in GenerateVirtualMachine: %v", err)
300+
return nil, fmt.Errorf("error querying vm in GenerateVirtualMachine: %v", err)
302301
}
303302

304303
newVM := &kubevirt.VirtualMachine{
@@ -344,7 +343,7 @@ func (c *Client) GenerateVirtualMachine(vm *migration.VirtualMachineImport) (*ku
344343
},
345344
Features: &kubevirt.Features{
346345
ACPI: kubevirt.FeatureState{
347-
Enabled: &boolTrue,
346+
Enabled: pointer.Bool(true),
348347
},
349348
},
350349
},
@@ -393,31 +392,18 @@ func (c *Client) GenerateVirtualMachine(vm *migration.VirtualMachineImport) (*ku
393392
})
394393
}
395394

396-
// setup bios/efi, secureboot and tpm settings
397-
398-
if o.Config.Firmware == "efi" {
399-
firmware := &kubevirt.Firmware{
400-
Bootloader: &kubevirt.Bootloader{
401-
EFI: &kubevirt.EFI{
402-
SecureBoot: &boolFalse,
403-
},
404-
},
405-
}
406-
if *o.Config.BootOptions.EfiSecureBootEnabled {
407-
firmware.Bootloader.EFI.SecureBoot = &boolTrue
408-
vmSpec.Template.Spec.Domain.Features.SMM = &kubevirt.FeatureState{
409-
Enabled: &boolTrue,
410-
}
411-
}
412-
vmSpec.Template.Spec.Domain.Firmware = firmware
413-
if *o.Summary.Config.TpmPresent {
414-
415-
vmSpec.Template.Spec.Domain.Devices.TPM = &kubevirt.TPMDevice{}
416-
}
395+
// Setup BIOS/EFI, SecureBoot and TPM settings.
396+
uefi := o.Config.Firmware == "efi"
397+
secureBoot := pointer.BoolDeref(o.Config.BootOptions.EfiSecureBootEnabled, false)
398+
tpm := pointer.BoolDeref(o.Summary.Config.TpmPresent, false)
399+
if uefi {
400+
source.VMSpecSetupUEFISettings(&vmSpec, secureBoot, tpm)
417401
}
402+
418403
vmSpec.Template.Spec.Networks = networkConfig
419404
vmSpec.Template.Spec.Domain.Devices.Interfaces = interfaces
420405
newVM.Spec = vmSpec
406+
421407
// disk attachment needs query by core controller for storage classes, so will be added by the migration controller
422408
return newVM, nil
423409
}

pkg/source/vmware/client_test.go

+85-4
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ import (
1010

1111
"github.com/ory/dockertest/v3"
1212
"github.com/stretchr/testify/require"
13+
"github.com/vmware/govmomi/find"
1314
"github.com/vmware/govmomi/vim25/mo"
15+
"github.com/vmware/govmomi/vim25/types"
1416
corev1 "k8s.io/api/core/v1"
1517
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18+
"k8s.io/utils/pointer"
1619

1720
migration "github.com/harvester/vm-import-controller/pkg/apis/migration.harvesterhci.io/v1beta1"
1821
"github.com/harvester/vm-import-controller/pkg/server"
@@ -27,10 +30,11 @@ func TestMain(t *testing.M) {
2730
log.Fatalf("error connecting to dockerd: %v", err)
2831
}
2932

33+
// https://hub.docker.com/r/vmware/vcsim
3034
runOpts := &dockertest.RunOptions{
3135
Name: "vcsim",
3236
Repository: "vmware/vcsim",
33-
Tag: "v0.29.0",
37+
Tag: "v0.49.0",
3438
}
3539

3640
vcsimMock, err := pool.RunWithOptions(runOpts)
@@ -202,7 +206,7 @@ func Test_ExportVirtualMachine(t *testing.T) {
202206
}
203207

204208
err = c.ExportVirtualMachine(vm)
205-
assert.NoError(err, "expected no error during vm export")
209+
assert.NoError(err, "expected no error during VM export")
206210
t.Log(vm.Status)
207211
}
208212

@@ -247,14 +251,91 @@ func Test_GenerateVirtualMachine(t *testing.T) {
247251
}
248252

249253
newVM, err := c.GenerateVirtualMachine(vm)
250-
assert.NoError(err, "expected no error during vm export")
254+
assert.NoError(err, "expected no error during VM CR generation")
251255
assert.Len(newVM.Spec.Template.Spec.Networks, 1, "should have found the default pod network")
252256
assert.Len(newVM.Spec.Template.Spec.Domain.Devices.Interfaces, 1, "should have found a network map")
253257
assert.Equal(newVM.Spec.Template.Spec.Domain.Memory.Guest.String(), "32M", "expected VM to have 32M memory")
254258
assert.NotEmpty(newVM.Spec.Template.Spec.Domain.Resources.Limits, "expect to find resource requests to be present")
255259

256260
}
257261

262+
func Test_GenerateVirtualMachine_secureboot(t *testing.T) {
263+
assert := require.New(t)
264+
ctx := context.TODO()
265+
endpoint := fmt.Sprintf("https://localhost:%s/sdk", vcsimPort)
266+
secret := &corev1.Secret{
267+
ObjectMeta: metav1.ObjectMeta{
268+
Name: "test",
269+
Namespace: "default",
270+
},
271+
Data: map[string][]byte{
272+
"username": []byte("user"),
273+
"password": []byte("pass"),
274+
},
275+
}
276+
vm := &migration.VirtualMachineImport{
277+
ObjectMeta: metav1.ObjectMeta{
278+
Name: "demo",
279+
Namespace: "default",
280+
},
281+
Spec: migration.VirtualMachineImportSpec{
282+
SourceCluster: corev1.ObjectReference{},
283+
VirtualMachineName: "test01",
284+
Mapping: []migration.NetworkMapping{
285+
{
286+
SourceNetwork: "DVSwitch: fea97929-4b2d-5972-b146-930c6d0b4014",
287+
DestinationNetwork: "default/vlan",
288+
},
289+
},
290+
},
291+
}
292+
293+
c, err := NewClient(ctx, endpoint, "DC0", secret)
294+
assert.NoError(err, "expected no error during creation of client")
295+
err = c.Verify()
296+
assert.NoError(err, "expected no error during verification of client")
297+
298+
// https://github.com/vmware/govmomi/blob/main/vcsim/README.md#default-vcenter-inventory
299+
f := find.NewFinder(c.Client.Client, true)
300+
301+
dc, err := f.Datacenter(ctx, c.dc)
302+
assert.NoError(err, "expected no error during datacenter lookup")
303+
304+
f.SetDatacenter(dc)
305+
306+
ds, err := f.DefaultDatastore(ctx)
307+
assert.NoError(err, "expected no error during datastore lookup")
308+
309+
pool, err := f.ResourcePool(ctx, "DC0_H0/Resources")
310+
assert.NoError(err, "expected no error during resource pool lookup")
311+
312+
folder, err := dc.Folders(ctx)
313+
assert.NoError(err, "expected no error during folder lookup")
314+
315+
vmConfigSpec := types.VirtualMachineConfigSpec{
316+
Name: vm.Spec.VirtualMachineName,
317+
GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest64),
318+
Firmware: string(types.GuestOsDescriptorFirmwareTypeEfi),
319+
BootOptions: &types.VirtualMachineBootOptions{
320+
EfiSecureBootEnabled: pointer.Bool(true),
321+
},
322+
Files: &types.VirtualMachineFileInfo{
323+
VmPathName: fmt.Sprintf("[%s] %s", ds.Name(), vm.Spec.VirtualMachineName),
324+
},
325+
}
326+
327+
task, err := folder.VmFolder.CreateVM(ctx, vmConfigSpec, pool, nil)
328+
assert.NoError(err, "expected no error when creating VM")
329+
330+
_, err = task.WaitForResult(ctx, nil)
331+
assert.NoError(err, "expected no error when waiting for task to complete")
332+
333+
newVM, err := c.GenerateVirtualMachine(vm)
334+
assert.NoError(err, "expected no error during VM CR generation")
335+
assert.True(*newVM.Spec.Template.Spec.Domain.Firmware.Bootloader.EFI.SecureBoot, "expected VM to have secure boot enabled")
336+
assert.True(*newVM.Spec.Template.Spec.Domain.Features.SMM.Enabled, "expected VM to have SMM enabled")
337+
}
338+
258339
func Test_identifyNetworkCards(t *testing.T) {
259340
ctx := context.TODO()
260341
endpoint := fmt.Sprintf("https://localhost:%s/sdk", vcsimPort)
@@ -277,7 +358,7 @@ func Test_identifyNetworkCards(t *testing.T) {
277358
assert.NoError(err, "expected no error during verification of client")
278359

279360
vmObj, err := c.findVM("", "DC0_H0_VM0")
280-
assert.NoError(err, "expected no error during vm lookup")
361+
assert.NoError(err, "expected no error during VM lookup")
281362

282363
var o mo.VirtualMachine
283364

0 commit comments

Comments
 (0)