Files
kata-containers/virtcontainers/device/manager/manager.go
Wei Zhang 6e6be98b15 devices: add interface "sandbox.AddDevice"
Fixes #50 .

Add new interface sandbox.AddDevice, then for Frakti use case, a device
can be attached to sandbox before container is created.

Signed-off-by: Wei Zhang <zhangwei555@huawei.com>
2018-08-15 15:24:12 +08:00

199 lines
4.7 KiB
Go

// Copyright (c) 2017-2018 Intel Corporation
// Copyright (c) 2018 Huawei Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package manager
import (
"encoding/hex"
"errors"
"sync"
"github.com/sirupsen/logrus"
"github.com/kata-containers/runtime/virtcontainers/device/api"
"github.com/kata-containers/runtime/virtcontainers/device/config"
"github.com/kata-containers/runtime/virtcontainers/device/drivers"
"github.com/kata-containers/runtime/virtcontainers/utils"
)
const (
// VirtioBlock indicates block driver is virtio-blk based
VirtioBlock string = "virtio-blk"
// VirtioSCSI indicates block driver is virtio-scsi based
VirtioSCSI string = "virtio-scsi"
)
var (
// ErrIDExhausted represents that devices are too many
// and no more IDs can be generated
ErrIDExhausted = errors.New("IDs are exhausted")
// ErrDeviceNotExist represents device hasn't been created before
ErrDeviceNotExist = errors.New("device with specified ID hasn't been created")
// ErrDeviceAttached represents the device is already attached
ErrDeviceAttached = errors.New("device is already attached")
// ErrDeviceNotAttached represents the device isn't attached
ErrDeviceNotAttached = errors.New("device isn't attached")
)
type deviceManager struct {
blockDriver string
devices map[string]api.Device
sync.RWMutex
}
func deviceLogger() *logrus.Entry {
return api.DeviceLogger().WithField("subsystem", "device")
}
// NewDeviceManager creates a deviceManager object behaved as api.DeviceManager
func NewDeviceManager(blockDriver string, devices []api.Device) api.DeviceManager {
dm := &deviceManager{
devices: make(map[string]api.Device),
}
if blockDriver == VirtioBlock {
dm.blockDriver = VirtioBlock
} else {
dm.blockDriver = VirtioSCSI
}
for _, dev := range devices {
dm.devices[dev.DeviceID()] = dev
}
return dm
}
// createDevice creates one device based on DeviceInfo
func (dm *deviceManager) createDevice(devInfo config.DeviceInfo) (api.Device, error) {
path, err := config.GetHostPathFunc(devInfo)
if err != nil {
return nil, err
}
devInfo.HostPath = path
// device ID must be generated by manager instead of device itself
// in case of ID collision
if devInfo.ID, err = dm.newDeviceID(); err != nil {
return nil, err
}
if isVFIO(path) {
return drivers.NewVFIODevice(&devInfo), nil
} else if isBlock(devInfo) {
if devInfo.DriverOptions == nil {
devInfo.DriverOptions = make(map[string]string)
}
devInfo.DriverOptions["block-driver"] = dm.blockDriver
return drivers.NewBlockDevice(&devInfo), nil
} else {
deviceLogger().WithField("device", path).Info("Device has not been passed to the container")
return drivers.NewGenericDevice(&devInfo), nil
}
}
// NewDevice creates a device based on specified DeviceInfo
func (dm *deviceManager) NewDevice(devInfo config.DeviceInfo) (api.Device, error) {
dm.Lock()
defer dm.Unlock()
dev, err := dm.createDevice(devInfo)
if err == nil {
dm.devices[dev.DeviceID()] = dev
}
return dev, err
}
// RemoveDevice deletes the device from list based on specified device id
func (dm *deviceManager) RemoveDevice(id string) error {
dm.Lock()
defer dm.Unlock()
if _, ok := dm.devices[id]; !ok {
return ErrDeviceNotExist
}
delete(dm.devices, id)
return nil
}
func (dm *deviceManager) newDeviceID() (string, error) {
for i := 0; i < 5; i++ {
// generate an random ID
randBytes, err := utils.GenerateRandomBytes(8)
if err != nil {
return "", err
}
id := hex.EncodeToString(randBytes)
// check ID collision, choose another one if ID is in use
if _, ok := dm.devices[id]; !ok {
return id, nil
}
}
return "", ErrIDExhausted
}
func (dm *deviceManager) AttachDevice(id string, dr api.DeviceReceiver) error {
dm.Lock()
defer dm.Unlock()
d, ok := dm.devices[id]
if !ok {
return ErrDeviceNotExist
}
if d.IsAttached() {
return ErrDeviceAttached
}
if err := d.Attach(dr); err != nil {
return err
}
return nil
}
func (dm *deviceManager) DetachDevice(id string, dr api.DeviceReceiver) error {
dm.Lock()
defer dm.Unlock()
d, ok := dm.devices[id]
if !ok {
return ErrDeviceNotExist
}
if !d.IsAttached() {
return ErrDeviceNotAttached
}
if err := d.Detach(dr); err != nil {
return err
}
return nil
}
func (dm *deviceManager) GetDeviceByID(id string) api.Device {
dm.RLock()
defer dm.RUnlock()
if d, ok := dm.devices[id]; ok {
return d
}
return nil
}
func (dm *deviceManager) GetAllDevices() []api.Device {
dm.RLock()
defer dm.RUnlock()
devices := []api.Device{}
for _, v := range dm.devices {
devices = append(devices, v)
}
return devices
}
func (dm *deviceManager) IsDeviceAttached(id string) bool {
dm.RLock()
defer dm.RUnlock()
d, ok := dm.devices[id]
if !ok {
return false
}
return d.IsAttached()
}