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
::Cfg
;
22 use PVE
::API2
::Ceph
::OSD
;
23 use PVE
::API2
::Ceph
::FS
;
24 use PVE
::API2
::Ceph
::MDS
;
25 use PVE
::API2
::Ceph
::MGR
;
26 use PVE
::API2
::Ceph
::MON
;
27 use PVE
::API2
::Ceph
::Pool
;
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::Cfg",
39 __PACKAGE__-
>register_method ({
40 subclass
=> "PVE::API2::Ceph::OSD",
44 __PACKAGE__-
>register_method ({
45 subclass
=> "PVE::API2::Ceph::MDS",
49 __PACKAGE__-
>register_method ({
50 subclass
=> "PVE::API2::Ceph::MGR",
54 __PACKAGE__-
>register_method ({
55 subclass
=> "PVE::API2::Ceph::MON",
59 __PACKAGE__-
>register_method ({
60 subclass
=> "PVE::API2::Ceph::FS",
64 __PACKAGE__-
>register_method ({
65 subclass
=> "PVE::API2::Ceph::Pool",
69 __PACKAGE__-
>register_method ({
73 description
=> "Directory index.",
74 permissions
=> { user
=> 'all' },
76 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
79 additionalProperties
=> 0,
81 node
=> get_standard_option
('pve-node'),
90 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
96 { name
=> 'cmd-safety' },
107 { name
=> 'restart' },
110 { name
=> 'status' },
117 __PACKAGE__-
>register_method ({
121 description
=> "Create initial ceph default configuration and setup symlinks.",
125 check
=> ['perm', '/', [ 'Sys.Modify' ]],
128 additionalProperties
=> 0,
130 node
=> get_standard_option
('pve-node'),
132 description
=> "Use specific network for all ceph related traffic",
133 type
=> 'string', format
=> 'CIDR',
137 'cluster-network' => {
138 description
=> "Declare a separate cluster network, OSDs will route" .
139 "heartbeat, object replication and recovery traffic over it",
140 type
=> 'string', format
=> 'CIDR',
141 requires
=> 'network',
146 description
=> 'Targeted number of replicas per object',
154 description
=> 'Minimum number of available replicas per object to allow I/O',
161 # TODO: deprecrated, remove with PVE 9
163 description
=> "Placement group bits, used to specify the " .
164 "default number of placement groups.\n\nDepreacted. This " .
165 "setting was deprecated in recent Ceph versions.",
173 description
=> "Disable cephx authentication.\n\n" .
174 "WARNING: cephx is a security feature protecting against " .
175 "man-in-the-middle attacks. Only consider disabling cephx ".
176 "if your network is private!",
183 returns
=> { type
=> 'null' },
187 my $version = PVE
::Ceph
::Tools
::get_local_version
(1);
189 if (!$version || $version < 14) {
190 die "Ceph Nautilus required - please run 'pveceph install'\n";
192 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_bin');
195 my $pve_ceph_cfgdir = PVE
::Ceph
::Tools
::get_config
('pve_ceph_cfgdir');
196 if (! -d
$pve_ceph_cfgdir) {
197 File
::Path
::make_path
($pve_ceph_cfgdir);
200 my $auth = $param->{disable_cephx
} ?
'none' : 'cephx';
202 # simply load old config if it already exists
203 PVE
::Cluster
::cfs_lock_file
('ceph.conf', undef, sub {
204 my $cfg = cfs_read_file
('ceph.conf');
206 if (!$cfg->{global
}) {
211 UUID
::generate
($uuid);
212 UUID
::unparse
($uuid, $fsid);
216 'auth_cluster_required' => $auth,
217 'auth_service_required' => $auth,
218 'auth_client_required' => $auth,
219 'osd_pool_default_size' => $param->{size
} // 3,
220 'osd_pool_default_min_size' => $param->{min_size
} // 2,
221 'mon_allow_pool_delete' => 'true',
224 # this does not work for default pools
225 #'osd pool default pg num' => $pg_num,
226 #'osd pool default pgp num' => $pg_num,
229 if ($auth eq 'cephx') {
230 $cfg->{client
}->{keyring
} = '/etc/pve/priv/$cluster.$name.keyring';
233 if ($param->{network
}) {
234 $cfg->{global
}->{'public_network'} = $param->{network
};
235 $cfg->{global
}->{'cluster_network'} = $param->{network
};
238 if ($param->{'cluster-network'}) {
239 $cfg->{global
}->{'cluster_network'} = $param->{'cluster-network'};
242 cfs_write_file
('ceph.conf', $cfg);
244 if ($auth eq 'cephx') {
245 PVE
::Ceph
::Tools
::get_or_create_admin_keyring
();
247 PVE
::Ceph
::Tools
::setup_pve_symlinks
();
254 __PACKAGE__-
>register_method ({
258 description
=> "Stop ceph services.",
262 check
=> ['perm', '/', [ 'Sys.Modify' ]],
265 additionalProperties
=> 0,
267 node
=> get_standard_option
('pve-node'),
269 description
=> 'Ceph service name.',
272 default => 'ceph.target',
273 pattern
=> '(ceph|mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
277 returns
=> { type
=> 'string' },
281 my $rpcenv = PVE
::RPCEnvironment
::get
();
283 my $authuser = $rpcenv->get_user();
285 PVE
::Ceph
::Tools
::check_ceph_inited
();
287 my $cfg = cfs_read_file
('ceph.conf');
288 scalar(keys %$cfg) || die "no configuration\n";
294 if ($param->{service
}) {
295 push @$cmd, $param->{service
};
298 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
301 return $rpcenv->fork_worker('srvstop', $param->{service
} || 'ceph',
305 __PACKAGE__-
>register_method ({
309 description
=> "Start ceph services.",
313 check
=> ['perm', '/', [ 'Sys.Modify' ]],
316 additionalProperties
=> 0,
318 node
=> get_standard_option
('pve-node'),
320 description
=> 'Ceph service name.',
323 default => 'ceph.target',
324 pattern
=> '(ceph|mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
328 returns
=> { type
=> 'string' },
332 my $rpcenv = PVE
::RPCEnvironment
::get
();
334 my $authuser = $rpcenv->get_user();
336 PVE
::Ceph
::Tools
::check_ceph_inited
();
338 my $cfg = cfs_read_file
('ceph.conf');
339 scalar(keys %$cfg) || die "no configuration\n";
345 if ($param->{service
}) {
346 push @$cmd, $param->{service
};
349 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
352 return $rpcenv->fork_worker('srvstart', $param->{service
} || 'ceph',
356 __PACKAGE__-
>register_method ({
360 description
=> "Restart ceph services.",
364 check
=> ['perm', '/', [ 'Sys.Modify' ]],
367 additionalProperties
=> 0,
369 node
=> get_standard_option
('pve-node'),
371 description
=> 'Ceph service name.',
374 default => 'ceph.target',
375 pattern
=> '(mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
379 returns
=> { type
=> 'string' },
383 my $rpcenv = PVE
::RPCEnvironment
::get
();
385 my $authuser = $rpcenv->get_user();
387 PVE
::Ceph
::Tools
::check_ceph_inited
();
389 my $cfg = cfs_read_file
('ceph.conf');
390 scalar(keys %$cfg) || die "no configuration\n";
395 my $cmd = ['restart'];
396 if ($param->{service
}) {
397 push @$cmd, $param->{service
};
400 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
403 return $rpcenv->fork_worker('srvrestart', $param->{service
} || 'ceph',
407 __PACKAGE__-
>register_method ({
411 description
=> "Get ceph status.",
415 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
418 additionalProperties
=> 0,
420 node
=> get_standard_option
('pve-node'),
423 returns
=> { type
=> 'object' },
427 PVE
::Ceph
::Tools
::check_ceph_inited
();
429 return PVE
::Ceph
::Tools
::ceph_cluster_status
();
433 __PACKAGE__-
>register_method ({
437 description
=> "Get OSD crush map",
441 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
444 additionalProperties
=> 0,
446 node
=> get_standard_option
('pve-node'),
449 returns
=> { type
=> 'string' },
453 PVE
::Ceph
::Tools
::check_ceph_inited
();
455 # this produces JSON (difficult to read for the user)
456 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
460 my $mapfile = "/var/tmp/ceph-crush.map.$$";
461 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
463 my $rados = PVE
::RADOS-
>new();
466 my $bindata = $rados->mon_command({ prefix
=> 'osd getcrushmap', format
=> 'plain' });
467 file_set_contents
($mapfile, $bindata);
468 run_command
(['crushtool', '-d', $mapfile, '-o', $mapdata]);
469 $txt = file_get_contents
($mapdata);
481 __PACKAGE__-
>register_method({
485 description
=> "Read ceph log",
488 check
=> ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
492 additionalProperties
=> 0,
494 node
=> get_standard_option
('pve-node'),
513 description
=> "Line number",
517 description
=> "Line text",
526 PVE
::Ceph
::Tools
::check_ceph_inited
();
528 my $rpcenv = PVE
::RPCEnvironment
::get
();
529 my $user = $rpcenv->get_user();
530 my $node = $param->{node
};
532 my $logfile = "/var/log/ceph/ceph.log";
533 my ($count, $lines) = PVE
::Tools
::dump_logfile
($logfile, $param->{start
}, $param->{limit
});
535 $rpcenv->set_result_attrib('total', $count);
540 __PACKAGE__-
>register_method ({
544 description
=> "List ceph rules.",
548 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
551 additionalProperties
=> 0,
553 node
=> get_standard_option
('pve-node'),
562 description
=> "Name of the CRUSH rule.",
567 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
572 PVE
::Ceph
::Tools
::check_ceph_inited
();
574 my $rados = PVE
::RADOS-
>new();
576 my $rules = $rados->mon_command({ prefix
=> 'osd crush rule ls' });
580 foreach my $rule (@$rules) {
581 push @$res, { name
=> $rule };
587 __PACKAGE__-
>register_method ({
588 name
=> 'cmd_safety',
589 path
=> 'cmd-safety',
591 description
=> "Heuristical check if it is safe to perform an action.",
595 check
=> ['perm', '/', [ 'Sys.Audit' ]],
598 additionalProperties
=> 0,
600 node
=> get_standard_option
('pve-node'),
602 description
=> 'Service type',
604 enum
=> ['osd', 'mon', 'mds'],
607 description
=> 'ID of the service',
611 description
=> 'Action to check',
613 enum
=> ['stop', 'destroy'],
622 description
=> 'If it is safe to run the command.',
627 description
=> 'Status message given by Ceph.'
634 PVE
::Ceph
::Tools
::check_ceph_inited
();
636 my $id = $param->{id
};
637 my $service = $param->{service
};
638 my $action = $param->{action
};
640 my $rados = PVE
::RADOS-
>new();
642 my $supported_actions = {
644 stop
=> 'ok-to-stop',
645 destroy
=> 'safe-to-destroy',
648 stop
=> 'ok-to-stop',
649 destroy
=> 'ok-to-rm',
652 stop
=> 'ok-to-stop',
656 die "Service does not support this action: ${service}: ${action}\n"
657 if !$supported_actions->{$service}->{$action};
665 prefix
=> "${service} $supported_actions->{$service}->{$action}",
668 if ($service eq 'mon' && $action eq 'destroy') {
671 $params->{ids
} = [ $id ];
674 $result = $rados->mon_cmd($params, 1);
677 $result->{safe
} = $result->{return_code
} == 0 ?
1 : 0;
678 $result->{status
} = $result->{status_message
};