1 package PVE
::API2
::Ceph
;
11 use PVE
::Ceph
::Services
;
12 use PVE
::Cluster
qw(cfs_read_file cfs_write_file);
13 use PVE
::JSONSchema
qw(get_standard_option);
17 use PVE
::RPCEnvironment
;
19 use PVE
::Tools
qw(run_command file_get_contents file_set_contents extract_param);
21 use PVE
::API2
::Ceph
::OSD
;
22 use PVE
::API2
::Ceph
::FS
;
23 use PVE
::API2
::Ceph
::MDS
;
24 use PVE
::API2
::Ceph
::MGR
;
25 use PVE
::API2
::Ceph
::MON
;
26 use PVE
::API2
::Ceph
::Pool
;
27 use PVE
::API2
::Ceph
::Pools
;
28 use PVE
::API2
::Storage
::Config
;
30 use base
qw(PVE::RESTHandler);
32 my $pve_osd_default_journal_size = 1024*5;
34 __PACKAGE__-
>register_method ({
35 subclass
=> "PVE::API2::Ceph::OSD",
39 __PACKAGE__-
>register_method ({
40 subclass
=> "PVE::API2::Ceph::MDS",
44 __PACKAGE__-
>register_method ({
45 subclass
=> "PVE::API2::Ceph::MGR",
49 __PACKAGE__-
>register_method ({
50 subclass
=> "PVE::API2::Ceph::MON",
54 __PACKAGE__-
>register_method ({
55 subclass
=> "PVE::API2::Ceph::FS",
59 __PACKAGE__-
>register_method ({
60 subclass
=> "PVE::API2::Ceph::Pool",
64 # TODO: deprecrated, remove with PVE 8
65 __PACKAGE__-
>register_method ({
66 subclass
=> "PVE::API2::Ceph::Pools",
70 __PACKAGE__-
>register_method ({
74 description
=> "Directory index.",
75 permissions
=> { user
=> 'all' },
77 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
80 additionalProperties
=> 0,
82 node
=> get_standard_option
('pve-node'),
91 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
97 { name
=> 'cmd-safety' },
99 { name
=> 'configdb' },
109 { name
=> 'restart' },
112 { name
=> 'status' },
119 __PACKAGE__-
>register_method ({
125 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
127 description
=> "Get the Ceph configuration file.",
129 additionalProperties
=> 0,
131 node
=> get_standard_option
('pve-node'),
134 returns
=> { type
=> 'string' },
138 PVE
::Ceph
::Tools
::check_ceph_inited
();
140 my $path = PVE
::Ceph
::Tools
::get_config
('pve_ceph_cfgpath');
141 return file_get_contents
($path);
145 __PACKAGE__-
>register_method ({
152 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
154 description
=> "Get the Ceph configuration database.",
156 additionalProperties
=> 0,
158 node
=> get_standard_option
('pve-node'),
166 section
=> { type
=> "string", },
167 name
=> { type
=> "string", },
168 value
=> { type
=> "string", },
169 level
=> { type
=> "string", },
170 'can_update_at_runtime' => { type
=> "boolean", },
171 mask
=> { type
=> "string" },
178 PVE
::Ceph
::Tools
::check_ceph_inited
();
180 my $rados = PVE
::RADOS-
>new();
181 my $res = $rados->mon_command( { prefix
=> 'config dump', format
=> 'json' });
182 foreach my $entry (@$res) {
183 $entry->{can_update_at_runtime
} = $entry->{can_update_at_runtime
}?
1 : 0; # JSON::true/false -> 1/0
190 __PACKAGE__-
>register_method ({
194 description
=> "Create initial ceph default configuration and setup symlinks.",
198 check
=> ['perm', '/', [ 'Sys.Modify' ]],
201 additionalProperties
=> 0,
203 node
=> get_standard_option
('pve-node'),
205 description
=> "Use specific network for all ceph related traffic",
206 type
=> 'string', format
=> 'CIDR',
210 'cluster-network' => {
211 description
=> "Declare a separate cluster network, OSDs will route" .
212 "heartbeat, object replication and recovery traffic over it",
213 type
=> 'string', format
=> 'CIDR',
214 requires
=> 'network',
219 description
=> 'Targeted number of replicas per object',
227 description
=> 'Minimum number of available replicas per object to allow I/O',
235 description
=> "Placement group bits, used to specify the " .
236 "default number of placement groups.\n\nNOTE: 'osd pool " .
237 "default pg num' does not work for default pools.",
245 description
=> "Disable cephx authentication.\n\n" .
246 "WARNING: cephx is a security feature protecting against " .
247 "man-in-the-middle attacks. Only consider disabling cephx ".
248 "if your network is private!",
255 returns
=> { type
=> 'null' },
259 my $version = PVE
::Ceph
::Tools
::get_local_version
(1);
261 if (!$version || $version < 14) {
262 die "Ceph Nautilus required - please run 'pveceph install'\n";
264 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_bin');
267 my $auth = $param->{disable_cephx
} ?
'none' : 'cephx';
269 # simply load old config if it already exists
270 PVE
::Cluster
::cfs_lock_file
('ceph.conf', undef, sub {
271 my $cfg = cfs_read_file
('ceph.conf');
273 if (!$cfg->{global
}) {
278 UUID
::generate
($uuid);
279 UUID
::unparse
($uuid, $fsid);
283 'auth cluster required' => $auth,
284 'auth service required' => $auth,
285 'auth client required' => $auth,
286 'osd pool default size' => $param->{size
} // 3,
287 'osd pool default min size' => $param->{min_size
} // 2,
288 'mon allow pool delete' => 'true',
291 # this does not work for default pools
292 #'osd pool default pg num' => $pg_num,
293 #'osd pool default pgp num' => $pg_num,
296 if ($auth eq 'cephx') {
297 $cfg->{client
}->{keyring
} = '/etc/pve/priv/$cluster.$name.keyring';
300 if ($param->{pg_bits
}) {
301 $cfg->{global
}->{'osd pg bits'} = $param->{pg_bits
};
302 $cfg->{global
}->{'osd pgp bits'} = $param->{pg_bits
};
305 if ($param->{network
}) {
306 $cfg->{global
}->{'public network'} = $param->{network
};
307 $cfg->{global
}->{'cluster network'} = $param->{network
};
310 if ($param->{'cluster-network'}) {
311 $cfg->{global
}->{'cluster network'} = $param->{'cluster-network'};
314 cfs_write_file
('ceph.conf', $cfg);
316 if ($auth eq 'cephx') {
317 PVE
::Ceph
::Tools
::get_or_create_admin_keyring
();
319 PVE
::Ceph
::Tools
::setup_pve_symlinks
();
326 __PACKAGE__-
>register_method ({
330 description
=> "Stop ceph services.",
334 check
=> ['perm', '/', [ 'Sys.Modify' ]],
337 additionalProperties
=> 0,
339 node
=> get_standard_option
('pve-node'),
341 description
=> 'Ceph service name.',
344 default => 'ceph.target',
345 pattern
=> '(ceph|mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
349 returns
=> { type
=> 'string' },
353 my $rpcenv = PVE
::RPCEnvironment
::get
();
355 my $authuser = $rpcenv->get_user();
357 PVE
::Ceph
::Tools
::check_ceph_inited
();
359 my $cfg = cfs_read_file
('ceph.conf');
360 scalar(keys %$cfg) || die "no configuration\n";
366 if ($param->{service
}) {
367 push @$cmd, $param->{service
};
370 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
373 return $rpcenv->fork_worker('srvstop', $param->{service
} || 'ceph',
377 __PACKAGE__-
>register_method ({
381 description
=> "Start ceph services.",
385 check
=> ['perm', '/', [ 'Sys.Modify' ]],
388 additionalProperties
=> 0,
390 node
=> get_standard_option
('pve-node'),
392 description
=> 'Ceph service name.',
395 default => 'ceph.target',
396 pattern
=> '(ceph|mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
400 returns
=> { type
=> 'string' },
404 my $rpcenv = PVE
::RPCEnvironment
::get
();
406 my $authuser = $rpcenv->get_user();
408 PVE
::Ceph
::Tools
::check_ceph_inited
();
410 my $cfg = cfs_read_file
('ceph.conf');
411 scalar(keys %$cfg) || die "no configuration\n";
417 if ($param->{service
}) {
418 push @$cmd, $param->{service
};
421 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
424 return $rpcenv->fork_worker('srvstart', $param->{service
} || 'ceph',
428 __PACKAGE__-
>register_method ({
432 description
=> "Restart ceph services.",
436 check
=> ['perm', '/', [ 'Sys.Modify' ]],
439 additionalProperties
=> 0,
441 node
=> get_standard_option
('pve-node'),
443 description
=> 'Ceph service name.',
446 default => 'ceph.target',
447 pattern
=> '(mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
451 returns
=> { type
=> 'string' },
455 my $rpcenv = PVE
::RPCEnvironment
::get
();
457 my $authuser = $rpcenv->get_user();
459 PVE
::Ceph
::Tools
::check_ceph_inited
();
461 my $cfg = cfs_read_file
('ceph.conf');
462 scalar(keys %$cfg) || die "no configuration\n";
467 my $cmd = ['restart'];
468 if ($param->{service
}) {
469 push @$cmd, $param->{service
};
472 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
475 return $rpcenv->fork_worker('srvrestart', $param->{service
} || 'ceph',
479 __PACKAGE__-
>register_method ({
483 description
=> "Get ceph status.",
487 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
490 additionalProperties
=> 0,
492 node
=> get_standard_option
('pve-node'),
495 returns
=> { type
=> 'object' },
499 PVE
::Ceph
::Tools
::check_ceph_inited
();
501 return PVE
::Ceph
::Tools
::ceph_cluster_status
();
505 __PACKAGE__-
>register_method ({
509 description
=> "Get OSD crush map",
513 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
516 additionalProperties
=> 0,
518 node
=> get_standard_option
('pve-node'),
521 returns
=> { type
=> 'string' },
525 PVE
::Ceph
::Tools
::check_ceph_inited
();
527 # this produces JSON (difficult to read for the user)
528 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
532 my $mapfile = "/var/tmp/ceph-crush.map.$$";
533 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
535 my $rados = PVE
::RADOS-
>new();
538 my $bindata = $rados->mon_command({ prefix
=> 'osd getcrushmap', format
=> 'plain' });
539 file_set_contents
($mapfile, $bindata);
540 run_command
(['crushtool', '-d', $mapfile, '-o', $mapdata]);
541 $txt = file_get_contents
($mapdata);
553 __PACKAGE__-
>register_method({
557 description
=> "Read ceph log",
560 check
=> ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
564 additionalProperties
=> 0,
566 node
=> get_standard_option
('pve-node'),
585 description
=> "Line number",
589 description
=> "Line text",
598 PVE
::Ceph
::Tools
::check_ceph_inited
();
600 my $rpcenv = PVE
::RPCEnvironment
::get
();
601 my $user = $rpcenv->get_user();
602 my $node = $param->{node
};
604 my $logfile = "/var/log/ceph/ceph.log";
605 my ($count, $lines) = PVE
::Tools
::dump_logfile
($logfile, $param->{start
}, $param->{limit
});
607 $rpcenv->set_result_attrib('total', $count);
612 __PACKAGE__-
>register_method ({
616 description
=> "List ceph rules.",
620 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
623 additionalProperties
=> 0,
625 node
=> get_standard_option
('pve-node'),
634 description
=> "Name of the CRUSH rule.",
639 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
644 PVE
::Ceph
::Tools
::check_ceph_inited
();
646 my $rados = PVE
::RADOS-
>new();
648 my $rules = $rados->mon_command({ prefix
=> 'osd crush rule ls' });
652 foreach my $rule (@$rules) {
653 push @$res, { name
=> $rule };
659 __PACKAGE__-
>register_method ({
660 name
=> 'cmd_safety',
661 path
=> 'cmd-safety',
663 description
=> "Heuristical check if it is safe to perform an action.",
667 check
=> ['perm', '/', [ 'Sys.Audit' ]],
670 additionalProperties
=> 0,
672 node
=> get_standard_option
('pve-node'),
674 description
=> 'Service type',
676 enum
=> ['osd', 'mon', 'mds'],
679 description
=> 'ID of the service',
683 description
=> 'Action to check',
685 enum
=> ['stop', 'destroy'],
694 description
=> 'If it is safe to run the command.',
699 description
=> 'Status message given by Ceph.'
706 PVE
::Ceph
::Tools
::check_ceph_inited
();
708 my $id = $param->{id
};
709 my $service = $param->{service
};
710 my $action = $param->{action
};
712 my $rados = PVE
::RADOS-
>new();
714 my $supported_actions = {
716 stop
=> 'ok-to-stop',
717 destroy
=> 'safe-to-destroy',
720 stop
=> 'ok-to-stop',
721 destroy
=> 'ok-to-rm',
724 stop
=> 'ok-to-stop',
728 die "Service does not support this action: ${service}: ${action}\n"
729 if !$supported_actions->{$service}->{$action};
737 prefix
=> "${service} $supported_actions->{$service}->{$action}",
740 if ($service eq 'mon' && $action eq 'destroy') {
743 $params->{ids
} = [ $id ];
746 $result = $rados->mon_cmd($params, 1);
749 $result->{safe
} = $result->{return_code
} == 0 ?
1 : 0;
750 $result->{status
} = $result->{status_message
};