gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/virtcontainers/qemu_arch_base_test.go (about)

     1  // Copyright (c) 2018 Intel Corporation
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  
     6  package virtcontainers
     7  
     8  import (
     9  	"fmt"
    10  	"io/ioutil"
    11  	"net"
    12  	"os"
    13  	"path/filepath"
    14  	"testing"
    15  
    16  	govmmQemu "github.com/intel/govmm/qemu"
    17  	"github.com/stretchr/testify/assert"
    18  
    19  	"github.com/kata-containers/runtime/virtcontainers/device/config"
    20  	"github.com/kata-containers/runtime/virtcontainers/persist/fs"
    21  	"github.com/kata-containers/runtime/virtcontainers/types"
    22  	"github.com/pkg/errors"
    23  )
    24  
    25  const (
    26  	qemuArchBaseMachineType = "pc"
    27  	qemuArchBaseQemuPath    = "/usr/bin/qemu-system-x86_64"
    28  )
    29  
    30  var qemuArchBaseQemuPaths = map[string]string{
    31  	qemuArchBaseMachineType: qemuArchBaseQemuPath,
    32  }
    33  
    34  var qemuArchBaseKernelParamsNonDebug = []Param{
    35  	{"quiet", ""},
    36  	{"systemd.show_status", "false"},
    37  }
    38  
    39  var qemuArchBaseKernelParamsDebug = []Param{
    40  	{"debug", ""},
    41  	{"systemd.show_status", "true"},
    42  	{"systemd.log_level", "debug"},
    43  }
    44  
    45  var qemuArchBaseKernelParams = []Param{
    46  	{"root", "/dev/vda"},
    47  	{"rootfstype", "ext4"},
    48  }
    49  
    50  var qemuArchBaseSupportedQemuMachines = []govmmQemu.Machine{
    51  	{
    52  		Type: qemuArchBaseMachineType,
    53  	},
    54  }
    55  
    56  func newQemuArchBase() *qemuArchBase {
    57  	return &qemuArchBase{
    58  		machineType:           qemuArchBaseMachineType,
    59  		nestedRun:             false,
    60  		qemuPaths:             qemuArchBaseQemuPaths,
    61  		supportedQemuMachines: qemuArchBaseSupportedQemuMachines,
    62  		kernelParamsNonDebug:  qemuArchBaseKernelParamsNonDebug,
    63  		kernelParamsDebug:     qemuArchBaseKernelParamsDebug,
    64  		kernelParams:          qemuArchBaseKernelParams,
    65  	}
    66  }
    67  
    68  func TestQemuArchBaseEnableNestingChecks(t *testing.T) {
    69  	assert := assert.New(t)
    70  	qemuArchBase := newQemuArchBase()
    71  
    72  	qemuArchBase.enableNestingChecks()
    73  	assert.True(qemuArchBase.nestedRun)
    74  }
    75  
    76  func TestQemuArchBaseDisableNestingChecks(t *testing.T) {
    77  	assert := assert.New(t)
    78  	qemuArchBase := newQemuArchBase()
    79  
    80  	qemuArchBase.disableNestingChecks()
    81  	assert.False(qemuArchBase.nestedRun)
    82  }
    83  
    84  func TestQemuArchBaseMachine(t *testing.T) {
    85  	assert := assert.New(t)
    86  	qemuArchBase := newQemuArchBase()
    87  
    88  	m, err := qemuArchBase.machine()
    89  	assert.NoError(err)
    90  	assert.Equal(m.Type, qemuArchBaseMachineType)
    91  
    92  	machines := []govmmQemu.Machine{
    93  		{
    94  			Type: "bad",
    95  		},
    96  	}
    97  	qemuArchBase.supportedQemuMachines = machines
    98  	m, err = qemuArchBase.machine()
    99  	assert.Error(err)
   100  	assert.Equal("", m.Type)
   101  }
   102  
   103  func TestQemuArchBaseQemuPath(t *testing.T) {
   104  	assert := assert.New(t)
   105  	qemuArchBase := newQemuArchBase()
   106  
   107  	p, err := qemuArchBase.qemuPath()
   108  	assert.NoError(err)
   109  	assert.Equal(p, qemuArchBaseQemuPath)
   110  
   111  	paths := map[string]string{
   112  		"bad": qemuArchBaseQemuPath,
   113  	}
   114  	qemuArchBase.qemuPaths = paths
   115  	p, err = qemuArchBase.qemuPath()
   116  	assert.Error(err)
   117  	assert.Equal("", p)
   118  }
   119  
   120  func TestQemuArchBaseKernelParameters(t *testing.T) {
   121  	assert := assert.New(t)
   122  	qemuArchBase := newQemuArchBase()
   123  
   124  	// with debug params
   125  	expectedParams := qemuArchBaseKernelParams
   126  	debugParams := qemuArchBaseKernelParamsDebug
   127  	expectedParams = append(expectedParams, debugParams...)
   128  	p := qemuArchBase.kernelParameters(true)
   129  	assert.Equal(expectedParams, p)
   130  
   131  	// with non-debug params
   132  	expectedParams = qemuArchBaseKernelParams
   133  	nonDebugParams := qemuArchBaseKernelParamsNonDebug
   134  	expectedParams = append(expectedParams, nonDebugParams...)
   135  	p = qemuArchBase.kernelParameters(false)
   136  	assert.Equal(expectedParams, p)
   137  }
   138  
   139  func TestQemuArchBaseCapabilities(t *testing.T) {
   140  	assert := assert.New(t)
   141  	qemuArchBase := newQemuArchBase()
   142  
   143  	c := qemuArchBase.capabilities()
   144  	assert.True(c.IsBlockDeviceHotplugSupported())
   145  }
   146  
   147  func TestQemuArchBaseBridges(t *testing.T) {
   148  	assert := assert.New(t)
   149  	qemuArchBase := newQemuArchBase()
   150  	len := 5
   151  
   152  	qemuArchBase.bridges(uint32(len))
   153  	bridges := qemuArchBase.getBridges()
   154  	assert.Len(bridges, len)
   155  
   156  	for i, b := range bridges {
   157  		id := fmt.Sprintf("%s-bridge-%d", types.PCI, i)
   158  		assert.Equal(types.PCI, b.Type)
   159  		assert.Equal(id, b.ID)
   160  		assert.NotNil(b.Devices)
   161  	}
   162  }
   163  
   164  func TestQemuAddDeviceToBridge(t *testing.T) {
   165  	assert := assert.New(t)
   166  
   167  	// addDeviceToBridge successfully
   168  	q := newQemuArchBase()
   169  	q.machineType = QemuPC
   170  
   171  	q.bridges(1)
   172  	for i := uint32(1); i <= types.PCIBridgeMaxCapacity; i++ {
   173  		_, _, err := q.addDeviceToBridge(fmt.Sprintf("qemu-bridge-%d", i), types.PCI)
   174  		assert.Nil(err)
   175  	}
   176  
   177  	// fail to add device to bridge cause no more available bridge slot
   178  	_, _, err := q.addDeviceToBridge("qemu-bridge-31", types.PCI)
   179  	exceptErr := errors.New("no more bridge slots available")
   180  	assert.Equal(exceptErr.Error(), err.Error())
   181  
   182  	// addDeviceToBridge fails cause q.Bridges == 0
   183  	q = newQemuArchBase()
   184  	q.machineType = QemuPCLite
   185  	q.bridges(0)
   186  	_, _, err = q.addDeviceToBridge("qemu-bridge", types.PCI)
   187  	if assert.Error(err) {
   188  		exceptErr = errors.New("failed to get available address from bridges")
   189  		assert.Equal(exceptErr.Error(), err.Error())
   190  	}
   191  }
   192  
   193  func TestQemuArchBaseCPUTopology(t *testing.T) {
   194  	assert := assert.New(t)
   195  	qemuArchBase := newQemuArchBase()
   196  	vcpus := uint32(2)
   197  
   198  	expectedSMP := govmmQemu.SMP{
   199  		CPUs:    vcpus,
   200  		Sockets: defaultMaxQemuVCPUs,
   201  		Cores:   defaultCores,
   202  		Threads: defaultThreads,
   203  		MaxCPUs: defaultMaxQemuVCPUs,
   204  	}
   205  
   206  	smp := qemuArchBase.cpuTopology(vcpus, defaultMaxQemuVCPUs)
   207  	assert.Equal(expectedSMP, smp)
   208  }
   209  
   210  func TestQemuArchBaseCPUModel(t *testing.T) {
   211  	assert := assert.New(t)
   212  	qemuArchBase := newQemuArchBase()
   213  
   214  	assert.Equal(defaultCPUModel, qemuArchBase.cpuModel())
   215  }
   216  
   217  func TestQemuArchBaseMemoryTopology(t *testing.T) {
   218  	assert := assert.New(t)
   219  	qemuArchBase := newQemuArchBase()
   220  
   221  	hostMem := uint64(100)
   222  	mem := uint64(120)
   223  	slots := uint8(12)
   224  	expectedMemory := govmmQemu.Memory{
   225  		Size:   fmt.Sprintf("%dM", mem),
   226  		Slots:  slots,
   227  		MaxMem: fmt.Sprintf("%dM", hostMem),
   228  	}
   229  
   230  	m := qemuArchBase.memoryTopology(mem, hostMem, slots)
   231  	assert.Equal(expectedMemory, m)
   232  }
   233  
   234  func testQemuArchBaseAppend(t *testing.T, structure interface{}, expected []govmmQemu.Device) {
   235  	var devices []govmmQemu.Device
   236  	var err error
   237  	assert := assert.New(t)
   238  	qemuArchBase := newQemuArchBase()
   239  
   240  	switch s := structure.(type) {
   241  	case types.Volume:
   242  		devices, err = qemuArchBase.append9PVolume(devices, s)
   243  	case types.Socket:
   244  		devices = qemuArchBase.appendSocket(devices, s)
   245  	case config.BlockDrive:
   246  		devices, err = qemuArchBase.appendBlockDevice(devices, s)
   247  	case config.VFIODev:
   248  		devices = qemuArchBase.appendVFIODevice(devices, s)
   249  	case config.VhostUserDeviceAttrs:
   250  		devices, err = qemuArchBase.appendVhostUserDevice(devices, s)
   251  	}
   252  
   253  	assert.NoError(err)
   254  	assert.Equal(devices, expected)
   255  }
   256  
   257  func TestQemuArchBaseAppendConsoles(t *testing.T) {
   258  	var devices []govmmQemu.Device
   259  	var err error
   260  	assert := assert.New(t)
   261  	qemuArchBase := newQemuArchBase()
   262  
   263  	path := filepath.Join(filepath.Join(fs.MockRunStoragePath(), sandboxID), consoleSocket)
   264  
   265  	expectedOut := []govmmQemu.Device{
   266  		govmmQemu.SerialDevice{
   267  			Driver: govmmQemu.VirtioSerial,
   268  			ID:     "serial0",
   269  		},
   270  		govmmQemu.CharDevice{
   271  			Driver:   govmmQemu.Console,
   272  			Backend:  govmmQemu.Socket,
   273  			DeviceID: "console0",
   274  			ID:       "charconsole0",
   275  			Path:     path,
   276  		},
   277  	}
   278  
   279  	devices, err = qemuArchBase.appendConsole(devices, path)
   280  	assert.NoError(err)
   281  	assert.Equal(expectedOut, devices)
   282  }
   283  
   284  func TestQemuArchBaseAppendImage(t *testing.T) {
   285  	var devices []govmmQemu.Device
   286  	assert := assert.New(t)
   287  	qemuArchBase := newQemuArchBase()
   288  
   289  	image, err := ioutil.TempFile("", "img")
   290  	assert.NoError(err)
   291  	defer os.Remove(image.Name())
   292  	err = image.Close()
   293  	assert.NoError(err)
   294  
   295  	devices, err = qemuArchBase.appendImage(devices, image.Name())
   296  	assert.NoError(err)
   297  	assert.Len(devices, 1)
   298  
   299  	drive, ok := devices[0].(govmmQemu.BlockDevice)
   300  	assert.True(ok)
   301  
   302  	expectedOut := []govmmQemu.Device{
   303  		govmmQemu.BlockDevice{
   304  			Driver:    govmmQemu.VirtioBlock,
   305  			ID:        drive.ID,
   306  			File:      image.Name(),
   307  			AIO:       govmmQemu.Threads,
   308  			Format:    "raw",
   309  			Interface: "none",
   310  			ShareRW:   true,
   311  			ReadOnly:  true,
   312  		},
   313  	}
   314  
   315  	assert.Equal(expectedOut, devices)
   316  }
   317  
   318  func TestQemuArchBaseAppendBridges(t *testing.T) {
   319  	var devices []govmmQemu.Device
   320  	assert := assert.New(t)
   321  	qemuArchBase := newQemuArchBase()
   322  
   323  	qemuArchBase.bridges(1)
   324  	bridges := qemuArchBase.getBridges()
   325  	assert.Len(bridges, 1)
   326  
   327  	devices = qemuArchBase.appendBridges(devices)
   328  	assert.Len(devices, 1)
   329  
   330  	expectedOut := []govmmQemu.Device{
   331  		govmmQemu.BridgeDevice{
   332  			Type:    govmmQemu.PCIBridge,
   333  			Bus:     defaultBridgeBus,
   334  			ID:      bridges[0].ID,
   335  			Chassis: 1,
   336  			SHPC:    true,
   337  			Addr:    "2",
   338  		},
   339  	}
   340  
   341  	assert.Equal(expectedOut, devices)
   342  }
   343  
   344  func TestQemuArchBaseAppend9PVolume(t *testing.T) {
   345  	mountTag := "testMountTag"
   346  	hostPath := "testHostPath"
   347  
   348  	expectedOut := []govmmQemu.Device{
   349  		govmmQemu.FSDevice{
   350  			Driver:        govmmQemu.Virtio9P,
   351  			FSDriver:      govmmQemu.Local,
   352  			ID:            fmt.Sprintf("extra-9p-%s", mountTag),
   353  			Path:          hostPath,
   354  			MountTag:      mountTag,
   355  			SecurityModel: govmmQemu.None,
   356  		},
   357  	}
   358  
   359  	volume := types.Volume{
   360  		MountTag: mountTag,
   361  		HostPath: hostPath,
   362  	}
   363  
   364  	testQemuArchBaseAppend(t, volume, expectedOut)
   365  }
   366  
   367  func TestQemuArchBaseAppendSocket(t *testing.T) {
   368  	deviceID := "channelTest"
   369  	id := "charchTest"
   370  	hostPath := "/tmp/hyper_test.sock"
   371  	name := "sh.hyper.channel.test"
   372  
   373  	expectedOut := []govmmQemu.Device{
   374  		govmmQemu.CharDevice{
   375  			Driver:   govmmQemu.VirtioSerialPort,
   376  			Backend:  govmmQemu.Socket,
   377  			DeviceID: deviceID,
   378  			ID:       id,
   379  			Path:     hostPath,
   380  			Name:     name,
   381  		},
   382  	}
   383  
   384  	socket := types.Socket{
   385  		DeviceID: deviceID,
   386  		ID:       id,
   387  		HostPath: hostPath,
   388  		Name:     name,
   389  	}
   390  
   391  	testQemuArchBaseAppend(t, socket, expectedOut)
   392  }
   393  
   394  func TestQemuArchBaseAppendBlockDevice(t *testing.T) {
   395  	id := "blockDevTest"
   396  	file := "/root"
   397  	format := "raw"
   398  
   399  	expectedOut := []govmmQemu.Device{
   400  		govmmQemu.BlockDevice{
   401  			Driver:    govmmQemu.VirtioBlock,
   402  			ID:        id,
   403  			File:      "/root",
   404  			AIO:       govmmQemu.Threads,
   405  			Format:    govmmQemu.BlockDeviceFormat(format),
   406  			Interface: "none",
   407  		},
   408  	}
   409  
   410  	drive := config.BlockDrive{
   411  		File:   file,
   412  		Format: format,
   413  		ID:     id,
   414  	}
   415  
   416  	testQemuArchBaseAppend(t, drive, expectedOut)
   417  }
   418  
   419  func TestQemuArchBaseAppendVhostUserDevice(t *testing.T) {
   420  	socketPath := "nonexistentpath.sock"
   421  	macAddress := "00:11:22:33:44:55:66"
   422  	id := "deadbeef"
   423  
   424  	expectedOut := []govmmQemu.Device{
   425  		govmmQemu.VhostUserDevice{
   426  			SocketPath:    socketPath,
   427  			CharDevID:     fmt.Sprintf("char-%s", id),
   428  			TypeDevID:     fmt.Sprintf("net-%s", id),
   429  			Address:       macAddress,
   430  			VhostUserType: govmmQemu.VhostUserNet,
   431  		},
   432  	}
   433  
   434  	vhostUserDevice := config.VhostUserDeviceAttrs{
   435  		Type:       config.VhostUserNet,
   436  		MacAddress: macAddress,
   437  	}
   438  	vhostUserDevice.DevID = id
   439  	vhostUserDevice.SocketPath = socketPath
   440  
   441  	testQemuArchBaseAppend(t, vhostUserDevice, expectedOut)
   442  }
   443  
   444  func TestQemuArchBaseAppendVFIODevice(t *testing.T) {
   445  	bdf := "02:10.1"
   446  
   447  	expectedOut := []govmmQemu.Device{
   448  		govmmQemu.VFIODevice{
   449  			BDF: bdf,
   450  		},
   451  	}
   452  
   453  	vfDevice := config.VFIODev{
   454  		BDF: bdf,
   455  	}
   456  
   457  	testQemuArchBaseAppend(t, vfDevice, expectedOut)
   458  }
   459  
   460  func TestQemuArchBaseAppendVFIODeviceWithVendorDeviceID(t *testing.T) {
   461  	bdf := "02:10.1"
   462  	vendorID := "0x1234"
   463  	deviceID := "0x5678"
   464  
   465  	expectedOut := []govmmQemu.Device{
   466  		govmmQemu.VFIODevice{
   467  			BDF:      bdf,
   468  			VendorID: vendorID,
   469  			DeviceID: deviceID,
   470  		},
   471  	}
   472  
   473  	vfDevice := config.VFIODev{
   474  		BDF:      bdf,
   475  		VendorID: vendorID,
   476  		DeviceID: deviceID,
   477  	}
   478  
   479  	testQemuArchBaseAppend(t, vfDevice, expectedOut)
   480  }
   481  
   482  func TestQemuArchBaseAppendSCSIController(t *testing.T) {
   483  	var devices []govmmQemu.Device
   484  	assert := assert.New(t)
   485  	qemuArchBase := newQemuArchBase()
   486  
   487  	expectedOut := []govmmQemu.Device{
   488  		govmmQemu.SCSIController{
   489  			ID: scsiControllerID,
   490  		},
   491  	}
   492  
   493  	devices, ioThread, err := qemuArchBase.appendSCSIController(devices, false)
   494  	assert.Equal(expectedOut, devices)
   495  	assert.Nil(ioThread)
   496  	assert.NoError(err)
   497  
   498  	_, ioThread, err = qemuArchBase.appendSCSIController(devices, true)
   499  	assert.NotNil(ioThread)
   500  	assert.NoError(err)
   501  }
   502  
   503  func TestQemuArchBaseAppendNetwork(t *testing.T) {
   504  	var devices []govmmQemu.Device
   505  	var err error
   506  	assert := assert.New(t)
   507  	qemuArchBase := newQemuArchBase()
   508  
   509  	macAddr := net.HardwareAddr{0x02, 0x00, 0xCA, 0xFE, 0x00, 0x04}
   510  
   511  	macvlanEp := &BridgedMacvlanEndpoint{
   512  		NetPair: NetworkInterfacePair{
   513  			TapInterface: TapInterface{
   514  				ID:   "uniqueTestID-4",
   515  				Name: "br4_kata",
   516  				TAPIface: NetworkInterface{
   517  					Name: "tap4_kata",
   518  				},
   519  			},
   520  			VirtIface: NetworkInterface{
   521  				Name:     "eth4",
   522  				HardAddr: macAddr.String(),
   523  			},
   524  			NetInterworkingModel: DefaultNetInterworkingModel,
   525  		},
   526  		EndpointType: BridgedMacvlanEndpointType,
   527  	}
   528  
   529  	macvtapEp := &MacvtapEndpoint{
   530  		EndpointType: MacvtapEndpointType,
   531  		EndpointProperties: NetworkInfo{
   532  			Iface: NetlinkIface{
   533  				Type: "macvtap",
   534  			},
   535  		},
   536  	}
   537  
   538  	expectedOut := []govmmQemu.Device{
   539  		govmmQemu.NetDevice{
   540  			Type:       networkModelToQemuType(macvlanEp.NetPair.NetInterworkingModel),
   541  			Driver:     govmmQemu.VirtioNet,
   542  			ID:         fmt.Sprintf("network-%d", 0),
   543  			IFName:     macvlanEp.NetPair.TAPIface.Name,
   544  			MACAddress: macvlanEp.NetPair.TAPIface.HardAddr,
   545  			DownScript: "no",
   546  			Script:     "no",
   547  			FDs:        macvlanEp.NetPair.VMFds,
   548  			VhostFDs:   macvlanEp.NetPair.VhostFds,
   549  		},
   550  		govmmQemu.NetDevice{
   551  			Type:       govmmQemu.MACVTAP,
   552  			Driver:     govmmQemu.VirtioNet,
   553  			ID:         fmt.Sprintf("network-%d", 1),
   554  			IFName:     macvtapEp.Name(),
   555  			MACAddress: macvtapEp.HardwareAddr(),
   556  			DownScript: "no",
   557  			Script:     "no",
   558  			FDs:        macvtapEp.VMFds,
   559  			VhostFDs:   macvtapEp.VhostFds,
   560  		},
   561  	}
   562  
   563  	devices, err = qemuArchBase.appendNetwork(devices, macvlanEp)
   564  	assert.NoError(err)
   565  	devices, err = qemuArchBase.appendNetwork(devices, macvtapEp)
   566  	assert.NoError(err)
   567  	assert.Equal(expectedOut, devices)
   568  }