使用QMP管理QEMU虚拟机
2024-07-15
QEMU Machine Protocol (QMP) 是一种强大而灵活的协议,为管理和控制 QEMU 虚拟机提供了标准化的接口。作为一种基于 JSON 的协议,QMP 允许管理员和开发人员通过简单而结构化的命令与 QEMU 实例进行交互。无论是远程控制虚拟机、监控性能、管理设备, 还是执行复杂的自动化任务,QMP 都为虚拟化环境的管理提供了一个全面而强大的工具集。在许多 QEMU 管理平台也都能看到 QMP 的身影。
1. 工作原理
1.1. 连接建立
QMP 通过 TCP 或 unix Socket 与 QEMU 实例建立连接。当 QEMU 启动时, 可以指定一个 TCP 端口或 Socket 文件用于 QMP 连接。客户端 (如管理工具或脚本) 可以连接到这个端口或 Socket 文件来与 QEMU 进行通信。
1.2. 通信协议
QMP 使用 JSON (JavaScript Object Notation) 作为其数据交换格式。这种格式易于人类阅读和机器解析,使得 QMP 命令和响应结构清晰且灵活。
1.3. 命令结构
QMP 命令通常包含以下几个部分:
-
execute
: 指定要执行的命令名称 -
arguments
: 包含命令所需的参数 (如果有)
例如:
{ "execute": "query-status", "arguments": {} }
1.4. 响应结构
QEMU 对每个 QMP 命令都会返回一个 JSON 格式的响应, 通常包括:
-
return
: 包含命令执行结果 -
error
: 如果发生错误, 会包含错误信息
例如:
{ "return": { "status": "running", "singlestep": false, "running": true } }
2. 用法
-
启动 QEMU 虚拟机并启用 QMP 协议
# 以Socket形式启用QMP qemu-system-x86_64 -qmp unix:qmp.socket,server,nowait ... # 以TCP形式启用QMP qemu-system-x86_64 -qmp tcp:127.0.0.1:12345,server,nowait ...
-
连接 QMP
# 连接Socket协议QMP nc -U test.socket # 连接TCP协议QMP nc localhost 12345
连接成功后,会返回 qemu 的版本
{"QMP": {"version": {"qemu": {"micro": 5, "minor": 1, "major": 8}, "package": "pve-qemu-kvm_8.1.5-5"}, "capabilities": []}}
-
初始化 QMP 会话
首先在刚连接到 QMP 会话后,大部分指令都不能使用,需要先进行初始化,表明客户端准备好接收事件和处理命令
请求:{ "execute": "qmp_capabilities" }
响应:
{"return": {}}
-
检查虚拟机状态
请求
{ "execute": "query-status" }
响应
{"return": {"status": "running", "singlestep": false, "running": true}}
3. 常用命令
3.1. query-commands
这条命令查询目前支持的所有 QMP 命令
{ "execute": "query-commands" }
{"return": [{"name": "device_add"}, {"name": "cxl-inject-correctable-error"}, {"name": "cxl-inject-uncorrectable-errors"}, {"name": "cxl-inject-poison"}, {"name": "cxl-inject-memory-module-event"}, {"name": "cxl-inject-dram-event"}, {"name": "cxl-inject-general-media-event"}, {"name": "query-cryptodev"}, {"name": "x-query-virtio-queue-element"}, {"name": "x-query-virtio-vhost-queue-status"}, {"name": "x-query-virtio-queue-status"}, {"name": "x-query-virtio-status"}, {"name": "x-query-virtio"}, {"name": "query-stats-schemas"}, {"name": "query-stats"}, {"name": "query-pci"}, {"name": "query-acpi-ospm-status"}, {"name": "query-audiodevs"}, {"name": "xen-event-inject"}, {"name": "xen-event-list"}, {"name": "query-sgx-capabilities"}, {"name": "query-sgx"}, {"name": "query-sev-attestation-report"}, {"name": "sev-inject-launch-secret"}, {"name": "query-sev-capabilities"}, {"name": "query-sev-launch-measure"}, {"name": "query-sev"}, {"name": "rtc-reset-reinjection"}, {"name": "query-command-line-options"}, {"name": "savevm-end"}, {"name": "savevm-start"}, {"name": "query-fdsets"}, {"name": "remove-fd"}, {"name": "add-fd"}, {"name": "closefd"}, {"name": "getfd"}, {"name": "human-monitor-command"}, {"name": "x-exit-preconfig"}, {"name": "cont"}, {"name": "stop"}, {"name": "query-iothreads"}, {"name": "query-name"}, {"name": "add_client"}, {"name": "query-yank"}, {"name": "yank"}, {"name": "replay-seek"}, {"name": "replay-delete-break"}, {"name": "replay-break"}, {"name": "query-replay"}, {"name": "query-cpu-definitions"}, {"name": "query-cpu-model-expansion"}, {"name": "dumpdtb"}, {"name": "x-query-usb"}, {"name": "x-query-roms"}, {"name": "x-query-rdma"}, {"name": "x-query-ramblock"}, {"name": "x-query-opcount"}, {"name": "x-query-numa"}, {"name": "x-query-jit"}, {"name": "x-query-irq"}, {"name": "query-memory-devices"}, {"name": "query-memory-size-summary"}, {"name": "query-balloon"}, {"name": "balloon"}, {"name": "set-numa-node"}, {"name": "query-hotpluggable-cpus"}, {"name": "query-memdev"}, {"name": "pmemsave"}, {"name": "memsave"}, {"name": "query-kvm"}, {"name": "inject-nmi"}, {"name": "system_wakeup"}, {"name": "system_powerdown"}, {"name": "system_reset"}, {"name": "query-vm-generation-id"}, {"name": "query-uuid"}, {"name": "query-target"}, {"name": "query-current-machine"}, {"name": "query-machines"}, {"name": "query-cpus-fast"}, {"name": "device_del"}, {"name": "device-list-properties"}, {"name": "object-del"}, {"name": "object-add"}, {"name": "qom-list-properties"}, {"name": "qom-list-types"}, {"name": "qom-set"}, {"name": "qom-get"}, {"name": "qom-list"}, {"name": "query-qmp-schema"}, {"name": "quit"}, {"name": "query-commands"}, {"name": "query-version"}, {"name": "qmp_capabilities"}, {"name": "trace-event-set-state"}, {"name": "trace-event-get-state"}, {"name": "transaction"}, {"name": "snapshot-delete"}, {"name": "snapshot-load"}, {"name": "snapshot-save"}, {"name": "query-migrationthreads"}, {"name": "query-vcpu-dirty-limit"}, {"name": "cancel-vcpu-dirty-limit"}, {"name": "set-vcpu-dirty-limit"}, {"name": "query-dirty-rate"}, {"name": "calc-dirty-rate"}, {"name": "migrate-pause"}, {"name": "migrate-recover"}, {"name": "query-colo-status"}, {"name": "xen-colo-do-checkpoint"}, {"name": "query-xen-replication-status"}, {"name": "xen-set-replication"}, {"name": "xen-load-devices-state"}, {"name": "xen-set-global-dirty-log"}, {"name": "xen-save-devices-state"}, {"name": "migrate-incoming"}, {"name": "migrate"}, {"name": "migrate-continue"}, {"name": "migrate_cancel"}, {"name": "x-colo-lost-heartbeat"}, {"name": "migrate-start-postcopy"}, {"name": "query-migrate-parameters"}, {"name": "migrate-set-parameters"}, {"name": "query-migrate-capabilities"}, {"name": "migrate-set-capabilities"}, {"name": "query-migrate"}, {"name": "query-savevm"}, {"name": "client_migrate_info"}, {"name": "display-update"}, {"name": "display-reload"}, {"name": "query-display-options"}, {"name": "input-send-event"}, {"name": "send-key"}, {"name": "query-mice"}, {"name": "change-vnc-password"}, {"name": "query-vnc-servers"}, {"name": "query-vnc"}, {"name": "query-spice"}, {"name": "screendump"}, {"name": "expire_password"}, {"name": "set_password"}, {"name": "query-tpm"}, {"name": "query-tpm-types"}, {"name": "query-tpm-models"}, {"name": "query-rocker-of-dpa-groups"}, {"name": "query-rocker-of-dpa-flows"}, {"name": "query-rocker-ports"}, {"name": "query-rocker"}, {"name": "announce-self"}, {"name": "query-rx-filter"}, {"name": "netdev_del"}, {"name": "netdev_add"}, {"name": "set_link"}, {"name": "query-dump-guest-memory-capability"}, {"name": "query-dump"}, {"name": "dump-guest-memory"}, {"name": "chardev-send-break"}, {"name": "chardev-remove"}, {"name": "chardev-change"}, {"name": "chardev-add"}, {"name": "ringbuf-read"}, {"name": "ringbuf-write"}, {"name": "query-chardev-backends"}, {"name": "query-chardev"}, {"name": "query-block-exports"}, {"name": "block-export-del"}, {"name": "block-export-add"}, {"name": "nbd-server-stop"}, {"name": "nbd-server-remove"}, {"name": "nbd-server-add"}, {"name": "nbd-server-start"}, {"name": "blockdev-snapshot-delete-internal-sync"}, {"name": "blockdev-snapshot-internal-sync"}, {"name": "x-blockdev-set-iothread"}, {"name": "x-blockdev-change"}, {"name": "block-set-write-threshold"}, {"name": "x-blockdev-amend"}, {"name": "blockdev-create"}, {"name": "blockdev-del"}, {"name": "blockdev-reopen"}, {"name": "blockdev-add"}, {"name": "block-job-finalize"}, {"name": "block-job-dismiss"}, {"name": "block-job-complete"}, {"name": "block-job-resume"}, {"name": "block-job-pause"}, {"name": "block-job-cancel"}, {"name": "block-job-set-speed"}, {"name": "block-stream"}, {"name": "blockdev-mirror"}, {"name": "x-debug-block-dirty-bitmap-sha256"}, {"name": "block-dirty-bitmap-merge"}, {"name": "block-dirty-bitmap-disable"}, {"name": "block-dirty-bitmap-enable"}, {"name": "block-dirty-bitmap-clear"}, {"name": "block-dirty-bitmap-remove"}, {"name": "block-dirty-bitmap-add"}, {"name": "drive-mirror"}, {"name": "x-debug-query-block-graph"}, {"name": "query-named-block-nodes"}, {"name": "blockdev-backup"}, {"name": "drive-backup"}, {"name": "block-commit"}, {"name": "change-backing-file"}, {"name": "blockdev-snapshot"}, {"name": "blockdev-snapshot-sync"}, {"name": "block_resize"}, {"name": "query-block-jobs"}, {"name": "query-blockstats"}, {"name": "query-pbs-bitmap-info"}, {"name": "query-proxmox-support"}, {"name": "backup-cancel"}, {"name": "query-backup"}, {"name": "backup"}, {"name": "query-block"}, {"name": "block-latency-histogram-set"}, {"name": "block_set_io_throttle"}, {"name": "blockdev-change-medium"}, {"name": "blockdev-insert-medium"}, {"name": "blockdev-remove-medium"}, {"name": "blockdev-close-tray"}, {"name": "blockdev-open-tray"}, {"name": "eject"}, {"name": "query-pr-managers"}, {"name": "query-jobs"}, {"name": "job-finalize"}, {"name": "job-dismiss"}, {"name": "job-complete"}, {"name": "job-cancel"}, {"name": "job-resume"}, {"name": "job-pause"}, {"name": "set-action"}, {"name": "watchdog-set-action"}, {"name": "query-status"}]}
3.2. human-monitor-command
这个命令是在人工监视器上执行命令并返回输出,不过不建议使用该命令,因为通过 Json 输出的可读性很差,没有结构化的语义输出
{ "execute": "human-monitor-command", "arguments": { "command-line": "help" } }
{ "execute": "human-monitor-command", "arguments": { "command-line": "info version" } }
{ "execute": "human-monitor-command", "arguments": { "command-line": "info network" } }
{ "execute": "human-monitor-command", "arguments": { "command-line": "info qtree" } }
比如,执行第一条 help 命令的返回结果如下,包含了大量的转义字符,阅读起来不太方便
{"return": "announce_self [interfaces] [id] -- Trigger GARP/RARP announcements\r\nbackup backupfile [speed [devlist]] -- create a VM backup (VMA format).\r\nbackup_cancel -- cancel the current VM backup\r\nballoon target -- request VM to change its memory allocation (in MB)\r\nblock_job_cancel [-f] device -- stop an active background block operation (use -f\r\n\t\t\t if you want to abort the operation immediately\r\n\t\t\t instead of keep running until data is in sync)\r\nblock_job_complete device -- stop an active background block operation\r\nblock_job_pause device -- pause an active background block operation\r\nblock_job_resume device -- resume a paused background block operation\r\nblock_job_set_speed device speed -- set maximum speed for a background block operation\r\nblock_resize device size -- resize a block image\r\nblock_set_io_throttle device bps bps_rd bps_wr iops iops_rd iops_wr -- change I/O throttle limits for a block drive\r\nblock_stream device [speed [base]] -- copy data from a backing file into a block device\r\nboot_set bootdevice -- define new values for the boot device list\r\ncalc_dirty_rate [-r] [-b] second [sample_pages_per_GB] -- start a round of guest dirty rate measurement (using -r to\r\n\t\t\t specify dirty ring as the method of calculation and\r\n\t\t\t -b to specify dirty bitmap as method of calculation)\r\ncancel_vcpu_dirty_limit [cpu_index] -- cancel dirty page rate limit, use cpu_index to cancel\r\n\t\t\t\t\t limit on a specified virtual cpu\r\nchange device [-f] filename [format [read-only-mode]] -- change a removable medium, optional format, use -f to force the operation\r\nchardev-add args -- add chardev\r\nchardev-change id args -- change chardev\r\nchardev-remove id -- remove chardev\r\nchardev-send-break id -- send a break on chardev\r\nclient_migrate_info protocol hostname port tls-port cert-subject -- set migration information for remote display\r\nclosefd closefd name -- close a file descriptor previously passed via SCM rights\r\ncommit device|all -- commit changes to the disk images (if -snapshot is used) or backing files\r\ncont|c -- resume emulation\r\ncpu index -- set the default CPU\r\ndelvm tag -- delete a VM snapshot from its tag\r\ndevice_add driver[,prop=value][,...] -- add device, like -device on the command line\r\ndevice_del device -- remove device\r\ndrive_add [-n] [[<domain>:]<bus>:]<slot>\r\n[file=file][,if=type][,bus=n]\r\n[,unit=m][,media=d][,index=i]\r\n[,snapshot=on|off][,cache=on|off]\r\n[,readonly=on|off][,copy-on-read=on|off] -- add drive to PCI storage controller\r\ndrive_backup [-n] [-f] [-c] device target [format] -- initiates a point-in-time\r\n\t\t\tcopy for a device. The device's contents are\r\n\t\t\tcopied to the new image file, excluding data that\r\n\t\t\tis written after the command is started.\r\n\t\t\tThe -n flag requests QEMU to reuse the image found\r\n\t\t\tin new-image-file, instead of recreating it from scratch.\r\n\t\t\tThe -f flag requests QEMU to copy the whole disk,\r\n\t\t\tso that the result does not need a backing file.\r\n\t\t\tThe -c flag requests QEMU to compress backup data\r\n\t\t\t(if the target format supports it).\r\n\t\t\t\r\ndrive_del device -- remove host block device\r\ndrive_mirror [-n] [-f] device target [format] -- initiates live storage\r\n\t\t\tmigration for a device. The device's contents are\r\n\t\t\tcopied to the new image file, including data that\r\n\t\t\tis written after the command is started.\r\n\t\t\tThe -n flag requests QEMU to reuse the image found\r\n\t\t\tin new-image-file, instead of recreating it from scratch.\r\n\t\t\tThe -f flag requests QEMU to copy the whole disk,\r\n\t\t\tso that the result does not need a backing file.\r\n\t\t\t\r\ndump-guest-memory [-p] [-d] [-z|-l|-s|-w] filename [begin length] -- dump guest memory into file 'filename'.\r\n\t\t\t-p: do paging to get guest's memory mapping.\r\n\t\t\t-d: return immediately (do not wait for completion).\r\n\t\t\t-z: dump in kdump-compressed format, with zlib compression.\r\n\t\t\t-l: dump in kdump-compressed format, with lzo compression.\r\n\t\t\t-s: dump in kdump-compressed format, with snappy compression.\r\n\t\t\t-w: dump in Windows crashdump format (can be used instead of ELF-dump converting),\r\n\t\t\t for Windows x86 and x64 guests with vmcoreinfo driver only.\r\n\t\t\tbegin: the starting physical address.\r\n\t\t\tlength: the memory size, in bytes.\r\ndumpdtb filename -- dump the FDT in dtb format to 'filename'\r\neject [-f] device -- eject a removable medium (use -f to force it)\r\nexit_preconfig -- exit the preconfig state\r\nexpire_password protocol time [-d display] -- set spice/vnc password expire-time\r\ngdbserver [device] -- start gdbserver on given device (default 'tcp::1234'), stop with 'none'\r\ngetfd getfd name -- receive a file descriptor via SCM rights and assign it a name\r\ngpa2hpa addr -- print the host physical address corresponding to a guest physical address\r\ngpa2hva addr -- print the host virtual address corresponding to a guest physical address\r\ngva2gpa addr -- print the guest physical address corresponding to a guest virtual address\r\nhelp|? [cmd] -- show the help\r\nhostfwd_add [netdev_id] [tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport -- redirect TCP or UDP connections from host to guest (requires -net user)\r\nhostfwd_remove [netdev_id] [tcp|udp]:[hostaddr]:hostport -- remove host-to-guest TCP or UDP redirection\r\ni /fmt addr -- I/O port read\r\ninfo [subcommand] -- show various information about the system state\r\nloadvm tag -- restore a VM snapshot from its tag\r\nlog item1[,...] -- activate logging of the specified items\r\nlogfile filename -- output logs to 'filename'\r\nmce [-b] cpu bank status mcgstatus addr misc -- inject a MCE on the given CPU [and broadcast to other CPUs with -b option]\r\nmemsave addr size file -- save to disk virtual memory dump starting at 'addr' of size 'size'\r\nmigrate [-d] [-b] [-i] [-r] uri -- migrate to URI (using -d to not wait for completion)\r\n\t\t\t -b for migration without shared storage with full copy of disk\r\n\t\t\t -i for migration without shared storage with incremental copy of disk (base image shared between src and destination)\r\n\t\t\t -r to resume a paused migration\r\nmigrate_cancel -- cancel the current VM migration\r\nmigrate_continue state -- Continue migration from the given paused state\r\nmigrate_incoming uri -- Continue an incoming migration from an -incoming defer\r\nmigrate_pause -- Pause an ongoing migration (postcopy-only)\r\nmigrate_recover uri -- Continue a paused incoming postcopy migration\r\nmigrate_set_capability capability state -- Enable/Disable the usage of a capability for migration\r\nmigrate_set_parameter parameter value -- Set the parameter for migration\r\nmigrate_start_postcopy -- Followup to a migration command to switch the migration to postcopy mode. The postcopy-ram capability must be set on both source and destination before the original migration command .\r\nmouse_button state -- change mouse button state (1=L, 2=M, 4=R)\r\nmouse_move dx dy [dz] -- send mouse move events\r\nmouse_set index -- set which mouse device receives events\r\nnbd_server_add nbd_server_add [-w] device [name] -- export a block device via NBD\r\nnbd_server_remove nbd_server_remove [-f] name -- remove an export previously exposed via NBD\r\nnbd_server_start nbd_server_start [-a] [-w] host:port -- serve block devices on the given host and port\r\nnbd_server_stop nbd_server_stop -- stop serving block devices using the NBD protocol\r\nnetdev_add [user|tap|socket|stream|dgram|vde|bridge|hubport|netmap|vhost-user],id=str[,prop=value][,...] -- add host network device\r\nnetdev_del id -- remove host network device\r\nnmi -- inject an NMI\r\no /fmt addr value -- I/O port write\r\nobject_add [qom-type=]type,id=str[,prop=value][,...] -- create QOM object\r\nobject_del id -- destroy QOM object\r\none-insn-per-tb [on|off] -- run emulation with one guest instruction per translation block\r\npcie_aer_inject_error [-a] [-c] id <error_status> [<tlp header> [<tlp header prefix>]] -- inject pcie aer error\r\n\t\t\t -a for advisory non fatal error\r\n\t\t\t -c for correctable error\r\n\t\t\t<id> = qdev device id\r\n\t\t\t<error_status> = error string or 32bit\r\n\t\t\t<tlp header> = 32bit x 4\r\n\t\t\t<tlp header prefix> = 32bit x 4\r\npmemsave addr size file -- save to disk physical memory dump starting at 'addr' of size 'size'\r\nprint|p /fmt expr -- print expression value (use $reg for CPU register access)\r\nqemu-io [-d] [device] \"[command]\" -- run a qemu-io command on a block device\r\n\t\t\t-d: [device] is a device ID rather than a drive ID or node name\r\nqom-get path property -- print QOM property\r\nqom-list path -- list QOM properties\r\nqom-set [-j] path property value -- set QOM property.\r\n\t\t\t-j: the value is specified in json format.\r\nquit|q -- quit the emulator\r\nreplay_break icount -- set breakpoint at the specified instruction count\r\nreplay_delete_break -- remove replay breakpoint\r\nreplay_seek icount -- replay execution to the specified instruction count\r\nringbuf_read device size -- Read from a ring buffer character device\r\nringbuf_write device data -- Write to a ring buffer character device\r\nsavevm tag -- save a VM snapshot. If no tag is provided, a new snapshot is created\r\nsavevm-end -- Resume VM after snaphot.\r\nsavevm-start [statefile] -- Prepare for snapshot and halt VM. Save VM state to statefile.\r\nscreendump filename [-f format] [device [head]] -- save screen from head 'head' of display device 'device'in specified format 'format' as image 'filename'.Currently only 'png' and 'ppm' formats are supported.\r\nsendkey keys [hold_ms] -- send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)\r\nset_link name on|off -- change the link status of a network adapter\r\nset_password protocol password [-d display] [action-if-connected] -- set spice/vnc password\r\nset_vcpu_dirty_limit dirty_rate [cpu_index] -- set dirty page rate limit, use cpu_index to set limit\r\n\t\t\t\t\t on a specified virtual cpu\r\nsinglestep [on|off] -- deprecated synonym for one-insn-per-tb\r\nsnapshot_blkdev [-n] device [new-image-file] [format] -- initiates a live snapshot\r\n\t\t\tof device. If a new image file is specified, the\r\n\t\t\tnew image file will become the new root image.\r\n\t\t\tIf format is specified, the snapshot file will\r\n\t\t\tbe created in that format.\r\n\t\t\tThe default format is qcow2. The -n flag requests QEMU\r\n\t\t\tto reuse the image found in new-image-file, instead of\r\n\t\t\trecreating it from scratch.\r\nsnapshot_blkdev_internal device name -- take an internal snapshot of device.\r\n\t\t\tThe format of the image used by device must\r\n\t\t\tsupport it, such as qcow2.\r\n\t\t\t\r\nsnapshot_delete_blkdev_internal device name [id] -- delete an internal snapshot of device.\r\n\t\t\tIf id is specified, qemu will try delete\r\n\t\t\tthe snapshot matching both id and name.\r\n\t\t\tThe format of the image used by device must\r\n\t\t\tsupport it, such as qcow2.\r\n\t\t\t\r\nstopcapture capture index -- stop capture\r\nstop|s -- stop emulation\r\nsum addr size -- compute the checksum of a memory region\r\nsync-profile [on|off|reset] -- enable, disable or reset synchronization profiling. With no arguments, prints whether profiling is on or off.\r\nsystem_powerdown -- send system power down event\r\nsystem_reset -- reset the system\r\nsystem_wakeup -- wakeup guest from suspend\r\ntrace-event name on|off [vcpu] -- changes status of a specific trace event (vcpu: vCPU to set, default is all)\r\nwatchdog_action [reset|shutdown|poweroff|pause|debug|none] -- change watchdog action\r\nwavcapture path audiodev [frequency [bits [channels]]] -- capture audio to a wave file (default frequency=44100 bits=16 channels=2)\r\nx /fmt addr -- virtual memory dump starting at 'addr'\r\nx_colo_lost_heartbeat -- Tell COLO that heartbeat is lost,\r\n\t\t\ta failover or takeover is needed.\r\nxen-event-inject port -- inject event channel\r\nxen-event-list -- list event channel state\r\nxp /fmt addr -- physical memory dump starting at 'addr'\r\n"}
这条命令通常用于 Hypervisor 的监视器功能,比如 PVE 就提供了虚拟机的可视化交互的监视器功能
3.3. query 相关命令
QMP 包含大量 query- 开头的命令,这些命令主要用于查询
{ "execute": "query-qmp-schema" }
{ "execute": "query-machines" }
{ "execute": "query-version" }
{ "execute": "query-name" }
{ "execute": "query-status" }
{ "execute": "query-kvm" }
{ "execute": "query-pci" }
3.4. 虚拟机启停
QMP 还可以控制虚拟机暂停、恢复、重启等功能
# 暂停虚拟机
{ "execute": "stop" }
# 关闭虚拟机
{ "execute": "quit" }
# 恢复虚拟机
{ "execute": "cont" }
# 重启虚拟机
{ "execute": "system_reset" }
3.5. 查询内存占用
QMP 可以通过 Balloon 查询内存占用,Balloon(气球)是虚拟化技术中的一个内存管理机制,主要用于动态调整虚拟机(VM)的内存分配。
{"execute": "query-balloon"}
{"return": {"actual": 25769803776, "max_mem": 25769803776, "mem_swapped_in": 0, "minor_page_faults": 92281, "mem_swapped_out": 0, "last_update": 1720592730, "free_mem": 24756789248, "major_page_faults": 456, "total_mem": 25199112192}}
除此之外 QMP 还包含很多实用的命令,篇幅问题就不一一介绍了。
4. 自动化管理
最后附上一个 Python 脚本,方便自动化管理和操作虚拟机
import json
import socket
import select
import os
class QMPClient:
def __init__(self, address):
self.address = address
self.sock = None
def connect(self):
if isinstance(self.address, tuple):
# TCP connection
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect(self.address)
elif isinstance(self.address, str):
# Unix socket connection
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.sock.connect(self.address)
else:
raise ValueError("Invalid address format")
# 读取QMP的初始化消息
data = self.sock.recv(1024).decode()
print("Initial QMP message:", data)
# 发送QMP握手
self.send_command({"execute": "qmp_capabilities"})
def send_command(self, command):
self.sock.sendall(json.dumps(command).encode() + b"\n")
return self.receive_response()
def receive_response(self):
data = b""
while True:
ready, _, _ = select.select([self.sock], [], [], 1)
if ready:
chunk = self.sock.recv(4096)
if not chunk:
break
data += chunk
if b"\n" in chunk:
break
else:
break
return json.loads(data.decode())
def close(self):
if self.sock:
self.sock.close()
# 使用示例
if __name__ == "__main__":
# 选择连接方式
connection_type = input("Enter connection type (tcp/unix): ").lower()
if connection_type == "tcp":
host = input("Enter host (default: localhost): ") or "localhost"
port = int(input("Enter port: "))
qmp = QMPClient((host, port))
elif connection_type == "unix":
socket_path = input("Enter Unix socket path: ")
qmp = QMPClient(socket_path)
else:
print("Invalid connection type")
exit(1)
try:
qmp.connect()
# 查询状态
status = qmp.send_command({"execute": "query-status"})
print("VM Status:", json.dumps(status, indent=2))
# 暂停虚拟机
qmp.send_command({"execute": "stop"})
print("VM paused")
# 恢复虚拟机
qmp.send_command({"execute": "cont"})
print("VM resumed")
# 查询CPU信息
cpu_info = qmp.send_command({"execute": "query-cpus-fast"})
print("CPU Info:", json.dumps(cpu_info, indent=2))
except Exception as e:
print(f"An error occurred: {e}")
finally:
qmp.close()