Run commands on a guest

Matteo Calorio

Renowned Member
Jun 30, 2017
41
0
71
53
Hi everyone, what's the best way to run commands from an host on a guest (Linux) that can also be on a different host?

Something like:
  • qm guest exec 100 --node node01 -- apt update -y, or:
  • pvesh set /nodes/node01/qemu/100/agent/exec -command "apt update"?
M.
 
Hi,

qm can control only vms on the node, it is running on.
I would use pvesh and also use it to look up on which node the vm is running on. Something like this (requires jq installed):
Bash:
TARGET=100
NODE=$(pvesh get /cluster/resources --output-format json | \
        jq -r --argjson TARGETVM $TARGET '.[] | select(.vmid==$TARGETVM) | .node')
pvesh create /nodes/${NODE}/qemu/${TARGET}/agent/exec -command "apt" -command "update"

Aside of that: There are better ways for infrastructure tasks like using Ansible, Foreman or for simplicity Cockpit.
 
Last edited:
Bash:
pvesh create /nodes/${NODE}/qemu/${TARGET}/agent/exec -command "apt" -command "update"

Thanks fba, yes, I'm currently using pvesh in scripts like this:

Bash:
for vmid in 808 811 813 844; do
  nodename=$(pvesh get /cluster/resources --type vm --output-format json | jq -r " .[] | select(.vmid == $vmid) | .node")
  vmname=$(pvesh get /cluster/resources --type vm --output-format json | jq -r " .[] | select(.vmid == $vmid) | .name")
  vmstatus=$(pvesh get /nodes/$nodename/qemu/$vmid/status/current --output-format text --noborder | grep status | tail -1 | awk '{print $2}')
  echo "Virtual Machine $vmid ($vmname) on node $nodename: $vmstatus"
  if [ $vmstatus == "running" ]
  then
    pvesh create /nodes/$nodename/qemu/$vmid/status/shutdown
  fi
done

I tried running the command you suggested—I've already done it—but I still get this error:

Bash:
Wide character in die at /usr/share/perl5/PVE/RESTHandler.pm line 918.
proxy handler failed: Agent error: Guest agent command failed, error was 'Failed to execute child process “ARRAY(0x55f3d5e9d850)” (No such file or directory)'

I will also look into Ansible, which I already use for other things.

Bye and have a good day!
 
Bash:
Wide character in die at /usr/share/perl5/PVE/RESTHandler.pm line 918.
proxy handler failed: Agent error: Guest agent command failed, error was 'Failed to execute child process “ARRAY(0x55f3d5e9d850)” (No such file or directory)'
Which command triggers this error? The ARRAY-Thingy looks like an array reference in perl, but obviously a string is expected there.
 
Which command triggers this error? The ARRAY-Thingy looks like an array reference in perl, but obviously a string is expected there.
Bash:
pvesh create /nodes/node01/qemu/906/agent/exec -command "apt" -command "update"
 
Unusual error for this command. Looking into the mentioned file on my PVE 8.4.8 (libpve-common-perl 8.3.3), there is nothing on this line and actually even the function it belongs to seems to have nothing to do with the guest-agent.
Would you like to share output ofpveversion -v?
 
Sure, thanks!

Bash:
proxmox-ve: 8.0.2 (running kernel: 6.2.16-18-pve)
pve-manager: 8.0.4 (running version: 8.0.4/d258a813cfa6b390)
proxmox-kernel-helper: 8.0.3
pve-kernel-5.19: 7.2-15
proxmox-kernel-6.2.16-18-pve: 6.2.16-18
proxmox-kernel-6.2: 6.2.16-18
pve-kernel-5.19.17-2-pve: 5.19.17-2
ceph-fuse: 16.2.11+ds-2
corosync: 3.1.7-pve3
criu: 3.17.1-2
glusterfs-client: 10.3-5
ifupdown2: 3.2.0-1+pmx4
ksm-control-daemon: 1.4-1
libjs-extjs: 7.0.0-4
libknet1: 1.26-pve1
libproxmox-acme-perl: 1.4.6
libproxmox-backup-qemu0: 1.4.0
libproxmox-rs-perl: 0.3.0
libpve-access-control: 8.0.4
libpve-apiclient-perl: 3.3.1
libpve-common-perl: 8.0.7
libpve-guest-common-perl: 5.0.3
libpve-http-server-perl: 5.0.4
libpve-rs-perl: 0.8.4
libpve-storage-perl: 8.0.2
libspice-server1: 0.15.1-1
lvm2: 2.03.16-2
lxc-pve: 5.0.2-4
lxcfs: 5.0.3-pve3
novnc-pve: 1.4.0-2
proxmox-backup-client: 3.0.3-1
proxmox-backup-file-restore: 3.0.3-1
proxmox-kernel-helper: 8.0.3
proxmox-mail-forward: 0.2.0
proxmox-mini-journalreader: 1.4.0
proxmox-offline-mirror-helper: 0.6.2
proxmox-widget-toolkit: 4.0.9
pve-cluster: 8.0.2
pve-container: 5.0.4
pve-docs: 8.0.5
pve-edk2-firmware: 3.20230228-4
pve-firewall: 5.0.3
pve-firmware: 3.8-3
pve-ha-manager: 4.0.2
pve-i18n: 3.0.7
pve-qemu-kvm: 8.0.2-6
pve-xtermjs: 4.16.0-3
qemu-server: 8.0.7
smartmontools: 7.3-pve1
spiceterm: 3.3.0
swtpm: 0.8.0+pve1
vncterm: 1.8.0
zfsutils-linux: 2.1.13-pve1
 
