]> git.proxmox.com Git - pve-container.git/blob - src/PVE/API2/LXC/Config.pm
d/control: bump versioned dependency for guest-common
[pve-container.git] / src / PVE / API2 / LXC / Config.pm
1 package PVE::API2::LXC::Config;
2
3 use strict;
4 use warnings;
5
6 use PVE::SafeSyslog;
7 use PVE::Tools qw(extract_param run_command);
8 use PVE::Exception qw(raise raise_param_exc);
9 use PVE::INotify;
10 use PVE::Cluster qw(cfs_read_file);
11 use PVE::AccessControl;
12 use PVE::Firewall;
13 use PVE::GuestHelpers;
14 use PVE::Storage;
15 use PVE::RESTHandler;
16 use PVE::RPCEnvironment;
17 use PVE::LXC;
18 use PVE::LXC::Config;
19 use PVE::LXC::Create;
20 use PVE::JSONSchema qw(get_standard_option);
21
22 use base qw(PVE::RESTHandler);
23
24 __PACKAGE__->register_method({
25 name => 'vm_config',
26 path => '',
27 method => 'GET',
28 proxyto => 'node',
29 description => "Get container configuration.",
30 permissions => {
31 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
32 },
33 parameters => {
34 additionalProperties => 0,
35 properties => {
36 node => get_standard_option('pve-node'),
37 vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid }),
38 current => {
39 description => "Get current values (instead of pending values).",
40 optional => 1,
41 default => 0,
42 type => 'boolean',
43 },
44 snapshot => get_standard_option('pve-snapshot-name', {
45 description => "Fetch config values from given snapshot.",
46 optional => 1,
47 completion => sub {
48 my ($cmd, $pname, $cur, $args) = @_;
49 PVE::LXC::Config->snapshot_list($args->[0]);
50 },
51 }),
52 },
53 },
54 returns => {
55 type => "object",
56 properties => PVE::LXC::Config->json_config_properties({
57 lxc => {
58 description => "Array of lxc low-level configurations ([[key1, value1], [key2, value2] ...]).",
59 type => 'array',
60 items => { type => 'array', items => { type => 'string' }},
61 optional => 1,
62 },
63 digest => {
64 type => 'string',
65 description => 'SHA1 digest of configuration file. This can be used to prevent concurrent modifications.',
66 }
67 }),
68 },
69 code => sub {
70 my ($param) = @_;
71
72 raise_param_exc({ snapshot => "cannot use 'snapshot' parameter with 'current'",
73 current => "cannot use 'snapshot' parameter with 'current'"})
74 if ($param->{snapshot} && $param->{current});
75
76 my $conf;
77 if ($param->{snapshot}) {
78 $conf = PVE::LXC::Config->load_snapshot_config($param->{vmid}, $param->{snapshot});
79 } else {
80 $conf = PVE::LXC::Config->load_current_config($param->{vmid}, $param->{current});
81 }
82
83 return $conf;
84 }});
85
86 my $vm_config_perm_list = [
87 'VM.Config.Disk',
88 'VM.Config.CPU',
89 'VM.Config.Memory',
90 'VM.Config.Network',
91 'VM.Config.Options',
92 ];
93
94 __PACKAGE__->register_method({
95 name => 'update_vm',
96 path => '',
97 method => 'PUT',
98 protected => 1,
99 proxyto => 'node',
100 description => "Set container options.",
101 permissions => {
102 check => ['perm', '/vms/{vmid}', $vm_config_perm_list, any => 1],
103 description => 'non-volume mount points in rootfs and mp[n] are restricted to root@pam',
104 },
105 parameters => {
106 additionalProperties => 0,
107 properties => PVE::LXC::Config->json_config_properties(
108 {
109 node => get_standard_option('pve-node'),
110 vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid }),
111 delete => {
112 type => 'string', format => 'pve-configid-list',
113 description => "A list of settings you want to delete.",
114 optional => 1,
115 },
116 revert => {
117 type => 'string', format => 'pve-configid-list',
118 description => "Revert a pending change.",
119 optional => 1,
120 },
121 digest => {
122 type => 'string',
123 description => 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
124 maxLength => 40,
125 optional => 1,
126 }
127 }),
128 },
129 returns => { type => 'null'},
130 code => sub {
131 my ($param) = @_;
132
133 my $rpcenv = PVE::RPCEnvironment::get();
134 my $authuser = $rpcenv->get_user();
135
136 my $node = extract_param($param, 'node');
137 my $vmid = extract_param($param, 'vmid');
138
139 my $digest = extract_param($param, 'digest');
140
141 die "no options specified\n" if !scalar(keys %$param);
142
143 my $delete_str = extract_param($param, 'delete');
144 my @delete = PVE::Tools::split_list($delete_str);
145 my $revert_str = extract_param($param, 'revert');
146 my @revert = PVE::Tools::split_list($revert_str);
147
148 $param->{cpuunits} = PVE::CGroup::clamp_cpu_shares($param->{cpuunits})
149 if defined($param->{cpuunits}); # clamp value depending on cgroup version
150
151 my $code = sub {
152
153 my $conf = PVE::LXC::Config->load_config($vmid);
154 PVE::LXC::Config->check_lock($conf);
155
156 PVE::Tools::assert_if_modified($digest, $conf->{digest});
157
158 my $unprivileged = $conf->{unprivileged};
159 PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, $conf, {}, [@delete], $unprivileged);
160 PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, $conf, {}, [@revert], $unprivileged);
161
162 foreach my $opt (@revert) {
163 raise_param_exc({ revert => "unknown option '$opt'" })
164 if !PVE::LXC::Config->option_exists($opt);
165
166 raise_param_exc({ revert => "you can't use '-$opt' and '-revert $opt' at the same time" })
167 if defined($param->{$opt});
168 }
169
170 foreach my $opt (@delete) {
171 raise_param_exc({ delete => "unknown option '$opt'" })
172 if !PVE::LXC::Config->option_exists($opt);
173
174 raise_param_exc({ delete => "you can't use '-$opt' and -delete $opt' at the same time" })
175 if defined($param->{$opt});
176
177 raise_param_exc({ delete => "you can't use '-delete $opt' and '-revert $opt' at the same time" })
178 if grep(/^$opt$/, @revert);
179 }
180
181 PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, $conf, $param, [], $unprivileged);
182
183 my $storage_cfg = PVE::Storage::config();
184
185 my $repl_conf = PVE::ReplicationConfig->new();
186 my $is_replicated = $repl_conf->check_for_existing_jobs($vmid, 1);
187 if ($is_replicated) {
188 PVE::LXC::Config->foreach_volume($param, sub {
189 my ($opt, $mountpoint) = @_;
190 my $volid = $mountpoint->{volume};
191 return if !$volid || !($mountpoint->{replicate}//1);
192 if ($mountpoint->{type} eq 'volume') {
193 my ($storeid, $format);
194 if ($volid =~ $PVE::LXC::NEW_DISK_RE) {
195 $storeid = $1;
196 $format = $mountpoint->{format} || PVE::Storage::storage_default_format($storage_cfg, $storeid);
197 } else {
198 ($storeid, undef) = PVE::Storage::parse_volume_id($volid, 1);
199 $format = (PVE::Storage::parse_volname($storage_cfg, $volid))[6];
200 }
201 return if PVE::Storage::storage_can_replicate($storage_cfg, $storeid, $format);
202 my $scfg = PVE::Storage::storage_config($storage_cfg, $storeid);
203 return if $scfg->{shared};
204 }
205 die "cannot add non-replicatable volume to a replicated VM\n";
206 });
207 }
208
209 my $running = PVE::LXC::check_running($vmid);
210
211 my $errors = PVE::LXC::Config->update_pct_config($vmid, $conf, $running, $param, \@delete, \@revert);
212 PVE::LXC::Config->write_config($vmid, $conf);
213 $conf = PVE::LXC::Config->load_config($vmid);
214
215 PVE::LXC::update_lxc_config($vmid, $conf);
216 raise_param_exc($errors) if scalar(keys %$errors);
217 };
218
219 PVE::LXC::Config->lock_config($vmid, $code);
220
221 return undef;
222 }});
223
224 1;