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' },
99 { name
=> 'configdb' },
109 { name
=> 'restart' },
112 { name
=> 'status' },
119 __PACKAGE__-
>register_method ({
123 description
=> "Create initial ceph default configuration and setup symlinks.",
127 check
=> ['perm', '/', [ 'Sys.Modify' ]],
130 additionalProperties
=> 0,
132 node
=> get_standard_option
('pve-node'),
134 description
=> "Use specific network for all ceph related traffic",
135 type
=> 'string', format
=> 'CIDR',
139 'cluster-network' => {
140 description
=> "Declare a separate cluster network, OSDs will route" .
141 "heartbeat, object replication and recovery traffic over it",
142 type
=> 'string', format
=> 'CIDR',
143 requires
=> 'network',
148 description
=> 'Targeted number of replicas per object',
156 description
=> 'Minimum number of available replicas per object to allow I/O',
164 description
=> "Placement group bits, used to specify the " .
165 "default number of placement groups.\n\nNOTE: 'osd pool " .
166 "default pg num' does not work for default pools.",
174 description
=> "Disable cephx authentication.\n\n" .
175 "WARNING: cephx is a security feature protecting against " .
176 "man-in-the-middle attacks. Only consider disabling cephx ".
177 "if your network is private!",
184 returns
=> { type
=> 'null' },
188 my $version = PVE
::Ceph
::Tools
::get_local_version
(1);
190 if (!$version || $version < 14) {
191 die "Ceph Nautilus required - please run 'pveceph install'\n";
193 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_bin');
196 my $auth = $param->{disable_cephx
} ?
'none' : 'cephx';
198 # simply load old config if it already exists
199 PVE
::Cluster
::cfs_lock_file
('ceph.conf', undef, sub {
200 my $cfg = cfs_read_file
('ceph.conf');
202 if (!$cfg->{global
}) {
207 UUID
::generate
($uuid);
208 UUID
::unparse
($uuid, $fsid);
212 'auth cluster required' => $auth,
213 'auth service required' => $auth,
214 'auth client required' => $auth,
215 'osd pool default size' => $param->{size
} // 3,
216 'osd pool default min size' => $param->{min_size
} // 2,
217 'mon allow pool delete' => 'true',
220 # this does not work for default pools
221 #'osd pool default pg num' => $pg_num,
222 #'osd pool default pgp num' => $pg_num,
225 if ($auth eq 'cephx') {
226 $cfg->{client
}->{keyring
} = '/etc/pve/priv/$cluster.$name.keyring';
229 if ($param->{pg_bits
}) {
230 $cfg->{global
}->{'osd pg bits'} = $param->{pg_bits
};
231 $cfg->{global
}->{'osd pgp bits'} = $param->{pg_bits
};
234 if ($param->{network
}) {
235 $cfg->{global
}->{'public network'} = $param->{network
};
236 $cfg->{global
}->{'cluster network'} = $param->{network
};
239 if ($param->{'cluster-network'}) {
240 $cfg->{global
}->{'cluster network'} = $param->{'cluster-network'};
243 cfs_write_file
('ceph.conf', $cfg);
245 if ($auth eq 'cephx') {
246 PVE
::Ceph
::Tools
::get_or_create_admin_keyring
();
248 PVE
::Ceph
::Tools
::setup_pve_symlinks
();
255 __PACKAGE__-
>register_method ({
259 description
=> "Stop ceph services.",
263 check
=> ['perm', '/', [ 'Sys.Modify' ]],
266 additionalProperties
=> 0,
268 node
=> get_standard_option
('pve-node'),
270 description
=> 'Ceph service name.',
273 default => 'ceph.target',
274 pattern
=> '(ceph|mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
278 returns
=> { type
=> 'string' },
282 my $rpcenv = PVE
::RPCEnvironment
::get
();
284 my $authuser = $rpcenv->get_user();
286 PVE
::Ceph
::Tools
::check_ceph_inited
();
288 my $cfg = cfs_read_file
('ceph.conf');
289 scalar(keys %$cfg) || die "no configuration\n";
295 if ($param->{service
}) {
296 push @$cmd, $param->{service
};
299 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
302 return $rpcenv->fork_worker('srvstop', $param->{service
} || 'ceph',
306 __PACKAGE__-
>register_method ({
310 description
=> "Start ceph services.",
314 check
=> ['perm', '/', [ 'Sys.Modify' ]],
317 additionalProperties
=> 0,
319 node
=> get_standard_option
('pve-node'),
321 description
=> 'Ceph service name.',
324 default => 'ceph.target',
325 pattern
=> '(ceph|mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
329 returns
=> { type
=> 'string' },
333 my $rpcenv = PVE
::RPCEnvironment
::get
();
335 my $authuser = $rpcenv->get_user();
337 PVE
::Ceph
::Tools
::check_ceph_inited
();
339 my $cfg = cfs_read_file
('ceph.conf');
340 scalar(keys %$cfg) || die "no configuration\n";
346 if ($param->{service
}) {
347 push @$cmd, $param->{service
};
350 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
353 return $rpcenv->fork_worker('srvstart', $param->{service
} || 'ceph',
357 __PACKAGE__-
>register_method ({
361 description
=> "Restart ceph services.",
365 check
=> ['perm', '/', [ 'Sys.Modify' ]],
368 additionalProperties
=> 0,
370 node
=> get_standard_option
('pve-node'),
372 description
=> 'Ceph service name.',
375 default => 'ceph.target',
376 pattern
=> '(mon|mds|osd|mgr)(\.'.PVE
::Ceph
::Services
::SERVICE_REGEX
.')?',
380 returns
=> { type
=> 'string' },
384 my $rpcenv = PVE
::RPCEnvironment
::get
();
386 my $authuser = $rpcenv->get_user();
388 PVE
::Ceph
::Tools
::check_ceph_inited
();
390 my $cfg = cfs_read_file
('ceph.conf');
391 scalar(keys %$cfg) || die "no configuration\n";
396 my $cmd = ['restart'];
397 if ($param->{service
}) {
398 push @$cmd, $param->{service
};
401 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
404 return $rpcenv->fork_worker('srvrestart', $param->{service
} || 'ceph',
408 __PACKAGE__-
>register_method ({
412 description
=> "Get ceph status.",
416 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
419 additionalProperties
=> 0,
421 node
=> get_standard_option
('pve-node'),
424 returns
=> { type
=> 'object' },
428 PVE
::Ceph
::Tools
::check_ceph_inited
();
430 return PVE
::Ceph
::Tools
::ceph_cluster_status
();
434 __PACKAGE__-
>register_method ({
438 description
=> "Get OSD crush map",
442 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
445 additionalProperties
=> 0,
447 node
=> get_standard_option
('pve-node'),
450 returns
=> { type
=> 'string' },
454 PVE
::Ceph
::Tools
::check_ceph_inited
();
456 # this produces JSON (difficult to read for the user)
457 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
461 my $mapfile = "/var/tmp/ceph-crush.map.$$";
462 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
464 my $rados = PVE
::RADOS-
>new();
467 my $bindata = $rados->mon_command({ prefix
=> 'osd getcrushmap', format
=> 'plain' });
468 file_set_contents
($mapfile, $bindata);
469 run_command
(['crushtool', '-d', $mapfile, '-o', $mapdata]);
470 $txt = file_get_contents
($mapdata);
482 __PACKAGE__-
>register_method({
486 description
=> "Read ceph log",
489 check
=> ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
493 additionalProperties
=> 0,
495 node
=> get_standard_option
('pve-node'),
514 description
=> "Line number",
518 description
=> "Line text",
527 PVE
::Ceph
::Tools
::check_ceph_inited
();
529 my $rpcenv = PVE
::RPCEnvironment
::get
();
530 my $user = $rpcenv->get_user();
531 my $node = $param->{node
};
533 my $logfile = "/var/log/ceph/ceph.log";
534 my ($count, $lines) = PVE
::Tools
::dump_logfile
($logfile, $param->{start
}, $param->{limit
});
536 $rpcenv->set_result_attrib('total', $count);
541 __PACKAGE__-
>register_method ({
545 description
=> "List ceph rules.",
549 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
552 additionalProperties
=> 0,
554 node
=> get_standard_option
('pve-node'),
563 description
=> "Name of the CRUSH rule.",
568 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
573 PVE
::Ceph
::Tools
::check_ceph_inited
();
575 my $rados = PVE
::RADOS-
>new();
577 my $rules = $rados->mon_command({ prefix
=> 'osd crush rule ls' });
581 foreach my $rule (@$rules) {
582 push @$res, { name
=> $rule };
588 __PACKAGE__-
>register_method ({
589 name
=> 'cmd_safety',
590 path
=> 'cmd-safety',
592 description
=> "Heuristical check if it is safe to perform an action.",
596 check
=> ['perm', '/', [ 'Sys.Audit' ]],
599 additionalProperties
=> 0,
601 node
=> get_standard_option
('pve-node'),
603 description
=> 'Service type',
605 enum
=> ['osd', 'mon', 'mds'],
608 description
=> 'ID of the service',
612 description
=> 'Action to check',
614 enum
=> ['stop', 'destroy'],
623 description
=> 'If it is safe to run the command.',
628 description
=> 'Status message given by Ceph.'
635 PVE
::Ceph
::Tools
::check_ceph_inited
();
637 my $id = $param->{id
};
638 my $service = $param->{service
};
639 my $action = $param->{action
};
641 my $rados = PVE
::RADOS-
>new();
643 my $supported_actions = {
645 stop
=> 'ok-to-stop',
646 destroy
=> 'safe-to-destroy',
649 stop
=> 'ok-to-stop',
650 destroy
=> 'ok-to-rm',
653 stop
=> 'ok-to-stop',
657 die "Service does not support this action: ${service}: ${action}\n"
658 if !$supported_actions->{$service}->{$action};
666 prefix
=> "${service} $supported_actions->{$service}->{$action}",
669 if ($service eq 'mon' && $action eq 'destroy') {
672 $params->{ids
} = [ $id ];
675 $result = $rados->mon_cmd($params, 1);
678 $result->{safe
} = $result->{return_code
} == 0 ?
1 : 0;
679 $result->{status
} = $result->{status_message
};