Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Device: Support long device names for host path passthrough to VMs #13603

Merged
merged 17 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d9b3c7c
lxd/instance/drivers/driver/common: Update devicesUpdate to return sl…
tomponline Jun 13, 2024
772bfe5
lxd/instance/drivers/driver/lxc: Use devlxd events returned from d.de…
tomponline Jun 13, 2024
2a04bb5
lxd/instance/drivers/driver/qemu: Use devlxd events returned from d.d…
tomponline Jun 13, 2024
0fb4c8a
lxd/instance/drivers/driver/qemu: Update deviceAttachPath to return m…
tomponline Jun 13, 2024
2059bfc
lxd/instance/drivers/driver/common: Capture mount tag returned from d…
tomponline Jun 13, 2024
b356e62
lxd-agent/events: Detect mount tag in source field of options for dis…
tomponline Jun 13, 2024
0555dbf
lxd-agent/events: Linter fix
tomponline Jun 14, 2024
ff07ea1
lxd/instance/drivers/driver/qemu: Use consistent host drive share dev…
tomponline Jun 14, 2024
37b9359
lxd/instance/drivers: Extract hashing from `generateQemuDeviceName` t…
hamistao Jun 13, 2024
e2a4fcf
lxd/instance/drivers: Indicate max device ID length with `qemuDeviceI…
hamistao Jun 12, 2024
b331f38
lxd/instance/drivers/driver/qemu/templates: Fix qemuHostDriveDeviceID…
tomponline Jun 14, 2024
ad7a94a
lxd/instance/drivers/driver/qemu: Align fsdev and chardev names for h…
tomponline Jun 14, 2024
72f9a67
lxd/device/disk: Escape device name when using it as part of a path f…
tomponline Jun 14, 2024
d9e8f3d
lxd/instance/drivers/driver/qemu: Update path of virtiofsd in deviceA…
tomponline Jun 14, 2024
bb2bde7
lxd/instance/drivers/driver/qemu: Updates generateQemuDeviceName to a…
tomponline Jun 14, 2024
289d0d7
lxd/instance/drivers/driver/qemu: Update usage of d.generateQemuDevic…
tomponline Jun 14, 2024
1b914b4
lxd/instance/drivers/driver/qemu/config/test: Update host drive tests…
tomponline Jun 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions lxd-agent/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

"github.com/canonical/lxd/lxd/events"
"github.com/canonical/lxd/lxd/instance/instancetype"
"github.com/canonical/lxd/lxd/response"
"github.com/canonical/lxd/shared"
"github.com/canonical/lxd/shared/api"
Expand All @@ -27,6 +28,7 @@ type eventsServe struct {
d *Daemon
}

// Render starts event socket.
func (r *eventsServe) Render(w http.ResponseWriter) error {
return eventsSocket(r.d, r.req, w)
}
Expand Down Expand Up @@ -115,9 +117,10 @@ func eventsProcess(event api.Event) {
}

type deviceEvent struct {
Action string `json:"action"`
Config map[string]string `json:"config"`
Name string `json:"name"`
Action string `json:"action"`
Config map[string]string `json:"config"`
Name string `json:"name"`
Mount instancetype.VMAgentMount `json:"mount"`
}

e := deviceEvent{}
Expand All @@ -143,6 +146,9 @@ func eventsProcess(event api.Event) {

// Attempt to perform the mount.
mntSource := fmt.Sprintf("lxd_%s", e.Name)
if e.Mount.Source != "" {
mntSource = e.Mount.Source
}

_ = os.MkdirAll(e.Config["path"], 0755)
_, err = shared.RunCommand("mount", "-t", "virtiofs", mntSource, e.Config["path"])
Expand Down
8 changes: 4 additions & 4 deletions lxd/device/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -850,15 +850,15 @@ func (d *disk) startContainer() (*deviceConfig.RunConfig, error) {

// vmVirtfsProxyHelperPaths returns the path for PID file to use with virtfs-proxy-helper process.
func (d *disk) vmVirtfsProxyHelperPaths() string {
pidPath := filepath.Join(d.inst.DevicesPath(), fmt.Sprintf("%s.pid", d.name))
pidPath := filepath.Join(d.inst.DevicesPath(), fmt.Sprintf("%s.pid", filesystem.PathNameEncode(d.name)))

return pidPath
}

// vmVirtiofsdPaths returns the path for the socket and PID file to use with virtiofsd process.
func (d *disk) vmVirtiofsdPaths() (sockPath string, pidPath string) {
sockPath = filepath.Join(d.inst.DevicesPath(), fmt.Sprintf("virtio-fs.%s.sock", d.name))
pidPath = filepath.Join(d.inst.DevicesPath(), fmt.Sprintf("virtio-fs.%s.pid", d.name))
sockPath = filepath.Join(d.inst.DevicesPath(), fmt.Sprintf("virtio-fs.%s.sock", filesystem.PathNameEncode(d.name)))
pidPath = filepath.Join(d.inst.DevicesPath(), fmt.Sprintf("virtio-fs.%s.pid", filesystem.PathNameEncode(d.name)))

return sockPath, pidPath
}
Expand Down Expand Up @@ -1114,7 +1114,7 @@ func (d *disk) startVM() (*deviceConfig.RunConfig, error) {
// virtfs-proxy-helper 9p share. The 9p share will only be used as a fallback.
err = func() error {
sockPath, pidPath := d.vmVirtiofsdPaths()
logPath := filepath.Join(d.inst.LogPath(), fmt.Sprintf("disk.%s.log", d.name))
logPath := filepath.Join(d.inst.LogPath(), fmt.Sprintf("disk.%s.log", filesystem.PathNameEncode(d.name)))
_ = os.Remove(logPath) // Remove old log if needed.

revertFunc, unixListener, err := DiskVMVirtiofsdStart(d.state.OS.ExecPath, d.inst, sockPath, pidPath, logPath, mount.DevPath, rawIDMaps)
Expand Down
57 changes: 44 additions & 13 deletions lxd/instance/drivers/driver_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -1419,13 +1419,13 @@ func (d *common) devicesRegister(inst instance.Instance) {
}

// devicesUpdate applies device changes to an instance.
func (d *common) devicesUpdate(inst instance.Instance, removeDevices deviceConfig.Devices, addDevices deviceConfig.Devices, updateDevices deviceConfig.Devices, oldExpandedDevices deviceConfig.Devices, instanceRunning bool, userRequested bool) error {
func (d *common) devicesUpdate(inst instance.Instance, removeDevices deviceConfig.Devices, addDevices deviceConfig.Devices, updateDevices deviceConfig.Devices, oldExpandedDevices deviceConfig.Devices, instanceRunning bool, userRequested bool) (devlxdEvents []map[string]any, err error) {
revert := revert.New()
defer revert.Fail()

dm, ok := inst.(deviceManager)
if !ok {
return fmt.Errorf("Instance is not compatible with deviceManager interface")
return nil, fmt.Errorf("Instance is not compatible with deviceManager interface")
}

// Remove devices in reverse order to how they were added.
Expand All @@ -1446,13 +1446,19 @@ func (d *common) devicesUpdate(inst instance.Instance, removeDevices deviceConfi
if instanceRunning {
err = dm.deviceStop(dev, instanceRunning, "")
if err != nil {
return fmt.Errorf("Failed to stop device %q: %w", dev.Name(), err)
return nil, fmt.Errorf("Failed to stop device %q: %w", dev.Name(), err)
}

devlxdEvents = append(devlxdEvents, map[string]any{
"action": "removed",
"name": entry.Name,
"config": entry.Config,
})
}

err = d.deviceRemove(dev, instanceRunning)
if err != nil && err != device.ErrUnsupportedDevType {
return fmt.Errorf("Failed to remove device %q: %w", dev.Name(), err)
return nil, fmt.Errorf("Failed to remove device %q: %w", dev.Name(), err)
}
}

Expand All @@ -1461,7 +1467,7 @@ func (d *common) devicesUpdate(inst instance.Instance, removeDevices deviceConfi
// this device (as its an actual removal or a device type change).
err = d.deviceVolatileReset(entry.Name, entry.Config, addDevices[entry.Name])
if err != nil {
return fmt.Errorf("Failed to reset volatile data for device %q: %w", entry.Name, err)
return nil, fmt.Errorf("Failed to reset volatile data for device %q: %w", entry.Name, err)
}
}

Expand All @@ -1475,7 +1481,7 @@ func (d *common) devicesUpdate(inst instance.Instance, removeDevices deviceConfi
}

if userRequested {
return fmt.Errorf("Failed add validation for device %q: %w", entry.Name, err)
return nil, fmt.Errorf("Failed add validation for device %q: %w", entry.Name, err)
}

// If update is non-user requested (i.e from a snapshot restore), there's nothing we can
Expand All @@ -1488,7 +1494,7 @@ func (d *common) devicesUpdate(inst instance.Instance, removeDevices deviceConfi
err = d.deviceAdd(dev, instanceRunning)
if err != nil {
if userRequested {
return fmt.Errorf("Failed to add device %q: %w", dev.Name(), err)
return nil, fmt.Errorf("Failed to add device %q: %w", dev.Name(), err)
}

// If update is non-user requested (i.e from a snapshot restore), there's nothing we can
Expand All @@ -1501,15 +1507,34 @@ func (d *common) devicesUpdate(inst instance.Instance, removeDevices deviceConfi
if instanceRunning {
err = dev.PreStartCheck()
if err != nil {
return fmt.Errorf("Failed pre-start check for device %q: %w", dev.Name(), err)
return nil, fmt.Errorf("Failed pre-start check for device %q: %w", dev.Name(), err)
}

_, err := dm.deviceStart(dev, instanceRunning)
runConf, err := dm.deviceStart(dev, instanceRunning)
if err != nil && err != device.ErrUnsupportedDevType {
return fmt.Errorf("Failed to start device %q: %w", dev.Name(), err)
return nil, fmt.Errorf("Failed to start device %q: %w", dev.Name(), err)
}

revert.Add(func() { _ = dm.deviceStop(dev, instanceRunning, "") })

event := map[string]any{
"action": "added",
"name": entry.Name,
"config": entry.Config,
}

if len(runConf.Mounts) > 0 {
for _, opt := range runConf.Mounts[0].Opts {
tomponline marked this conversation as resolved.
Show resolved Hide resolved
if strings.HasPrefix(opt, "mountTag=") {
parts := strings.SplitN(opt, "=", 2)
event["mount"] = instancetype.VMAgentMount{
Source: parts[1],
}
}
}
}

devlxdEvents = append(devlxdEvents, event)
}
}

Expand All @@ -1522,7 +1547,7 @@ func (d *common) devicesUpdate(inst instance.Instance, removeDevices deviceConfi
}

if userRequested {
return fmt.Errorf("Failed update validation for device %q: %w", entry.Name, err)
return nil, fmt.Errorf("Failed update validation for device %q: %w", entry.Name, err)
}

// If update is non-user requested (i.e from a snapshot restore), there's nothing we can
Expand All @@ -1542,6 +1567,12 @@ func (d *common) devicesUpdate(inst instance.Instance, removeDevices deviceConfi
if err != nil {
l.Error("Failed to stop device after update validation failed", logger.Ctx{"err": err})
}

devlxdEvents = append(devlxdEvents, map[string]any{
"action": "updated",
"name": entry.Name,
"config": entry.Config,
})
}

err = d.deviceRemove(dev, instanceRunning)
Expand All @@ -1555,12 +1586,12 @@ func (d *common) devicesUpdate(inst instance.Instance, removeDevices deviceConfi

err = dev.Update(oldExpandedDevices, instanceRunning)
if err != nil {
return fmt.Errorf("Failed to update device %q: %w", dev.Name(), err)
return nil, fmt.Errorf("Failed to update device %q: %w", dev.Name(), err)
}
}

revert.Success()
return nil
return devlxdEvents, nil
}

// devicesRemove runs device removal function for each device.
Expand Down
40 changes: 4 additions & 36 deletions lxd/instance/drivers/driver_lxc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4384,7 +4384,7 @@ func (d *lxc) Update(args db.InstanceArgs, userRequested bool) error {
isRunning := d.IsRunning()

// Use the device interface to apply update changes.
err = d.devicesUpdate(d, removeDevices, addDevices, updateDevices, oldExpandedDevices, isRunning, userRequested)
devlxdEvents, err := d.devicesUpdate(d, removeDevices, addDevices, updateDevices, oldExpandedDevices, isRunning, userRequested)
if err != nil {
return err
}
Expand Down Expand Up @@ -4819,41 +4819,9 @@ func (d *lxc) Update(args db.InstanceArgs, userRequested bool) error {
}
}

// Device changes
for k, m := range removeDevices {
msg := map[string]any{
"action": "removed",
"name": k,
"config": m,
}

err = d.devlxdEventSend("device", msg)
if err != nil {
return err
}
}

for k, m := range updateDevices {
msg := map[string]any{
"action": "updated",
"name": k,
"config": m,
}

err = d.devlxdEventSend("device", msg)
if err != nil {
return err
}
}

for k, m := range addDevices {
msg := map[string]any{
"action": "added",
"name": k,
"config": m,
}

err = d.devlxdEventSend("device", msg)
// Device events.
for _, event := range devlxdEvents {
err = d.devlxdEventSend("device", event)
if err != nil {
return err
}
Expand Down
Loading
Loading