]>
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);
10 use MIME
::Base64
qw(encode_base64 decode_base64);
13 use base
qw(PVE::RESTHandler);
15 # max size for file-read over the api
16 my $MAX_READ_SIZE = 16 * 1024 * 1024; # 16 MiB
19 # will generate one api endpoint per command
20 # needs a 'method' property and optionally a 'perms' property (default VM.Monitor)
21 my $guest_agent_commands = {
31 'fsfreeze-status' => {
34 'fsfreeze-freeze' => {
43 'network-get-interfaces' => {
52 'get-memory-blocks' => {
55 'get-memory-block-info' => {
70 # added since qemu 2.9
85 __PACKAGE__-
>register_method({
90 description
=> "Qemu Agent command index.",
95 additionalProperties
=> 1,
97 node
=> get_standard_option
('pve-node'),
98 vmid
=> get_standard_option
('pve-vmid', {
99 completion
=> \
&PVE
::QemuServer
::complete_vmid_running
}),
108 links
=> [ { rel
=> 'child', href
=> '{name}' } ],
109 description
=> "Returns the list of Qemu Agent commands",
116 my $cmds = [keys %$guest_agent_commands];
124 for my $cmd ( sort @$cmds) {
125 push @$result, { name
=> $cmd };
131 sub register_command
{
132 my ($class, $command, $method, $perm) = @_;
134 die "no method given\n" if !$method;
135 die "no command given\n" if !defined($command);
139 if (ref($perm) eq 'HASH') {
142 $perm //= 'VM.Monitor';
143 $permission = { check
=> [ 'perm', '/vms/{vmid}', [ $perm ]]};
147 additionalProperties
=> 0,
149 node
=> get_standard_option
('pve-node'),
150 vmid
=> get_standard_option
('pve-vmid', {
151 completion
=> \
&PVE
::QemuServer
::complete_vmid_running
}),
154 description
=> "The QGA command.",
155 enum
=> [ sort keys %$guest_agent_commands ],
160 my $description = "Execute Qemu Guest Agent commands.";
163 if ($command ne '') {
164 $description = "Execute $command.";
166 delete $parameters->{properties
}->{command
};
169 __PACKAGE__-
>register_method({
175 description
=> $description,
176 permissions
=> $permission,
177 parameters
=> $parameters,
180 description
=> "Returns an object with a single `result` property.",
185 my $vmid = $param->{vmid
};
187 my $conf = PVE
::QemuConfig-
>load_config ($vmid); # check if VM exists
189 agent_available
($vmid, $conf);
191 my $cmd = $param->{command
} // $command;
192 my $res = PVE
::QemuServer
::vm_mon_cmd
($vmid, "guest-$cmd");
194 return { result
=> $res };
198 # old {vmid}/agent POST endpoint, here for compatibility
199 __PACKAGE__-
>register_command('', 'POST');
201 for my $cmd (sort keys %$guest_agent_commands) {
202 my $props = $guest_agent_commands->{$cmd};
203 __PACKAGE__-
>register_command($cmd, $props->{method}, $props->{perms
});
206 # commands with parameters are complicated and we want to register them manually
207 __PACKAGE__-
>register_method({
208 name
=> 'set-user-password',
209 path
=> 'set-user-password',
213 description
=> "Sets the password for the given user to the given password",
214 permissions
=> { check
=> [ 'perm', '/vms/{vmid}', [ 'VM.Monitor' ]]},
216 additionalProperties
=> 0,
218 node
=> get_standard_option
('pve-node'),
219 vmid
=> get_standard_option
('pve-vmid', {
220 completion
=> \
&PVE
::QemuServer
::complete_vmid_running
}),
223 description
=> 'The user to set the password for.'
227 description
=> 'The new password.',
233 description
=> 'set to 1 if the password has already been passed through crypt()',
241 description
=> "Returns an object with a single `result` property.",
246 my $vmid = $param->{vmid
};
248 my $crypted = $param->{crypted
} // 0;
250 username
=> $param->{username
},
251 password
=> encode_base64
($param->{password
}),
252 crypted
=> $crypted ? JSON
::true
: JSON
::false
,
254 my $res = agent_cmd
($vmid, "set-user-password", %$args, 'cannot set user password');
256 return { result
=> $res };
259 __PACKAGE__-
>register_method({
265 description
=> "Executes the given command in the vm via the guest-agent and returns an object with the pid.",
266 permissions
=> { check
=> [ 'perm', '/vms/{vmid}', [ 'VM.Monitor' ]]},
268 additionalProperties
=> 0,
270 node
=> get_standard_option
('pve-node'),
271 vmid
=> get_standard_option
('pve-vmid', {
272 completion
=> \
&PVE
::QemuServer
::complete_vmid_running
}),
275 format
=> 'string-alist',
276 description
=> 'The command as a list of program + arguments',
285 description
=> "The PID of the process started by the guest-agent.",
292 my $vmid = $param->{vmid
};
293 my $cmd = [PVE
::Tools
::split_list
($param->{command
})];
295 my $res = PVE
::QemuServer
::Agent
::qemu_exec
($vmid, $cmd);
299 __PACKAGE__-
>register_method({
300 name
=> 'exec-status',
301 path
=> 'exec-status',
305 description
=> "Gets the status of the given pid started by the guest-agent",
306 permissions
=> { check
=> [ 'perm', '/vms/{vmid}', [ 'VM.Monitor' ]]},
308 additionalProperties
=> 0,
310 node
=> get_standard_option
('pve-node'),
311 vmid
=> get_standard_option
('pve-vmid', {
312 completion
=> \
&PVE
::QemuServer
::complete_vmid_running
}),
315 description
=> 'The PID to query'
324 description
=> 'Tells if the given command has exited yet.',
329 description
=> 'process exit code if it was normally terminated.',
334 description
=> 'signal number or exception code if the process was abnormally terminated.',
339 description
=> 'stdout of the process',
344 description
=> 'stderr of the process',
349 description
=> 'true if stdout was not fully captured',
354 description
=> 'true if stderr was not fully captured',
361 my $vmid = $param->{vmid
};
362 my $pid = int($param->{pid
});
364 my $res = PVE
::QemuServer
::Agent
::qemu_exec_status
($vmid, $pid);
369 __PACKAGE__-
>register_method({
375 description
=> "Reads the given file via guest agent. Is limited to $MAX_READ_SIZE bytes.",
376 permissions
=> { check
=> [ 'perm', '/vms/{vmid}', [ 'VM.Monitor' ]]},
378 additionalProperties
=> 0,
380 node
=> get_standard_option
('pve-node'),
381 vmid
=> get_standard_option
('pve-vmid', {
382 completion
=> \
&PVE
::QemuServer
::complete_vmid_running
}),
385 description
=> 'The path to the file'
391 description
=> "Returns an object with a `content` property.",
395 description
=> "The content of the file, maximum $MAX_READ_SIZE",
400 description
=> "If set to 1, the output is truncated and not complete"
407 my $vmid = $param->{vmid
};
409 my $qgafh = agent_cmd
($vmid, "file-open", { path
=> $param->{file
} }, "can't open file");
411 my $bytes_left = $MAX_READ_SIZE;
413 my $read_size = 1024*1024;
416 while ($bytes_left > 0 && !$eof) {
417 my $read = PVE
::QemuServer
::vm_mon_cmd
($vmid, "guest-file-read", handle
=> $qgafh, count
=> int($read_size));
418 check_agent_error
($read, "can't read from file");
420 $content .= decode_base64
($read->{'buf-b64'});
421 $bytes_left -= $read->{count
};
422 $eof = $read->{eof} // 0;
425 my $res = PVE
::QemuServer
::vm_mon_cmd
($vmid, "guest-file-close", handle
=> $qgafh);
426 check_agent_error
($res, "can't close file", 1);
430 'bytes-read' => ($MAX_READ_SIZE-$bytes_left),
434 warn "agent file-read: reached maximum read size: $MAX_READ_SIZE bytes. output might be truncated.\n";
435 $result->{truncated
} = 1;