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
::Pools
;
27 use PVE
::API2
::Storage
::Config
;
29 use base
qw(PVE::RESTHandler);
31 my $pve_osd_default_journal_size = 1024*5;
33 __PACKAGE__-
>register_method ({
34 subclass
=> "PVE::API2::Ceph::OSD",
38 __PACKAGE__-
>register_method ({
39 subclass
=> "PVE::API2::Ceph::MDS",
43 __PACKAGE__-
>register_method ({
44 subclass
=> "PVE::API2::Ceph::MGR",
48 __PACKAGE__-
>register_method ({
49 subclass
=> "PVE::API2::Ceph::MON",
53 __PACKAGE__-
>register_method ({
54 subclass
=> "PVE::API2::Ceph::FS",
58 __PACKAGE__-
>register_method ({
59 subclass
=> "PVE::API2::Ceph::Pools",
63 __PACKAGE__-
>register_method ({
67 description
=> "Directory index.",
68 permissions
=> { user
=> 'all' },
70 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
73 additionalProperties
=> 0,
75 node
=> get_standard_option
('pve-node'),
84 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
90 { name
=> 'cmd-safety' },
92 { name
=> 'configdb' },
102 { name
=> 'restart' },
105 { name
=> 'status' },
112 __PACKAGE__-
>register_method ({
118 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
120 description
=> "Get the Ceph configuration file.",
122 additionalProperties
=> 0,
124 node
=> get_standard_option
('pve-node'),
127 returns
=> { type
=> 'string' },
131 PVE
::Ceph
::Tools
::check_ceph_inited
();
133 my $path = PVE
::Ceph
::Tools
::get_config
('pve_ceph_cfgpath');
134 return file_get_contents
($path);
138 __PACKAGE__-
>register_method ({
145 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
147 description
=> "Get the Ceph configuration database.",
149 additionalProperties
=> 0,
151 node
=> get_standard_option
('pve-node'),
159 section
=> { type
=> "string", },
160 name
=> { type
=> "string", },
161 value
=> { type
=> "string", },
162 level
=> { type
=> "string", },
163 'can_update_at_runtime' => { type
=> "boolean", },
164 mask
=> { type
=> "string" },
171 PVE
::Ceph
::Tools
::check_ceph_inited
();
173 my $rados = PVE
::RADOS-
>new();
174 my $res = $rados->mon_command( { prefix
=> 'config dump', format
=> 'json' });
175 foreach my $entry (@$res) {
176 $entry->{can_update_at_runtime
} = $entry->{can_update_at_runtime
}?
1 : 0; # JSON::true/false -> 1/0
183 __PACKAGE__-
>register_method ({
187 description
=> "Create initial ceph default configuration and setup symlinks.",
191 check
=> ['perm', '/', [ 'Sys.Modify' ]],
194 additionalProperties
=> 0,
196 node
=> get_standard_option
('pve-node'),
198 description
=> "Use specific network for all ceph related traffic",
199 type
=> 'string', format
=> 'CIDR',
203 'cluster-network' => {
204 description
=> "Declare a separate cluster network, OSDs will route" .
205 "heartbeat, object replication and recovery traffic over it",
206 type
=> 'string', format
=> 'CIDR',
207 requires
=> 'network',
212 description
=> 'Targeted number of replicas per object',
220 description
=> 'Minimum number of available replicas per object to allow I/O',
228 description
=> "Placement group bits, used to specify the " .
229 "default number of placement groups.\n\nNOTE: 'osd pool " .
230 "default pg num' does not work for default pools.",
238 description
=> "Disable cephx authentication.\n\n" .
239 "WARNING: cephx is a security feature protecting against " .
240 "man-in-the-middle attacks. Only consider disabling cephx ".
241 "if your network is private!",
248 returns
=> { type
=> 'null' },
252 my $version = PVE
::Ceph
::Tools
::get_local_version
(1);
254 if (!$version || $version < 14) {
255 die "Ceph Nautilus required - please run 'pveceph install'\n";
257 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_bin');
260 my $auth = $param->{disable_cephx
} ?
'none' : 'cephx';
262 # simply load old config if it already exists
263 PVE
::Cluster
::cfs_lock_file
('ceph.conf', undef, sub {
264 my $cfg = cfs_read_file
('ceph.conf');
266 if (!$cfg->{global
}) {
271 UUID
::generate
($uuid);
272 UUID
::unparse
($uuid, $fsid);
276 'auth cluster required' => $auth,
277 'auth service required' => $auth,
278 'auth client required' => $auth,
279 'osd pool default size' => $param->{size
} // 3,
280 'osd pool default min size' => $param->{min_size
} // 2,
281 'mon allow pool delete' => 'true',
284 # this does not work for default pools
285 #'osd pool default pg num' => $pg_num,
286 #'osd pool default pgp num' => $pg_num,
289 if ($auth eq 'cephx') {
290 $cfg->{client
}->{keyring
} = '/etc/pve/priv/$cluster.$name.keyring';
293 if ($param->{pg_bits
}) {
294 $cfg->{global
}->{'osd pg bits'} = $param->{pg_bits
};
295 $cfg->{global
}->{'osd pgp bits'} = $param->{pg_bits
};
298 if ($param->{network
}) {
299 $cfg->{global
}->{'public network'} = $param->{network
};
300 $cfg->{global
}->{'cluster network'} = $param->{network
};
303 if ($param->{'cluster-network'}) {
304 $cfg->{global
}->{'cluster network'} = $param->{'cluster-network'};
307 cfs_write_file
('ceph.conf', $cfg);
309 if ($auth eq 'cephx') {
310 PVE
::Ceph
::Tools
::get_or_create_admin_keyring
();
312 PVE
::Ceph
::Tools
::setup_pve_symlinks
();
319 __PACKAGE__-
>register_method ({
323 description
=> "Stop ceph services.",
327 check
=> ['perm', '/', [ 'Sys.Modify' ]],
330 additionalProperties
=> 0,
332 node
=> get_standard_option
('pve-node'),
334 description
=> 'Ceph service name.',
337 default => 'ceph.target',
338 pattern
=> '(ceph|mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
342 returns
=> { type
=> 'string' },
346 my $rpcenv = PVE
::RPCEnvironment
::get
();
348 my $authuser = $rpcenv->get_user();
350 PVE
::Ceph
::Tools
::check_ceph_inited
();
352 my $cfg = cfs_read_file
('ceph.conf');
353 scalar(keys %$cfg) || die "no configuration\n";
359 if ($param->{service
}) {
360 push @$cmd, $param->{service
};
363 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
366 return $rpcenv->fork_worker('srvstop', $param->{service
} || 'ceph',
370 __PACKAGE__-
>register_method ({
374 description
=> "Start ceph services.",
378 check
=> ['perm', '/', [ 'Sys.Modify' ]],
381 additionalProperties
=> 0,
383 node
=> get_standard_option
('pve-node'),
385 description
=> 'Ceph service name.',
388 default => 'ceph.target',
389 pattern
=> '(ceph|mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
393 returns
=> { type
=> 'string' },
397 my $rpcenv = PVE
::RPCEnvironment
::get
();
399 my $authuser = $rpcenv->get_user();
401 PVE
::Ceph
::Tools
::check_ceph_inited
();
403 my $cfg = cfs_read_file
('ceph.conf');
404 scalar(keys %$cfg) || die "no configuration\n";
410 if ($param->{service
}) {
411 push @$cmd, $param->{service
};
414 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
417 return $rpcenv->fork_worker('srvstart', $param->{service
} || 'ceph',
421 __PACKAGE__-
>register_method ({
425 description
=> "Restart ceph services.",
429 check
=> ['perm', '/', [ 'Sys.Modify' ]],
432 additionalProperties
=> 0,
434 node
=> get_standard_option
('pve-node'),
436 description
=> 'Ceph service name.',
439 default => 'ceph.target',
440 pattern
=> '(mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
444 returns
=> { type
=> 'string' },
448 my $rpcenv = PVE
::RPCEnvironment
::get
();
450 my $authuser = $rpcenv->get_user();
452 PVE
::Ceph
::Tools
::check_ceph_inited
();
454 my $cfg = cfs_read_file
('ceph.conf');
455 scalar(keys %$cfg) || die "no configuration\n";
460 my $cmd = ['restart'];
461 if ($param->{service
}) {
462 push @$cmd, $param->{service
};
465 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
468 return $rpcenv->fork_worker('srvrestart', $param->{service
} || 'ceph',
472 __PACKAGE__-
>register_method ({
476 description
=> "Get ceph status.",
480 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
483 additionalProperties
=> 0,
485 node
=> get_standard_option
('pve-node'),
488 returns
=> { type
=> 'object' },
492 PVE
::Ceph
::Tools
::check_ceph_inited
();
494 return PVE
::Ceph
::Tools
::ceph_cluster_status
();
498 __PACKAGE__-
>register_method ({
502 description
=> "Get OSD crush map",
506 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
509 additionalProperties
=> 0,
511 node
=> get_standard_option
('pve-node'),
514 returns
=> { type
=> 'string' },
518 PVE
::Ceph
::Tools
::check_ceph_inited
();
520 # this produces JSON (difficult to read for the user)
521 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
525 my $mapfile = "/var/tmp/ceph-crush.map.$$";
526 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
528 my $rados = PVE
::RADOS-
>new();
531 my $bindata = $rados->mon_command({ prefix
=> 'osd getcrushmap', format
=> 'plain' });
532 file_set_contents
($mapfile, $bindata);
533 run_command
(['crushtool', '-d', $mapfile, '-o', $mapdata]);
534 $txt = file_get_contents
($mapdata);
546 __PACKAGE__-
>register_method({
550 description
=> "Read ceph log",
553 check
=> ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
557 additionalProperties
=> 0,
559 node
=> get_standard_option
('pve-node'),
578 description
=> "Line number",
582 description
=> "Line text",
591 PVE
::Ceph
::Tools
::check_ceph_inited
();
593 my $rpcenv = PVE
::RPCEnvironment
::get
();
594 my $user = $rpcenv->get_user();
595 my $node = $param->{node
};
597 my $logfile = "/var/log/ceph/ceph.log";
598 my ($count, $lines) = PVE
::Tools
::dump_logfile
($logfile, $param->{start
}, $param->{limit
});
600 $rpcenv->set_result_attrib('total', $count);
605 __PACKAGE__-
>register_method ({
609 description
=> "List ceph rules.",
613 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
616 additionalProperties
=> 0,
618 node
=> get_standard_option
('pve-node'),
627 description
=> "Name of the CRUSH rule.",
632 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
637 PVE
::Ceph
::Tools
::check_ceph_inited
();
639 my $rados = PVE
::RADOS-
>new();
641 my $rules = $rados->mon_command({ prefix
=> 'osd crush rule ls' });
645 foreach my $rule (@$rules) {
646 push @$res, { name
=> $rule };
652 __PACKAGE__-
>register_method ({
653 name
=> 'cmd_safety',
654 path
=> 'cmd-safety',
656 description
=> "Heuristical check if it is safe to perform an action.",
660 check
=> ['perm', '/', [ 'Sys.Audit' ]],
663 additionalProperties
=> 0,
665 node
=> get_standard_option
('pve-node'),
667 description
=> 'Service type',
669 enum
=> ['osd', 'mon', 'mds'],
672 description
=> 'ID of the service',
676 description
=> 'Action to check',
678 enum
=> ['stop', 'destroy'],
687 description
=> 'If it is safe to run the command.',
692 description
=> 'Status message given by Ceph.'
699 PVE
::Ceph
::Tools
::check_ceph_inited
();
701 my $id = $param->{id
};
702 my $service = $param->{service
};
703 my $action = $param->{action
};
705 my $rados = PVE
::RADOS-
>new();
707 my $supported_actions = {
709 stop
=> 'ok-to-stop',
710 destroy
=> 'safe-to-destroy',
713 stop
=> 'ok-to-stop',
714 destroy
=> 'ok-to-rm',
717 stop
=> 'ok-to-stop',
721 die "Service does not support this action: ${service}: ${action}\n"
722 if !$supported_actions->{$service}->{$action};
730 prefix
=> "${service} $supported_actions->{$service}->{$action}",
733 if ($service eq 'mon' && $action eq 'destroy') {
736 $params->{ids
} = [ $id ];
739 $result = $rados->mon_cmd($params, 1);
742 $result->{safe
} = $result->{return_code
} == 0 ?
1 : 0;
743 $result->{status
} = $result->{status_message
};