Skip to content

Commit

Permalink
incusd/scriptlet: Add useful QMP functions
Browse files Browse the repository at this point in the history
Signed-off-by: Benjamin Somers <benjamin.somers@imt-atlantique.fr>
  • Loading branch information
bensmrs committed Oct 29, 2024
1 parent fbba738 commit a0c30c0
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 4 deletions.
15 changes: 15 additions & 0 deletions internal/server/scriptlet/load/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@ func QEMUCompile(name string, src string) (*starlark.Program, error) {
"log_warn",
"log_error",
"run_qmp",
"run_command",
"blockdev_add",
"blockdev_del",
"chardev_add",
"chardev_change",
"chardev_remove",
"device_add",
"device_del",
"netdev_add",
"netdev_del",
"object_add",
"object_del",
"qom_get",
"qom_list",
"qom_set",
})
}

Expand Down
106 changes: 102 additions & 4 deletions internal/server/scriptlet/qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package scriptlet
import (
"encoding/json"
"fmt"
"strings"

"go.starlark.net/starlark"

Expand Down Expand Up @@ -46,13 +47,110 @@ func QEMURun(l logger.Logger, m *qmp.Monitor, instance string, stage string) err
return rv, nil
}

runCommandFromKwargs := func(funName string, kwargs []starlark.Tuple) (starlark.Value, error) {
qmpArgs := make(map[string]any)
for _, kwarg := range kwargs {
key, err := StarlarkUnmarshal(kwarg.Index(0))
if err != nil {
return nil, err
}

value, err := StarlarkUnmarshal(kwarg.Index(1))
if err != nil {
return nil, err
}

qmpArgs[key.(string)] = value
}

var resp struct {
Return any
}

err := m.Run(funName, qmpArgs, &resp)
if err != nil {
return nil, err
}

// Extract the return value
rv, err := StarlarkMarshal(resp.Return)
if err != nil {
return nil, fmt.Errorf("Marshalling QMP response failed: %w", err)
}

return rv, nil
}

runCommandFunc := func(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
frame := thread.CallFrame(1)
errPrefix := fmt.Sprintf("run_command (%d:%d):", frame.Pos.Line, frame.Pos.Col)

argsLen := args.Len()
if argsLen != 1 {
return nil, fmt.Errorf("%s Expected exactly one positional argument, got %d", errPrefix, argsLen)
}

arg, err := StarlarkUnmarshal(args.Index(0))
if err != nil {
return nil, err
}

funName, ok := arg.(string)
if !ok {
return nil, fmt.Errorf("%s The positional argument must be a string representing a QMP function", errPrefix)
}

rv, err := runCommandFromKwargs(funName, kwargs)
if err != nil {
return nil, err
}

return rv, nil
}

makeQOM := func(funName string) *starlark.Builtin {
fun := func(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
frame := thread.CallFrame(1)
errPrefix := fmt.Sprintf("%s (%d:%d):", strings.ReplaceAll(funName, "-", "_"), frame.Pos.Line, frame.Pos.Col)

argsLen := args.Len()
if argsLen != 0 {
return nil, fmt.Errorf("%s Expected only keyword arguments, got %d positional argument(s)", errPrefix, argsLen)
}

rv, err := runCommandFromKwargs(funName, kwargs)
if err != nil {
return nil, err
}

return rv, nil
}

return starlark.NewBuiltin(funName, fun)
}

// Remember to match the entries in scriptletLoad.QEMUCompile() with this list so Starlark can
// perform compile time validation of functions used.
env := starlark.StringDict{
"log_info": starlark.NewBuiltin("log_info", logFunc),
"log_warn": starlark.NewBuiltin("log_warn", logFunc),
"log_error": starlark.NewBuiltin("log_error", logFunc),
"run_qmp": starlark.NewBuiltin("run_qmp", runQMPFunc),
"log_info": starlark.NewBuiltin("log_info", logFunc),
"log_warn": starlark.NewBuiltin("log_warn", logFunc),
"log_error": starlark.NewBuiltin("log_error", logFunc),
"run_qmp": starlark.NewBuiltin("run_qmp", runQMPFunc),
"run_command": starlark.NewBuiltin("run_command", runCommandFunc),
"blockdev_add": makeQOM("blockdev-add"),
"blockdev_del": makeQOM("blockdev-del"),
"chardev_add": makeQOM("chardev-add"),
"chardev_change": makeQOM("chardev-change"),
"chardev_remove": makeQOM("chardev-remove"),
"device_add": makeQOM("device_add"),
"device_del": makeQOM("device_del"),
"netdev_add": makeQOM("netdev_add"),
"netdev_del": makeQOM("netdev_del"),
"object_add": makeQOM("object-add"),
"object_del": makeQOM("object-del"),
"qom_get": makeQOM("qom-get"),
"qom_list": makeQOM("qom-list"),
"qom_set": makeQOM("qom-set"),
}

prog, thread, err := scriptletLoad.QEMUProgram(instance)
Expand Down

0 comments on commit a0c30c0

Please sign in to comment.