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

     1  // Copyright (c) 2017 Intel Corporation
     2  // Copyright (c) 2018 Huawei Corporation
     3  //
     4  // SPDX-License-Identifier: Apache-2.0
     5  //
     6  
     7  package manager
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"path/filepath"
    14  	"strconv"
    15  	"testing"
    16  
    17  	ktu "github.com/kata-containers/runtime/pkg/katatestutils"
    18  	"github.com/kata-containers/runtime/virtcontainers/device/api"
    19  	"github.com/kata-containers/runtime/virtcontainers/device/config"
    20  	"github.com/kata-containers/runtime/virtcontainers/device/drivers"
    21  	"github.com/stretchr/testify/assert"
    22  
    23  	"golang.org/x/sys/unix"
    24  )
    25  
    26  const fileMode0640 = os.FileMode(0640)
    27  
    28  // dirMode is the permission bits used for creating a directory
    29  const dirMode = os.FileMode(0750) | os.ModeDir
    30  
    31  func TestNewDevice(t *testing.T) {
    32  	dm := &deviceManager{
    33  		blockDriver: VirtioBlock,
    34  		devices:     make(map[string]api.Device),
    35  	}
    36  	savedSysDevPrefix := config.SysDevPrefix
    37  
    38  	major := int64(252)
    39  	minor := int64(3)
    40  
    41  	tmpDir, err := ioutil.TempDir("", "")
    42  	assert.Nil(t, err)
    43  
    44  	config.SysDevPrefix = tmpDir
    45  	defer func() {
    46  		os.RemoveAll(tmpDir)
    47  		config.SysDevPrefix = savedSysDevPrefix
    48  	}()
    49  
    50  	path := "/dev/vfio/2"
    51  	deviceInfo := config.DeviceInfo{
    52  		ContainerPath: "",
    53  		Major:         major,
    54  		Minor:         minor,
    55  		UID:           2,
    56  		GID:           2,
    57  		DevType:       "c",
    58  	}
    59  
    60  	_, err = dm.NewDevice(deviceInfo)
    61  	assert.NotNil(t, err)
    62  
    63  	format := strconv.FormatInt(major, 10) + ":" + strconv.FormatInt(minor, 10)
    64  	ueventPathPrefix := filepath.Join(config.SysDevPrefix, "char", format)
    65  	ueventPath := filepath.Join(ueventPathPrefix, "uevent")
    66  
    67  	// Return true for non-existent /sys/dev path.
    68  	deviceInfo.ContainerPath = path
    69  	_, err = dm.NewDevice(deviceInfo)
    70  	assert.Nil(t, err)
    71  
    72  	err = os.MkdirAll(ueventPathPrefix, dirMode)
    73  	assert.Nil(t, err)
    74  
    75  	// Should return error for bad data in uevent file
    76  	content := []byte("nonkeyvaluedata")
    77  	err = ioutil.WriteFile(ueventPath, content, fileMode0640)
    78  	assert.Nil(t, err)
    79  
    80  	_, err = dm.NewDevice(deviceInfo)
    81  	assert.NotNil(t, err)
    82  
    83  	content = []byte("MAJOR=252\nMINOR=3\nDEVNAME=vfio/2")
    84  	err = ioutil.WriteFile(ueventPath, content, fileMode0640)
    85  	assert.Nil(t, err)
    86  
    87  	device, err := dm.NewDevice(deviceInfo)
    88  	assert.Nil(t, err)
    89  
    90  	vfioDev, ok := device.(*drivers.VFIODevice)
    91  	assert.True(t, ok)
    92  	assert.Equal(t, vfioDev.DeviceInfo.HostPath, path)
    93  	assert.Equal(t, vfioDev.DeviceInfo.ContainerPath, path)
    94  	assert.Equal(t, vfioDev.DeviceInfo.DevType, "c")
    95  	assert.Equal(t, vfioDev.DeviceInfo.Major, major)
    96  	assert.Equal(t, vfioDev.DeviceInfo.Minor, minor)
    97  	assert.Equal(t, vfioDev.DeviceInfo.UID, uint32(2))
    98  	assert.Equal(t, vfioDev.DeviceInfo.GID, uint32(2))
    99  }
   100  
   101  func TestAttachVFIODevice(t *testing.T) {
   102  	dm := &deviceManager{
   103  		blockDriver: VirtioBlock,
   104  		devices:     make(map[string]api.Device),
   105  	}
   106  	tmpDir, err := ioutil.TempDir("", "")
   107  	assert.Nil(t, err)
   108  	os.RemoveAll(tmpDir)
   109  
   110  	testFDIOGroup := "2"
   111  	testDeviceBDFPath := "0000:00:1c.0"
   112  
   113  	devicesDir := filepath.Join(tmpDir, testFDIOGroup, "devices")
   114  	err = os.MkdirAll(devicesDir, dirMode)
   115  	assert.Nil(t, err)
   116  
   117  	deviceFile := filepath.Join(devicesDir, testDeviceBDFPath)
   118  	_, err = os.Create(deviceFile)
   119  	assert.Nil(t, err)
   120  
   121  	savedIOMMUPath := config.SysIOMMUPath
   122  	config.SysIOMMUPath = tmpDir
   123  
   124  	defer func() {
   125  		config.SysIOMMUPath = savedIOMMUPath
   126  	}()
   127  
   128  	path := filepath.Join(vfioPath, testFDIOGroup)
   129  	deviceInfo := config.DeviceInfo{
   130  		HostPath:      path,
   131  		ContainerPath: path,
   132  		DevType:       "c",
   133  	}
   134  
   135  	device, err := dm.NewDevice(deviceInfo)
   136  	assert.Nil(t, err)
   137  	_, ok := device.(*drivers.VFIODevice)
   138  	assert.True(t, ok)
   139  
   140  	devReceiver := &api.MockDeviceReceiver{}
   141  	err = device.Attach(devReceiver)
   142  	assert.Nil(t, err)
   143  
   144  	err = device.Detach(devReceiver)
   145  	assert.Nil(t, err)
   146  }
   147  
   148  func TestAttachGenericDevice(t *testing.T) {
   149  	dm := &deviceManager{
   150  		blockDriver: VirtioBlock,
   151  		devices:     make(map[string]api.Device),
   152  	}
   153  	path := "/dev/tty2"
   154  	deviceInfo := config.DeviceInfo{
   155  		HostPath:      path,
   156  		ContainerPath: path,
   157  		DevType:       "c",
   158  	}
   159  
   160  	device, err := dm.NewDevice(deviceInfo)
   161  	assert.Nil(t, err)
   162  	_, ok := device.(*drivers.GenericDevice)
   163  	assert.True(t, ok)
   164  
   165  	devReceiver := &api.MockDeviceReceiver{}
   166  	err = device.Attach(devReceiver)
   167  	assert.Nil(t, err)
   168  
   169  	err = device.Detach(devReceiver)
   170  	assert.Nil(t, err)
   171  }
   172  
   173  func TestAttachBlockDevice(t *testing.T) {
   174  	dm := &deviceManager{
   175  		blockDriver: VirtioBlock,
   176  		devices:     make(map[string]api.Device),
   177  	}
   178  	path := "/dev/hda"
   179  	deviceInfo := config.DeviceInfo{
   180  		HostPath:      path,
   181  		ContainerPath: path,
   182  		DevType:       "b",
   183  	}
   184  
   185  	devReceiver := &api.MockDeviceReceiver{}
   186  	device, err := dm.NewDevice(deviceInfo)
   187  	assert.Nil(t, err)
   188  	_, ok := device.(*drivers.BlockDevice)
   189  	assert.True(t, ok)
   190  
   191  	err = device.Attach(devReceiver)
   192  	assert.Nil(t, err)
   193  
   194  	err = device.Detach(devReceiver)
   195  	assert.Nil(t, err)
   196  
   197  	// test virtio SCSI driver
   198  	dm.blockDriver = VirtioSCSI
   199  	device, err = dm.NewDevice(deviceInfo)
   200  	assert.Nil(t, err)
   201  	err = device.Attach(devReceiver)
   202  	assert.Nil(t, err)
   203  
   204  	err = device.Detach(devReceiver)
   205  	assert.Nil(t, err)
   206  }
   207  
   208  func TestAttachVhostUserBlkDevice(t *testing.T) {
   209  	rootEnabled := true
   210  	tc := ktu.NewTestConstraint(false)
   211  	if tc.NotValid(ktu.NeedRoot()) {
   212  		rootEnabled = false
   213  	}
   214  
   215  	tmpDir, err := ioutil.TempDir("", "")
   216  	dm := &deviceManager{
   217  		blockDriver:           VirtioBlock,
   218  		devices:               make(map[string]api.Device),
   219  		vhostUserStoreEnabled: true,
   220  		vhostUserStorePath:    tmpDir,
   221  	}
   222  	assert.Nil(t, err)
   223  	os.RemoveAll(tmpDir)
   224  
   225  	vhostUserDevNodePath := filepath.Join(tmpDir, "/block/devices/")
   226  	vhostUserSockPath := filepath.Join(tmpDir, "/block/sockets/")
   227  	deviceNodePath := filepath.Join(vhostUserDevNodePath, "vhostblk0")
   228  	deviceSockPath := filepath.Join(vhostUserSockPath, "vhostblk0")
   229  
   230  	err = os.MkdirAll(vhostUserDevNodePath, dirMode)
   231  	assert.Nil(t, err)
   232  	err = os.MkdirAll(vhostUserSockPath, dirMode)
   233  	assert.Nil(t, err)
   234  	_, err = os.Create(deviceSockPath)
   235  	assert.Nil(t, err)
   236  
   237  	// mknod requires root privilege, call mock function for non-root to
   238  	// get VhostUserBlk device type.
   239  	if rootEnabled == true {
   240  		err = unix.Mknod(deviceNodePath, unix.S_IFBLK, int(unix.Mkdev(config.VhostUserBlkMajor, 0)))
   241  		assert.Nil(t, err)
   242  	} else {
   243  		savedFunc := config.GetVhostUserNodeStatFunc
   244  
   245  		_, err = os.Create(deviceNodePath)
   246  		assert.Nil(t, err)
   247  
   248  		config.GetVhostUserNodeStatFunc = func(devNodePath string,
   249  			devNodeStat *unix.Stat_t) error {
   250  			if deviceNodePath != devNodePath {
   251  				return fmt.Errorf("mock GetVhostUserNodeStatFunc error")
   252  			}
   253  
   254  			devNodeStat.Rdev = unix.Mkdev(config.VhostUserBlkMajor, 0)
   255  			return nil
   256  		}
   257  
   258  		defer func() {
   259  			config.GetVhostUserNodeStatFunc = savedFunc
   260  		}()
   261  	}
   262  
   263  	path := "/dev/vda"
   264  	deviceInfo := config.DeviceInfo{
   265  		HostPath:      deviceNodePath,
   266  		ContainerPath: path,
   267  		DevType:       "b",
   268  		Major:         config.VhostUserBlkMajor,
   269  		Minor:         0,
   270  	}
   271  
   272  	devReceiver := &api.MockDeviceReceiver{}
   273  	device, err := dm.NewDevice(deviceInfo)
   274  	assert.Nil(t, err)
   275  	_, ok := device.(*drivers.VhostUserBlkDevice)
   276  	assert.True(t, ok)
   277  
   278  	err = device.Attach(devReceiver)
   279  	assert.Nil(t, err)
   280  
   281  	err = device.Detach(devReceiver)
   282  	assert.Nil(t, err)
   283  }
   284  
   285  func TestAttachDetachDevice(t *testing.T) {
   286  	dm := NewDeviceManager(VirtioSCSI, false, "", nil)
   287  
   288  	path := "/dev/hda"
   289  	deviceInfo := config.DeviceInfo{
   290  		HostPath:      path,
   291  		ContainerPath: path,
   292  		DevType:       "b",
   293  	}
   294  
   295  	devReceiver := &api.MockDeviceReceiver{}
   296  	device, err := dm.NewDevice(deviceInfo)
   297  	assert.Nil(t, err)
   298  
   299  	// attach non-exist device
   300  	err = dm.AttachDevice("non-exist", devReceiver)
   301  	assert.NotNil(t, err)
   302  
   303  	// attach device
   304  	err = dm.AttachDevice(device.DeviceID(), devReceiver)
   305  	assert.Nil(t, err)
   306  	assert.Equal(t, device.GetAttachCount(), uint(1), "attach device count should be 1")
   307  	// attach device again(twice)
   308  	err = dm.AttachDevice(device.DeviceID(), devReceiver)
   309  	assert.Nil(t, err)
   310  	assert.Equal(t, device.GetAttachCount(), uint(2), "attach device count should be 2")
   311  
   312  	attached := dm.IsDeviceAttached(device.DeviceID())
   313  	assert.True(t, attached)
   314  
   315  	// detach device
   316  	err = dm.DetachDevice(device.DeviceID(), devReceiver)
   317  	assert.Nil(t, err)
   318  	assert.Equal(t, device.GetAttachCount(), uint(1), "attach device count should be 1")
   319  	// detach device again(twice)
   320  	err = dm.DetachDevice(device.DeviceID(), devReceiver)
   321  	assert.Nil(t, err)
   322  	assert.Equal(t, device.GetAttachCount(), uint(0), "attach device count should be 0")
   323  	// detach device again should report error
   324  	err = dm.DetachDevice(device.DeviceID(), devReceiver)
   325  	assert.NotNil(t, err)
   326  	assert.Equal(t, err, ErrDeviceNotAttached, "")
   327  	assert.Equal(t, device.GetAttachCount(), uint(0), "attach device count should be 0")
   328  
   329  	attached = dm.IsDeviceAttached(device.DeviceID())
   330  	assert.False(t, attached)
   331  
   332  	err = dm.RemoveDevice(device.DeviceID())
   333  	assert.Nil(t, err)
   334  }