gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/virtcontainers/qemu_test.go (about) 1 // Copyright (c) 2016 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 package virtcontainers 7 8 import ( 9 "context" 10 "fmt" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "strings" 15 "testing" 16 17 govmmQemu "github.com/intel/govmm/qemu" 18 "github.com/kata-containers/runtime/virtcontainers/device/config" 19 "github.com/kata-containers/runtime/virtcontainers/persist" 20 "github.com/kata-containers/runtime/virtcontainers/types" 21 "github.com/kata-containers/runtime/virtcontainers/utils" 22 "github.com/pkg/errors" 23 "github.com/stretchr/testify/assert" 24 ) 25 26 func newQemuConfig() HypervisorConfig { 27 return HypervisorConfig{ 28 KernelPath: testQemuKernelPath, 29 ImagePath: testQemuImagePath, 30 InitrdPath: testQemuInitrdPath, 31 HypervisorPath: testQemuPath, 32 NumVCPUs: defaultVCPUs, 33 MemorySize: defaultMemSzMiB, 34 DefaultBridges: defaultBridges, 35 BlockDeviceDriver: defaultBlockDriver, 36 DefaultMaxVCPUs: defaultMaxQemuVCPUs, 37 Msize9p: defaultMsize9p, 38 } 39 } 40 41 func testQemuKernelParameters(t *testing.T, kernelParams []Param, expected string, debug bool) { 42 qemuConfig := newQemuConfig() 43 qemuConfig.KernelParams = kernelParams 44 assert := assert.New(t) 45 46 if debug == true { 47 qemuConfig.Debug = true 48 } 49 50 q := &qemu{ 51 config: qemuConfig, 52 arch: &qemuArchBase{}, 53 } 54 55 params := q.kernelParameters() 56 assert.Equal(params, expected) 57 } 58 59 func TestQemuKernelParameters(t *testing.T) { 60 expectedOut := fmt.Sprintf("panic=1 nr_cpus=%d agent.use_vsock=false foo=foo bar=bar", MaxQemuVCPUs()) 61 params := []Param{ 62 { 63 Key: "foo", 64 Value: "foo", 65 }, 66 { 67 Key: "bar", 68 Value: "bar", 69 }, 70 } 71 72 testQemuKernelParameters(t, params, expectedOut, true) 73 testQemuKernelParameters(t, params, expectedOut, false) 74 } 75 76 func TestQemuCreateSandbox(t *testing.T) { 77 qemuConfig := newQemuConfig() 78 assert := assert.New(t) 79 80 store, err := persist.GetDriver() 81 assert.NoError(err) 82 q := &qemu{ 83 store: store, 84 } 85 sandbox := &Sandbox{ 86 ctx: context.Background(), 87 id: "testSandbox", 88 config: &SandboxConfig{ 89 HypervisorConfig: qemuConfig, 90 }, 91 } 92 93 // Create the hypervisor fake binary 94 testQemuPath := filepath.Join(testDir, testHypervisor) 95 _, err = os.Create(testQemuPath) 96 assert.NoError(err) 97 98 // Create parent dir path for hypervisor.json 99 parentDir := filepath.Join(q.store.RunStoragePath(), sandbox.id) 100 assert.NoError(os.MkdirAll(parentDir, DirMode)) 101 102 err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false) 103 assert.NoError(err) 104 assert.NoError(os.RemoveAll(parentDir)) 105 assert.Exactly(qemuConfig, q.config) 106 } 107 108 func TestQemuCreateSandboxMissingParentDirFail(t *testing.T) { 109 qemuConfig := newQemuConfig() 110 assert := assert.New(t) 111 112 store, err := persist.GetDriver() 113 assert.NoError(err) 114 q := &qemu{ 115 store: store, 116 } 117 sandbox := &Sandbox{ 118 ctx: context.Background(), 119 id: "testSandbox", 120 config: &SandboxConfig{ 121 HypervisorConfig: qemuConfig, 122 }, 123 } 124 125 // Create the hypervisor fake binary 126 testQemuPath := filepath.Join(testDir, testHypervisor) 127 _, err = os.Create(testQemuPath) 128 assert.NoError(err) 129 130 // Ensure parent dir path for hypervisor.json does not exist. 131 parentDir := filepath.Join(q.store.RunStoragePath(), sandbox.id) 132 assert.NoError(os.RemoveAll(parentDir)) 133 134 err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false) 135 assert.NoError(err) 136 } 137 138 func TestQemuCPUTopology(t *testing.T) { 139 assert := assert.New(t) 140 vcpus := 1 141 142 q := &qemu{ 143 arch: &qemuArchBase{}, 144 config: HypervisorConfig{ 145 NumVCPUs: uint32(vcpus), 146 DefaultMaxVCPUs: uint32(vcpus), 147 }, 148 } 149 150 expectedOut := govmmQemu.SMP{ 151 CPUs: uint32(vcpus), 152 Sockets: uint32(vcpus), 153 Cores: defaultCores, 154 Threads: defaultThreads, 155 MaxCPUs: uint32(vcpus), 156 } 157 158 smp := q.cpuTopology() 159 assert.Exactly(smp, expectedOut) 160 } 161 162 func TestQemuMemoryTopology(t *testing.T) { 163 mem := uint32(1000) 164 slots := uint32(8) 165 assert := assert.New(t) 166 167 q := &qemu{ 168 arch: &qemuArchBase{}, 169 config: HypervisorConfig{ 170 MemorySize: mem, 171 MemSlots: slots, 172 }, 173 } 174 175 hostMemKb, err := getHostMemorySizeKb(procMemInfo) 176 assert.NoError(err) 177 memMax := fmt.Sprintf("%dM", int(float64(hostMemKb)/1024)) 178 179 expectedOut := govmmQemu.Memory{ 180 Size: fmt.Sprintf("%dM", mem), 181 Slots: uint8(slots), 182 MaxMem: memMax, 183 } 184 185 memory, err := q.memoryTopology() 186 assert.NoError(err) 187 assert.Exactly(memory, expectedOut) 188 } 189 190 func testQemuAddDevice(t *testing.T, devInfo interface{}, devType deviceType, expected []govmmQemu.Device) { 191 assert := assert.New(t) 192 q := &qemu{ 193 ctx: context.Background(), 194 arch: &qemuArchBase{}, 195 } 196 197 err := q.addDevice(devInfo, devType) 198 assert.NoError(err) 199 assert.Exactly(q.qemuConfig.Devices, expected) 200 } 201 202 func TestQemuAddDeviceFsDev(t *testing.T) { 203 mountTag := "testMountTag" 204 hostPath := "testHostPath" 205 206 expectedOut := []govmmQemu.Device{ 207 govmmQemu.FSDevice{ 208 Driver: govmmQemu.Virtio9P, 209 FSDriver: govmmQemu.Local, 210 ID: fmt.Sprintf("extra-9p-%s", mountTag), 211 Path: hostPath, 212 MountTag: mountTag, 213 SecurityModel: govmmQemu.None, 214 }, 215 } 216 217 volume := types.Volume{ 218 MountTag: mountTag, 219 HostPath: hostPath, 220 } 221 222 testQemuAddDevice(t, volume, fsDev, expectedOut) 223 } 224 225 func TestQemuAddDeviceVhostUserBlk(t *testing.T) { 226 socketPath := "/test/socket/path" 227 devID := "testDevID" 228 229 expectedOut := []govmmQemu.Device{ 230 govmmQemu.VhostUserDevice{ 231 SocketPath: socketPath, 232 CharDevID: utils.MakeNameID("char", devID, maxDevIDSize), 233 VhostUserType: govmmQemu.VhostUserBlk, 234 }, 235 } 236 237 vDevice := config.VhostUserDeviceAttrs{ 238 DevID: devID, 239 SocketPath: socketPath, 240 Type: config.VhostUserBlk, 241 } 242 243 testQemuAddDevice(t, vDevice, vhostuserDev, expectedOut) 244 } 245 246 func TestQemuAddDeviceSerialPortDev(t *testing.T) { 247 deviceID := "channelTest" 248 id := "charchTest" 249 hostPath := "/tmp/hyper_test.sock" 250 name := "sh.hyper.channel.test" 251 252 expectedOut := []govmmQemu.Device{ 253 govmmQemu.CharDevice{ 254 Driver: govmmQemu.VirtioSerialPort, 255 Backend: govmmQemu.Socket, 256 DeviceID: deviceID, 257 ID: id, 258 Path: hostPath, 259 Name: name, 260 }, 261 } 262 263 socket := types.Socket{ 264 DeviceID: deviceID, 265 ID: id, 266 HostPath: hostPath, 267 Name: name, 268 } 269 270 testQemuAddDevice(t, socket, serialPortDev, expectedOut) 271 } 272 273 func TestQemuAddDeviceKataVSOCK(t *testing.T) { 274 assert := assert.New(t) 275 276 dir, err := ioutil.TempDir("", "") 277 assert.NoError(err) 278 defer os.RemoveAll(dir) 279 280 vsockFilename := filepath.Join(dir, "vsock") 281 282 contextID := uint64(3) 283 port := uint32(1024) 284 285 vsockFile, err := os.Create(vsockFilename) 286 assert.NoError(err) 287 defer vsockFile.Close() 288 289 expectedOut := []govmmQemu.Device{ 290 govmmQemu.VSOCKDevice{ 291 ID: fmt.Sprintf("vsock-%d", contextID), 292 ContextID: contextID, 293 VHostFD: vsockFile, 294 }, 295 } 296 297 vsock := types.VSock{ 298 ContextID: contextID, 299 Port: port, 300 VhostFd: vsockFile, 301 } 302 303 testQemuAddDevice(t, vsock, vSockPCIDev, expectedOut) 304 } 305 306 func TestQemuGetSandboxConsole(t *testing.T) { 307 assert := assert.New(t) 308 store, err := persist.GetDriver() 309 assert.NoError(err) 310 q := &qemu{ 311 ctx: context.Background(), 312 store: store, 313 } 314 sandboxID := "testSandboxID" 315 expected := filepath.Join(q.store.RunVMStoragePath(), sandboxID, consoleSocket) 316 317 result, err := q.getSandboxConsole(sandboxID) 318 assert.NoError(err) 319 assert.Equal(result, expected) 320 } 321 322 func TestQemuCapabilities(t *testing.T) { 323 assert := assert.New(t) 324 q := &qemu{ 325 ctx: context.Background(), 326 arch: &qemuArchBase{}, 327 } 328 329 caps := q.capabilities() 330 assert.True(caps.IsBlockDeviceHotplugSupported()) 331 } 332 333 func TestQemuQemuPath(t *testing.T) { 334 assert := assert.New(t) 335 336 f, err := ioutil.TempFile("", "qemu") 337 assert.NoError(err) 338 defer func() { _ = f.Close() }() 339 defer func() { _ = os.Remove(f.Name()) }() 340 341 expectedPath := f.Name() 342 qemuConfig := newQemuConfig() 343 qemuConfig.HypervisorPath = expectedPath 344 qkvm := &qemuArchBase{ 345 machineType: "pc", 346 qemuPaths: map[string]string{ 347 "pc": expectedPath, 348 }, 349 } 350 351 q := &qemu{ 352 config: qemuConfig, 353 arch: qkvm, 354 } 355 356 // get config hypervisor path 357 path, err := q.qemuPath() 358 assert.NoError(err) 359 assert.Equal(path, expectedPath) 360 361 // config hypervisor path does not exist 362 q.config.HypervisorPath = "/abc/rgb/123" 363 path, err = q.qemuPath() 364 assert.Error(err) 365 assert.Equal(path, "") 366 367 // get arch hypervisor path 368 q.config.HypervisorPath = "" 369 path, err = q.qemuPath() 370 assert.NoError(err) 371 assert.Equal(path, expectedPath) 372 373 // bad machine type, arch should fail 374 qkvm.machineType = "rgb" 375 q.arch = qkvm 376 path, err = q.qemuPath() 377 assert.Error(err) 378 assert.Equal(path, "") 379 } 380 381 func TestHotplugUnsupportedDeviceType(t *testing.T) { 382 assert := assert.New(t) 383 384 qemuConfig := newQemuConfig() 385 q := &qemu{ 386 ctx: context.Background(), 387 id: "qemuTest", 388 config: qemuConfig, 389 } 390 391 _, err := q.hotplugAddDevice(&memoryDevice{0, 128, uint64(0), false}, fsDev) 392 assert.Error(err) 393 _, err = q.hotplugRemoveDevice(&memoryDevice{0, 128, uint64(0), false}, fsDev) 394 assert.Error(err) 395 } 396 397 func TestQMPSetupShutdown(t *testing.T) { 398 assert := assert.New(t) 399 400 qemuConfig := newQemuConfig() 401 q := &qemu{ 402 config: qemuConfig, 403 } 404 405 q.qmpShutdown() 406 407 q.qmpMonitorCh.qmp = &govmmQemu.QMP{} 408 err := q.qmpSetup() 409 assert.Nil(err) 410 } 411 412 func TestQemuCleanup(t *testing.T) { 413 assert := assert.New(t) 414 415 q := &qemu{ 416 ctx: context.Background(), 417 config: newQemuConfig(), 418 } 419 420 err := q.cleanup() 421 assert.Nil(err) 422 } 423 424 func TestQemuGrpc(t *testing.T) { 425 assert := assert.New(t) 426 427 config := newQemuConfig() 428 q := &qemu{ 429 id: "testqemu", 430 config: config, 431 } 432 433 json, err := q.toGrpc() 434 assert.Nil(err) 435 436 var q2 qemu 437 err = q2.fromGrpc(context.Background(), &config, json) 438 assert.Nil(err) 439 440 assert.True(q.id == q2.id) 441 } 442 443 func TestQemuFileBackedMem(t *testing.T) { 444 assert := assert.New(t) 445 446 // Check default Filebackedmem location for virtio-fs 447 sandbox, err := createQemuSandboxConfig() 448 assert.NoError(err) 449 450 q := &qemu{ 451 store: sandbox.newStore, 452 } 453 sandbox.config.HypervisorConfig.SharedFS = config.VirtioFS 454 err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false) 455 assert.NoError(err) 456 457 assert.Equal(q.qemuConfig.Knobs.FileBackedMem, true) 458 assert.Equal(q.qemuConfig.Knobs.MemShared, true) 459 assert.Equal(q.qemuConfig.Memory.Path, fallbackFileBackedMemDir) 460 461 // Check failure for VM templating 462 sandbox, err = createQemuSandboxConfig() 463 assert.NoError(err) 464 465 q = &qemu{ 466 store: sandbox.newStore, 467 } 468 sandbox.config.HypervisorConfig.BootToBeTemplate = true 469 sandbox.config.HypervisorConfig.SharedFS = config.VirtioFS 470 sandbox.config.HypervisorConfig.MemoryPath = fallbackFileBackedMemDir 471 472 err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false) 473 474 expectErr := errors.New("VM templating has been enabled with either virtio-fs or file backed memory and this configuration will not work") 475 assert.Equal(expectErr.Error(), err.Error()) 476 477 // Check Setting of non-existent shared-mem path 478 sandbox, err = createQemuSandboxConfig() 479 assert.NoError(err) 480 481 q = &qemu{ 482 store: sandbox.newStore, 483 } 484 sandbox.config.HypervisorConfig.FileBackedMemRootDir = "/tmp/xyzabc" 485 err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false) 486 assert.NoError(err) 487 assert.Equal(q.qemuConfig.Knobs.FileBackedMem, false) 488 assert.Equal(q.qemuConfig.Knobs.MemShared, false) 489 assert.Equal(q.qemuConfig.Memory.Path, "") 490 491 // Check setting vhost-user storage with Hugepages 492 sandbox, err = createQemuSandboxConfig() 493 assert.NoError(err) 494 495 q = &qemu{ 496 store: sandbox.newStore, 497 } 498 sandbox.config.HypervisorConfig.EnableVhostUserStore = true 499 sandbox.config.HypervisorConfig.HugePages = true 500 err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false) 501 assert.NoError(err) 502 assert.Equal(q.qemuConfig.Knobs.MemShared, true) 503 504 // Check failure for vhost-user storage 505 sandbox, err = createQemuSandboxConfig() 506 assert.NoError(err) 507 508 q = &qemu{ 509 store: sandbox.newStore, 510 } 511 sandbox.config.HypervisorConfig.EnableVhostUserStore = true 512 sandbox.config.HypervisorConfig.HugePages = false 513 err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false) 514 515 expectErr = errors.New("Vhost-user-blk/scsi is enabled without HugePages. This configuration will not work") 516 assert.Equal(expectErr.Error(), err.Error()) 517 } 518 519 func createQemuSandboxConfig() (*Sandbox, error) { 520 521 qemuConfig := newQemuConfig() 522 sandbox := Sandbox{ 523 ctx: context.Background(), 524 id: "testSandbox", 525 config: &SandboxConfig{ 526 HypervisorConfig: qemuConfig, 527 }, 528 } 529 530 newStore, err := persist.GetDriver() 531 if err != nil { 532 return &Sandbox{}, err 533 } 534 sandbox.newStore = newStore 535 536 return &sandbox, nil 537 } 538 539 func TestQemuVirtiofsdArgs(t *testing.T) { 540 assert := assert.New(t) 541 542 q := &qemu{ 543 id: "foo", 544 config: HypervisorConfig{ 545 VirtioFSCache: "none", 546 Debug: true, 547 }, 548 } 549 550 savedKataHostSharedDir := kataHostSharedDir 551 kataHostSharedDir = func() string { 552 return "test-share-dir" 553 } 554 defer func() { 555 kataHostSharedDir = savedKataHostSharedDir 556 }() 557 558 result := "--fd=123 -o source=test-share-dir/foo -o cache=none --syslog -o no_posix_lock -d" 559 args := q.virtiofsdArgs(123) 560 assert.Equal(strings.Join(args, " "), result) 561 562 q.config.Debug = false 563 result = "--fd=123 -o source=test-share-dir/foo -o cache=none --syslog -o no_posix_lock -f" 564 args = q.virtiofsdArgs(123) 565 assert.Equal(strings.Join(args, " "), result) 566 } 567 568 func TestQemuGetpids(t *testing.T) { 569 assert := assert.New(t) 570 571 qemuConfig := newQemuConfig() 572 q := &qemu{} 573 pids := q.getPids() 574 assert.NotNil(pids) 575 assert.True(len(pids) == 1) 576 assert.True(pids[0] == 0) 577 578 q = &qemu{ 579 config: qemuConfig, 580 } 581 f, err := ioutil.TempFile("", "qemu-test-") 582 assert.Nil(err) 583 tmpfile := f.Name() 584 f.Close() 585 defer os.Remove(tmpfile) 586 587 q.qemuConfig.PidFile = tmpfile 588 pids = q.getPids() 589 assert.True(len(pids) == 1) 590 assert.True(pids[0] == 0) 591 592 err = ioutil.WriteFile(tmpfile, []byte("100"), 0) 593 assert.Nil(err) 594 pids = q.getPids() 595 assert.True(len(pids) == 1) 596 assert.True(pids[0] == 100) 597 598 q.state.VirtiofsdPid = 200 599 pids = q.getPids() 600 assert.True(len(pids) == 2) 601 assert.True(pids[0] == 100) 602 assert.True(pids[1] == 200) 603 }