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 }