]>
git.proxmox.com Git - pve-container.git/blob - src/PVE/API2/LXC.pm
1 package PVE
::API2
::LXC
;
7 use PVE
::Tools
qw(extract_param run_command);
8 use PVE
::Exception
qw(raise raise_param_exc);
10 use PVE
::Cluster
qw(cfs_read_file);
11 use PVE
::AccessControl
;
14 use PVE
::RPCEnvironment
;
16 use PVE
::JSONSchema
qw(get_standard_option);
17 use base
qw(PVE::RESTHandler);
19 use Data
::Dumper
; # fixme: remove
21 my $get_container_storage = sub {
22 my ($stcfg, $vmid, $lxc_conf) = @_;
24 my $path = $lxc_conf->{'lxc.rootfs'};
25 my ($vtype, $volid) = PVE
::Storage
::path_to_volume_id
($stcfg, $path);
26 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1) if $volid;
27 return wantarray ?
($sid, $volname, $path) : $sid;
30 my $check_ct_modify_config_perm = sub {
31 my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
33 return 1 if $authuser ne 'root@pam';
35 foreach my $opt (@$key_list) {
37 if ($opt eq 'cpus' || $opt eq 'cpuunits' || $opt eq 'cpulimit') {
38 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
39 } elsif ($opt eq 'disk') {
40 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
41 } elsif ($opt eq 'memory' || $opt eq 'swap') {
42 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
43 } elsif ($opt =~ m/^net\d+$/ || $opt eq 'nameserver' ||
44 $opt eq 'searchdomain' || $opt eq 'hostname') {
45 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
47 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
55 __PACKAGE__-
>register_method({
59 description
=> "LXC container index (per node).",
61 description
=> "Only list CTs where you have VM.Audit permissons on /vms/<vmid>.",
65 protected
=> 1, # /proc files are only readable by root
67 additionalProperties
=> 0,
69 node
=> get_standard_option
('pve-node'),
78 links
=> [ { rel
=> 'child', href
=> "{vmid}" } ],
83 my $rpcenv = PVE
::RPCEnvironment
::get
();
84 my $authuser = $rpcenv->get_user();
86 my $vmstatus = PVE
::LXC
::vmstatus
();
89 foreach my $vmid (keys %$vmstatus) {
90 next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
92 my $data = $vmstatus->{$vmid};
93 $data->{vmid
} = $vmid;
101 __PACKAGE__-
>register_method({
105 description
=> "Create or restore a container.",
107 user
=> 'all', # check inside
108 description
=> "You need 'VM.Allocate' permissions on /vms/{vmid} or on the VM pool /pool/{pool}. " .
109 "For restore, it is enough if the user has 'VM.Backup' permission and the VM already exists. " .
110 "You also need 'Datastore.AllocateSpace' permissions on the storage.",
115 additionalProperties
=> 0,
116 properties
=> PVE
::LXC
::json_config_properties
({
117 node
=> get_standard_option
('pve-node'),
118 vmid
=> get_standard_option
('pve-vmid'),
120 description
=> "The OS template or backup file.",
127 description
=> "Sets root password inside container.",
129 storage
=> get_standard_option
('pve-storage-id', {
130 description
=> "Target storage.",
137 description
=> "Allow to overwrite existing container.",
142 description
=> "Mark this as restore task.",
146 type
=> 'string', format
=> 'pve-poolid',
147 description
=> "Add the VM to the specified pool.",
157 my $rpcenv = PVE
::RPCEnvironment
::get
();
159 my $authuser = $rpcenv->get_user();
161 my $node = extract_param
($param, 'node');
163 my $vmid = extract_param
($param, 'vmid');
165 my $basecfg_fn = PVE
::LXC
::config_file
($vmid);
167 my $same_container_exists = -f
$basecfg_fn;
169 my $restore = extract_param
($param, 'restore');
171 my $force = extract_param
($param, 'force');
173 if (!($same_container_exists && $restore && $force)) {
174 PVE
::Cluster
::check_vmid_unused
($vmid);
177 my $password = extract_param
($param, 'password');
179 my $storage = extract_param
($param, 'storage') || 'local';
181 my $pool = extract_param
($param, 'pool');
183 my $storage_cfg = cfs_read_file
("storage.cfg");
185 my $scfg = PVE
::Storage
::storage_check_node
($storage_cfg, $storage, $node);
187 raise_param_exc
({ storage
=> "storage '$storage' does not support container root directories"})
188 if !$scfg->{content
}->{rootdir
};
190 my $private = PVE
::Storage
::get_private_dir
($storage_cfg, $storage, $vmid);
192 if (defined($pool)) {
193 $rpcenv->check_pool_exist($pool);
194 $rpcenv->check_perm_modify($authuser, "/pool/$pool");
197 if ($rpcenv->check($authuser, "/vms/$vmid", ['VM.Allocate'], 1)) {
199 } elsif ($pool && $rpcenv->check($authuser, "/pool/$pool", ['VM.Allocate'], 1)) {
201 } elsif ($restore && $force && $same_container_exists &&
202 $rpcenv->check($authuser, "/vms/$vmid", ['VM.Backup'], 1)) {
203 # OK: user has VM.Backup permissions, and want to restore an existing VM
208 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
210 PVE
::Storage
::activate_storage
($storage_cfg, $storage);
212 my $ostemplate = extract_param
($param, 'ostemplate');
216 if ($ostemplate eq '-') {
217 die "archive pipe not implemented\n"
220 $rpcenv->check_volume_access($authuser, $storage_cfg, $vmid, $ostemplate);
221 $archive = PVE
::Storage
::abs_filesystem_path
($storage_cfg, $ostemplate);
224 my $memory = $param->{memory
} || 512;
225 my $hostname = $param->{hostname
} || "T$vmid";
228 $conf->{'lxc.utsname'} = $param->{hostname
} || "CT$vmid";
229 $conf->{'lxc.cgroup.memory.limit_in_bytes'} = "${memory}M";
232 my $temp_conf_fn = PVE
::LXC
::write_temp_config
($vmid, $conf);
234 my $cmd = ['lxc-create', '-f', $temp_conf_fn, '-t', 'pve', '-n', $vmid,
235 '--', '--archive', $archive];
237 eval { PVE
::Tools
::run_command
($cmd); };
240 unlink $temp_conf_fn;
245 my $realcmd = sub { PVE
::LXC
::lock_container
($vmid, 1, $code); };
247 return $rpcenv->fork_worker($param->{restore
} ?
'vzrestore' : 'vzcreate',
248 $vmid, $authuser, $realcmd);
252 my $vm_config_perm_list = [
260 __PACKAGE__-
>register_method({
262 path
=> '{vmid}/config',
266 description
=> "Set container options.",
268 check
=> ['perm', '/vms/{vmid}', $vm_config_perm_list, any
=> 1],
271 additionalProperties
=> 0,
272 properties
=> PVE
::LXC
::json_config_properties
(
274 node
=> get_standard_option
('pve-node'),
275 vmid
=> get_standard_option
('pve-vmid'),
278 description
=> 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
284 returns
=> { type
=> 'null'},
288 my $rpcenv = PVE
::RPCEnvironment
::get
();
290 my $authuser = $rpcenv->get_user();
292 my $node = extract_param
($param, 'node');
294 my $vmid = extract_param
($param, 'vmid');
296 my $digest = extract_param
($param, 'digest');
298 die "no options specified\n" if !scalar(keys %$param);
300 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
304 my $conf = PVE
::LXC
::load_config
($vmid);
306 PVE
::Tools
::assert_if_modified
($digest, $conf->{digest
});
311 PVE
::LXC
::lock_container
($vmid, undef, $code);
316 __PACKAGE__-
>register_method ({
317 subclass
=> "PVE::API2::Firewall::CT",
318 path
=> '{vmid}/firewall',
321 __PACKAGE__-
>register_method({
326 description
=> "Directory index",
331 additionalProperties
=> 0,
333 node
=> get_standard_option
('pve-node'),
334 vmid
=> get_standard_option
('pve-vmid'),
342 subdir
=> { type
=> 'string' },
345 links
=> [ { rel
=> 'child', href
=> "{subdir}" } ],
351 my $conf = PVE
::OpenVZ
::load_config
($param->{vmid
});
354 { subdir
=> 'config' },
355 # { subdir => 'status' },
356 # { subdir => 'vncproxy' },
357 # { subdir => 'spiceproxy' },
358 # { subdir => 'migrate' },
359 # { subdir => 'initlog' },
361 { subdir
=> 'rrddata' },
362 { subdir
=> 'firewall' },
368 __PACKAGE__-
>register_method({
370 path
=> '{vmid}/rrd',
372 protected
=> 1, # fixme: can we avoid that?
374 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
376 description
=> "Read VM RRD statistics (returns PNG)",
378 additionalProperties
=> 0,
380 node
=> get_standard_option
('pve-node'),
381 vmid
=> get_standard_option
('pve-vmid'),
383 description
=> "Specify the time frame you are interested in.",
385 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
388 description
=> "The list of datasources you want to display.",
389 type
=> 'string', format
=> 'pve-configid-list',
392 description
=> "The RRD consolidation function",
394 enum
=> [ 'AVERAGE', 'MAX' ],
402 filename
=> { type
=> 'string' },
408 return PVE
::Cluster
::create_rrd_graph
(
409 "pve2-vm/$param->{vmid}", $param->{timeframe
},
410 $param->{ds
}, $param->{cf
});
414 __PACKAGE__-
>register_method({
416 path
=> '{vmid}/rrddata',
418 protected
=> 1, # fixme: can we avoid that?
420 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
422 description
=> "Read VM RRD statistics",
424 additionalProperties
=> 0,
426 node
=> get_standard_option
('pve-node'),
427 vmid
=> get_standard_option
('pve-vmid'),
429 description
=> "Specify the time frame you are interested in.",
431 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
434 description
=> "The RRD consolidation function",
436 enum
=> [ 'AVERAGE', 'MAX' ],
451 return PVE
::Cluster
::create_rrd_data
(
452 "pve2-vm/$param->{vmid}", $param->{timeframe
}, $param->{cf
});
456 __PACKAGE__-
>register_method({
458 path
=> '{vmid}/config',
461 description
=> "Get container configuration.",
463 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
466 additionalProperties
=> 0,
468 node
=> get_standard_option
('pve-node'),
469 vmid
=> get_standard_option
('pve-vmid'),
477 description
=> 'SHA1 digest of configuration file. This can be used to prevent concurrent modifications.',
484 my $lxc_conf = PVE
::LXC
::load_config
($param->{vmid
});
486 # NOTE: we only return selected/converted values
488 my $conf = { digest
=> $lxc_conf->{digest
} };
490 my $stcfg = PVE
::Cluster
::cfs_read_file
("storage.cfg");
492 my ($sid, undef, $path) = &$get_container_storage($stcfg, $param->{vmid
}, $lxc_conf);
493 $conf->{storage
} = $sid || $path;
495 my $properties = PVE
::LXC
::json_config_properties
();
497 foreach my $k (keys %$properties) {
499 if ($k eq 'description') {
500 if (my $raw = $lxc_conf->{'pve.comment'}) {
501 $conf->{$k} = PVE
::Tools
::decode_text
($raw);
503 } elsif ($k eq 'hostname') {
504 $conf->{$k} = $lxc_conf->{'lxc.utsname'} if $lxc_conf->{'lxc.utsname'};
505 } elsif ($k =~ m/^net\d$/) {
506 my $net = $lxc_conf->{$k};
508 $conf->{$k} = PVE
::LXC
::print_netif
($net);
515 __PACKAGE__-
>register_method({
516 name
=> 'destroy_vm',
521 description
=> "Destroy the container (also delete all uses files).",
523 check
=> [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
526 additionalProperties
=> 0,
528 node
=> get_standard_option
('pve-node'),
529 vmid
=> get_standard_option
('pve-vmid'),
538 my $rpcenv = PVE
::RPCEnvironment
::get
();
540 my $authuser = $rpcenv->get_user();
542 my $vmid = $param->{vmid
};
544 # test if container exists
545 my $conf = PVE
::LXC
::load_config
($param->{vmid
});
548 my $cmd = ['lxc-destroy', '-n', $vmid ];
552 PVE
::AccessControl
::remove_vm_from_pool
($vmid);
555 return $rpcenv->fork_worker('vzdestroy', $vmid, $authuser, $realcmd);