]> git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Cluster/Ceph.pm
api: cluster/ceph: allow version metadata call on non-ceph nodes
[pve-manager.git] / PVE / API2 / Cluster / Ceph.pm
1 package PVE::API2::Cluster::Ceph;
2
3 use strict;
4 use warnings;
5
6 use JSON;
7
8 use PVE::Ceph::Services;
9 use PVE::Ceph::Tools;
10 use PVE::Cluster;
11 use PVE::Exception qw(raise_param_exc);
12 use PVE::JSONSchema qw(get_standard_option);
13 use PVE::RADOS;
14 use PVE::RESTHandler;
15 use PVE::SafeSyslog;
16 use PVE::Tools qw(extract_param);
17
18 use base qw(PVE::RESTHandler);
19
20 __PACKAGE__->register_method ({
21 name => 'cephindex',
22 path => '',
23 method => 'GET',
24 description => "Cluster ceph index.",
25 permissions => { user => 'all' },
26 parameters => {
27 additionalProperties => 0,
28 properties => {},
29 },
30 returns => {
31 type => 'array',
32 items => {
33 type => "object",
34 properties => {},
35 },
36 links => [ { rel => 'child', href => "{name}" } ],
37 },
38 code => sub {
39 my ($param) = @_;
40
41 my $result = [
42 { name => 'metadata' },
43 { name => 'status' },
44 { name => 'flags' },
45 ];
46
47 return $result;
48 }
49 });
50
51 __PACKAGE__->register_method ({
52 name => 'metadata',
53 path => 'metadata',
54 method => 'GET',
55 description => "Get ceph metadata.",
56 protected => 1,
57 permissions => {
58 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
59 },
60 parameters => {
61 additionalProperties => 0,
62 properties => {
63 scope => {
64 type => 'string',
65 optional => 1,
66 default => 'all',
67 enum => ['all', 'versions', ],
68 },
69 },
70 },
71 returns => { type => 'object' },
72 code => sub {
73 my ($param) = @_;
74
75 my $scope = $param->{scope} // 'all';
76
77 my $res = {
78 # FIXME: remove with 7.0 depreacated by structured 'versions'
79 version => PVE::Cluster::get_node_kv("ceph-version"),
80 };
81
82 if (defined(my $versions = PVE::Ceph::Services::get_ceph_versions())) {
83 $res->{node} = $versions;
84 }
85
86 return $res if ($scope eq 'versions');
87
88 # only check now, we want to allow calls with scope 'versions' on non-ceph nodes too!
89 PVE::Ceph::Tools::check_ceph_inited();
90 my $rados = PVE::RADOS->new();
91
92 for my $type ( qw(mon mgr mds) ) {
93 my $typedata = PVE::Ceph::Services::get_cluster_service($type);
94 my $data = {};
95 for my $host (sort keys %$typedata) {
96 for my $service (sort keys %{$typedata->{$host}}) {
97 $data->{"$service\@$host"} = $typedata->{$host}->{$service};
98 }
99 }
100
101 # get data from metadata call and merge 'our' data
102 my $services = $rados->mon_command({ prefix => "$type metadata" });
103 for my $service ( @$services ) {
104 my $hostname = $service->{hostname};
105 next if !defined($hostname); # can happen if node is dead
106
107 my $servicename = $service->{name} // $service->{id};
108 my $id = "$servicename\@$hostname";
109
110 if ($data->{$id}) { # copy values over to the metadata hash
111 for my $k (keys %{$data->{$id}}) {
112 $service->{$k} = $data->{$id}->{$k};
113 }
114 }
115 $data->{$id} = $service;
116 }
117
118 $res->{$type} = $data;
119 }
120
121 $res->{osd} = $rados->mon_command({ prefix => "osd metadata" });
122
123 return $res;
124 }
125 });
126
127 __PACKAGE__->register_method ({
128 name => 'status',
129 path => 'status',
130 method => 'GET',
131 description => "Get ceph status.",
132 protected => 1,
133 permissions => {
134 check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
135 },
136 parameters => {
137 additionalProperties => 0,
138 properties => { },
139 },
140 returns => { type => 'object' },
141 code => sub {
142 my ($param) = @_;
143
144 PVE::Ceph::Tools::check_ceph_inited();
145
146 return PVE::Ceph::Tools::ceph_cluster_status();
147 }
148 });
149
150 my $possible_flags = PVE::Ceph::Tools::get_possible_osd_flags();
151 my $possible_flags_list = [ sort keys %$possible_flags ];
152
153 my $get_current_set_flags = sub {
154 my $rados = shift;
155
156 $rados //= PVE::RADOS->new();
157
158 my $stat = $rados->mon_command({ prefix => 'osd dump' });
159 my $setflags = $stat->{flags} // '';
160 return { map { $_ => 1 } PVE::Tools::split_list($setflags) };
161 };
162
163 __PACKAGE__->register_method ({
164 name => 'get_all_flags',
165 path => 'flags',
166 method => 'GET',
167 description => "get the status of all ceph flags",
168 protected => 1,
169 permissions => {
170 check => ['perm', '/', [ 'Sys.Audit' ]],
171 },
172 parameters => {
173 additionalProperties => 0,
174 properties => {
175 },
176 },
177 returns => {
178 type => 'array',
179 items => {
180 type => "object",
181 additionalProperties => 1,
182 properties => {
183 name => {
184 description => "Flag name.",
185 type => 'string', enum => $possible_flags_list,
186 },
187 },
188 },
189 links => [ { rel => 'child', href => "{name}" } ],
190 },
191 code => sub {
192 my ($param) = @_;
193
194 PVE::Ceph::Tools::check_ceph_configured();
195
196 my $setflags = $get_current_set_flags->();
197
198 my $res = [];
199 foreach my $flag (@$possible_flags_list) {
200 my $el = {
201 name => $flag,
202 description => $possible_flags->{$flag}->{description},
203 value => 0,
204 };
205
206 my $realflag = PVE::Ceph::Tools::get_real_flag_name($flag);
207 if ($setflags->{$realflag}) {
208 $el->{value} = 1;
209 }
210
211 push @$res, $el;
212 }
213
214 return $res;
215 }
216 });
217
218 __PACKAGE__->register_method ({
219 name => 'set_flags',
220 path => 'flags',
221 method => 'PUT',
222 description => "Set/Unset multiple ceph flags at once.",
223 protected => 1,
224 permissions => {
225 check => ['perm', '/', [ 'Sys.Modify' ]],
226 },
227 parameters => {
228 additionalProperties => 0,
229 properties => $possible_flags,
230 },
231 returns => { type => 'string' },
232 code => sub {
233 my ($param) = @_;
234
235 my $rpcenv = PVE::RPCEnvironment::get();
236 my $user = $rpcenv->get_user();
237 PVE::Ceph::Tools::check_ceph_configured();
238
239 my $worker = sub {
240 my $rados = PVE::RADOS->new(); # (re-)open for forked worker
241
242 my $setflags = $get_current_set_flags->($rados);
243
244 my $errors = 0;
245 foreach my $flag (@$possible_flags_list) {
246 next if !defined($param->{$flag});
247 my $val = $param->{$flag};
248 my $realflag = PVE::Ceph::Tools::get_real_flag_name($flag);
249
250 next if !$val == !$setflags->{$realflag}; # we do not set/unset flags to the same state
251
252 my $prefix = $val ? 'set' : 'unset';
253 eval {
254 print "$prefix $flag\n";
255 $rados->mon_command({ prefix => "osd $prefix", key => $flag, });
256 };
257 if (my $err = $@) {
258 warn "error with $flag: '$err'\n";
259 $errors++;
260 }
261 }
262
263 if ($errors) {
264 die "could not set/unset $errors flags\n";
265 }
266 };
267
268 return $rpcenv->fork_worker('cephsetflags', undef, $user, $worker);
269 }});
270
271
272 __PACKAGE__->register_method ({
273 name => 'get_flag',
274 path => 'flags/{flag}',
275 method => 'GET',
276 description => "Get the status of a specific ceph flag.",
277 protected => 1,
278 permissions => {
279 check => ['perm', '/', [ 'Sys.Audit' ]],
280 },
281 parameters => {
282 additionalProperties => 0,
283 properties => {
284 flag => {
285 description => "The name of the flag name to get.",
286 type => 'string', enum => $possible_flags_list,
287 },
288 },
289 },
290 returns => {
291 type => 'boolean',
292 },
293 code => sub {
294 my ($param) = @_;
295
296 PVE::Ceph::Tools::check_ceph_configured();
297
298 my $realflag = PVE::Ceph::Tools::get_real_flag_name($param->{flag});
299
300 my $setflags = $get_current_set_flags->();
301 if ($setflags->{$realflag}) {
302 return 1;
303 }
304
305 return 0;
306 }});
307
308 __PACKAGE__->register_method ({
309 name => 'update_flag',
310 path => 'flags/{flag}',
311 method => 'PUT',
312 description => "Set or clear (unset) a specific ceph flag",
313 protected => 1,
314 permissions => {
315 check => ['perm', '/', [ 'Sys.Modify' ]],
316 },
317 parameters => {
318 additionalProperties => 0,
319 properties => {
320 flag => {
321 description => 'The ceph flag to update',
322 type => 'string',
323 enum => $possible_flags_list,
324 },
325 value => {
326 description => 'The new value of the flag',
327 type => 'boolean',
328 },
329 },
330 },
331 returns => { type => 'null' },
332 code => sub {
333 my ($param) = @_;
334
335 PVE::Ceph::Tools::check_ceph_configured();
336
337 my $cmd = $param->{value} ? 'set' : 'unset';
338
339 my $rados = PVE::RADOS->new();
340 $rados->mon_command({
341 prefix => "osd $cmd",
342 key => $param->{flag},
343 });
344
345 return undef;
346 }});
347
348
349 1;