Just to make sure, that the guest-agent is working as expected:
Run on the node with the vm
socat - /var/run/qemu-server/906.qga
then paste the following: {"execute": "guest-exec", "arguments": {"path": "/bin/bash", "arg": ["-c", "apt update"], "capture-output": true}}
this should output something like {"return": {"pid": 3558}}. This means that the qemu-guest-agent might work as expected. You can go further and paste (with pid from last output) {"execute": "guest-exec-status", "arguments": {"pid": 3558}}, which will return base64 encoded "err-data" and "out-data" of the executed command.

Otherwise: have you considered updating the PVE to a more current version?
 
Here it is:

Bash:
# socat - /var/run/qemu-server/3006.qga

{"execute": "guest-exec", "arguments": {"path": "/bin/bash", "arg": ["-c", "apt update"], "capture-output": true}}
{"return": {"pid": 3566564}}
{"execute": "guest-exec-status", "arguments": {"pid": 3566564}}   
{"return": {"exitcode": 100, "err-data": "CldBUk5JTkc6IGFwdCBkb2VzIG5vdCBoYXZlIGEgc3RhYmxlIENMSSBpbnRlcmZhY2UuIFVzZSB3aXRoIGNhdXRpb24gaW4gc2NyaXB0cy4KCkU6IFRoZSByZXBvc2l0b3J5ICdodHRwczovL2Z0cC5kZWJpYW4ub3JnL2RlYmlhbiBidWxsc2V5ZS1iYWNrcG
9ydHMgUmVsZWFzZScgbm8gbG9uZ2VyIGhhcyBhIFJlbGVhc2UgZmlsZS4K", "out-data": "SGl0OjEgaHR0cHM6Ly9kZWIuZGViaWFuLm9yZy9kZWJpYW4gYnVsbHNleWUgSW5SZWxlYXNlCkhpdDoyIGh0dHBzOi8vZGViLmRlYmlhbi5vcmcvZGViaWFuIGJ1bGxzZXllLXVwZGF0ZXMgSW5SZWxlYXNlCkhpdD
ozIGh0dHBzOi8vc2VjdXJpdHkuZGViaWFuLm9yZy9kZWJpYW4tc2VjdXJpdHkgYnVsbHNleWUtc2VjdXJpdHkgSW5SZWxlYXNlCklnbjo0IGh0dHBzOi8vZnRwLmRlYmlhbi5vcmcvZGViaWFuIGJ1bGxzZXllLWJhY2twb3J0cyBJblJlbGVhc2UKRXJyOjUgaHR0cHM6Ly9mdHAuZGViaWFuLm9yZy9kZWJpYW4gYn
VsbHNleWUtYmFja3BvcnRzIFJlbGVhc2UKICA0MDQgIE5vdCBGb3VuZCBbSVA6IDE5Mi4xNjguOC4zNyAzMTQyXQpSZWFkaW5nIHBhY2thZ2UgbGlzdHMuLi4K", "exited": true}}
^C
# echo "CldBUk5JTkc6IGFwdCBkb2VzIG5vdCBoYXZlIGEgc3RhYmxlIENMSSBpbnRlcmZhY2UuIFVzZSB3aXRoIGNhdXRpb24gaW4gc2NyaXB0cy4KCkU6IFRoZSByZXBvc2l0b3J5ICdodHRwczovL2Z0cC5kZWJpYW4ub3JnL2RlYmlhbiBidWxsc2V5ZS1iYWNrcG9ydHMgUmVsZWFzZ
Scgbm8gbG9uZ2VyIGhhcyBhIFJlbGVhc2UgZmlsZS4K" | base64 -d

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

E: The repository 'https://ftp.debian.org/debian bullseye-backports Release' no longer has a Release file.
# echo "SGl0OjEgaHR0cHM6Ly9kZWIuZGViaWFuLm9yZy9kZWJpYW4gYnVsbHNleWUgSW5SZWxlYXNlCkhpdDoyIGh0dHBzOi8vZGViLmRlYmlhbi5vcmcvZGViaWFuIGJ1bGxzZXllLXVwZGF0ZXMgSW5SZWxlYXNlCkhpdDozIGh0dHBzOi8vc2VjdXJpdHkuZGViaWFuLm9yZy9kZWJpYW4
tc2VjdXJpdHkgYnVsbHNleWUtc2VjdXJpdHkgSW5SZWxlYXNlCklnbjo0IGh0dHBzOi8vZnRw" | base64 -d
Hit:1 https://deb.debian.org/debian bullseye InRelease
Hit:2 https://deb.debian.org/debian bullseye-updates InRelease
Hit:3 https://security.debian.org/debian-security bullseye-security InRelease

Thanks, it seems it works... and yes, we should upgrade our systems next autumn/winter.

Have a nice day!
 
This shows, that the error you get is not related to the guest-agent itself.
But aside of doing a reinstall of the libpve-common-perl (apt reinstall libpve-common-perl), I have no further idea for troubleshooting as I dont have a - no offence meant - old version as this around anymore.
With a suitable subscription you might open a ticket at Proxmox.
 
You can also hack together something with python to do that if you're not into bash or perl:

Python:
#!/usr/bin/env python3

import requests
import time

headers = {
    "Authorization": "PVEAPIToken=user@realm!testtoken=<redact>",
    "Content-Type": "application/json"
}

node = "proxmox"
vmid = "104"
base_url = f"https://{node}:8006/api2/json"
post_url = f"{base_url}/nodes/{node}/qemu/{vmid}/agent/exec"
post_data = {"node": node, "vmid": vmid, "command": "dmidecode"}

print(post_url)
response = requests.post(post_url, json=post_data, headers=headers, verify=False)
response.raise_for_status()

pid = response.json().get("pid")

print(f"Gets executed in PID {pid}")
time.sleep(2)

get_url = f"{base_url}/nodes/{node}/qemu/{vmid}/agent/exec-status?pid={pid}"
get_response = requests.get(get_url, verify=False)
get_response.raise_for_status()

print(get_response.json())
 
This shows, that the error you get is not related to the guest-agent itself.
But aside of doing a reinstall of the libpve-common-perl (apt reinstall libpve-common-perl), I have no further idea for troubleshooting as I dont have a - no offence meant - old version as this around anymore.
With a suitable subscription you might open a ticket at Proxmox.
Okay, thanks so much for your time. I think we'll move to the Ansible solution and check to see if the issue persists with a newer version of Proxmox as soon as we upgrade or open a ticket with our Proxmox subscription. Have a great day!
 
You can also hack together something with python to do that if you're not into bash or perl:

Python:
#!/usr/bin/env python3

import requests
import time

headers = {
    "Authorization": "PVEAPIToken=user@realm!testtoken=<redact>",
    "Content-Type": "application/json"
}

node = "proxmox"
vmid = "104"
base_url = f"https://{node}:8006/api2/json"
post_url = f"{base_url}/nodes/{node}/qemu/{vmid}/agent/exec"
post_data = {"node": node, "vmid": vmid, "command": "dmidecode"}

print(post_url)
response = requests.post(post_url, json=post_data, headers=headers, verify=False)
response.raise_for_status()

pid = response.json().get("pid")

print(f"Gets executed in PID {pid}")
time.sleep(2)

get_url = f"{base_url}/nodes/{node}/qemu/{vmid}/agent/exec-status?pid={pid}"
get_response = requests.get(get_url, verify=False)
get_response.raise_for_status()

print(get_response.json())
Thanks, I tried creating a token and filling in all the values in the script, but I get this error:
Bash:
https://proxmox:8006/api2/json/nodes/proxmox/qemu/306/agent/exec
Gets executed in PID None
Traceback (most recent call last):
  File "/home/m/./p.py", line 28, in <module>
    get_response.raise_for_status()
  File "/usr/lib/python3/dist-packages/requests/models.py", line 1021, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 401 Client Error: No ticket for url: https://proxmox:8006/api2/json/nodes/proxmox/qemu/306/agent/exec-status?pid=None