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}" } ],
98 { name
=> 'restart' },
101 { name
=> 'config' },
109 __PACKAGE__-
>register_method ({
115 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
117 description
=> "Get Ceph configuration.",
119 additionalProperties
=> 0,
121 node
=> get_standard_option
('pve-node'),
124 returns
=> { type
=> 'string' },
128 PVE
::Ceph
::Tools
::check_ceph_inited
();
130 my $path = PVE
::Ceph
::Tools
::get_config
('pve_ceph_cfgpath');
131 return file_get_contents
($path);
135 __PACKAGE__-
>register_method ({
142 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
144 description
=> "Get Ceph configuration database.",
146 additionalProperties
=> 0,
148 node
=> get_standard_option
('pve-node'),
156 section
=> { type
=> "string", },
157 name
=> { type
=> "string", },
158 value
=> { type
=> "string", },
159 level
=> { type
=> "string", },
160 'can_update_at_runtime' => { type
=> "boolean", },
161 mask
=> { type
=> "string" },
168 PVE
::Ceph
::Tools
::check_ceph_inited
();
170 my $rados = PVE
::RADOS-
>new();
171 my $res = $rados->mon_command( { prefix
=> 'config dump', format
=> 'json' });
172 foreach my $entry (@$res) {
173 $entry->{can_update_at_runtime
} = $entry->{can_update_at_runtime
}?
1 : 0; # JSON::true/false -> 1/0
180 __PACKAGE__-
>register_method ({
184 description
=> "Create initial ceph default configuration and setup symlinks.",
188 check
=> ['perm', '/', [ 'Sys.Modify' ]],
191 additionalProperties
=> 0,
193 node
=> get_standard_option
('pve-node'),
195 description
=> "Use specific network for all ceph related traffic",
196 type
=> 'string', format
=> 'CIDR',
200 'cluster-network' => {
201 description
=> "Declare a separate cluster network, OSDs will route" .
202 "heartbeat, object replication and recovery traffic over it",
203 type
=> 'string', format
=> 'CIDR',
204 requires
=> 'network',
209 description
=> 'Targeted number of replicas per object',
217 description
=> 'Minimum number of available replicas per object to allow I/O',
225 description
=> "Placement group bits, used to specify the " .
226 "default number of placement groups.\n\nNOTE: 'osd pool " .
227 "default pg num' does not work for default pools.",
235 description
=> "Disable cephx authentication.\n\n" .
236 "WARNING: cephx is a security feature protecting against " .
237 "man-in-the-middle attacks. Only consider disabling cephx ".
238 "if your network is private!",
245 returns
=> { type
=> 'null' },
249 my $version = PVE
::Ceph
::Tools
::get_local_version
(1);
251 if (!$version || $version < 14) {
252 die "Ceph Nautilus required - please run 'pveceph install'\n";
254 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_bin');
257 my $auth = $param->{disable_cephx
} ?
'none' : 'cephx';
259 # simply load old config if it already exists
260 PVE
::Cluster
::cfs_lock_file
('ceph.conf', undef, sub {
261 my $cfg = cfs_read_file
('ceph.conf');
263 if (!$cfg->{global
}) {
268 UUID
::generate
($uuid);
269 UUID
::unparse
($uuid, $fsid);
273 'auth cluster required' => $auth,
274 'auth service required' => $auth,
275 'auth client required' => $auth,
276 'osd pool default size' => $param->{size
} // 3,
277 'osd pool default min size' => $param->{min_size
} // 2,
278 'mon allow pool delete' => 'true',
281 # this does not work for default pools
282 #'osd pool default pg num' => $pg_num,
283 #'osd pool default pgp num' => $pg_num,
286 if ($auth eq 'cephx') {
287 $cfg->{client
}->{keyring
} = '/etc/pve/priv/$cluster.$name.keyring';
290 if ($param->{pg_bits
}) {
291 $cfg->{global
}->{'osd pg bits'} = $param->{pg_bits
};
292 $cfg->{global
}->{'osd pgp bits'} = $param->{pg_bits
};
295 if ($param->{network
}) {
296 $cfg->{global
}->{'public network'} = $param->{network
};
297 $cfg->{global
}->{'cluster network'} = $param->{network
};
300 if ($param->{'cluster-network'}) {
301 $cfg->{global
}->{'cluster network'} = $param->{'cluster-network'};
304 cfs_write_file
('ceph.conf', $cfg);
306 if ($auth eq 'cephx') {
307 PVE
::Ceph
::Tools
::get_or_create_admin_keyring
();
309 PVE
::Ceph
::Tools
::setup_pve_symlinks
();
316 __PACKAGE__-
>register_method ({
320 description
=> "Stop ceph services.",
324 check
=> ['perm', '/', [ 'Sys.Modify' ]],
327 additionalProperties
=> 0,
329 node
=> get_standard_option
('pve-node'),
331 description
=> 'Ceph service name.',
334 default => 'ceph.target',
335 pattern
=> '(ceph|mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
339 returns
=> { type
=> 'string' },
343 my $rpcenv = PVE
::RPCEnvironment
::get
();
345 my $authuser = $rpcenv->get_user();
347 PVE
::Ceph
::Tools
::check_ceph_inited
();
349 my $cfg = cfs_read_file
('ceph.conf');
350 scalar(keys %$cfg) || die "no configuration\n";
356 if ($param->{service
}) {
357 push @$cmd, $param->{service
};
360 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
363 return $rpcenv->fork_worker('srvstop', $param->{service
} || 'ceph',
367 __PACKAGE__-
>register_method ({
371 description
=> "Start ceph services.",
375 check
=> ['perm', '/', [ 'Sys.Modify' ]],
378 additionalProperties
=> 0,
380 node
=> get_standard_option
('pve-node'),
382 description
=> 'Ceph service name.',
385 default => 'ceph.target',
386 pattern
=> '(ceph|mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
390 returns
=> { type
=> 'string' },
394 my $rpcenv = PVE
::RPCEnvironment
::get
();
396 my $authuser = $rpcenv->get_user();
398 PVE
::Ceph
::Tools
::check_ceph_inited
();
400 my $cfg = cfs_read_file
('ceph.conf');
401 scalar(keys %$cfg) || die "no configuration\n";
407 if ($param->{service
}) {
408 push @$cmd, $param->{service
};
411 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
414 return $rpcenv->fork_worker('srvstart', $param->{service
} || 'ceph',
418 __PACKAGE__-
>register_method ({
422 description
=> "Restart ceph services.",
426 check
=> ['perm', '/', [ 'Sys.Modify' ]],
429 additionalProperties
=> 0,
431 node
=> get_standard_option
('pve-node'),
433 description
=> 'Ceph service name.',
436 default => 'ceph.target',
437 pattern
=> '(mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
441 returns
=> { type
=> 'string' },
445 my $rpcenv = PVE
::RPCEnvironment
::get
();
447 my $authuser = $rpcenv->get_user();
449 PVE
::Ceph
::Tools
::check_ceph_inited
();
451 my $cfg = cfs_read_file
('ceph.conf');
452 scalar(keys %$cfg) || die "no configuration\n";
457 my $cmd = ['restart'];
458 if ($param->{service
}) {
459 push @$cmd, $param->{service
};
462 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
465 return $rpcenv->fork_worker('srvrestart', $param->{service
} || 'ceph',
469 __PACKAGE__-
>register_method ({
473 description
=> "Get ceph status.",
477 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
480 additionalProperties
=> 0,
482 node
=> get_standard_option
('pve-node'),
485 returns
=> { type
=> 'object' },
489 PVE
::Ceph
::Tools
::check_ceph_inited
();
491 return PVE
::Ceph
::Tools
::ceph_cluster_status
();
495 __PACKAGE__-
>register_method ({
499 description
=> "Get OSD crush map",
503 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
506 additionalProperties
=> 0,
508 node
=> get_standard_option
('pve-node'),
511 returns
=> { type
=> 'string' },
515 PVE
::Ceph
::Tools
::check_ceph_inited
();
517 # this produces JSON (difficult to read for the user)
518 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
522 my $mapfile = "/var/tmp/ceph-crush.map.$$";
523 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
525 my $rados = PVE
::RADOS-
>new();
528 my $bindata = $rados->mon_command({ prefix
=> 'osd getcrushmap', format
=> 'plain' });
529 file_set_contents
($mapfile, $bindata);
530 run_command
(['crushtool', '-d', $mapfile, '-o', $mapdata]);
531 $txt = file_get_contents
($mapdata);
543 __PACKAGE__-
>register_method({
547 description
=> "Read ceph log",
550 check
=> ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
554 additionalProperties
=> 0,
556 node
=> get_standard_option
('pve-node'),
575 description
=> "Line number",
579 description
=> "Line text",
588 PVE
::Ceph
::Tools
::check_ceph_inited
();
590 my $rpcenv = PVE
::RPCEnvironment
::get
();
591 my $user = $rpcenv->get_user();
592 my $node = $param->{node
};
594 my $logfile = "/var/log/ceph/ceph.log";
595 my ($count, $lines) = PVE
::Tools
::dump_logfile
($logfile, $param->{start
}, $param->{limit
});
597 $rpcenv->set_result_attrib('total', $count);
602 __PACKAGE__-
>register_method ({
606 description
=> "List ceph rules.",
610 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
613 additionalProperties
=> 0,
615 node
=> get_standard_option
('pve-node'),
624 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
629 PVE
::Ceph
::Tools
::check_ceph_inited
();
631 my $rados = PVE
::RADOS-
>new();
633 my $rules = $rados->mon_command({ prefix
=> 'osd crush rule ls' });
637 foreach my $rule (@$rules) {
638 push @$res, { name
=> $rule };
644 __PACKAGE__-
>register_method ({
645 name
=> 'cmd_safety',
646 path
=> 'cmd-safety',
648 description
=> "Heuristical check if it is safe to perform an action.",
652 check
=> ['perm', '/', [ 'Sys.audit' ]],
655 additionalProperties
=> 0,
657 node
=> get_standard_option
('pve-node'),
659 description
=> 'Service type',
661 enum
=> ['osd', 'mon', 'mds'],
664 description
=> 'ID of the service',
668 description
=> 'Action to check',
670 enum
=> ['stop', 'destroy'],
679 description
=> 'If it is safe to run the command.',
684 description
=> 'Status message given by Ceph.'
691 PVE
::Ceph
::Tools
::check_ceph_inited
();
693 my $id = $param->{id
};
694 my $service = $param->{service
};
695 my $action = $param->{action
};
697 my $rados = PVE
::RADOS-
>new();
699 my $supported_actions = {
701 stop
=> 'ok-to-stop',
702 destroy
=> 'safe-to-destroy',
705 stop
=> 'ok-to-stop',
706 destroy
=> 'ok-to-rm',
709 stop
=> 'ok-to-stop',
713 die "Service does not support this action: ${service}: ${action}\n"
714 if !$supported_actions->{$service}->{$action};
722 prefix
=> "${service} $supported_actions->{$service}->{$action}",
725 if ($service eq 'mon' && $action eq 'destroy') {
728 $params->{ids
} = [ $id ];
731 $result = $rados->mon_cmd($params, 1);
734 $result->{safe
} = $result->{return_code
} == 0 ?
1 : 0;
735 $result->{status
} = $result->{status_message
};