]>
git.proxmox.com Git - qemu-server.git/blob - PVE/API2/Qemu/Agent.pm
1 package PVE
::API2
::Qemu
::Agent
;
7 use PVE
::JSONSchema
qw(get_standard_option);
9 use PVE
::QemuServer
::Agent
qw(agent_available agent_cmd check_agent_error);
10 use PVE
::QemuServer
::Monitor
qw(mon_cmd);
11 use MIME
::Base64
qw(encode_base64 decode_base64);
14 use base
qw(PVE::RESTHandler);
16 # max size for file-read over the api
17 my $MAX_READ_SIZE = 16 * 1024 * 1024; # 16 MiB
20 # will generate one api endpoint per command
21 # needs a 'method' property and optionally a 'perms' property (default VM.Monitor)
22 my $guest_agent_commands = {
32 'fsfreeze-status' => {
35 'fsfreeze-freeze' => {
44 'network-get-interfaces' => {
53 'get-memory-blocks' => {
56 'get-memory-block-info' => {
71 # added since qemu 2.9
86 __PACKAGE__-
>register_method({
91 description
=> "QEMU Guest Agent command index.",
96 additionalProperties
=> 1,
98 node
=> get_standard_option
('pve-node'),
99 vmid
=> get_standard_option
('pve-vmid', {
100 completion
=> \
&PVE
::QemuServer
::complete_vmid_running
}),
109 links
=> [ { rel
=> 'child', href
=> '{name}' } ],
110 description
=> "Returns the list of QEMU Guest Agent commands",
117 my $cmds = [keys %$guest_agent_commands];
126 for my $cmd ( sort @$cmds) {
127 push @$result, { name
=> $cmd };
133 sub register_command
{
134 my ($class, $command, $method, $perm) = @_;
136 die "no method given\n" if !$method;
137 die "no command given\n" if !defined($command);
141 if (ref($perm) eq 'HASH') {
144 $perm //= 'VM.Monitor';
145 $permission = { check
=> [ 'perm', '/vms/{vmid}', [ $perm ]]};
149 additionalProperties
=> 0,
151 node
=> get_standard_option
('pve-node'),
152 vmid
=> get_standard_option
('pve-vmid', {
153 completion
=> \
&PVE
::QemuServer
::complete_vmid_running
,
157 description
=> "The QGA command.",
158 enum
=> [ sort keys %$guest_agent_commands ],
163 my $description = "Execute QEMU Guest Agent commands.";
166 if ($command ne '') {
167 $description = "Execute $command.";
169 delete $parameters->{properties
}->{command
};
172 __PACKAGE__-
>register_method({
178 description
=> $description,
179 permissions
=> $permission,
180 parameters
=> $parameters,
183 description
=> "Returns an object with a single `result` property.",
188 my $vmid = $param->{vmid
};
190 my $conf = PVE
::QemuConfig-
>load_config ($vmid); # check if VM exists
192 agent_available
($vmid, $conf);
194 my $cmd = $param->{command
} // $command;
195 my $res = mon_cmd
($vmid, "guest-$cmd");
197 return { result
=> $res };
201 # old {vmid}/agent POST endpoint, here for compatibility
202 __PACKAGE__-
>register_command('', 'POST');
204 for my $cmd (sort keys %$guest_agent_commands) {
205 my $props = $guest_agent_commands->{$cmd};
206 __PACKAGE__-
>register_command($cmd, $props->{method}, $props->{perms
});
209 # commands with parameters are complicated and we want to register them manually
210 __PACKAGE__-
>register_method({
211 name
=> 'set-user-password',
212 path
=> 'set-user-password',
216 description
=> "Sets the password for the given user to the given password",
217 permissions
=> { check
=> [ 'perm', '/vms/{vmid}', [ 'VM.Monitor' ]]},
219 additionalProperties
=> 0,
221 node
=> get_standard_option
('pve-node'),
222 vmid
=> get_standard_option
('pve-vmid', {
223 completion
=> \
&PVE
::QemuServer
::complete_vmid_running
}),
226 description
=> 'The user to set the password for.'
230 description
=> 'The new password.',
236 description
=> 'set to 1 if the password has already been passed through crypt()',
244 description
=> "Returns an object with a single `result` property.",
249 my $vmid = $param->{vmid
};
251 my $crypted = $param->{crypted
} // 0;
253 username
=> $param->{username
},
254 password
=> encode_base64
($param->{password
}),
255 crypted
=> $crypted ? JSON
::true
: JSON
::false
,
257 my $res = agent_cmd
($vmid, "set-user-password", $args, 'cannot set user password');
259 return { result
=> $res };
262 __PACKAGE__-
>register_method({
268 description
=> "Executes the given command in the vm via the guest-agent and returns an object with the pid.",
269 permissions
=> { check
=> [ 'perm', '/vms/{vmid}', [ 'VM.Monitor' ]]},
271 additionalProperties
=> 0,
273 node
=> get_standard_option
('pve-node'),
274 vmid
=> get_standard_option
('pve-vmid', {
275 completion
=> \
&PVE
::QemuServer
::complete_vmid_running
}),
278 description
=> 'The command as a list of program + arguments.',
281 description
=> 'A single part of the program + arguments.',
286 maxLength
=> 64 * 1024,
287 description
=> "Data to pass as 'input-data' to the guest. Usually treated as STDIN to 'command'.",
297 description
=> "The PID of the process started by the guest-agent.",
304 my $vmid = $param->{vmid
};
305 my $cmd = $param->{command
};
307 my $res = PVE
::QemuServer
::Agent
::qemu_exec
($vmid, $param->{'input-data'}, $cmd);
311 __PACKAGE__-
>register_method({
312 name
=> 'exec-status',
313 path
=> 'exec-status',
317 description
=> "Gets the status of the given pid started by the guest-agent",
318 permissions
=> { check
=> [ 'perm', '/vms/{vmid}', [ 'VM.Monitor' ]]},
320 additionalProperties
=> 0,
322 node
=> get_standard_option
('pve-node'),
323 vmid
=> get_standard_option
('pve-vmid', {
324 completion
=> \
&PVE
::QemuServer
::complete_vmid_running
}),
327 description
=> 'The PID to query'
336 description
=> 'Tells if the given command has exited yet.',
341 description
=> 'process exit code if it was normally terminated.',
346 description
=> 'signal number or exception code if the process was abnormally terminated.',
351 description
=> 'stdout of the process',
356 description
=> 'stderr of the process',
361 description
=> 'true if stdout was not fully captured',
366 description
=> 'true if stderr was not fully captured',
373 my $vmid = $param->{vmid
};
374 my $pid = int($param->{pid
});
376 my $res = PVE
::QemuServer
::Agent
::qemu_exec_status
($vmid, $pid);
381 __PACKAGE__-
>register_method({
387 description
=> "Reads the given file via guest agent. Is limited to $MAX_READ_SIZE bytes.",
388 permissions
=> { check
=> [ 'perm', '/vms/{vmid}', [ 'VM.Monitor' ]]},
390 additionalProperties
=> 0,
392 node
=> get_standard_option
('pve-node'),
393 vmid
=> get_standard_option
('pve-vmid', {
394 completion
=> \
&PVE
::QemuServer
::complete_vmid_running
}),
397 description
=> 'The path to the file'
403 description
=> "Returns an object with a `content` property.",
407 description
=> "The content of the file, maximum $MAX_READ_SIZE",
412 description
=> "If set to 1, the output is truncated and not complete"
419 my $vmid = $param->{vmid
};
421 my $qgafh = agent_cmd
($vmid, "file-open", { path
=> $param->{file
} }, "can't open file");
423 my $bytes_left = $MAX_READ_SIZE;
425 my $read_size = 1024*1024;
428 while ($bytes_left > 0 && !$eof) {
429 my $read = mon_cmd
($vmid, "guest-file-read", handle
=> $qgafh, count
=> int($read_size));
430 check_agent_error
($read, "can't read from file");
432 $content .= decode_base64
($read->{'buf-b64'});
433 $bytes_left -= $read->{count
};
434 $eof = $read->{eof} // 0;
437 my $res = mon_cmd
($vmid, "guest-file-close", handle
=> $qgafh);
438 check_agent_error
($res, "can't close file", 1);
442 'bytes-read' => ($MAX_READ_SIZE-$bytes_left),
446 warn "agent file-read: reached maximum read size: $MAX_READ_SIZE bytes. output might be truncated.\n";
447 $result->{truncated
} = 1;
453 __PACKAGE__-
>register_method({
454 name
=> 'file-write',
455 path
=> 'file-write',
459 description
=> "Writes the given file via guest agent.",
460 permissions
=> { check
=> [ 'perm', '/vms/{vmid}', [ 'VM.Monitor' ]]},
462 additionalProperties
=> 0,
464 node
=> get_standard_option
('pve-node'),
465 vmid
=> get_standard_option
('pve-vmid', {
466 completion
=> \
&PVE
::QemuServer
::complete_vmid_running
}),
469 description
=> 'The path to the file.'
473 maxLength
=> 60 * 1024, # 60k, smaller than our 64k POST limit
474 description
=> "The content to write into the file."
478 description
=> "If set, the content will be encoded as base64 (required by QEMU)."
479 ."Otherwise the content needs to be encoded beforehand - defaults to true.",
485 returns
=> { type
=> 'null' },
489 my $vmid = $param->{vmid
};
491 my $buf = ($param->{encode
} // 1) ? encode_base64
($param->{content
}) : $param->{content
};
493 my $qgafh = agent_cmd
($vmid, "file-open", { path
=> $param->{file
}, mode
=> 'wb' }, "can't open file");
494 my $write = agent_cmd
($vmid, "file-write", { handle
=> $qgafh, 'buf-b64' => $buf }, "can't write to file");
495 my $res = agent_cmd
($vmid, "file-close", { handle
=> $qgafh }, "can't close file");