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